sneakoscope 4.8.7 → 5.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (288) hide show
  1. package/README.md +51 -726
  2. package/bench/tasks/t01-off-by-one/repo/package.json +9 -0
  3. package/bench/tasks/t01-off-by-one/repo/src/pagination.js +5 -0
  4. package/bench/tasks/t01-off-by-one/repo/test.js +5 -0
  5. package/bench/tasks/t01-off-by-one/task.json +13 -0
  6. package/bench/tasks/t02-signature-cochange/repo/package.json +9 -0
  7. package/bench/tasks/t02-signature-cochange/repo/src/admin.js +5 -0
  8. package/bench/tasks/t02-signature-cochange/repo/src/audit.js +5 -0
  9. package/bench/tasks/t02-signature-cochange/repo/src/labels.js +3 -0
  10. package/bench/tasks/t02-signature-cochange/repo/src/profile.js +5 -0
  11. package/bench/tasks/t02-signature-cochange/repo/test.js +9 -0
  12. package/bench/tasks/t02-signature-cochange/task.json +12 -0
  13. package/bench/tasks/t03-type-puzzle/repo/package.json +9 -0
  14. package/bench/tasks/t03-type-puzzle/repo/src/config.js +4 -0
  15. package/bench/tasks/t03-type-puzzle/repo/test.js +6 -0
  16. package/bench/tasks/t03-type-puzzle/task.json +13 -0
  17. package/bench/tasks/t04-refactor-preserve/repo/package.json +9 -0
  18. package/bench/tasks/t04-refactor-preserve/repo/src/cart.js +8 -0
  19. package/bench/tasks/t04-refactor-preserve/repo/test.js +4 -0
  20. package/bench/tasks/t04-refactor-preserve/task.json +12 -0
  21. package/bench/tasks/t05-performance/repo/package.json +9 -0
  22. package/bench/tasks/t05-performance/repo/src/pairs.js +9 -0
  23. package/bench/tasks/t05-performance/repo/test.js +11 -0
  24. package/bench/tasks/t05-performance/task.json +12 -0
  25. package/bench/tasks/t06-mistake-rule/repo/package.json +9 -0
  26. package/bench/tasks/t06-mistake-rule/repo/src/loader.js +6 -0
  27. package/bench/tasks/t06-mistake-rule/repo/test.js +7 -0
  28. package/bench/tasks/t06-mistake-rule/task.json +12 -0
  29. package/config/bench-baseline.json +7 -0
  30. package/crates/sks-core/Cargo.lock +1 -1
  31. package/crates/sks-core/Cargo.toml +1 -1
  32. package/crates/sks-core/src/main.rs +1 -1
  33. package/dist/bin/install.js +35 -0
  34. package/dist/bin/sks.js +1 -1
  35. package/dist/cli/cli-theme.js +51 -0
  36. package/dist/cli/command-registry.js +15 -4
  37. package/dist/cli/help-fast.js +18 -8
  38. package/dist/cli/insane-search-command.js +36 -8
  39. package/dist/cli/install-helpers.js +32 -0
  40. package/dist/commands/codex-lb.js +2 -0
  41. package/dist/commands/doctor.js +62 -6
  42. package/dist/commands/proof.js +6 -3
  43. package/dist/config/skills-manifest.json +82 -54
  44. package/dist/core/agents/agent-conflict-graph.js +5 -0
  45. package/dist/core/agents/agent-lease.js +5 -0
  46. package/dist/core/agents/agent-ledger-schemas.js +1 -0
  47. package/dist/core/agents/agent-orchestrator.js +268 -62
  48. package/dist/core/agents/agent-output-validator.js +14 -3
  49. package/dist/core/agents/agent-patch-queue-store.js +95 -2
  50. package/dist/core/agents/agent-patch-queue.js +6 -3
  51. package/dist/core/agents/agent-patch-schema.js +13 -0
  52. package/dist/core/agents/agent-proof-evidence.js +6 -1
  53. package/dist/core/agents/agent-runner-fake.js +40 -0
  54. package/dist/core/agents/agent-worker-pipeline.js +36 -2
  55. package/dist/core/agents/codex-exec-worker-adapter.js +2 -0
  56. package/dist/core/agents/native-worker-backend-router.js +58 -1
  57. package/dist/core/codex-app/sks-menubar.js +18 -5
  58. package/dist/core/codex-app.js +1 -0
  59. package/dist/core/codex-control/codex-sdk-config-policy.js +2 -1
  60. package/dist/core/codex-control/codex-sdk-env-policy.js +1 -1
  61. package/dist/core/codex-control/schemas/agent-worker-result.schema.js +12 -3
  62. package/dist/core/codex-hooks/codex-hook-managed-install.js +5 -0
  63. package/dist/core/codex-hooks/codex-hook-state-writer.js +70 -11
  64. package/dist/core/codex-hooks/codex-hook-trust-doctor.js +2 -2
  65. package/dist/core/codex-lb/codex-lb-env.js +20 -1
  66. package/dist/core/commands/basic-cli.js +21 -2
  67. package/dist/core/commands/check-command.js +37 -5
  68. package/dist/core/commands/computer-use-command.js +62 -7
  69. package/dist/core/commands/db-command.js +31 -5
  70. package/dist/core/commands/fast-mode-command.js +3 -0
  71. package/dist/core/commands/gate-result-contract.js +43 -0
  72. package/dist/core/commands/gates-command.js +6 -1
  73. package/dist/core/commands/gc-command.js +29 -2
  74. package/dist/core/commands/goal-command.js +9 -2
  75. package/dist/core/commands/gx-command.js +79 -7
  76. package/dist/core/commands/image-ux-review-command.js +188 -13
  77. package/dist/core/commands/mad-db-command.js +85 -176
  78. package/dist/core/commands/mad-sks-command.js +230 -29
  79. package/dist/core/commands/menubar-command.js +22 -1
  80. package/dist/core/commands/naruto-command.js +64 -10
  81. package/dist/core/commands/plan-command.js +76 -0
  82. package/dist/core/commands/ppt-command.js +159 -24
  83. package/dist/core/commands/qa-loop-command.js +6 -2
  84. package/dist/core/commands/release-command.js +55 -2
  85. package/dist/core/commands/research-command.js +1 -1
  86. package/dist/core/commands/review-command.js +217 -0
  87. package/dist/core/commands/route-success-helpers.js +59 -0
  88. package/dist/core/commands/run-command.js +19 -7
  89. package/dist/core/commands/seo-command.js +49 -1
  90. package/dist/core/commands/ui-command.js +161 -0
  91. package/dist/core/commands/uninstall-command.js +15 -1
  92. package/dist/core/db-safety.js +2 -2
  93. package/dist/core/doctor/doctor-transaction.js +8 -0
  94. package/dist/core/doctor/doctor-zellij-repair.js +1 -0
  95. package/dist/core/doctor/imagegen-repair.js +161 -0
  96. package/dist/core/feature-fixture-runner.js +2 -2
  97. package/dist/core/feature-fixtures.js +58 -37
  98. package/dist/core/feature-registry.js +102 -2
  99. package/dist/core/fsx.js +39 -6
  100. package/dist/core/image-ux-review/imagegen-adapter.js +48 -11
  101. package/dist/core/image-ux-review.js +16 -0
  102. package/dist/core/imagegen/imagegen-capability.js +11 -5
  103. package/dist/core/imagegen/require-imagegen.js +57 -0
  104. package/dist/core/init/skills.js +10 -5
  105. package/dist/core/mad-db/mad-db-coordinator.js +94 -18
  106. package/dist/core/mad-db/mad-db-policy.js +12 -10
  107. package/dist/core/mad-sks/executors/executor-base.js +8 -2
  108. package/dist/core/mad-sks/executors/index.js +4 -0
  109. package/dist/core/mad-sks/executors/sql-plane-executor.js +194 -0
  110. package/dist/core/naruto/naruto-active-pool.js +15 -4
  111. package/dist/core/naruto/naruto-real-worker-child.js +10 -0
  112. package/dist/core/naruto/naruto-role-policy.js +3 -0
  113. package/dist/core/naruto/naruto-task-hints.js +10 -0
  114. package/dist/core/naruto/naruto-work-graph.js +8 -2
  115. package/dist/core/naruto/naruto-work-item.js +6 -0
  116. package/dist/core/naruto/solution-tournament.js +101 -0
  117. package/dist/core/permission-gates.js +30 -0
  118. package/dist/core/pipeline-internals/runtime-core.js +11 -3
  119. package/dist/core/pipeline-internals/runtime-gates.js +27 -0
  120. package/dist/core/ppt-review/index.js +6 -1
  121. package/dist/core/ppt-review/slide-imagegen-review.js +75 -10
  122. package/dist/core/ppt.js +70 -2
  123. package/dist/core/proof/auto-finalize.js +70 -11
  124. package/dist/core/proof/proof-schema.js +2 -0
  125. package/dist/core/proof/route-adapter.js +5 -1
  126. package/dist/core/proof/route-finalizer.js +8 -5
  127. package/dist/core/proof/selftest-proof-fixtures.js +18 -5
  128. package/dist/core/proof/validation.js +2 -0
  129. package/dist/core/provider/model-router.js +53 -0
  130. package/dist/core/release/release-gate-node.js +3 -0
  131. package/dist/core/release/release-gate-resource-governor.js +1 -0
  132. package/dist/core/routes/constants.js +1 -1
  133. package/dist/core/routes.js +65 -18
  134. package/dist/core/safety/mutation-guard.js +5 -1
  135. package/dist/core/stop-gate/gate-evaluator.js +102 -0
  136. package/dist/core/stop-gate/stop-gate-check.js +16 -1
  137. package/dist/core/stop-gate/stop-gate-writer.js +4 -0
  138. package/dist/core/triwiki/agents-md-projector.js +184 -0
  139. package/dist/core/triwiki-wrongness/wrongness-schema.js +3 -1
  140. package/dist/core/trust-kernel/trust-kernel-schema.js +1 -0
  141. package/dist/core/trust-kernel/trust-report.js +1 -1
  142. package/dist/core/trust-kernel/trust-status.js +2 -0
  143. package/dist/core/ui/dashboard-html.js +111 -0
  144. package/dist/core/update/update-migration-state.js +366 -15
  145. package/dist/core/update-check.js +178 -81
  146. package/dist/core/verification/diff-quality.js +100 -0
  147. package/dist/core/verification/impact-scan.js +164 -0
  148. package/dist/core/verification/machine-feedback.js +146 -0
  149. package/dist/core/verification/mistake-rule-compiler.js +195 -0
  150. package/dist/core/version.js +1 -1
  151. package/dist/core/zellij/zellij-self-heal.js +9 -6
  152. package/dist/scripts/check-feature-quality.js +2 -2
  153. package/dist/scripts/cli-output-consistency-check.js +57 -0
  154. package/dist/scripts/coding-bench-check.js +136 -0
  155. package/dist/scripts/doctor-imagegen-repair-check.js +62 -0
  156. package/dist/scripts/ensure-bin-executable.js +11 -3
  157. package/dist/scripts/harness-benchmark-check.js +104 -0
  158. package/dist/scripts/legacy-update-e2e-check.js +228 -0
  159. package/dist/scripts/mad-db-command-check.js +11 -8
  160. package/dist/scripts/mad-db-route-identity-check.js +8 -6
  161. package/dist/scripts/mad-db-safety-conflict-matrix-check.js +31 -8
  162. package/dist/scripts/mad-db-skill-policy-snapshot-check.js +4 -5
  163. package/dist/scripts/ppt-real-imagegen-wiring-check.js +5 -1
  164. package/dist/scripts/proof-root-cause-policy-check.js +2 -0
  165. package/dist/scripts/release-dag-full-coverage-check.js +13 -4
  166. package/dist/scripts/release-gate-dag-runner.js +14 -0
  167. package/dist/scripts/release-gate-existence-audit.js +2 -1
  168. package/dist/scripts/release-gate-planner.js +2 -1
  169. package/dist/scripts/release-gate-script-parity-check.js +1 -1
  170. package/dist/scripts/release-metadata-1-19-check.js +2 -1
  171. package/dist/scripts/seo-geo-feature-fixture-quality-check.js +1 -1
  172. package/dist/scripts/sks-1-11-gate-lib.js +8 -6
  173. package/dist/scripts/ux-review-real-loop-fixture-check.js +9 -4
  174. package/dist/scripts/ux-review-run-wires-imagegen-check.js +4 -0
  175. package/docs/assets/sneakoscope-architecture-pipeline.jpg +0 -0
  176. package/docs/demo.tape +28 -0
  177. package/package.json +67 -3
  178. package/schemas/codex/completion-proof.schema.json +3 -2
  179. package/schemas/release/release-gate-node.schema.json +2 -1
  180. package/dist/scripts/agent-backfill-route-blackbox.js +0 -5
  181. package/dist/scripts/agent-dynamic-pool-route-blackbox.js +0 -5
  182. package/dist/scripts/agent-parallel-write-blackbox.js +0 -56
  183. package/dist/scripts/agent-patch-swarm-route-blackbox.js +0 -10
  184. package/dist/scripts/agent-route-blackbox-lib.js +0 -132
  185. package/dist/scripts/blackbox-command-import-smoke.js +0 -143
  186. package/dist/scripts/blackbox-global-shim.js +0 -77
  187. package/dist/scripts/blackbox-matrix.js +0 -70
  188. package/dist/scripts/blackbox-npx-one-shot.js +0 -69
  189. package/dist/scripts/blackbox-pack-install.js +0 -174
  190. package/dist/scripts/brand-neutrality-zero-leakage-blackbox.js +0 -4
  191. package/dist/scripts/build-once-runner-blackbox.js +0 -34
  192. package/dist/scripts/codex-0140-integration-blackbox.js +0 -13
  193. package/dist/scripts/codex-agent-type-blackbox.js +0 -4
  194. package/dist/scripts/codex-app-harness-blackbox.js +0 -4
  195. package/dist/scripts/codex-app-skill-agent-blackbox.js +0 -4
  196. package/dist/scripts/codex-hook-approval-blackbox.js +0 -4
  197. package/dist/scripts/codex-init-deep-directory-local-blackbox.js +0 -4
  198. package/dist/scripts/codex-native-feature-broker-blackbox.js +0 -4
  199. package/dist/scripts/codex-native-pattern-analysis-blackbox.js +0 -4
  200. package/dist/scripts/codex-native-read-repair-split-blackbox.js +0 -55
  201. package/dist/scripts/codex-native-reference-cache-blackbox.js +0 -41
  202. package/dist/scripts/core-skill-integrity-blackbox.js +0 -32
  203. package/dist/scripts/dfix-fast-blackbox-check.js +0 -37
  204. package/dist/scripts/dfix-parallel-write-blackbox.js +0 -48
  205. package/dist/scripts/dfix-patch-swarm-route-blackbox.js +0 -10
  206. package/dist/scripts/doctor-context7-mcp-repair-blackbox.js +0 -16
  207. package/dist/scripts/doctor-dirty-repair-blackbox.js +0 -22
  208. package/dist/scripts/doctor-dirty-semantic-blackbox.js +0 -8
  209. package/dist/scripts/doctor-fix-production-blackbox.js +0 -26
  210. package/dist/scripts/doctor-native-capability-repair-blackbox.js +0 -39
  211. package/dist/scripts/doctor-startup-config-repair-blackbox.js +0 -13
  212. package/dist/scripts/doctor-supabase-mcp-repair-blackbox.js +0 -14
  213. package/dist/scripts/doctor-transaction-engine-blackbox.js +0 -28
  214. package/dist/scripts/doctor-zellij-fix-blackbox.js +0 -4
  215. package/dist/scripts/doctor-zellij-no-homebrew-blackbox.js +0 -4
  216. package/dist/scripts/doctor-zellij-upgrade-blackbox.js +0 -4
  217. package/dist/scripts/gate-pack-runner-blackbox.js +0 -27
  218. package/dist/scripts/gate-pack-v2-blackbox.js +0 -18
  219. package/dist/scripts/geo-cli-blackbox-check.js +0 -18
  220. package/dist/scripts/loop-collision-blackbox.js +0 -3
  221. package/dist/scripts/loop-concurrency-oversubscription-blackbox.js +0 -3
  222. package/dist/scripts/loop-fixture-production-misuse-blackbox.js +0 -3
  223. package/dist/scripts/loop-kill-interrupt-real-blackbox.js +0 -3
  224. package/dist/scripts/loop-merge-strategy-blackbox.js +0 -3
  225. package/dist/scripts/loop-mesh-production-e2e-blackbox.js +0 -3
  226. package/dist/scripts/loop-side-effect-blackbox.js +0 -3
  227. package/dist/scripts/mad-db-operation-lifecycle-blackbox.js +0 -29
  228. package/dist/scripts/mad-sks-actual-executor-blackbox.js +0 -5
  229. package/dist/scripts/mad-zellij-headless-fallback-blackbox.js +0 -4
  230. package/dist/scripts/mad-zellij-self-heal-blackbox.js +0 -4
  231. package/dist/scripts/naruto-loop-mesh-blackbox.js +0 -3
  232. package/dist/scripts/naruto-real-parallelism-blackbox.js +0 -307
  233. package/dist/scripts/naruto-worktree-coding-blackbox.js +0 -29
  234. package/dist/scripts/parallel-runtime-real-blackbox.js +0 -44
  235. package/dist/scripts/pipeline-codex-native-doctor-mad-routing-real-blackbox.js +0 -77
  236. package/dist/scripts/pipeline-codex-native-e2e-blackbox.js +0 -13
  237. package/dist/scripts/pipeline-codex-native-image-routing-real-blackbox.js +0 -50
  238. package/dist/scripts/pipeline-codex-native-loop-routing-real-blackbox.js +0 -74
  239. package/dist/scripts/pipeline-codex-native-qa-routing-real-blackbox.js +0 -51
  240. package/dist/scripts/pipeline-codex-native-research-routing-real-blackbox.js +0 -45
  241. package/dist/scripts/pipeline-execution-profile-routing-blackbox.js +0 -4
  242. package/dist/scripts/postinstall-global-doctor-blackbox.js +0 -12
  243. package/dist/scripts/ppt-full-e2e-blackbox-check.js +0 -109
  244. package/dist/scripts/ppt-imagegen-blackbox-check.js +0 -46
  245. package/dist/scripts/project-skill-dedupe-blackbox.js +0 -36
  246. package/dist/scripts/qa-backfill-route-blackbox.js +0 -5
  247. package/dist/scripts/qa-patch-swarm-route-blackbox.js +0 -10
  248. package/dist/scripts/release-full-parallelism-blackbox.js +0 -41
  249. package/dist/scripts/release-triwiki-first-runner-blackbox.js +0 -48
  250. package/dist/scripts/release-wiring-3110-blackbox.js +0 -27
  251. package/dist/scripts/release-wiring-3112-blackbox.js +0 -17
  252. package/dist/scripts/release-wiring-3113-blackbox.js +0 -17
  253. package/dist/scripts/research-backfill-route-blackbox.js +0 -5
  254. package/dist/scripts/research-final-reviewer-blackbox.js +0 -70
  255. package/dist/scripts/research-stage-cycle-runtime-blackbox.js +0 -40
  256. package/dist/scripts/research-synthesis-writer-blackbox.js +0 -24
  257. package/dist/scripts/route-blackbox-realism-check.js +0 -21
  258. package/dist/scripts/scheduler-resource-claim-blackbox.js +0 -24
  259. package/dist/scripts/seo-cli-blackbox-check.js +0 -18
  260. package/dist/scripts/sks-3110-all-feature-regression-blackbox.js +0 -116
  261. package/dist/scripts/sks-3112-all-feature-regression-blackbox.js +0 -29
  262. package/dist/scripts/sks-3113-all-feature-regression-blackbox.js +0 -17
  263. package/dist/scripts/sks-400-all-feature-regression-blackbox.js +0 -21
  264. package/dist/scripts/sks-400-extreme-parallel-blackbox.js +0 -8
  265. package/dist/scripts/sks-400-five-minute-blackbox.js +0 -9
  266. package/dist/scripts/sks-400-legacy-purge-blackbox.js +0 -8
  267. package/dist/scripts/sks-401-all-feature-regression-blackbox.js +0 -46
  268. package/dist/scripts/sks-401-five-minute-actual-blackbox.js +0 -23
  269. package/dist/scripts/sks-402-all-feature-regression-blackbox.js +0 -9
  270. package/dist/scripts/sks-402-five-minute-real-blackbox.js +0 -22
  271. package/dist/scripts/sksd-warm-cache-blackbox.js +0 -12
  272. package/dist/scripts/supabase-secret-preservation-blackbox.js +0 -29
  273. package/dist/scripts/team-backfill-route-blackbox.js +0 -5
  274. package/dist/scripts/team-parallel-write-blackbox.js +0 -55
  275. package/dist/scripts/team-patch-swarm-route-blackbox.js +0 -10
  276. package/dist/scripts/triwiki-affected-graph-blackbox.js +0 -28
  277. package/dist/scripts/triwiki-proof-bank-blackbox.js +0 -30
  278. package/dist/scripts/triwiki-proof-bank-lock-blackbox.js +0 -7
  279. package/dist/scripts/update-preserves-supabase-keys-blackbox.js +0 -27
  280. package/dist/scripts/ux-patch-swarm-route-blackbox.js +0 -10
  281. package/dist/scripts/ux-review-imagegen-blackbox-check.js +0 -67
  282. package/dist/scripts/zellij-initial-main-only-blackbox.js +0 -28
  283. package/dist/scripts/zellij-pane-lock-concurrency-blackbox.js +0 -80
  284. package/dist/scripts/zellij-pane-lock-open-worker-integration-blackbox.js +0 -137
  285. package/dist/scripts/zellij-self-heal-typed-blackbox.js +0 -4
  286. package/dist/scripts/zellij-slot-telemetry-real-blackbox.js +0 -20
  287. package/dist/scripts/zellij-stacked-fallback-integration-blackbox.js +0 -81
  288. package/dist/scripts/zellij-worker-pane-real-ui-blackbox.js +0 -202
