codehere 0.2.1 → 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 +10 -49
  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 +334 -43
  33. package/dist/presentation/cli/commands/trust-command.js +115 -99
  34. package/dist/presentation/cli/error-display.js +17 -21
  35. package/dist/ui.js +17 -12
  36. package/package.json +35 -67
  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 -530
  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 -652
  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
@@ -16,21 +16,23 @@ import { colors, newline, sectionHeader } from '../../../ui.js';
16
16
  import { getVersion } from '../../../utils/version.js';
17
17
  import { loadProductContext } from '../../../infrastructure/config/product-context.js';
18
18
  import { AUDIT_FILE } from '../../../infrastructure/hooks/claude-sdk-hook.js';
19
- import { analyzeSessions, detectAmplificationAlerts, formatDuration } from '../../../infrastructure/hooks/session-analyzer.js';
19
+ import { analyzeSessions, detectAmplificationAlerts, formatDuration, } from '../../../infrastructure/hooks/session-analyzer.js';
20
20
  function readAuditLog(maxLines = 5000) {
21
21
  if (!existsSync(AUDIT_FILE))
22
22
  return [];
23
23
  try {
24
24
  const lines = readFileSync(AUDIT_FILE, 'utf-8').split('\n').filter(Boolean);
25
25
  const recent = lines.slice(-maxLines);
26
- return recent.map(l => {
26
+ return recent
27
+ .map((l) => {
27
28
  try {
28
29
  return JSON.parse(l);
29
30
  }
30
31
  catch {
31
32
  return null;
32
33
  }
33
- }).filter(Boolean);
34
+ })
35
+ .filter(Boolean);
34
36
  }
35
37
  catch {
36
38
  return [];
@@ -69,8 +71,12 @@ function extractBlockReason(entry) {
69
71
  return parts.slice(-2).join('/');
70
72
  }
71
73
  }
72
- catch { /* parse failed — truncated JSON, try regex */ }
74
+ catch {
75
+ /* parse failed — truncated JSON, try regex */
76
+ }
73
77
  // Fallback: extract file_path or command from truncated JSON via regex
78
+ if (!entry.toolInput)
79
+ return 'tool call';
74
80
  const fpMatch = entry.toolInput.match(/"file_path"\s*:\s*"([^"]+)"/);
