codehere 0.2.2 → 0.3.0-rc.1

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.
Files changed (235) hide show
  1. package/README.md +88 -68
  2. package/dist/domain/entities/task.js +4 -4
  3. package/dist/error-handler.js +4 -9
  4. package/dist/index.js +253 -1219
  5. package/dist/infrastructure/config/product-context.js +7 -6
  6. package/dist/infrastructure/hooks/claude-sdk-hook.js +335 -63
  7. package/dist/infrastructure/observability/execution-tracer.js +11 -6
  8. package/dist/infrastructure/orchestration/agent-adapter.js +13 -0
  9. package/dist/infrastructure/orchestration/agent-installer.js +101 -0
  10. package/dist/infrastructure/orchestration/agents-config-seed.js +56 -0
  11. package/dist/infrastructure/orchestration/aider-adapter.js +319 -0
  12. package/dist/infrastructure/orchestration/artifacts-extractor.js +175 -0
  13. package/dist/infrastructure/orchestration/claude-code-adapter.js +352 -0
  14. package/dist/infrastructure/orchestration/codex-adapter.js +310 -0
  15. package/dist/infrastructure/orchestration/generic-cli-adapter.js +553 -0
  16. package/dist/infrastructure/orchestration/opencode-adapter.js +275 -0
  17. package/dist/infrastructure/orchestration/progress-aggregator.js +189 -0
  18. package/dist/infrastructure/orchestration/readiness.js +106 -0
  19. package/dist/infrastructure/orchestration/session-manager.js +272 -0
  20. package/dist/infrastructure/orchestration/session-persistence.js +109 -0
  21. package/dist/infrastructure/protocols/mcp-helpers.js +10 -30
  22. package/dist/infrastructure/protocols/mcp-server-impl.js +359 -21
  23. package/dist/infrastructure/protocols/ui-page.js +1827 -0
  24. package/dist/infrastructure/security/file-collection.js +115 -0
  25. package/dist/infrastructure/security/trust-scorer.js +301 -56
  26. package/dist/infrastructure/storage/task-repository.js +8 -7
  27. package/dist/monitoring.js +9 -38
  28. package/dist/presentation/cli/commands/help-command.js +96 -139
  29. package/dist/presentation/cli/commands/hook-command.js +17 -24
  30. package/dist/presentation/cli/commands/report-command.js +59 -72
  31. package/dist/presentation/cli/commands/setup-command.js +4 -4
  32. package/dist/presentation/cli/commands/status-command.js +321 -30
  33. package/dist/presentation/cli/commands/trust-command.js +84 -72
  34. package/dist/presentation/cli/error-display.js +17 -21
  35. package/dist/ui.js +11 -11
  36. package/package.json +24 -62
  37. package/dist/agents/base.js +0 -101
  38. package/dist/agents/orchestrator-model-driven.js +0 -141
  39. package/dist/agents/react-orchestrator-agent.js +0 -54
  40. package/dist/application/agents/execution-agent.js +0 -508
  41. package/dist/application/agents/multi-agent-orchestrator.js +0 -295
  42. package/dist/application/agents/planning-agent.js +0 -433
  43. package/dist/application/agents/validation-agent.js +0 -317
  44. package/dist/application/architectures/plan-and-act.js +0 -242
  45. package/dist/application/index.js +0 -12
  46. package/dist/application/services/agent-factory.js +0 -28
  47. package/dist/application/services/dependency-container.js +0 -596
  48. package/dist/application/services/git-context-service.js +0 -83
  49. package/dist/application/services/intelligent-router.js +0 -934
  50. package/dist/application/services/tool-executor-service.js +0 -94
  51. package/dist/application/use-cases/ask-question-use-case.js +0 -338
  52. package/dist/application/use-cases/edit-file-use-case.js +0 -830
  53. package/dist/application/use-cases/explain-file-use-case.js +0 -50
  54. package/dist/application/use-cases/index-codebase-use-case.js +0 -113
  55. package/dist/application/use-cases/planning-use-case.js +0 -276
  56. package/dist/application/use-cases/react-orchestration-use-case.js +0 -329
  57. package/dist/application/use-cases/reflexion-use-case.js +0 -30
  58. package/dist/application/workflows/deterministic-workflows.js +0 -189
  59. package/dist/ast-parser.js +0 -331
  60. package/dist/cache.js +0 -180
  61. package/dist/chat.js +0 -251
  62. package/dist/code-analysis.js +0 -327
  63. package/dist/context.js +0 -187
  64. package/dist/docs.js +0 -146
  65. package/dist/domain/entities/code-chunk.js +0 -6
  66. package/dist/domain/entities/decision.js +0 -9
  67. package/dist/domain/entities/edit.js +0 -6
  68. package/dist/domain/entities/knowledge-node.js +0 -9
  69. package/dist/domain/entities/knowledge-preservation.js +0 -10
  70. package/dist/domain/entities/memory.js +0 -6
  71. package/dist/domain/entities/pattern.js +0 -7
  72. package/dist/domain/entities/plan.js +0 -6
  73. package/dist/domain/entities/query.js +0 -6
  74. package/dist/domain/errors/embeddings-not-found-error.js +0 -13
  75. package/dist/domain/index.js +0 -19
  76. package/dist/domain/interfaces/ai-service.interface.js +0 -6
  77. package/dist/domain/interfaces/code-analyzer.interface.js +0 -6
  78. package/dist/domain/interfaces/decision-repository.interface.js +0 -9
  79. package/dist/domain/interfaces/embedding-repository.interface.js +0 -6
  80. package/dist/domain/interfaces/knowledge-graph-repository.interface.js +0 -9
  81. package/dist/domain/interfaces/knowledge-preservation-repository.interface.js +0 -8
  82. package/dist/domain/interfaces/memory-repository.interface.js +0 -6
  83. package/dist/domain/interfaces/pattern-repository.interface.js +0 -6
  84. package/dist/domain/interfaces/verification-service.interface.js +0 -6
  85. package/dist/domain/services/cross-team-pattern-sharing.js +0 -125
  86. package/dist/domain/services/decision-recorder.js +0 -223
  87. package/dist/domain/services/decision-retriever.js +0 -133
  88. package/dist/domain/services/environment-mode-manager.js +0 -22
  89. package/dist/domain/services/graded-memory-activation.js +0 -69
  90. package/dist/domain/services/knowledge-access.js +0 -132
  91. package/dist/domain/services/knowledge-extractor.js +0 -247
  92. package/dist/domain/services/knowledge-graph-builder.js +0 -268
  93. package/dist/domain/services/knowledge-graph-query.js +0 -187
  94. package/dist/domain/services/memory-retriever.js +0 -82
  95. package/dist/domain/services/memory-synthesizer.js +0 -91
  96. package/dist/domain/services/pattern-extractor.js +0 -442
  97. package/dist/domain/services/query-intent-classifier.js +0 -140
  98. package/dist/domain/services/react-loop.js +0 -186
  99. package/dist/domain/services/risk-tier-detector.js +0 -78
  100. package/dist/edit.js +0 -319
  101. package/dist/embed.js +0 -459
  102. package/dist/formatter.js +0 -168
  103. package/dist/git.js +0 -198
  104. package/dist/infrastructure/ai/cohere-ai-service.js +0 -607
  105. package/dist/infrastructure/ai/command-r-evaluation.js +0 -206
  106. package/dist/infrastructure/ai/model-config.js +0 -84
  107. package/dist/infrastructure/ai/provider-ai-service.js +0 -477
  108. package/dist/infrastructure/cache/embedding-cache.js +0 -175
  109. package/dist/infrastructure/cache/query-result-cache.js +0 -66
  110. package/dist/infrastructure/cache/response-cache.js +0 -48
  111. package/dist/infrastructure/cache/security-scan-cache.js +0 -47
  112. package/dist/infrastructure/chunking/ast-code-chunker.js +0 -19
  113. package/dist/infrastructure/chunking/code-aware-chunker.js +0 -397
  114. package/dist/infrastructure/context/context-compressor.js +0 -329
  115. package/dist/infrastructure/context/conversation-context.js +0 -128
  116. package/dist/infrastructure/context/session-manager.js +0 -94
  117. package/dist/infrastructure/cost/cost-tracker.js +0 -360
  118. package/dist/infrastructure/file-scanner/file-scanner.js +0 -106
  119. package/dist/infrastructure/governance/nist-ai-rmf.js +0 -292
  120. package/dist/infrastructure/governance/policy-as-code.js +0 -258
  121. package/dist/infrastructure/governance/prompt-to-code-lineage.js +0 -239
  122. package/dist/infrastructure/index.js +0 -7
  123. package/dist/infrastructure/observability/prompt-versioner.js +0 -145
  124. package/dist/infrastructure/protocols/a2a-metadata.js +0 -137
  125. package/dist/infrastructure/protocols/a2a-protocol.js +0 -314
  126. package/dist/infrastructure/protocols/mcp-oauth.js +0 -184
  127. package/dist/infrastructure/protocols/mcp-server.js +0 -333
  128. package/dist/infrastructure/protocols/streaming-handler.js +0 -103
  129. package/dist/infrastructure/providers/cohere-provider.js +0 -444
  130. package/dist/infrastructure/providers/fake-provider.js +0 -130
  131. package/dist/infrastructure/providers/local-llm-provider.js +0 -330
  132. package/dist/infrastructure/providers/model-config.types.js +0 -9
  133. package/dist/infrastructure/providers/model-provider.interface.js +0 -8
  134. package/dist/infrastructure/providers/openai-provider.js +0 -413
  135. package/dist/infrastructure/providers/openrouter-provider.js +0 -399
  136. package/dist/infrastructure/providers/operation-provider-resolver.js +0 -105
  137. package/dist/infrastructure/providers/provider-config-loader.js +0 -120
  138. package/dist/infrastructure/providers/provider-registry.js +0 -88
  139. package/dist/infrastructure/safety/pattern-detector.js +0 -133
  140. package/dist/infrastructure/security/ai-sast-scanner.js +0 -290
  141. package/dist/infrastructure/security/docker-sandbox.js +0 -178
  142. package/dist/infrastructure/security/enhanced-security-gate.js +0 -151
  143. package/dist/infrastructure/security/license-scanner.js +0 -167
  144. package/dist/infrastructure/security/provider-bias-detector.js +0 -164
  145. package/dist/infrastructure/security/sandbox-executor.js +0 -64
  146. package/dist/infrastructure/serialization/toon-serializer.js +0 -119
  147. package/dist/infrastructure/storage/memory-stream-service.js +0 -204
  148. package/dist/infrastructure/storage/plan-repository.js +0 -96
  149. package/dist/infrastructure/storage/sqlite-decision-repository.js +0 -232
  150. package/dist/infrastructure/storage/sqlite-embedding-repository.js +0 -311
  151. package/dist/infrastructure/storage/sqlite-knowledge-graph-repository.js +0 -417
  152. package/dist/infrastructure/storage/sqlite-knowledge-preservation-repository.js +0 -230
  153. package/dist/infrastructure/storage/sqlite-memory-repository.js +0 -229
  154. package/dist/infrastructure/storage/sqlite-pattern-repository.js +0 -249
  155. package/dist/infrastructure/storage/task-helpers.js +0 -197
  156. package/dist/infrastructure/telemetry/telemetry-service.js +0 -167
  157. package/dist/infrastructure/ux/appropriate-friction.js +0 -213
  158. package/dist/infrastructure/ux/bi-directional-learning.js +0 -337
  159. package/dist/infrastructure/ux/capability-boundaries.js +0 -42
  160. package/dist/infrastructure/ux/comprehensive-formatter.js +0 -117
  161. package/dist/infrastructure/ux/contextual-feature-discovery.js +0 -10
  162. package/dist/infrastructure/ux/expectation-management.js +0 -327
  163. package/dist/infrastructure/ux/feature-discovery.js +0 -14
  164. package/dist/infrastructure/ux/feedback-prompt.js +0 -45
  165. package/dist/infrastructure/ux/hitl-validation.js +0 -297
  166. package/dist/infrastructure/ux/progress-indicator.js +0 -116
  167. package/dist/infrastructure/ux/progressive-disclosure.js +0 -236
  168. package/dist/infrastructure/ux/review-checkpoint.js +0 -72
  169. package/dist/infrastructure/ux/staged-feature-intro.js +0 -24
  170. package/dist/infrastructure/ux/stream-markdown.js +0 -151
  171. package/dist/infrastructure/ux/syntax-highlighter.js +0 -91
  172. package/dist/infrastructure/ux/team-standards.js +0 -196
  173. package/dist/infrastructure/ux/transparency.js +0 -71
  174. package/dist/infrastructure/validation/agent-feedback.js +0 -55
  175. package/dist/infrastructure/validation/agent-validation-helper.js +0 -137
  176. package/dist/infrastructure/validation/review-handler.js +0 -40
  177. package/dist/infrastructure/validation/summary-aggregator.js +0 -70
  178. package/dist/infrastructure/validation/summary-extractor.js +0 -45
  179. package/dist/infrastructure/validation/trace-summary.js +0 -52
  180. package/dist/infrastructure/verification/verification-service.js +0 -47
  181. package/dist/infrastructure/xai/attention-visualizer.js +0 -172
  182. package/dist/infrastructure/xai/cot-visualizer.js +0 -283
  183. package/dist/infrastructure/xai/decision-tree-log.js +0 -212
  184. package/dist/infrastructure/xai/interactive-xai.js +0 -260
  185. package/dist/infrastructure/xai/uncertainty-quantifier.js +0 -198
  186. package/dist/intelligent-retrieval.js +0 -272
  187. package/dist/intent/classifier.js +0 -2
  188. package/dist/intent/context.js +0 -2
  189. package/dist/iterative-refinement.js +0 -189
  190. package/dist/learning.js +0 -232
  191. package/dist/log.js +0 -31
  192. package/dist/migrate.js +0 -133
  193. package/dist/parallel-processor.js +0 -534
  194. package/dist/planner.js +0 -198
  195. package/dist/policy.js +0 -26
  196. package/dist/presentation/cli/commands/ask-command.js +0 -314
  197. package/dist/presentation/cli/commands/cost-command.js +0 -191
  198. package/dist/presentation/cli/commands/decision-command.js +0 -207
  199. package/dist/presentation/cli/commands/docs-command.js +0 -25
  200. package/dist/presentation/cli/commands/fix-command.js +0 -53
  201. package/dist/presentation/cli/commands/knowledge-command.js +0 -129
  202. package/dist/presentation/cli/commands/learning-dashboard-command.js +0 -183
  203. package/dist/presentation/cli/commands/models-command.js +0 -194
  204. package/dist/presentation/cli/commands/orchestrate-command.js +0 -207
  205. package/dist/presentation/cli/commands/plan-command.js +0 -246
  206. package/dist/presentation/cli/commands/product-command.js +0 -199
  207. package/dist/presentation/cli/commands/react-command.js +0 -181
  208. package/dist/presentation/cli/commands/review-command.js +0 -154
  209. package/dist/presentation/cli/commands/run-command.js +0 -188
  210. package/dist/presentation/cli/commands/smart-command.js +0 -395
  211. package/dist/presentation/cli/commands/suggest-command.js +0 -138
  212. package/dist/presentation/cli/commands/trace-command.js +0 -129
  213. package/dist/presentation/cli/commands/undo-command.js +0 -150
  214. package/dist/query-optimizer.js +0 -205
  215. package/dist/refactor.js +0 -116
  216. package/dist/review.js +0 -206
  217. package/dist/search.js +0 -163
  218. package/dist/session.js +0 -704
  219. package/dist/task-verification.js +0 -336
  220. package/dist/testgen.js +0 -166
  221. package/dist/token-optimizer.js +0 -277
  222. package/dist/tools/bash.js +0 -213
  223. package/dist/tools/edit.js +0 -235
  224. package/dist/tools/executor.js +0 -153
  225. package/dist/tools/git-tool.js +0 -292
  226. package/dist/tools/parser.js +0 -158
  227. package/dist/tools/registry.js +0 -302
  228. package/dist/tools/search.js +0 -100
  229. package/dist/types.js +0 -2
  230. package/dist/utils/check-api-status.js +0 -100
  231. package/dist/utils/diff-parser.js +0 -162
  232. package/dist/verification/backup.js +0 -259
  233. package/dist/verification/compiler.js +0 -264
  234. package/dist/verification/index.js +0 -273
  235. package/dist/verification/test-runner.js +0 -379