@@ -0,0 +1,59 @@
1
+ import path from 'node:path';
2
+ import { exists, readJson, readText } from '../fsx.js';
3
+ export async function context7EvidenceStatus(root, missionId = null) {
4
+ const candidates = [
5
+ missionId ? path.join(root, '.sneakoscope', 'missions', missionId, 'context7-evidence.jsonl') : null,
6
+ path.join(root, '.sneakoscope', 'state', 'context7-evidence.jsonl'),
7
+ path.join(root, '.sneakoscope', 'memory', 'q2_facts', 'stack-current-docs.md')
8
+ ].filter(Boolean);
9
+ for (const file of candidates) {
10
+ if (!(await exists(file)))
11
+ continue;
12
+ const text = await readText(file, '');
13
+ if (/\S/.test(text) && /(context7|resolve[-_]?library|query[-_]?docs|get[-_]?library|official docs|vendor docs)/i.test(text)) {
14
+ return { ok: true, policy: 'required_satisfied', evidence: path.relative(root, file).split(path.sep).join('/') };
15
+ }
16
+ }
17
+ return { ok: false, policy: 'required_not_satisfied', evidence: null, blocker: 'context7_policy_required_not_satisfied' };
18
+ }
19
+ export async function artifactPresence(root, dir, artifacts) {
20
+ const rows = await Promise.all(artifacts.map(async (artifact) => {
21
+ const file = path.isAbsolute(artifact) ? artifact : path.join(dir, artifact);
22
+ return { artifact, present: await exists(file) };
23
+ }));
24
+ const missing = rows.filter((row) => !row.present).map((row) => row.artifact);
25
+ return { ok: missing.length === 0, rows, missing };
26
+ }
27
+ export async function evaluateLocalGate(input) {
28
+ const missionDir = input.dir || (input.missionId ? path.join(input.root, '.sneakoscope', 'missions', input.missionId) : input.root);
29
+ const gatePath = path.isAbsolute(input.gateFile) ? input.gateFile : path.join(missionDir, input.gateFile);
30
+ const gate = await readJson(gatePath, null);
31
+ const blockers = [];
32
+ if (!gate)
33
+ blockers.push(`${path.basename(input.gateFile)}_missing`);
34
+ if (gate) {
35
+ if (!(gate.passed === true || gate.ok === true || gate.status === 'pass' || gate.status === 'passed'))
36
+ blockers.push('gate_not_passed');
37
+ if (gate.ok === false)
38
+ blockers.push('gate_ok_false');
39
+ if (Array.isArray(gate.blockers) && gate.blockers.length)
40
+ blockers.push(...gate.blockers.map(String));
41
+ if (Array.isArray(gate.missing_fields) && gate.missing_fields.length)
42
+ blockers.push(...gate.missing_fields.map((field) => `missing_field:${field}`));
43
+ for (const section of input.requiredSections || []) {
44
+ if (gate[section] == null)
45
+ blockers.push(`missing_gate_section:${section}`);
46
+ }
47
+ }
48
+ const artifactCheck = await artifactPresence(input.root, missionDir, input.requiredArtifacts || []);
49
+ blockers.push(...artifactCheck.missing.map((artifact) => `missing_artifact:${artifact}`));
50
+ const uniqueBlockers = [...new Set(blockers)];
51
+ return {
52
+ ok: uniqueBlockers.length === 0,
53
+ gate,
54
+ gate_path: gatePath,
55
+ blockers: uniqueBlockers,
56
+ artifacts: artifactCheck
57
+ };
58
+ }
59
+ //# sourceMappingURL=route-success-helpers.js.map
@@ -62,7 +62,15 @@ export async function runCommand(args = []) {
62
62
  return finalizeMockRun(root, { id, route, prompt, args, classification, mode });
63
63
  }
