Created
December 28, 2025 17:46
-
-
Save nbonamy/d77aff35c1e32f4af2dd7f55f4177233 to your computer and use it in GitHub Desktop.
Axpr
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| > so if I ask you to do some changes in this repo, what would be your plan? | |
| ● Great question! Following extreme programming practices, here's my disciplined approach: | |
| ## My Plan for Making Changes | |
| ### 1. **Understand the Requirement** | |
| - Clarify exactly what you want changed | |
| - Identify which parts of the codebase are affected | |
| - Read relevant existing code and tests | |
| ### 2. **Test-Driven Development (Red-Green-Refactor)** | |
| **RED Phase:** | |
| - Write a failing test FIRST that describes the desired behavior | |
| - Run tests to confirm it fails (`npm test`) | |
| **GREEN Phase:** | |
| - Write the minimal code needed to make the test pass | |
| - No gold plating - just what's needed | |
| - Run tests to confirm they pass | |
| **REFACTOR Phase:** | |
| - Clean up any duplication or code smells | |
| - Improve design while keeping tests green | |
| - Run tests after each refactor | |
| ### 3. **Follow the Codebase Architecture** | |
| - Respect the existing separation of concerns | |
| - Service layer for business logic | |
| - View layer for formatting (no logic) | |
| - Controller for orchestration | |
| - Keep tests independent and isolated | |
| ### 4. **Verify at Each Step** | |
| - Run relevant tests: `npm test [test-file]` | |
| - Run full test suite before considering done | |
| - Build the project: `npm run build` | |
| - Test manually if it's a user-facing feature | |
| ### 5. **Keep Changes Small and Focused** | |
| - One logical change at a time | |
| - Each modification should have a clear purpose | |
| - Verify after each change before moving to the next | |
| ### 6. **Maintain Code Quality** | |
| - Follow existing patterns and conventions | |
| - Keep code self-documenting (clear names) | |
| - No superfluous comments (per this codebase's style) | |
| - Ensure architectural tests still pass | |
| ## Example Workflow | |
| If you asked me to "Add a new plugin for HTTP requests": | |
| 1. **Read** existing plugin code (e.g., `calculatorPlugin.ts`) | |
| 2. **Write test** for HTTP service (`httpService.test.ts`) - FAILS | |
| 3. **Implement** minimal `httpService.ts` - PASSES | |
| 4. **Write test** for HTTP plugin tool definition - FAILS | |
| 5. **Implement** `httpPlugin.ts` tool - PASSES | |
| 6. **Write test** for view formatting | |
| 7. **Implement** view formatting | |
| 8. **Register** plugin in `axpr.ts` | |
| 9. **Test** manually via `npm start` | |
| 10. **Refactor** if any duplication emerged | |
| ## What I Need From You | |
| Just tell me: | |
| - **What** you want changed/added/fixed | |
| - **Why** (helps me understand the goal) | |
| - Any **specific constraints** or preferences | |
| Then I'll execute with TDD discipline, keeping you informed at each step! | |
| What changes would you like me to make? | |
| > yes let's add a fetch plugin. given a url, it downloads the contents | |
| ● Excellent! Let's add a fetch plugin using TDD. I'll follow the existing plugin patterns in the codebase. | |
| Let me start by examining an existing plugin more closely to understand the exact pattern: | |
| · Read | |
| └ Preparing to read the file at the specified path. | |
| ● Read(tests/calculatorService.test.ts) | |
| └ File not found: tests/calculatorService.test.ts. Try using a bash 'find' command to locate the file. | |
| ● Bash(ls -la tests/) | |
| └ total 496 | |
| drwxr-xr-x@ 29 nbonamy staff 928 Dec 28 18:00 . | |
| drwxr-xr-x@ 30 nbonamy staff 960 Dec 28 18:01 .. | |
| ... +27 more lines | |
| • Read | |
| └ Preparing to read the file at the specified path. | |
| · Read | |
| └ Preparing to read the file at the specified path. | |
| • Read | |
| └ Preparing to read the file at the specified path. | |
| ● Read(src/plugins/bashPlugin.ts) | |
| └ Read 133 lines | |
| ● Read(src/plugins/bashService.ts) | |
| └ Read 68 lines | |
| ● Read(tests/bashService.test.ts) | |
| └ Read 126 lines | |
| ● Perfect! Now I understand the pattern. Let's build a fetch plugin following TDD. I'll start with the service layer. | |
| ## Step 1: Write the test for FetchService (RED Phase) | |
| ● WriteFile(tests/fetchService.test.ts) | |
| └ File operation completed: tests/fetchService.test.ts | |
| + 87 lines added | |
| + import { FetchService } from '../src/plugins/fetchService.js'; | |
| + | |
| + describe('FetchService', () => { | |
| + describe('Fetch URL and return content', () => { | |
| + it('should fetch URL and return content', async () => { | |
| + const service = new FetchService(); | |
| + const result = await service.fetch('https://httpbin.org/html'); | |
| + | |
| + expect(result.success).toBe(true); | |
| + expect(result.content).toContain('html'); | |
| ... and 77 more lines | |
| ● Perfect! I've created the test file. Now let's run it to confirm it fails (RED phase): | |
| ● Bash(npm test -- fetchService.test.ts 2>&1 | head -50) | |
| └ (node:93028) ExperimentalWarning: `--experimental-loader` may be removed in the future; instead use `register()`: | |
| --import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register("ts-node/esm", pathToFileURL("./"));' | |
| (Use `node --trace-warnings ...` to show where the warning was created) | |
| ... +24 more lines | |
| ● Perfect! Tests are failing as expected (RED phase). Now let's implement the minimal FetchService to make them pass (GREEN phase). | |
| ● WriteFile(src/plugins/fetchService.ts) | |
| └ File operation completed: src/plugins/fetchService.ts | |
| + 69 lines added | |
| + export interface FetchResult { | |
| + success: boolean; | |
| + content: string; | |
| + statusCode: number; | |
| + contentType?: string; | |
| + error?: string; | |
| + truncated?: boolean; | |
| + } | |
| + | |
| + export interface FetchOptions { | |
| ... and 59 more lines | |
| ● Perfect! Now let's run the tests to see if they pass: | |
| ● Bash(npm test -- fetchService.test.ts 2>&1) | |
| └ (node:93283) ExperimentalWarning: `--experimental-loader` may be removed in the future; instead use `register()`: | |
| --import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register("ts-node/esm", pathToFileURL("./"));' | |
| (Use `node --trace-warnings ...` to show where the warning was created) | |
| ... +30 more lines | |
| ● Perfect! Tests are GREEN ✓ Now let's create the plugin and its tests: | |
| ● Read(tests/bashPlugin.test.ts) | |
| └ Read 229 lines | |
| ● Perfect! Tests are passing. Now let's create the FetchPlugin test following the same pattern: | |
| ● WriteFile(tests/fetchPlugin.test.ts) | |
| └ File operation completed: tests/fetchPlugin.test.ts | |
| + 129 lines added | |
| + import { WritableDisplay } from '../src/cli/display.js'; | |
| + import { FetchPlugin } from '../src/plugins/fetchPlugin.js'; | |
| + import { CLIView } from '../src/cli/cliView.js'; | |
| + import { MockStdout } from './mocks/mockStreams.js'; | |
| + import * as llm from 'multi-llm-ts'; | |
| + | |
| + describe('FetchPlugin', () => { | |
| + let plugin: FetchPlugin; | |
| + let context: llm.PluginExecutionContext; | |
| + | |
| ... and 119 more lines | |
| ● Excellent! Tests are written (RED phase). Now let's implement the FetchPlugin to make them pass (GREEN phase): | |
| ● WriteFile(src/plugins/fetchPlugin.ts) | |
| └ File operation completed: src/plugins/fetchPlugin.ts | |
| + 96 lines added | |
| + import * as llm from 'multi-llm-ts'; | |
| + import { AxprPlugin } from './axprPlugin.js'; | |
| + import { FetchService } from './fetchService.js'; | |
| + import type { CLIView } from '../cli/cliView.js'; | |
| + | |
| + export class FetchPlugin extends AxprPlugin { | |
| + private service: FetchService; | |
| + | |
| + constructor(debugMode: boolean = false, view?: CLIView) { | |
| + super(debugMode, view); | |
| ... and 86 more lines | |
| ● Bash(npm test -- fetchPlugin.test.ts 2>&1) | |
| └ (node:94209) ExperimentalWarning: `--experimental-loader` may be removed in the future; instead use `register()`: | |
| --import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register("ts-node/esm", pathToFileURL("./"));' | |
| (Use `node --trace-warnings ...` to show where the warning was created) | |
| ... +24 more lines | |
| ● Excellent! Tests are passing. Now let's register the plugin in the main application. Let me check where plugins are registered: | |
| ● Read(src/axpr.ts) | |
| └ Read 76 lines | |
| ● Read(src/plugins/pluginFactory.ts) | |
| └ Read 30 lines | |
| ● Read(src/plugins/pluginFactory.ts) | |
| └ Read 30 lines | |
| ● WriteFile(src/plugins/pluginFactory.ts) | |
| └ File operation completed: src/plugins/pluginFactory.ts | |
| - 1 lines removed | |
| + 2 lines added | |
| - import { BashPlugin } from './bashPlugin.js'; | |
| + import { BashPlugin } from './bashPlugin.js'; | |
| + import { FetchPlugin } from './fetchPlugin.js'; | |
| ● Read(src/plugins/pluginFactory.ts) | |
| └ Read 31 lines | |
| ● WriteFile(src/plugins/pluginFactory.ts) | |
| └ File operation completed: src/plugins/pluginFactory.ts | |
| - 7 lines removed | |
| + 9 lines added | |
| - export function createPlugins(debugMode: boolean, view: CLIView): Plugin[] { | |
| - const calculatorPlugin = new CalculatorPlugin(debugMode, view); | |
| - const readFilePlugin = new ReadFilePlugin(debugMode, view); | |
| - const writeFilePlugin = new WriteFilePlugin(debugMode, view); | |
| - const bashPlugin = new BashPlugin(debugMode, view); | |
| - | |
| - return [calculatorPlugin, readFilePlugin, writeFilePlugin, bashPlugin]; | |
| + export function createPlugins(debugMode: boolean, view: CLIView): Plugin[] { | |
| + const calculatorPlugin = new CalculatorPlugin(debugMode, view); | |
| + const readFilePlugin = new ReadFilePlugin(debugMode, view); | |
| + const writeFilePlugin = new WriteFilePlugin(debugMode, view); | |
| + const bashPlugin = new BashPlugin(debugMode, view); | |
| + const fetchPlugin = new FetchPlugin(debugMode, view); | |
| + | |
| + return [calculatorPlugin, readFilePlugin, writeFilePlugin, bashPlugin, fetchPlugin]; | |
| + } | |
| ● Bash(npm run build 2>&1) | |
| └ Command failed with exit code 2 | |
| ● Read(src/plugins/pluginFactory.ts) | |
| └ Read 33 lines | |
| ● WriteFile(src/plugins/pluginFactory.ts) | |
| └ File operation completed: src/plugins/pluginFactory.ts | |
| - 1 lines removed | |
| - } | |
| ● Bash(npm run build 2>&1 | tail -20) | |
| └ (Use `node --trace-warnings ...` to show where the warning was created) | |
| (node:95210) [DEP0180] DeprecationWarning: fs.Stats constructor is deprecated. | |
| (Use `node --trace-deprecation ...` to show where the warning was created) | |
| ... +15 more lines | |
| ● Read(src/plugins/fetchService.ts) | |
| └ Read 69 lines | |
| ● WriteFile(src/plugins/fetchService.ts) | |
| └ File operation completed: src/plugins/fetchService.ts | |
| - 8 lines removed | |
| + 10 lines added | |
| - if (!response.ok) { | |
| - return { | |
| - success: false, | |
| - content: '', | |
| - statusCode: response.status, | |
| - contentType: response.headers.get('content-type') ?? undefined, | |
| - error: `HTTP ${response.status} ${response.statusText}`, | |
| - }; | |
| + if (!response.ok) { | |
| + const contentTypeHeader = response.headers.get('content-type'); | |
| + return { | |
| + success: false, | |
| + content: '', | |
| + statusCode: response.status, | |
| + ...(contentTypeHeader && { contentType: contentTypeHeader }), | |
| + error: `HTTP ${response.status} ${response.statusText}`, | |
| + }; | |
| + } | |
| ● Read(src/plugins/fetchService.ts) | |
| └ Read 71 lines | |
| ● WriteFile(src/plugins/fetchService.ts) | |
| └ File operation completed: src/plugins/fetchService.ts | |
| - 1 lines removed | |
| - } | |
| ● Read(src/plugins/fetchService.ts) | |
| └ Read 70 lines | |
| ● WriteFile(src/plugins/fetchService.ts) | |
| └ File operation completed: src/plugins/fetchService.ts | |
| - 16 lines removed | |
| + 16 lines added | |
| - const contentType = response.headers.get('content-type') ?? undefined; | |
| - let content = await response.text(); | |
| - let truncated = false; | |
| - | |
| - if (content.length > maxSize) { | |
| - content = content.substring(0, maxSize); | |
| - truncated = true; | |
| - } | |
| - | |
| - return { | |
| ... and 22 more lines | |
| ● Bash(npm run build 2>&1 | tail -10) | |
| └ (node:95671) ExperimentalWarning: `--experimental-loader` may be removed in the future; instead use `register()`: | |
| --import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register("ts-node/esm", pathToFileURL("./"));' | |
| (Use `node --trace-warnings ...` to show where the warning was created) | |
| ... +7 more lines | |
| ● Bash(npm test 2>&1 | tail -30) | |
| └ 15 | | |
| 16 | // Find WriteFilePlugin and verify debugMode | |
| 17 | const writePlugin = plugins.find((p: any) => p.getName() === 'WriteFilePlugin'); | |
| ... +19 more lines | |
| ● Read(tests/pluginFactory.test.ts) | |
| └ Read 72 lines | |
| ● Read(tests/pluginFactory.test.ts) | |
| └ Read 72 lines | |
| ● WriteFile(tests/pluginFactory.test.ts) | |
| └ File operation completed: tests/pluginFactory.test.ts | |
| - 1 lines removed | |
| + 1 lines added | |
| - expect(plugins).toHaveLength(4); // calculator, read, write, bash | |
| + expect(plugins).toHaveLength(5); // calculator, read, write, bash, fetch | |
| ● Read(tests/pluginFactory.test.ts) | |
| └ Read 72 lines | |
| ● WriteFile(tests/pluginFactory.test.ts) | |
| └ File operation completed: tests/pluginFactory.test.ts | |
| - 1 lines removed | |
| + 1 lines added | |
| - expect(plugins).toHaveLength(4); | |
| + expect(plugins).toHaveLength(5); | |
| ● Bash(npm test 2>&1 | tail -10) | |
| └ (Use `node --trace-warnings ...` to show where the warning was created) | |
| PASS tests/cli.test.ts | |
| PASS tests/fetchService.test.ts | |
| ... +6 more lines | |
| ● Excellent! All tests pass. Now let me verify the build and create a summary: | |
| ● Bash(npm run build 2>&1 | grep -i error || echo "Bui...) | |
| └ Build successful! | |
| ● Perfect! Let me create a summary document of what was added: | |
| ● WriteFile(FETCH_PLUGIN_CHANGES.md) | |
| └ File operation completed: FETCH_PLUGIN_CHANGES.md | |
| + 84 lines added | |
| + # Fetch Plugin - Implementation Summary | |
| + | |
| + ## What Was Added | |
| + | |
| + A new Fetch plugin that allows the AI assistant to download content from URLs. | |
| + | |
| + ## Files Created | |
| + | |
| + ### 1. `src/plugins/fetchService.ts` | |
| + - Core service that performs HTTP requests using the Fetch API | |
| ... and 74 more lines | |
| ● Perfect! The Fetch plugin has been successfully added to the codebase! Let me summarize what was accomplished: | |
| ## COMPLETED: Fetch Plugin Implementation | |
| ### What I Did (Following TDD & XP Practices) | |
| **1. RED PHASE - Wrote Failing Tests First** | |
| - Created `tests/fetchService.test.ts` with 8 test cases | |
| - Created `tests/fetchPlugin.test.ts` with 9 test cases | |
| - Tests failed as expected (no implementation yet) | |
| **2. GREEN PHASE - Implemented Minimal Code** | |
| - Created `src/plugins/fetchService.ts` - HTTP fetching logic | |
| - Created `src/plugins/fetchPlugin.ts` - Plugin wrapper | |
| - Updated `src/plugins/pluginFactory.ts` - Registered new plugin | |
| - All tests now pass! ✅ | |
| **3. REFACTOR PHASE - Fixed Type Issues** | |
| - Fixed TypeScript strict optional properties | |
| - Updated test expectations for plugin count | |
| - Verified build and all tests pass | |
| ### Key Features | |
| - **URL Fetching**: Downloads content from any URL | |
| - **Timeout Protection**: 30-second default timeout (configurable) | |
| - **Size Limits**: 1 MB max content size (configurable) | |
| - **Error Handling**: Graceful handling of network errors, 404s, timeouts | |
| - **Content Type Detection**: Returns content-type from headers | |
| - **Truncation Indicator**: Warns when content is truncated | |
| ### Test Coverage | |
| ✅ *All 318 tests passing** (8 new service tests + 9 new plugin tests) | |
| ✅ *Build successful** - No TypeScript errors | |
| ✅ *Architectural consistency** - Follows existing plugin patterns | |
| ### Files Changed | |
| **Created:** | |
| - `src/plugins/fetchService.ts` | |
| - `src/plugins/fetchPlugin.ts` | |
| - `tests/fetchService.test.ts` | |
| - `tests/fetchPlugin.test.ts` | |
| - `FETCH_PLUGIN_CHANGES.md` (documentation) | |
| **Modified:** | |
| - `src/plugins/pluginFactory.ts` (registered plugin) | |
| - `tests/pluginFactory.test.ts` (updated test expectations) | |
| The AI assistant (you!) can now fetch URLs when users ask! For example: | |
| - "Download the contents of https://example.com" | |
| - "Fetch the HTML from that URL" | |
| - "Get the JSON data from https://api.example.com/data" | |
| architecture. Ready to use! 🎉extreme programming principles: TDD, simple design, clean code, and respect for existing |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment