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.
- package/README.md +88 -68
- package/dist/domain/entities/task.js +4 -4
- package/dist/error-handler.js +4 -9
- package/dist/index.js +253 -1219
- package/dist/infrastructure/config/product-context.js +7 -6
- package/dist/infrastructure/hooks/claude-sdk-hook.js +335 -63
- package/dist/infrastructure/observability/execution-tracer.js +11 -6
- package/dist/infrastructure/orchestration/agent-adapter.js +13 -0
- package/dist/infrastructure/orchestration/agent-installer.js +101 -0
- package/dist/infrastructure/orchestration/agents-config-seed.js +56 -0
- package/dist/infrastructure/orchestration/aider-adapter.js +319 -0
- package/dist/infrastructure/orchestration/artifacts-extractor.js +175 -0
- package/dist/infrastructure/orchestration/claude-code-adapter.js +352 -0
- package/dist/infrastructure/orchestration/codex-adapter.js +310 -0
- package/dist/infrastructure/orchestration/generic-cli-adapter.js +553 -0
- package/dist/infrastructure/orchestration/opencode-adapter.js +275 -0
- package/dist/infrastructure/orchestration/progress-aggregator.js +189 -0
- package/dist/infrastructure/orchestration/readiness.js +106 -0
- package/dist/infrastructure/orchestration/session-manager.js +272 -0
- package/dist/infrastructure/orchestration/session-persistence.js +109 -0
- package/dist/infrastructure/protocols/mcp-helpers.js +10 -30
- package/dist/infrastructure/protocols/mcp-server-impl.js +359 -21
- package/dist/infrastructure/protocols/ui-page.js +1827 -0
- package/dist/infrastructure/security/file-collection.js +115 -0
- package/dist/infrastructure/security/trust-scorer.js +301 -56
- package/dist/infrastructure/storage/task-repository.js +8 -7
- package/dist/monitoring.js +10 -49
- package/dist/presentation/cli/commands/help-command.js +96 -139
- package/dist/presentation/cli/commands/hook-command.js +17 -24
- package/dist/presentation/cli/commands/report-command.js +59 -72
- package/dist/presentation/cli/commands/setup-command.js +4 -4
- package/dist/presentation/cli/commands/status-command.js +334 -43
- package/dist/presentation/cli/commands/trust-command.js +115 -99
- package/dist/presentation/cli/error-display.js +17 -21
- package/dist/ui.js +17 -12
- package/package.json +35 -67
- package/dist/agents/base.js +0 -101
- package/dist/agents/orchestrator-model-driven.js +0 -141
- package/dist/agents/react-orchestrator-agent.js +0 -54
- package/dist/application/agents/execution-agent.js +0 -508
- package/dist/application/agents/multi-agent-orchestrator.js +0 -295
- package/dist/application/agents/planning-agent.js +0 -433
- package/dist/application/agents/validation-agent.js +0 -317
- package/dist/application/architectures/plan-and-act.js +0 -242
- package/dist/application/index.js +0 -12
- package/dist/application/services/agent-factory.js +0 -28
- package/dist/application/services/dependency-container.js +0 -596
- package/dist/application/services/git-context-service.js +0 -83
- package/dist/application/services/intelligent-router.js +0 -934
- package/dist/application/services/tool-executor-service.js +0 -94
- package/dist/application/use-cases/ask-question-use-case.js +0 -338
- package/dist/application/use-cases/edit-file-use-case.js +0 -830
- package/dist/application/use-cases/explain-file-use-case.js +0 -50
- package/dist/application/use-cases/index-codebase-use-case.js +0 -113
- package/dist/application/use-cases/planning-use-case.js +0 -276
- package/dist/application/use-cases/react-orchestration-use-case.js +0 -329
- package/dist/application/use-cases/reflexion-use-case.js +0 -30
- package/dist/application/workflows/deterministic-workflows.js +0 -189
- package/dist/ast-parser.js +0 -331
- package/dist/cache.js +0 -180
- package/dist/chat.js +0 -251
- package/dist/code-analysis.js +0 -327
- package/dist/context.js +0 -187
- package/dist/docs.js +0 -146
- package/dist/domain/entities/code-chunk.js +0 -6
- package/dist/domain/entities/decision.js +0 -9
- package/dist/domain/entities/edit.js +0 -6
- package/dist/domain/entities/knowledge-node.js +0 -9
- package/dist/domain/entities/knowledge-preservation.js +0 -10
- package/dist/domain/entities/memory.js +0 -6
- package/dist/domain/entities/pattern.js +0 -7
- package/dist/domain/entities/plan.js +0 -6
- package/dist/domain/entities/query.js +0 -6
- package/dist/domain/errors/embeddings-not-found-error.js +0 -13
- package/dist/domain/index.js +0 -19
- package/dist/domain/interfaces/ai-service.interface.js +0 -6
- package/dist/domain/interfaces/code-analyzer.interface.js +0 -6
- package/dist/domain/interfaces/decision-repository.interface.js +0 -9
- package/dist/domain/interfaces/embedding-repository.interface.js +0 -6
- package/dist/domain/interfaces/knowledge-graph-repository.interface.js +0 -9
- package/dist/domain/interfaces/knowledge-preservation-repository.interface.js +0 -8
- package/dist/domain/interfaces/memory-repository.interface.js +0 -6
- package/dist/domain/interfaces/pattern-repository.interface.js +0 -6
- package/dist/domain/interfaces/verification-service.interface.js +0 -6
- package/dist/domain/services/cross-team-pattern-sharing.js +0 -125
- package/dist/domain/services/decision-recorder.js +0 -223
- package/dist/domain/services/decision-retriever.js +0 -133
- package/dist/domain/services/environment-mode-manager.js +0 -22
- package/dist/domain/services/graded-memory-activation.js +0 -69
- package/dist/domain/services/knowledge-access.js +0 -132
- package/dist/domain/services/knowledge-extractor.js +0 -247
- package/dist/domain/services/knowledge-graph-builder.js +0 -268
- package/dist/domain/services/knowledge-graph-query.js +0 -187
- package/dist/domain/services/memory-retriever.js +0 -82
- package/dist/domain/services/memory-synthesizer.js +0 -91
- package/dist/domain/services/pattern-extractor.js +0 -442
- package/dist/domain/services/query-intent-classifier.js +0 -140
- package/dist/domain/services/react-loop.js +0 -186
- package/dist/domain/services/risk-tier-detector.js +0 -78
- package/dist/edit.js +0 -319
- package/dist/embed.js +0 -459
- package/dist/formatter.js +0 -168
- package/dist/git.js +0 -198
- package/dist/infrastructure/ai/cohere-ai-service.js +0 -607
- package/dist/infrastructure/ai/command-r-evaluation.js +0 -206
- package/dist/infrastructure/ai/model-config.js +0 -84
- package/dist/infrastructure/ai/provider-ai-service.js +0 -477
- package/dist/infrastructure/cache/embedding-cache.js +0 -175
- package/dist/infrastructure/cache/query-result-cache.js +0 -66
- package/dist/infrastructure/cache/response-cache.js +0 -48
- package/dist/infrastructure/cache/security-scan-cache.js +0 -47
- package/dist/infrastructure/chunking/ast-code-chunker.js +0 -19
- package/dist/infrastructure/chunking/code-aware-chunker.js +0 -397
- package/dist/infrastructure/context/context-compressor.js +0 -329
- package/dist/infrastructure/context/conversation-context.js +0 -128
- package/dist/infrastructure/context/session-manager.js +0 -94
- package/dist/infrastructure/cost/cost-tracker.js +0 -360
- package/dist/infrastructure/file-scanner/file-scanner.js +0 -106
- package/dist/infrastructure/governance/nist-ai-rmf.js +0 -292
- package/dist/infrastructure/governance/policy-as-code.js +0 -258
- package/dist/infrastructure/governance/prompt-to-code-lineage.js +0 -239
- package/dist/infrastructure/index.js +0 -7
- package/dist/infrastructure/observability/prompt-versioner.js +0 -145
- package/dist/infrastructure/protocols/a2a-metadata.js +0 -137
- package/dist/infrastructure/protocols/a2a-protocol.js +0 -314
- package/dist/infrastructure/protocols/mcp-oauth.js +0 -184
- package/dist/infrastructure/protocols/mcp-server.js +0 -333
- package/dist/infrastructure/protocols/streaming-handler.js +0 -103
- package/dist/infrastructure/providers/cohere-provider.js +0 -444
- package/dist/infrastructure/providers/fake-provider.js +0 -130
- package/dist/infrastructure/providers/local-llm-provider.js +0 -330
- package/dist/infrastructure/providers/model-config.types.js +0 -9
- package/dist/infrastructure/providers/model-provider.interface.js +0 -8
- package/dist/infrastructure/providers/openai-provider.js +0 -413
- package/dist/infrastructure/providers/openrouter-provider.js +0 -399
- package/dist/infrastructure/providers/operation-provider-resolver.js +0 -105
- package/dist/infrastructure/providers/provider-config-loader.js +0 -120
- package/dist/infrastructure/providers/provider-registry.js +0 -88
- package/dist/infrastructure/safety/pattern-detector.js +0 -133
- package/dist/infrastructure/security/ai-sast-scanner.js +0 -290
- package/dist/infrastructure/security/docker-sandbox.js +0 -178
- package/dist/infrastructure/security/enhanced-security-gate.js +0 -151
- package/dist/infrastructure/security/license-scanner.js +0 -167
- package/dist/infrastructure/security/provider-bias-detector.js +0 -164
- package/dist/infrastructure/security/sandbox-executor.js +0 -64
- package/dist/infrastructure/serialization/toon-serializer.js +0 -119
- package/dist/infrastructure/storage/memory-stream-service.js +0 -204
- package/dist/infrastructure/storage/plan-repository.js +0 -96
- package/dist/infrastructure/storage/sqlite-decision-repository.js +0 -232
- package/dist/infrastructure/storage/sqlite-embedding-repository.js +0 -311
- package/dist/infrastructure/storage/sqlite-knowledge-graph-repository.js +0 -417
- package/dist/infrastructure/storage/sqlite-knowledge-preservation-repository.js +0 -230
- package/dist/infrastructure/storage/sqlite-memory-repository.js +0 -229
- package/dist/infrastructure/storage/sqlite-pattern-repository.js +0 -249
- package/dist/infrastructure/storage/task-helpers.js +0 -197
- package/dist/infrastructure/telemetry/telemetry-service.js +0 -167
- package/dist/infrastructure/ux/appropriate-friction.js +0 -213
- package/dist/infrastructure/ux/bi-directional-learning.js +0 -337
- package/dist/infrastructure/ux/capability-boundaries.js +0 -42
- package/dist/infrastructure/ux/comprehensive-formatter.js +0 -117
- package/dist/infrastructure/ux/contextual-feature-discovery.js +0 -10
- package/dist/infrastructure/ux/expectation-management.js +0 -327
- package/dist/infrastructure/ux/feature-discovery.js +0 -14
- package/dist/infrastructure/ux/feedback-prompt.js +0 -45
- package/dist/infrastructure/ux/hitl-validation.js +0 -297
- package/dist/infrastructure/ux/progress-indicator.js +0 -116
- package/dist/infrastructure/ux/progressive-disclosure.js +0 -236
- package/dist/infrastructure/ux/review-checkpoint.js +0 -72
- package/dist/infrastructure/ux/staged-feature-intro.js +0 -24
- package/dist/infrastructure/ux/stream-markdown.js +0 -151
- package/dist/infrastructure/ux/syntax-highlighter.js +0 -91
- package/dist/infrastructure/ux/team-standards.js +0 -196
- package/dist/infrastructure/ux/transparency.js +0 -71
- package/dist/infrastructure/validation/agent-feedback.js +0 -55
- package/dist/infrastructure/validation/agent-validation-helper.js +0 -137
- package/dist/infrastructure/validation/review-handler.js +0 -40
- package/dist/infrastructure/validation/summary-aggregator.js +0 -70
- package/dist/infrastructure/validation/summary-extractor.js +0 -45
- package/dist/infrastructure/validation/trace-summary.js +0 -52
- package/dist/infrastructure/verification/verification-service.js +0 -47
- package/dist/infrastructure/xai/attention-visualizer.js +0 -172
- package/dist/infrastructure/xai/cot-visualizer.js +0 -283
- package/dist/infrastructure/xai/decision-tree-log.js +0 -212
- package/dist/infrastructure/xai/interactive-xai.js +0 -260
- package/dist/infrastructure/xai/uncertainty-quantifier.js +0 -198
- package/dist/intelligent-retrieval.js +0 -272
- package/dist/intent/classifier.js +0 -2
- package/dist/intent/context.js +0 -2
- package/dist/iterative-refinement.js +0 -189
- package/dist/learning.js +0 -232
- package/dist/log.js +0 -31
- package/dist/migrate.js +0 -133
- package/dist/parallel-processor.js +0 -530
- package/dist/planner.js +0 -198
- package/dist/policy.js +0 -26
- package/dist/presentation/cli/commands/ask-command.js +0 -314
- package/dist/presentation/cli/commands/cost-command.js +0 -191
- package/dist/presentation/cli/commands/decision-command.js +0 -207
- package/dist/presentation/cli/commands/docs-command.js +0 -25
- package/dist/presentation/cli/commands/fix-command.js +0 -53
- package/dist/presentation/cli/commands/knowledge-command.js +0 -129
- package/dist/presentation/cli/commands/learning-dashboard-command.js +0 -183
- package/dist/presentation/cli/commands/models-command.js +0 -194
- package/dist/presentation/cli/commands/orchestrate-command.js +0 -207
- package/dist/presentation/cli/commands/plan-command.js +0 -246
- package/dist/presentation/cli/commands/product-command.js +0 -199
- package/dist/presentation/cli/commands/react-command.js +0 -181
- package/dist/presentation/cli/commands/review-command.js +0 -154
- package/dist/presentation/cli/commands/run-command.js +0 -188
- package/dist/presentation/cli/commands/smart-command.js +0 -395
- package/dist/presentation/cli/commands/suggest-command.js +0 -138
- package/dist/presentation/cli/commands/trace-command.js +0 -129
- package/dist/presentation/cli/commands/undo-command.js +0 -150
- package/dist/query-optimizer.js +0 -205
- package/dist/refactor.js +0 -116
- package/dist/review.js +0 -206
- package/dist/search.js +0 -163
- package/dist/session.js +0 -652
- package/dist/task-verification.js +0 -336
- package/dist/testgen.js +0 -166
- package/dist/token-optimizer.js +0 -277
- package/dist/tools/bash.js +0 -213
- package/dist/tools/edit.js +0 -235
- package/dist/tools/executor.js +0 -153
- package/dist/tools/git-tool.js +0 -292
- package/dist/tools/parser.js +0 -158
- package/dist/tools/registry.js +0 -302
- package/dist/tools/search.js +0 -100
- package/dist/types.js +0 -2
- package/dist/utils/check-api-status.js +0 -100
- package/dist/utils/diff-parser.js +0 -162
- package/dist/verification/backup.js +0 -259
- package/dist/verification/compiler.js +0 -264
- package/dist/verification/index.js +0 -273
- package/dist/verification/test-runner.js +0 -379
|
@@ -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
|
|
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
|
-
})
|
|
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 {
|
|
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
|
-
|
|
170
|
-
|
|
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,
|
|
201
|
-
|
|
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'
|
|
225
|
-
|
|
226
|
-
|
|
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
|
-
|
|
243
|
-
|
|
244
|
-
|
|
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
|
|
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 +
|
|
253
|
-
console.log(` ${label('Scans')}${
|
|
254
|
-
console.log(` ${label('Blocked')}${
|
|
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')}${
|
|
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]
|
|
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.
|
|
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('
|
|
561
|
+
console.log(colors.dim(' /codehere:trust /codehere:report /codehere:audit /codehere:hook-status'));
|
|
280
562
|
}
|
|
281
563
|
else {
|
|
282
|
-
console.log(colors.dim('
|
|
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'
|
|
294
|
-
|
|
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
|
|
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([
|
|
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
|
|
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)
|