sneakoscope 0.9.11 → 0.9.13

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 (62) hide show
  1. package/README.md +28 -2
  2. package/crates/sks-core/Cargo.lock +7 -0
  3. package/crates/sks-core/Cargo.toml +10 -0
  4. package/crates/sks-core/src/main.rs +202 -0
  5. package/package.json +15 -3
  6. package/src/cli/args.mjs +49 -0
  7. package/src/cli/command-registry.mjs +128 -0
  8. package/src/cli/feature-commands.mjs +112 -6
  9. package/src/cli/install-helpers.mjs +14 -7
  10. package/src/cli/legacy-main.mjs +4147 -0
  11. package/src/cli/main.mjs +7 -4138
  12. package/src/cli/output.mjs +9 -0
  13. package/src/cli/router.mjs +30 -0
  14. package/src/commands/all-features.mjs +6 -0
  15. package/src/commands/codex-app.mjs +30 -0
  16. package/src/commands/codex-lb.mjs +47 -0
  17. package/src/commands/db.mjs +6 -0
  18. package/src/commands/doctor.mjs +46 -0
  19. package/src/commands/features.mjs +6 -0
  20. package/src/commands/help.mjs +77 -0
  21. package/src/commands/hooks.mjs +6 -0
  22. package/src/commands/perf.mjs +91 -0
  23. package/src/commands/proof.mjs +103 -0
  24. package/src/commands/root.mjs +24 -0
  25. package/src/commands/version.mjs +5 -0
  26. package/src/commands/wiki.mjs +95 -0
  27. package/src/core/codex-lb-circuit.mjs +130 -0
  28. package/src/core/db-safety.mjs +18 -3
  29. package/src/core/feature-fixtures.mjs +103 -0
  30. package/src/core/feature-registry.mjs +117 -11
  31. package/src/core/fsx.mjs +1 -1
  32. package/src/core/hooks-runtime.mjs +17 -6
  33. package/src/core/language-preference.mjs +106 -0
  34. package/src/core/pipeline.mjs +24 -0
  35. package/src/core/proof/claim-ledger.mjs +9 -0
  36. package/src/core/proof/command-ledger.mjs +17 -0
  37. package/src/core/proof/evidence-collector.mjs +33 -0
  38. package/src/core/proof/file-change-ledger.mjs +6 -0
  39. package/src/core/proof/proof-reader.mjs +30 -0
  40. package/src/core/proof/proof-redaction.test-helper.mjs +9 -0
  41. package/src/core/proof/proof-schema.mjs +42 -0
  42. package/src/core/proof/proof-writer.mjs +81 -0
  43. package/src/core/proof/route-adapter.mjs +74 -0
  44. package/src/core/proof/route-proof-gate.mjs +33 -0
  45. package/src/core/proof/route-proof-policy.mjs +96 -0
  46. package/src/core/proof/selftest-proof-fixtures.mjs +54 -0
  47. package/src/core/proof/validation.mjs +20 -0
  48. package/src/core/routes.mjs +4 -3
  49. package/src/core/rust-accelerator.mjs +55 -3
  50. package/src/core/secret-redaction.mjs +69 -0
  51. package/src/core/version-manager.mjs +11 -7
  52. package/src/core/version.mjs +1 -0
  53. package/src/core/wiki-image/bbox.mjs +10 -0
  54. package/src/core/wiki-image/callout-parser.mjs +16 -0
  55. package/src/core/wiki-image/computer-use-ledger.mjs +38 -0
  56. package/src/core/wiki-image/image-hash.mjs +42 -0
  57. package/src/core/wiki-image/image-relation.mjs +2 -0
  58. package/src/core/wiki-image/image-voxel-ledger.mjs +147 -0
  59. package/src/core/wiki-image/image-voxel-schema.mjs +16 -0
  60. package/src/core/wiki-image/proof-linker.mjs +12 -0
  61. package/src/core/wiki-image/validation.mjs +53 -0
  62. package/src/core/wiki-image/visual-anchor.mjs +42 -0