package/dist/index.js CHANGED
@@ -1,51 +1,32 @@
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';
17
+ import { existsSync, readFileSync } from 'fs';
12
18
  import { executeSetupCommand } from './presentation/cli/commands/setup-command.js';
13
19
  import { executeConfigCommand } from './presentation/cli/commands/config-command.js';
14
20
  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
21
  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
22
  import { executeHookCommand } from './presentation/cli/commands/hook-command.js';
29
23
  import { executeTrustCommand } from './presentation/cli/commands/trust-command.js';
30
24
  import { executeReportCommand } from './presentation/cli/commands/report-command.js';
31
25
  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
26
  import { displayError } from './presentation/cli/error-display.js';
46
- import { getTelemetryService } from './infrastructure/telemetry/telemetry-service.js';
47
- import { colors, createSpinner, createBanner, createTable, success, error, warning, info, formatPath, formatNumber, sectionHeader, newline, createWelcomeMessage, isMinimalStyle, printMinimalHeader, requireApiKey, hasAnyApiKey, } from './ui.js';
48
- // Load environment variables - try agent directory first, then current directory
27
+ import { performHealthCheck, formatHealthStatus, getMetrics } from './monitoring.js';
28
+ import { colors, createBanner, createTable, createWelcomeMessage, success, error, info, sectionHeader, formatNumber, newline, isMinimalStyle, printMinimalHeader, } from './ui.js';
29
+ // Load .env — agent dir first, then cwd
49
30
  try {
50
31
  const __filename = fileURLToPath(import.meta.url);
51
32
  const __dirname = dirname(__filename);
@@ -53,97 +34,66 @@ try {
53
34
  config({ path: join(agentDir, '.env') });
54
35
  }
55
36
  catch {
56
- // Fallback to current directory
37
+ // Fallback to cwd
57
38
  }
58
- config(); // Also try current directory
59
- // Import centralized version utility
39
+ config();
60
40
  import { getVersion } from './utils/version.js';
61
41
  const program = new Command();
62
42
  program
63
43
  .name('codehere')
64
- .description('AI coding agent with built-in safety')
44
+ .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
45
  .version(getVersion())
66
- .configureHelp({
67
- helpWidth: 80,
68
- sortSubcommands: true,
69
- });
70
- // Smart routing: if query provided as argument, route intelligently
46
+ .configureHelp({ helpWidth: 80, sortSubcommands: true });
47
+ // Default action · show welcome + quick start (no REPL; v0.3 is web-UI-first)
71
48
  program
72
- .argument('[query]', 'Your query - Codehere will understand what you need')
73
- .action(async (query, options) => {
49
+ .argument('[query]', 'Reserved — the PM intake lives at http://127.0.0.1:3773 after you run `codehere start`')
50
+ .action(async (query) => {
74
51
  if (query && query.trim()) {
75
- // Direct query - use intelligent router
76
- initLogFile();
77
- if (!hasAnyApiKey()) {
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
- }
52
+ console.error(error('Direct-query routing was removed in v0.3 (D7 trim).'));
53
+ console.log(colors.dim('Use the PM intake instead:'));
54
+ console.log(' ' + colors.cyan('codehere start') + ' → web UI at http://127.0.0.1:3773');
55
+ console.log(' ' + colors.cyan(`codehere task "${query.trim().slice(0, 40)}..."`) + ' → spawn via CLI');
56
+ process.exit(1);
129
57
  }
130
- else {
131
- // Non-interactive — show quick start
58
+ if (!process.stdin.isTTY) {
132
59
  console.log(createWelcomeMessage());
60
+ return;
61
+ }
62
+ const codehereDir = join(process.env.HOME || '', '.codehere');
63
+ const settingsPath = join(process.env.HOME || '', '.claude', 'settings.json');
64
+ const hasHooks = existsSync(settingsPath) && readFileSync(settingsPath, 'utf-8').includes('codehere');
65
+ const hasAuditLog = existsSync(join(codehereDir, 'audit', 'hook-events.jsonl'));
66
+ console.log(createWelcomeMessage());
67
+ newline();
68
+ if (!hasHooks && !hasAuditLog) {
69
+ console.log(colors.bold("Welcome! Let's get you set up."));
133
70
  newline();
134
- console.log(colors.bold('Quick start:'));
71
+ console.log(' ' + colors.bold('Safety first — no API key, instant'));
72
+ console.log(colors.dim(' Every AI write gets scanned. Catches injections, hardcoded secrets,'));
73
+ console.log(colors.dim(' destructive ops, and more.'));
135
74
  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');
75
+ console.log(' → Run ' + colors.cyan('codehere hook install') + ' to activate safety');
76
+ console.log(' → Run ' + colors.cyan('codehere trust .') + ' to scan this project');
139
77
  newline();
140
- console.log(colors.bold('With an API key:'));
141
- console.log(colors.cyan(' codehere "<your query>"') + ' Ask, fix, plan — routes automatically');
78
+ console.log(' ' + colors.bold('Delegate tasks — the PM for your AI coding agents'));
79
+ console.log(colors.dim(' Pick Claude Code, Codex, Aider, or OpenCode. codehere coordinates.'));
142
80
  newline();
143
- console.log(colors.dim('Run "codehere --help" for all commands'));
81
+ console.log(' → Run ' + colors.cyan('codehere start') + ' to open the task UI');
82
+ newline();
83
+ }
84
+ else {
85
+ console.log(colors.bold('Quick start:'));
86
+ newline();
87
+ console.log(' ' + colors.cyan('codehere start') + ' Task intake UI at http://127.0.0.1:3773');
88
+ console.log(' ' + colors.cyan('codehere status') + ' Dashboard · sessions · blocked calls');
89
+ console.log(' ' + colors.cyan('codehere report') + ' Generate a shareable audit report');
90
+ console.log(' ' + colors.cyan('codehere trust .') + ' Scan this project now');
91
+ newline();
92
+ console.log(colors.dim(' Run `codehere --help` for all commands.'));
144
93
  newline();
145
94
  }
146
95
  });
96
+ // ── setup ────────────────────────────────────────────────────────────────────
147
97
  program
148
98
  .command('setup')
149
99
  .description('Interactive setup wizard for first-time users')
@@ -151,6 +101,7 @@ program
151
101
  .action(async (options) => {
152
102
  await executeSetupCommand(options.force || false);
153
103
  });
104
+ // ── config ───────────────────────────────────────────────────────────────────
154
105
  program
155
106
  .command('config')
156
107
  .description('Manage configuration and preferences')
@@ -160,62 +111,7 @@ program
160
111
  .action(async (action, key, value) => {
161
112
  await executeConfigCommand(action, key, value);
162
113
  });
163
- program
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
- });
114
+ // ── help ─────────────────────────────────────────────────────────────────────
219
115
  program
220
116
  .command('help')
221
117
  .description('Show help for commands')
@@ -223,7 +119,6 @@ program
223
119
  .option('--all', 'Show all commands (including advanced)')
224
120
  .action(async (commandName, options) => {
225
121
  if (options?.all && !commandName) {
226
- // Temporarily unhide all commands and show Commander's built-in help
227
122
  for (const cmd of program.commands) {
228
123
  cmd._hidden = false;
229
124
  }
@@ -233,273 +128,7 @@ program
233
128
  await executeHelpCommand(commandName);
234
129
  }
235
130
  });
236
- program
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
- });
131
+ // ── health ───────────────────────────────────────────────────────────────────
503
132
  program
