Skip to content

Commit 483e51c

Browse files
authored
Merge pull request #46 from sakamotopaya/eo/get-cli-working
get cli working
2 parents be06f8f + 559b32c commit 483e51c

33 files changed

+2316
-127
lines changed

docs/product-stories/cli-utility/cli-refinement.md

Whitespace-only changes.
Lines changed: 131 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,140 @@
1-
we are ready to work on issue #18 (docs/product-stories/cli-utility/story-18-update-documentation.md) in repo https://github.com/sakamotopaya/code-agent.
1+
we are ready to work on issue #20 (docs/product-stories/cli-utility/story-20-performance-optimization.md) in repo https://github.com/sakamotopaya/code-agent.
22
follow the normal git flow. create a new local branch for the story.
33
code the tasks and unit tests that prove the task are complete.
44
if you need information about prior stories, you can find them locally here docs/product-stories/cli-utility
5-
we often get rejected trying to push our changes. make sure and run a build and lint prior to trying to push
5+
we often get rejected trying to push our changes. make sure and run a build and lint prior to trying to push. you will not be able to push the local branch if there are any lint warnings
66
when you are finished with the code and tests, update the issue with a new comment describing your work and then
77
push your branch and create a pull request for this branch against main
88
you should use regular git command but it might be best to create the PR using the mcp server
99

10-
We need to resume work on issue #17 (docs/product-stories/cli-utility/story-17-comprehensive-cli-testing.md) in repo https://github.com/sakamotopaya/code-agent.
10+
We need to resume work on issue #18 (docs/product-stories/cli-utility/story-18-update-documentation.md) in repo https://github.com/sakamotopaya/code-agent.
1111