75
81
  if (fpMatch) {
76
82
  const parts = fpMatch[1].split('/');
@@ -84,15 +90,15 @@ function extractBlockReason(entry) {
84
90
  // ── main command ──────────────────────────────────────────────────────────────
85
91
  // ── session drill-down ────────────────────────────────────────────────────────
86
92
  function renderSessionDrillDown(sessionId, entries) {
87
- const sessionEntries = entries.filter(e => e.sessionId === sessionId || e.sessionId?.startsWith(sessionId));
93
+ const sessionEntries = entries.filter((e) => e.sessionId === sessionId || e.sessionId?.startsWith(sessionId));
88
94
  if (sessionEntries.length === 0) {
89
95
  console.log(colors.yellow(`No entries found for session: ${sessionId}`));
90
96
  console.log(colors.dim(' Tip: use the first 8+ characters of a session ID from `codehere status --sessions`'));
91
97
  return;
92
98
  }
93
99
  const sorted = [...sessionEntries].sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
94
- const blocked = sorted.filter(e => e.blocked);
95
- const critical = sorted.filter(e => e.criticalFindings > 0);
100
+ const blocked = sorted.filter((e) => e.blocked);
101
+ const critical = sorted.filter((e) => e.criticalFindings > 0);
96
102
  const durationMs = new Date(sorted[sorted.length - 1].timestamp).getTime() - new Date(sorted[0].timestamp).getTime();
97
103
  const toolCounts = {};
98
104
  for (const e of sorted) {
@@ -157,6 +163,252 @@ function renderSessionDrillDown(sessionId, entries) {
157
163
  }
158
164
  newline();
159
165
  }
166
+ function extractFilePath(entry) {
167
+ if (!entry.toolInput)
168
+ return null;
169
+ try {
170
+ const input = JSON.parse(entry.toolInput);
171
+ if (input.file_path)
172
+ return String(input.file_path);
173
+ if (Array.isArray(input.edits) && input.edits[0]?.file_path)
174
+ return String(input.edits[0].file_path);
175
+ }
176
+ catch {
177
+ const m = entry.toolInput.match(/"file_path"\s*:\s*"([^"]+)"/);
178
+ if (m)
179
+ return m[1];
180
+ }
181
+ return null;
182
+ }
183
+ function shortReason(entry, maxLen = 60) {
184
+ const r = extractBlockReason(entry);
185
+ return r.length <= maxLen ? r : r.substring(0, maxLen - 1) + '…';
186
+ }
187
+ const BLOCK_CATEGORY_LABEL = {
188
+ 'self-test': "codehere's own canary tests",
189
+ 'cache-cleanup': 'routine cache/tmp deletion',
190
+ 'content-match': 'editing files that mention dangerous patterns (docs/tests)',
191
+ secret: 'potential credential in content',
192
+ destructive: 'destructive command (rm -rf, force push)',
193
+ injection: 'code/command injection pattern',
194
+ other: 'uncategorized',
195
+ };
196
+ function classifyBlock(entry) {
197
+ const raw = entry.toolInput.toLowerCase();
198
+ // Codehere's own test canaries (seeing these means someone is running our tests)
199
+ if (raw.includes('rm -rf /home/user') || raw.includes('evil.com')) {
200
+ return 'self-test';
201
+ }
202
+ // rm -rf against caches/tmp/build artifacts — real cleanup, not a threat
203
+ if (raw.includes('rm -rf') &&
204
+ (raw.includes('/.cache/') ||
205
+ raw.includes('library/caches/') ||
206
+ raw.includes('/tmp/') ||
207
+ raw.includes('node_modules') ||
208
+ raw.includes('/dist/') ||
209
+ raw.includes('/.next/') ||
210
+ raw.includes('/build/'))) {
211
+ return 'cache-cleanup';
212
+ }
213
+ // Edit/Write into docs/source that merely mentions dangerous patterns — content match FP
214
+ if (entry.toolName === 'Edit' || entry.toolName === 'MultiEdit' || entry.toolName === 'Write') {
215
+ const fp = extractFilePath(entry) || '';
216
+ if (/\.(md|tsx?|jsx?|html|rst|txt|ya?ml|json)$/i.test(fp)) {
217
+ return 'content-match';
218
+ }
219
+ }
220
+ // Destructive (not caught as cleanup above — so targeting user data)
221
+ if (raw.includes('rm -rf') || raw.includes('--force') || raw.includes('force push')) {
222
+ return 'destructive';
223
+ }
224
+ // Secret-in-content
225
+ if (/api_key|apikey|auth_token|bearer\s|password\s*[:=]|secret\s*[:=]|sk-[a-z0-9]{20,}/i.test(raw)) {
226
+ return 'secret';
227
+ }
228
+ // Code/command injection
229
+ if (raw.includes('eval(') || raw.includes('child_process.exec') || raw.includes('innerhtml')) {
230
+ return 'injection';
231
+ }
232
+ return 'other';
233
+ }
234
+ function formatDateRange(fromMs, toMs) {
235
+ const f = new Date(fromMs);
236
+ const t = new Date(toMs);
237
+ const opt = { month: 'short', day: 'numeric' };
238
+ return `${f.toLocaleDateString('en-US', opt)} – ${t.toLocaleDateString('en-US', opt)}`;
239
+ }
240
+ function renderWeeklyRecap(entries, jsonOut) {
241
+ const weekMs = 7 * 24 * 3600 * 1000;
242
+ const now = Date.now();
243
+ const windowStart = now - weekMs;
244
+ const week = entries.filter((e) => new Date(e.timestamp).getTime() >= windowStart);
245
+ // Aggregates
246
+ const sessions = new Set(week.map((e) => e.sessionId).filter(Boolean));
247
+ const writes = week.filter((e) => e.toolName === 'Write');
248
+ const edits = week.filter((e) => e.toolName === 'Edit' || e.toolName === 'MultiEdit');
249
+ const bash = week.filter((e) => e.toolName === 'Bash');
250
+ const blocks = week.filter((e) => e.blocked);
251
+ const fileStats = {};
252
+ for (const e of [...writes, ...edits]) {
253
+ const fp = extractFilePath(e);
254
+ if (!fp)
255
+ continue;
256
+ if (!fileStats[fp])
257
+ fileStats[fp] = { path: fp, edits: 0, writes: 0 };
258
+ if (e.toolName === 'Write')
259
+ fileStats[fp].writes++;
260
+ else
261
+ fileStats[fp].edits++;
262
+ }
263
+ const topFiles = Object.values(fileStats)
264
+ .sort((a, b) => b.edits + b.writes - (a.edits + a.writes))
265
+ .slice(0, 5);
266
+ const uniqueFiles = Object.keys(fileStats).length;
267
+ // Classify blocks — most of the 18-blocks headline is noise, users deserve to see which kind
268
+ const blockCategories = {
269
+ 'self-test': 0,
270
+ 'cache-cleanup': 0,
271
+ 'content-match': 0,
272
+ secret: 0,
273
+ destructive: 0,
274
+ injection: 0,
275
+ other: 0,
276
+ };
277
+ for (const b of blocks) {
278
+ blockCategories[classifyBlock(b)]++;
279
+ }
280
+ const blockBreakdown = Object.entries(blockCategories)
281
+ .filter(([, n]) => n > 0)
282
+ .sort((a, b) => b[1] - a[1]);
283
+ const shortPath = (p) => {
284
+ const parts = p.split('/').filter(Boolean);
285
+ return parts.slice(-2).join('/');
286
+ };
287
+ if (jsonOut) {
288
+ console.log(JSON.stringify({
289
+ range: { from: new Date(windowStart).toISOString(), to: new Date(now).toISOString() },
290
+ sessions: sessions.size,
291
+ toolCalls: week.length,
292
+ writes: writes.length,
293
+ edits: edits.length,
294
+ bashCommands: bash.length,
295
+ uniqueFiles,
296
+ blocks: blocks.length,
297
+ topFiles: topFiles.map((f) => ({ file: shortPath(f.path), edits: f.edits, writes: f.writes })),
298
+ blockBreakdown: blockBreakdown.map(([category, count]) => ({
299
+ category,
300
+ count,
301
+ label: BLOCK_CATEGORY_LABEL[category],
302
+ })),
303
+ }, null, 2));
304
+ return;
305
+ }
306
+ const pad = (s, n) => (s.length >= n ? s : s + ' '.repeat(n - s.length));
307
+ const num = (n) => colors.bold(String(n).padStart(6));
308
+ sectionHeader(`codehere weekly · ${formatDateRange(windowStart, now)}`);
309
+ newline();
310
+ if (week.length === 0) {
311
+ console.log(colors.dim(' No activity in the last 7 days.'));
312
+ console.log(colors.dim(' Run Claude Code with the codehere hook installed to start tracking.'));
313
+ newline();
314
+ return;
315
+ }
316
+ // Headline numbers
317
+ console.log(` ${colors.bold(String(sessions.size))} session${sessions.size === 1 ? '' : 's'} · ${colors.bold(week.length.toLocaleString())} tool call${week.length === 1 ? '' : 's'} scanned · ${blocks.length > 0 ? colors.yellow(String(blocks.length)) : colors.dim('0')} block${blocks.length === 1 ? '' : 's'}`);
318
+ newline();
319
+ // Activity breakdown
320
+ console.log(colors.dim(' ACTIVITY'));
321
+ console.log(` ${num(writes.length)} new files written`);
322
+ console.log(` ${num(edits.length)} edits to existing files`);
323
+ console.log(` ${num(bash.length)} shell commands`);
324
+ console.log(` ${num(uniqueFiles)} unique files touched`);
325
+ newline();
326
+ // Top files
327
+ if (topFiles.length > 0) {
328
+ console.log(colors.dim(' TOP FILES'));
329
+ for (const f of topFiles) {
330
+ const count = f.edits + f.writes;
331
+ const kind = f.writes > 0 && f.edits === 0 ? 'write' : f.edits > 0 && f.writes === 0 ? 'edit' : 'touch';
332
+ const plural = count === 1 ? '' : kind === 'touch' ? 'es' : 's';
333
+ const countStr = String(count).padStart(3);
334
+ const kindStr = `${kind}${plural}`.padEnd(7);
335
+ console.log(` ${colors.dim(countStr)} ${colors.dim(kindStr)} ${shortPath(f.path)}`);
336
+ }
337
+ newline();
338
+ }
339
+ // Blocks — categorized so users see signal vs noise at a glance
340
+ if (blocks.length > 0) {
341
+ console.log(colors.dim(` BLOCKS ${blocks.length} total`));
342
+ for (const [category, count] of blockBreakdown) {
343
+ const categoryColor = category === 'other' || category === 'destructive' || category === 'secret' || category === 'injection'
344
+ ? colors.yellow
345
+ : colors.dim;
346
+ const countStr = String(count).padStart(3);
347
+ console.log(` ${colors.dim(countStr)} ${categoryColor(pad(category, 15))} ${colors.dim(BLOCK_CATEGORY_LABEL[category])}`);
348
+ }
349
+ newline();
350
+ console.log(colors.dim(' Drill in: codehere status --sessions'));
351
+ newline();
352
+ }
353
+ else {
354
+ console.log(` ${colors.green('✓')} Nothing blocked this week.`);
355
+ newline();
356
+ }
357
+ console.log(colors.dim(' ─────'));
358
+ console.log(colors.dim(' codehere status --weekly --json Export for sharing'));
359
+ newline();
360
+ }
361
+ // ── audit export ─────────────────────────────────────────────────────────────
362
+ function parseDurationToHours(s) {
363
+ const m = /^(\d+)([hdw])$/.exec(s.trim());
364
+ if (!m)
365
+ return null;
366
+ const n = parseInt(m[1], 10);
367
+ switch (m[2]) {
368
+ case 'h':
369
+ return n;
370
+ case 'd':
371
+ return n * 24;
372
+ case 'w':
373
+ return n * 24 * 7;
374
+ }
375
+ return null;
376
+ }
377
+ function exportAudit(entries, format, sinceHours) {
378
+ const filtered = sinceHours != null ? entries.filter((e) => withinHours(e.timestamp, sinceHours)) : entries;
379
+ if (format === 'jsonl') {
380
+ for (const e of filtered)
381
+ console.log(JSON.stringify(e));
382
+ return;
383
+ }
384
+ if (format === 'csv') {
385
+ console.log('timestamp,sessionId,toolName,riskScore,blocked,findings,criticalFindings');
386
+ for (const e of filtered) {
387
+ const row = [e.timestamp, e.sessionId, e.toolName, e.riskScore, e.blocked, e.findings, e.criticalFindings].join(',');
388
+ console.log(row);
389
+ }
390
+ return;
391
+ }
392
+ if (format === 'json') {
393
+ console.log(JSON.stringify(filtered, null, 2));
394
+ return;
395
+ }
396
+ if (format === 'md') {
397
+ console.log(`# codehere audit log · ${filtered.length} events`);
398
+ console.log('');
399
+ if (sinceHours != null)
400
+ console.log(`*Window: last ${sinceHours} hour${sinceHours === 1 ? '' : 's'}*\n`);
401
+ console.log('| Timestamp | Session | Tool | Risk | Blocked | Findings |');
402
+ console.log('|---|---|---|---:|:---:|---:|');
403
+ for (const e of filtered) {
404
+ const sid = (e.sessionId || '').substring(0, 8);
405
+ const blocked = e.blocked ? '✗' : '✓';
406
+ console.log(`| \`${e.timestamp}\` | \`${sid}\` | ${e.toolName} | ${e.riskScore} | ${blocked} | ${e.findings} |`);
407
+ }
408
+ return;
409
+ }
410
+ throw new Error(`Unsupported export format: ${format}. Use jsonl, json, csv, or md.`);
411
+ }
160
412
  export async function executeStatusCommand(options = {}) {
161
413
  const cwd = process.cwd();
162
414
  const version = getVersion();
@@ -166,12 +418,33 @@ export async function executeStatusCommand(options = {}) {
166
418
  renderSessionDrillDown(options.session, entries);
167
419
  return;
168
420
  }
169
- const last24h = entries.filter(e => withinHours(e.timestamp, 24));
170
- const last7d = entries.filter(e => withinHours(e.timestamp, 24 * 7));
421
+ // Audit export mode — dump raw events for archival/compliance/sharing
422
+ if (options.export) {
423
+ const sinceHours = options.since ? parseDurationToHours(options.since) : null;
424
+ if (options.since && sinceHours == null) {
425
+ console.error(`Invalid --since value: "${options.since}". Use e.g. 24h, 7d, 4w.`);
426
+ process.exit(1);
427
+ }
428
+ try {
429
+ exportAudit(entries, options.export, sinceHours);
430
+ }
431
+ catch (e) {
432
+ console.error(e.message);
433
+ process.exit(1);
434
+ }
435
+ return;
436
+ }
437
+ // Weekly recap mode — shareable agent-activity summary
438
+ if (options.weekly) {
439
+ renderWeeklyRecap(entries, options.json);
440
+ return;
441
+ }
442
+ const last24h = entries.filter((e) => withinHours(e.timestamp, 24));
443
+ const last7d = entries.filter((e) => withinHours(e.timestamp, 24 * 7));
171
444
  const stats = (subset) => ({
172
445
  total: subset.length,
173
- blocked: subset.filter(e => e.blocked).length,
174
- critical: subset.filter(e => e.criticalFindings > 0).length,
446
+ blocked: subset.filter((e) => e.blocked).length,
447
+ critical: subset.filter((e) => e.criticalFindings > 0).length,
175
448
  });
176
449
  const s24 = stats(last24h);
177
450
  const s7d = stats(last7d);
@@ -196,9 +469,14 @@ export async function executeStatusCommand(options = {}) {
196
469
  const { getTaskRepository } = await import('../../../infrastructure/storage/task-repository.js');
197
470
  const taskRepo = getTaskRepository();
198
471
  const tasks = await taskRepo.getAll();
199
- result.tasks = tasks.slice(0, 20).map(t => ({
200
- id: t.id, mode: t.mode, status: t.status,
201
- riskTier: t.riskTier, environment: t.environment, goal: t.goal, createdAt: t.createdAt,
472
+ result.tasks = tasks.slice(0, 20).map((t) => ({
473
+ id: t.id,
474
+ mode: t.mode,
475
+ status: t.status,
476
+ riskTier: t.riskTier,
477
+ environment: t.environment,
478
+ goal: t.goal,
479
+ createdAt: t.createdAt,
202
480
  }));
203
481
  result.taskCount = tasks.length;
204
482
  }
@@ -214,16 +492,17 @@ export async function executeStatusCommand(options = {}) {
214
492
  sectionHeader('Codehere Status');
215
493
  newline();
216
494
  // Version + hooks
217
- const hookStatus = hookRunning
218
- ? colors.green('✓') + ' Hooks running'
219
- : colors.yellow('⚠') + ' Hooks not installed';
495
+ const hookStatus = hookRunning ? colors.green('✓') + ' Hooks running' : colors.yellow('⚠') + ' Hooks not installed';
220
496
  console.log(` Version: ${version} · Status: Running · ${hookStatus}`);
221
497
  newline();
222
498
  // Product brain
223
499
  if (productCtx) {
224
- const riskColor = productCtx.riskLevel === 'critical' ? colors.red
225
- : productCtx.riskLevel === 'high' ? colors.yellow
226
- : productCtx.riskLevel === 'medium' ? colors.cyan
500
+ const riskColor = productCtx.riskLevel === 'critical'
501
+ ? colors.red
502
+ : productCtx.riskLevel === 'high'
503
+ ? colors.yellow
504
+ : productCtx.riskLevel === 'medium'
505
+ ? colors.cyan
227
506
  : colors.green;
228
507
  const parts = [
229
508
  colors.bold(productCtx.name),
@@ -239,27 +518,29 @@ export async function executeStatusCommand(options = {}) {
239
518
  }
240
519
  else {
241
520
  // Activity table
242
- const col = (n, color) => {
243
- const s = String(n);
244
- return color && n > 0 ? color(s) : colors.dim(s);
521
+ // Pad number BEFORE applying color, so ANSI codes don't break alignment
522
+ const fmtNum = (n, color) => {
523
+ const padded = String(n).padStart(10);
524
+ return color && n > 0 ? color(padded) : colors.dim(padded);
245
525
  };
246
526
  const labelW = 12;
247
- const numW = 10;
248
527
  const label = (s) => s.padEnd(labelW);
249
- const num = (s) => s.padStart(numW);
250
- const header = ` ${label('')}${num('Last 24h')}${num('This week')}${num('All time')}`;
528
+ const header = ` ${label('')}${'Last 24h'.padStart(10)}${'This week'.padStart(10)}${'All time'.padStart(10)}`;
251
529
  console.log(colors.dim(header));
252
- console.log(colors.dim(` ${'-'.repeat(labelW + numW * 3)}`));
253
- console.log(` ${label('Scans')}${num(col(s24.total))}${num(col(s7d.total))}${num(col(sAll.total))}`);
254
- console.log(` ${label('Blocked')}${num(col(s24.blocked, colors.red))}${num(col(s7d.blocked, colors.red))}${num(col(sAll.blocked, colors.red))}`);
530
+ console.log(colors.dim(` ${'-'.repeat(labelW + 30)}`));
531
+ console.log(` ${label('Scans')}${fmtNum(s24.total)}${fmtNum(s7d.total)}${fmtNum(sAll.total)}`);
532
+ console.log(` ${label('Blocked')}${fmtNum(s24.blocked, colors.red)}${fmtNum(s7d.blocked, colors.red)}${fmtNum(sAll.blocked, colors.red)}`);
255
533
  if (sAll.critical > 0) {
256
- console.log(` ${label('Critical')}${num(col(s24.critical, colors.red))}${num(col(s7d.critical, colors.red))}${num(col(sAll.critical, colors.red))}`);
534
+ console.log(` ${label('Critical')}${fmtNum(s24.critical, colors.red)}${fmtNum(s7d.critical, colors.red)}${fmtNum(sAll.critical, colors.red)}`);
257
535
  }
258
536
  newline();
259
537
  // Recent blocks
260
- const recentBlocked = [...entries].reverse().filter(e => e.blocked).slice(0, 5);
538
+ const recentBlocked = [...entries]
539
+ .reverse()
540
+ .filter((e) => e.blocked)
541
+ .slice(0, 5);
261
542
  if (recentBlocked.length > 0) {
262
- console.log(colors.bold('Recent blocks:'));
543
+ console.log(colors.dim(' RECENT BLOCKS'));
263
544
  for (const entry of recentBlocked) {
264
545
  const tool = colors.cyan(entry.toolName.padEnd(10));
265
546
  const time = colors.dim(timeAgo(entry.timestamp).padEnd(8));
@@ -275,23 +556,26 @@ export async function executeStatusCommand(options = {}) {
275
556
  }
276
557
  }
277
558
  // Skills status
559
+ console.log(colors.dim(' ─────'));
278
560
  if (hasSkills) {
279
- console.log(colors.dim(' Skills: /codehere:trust /codehere:report /codehere:audit /codehere:hook-status'));
561
+ console.log(colors.dim(' /codehere:trust /codehere:report /codehere:audit /codehere:hook-status'));
280
562
  }
281
563
  else {
282
- console.log(colors.dim(' Skills not installed. Run `codehere hook install` to add them.'));
564
+ console.log(colors.dim(' Run codehere hook install to add slash commands'));
283
565
  }
284
566
  newline();
285
567
  // Multi-agent session analysis
286
568
  if (options.sessions && entries.length > 0) {
287
569
  const allSessions = analyzeSessions(entries);
288
- const sessions = allSessions.filter(s => s.sessionId !== 'unknown');
570
+ const sessions = allSessions.filter((s) => s.sessionId !== 'unknown');
289
571
  const alerts = detectAmplificationAlerts(sessions);
290
572
  console.log(colors.bold('Sessions (last 10):'));
291
573
  for (const s of sessions.slice(0, 10)) {
292
574
  const sid = s.sessionId.substring(0, 8);
293
- const risk = s.amplificationRisk === 'high' ? colors.red('high')
294
- : s.amplificationRisk === 'medium' ? colors.yellow('med')
575
+ const risk = s.amplificationRisk === 'high'
576
+ ? colors.red('high')
577
+ : s.amplificationRisk === 'medium'
578
+ ? colors.yellow('med')
295
579
  : colors.green('low');
296
580
  const blocked = s.blockedCalls > 0 ? colors.red(` ${s.blockedCalls} blocked`) : '';
297
581
  const dur = formatDuration(s.durationMs);
@@ -323,15 +607,22 @@ export async function executeStatusCommand(options = {}) {
323
607
  const taskRepo = getTaskRepository();
324
608
  const tasks = await taskRepo.getAll();
325
609
  if (tasks.length === 0) {
326
- console.log(colors.dim('No tasks found. Run "codehere suggest" or "codehere run" to create tasks.'));
610
+ console.log(colors.dim('No tasks found. Run "codehere start" to open the task intake, or "codehere task <prompt>" from the CLI.'));
327
611
  }
328
612
  else {
329
- const pad = (s, n) => s.length >= n ? s.substring(0, n) : s + ' '.repeat(n - s.length);
330
- const trunc = (s, n) => s.length <= n ? s : s.substring(0, n - 1) + '…';
613
+ const pad = (s, n) => (s.length >= n ? s.substring(0, n) : s + ' '.repeat(n - s.length));
614
+ const trunc = (s, n) => (s.length <= n ? s : s.substring(0, n - 1) + '…');
331
615
  console.log(colors.dim([pad('ID', 10), pad('Mode', 8), pad('Status', 12), pad('Risk', 4), pad('Env', 6), 'Goal'].join(' ')));
332
616
  console.log(colors.dim(['-'.repeat(10), '-'.repeat(8), '-'.repeat(12), '-'.repeat(4), '-'.repeat(6), '-'.repeat(30)].join(' ')));
333
- tasks.slice(0, 10).forEach(task => {
334
- console.log([pad(task.id.substring(0, 8) + '..', 10), pad(task.mode, 8), pad(task.status, 12), pad(task.riskTier.toUpperCase(), 4), pad(task.environment.substring(0, 6), 6), trunc(task.goal, 30)].join(' '));
617
+ tasks.slice(0, 10).forEach((task) => {
618
+ console.log([
619
+ pad(task.id.substring(0, 8) + '..', 10),
620
+ pad(task.mode, 8),
621
+ pad(task.status, 12),
622
+ pad(task.riskTier.toUpperCase(), 4),
623
+ pad(task.environment.substring(0, 6), 6),
624
+ trunc(task.goal, 30),
625
+ ].join(' '));
335
626
  });
336
627
  if (tasks.length > 10) {
337
628
  newline();
@@ -340,7 +631,7 @@ export async function executeStatusCommand(options = {}) {
340
631
  }
341
632
  }
342
633
  catch {
343
- console.log(colors.dim('No tasks found. Run "codehere suggest" or "codehere run" to create tasks.'));
634
+ console.log(colors.dim('No tasks found. Run "codehere start" to open the task intake, or "codehere task <prompt>" from the CLI.'));
344
635
  }
345
636
  }
346
637
  // Quick trust scan (opt-in)