codehere 1.0.1 → 1.0.2
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 +36 -192
- package/dist/application/use-cases/check-claimed-tests.js +81 -0
- package/dist/application/use-cases/detect-test-weakening.js +237 -0
- package/dist/application/use-cases/rerun-tests.js +110 -7
- package/dist/index.js +38 -1212
- package/dist/infrastructure/orchestration/artifacts-extractor.js +2 -323
- package/dist/presentation/cli/commands/check-command.js +178 -23
- package/dist/presentation/cli/commands/help-command.js +21 -285
- package/dist/ui.js +5 -236
- package/dist/utils/version.js +0 -6
- package/package.json +9 -38
- package/dist/application/services/verdict-service.js +0 -154
- package/dist/application/use-cases/assess-cross-vendor.js +0 -126
- package/dist/application/use-cases/build-cross-vendor-verdict.js +0 -118
- package/dist/application/use-cases/domain-trust.js +0 -110
- package/dist/application/use-cases/export-aat.js +0 -107
- package/dist/application/use-cases/generate-acceptance-criteria.js +0 -223
- package/dist/application/use-cases/generate-final-report.js +0 -259
- package/dist/application/use-cases/generate-narration.js +0 -130
- package/dist/application/use-cases/generate-thread-title.js +0 -181
- package/dist/application/use-cases/narrator-prompt-template.js +0 -158
- package/dist/application/use-cases/plan-auto-review.js +0 -57
- package/dist/application/use-cases/reconcile-audit.js +0 -100
- package/dist/application/use-cases/search-sessions.js +0 -70
- package/dist/application/use-cases/should-auto-review.js +0 -71
- package/dist/application/use-cases/thread-export.js +0 -245
- package/dist/application/use-cases/verify-claim.js +0 -456
- package/dist/application/use-cases/write-pass.js +0 -277
- package/dist/domain/audit.js +0 -11
- package/dist/domain/services/codebase-detector.js +0 -85
- package/dist/domain/task.js +0 -91
- package/dist/error-handler.js +0 -139
- package/dist/infrastructure/completion/bash-completion.js +0 -29
- package/dist/infrastructure/completion/fish-completion.js +0 -17
- package/dist/infrastructure/completion/zsh-completion.js +0 -25
- package/dist/infrastructure/config/config-manager.js +0 -174
- package/dist/infrastructure/config/product-context.js +0 -49
- package/dist/infrastructure/context/adapters/drawio-source.js +0 -157
- package/dist/infrastructure/context/adapters/figma-source.js +0 -198
- package/dist/infrastructure/context/adapters/github-issue-source.js +0 -160
- package/dist/infrastructure/context/adapters/linear-source.js +0 -190
- package/dist/infrastructure/context/context-resolver.js +0 -144
- package/dist/infrastructure/context/context-source.js +0 -114
- package/dist/infrastructure/context/github-api.js +0 -110
- package/dist/infrastructure/context/github-device-flow.js +0 -65
- package/dist/infrastructure/context/oauth-flow.js +0 -207
- package/dist/infrastructure/context/oauth-store.js +0 -122
- package/dist/infrastructure/context/registry-bootstrap.js +0 -95
- package/dist/infrastructure/context/write-pass-resolver.js +0 -106
- package/dist/infrastructure/cost/budget-config.js +0 -114
- package/dist/infrastructure/cost/cost-router.js +0 -97
- package/dist/infrastructure/cost/spend-aggregator.js +0 -222
- package/dist/infrastructure/cost/team-ledger.js +0 -355
- package/dist/infrastructure/discovery/discovery-log.js +0 -85
- package/dist/infrastructure/hooks/audit-tip-signature.js +0 -336
- package/dist/infrastructure/hooks/claude-sdk-hook.js +0 -1018
- package/dist/infrastructure/hooks/hook-mode.js +0 -87
- package/dist/infrastructure/hooks/mirror-tool-call-to-audit.js +0 -49
- package/dist/infrastructure/hooks/pre-prompt-scan.js +0 -122
- package/dist/infrastructure/hooks/session-analyzer.js +0 -177
- package/dist/infrastructure/observability/execution-tracer.js +0 -227
- package/dist/infrastructure/orchestration/agent-adapter.js +0 -13
- package/dist/infrastructure/orchestration/agent-installer.js +0 -101
- package/dist/infrastructure/orchestration/agents-config-seed.js +0 -68
- package/dist/infrastructure/orchestration/aider-adapter.js +0 -611
- package/dist/infrastructure/orchestration/cassette-recorder.js +0 -120
- package/dist/infrastructure/orchestration/claude-code-adapter.js +0 -592
- package/dist/infrastructure/orchestration/codex-adapter.js +0 -574
- package/dist/infrastructure/orchestration/conductor-adapter.js +0 -416
- package/dist/infrastructure/orchestration/context-handoff.js +0 -162
- package/dist/infrastructure/orchestration/control/codex-control-surface.js +0 -99
- package/dist/infrastructure/orchestration/control/control-surface.js +0 -15
- package/dist/infrastructure/orchestration/detect-ollama.js +0 -75
- package/dist/infrastructure/orchestration/failover-classifier.js +0 -151
- package/dist/infrastructure/orchestration/failover-config.js +0 -67
- package/dist/infrastructure/orchestration/failover-policy.js +0 -106
- package/dist/infrastructure/orchestration/gemini-adapter.js +0 -663
- package/dist/infrastructure/orchestration/generic-cli-adapter.js +0 -689
- package/dist/infrastructure/orchestration/graphify-context.js +0 -209
- package/dist/infrastructure/orchestration/memory-injector.js +0 -380
- package/dist/infrastructure/orchestration/mocks/mock-agent-repl.js +0 -388
- package/dist/infrastructure/orchestration/mocks/mock-codex-app-server.js +0 -73
- package/dist/infrastructure/orchestration/mocks/mock-synthesis.js +0 -83
- package/dist/infrastructure/orchestration/mocks/resolve-agent-spawn.js +0 -37
- package/dist/infrastructure/orchestration/opencode-adapter.js +0 -580
- package/dist/infrastructure/orchestration/progress-aggregator.js +0 -906
- package/dist/infrastructure/orchestration/readiness.js +0 -103
- package/dist/infrastructure/orchestration/session-manager.js +0 -1590
- package/dist/infrastructure/orchestration/session-persistence.js +0 -394
- package/dist/infrastructure/orchestration/thread-key-store.js +0 -127
- package/dist/infrastructure/orchestration/write-pass-store.js +0 -38
- package/dist/infrastructure/protocols/cloud-sync.js +0 -207
- package/dist/infrastructure/protocols/mcp-helpers.js +0 -25
- package/dist/infrastructure/protocols/mcp-server-impl.js +0 -3935
- package/dist/infrastructure/protocols/mcp-tools.js +0 -401
- package/dist/infrastructure/protocols/probe-port.js +0 -36
- package/dist/infrastructure/protocols/thread-phase.js +0 -105
- package/dist/infrastructure/protocols/ui/app-script-artifacts.js +0 -121
- package/dist/infrastructure/protocols/ui/app-script-audit-footer.js +0 -44
- package/dist/infrastructure/protocols/ui/app-script-audit-verify.js +0 -56
- package/dist/infrastructure/protocols/ui/app-script-composer-cost.js +0 -588
- package/dist/infrastructure/protocols/ui/app-script-context-sources.js +0 -168
- package/dist/infrastructure/protocols/ui/app-script-conversation.js +0 -67
- package/dist/infrastructure/protocols/ui/app-script-drawer.js +0 -115
- package/dist/infrastructure/protocols/ui/app-script-failover.js +0 -133
- package/dist/infrastructure/protocols/ui/app-script-github.js +0 -113
- package/dist/infrastructure/protocols/ui/app-script-observer.js +0 -104
- package/dist/infrastructure/protocols/ui/app-script-onboarding.js +0 -80
- package/dist/infrastructure/protocols/ui/app-script-overlay.js +0 -203
- package/dist/infrastructure/protocols/ui/app-script-recovery.js +0 -136
- package/dist/infrastructure/protocols/ui/app-script-render.js +0 -166
- package/dist/infrastructure/protocols/ui/app-script-share.js +0 -78
- package/dist/infrastructure/protocols/ui/app-script-sidebar.js +0 -194
- package/dist/infrastructure/protocols/ui/app-script-tour.js +0 -78
- package/dist/infrastructure/protocols/ui/app-script-wpass.js +0 -312
- package/dist/infrastructure/protocols/ui/app-script.js +0 -2998
- package/dist/infrastructure/protocols/ui/font-faces.js +0 -7
- package/dist/infrastructure/protocols/ui/styles.js +0 -2147
- package/dist/infrastructure/protocols/ui/tokens.js +0 -96
- package/dist/infrastructure/protocols/ui-page-v2.js +0 -320
- package/dist/infrastructure/protocols/verify-page.js +0 -1677
- package/dist/infrastructure/security/file-collection.js +0 -118
- package/dist/infrastructure/security/fix-code.js +0 -144
- package/dist/infrastructure/security/fix-secret.js +0 -89
- package/dist/infrastructure/security/permissions-config.js +0 -235
- package/dist/infrastructure/security/scan-types.js +0 -10
- package/dist/infrastructure/security/ship-scan.js +0 -195
- package/dist/infrastructure/security/trust-scorer.js +0 -724
- package/dist/infrastructure/security/vibe-checks.js +0 -99
- package/dist/infrastructure/storage/atomic-write.js +0 -86
- package/dist/infrastructure/storage/cross-tool-agents.js +0 -268
- package/dist/infrastructure/storage/project-thread-migration.js +0 -237
- package/dist/infrastructure/storage/project-thread-mirror.js +0 -671
- package/dist/infrastructure/storage/project-thread-reader.js +0 -409
- package/dist/infrastructure/storage/project-thread-title-backfill.js +0 -214
- package/dist/monitoring.js +0 -212
- package/dist/presentation/cli/commands/audit-command.js +0 -201
- package/dist/presentation/cli/commands/budget-command.js +0 -79
- package/dist/presentation/cli/commands/config-command.js +0 -125
- package/dist/presentation/cli/commands/context-source-command.js +0 -217
- package/dist/presentation/cli/commands/hook-command.js +0 -265
- package/dist/presentation/cli/commands/init-command.js +0 -590
- package/dist/presentation/cli/commands/memory-command.js +0 -1031
- package/dist/presentation/cli/commands/narrator-command.js +0 -101
- package/dist/presentation/cli/commands/permission-command.js +0 -105
- package/dist/presentation/cli/commands/protocols-builder.js +0 -254
- package/dist/presentation/cli/commands/protocols-command.js +0 -84
- package/dist/presentation/cli/commands/repo-map-builder.js +0 -247
- package/dist/presentation/cli/commands/report-command.js +0 -403
- package/dist/presentation/cli/commands/scan-command.js +0 -52
- package/dist/presentation/cli/commands/scan-tool-command.js +0 -110
- package/dist/presentation/cli/commands/search-command.js +0 -51
- package/dist/presentation/cli/commands/setup-command.js +0 -330
- package/dist/presentation/cli/commands/status-command.js +0 -614
- package/dist/presentation/cli/commands/team-command.js +0 -256
- package/dist/presentation/cli/commands/thread-command.js +0 -370
- package/dist/presentation/cli/commands/trust-command.js +0 -328
- package/dist/presentation/cli/commands/verify-command.js +0 -298
- package/dist/presentation/cli/error-display.js +0 -328
- package/dist/presentation/cli/repl.js +0 -805
- package/dist/presentation/cli/task-deliverable.js +0 -153
- package/dist/testing/cassette.js +0 -125
- package/dist/testing/replay-adapter.js +0 -147
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* PM Narration — v0.6 M3 mid-task summary.
|
|
3
|
-
*
|
|
4
|
-
* While a task is in flight, periodically generates a one-paragraph
|
|
5
|
-
* plain-English summary of what the agent has done so far. Fires on
|
|
6
|
-
* step-count or elapsed-time thresholds (caller decides when).
|
|
7
|
-
*
|
|
8
|
-
* Per claim_user_persona HIGH: non-technical users primary. They don't
|
|
9
|
-
* read tool calls; they read narration. Mid-task narration tells them
|
|
10
|
-
* "the agent is currently editing your auth file and ran the tests"
|
|
11
|
-
* instead of forcing them to interpret a list of Bash/Edit/Read steps.
|
|
12
|
-
*
|
|
13
|
-
* Per feedback_overconfidence_guard.md: narration is an LLM call.
|
|
14
|
-
* Hallucination is possible. Surface MUST frame it as "narrator says…"
|
|
15
|
-
* not authoritative truth. The PROMPT_TEMPLATE includes the same
|
|
16
|
-
* anti-hallucination rule as generate-final-report (ground every claim
|
|
17
|
-
* in the structured data).
|
|
18
|
-
*
|
|
19
|
-
* Provider strategy: env override, else claude (the dead claude-free
|
|
20
|
-
* first, fall back to claude). Returns null if neither is installed.
|
|
21
|
-
*/
|
|
22
|
-
import { spawn } from 'child_process';
|
|
23
|
-
import { loadTemplate, renderTemplate } from './narrator-prompt-template.js';
|
|
24
|
-
import { isMockMode } from '../../infrastructure/orchestration/mocks/resolve-agent-spawn.js';
|
|
25
|
-
import { mockNarration } from '../../infrastructure/orchestration/mocks/mock-synthesis.js';
|
|
26
|
-
/** v0.6 S6 — prompt template lifted into ~/.codehere/narrator-prompt.md so
|
|
27
|
-
* the user can edit the agent's voice. loadTemplate() falls back to the
|
|
28
|
-
* baked-in default on missing file or missing required placeholders, so
|
|
29
|
-
* the prompt is never silently broken. See narrator-prompt-template.ts. */
|
|
30
|
-
function buildPrompt(input) {
|
|
31
|
-
const { template } = loadTemplate();
|
|
32
|
-
return renderTemplate(template, input);
|
|
33
|
-
}
|
|
34
|
-
function selectProviderBinary() {
|
|
35
|
-
// MED #27 (atomic audit) · the old chain led with 'claude-free' (the dead
|
|
36
|
-
// FCC/NIM proxy final-report.ts abandoned) — every narration burned its
|
|
37
|
-
// timeout against a down proxy first. Env override → claude, that's it.
|
|
38
|
-
const override = process.env.CODEHERE_NARRATOR_BINARY?.trim();
|
|
39
|
-
if (override)
|
|
40
|
-
return [override];
|
|
41
|
-
return ['claude'];
|
|
42
|
-
}
|
|
43
|
-
export async function generateNarration(input) {
|
|
44
|
-
if (isMockMode()) {
|
|
45
|
-
const m = mockNarration({ steps: input.steps });
|
|
46
|
-
return {
|
|
47
|
-
summary: m.summary,
|
|
48
|
-
generatedAt: Date.now(),
|
|
49
|
-
durationMs: m.durationMs,
|
|
50
|
-
stepCountAtSnapshot: input.steps.length,
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
const prompt = buildPrompt(input);
|
|
54
|
-
const candidates = selectProviderBinary();
|
|
55
|
-
for (const binary of candidates) {
|
|
56
|
-
const result = await tryProvider(binary, prompt, input.sessionId, input.steps.length);
|
|
57
|
-
if (result)
|
|
58
|
-
return result;
|
|
59
|
-
}
|
|
60
|
-
return null;
|
|
61
|
-
}
|
|
62
|
-
function tryProvider(binary, prompt, sessionId, stepCount) {
|
|
63
|
-
return new Promise((resolve) => {
|
|
64
|
-
const startedAt = Date.now();
|
|
65
|
-
let stdout = '';
|
|
66
|
-
let stderr = '';
|
|
67
|
-
let resolved = false;
|
|
68
|
-
const finish = (val) => {
|
|
69
|
-
if (resolved)
|
|
70
|
-
return;
|
|
71
|
-
resolved = true;
|
|
72
|
-
resolve(val);
|
|
73
|
-
};
|
|
74
|
-
let child;
|
|
75
|
-
try {
|
|
76
|
-
// Same shape as generate-final-report's tryProvider — claude --bare -p.
|
|
77
|
-
child = spawn(binary, ['--bare', '-p', prompt], {
|
|
78
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
catch {
|
|
82
|
-
finish(null);
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
// 20s timeout — narration should be SNAPPIER than Final Report
|
|
86
|
-
// (which is 60s). Mid-task = the user is actively waiting.
|
|
87
|
-
const timeout = setTimeout(() => {
|
|
88
|
-
try {
|
|
89
|
-
child.kill();
|
|
90
|
-
}
|
|
91
|
-
catch {
|
|
92
|
-
/* ignore */
|
|
93
|
-
}
|
|
94
|
-
finish(null);
|
|
95
|
-
}, 20_000);
|
|
96
|
-
child.stdout?.on('data', (chunk) => {
|
|
97
|
-
stdout += chunk.toString('utf-8');
|
|
98
|
-
});
|
|
99
|
-
child.stderr?.on('data', (chunk) => {
|
|
100
|
-
stderr += chunk.toString('utf-8');
|
|
101
|
-
});
|
|
102
|
-
child.on('close', (code) => {
|
|
103
|
-
clearTimeout(timeout);
|
|
104
|
-
if (code !== 0) {
|
|
105
|
-
// Non-zero usually means binary missing OR the model refused. Either
|
|
106
|
-
// way silent fail; caller treats null as "no narration this cycle".
|
|
107
|
-
void stderr;
|
|
108
|
-
void sessionId;
|
|
109
|
-
finish(null);
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
const summary = stdout.trim();
|
|
113
|
-
if (!summary || summary.length < 10) {
|
|
114
|
-
finish(null);
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
finish({
|
|
118
|
-
summary,
|
|
119
|
-
generatedAt: Date.now(),
|
|
120
|
-
durationMs: Date.now() - startedAt,
|
|
121
|
-
stepCountAtSnapshot: stepCount,
|
|
122
|
-
});
|
|
123
|
-
});
|
|
124
|
-
child.on('error', () => {
|
|
125
|
-
clearTimeout(timeout);
|
|
126
|
-
finish(null);
|
|
127
|
-
});
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
//# sourceMappingURL=generate-narration.js.map
|
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Hidden Title agent — v0.6.0 M9.
|
|
3
|
-
*
|
|
4
|
-
* Generates a 4-6 word title for a project thread from its first user prompt.
|
|
5
|
-
* Hidden = users don't pick it from any picker; fires automatically when the
|
|
6
|
-
* project thread gets its first task entry. The title surfaces in the browser
|
|
7
|
-
* tab + future thread switcher UI.
|
|
8
|
-
*
|
|
9
|
-
* Companion to generate-final-report.ts:
|
|
10
|
-
* - Provider: env override → claude → graceful skip (dead claude-free hop removed)
|
|
11
|
-
* - Same single-shot, no streaming, returns string
|
|
12
|
-
* - Different prompt (terse title generation, NOT 3-paragraph narrative)
|
|
13
|
-
* - Cheaper budget (~50 tokens per title vs 200 per Final Report paragraph)
|
|
14
|
-
*
|
|
15
|
-
* Triggering: project-thread-mirror invokes this when seeding the project-meta
|
|
16
|
-
* header for a NEW project (i.e., this cwd's thread.jsonl didn't exist before).
|
|
17
|
-
* Idempotent: subsequent task entries do NOT regenerate the title.
|
|
18
|
-
*
|
|
19
|
-
* Cost: ~1 LLM call per NEW project (not per task). At a typical 1-2 new
|
|
20
|
-
* projects per week per user, the cost is negligible. FCC-routed when
|
|
21
|
-
* available; degrades silently to "(untitled)" when no provider responds.
|
|
22
|
-
*/
|
|
23
|
-
import { spawn } from 'child_process';
|
|
24
|
-
import { isMockMode } from '../../infrastructure/orchestration/mocks/resolve-agent-spawn.js';
|
|
25
|
-
import { mockTitle } from '../../infrastructure/orchestration/mocks/mock-synthesis.js';
|
|
26
|
-
const TITLE_TIMEOUT_MS = 15_000; // titles are short; abort fast if stuck
|
|
27
|
-
const PROMPT_TEMPLATE = (firstPrompt, cwd) => `Generate a 4-6 word project title from this first user prompt.
|
|
28
|
-
|
|
29
|
-
Project working directory: ${cwd}
|
|
30
|
-
First prompt: ${firstPrompt}
|
|
31
|
-
|
|
32
|
-
Rules:
|
|
33
|
-
- 4 to 6 words MAX
|
|
34
|
-
- No quotes, no punctuation, no trailing period
|
|
35
|
-
- Title-case (e.g. "Auth Bug Fix Sprint")
|
|
36
|
-
- Ground the title in the prompt + cwd ONLY. Do NOT invent project subject
|
|
37
|
-
matter you can't derive from the data above (e.g. don't infer "Thread
|
|
38
|
-
Interface Updates" from a "list files" prompt — say what the user
|
|
39
|
-
actually asked, not what you imagine the project is about).
|
|
40
|
-
- If the prompt is too generic to title meaningfully, prefer naming the
|
|
41
|
-
cwd's last path segment (e.g. "Taste V0.6 Workspace") over invention.
|
|
42
|
-
- Output ONLY the title — no preamble, no explanation
|
|
43
|
-
|
|
44
|
-
Title:`;
|
|
45
|
-
function selectProviderBinary() {
|
|
46
|
-
// MED #28 (atomic audit) · same dead 'claude-free' first hop as narration —
|
|
47
|
-
// removed so a title either comes from claude or degrades fast, not after
|
|
48
|
-
// a proxy timeout.
|
|
49
|
-
const override = process.env.CODEHERE_TITLE_BINARY;
|
|
50
|
-
if (override)
|
|
51
|
-
return [override];
|
|
52
|
-
return ['claude'];
|
|
53
|
-
}
|
|
54
|
-
function tryProvider(binary, prompt) {
|
|
55
|
-
return new Promise((resolve) => {
|
|
56
|
-
let stdout = '';
|
|
57
|
-
let settled = false;
|
|
58
|
-
const finish = (result) => {
|
|
59
|
-
if (settled)
|
|
60
|
-
return;
|
|
61
|
-
settled = true;
|
|
62
|
-
resolve(result);
|
|
63
|
-
};
|
|
64
|
-
const child = spawn(binary, ['--bare', '-p', prompt], {
|
|
65
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
66
|
-
});
|
|
67
|
-
const timeout = setTimeout(() => {
|
|
68
|
-
child.kill('SIGTERM');
|
|
69
|
-
if (process.env.CODEHERE_DEBUG === '1') {
|
|
70
|
-
console.error(`[title-agent] ${binary} timeout after ${TITLE_TIMEOUT_MS}ms`);
|
|
71
|
-
}
|
|
72
|
-
finish(null);
|
|
73
|
-
}, TITLE_TIMEOUT_MS);
|
|
74
|
-
child.on('error', () => {
|
|
75
|
-
clearTimeout(timeout);
|
|
76
|
-
finish(null); // ENOENT etc. — silent fallback to next provider
|
|
77
|
-
});
|
|
78
|
-
child.stdout?.on('data', (chunk) => {
|
|
79
|
-
stdout += chunk.toString('utf-8');
|
|
80
|
-
});
|
|
81
|
-
child.on('exit', (code) => {
|
|
82
|
-
clearTimeout(timeout);
|
|
83
|
-
if (code !== 0) {
|
|
84
|
-
finish(null);
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
const title = sanitizeTitle(stdout);
|
|
88
|
-
if (!title) {
|
|
89
|
-
finish(null);
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
finish(title);
|
|
93
|
-
});
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* Strip whitespace / quotes / trailing punctuation. Cap at 80 chars.
|
|
98
|
-
*
|
|
99
|
-
* Garbage rejection (added 2026-05-06 after dogfood Bug A4): the underlying
|
|
100
|
-
* `claude --bare` call can return error-shaped text on stdout with exit 0
|
|
101
|
-
* (rate-limit messages, auth errors, refusals). Pre-fix those flowed straight
|
|
102
|
-
* into the project sidebar as titles like "Request ID: req_xyz...". We now
|
|
103
|
-
* detect obvious error / refusal patterns and return '' so the caller falls
|
|
104
|
-
* back to the cwd-derived title.
|
|
105
|
-
*/
|
|
106
|
-
export function sanitizeTitle(raw) {
|
|
107
|
-
const lines = raw
|
|
108
|
-
.split('\n')
|
|
109
|
-
.map((l) => l.trim())
|
|
110
|
-
.filter(Boolean);
|
|
111
|
-
if (lines.length === 0)
|
|
112
|
-
return '';
|
|
113
|
-
let title = lines[lines.length - 1]; // last non-empty line is the answer
|
|
114
|
-
// Strip surrounding quotes
|
|
115
|
-
title = title.replace(/^["'`]+|["'`]+$/g, '');
|
|
116
|
-
// Strip trailing punctuation
|
|
117
|
-
title = title.replace(/[.!?,;:]+$/, '');
|
|
118
|
-
// Cap at 80 chars
|
|
119
|
-
if (title.length > 80)
|
|
120
|
-
title = title.slice(0, 77) + '...';
|
|
121
|
-
title = title.trim();
|
|
122
|
-
if (!title)
|
|
123
|
-
return '';
|
|
124
|
-
// Garbage rejection — patterns that indicate an error response, NOT a title.
|
|
125
|
-
// Each pattern matches a real failure mode observed in dogfood:
|
|
126
|
-
// - "Request ID: req_..." → API error envelope leaking through
|
|
127
|
-
// - "Error:" / "ERROR" / "error " → generic error prefix
|
|
128
|
-
// - "I cannot" / "I can't" → model refusal
|
|
129
|
-
// - "I apologize" / "I'm sorry" → model refusal
|
|
130
|
-
// - "Rate limit" / "rate-limited" → quota error
|
|
131
|
-
// - "Authentication" / "Unauthorized" / "401" / "403" → auth failure
|
|
132
|
-
// - Long sentences (>10 words) → not a title; model ignored the rules
|
|
133
|
-
// - Contains http:// or https:// → URL leakage from error envelope
|
|
134
|
-
const lower = title.toLowerCase();
|
|
135
|
-
const garbageMarkers = [
|
|
136
|
-
/^request\s+id\s*[:=]/i,
|
|
137
|
-
/^error\b/i,
|
|
138
|
-
/\berror[:\s]/i,
|
|
139
|
-
/^i\s+(cannot|can'?t|am\s+unable|won'?t)\b/i,
|
|
140
|
-
/^i\s+(apologi[sz]e|am\s+sorry)\b/i,
|
|
141
|
-
/^i'?m\s+sorry\b/i,
|
|
142
|
-
/\brate\s*[-_]?\s*limit/i,
|
|
143
|
-
/\b(authentication|unauthori[sz]ed|forbidden)\b/i,
|
|
144
|
-
/\b(401|403|429|500|502|503)\b/i,
|
|
145
|
-
/https?:\/\//i,
|
|
146
|
-
/api[_-]?key/i,
|
|
147
|
-
];
|
|
148
|
-
if (garbageMarkers.some((re) => re.test(lower))) {
|
|
149
|
-
if (process.env.CODEHERE_DEBUG === '1') {
|
|
150
|
-
console.error(`[title-agent] rejected garbage title: ${title.slice(0, 60)}`);
|
|
151
|
-
}
|
|
152
|
-
return '';
|
|
153
|
-
}
|
|
154
|
-
// Length sanity: a real title is 4-6 words per the prompt; allow up to 10
|
|
155
|
-
// for slack. Anything longer is the model ignoring instructions.
|
|
156
|
-
const wordCount = title.split(/\s+/).filter(Boolean).length;
|
|
157
|
-
if (wordCount > 10) {
|
|
158
|
-
if (process.env.CODEHERE_DEBUG === '1') {
|
|
159
|
-
console.error(`[title-agent] rejected long title (${wordCount} words): ${title.slice(0, 60)}`);
|
|
160
|
-
}
|
|
161
|
-
return '';
|
|
162
|
-
}
|
|
163
|
-
return title;
|
|
164
|
-
}
|
|
165
|
-
/**
|
|
166
|
-
* Generate a thread title from the first user prompt + cwd context.
|
|
167
|
-
* Returns the title string, or null if no provider is reachable.
|
|
168
|
-
* Never throws — failure modes return null.
|
|
169
|
-
*/
|
|
170
|
-
export async function generateThreadTitle(firstPrompt, cwd) {
|
|
171
|
-
if (isMockMode())
|
|
172
|
-
return sanitizeTitle(mockTitle(firstPrompt, cwd)) || null;
|
|
173
|
-
const prompt = PROMPT_TEMPLATE(firstPrompt, cwd);
|
|
174
|
-
for (const binary of selectProviderBinary()) {
|
|
175
|
-
const result = await tryProvider(binary, prompt);
|
|
176
|
-
if (result)
|
|
177
|
-
return result;
|
|
178
|
-
}
|
|
179
|
-
return null;
|
|
180
|
-
}
|
|
181
|
-
//# sourceMappingURL=generate-thread-title.js.map
|
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* v0.6 S6 — editable PM narrator prompt template.
|
|
3
|
-
*
|
|
4
|
-
* The mid-task narration (M3) used to ship a hard-coded prompt baked into
|
|
5
|
-
* generate-narration.ts. That made the agent's "voice" un-tunable: a user
|
|
6
|
-
* who wanted shorter / longer / more-jargony / less-jargony narration
|
|
7
|
-
* had no lever short of forking the source.
|
|
8
|
-
*
|
|
9
|
-
* S6 lifts the template into ~/.codehere/narrator-prompt.md, a plain
|
|
10
|
-
* Markdown file the user edits with whatever editor they like. The file
|
|
11
|
-
* uses `{{placeholder}}` substitution for the runtime values:
|
|
12
|
-
* {{prompt}} — the user's original task ask (truncated 500ch)
|
|
13
|
-
* {{agent}} — which adapter is running ("claude-code" etc.)
|
|
14
|
-
* {{elapsed_seconds}} — how long the task has been running
|
|
15
|
-
* {{step_count}} — how many steps completed/in-progress
|
|
16
|
-
* {{steps}} — last 15 steps, " N. [state] label"
|
|
17
|
-
*
|
|
18
|
-
* If the file is missing OR the user-edited file is missing required
|
|
19
|
-
* placeholders, we fall back to the baked-in DEFAULT_TEMPLATE so a typo
|
|
20
|
-
* never breaks narration silently.
|
|
21
|
-
*
|
|
22
|
-
* Per claim_user_persona HIGH: the narrator IS the product voice for
|
|
23
|
-
* non-technical users. Letting them control it is a v1 promise.
|
|
24
|
-
*
|
|
25
|
-
* Per feedback_overconfidence_guard.md: the DEFAULT_TEMPLATE keeps the
|
|
26
|
-
* anti-hallucination rule + "frame as narrator not authority" cue. Users
|
|
27
|
-
* editing the template can drop those rules at their own risk; the file
|
|
28
|
-
* comment warns about it.
|
|
29
|
-
*/
|
|
30
|
-
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'fs';
|
|
31
|
-
import { dirname, join } from 'path';
|
|
32
|
-
import { homedir } from 'os';
|
|
33
|
-
/** The required placeholders. If a user-edited template is missing ANY of
|
|
34
|
-
* these we fall back to the default so the rendered prompt is never
|
|
35
|
-
* silently broken. {{step_count}} is omitted from the required list — it's
|
|
36
|
-
* derivable from {{steps}} and a custom template might intentionally drop it. */
|
|
37
|
-
const REQUIRED_PLACEHOLDERS = ['{{prompt}}', '{{agent}}', '{{elapsed_seconds}}', '{{steps}}'];
|
|
38
|
-
export const DEFAULT_TEMPLATE = `<!--
|
|
39
|
-
codehere narrator prompt — drives the mid-task summary the user sees as
|
|
40
|
-
"narrator says…" while their AI coding agent is working. Edit freely.
|
|
41
|
-
|
|
42
|
-
Available placeholders (substituted at render time):
|
|
43
|
-
{{prompt}} user's original task ask
|
|
44
|
-
{{agent}} which adapter is running
|
|
45
|
-
{{elapsed_seconds}} how long the task has been running
|
|
46
|
-
{{step_count}} number of steps completed/in-progress
|
|
47
|
-
{{steps}} last 15 steps, formatted as " N. [state] label"
|
|
48
|
-
|
|
49
|
-
REQUIRED: {{prompt}}, {{agent}}, {{elapsed_seconds}}, {{steps}}.
|
|
50
|
-
Missing any of those → codehere falls back to its baked-in default.
|
|
51
|
-
|
|
52
|
-
Reset to default any time with: codehere narrator reset
|
|
53
|
-
-->
|
|
54
|
-
|
|
55
|
-
You are a PM giving a 1-sentence-or-2 mid-task update to a non-technical user
|
|
56
|
-
about what their AI coding agent is doing RIGHT NOW. Write ONE short paragraph
|
|
57
|
-
(2-3 sentences). No headers, no bullet lists, no markdown.
|
|
58
|
-
|
|
59
|
-
CRITICAL anti-hallucination rule: ground every claim in the structured data
|
|
60
|
-
below. Do not invent file names, status, or actions not in the steps list.
|
|
61
|
-
If you don't know what something does, say "the agent is working on …" with
|
|
62
|
-
the literal target. Better to under-claim than to over-claim.
|
|
63
|
-
|
|
64
|
-
Tone: like a colleague checking in. "It's reading the auth file and ran the
|
|
65
|
-
tests — looks like 2 failed so far." Not robotic, not breathless.
|
|
66
|
-
|
|
67
|
-
Reference frame for the user (not for the LLM): the user did NOT see this
|
|
68
|
-
list directly; you're translating it for them. The user only sees your
|
|
69
|
-
paragraph.
|
|
70
|
-
|
|
71
|
-
────────── Structured data (the only source of truth) ──────────
|
|
72
|
-
Task: "{{prompt}}"
|
|
73
|
-
Agent: {{agent}}
|
|
74
|
-
Elapsed: {{elapsed_seconds}}s
|
|
75
|
-
Steps so far ({{step_count}}):
|
|
76
|
-
{{steps}}
|
|
77
|
-
|
|
78
|
-
Write the paragraph now. Do not preface with "Update:" or anything similar.
|
|
79
|
-
Plain prose. No "I'm currently…" — narrate the AGENT, not yourself.
|
|
80
|
-
`;
|
|
81
|
-
function templateFile() {
|
|
82
|
-
return join(process.env.CODEHERE_HOME || homedir(), '.codehere', 'narrator-prompt.md');
|
|
83
|
-
}
|
|
84
|
-
/** Read the user-customized template if present and valid. Falls back to
|
|
85
|
-
* baked-in default on missing file, parse failure, or missing required
|
|
86
|
-
* placeholders (so a typo never silently breaks narration). */
|
|
87
|
-
export function loadTemplate() {
|
|
88
|
-
const path = templateFile();
|
|
89
|
-
if (!existsSync(path))
|
|
90
|
-
return { source: 'default', template: DEFAULT_TEMPLATE };
|
|
91
|
-
let raw;
|
|
92
|
-
try {
|
|
93
|
-
raw = readFileSync(path, 'utf-8');
|
|
94
|
-
}
|
|
95
|
-
catch {
|
|
96
|
-
return { source: 'default', template: DEFAULT_TEMPLATE };
|
|
97
|
-
}
|
|
98
|
-
for (const ph of REQUIRED_PLACEHOLDERS) {
|
|
99
|
-
if (!raw.includes(ph)) {
|
|
100
|
-
return { source: 'default', template: DEFAULT_TEMPLATE };
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
return { source: 'user', template: raw };
|
|
104
|
-
}
|
|
105
|
-
/** Persist the default template to disk so the user has a starting point
|
|
106
|
-
* to edit. Used by `codehere narrator edit`. Doesn't overwrite an existing
|
|
107
|
-
* user-edited file unless `force` is set. */
|
|
108
|
-
export function writeDefaultTemplate(force = false) {
|
|
109
|
-
const path = templateFile();
|
|
110
|
-
if (!force && existsSync(path))
|
|
111
|
-
return { wrote: false, path };
|
|
112
|
-
const dir = dirname(path);
|
|
113
|
-
if (!existsSync(dir))
|
|
114
|
-
mkdirSync(dir, { recursive: true });
|
|
115
|
-
writeFileSync(path, DEFAULT_TEMPLATE, 'utf-8');
|
|
116
|
-
return { wrote: true, path };
|
|
117
|
-
}
|
|
118
|
-
/** Delete the user-customized template. Future loads use the baked-in
|
|
119
|
-
* default. Idempotent: missing file is a no-op success. */
|
|
120
|
-
export function resetTemplate() {
|
|
121
|
-
const path = templateFile();
|
|
122
|
-
if (!existsSync(path))
|
|
123
|
-
return { removed: false, path };
|
|
124
|
-
try {
|
|
125
|
-
unlinkSync(path);
|
|
126
|
-
return { removed: true, path };
|
|
127
|
-
}
|
|
128
|
-
catch {
|
|
129
|
-
return { removed: false, path };
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
/** Substitute the runtime values into the template. Same {{placeholder}}
|
|
133
|
-
* protocol as the template-file comments document. */
|
|
134
|
-
export function renderTemplate(template, input) {
|
|
135
|
-
const stepsBlock = input.steps
|
|
136
|
-
.slice(-15)
|
|
137
|
-
.map((s, idx) => ` ${idx + 1}. [${s.state}] ${(s.label ?? '').slice(0, 120)}`)
|
|
138
|
-
.join('\n');
|
|
139
|
-
const replacements = {
|
|
140
|
-
'{{prompt}}': input.prompt.slice(0, 500),
|
|
141
|
-
'{{agent}}': input.agent,
|
|
142
|
-
'{{elapsed_seconds}}': String(Math.round(input.elapsedMs / 1000)),
|
|
143
|
-
'{{step_count}}': String(input.steps.length),
|
|
144
|
-
'{{steps}}': stepsBlock,
|
|
145
|
-
};
|
|
146
|
-
let out = template;
|
|
147
|
-
for (const [k, v] of Object.entries(replacements)) {
|
|
148
|
-
// Replace ALL occurrences. Use split/join to avoid regex escaping
|
|
149
|
-
// pain on the curly braces.
|
|
150
|
-
out = out.split(k).join(v);
|
|
151
|
-
}
|
|
152
|
-
return out;
|
|
153
|
-
}
|
|
154
|
-
/** Path to the user-customized template (for CLI status display). */
|
|
155
|
-
export function templatePath() {
|
|
156
|
-
return templateFile();
|
|
157
|
-
}
|
|
158
|
-
//# sourceMappingURL=narrator-prompt-template.js.map
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tier 2.1 · slice 2b — turn the eligibility decision into a concrete,
|
|
3
|
-
* loop-safe plan for the auto cross-vendor review spawn. Still pure: it decides
|
|
4
|
-
* WHAT to spawn (vendor, prompt, cwd, tag), not how — the live spawn + the
|
|
5
|
-
* real-spend verification are the next slice. Kept pure so the loop guard and
|
|
6
|
-
* the refute framing are unit-testable without a daemon or spend.
|
|
7
|
-
*
|
|
8
|
-
* THE LOOP GUARD is the load-bearing safety here: an auto-review is itself a
|
|
9
|
-
* task that completes and flows through the same completion handler. Without a
|
|
10
|
-
* guard it would trigger its own auto-review, recursing + burning spend forever.
|
|
11
|
-
* A reviewer session is tagged; planAutoReview returns null for tagged sessions.
|
|
12
|
-
*
|
|
13
|
-
* The reviewer runs in REFUTE mode and READ-ONLY: codehere proves the PROCESS
|
|
14
|
-
* (a different vendor independently reviewed + recorded findings), it never
|
|
15
|
-
* adjudicates code correctness (assess-cross-vendor honesty note). The prompt
|
|
16
|
-
* asks the reviewer to try to disprove "done", not to fix anything.
|
|
17
|
-
*/
|
|
18
|
-
import { shouldAutoReview } from './should-auto-review.js';
|
|
19
|
-
/** Tag stamped on an auto-review reviewer session so it never triggers its own
|
|
20
|
-
* auto-review (the loop guard) and so surfaces can label it. */
|
|
21
|
-
export const AUTO_REVIEW_TAG = 'auto-cross-vendor-review';
|
|
22
|
-
/** The refute-mode brief: read-only, adversarial, names the writer's files. */
|
|
23
|
-
function buildRefutePrompt(writerAgent, files) {
|
|
24
|
-
const fileList = files.length <= 10 ? files.join(', ') : `${files.slice(0, 10).join(', ')} (+${files.length - 10} more)`;
|
|
25
|
-
return (`A different agent (${writerAgent}) just changed these files in this repo: ${fileList}. ` +
|
|
26
|
-
`Independently review that work READ-ONLY — do not edit anything. ` +
|
|
27
|
-
`Try to disprove that the task is correctly done: look for bugs, missed edge cases, ` +
|
|
28
|
-
`or claims the diff doesn't actually support. Report what you find (or that it holds up). ` +
|
|
29
|
-
`You are the second vendor in a cross-vendor check; your job is to refute, not to fix.`);
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Build the auto-review spawn plan, or null if it shouldn't / can't run.
|
|
33
|
-
* Returns null (with no side effects) for: the loop guard, or any failed
|
|
34
|
-
* eligibility gate. Pure.
|
|
35
|
-
*/
|
|
36
|
-
export function planAutoReview(facts, thresholds) {
|
|
37
|
-
// Loop guard FIRST — a reviewer's own completion must never spawn a review.
|
|
38
|
-
if (facts.isAutoReviewSession)
|
|
39
|
-
return null;
|
|
40
|
-
const inputs = {
|
|
41
|
-
writerAgent: facts.writerAgent,
|
|
42
|
-
installedAgents: facts.installedAgents,
|
|
43
|
-
filesChanged: facts.writerFiles.length,
|
|
44
|
-
budgetRemainingUsd: facts.budgetRemainingUsd,
|
|
45
|
-
};
|
|
46
|
-
const decision = shouldAutoReview(inputs, thresholds);
|
|
47
|
-
if (!decision.eligible || !decision.reviewerVendor)
|
|
48
|
-
return null;
|
|
49
|
-
return {
|
|
50
|
-
reviewerVendor: decision.reviewerVendor,
|
|
51
|
-
prompt: buildRefutePrompt(facts.writerAgent, facts.writerFiles),
|
|
52
|
-
cwd: facts.cwd,
|
|
53
|
-
tag: AUTO_REVIEW_TAG,
|
|
54
|
-
reason: decision.reason,
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
//# sourceMappingURL=plan-auto-review.js.map
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tier 1.2 (verdict-trust punch list, 2026-06-09) · reconcile the AUDIT against
|
|
3
|
-
* the real on-disk effect.
|
|
4
|
-
*
|
|
5
|
-
* The gap this closes: an audit entry records the agent's DECLARED tool call
|
|
6
|
-
* (PreToolUse intent for claude, a post-hoc mirror for the others) — not an
|
|
7
|
-
* independent observation that the write actually landed. An agent (or adapter)
|
|
8
|
-
* that emits a file-write tool_use which never really changed anything lands that
|
|
9
|
-
* fiction in the integrity-checked chain as a first-class entry. The chain then
|
|
10
|
-
* faithfully shows the CLAIM was never casually edited — it never checks the claim
|
|
11
|
-
* against reality.
|
|
12
|
-
*
|
|
13
|
-
* This reconciles the audit's file-write tool calls against the SAME git-diff
|
|
14
|
-
* ground truth verify-claim already uses for file claims. It does NOT mutate the
|
|
15
|
-
* append-only, hash-chained audit (that would break tamper-evidence) — it's a
|
|
16
|
-
* read-only layer that produces an honest verdict the surface can show:
|
|
17
|
-
* - consistent · writes recorded AND files changed on disk
|
|
18
|
-
* - unconfirmed · writes recorded but codehere couldn't tie them to a tracked
|
|
19
|
-
* git change. Honest, NOT an accusation: the writes may have
|
|
20
|
-
* targeted gitignored / scratch / .codehere paths (which the
|
|
21
|
-
* git-diff ground truth excludes), been reverted within the
|
|
22
|
-
* run, or — the case worth a look — never really happened.
|
|
23
|
-
* "couldn't confirm", never "it's fake" (refuter 2026-06-09:
|
|
24
|
-
* a false fabrication accusation on honest work is the worst
|
|
25
|
-
* error class on a proof surface).
|
|
26
|
-
* - no-writes · nothing claimed, nothing to reconcile
|
|
27
|
-
* - unobservable · not a git repo, so codehere can't confirm
|
|
28
|
-
*
|
|
29
|
-
* Count-based by design: toolInput is truncated/heterogeneous across adapters, so
|
|
30
|
-
* per-file path matching is unreliable; the file-write COUNT vs the git-change
|
|
31
|
-
* count is robust. Bash/shell tools can also write files but codehere can't tell,
|
|
32
|
-
* so they're deliberately NOT counted as declared writes (conservative — never
|
|
33
|
-
* fabricates a "declared-not-observed" the user can't act on).
|
|
34
|
-
*/
|
|
35
|
-
/** Tool names that explicitly mutate a file on disk, across every adapter.
|
|
36
|
-
* Derived from the real audit's toolName distribution (Write/Edit/write_file/
|
|
37
|
-
* edit_file observed) plus the standard variants. Shell tools are excluded — a
|
|
38
|
-
* bash `echo > f` can't be distinguished from a read, so counting it would
|
|
39
|
-
* manufacture false "declared-not-observed" verdicts. */
|
|
40
|
-
// Matched case-INSENSITIVELY (adapters emit varied casings: claude 'Edit',
|
|
41
|
-
// codex 'Delete', gemini raw tool_name like 'write_file'/'replace'). A DELETE is
|
|
42
|
-
// a real file mutation that shows in git — omitting it made a codex delete-only
|
|
43
|
-
// run falsely read "no writes recorded" (self-audit 2026-06-09). Keep this broad;
|
|
44
|
-
// over-counting a non-write is far less harmful than a false "nothing happened".
|
|
45
|
-
const WRITE_TOOLS = new Set(
|
|
46
|
-
// SPECIFIC file-mutation tool names only. Bare generic verbs ('replace',
|
|
47
|
-
// 'patch') are deliberately EXCLUDED — a non-file tool named 'replace' (an
|
|
48
|
-
// in-memory string replace) would false-count and produce a false 'consistent'
|
|
49
|
-
// (worse than under-counting, which is an honest 'unconfirmed'). A real write
|
|
50
|
-
// the set misses → 'unconfirmed' (honest), never a fabricated-write laundered
|
|
51
|
-
// into green (self-audit round 2, 2026-06-09).
|
|
52
|
-
[
|
|
53
|
-
'Write',
|
|
54
|
-
'Edit',
|
|
55
|
-
'MultiEdit',
|
|
56
|
-
'NotebookEdit',
|
|
57
|
-
'Delete',
|
|
58
|
-
'write_file',
|
|
59
|
-
'edit_file',
|
|
60
|
-
'create_file',
|
|
61
|
-
'delete_file',
|
|
62
|
-
'str_replace',
|
|
63
|
-
'str_replace_editor',
|
|
64
|
-
'apply_patch',
|
|
65
|
-
].map((t) => t.toLowerCase()));
|
|
66
|
-
export function reconcileAudit(entries, artifacts) {
|
|
67
|
-
const writeToolCalls = entries.filter((e) => WRITE_TOOLS.has((e.toolName || '').toLowerCase())).length;
|
|
68
|
-
const filesObserved = artifacts?.files?.length ?? 0;
|
|
69
|
-
if (artifacts?.noGitTracking) {
|
|
70
|
-
return {
|
|
71
|
-
writeToolCalls,
|
|
72
|
-
filesObserved: 0,
|
|
73
|
-
verdict: 'unobservable',
|
|
74
|
-
note: `${writeToolCalls} file-write tool call${writeToolCalls === 1 ? '' : 's'} recorded, but this folder isn't a git repo — codehere can't confirm them against disk.`,
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
if (writeToolCalls === 0) {
|
|
78
|
-
return {
|
|
79
|
-
writeToolCalls,
|
|
80
|
-
filesObserved,
|
|
81
|
-
verdict: 'no-writes',
|
|
82
|
-
note: 'No file-write tool calls recorded — nothing to reconcile against disk.',
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
if (filesObserved === 0) {
|
|
86
|
-
return {
|
|
87
|
-
writeToolCalls,
|
|
88
|
-
filesObserved,
|
|
89
|
-
verdict: 'unconfirmed',
|
|
90
|
-
note: `${writeToolCalls} file-write tool call${writeToolCalls === 1 ? '' : 's'} recorded, but codehere couldn't confirm them against tracked git changes — they may have targeted gitignored/scratch paths or been reverted. Worth a look, not proof of a problem.`,
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
return {
|
|
94
|
-
writeToolCalls,
|
|
95
|
-
filesObserved,
|
|
96
|
-
verdict: 'consistent',
|
|
97
|
-
note: `${writeToolCalls} file-write tool call${writeToolCalls === 1 ? '' : 's'} recorded · ${filesObserved} file${filesObserved === 1 ? '' : 's'} confirmed changed on disk.`,
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
//# sourceMappingURL=reconcile-audit.js.map
|