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,52 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CLI: scan — point codehere at an app, get the ONE thing that will get you hacked, in plain words.
|
|
3
|
-
* Deterministic (orchestrates gitleaks + semgrep), zero model tokens. The 60-second ship-readiness hook
|
|
4
|
-
* for non-experts (docs/PRODUCT_10X.md). Exit 1 when a critical blocker is found (CI-usable).
|
|
5
|
-
*/
|
|
6
|
-
import { resolve } from 'node:path';
|
|
7
|
-
import { colors, symbols } from '../../../ui.js';
|
|
8
|
-
import { shipScan } from '../../../infrastructure/security/ship-scan.js';
|
|
9
|
-
export async function executeScanCommand(target, options = {}) {
|
|
10
|
-
const dir = resolve(target || '.');
|
|
11
|
-
const r = shipScan(dir);
|
|
12
|
-
if (options.json) {
|
|
13
|
-
process.stdout.write(JSON.stringify(r, null, 2) + '\n');
|
|
14
|
-
return r.top && r.top.tier === 'critical' ? 1 : 0;
|
|
15
|
-
}
|
|
16
|
-
if (r.missing.length) {
|
|
17
|
-
process.stdout.write(colors.dim(`(${r.missing.join(' + ')} not installed — coverage reduced. brew install ${r.missing.join(' ')})\n`));
|
|
18
|
-
}
|
|
19
|
-
if (r.failed.length) {
|
|
20
|
-
process.stdout.write(colors.dim(`(${r.failed.join(' + ')} did not finish — that part of the app went unscanned.)\n`));
|
|
21
|
-
}
|
|
22
|
-
if (!r.top) {
|
|
23
|
-
// Name the scanners that ACTUALLY ran. The old copy hardcoded "gitleaks + semgrep" and printed
|
|
24
|
-
// "Observed, not assumed" even when semgrep was absent or had timed out — the product asserting
|
|
25
|
-
// a scan it never performed, which is the exact failure it exists to catch elsewhere.
|
|
26
|
-
const ran = [r.ran.gitleaks && 'gitleaks', r.ran.semgrep && 'semgrep', 'codehere’s own checks'].filter(Boolean);
|
|
27
|
-
const partial = r.missing.length > 0 || r.failed.length > 0;
|
|
28
|
-
process.stdout.write('\n' +
|
|
29
|
-
// Green is for a clean pass at full coverage. A hole in coverage is not a pass, so it does not
|
|
30
|
-
// get the green — it gets a plain line that says what was and wasn't looked at.
|
|
31
|
-
(partial
|
|
32
|
-
? `${symbols.info} No ship-blockers found in what was scanned`
|
|
33
|
-
: colors.success(`${symbols.success} No ship-blockers found`)) +
|
|
34
|
-
colors.dim(` — ${ran.join(' + ')} ran on this app and nothing critical surfaced.\n` +
|
|
35
|
-
(partial ? `Coverage was incomplete, so this is not an all-clear.\n` : '') +
|
|
36
|
-
`Observed, not assumed. (Fast pass on what actually gets apps hacked — not a full audit.)\n\n`));
|
|
37
|
-
return 0;
|
|
38
|
-
}
|
|
39
|
-
const t = r.top;
|
|
40
|
-
const crit = r.findings.filter((f) => f.tier === 'critical').length;
|
|
41
|
-
const rest = r.findings.length - 1;
|
|
42
|
-
// THE ONE — consequence first, plain language (the EPAM lesson: surface + don't bury it).
|
|
43
|
-
process.stdout.write('\n' + colors.error(`${symbols.error} 1 thing will get you hacked:`) + '\n\n');
|
|
44
|
-
process.stdout.write(` ${t.what}\n`);
|
|
45
|
-
process.stdout.write(colors.dim(` → ${t.file}:${t.line} (found by ${t.tool}: ${t.ruleId})\n\n`));
|
|
46
|
-
if (rest > 0) {
|
|
47
|
-
const alsoCrit = crit > 1 ? ` (${crit - 1} also critical)` : '';
|
|
48
|
-
process.stdout.write(colors.dim(`Plus ${rest} more issue${rest > 1 ? 's' : ''}${alsoCrit} — fix the one above first.\n\n`));
|
|
49
|
-
}
|
|
50
|
-
return 1; // not safe to ship
|
|
51
|
-
}
|
|
52
|
-
//# sourceMappingURL=scan-command.js.map
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CLI Command: scan-tool
|
|
3
|
-
*
|
|
4
|
-
* Reads a Claude Code PreToolUse hook event from stdin (JSON),
|
|
5
|
-
* runs Layer 0 security scan, and outputs a block/allow decision.
|
|
6
|
-
*
|
|
7
|
-
* This is the actual binary that Claude Code's hook system invokes.
|
|
8
|
-
* It must be fast (<100ms) and never crash — any failure falls through to allow.
|
|
9
|
-
*
|
|
10
|
-
* Trust-override audit (added 2026-05-06 per CC frontier Q1 — auditable
|
|
11
|
-
* permission trails): every fail-open path now writes an `overrideReason`
|
|
12
|
-
* audit entry so the audit log captures EVERY tool call, not just the ones
|
|
13
|
-
* the scanner could evaluate. Reviewers can `codehere audit --override-only`
|
|
14
|
-
* to see all cases where the safety policy didn't run normally.
|
|
15
|
-
*
|
|
16
|
-
* Usage (by hook system, not user):
|
|
17
|
-
* echo '{"hook_event_name":"PreToolUse","tool_name":"Bash","tool_input":{"command":"rm -rf /"}}' | codehere scan-tool
|
|
18
|
-
*/
|
|
19
|
-
import { processHookEvent, appendOverrideAudit, enableHookAuditQueue, flushHookAuditQueue, } from '../../../infrastructure/hooks/claude-sdk-hook.js';
|
|
20
|
-
/** R1.2 · every exit path flushes the queued audit entries first: each is
|
|
21
|
-
* offered to the daemon (250ms cap) and falls back to a direct append marked
|
|
22
|
-
* writer:'hook-direct'. The flush itself never throws — the user's tool call
|
|
23
|
-
* must never break or hang on audit plumbing (core value #5). */
|
|
24
|
-
async function flushAndExit(code) {
|
|
25
|
-
try {
|
|
26
|
-
await flushHookAuditQueue();
|
|
27
|
-
}
|
|
28
|
-
catch {
|
|
29
|
-
/* fail-open */
|
|
30
|
-
}
|
|
31
|
-
process.exit(code);
|
|
32
|
-
}
|
|
33
|
-
export async function executeScanToolCommand(options) {
|
|
34
|
-
// R1.2 · queue mode: appends in this hook process buffer instead of writing,
|
|
35
|
-
// so the flush can offer them to the daemon (the sole-writer path).
|
|
36
|
-
enableHookAuditQueue();
|
|
37
|
-
// Read all stdin
|
|
38
|
-
let raw = '';
|
|
39
|
-
try {
|
|
40
|
-
for await (const chunk of process.stdin) {
|
|
41
|
-
raw += chunk;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
catch {
|
|
45
|
-
// If stdin read fails, allow the tool call — but log the override so
|
|
46
|
-
// the audit trail records that the scanner didn't get a chance to run.
|
|
47
|
-
appendOverrideAudit('stdin_read_failure');
|
|
48
|
-
return flushAndExit(0);
|
|
49
|
-
}
|
|
50
|
-
if (!raw.trim()) {
|
|
51
|
-
return flushAndExit(0);
|
|
52
|
-
}
|
|
53
|
-
// Parse event JSON
|
|
54
|
-
let event;
|
|
55
|
-
try {
|
|
56
|
-
event = JSON.parse(raw);
|
|
57
|
-
}
|
|
58
|
-
catch {
|
|
59
|
-
// Malformed JSON — allow by default (fail open for dev experience).
|
|
60
|
-
// We still record the override with a truncated copy of the raw payload
|
|
61
|
-
// so reviewers can see what arrived (helps diagnose hook misconfiguration).
|
|
62
|
-
appendOverrideAudit('event_parse_failure', {
|
|
63
|
-
toolInput: raw.substring(0, 500),
|
|
64
|
-
});
|
|
65
|
-
if (options.debug) {
|
|
66
|
-
process.stderr.write('[codehere] Warning: could not parse hook event JSON\n');
|
|
67
|
-
}
|
|
68
|
-
return flushAndExit(0);
|
|
69
|
-
}
|
|
70
|
-
try {
|
|
71
|
-
const decision = processHookEvent(event);
|
|
72
|
-
if (options.logOnly) {
|
|
73
|
-
// Log-only mode: never block, but log everything. If the scanner WOULD
|
|
74
|
-
// have blocked, record the override so the audit shows scanner intent
|
|
75
|
-
// vs actual outcome. Without this, --log-only mode produces an audit
|
|
76
|
-
// trail indistinguishable from a clean session.
|
|
77
|
-
if (decision?.decision === 'block') {
|
|
78
|
-
appendOverrideAudit('log_only_mode', {
|
|
79
|
-
sessionId: event.session_id,
|
|
80
|
-
toolName: event.tool_name,
|
|
81
|
-
});
|
|
82
|
-
if (options.debug) {
|
|
83
|
-
process.stderr.write(`[codehere] Would block: ${decision.reason}\n`);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
return flushAndExit(0);
|
|
87
|
-
}
|
|
88
|
-
if (decision?.decision === 'block') {
|
|
89
|
-
// Output block decision as JSON (Claude Code hook contract)
|
|
90
|
-
process.stdout.write(JSON.stringify(decision) + '\n');
|
|
91
|
-
// Also write to stderr for user visibility
|
|
92
|
-
process.stderr.write(`\n${decision.reason}\n`);
|
|
93
|
-
return flushAndExit(2); // non-zero exit = Claude Code treats as block signal
|
|
94
|
-
}
|
|
95
|
-
// Allow — exit 0 with no output
|
|
96
|
-
return flushAndExit(0);
|
|
97
|
-
}
|
|
98
|
-
catch {
|
|
99
|
-
// Any scan failure: fail open (allow), never break developer workflow.
|
|
100
|
-
// Record the override so the audit trail shows the scanner crashed for
|
|
101
|
-
// this tool call — reviewers can correlate with the timestamp to find
|
|
102
|
-
// the underlying error.
|
|
103
|
-
appendOverrideAudit('scan_exception', {
|
|
104
|
-
sessionId: event.session_id,
|
|
105
|
-
toolName: event.tool_name,
|
|
106
|
-
});
|
|
107
|
-
return flushAndExit(0);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
//# sourceMappingURL=scan-tool-command.js.map
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `codehere search "<query>"` — REQUEST_METHOD stage 7 (Learn) / #19.
|
|
3
|
-
* Search past agent sessions with PROVENANCE: which agent ran it, whether it
|
|
4
|
-
* succeeded, in which project. Local-first (reads on-disk records, no network).
|
|
5
|
-
* Follows the CLI color economy (DESIGN.md §5.2): colour carries meaning, and
|
|
6
|
-
* the output stays legible under NO_COLOR (glyph + word carry the status).
|
|
7
|
-
*/
|
|
8
|
-
import { searchSessions } from '../../../application/use-cases/search-sessions.js';
|
|
9
|
-
import { colors, symbols, newline } from '../../../ui.js';
|
|
10
|
-
function statusGlyph(status) {
|
|
11
|
-
const s = status.toLowerCase();
|
|
12
|
-
if (/completed|done/.test(s))
|
|
13
|
-
return colors.verified(symbols.success); // ✓ verified
|
|
14
|
-
if (/error|caught|stopped|failed/.test(s))
|
|
15
|
-
return colors.caught(symbols.error); // ✕
|
|
16
|
-
return colors.dim('·'); // · pending / other
|
|
17
|
-
}
|
|
18
|
-
export function executeSearchCommand(query, options) {
|
|
19
|
-
const limit = options.limit ? Math.max(1, parseInt(options.limit, 10) || 10) : 10;
|
|
20
|
-
const hits = searchSessions(query, { limit });
|
|
21
|
-
if (options.json) {
|
|
22
|
-
process.stdout.write(JSON.stringify(hits, null, 2) + '\n');
|
|
23
|
-
return 0;
|
|
24
|
-
}
|
|
25
|
-
if (!hits.length) {
|
|
26
|
-
process.stdout.write(colors.dim(`No past sessions match "${query}".\n`));
|
|
27
|
-
return 0;
|
|
28
|
-
}
|
|
29
|
-
newline();
|
|
30
|
-
process.stdout.write(colors.heading(`${hits.length} past session${hits.length === 1 ? '' : 's'} for "${query}"`) +
|
|
31
|
-
colors.dim(' (provenance: which agent · whether it succeeded)') +
|
|
32
|
-
'\n\n');
|
|
33
|
-
for (const h of hits) {
|
|
34
|
-
// provenance line: agent · verdict · status · id · project
|
|
35
|
-
process.stdout.write(' ' +
|
|
36
|
-
colors.identity(h.agent.padEnd(12)) +
|
|
37
|
-
' ' +
|
|
38
|
-
statusGlyph(h.status) +
|
|
39
|
-
' ' +
|
|
40
|
-
colors.dim(h.status.padEnd(10)) +
|
|
41
|
-
' ' +
|
|
42
|
-
colors.evidence(h.sessionId.slice(0, 8)) +
|
|
43
|
-
' ' +
|
|
44
|
-
colors.dim(h.project) +
|
|
45
|
-
'\n');
|
|
46
|
-
process.stdout.write(' ' + colors.dim(h.snippet) + '\n\n');
|
|
47
|
-
}
|
|
48
|
-
process.stdout.write(colors.dim('Open one: ') + colors.bold('codehere verify <id>') + '\n\n');
|
|
49
|
-
return 0;
|
|
50
|
-
}
|
|
51
|
-
//# sourceMappingURL=search-command.js.map
|
|
@@ -1,330 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Setup Wizard Command
|
|
3
|
-
* Interactive first-time setup for Codehere
|
|
4
|
-
* Checks environment, API keys, and guides users through configuration
|
|
5
|
-
*/
|
|
6
|
-
import { readFileSync, existsSync, writeFileSync, mkdirSync } from 'fs';
|
|
7
|
-
import { join } from 'path';
|
|
8
|
-
import { getConfigManager } from '../../../infrastructure/config/config-manager.js';
|
|
9
|
-
import { colors, separator, newline, sectionHeader, success, error, warning, info } from '../../../ui.js';
|
|
10
|
-
import { executionTracer } from '../../../infrastructure/observability/execution-tracer.js';
|
|
11
|
-
import { findCodebaseRoot } from '../../../domain/services/codebase-detector.js';
|
|
12
|
-
import { readinessForAllAgents } from '../../../infrastructure/orchestration/session-manager.js';
|
|
13
|
-
/**
|
|
14
|
-
* What codehere actually needs to run a task: at least one coding-agent CLI
|
|
15
|
-
* that's installed AND signed in. Most engineers have `claude` or `codex`
|
|
16
|
-
* logged in by subscription with no API key set, so framing setup around
|
|
17
|
-
* `ANTHROPIC_API_KEY` told them they were blocked when they weren't. This
|
|
18
|
-
* reuses the same readiness check that powers the web dashboard's squad bar.
|
|
19
|
-
*/
|
|
20
|
-
function summarizeAgents() {
|
|
21
|
-
let all;
|
|
22
|
-
try {
|
|
23
|
-
all = readinessForAllAgents();
|
|
24
|
-
}
|
|
25
|
-
catch {
|
|
26
|
-
all = [];
|
|
27
|
-
}
|
|
28
|
-
return { ready: all.filter((a) => a.state === 'ready'), all };
|
|
29
|
-
}
|
|
30
|
-
/** One status line per agent, plain English. Used by both the TTY wizard and
|
|
31
|
-
* the non-TTY summary so they say the same thing. */
|
|
32
|
-
function printAgentLines(all) {
|
|
33
|
-
for (const a of all) {
|
|
34
|
-
if (a.state === 'ready') {
|
|
35
|
-
console.log(success(`${a.label}: ready`));
|
|
36
|
-
}
|
|
37
|
-
else if (a.state === 'needs_auth') {
|
|
38
|
-
const hint = a.authHint?.command ? colors.dim(` (sign in: ${a.authHint.command})`) : '';
|
|
39
|
-
console.log(warning(`${a.label}: installed, needs sign-in`) + hint);
|
|
40
|
-
}
|
|
41
|
-
else if (a.state === 'billed_key_required') {
|
|
42
|
-
// Surface the adapter's own authHint instead of a hardcoded "paid" label —
|
|
43
|
-
// some agents (Aider via LiteLLM) actually have a $0 path the detail names.
|
|
44
|
-
console.log(colors.dim(`○ ${a.label}: installed, needs a model + key`));
|
|
45
|
-
if (a.authHint?.detail)
|
|
46
|
-
console.log(colors.dim(` ${a.authHint.detail}`));
|
|
47
|
-
}
|
|
48
|
-
else {
|
|
49
|
-
const hint = a.installHint?.command ? colors.dim(` (install: ${a.installHint.command})`) : '';
|
|
50
|
-
console.log(colors.dim(`○ ${a.label}: not installed`) + hint);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* The free-tier setup recipe (Phase 1 — free-tier-first). One compact,
|
|
56
|
-
* copy-pasteable line per vendor showing the CHEAPEST way to get it running —
|
|
57
|
-
* so a user testing all 5 vendors knows exactly how to do it for ~$0. Shown
|
|
58
|
-
* when no agent is ready yet (the moment the user needs it). Mirrors the
|
|
59
|
-
* per-agent readiness authHints; kept here as one consolidated cheatsheet
|
|
60
|
-
* because the readiness hints surface one-at-a-time, not as a plan.
|
|
61
|
-
*/
|
|
62
|
-
function printFreeTierRecipe() {
|
|
63
|
-
console.log(colors.bold('Set up any agent for free (or near-free):'));
|
|
64
|
-
console.log(colors.cyan(' Claude') +
|
|
65
|
-
colors.dim(' run `claude` › /login · uses your Claude subscription (no API key, no per-token bill)'));
|
|
66
|
-
console.log(colors.cyan(' Gemini') +
|
|
67
|
-
colors.dim(' export GEMINI_API_KEY=… free tier › https://aistudio.google.com/apikey'));
|
|
68
|
-
console.log(colors.cyan(' Codex') +
|
|
69
|
-
colors.dim(' run `codex login` with a ChatGPT Plus/Pro account · flat, no per-token charge'));
|
|
70
|
-
console.log(colors.cyan(' OpenCode') +
|
|
71
|
-
colors.dim(' run `opencode auth` · point at a local Ollama model or a free provider ($0)'));
|
|
72
|
-
console.log(colors.cyan(' Aider') +
|
|
73
|
-
colors.dim(' reuse the free Gemini key: `--model gemini/gemini-2.5-flash` (routes via LiteLLM, $0)'));
|
|
74
|
-
}
|
|
75
|
-
/**
|
|
76
|
-
* Check if any API key is configured
|
|
77
|
-
*/
|
|
78
|
-
function checkAPIKey() {
|
|
79
|
-
const providers = [
|
|
80
|
-
{ env: 'ANTHROPIC_API_KEY', name: 'Anthropic' },
|
|
81
|
-
{ env: 'OPENAI_API_KEY', name: 'OpenAI' },
|
|
82
|
-
{ env: 'COHERE_API_KEY', name: 'Cohere' },
|
|
83
|
-
{ env: 'OPENROUTER_API_KEY', name: 'OpenRouter' },
|
|
84
|
-
];
|
|
85
|
-
for (const { env, name } of providers) {
|
|
86
|
-
const key = process.env[env];
|
|
87
|
-
if (key && key.trim() && !key.includes('your_key_here') && key.length > 10) {
|
|
88
|
-
return { configured: true, provider: name, key };
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
return { configured: false };
|
|
92
|
-
}
|
|
93
|
-
/**
|
|
94
|
-
* Detect codebase type and root
|
|
95
|
-
*/
|
|
96
|
-
function detectCodebase() {
|
|
97
|
-
const cwd = process.cwd();
|
|
98
|
-
// Check for common project indicators
|
|
99
|
-
const indicators = {
|
|
100
|
-
node: ['package.json'],
|
|
101
|
-
python: ['requirements.txt', 'setup.py', 'pyproject.toml'],
|
|
102
|
-
rust: ['Cargo.toml'],
|
|
103
|
-
go: ['go.mod'],
|
|
104
|
-
java: ['pom.xml', 'build.gradle'],
|
|
105
|
-
};
|
|
106
|
-
for (const [type, files] of Object.entries(indicators)) {
|
|
107
|
-
for (const file of files) {
|
|
108
|
-
if (existsSync(join(cwd, file))) {
|
|
109
|
-
return { detected: true, type, root: cwd };
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
return { detected: false, root: cwd };
|
|
114
|
-
}
|
|
115
|
-
/**
|
|
116
|
-
* Detect preferred model from config
|
|
117
|
-
*/
|
|
118
|
-
function detectModel() {
|
|
119
|
-
try {
|
|
120
|
-
const configPath = join(process.cwd(), 'data', 'model-config.json');
|
|
121
|
-
if (existsSync(configPath)) {
|
|
122
|
-
const config = JSON.parse(readFileSync(configPath, 'utf-8'));
|
|
123
|
-
return config.preferredModel || config.modelFamily || null;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
catch {
|
|
127
|
-
// Ignore errors
|
|
128
|
-
}
|
|
129
|
-
return null;
|
|
130
|
-
}
|
|
131
|
-
/**
|
|
132
|
-
* Create config directory if needed
|
|
133
|
-
*/
|
|
134
|
-
function ensureConfigDir() {
|
|
135
|
-
const configDir = join(process.cwd(), 'data');
|
|
136
|
-
if (!existsSync(configDir)) {
|
|
137
|
-
mkdirSync(configDir, { recursive: true });
|
|
138
|
-
}
|
|
139
|
-
return configDir;
|
|
140
|
-
}
|
|
141
|
-
/**
|
|
142
|
-
* Save setup configuration
|
|
143
|
-
*/
|
|
144
|
-
function saveSetupConfig(result) {
|
|
145
|
-
const configDir = ensureConfigDir();
|
|
146
|
-
const configPath = join(configDir, 'setup-config.json');
|
|
147
|
-
const config = {
|
|
148
|
-
setupComplete: true,
|
|
149
|
-
setupDate: new Date().toISOString(),
|
|
150
|
-
apiKeyConfigured: result.apiKeyConfigured,
|
|
151
|
-
modelDetected: result.modelDetected,
|
|
152
|
-
codebaseDetected: result.codebaseDetected,
|
|
153
|
-
};
|
|
154
|
-
writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
155
|
-
}
|
|
156
|
-
/**
|
|
157
|
-
* Check if setup has been completed before
|
|
158
|
-
*/
|
|
159
|
-
function isSetupComplete() {
|
|
160
|
-
try {
|
|
161
|
-
const configPath = join(process.cwd(), 'data', 'setup-config.json');
|
|
162
|
-
if (existsSync(configPath)) {
|
|
163
|
-
const config = JSON.parse(readFileSync(configPath, 'utf-8'));
|
|
164
|
-
return config.setupComplete === true;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
catch {
|
|
168
|
-
// Ignore errors
|
|
169
|
-
}
|
|
170
|
-
return false;
|
|
171
|
-
}
|
|
172
|
-
/**
|
|
173
|
-
* Execute setup wizard
|
|
174
|
-
*/
|
|
175
|
-
export async function executeSetupCommand(force = false) {
|
|
176
|
-
// Check for interactive terminal
|
|
177
|
-
const isTTY = process.stdin.isTTY && process.stdout.isTTY;
|
|
178
|
-
if (!isTTY) {
|
|
179
|
-
const { ready, all } = summarizeAgents();
|
|
180
|
-
console.log(colors.bold('Setup'));
|
|
181
|
-
console.log();
|
|
182
|
-
if (all.length)
|
|
183
|
-
printAgentLines(all);
|
|
184
|
-
console.log();
|
|
185
|
-
if (ready.length) {
|
|
186
|
-
console.log(success(`You're ready. ${ready[0].label} is set up.`));
|
|
187
|
-
console.log(colors.dim(' Next: codehere start (open the dashboard)'));
|
|
188
|
-
}
|
|
189
|
-
else {
|
|
190
|
-
console.log(warning('No coding agent is set up yet.'));
|
|
191
|
-
console.log();
|
|
192
|
-
printFreeTierRecipe();
|
|
193
|
-
}
|
|
194
|
-
return;
|
|
195
|
-
}
|
|
196
|
-
// Start execution tracing
|
|
197
|
-
const codebaseInfo = findCodebaseRoot(process.cwd());
|
|
198
|
-
executionTracer.startRun('setup', {
|
|
199
|
-
codebaseId: codebaseInfo?.rootPath || process.cwd(),
|
|
200
|
-
});
|
|
201
|
-
try {
|
|
202
|
-
const isFirstTime = !isSetupComplete() || force;
|
|
203
|
-
newline();
|
|
204
|
-
console.log(colors.bold('Setup'));
|
|
205
|
-
newline();
|
|
206
|
-
// Check if already set up
|
|
207
|
-
if (!force && isSetupComplete()) {
|
|
208
|
-
console.log(info('Setup has already been completed.'));
|
|
209
|
-
newline();
|
|
210
|
-
console.log(colors.dim('Run with --force to reconfigure.'));
|
|
211
|
-
newline();
|
|
212
|
-
return;
|
|
213
|
-
}
|
|
214
|
-
const result = {
|
|
215
|
-
apiKeyConfigured: false,
|
|
216
|
-
modelDetected: null,
|
|
217
|
-
codebaseDetected: false,
|
|
218
|
-
configPath: join(process.cwd(), 'data'),
|
|
219
|
-
};
|
|
220
|
-
// Step 1: Coding agents — codehere drives agent CLIs, not raw API keys.
|
|
221
|
-
sectionHeader('Step 1: Your coding agents');
|
|
222
|
-
const { ready, all } = summarizeAgents();
|
|
223
|
-
if (all.length)
|
|
224
|
-
printAgentLines(all);
|
|
225
|
-
newline();
|
|
226
|
-
const apiKeyCheck = checkAPIKey();
|
|
227
|
-
if (ready.length) {
|
|
228
|
-
console.log(success(`You're ready. ${ready[0].label} is set up.`));
|
|
229
|
-
result.apiKeyConfigured = true; // at least one agent can spawn right now
|
|
230
|
-
}
|
|
231
|
-
else if (apiKeyCheck.configured) {
|
|
232
|
-
// No agent CLI ready, but a key is present — some adapters (e.g. Aider) run off it.
|
|
233
|
-
console.log(success(`${apiKeyCheck.provider} API key found.`));
|
|
234
|
-
console.log(colors.dim(' No agent CLI detected yet · install one for the full experience.'));
|
|
235
|
-
result.apiKeyConfigured = true;
|
|
236
|
-
}
|
|
237
|
-
else {
|
|
238
|
-
console.log(warning('No coding agent is set up yet.'));
|
|
239
|
-
newline();
|
|
240
|
-
printFreeTierRecipe();
|
|
241
|
-
newline();
|
|
242
|
-
}
|
|
243
|
-
newline();
|
|
244
|
-
// Step 2: Model Detection
|
|
245
|
-
sectionHeader('Step 2: Model Configuration');
|
|
246
|
-
const model = detectModel();
|
|
247
|
-
if (model) {
|
|
248
|
-
console.log(success(`Preferred Model: ${model}`));
|
|
249
|
-
result.modelDetected = model;
|
|
250
|
-
}
|
|
251
|
-
else {
|
|
252
|
-
console.log(info('Model: Using default (Claude Sonnet)'));
|
|
253
|
-
console.log(colors.dim(' Configurable via .codehere/providers.json'));
|
|
254
|
-
result.modelDetected = 'Claude Sonnet (default)';
|
|
255
|
-
}
|
|
256
|
-
newline();
|
|
257
|
-
// Step 3: Codebase Detection
|
|
258
|
-
sectionHeader('Step 3: Codebase Detection');
|
|
259
|
-
const codebase = detectCodebase();
|
|
260
|
-
if (codebase.detected) {
|
|
261
|
-
console.log(success(`Codebase: Detected (${codebase.type})`));
|
|
262
|
-
console.log(colors.dim(` Root: ${codebase.root}`));
|
|
263
|
-
result.codebaseDetected = true;
|
|
264
|
-
}
|
|
265
|
-
else {
|
|
266
|
-
console.log(warning('Codebase: Not detected'));
|
|
267
|
-
console.log(colors.dim(" Make sure you're in your project directory."));
|
|
268
|
-
result.codebaseDetected = false;
|
|
269
|
-
}
|
|
270
|
-
newline();
|
|
271
|
-
// Step 4: Summary
|
|
272
|
-
sectionHeader('Setup Summary');
|
|
273
|
-
const summaryItems = [
|
|
274
|
-
['API Key', result.apiKeyConfigured ? success('Configured') : error('Not Configured')],
|
|
275
|
-
['Model', result.modelDetected || info('Default')],
|
|
276
|
-
['Codebase', result.codebaseDetected ? success('Detected') : warning('Not Detected')],
|
|
277
|
-
];
|
|
278
|
-
summaryItems.forEach(([label, status]) => {
|
|
279
|
-
console.log(` ${label.padEnd(15)} ${status}`);
|
|
280
|
-
});
|
|
281
|
-
newline();
|
|
282
|
-
// Step 5: Save Configuration
|
|
283
|
-
if (result.apiKeyConfigured) {
|
|
284
|
-
const configManager = getConfigManager();
|
|
285
|
-
// Update codebase info if detected
|
|
286
|
-
if (codebase.detected) {
|
|
287
|
-
configManager.updateCodebase(codebase.root);
|
|
288
|
-
}
|
|
289
|
-
// Update model preference if detected
|
|
290
|
-
if (model) {
|
|
291
|
-
configManager.setPreference('defaultModel', model.replace(' (default)', ''));
|
|
292
|
-
}
|
|
293
|
-
// Save configuration
|
|
294
|
-
configManager.saveConfig();
|
|
295
|
-
result.configPath = configManager.getConfigPath();
|
|
296
|
-
newline();
|
|
297
|
-
console.log(success('Done.'));
|
|
298
|
-
newline();
|
|
299
|
-
console.log(colors.bold('Next steps:'));
|
|
300
|
-
console.log(colors.dim(` 1. ${colors.cyan('codehere start')} Open the task intake at :3773`));
|
|
301
|
-
console.log(colors.dim(` 2. ${colors.cyan('codehere hook install')} Audit every Claude Code action`));
|
|
302
|
-
console.log(colors.dim(` 3. ${colors.cyan('codehere trust .')} Scan your project (no API key)`));
|
|
303
|
-
newline();
|
|
304
|
-
saveSetupConfig(result);
|
|
305
|
-
// Track onboarding completion for first-time users
|
|
306
|
-
if (isFirstTime) {
|
|
307
|
-
const onboardingConfig = {
|
|
308
|
-
onboardingComplete: true,
|
|
309
|
-
onboardingDate: new Date().toISOString(),
|
|
310
|
-
commandCount: 0, // Will be tracked by feature discovery
|
|
311
|
-
};
|
|
312
|
-
const onboardingPath = join(ensureConfigDir(), 'onboarding.json');
|
|
313
|
-
writeFileSync(onboardingPath, JSON.stringify(onboardingConfig, null, 2));
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
else {
|
|
317
|
-
console.log(separator('═', 80));
|
|
318
|
-
console.log(warning('Setup Incomplete'));
|
|
319
|
-
console.log(separator('═', 80));
|
|
320
|
-
newline();
|
|
321
|
-
console.log(colors.bold('Please configure your API key to continue.'));
|
|
322
|
-
console.log(colors.dim('Run this command again after setting up your API key.'));
|
|
323
|
-
newline();
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
finally {
|
|
327
|
-
executionTracer.endRun();
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
//# sourceMappingURL=setup-command.js.map
|