504
133
  .command('health')
505
134
  .description('Check system health and metrics')
@@ -529,418 +158,13 @@ program
529
158
  ['Cache Hits', formatNumber(metrics.performance.cacheHits)],
530
159
  ]);
531
160
  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
161
  }
542
162
  newline();
543
163
  if (health.status === 'unhealthy') {
544
164
  process.exit(1);
545
165
  }
546
166
  });
547
- program
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
167
+ // ── status ───────────────────────────────────────────────────────────────────
944
168
  program
945
169
  .command('status')
946
170
  .description('Security dashboard — hook activity, blocked calls, cost routing')
@@ -949,14 +173,20 @@ program
949
173
  .option('--sessions', 'Show per-session audit trail (multi-agent analysis)')
950
174
  .option('--session <id>', 'Drill into a specific session (first 8+ chars of session ID)')
951
175
  .option('--trust', 'Include a quick trust scan of the current directory')
176
+ .option('--weekly', 'Show shareable weekly agent-activity recap (last 7 days)')
177
+ .option('--export <format>', 'Export raw audit events: jsonl, json, csv, or md')
178
+ .option('--since <duration>', 'Limit --export window, e.g. 24h, 7d, 4w')
952
179
  .option('--env', 'Show environment details')
953
180
  .option('--track', 'Show command success rates')
