codehere 0.7.1 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -13
- package/dist/application/services/verdict-service.js +154 -0
- package/dist/application/use-cases/assess-cross-vendor.js +126 -0
- package/dist/application/use-cases/build-cross-vendor-verdict.js +118 -0
- package/dist/application/use-cases/export-aat.js +107 -0
- package/dist/application/use-cases/generate-acceptance-criteria.js +223 -0
- package/dist/application/use-cases/generate-final-report.js +140 -15
- package/dist/application/use-cases/generate-narration.js +5 -3
- package/dist/application/use-cases/generate-thread-title.js +5 -2
- package/dist/application/use-cases/narrator-prompt-template.js +1 -1
- package/dist/application/use-cases/plan-auto-review.js +57 -0
- package/dist/application/use-cases/reconcile-audit.js +100 -0
- package/dist/application/use-cases/rerun-tests.js +226 -0
- package/dist/application/use-cases/search-sessions.js +70 -0
- package/dist/application/use-cases/should-auto-review.js +71 -0
- package/dist/application/use-cases/thread-export.js +90 -0
- package/dist/application/use-cases/verify-claim.js +187 -5
- package/dist/domain/audit.js +11 -0
- package/dist/domain/services/codebase-detector.js +0 -6
- package/dist/domain/task.js +91 -0
- package/dist/error-handler.js +3 -115
- package/dist/index.js +279 -31
- package/dist/infrastructure/completion/bash-completion.js +7 -3
- package/dist/infrastructure/completion/fish-completion.js +7 -12
- package/dist/infrastructure/completion/zsh-completion.js +7 -12
- package/dist/infrastructure/config/config-manager.js +4 -3
- package/dist/infrastructure/config/product-context.js +1 -19
- package/dist/infrastructure/context/adapters/figma-source.js +37 -2
- package/dist/infrastructure/context/adapters/github-issue-source.js +3 -2
- package/dist/infrastructure/context/github-api.js +110 -0
- package/dist/infrastructure/context/github-device-flow.js +65 -0
- package/dist/infrastructure/context/oauth-store.js +5 -2
- package/dist/infrastructure/context/registry-bootstrap.js +0 -4
- package/dist/infrastructure/cost/budget-config.js +4 -3
- package/dist/infrastructure/cost/cost-router.js +16 -23
- package/dist/infrastructure/cost/spend-aggregator.js +91 -16
- package/dist/infrastructure/cost/team-ledger.js +355 -0
- package/dist/infrastructure/discovery/discovery-log.js +85 -0
- package/dist/infrastructure/hooks/audit-tip-signature.js +336 -0
- package/dist/infrastructure/hooks/claude-sdk-hook.js +139 -3
- package/dist/infrastructure/hooks/hook-mode.js +20 -4
- package/dist/infrastructure/hooks/mirror-tool-call-to-audit.js +8 -2
- package/dist/infrastructure/hooks/pre-prompt-scan.js +3 -3
- package/dist/infrastructure/observability/execution-tracer.js +1 -1
- package/dist/infrastructure/orchestration/agents-config-seed.js +23 -11
- package/dist/infrastructure/orchestration/aider-adapter.js +188 -24
- package/dist/infrastructure/orchestration/artifacts-extractor.js +60 -2
- package/dist/infrastructure/orchestration/cassette-recorder.js +1 -1
- package/dist/infrastructure/orchestration/claude-code-adapter.js +117 -3
- package/dist/infrastructure/orchestration/codex-adapter.js +52 -16
- package/dist/infrastructure/orchestration/conductor-adapter.js +46 -7
- package/dist/infrastructure/orchestration/context-handoff.js +32 -14
- package/dist/infrastructure/orchestration/detect-ollama.js +75 -0
- package/dist/infrastructure/orchestration/failover-config.js +1 -11
- package/dist/infrastructure/orchestration/gemini-adapter.js +92 -12
- package/dist/infrastructure/orchestration/generic-cli-adapter.js +134 -3
- package/dist/infrastructure/orchestration/graphify-context.js +14 -59
- package/dist/infrastructure/orchestration/memory-injector.js +4 -1
- package/dist/infrastructure/orchestration/mocks/mock-agent-repl.js +167 -9
- package/dist/infrastructure/orchestration/opencode-adapter.js +84 -14
- package/dist/infrastructure/orchestration/progress-aggregator.js +275 -23
- package/dist/infrastructure/orchestration/session-manager.js +593 -94
- package/dist/infrastructure/orchestration/session-persistence.js +177 -41
- package/dist/infrastructure/orchestration/thread-key-store.js +1 -1
- package/dist/infrastructure/orchestration/write-pass-store.js +0 -8
- package/dist/infrastructure/protocols/cloud-sync.js +2 -2
- package/dist/infrastructure/protocols/mcp-helpers.js +1 -1
- package/dist/infrastructure/protocols/mcp-server-impl.js +1498 -354
- package/dist/infrastructure/protocols/mcp-tools.js +401 -0
- package/dist/infrastructure/protocols/ui/app-script-artifacts.js +121 -0
- package/dist/infrastructure/protocols/ui/app-script-audit-footer.js +44 -0
- package/dist/infrastructure/protocols/ui/app-script-audit-verify.js +56 -0
- package/dist/infrastructure/protocols/ui/app-script-composer-cost.js +588 -0
- package/dist/infrastructure/protocols/ui/app-script-context-sources.js +168 -0
- package/dist/infrastructure/protocols/ui/app-script-conversation.js +67 -0
- package/dist/infrastructure/protocols/ui/app-script-drawer.js +115 -0
- package/dist/infrastructure/protocols/ui/app-script-failover.js +133 -0
- package/dist/infrastructure/protocols/ui/app-script-github.js +113 -0
- package/dist/infrastructure/protocols/ui/app-script-observer.js +104 -0
- package/dist/infrastructure/protocols/ui/app-script-onboarding.js +80 -0
- package/dist/infrastructure/protocols/ui/app-script-overlay.js +203 -0
- package/dist/infrastructure/protocols/ui/app-script-recovery.js +136 -0
- package/dist/infrastructure/protocols/ui/app-script-render.js +166 -0
- package/dist/infrastructure/protocols/ui/app-script-share.js +78 -0
- package/dist/infrastructure/protocols/ui/app-script-sidebar.js +194 -0
- package/dist/infrastructure/protocols/ui/app-script-tour.js +78 -0
- package/dist/infrastructure/protocols/ui/app-script-wpass.js +312 -0
- package/dist/infrastructure/protocols/ui/app-script.js +2998 -0
- package/dist/infrastructure/protocols/ui/font-faces.js +7 -0
- package/dist/infrastructure/protocols/ui/styles.js +2147 -0
- package/dist/infrastructure/protocols/ui/tokens.js +96 -0
- package/dist/infrastructure/protocols/ui-page-v2.js +116 -4126
- package/dist/infrastructure/protocols/verify-page.js +884 -148
- package/dist/infrastructure/security/fix-code.js +144 -0
- package/dist/infrastructure/security/fix-secret.js +89 -0
- package/dist/infrastructure/security/permissions-config.js +3 -2
- package/dist/infrastructure/security/scan-types.js +10 -0
- package/dist/infrastructure/security/ship-scan.js +195 -0
- package/dist/infrastructure/security/trust-scorer.js +70 -3
- package/dist/infrastructure/security/vibe-checks.js +99 -0
- package/dist/infrastructure/storage/atomic-write.js +86 -0
- package/dist/infrastructure/storage/project-thread-mirror.js +151 -4
- package/dist/infrastructure/storage/project-thread-reader.js +262 -25
- package/dist/infrastructure/storage/project-thread-title-backfill.js +1 -1
- package/dist/monitoring.js +2 -2
- package/dist/presentation/cli/commands/audit-command.js +79 -4
- package/dist/presentation/cli/commands/budget-command.js +2 -2
- package/dist/presentation/cli/commands/check-command.js +96 -0
- package/dist/presentation/cli/commands/context-source-command.js +20 -1
- package/dist/presentation/cli/commands/help-command.js +5 -5
- package/dist/presentation/cli/commands/hook-command.js +5 -5
- package/dist/presentation/cli/commands/init-command.js +89 -66
- package/dist/presentation/cli/commands/memory-command.js +28 -28
- package/dist/presentation/cli/commands/permission-command.js +1 -12
- package/dist/presentation/cli/commands/protocols-builder.js +2 -2
- package/dist/presentation/cli/commands/protocols-command.js +4 -4
- package/dist/presentation/cli/commands/repo-map-builder.js +1 -1
- package/dist/presentation/cli/commands/report-command.js +48 -32
- package/dist/presentation/cli/commands/scan-command.js +52 -0
- package/dist/presentation/cli/commands/scan-tool-command.js +24 -8
- package/dist/presentation/cli/commands/search-command.js +51 -0
- package/dist/presentation/cli/commands/setup-command.js +31 -8
- package/dist/presentation/cli/commands/status-command.js +39 -86
- package/dist/presentation/cli/commands/team-command.js +256 -0
- package/dist/presentation/cli/commands/thread-command.js +87 -5
- package/dist/presentation/cli/commands/trust-command.js +53 -31
- package/dist/presentation/cli/commands/verify-command.js +74 -9
- package/dist/presentation/cli/error-display.js +0 -8
- package/dist/presentation/cli/repl.js +15 -7
- package/dist/presentation/cli/task-deliverable.js +36 -10
- package/dist/testing/replay-adapter.js +0 -19
- package/dist/ui.js +51 -239
- package/dist/utils/version.js +0 -6
- package/package.json +30 -10
- package/dist/domain/entities/task.js +0 -61
- package/dist/infrastructure/protocols/ui-page.js +0 -2377
- package/dist/infrastructure/security/input-validator.js +0 -152
- package/dist/infrastructure/storage/embeddings-path.js +0 -37
- package/dist/infrastructure/storage/task-repository.js +0 -206
package/dist/ui.js
CHANGED
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
-
import ora from 'ora';
|
|
3
|
-
import cliProgress from 'cli-progress';
|
|
4
|
-
import boxen from 'boxen';
|
|
5
2
|
import Table from 'cli-table3';
|
|
6
|
-
import { existsSync } from 'fs';
|
|
7
|
-
import { join, dirname } from 'path';
|
|
8
|
-
import { fileURLToPath } from 'url';
|
|
9
3
|
let cachedCliStyle = null;
|
|
10
4
|
export function getCliStyle() {
|
|
11
5
|
if (cachedCliStyle)
|
|
@@ -27,24 +21,16 @@ export function printMinimalHeader(command, query) {
|
|
|
27
21
|
}
|
|
28
22
|
console.log();
|
|
29
23
|
}
|
|
30
|
-
// Minimal safety note (used at end of AI responses)
|
|
31
|
-
export function printSafetyNote() {
|
|
32
|
-
console.log(colors.dim('Note: AI output may be inaccurate. Review before committing.'));
|
|
33
|
-
}
|
|
34
|
-
// Minimal context summary
|
|
35
|
-
export function printContextSummary(files, maxShow = 3) {
|
|
36
|
-
if (files.length === 0)
|
|
37
|
-
return;
|
|
38
|
-
const unique = [...new Set(files)];
|
|
39
|
-
const shown = unique.slice(0, maxShow);
|
|
40
|
-
const more = unique.length - maxShow;
|
|
41
|
-
console.log(colors.dim('Context:'));
|
|
42
|
-
shown.forEach((f) => console.log(colors.dim(` - ${f}`)));
|
|
43
|
-
if (more > 0) {
|
|
44
|
-
console.log(colors.dim(` ... and ${more} more file${more > 1 ? 's' : ''}`));
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
24
|
// Professional color scheme - subtle and enterprise-ready
|
|
25
|
+
// CLI color economy (DESIGN.md §5.2) — color carries MEANING, not decoration,
|
|
26
|
+
// and is always redundant with a glyph + word so NO_COLOR / piped output stays
|
|
27
|
+
// legible (chalk auto-disables on non-TTY and when NO_COLOR is set). The
|
|
28
|
+
// semantic names below are the vocabulary CLI output should use; the raw chalk
|
|
29
|
+
// names are kept for back-compat but new output uses meaning:
|
|
30
|
+
// verified = green (independently verified ONLY) caught = red (a contradiction caught ONLY)
|
|
31
|
+
// warn = amber (budget/attention) evidence = dim (machine facts, metadata)
|
|
32
|
+
// identity = cyan (an agent/session id, sparingly) heading = bold (the one thing that matters)
|
|
33
|
+
// Retired: magenta as decoration.
|
|
48
34
|
export const colors = {
|
|
49
35
|
success: chalk.green,
|
|
50
36
|
error: chalk.red,
|
|
@@ -59,57 +45,33 @@ export const colors = {
|
|
|
59
45
|
green: chalk.green,
|
|
60
46
|
gray: chalk.gray,
|
|
61
47
|
blue: chalk.blue,
|
|
48
|
+
// semantic economy — prefer these in new CLI output
|
|
49
|
+
verified: chalk.green,
|
|
50
|
+
caught: chalk.red,
|
|
51
|
+
warn: chalk.yellow,
|
|
52
|
+
evidence: chalk.dim,
|
|
53
|
+
identity: chalk.cyan,
|
|
54
|
+
heading: chalk.bold,
|
|
62
55
|
};
|
|
63
|
-
//
|
|
56
|
+
// S19 glyph language (BINDING, shared with the web design tokens in
|
|
57
|
+
// ui-page-v2): status = the sanctioned geometric set + ANSI color —
|
|
58
|
+
// ✓ ✕ · — › ● ○ ⊘ — never emoji-as-icon, never pictographs in stdout/stderr.
|
|
64
59
|
export const symbols = {
|
|
65
60
|
success: '✓',
|
|
66
|
-
error: '
|
|
67
|
-
warning: '
|
|
68
|
-
info: '
|
|
69
|
-
processing: '
|
|
70
|
-
arrow: '
|
|
71
|
-
dot: '
|
|
61
|
+
error: '✕',
|
|
62
|
+
warning: '!',
|
|
63
|
+
info: '›',
|
|
64
|
+
processing: '›',
|
|
65
|
+
arrow: '›',
|
|
66
|
+
dot: '·',
|
|
72
67
|
check: '✓',
|
|
73
|
-
cross: '
|
|
68
|
+
cross: '✕',
|
|
74
69
|
pipe: '│',
|
|
75
70
|
dash: '─',
|
|
76
71
|
plus: '+',
|
|
77
72
|
minus: '-',
|
|
78
73
|
bullet: '•',
|
|
79
74
|
};
|
|
80
|
-
// Spinner helper - minimal text only with smooth transitions
|
|
81
|
-
export function createSpinner(text) {
|
|
82
|
-
return ora({
|
|
83
|
-
text: colors.dim(text),
|
|
84
|
-
spinner: 'dots', // Smooth animated dots
|
|
85
|
-
color: 'cyan',
|
|
86
|
-
interval: 80, // Faster animation for better perceived performance
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
// Progress bar helper - clean and minimal with smooth transitions
|
|
90
|
-
export function createProgressBar(total, format) {
|
|
91
|
-
const bar = new cliProgress.SingleBar({
|
|
92
|
-
format: format || colors.dim('Progress') + ' |{bar}| ' + colors.cyan('{percentage}%') + ' | {value}/{total}',
|
|
93
|
-
barCompleteChar: '\u2588', // Full block for smooth appearance
|
|
94
|
-
barIncompleteChar: '\u2591', // Light shade for incomplete
|
|
95
|
-
hideCursor: true,
|
|
96
|
-
clearOnComplete: true,
|
|
97
|
-
etaBuffer: 10, // Smooth ETA calculation
|
|
98
|
-
forceRedraw: true, // Force redraw for smooth updates
|
|
99
|
-
}, cliProgress.Presets.shades_classic);
|
|
100
|
-
bar.start(total, 0);
|
|
101
|
-
return bar;
|
|
102
|
-
}
|
|
103
|
-
// Professional box helper - minimal borders
|
|
104
|
-
export function createBox(content, options) {
|
|
105
|
-
return boxen(content, {
|
|
106
|
-
padding: { top: 0, bottom: 0, left: 1, right: 1 },
|
|
107
|
-
margin: { top: 0, bottom: 1, left: 0, right: 0 },
|
|
108
|
-
borderStyle: 'single',
|
|
109
|
-
borderColor: 'gray',
|
|
110
|
-
...options,
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
75
|
// Professional banner - clean and minimal
|
|
114
76
|
export function createBanner(title, subtitle) {
|
|
115
77
|
const width = Math.max(title.length, subtitle?.length || 0) + 4;
|
|
@@ -144,6 +106,13 @@ export function warning(message) {
|
|
|
144
106
|
export function info(message) {
|
|
145
107
|
return colors.blue(symbols.info + ' ' + message);
|
|
146
108
|
}
|
|
109
|
+
// Section heading (DESIGN.md §5.2) — the one structural line per block. Bold,
|
|
110
|
+
// no color: color is reserved for meaning (verdict/identity), so a heading
|
|
111
|
+
// earns emphasis from weight, not decoration. Legible with NO_COLOR (bold is
|
|
112
|
+
// the fallback; the words carry it regardless).
|
|
113
|
+
export function heading(message) {
|
|
114
|
+
return colors.heading(message);
|
|
115
|
+
}
|
|
147
116
|
// Create professional table - minimal styling
|
|
148
117
|
export function createTable(headers, rows) {
|
|
149
118
|
const table = new Table({
|
|
@@ -178,32 +147,10 @@ export function createTable(headers, rows) {
|
|
|
178
147
|
rows.forEach((row) => table.push(row));
|
|
179
148
|
return table;
|
|
180
149
|
}
|
|
181
|
-
// Format file path - subtle
|
|
182
|
-
export function formatPath(path) {
|
|
183
|
-
return colors.cyan(path);
|
|
184
|
-
}
|
|
185
150
|
// Format number - bold
|
|
186
151
|
export function formatNumber(num) {
|
|
187
152
|
return colors.bold(num.toString());
|
|
188
153
|
}
|
|
189
|
-
// Format percentage - subtle
|
|
190
|
-
export function formatPercent(value, total) {
|
|
191
|
-
if (total !== undefined) {
|
|
192
|
-
const percent = Math.round((value / total) * 100);
|
|
193
|
-
return colors.dim(`${percent}%`);
|
|
194
|
-
}
|
|
195
|
-
// If just a percentage value (0-100)
|
|
196
|
-
return colors.dim(`${value.toFixed(1)}%`);
|
|
197
|
-
}
|
|
198
|
-
// Format score (for search results) - color by relevance
|
|
199
|
-
export function formatScore(score) {
|
|
200
|
-
const percent = Math.round(score * 100);
|
|
201
|
-
if (percent >= 50)
|
|
202
|
-
return colors.green(`${percent}%`);
|
|
203
|
-
if (percent >= 30)
|
|
204
|
-
return colors.yellow(`${percent}%`);
|
|
205
|
-
return colors.dim(`${percent}%`);
|
|
206
|
-
}
|
|
207
154
|
// Professional section header
|
|
208
155
|
export function sectionHeader(title) {
|
|
209
156
|
console.log(colors.bold(title));
|
|
@@ -215,46 +162,6 @@ export function newline(count = 1) {
|
|
|
215
162
|
console.log();
|
|
216
163
|
}
|
|
217
164
|
}
|
|
218
|
-
/**
|
|
219
|
-
* Show a human-voice "you need an API key" message and exit.
|
|
220
|
-
* Covers all supported providers — not just Cohere.
|
|
221
|
-
*/
|
|
222
|
-
export function hasAnyApiKey() {
|
|
223
|
-
return !!(process.env.ANTHROPIC_API_KEY ||
|
|
224
|
-
process.env.OPENAI_API_KEY ||
|
|
225
|
-
process.env.COHERE_API_KEY ||
|
|
226
|
-
process.env.OPENROUTER_API_KEY ||
|
|
227
|
-
process.env.CODEHERE_AI_PROVIDER === 'local' ||
|
|
228
|
-
process.env.CODEHERE_FAKE_AI === '1');
|
|
229
|
-
}
|
|
230
|
-
export function requireApiKey() {
|
|
231
|
-
newline();
|
|
232
|
-
console.log(error('You need an API key to use this command.'));
|
|
233
|
-
newline();
|
|
234
|
-
console.log(' Fastest options:');
|
|
235
|
-
console.log(colors.cyan(' export ANTHROPIC_API_KEY=sk-ant-...') + colors.dim(' (Claude models — recommended)'));
|
|
236
|
-
console.log(colors.cyan(' export OPENAI_API_KEY=sk-...'));
|
|
237
|
-
console.log(colors.cyan(' export COHERE_API_KEY=...'));
|
|
238
|
-
console.log(colors.cyan(' export CODEHERE_AI_PROVIDER=local') + colors.dim(' (Ollama — no key needed)'));
|
|
239
|
-
newline();
|
|
240
|
-
console.log(colors.dim(' These work right now, no key needed:'));
|
|
241
|
-
console.log(colors.dim(' codehere trust .'));
|
|
242
|
-
console.log(colors.dim(' codehere hook status'));
|
|
243
|
-
console.log(colors.dim(' codehere serve'));
|
|
244
|
-
newline();
|
|
245
|
-
process.exit(1);
|
|
246
|
-
}
|
|
247
|
-
// Status indicator
|
|
248
|
-
export function statusIndicator(status) {
|
|
249
|
-
const map = {
|
|
250
|
-
success: colors.green(symbols.success),
|
|
251
|
-
error: colors.red(symbols.error),
|
|
252
|
-
warning: colors.yellow(symbols.warning),
|
|
253
|
-
info: colors.blue(symbols.info),
|
|
254
|
-
processing: colors.cyan(symbols.processing),
|
|
255
|
-
};
|
|
256
|
-
return map[status];
|
|
257
|
-
}
|
|
258
165
|
// Gradient logo for "codehere" - coral → purple → blue
|
|
259
166
|
export function createCodehereLogo() {
|
|
260
167
|
const text = 'codehere';
|
|
@@ -288,115 +195,20 @@ export function createWelcomeMessage() {
|
|
|
288
195
|
// Matches the positioning in `codehere --help` and `codehere help`. codehere
|
|
289
196
|
// is the LAYER over your coding agents, not itself an agent — the old
|
|
290
197
|
// "AI coding agent with built-in safety" line implied the opposite.
|
|
291
|
-
const tagline = '
|
|
198
|
+
const tagline = 'Your AI coding agents: audited, cost-bounded, cross-vendor';
|
|
292
199
|
return `
|
|
293
200
|
${colors.bold(logo)} ${colors.dim(version)}
|
|
294
201
|
${colors.dim(tagline)}
|
|
295
202
|
`;
|
|
296
203
|
}
|
|
297
|
-
// Helper to get visual length (strip ANSI codes)
|
|
298
|
-
function visualLength(str) {
|
|
299
|
-
// Remove ANSI escape codes
|
|
300
|
-
return str.replace(/\u001b\[[0-9;]*m/g, '').length;
|
|
301
|
-
}
|
|
302
|
-
// Enhanced welcome message for interactive session
|
|
303
|
-
export function createSessionWelcome() {
|
|
304
|
-
const logo = createCodehereLogo();
|
|
305
|
-
const version = getVersionWithPrefix();
|
|
306
|
-
// Check if indexed (legacy check - uses global path for backward compatibility)
|
|
307
|
-
// Note: Active CLI flows use per-repo embeddings via getEmbeddingsDbPath()
|
|
308
|
-
let isIndexed = false;
|
|
309
|
-
try {
|
|
310
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
311
|
-
const __dirname = dirname(__filename);
|
|
312
|
-
const agentDir = join(__dirname, '..');
|
|
313
|
-
const dbPath = join(agentDir, 'data', 'embeddings.sqlite');
|
|
314
|
-
isIndexed = existsSync(dbPath);
|
|
315
|
-
}
|
|
316
|
-
catch {
|
|
317
|
-
// Ignore errors - will default to not indexed
|
|
318
|
-
}
|
|
319
|
-
const width = 54;
|
|
320
|
-
const greeting = 'Hello there, code here!';
|
|
321
|
-
// Use subtle blue-gray for borders (like Claude Code)
|
|
322
|
-
const borderColor = chalk.hex('#6B7280'); // Subtle gray-blue
|
|
323
|
-
// Helper to create a line with proper padding
|
|
324
|
-
const createLine = (content) => {
|
|
325
|
-
const contentVisual = visualLength(content);
|
|
326
|
-
// Total width = left border (2) + content + padding + right border (2)
|
|
327
|
-
// So: width = 2 + contentVisual + padding + 2
|
|
328
|
-
// padding = width - contentVisual - 4
|
|
329
|
-
const padding = Math.max(0, width - contentVisual - 4);
|
|
330
|
-
return borderColor('│ ') + content + ' '.repeat(padding) + borderColor(' │\n');
|
|
331
|
-
};
|
|
332
|
-
let welcomeBox = '';
|
|
333
|
-
welcomeBox += borderColor('┌' + '─'.repeat(width - 2) + '┐\n');
|
|
334
|
-
// Header line with logo and version
|
|
335
|
-
const headerContent = colors.bold(logo) + ' ' + colors.dim(version);
|
|
336
|
-
welcomeBox += createLine(headerContent);
|
|
337
|
-
welcomeBox += borderColor('│') + ' '.repeat(width - 2) + borderColor('│\n');
|
|
338
|
-
// Greeting line
|
|
339
|
-
welcomeBox += createLine(greeting);
|
|
340
|
-
welcomeBox += borderColor('│') + ' '.repeat(width - 2) + borderColor('│\n');
|
|
341
|
-
// Tips section
|
|
342
|
-
const tipsHeader = 'Tips for getting started:';
|
|
343
|
-
welcomeBox += createLine(colors.yellow(tipsHeader));
|
|
344
|
-
if (!isIndexed) {
|
|
345
|
-
const tip1 = ' Run "index" to build your codebase search index';
|
|
346
|
-
welcomeBox += createLine(colors.dim(tip1));
|
|
347
|
-
}
|
|
348
|
-
else {
|
|
349
|
-
const tip1 = ' Try asking: "How does authentication work?"';
|
|
350
|
-
welcomeBox += createLine(colors.dim(tip1));
|
|
351
|
-
}
|
|
352
|
-
const tip2 = ' Type "help" to see all available commands';
|
|
353
|
-
welcomeBox += createLine(colors.dim(tip2));
|
|
354
|
-
welcomeBox += borderColor('│') + ' '.repeat(width - 2) + borderColor('│\n');
|
|
355
|
-
// Quick examples
|
|
356
|
-
const examplesHeader = 'Quick start:';
|
|
357
|
-
welcomeBox += createLine(colors.cyan(examplesHeader));
|
|
358
|
-
const example1 = ' • codehere hook install — activate pre-execution scanning';
|
|
359
|
-
welcomeBox += createLine(colors.dim(example1));
|
|
360
|
-
const example2 = ' • codehere trust . — score this codebase (no API key needed)';
|
|
361
|
-
welcomeBox += createLine(colors.dim(example2));
|
|
362
|
-
const example3 = ' • codehere report — generate shareable audit report';
|
|
363
|
-
welcomeBox += createLine(colors.dim(example3));
|
|
364
|
-
welcomeBox += borderColor('└' + '─'.repeat(width - 2) + '┘');
|
|
365
|
-
return welcomeBox;
|
|
366
|
-
}
|
|
367
204
|
// ── Delight utilities ─────────────────────────────────────────────────────────
|
|
368
|
-
const THINKING_MESSAGES = [
|
|
369
|
-
'On it...',
|
|
370
|
-
'Let me look...',
|
|
371
|
-
'Thinking...',
|
|
372
|
-
'One sec...',
|
|
373
|
-
'Reading your code...',
|
|
374
|
-
'Working through this...',
|
|
375
|
-
'Bear with me...',
|
|
376
|
-
'Pulling it together...',
|
|
377
|
-
];
|
|
378
|
-
const FAREWELL_MESSAGES = [
|
|
379
|
-
'See you later! 👋',
|
|
380
|
-
'Happy shipping!',
|
|
381
|
-
'Good luck out there.',
|
|
382
|
-
'Back to building.',
|
|
383
|
-
'See you on the next one.',
|
|
384
|
-
'Later! Go build something great.',
|
|
385
|
-
'Until next time.',
|
|
386
|
-
];
|
|
387
205
|
const CLEAN_SCAN_CELEBRATIONS = [
|
|
388
206
|
'Perfect score. Nothing to worry about.',
|
|
389
|
-
'All clear
|
|
207
|
+
'All clear · clean scan.',
|
|
390
208
|
'Zero findings. Enjoy the green.',
|
|
391
209
|
'Nothing suspicious. Carry on.',
|
|
392
210
|
'Clean as a whistle.',
|
|
393
211
|
];
|
|
394
|
-
export function randomThinkingMessage() {
|
|
395
|
-
return colors.dim(THINKING_MESSAGES[Math.floor(Math.random() * THINKING_MESSAGES.length)]);
|
|
396
|
-
}
|
|
397
|
-
export function randomFarewell() {
|
|
398
|
-
return info(FAREWELL_MESSAGES[Math.floor(Math.random() * FAREWELL_MESSAGES.length)]);
|
|
399
|
-
}
|
|
400
212
|
export function randomCleanScan() {
|
|
401
213
|
return CLEAN_SCAN_CELEBRATIONS[Math.floor(Math.random() * CLEAN_SCAN_CELEBRATIONS.length)];
|
|
402
214
|
}
|
|
@@ -420,11 +232,24 @@ export const SLASH_COMMANDS = [
|
|
|
420
232
|
{ name: 'permission', args: '<sub>', description: 'Manage .codehere/permissions.yaml allow/deny', noKey: true },
|
|
421
233
|
{ name: 'narrator', args: '', description: 'Customize the PM narrator prompt', noKey: true },
|
|
422
234
|
{ name: 'budget', args: '[set <amount>]', description: 'Daily spend budget', noKey: true },
|
|
235
|
+
{
|
|
236
|
+
name: 'team',
|
|
237
|
+
args: '[set-dir <path>|budget <usd>]',
|
|
238
|
+
description: "Merge each machine's spend into one team total · local-first",
|
|
239
|
+
noKey: true,
|
|
240
|
+
},
|
|
423
241
|
// Safety + observability (no key)
|
|
424
242
|
{ name: 'hook', args: '[install|status]', description: 'Safety hook management', noKey: true },
|
|
425
243
|
{ name: 'trust', args: '[path]', description: 'Scan for vulnerabilities', noKey: true },
|
|
244
|
+
{
|
|
245
|
+
name: 'scan',
|
|
246
|
+
args: '[path]',
|
|
247
|
+
description: 'Is your app safe to ship? — the 1 thing that gets you hacked',
|
|
248
|
+
noKey: true,
|
|
249
|
+
},
|
|
250
|
+
{ name: 'check', args: '[path]', description: 'Re-run your tests — proof, not the AI’s claim', noKey: true },
|
|
426
251
|
{ name: 'report', args: '[path]', description: 'Shareable security audit', noKey: true },
|
|
427
|
-
{ name: 'status', args: '', description: 'Security dashboard
|
|
252
|
+
{ name: 'status', args: '', description: 'Security dashboard · hook activity', noKey: true },
|
|
428
253
|
{
|
|
429
254
|
name: 'verify',
|
|
430
255
|
args: '[session-id]',
|
|
@@ -432,6 +257,7 @@ export const SLASH_COMMANDS = [
|
|
|
432
257
|
noKey: true,
|
|
433
258
|
},
|
|
434
259
|
{ name: 'audit', args: '[verify|show]', description: 'Inspect the hash-chained audit log', noKey: true },
|
|
260
|
+
{ name: 'search', args: '<query>', description: 'Search past sessions · every hit cites its evidence', noKey: true },
|
|
435
261
|
{
|
|
436
262
|
name: 'context-source',
|
|
437
263
|
args: '[list|connect|disconnect]',
|
|
@@ -444,6 +270,7 @@ export const SLASH_COMMANDS = [
|
|
|
444
270
|
{ name: 'start', args: '', description: 'Open the dashboard at localhost:3773' },
|
|
445
271
|
{ name: 'serve', args: '', description: 'Run headless MCP server (no UI)', noKey: true },
|
|
446
272
|
{ name: 'task', args: '<prompt>', description: 'Delegate a task to a coding agent' },
|
|
273
|
+
{ name: 'reply', args: '<id> <text>', description: 'Continue a goal · send a follow-up to a session' },
|
|
447
274
|
{ name: 'kill', args: '<sessionId>', description: 'Kill a running task' },
|
|
448
275
|
// Utility (no key)
|
|
449
276
|
{ name: 'help', args: '', description: 'Command reference', noKey: true },
|
|
@@ -454,19 +281,4 @@ export const SLASH_COMMANDS = [
|
|
|
454
281
|
{ name: 'clear', args: '', description: 'Clear the screen', noKey: true, replOnly: true },
|
|
455
282
|
{ name: 'exit', args: '', description: 'Leave the session', noKey: true, replOnly: true },
|
|
456
283
|
];
|
|
457
|
-
export function showSlashCommandMenu() {
|
|
458
|
-
newline();
|
|
459
|
-
console.log(colors.bold(' Slash commands'));
|
|
460
|
-
newline();
|
|
461
|
-
const maxNameLen = Math.max(...SLASH_COMMANDS.map((c) => c.name.length + c.args.length)) + 1;
|
|
462
|
-
for (const cmd of SLASH_COMMANDS) {
|
|
463
|
-
const signature = `/${cmd.name}${cmd.args ? ' ' + cmd.args : ''}`;
|
|
464
|
-
const pad = ' '.repeat(Math.max(0, maxNameLen + 2 - signature.length + 8));
|
|
465
|
-
const badge = cmd.noKey ? colors.dim(' (no key)') : '';
|
|
466
|
-
console.log(` ${colors.cyan(signature)}${pad}${colors.dim(cmd.description)}${badge}`);
|
|
467
|
-
}
|
|
468
|
-
newline();
|
|
469
|
-
console.log(colors.dim(' Type any command with / prefix, or just ask a question directly.'));
|
|
470
|
-
newline();
|
|
471
|
-
}
|
|
472
284
|
//# sourceMappingURL=ui.js.map
|
package/dist/utils/version.js
CHANGED
|
@@ -43,10 +43,4 @@ export function getVersion() {
|
|
|
43
43
|
export function getVersionWithPrefix() {
|
|
44
44
|
return `v${getVersion()}`;
|
|
45
45
|
}
|
|
46
|
-
/**
|
|
47
|
-
* Reset version cache (useful for testing)
|
|
48
|
-
*/
|
|
49
|
-
export function resetVersionCache() {
|
|
50
|
-
cachedVersion = null;
|
|
51
|
-
}
|
|
52
46
|
//# sourceMappingURL=version.js.map
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codehere",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "See what your AI coding agents cost, and prove what they actually did. Runs Claude Code, Codex, Aider, Gemini and OpenCode on your machine, records every action in a hash-chained log, and re-runs your tests instead of taking an agent's word for it.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
@@ -16,6 +16,10 @@
|
|
|
16
16
|
"engines": {
|
|
17
17
|
"node": ">=20"
|
|
18
18
|
},
|
|
19
|
+
"os": [
|
|
20
|
+
"darwin",
|
|
21
|
+
"linux"
|
|
22
|
+
],
|
|
19
23
|
"scripts": {
|
|
20
24
|
"prepare": "bash ../scripts/install-hooks.sh || true",
|
|
21
25
|
"prebuild": "rm -rf dist",
|
|
@@ -29,15 +33,26 @@
|
|
|
29
33
|
"test": "npm run build && vitest run",
|
|
30
34
|
"test:watch": "vitest",
|
|
31
35
|
"lint": "eslint src/ --ext .ts",
|
|
36
|
+
"knip": "knip",
|
|
37
|
+
"knip:gate": "node scripts/knip-gate.mjs",
|
|
38
|
+
"lint:tokens": "node scripts/ui-token-gate.mjs",
|
|
39
|
+
"arch:gate": "tsc --noEmit && eslint src/ --ext .ts && node scripts/knip-gate.mjs && node scripts/ui-token-gate.mjs",
|
|
32
40
|
"format": "prettier --write 'src/**/*.ts' 'test/**/*.ts'",
|
|
33
41
|
"format:check": "prettier --check 'src/**/*.ts' 'test/**/*.ts'",
|
|
34
42
|
"sbom": "npm sbom --sbom-format=spdx --omit=dev",
|
|
35
43
|
"pretag:check": "../scripts/pretag-npm-check.sh",
|
|
36
|
-
"cassette:record": "../scripts/capture-cassette.sh"
|
|
44
|
+
"cassette:record": "../scripts/capture-cassette.sh",
|
|
45
|
+
"e2e": "node test/e2e/run.mjs",
|
|
46
|
+
"e2e:live": "CODEHERE_E2E_LIVE=1 node test/e2e/run.mjs",
|
|
47
|
+
"degraded": "node scripts/degraded-matrix.mjs",
|
|
48
|
+
"inventory": "node scripts/inventory-drift.mjs"
|
|
37
49
|
},
|
|
38
50
|
"keywords": [
|
|
39
51
|
"ai-coding-agents",
|
|
40
|
-
"
|
|
52
|
+
"verification",
|
|
53
|
+
"agent-audit",
|
|
54
|
+
"llm-cost",
|
|
55
|
+
"ai-cost-tracking",
|
|
41
56
|
"audit-trail",
|
|
42
57
|
"trust-scoring",
|
|
43
58
|
"ai-safety",
|
|
@@ -62,27 +77,32 @@
|
|
|
62
77
|
},
|
|
63
78
|
"homepage": "https://codehere.uk",
|
|
64
79
|
"dependencies": {
|
|
65
|
-
"@mohammednagy/graphify-ts": "^0.22.3",
|
|
66
|
-
"boxen": "^8.0.1",
|
|
67
80
|
"chalk": "^5.3.0",
|
|
68
|
-
"cli-progress": "^3.12.0",
|
|
69
81
|
"cli-table3": "^0.6.5",
|
|
70
82
|
"commander": "^11.1.0",
|
|
71
83
|
"dotenv": "^16.3.1",
|
|
72
|
-
"gray-matter": "^4.0.3",
|
|
73
|
-
"ora": "^7.0.1",
|
|
74
84
|
"yaml": "^2.8.4"
|
|
75
85
|
},
|
|
76
86
|
"devDependencies": {
|
|
77
87
|
"@eslint/js": "^10.0.1",
|
|
78
|
-
"@types/cli-progress": "^3.11.5",
|
|
79
88
|
"@types/node": "^20.19.25",
|
|
80
89
|
"eslint": "^10.2.0",
|
|
90
|
+
"eslint-import-resolver-typescript": "^4.4.5",
|
|
91
|
+
"eslint-plugin-import-x": "^4.16.2",
|
|
92
|
+
"knip": "^6.16.1",
|
|
81
93
|
"playwright": "^1.60.0",
|
|
82
94
|
"prettier": "^3.8.3",
|
|
83
95
|
"tsx": "^4.21.0",
|
|
84
96
|
"typescript": "^5.9.3",
|
|
85
97
|
"typescript-eslint": "^8.58.2",
|
|
86
98
|
"vitest": "^4.1.4"
|
|
99
|
+
},
|
|
100
|
+
"peerDependencies": {
|
|
101
|
+
"@mohammednagy/graphify-ts": "^0.22.3"
|
|
102
|
+
},
|
|
103
|
+
"peerDependenciesMeta": {
|
|
104
|
+
"@mohammednagy/graphify-ts": {
|
|
105
|
+
"optional": true
|
|
106
|
+
}
|
|
87
107
|
}
|
|
88
108
|
}
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Domain Entity: CodehereTask
|
|
3
|
-
* Task and workflow entities for the 4-mode interaction model
|
|
4
|
-
*
|
|
5
|
-
* Phase 1: v0.5.0-alpha
|
|
6
|
-
* See: .internal/ARCHITECTURE.md (internal docs)
|
|
7
|
-
*/
|
|
8
|
-
/**
|
|
9
|
-
* Task Mode - The interaction mode that created this task
|
|
10
|
-
*/
|
|
11
|
-
export var TaskMode;
|
|
12
|
-
(function (TaskMode) {
|
|
13
|
-
TaskMode["SUGGEST"] = "suggest";
|
|
14
|
-
TaskMode["PLAN"] = "plan";
|
|
15
|
-
TaskMode["RUN"] = "run";
|
|
16
|
-
TaskMode["REVIEW"] = "review";
|
|
17
|
-
})(TaskMode || (TaskMode = {}));
|
|
18
|
-
/**
|
|
19
|
-
* Task Status - Current lifecycle state
|
|
20
|
-
*/
|
|
21
|
-
export var TaskStatus;
|
|
22
|
-
(function (TaskStatus) {
|
|
23
|
-
TaskStatus["CREATED"] = "created";
|
|
24
|
-
TaskStatus["PLANNED"] = "planned";
|
|
25
|
-
TaskStatus["RUNNING"] = "running";
|
|
26
|
-
TaskStatus["COMPLETED"] = "completed";
|
|
27
|
-
TaskStatus["FAILED"] = "failed";
|
|
28
|
-
TaskStatus["NEEDS_REVIEW"] = "needs_review";
|
|
29
|
-
TaskStatus["APPROVED"] = "approved";
|
|
30
|
-
TaskStatus["REJECTED"] = "rejected";
|
|
31
|
-
TaskStatus["CANCELLED"] = "cancelled";
|
|
32
|
-
})(TaskStatus || (TaskStatus = {}));
|
|
33
|
-
/**
|
|
34
|
-
* Risk Tier - Risk level for operation
|
|
35
|
-
*/
|
|
36
|
-
export var RiskTier;
|
|
37
|
-
(function (RiskTier) {
|
|
38
|
-
RiskTier["T1"] = "t1";
|
|
39
|
-
RiskTier["T2"] = "t2";
|
|
40
|
-
RiskTier["T3"] = "t3";
|
|
41
|
-
})(RiskTier || (RiskTier = {}));
|
|
42
|
-
/**
|
|
43
|
-
* Environment Mode - Data access boundaries
|
|
44
|
-
*/
|
|
45
|
-
export var EnvironmentMode;
|
|
46
|
-
(function (EnvironmentMode) {
|
|
47
|
-
EnvironmentMode["LOCAL"] = "local";
|
|
48
|
-
EnvironmentMode["ORG"] = "org";
|
|
49
|
-
EnvironmentMode["INTERNET"] = "internet";
|
|
50
|
-
})(EnvironmentMode || (EnvironmentMode = {}));
|
|
51
|
-
/**
|
|
52
|
-
* Review Status
|
|
53
|
-
*/
|
|
54
|
-
export var ReviewStatus;
|
|
55
|
-
(function (ReviewStatus) {
|
|
56
|
-
ReviewStatus["NOT_REQUIRED"] = "not_required";
|
|
57
|
-
ReviewStatus["PENDING"] = "pending";
|
|
58
|
-
ReviewStatus["APPROVED"] = "approved";
|
|
59
|
-
ReviewStatus["REJECTED"] = "rejected";
|
|
60
|
-
})(ReviewStatus || (ReviewStatus = {}));
|
|
61
|
-
//# sourceMappingURL=task.js.map
|