12-
you were creating a pr when you filled up context. here is where you were on the pr creation:
13-
{
14-
"owner": "sakamotopaya",
15-
"repo": "code-agent",
16-
"title": "feat: Comprehensive CLI Testing Framework (Story 17)",
17-
"body": "## Summary\n\nImplements Story 17: Comprehensive CLI Testing with a complete testing framework for the CLI utility.\n\n## Changes Made\n\n### Testing Infrastructure\n- **Jest Configuration**: CLI-specific Jest config with 90% coverage threshold\n- **Test Utilities**: `TestHelpers` class with workspace management, CLI command execution, and performance measurement\n- **Mock Services**: Comprehensive mocking for UI, browser, session, and MCP services\n- **Test Setup**: Global test configuration with automatic cleanup\n\n### Test Categories Implemented\n\n#### Unit Tests (29 tests)\n- Console output handling\n- Basic functionality validation\n- Error handling and recovery\n- Async operations\n- Data types and validation\n- Environment and platform detection\n- Performance and memory operations\n- JSON and data processing\n- Edge cases and error conditions\n- Concurrency and timing\n\n#### Integration Tests\n- File operations (create, read, write, delete)\n- Directory operations and traversal\n- Project structure management\n- Configuration management\n- Output formatting validation (JSON, YAML, table, plain text)\n- Session management\n- Performance characteristics\n- Cross-platform compatibility\n- Concurrent operations\n- Resource cleanup\n\n#### End-to-End Tests\n- New user onboarding workflows\n- Complete development workflows\n- Project creation (simple, React, Node.js)\n- File management operations\n- Data processing workflows\n- Session lifecycle management\n- Error recovery scenarios\n- Configuration workflows\n\n#### Performance Tests\n- Startup time benchmarking (< 2 seconds)\n- Memory usage profiling (< 100MB increase)\n- Large file processing (< 5 seconds for 10MB)\n- Concurrent operation testing\n- Resource cleanup validation\n- Memory pressure testing\n\n#### Cross-Platform Tests\n- Platform detection (Windows, macOS, Linux)\n- File path handling\n- Environment variable management\n- Process operations\n- File system permissions\n- Text encoding and line endings\n- Node.js version compatibility\n\n### Test Scripts Added\n`bash\nnpm run test:cli # All CLI tests\nnpm run test:cli:unit # Unit tests only\nnpm run test:cli:integration # Integration tests only\nnpm run test:cli:e2e # End-to-end tests only\nnpm run test:cli:performance # Performance tests only\nnpm run test:cli:platform # Cross-platform tests only\nnpm run test:cli:coverage # With coverage report\nnpm run test:cli:watch # Watch mode for development\n`\n\n### Coverage Requirements\n- **Branches**: 90%\n- **Functions**: 90%\n- **Lines**: 90%\n- **Statements**: 90%\n\n## Test Results\n- ✅ All unit tests passing (29/29)\n- ✅ Lint passing with zero warnings\n- ✅ TypeScript compilation successful\n- ✅ Test infrastructure functional\n\n## Quality Assurance\n- All code follows ESLint rules\n- TypeScript strict mode compliance\n- Comprehensive error handling\n- Resource cleanup in all tests\n- Platform-agnostic test design\n- Performance benchmarks established\n\n## Files Added\n- `src/cli/jest.config.mjs` - Jest configuration for CLI testing\n- `src/cli/__tests__/setup.ts` - Global test setup and teardown\n- `src/cli/__tests__/utils/TestHelpers.ts` - Core test utilities\n- `src/cli/__tests__/utils/MockServices.ts` - Mock service implementations\n- `src/cli/__tests__/unit/services/CLIUIService.test.ts` - Unit tests\n- `src/cli/**tests**/integration/CLIIntegration.test.
12+
you were creating unit tests when you ran out of context. specifically you were editing src/cli/**tests**/unit/commands/HelpCommand.test.ts when you ran out of context
1813

19-
finish the PR and then add your comment to the issue. be sure and mark the issue as closes.
14+
getting this error trying to run the client. any code that uses vscode as a dependency needs to be put behind an interface so that we can provide an implementation for the CLI
15+
[copyLocales] Copied 34 locale files to /Users/eo/code/code-agent/src/dist/i18n/locales
16+
[esbuild-problem-matcher#onEnd]
17+
eo@m3x src % node dist/cli/index.js --help
18+
node:internal/modules/cjs/loader:1228
19+
throw err;
20+
^
21+
22+
Error: Cannot find module 'vscode'
23+
Require stack:
24+
25+
- /Users/eo/code/code-agent/src/dist/cli/index.js
26+
at Module.\_resolveFilename (node:internal/modules/cjs/loader:1225:15)
27+
at Module.\_load (node:internal/modules/cjs/loader:1051:27)
28+
at Module.require (node:internal/modules/cjs/loader:1311:19)
29+
at require (node:internal/modules/helpers:179:18)
30+
at utils/storage.ts (/Users/eo/code/code-agent/src/dist/cli/index.js:190467:22)
31+
at **init (/Users/eo/code/code-agent/src/dist/cli/index.js:15:56)
32+
at api/providers/fetchers/modelCache.ts (/Users/eo/code/code-agent/src/dist/cli/index.js:198348:5)
33+
at **init (/Users/eo/code/code-agent/src/dist/cli/index.js:15:56)
34+
at api/providers/router-provider.ts (/Users/eo/code/code-agent/src/dist/cli/index.js:198409:5)
35+
at \_\_init (/Users/eo/code/code-agent/src/dist/cli/index.js:15:56) {
36+
code: 'MODULE_NOT_FOUND',
37+
requireStack: [ '/Users/eo/code/code-agent/src/dist/cli/index.js' ]
38+
}
39+
40+
Node.js v20.18.3
41+
eo@m3x src % npm run build:cli
42+
43+
> [email protected] build:cli
44+
> node esbuild.mjs
45+
46+
[extension] Cleaning dist directory: /Users/eo/code/code-agent/src/dist
47+
[esbuild-problem-matcher#onStart]
48+
[copyPaths] Copied ../README.md to README.md
49+
[copyPaths] Copied ../CHANGELOG.md to CHANGELOG.md
50+
[copyPaths] Copied ../LICENSE to LICENSE
51+
[copyPaths] Optional file not found: ../.env
52+
[copyPaths] Copied 911 files from node_modules/vscode-material-icons/generated to assets/vscode-material-icons
53+
[copyPaths] Copied 3 files from ../webview-ui/audio to webview-ui/audio
54+
[copyWasms] Copied tiktoken WASMs to /Users/eo/code/code-agent/src/dist
55+
[copyWasms] Copied tiktoken WASMs to /Users/eo/code/code-agent/src/dist/workers
56+
[copyWasms] Copied tree-sitter.wasm to /Users/eo/code/code-agent/src/dist
57+
[copyWasms] Copied 35 tree-sitter language wasms to /Users/eo/code/code-agent/src/dist
58+
[copyLocales] Copied 34 locale files to /Users/eo/code/code-agent/src/dist/i18n/locales
59+
[esbuild-problem-matcher#onEnd]
60+
eo@m3x src % node dist/cli/index.js --help
61+
Error processing directory /Users/eo/code/code-agent/src/dist/cli/i18n/locales: Error: ENOENT: no such file or directory, scandir '/Users/eo/code/code-agent/src/dist/cli/i18n/locales'
62+
at Object.readdirSync (node:fs:1507:26)
63+
at i18n/setup.ts (/Users/eo/code/code-agent/src/dist/cli/index.js:190373:37)
64+
at **init (/Users/eo/code/code-agent/src/dist/cli/index.js:15:56)
65+
at i18n/index.ts (/Users/eo/code/code-agent/src/dist/cli/index.js:190420:5)
66+
at **init (/Users/eo/code/code-agent/src/dist/cli/index.js:15:56)
67+
at utils/storage.ts (/Users/eo/code/code-agent/src/dist/cli/index.js:190474:5)
68+
at **init (/Users/eo/code/code-agent/src/dist/cli/index.js:15:56)
69+
at api/providers/fetchers/modelCache.ts (/Users/eo/code/code-agent/src/dist/cli/index.js:198351:5)
70+
at **init (/Users/eo/code/code-agent/src/dist/cli/index.js:15:56)
71+
at api/providers/router-provider.ts (/Users/eo/code/code-agent/src/dist/cli/index.js:198412:5) {
72+
errno: -2,
73+
code: 'ENOENT',
74+
syscall: 'scandir',
75+
path: '/Users/eo/code/code-agent/src/dist/cli/i18n/locales'
76+
}
77+
node:internal/modules/cjs/loader:1228
78+
throw err;
79+
^
80+
81+
Error: Cannot find module 'vscode'
82+
Require stack:
83+
84+
- /Users/eo/code/code-agent/src/dist/cli/index.js
85+
at Module.\_resolveFilename (node:internal/modules/cjs/loader:1225:15)
86+
at Module.\_load (node:internal/modules/cjs/loader:1051:27)
87+
at Module.require (node:internal/modules/cjs/loader:1311:19)
88+
at require (node:internal/modules/helpers:179:18)
89+
at api/providers/human-relay.ts (/Users/eo/code/code-agent/src/dist/cli/index.js:198620:23)
90+
at **init (/Users/eo/code/code-agent/src/dist/cli/index.js:15:56)
91+
at api/providers/index.ts (/Users/eo/code/code-agent/src/dist/cli/index.js:224236:5)
92+
at **init (/Users/eo/code/code-agent/src/dist/cli/index.js:15:56)
93+
at api/index.ts (/Users/eo/code/code-agent/src/dist/cli/index.js:224309:5)
94+
at \_\_init (/Users/eo/code/code-agent/src/dist/cli/index.js:15:56) {
95+
code: 'MODULE_NOT_FOUND',
96+
requireStack: [ '/Users/eo/code/code-agent/src/dist/cli/index.js' ]
97+
}
98+
99+
Node.js v20.18.3
100+
eo@m3x src % node dist/cli/index.js --help
101+
Error processing directory /Users/eo/code/code-agent/src/dist/cli/i18n/locales: Error: ENOENT: no such file or directory, scandir '/Users/eo/code/code-agent/src/dist/cli/i18n/locales'
102+
at Object.readdirSync (node:fs:1507:26)
103+
at i18n/setup.ts (/Users/eo/code/code-agent/src/dist/cli/index.js:190373:37)
104+
at **init (/Users/eo/code/code-agent/src/dist/cli/index.js:15:56)
105+
at i18n/index.ts (/Users/eo/code/code-agent/src/dist/cli/index.js:190420:5)
106+
at **init (/Users/eo/code/code-agent/src/dist/cli/index.js:15:56)
107+
at utils/storage.ts (/Users/eo/code/code-agent/src/dist/cli/index.js:190474:5)
108+
at **init (/Users/eo/code/code-agent/src/dist/cli/index.js:15:56)
109+
at api/providers/fetchers/modelCache.ts (/Users/eo/code/code-agent/src/dist/cli/index.js:198351:5)
110+
at **init (/Users/eo/code/code-agent/src/dist/cli/index.js:15:56)
111+
at api/providers/router-provider.ts (/Users/eo/code/code-agent/src/dist/cli/index.js:198412:5) {
112+
errno: -2,
113+
code: 'ENOENT',
114+
syscall: 'scandir',
115+
path: '/Users/eo/code/code-agent/src/dist/cli/i18n/locales'
116+
}
117+
node:internal/modules/cjs/loader:1228
118+
throw err;
119+
^
120+
121+
Error: Cannot find module 'vscode'
122+
Require stack:
123+
124+
- /Users/eo/code/code-agent/src/dist/cli/index.js
125+
at Module.\_resolveFilename (node:internal/modules/cjs/loader:1225:15)
126+
at Module.\_load (node:internal/modules/cjs/loader:1051:27)
127+
at Module.require (node:internal/modules/cjs/loader:1311:19)
128+
at require (node:internal/modules/helpers:179:18)
129+
at api/providers/human-relay.ts (/Users/eo/code/code-agent/src/dist/cli/index.js:198620:23)
130+
at **init (/Users/eo/code/code-agent/src/dist/cli/index.js:15:56)
131+
at api/providers/index.ts (/Users/eo/code/code-agent/src/dist/cli/index.js:224236:5)
132+
at **init (/Users/eo/code/code-agent/src/dist/cli/index.js:15:56)
133+
at api/index.ts (/Users/eo/code/code-agent/src/dist/cli/index.js:224309:5)
134+
at \_\_init (/Users/eo/code/code-agent/src/dist/cli/index.js:15:56) {
135+
code: 'MODULE_NOT_FOUND',
136+
requireStack: [ '/Users/eo/code/code-agent/src/dist/cli/index.js' ]
137+
}
138+
139+
Node.js v20.18.3
140+
eo@m3x src %

packages/telemetry/src/cli.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// CLI-specific telemetry exports that don't include VSCode dependencies
2+
export * from "./BaseTelemetryClient"
3+
export * from "./TelemetryService"
4+
5+
// Note: PostHogTelemetryClient is NOT exported here since it depends on vscode

src/api/providers/fetchers/modelCache.ts

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import fs from "fs/promises"
33

44
import NodeCache from "node-cache"
55

6-
import { ContextProxy } from "../../../core/config/ContextProxy"
76
import { getCacheDirectoryPath } from "../../../utils/storage"
87
import { RouterName, ModelRecord } from "../../../shared/api"
98
import { fileExistsAtPath } from "../../../utils/fs"
@@ -18,13 +17,37 @@ const memoryCache = new NodeCache({ stdTTL: 5 * 60, checkperiod: 5 * 60 })
1817

1918
async function writeModels(router: RouterName, data: ModelRecord) {
2019
const filename = `${router}_models.json`
21-
const cacheDir = await getCacheDirectoryPath(ContextProxy.instance.globalStorageUri.fsPath)
20+
let globalStoragePath: string
21+
22+
try {
23+
// Try to use VSCode context if available
24+
const { ContextProxy } = require("../../../core/config/ContextProxy")
25+
globalStoragePath = ContextProxy.instance.globalStorageUri.fsPath
26+
} catch (error) {
27+
// Fallback for CLI usage
28+
const os = require("os")
29+
globalStoragePath = path.join(os.homedir(), ".roo-code")
30+
}
31+
32+
const cacheDir = await getCacheDirectoryPath(globalStoragePath)
2233
await fs.writeFile(path.join(cacheDir, filename), JSON.stringify(data))
2334
}
2435

2536
async function readModels(router: RouterName): Promise<ModelRecord | undefined> {
2637
const filename = `${router}_models.json`
27-
const cacheDir = await getCacheDirectoryPath(ContextProxy.instance.globalStorageUri.fsPath)
38+
let globalStoragePath: string
39+
40+
try {
41+
// Try to use VSCode context if available
42+
const { ContextProxy } = require("../../../core/config/ContextProxy")
43+
globalStoragePath = ContextProxy.instance.globalStorageUri.fsPath
44+
} catch (error) {
45+
// Fallback for CLI usage
46+
const os = require("os")
47+
globalStoragePath = path.join(os.homedir(), ".roo-code")
48+
}
49+
50+
const cacheDir = await getCacheDirectoryPath(globalStoragePath)
2851
const filePath = path.join(cacheDir, filename)
2952
const exists = await fileExistsAtPath(filePath)
3053
return exists ? JSON.parse(await fs.readFile(filePath, "utf8")) : undefined

src/api/providers/fetchers/modelEndpointCache.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,35 @@ const getCacheKey = (router: RouterName, modelId: string) => sanitize(`${router}
1717

1818
async function writeModelEndpoints(key: string, data: ModelRecord) {
1919
const filename = `${key}_endpoints.json`
20-
const cacheDir = await getCacheDirectoryPath(ContextProxy.instance.globalStorageUri.fsPath)
20+
let globalStoragePath: string
21+
22+
try {
23+
// Try to use VSCode context if available
24+
globalStoragePath = ContextProxy.instance.globalStorageUri.fsPath
25+
} catch (error) {
26+
// Fallback for CLI usage
27+
const os = require("os")
28+
globalStoragePath = path.join(os.homedir(), ".roo-code")
29+
}
30+
31+
const cacheDir = await getCacheDirectoryPath(globalStoragePath)
2132
await fs.writeFile(path.join(cacheDir, filename), JSON.stringify(data, null, 2))
2233
}
2334

2435
async function readModelEndpoints(key: string): Promise<ModelRecord | undefined> {
2536
const filename = `${key}_endpoints.json`
26-
const cacheDir = await getCacheDirectoryPath(ContextProxy.instance.globalStorageUri.fsPath)
37+
let globalStoragePath: string
38+
39+
try {
40+
// Try to use VSCode context if available
41+
globalStoragePath = ContextProxy.instance.globalStorageUri.fsPath
42+
} catch (error) {
43+
// Fallback for CLI usage
44+
const os = require("os")
45+
globalStoragePath = path.join(os.homedir(), ".roo-code")
46+
}
47+
48+
const cacheDir = await getCacheDirectoryPath(globalStoragePath)
2749
const filePath = path.join(cacheDir, filename)
2850
const exists = await fileExistsAtPath(filePath)
2951
return exists ? JSON.parse(await fs.readFile(filePath, "utf8")) : undefined

src/api/providers/human-relay.ts

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { Anthropic } from "@anthropic-ai/sdk"
2-
import * as vscode from "vscode"
32

43
import type { ModelInfo } from "@roo-code/types"
54

65
import { getCommand } from "../../utils/commands"
76
import { ApiStream } from "../transform/stream"
7+
import { getPlatformServices, isVsCodeContext } from "../../core/adapters/PlatformServiceFactory"
88

99
import type { ApiHandler, SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index"
1010

@@ -44,7 +44,8 @@ export class HumanRelayHandler implements ApiHandler, SingleCompletionHandler {
4444
}
4545

4646
// Copy to clipboard
47-
await vscode.env.clipboard.writeText(promptText)
47+
const platformServices = await getPlatformServices()
48+
await platformServices.clipboard.writeText(promptText)
4849

4950
// A dialog box pops up to request user action
5051
const response = await showHumanRelayDialog(promptText)
@@ -84,7 +85,8 @@ export class HumanRelayHandler implements ApiHandler, SingleCompletionHandler {
8485
*/
8586
async completePrompt(prompt: string): Promise<string> {
8687
// Copy to clipboard
87-
await vscode.env.clipboard.writeText(prompt)
88+
const platformServices = await getPlatformServices()
89+
await platformServices.clipboard.writeText(prompt)
8890

8991
// A dialog box pops up to request user action
9092
const response = await showHumanRelayDialog(prompt)
@@ -118,18 +120,52 @@ function getMessageContent(message: Anthropic.Messages.MessageParam): string {
118120
* @returns The user's input response or undefined (if canceled).
119121
*/
120122
async function showHumanRelayDialog(promptText: string): Promise<string | undefined> {
123+
if (!isVsCodeContext()) {
124+
// In CLI mode, prompt user for input
125+
console.log("\n📋 Prompt copied to clipboard. Please:")
126+
console.log("1. Paste the prompt into your AI model interface")
127+
console.log("2. Copy the AI's response")
128+
console.log("3. Enter the response below")
129+
console.log("\nPrompt:")
130+
console.log("---")
131+
console.log(promptText)
132+
console.log("---\n")
133+
134+
const platformServices = await getPlatformServices()
135+
return await platformServices.userInterface.showInputBox({
136+
prompt: "Enter the AI's response",
137+
placeHolder: "Paste the response here...",
138+
})
139+
}
140+
141+
// VSCode mode - use command-based dialog
121142
return new Promise<string | undefined>((resolve) => {
122143
// Create a unique request ID.
123144
const requestId = Date.now().toString()
124145

125-
// Register a global callback function.
126-
vscode.commands.executeCommand(
127-
getCommand("registerHumanRelayCallback"),
128-
requestId,
129-
(response: string | undefined) => resolve(response),
130-
)
146+
const setupPromise = async () => {
147+
try {
148+
const platformServices = await getPlatformServices()
149+
150+
// Register a global callback function.
151+
await platformServices.commandExecutor.executeCommand(
152+
getCommand("registerHumanRelayCallback"),
153+
requestId,
154+
(response: string | undefined) => resolve(response),
155+
)
156+
157+
// Open the dialog box directly using the current panel.
158+
await platformServices.commandExecutor.executeCommand(getCommand("showHumanRelayDialog"), {
159+
requestId,
160+
promptText,
161+
})
162+
} catch (error) {
163+
console.error("Failed to show human relay dialog:", error)
164+
resolve(undefined)
165+
}
166+
}
131167

132-
// Open the dialog box directly using the current panel.
133-
vscode.commands.executeCommand(getCommand("showHumanRelayDialog"), { requestId, promptText })
168+
// Call the async setup function
169+
setupPromise()
134170
})
135171
}

src/api/providers/index.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,21 @@ export { OpenRouterHandler } from "./openrouter"
1818
export { RequestyHandler } from "./requesty"
1919
export { UnboundHandler } from "./unbound"
2020
export { VertexHandler } from "./vertex"
21-
export { VsCodeLmHandler } from "./vscode-lm"
2221
export { XAIHandler } from "./xai"
22+
23+
// Conditional exports for VSCode-specific providers
24+
let VsCodeLmHandler: any = null
25+
26+
try {
27+
// Only export VSCode LM handler if in VSCode context
28+
if (typeof require !== "undefined") {
29+
// Try to require vscode to check if we're in VSCode context
30+
require("vscode")
31+
VsCodeLmHandler = require("./vscode-lm").VsCodeLmHandler
32+
}
33+
} catch (error) {
34+
// VSCode not available (CLI mode)
35+
VsCodeLmHandler = null
36+
}
37+
38+
export { VsCodeLmHandler }

0 commit comments

Comments
 (0)