954
181
  .addHelpText('after', `
955
182
  Examples:
956
- $ codehere status # full dashboard
957
- $ codehere status --sessions # show per-session breakdown
958
- $ codehere status --session abc12345 # drill into a specific session
959
- $ codehere status --json # machine-readable output
183
+ $ codehere status # full dashboard
184
+ $ codehere status --weekly # shareable 7-day agent recap
185
+ $ codehere status --sessions # show per-session breakdown
186
+ $ codehere status --session abc12345 # drill into a specific session
187
+ $ codehere status --export jsonl > audit.jsonl # raw audit log export
188
+ $ codehere status --export csv --since 7d # 7-day CSV for spreadsheets
189
+ $ codehere status --export md --since 24h # 24-hour markdown report
960
190
  `)
961
191
  .action(async (options) => {
962
192
  await executeStatusCommand({
@@ -965,321 +195,14 @@ Examples:
965
195
  sessions: Boolean(options.sessions),
966
196
  session: options.session,
967
197
  trust: Boolean(options.trust),
198
+ weekly: Boolean(options.weekly),
199
+ export: options.export,
200
+ since: options.since,
968
201
  env: Boolean(options.env),
969
202
  track: Boolean(options.track),
970
203
  });
971
204
  });
972
- // Suggest command - Read-only assistant mode
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 ─────────────────────────────────────────────────────────────────────
205
+ // ── trust ────────────────────────────────────────────────────────────────────
1283
206
  program
