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.
Files changed (163) hide show
  1. package/README.md +36 -192
  2. package/dist/application/use-cases/check-claimed-tests.js +81 -0
  3. package/dist/application/use-cases/detect-test-weakening.js +237 -0
  4. package/dist/application/use-cases/rerun-tests.js +110 -7
  5. package/dist/index.js +38 -1212
  6. package/dist/infrastructure/orchestration/artifacts-extractor.js +2 -323
  7. package/dist/presentation/cli/commands/check-command.js +178 -23
  8. package/dist/presentation/cli/commands/help-command.js +21 -285
  9. package/dist/ui.js +5 -236
  10. package/dist/utils/version.js +0 -6
  11. package/package.json +9 -38
  12. package/dist/application/services/verdict-service.js +0 -154
  13. package/dist/application/use-cases/assess-cross-vendor.js +0 -126
  14. package/dist/application/use-cases/build-cross-vendor-verdict.js +0 -118
  15. package/dist/application/use-cases/domain-trust.js +0 -110
  16. package/dist/application/use-cases/export-aat.js +0 -107
  17. package/dist/application/use-cases/generate-acceptance-criteria.js +0 -223
  18. package/dist/application/use-cases/generate-final-report.js +0 -259
  19. package/dist/application/use-cases/generate-narration.js +0 -130
  20. package/dist/application/use-cases/generate-thread-title.js +0 -181
  21. package/dist/application/use-cases/narrator-prompt-template.js +0 -158
  22. package/dist/application/use-cases/plan-auto-review.js +0 -57
  23. package/dist/application/use-cases/reconcile-audit.js +0 -100
  24. package/dist/application/use-cases/search-sessions.js +0 -70
  25. package/dist/application/use-cases/should-auto-review.js +0 -71
  26. package/dist/application/use-cases/thread-export.js +0 -245
  27. package/dist/application/use-cases/verify-claim.js +0 -456
  28. package/dist/application/use-cases/write-pass.js +0 -277
  29. package/dist/domain/audit.js +0 -11
  30. package/dist/domain/services/codebase-detector.js +0 -85
  31. package/dist/domain/task.js +0 -91
  32. package/dist/error-handler.js +0 -139
  33. package/dist/infrastructure/completion/bash-completion.js +0 -29
  34. package/dist/infrastructure/completion/fish-completion.js +0 -17
  35. package/dist/infrastructure/completion/zsh-completion.js +0 -25
  36. package/dist/infrastructure/config/config-manager.js +0 -174
  37. package/dist/infrastructure/config/product-context.js +0 -49
  38. package/dist/infrastructure/context/adapters/drawio-source.js +0 -157
  39. package/dist/infrastructure/context/adapters/figma-source.js +0 -198
  40. package/dist/infrastructure/context/adapters/github-issue-source.js +0 -160
  41. package/dist/infrastructure/context/adapters/linear-source.js +0 -190
  42. package/dist/infrastructure/context/context-resolver.js +0 -144
  43. package/dist/infrastructure/context/context-source.js +0 -114
  44. package/dist/infrastructure/context/github-api.js +0 -110
  45. package/dist/infrastructure/context/github-device-flow.js +0 -65
  46. package/dist/infrastructure/context/oauth-flow.js +0 -207
  47. package/dist/infrastructure/context/oauth-store.js +0 -122
  48. package/dist/infrastructure/context/registry-bootstrap.js +0 -95
  49. package/dist/infrastructure/context/write-pass-resolver.js +0 -106
  50. package/dist/infrastructure/cost/budget-config.js +0 -114
  51. package/dist/infrastructure/cost/cost-router.js +0 -97
  52. package/dist/infrastructure/cost/spend-aggregator.js +0 -222
  53. package/dist/infrastructure/cost/team-ledger.js +0 -355
  54. package/dist/infrastructure/discovery/discovery-log.js +0 -85
  55. package/dist/infrastructure/hooks/audit-tip-signature.js +0 -336
  56. package/dist/infrastructure/hooks/claude-sdk-hook.js +0 -1018
  57. package/dist/infrastructure/hooks/hook-mode.js +0 -87
  58. package/dist/infrastructure/hooks/mirror-tool-call-to-audit.js +0 -49
  59. package/dist/infrastructure/hooks/pre-prompt-scan.js +0 -122
  60. package/dist/infrastructure/hooks/session-analyzer.js +0 -177
  61. package/dist/infrastructure/observability/execution-tracer.js +0 -227
  62. package/dist/infrastructure/orchestration/agent-adapter.js +0 -13
  63. package/dist/infrastructure/orchestration/agent-installer.js +0 -101
  64. package/dist/infrastructure/orchestration/agents-config-seed.js +0 -68
  65. package/dist/infrastructure/orchestration/aider-adapter.js +0 -611
  66. package/dist/infrastructure/orchestration/cassette-recorder.js +0 -120
  67. package/dist/infrastructure/orchestration/claude-code-adapter.js +0 -592
  68. package/dist/infrastructure/orchestration/codex-adapter.js +0 -574
  69. package/dist/infrastructure/orchestration/conductor-adapter.js +0 -416
  70. package/dist/infrastructure/orchestration/context-handoff.js +0 -162
  71. package/dist/infrastructure/orchestration/control/codex-control-surface.js +0 -99
  72. package/dist/infrastructure/orchestration/control/control-surface.js +0 -15
  73. package/dist/infrastructure/orchestration/detect-ollama.js +0 -75
  74. package/dist/infrastructure/orchestration/failover-classifier.js +0 -151
  75. package/dist/infrastructure/orchestration/failover-config.js +0 -67
  76. package/dist/infrastructure/orchestration/failover-policy.js +0 -106
  77. package/dist/infrastructure/orchestration/gemini-adapter.js +0 -663
  78. package/dist/infrastructure/orchestration/generic-cli-adapter.js +0 -689
  79. package/dist/infrastructure/orchestration/graphify-context.js +0 -209
  80. package/dist/infrastructure/orchestration/memory-injector.js +0 -380
  81. package/dist/infrastructure/orchestration/mocks/mock-agent-repl.js +0 -388
  82. package/dist/infrastructure/orchestration/mocks/mock-codex-app-server.js +0 -73
  83. package/dist/infrastructure/orchestration/mocks/mock-synthesis.js +0 -83
  84. package/dist/infrastructure/orchestration/mocks/resolve-agent-spawn.js +0 -37
  85. package/dist/infrastructure/orchestration/opencode-adapter.js +0 -580
  86. package/dist/infrastructure/orchestration/progress-aggregator.js +0 -906
  87. package/dist/infrastructure/orchestration/readiness.js +0 -103
  88. package/dist/infrastructure/orchestration/session-manager.js +0 -1590
  89. package/dist/infrastructure/orchestration/session-persistence.js +0 -394
  90. package/dist/infrastructure/orchestration/thread-key-store.js +0 -127
  91. package/dist/infrastructure/orchestration/write-pass-store.js +0 -38
  92. package/dist/infrastructure/protocols/cloud-sync.js +0 -207
  93. package/dist/infrastructure/protocols/mcp-helpers.js +0 -25
  94. package/dist/infrastructure/protocols/mcp-server-impl.js +0 -3935
  95. package/dist/infrastructure/protocols/mcp-tools.js +0 -401
  96. package/dist/infrastructure/protocols/probe-port.js +0 -36
  97. package/dist/infrastructure/protocols/thread-phase.js +0 -105
  98. package/dist/infrastructure/protocols/ui/app-script-artifacts.js +0 -121
  99. package/dist/infrastructure/protocols/ui/app-script-audit-footer.js +0 -44
  100. package/dist/infrastructure/protocols/ui/app-script-audit-verify.js +0 -56
  101. package/dist/infrastructure/protocols/ui/app-script-composer-cost.js +0 -588
  102. package/dist/infrastructure/protocols/ui/app-script-context-sources.js +0 -168
  103. package/dist/infrastructure/protocols/ui/app-script-conversation.js +0 -67
  104. package/dist/infrastructure/protocols/ui/app-script-drawer.js +0 -115
  105. package/dist/infrastructure/protocols/ui/app-script-failover.js +0 -133
  106. package/dist/infrastructure/protocols/ui/app-script-github.js +0 -113
  107. package/dist/infrastructure/protocols/ui/app-script-observer.js +0 -104
  108. package/dist/infrastructure/protocols/ui/app-script-onboarding.js +0 -80
  109. package/dist/infrastructure/protocols/ui/app-script-overlay.js +0 -203
  110. package/dist/infrastructure/protocols/ui/app-script-recovery.js +0 -136
  111. package/dist/infrastructure/protocols/ui/app-script-render.js +0 -166
  112. package/dist/infrastructure/protocols/ui/app-script-share.js +0 -78
  113. package/dist/infrastructure/protocols/ui/app-script-sidebar.js +0 -194
  114. package/dist/infrastructure/protocols/ui/app-script-tour.js +0 -78
  115. package/dist/infrastructure/protocols/ui/app-script-wpass.js +0 -312
  116. package/dist/infrastructure/protocols/ui/app-script.js +0 -2998
  117. package/dist/infrastructure/protocols/ui/font-faces.js +0 -7
  118. package/dist/infrastructure/protocols/ui/styles.js +0 -2147
  119. package/dist/infrastructure/protocols/ui/tokens.js +0 -96
  120. package/dist/infrastructure/protocols/ui-page-v2.js +0 -320
  121. package/dist/infrastructure/protocols/verify-page.js +0 -1677
  122. package/dist/infrastructure/security/file-collection.js +0 -118
  123. package/dist/infrastructure/security/fix-code.js +0 -144
  124. package/dist/infrastructure/security/fix-secret.js +0 -89
  125. package/dist/infrastructure/security/permissions-config.js +0 -235
  126. package/dist/infrastructure/security/scan-types.js +0 -10
  127. package/dist/infrastructure/security/ship-scan.js +0 -195
  128. package/dist/infrastructure/security/trust-scorer.js +0 -724
  129. package/dist/infrastructure/security/vibe-checks.js +0 -99
  130. package/dist/infrastructure/storage/atomic-write.js +0 -86
  131. package/dist/infrastructure/storage/cross-tool-agents.js +0 -268
  132. package/dist/infrastructure/storage/project-thread-migration.js +0 -237
  133. package/dist/infrastructure/storage/project-thread-mirror.js +0 -671
  134. package/dist/infrastructure/storage/project-thread-reader.js +0 -409
  135. package/dist/infrastructure/storage/project-thread-title-backfill.js +0 -214
  136. package/dist/monitoring.js +0 -212
  137. package/dist/presentation/cli/commands/audit-command.js +0 -201
  138. package/dist/presentation/cli/commands/budget-command.js +0 -79
  139. package/dist/presentation/cli/commands/config-command.js +0 -125
  140. package/dist/presentation/cli/commands/context-source-command.js +0 -217
  141. package/dist/presentation/cli/commands/hook-command.js +0 -265
  142. package/dist/presentation/cli/commands/init-command.js +0 -590
  143. package/dist/presentation/cli/commands/memory-command.js +0 -1031
  144. package/dist/presentation/cli/commands/narrator-command.js +0 -101
  145. package/dist/presentation/cli/commands/permission-command.js +0 -105
  146. package/dist/presentation/cli/commands/protocols-builder.js +0 -254
  147. package/dist/presentation/cli/commands/protocols-command.js +0 -84
  148. package/dist/presentation/cli/commands/repo-map-builder.js +0 -247
  149. package/dist/presentation/cli/commands/report-command.js +0 -403
  150. package/dist/presentation/cli/commands/scan-command.js +0 -52
  151. package/dist/presentation/cli/commands/scan-tool-command.js +0 -110
  152. package/dist/presentation/cli/commands/search-command.js +0 -51
  153. package/dist/presentation/cli/commands/setup-command.js +0 -330
  154. package/dist/presentation/cli/commands/status-command.js +0 -614
  155. package/dist/presentation/cli/commands/team-command.js +0 -256
  156. package/dist/presentation/cli/commands/thread-command.js +0 -370
  157. package/dist/presentation/cli/commands/trust-command.js +0 -328
  158. package/dist/presentation/cli/commands/verify-command.js +0 -298
  159. package/dist/presentation/cli/error-display.js +0 -328
  160. package/dist/presentation/cli/repl.js +0 -805
  161. package/dist/presentation/cli/task-deliverable.js +0 -153
  162. package/dist/testing/cassette.js +0 -125
  163. 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