codehere 1.0.2 → 2.0.0-beta.0

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/dist/index.js DELETED
@@ -1,111 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * codehere CLI entry: `check` re-runs a project's tests and reports what actually passed.
4
- * The earlier orchestrator, daemon, web UI and the other commands are on the legacy/2026-10 branch.
5
- */
6
- import { Command } from 'commander';
7
- import { join, dirname } from 'path';
8
- import { fileURLToPath } from 'url';
9
- import { readFileSync } from 'fs';
10
- // A crash must stop the process: `process.exitCode = 1` is only honoured once the event loop drains.
11
- function safeVersion() {
12
- try {
13
- const here = dirname(fileURLToPath(import.meta.url));
14
- return JSON.parse(readFileSync(join(here, '..', 'package.json'), 'utf-8')).version ?? 'unknown';
15
- }
16
- catch {
17
- return 'unknown';
18
- }
19
- }
20
- function crashReport(kind, err) {
21
- const e = err instanceof Error ? err : new Error(String(err));
22
- return [
23
- '',
24
- `[codehere] ${kind} — this process is stopping.`,
25
- ` version : ${safeVersion()}`,
26
- ` node : ${process.version} on ${process.platform} ${process.arch}`,
27
- '',
28
- (e.stack ?? e.message)
29
- .split('\n')
30
- .map((l) => ` ${l}`)
31
- .join('\n'),
32
- '',
33
- ' Report at https://github.com/muhammadegaa/codehere/issues with the block above.',
34
- '',
35
- ].join('\n');
36
- }
37
- process.on('uncaughtException', (err) => {
38
- console.error(crashReport('uncaughtException', err));
39
- process.exit(1);
40
- });
41
- process.on('unhandledRejection', (reason) => {
42
- console.error(crashReport('unhandledRejection', reason));
43
- process.exit(1);
44
- });
45
- import { executeCheckCommand } from './presentation/cli/commands/check-command.js';
46
- import { executeHelpCommand } from './presentation/cli/commands/help-command.js';
47
- import { error } from './ui.js';
48
- import { getVersion } from './utils/version.js';
49
- import { checkTypo, looksLikeUnknownCommand } from './utils/typo-guard.js';
50
- const program = new Command();
51
- program
52
- .name('codehere')
53
- .description('Re-run your project\'s tests yourself, so an AI\'s "all tests pass" is checked, not taken on trust.')
54
- .version(getVersion())
55
- .configureHelp({ helpWidth: 80, sortSubcommands: true });
56
- // Bare `codehere` prints help. Anything else that is not a command goes through the typo guard.
57
- program.allowExcessArguments(true).action(() => {
58
- const input = program.args.join(' ').trim();
59
- if (!input) {
60
- program.outputHelp();
61
- return;
62
- }
63
- const known = program.commands.map((c) => c.name());
64
- const suggestions = checkTypo(input, known);
65
- const unknown = suggestions
66
- ? input.split(/\s+/)[0]
67
- : (looksLikeUnknownCommand(input, known) ?? input.split(/\s+/)[0]);
68
- console.error(`codehere: unknown command '${unknown}'.`);
69
- console.error('');
70
- if (suggestions) {
71
- console.error(' Did you mean:');
72
- for (const s of suggestions)
73
- console.error(` codehere ${s}`);
74
- }
75
- else {
76
- console.error(' Run `codehere help` to see available commands.');
77
- }
78
- console.error('');
79
- process.exit(1);
80
- });
81
- program
82
- .command('check')
83
- .description('Re-run your tests independently — proof of what the AI actually did, not what it claimed')
84
- .argument('[target]', 'Repo or directory to check (default: current directory)')
85
- .option('--json', 'Machine-readable output')
86
- .option('--proof', 'Emit a paste-ready proof block for a PR description or review comment')
87
- .option('--claim <text>', "The AI's own summary: test files it names that are not in the repo are flagged")
88
- .option('--base <ref>', 'Look for weakened tests in everything since this branch left <ref> (e.g. origin/main)')
89
- .action(async (target, options) => {
90
- try {
91
- process.exitCode = await executeCheckCommand(target, {
92
- json: options?.json,
93
- proof: options?.proof,
94
- claim: options?.claim,
95
- base: options?.base,
96
- });
97
- }
98
- catch (err) {
99
- console.error(error('Error:'), err instanceof Error ? err.message : String(err));
100
- process.exit(1);
101
- }
102
- });
103
- program
104
- .command('help')
105
- .description('Show help for commands')
106
- .argument('[command]', 'Command name to get help for')
107
- .action(async (commandName) => {
108
- await executeHelpCommand(commandName);
109
- });
110
- program.parse();
111
- //# sourceMappingURL=index.js.map
@@ -1,66 +0,0 @@
1
- /**
2
- * Test summary parsing: "N passed", "M failed" and the runner-specific variants, out of collected stdout.
3
- * Used by `codehere check` to read what a test run reported.
4
- */
5
- /** Parse "N passed", "M failed", "K tests" out of accumulated stdout. Best-effort.
6
- * Vitest + jest output both have TWO pass/fail lines ("Test Files 11 passed" then
7
- * "Tests 47 passed"). We want the second/last. Take the last match in the buffer,
8
- * which is also what appears in the final summary. */
9
- function lastMatch(s, re) {
10
- const g = new RegExp(re.source, re.flags.includes('g') ? re.flags : re.flags + 'g');
11
- let last = null;
12
- let m;
13
- while ((m = g.exec(s)) !== null)
14
- last = m;
15
- return last;
16
- }
17
- /** Parse a test pass/fail summary out of accumulated stdout. Best-effort and
18
- * runner-agnostic so "trust the result" holds across whatever the repo uses,
19
- * not just node runners. Exported for direct unit coverage. */
20
- export function parseTestSummary(stdout) {
21
- if (!stdout)
22
- return undefined;
23
- const s = stdout.slice(-8000); // only look at the tail
24
- // Primary — "N passed" / "M failed": vitest, jest, pytest, mocha ("passing"),
25
- // cargo ("5 passed; 0 failed"). Unchanged so existing matches never shift.
26
- let passed = lastMatch(s, /(\d+)\s+pass(?:ed|ing)/i);
27
- let failed = lastMatch(s, /(\d+)\s+fail(?:ed|ing)/i);
28
- // Fallback A — reversed order "Passed: N" / "Failed: N" (dotnet test, some CI
29
- // summaries). Only consulted when the primary matched nothing, so the common
30
- // path is untouched.
31
- if (!passed && !failed) {
32
- passed = lastMatch(s, /pass(?:ed)?:\s*(\d+)/i);
33
- failed = lastMatch(s, /fail(?:ed)?:\s*(\d+)/i);
34
- }
35
- // Fallback B — python unittest: "Ran 5 tests in 0.0s" then "OK" or
36
- // "FAILED (failures=2, errors=1)". There is no "passed" word at all, so the
37
- // earlier patterns never see it — this was a real drop the dogfood surfaced.
38
- if (!passed && !failed) {
39
- const ran = lastMatch(s, /Ran\s+(\d+)\s+tests?\b/i);
40
- if (ran) {
41
- const total = parseInt(ran[1], 10);
42
- const f = lastMatch(s, /failures=(\d+)/i);
43
- const e = lastMatch(s, /errors=(\d+)/i);
44
- const failedCount = (f ? parseInt(f[1], 10) : 0) + (e ? parseInt(e[1], 10) : 0);
45
- const unittest = {
46
- passed: Math.max(0, total - failedCount),
47
- failed: failedCount,
48
- };
49
- const d = lastMatch(s, /in\s+([\d.]+)s\b/i);
50
- if (d)
51
- unittest.durationMs = Math.round(parseFloat(d[1]) * 1000);
52
- return unittest;
53
- }
54
- }
55
- if (!passed && !failed)
56
- return undefined;
57
- const result = {
58
- passed: passed ? parseInt(passed[1], 10) : 0,
59
- failed: failed ? parseInt(failed[1], 10) : 0,
60
- };
61
- const dur = lastMatch(s, /Duration\s+([\d.]+)s/i) ?? lastMatch(s, /Time:\s+([\d.]+)\s*s/i);
62
- if (dur)
63
- result.durationMs = Math.round(parseFloat(dur[1]) * 1000);
64
- return result;
65
- }
66
- //# sourceMappingURL=artifacts-extractor.js.map
@@ -1,251 +0,0 @@
1
- /**
2
- * CLI: check — the simplest proof. codehere independently RE-RUNS your project's
3
- * tests and reports the real result, so you trust what the AI actually did, not
4
- * what it claimed. Zero setup, no daemon, no API. Exit codes for CI: 0 a verified pass, 1 a finding (tests
5
- * failed, no test ran, a test weakened, a claimed test file missing), 2 nothing could be verified.
6
- */
7
- import { execFileSync } from 'node:child_process';
8
- import { statSync } from 'node:fs';
9
- import { resolve } from 'node:path';
10
- import { colors, symbols } from '../../../ui.js';
11
- import { rerunTests } from '../../../application/use-cases/rerun-tests.js';
12
- import { scanTestWeakening, } from '../../../application/use-cases/detect-test-weakening.js';
13
- import { findMissingClaimedTests } from '../../../application/use-cases/check-claimed-tests.js';
14
- const WHY_TEXT = {
15
- 'trivial-assertion': 'assertion that checks nothing',
16
- skipped: 'test skipped',
17
- 'commented-out': 'assertion commented out',
18
- 'unreachable-assertion': 'assertion after an early return',
19
- };
20
- // File names, diff lines and branch names come from whoever wrote the change, so they are treated as untrusted:
21
- // control characters (terminal escapes among them) are replaced, and in the markdown receipt the text sits inside
22
- // an inline code span, where no link, @mention or emphasis renders.
23
- // Control characters, plus direction overrides, zero-width, soft-hyphen, line-separator and tag characters that can
24
- // disguise a file name.
25
- const plain = (s) => s.replace(/[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u180e\u200b-\u200f\u2028-\u202e\u2060-\u2069\ufeff\u{e0000}-\u{e007f}]|\u034f/gu, '?');
26
- const code = (s) => '`' + plain(s).replace(/`/g, "'") + '`';
27
- /** Characters a branch name usually has; anything else is not repeated in the reproduce command. */
28
- const SAFE_REF = /^[A-Za-z0-9._/@+-]+$/;
29
- const weakenedLines = (w) => w.map((f) => `${plain(f.file)}:${f.line} · ${WHY_TEXT[f.why]} · ${plain(f.text)}`);
30
- const weakenedMarkdown = (w) => w.map((f) => `${code(f.file)}:${f.line} · ${WHY_TEXT[f.why]} · ${code(f.text)}`);
31
- export const RECEIPT_MARKER = '<!-- codehere-receipt -->';
32
- /**
33
- * One word per result, for CI. Green (`pass`) only when codehere re-ran the suite, it exited 0, no test
34
- * was weakened, no named test file was missing and the weakened-test scan finished. Tampering is `fail` even if
35
- * no suite could run; every other case where nothing was verified is `unverified`, never a pass.
36
- */
37
- export function checkVerdict(r, weakened, missingClaimed, scanError) {
38
- if (weakened.length || missingClaimed.length)
39
- return 'fail';
40
- if (r.kind === 'disabled')
41
- return 'skipped';
42
- if (r.kind === 'failed')
43
- return 'fail';
44
- if (scanError)
45
- return 'unverified';
46
- if (r.kind === 'passed' && r.ran && (r.exitCode ?? 1) === 0)
47
- return 'pass';
48
- return 'unverified';
49
- }
50
- /** The check command that gives the same verdict as this run: the options that changed it are repeated. */
51
- function reproduceCommand({ base, claim }) {
52
- let cmd = 'codehere check';
53
- if (base)
54
- cmd += SAFE_REF.test(base) ? ` --base ${base}` : ' --base <the same base>';
55
- if (claim) {
56
- const inlineable = claim.length <= 160 && !/[`'\n\r]/.test(claim);
57
- cmd += inlineable ? ` --claim '${plain(claim)}'` : ' --claim <the same summary>';
58
- }
59
- return cmd;
60
- }
61
- /** Short commit SHA of `cwd`'s repo, or null when it isn't a git checkout. */
62
- function gitShortSha(cwd) {
63
- try {
64
- return execFileSync('git', ['rev-parse', '--short', 'HEAD'], { cwd, stdio: ['ignore', 'pipe', 'ignore'] })
65
- .toString()
66
- .trim();
67
- }
68
- catch {
69
- return null;
70
- }
71
- }
72
- /**
73
- * A paste-ready "verified outcome receipt": the one thing a reviewer can trust
74
- * without re-running anything — codehere independently re-ran the suite, here's the
75
- * observed result, pinned to a commit + timestamp, WITH the exact command to
76
- * reproduce it independently. Plain markdown so it drops into a PR description or
77
- * review comment as-is.
78
- *
79
- * Why reproducibility is the trust mechanism (not a digest): a self-computed hash
80
- * over self-reported facts proves nothing — a sender could edit the facts AND the
81
- * hash. What a third party can actually trust is REPRODUCING it: check out the
82
- * pinned commit, run `codehere check`, get the same exit code. So the receipt leads
83
- * with the reproduce command and is honest about SCOPE (a test re-run, not a
84
- * security/logic audit) — it never says more than it verified.
85
- *
86
- * ponytail: this is the standalone (no-daemon) receipt. Upgrade path to true
87
- * tamper-EVIDENCE (authenticity, not just reproducibility): when a ~/.codehere audit
88
- * chain covers this commit, fold in its verified hash (audit-chain.ts) so the receipt
89
- * also carries a digest a third party can check against an append-only log.
90
- */
91
- function buildProofBlock(cwd, r, weakened = [], missingClaimed = [], repro = {}, scan = { findings: [] }) {
92
- const sha = gitShortSha(cwd);
93
- const when = new Date().toISOString().replace('T', ' ').slice(0, 16) + ' UTC';
94
- const at = sha ? `commit \`${sha}\`` : 'the working tree';
95
- const weakenedNote = weakened.length
96
- ? `> Tests changed in this diff, so a pass may check nothing: ${weakenedMarkdown(weakened).join(' · ')}\n`
97
- : '';
98
- const missingNote = missingClaimed.length
99
- ? `> The AI named test files that are not in the repo: ${missingClaimed.map(code).join(' · ')}\n`
100
- : '';
101
- const scanNote = scan.error
102
- ? `> Weakened-test scan could not run: ${scan.error}\n`
103
- : scan.skipped
104
- ? `> Weakened-test scan skipped: ${scan.skipped}\n`
105
- : '';
106
- // A finding is reported even when the suite could not run.
107
- const notes = weakenedNote + missingNote + scanNote;
108
- if (!r.ran) {
109
- if (r.kind === 'command-not-found') {
110
- const fix = r.remedy ? ` · fix: \`${r.remedy}\`` : '';
111
- return `> **codehere · verified outcome receipt** — couldn't run ${code(r.command || 'the tests')} (runner/deps not installed) · unverified${fix} · ${when}\n${notes}`;
112
- }
113
- return `> **codehere · verified outcome receipt** — no suite re-run (${code(r.skippedReason || 'none detected')}) · nothing verified · ${when}\n${notes}`;
114
- }
115
- if (r.kind === 'runner-missing') {
116
- const fix = r.remedy ? ` · fix: \`${r.remedy}\`` : '';
117
- return `> **codehere · verified outcome receipt** — couldn't run ${code(r.command ?? '')} on ${at} (runner not installed, exit ${r.exitCode}) · unverified${fix} · ${when}\n${notes}`;
118
- }
119
- const passed = (r.exitCode ?? 1) === 0 && !r.noTests;
120
- const counts = typeof r.passed === 'number' || typeof r.failed === 'number'
121
- ? ` (${r.passed ?? '?'} passed · ${r.failed ?? 0} failed)`
122
- : '';
123
- const verdict = r.noTests
124
- ? '✕ no tests ran'
125
- : !passed
126
- ? '✕ tests FAIL'
127
- : weakened.length
128
- ? `✕ tests pass · ${weakened.length} weakened`
129
- : missingClaimed.length
130
- ? `✕ tests pass · ${missingClaimed.length} claimed test file not found`
131
- : scan.error
132
- ? '✕ tests pass · weakened-test scan could not run'
133
- : '✓ tests pass';
134
- // Reproduce line: the real, verifiable trust — anyone can re-run and confirm.
135
- const reproduce = sha ? `> Reproduce independently: \`git checkout ${sha} && ${reproduceCommand(repro)}\`\n` : '';
136
- return (`> **codehere · verified outcome receipt** · ${verdict}${counts}\n` +
137
- `> codehere independently re-ran ${code(r.command ?? '')} on ${at} — exit ${r.exitCode}. Observed, not the AI's claim. · ${when}\n` +
138
- notes +
139
- reproduce +
140
- `> Scope: the project's test suite — not a security or logic audit.\n`);
141
- }
142
- export async function executeCheckCommand(target, options = {}) {
143
- const cwd = resolve(target || '.');
144
- let isFolder = false;
145
- try {
146
- isFolder = statSync(cwd).isDirectory();
147
- }
148
- catch {
149
- /* missing path: handled below */
150
- }
151
- if (!isFolder)
152
- throw new Error(`No such folder: ${target}`);
153
- const r = await rerunTests(cwd);
154
- const scan = scanTestWeakening(cwd, options.base);
155
- const weakened = scan.findings;
156
- const missingClaimed = options.claim ? findMissingClaimedTests(cwd, options.claim) : [];
157
- const verdict = checkVerdict(r, weakened, missingClaimed, scan.error);
158
- const weakeningScan = scan.error ? `could not run: ${scan.error}` : scan.skipped ? `skipped: ${scan.skipped}` : 'ran';
159
- const exitCode = verdict === 'pass' ? 0 : verdict === 'fail' ? 1 : 2;
160
- const weakenedText = () => weakened.length
161
- ? weakenedLines(weakened)
162
- .map((l) => colors.dim(` ${l}\n`))
163
- .join('') + colors.dim(` › ${options.base ? `git diff ${plain(options.base)}...HEAD` : 'git diff HEAD'}\n`)
164
- : '';
165
- const scanText = () => scan.error
166
- ? colors.dim(` weakened-test scan could not run: ${scan.error}\n`)
167
- : scan.skipped
168
- ? colors.dim(` weakened-test scan skipped: ${scan.skipped}\n`)
169
- : '';
170
- const missingLines = () => missingClaimed.length
171
- ? missingClaimed.map((f) => colors.dim(` ${plain(f)} · named by the AI, not in the repo\n`)).join('')
172
- : '';
173
- if (options.json) {
174
- const receipt = RECEIPT_MARKER +
175
- '\n' +
176
- buildProofBlock(cwd, r, weakened, missingClaimed, { base: options.base, claim: options.claim }, scan);
177
- process.stdout.write(JSON.stringify({ cwd, ...r, weakened, missingClaimed, weakeningScan, verdict, receipt }, null, 2) + '\n');
178
- return exitCode;
179
- }
180
- if (options.proof) {
181
- process.stdout.write(buildProofBlock(cwd, r, weakened, missingClaimed, { base: options.base, claim: options.claim }, scan));
182
- return exitCode;
183
- }
184
- if (!r.ran) {
185
- // The runner/deps aren't installed (e.g. `spawn pytest ENOENT`) — that's a
186
- // fixable environment gap, not "no tests". Name the one command that fixes it.
187
- if (r.kind === 'command-not-found') {
188
- process.stdout.write('\n' +
189
- colors.error(`${symbols.error} Couldn't run the tests`) +
190
- colors.dim(` — the runner for '${r.command || 'your tests'}' isn't installed here.\n`) +
191
- (r.remedy
192
- ? colors.dim(' Fix it, then re-check: ') + colors.success(`${r.remedy}\n\n`)
193
- : colors.dim(' Install the test runner, then run check again.\n\n')));
194
- process.stdout.write(weakenedText() + missingLines() + scanText());
195
- return exitCode; // couldn't verify (2); a weakened test or a missing named test file still counts (1)
196
- }
197
- if (r.kind === 'timed-out' || r.kind === 'disabled') {
198
- process.stdout.write(colors.error(`${symbols.error} Nothing verified`) +
199
- colors.dim(` — ${r.kind === 'timed-out' ? `'${r.command}' ${r.skippedReason?.replace(/^test re-run /, '')}` : r.skippedReason}.\n`));
200
- process.stdout.write(weakenedText() + missingLines() + scanText());
201
- return exitCode;
202
- }
203
- process.stdout.write(colors.dim(`No test suite codehere can run here — ${r.skippedReason || 'none detected'}.\n`) +
204
- colors.dim('codehere proves the AI by re-running your tests; add one (npm test / node --test / pytest / go test / cargo test) and run check again.\n'));
205
- process.stdout.write(weakenedText() + missingLines() + scanText());
206
- return exitCode; // nothing was verified (2); a weakened test or a missing named test file still counts (1)
207
- }
208
- // Honesty: runner-missing (exit 126/127) means the test command couldn't be RUN
209
- // (deps not installed), not that the tests failed. Never report "FAIL" for that —
210
- // name the one command that makes it runnable.
211
- if (r.kind === 'runner-missing') {
212
- process.stdout.write('\n' +
213
- colors.error(`${symbols.error} Couldn't run the tests`) +
214
- colors.dim(` — '${r.command}' exited ${r.exitCode}: the deps/runner aren't installed here.\n`) +
215
- (r.remedy
216
- ? colors.dim(' Fix it, then re-check: ') + colors.success(`${r.remedy}\n\n`)
217
- : colors.dim(' Install deps, then run check again.\n\n')));
218
- process.stdout.write(weakenedText() + missingLines() + scanText());
219
- return exitCode; // non-zero for CI (unverified), but NOT a false "tests failed"
220
- }
221
- const passed = (r.exitCode ?? 1) === 0 && !r.noTests;
222
- const counts = typeof r.passed === 'number' || typeof r.failed === 'number'
223
- ? ` ${r.passed ?? '?'} passed · ${r.failed ?? 0} failed`
224
- : '';
225
- const headline = r.noTests
226
- ? colors.error(`${symbols.error} No tests ran`)
227
- : !passed
228
- ? colors.error(`${symbols.error} Tests FAIL`)
229
- : weakened.length
230
- ? colors.error(`${symbols.error} Tests pass · ${weakened.length} weakened in this change`)
231
- : missingClaimed.length
232
- ? colors.error(`${symbols.error} Tests pass · ${missingClaimed.length} claimed test file not found`)
233
- : scan.error
234
- ? colors.error(`${symbols.error} Tests pass · weakened-test scan could not run`)
235
- : colors.success(`${symbols.success} Tests pass`);
236
- process.stdout.write('\n' +
237
- headline +
238
- colors.dim(` — codehere re-ran them itself (${r.command}, exit ${r.exitCode})${counts}\n`) +
239
- colors.dim("This is observed reality, not the agent's claim.\n") +
240
- (r.noTests
241
- ? colors.dim(r.allSkipped
242
- ? ' The runner says every test was skipped, so nothing was tested.\n'
243
- : ' The runner reported 0 tests, so nothing was tested. Check the test file names.\n')
244
- : '') +
245
- weakenedText() +
246
- missingLines() +
247
- scanText() +
248
- '\n');
249
- return exitCode;
250
- }
251
- //# sourceMappingURL=check-command.js.map
@@ -1,77 +0,0 @@
1
- /**
2
- * Help Command
3
- * Contextual help for the commands codehere ships: check and help.
4
- */
5
- import { colors, newline } from '../../../ui.js';
6
- const COMMAND_INFO = {
7
- check: {
8
- name: 'check',
9
- description: 'Re-run your tests and report what actually passed',
10
- usage: 'codehere check [target] [options]',
11
- examples: [
12
- 'codehere check',
13
- 'codehere check --base origin/main',
14
- 'codehere check --claim "added tests, all pass"',
15
- 'codehere check --proof',
16
- ],
17
- options: [
18
- { flag: '--json', description: 'Machine-readable output' },
19
- { flag: '--proof', description: 'Paste-ready proof block for a pull request' },
20
- { flag: '--claim <text>', description: "The AI's summary: test files it names that do not exist are flagged" },
21
- { flag: '--base <ref>', description: 'Look for weakened tests in everything since this branch left <ref>' },
22
- ],
23
- },
24
- help: {
25
- name: 'help',
26
- description: 'Show help for commands',
27
- usage: 'codehere help [command]',
28
- examples: ['codehere help', 'codehere help check'],
29
- },
30
- };
31
- function displayCommandHelp(commandName) {
32
- const command = COMMAND_INFO[commandName.toLowerCase()];
33
- if (!command) {
34
- newline();
35
- console.error(colors.red(`Command '${commandName}' not found.`));
36
- console.log(colors.dim('Run') + ' ' + colors.cyan('codehere --help') + ' ' + colors.dim('to see all commands.'));
37
- newline();
38
- return;
39
- }
40
- newline();
41
- console.log(colors.bold(command.name) + colors.dim(' · ' + command.description));
42
- newline();
43
- console.log(colors.bold('Usage'));
44
- console.log(' ' + colors.cyan(command.usage));
45
- newline();
46
- console.log(colors.bold('Examples'));
47
- command.examples.forEach((example) => {
48
- console.log(colors.dim(' $ ') + colors.cyan(example));
49
- });
50
- newline();
51
- if (command.options && command.options.length > 0) {
52
- console.log(colors.bold('Options'));
53
- command.options.forEach((opt) => {
54
- console.log(' ' + colors.cyan(opt.flag.padEnd(24)) + colors.dim(opt.description));
55
- });
56
- newline();
57
- }
58
- }
59
- function displayGeneralHelp() {
60
- newline();
61
- console.log(colors.bold('codehere') + colors.dim(' · re-run the tests an AI says passed'));
62
- newline();
63
- console.log(colors.bold('Check'));
64
- console.log(' ' + colors.cyan('check'.padEnd(14)) + colors.dim('Re-run your tests and report what actually passed'));
65
- newline();
66
- console.log(colors.dim('Run ') + colors.cyan('codehere help check') + colors.dim(' for its options.'));
67
- newline();
68
- }
69
- export async function executeHelpCommand(commandName) {
70
- if (commandName) {
71
- displayCommandHelp(commandName);
72
- }
73
- else {
74
- displayGeneralHelp();
75
- }
76
- }
77
- //# sourceMappingURL=help-command.js.map
package/dist/ui.js DELETED
@@ -1,53 +0,0 @@
1
- import chalk from 'chalk';
2
- // Color roles (DESIGN.md §1): color carries meaning.
3
- // verified = green (independently verified ONLY) caught = red (a contradiction caught ONLY)
4
- // warn = amber (attention) evidence = dim (machine facts, metadata)
5
- // identity = cyan (an id, sparingly) heading = bold (the one thing that matters)
6
- export const colors = {
7
- success: chalk.green,
8
- error: chalk.red,
9
- warning: chalk.yellow,
10
- info: chalk.blue,
11
- dim: chalk.dim,
12
- bold: chalk.bold,
13
- cyan: chalk.cyan,
14
- magenta: chalk.magenta,
15
- yellow: chalk.yellow,
16
- red: chalk.red,
17
- green: chalk.green,
18
- gray: chalk.gray,
19
- blue: chalk.blue,
20
- verified: chalk.green,
21
- caught: chalk.red,
22
- warn: chalk.yellow,
23
- evidence: chalk.dim,
24
- identity: chalk.cyan,
25
- heading: chalk.bold,
26
- };
27
- // Glyph language (DESIGN.md): the sanctioned geometric set plus ANSI color,
28
- // never emoji-as-icon.
29
- export const symbols = {
30
- success: '✓',
31
- error: '✕',
32
- warning: '!',
33
- info: '›',
34
- processing: '›',
35
- arrow: '›',
36
- dot: '·',
37
- check: '✓',
38
- cross: '✕',
39
- pipe: '│',
40
- dash: '─',
41
- plus: '+',
42
- minus: '-',
43
- bullet: '•',
44
- };
45
- export function error(message) {
46
- return colors.red(symbols.error + ' ' + message);
47
- }
48
- export function newline(count = 1) {
49
- for (let i = 0; i < count; i++) {
50
- console.log();
51
- }
52
- }
53
- //# sourceMappingURL=ui.js.map