1284
207
  .command('trust')
1285
208
  .description('Layer 0 trust score — instant static analysis, zero API calls')
@@ -1304,32 +227,29 @@ program
1304
227
  process.exit(1);
1305
228
  }
1306
229
  });
1307
- // ── report ────────────────────────────────────────────────────────────────────
230
+ // ── report ───────────────────────────────────────────────────────────────────
1308
231
  program
1309
232
  .command('report')
1310
233
  .description('Generate a shareable security audit report — narrative, not just a score')
1311
234
  .argument('[target]', 'File or directory to audit (default: current directory)')
1312
- .option('--out <file>', 'Write report to a file (default: print to stdout)')
235
+ .option('--out <file>', 'Write report to a specific file (default: codehere-audit.md)')
236
+ .option('--stdout', 'Print the report to stdout instead of writing a file')
1313
237
  .option('--json', 'Output machine-readable JSON instead of markdown')
1314
238
  .option('--simple', 'Plain English output — no jargon, for non-technical stakeholders')
1315
239
  .addHelpText('after', `
1316
240
  Examples:
1317
- $ codehere report # audit current dir, print markdown
241
+ $ codehere report # writes codehere-audit.md, prints summary
1318
242
  $ codehere report src/ # audit a subdirectory
1319
- $ codehere report --out report.md # write to file
1320
- $ codehere report > report.md # pipe to file
243
+ $ codehere report --out my-audit.md # write to a specific path
244
+ $ codehere report --stdout # print to terminal (pipe-friendly)
1321
245
  $ codehere report --json # machine-readable output
1322
246
  $ 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
247
  `)