64
64
  async function finalizeMockRun(root, { id, route, prompt, args, classification, mode, }) {
65
- const gate = { schema: 'sks.run-gate.v1', ok: true, passed: true, route: route.command, mock: true };
65
+ const gate = {
66
+ schema: 'sks.run-gate.v1',
67
+ ok: false,
68
+ passed: false,
69
+ route: route.command,
70
+ mock: true,
71
+ execution_class: 'mock_fixture',
72
+ blockers: ['run_mock_fixture_cannot_claim_real_completion']
73
+ };
66
74
  await writeJsonAtomic(path.join(missionDir(root, id), 'run-gate.json'), gate);
67
75
  const proof = await finalizeRoute(root, {
68
76
  missionId: id,
@@ -76,19 +84,20 @@ async function finalizeMockRun(root, { id, route, prompt, args, classification,
76
84
  command: { cmd: `sks run "${prompt}" --mock`, status: 0 },
77
85
  });
78
86
  const trust = await loadTrustReport(root, id);
87
+ const completionOk = proof.ok && proof.proof?.status !== 'mock_only' && gate.passed === true;
79
88
  await setCurrent(root, {
80
89
  mission_id: id,
81
90
  mode: 'RUN',
82
91
  route: route.id,
83
92
  route_command: route.command,
84
- phase: proof.ok ? 'RUN_MOCK_FINALIZED' : 'RUN_MOCK_BLOCKED',
93
+ phase: completionOk ? 'RUN_MOCK_FINALIZED' : 'RUN_MOCK_BLOCKED',
85
94
  implementation_allowed: true,
86
95
  completion_proof: 'completion-proof.json',
87
96
  trust_report: 'trust-report.json',
88
97
  });
89
98
  const result = {
90
99
  schema: 'sks.run.v2',
91
- ok: proof.ok,
100
+ ok: completionOk,
92
101
  mission_id: id,
93
102
  route: route.command,
94
103
  mode,
@@ -98,6 +107,8 @@ async function finalizeMockRun(root, { id, route, prompt, args, classification,
98
107
  completion_proof: { ok: proof.ok, validation: proof.validation },
99
108
  trust_report: trust,
100
109
  };
110
+ if (!result.ok)
111
+ process.exitCode = 1;
101
112
  if (flag(args, '--json')) {
102
113
  console.log(JSON.stringify(result, null, 2));
103
114
  return result;
@@ -235,7 +246,7 @@ async function executeRouteCommand(root, route, prompt, { auto = false } = {}) {
235
246
  return routeExecutionResult(route, ['sks', ...commandArgs].join(' '), result, {
236
247
  okStatus: 'completed',
237
248
  trustStatus: 'verified_partial',
238
- executionKind: route.command === '$DB' || route.command === '$Wiki' || route.command === '$Fast-Mode' || route.command === '$with-local-llm-on' || route.command === '$Commit' || route.command === '$Commit-And-Push' || route.command === '$Ultra-Search' || route.command === '$SEO-GEO-OPTIMIZER' ? 'safe_deterministic' : 'mock_safe',
249
+ executionKind: 'live_route',
239
250
  });
240
251
  }
241
252
  async function runAutoVerification(root, missionId) {
@@ -304,7 +315,7 @@ function routeExecutionResult(route, command, result, options = {}) {
304
315
  schema: 'sks.run-route-execution.v1',
305
316
  ok,
306
317
  status: ok ? (options.okStatus || 'completed') : 'blocked',
307
- execution_kind: ok ? (options.executionKind || 'safe_deterministic') : 'blocked',
318
+ execution_kind: ok ? (options.executionKind || 'live_route') : 'blocked',
308
319
  route: route.command,
309
320
  command,
310
321
  exit_code: result.code,
@@ -314,7 +325,7 @@ function routeExecutionResult(route, command, result, options = {}) {
314
325
  trust_status: ok ? options.trustStatus || 'verified_partial' : 'blocked',
315
326
  blockers: ok ? [] : ['route_command_failed'],
316
327
  unverified: ok
317
- ? options.unverified || ['sks run --execute used the deterministic safe route command path; real external dependencies remain route-specific.']
328
+ ? options.unverified || ['sks run --execute ran the selected route command; route-specific gates remain authoritative for final trust.']
318
329
  : [],
319
330
  next_action: ok ? 'review completion proof and trust report' : 'inspect run-route-execution.json stderr_tail',
320
331
  };
@@ -538,7 +549,7 @@ async function writeLightweightTrustReport(root, missionId, route, statusHint, p
538
549
  const report = {
539
550
  schema: TRUST_REPORT_SCHEMA,
540
551
  ...trustKernelMetadata(),
541
- ok: proofOk && !['blocked', 'failed', 'not_verified'].includes(status),
552
+ ok: proofOk && !['blocked', 'failed', 'not_verified', 'mock_only'].includes(status),
542
553
  mission_id: missionId,
543
554
  route,
544
555
  status,
@@ -551,6 +562,7 @@ async function writeLightweightTrustReport(root, missionId, route, statusHint, p
551
562
  lightweight: true,
552
563
  reason: 'safe_deterministic_run_wrapper'
553
564
  },
565
+ trust_basis: 'lightweight',
554
566
  evidence: {
555
567
  completion_proof: `.sneakoscope/missions/${missionId}/completion-proof.json`,
556
568
  route_contract: null,
@@ -2,7 +2,8 @@ import path from 'node:path';
2
2
  import { printJson } from '../../cli/output.js';
3
3
  import { projectRoot } from '../fsx.js';
4
4
  import { flag, readOption } from './command-utils.js';
5
- import { runSearchVisibilityApply, runSearchVisibilityAudit, runSearchVisibilityDoctor, runSearchVisibilityFixture, runSearchVisibilityPlan, runSearchVisibilityRollback, runSearchVisibilityStatus, runSearchVisibilityVerify, } from '../search-visibility/index.js';
5
+ import { runSearchVisibilityApply, runSearchVisibilityAudit, runSearchVisibilityDoctor, runSearchVisibilityFixture, runSearchVisibilityPlan, runSearchVisibilityRollback, runSearchVisibilityStatus, runSearchVisibilityVerify, resolveSearchVisibilityMission, } from '../search-visibility/index.js';
6
+ import { evaluateLocalGate } from './route-success-helpers.js';
6
7
  export async function seoCommand(args = []) {
7
8
  return runSearchVisibilityCommand('seo', args, 'seo');
8
9
  }
@@ -15,6 +16,26 @@ export async function runSearchVisibilityCommand(mode, args = [], displayCommand
15
16
  const rest = args.slice(1);
16
17
  const options = await parseOptions(rest);
17
18
  let result;
19
+ if (action === 'apply' && options.apply) {
20
+ const mission = await resolveSearchVisibilityMission(options.root, firstPositional(rest) || 'latest');
21
+ const blockers = [];
22
+ if (!mission)
23
+ blockers.push('mission_required_for_apply');
24
+ else {
25
+ const planGate = await evaluateLocalGate({ root: mission.root, dir: mission.dir, gateFile: mode === 'seo' ? 'seo-gate.json' : 'geo-gate.json', requiredArtifacts: ['search-visibility/mutation-plan.json'] });
26
+ blockers.push(...planGate.blockers.filter((blocker) => blocker !== 'gate_not_passed' && blocker !== 'gate_ok_false'));
27
+ }
28
+ if (blockers.length) {
29
+ result = { schema: 'sks.search-visibility.apply-command.v1', ok: false, route: mode === 'seo' ? '$SEO-GEO-OPTIMIZER' : '$SEO-GEO-OPTIMIZER', status: 'blocked', blockers };
30
+ process.exitCode = 1;
31
+ if (options.json) {
32
+ printJson(result);
33
+ return result;
34
+ }
35
+ printHuman(mode, action, result);
36
+ return result;
37
+ }
38
+ }
18
39
  if (action === 'doctor')
19
40
  result = await runSearchVisibilityDoctor(mode, options);
20
41
  else if (action === 'audit')
@@ -33,6 +54,7 @@ export async function runSearchVisibilityCommand(mode, args = [], displayCommand
33
54
  result = await runSearchVisibilityFixture(mode, options);
34
55
  else
35
56
  return usage(mode, 2, displayCommand);
57
+ result = await applySearchVisibilityGateResult(mode, action, result, options);
36
58
  if (isBlocked(result))
37
59
  process.exitCode = 1;
38
60
  if (options.json) {
@@ -42,6 +64,32 @@ export async function runSearchVisibilityCommand(mode, args = [], displayCommand
42
64
  printHuman(mode, action, result);
43
65
  return result;
44
66
  }
67
+ async function applySearchVisibilityGateResult(mode, action, result, options) {
68
+ if (!result || typeof result !== 'object' || !result.mission_id)
69
+ return result;
70
+ const mission = await resolveSearchVisibilityMission(options.root, result.mission_id);
71
+ if (!mission)
72
+ return { ...result, ok: false, blockers: [...(result.blockers || []), 'mission_missing_for_gate_evaluation'] };
73
+ const requiredArtifacts = action === 'apply'
74
+ ? ['search-visibility/mutation-plan.json', 'search-visibility/rollback-manifest.json', 'search-visibility/verification-report.json']
75
+ : action === 'status'
76
+ ? []
77
+ : ['search-visibility/verification-report.json'];
78
+ const evaluated = await evaluateLocalGate({
79
+ root: mission.root,
80
+ dir: mission.dir,
81
+ gateFile: mode === 'seo' ? 'seo-gate.json' : 'geo-gate.json',
82
+ requiredArtifacts
83
+ });
84
+ const blockers = [...new Set([...(result.blockers || []), ...evaluated.blockers])];
85
+ return {
86
+ ...result,
87
+ ok: result.ok === true && evaluated.ok === true,
88
+ status: result.ok === true && evaluated.ok === true ? result.status : 'blocked',
89
+ gate_evaluation: evaluated,
90
+ blockers
91
+ };
92
+ }
45
93
  function normalizeOptimizerArgs(args) {
46
94
  const out = [...args];
47
95
  const first = String(out[0] || '').toLowerCase();
@@ -0,0 +1,161 @@
1
+ import * as http from 'node:http';
2
+ import path from 'node:path';
3
+ import { flag } from '../../cli/args.js';
4
+ import { printJson } from '../../cli/output.js';
5
+ import { ui } from '../../cli/cli-theme.js';
6
+ import { DASHBOARD_HTML } from '../ui/dashboard-html.js';
7
+ import { findLatestMission, missionDir } from '../mission.js';
8
+ import { nowIso, projectRoot, readJson, readText, runProcess } from '../fsx.js';
9
+ import { readZellijSlotTelemetrySnapshot } from '../zellij/zellij-slot-telemetry.js';
10
+ export async function uiCommand(args = []) {
11
+ const root = path.resolve(String(readOption(args, '--root', '') || await projectRoot()));
12
+ if (flag(args, '--once')) {
13
+ const state = await collectUiState(root, String(readOption(args, '--mission', 'latest') || 'latest'));
14
+ if (flag(args, '--json'))
15
+ return printJson(state);
16
+ ui.banner('ui');
17
+ ui.ok(`state ${state.mission_id || 'latest'} ${state.gates.length} gates`);
18
+ return state;
19
+ }
20
+ const requestedPort = Number(readOption(args, '--port', '4477'));
21
+ const port = Number.isFinite(requestedPort) && requestedPort > 0 ? Math.floor(requestedPort) : 4477;
22
+ const mission = String(readOption(args, '--mission', 'latest') || 'latest');
23
+ const server = http.createServer(async (req, res) => {
24
+ try {
25
+ const url = new URL(req.url || '/', 'http://127.0.0.1');
26
+ if (url.pathname === '/') {
27
+ res.writeHead(200, { 'content-type': 'text/html; charset=utf-8' });
28
+ res.end(DASHBOARD_HTML);
29
+ return;
30
+ }
31
+ if (url.pathname === '/events')
32
+ return sseStream(res, root, String(url.searchParams.get('mission') || mission));
33
+ if (url.pathname === '/api/state') {
34
+ res.writeHead(200, { 'content-type': 'application/json; charset=utf-8', 'cache-control': 'no-store' });
35
+ res.end(JSON.stringify(await collectUiState(root, String(url.searchParams.get('mission') || mission))));
36
+ return;
37
+ }
38
+ res.writeHead(404);
39
+ res.end('not found');
40
+ }
41
+ catch (err) {
42
+ res.writeHead(500, { 'content-type': 'application/json; charset=utf-8' });
43
+ res.end(JSON.stringify({ ok: false, error: sanitizeText(err?.message || String(err)) }));
44
+ }
45
+ });
46
+ const actualPort = await listenLocal(server, port);
47
+ const url = `http://127.0.0.1:${actualPort}`;
48
+ ui.banner('ui');
49
+ ui.ok(`dashboard ${url}`);
50
+ void runProcess('/usr/bin/open', [url], { timeoutMs: 5000, maxOutputBytes: 4096 }).catch(() => undefined);
51
+ return new Promise(() => undefined);
52
+ }
53
+ export async function collectUiState(root, missionInput = 'latest') {
54
+ const missionId = missionInput === 'latest' ? await findLatestMission(root) : missionInput;
55
+ const snapshot = missionId ? await readZellijSlotTelemetrySnapshot(root, missionId).catch(() => null) : null;
56
+ const gates = missionId ? await readLatestGateSummaries(root, missionId) : [];
57
+ const events = missionId ? await tailJsonl(path.join(missionDir(root, missionId), 'events.jsonl'), 30) : [];
58
+ const mission = missionId ? await readJson(path.join(missionDir(root, missionId), 'mission.json'), null).catch(() => null) : null;
59
+ return sanitizeForUi({
60
+ schema: 'sks.ui-state.v1',
61
+ ok: true,
62
+ ts: nowIso(),
63
+ mission_id: missionId,
64
+ route: mission?.mode || mission?.route || null,
65
+ snapshot,
66
+ gates,
67
+ events
68
+ });
69
+ }
70
+ async function sseStream(res, root, mission) {
71
+ res.writeHead(200, {
72
+ 'content-type': 'text/event-stream',
73
+ 'cache-control': 'no-cache, no-store',
74
+ connection: 'keep-alive'
75
+ });
76
+ const tick = async () => {
77
+ const state = await collectUiState(root, mission).catch((err) => ({ ok: false, ts: nowIso(), error: sanitizeText(err?.message || String(err)) }));
78
+ res.write(`data: ${JSON.stringify(state)}\n\n`);
79
+ };
80
+ await tick();
81
+ const timer = setInterval(tick, 1000);
82
+ res.on('close', () => clearInterval(timer));
83
+ }
84
+ async function readLatestGateSummaries(root, missionId) {
85
+ const dir = missionDir(root, missionId);
86
+ const files = ['stop-gate.json', 'naruto-gate.json', 'team-gate.json', 'qa-gate.json', 'reflection-gate.json', 'completion-proof.json'];
87
+ const rows = [];
88
+ for (const file of files) {
89
+ const gate = await readJson(path.join(dir, file), null).catch(() => null);
90
+ if (!gate)
91
+ continue;
92
+ rows.push({
93
+ id: file,
94
+ ok: gate.passed === true || gate.ok === true || gate.status === 'passed',
95
+ missing: Array.isArray(gate.missing) ? gate.missing : Array.isArray(gate.blockers) ? gate.blockers : [],
96
+ source: `.sneakoscope/missions/${missionId}/${file}`
97
+ });
98
+ }
99
+ return rows;
100
+ }
101
+ async function tailJsonl(file, limit) {
102
+ const text = await readText(file, '').catch(() => '');
103
+ return String(text || '').split(/\r?\n/).filter(Boolean).slice(-limit).map((line) => {
104
+ try {
105
+ return JSON.parse(line);
106
+ }
107
+ catch {
108
+ return { ts: null, type: 'raw', text: line.slice(0, 500) };
109
+ }
110
+ });
111
+ }
112
+ async function listenLocal(server, startPort) {
113
+ for (let offset = 0; offset < 20; offset += 1) {
114
+ const port = startPort + offset;
115
+ const result = await new Promise((resolve) => {
116
+ const onError = (err) => {
117
+ server.off('listening', onListen);
118
+ resolve({ ok: false, code: err.code || 'listen_failed' });
119
+ };
120
+ const onListen = () => {
121
+ server.off('error', onError);
122
+ resolve({ ok: true });
123
+ };
124
+ server.once('error', onError);
125
+ server.once('listening', onListen);
126
+ server.listen(port, '127.0.0.1');
127
+ });
128
+ if (result.ok)
129
+ return port;
130
+ if (result.code !== 'EADDRINUSE')
131
+ throw new Error(`dashboard listen failed: ${result.code}`);
132
+ }
133
+ throw new Error('dashboard listen failed: no available localhost port');
134
+ }
135
+ function sanitizeForUi(value) {
136
+ if (Array.isArray(value))
137
+ return value.map((item) => sanitizeForUi(item));
138
+ if (!value || typeof value !== 'object')
139
+ return typeof value === 'string' ? sanitizeText(value) : value;
140
+ const out = {};
141
+ for (const [key, child] of Object.entries(value)) {
142
+ if (/(?:key|token|secret|password|credential|cookie|auth)/i.test(key))
143
+ continue;
144
+ out[key] = sanitizeForUi(child);
145
+ }
146
+ return out;
147
+ }
148
+ function sanitizeText(value) {
149
+ return String(value || '')
150
+ .replace(/sk-[A-Za-z0-9_-]{12,}/g, '[redacted]')
151
+ .replace(/(?:api[_-]?key|secret|token|password|credential)/ig, '[redacted-field]')
152
+ .slice(0, 2000);
153
+ }
154
+ function readOption(args = [], name, fallback = null) {
155
+ const index = args.indexOf(name);
156
+ if (index >= 0 && args[index + 1] && !String(args[index + 1]).startsWith('--'))
157
+ return args[index + 1];
158
+ const prefixed = args.find((arg) => String(arg).startsWith(`${name}=`));
159
+ return prefixed ? prefixed.slice(name.length + 1) : fallback;
160
+ }
161
+ //# sourceMappingURL=ui-command.js.map
@@ -4,10 +4,12 @@ import fsp from 'node:fs/promises';
4
4
  import readline from 'node:readline/promises';
5
5
  import { stdin as input, stdout as output } from 'node:process';
6
6
  import { ensureDir, exists, readJson, readText, runProcess, writeJsonAtomic } from '../fsx.js';
7
+ import { ui as cliUi } from '../../cli/cli-theme.js';
7
8
  import { uninstallSksMenuBar } from '../codex-app/sks-menubar.js';
8
9
  import { writeCodexConfigGuarded } from '../codex/codex-config-guard.js';
9
10
  import { sweepSksTempDirs } from '../retention.js';
10
11
  import { reconcileSkills } from '../init/skills.js';
12
+ import { removeTriwikiAgentsMdBlocks } from '../triwiki/agents-md-projector.js';
11
13
  export async function uninstallCommand(args = []) {
12
14
  const opts = {
13
15
  yes: hasFlag(args, '--yes') || hasFlag(args, '-y'),
@@ -24,8 +26,11 @@ export async function uninstallCommand(args = []) {
24
26
  const result = { schema: 'sks.uninstall.v1', ok: true, dry_run: true, inventory };
25
27
  if (opts.json)
26
28
  console.log(JSON.stringify(result, null, 2));
27
- else
29
+ else {
30
+ cliUi.banner('uninstall');
31
+ cliUi.warn('dry run only');
28
32
  printInventoryTable(inventory);
33
+ }
29
34
  return result;
30
35
  }
31
36
  if (!opts.yes && !(await confirmUninstall())) {
@@ -66,6 +71,8 @@ export async function uninstallCommand(args = []) {
66
71
  if (opts.json)
67
72
  console.log(JSON.stringify(report, null, 2));
68
73
  else {
74
+ cliUi.banner('uninstall');
75
+ report.ok ? cliUi.ok('removed configured SKS surfaces') : cliUi.fail('uninstall completed with errors');
69
76
  console.log('SKS uninstall complete.');
70
77
  console.log('Final package removal command: npm uninstall -g sneakoscope');
71
78
  }
@@ -84,6 +91,7 @@ async function collectSksInventory(opts) {
84
91
  ['hooks-json', path.join(home, '.codex', 'hooks.json'), 'strip SKS-managed hooks', '--keep-config'],
85
92
  ['sks-home', path.join(home, '.sneakoscope'), 'remove', '--keep-data'],
86
93
  ['sks-global-home', path.join(home, '.sneakoscope-global'), 'remove', '--keep-data'],
94
+ ['agents-md-memory-block', path.join(opts.root, 'AGENTS.md'), 'strip SKS Project Memory block', '--purge-projects'],
87
95
  ['tmp', os.tmpdir(), 'sweep sks temp dirs', 'default']
88
96
  ];
89
97
  const out = [];
@@ -263,6 +271,12 @@ async function purgeProjectArtifacts(report, opts) {
263
271
  report.errors.push({ id: 'project-skills', root, error: err?.message || String(err) });
264
272
  });
265
273
  await fsp.rm(path.join(root, '.codex'), { recursive: true, force: true }).catch(() => null);
274
+ const removedBlocks = await removeTriwikiAgentsMdBlocks(root).catch((err) => {
275
+ report.errors.push({ id: 'agents-md-memory-block', root, error: err?.message || String(err) });
276
+ return [];
277
+ });
278
+ if (removedBlocks.length)
279
+ report.removed.push({ id: 'agents-md-memory-block', root, result: { ok: true, files: removedBlocks.length } });
266
280
  await fsp.rm(path.join(root, 'SNEAKOSCOPE.md'), { force: true }).catch(() => null);
267
281
  if (!opts.keepData)
268
282
  await fsp.rm(path.join(root, '.sneakoscope'), { recursive: true, force: true }).catch(() => null);
@@ -609,8 +609,8 @@ export function dbBlockReason(decision) {
609
609
  return [
610
610
  'Sneakoscope Codex Database Safety Gate blocked this operation.',
611
611
  `Reasons: ${(decision.reasons || []).join(', ') || 'unknown'}.`,
612
- 'Default DB mode is read-only. Destructive SQL-plane operations require an explicit active MadDB v2 capability opened by $MAD-DB or sks mad-db run|exec|apply-migration.',
613
- 'Use read-only/project-scoped Supabase MCP URLs outside MadDB. Supabase project/account/billing/credential control-plane operations remain denied even in MadDB.'
612
+ 'Default DB mode is read-only. Destructive SQL-plane operations require an explicit active MAD-SKS sql-plane capability opened by $MAD-SKS/sks mad-sks sql|apply-migration or the deprecated $MAD-DB/sks mad-db redirect.',
613
+ 'Use read-only/project-scoped Supabase MCP URLs outside MAD-SKS sql-plane. Supabase project/account/billing/credential control-plane operations remain denied even there.'
614
614
  ].join(' ');
615
615
  }
616
616
  export async function scanDbSafety(root, opts = {}) {
@@ -1,14 +1,18 @@
1
1
  import path from 'node:path';
2
2
  import { nowIso, writeJsonAtomic } from '../fsx.js';
3
3
  import { isDoctorPhaseClean, markDoctorPhaseClean } from './doctor-dirty-planner.js';
4
+ import { ui as cliUi } from '../../cli/cli-theme.js';
4
5
  export async function runDoctorFixTransaction(input) {
5
6
  const startedAt = nowIso();
6
7
  const phases = [];
7
8
  const proofIdsUsed = [];
8
9
  let rollbackPerformed = false;
10
+ const liveOutput = input.json !== true && input.machineOnly !== true;
9
11
  for (const definition of input.phases) {
10
12
  const phaseStarted = nowIso();
11
13
  const startedMs = Date.now();
14
+ if (liveOutput)
15
+ cliUi.step(`▸ ${definition.id} ...`);
12
16
  let phase = {
13
17
  id: definition.id,
14
18
  ok: false,
@@ -32,6 +36,8 @@ export async function runDoctorFixTransaction(input) {
32
36
  completed_at: nowIso(),
33
37
  duration_ms: Math.max(0, Date.now() - startedMs)
34
38
  });
39
+ if (liveOutput)
40
+ cliUi.step(`✔ ${definition.id} (${Math.round((Date.now() - startedMs) / 1000)}s)`);
35
41
  continue;
36
42
  }
37
43
  try {
@@ -71,6 +77,8 @@ export async function runDoctorFixTransaction(input) {
71
77
  proofIdsUsed.push(proofId);
72
78
  }
73
79
  phases.push(phase);
80
+ if (liveOutput)
81
+ cliUi.step(`${phase.ok ? '✔' : '✖'} ${definition.id} (${Math.round((phase.duration_ms || 0) / 1000)}s)`);
74
82
  }
75
83
  const writeInput = {
76
84
  root: input.root,
@@ -10,6 +10,7 @@ export async function runDoctorZellijRepair(input) {
10
10
  autoApprove: args.includes('--yes') || args.includes('-y'),
11
11
  installHomebrew: args.includes('--install-homebrew') || process.env.SKS_ALLOW_HOMEBREW_INSTALL === '1',
12
12
  dryRun: args.includes('--dry-run'),
13
+ quiet: args.includes('--json') || args.includes('--machine-only'),
13
14
  interactive: Boolean(process.stdin.isTTY && process.stdout.isTTY && process.env.SKS_NO_QUESTION !== '1'),
14
15
  allowHeadlessFallback: false,
15
16
  env: process.env
@@ -0,0 +1,161 @@
1
+ import path from 'node:path';
2
+ import { CODEX_APP_IMAGE_GENERATION_DOC_URL } from '../routes.js';
3
+ import { detectImagegenCapability } from '../imagegen/imagegen-capability.js';
4
+ import { ensureDir, nowIso, runProcess, which, writeJsonAtomic } from '../fsx.js';
5
+ export const DOCTOR_IMAGEGEN_REPAIR_SCHEMA = 'sks.doctor-imagegen-repair.v1';
6
+ export async function repairCodexImagegen(input) {
7
+ const root = path.resolve(input.root || process.cwd());
8
+ const apply = input.apply === true;
9
+ const steps = [];
10
+ const before = await detectImagegenCapability({
11
+ codexBin: input.codexBin || undefined,
12
+ timeoutMs: input.timeoutMs || 5000
13
+ }).catch((err) => ({ ok: false, core_ready: false, blockers: [messageOf(err)], codex_app: { available: false, blocker: messageOf(err) } }));
14
+ let codexBin = input.codexBin || await which('codex').catch(() => null);
15
+ let versionStep;
16
+ if (codexBin) {
17
+ const version = await runProcess(codexBin, ['--version'], { timeoutMs: input.timeoutMs || 5000, maxOutputBytes: 16 * 1024 })
18
+ .catch((err) => ({ code: 1, stdout: '', stderr: messageOf(err) }));
19
+ versionStep = {
20
+ id: 'codex_cli_version',
21
+ ok: version.code === 0,
22
+ attempted: true,
23
+ command: `${codexBin} --version`,
24
+ exit_code: version.code,
25
+ stdout_tail: tail(version.stdout),
26
+ stderr_tail: tail(version.stderr),
27
+ blocker: version.code === 0 ? null : 'codex_cli_version_failed'
28
+ };
29
+ }
30
+ else {
31
+ versionStep = {
32
+ id: 'codex_cli_version',
33
+ ok: false,
34
+ attempted: false,
35
+ command: 'codex --version',
36
+ blocker: 'codex_binary_missing'
37
+ };
38
+ }
39
+ steps.push(versionStep);
40
+ if (!codexBin && apply && (input.autoInstallCodex === true || process.env.SKS_IMAGEGEN_AUTO_INSTALL_CODEX === '1')) {
41
+ const npmBin = await which('npm').catch(() => null);
42
+ if (npmBin) {
43
+ const install = await runProcess(npmBin, ['i', '-g', '@openai/codex@latest'], {
44
+ timeoutMs: 180000,
45
+ maxOutputBytes: 128 * 1024
46
+ }).catch((err) => ({ code: 1, stdout: '', stderr: messageOf(err) }));
47
+ steps.push({
48
+ id: 'codex_cli_install',
49
+ ok: install.code === 0,
50
+ attempted: true,
51
+ command: `${npmBin} i -g @openai/codex@latest`,
52
+ exit_code: install.code,
53
+ stdout_tail: tail(install.stdout),
54
+ stderr_tail: tail(install.stderr),
55
+ blocker: install.code === 0 ? null : 'codex_cli_install_failed'
56
+ });
57
+ codexBin = await which('codex').catch(() => null);
58
+ }
59
+ else {
60
+ steps.push({
61
+ id: 'codex_cli_install',
62
+ ok: false,
63
+ attempted: false,
64
+ command: 'npm i -g @openai/codex@latest',
65
+ blocker: 'npm_missing'
66
+ });
67
+ }
68
+ }
69
+ else if (!codexBin) {
70
+ steps.push({
71
+ id: 'codex_cli_install',
72
+ ok: false,
73
+ attempted: false,
74
+ command: 'npm i -g @openai/codex@latest',
75
+ blocker: apply ? 'auto_install_requires_SKS_IMAGEGEN_AUTO_INSTALL_CODEX_1' : 'doctor_fix_not_requested'
76
+ });
77
+ }
78
+ if (codexBin && before?.core_ready !== true && apply) {
79
+ const enable = await runProcess(codexBin, ['features', 'enable', 'image_generation'], {
80
+ timeoutMs: input.timeoutMs || 10000,
81
+ maxOutputBytes: 32 * 1024
82
+ }).catch((err) => ({ code: 1, stdout: '', stderr: messageOf(err) }));
83
+ steps.push({
84
+ id: 'image_generation_feature_enable',
85
+ ok: enable.code === 0,
86
+ attempted: true,
87
+ command: `${codexBin} features enable image_generation`,
88
+ exit_code: enable.code,
89
+ stdout_tail: tail(enable.stdout),
90
+ stderr_tail: tail(enable.stderr),
91
+ blocker: enable.code === 0 ? null : 'codex_feature_enable_unsupported_or_failed'
92
+ });
93
+ }
94
+ else {
95
+ steps.push({
96
+ id: 'image_generation_feature_enable',
97
+ ok: before?.core_ready === true,
98
+ attempted: false,
99
+ command: codexBin ? `${codexBin} features enable image_generation` : 'codex features enable image_generation',
100
+ blocker: before?.core_ready === true ? null : apply ? 'codex_cli_missing' : 'doctor_fix_not_requested'
101
+ });
102
+ }
103
+ const after = await detectImagegenCapability({
104
+ codexBin: codexBin || undefined,
105
+ timeoutMs: input.timeoutMs || 5000
106
+ }).catch((err) => ({ ok: false, core_ready: false, blockers: [messageOf(err)], codex_app: { available: false, blocker: messageOf(err) } }));
107
+ steps.push({
108
+ id: 'imagegen_capability_redetect',
109
+ ok: after?.core_ready === true,
110
+ attempted: true,
111
+ command: 'codex features list --json',
112
+ blocker: after?.core_ready === true ? null : 'codex_app_builtin_imagegen_capability_missing'
113
+ });
114
+ const recovered = after?.core_ready === true;
115
+ const blockers = recovered ? [] : [
116
+ ...new Set([
117
+ ...(after?.core_blockers || []),
118
+ ...(after?.blockers || []),
119
+ 'codex_imagegen_unavailable'
120
+ ].map(String))
121
+ ];
122
+ let report = {
123
+ schema: DOCTOR_IMAGEGEN_REPAIR_SCHEMA,
124
+ generated_at: nowIso(),
125
+ ok: recovered,
126
+ attempted: before?.core_ready !== true,
127
+ apply,
128
+ recovered,
129
+ before,
130
+ after,
131
+ steps,
132
+ blockers,
133
+ manual_actions: recovered ? [] : [
134
+ `Install/update Codex CLI if missing: npm i -g @openai/codex@latest`,
135
+ `Open Codex App settings and enable image_generation / $imagegen.`,
136
+ `Verify with: codex features list --json`,
137
+ `Docs: ${CODEX_APP_IMAGE_GENERATION_DOC_URL}`
138
+ ],
139
+ docs_url: CODEX_APP_IMAGE_GENERATION_DOC_URL
140
+ };
141
+ if (input.reportPath !== null) {
142
+ const reportPath = input.reportPath || path.join(root, '.sneakoscope', 'reports', 'doctor-imagegen-repair.json');
143
+ try {
144
+ await ensureDir(path.dirname(reportPath));
145
+ await writeJsonAtomic(reportPath, report);
146
+ report = { ...report, report_path: reportPath };
147
+ }
148
+ catch (err) {
149
+ report = { ...report, report_write_failed: true, report_write_error: messageOf(err) };
150
+ }
151
+ }
152
+ return report;
153
+ }
154
+ function tail(value, max = 2000) {
155
+ const text = String(value || '');
156
+ return text.length > max ? text.slice(-max) : text;
157
+ }
158
+ function messageOf(err) {
159
+ return err instanceof Error ? err.message : String(err);
160
+ }
161
+ //# sourceMappingURL=imagegen-repair.js.map