@@ -0,0 +1,9 @@
1
+ import { PACKAGE_VERSION } from '../core/version.mjs';
2
+
3
+ export function sksTextLogo() {
4
+ return `SKS\nSNEAKOSCOPE CODEX v${PACKAGE_VERSION}`;
5
+ }
6
+
7
+ export function printJson(value) {
8
+ console.log(JSON.stringify(value, null, 2));
9
+ }
@@ -0,0 +1,30 @@
1
+ import { COMMAND_ALIASES, COMMANDS } from './command-registry.mjs';
2
+
3
+ function normalizeCommand(args = []) {
4
+ const cmd = args[0];
5
+ if (!cmd) return { command: null, args };
6
+ const mapped = COMMAND_ALIASES[cmd] || cmd;
7
+ return {
8
+ command: mapped,
9
+ args: mapped === cmd ? args.slice(1) : args.slice(1)
10
+ };
11
+ }
12
+
13
+ export async function dispatch(args = []) {
14
+ const { command, args: rest } = normalizeCommand(args);
15
+ if (!command) {
16
+ const legacy = await import('./legacy-main.mjs');
17
+ return legacy.main(args);
18
+ }
19
+ const entry = COMMANDS[command];
20
+ if (!entry) {
21
+ console.error(`Unknown command: ${command}`);
22
+ process.exitCode = 1;
23
+ return;
24
+ }
25
+ const mod = await entry.lazy();
26
+ const runner = mod.run || mod.main || mod.default;
27
+ if (typeof runner !== 'function') throw new Error(`Command ${command} has no run/main export`);
28
+ if (mod.IS_LEGACY_CLI) return runner([command, ...rest]);
29
+ return runner(command, rest);
30
+ }
@@ -0,0 +1,6 @@
1
+ import { allFeaturesCommand } from '../cli/feature-commands.mjs';
2
+
3
+ export async function run(_command, args = []) {
4
+ const [sub, ...rest] = args;
5
+ return allFeaturesCommand(sub, rest);
6
+ }
@@ -0,0 +1,30 @@
1
+ import { flag } from '../cli/args.mjs';
2
+ import { printJson } from '../cli/output.mjs';
3
+ import { codexAccessTokenStatus, codexAppIntegrationStatus, formatCodexAppStatus } from '../core/codex-app.mjs';
4
+ import { codexAppRemoteControlCommand } from '../cli/codex-app-command.mjs';
5
+
6
+ export async function run(_command, args = []) {
7
+ const action = args[0] || 'check';
8
+ if (action === 'remote-control' || action === 'remote') return codexAppRemoteControlCommand(args.slice(1));
9
+ if (action === 'pat') {
10
+ const status = codexAccessTokenStatus();
11
+ if (flag(args, '--json')) return printJson(status);
12
+ console.log('Codex App PAT status');
13
+ console.log(`Status: ${status.status}`);
14
+ for (const entry of status.access_token_env_vars) console.log(`${entry.name}: ${entry.present ? entry.value : 'missing'}`);
15
+ return;
16
+ }
17
+ if (action === 'check' || action === 'status') {
18
+ const status = await codexAppIntegrationStatus();
19
+ if (flag(args, '--json')) {
20
+ printJson(status);
21
+ if (!status.ok) process.exitCode = 1;
22
+ return;
23
+ }
24
+ console.log(formatCodexAppStatus(status, { includeRaw: flag(args, '--verbose') }));
25
+ if (!status.ok) process.exitCode = 1;
26
+ return;
27
+ }
28
+ console.error('Usage: sks codex-app check|status|pat status|remote-control [--json]');
29
+ process.exitCode = 1;
30
+ }
@@ -0,0 +1,47 @@
1
+ import path from 'node:path';
2
+ import { projectRoot } from '../core/fsx.mjs';
3
+ import { flag, readOption } from '../cli/args.mjs';
4
+ import { printJson } from '../cli/output.mjs';
5
+ import { codexLbMetrics, readCodexLbCircuit, recordCodexLbHealthEvent, resetCodexLbCircuit } from '../core/codex-lb-circuit.mjs';
6
+
7
+ export async function run(command, args = []) {
8
+ const root = await projectRoot();
9
+ const action = args[0] || 'status';
10
+ if (action === 'metrics') {
11
+ const result = codexLbMetrics(await readCodexLbCircuit(root));
12
+ if (flag(args, '--json')) return printJson(result);
13
+ console.log(`codex-lb circuit: ${result.circuit.state}`);
14
+ if (!result.ok) process.exitCode = 1;
15
+ return;
16
+ }
17
+ if (action === 'doctor' && flag(args, '--deep')) {
18
+ const result = { schema: 'sks.codex-lb-doctor.v1', deep: true, ...codexLbMetrics(await readCodexLbCircuit(root)) };
19
+ if (flag(args, '--json')) return printJson(result);
20
+ console.log(`codex-lb deep doctor: ${result.ok ? 'ok' : 'blocked'} (${result.circuit.state})`);
21
+ if (!result.ok) process.exitCode = 1;
22
+ return;
23
+ }
24
+ if (action === 'circuit' && args[1] === 'reset') {
25
+ const result = await resetCodexLbCircuit(root);
26
+ if (flag(args, '--json')) return printJson({ ok: true, circuit: result });
27
+ console.log('codex-lb circuit reset');
28
+ return;
29
+ }
30
+ if (action === 'circuit' && args[1] === 'record-fixture') {
31
+ const fixturePath = args[2] || readOption(args, '--fixture', null);
32
+ if (!fixturePath) {
33
+ console.error('Usage: sks codex-lb circuit record-fixture <fixture.json> [--json]');
34
+ process.exitCode = 1;
35
+ return;
36
+ }
37
+ const { readJson } = await import('../core/fsx.mjs');
38
+ const event = await readJson(path.isAbsolute(fixturePath) ? fixturePath : path.resolve(root, fixturePath), {});
39
+ const circuit = await recordCodexLbHealthEvent(root, event);
40
+ const result = { schema: 'sks.codex-lb-circuit-record-fixture.v1', ok: true, fixture: fixturePath, circuit };
41
+ if (flag(args, '--json')) return printJson(result);
42
+ console.log(`codex-lb circuit: ${circuit.state}`);
43
+ return;
44
+ }
45
+ const legacy = await import('../cli/legacy-main.mjs');
46
+ return legacy.main([command, ...args]);
47
+ }
@@ -0,0 +1,6 @@
1
+ import { dbCommand } from '../cli/maintenance-commands.mjs';
2
+
3
+ export async function run(_command, args = []) {
4
+ const [sub = 'policy', ...rest] = args;
5
+ return dbCommand(sub, rest);
6
+ }
@@ -0,0 +1,46 @@
1
+ import { projectRoot, dirSize, exists, formatBytes } from '../core/fsx.mjs';
2
+ import { flag } from '../cli/args.mjs';
3
+ import { printJson } from '../cli/output.mjs';
4
+ import { getCodexInfo } from '../core/codex-adapter.mjs';
5
+ import { rustInfo } from '../core/rust-accelerator.mjs';
6
+ import { codexAppIntegrationStatus } from '../core/codex-app.mjs';
7
+ import { codexLbMetrics, readCodexLbCircuit } from '../core/codex-lb-circuit.mjs';
8
+
9
+ export async function run(_command, args = []) {
10
+ if (flag(args, '--fix')) {
11
+ const legacy = await import('../cli/legacy-main.mjs');
12
+ return legacy.main(['doctor', ...args]);
13
+ }
14
+ const root = await projectRoot();
15
+ const codex = await getCodexInfo().catch((err) => ({ available: false, error: err.message }));
16
+ const rust = await rustInfo().catch((err) => ({ available: false, error: err.message }));
17
+ const codexApp = await codexAppIntegrationStatus({ codex }).catch((err) => ({ ok: false, error: err.message }));
18
+ const codexLb = codexLbMetrics(await readCodexLbCircuit(root).catch(() => ({})));
19
+ const pkgBytes = await dirSize(root).catch(() => 0);
20
+ const result = {
21
+ schema: 'sks.doctor-status.v1',
22
+ ok: Boolean(codex.bin) && codexApp.ok && codexLb.ok,
23
+ root,
24
+ node: { ok: Number(process.versions.node.split('.')[0]) >= 20, version: process.version },
25
+ codex,
26
+ rust,
27
+ codex_app: codexApp,
28
+ codex_lb: codexLb,
29
+ sneakoscope: { ok: await exists(`${root}/.sneakoscope`) },
30
+ package: { bytes: pkgBytes, human: formatBytes(pkgBytes) }
31
+ };
32
+ if (flag(args, '--json')) {
33
+ printJson(result);
34
+ if (!result.ok) process.exitCode = 1;
35
+ return;
36
+ }
37
+ console.log('SKS Doctor');
38
+ console.log(`Root: ${root}`);
39
+ console.log(`Node: ${result.node.ok ? 'ok' : 'fail'} ${result.node.version}`);
40
+ console.log(`Codex: ${codex.bin ? 'ok' : 'missing'} ${codex.version || ''}`);
41
+ console.log(`Rust acc.: ${rust.available ? rust.version : 'optional-missing'}`);
42
+ console.log(`Codex App: ${codexApp.ok ? 'ok' : 'needs setup'}`);
43
+ console.log(`codex-lb: ${codexLb.ok ? 'ok' : `blocked ${codexLb.circuit?.state || 'unknown'}`}`);
44
+ console.log(`Ready: ${result.ok ? 'yes' : 'no'}`);
45
+ if (!result.ok) process.exitCode = 1;
46
+ }
@@ -0,0 +1,6 @@
1
+ import { featuresCommand } from '../cli/feature-commands.mjs';
2
+
3
+ export async function run(_command, args = []) {
4
+ const [sub, ...rest] = args;
5
+ return featuresCommand(sub, rest);
6
+ }
@@ -0,0 +1,77 @@
1
+ import { COMMANDS } from '../cli/command-registry.mjs';
2
+ import { flag } from '../cli/args.mjs';
3
+ import { printJson, sksTextLogo } from '../cli/output.mjs';
4
+
5
+ const FALLBACK_CATALOG = [
6
+ { name: 'help', usage: 'sks help [stable|beta|labs|all]', description: 'Show concise SKS help.' },
7
+ { name: 'version', usage: 'sks version | sks --version', description: 'Print the installed version.' },
8
+ { name: 'commands', usage: 'sks commands [--json]', description: 'List the command registry.' },
9
+ { name: 'root', usage: 'sks root [--json]', description: 'Show the active SKS root.' },
10
+ { name: 'doctor', usage: 'sks doctor [--json]', description: 'Check local SKS readiness.' },
11
+ { name: 'features', usage: 'sks features check --json', description: 'Validate feature coverage and fixtures.' },
12
+ { name: 'all-features', usage: 'sks all-features selftest --mock --json', description: 'Run mock feature fixture checks.' },
13
+ { name: 'proof', usage: 'sks proof show|validate|latest|export [--json|--md]', description: 'Inspect completion proof.' },
14
+ { name: 'wiki', usage: 'sks wiki image-ingest|image-validate|image-summary ...', description: 'Manage TriWiki and image voxel ledgers.' },
15
+ { name: 'hooks', usage: 'sks hooks explain|status|trust-report|replay ...', description: 'Inspect Codex hook policy and trust evidence.' },
16
+ { name: 'codex-lb', usage: 'sks codex-lb status|metrics|doctor|circuit ...', description: 'Inspect codex-lb readiness and circuit state.' },
17
+ { name: 'perf', usage: 'sks perf cold-start --json', description: 'Measure CLI cold-start budgets.' },
18
+ { name: 'team', usage: 'sks team "task"', description: 'Create and observe Team missions.' },
19
+ { name: 'qa-loop', usage: 'sks qa-loop prepare|run|status ...', description: 'Run QA loop missions.' },
20
+ { name: 'research', usage: 'sks research prepare|run|status ...', description: 'Run research missions.' },
21
+ { name: 'ppt', usage: 'sks ppt build|status ...', description: 'Build or inspect PPT route artifacts.' },
22
+ { name: 'image-ux-review', usage: 'sks image-ux-review status ...', description: 'Inspect image UX review artifacts.' },
23
+ { name: 'db', usage: 'sks db policy|scan|check ...', description: 'Inspect database safety policy.' },
24
+ { name: 'gx', usage: 'sks gx init|render|validate|drift|snapshot ...', description: 'Create and verify visual context cartridges.' },
25
+ { name: 'goal', usage: 'sks goal create|status|pause|resume|clear ...', description: 'Manage the Goal bridge.' }
26
+ ];
27
+
28
+ export async function run(command, args = []) {
29
+ if (command === 'commands') return commands(args);
30
+ const topic = args[0];
31
+ if (topic === 'all') return printHelp('all');
32
+ if (['stable', 'beta', 'labs'].includes(topic)) return printHelp(topic);
33
+ if (topic) return printTopic(topic);
34
+ return printHelp('default');
35
+ }
36
+
37
+ function commands(args = []) {
38
+ const commands = commandRows('all');
39
+ if (flag(args, '--json')) {
40
+ return printJson({
41
+ schema: 'sks.command-registry.v1',
42
+ aliases: ['sks', 'sneakoscope'],
43
+ commands
44
+ });
45
+ }
46
+ console.log(`${sksTextLogo()}\n\nCommands\n`);
47
+ const width = Math.max(...commands.map((entry) => entry.usage.length));
48
+ for (const entry of commands) console.log(`${entry.usage.padEnd(width)} ${entry.description}`);
49
+ }
50
+
51
+ function printHelp(filter) {
52
+ const rows = commandRows(filter === 'default' ? 'stable-beta' : filter);
53
+ console.log(`${sksTextLogo()}\n\nUsage\n`);
54
+ console.log(' sks help [stable|beta|labs|all]');
55
+ console.log(' sks commands [--json]');
56
+ console.log(' sks root [--json]');
57
+ console.log(' sks proof show --json');
58
+ console.log('');
59
+ for (const row of rows) console.log(` ${row.usage.padEnd(54)} ${row.description}`);
60
+ console.log('\nCore promises: image-based Voxel TriWiki, Codex App/codex-lb readiness, and completion proof for serious routes.');
61
+ }
62
+
63
+ function printTopic(topic) {
64
+ const row = commandRows('all').find((entry) => entry.name === topic);
65
+ if (!row) return printHelp('default');
66
+ console.log(`${sksTextLogo()}\n\n${row.name}\n`);
67
+ console.log(`Usage: ${row.usage}`);
68
+ console.log(row.description);
69
+ }
70
+
71
+ function commandRows(filter) {
72
+ const maturityByName = new Map(Object.entries(COMMANDS).map(([name, meta]) => [name, meta.maturity || 'labs']));
73
+ const rows = FALLBACK_CATALOG.map((entry) => ({ ...entry, maturity: maturityByName.get(entry.name) || 'labs' }));
74
+ if (filter === 'all') return rows;
75
+ if (filter === 'stable-beta') return rows.filter((row) => row.maturity === 'stable' || row.maturity === 'beta');
76
+ return rows.filter((row) => row.maturity === filter);
77
+ }
@@ -0,0 +1,6 @@
1
+ import { hooksCommand } from '../cli/feature-commands.mjs';
2
+
3
+ export async function run(_command, args = []) {
4
+ const [sub, ...rest] = args;
5
+ return hooksCommand(sub, rest);
6
+ }
@@ -0,0 +1,91 @@
1
+ import { performance } from 'node:perf_hooks';
2
+ import { spawnSync } from 'node:child_process';
3
+ import { PACKAGE_VERSION, projectRoot } from '../core/fsx.mjs';
4
+ import { flag } from '../cli/args.mjs';
5
+ import { printJson } from '../cli/output.mjs';
6
+
7
+ export const DEFAULT_COLD_START_ITERATIONS = 20;
8
+
9
+ const COLD_START_COMMANDS = Object.freeze([
10
+ { cmd: 'sks --version', args: ['--version'], budget_p95_ms: 80 },
11
+ { cmd: 'sks help', args: ['help'], budget_p95_ms: 150 },
12
+ { cmd: 'sks root --json', args: ['root', '--json'], budget_p95_ms: 150 },
13
+ { cmd: 'sks features check --json', args: ['features', 'check', '--json'], budget_p95_ms: 1500 }
14
+ ]);
15
+
16
+ export async function run(_command, args = []) {
17
+ const action = args[0] || 'run';
18
+ if (action === 'cold-start') {
19
+ const root = await projectRoot();
20
+ const iterations = resolveColdStartIterations(readArg(args, '--iterations', process.env.SKS_COLD_START_ITERATIONS));
21
+ const result = runColdStart({ root, iterations });
22
+ if (flag(args, '--json')) return printJson(result);
23
+ console.log(`Cold-start: ${result.ok ? 'pass' : 'fail'}`);
24
+ for (const row of result.commands) console.log(`- ${row.cmd}: p95=${row.p95_ms}ms budget=${row.budget_p95_ms}ms ${row.ok ? 'ok' : 'blocked'}`);
25
+ if (!result.ok) process.exitCode = 1;
26
+ return;
27
+ }
28
+ const legacy = await import('../cli/legacy-main.mjs');
29
+ return legacy.main(['perf', ...args]);
30
+ }
31
+
32
+ export function runColdStart({ root = process.cwd(), iterations = DEFAULT_COLD_START_ITERATIONS } = {}) {
33
+ const script = new URL('../../bin/sks.mjs', import.meta.url).pathname;
34
+ const measuredIterations = resolveColdStartIterations(iterations);
35
+ const commands = COLD_START_COMMANDS.map((spec) => measureCommand(root, script, spec, measuredIterations));
36
+ return {
37
+ schema: 'sks.perf.cold-start.v1',
38
+ version: PACKAGE_VERSION,
39
+ node: process.version,
40
+ platform: `${process.platform}-${process.arch}`,
41
+ commands,
42
+ ok: commands.every((row) => row.ok)
43
+ };
44
+ }
45
+
46
+ export function resolveColdStartIterations(value = DEFAULT_COLD_START_ITERATIONS) {
47
+ const parsed = Number(value ?? DEFAULT_COLD_START_ITERATIONS);
48
+ if (!Number.isFinite(parsed) || parsed <= 0) return DEFAULT_COLD_START_ITERATIONS;
49
+ return Math.max(1, Math.floor(parsed));
50
+ }
51
+
52
+ function measureCommand(root, script, spec, iterations) {
53
+ const values = [];
54
+ const failures = [];
55
+ for (let i = 0; i < iterations; i += 1) {
56
+ const t0 = performance.now();
57
+ const res = spawnSync(process.execPath, [script, ...spec.args], {
58
+ cwd: root,
59
+ env: { ...process.env, SKS_SKIP_UPDATE_CHECK: '1', CI: 'true' },
60
+ encoding: 'utf8',
61
+ maxBuffer: 1024 * 1024,
62
+ shell: false
63
+ });
64
+ values.push(performance.now() - t0);
65
+ if (res.status !== 0) failures.push({ status: res.status, stderr: String(res.stderr || '').slice(0, 400) });
66
+ }
67
+ values.sort((a, b) => a - b);
68
+ const p50 = percentile(values, 50);
69
+ const p95 = percentile(values, 95);
70
+ const p95Rounded = Math.round(p95);
71
+ return {
72
+ cmd: spec.cmd,
73
+ iterations,
74
+ p50_ms: Math.round(p50),
75
+ p95_ms: p95Rounded,
76
+ budget_p95_ms: spec.budget_p95_ms,
77
+ ok: failures.length === 0 && p95Rounded <= spec.budget_p95_ms,
78
+ failures
79
+ };
80
+ }
81
+
82
+ function percentile(values, p) {
83
+ if (!values.length) return 0;
84
+ const idx = Math.min(values.length - 1, Math.ceil((p / 100) * values.length) - 1);
85
+ return values[idx];
86
+ }
87
+
88
+ function readArg(args, name, fallback) {
89
+ const i = args.indexOf(name);
90
+ return i >= 0 && args[i + 1] ? args[i + 1] : fallback;
91
+ }
@@ -0,0 +1,103 @@
1
+ import { projectRoot } from '../core/fsx.mjs';
2
+ import { flag } from '../cli/args.mjs';
3
+ import { printJson } from '../cli/output.mjs';
4
+ import { collectProofEvidence } from '../core/proof/evidence-collector.mjs';
5
+ import { findLatestMission } from '../core/mission.mjs';
6
+ import { readLatestProof, readLatestProofMarkdown, readRouteProof } from '../core/proof/proof-reader.mjs';
7
+ import { writeRouteCompletionProof } from '../core/proof/route-adapter.mjs';
8
+ import { renderProofMarkdown, writeCompletionProof } from '../core/proof/proof-writer.mjs';
9
+ import { validateCompletionProof } from '../core/proof/validation.mjs';
10
+
11
+ export async function run(_command, args = []) {
12
+ const root = await projectRoot();
13
+ const action = args[0] || 'show';
14
+ const rest = args.slice(1);
15
+ if (action === 'show' || action === 'latest') {
16
+ const proof = await withFreshSummaries(root, await readLatestProof(root));
17
+ if (flag(args, '--json') || action === 'latest') return printJson(proof);
18
+ process.stdout.write(renderProofMarkdown(proof));
19
+ return;
20
+ }
21
+ if (action === 'validate') {
22
+ const proof = await withFreshSummaries(root, await readLatestProof(root));
23
+ const validation = validateCompletionProof(proof);
24
+ const result = { schema: 'sks.completion-proof-validation.v1', ok: validation.ok, status: validation.status, issues: validation.issues, proof_status: proof.status };
25
+ if (flag(args, '--json')) return printJson(result);
26
+ console.log(`Completion proof validation: ${result.ok ? 'pass' : 'fail'} (${result.proof_status})`);
27
+ for (const issue of result.issues) console.log(`- ${issue}`);
28
+ if (!result.ok) process.exitCode = 1;
29
+ return;
30
+ }
31
+ if (action === 'route') {
32
+ const missionArg = rest.find((arg) => !String(arg).startsWith('--')) || 'latest';
33
+ const missionId = missionArg === 'latest' ? await findLatestMission(root) : missionArg;
34
+ const proof = await readRouteProof(root, missionId);
35
+ const result = proof
36
+ ? { schema: 'sks.completion-proof-route.v1', ok: true, mission_id: missionId, proof }
37
+ : { schema: 'sks.completion-proof-route.v1', ok: false, mission_id: missionId, status: 'blocked', issues: ['completion_proof_missing'] };
38
+ if (flag(args, '--json')) return printJson(result);
39
+ if (proof) process.stdout.write(renderProofMarkdown(proof));
40
+ else console.log(`Completion proof missing for mission ${missionId || 'latest'}`);
41
+ if (!result.ok) process.exitCode = 1;
42
+ return;
43
+ }
44
+ if (action === 'export' && (flag(rest, '--md') || flag(args, '--md'))) {
45
+ process.stdout.write(await readLatestProofMarkdown(root));
46
+ return;
47
+ }
48
+ if (action === 'repair' && rest[0] === 'latest') {
49
+ const missionId = await findLatestMission(root);
50
+ const evidence = await collectProofEvidence(root);
51
+ const result = await writeRouteCompletionProof(root, {
52
+ missionId,
53
+ route: '$SKS',
54
+ status: 'verified_partial',
55
+ evidence,
56
+ summary: {
57
+ files_changed: evidence.files?.length || 0,
58
+ commands_run: 1,
59
+ manual_review_required: true
60
+ },
61
+ claims: [{ id: 'proof-repair-latest', status: 'supported', evidence: '.sneakoscope/proof/latest.json' }],
62
+ unverified: ['Repair proof records current local evidence and remains verified_partial until a route-specific gate passes.']
63
+ });
64
+ if (flag(args, '--json')) return printJson({ schema: 'sks.completion-proof-repair.v1', ok: result.ok, mission_id: missionId, files: result.files, validation: result.validation });
65
+ console.log(`Completion proof repaired: ${result.files.latest_json}`);
66
+ if (!result.ok) process.exitCode = 1;
67
+ return;
68
+ }
69
+ if (action === 'smoke') {
70
+ const evidence = await collectProofEvidence(root);
71
+ const result = await writeCompletionProof(root, {
72
+ route: '$SKS',
73
+ status: 'verified_partial',
74
+ summary: {
75
+ files_changed: evidence.files?.length || 0,
76
+ commands_run: 1,
77
+ tests_passed: 1,
78
+ tests_failed: 0,
79
+ manual_review_required: true
80
+ },
81
+ evidence,
82
+ claims: [{ id: 'proof-smoke', status: 'supported', evidence: '.sneakoscope/proof/latest.json' }],
83
+ unverified: ['Smoke proof is fixture evidence, not a real route completion.']
84
+ }, { command: { cmd: 'sks proof smoke', status: 'verified_partial' } });
85
+ if (flag(args, '--json')) return printJson(result);
86
+ console.log(`Completion proof written: ${result.files.latest_json}`);
87
+ return;
88
+ }
89
+ console.error('Usage: sks proof show|latest|validate|route <mission-id|latest>|export --md|repair latest|smoke [--json]');
90
+ process.exitCode = 1;
91
+ }
92
+
93
+ async function withFreshSummaries(root, proof) {
94
+ const evidence = await collectProofEvidence(root);
95
+ return {
96
+ ...proof,
97
+ evidence: {
98
+ ...proof.evidence,
99
+ image_voxels: proof.evidence?.image_voxels || evidence.image_voxels,
100
+ triwiki: proof.evidence?.triwiki || evidence.triwiki
101
+ }
102
+ };
103
+ }
@@ -0,0 +1,24 @@
1
+ import { findProjectRoot, globalSksRoot, sksRoot } from '../core/fsx.mjs';
2
+ import { flag } from '../cli/args.mjs';
3
+ import { printJson, sksTextLogo } from '../cli/output.mjs';
4
+
5
+ export async function run(_command, args = []) {
6
+ const project = await findProjectRoot();
7
+ const global = globalSksRoot();
8
+ const active = await sksRoot();
9
+ const result = {
10
+ cwd: process.cwd(),
11
+ mode: project ? 'project' : 'global',
12
+ active_root: active,
13
+ project_root: project,
14
+ global_root: global,
15
+ using_global_root: !project
16
+ };
17
+ if (flag(args, '--json')) return printJson(result);
18
+ console.log(`${sksTextLogo()}\n\nRoot\n`);
19
+ console.log(`Mode: ${result.mode}`);
20
+ console.log(`Active root: ${active}`);
21
+ console.log(`Project: ${project || 'none'}`);
22
+ console.log(`Global root: ${global}`);
23
+ if (!project) console.log('\nNo project marker was found here, so SKS will use the per-user global runtime root. Run `sks bootstrap` to initialize the current directory as a project.');
24
+ }
@@ -0,0 +1,5 @@
1
+ import { PACKAGE_VERSION } from '../core/version.mjs';
2
+
3
+ export async function run() {
4
+ console.log(`sneakoscope ${PACKAGE_VERSION}`);
5
+ }
@@ -0,0 +1,95 @@
1
+ import path from 'node:path';
2
+ import { projectRoot } from '../core/fsx.mjs';
3
+ import { flag, readOption } from '../cli/args.mjs';
4
+ import { printJson } from '../cli/output.mjs';
5
+ import { addImageRelation, addVisualAnchor, ingestImage, imageVoxelSummary, readImageVoxelLedger } from '../core/wiki-image/image-voxel-ledger.mjs';
6
+ import { imageVoxelProofEvidence } from '../core/wiki-image/proof-linker.mjs';
7
+ import { validateImageVoxelLedger } from '../core/wiki-image/validation.mjs';
8
+
9
+ export async function run(_command, args = []) {
10
+ const root = await projectRoot();
11
+ const action = args[0] || 'help';
12
+ if (action === 'image-ingest') {
13
+ const imagePath = args.find((arg, i) => i > 0 && !String(arg).startsWith('--'));
14
+ const result = await ingestImage(root, imagePath, {
15
+ source: readOption(args, '--source', 'manual'),
16
+ missionId: readOption(args, '--mission-id', null)
17
+ });
18
+ if (flag(args, '--json')) return printJson(result);
19
+ console.log(`Image ingested: ${result.image.id}`);
20
+ console.log(`Ledger: ${path.relative(root, path.join(root, '.sneakoscope', 'wiki', 'image-voxel-ledger.json'))}`);
21
+ if (!result.ok) process.exitCode = 1;
22
+ return;
23
+ }
24
+ if (action === 'image-validate') {
25
+ const ledgerPath = args.find((arg, i) => i > 0 && !String(arg).startsWith('--'));
26
+ const ledger = await readImageVoxelLedger(root, ledgerPath ? path.resolve(root, ledgerPath) : undefined);
27
+ const result = { schema: 'sks.image-voxel-validation.v1', ...validateImageVoxelLedger(ledger, {
28
+ requireAnchors: flag(args, '--require-anchors'),
29
+ requireRelations: flag(args, '--require-relations'),
30
+ route: readOption(args, '--route', '$Wiki')
31
+ }) };
32
+ if (flag(args, '--json')) return printJson(result);
33
+ console.log(`Image voxel ledger: ${result.ok ? 'pass' : 'blocked'}`);
34
+ for (const issue of result.issues) console.log(`- ${issue}`);
35
+ if (!result.ok) process.exitCode = 1;
36
+ return;
37
+ }
38
+ if (action === 'image-summary') {
39
+ const result = await imageVoxelSummary(root);
40
+ if (flag(args, '--json')) return printJson(result);
41
+ console.log(`Images: ${result.images}`);
42
+ console.log(`Anchors: ${result.anchors}`);
43
+ console.log(`Relations: ${result.relations}`);
44
+ if (!result.ok) process.exitCode = 1;
45
+ return;
46
+ }
47
+ if (action === 'anchor-add') {
48
+ const result = await addVisualAnchor(root, {
49
+ imageId: readOption(args, '--image-id', null),
50
+ bbox: parseBbox(readOption(args, '--bbox', '')),
51
+ label: readOption(args, '--label', 'Visual anchor'),
52
+ source: readOption(args, '--source', 'manual'),
53
+ evidencePath: readOption(args, '--evidence', null),
54
+ route: readOption(args, '--route', '$Wiki'),
55
+ claimId: readOption(args, '--claim-id', null),
56
+ missionId: readOption(args, '--mission-id', null)
57
+ });
58
+ if (flag(args, '--json')) return printJson(result);
59
+ console.log(`Visual anchor: ${result.ok ? 'added' : 'blocked'} ${result.anchor.id}`);
60
+ for (const issue of result.validation.issues) console.log(`- ${issue}`);
61
+ if (!result.ok) process.exitCode = 1;
62
+ return;
63
+ }
64
+ if (action === 'relation-add') {
65
+ const result = await addImageRelation(root, {
66
+ type: readOption(args, '--type', 'before_after'),
67
+ beforeImageId: readOption(args, '--before', null),
68
+ afterImageId: readOption(args, '--after', null),
69
+ anchors: String(readOption(args, '--anchors', '') || '').split(',').map((x) => x.trim()).filter(Boolean),
70
+ verification: readOption(args, '--verification', 'changed-screen-recheck'),
71
+ status: readOption(args, '--status', 'verified_partial'),
72
+ route: readOption(args, '--route', '$Wiki'),
73
+ missionId: readOption(args, '--mission-id', null)
74
+ });
75
+ if (flag(args, '--json')) return printJson(result);
76
+ console.log(`Image relation: ${result.ok ? 'added' : 'blocked'} ${result.relation.type}`);
77
+ for (const issue of result.validation.issues) console.log(`- ${issue}`);
78
+ if (!result.ok) process.exitCode = 1;
79
+ return;
80
+ }
81
+ if (action === 'image-link-proof') {
82
+ const result = await imageVoxelProofEvidence(root);
83
+ if (flag(args, '--json')) return printJson(result);
84
+ console.log(`Image voxel proof link: ${result.ok ? 'ok' : 'blocked'}`);
85
+ if (!result.ok) process.exitCode = 1;
86
+ return;
87
+ }
88
+ const legacy = await import('../cli/legacy-main.mjs');
89
+ return legacy.main(['wiki', ...args]);
90
+ }
91
+
92
+ function parseBbox(raw) {
93
+ const parts = String(raw || '').split(',').map((part) => Number(part.trim()));
94
+ return parts.length === 4 && parts.every(Number.isFinite) ? parts : null;
95
+ }