1329
248
  .action(async (target, options) => {
1330
249
  try {
1331
250
  await executeReportCommand(target, {
1332
251
  out: options?.out,
252
+ stdout: options?.stdout,
1333
253
  json: options?.json,
1334
254
  simple: options?.simple,
1335
255
  });
@@ -1339,7 +259,7 @@ The report is a markdown document you can:
1339
259
  process.exit(1);
1340
260
  }
1341
261
  });
1342
- // ── hook ──────────────────────────────────────────────────────────────────────
262
+ // ── hook ─────────────────────────────────────────────────────────────────────
1343
263
  program
1344
264
  .command('hook')
1345
265
  .description('Manage Claude Agent SDK hook integration (intercepts all tool calls)')
@@ -1375,7 +295,7 @@ program
1375
295
  const host = options?.host ?? '127.0.0.1';
1376
296
  const token = options?.token;
1377
297
  if (options?.showConfig) {
1378
- const config = {
298
+ const configBlock = {
1379
299
  mcpServers: {
1380
300
  codehere: {
1381
301
  url: `http://${host}:${port}/mcp`,
@@ -1384,7 +304,7 @@ program
1384
304
  },
1385
305
  };
1386
306
  console.log('\nAdd to Claude Desktop or Cursor MCP settings:\n');
1387
- console.log(JSON.stringify(config, null, 2));
307
+ console.log(JSON.stringify(configBlock, null, 2));
1388
308
  newline();
1389
309
  console.log(colors.dim('Claude Desktop: ~/Library/Application Support/Claude/claude_desktop_config.json'));
1390
310
  console.log(colors.dim('Cursor: Settings > MCP Servers'));
@@ -1409,7 +329,6 @@ program
1409
329
  console.log(colors.dim(' Run with --show-config to get the MCP client config block'));
1410
330
  console.log(colors.dim(' Press Ctrl+C to stop'));
1411
331
  newline();
1412
- // Keep alive
1413
332
  process.on('SIGINT', () => {
1414
333
  server.close();
1415
334
  console.log(colors.dim('\ncodehere MCP server stopped'));
@@ -1424,7 +343,168 @@ program
1424
343
  process.exit(1);
1425
344
  }
1426
345
  });
1427
- // ── scan-tool (called by Claude Code hooks system, not users) ─────────────────
346
+ // ── start (Mission Control: daemon + web UI) ─────────────────────────────────
347
+ program
348
+ .command('start')
349
+ .description('Start the codehere daemon — task intake UI + MCP server at localhost:3773')
350
+ .option('-p, --port <number>', 'Port to listen on', '3773')
351
+ .option('--host <host>', 'Host to bind to', '127.0.0.1')
352
+ .option('--no-open', "Don't auto-open the browser")
353
+ .action(async (options) => {
354
+ const port = parseInt(options?.port ?? '3773', 10);
355
+ const host = options?.host ?? '127.0.0.1';
356
+ const shouldOpen = options?.open !== false;
357
+ try {
358
+ const { startMCPServer } = await import('./infrastructure/protocols/mcp-server-impl.js');
359
+ const server = startMCPServer({ port, host });
360
+ const url = `http://${host}:${port}/`;
361
+ newline();
362
+ console.log(success('codehere is running'));
363
+ newline();
364
+ console.log(colors.bold('Tasks:') + ' ' + url);
365
+ console.log(colors.bold('History:') + ' ' + `http://${host}:${port}/history`);
366
+ console.log(colors.bold('MCP:') + ' ' + `http://${host}:${port}/mcp`);
367
+ newline();
368
+ console.log(colors.dim(' Press Ctrl+C to stop'));
369
+ newline();
370
+ if (shouldOpen) {
371
+ const { spawn } = await import('child_process');
372
+ const opener = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'start' : 'xdg-open';
373
+ try {
374
+ spawn(opener, [url], { detached: true, stdio: 'ignore' }).unref();
375
+ }
376
+ catch {
377
+ /* browser open is best-effort */
378
+ }
379
+ }
380
+ process.on('SIGINT', () => {
381
+ server.close();
382
+ console.log(colors.dim('\ncodehere stopped'));
383
+ process.exit(0);
384
+ });
385
+ await new Promise((resolve) => {
386
+ server.on('close', resolve);
387
+ });
388
+ }
389
+ catch (err) {
390
+ console.error(error('Failed to start:'), err instanceof Error ? err.message : String(err));
391
+ process.exit(1);
392
+ }
393
+ });
394
+ // ── task (spawn an agent session via CLI) ────────────────────────────────────
395
+ program
396
+ .command('task <prompt...>')
397
+ .description('Assign a task — spawns the default agent under codehere. Requires `codehere start` running.')
398
+ .option('-p, --port <number>', 'Daemon port', '3773')
399
+ .option('--host <host>', 'Daemon host', '127.0.0.1')
400
+ .option('--watch', 'Stream events to the terminal (default: just returns the session id)')
401
+ .action(async (promptParts, options) => {
402
+ const prompt = promptParts.join(' ').trim();
403
+ if (!prompt) {
404
+ console.error(error('No prompt. Usage: codehere task "fix auth bug in session.ts"'));
405
+ process.exit(1);
406
+ }
407
+ const port = parseInt(options?.port ?? '3773', 10);
408
+ const host = options?.host ?? '127.0.0.1';
409
+ const base = `http://${host}:${port}`;
410
+ const http = await import('http');
411
+ const spawnResult = await new Promise((resolve) => {
412
+ const body = JSON.stringify({ prompt });
413
+ const r = http.request(`${base}/api/task`, {
414
+ method: 'POST',
415
+ headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body) },
416
+ }, (res) => {
417
+ let buf = '';
418
+ res.on('data', (c) => {
419
+ buf += c.toString();
420
+ });
421
+ res.on('end', () => {
422
+ try {
423
+ resolve(JSON.parse(buf));
424
+ }
425
+ catch {
426
+ resolve(null);
427
+ }
428
+ });
429
+ });
430
+ r.on('error', () => resolve(null));
431
+ r.write(body);
432
+ r.end();
433
+ });
434
+ if (!spawnResult || !spawnResult.sessionId) {
435
+ console.error(error(`Could not reach daemon at ${base}. Start it with: codehere start`));
436
+ process.exit(1);
437
+ }
438
+ console.log(success(`Task assigned · session ${spawnResult.sessionId}`));
439
+ console.log(colors.dim(` Watch: ${base}/ · Kill: codehere kill ${spawnResult.sessionId}`));
440
+ if (!options?.watch)
441
+ return;
442
+ const streamUrl = `${base}/api/task/${spawnResult.sessionId}/events`;
443
+ await new Promise((resolve) => {
444
+ const r = http.get(streamUrl, (res) => {
445
+ let buf = '';
446
+ res.on('data', (c) => {
447
+ buf += c.toString();
448
+ let i;
449
+ while ((i = buf.indexOf('\n\n')) >= 0) {
450
+ const frame = buf.slice(0, i).trim();
451
+ buf = buf.slice(i + 2);
452
+ if (!frame.startsWith('data:'))
453
+ continue;
454
+ const payload = frame.slice(5).trim();
455
+ try {
456
+ const event = JSON.parse(payload);
457
+ const type = String(event.type || 'message');
458
+ if (type === 'exit') {
459
+ console.log(colors.dim(` [exit] ${String(event.data?.status ?? '?')}`));
460
+ res.destroy();
461
+ resolve();
462
+ return;
463
+ }
464
+ console.log(colors.dim(` [${type}]`));
465
+ }
466
+ catch {
467
+ /* skip */
468
+ }
469
+ }
470
+ });
471
+ res.on('end', () => resolve());
472
+ });
473
+ r.on('error', () => resolve());
474
+ });
475
+ });
476
+ // ── kill (stop a running task) ───────────────────────────────────────────────
477
+ program
478
+ .command('kill <sessionId>')
479
+ .description('Kill a running task session.')
480
+ .option('-p, --port <number>', 'Daemon port', '3773')
481
+ .option('--host <host>', 'Daemon host', '127.0.0.1')
482
+ .action(async (sessionId, options) => {
483
+ const port = parseInt(options?.port ?? '3773', 10);
484
+ const host = options?.host ?? '127.0.0.1';
485
+ const http = await import('http');
486
+ await new Promise((resolve) => {
487
+ const r = http.request(`http://${host}:${port}/api/task/${sessionId}/kill`, { method: 'POST' }, (res) => {
488
+ let buf = '';
489
+ res.on('data', (c) => {
490
+ buf += c.toString();
491
+ });
492
+ res.on('end', () => {
493
+ if (res.statusCode === 200)
494
+ console.log(success(`Killed ${sessionId}`));
495
+ else
496
+ console.error(error(`Failed (${res.statusCode}): ${buf}`));
497
+ resolve();
498
+ });
499
+ });
500
+ r.on('error', (err) => {
501
+ console.error(error(err.message));
502
+ resolve();
503
+ });
504
+ r.end();
505
+ });
506
+ });
507
+ // ── scan-tool (called by Claude Code hooks system, not users directly) ───────
1428
508
  program
