codehere 0.2.2 → 0.3.0-rc.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +88 -68
- package/dist/domain/entities/task.js +4 -4
- package/dist/error-handler.js +4 -9
- package/dist/index.js +261 -1224
- package/dist/infrastructure/config/product-context.js +7 -6
- package/dist/infrastructure/hooks/claude-sdk-hook.js +348 -72
- package/dist/infrastructure/observability/execution-tracer.js +11 -6
- package/dist/infrastructure/orchestration/agent-adapter.js +13 -0
- package/dist/infrastructure/orchestration/agent-installer.js +101 -0
- package/dist/infrastructure/orchestration/agents-config-seed.js +56 -0
- package/dist/infrastructure/orchestration/aider-adapter.js +319 -0
- package/dist/infrastructure/orchestration/artifacts-extractor.js +175 -0
- package/dist/infrastructure/orchestration/claude-code-adapter.js +352 -0
- package/dist/infrastructure/orchestration/codex-adapter.js +310 -0
- package/dist/infrastructure/orchestration/conductor-adapter.js +340 -0
- package/dist/infrastructure/orchestration/generic-cli-adapter.js +553 -0
- package/dist/infrastructure/orchestration/opencode-adapter.js +275 -0
- package/dist/infrastructure/orchestration/progress-aggregator.js +189 -0
- package/dist/infrastructure/orchestration/readiness.js +106 -0
- package/dist/infrastructure/orchestration/session-manager.js +278 -0
- package/dist/infrastructure/orchestration/session-persistence.js +109 -0
- package/dist/infrastructure/protocols/mcp-helpers.js +10 -30
- package/dist/infrastructure/protocols/mcp-server-impl.js +359 -21
- package/dist/infrastructure/protocols/probe-port.js +36 -0
- package/dist/infrastructure/protocols/ui-page.js +1827 -0
- package/dist/infrastructure/security/file-collection.js +115 -0
- package/dist/infrastructure/security/trust-scorer.js +301 -56
- package/dist/infrastructure/storage/task-repository.js +8 -7
- package/dist/monitoring.js +9 -38
- package/dist/presentation/cli/commands/help-command.js +96 -139
- package/dist/presentation/cli/commands/hook-command.js +17 -24
- package/dist/presentation/cli/commands/report-command.js +59 -72
- package/dist/presentation/cli/commands/setup-command.js +4 -4
- package/dist/presentation/cli/commands/status-command.js +321 -30
- package/dist/presentation/cli/commands/trust-command.js +84 -72
- package/dist/presentation/cli/error-display.js +17 -21
- package/dist/presentation/cli/repl.js +474 -0
- package/dist/ui.js +11 -11
- package/package.json +24 -62
- package/dist/agents/base.js +0 -101
- package/dist/agents/orchestrator-model-driven.js +0 -141
- package/dist/agents/react-orchestrator-agent.js +0 -54
- package/dist/application/agents/execution-agent.js +0 -508
- package/dist/application/agents/multi-agent-orchestrator.js +0 -295
- package/dist/application/agents/planning-agent.js +0 -433
- package/dist/application/agents/validation-agent.js +0 -317
- package/dist/application/architectures/plan-and-act.js +0 -242
- package/dist/application/index.js +0 -12
- package/dist/application/services/agent-factory.js +0 -28
- package/dist/application/services/dependency-container.js +0 -596
- package/dist/application/services/git-context-service.js +0 -83
- package/dist/application/services/intelligent-router.js +0 -934
- package/dist/application/services/tool-executor-service.js +0 -94
- package/dist/application/use-cases/ask-question-use-case.js +0 -338
- package/dist/application/use-cases/edit-file-use-case.js +0 -830
- package/dist/application/use-cases/explain-file-use-case.js +0 -50
- package/dist/application/use-cases/index-codebase-use-case.js +0 -113
- package/dist/application/use-cases/planning-use-case.js +0 -276
- package/dist/application/use-cases/react-orchestration-use-case.js +0 -329
- package/dist/application/use-cases/reflexion-use-case.js +0 -30
- package/dist/application/workflows/deterministic-workflows.js +0 -189
- package/dist/ast-parser.js +0 -331
- package/dist/cache.js +0 -180
- package/dist/chat.js +0 -251
- package/dist/code-analysis.js +0 -327
- package/dist/context.js +0 -187
- package/dist/docs.js +0 -146
- package/dist/domain/entities/code-chunk.js +0 -6
- package/dist/domain/entities/decision.js +0 -9
- package/dist/domain/entities/edit.js +0 -6
- package/dist/domain/entities/knowledge-node.js +0 -9
- package/dist/domain/entities/knowledge-preservation.js +0 -10
- package/dist/domain/entities/memory.js +0 -6
- package/dist/domain/entities/pattern.js +0 -7
- package/dist/domain/entities/plan.js +0 -6
- package/dist/domain/entities/query.js +0 -6
- package/dist/domain/errors/embeddings-not-found-error.js +0 -13
- package/dist/domain/index.js +0 -19
- package/dist/domain/interfaces/ai-service.interface.js +0 -6
- package/dist/domain/interfaces/code-analyzer.interface.js +0 -6
- package/dist/domain/interfaces/decision-repository.interface.js +0 -9
- package/dist/domain/interfaces/embedding-repository.interface.js +0 -6
- package/dist/domain/interfaces/knowledge-graph-repository.interface.js +0 -9
- package/dist/domain/interfaces/knowledge-preservation-repository.interface.js +0 -8
- package/dist/domain/interfaces/memory-repository.interface.js +0 -6
- package/dist/domain/interfaces/pattern-repository.interface.js +0 -6
- package/dist/domain/interfaces/verification-service.interface.js +0 -6
- package/dist/domain/services/cross-team-pattern-sharing.js +0 -125
- package/dist/domain/services/decision-recorder.js +0 -223
- package/dist/domain/services/decision-retriever.js +0 -133
- package/dist/domain/services/environment-mode-manager.js +0 -22
- package/dist/domain/services/graded-memory-activation.js +0 -69
- package/dist/domain/services/knowledge-access.js +0 -132
- package/dist/domain/services/knowledge-extractor.js +0 -247
- package/dist/domain/services/knowledge-graph-builder.js +0 -268
- package/dist/domain/services/knowledge-graph-query.js +0 -187
- package/dist/domain/services/memory-retriever.js +0 -82
- package/dist/domain/services/memory-synthesizer.js +0 -91
- package/dist/domain/services/pattern-extractor.js +0 -442
- package/dist/domain/services/query-intent-classifier.js +0 -140
- package/dist/domain/services/react-loop.js +0 -186
- package/dist/domain/services/risk-tier-detector.js +0 -78
- package/dist/edit.js +0 -319
- package/dist/embed.js +0 -459
- package/dist/formatter.js +0 -168
- package/dist/git.js +0 -198
- package/dist/infrastructure/ai/cohere-ai-service.js +0 -607
- package/dist/infrastructure/ai/command-r-evaluation.js +0 -206
- package/dist/infrastructure/ai/model-config.js +0 -84
- package/dist/infrastructure/ai/provider-ai-service.js +0 -477
- package/dist/infrastructure/cache/embedding-cache.js +0 -175
- package/dist/infrastructure/cache/query-result-cache.js +0 -66
- package/dist/infrastructure/cache/response-cache.js +0 -48
- package/dist/infrastructure/cache/security-scan-cache.js +0 -47
- package/dist/infrastructure/chunking/ast-code-chunker.js +0 -19
- package/dist/infrastructure/chunking/code-aware-chunker.js +0 -397
- package/dist/infrastructure/context/context-compressor.js +0 -329
- package/dist/infrastructure/context/conversation-context.js +0 -128
- package/dist/infrastructure/context/session-manager.js +0 -94
- package/dist/infrastructure/cost/cost-tracker.js +0 -360
- package/dist/infrastructure/file-scanner/file-scanner.js +0 -106
- package/dist/infrastructure/governance/nist-ai-rmf.js +0 -292
- package/dist/infrastructure/governance/policy-as-code.js +0 -258
- package/dist/infrastructure/governance/prompt-to-code-lineage.js +0 -239
- package/dist/infrastructure/index.js +0 -7
- package/dist/infrastructure/observability/prompt-versioner.js +0 -145
- package/dist/infrastructure/protocols/a2a-metadata.js +0 -137
- package/dist/infrastructure/protocols/a2a-protocol.js +0 -314
- package/dist/infrastructure/protocols/mcp-oauth.js +0 -184
- package/dist/infrastructure/protocols/mcp-server.js +0 -333
- package/dist/infrastructure/protocols/streaming-handler.js +0 -103
- package/dist/infrastructure/providers/cohere-provider.js +0 -444
- package/dist/infrastructure/providers/fake-provider.js +0 -130
- package/dist/infrastructure/providers/local-llm-provider.js +0 -330
- package/dist/infrastructure/providers/model-config.types.js +0 -9
- package/dist/infrastructure/providers/model-provider.interface.js +0 -8
- package/dist/infrastructure/providers/openai-provider.js +0 -413
- package/dist/infrastructure/providers/openrouter-provider.js +0 -399
- package/dist/infrastructure/providers/operation-provider-resolver.js +0 -105
- package/dist/infrastructure/providers/provider-config-loader.js +0 -120
- package/dist/infrastructure/providers/provider-registry.js +0 -88
- package/dist/infrastructure/safety/pattern-detector.js +0 -133
- package/dist/infrastructure/security/ai-sast-scanner.js +0 -290
- package/dist/infrastructure/security/docker-sandbox.js +0 -178
- package/dist/infrastructure/security/enhanced-security-gate.js +0 -151
- package/dist/infrastructure/security/license-scanner.js +0 -167
- package/dist/infrastructure/security/provider-bias-detector.js +0 -164
- package/dist/infrastructure/security/sandbox-executor.js +0 -64
- package/dist/infrastructure/serialization/toon-serializer.js +0 -119
- package/dist/infrastructure/storage/memory-stream-service.js +0 -204
- package/dist/infrastructure/storage/plan-repository.js +0 -96
- package/dist/infrastructure/storage/sqlite-decision-repository.js +0 -232
- package/dist/infrastructure/storage/sqlite-embedding-repository.js +0 -311
- package/dist/infrastructure/storage/sqlite-knowledge-graph-repository.js +0 -417
- package/dist/infrastructure/storage/sqlite-knowledge-preservation-repository.js +0 -230
- package/dist/infrastructure/storage/sqlite-memory-repository.js +0 -229
- package/dist/infrastructure/storage/sqlite-pattern-repository.js +0 -249
- package/dist/infrastructure/storage/task-helpers.js +0 -197
- package/dist/infrastructure/telemetry/telemetry-service.js +0 -167
- package/dist/infrastructure/ux/appropriate-friction.js +0 -213
- package/dist/infrastructure/ux/bi-directional-learning.js +0 -337
- package/dist/infrastructure/ux/capability-boundaries.js +0 -42
- package/dist/infrastructure/ux/comprehensive-formatter.js +0 -117
- package/dist/infrastructure/ux/contextual-feature-discovery.js +0 -10
- package/dist/infrastructure/ux/expectation-management.js +0 -327
- package/dist/infrastructure/ux/feature-discovery.js +0 -14
- package/dist/infrastructure/ux/feedback-prompt.js +0 -45
- package/dist/infrastructure/ux/hitl-validation.js +0 -297
- package/dist/infrastructure/ux/progress-indicator.js +0 -116
- package/dist/infrastructure/ux/progressive-disclosure.js +0 -236
- package/dist/infrastructure/ux/review-checkpoint.js +0 -72
- package/dist/infrastructure/ux/staged-feature-intro.js +0 -24
- package/dist/infrastructure/ux/stream-markdown.js +0 -151
- package/dist/infrastructure/ux/syntax-highlighter.js +0 -91
- package/dist/infrastructure/ux/team-standards.js +0 -196
- package/dist/infrastructure/ux/transparency.js +0 -71
- package/dist/infrastructure/validation/agent-feedback.js +0 -55
- package/dist/infrastructure/validation/agent-validation-helper.js +0 -137
- package/dist/infrastructure/validation/review-handler.js +0 -40
- package/dist/infrastructure/validation/summary-aggregator.js +0 -70
- package/dist/infrastructure/validation/summary-extractor.js +0 -45
- package/dist/infrastructure/validation/trace-summary.js +0 -52
- package/dist/infrastructure/verification/verification-service.js +0 -47
- package/dist/infrastructure/xai/attention-visualizer.js +0 -172
- package/dist/infrastructure/xai/cot-visualizer.js +0 -283
- package/dist/infrastructure/xai/decision-tree-log.js +0 -212
- package/dist/infrastructure/xai/interactive-xai.js +0 -260
- package/dist/infrastructure/xai/uncertainty-quantifier.js +0 -198
- package/dist/intelligent-retrieval.js +0 -272
- package/dist/intent/classifier.js +0 -2
- package/dist/intent/context.js +0 -2
- package/dist/iterative-refinement.js +0 -189
- package/dist/learning.js +0 -232
- package/dist/log.js +0 -31
- package/dist/migrate.js +0 -133
- package/dist/parallel-processor.js +0 -534
- package/dist/planner.js +0 -198
- package/dist/policy.js +0 -26
- package/dist/presentation/cli/commands/ask-command.js +0 -314
- package/dist/presentation/cli/commands/cost-command.js +0 -191
- package/dist/presentation/cli/commands/decision-command.js +0 -207
- package/dist/presentation/cli/commands/docs-command.js +0 -25
- package/dist/presentation/cli/commands/fix-command.js +0 -53
- package/dist/presentation/cli/commands/knowledge-command.js +0 -129
- package/dist/presentation/cli/commands/learning-dashboard-command.js +0 -183
- package/dist/presentation/cli/commands/models-command.js +0 -194
- package/dist/presentation/cli/commands/orchestrate-command.js +0 -207
- package/dist/presentation/cli/commands/plan-command.js +0 -246
- package/dist/presentation/cli/commands/product-command.js +0 -199
- package/dist/presentation/cli/commands/react-command.js +0 -181
- package/dist/presentation/cli/commands/review-command.js +0 -154
- package/dist/presentation/cli/commands/run-command.js +0 -188
- package/dist/presentation/cli/commands/smart-command.js +0 -395
- package/dist/presentation/cli/commands/suggest-command.js +0 -138
- package/dist/presentation/cli/commands/trace-command.js +0 -129
- package/dist/presentation/cli/commands/undo-command.js +0 -150
- package/dist/query-optimizer.js +0 -205
- package/dist/refactor.js +0 -116
- package/dist/review.js +0 -206
- package/dist/search.js +0 -163
- package/dist/session.js +0 -704
- package/dist/task-verification.js +0 -336
- package/dist/testgen.js +0 -166
- package/dist/token-optimizer.js +0 -277
- package/dist/tools/bash.js +0 -213
- package/dist/tools/edit.js +0 -235
- package/dist/tools/executor.js +0 -153
- package/dist/tools/git-tool.js +0 -292
- package/dist/tools/parser.js +0 -158
- package/dist/tools/registry.js +0 -302
- package/dist/tools/search.js +0 -100
- package/dist/types.js +0 -2
- package/dist/utils/check-api-status.js +0 -100
- package/dist/utils/diff-parser.js +0 -162
- package/dist/verification/backup.js +0 -259
- package/dist/verification/compiler.js +0 -264
- package/dist/verification/index.js +0 -273
- package/dist/verification/test-runner.js +0 -379
package/dist/index.js
CHANGED
|
@@ -1,51 +1,31 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* codehere CLI entry · v0.3 PM-for-AI-coding-agents surface.
|
|
4
|
+
*
|
|
5
|
+
* Command surface trimmed to the v0.3 story (see Decision 10 in memory):
|
|
6
|
+
* setup · config · help · health · status · trust · report · hook ·
|
|
7
|
+
* serve · start · task · kill · scan-tool · completion
|
|
8
|
+
*
|
|
9
|
+
* v0.2 commands (ask / fix / plan / explain / refactor / index / etc.) and
|
|
10
|
+
* their backing code were removed in the D7 trim. The v0.2 npm releases
|
|
11
|
+
* retain that surface for users who depended on it.
|
|
12
|
+
*/
|
|
2
13
|
import { Command } from 'commander';
|
|
3
14
|
import { config } from 'dotenv';
|
|
4
15
|
import { join, dirname } from 'path';
|
|
5
16
|
import { fileURLToPath } from 'url';
|
|
6
|
-
import { readFileSync, existsSync } from 'fs';
|
|
7
|
-
import { indexRepositoryParallel } from './embed.js';
|
|
8
|
-
import { executeAskCommand } from './presentation/cli/commands/ask-command.js';
|
|
9
|
-
import { executeReactCommand } from './presentation/cli/commands/react-command.js';
|
|
10
|
-
import { executeSmartCommand } from './presentation/cli/commands/smart-command.js';
|
|
11
|
-
import { executeFixCommand } from './presentation/cli/commands/fix-command.js';
|
|
12
17
|
import { executeSetupCommand } from './presentation/cli/commands/setup-command.js';
|
|
13
18
|
import { executeConfigCommand } from './presentation/cli/commands/config-command.js';
|
|
14
19
|
import { executeHelpCommand } from './presentation/cli/commands/help-command.js';
|
|
15
|
-
import { initLogFile, logEntry } from './log.js';
|
|
16
|
-
import { performHealthCheck, formatHealthStatus, getMetrics } from './monitoring.js';
|
|
17
|
-
import { refactorMultiFile, findFilesByPattern } from './refactor.js';
|
|
18
|
-
import { getGitStatus, formatGitStatus, getAllGitDiffs, analyzeDiff, getGitDiff } from './git.js';
|
|
19
|
-
import { executePlanCommand } from './presentation/cli/commands/plan-command.js';
|
|
20
|
-
import { executeOrchestrateCommand } from './presentation/cli/commands/orchestrate-command.js';
|
|
21
|
-
import { executeDocsCommand } from './presentation/cli/commands/docs-command.js';
|
|
22
|
-
import { executeProductCommand } from './presentation/cli/commands/product-command.js';
|
|
23
20
|
import { executeStatusCommand } from './presentation/cli/commands/status-command.js';
|
|
24
|
-
import { executeSuggestCommand } from './presentation/cli/commands/suggest-command.js';
|
|
25
|
-
import { executeRunCommand } from './presentation/cli/commands/run-command.js';
|
|
26
|
-
import { executeModelsCommand } from './presentation/cli/commands/models-command.js';
|
|
27
|
-
import { executeReviewCommand } from './presentation/cli/commands/review-command.js';
|
|
28
21
|
import { executeHookCommand } from './presentation/cli/commands/hook-command.js';
|
|
29
22
|
import { executeTrustCommand } from './presentation/cli/commands/trust-command.js';
|
|
30
23
|
import { executeReportCommand } from './presentation/cli/commands/report-command.js';
|
|
31
24
|
import { executeScanToolCommand } from './presentation/cli/commands/scan-tool-command.js';
|
|
32
|
-
import { executeDecisionCommand } from './presentation/cli/commands/decision-command.js';
|
|
33
|
-
import { executeCostCommand } from './presentation/cli/commands/cost-command.js';
|
|
34
|
-
import { executeKnowledgeCommand } from './presentation/cli/commands/knowledge-command.js';
|
|
35
|
-
import { executeLearningDashboardCommand } from './presentation/cli/commands/learning-dashboard-command.js';
|
|
36
|
-
import { executeTraceCommand } from './presentation/cli/commands/trace-command.js';
|
|
37
|
-
import { executeUndoCommand } from './presentation/cli/commands/undo-command.js';
|
|
38
|
-
import { reviewFile, formatReview } from './review.js';
|
|
39
|
-
import { formatLearningInsights, getPerformanceInsights } from './learning.js';
|
|
40
|
-
import { getCacheStats, clearAllCaches } from './cache.js';
|
|
41
|
-
import { planMigration, formatMigrationPlan } from './migrate.js';
|
|
42
|
-
import { generateTests, getTestFilePath, saveTests, improveTests } from './testgen.js';
|
|
43
|
-
import { startSession } from './session.js';
|
|
44
|
-
import { cleanResponse } from './formatter.js';
|
|
45
25
|
import { displayError } from './presentation/cli/error-display.js';
|
|
46
|
-
import {
|
|
47
|
-
import { colors,
|
|
48
|
-
// Load
|
|
26
|
+
import { performHealthCheck, formatHealthStatus, getMetrics } from './monitoring.js';
|
|
27
|
+
import { colors, createBanner, createTable, createWelcomeMessage, success, error, info, sectionHeader, formatNumber, newline, isMinimalStyle, printMinimalHeader, } from './ui.js';
|
|
28
|
+
// Load .env — agent dir first, then cwd
|
|
49
29
|
try {
|
|
50
30
|
const __filename = fileURLToPath(import.meta.url);
|
|
51
31
|
const __dirname = dirname(__filename);
|
|
@@ -53,97 +33,37 @@ try {
|
|
|
53
33
|
config({ path: join(agentDir, '.env') });
|
|
54
34
|
}
|
|
55
35
|
catch {
|
|
56
|
-
// Fallback to
|
|
36
|
+
// Fallback to cwd
|
|
57
37
|
}
|
|
58
|
-
config();
|
|
59
|
-
// Import centralized version utility
|
|
38
|
+
config();
|
|
60
39
|
import { getVersion } from './utils/version.js';
|
|
61
40
|
const program = new Command();
|
|
62
41
|
program
|
|
63
42
|
.name('codehere')
|
|
64
|
-
.description('AI coding
|
|
43
|
+
.description('A PM for your AI coding agents. Delegate a task, watch it run, get a plain-English report with a trust score on every file.')
|
|
65
44
|
.version(getVersion())
|
|
66
|
-
.configureHelp({
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
// Smart routing: if query provided as argument, route intelligently
|
|
45
|
+
.configureHelp({ helpWidth: 80, sortSubcommands: true });
|
|
46
|
+
// Default action · show welcome, then drop into the interactive REPL on TTY.
|
|
47
|
+
// codehere offers TWO surfaces, both first-class: the CLI REPL (here) and the
|
|
48
|
+
// web UI (`codehere start`). Users pick whichever fits their workflow.
|
|
71
49
|
program
|
|
72
|
-
.argument('[query]', '
|
|
73
|
-
.action(async (query
|
|
50
|
+
.argument('[query]', 'Optional initial task to delegate (otherwise enter interactive REPL)')
|
|
51
|
+
.action(async (query) => {
|
|
52
|
+
// One-shot delegate: `codehere "review src/auth.ts"` runs that task and exits.
|
|
53
|
+
// Useful for scripting or quick one-offs without entering the REPL.
|
|
74
54
|
if (query && query.trim()) {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
requireApiKey();
|
|
79
|
-
}
|
|
80
|
-
try {
|
|
81
|
-
const opts = program.opts();
|
|
82
|
-
await executeSmartCommand(query.trim(), { review: opts.review || false });
|
|
83
|
-
logEntry({
|
|
84
|
-
timestamp: new Date().toISOString(),
|
|
85
|
-
command: 'smart',
|
|
86
|
-
query: query.trim(),
|
|
87
|
-
policyStatus: 'allowed',
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
catch (err) {
|
|
91
|
-
console.error(error('Error:'), err instanceof Error ? err.message : String(err));
|
|
92
|
-
process.exit(1);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
else if (process.stdin.isTTY) {
|
|
96
|
-
// Check if first run — show onboarding
|
|
97
|
-
const codehereDir = join(process.env.HOME || '', '.codehere');
|
|
98
|
-
const settingsPath = join(process.env.HOME || '', '.claude', 'settings.json');
|
|
99
|
-
const hasHooks = existsSync(settingsPath) && readFileSync(settingsPath, 'utf-8').includes('codehere');
|
|
100
|
-
const hasAuditLog = existsSync(join(codehereDir, 'audit', 'hook-events.jsonl'));
|
|
101
|
-
if (!hasHooks && !hasAuditLog) {
|
|
102
|
-
// First run — onboarding
|
|
103
|
-
console.log(createWelcomeMessage());
|
|
104
|
-
newline();
|
|
105
|
-
console.log(colors.bold('Welcome! Let\'s get you set up.'));
|
|
106
|
-
newline();
|
|
107
|
-
console.log(colors.dim(' codehere works in two layers:'));
|
|
108
|
-
newline();
|
|
109
|
-
console.log(colors.bold(' Layer 0 — Safety (no API key, free, instant)'));
|
|
110
|
-
console.log(colors.dim(' Scans every AI write before it runs. Catches injections,'));
|
|
111
|
-
console.log(colors.dim(' hardcoded secrets, destructive ops, and more.'));
|
|
112
|
-
newline();
|
|
113
|
-
console.log(' → Run ' + colors.cyan('codehere hook install') + ' to activate');
|
|
114
|
-
console.log(' → Run ' + colors.cyan('codehere trust .') + ' to scan this project');
|
|
115
|
-
newline();
|
|
116
|
-
console.log(colors.bold(' Layer 1 — Intelligence (needs API key)'));
|
|
117
|
-
console.log(colors.dim(' Ask questions, fix code, plan tasks — all with AI.'));
|
|
118
|
-
console.log(colors.dim(' Works with Anthropic, OpenAI, or Ollama.'));
|
|
119
|
-
newline();
|
|
120
|
-
console.log(' → Run ' + colors.cyan('codehere setup') + ' to configure');
|
|
121
|
-
newline();
|
|
122
|
-
console.log(colors.dim('Most people start with: ') + colors.cyan('codehere hook install'));
|
|
123
|
-
newline();
|
|
124
|
-
}
|
|
125
|
-
else {
|
|
126
|
-
// Returning user — interactive session
|
|
127
|
-
await startSession();
|
|
128
|
-
}
|
|
55
|
+
const { runOneShot } = await import('./presentation/cli/repl.js');
|
|
56
|
+
await runOneShot(query.trim());
|
|
57
|
+
return;
|
|
129
58
|
}
|
|
130
|
-
|
|
131
|
-
// Non-interactive — show quick start
|
|
59
|
+
if (!process.stdin.isTTY) {
|
|
132
60
|
console.log(createWelcomeMessage());
|
|
133
|
-
|
|
134
|
-
console.log(colors.bold('Quick start:'));
|
|
135
|
-
newline();
|
|
136
|
-
console.log(colors.cyan(' codehere hook install') + ' Scan every AI write before it runs');
|
|
137
|
-
console.log(colors.cyan(' codehere trust .') + ' Scan your project now (no API key)');
|
|
138
|
-
console.log(colors.cyan(' codehere report') + ' Generate security audit report');
|
|
139
|
-
newline();
|
|
140
|
-
console.log(colors.bold('With an API key:'));
|
|
141
|
-
console.log(colors.cyan(' codehere "<your query>"') + ' Ask, fix, plan — routes automatically');
|
|
142
|
-
newline();
|
|
143
|
-
console.log(colors.dim('Run "codehere --help" for all commands'));
|
|
144
|
-
newline();
|
|
61
|
+
return;
|
|
145
62
|
}
|
|
63
|
+
const { runInteractiveRepl } = await import('./presentation/cli/repl.js');
|
|
64
|
+
await runInteractiveRepl();
|
|
146
65
|
});
|
|
66
|
+
// ── setup ────────────────────────────────────────────────────────────────────
|
|
147
67
|
program
|
|
148
68
|
.command('setup')
|
|
149
69
|
.description('Interactive setup wizard for first-time users')
|
|
@@ -151,6 +71,7 @@ program
|
|
|
151
71
|
.action(async (options) => {
|
|
152
72
|
await executeSetupCommand(options.force || false);
|
|
153
73
|
});
|
|
74
|
+
// ── config ───────────────────────────────────────────────────────────────────
|
|
154
75
|
program
|
|
155
76
|
.command('config')
|
|
156
77
|
.description('Manage configuration and preferences')
|
|
@@ -160,62 +81,7 @@ program
|
|
|
160
81
|
.action(async (action, key, value) => {
|
|
161
82
|
await executeConfigCommand(action, key, value);
|
|
162
83
|
});
|
|
163
|
-
|
|
164
|
-
.command('models')
|
|
165
|
-
.description('List, search, and get info about available AI models')
|
|
166
|
-
.option('-l, --list', 'List all available models')
|
|
167
|
-
.option('-s, --search <query>', 'Search models by name or ID')
|
|
168
|
-
.option('-i, --info <model-id>', 'Get detailed info about a specific model')
|
|
169
|
-
.option('-p, --provider <provider>', 'Filter by provider (cohere, openai, openrouter, etc.)')
|
|
170
|
-
.option('--json', 'Output in JSON format')
|
|
171
|
-
.action(async (options) => {
|
|
172
|
-
initLogFile();
|
|
173
|
-
try {
|
|
174
|
-
await executeModelsCommand({
|
|
175
|
-
list: options.list,
|
|
176
|
-
search: options.search,
|
|
177
|
-
info: options.info,
|
|
178
|
-
provider: options.provider,
|
|
179
|
-
json: options.json,
|
|
180
|
-
});
|
|
181
|
-
}
|
|
182
|
-
catch (err) {
|
|
183
|
-
console.error(error('Error:'), err instanceof Error ? err.message : String(err));
|
|
184
|
-
process.exit(1);
|
|
185
|
-
}
|
|
186
|
-
});
|
|
187
|
-
program
|
|
188
|
-
.command('telemetry')
|
|
189
|
-
.description('Manage telemetry settings (opt-in, privacy-first)')
|
|
190
|
-
.option('--enable', 'Enable telemetry')
|
|
191
|
-
.option('--disable', 'Disable telemetry')
|
|
192
|
-
.option('--status', 'Show telemetry status')
|
|
193
|
-
.action(async (options) => {
|
|
194
|
-
const telemetry = getTelemetryService();
|
|
195
|
-
if (options.enable) {
|
|
196
|
-
telemetry.enable();
|
|
197
|
-
console.log(success('Telemetry is on. Appreciate it!'));
|
|
198
|
-
console.log(colors.dim('Data is anonymized and no code/content is transmitted.'));
|
|
199
|
-
newline();
|
|
200
|
-
}
|
|
201
|
-
else if (options.disable) {
|
|
202
|
-
telemetry.disable();
|
|
203
|
-
console.log(success('Telemetry turned off.'));
|
|
204
|
-
newline();
|
|
205
|
-
}
|
|
206
|
-
else {
|
|
207
|
-
console.log(colors.bold('Telemetry Status:'));
|
|
208
|
-
newline();
|
|
209
|
-
console.log(telemetry.isEnabled()
|
|
210
|
-
? success('Enabled (opt-in)')
|
|
211
|
-
: colors.dim('Disabled (set --enable to opt-in)'));
|
|
212
|
-
newline();
|
|
213
|
-
console.log(colors.dim('Telemetry helps us understand usage patterns and improve Codehere.'));
|
|
214
|
-
console.log(colors.dim('All data is anonymized. No code or content is transmitted.'));
|
|
215
|
-
console.log(colors.dim('Enable with: codehere telemetry --enable'));
|
|
216
|
-
newline();
|
|
217
|
-
}
|
|
218
|
-
});
|
|
84
|
+
// ── help ─────────────────────────────────────────────────────────────────────
|
|
219
85
|
program
|
|
220
86
|
.command('help')
|
|
221
87
|
.description('Show help for commands')
|
|
@@ -223,7 +89,6 @@ program
|
|
|
223
89
|
.option('--all', 'Show all commands (including advanced)')
|
|
224
90
|
.action(async (commandName, options) => {
|
|
225
91
|
if (options?.all && !commandName) {
|
|
226
|
-
// Temporarily unhide all commands and show Commander's built-in help
|
|
227
92
|
for (const cmd of program.commands) {
|
|
228
93
|
cmd._hidden = false;
|
|
229
94
|
}
|
|
@@ -233,273 +98,7 @@ program
|
|
|
233
98
|
await executeHelpCommand(commandName);
|
|
234
99
|
}
|
|
235
100
|
});
|
|
236
|
-
|
|
237
|
-
.command('index')
|
|
238
|
-
.description('Build embeddings index of the repository')
|
|
239
|
-
.option('-r, --repo <path>', 'Repository path (default: current directory)')
|
|
240
|
-
.action(async (options) => {
|
|
241
|
-
initLogFile();
|
|
242
|
-
// Allow fake AI for testing, or require real API key
|
|
243
|
-
if (!hasAnyApiKey()) {
|
|
244
|
-
requireApiKey();
|
|
245
|
-
}
|
|
246
|
-
try {
|
|
247
|
-
if (isMinimalStyle()) {
|
|
248
|
-
console.log(colors.cyan('Indexing codebase...'));
|
|
249
|
-
newline();
|
|
250
|
-
}
|
|
251
|
-
else {
|
|
252
|
-
console.log(createBanner('Indexing Repository', 'Building semantic search index'));
|
|
253
|
-
newline();
|
|
254
|
-
}
|
|
255
|
-
const repoPath = options.repo || process.cwd();
|
|
256
|
-
// NEW: Use parallel indexing with caching
|
|
257
|
-
await indexRepositoryParallel(repoPath, {
|
|
258
|
-
useCache: true,
|
|
259
|
-
maxConcurrency: 5,
|
|
260
|
-
});
|
|
261
|
-
logEntry({
|
|
262
|
-
timestamp: new Date().toISOString(),
|
|
263
|
-
command: 'index',
|
|
264
|
-
policyStatus: 'allowed',
|
|
265
|
-
});
|
|
266
|
-
newline();
|
|
267
|
-
if (isMinimalStyle()) {
|
|
268
|
-
console.log(success('All indexed. Ready to go.'));
|
|
269
|
-
}
|
|
270
|
-
else {
|
|
271
|
-
console.log(success('All indexed.'));
|
|
272
|
-
}
|
|
273
|
-
process.exit(0);
|
|
274
|
-
}
|
|
275
|
-
catch (err) {
|
|
276
|
-
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
277
|
-
// Check for 401 Unauthorized errors
|
|
278
|
-
if (errorMessage.includes('401') || errorMessage.includes('Unauthorized') || errorMessage.includes('no api key')) {
|
|
279
|
-
newline();
|
|
280
|
-
console.error(error('API key rejected — looks like it might be invalid or expired.'));
|
|
281
|
-
newline();
|
|
282
|
-
console.log(colors.dim(' Check the key is set and still valid:'));
|
|
283
|
-
console.log(colors.cyan(' echo $ANTHROPIC_API_KEY') + colors.dim(' (or whichever provider you\'re using)'));
|
|
284
|
-
console.log(colors.dim(' For testing without API calls: CODEHERE_FAKE_AI=1 codehere index'));
|
|
285
|
-
newline();
|
|
286
|
-
}
|
|
287
|
-
else {
|
|
288
|
-
console.error(error('Error indexing repository:'), errorMessage);
|
|
289
|
-
}
|
|
290
|
-
process.exit(1);
|
|
291
|
-
}
|
|
292
|
-
});
|
|
293
|
-
program
|
|
294
|
-
.command('ask')
|
|
295
|
-
.description('Ask questions about your codebase using semantic search')
|
|
296
|
-
.argument('<question>', 'The question to ask about your code')
|
|
297
|
-
.alias('q')
|
|
298
|
-
.addHelpText('after', `
|
|
299
|
-
Examples:
|
|
300
|
-
$ codehere ask "How does authentication work?"
|
|
301
|
-
$ codehere ask "Where is the user login function?"
|
|
302
|
-
$ codehere q "Explain the database schema"
|
|
303
|
-
|
|
304
|
-
This command will:
|
|
305
|
-
• Search your indexed codebase for relevant code
|
|
306
|
-
• Use AI to generate a contextual answer
|
|
307
|
-
• Show which files were used as context
|
|
308
|
-
|
|
309
|
-
Make sure to run "codehere index" first!
|
|
310
|
-
`)
|
|
311
|
-
.action(async (question) => {
|
|
312
|
-
initLogFile();
|
|
313
|
-
if (!hasAnyApiKey()) {
|
|
314
|
-
requireApiKey();
|
|
315
|
-
}
|
|
316
|
-
try {
|
|
317
|
-
await executeAskCommand(question);
|
|
318
|
-
logEntry({
|
|
319
|
-
timestamp: new Date().toISOString(),
|
|
320
|
-
command: 'ask',
|
|
321
|
-
query: question,
|
|
322
|
-
policyStatus: 'allowed',
|
|
323
|
-
});
|
|
324
|
-
}
|
|
325
|
-
catch (err) {
|
|
326
|
-
const errMessage = err instanceof Error ? err.message : String(err);
|
|
327
|
-
// If it's a missing embeddings error, exit with 1
|
|
328
|
-
if (errMessage.includes('No embeddings found')) {
|
|
329
|
-
process.exit(1);
|
|
330
|
-
}
|
|
331
|
-
console.error(error('Error:'), errMessage);
|
|
332
|
-
process.exit(1);
|
|
333
|
-
}
|
|
334
|
-
});
|
|
335
|
-
program
|
|
336
|
-
.command('fix')
|
|
337
|
-
.description('Apply AI-generated code edits to a file')
|
|
338
|
-
.argument('[file]', 'Path to the file to edit')
|
|
339
|
-
.argument('[instruction]', 'Natural language instruction for the edit')
|
|
340
|
-
.alias('edit')
|
|
341
|
-
.option('--review', 'Enable review & feedback capture (used for trust scoring and Validation Mode)')
|
|
342
|
-
.addHelpText('after', `
|
|
343
|
-
Examples:
|
|
344
|
-
$ codehere fix src/auth.ts "Add error handling for invalid tokens"
|
|
345
|
-
$ codehere fix utils/helpers.js "Rename function foo() to bar()" --review
|
|
346
|
-
$ codehere edit app.js "Add input validation"
|
|
347
|
-
|
|
348
|
-
This command will:
|
|
349
|
-
• Generate a unified diff using AI
|
|
350
|
-
• Apply safety policy checks
|
|
351
|
-
• Apply the changes to your file
|
|
352
|
-
• Log the operation for audit
|
|
353
|
-
|
|
354
|
-
Options:
|
|
355
|
-
--review Enable review & feedback capture (used for trust scoring and Validation Mode)
|
|
356
|
-
|
|
357
|
-
Safety policies:
|
|
358
|
-
• Maximum 50 lines changed per edit
|
|
359
|
-
• Cannot edit files in restricted folders (infra/, billing/)
|
|
360
|
-
• Only one file can be edited at a time
|
|
361
|
-
`)
|
|
362
|
-
.action(async (file, instruction, options) => {
|
|
363
|
-
if (!file || !instruction) {
|
|
364
|
-
newline();
|
|
365
|
-
console.log(colors.bold('Usage:') + ' codehere fix <file> "<instruction>"');
|
|
366
|
-
newline();
|
|
367
|
-
console.log('Examples:');
|
|
368
|
-
console.log(colors.cyan(' codehere fix src/auth.ts "add input validation"'));
|
|
369
|
-
console.log(colors.cyan(' codehere fix src/api.js "switch to parameterized queries"'));
|
|
370
|
-
newline();
|
|
371
|
-
process.exit(1);
|
|
372
|
-
}
|
|
373
|
-
initLogFile();
|
|
374
|
-
if (!hasAnyApiKey()) {
|
|
375
|
-
requireApiKey();
|
|
376
|
-
}
|
|
377
|
-
try {
|
|
378
|
-
// Check if embeddings exist for this repository (skip check if using fake AI for testing)
|
|
379
|
-
if (process.env.CODEHERE_FAKE_AI !== '1') {
|
|
380
|
-
const { container } = await import('./application/services/dependency-container.js');
|
|
381
|
-
const embeddingRepo = container.getEmbeddingRepository(process.cwd());
|
|
382
|
-
const hasEmbeddings = await embeddingRepo.hasEmbeddings();
|
|
383
|
-
if (!hasEmbeddings) {
|
|
384
|
-
newline();
|
|
385
|
-
console.error(colors.red('No embeddings found for this repository.'));
|
|
386
|
-
newline();
|
|
387
|
-
console.log('Run:');
|
|
388
|
-
console.log(colors.cyan(' codehere index'));
|
|
389
|
-
console.log('');
|
|
390
|
-
console.log('then re-run this command.');
|
|
391
|
-
newline();
|
|
392
|
-
process.exit(1);
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
await executeFixCommand(file, instruction, { review: options?.review });
|
|
396
|
-
logEntry({
|
|
397
|
-
timestamp: new Date().toISOString(),
|
|
398
|
-
command: 'fix',
|
|
399
|
-
query: instruction,
|
|
400
|
-
filepath: file,
|
|
401
|
-
policyStatus: 'allowed',
|
|
402
|
-
});
|
|
403
|
-
}
|
|
404
|
-
catch (err) {
|
|
405
|
-
displayError(err, {
|
|
406
|
-
operation: 'edit',
|
|
407
|
-
query: instruction,
|
|
408
|
-
filepath: file,
|
|
409
|
-
showSuggestions: true,
|
|
410
|
-
});
|
|
411
|
-
process.exit(1);
|
|
412
|
-
}
|
|
413
|
-
});
|
|
414
|
-
program
|
|
415
|
-
.command('explain')
|
|
416
|
-
.description('Get AI explanation of a code file')
|
|
417
|
-
.argument('<file>', 'Path to the file to explain')
|
|
418
|
-
.alias('e')
|
|
419
|
-
.addHelpText('after', `
|
|
420
|
-
Examples:
|
|
421
|
-
$ codehere explain src/auth.ts
|
|
422
|
-
$ codehere explain utils/helpers.js
|
|
423
|
-
$ codehere e app.js
|
|
424
|
-
|
|
425
|
-
This command will:
|
|
426
|
-
• Read the file content
|
|
427
|
-
• Generate an AI explanation
|
|
428
|
-
• Show file structure, functions, and logic
|
|
429
|
-
`)
|
|
430
|
-
.action(async (file) => {
|
|
431
|
-
initLogFile();
|
|
432
|
-
if (!hasAnyApiKey()) {
|
|
433
|
-
requireApiKey();
|
|
434
|
-
}
|
|
435
|
-
try {
|
|
436
|
-
// Check if embeddings exist for this repository (skip check if using fake AI for testing)
|
|
437
|
-
if (process.env.CODEHERE_FAKE_AI !== '1') {
|
|
438
|
-
const { container } = await import('./application/services/dependency-container.js');
|
|
439
|
-
const embeddingRepo = container.getEmbeddingRepository(process.cwd());
|
|
440
|
-
const hasEmbeddings = await embeddingRepo.hasEmbeddings();
|
|
441
|
-
if (!hasEmbeddings) {
|
|
442
|
-
newline();
|
|
443
|
-
console.error(colors.red('No embeddings found for this repository.'));
|
|
444
|
-
newline();
|
|
445
|
-
console.log('Run:');
|
|
446
|
-
console.log(colors.cyan(' codehere index'));
|
|
447
|
-
console.log('');
|
|
448
|
-
console.log('then re-run this command.');
|
|
449
|
-
newline();
|
|
450
|
-
process.exit(1);
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
if (isMinimalStyle()) {
|
|
454
|
-
printMinimalHeader('explain', file);
|
|
455
|
-
}
|
|
456
|
-
else {
|
|
457
|
-
console.log(createBanner('Explain File', 'AI-powered code explanation'));
|
|
458
|
-
newline();
|
|
459
|
-
}
|
|
460
|
-
const fullPath = join(process.cwd(), file);
|
|
461
|
-
// Check if file exists
|
|
462
|
-
const { existsSync } = await import('fs');
|
|
463
|
-
if (!existsSync(fullPath)) {
|
|
464
|
-
newline();
|
|
465
|
-
console.error(error(`File not found: ${file}`));
|
|
466
|
-
console.log(info(`Looking for: ${fullPath}`));
|
|
467
|
-
console.log(info('Tip: Use a relative path from your current directory'));
|
|
468
|
-
newline();
|
|
469
|
-
process.exit(1);
|
|
470
|
-
}
|
|
471
|
-
// Use clean architecture
|
|
472
|
-
const { container } = await import('./application/services/dependency-container.js');
|
|
473
|
-
const useCase = container.explainFileUseCase;
|
|
474
|
-
const explainSpinner = createSpinner('Generating explanation...');
|
|
475
|
-
explainSpinner.start();
|
|
476
|
-
const result = await useCase.execute({ filepath: file });
|
|
477
|
-
explainSpinner.stop();
|
|
478
|
-
// File info
|
|
479
|
-
const lines = result.fileContent.split('\n').length;
|
|
480
|
-
const size = (result.fileContent.length / 1024).toFixed(2);
|
|
481
|
-
console.log(colors.bold('File:') + ' ' + formatPath(file));
|
|
482
|
-
console.log(colors.dim(` Lines: ${lines} | Size: ${size} KB`));
|
|
483
|
-
newline();
|
|
484
|
-
// Display explanation with markdown formatting
|
|
485
|
-
sectionHeader('Explanation');
|
|
486
|
-
const { formatMarkdownForTerminal } = await import('./infrastructure/ux/stream-markdown.js');
|
|
487
|
-
console.log(formatMarkdownForTerminal(result.explanation));
|
|
488
|
-
newline();
|
|
489
|
-
logEntry({
|
|
490
|
-
timestamp: new Date().toISOString(),
|
|
491
|
-
command: 'explain',
|
|
492
|
-
query: `Explain ${file}`,
|
|
493
|
-
filepath: file,
|
|
494
|
-
policyStatus: 'allowed',
|
|
495
|
-
retrievedFiles: [file],
|
|
496
|
-
});
|
|
497
|
-
}
|
|
498
|
-
catch (err) {
|
|
499
|
-
console.error(error('Error:'), err instanceof Error ? err.message : String(err));
|
|
500
|
-
process.exit(1);
|
|
501
|
-
}
|
|
502
|
-
});
|
|
101
|
+
// ── health ───────────────────────────────────────────────────────────────────
|
|
503
102
|
program
|
|
504
103
|
.command('health')
|
|
505
104
|
.description('Check system health and metrics')
|
|
@@ -529,418 +128,13 @@ program
|
|
|
529
128
|
['Cache Hits', formatNumber(metrics.performance.cacheHits)],
|
|
530
129
|
]);
|
|
531
130
|
console.log(metricsTable.toString());
|
|
532
|
-
if (Object.keys(metrics.errors.byType).length > 0) {
|
|
533
|
-
newline();
|
|
534
|
-
sectionHeader('Error Summary');
|
|
535
|
-
const errorTable = createTable(['Error Type', 'Count'], Object.entries(metrics.errors.byType).map(([type, count]) => [
|
|
536
|
-
type,
|
|
537
|
-
formatNumber(count),
|
|
538
|
-
]));
|
|
539
|
-
console.log(errorTable.toString());
|
|
540
|
-
}
|
|
541
131
|
}
|
|
542
132
|
newline();
|
|
543
133
|
if (health.status === 'unhealthy') {
|
|
544
134
|
process.exit(1);
|
|
545
135
|
}
|
|
546
136
|
});
|
|
547
|
-
|
|
548
|
-
.command('refactor')
|
|
549
|
-
.description('Refactor multiple files with coordinated changes')
|
|
550
|
-
.argument('<pattern>', 'File pattern (e.g., "src/**/*.ts") or comma-separated files')
|
|
551
|
-
.argument('<instruction>', 'Refactoring instruction')
|
|
552
|
-
.option('--dry-run', 'Show changes without applying')
|
|
553
|
-
.option('--max-files <number>', 'Maximum files to process', '10')
|
|
554
|
-
.action(async (pattern, instruction, options) => {
|
|
555
|
-
initLogFile();
|
|
556
|
-
if (!hasAnyApiKey()) {
|
|
557
|
-
requireApiKey();
|
|
558
|
-
}
|
|
559
|
-
try {
|
|
560
|
-
console.log(createBanner('Multi-File Refactor', 'Coordinated changes across files'));
|
|
561
|
-
newline();
|
|
562
|
-
// Parse files from pattern
|
|
563
|
-
let files;
|
|
564
|
-
if (pattern.includes(',')) {
|
|
565
|
-
files = pattern.split(',').map(f => f.trim());
|
|
566
|
-
}
|
|
567
|
-
else {
|
|
568
|
-
files = findFilesByPattern(pattern);
|
|
569
|
-
}
|
|
570
|
-
if (files.length === 0) {
|
|
571
|
-
console.log(warning('No files found matching pattern'));
|
|
572
|
-
return;
|
|
573
|
-
}
|
|
574
|
-
console.log(colors.bold('Files to refactor:'));
|
|
575
|
-
files.slice(0, 10).forEach(f => console.log(colors.cyan(' ' + f)));
|
|
576
|
-
if (files.length > 10) {
|
|
577
|
-
console.log(colors.dim(` ... and ${files.length - 10} more`));
|
|
578
|
-
}
|
|
579
|
-
newline();
|
|
580
|
-
console.log(colors.bold('Instruction:') + ' ' + colors.cyan(instruction));
|
|
581
|
-
newline();
|
|
582
|
-
const maxFiles = parseInt(options.maxFiles || '10', 10);
|
|
583
|
-
const result = await refactorMultiFile(files.slice(0, maxFiles), instruction, {
|
|
584
|
-
dryRun: options.dryRun || false,
|
|
585
|
-
maxFiles,
|
|
586
|
-
});
|
|
587
|
-
// Display results
|
|
588
|
-
sectionHeader('Refactoring Results');
|
|
589
|
-
const resultsTable = createTable(['Metric', 'Value'], [
|
|
590
|
-
['Files Processed', formatNumber(files.length)],
|
|
591
|
-
['Files Changed', formatNumber(result.filesChanged.length)],
|
|
592
|
-
['Total Lines Added', formatNumber(result.totalChanges.linesAdded)],
|
|
593
|
-
['Total Lines Removed', formatNumber(result.totalChanges.linesRemoved)],
|
|
594
|
-
['Errors', formatNumber(result.errors.length)],
|
|
595
|
-
]);
|
|
596
|
-
console.log(resultsTable.toString());
|
|
597
|
-
if (result.errors.length > 0) {
|
|
598
|
-
newline();
|
|
599
|
-
sectionHeader('Errors');
|
|
600
|
-
result.errors.forEach(err => {
|
|
601
|
-
console.log(error(`${err.file}: ${err.error}`));
|
|
602
|
-
});
|
|
603
|
-
}
|
|
604
|
-
if (result.filesChanged.length > 0) {
|
|
605
|
-
newline();
|
|
606
|
-
console.log(success(options.dryRun ? 'Refactor planned.' : 'Refactoring done.'));
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
catch (err) {
|
|
610
|
-
console.error(error('Error:'), err instanceof Error ? err.message : String(err));
|
|
611
|
-
process.exit(1);
|
|
612
|
-
}
|
|
613
|
-
});
|
|
614
|
-
program
|
|
615
|
-
.command('git-review')
|
|
616
|
-
.description('Review git changes and suggest improvements')
|
|
617
|
-
.option('-f, --file <file>', 'Review specific file')
|
|
618
|
-
.action((options) => {
|
|
619
|
-
initLogFile();
|
|
620
|
-
try {
|
|
621
|
-
console.log(createBanner('Git Review', 'Git-aware code analysis'));
|
|
622
|
-
newline();
|
|
623
|
-
const status = getGitStatus();
|
|
624
|
-
if (!status.isRepo) {
|
|
625
|
-
console.log(warning('Not a git repository'));
|
|
626
|
-
return;
|
|
627
|
-
}
|
|
628
|
-
console.log(formatGitStatus(status));
|
|
629
|
-
newline();
|
|
630
|
-
if (!status.hasChanges) {
|
|
631
|
-
console.log(info('No changes to review'));
|
|
632
|
-
return;
|
|
633
|
-
}
|
|
634
|
-
// Get diffs
|
|
635
|
-
const diffs = options.file
|
|
636
|
-
? (() => {
|
|
637
|
-
const diff = getGitDiff(options.file);
|
|
638
|
-
return diff ? new Map([[options.file, diff]]) : new Map();
|
|
639
|
-
})()
|
|
640
|
-
: getAllGitDiffs();
|
|
641
|
-
if (diffs.size === 0) {
|
|
642
|
-
console.log(info('No diffs found'));
|
|
643
|
-
return;
|
|
644
|
-
}
|
|
645
|
-
sectionHeader('Change Analysis');
|
|
646
|
-
for (const [file, diff] of diffs.entries()) {
|
|
647
|
-
const analysis = analyzeDiff(diff);
|
|
648
|
-
console.log(colors.bold(file));
|
|
649
|
-
console.log(colors.dim(` ${analysis.summary}`));
|
|
650
|
-
console.log(colors.dim(` Risk: ${analysis.riskLevel.toUpperCase()}`));
|
|
651
|
-
if (analysis.suggestions.length > 0) {
|
|
652
|
-
console.log(colors.yellow(' Suggestions:'));
|
|
653
|
-
analysis.suggestions.forEach(s => {
|
|
654
|
-
console.log(colors.yellow(` - ${s}`));
|
|
655
|
-
});
|
|
656
|
-
}
|
|
657
|
-
newline();
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
catch (err) {
|
|
661
|
-
console.error(error('Error:'), err instanceof Error ? err.message : String(err));
|
|
662
|
-
process.exit(1);
|
|
663
|
-
}
|
|
664
|
-
});
|
|
665
|
-
program
|
|
666
|
-
.command('plan')
|
|
667
|
-
.description('Plan a complex multi-step task')
|
|
668
|
-
.argument('<instruction>', 'Complex instruction to plan')
|
|
669
|
-
.option('-e, --execute', 'Execute the plan after creating it')
|
|
670
|
-
.option('--review', 'Enable review & feedback capture (used for trust scoring and Validation Mode)')
|
|
671
|
-
.action(async (instruction, options) => {
|
|
672
|
-
initLogFile();
|
|
673
|
-
if (!hasAnyApiKey()) {
|
|
674
|
-
requireApiKey();
|
|
675
|
-
}
|
|
676
|
-
try {
|
|
677
|
-
if (isMinimalStyle()) {
|
|
678
|
-
printMinimalHeader('plan', instruction);
|
|
679
|
-
}
|
|
680
|
-
else {
|
|
681
|
-
console.log(createBanner('Task Planning', 'Intelligent multi-step execution'));
|
|
682
|
-
newline();
|
|
683
|
-
console.log(colors.bold('Instruction:') + ' ' + colors.cyan(instruction));
|
|
684
|
-
}
|
|
685
|
-
newline();
|
|
686
|
-
// Use new memory-based planning
|
|
687
|
-
await executePlanCommand(instruction, { execute: options.execute, review: options.review });
|
|
688
|
-
}
|
|
689
|
-
catch (err) {
|
|
690
|
-
console.error(error('Error:'), err instanceof Error ? err.message : String(err));
|
|
691
|
-
process.exit(1);
|
|
692
|
-
}
|
|
693
|
-
});
|
|
694
|
-
program
|
|
695
|
-
.command('decision')
|
|
696
|
-
.description('Record and query architectural/design decisions')
|
|
697
|
-
.argument('<action>', 'Action: record, list, query, or explain')
|
|
698
|
-
.argument('[args...]', 'Arguments for the action')
|
|
699
|
-
.option('-c, --codebase <path>', 'Codebase path (default: current directory)')
|
|
700
|
-
.option('-t, --type <type>', 'Decision type filter (for list)')
|
|
701
|
-
.option('-l, --limit <number>', 'Limit number of results', '10')
|
|
702
|
-
.action(async (action, args, options) => {
|
|
703
|
-
initLogFile();
|
|
704
|
-
try {
|
|
705
|
-
await executeDecisionCommand(action, args, {
|
|
706
|
-
codebase: options.codebase,
|
|
707
|
-
type: options.type,
|
|
708
|
-
limit: options.limit ? parseInt(options.limit, 10) : undefined,
|
|
709
|
-
});
|
|
710
|
-
}
|
|
711
|
-
catch (err) {
|
|
712
|
-
console.error(error('Error:'), err instanceof Error ? err.message : String(err));
|
|
713
|
-
process.exit(1);
|
|
714
|
-
}
|
|
715
|
-
});
|
|
716
|
-
program
|
|
717
|
-
.command('knowledge')
|
|
718
|
-
.description('Query and manage preserved knowledge from codebase')
|
|
719
|
-
.argument('<action>', 'Action: query, list, summary, or extract')
|
|
720
|
-
.argument('[args...]', 'Arguments for the action')
|
|
721
|
-
.option('-c, --codebase <path>', 'Codebase path (default: current directory)')
|
|
722
|
-
.option('-t, --type <type>', 'Knowledge type filter (for list)')
|
|
723
|
-
.option('--tags <tags>', 'Comma-separated tags (for list)')
|
|
724
|
-
.option('-l, --limit <number>', 'Limit number of results', '10')
|
|
725
|
-
.action(async (action, args, options) => {
|
|
726
|
-
initLogFile();
|
|
727
|
-
try {
|
|
728
|
-
await executeKnowledgeCommand(action, args, {
|
|
729
|
-
codebase: options.codebase,
|
|
730
|
-
type: options.type,
|
|
731
|
-
tags: options.tags ? options.tags.split(',') : undefined,
|
|
732
|
-
limit: options.limit ? parseInt(options.limit, 10) : undefined,
|
|
733
|
-
});
|
|
734
|
-
}
|
|
735
|
-
catch (err) {
|
|
736
|
-
console.error(error('Error:'), err instanceof Error ? err.message : String(err));
|
|
737
|
-
process.exit(1);
|
|
738
|
-
}
|
|
739
|
-
});
|
|
740
|
-
program
|
|
741
|
-
.command('learning')
|
|
742
|
-
.description('Learning dashboard - View patterns, knowledge, and insights')
|
|
743
|
-
.argument('<action>', 'Action: overview, patterns, knowledge, or insights')
|
|
744
|
-
.option('-c, --codebase <path>', 'Codebase path (default: current directory)')
|
|
745
|
-
.action(async (action, options) => {
|
|
746
|
-
initLogFile();
|
|
747
|
-
try {
|
|
748
|
-
await executeLearningDashboardCommand(action, options.codebase);
|
|
749
|
-
}
|
|
750
|
-
catch (err) {
|
|
751
|
-
console.error(error('Error:'), err instanceof Error ? err.message : String(err));
|
|
752
|
-
process.exit(1);
|
|
753
|
-
}
|
|
754
|
-
});
|
|
755
|
-
program
|
|
756
|
-
.command('review-file')
|
|
757
|
-
.description('Automated code review for a file')
|
|
758
|
-
.argument('<file>', 'File to review')
|
|
759
|
-
.action(async (file) => {
|
|
760
|
-
initLogFile();
|
|
761
|
-
if (!hasAnyApiKey()) {
|
|
762
|
-
requireApiKey();
|
|
763
|
-
}
|
|
764
|
-
try {
|
|
765
|
-
if (isMinimalStyle()) {
|
|
766
|
-
console.log(colors.bold(`Review: ${file}`));
|
|
767
|
-
newline();
|
|
768
|
-
}
|
|
769
|
-
else {
|
|
770
|
-
console.log(createBanner('Code Review', 'Automated senior-level analysis'));
|
|
771
|
-
newline();
|
|
772
|
-
}
|
|
773
|
-
const reviewSpinner = createSpinner('Reviewing code...');
|
|
774
|
-
reviewSpinner.start();
|
|
775
|
-
const review = await reviewFile(file);
|
|
776
|
-
reviewSpinner.stop();
|
|
777
|
-
newline();
|
|
778
|
-
if (isMinimalStyle()) {
|
|
779
|
-
console.log(colors.bold('Findings:'));
|
|
780
|
-
console.log(formatReview(review));
|
|
781
|
-
newline();
|
|
782
|
-
console.log(colors.dim(`Score: ${review.score}/100`));
|
|
783
|
-
}
|
|
784
|
-
else {
|
|
785
|
-
console.log(formatReview(review));
|
|
786
|
-
// Display score
|
|
787
|
-
newline();
|
|
788
|
-
sectionHeader('Review Score');
|
|
789
|
-
const scoreTable = createTable(['Metric', 'Value'], [
|
|
790
|
-
['Overall Score', `${review.score}/100`],
|
|
791
|
-
['Errors', formatNumber(review.issues.filter(i => i.severity === 'error').length)],
|
|
792
|
-
['Warnings', formatNumber(review.issues.filter(i => i.severity === 'warning').length)],
|
|
793
|
-
['Info', formatNumber(review.issues.filter(i => i.severity === 'info').length)],
|
|
794
|
-
]);
|
|
795
|
-
console.log(scoreTable.toString());
|
|
796
|
-
}
|
|
797
|
-
}
|
|
798
|
-
catch (err) {
|
|
799
|
-
console.error(error('Error:'), err instanceof Error ? err.message : String(err));
|
|
800
|
-
process.exit(1);
|
|
801
|
-
}
|
|
802
|
-
});
|
|
803
|
-
program
|
|
804
|
-
.command('learn')
|
|
805
|
-
.description('Show learning insights and performance recommendations')
|
|
806
|
-
.action(() => {
|
|
807
|
-
try {
|
|
808
|
-
console.log(createBanner('Learning Insights', 'What we\'ve learned from your codebase'));
|
|
809
|
-
newline();
|
|
810
|
-
const insights = formatLearningInsights();
|
|
811
|
-
console.log(insights);
|
|
812
|
-
const perfInsights = getPerformanceInsights();
|
|
813
|
-
if (perfInsights.recommendations.length > 0) {
|
|
814
|
-
sectionHeader('Performance Recommendations');
|
|
815
|
-
perfInsights.recommendations.forEach(rec => {
|
|
816
|
-
console.log(info(rec));
|
|
817
|
-
});
|
|
818
|
-
}
|
|
819
|
-
const cacheStats = getCacheStats();
|
|
820
|
-
newline();
|
|
821
|
-
sectionHeader('Cache Statistics');
|
|
822
|
-
const cacheTable = createTable(['Metric', 'Value'], [
|
|
823
|
-
['Memory Cache', `${cacheStats.memorySize}/${cacheStats.memoryMaxSize}`],
|
|
824
|
-
['File Cache', formatNumber(cacheStats.fileCacheCount)],
|
|
825
|
-
]);
|
|
826
|
-
console.log(cacheTable.toString());
|
|
827
|
-
}
|
|
828
|
-
catch (err) {
|
|
829
|
-
console.error(error('Error:'), err instanceof Error ? err.message : String(err));
|
|
830
|
-
process.exit(1);
|
|
831
|
-
}
|
|
832
|
-
});
|
|
833
|
-
program
|
|
834
|
-
.command('cache')
|
|
835
|
-
.description('Manage cache')
|
|
836
|
-
.option('--clear', 'Clear all caches')
|
|
837
|
-
.option('--stats', 'Show cache statistics')
|
|
838
|
-
.action((options) => {
|
|
839
|
-
if (options.clear) {
|
|
840
|
-
clearAllCaches();
|
|
841
|
-
console.log(success('All caches cleared'));
|
|
842
|
-
}
|
|
843
|
-
else if (options.stats) {
|
|
844
|
-
const stats = getCacheStats();
|
|
845
|
-
sectionHeader('Cache Statistics');
|
|
846
|
-
const cacheTable = createTable(['Metric', 'Value'], [
|
|
847
|
-
['Memory Cache', `${stats.memorySize}/${stats.memoryMaxSize}`],
|
|
848
|
-
['File Cache', formatNumber(stats.fileCacheCount)],
|
|
849
|
-
]);
|
|
850
|
-
console.log(cacheTable.toString());
|
|
851
|
-
}
|
|
852
|
-
else {
|
|
853
|
-
console.log(info('Use --clear to clear caches or --stats to show statistics'));
|
|
854
|
-
}
|
|
855
|
-
});
|
|
856
|
-
program
|
|
857
|
-
.command('migrate')
|
|
858
|
-
.description('Plan and execute framework migrations')
|
|
859
|
-
.argument('<from>', 'Source framework/version')
|
|
860
|
-
.argument('<to>', 'Target framework/version')
|
|
861
|
-
.option('-f, --files <files...>', 'Specific files to migrate')
|
|
862
|
-
.option('--plan-only', 'Only show migration plan, do not execute')
|
|
863
|
-
.action(async (from, to, options) => {
|
|
864
|
-
initLogFile();
|
|
865
|
-
if (!hasAnyApiKey()) {
|
|
866
|
-
requireApiKey();
|
|
867
|
-
}
|
|
868
|
-
try {
|
|
869
|
-
console.log(createBanner('Migration Assistant', `From ${from} to ${to}`));
|
|
870
|
-
newline();
|
|
871
|
-
const plan = await planMigration(from, to, options.files);
|
|
872
|
-
sectionHeader('Migration Plan');
|
|
873
|
-
console.log(formatMigrationPlan(plan));
|
|
874
|
-
if (!options.planOnly) {
|
|
875
|
-
newline();
|
|
876
|
-
console.log(info('Migration execution coming soon...'));
|
|
877
|
-
}
|
|
878
|
-
}
|
|
879
|
-
catch (err) {
|
|
880
|
-
console.error(error('Error:'), err instanceof Error ? err.message : String(err));
|
|
881
|
-
process.exit(1);
|
|
882
|
-
}
|
|
883
|
-
});
|
|
884
|
-
// DocumentationAgent-based docs command
|
|
885
|
-
program
|
|
886
|
-
.command('docs')
|
|
887
|
-
.description('Generate documentation using DocumentationAgent')
|
|
888
|
-
.argument('<generate>', 'Command: generate')
|
|
889
|
-
.argument('<type>', 'Documentation type: readme, api, or all')
|
|
890
|
-
.option('--target <dir>', 'Target directory for API docs (default: src)')
|
|
891
|
-
.option('--update-existing', 'Update existing documentation files')
|
|
892
|
-
.option('--output <path>', 'Output path for generated docs')
|
|
893
|
-
.option('--review', 'Enable review & feedback capture (used for trust scoring and Validation Mode)')
|
|
894
|
-
.addHelpText('after', `
|
|
895
|
-
Examples:
|
|
896
|
-
$ codehere docs generate readme
|
|
897
|
-
$ codehere docs generate api --target src
|
|
898
|
-
$ codehere docs generate all --update-existing
|
|
899
|
-
$ codehere docs generate readme --review
|
|
900
|
-
|
|
901
|
-
Warning: This command may modify README.md and docs/api.md files.
|
|
902
|
-
`)
|
|
903
|
-
.action(async (cmd, type, options) => {
|
|
904
|
-
if (cmd !== 'generate') {
|
|
905
|
-
console.error(error(`Unknown command: ${cmd}. Use 'generate' to create documentation.`));
|
|
906
|
-
process.exit(1);
|
|
907
|
-
}
|
|
908
|
-
if (!hasAnyApiKey()) {
|
|
909
|
-
requireApiKey();
|
|
910
|
-
}
|
|
911
|
-
initLogFile();
|
|
912
|
-
await executeDocsCommand(type, {
|
|
913
|
-
target: options.target,
|
|
914
|
-
updateExisting: options.updateExisting,
|
|
915
|
-
outputPath: options.output,
|
|
916
|
-
review: Boolean(options.review),
|
|
917
|
-
});
|
|
918
|
-
});
|
|
919
|
-
// Product brain command — project context for context-aware recommendations
|
|
920
|
-
program
|
|
921
|
-
.command('product')
|
|
922
|
-
.description('Set up product brain — helps trust and report give context-aware recommendations')
|
|
923
|
-
.argument('[subcommand]', 'init | show | set | spec', 'show')
|
|
924
|
-
.argument('[goal]', 'Goal or description (used with spec subcommand)')
|
|
925
|
-
.option('--risk <level>', 'Set risk level: low, medium, high, critical')
|
|
926
|
-
.option('--name <name>', 'Update project name')
|
|
927
|
-
.option('--production', 'Mark as production app')
|
|
928
|
-
.option('--force', 'Re-run init even if already configured')
|
|
929
|
-
.addHelpText('after', `
|
|
930
|
-
Examples:
|
|
931
|
-
$ codehere product init # interactive setup (2 minutes)
|
|
932
|
-
$ codehere product show # display current config
|
|
933
|
-
$ codehere product set --risk high
|
|
934
|
-
`)
|
|
935
|
-
.action(async (subcommand, goal, options) => {
|
|
936
|
-
await executeProductCommand(subcommand, {
|
|
937
|
-
risk: options.risk,
|
|
938
|
-
production: options.production,
|
|
939
|
-
name: goal || options.name,
|
|
940
|
-
force: options.force,
|
|
941
|
-
});
|
|
942
|
-
});
|
|
943
|
-
// Status command
|
|
137
|
+
// ── status ───────────────────────────────────────────────────────────────────
|
|
944
138
|
program
|
|
945
139
|
.command('status')
|
|
946
140
|
.description('Security dashboard — hook activity, blocked calls, cost routing')
|
|
@@ -949,14 +143,20 @@ program
|
|
|
949
143
|
.option('--sessions', 'Show per-session audit trail (multi-agent analysis)')
|
|
950
144
|
.option('--session <id>', 'Drill into a specific session (first 8+ chars of session ID)')
|
|
951
145
|
.option('--trust', 'Include a quick trust scan of the current directory')
|
|
146
|
+
.option('--weekly', 'Show shareable weekly agent-activity recap (last 7 days)')
|
|
147
|
+
.option('--export <format>', 'Export raw audit events: jsonl, json, csv, or md')
|
|
148
|
+
.option('--since <duration>', 'Limit --export window, e.g. 24h, 7d, 4w')
|
|
952
149
|
.option('--env', 'Show environment details')
|
|
953
150
|
.option('--track', 'Show command success rates')
|
|
954
151
|
.addHelpText('after', `
|
|
955
152
|
Examples:
|
|
956
|
-
$ codehere status
|
|
957
|
-
$ codehere status --
|
|
958
|
-
$ codehere status --
|
|
959
|
-
$ codehere status --
|
|
153
|
+
$ codehere status # full dashboard
|
|
154
|
+
$ codehere status --weekly # shareable 7-day agent recap
|
|
155
|
+
$ codehere status --sessions # show per-session breakdown
|
|
156
|
+
$ codehere status --session abc12345 # drill into a specific session
|
|
157
|
+
$ codehere status --export jsonl > audit.jsonl # raw audit log export
|
|
158
|
+
$ codehere status --export csv --since 7d # 7-day CSV for spreadsheets
|
|
159
|
+
$ codehere status --export md --since 24h # 24-hour markdown report
|
|
960
160
|
`)
|
|
961
161
|
.action(async (options) => {
|
|
962
162
|
await executeStatusCommand({
|
|
@@ -965,321 +165,14 @@ Examples:
|
|
|
965
165
|
sessions: Boolean(options.sessions),
|
|
966
166
|
session: options.session,
|
|
967
167
|
trust: Boolean(options.trust),
|
|
168
|
+
weekly: Boolean(options.weekly),
|
|
169
|
+
export: options.export,
|
|
170
|
+
since: options.since,
|
|
968
171
|
env: Boolean(options.env),
|
|
969
172
|
track: Boolean(options.track),
|
|
970
173
|
});
|
|
971
174
|
});
|
|
972
|
-
//
|
|
973
|
-
program
|
|
974
|
-
.command('suggest')
|
|
975
|
-
.description('Read-only assistant mode - ask questions and get explanations without making changes')
|
|
976
|
-
.argument('<query>', 'The question or request')
|
|
977
|
-
.option('--file <file>', 'Focus on a specific file')
|
|
978
|
-
.addHelpText('after', `
|
|
979
|
-
Examples:
|
|
980
|
-
$ codehere suggest "How does authentication work?"
|
|
981
|
-
$ codehere suggest "Explain this function" --file src/auth.ts
|
|
982
|
-
|
|
983
|
-
This command:
|
|
984
|
-
• Searches your codebase for relevant code
|
|
985
|
-
• Provides contextual answers and explanations
|
|
986
|
-
• Does NOT make any changes (read-only mode)
|
|
987
|
-
• Creates a task record for tracking
|
|
988
|
-
`)
|
|
989
|
-
.action(async (query, options) => {
|
|
990
|
-
initLogFile();
|
|
991
|
-
if (!hasAnyApiKey()) {
|
|
992
|
-
requireApiKey();
|
|
993
|
-
}
|
|
994
|
-
try {
|
|
995
|
-
await executeSuggestCommand(query, {
|
|
996
|
-
file: options.file,
|
|
997
|
-
stream: false,
|
|
998
|
-
});
|
|
999
|
-
logEntry({
|
|
1000
|
-
timestamp: new Date().toISOString(),
|
|
1001
|
-
command: 'suggest',
|
|
1002
|
-
query,
|
|
1003
|
-
policyStatus: 'allowed',
|
|
1004
|
-
});
|
|
1005
|
-
}
|
|
1006
|
-
catch (err) {
|
|
1007
|
-
const errMessage = err instanceof Error ? err.message : String(err);
|
|
1008
|
-
// If it's a missing embeddings error, exit with 1 (message already shown)
|
|
1009
|
-
if (errMessage.includes('No embeddings found')) {
|
|
1010
|
-
process.exit(1);
|
|
1011
|
-
}
|
|
1012
|
-
console.error(error('Error:'), errMessage);
|
|
1013
|
-
process.exit(1);
|
|
1014
|
-
}
|
|
1015
|
-
});
|
|
1016
|
-
// Run command - Unified agentic execution
|
|
1017
|
-
program
|
|
1018
|
-
.command('run')
|
|
1019
|
-
.description('Unified agentic execution - execute a task or goal')
|
|
1020
|
-
.argument('<goal>', 'The goal to execute')
|
|
1021
|
-
.option('--task-id <id>', 'Continue execution of an existing task')
|
|
1022
|
-
.option('--file <file>', 'Focus on a specific file')
|
|
1023
|
-
.option('--files <files>', 'Comma-separated list of files to process')
|
|
1024
|
-
.option('--review', 'Enable review before applying changes')
|
|
1025
|
-
.addHelpText('after', `
|
|
1026
|
-
Examples:
|
|
1027
|
-
$ codehere run "Add error handling to the login function"
|
|
1028
|
-
$ codehere run "Refactor authentication module" --file src/auth.ts
|
|
1029
|
-
$ codehere run "Fix bug in payment processing" --task-id task-12345
|
|
1030
|
-
|
|
1031
|
-
This command:
|
|
1032
|
-
• Creates a plan for the goal
|
|
1033
|
-
• Executes the plan step-by-step
|
|
1034
|
-
• Creates/updates task records for tracking
|
|
1035
|
-
• Shows risk tier and environment mode (advisory)
|
|
1036
|
-
`)
|
|
1037
|
-
.action(async (goal, options) => {
|
|
1038
|
-
initLogFile();
|
|
1039
|
-
if (!hasAnyApiKey()) {
|
|
1040
|
-
requireApiKey();
|
|
1041
|
-
}
|
|
1042
|
-
try {
|
|
1043
|
-
const files = options.files ? options.files.split(',').map((f) => f.trim()) : undefined;
|
|
1044
|
-
await executeRunCommand(goal, {
|
|
1045
|
-
taskId: options.taskId,
|
|
1046
|
-
file: options.file,
|
|
1047
|
-
files,
|
|
1048
|
-
review: Boolean(options.review),
|
|
1049
|
-
});
|
|
1050
|
-
logEntry({
|
|
1051
|
-
timestamp: new Date().toISOString(),
|
|
1052
|
-
command: 'run',
|
|
1053
|
-
query: goal,
|
|
1054
|
-
policyStatus: 'allowed',
|
|
1055
|
-
});
|
|
1056
|
-
}
|
|
1057
|
-
catch (err) {
|
|
1058
|
-
console.error(error('Error:'), err instanceof Error ? err.message : String(err));
|
|
1059
|
-
process.exit(1);
|
|
1060
|
-
}
|
|
1061
|
-
});
|
|
1062
|
-
// Review command - Task review interface
|
|
1063
|
-
program
|
|
1064
|
-
.command('review')
|
|
1065
|
-
.description('Review and approve/reject tasks')
|
|
1066
|
-
.argument('[task-id]', 'Task ID to review (omit to list tasks)')
|
|
1067
|
-
.option('--list', 'List tasks needing review')
|
|
1068
|
-
.option('--approve', 'Approve the task')
|
|
1069
|
-
.option('--reject', 'Reject the task')
|
|
1070
|
-
.option('--comment <text>', 'Add a comment to the review')
|
|
1071
|
-
.option('--all', 'Show all completed tasks in list mode')
|
|
1072
|
-
.addHelpText('after', `
|
|
1073
|
-
Examples:
|
|
1074
|
-
$ codehere review --list
|
|
1075
|
-
$ codehere review task-12345
|
|
1076
|
-
$ codehere review task-12345 --approve
|
|
1077
|
-
$ codehere review task-12345 --reject --comment "Security concerns"
|
|
1078
|
-
|
|
1079
|
-
This command:
|
|
1080
|
-
• Lists tasks that need review
|
|
1081
|
-
• Shows task details (files changed, risk tier, etc.)
|
|
1082
|
-
• Allows approving or rejecting tasks
|
|
1083
|
-
• Updates task status and review records
|
|
1084
|
-
`)
|
|
1085
|
-
.action(async (taskId, options) => {
|
|
1086
|
-
initLogFile();
|
|
1087
|
-
try {
|
|
1088
|
-
await executeReviewCommand(taskId, {
|
|
1089
|
-
list: Boolean(options.list),
|
|
1090
|
-
approve: Boolean(options.approve),
|
|
1091
|
-
reject: Boolean(options.reject),
|
|
1092
|
-
comment: options.comment,
|
|
1093
|
-
all: Boolean(options.all),
|
|
1094
|
-
});
|
|
1095
|
-
logEntry({
|
|
1096
|
-
timestamp: new Date().toISOString(),
|
|
1097
|
-
command: 'review',
|
|
1098
|
-
query: taskId,
|
|
1099
|
-
policyStatus: 'allowed',
|
|
1100
|
-
});
|
|
1101
|
-
}
|
|
1102
|
-
catch (err) {
|
|
1103
|
-
console.error(error('Error:'), err instanceof Error ? err.message : String(err));
|
|
1104
|
-
process.exit(1);
|
|
1105
|
-
}
|
|
1106
|
-
});
|
|
1107
|
-
program
|
|
1108
|
-
.command('test')
|
|
1109
|
-
.description('Generate tests for code')
|
|
1110
|
-
.argument('<file>', 'File to generate tests for')
|
|
1111
|
-
.option('--framework <framework>', 'Test framework (jest, pytest, etc.)')
|
|
1112
|
-
.option('--coverage <type>', 'Coverage type (unit, integration, both)', 'unit')
|
|
1113
|
-
.option('--improve', 'Improve existing test file')
|
|
1114
|
-
.option('--save', 'Save tests to file')
|
|
1115
|
-
.action(async (file, options) => {
|
|
1116
|
-
initLogFile();
|
|
1117
|
-
if (!hasAnyApiKey()) {
|
|
1118
|
-
requireApiKey();
|
|
1119
|
-
}
|
|
1120
|
-
try {
|
|
1121
|
-
console.log(createBanner('Test Generator', 'Comprehensive test generation'));
|
|
1122
|
-
newline();
|
|
1123
|
-
const testSpinner = createSpinner('Generating tests...');
|
|
1124
|
-
testSpinner.start();
|
|
1125
|
-
let tests;
|
|
1126
|
-
if (options.improve) {
|
|
1127
|
-
tests = await improveTests(file);
|
|
1128
|
-
}
|
|
1129
|
-
else {
|
|
1130
|
-
tests = await generateTests(file, {
|
|
1131
|
-
framework: options.framework,
|
|
1132
|
-
coverage: options.coverage,
|
|
1133
|
-
});
|
|
1134
|
-
}
|
|
1135
|
-
testSpinner.stop();
|
|
1136
|
-
newline();
|
|
1137
|
-
sectionHeader('Generated Tests');
|
|
1138
|
-
const formattedTests = cleanResponse(tests);
|
|
1139
|
-
console.log(formattedTests);
|
|
1140
|
-
if (options.save) {
|
|
1141
|
-
const testPath = options.improve ? file : getTestFilePath(file, options.framework);
|
|
1142
|
-
saveTests(testPath, tests);
|
|
1143
|
-
console.log(success(`Tests saved to ${testPath}`));
|
|
1144
|
-
}
|
|
1145
|
-
}
|
|
1146
|
-
catch (err) {
|
|
1147
|
-
console.error(error('Error:'), err instanceof Error ? err.message : String(err));
|
|
1148
|
-
process.exit(1);
|
|
1149
|
-
}
|
|
1150
|
-
});
|
|
1151
|
-
program
|
|
1152
|
-
.command('react')
|
|
1153
|
-
.description('Execute query using ReAct loop (iterative reasoning with tools)')
|
|
1154
|
-
.argument('<query>', 'The query to process with ReAct reasoning')
|
|
1155
|
-
.addHelpText('after', `
|
|
1156
|
-
Examples:
|
|
1157
|
-
$ codehere react "How does authentication work in this codebase?"
|
|
1158
|
-
$ codehere react "Find and explain the user login function"
|
|
1159
|
-
$ codehere react "What files handle API requests?"
|
|
1160
|
-
|
|
1161
|
-
This command uses ReAct (Reasoning + Acting) pattern:
|
|
1162
|
-
• Generates thoughts about the problem
|
|
1163
|
-
• Takes actions (uses tools, searches code)
|
|
1164
|
-
• Observes results
|
|
1165
|
-
• Iterates until solution found
|
|
1166
|
-
• Learns from memory of past experiences
|
|
1167
|
-
`)
|
|
1168
|
-
.action(async (query) => {
|
|
1169
|
-
initLogFile();
|
|
1170
|
-
if (!hasAnyApiKey()) {
|
|
1171
|
-
requireApiKey();
|
|
1172
|
-
}
|
|
1173
|
-
try {
|
|
1174
|
-
await executeReactCommand(query);
|
|
1175
|
-
logEntry({
|
|
1176
|
-
timestamp: new Date().toISOString(),
|
|
1177
|
-
command: 'react',
|
|
1178
|
-
query: query,
|
|
1179
|
-
policyStatus: 'allowed',
|
|
1180
|
-
});
|
|
1181
|
-
}
|
|
1182
|
-
catch (err) {
|
|
1183
|
-
console.error(error('Error:'), err instanceof Error ? err.message : String(err));
|
|
1184
|
-
process.exit(1);
|
|
1185
|
-
}
|
|
1186
|
-
});
|
|
1187
|
-
program
|
|
1188
|
-
.command('orchestrate')
|
|
1189
|
-
.description('Orchestrate multi-agent workflows (plan, execute, validate)')
|
|
1190
|
-
.argument('<goal>', 'The goal to orchestrate')
|
|
1191
|
-
.option('-m, --mode <mode>', 'Orchestration mode: plan-only, execute-only, or full', 'full')
|
|
1192
|
-
.option('-p, --plan-id <planId>', 'Load and execute a previously saved plan by ID')
|
|
1193
|
-
.option('--review', 'Enable review & feedback capture (used for trust scoring and Validation Mode)')
|
|
1194
|
-
.action(async (goal, options) => {
|
|
1195
|
-
initLogFile();
|
|
1196
|
-
if (!hasAnyApiKey()) {
|
|
1197
|
-
requireApiKey();
|
|
1198
|
-
}
|
|
1199
|
-
try {
|
|
1200
|
-
await executeOrchestrateCommand(goal, {
|
|
1201
|
-
mode: options.mode,
|
|
1202
|
-
planId: options.planId,
|
|
1203
|
-
review: options.review,
|
|
1204
|
-
});
|
|
1205
|
-
logEntry({
|
|
1206
|
-
timestamp: new Date().toISOString(),
|
|
1207
|
-
command: 'orchestrate',
|
|
1208
|
-
query: goal,
|
|
1209
|
-
policyStatus: 'allowed',
|
|
1210
|
-
});
|
|
1211
|
-
}
|
|
1212
|
-
catch (err) {
|
|
1213
|
-
console.error(error('Error:'), err instanceof Error ? err.message : String(err));
|
|
1214
|
-
process.exit(1);
|
|
1215
|
-
}
|
|
1216
|
-
});
|
|
1217
|
-
program
|
|
1218
|
-
.command('trace')
|
|
1219
|
-
.description('View execution traces and reproduce runs')
|
|
1220
|
-
.argument('<action>', 'Action: list, show, or reproduce')
|
|
1221
|
-
.argument('[runId]', 'Run ID (required for show/reproduce)')
|
|
1222
|
-
.action(async (action, runId) => {
|
|
1223
|
-
initLogFile();
|
|
1224
|
-
try {
|
|
1225
|
-
if (action === 'list') {
|
|
1226
|
-
await executeTraceCommand('list');
|
|
1227
|
-
}
|
|
1228
|
-
else if (action === 'show') {
|
|
1229
|
-
if (!runId) {
|
|
1230
|
-
console.error(error('Run ID is required for "show" action'));
|
|
1231
|
-
console.log(info('Usage: codehere trace show <runId>'));
|
|
1232
|
-
process.exit(1);
|
|
1233
|
-
}
|
|
1234
|
-
await executeTraceCommand('show', runId);
|
|
1235
|
-
}
|
|
1236
|
-
else if (action === 'reproduce') {
|
|
1237
|
-
if (!runId) {
|
|
1238
|
-
console.error(error('Run ID is required for "reproduce" action'));
|
|
1239
|
-
console.log(info('Usage: codehere trace reproduce <runId>'));
|
|
1240
|
-
process.exit(1);
|
|
1241
|
-
}
|
|
1242
|
-
await executeTraceCommand('reproduce', runId);
|
|
1243
|
-
}
|
|
1244
|
-
else {
|
|
1245
|
-
console.error(error(`Unknown action: ${action}`));
|
|
1246
|
-
console.log(info('Available actions: list, show, reproduce'));
|
|
1247
|
-
process.exit(1);
|
|
1248
|
-
}
|
|
1249
|
-
}
|
|
1250
|
-
catch (err) {
|
|
1251
|
-
console.error(error('Error:'), err instanceof Error ? err.message : String(err));
|
|
1252
|
-
process.exit(1);
|
|
1253
|
-
}
|
|
1254
|
-
});
|
|
1255
|
-
program
|
|
1256
|
-
.command('undo')
|
|
1257
|
-
.description('Undo file changes by restoring from backups')
|
|
1258
|
-
.argument('<action>', 'Action: undo, list, restore')
|
|
1259
|
-
.argument('[filepath]', 'File path (required for undo/list)')
|
|
1260
|
-
.argument('[backupPath]', 'Backup path (required for restore)')
|
|
1261
|
-
.addHelpText('after', `
|
|
1262
|
-
Examples:
|
|
1263
|
-
$ codehere undo undo src/file.ts
|
|
1264
|
-
$ codehere undo list src/file.ts
|
|
1265
|
-
$ codehere undo restore src/file.ts .codehere/backups/file.ts.2024-12-30.backup
|
|
1266
|
-
|
|
1267
|
-
Actions:
|
|
1268
|
-
undo - Restore file from most recent backup
|
|
1269
|
-
list - List all available backups for a file
|
|
1270
|
-
restore - Restore file from a specific backup path
|
|
1271
|
-
`)
|
|
1272
|
-
.action(async (action, filepath, backupPath) => {
|
|
1273
|
-
initLogFile();
|
|
1274
|
-
try {
|
|
1275
|
-
await executeUndoCommand(action, filepath, backupPath);
|
|
1276
|
-
}
|
|
1277
|
-
catch (err) {
|
|
1278
|
-
console.error(error('Error:'), err instanceof Error ? err.message : String(err));
|
|
1279
|
-
process.exit(1);
|
|
1280
|
-
}
|
|
1281
|
-
});
|
|
1282
|
-
// ── trust ─────────────────────────────────────────────────────────────────────
|
|
175
|
+
// ── trust ────────────────────────────────────────────────────────────────────
|
|
1283
176
|
program
|
|
1284
177
|
.command('trust')
|
|
1285
178
|
.description('Layer 0 trust score — instant static analysis, zero API calls')
|
|
@@ -1304,32 +197,29 @@ program
|
|
|
1304
197
|
process.exit(1);
|
|
1305
198
|
}
|
|
1306
199
|
});
|
|
1307
|
-
// ── report
|
|
200
|
+
// ── report ───────────────────────────────────────────────────────────────────
|
|
1308
201
|
program
|
|
1309
202
|
.command('report')
|
|
1310
203
|
.description('Generate a shareable security audit report — narrative, not just a score')
|
|
1311
204
|
.argument('[target]', 'File or directory to audit (default: current directory)')
|
|
1312
|
-
.option('--out <file>', 'Write report to a file (default:
|
|
205
|
+
.option('--out <file>', 'Write report to a specific file (default: codehere-audit.md)')
|
|
206
|
+
.option('--stdout', 'Print the report to stdout instead of writing a file')
|
|
1313
207
|
.option('--json', 'Output machine-readable JSON instead of markdown')
|
|
1314
208
|
.option('--simple', 'Plain English output — no jargon, for non-technical stakeholders')
|
|
1315
209
|
.addHelpText('after', `
|
|
1316
210
|
Examples:
|
|
1317
|
-
$ codehere report # audit
|
|
211
|
+
$ codehere report # writes codehere-audit.md, prints summary
|
|
1318
212
|
$ codehere report src/ # audit a subdirectory
|
|
1319
|
-
$ codehere report --out
|
|
1320
|
-
$ codehere report
|
|
213
|
+
$ codehere report --out my-audit.md # write to a specific path
|
|
214
|
+
$ codehere report --stdout # print to terminal (pipe-friendly)
|
|
1321
215
|
$ codehere report --json # machine-readable output
|
|
1322
216
|
$ codehere report --simple # plain English for non-technical stakeholders
|
|
1323
|
-
|
|
1324
|
-
The report is a markdown document you can:
|
|
1325
|
-
• Paste directly into a PR description
|
|
1326
|
-
• Commit as a security audit artifact
|
|
1327
|
-
• Include the badge in your README
|
|
1328
217
|
`)
|
|
1329
218
|
.action(async (target, options) => {
|
|
1330
219
|
try {
|
|
1331
220
|
await executeReportCommand(target, {
|
|
1332
221
|
out: options?.out,
|
|
222
|
+
stdout: options?.stdout,
|
|
1333
223
|
json: options?.json,
|
|
1334
224
|
simple: options?.simple,
|
|
1335
225
|
});
|
|
@@ -1339,7 +229,7 @@ The report is a markdown document you can:
|
|
|
1339
229
|
process.exit(1);
|
|
1340
230
|
}
|
|
1341
231
|
});
|
|
1342
|
-
// ── hook
|
|
232
|
+
// ── hook ─────────────────────────────────────────────────────────────────────
|
|
1343
233
|
program
|
|
1344
234
|
.command('hook')
|
|
1345
235
|
.description('Manage Claude Agent SDK hook integration (intercepts all tool calls)')
|
|
@@ -1375,7 +265,7 @@ program
|
|
|
1375
265
|
const host = options?.host ?? '127.0.0.1';
|
|
1376
266
|
const token = options?.token;
|
|
1377
267
|
if (options?.showConfig) {
|
|
1378
|
-
const
|
|
268
|
+
const configBlock = {
|
|
1379
269
|
mcpServers: {
|
|
1380
270
|
codehere: {
|
|
1381
271
|
url: `http://${host}:${port}/mcp`,
|
|
@@ -1384,7 +274,7 @@ program
|
|
|
1384
274
|
},
|
|
1385
275
|
};
|
|
1386
276
|
console.log('\nAdd to Claude Desktop or Cursor MCP settings:\n');
|
|
1387
|
-
console.log(JSON.stringify(
|
|
277
|
+
console.log(JSON.stringify(configBlock, null, 2));
|
|
1388
278
|
newline();
|
|
1389
279
|
console.log(colors.dim('Claude Desktop: ~/Library/Application Support/Claude/claude_desktop_config.json'));
|
|
1390
280
|
console.log(colors.dim('Cursor: Settings > MCP Servers'));
|
|
@@ -1409,7 +299,6 @@ program
|
|
|
1409
299
|
console.log(colors.dim(' Run with --show-config to get the MCP client config block'));
|
|
1410
300
|
console.log(colors.dim(' Press Ctrl+C to stop'));
|
|
1411
301
|
newline();
|
|
1412
|
-
// Keep alive
|
|
1413
302
|
process.on('SIGINT', () => {
|
|
1414
303
|
server.close();
|
|
1415
304
|
console.log(colors.dim('\ncodehere MCP server stopped'));
|
|
@@ -1424,7 +313,201 @@ program
|
|
|
1424
313
|
process.exit(1);
|
|
1425
314
|
}
|
|
1426
315
|
});
|
|
1427
|
-
// ──
|
|
316
|
+
// ── start (Mission Control: daemon + web UI) ─────────────────────────────────
|
|
317
|
+
program
|
|
318
|
+
.command('start')
|
|
319
|
+
.description('Start the codehere daemon — task intake UI + MCP server at localhost:3773')
|
|
320
|
+
.option('-p, --port <number>', 'Port to listen on', '3773')
|
|
321
|
+
.option('--host <host>', 'Host to bind to', '127.0.0.1')
|
|
322
|
+
.option('--no-open', "Don't auto-open the browser")
|
|
323
|
+
.action(async (options) => {
|
|
324
|
+
const port = parseInt(options?.port ?? '3773', 10);
|
|
325
|
+
const host = options?.host ?? '127.0.0.1';
|
|
326
|
+
const shouldOpen = options?.open !== false;
|
|
327
|
+
const url = `http://${host}:${port}/`;
|
|
328
|
+
const openBrowser = async () => {
|
|
329
|
+
if (!shouldOpen)
|
|
330
|
+
return;
|
|
331
|
+
const { spawn } = await import('child_process');
|
|
332
|
+
const opener = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'start' : 'xdg-open';
|
|
333
|
+
try {
|
|
334
|
+
spawn(opener, [url], { detached: true, stdio: 'ignore' }).unref();
|
|
335
|
+
}
|
|
336
|
+
catch {
|
|
337
|
+
/* browser open is best-effort */
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
try {
|
|
341
|
+
const { startMCPServer } = await import('./infrastructure/protocols/mcp-server-impl.js');
|
|
342
|
+
const server = startMCPServer({ port, host });
|
|
343
|
+
// Wait for the server to either start listening or fail. The default
|
|
344
|
+
// Node behavior on listen() failure is an unhandled 'error' event which
|
|
345
|
+
// crashes the process with a stack trace. We catch it here so the user
|
|
346
|
+
// sees a friendly message instead.
|
|
347
|
+
await new Promise((resolve, reject) => {
|
|
348
|
+
server.once('listening', () => resolve());
|
|
349
|
+
server.once('error', reject);
|
|
350
|
+
});
|
|
351
|
+
newline();
|
|
352
|
+
console.log(success('codehere is running'));
|
|
353
|
+
newline();
|
|
354
|
+
console.log(colors.bold('Tasks:') + ' ' + url);
|
|
355
|
+
console.log(colors.bold('History:') + ' ' + `http://${host}:${port}/history`);
|
|
356
|
+
console.log(colors.bold('MCP:') + ' ' + `http://${host}:${port}/mcp`);
|
|
357
|
+
newline();
|
|
358
|
+
console.log(colors.dim(' Press Ctrl+C to stop'));
|
|
359
|
+
newline();
|
|
360
|
+
await openBrowser();
|
|
361
|
+
process.on('SIGINT', () => {
|
|
362
|
+
server.close();
|
|
363
|
+
console.log(colors.dim('\ncodehere stopped'));
|
|
364
|
+
process.exit(0);
|
|
365
|
+
});
|
|
366
|
+
await new Promise((resolve) => {
|
|
367
|
+
server.on('close', resolve);
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
catch (err) {
|
|
371
|
+
const code = err?.code;
|
|
372
|
+
if (code === 'EADDRINUSE') {
|
|
373
|
+
// Port is taken. Probe to find out by what — if it's a running codehere,
|
|
374
|
+
// we should hand the user the existing URL; if it's something else,
|
|
375
|
+
// tell them to pick a different port.
|
|
376
|
+
const { isCodehereOnPort } = await import('./infrastructure/protocols/probe-port.js');
|
|
377
|
+
const existing = await isCodehereOnPort(host, port);
|
|
378
|
+
newline();
|
|
379
|
+
if (existing.isCodehere) {
|
|
380
|
+
console.log(success(`codehere is already running at ${url}`));
|
|
381
|
+
if (existing.version) {
|
|
382
|
+
console.log(colors.dim(` version: ${existing.version}`));
|
|
383
|
+
}
|
|
384
|
+
newline();
|
|
385
|
+
console.log(colors.dim(' Opening browser...'));
|
|
386
|
+
await openBrowser();
|
|
387
|
+
process.exit(0);
|
|
388
|
+
}
|
|
389
|
+
console.error(error(`Port ${port} is in use by another process.`));
|
|
390
|
+
console.error(colors.dim(` Try: codehere start --port ${port + 1}`));
|
|
391
|
+
process.exit(1);
|
|
392
|
+
}
|
|
393
|
+
console.error(error('Failed to start:'), err instanceof Error ? err.message : String(err));
|
|
394
|
+
process.exit(1);
|
|
395
|
+
}
|
|
396
|
+
});
|
|
397
|
+
// ── task (spawn an agent session via CLI) ────────────────────────────────────
|
|
398
|
+
program
|
|
399
|
+
.command('task <prompt...>')
|
|
400
|
+
.description('Assign a task — spawns the default agent under codehere. Requires `codehere start` running.')
|
|
401
|
+
.option('-p, --port <number>', 'Daemon port', '3773')
|
|
402
|
+
.option('--host <host>', 'Daemon host', '127.0.0.1')
|
|
403
|
+
.option('--watch', 'Stream events to the terminal (default: just returns the session id)')
|
|
404
|
+
.action(async (promptParts, options) => {
|
|
405
|
+
const prompt = promptParts.join(' ').trim();
|
|
406
|
+
if (!prompt) {
|
|
407
|
+
console.error(error('No prompt. Usage: codehere task "fix auth bug in session.ts"'));
|
|
408
|
+
process.exit(1);
|
|
409
|
+
}
|
|
410
|
+
const port = parseInt(options?.port ?? '3773', 10);
|
|
411
|
+
const host = options?.host ?? '127.0.0.1';
|
|
412
|
+
const base = `http://${host}:${port}`;
|
|
413
|
+
const http = await import('http');
|
|
414
|
+
const spawnResult = await new Promise((resolve) => {
|
|
415
|
+
const body = JSON.stringify({ prompt });
|
|
416
|
+
const r = http.request(`${base}/api/task`, {
|
|
417
|
+
method: 'POST',
|
|
418
|
+
headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body) },
|
|
419
|
+
}, (res) => {
|
|
420
|
+
let buf = '';
|
|
421
|
+
res.on('data', (c) => {
|
|
422
|
+
buf += c.toString();
|
|
423
|
+
});
|
|
424
|
+
res.on('end', () => {
|
|
425
|
+
try {
|
|
426
|
+
resolve(JSON.parse(buf));
|
|
427
|
+
}
|
|
428
|
+
catch {
|
|
429
|
+
resolve(null);
|
|
430
|
+
}
|
|
431
|
+
});
|
|
432
|
+
});
|
|
433
|
+
r.on('error', () => resolve(null));
|
|
434
|
+
r.write(body);
|
|
435
|
+
r.end();
|
|
436
|
+
});
|
|
437
|
+
if (!spawnResult || !spawnResult.sessionId) {
|
|
438
|
+
console.error(error(`Could not reach daemon at ${base}. Start it with: codehere start`));
|
|
439
|
+
process.exit(1);
|
|
440
|
+
}
|
|
441
|
+
console.log(success(`Task assigned · session ${spawnResult.sessionId}`));
|
|
442
|
+
console.log(colors.dim(` Watch: ${base}/ · Kill: codehere kill ${spawnResult.sessionId}`));
|
|
443
|
+
if (!options?.watch)
|
|
444
|
+
return;
|
|
445
|
+
const streamUrl = `${base}/api/task/${spawnResult.sessionId}/events`;
|
|
446
|
+
await new Promise((resolve) => {
|
|
447
|
+
const r = http.get(streamUrl, (res) => {
|
|
448
|
+
let buf = '';
|
|
449
|
+
res.on('data', (c) => {
|
|
450
|
+
buf += c.toString();
|
|
451
|
+
let i;
|
|
452
|
+
while ((i = buf.indexOf('\n\n')) >= 0) {
|
|
453
|
+
const frame = buf.slice(0, i).trim();
|
|
454
|
+
buf = buf.slice(i + 2);
|
|
455
|
+
if (!frame.startsWith('data:'))
|
|
456
|
+
continue;
|
|
457
|
+
const payload = frame.slice(5).trim();
|
|
458
|
+
try {
|
|
459
|
+
const event = JSON.parse(payload);
|
|
460
|
+
const type = String(event.type || 'message');
|
|
461
|
+
if (type === 'exit') {
|
|
462
|
+
console.log(colors.dim(` [exit] ${String(event.data?.status ?? '?')}`));
|
|
463
|
+
res.destroy();
|
|
464
|
+
resolve();
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
console.log(colors.dim(` [${type}]`));
|
|
468
|
+
}
|
|
469
|
+
catch {
|
|
470
|
+
/* skip */
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
});
|
|
474
|
+
res.on('end', () => resolve());
|
|
475
|
+
});
|
|
476
|
+
r.on('error', () => resolve());
|
|
477
|
+
});
|
|
478
|
+
});
|
|
479
|
+
// ── kill (stop a running task) ───────────────────────────────────────────────
|
|
480
|
+
program
|
|
481
|
+
.command('kill <sessionId>')
|
|
482
|
+
.description('Kill a running task session.')
|
|
483
|
+
.option('-p, --port <number>', 'Daemon port', '3773')
|
|
484
|
+
.option('--host <host>', 'Daemon host', '127.0.0.1')
|
|
485
|
+
.action(async (sessionId, options) => {
|
|
486
|
+
const port = parseInt(options?.port ?? '3773', 10);
|
|
487
|
+
const host = options?.host ?? '127.0.0.1';
|
|
488
|
+
const http = await import('http');
|
|
489
|
+
await new Promise((resolve) => {
|
|
490
|
+
const r = http.request(`http://${host}:${port}/api/task/${sessionId}/kill`, { method: 'POST' }, (res) => {
|
|
491
|
+
let buf = '';
|
|
492
|
+
res.on('data', (c) => {
|
|
493
|
+
buf += c.toString();
|
|
494
|
+
});
|
|
495
|
+
res.on('end', () => {
|
|
496
|
+
if (res.statusCode === 200)
|
|
497
|
+
console.log(success(`Killed ${sessionId}`));
|
|
498
|
+
else
|
|
499
|
+
console.error(error(`Failed (${res.statusCode}): ${buf}`));
|
|
500
|
+
resolve();
|
|
501
|
+
});
|
|
502
|
+
});
|
|
503
|
+
r.on('error', (err) => {
|
|
504
|
+
console.error(error(err.message));
|
|
505
|
+
resolve();
|
|
506
|
+
});
|
|
507
|
+
r.end();
|
|
508
|
+
});
|
|
509
|
+
});
|
|
510
|
+
// ── scan-tool (called by Claude Code hooks system, not users directly) ───────
|
|
1428
511
|
program
|
|
1429
512
|
.command('scan-tool')
|
|
1430
513
|
.description('Hook handler: reads Claude Code tool event from stdin, outputs block/allow (used by hook system)')
|
|
@@ -1438,7 +521,7 @@ program
|
|
|
1438
521
|
debug: options?.debug,
|
|
1439
522
|
});
|
|
1440
523
|
});
|
|
1441
|
-
//
|
|
524
|
+
// ── completion (shell completion script generator) ──────────────────────────
|
|
1442
525
|
program
|
|
1443
526
|
.command('completion')
|
|
1444
527
|
.description('Generate shell completion script')
|
|
@@ -1469,61 +552,15 @@ program
|
|
|
1469
552
|
}
|
|
1470
553
|
}
|
|
1471
554
|
catch (err) {
|
|
1472
|
-
displayError(err, {
|
|
1473
|
-
operation: 'completion',
|
|
1474
|
-
showSuggestions: true,
|
|
1475
|
-
});
|
|
1476
|
-
process.exit(1);
|
|
1477
|
-
}
|
|
1478
|
-
});
|
|
1479
|
-
program
|
|
1480
|
-
.command('cost')
|
|
1481
|
-
.description('View API cost tracking and budget status')
|
|
1482
|
-
.argument('[action]', 'Action: status, summary, reset (default: status)')
|
|
1483
|
-
.argument('[run-id]', 'Run ID for detailed breakdown')
|
|
1484
|
-
.action(async (action, runId) => {
|
|
1485
|
-
try {
|
|
1486
|
-
await executeCostCommand(action || 'status', runId);
|
|
1487
|
-
}
|
|
1488
|
-
catch (err) {
|
|
1489
|
-
displayError(err, { operation: 'cost', showSuggestions: true });
|
|
555
|
+
displayError(err, { operation: 'completion', showSuggestions: true });
|
|
1490
556
|
process.exit(1);
|
|
1491
557
|
}
|
|
1492
558
|
});
|
|
1493
|
-
//
|
|
1494
|
-
const telemetry = getTelemetryService();
|
|
1495
|
-
// Track command usage (if enabled)
|
|
1496
|
-
const commandName = process.argv[2] || 'default';
|
|
1497
|
-
if (commandName !== 'telemetry') {
|
|
1498
|
-
telemetry.trackCommand(commandName);
|
|
1499
|
-
}
|
|
1500
|
-
// Check for first run (install tracking)
|
|
1501
|
-
const codebaseRoot = process.cwd();
|
|
1502
|
-
const codehereDir = join(codebaseRoot, '.codehere');
|
|
1503
|
-
if (!existsSync(codehereDir)) {
|
|
1504
|
-
// First run - track install
|
|
1505
|
-
telemetry.track('install', {
|
|
1506
|
-
version: process.env.npm_package_version || 'unknown',
|
|
1507
|
-
os: process.platform,
|
|
1508
|
-
nodeVersion: process.version,
|
|
1509
|
-
});
|
|
1510
|
-
}
|
|
1511
|
-
// Slash command routing: `codehere /ask "..."` → `codehere ask "..."`
|
|
1512
|
-
// Hide non-core commands from default --help (still usable, shown via `help --all`)
|
|
1513
|
-
const CORE_COMMANDS = new Set([
|
|
1514
|
-
'setup', 'index', 'ask', 'fix', 'explain', 'health',
|
|
1515
|
-
'status', 'plan', 'trust', 'report', 'hook', 'serve', 'help',
|
|
1516
|
-
]);
|
|
1517
|
-
for (const cmd of program.commands) {
|
|
1518
|
-
if (!CORE_COMMANDS.has(cmd.name())) {
|
|
1519
|
-
cmd._hidden = true;
|
|
1520
|
-
}
|
|
1521
|
-
}
|
|
1522
|
-
// Lets users use slash-prefixed commands from the terminal (like Claude Code's /commands)
|
|
559
|
+
// ── slash-command routing: `codehere /trust .` → `codehere trust .` ─────────
|
|
1523
560
|
const rawArgs = process.argv.slice(2);
|
|
1524
561
|
if (rawArgs.length > 0 && rawArgs[0].startsWith('/') && rawArgs[0].length > 1) {
|
|
1525
|
-
const stripped = rawArgs[0].slice(1);
|
|
1526
|
-
process.argv.splice(2, 1, stripped);
|
|
562
|
+
const stripped = rawArgs[0].slice(1);
|
|
563
|
+
process.argv.splice(2, 1, stripped);
|
|
1527
564
|
}
|
|
1528
565
|
program.parse();
|
|
1529
566
|
//# sourceMappingURL=index.js.map
|