1429
509
  .command('scan-tool')
1430
510
  .description('Hook handler: reads Claude Code tool event from stdin, outputs block/allow (used by hook system)')
@@ -1438,7 +518,7 @@ program
1438
518
  debug: options?.debug,
1439
519
  });
1440
520
  });
1441
- // Add completion command
521
+ // ── completion (shell completion script generator) ──────────────────────────
1442
522
  program
1443
523
  .command('completion')
1444
524
  .description('Generate shell completion script')
@@ -1469,61 +549,15 @@ program
1469
549
  }
1470
550
  }
1471
551
  catch (err) {
1472
- displayError(err, {
1473
- operation: 'completion',
1474
- showSuggestions: true,
1475
- });
552
+ displayError(err, { operation: 'completion', showSuggestions: true });
1476
553
  process.exit(1);
1477
554
  }
1478
555
  });
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 });
1490
- process.exit(1);
1491
- }
1492
- });
1493
- // Initialize telemetry and track command
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)
556
+ // ── slash-command routing: `codehere /trust .` → `codehere trust .` ─────────
1523
557
  const rawArgs = process.argv.slice(2);
1524
558
  if (rawArgs.length > 0 && rawArgs[0].startsWith('/') && rawArgs[0].length > 1) {
1525
- const stripped = rawArgs[0].slice(1); // remove leading /
1526
- process.argv.splice(2, 1, stripped); // replace /cmd with cmd in-place
559
+ const stripped = rawArgs[0].slice(1);
560
+ process.argv.splice(2, 1, stripped);
1527
561
  }
1528
562
  program.parse();
1529
563
  //# sourceMappingURL=index.js.map