sneakoscope 2.0.5 → 2.0.7

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 (70) hide show
  1. package/README.md +12 -4
  2. package/crates/sks-core/Cargo.lock +1 -1
  3. package/crates/sks-core/Cargo.toml +1 -1
  4. package/crates/sks-core/src/main.rs +1 -1
  5. package/dist/.sks-build-stamp.json +4 -4
  6. package/dist/bin/sks.js +1 -1
  7. package/dist/build-manifest.json +37 -8
  8. package/dist/cli/install-helpers.js +23 -0
  9. package/dist/commands/codex-app.js +25 -3
  10. package/dist/commands/doctor.js +19 -4
  11. package/dist/commands/mad-sks.js +2 -2
  12. package/dist/core/agents/agent-orchestrator.js +160 -6
  13. package/dist/core/agents/agent-patch-schema.js +16 -2
  14. package/dist/core/agents/agent-proof-evidence.js +27 -2
  15. package/dist/core/agents/agent-worker-pipeline.js +9 -1
  16. package/dist/core/agents/native-cli-session-swarm.js +25 -4
  17. package/dist/core/agents/native-cli-worker.js +28 -1
  18. package/dist/core/agents/native-worker-backend-router.js +19 -1
  19. package/dist/core/codex-app.js +124 -2
  20. package/dist/core/codex-control/python-codex-sdk-adapter.js +28 -4
  21. package/dist/core/commands/naruto-command.js +48 -14
  22. package/dist/core/feature-registry.js +2 -0
  23. package/dist/core/fsx.js +1 -1
  24. package/dist/core/git/git-integration-worktree.js +15 -0
  25. package/dist/core/git/git-repo-detection.js +72 -0
  26. package/dist/core/git/git-worktree-cache-policy.js +36 -0
  27. package/dist/core/git/git-worktree-capability.js +54 -0
  28. package/dist/core/git/git-worktree-cleanup.js +51 -0
  29. package/dist/core/git/git-worktree-conflict-resolver.js +13 -0
  30. package/dist/core/git/git-worktree-diff.js +50 -0
  31. package/dist/core/git/git-worktree-manager.js +86 -0
  32. package/dist/core/git/git-worktree-merge-queue.js +55 -0
  33. package/dist/core/git/git-worktree-patch-envelope.js +35 -0
  34. package/dist/core/git/git-worktree-pool.js +23 -0
  35. package/dist/core/git/git-worktree-root.js +52 -0
  36. package/dist/core/git/git-worktree-runner.js +40 -0
  37. package/dist/core/hooks-runtime.js +2 -233
  38. package/dist/core/init.js +8 -8
  39. package/dist/core/naruto/naruto-active-pool.js +55 -4
  40. package/dist/core/naruto/naruto-gpt-final-pack.js +2 -0
  41. package/dist/core/naruto/naruto-work-graph.js +16 -1
  42. package/dist/core/pipeline-internals/runtime-core.js +1 -1
  43. package/dist/core/ppt.js +31 -8
  44. package/dist/core/product-design-app-server.js +410 -0
  45. package/dist/core/product-design-plugin.js +139 -0
  46. package/dist/core/routes.js +8 -8
  47. package/dist/core/version.js +1 -1
  48. package/dist/core/zellij/zellij-naruto-dashboard.js +10 -1
  49. package/dist/core/zellij/zellij-worker-pane-manager.js +6 -4
  50. package/dist/scripts/git-worktree-cache-performance-check.js +25 -0
  51. package/dist/scripts/git-worktree-capability-check.js +27 -0
  52. package/dist/scripts/git-worktree-cleanup-check.js +27 -0
  53. package/dist/scripts/git-worktree-diff-export-check.js +43 -0
  54. package/dist/scripts/git-worktree-manager-check.js +37 -0
  55. package/dist/scripts/git-worktree-merge-queue-check.js +30 -0
  56. package/dist/scripts/git-worktree-pool-performance-check.js +20 -0
  57. package/dist/scripts/lib/git-worktree-fixture.js +33 -0
  58. package/dist/scripts/naruto-active-pool-check.js +13 -1
  59. package/dist/scripts/naruto-readonly-routing-check.js +116 -0
  60. package/dist/scripts/naruto-shadow-clone-swarm-check.js +16 -5
  61. package/dist/scripts/naruto-worktree-coding-check.js +44 -0
  62. package/dist/scripts/naruto-worktree-gpt-final-check.js +45 -0
  63. package/dist/scripts/naruto-worktree-zellij-ui-check.js +28 -0
  64. package/dist/scripts/product-design-auto-install-check.js +119 -0
  65. package/dist/scripts/product-design-plugin-routing-check.js +101 -0
  66. package/dist/scripts/release-parallel-check.js +16 -2
  67. package/dist/scripts/release-provenance-check.js +21 -0
  68. package/package.json +17 -3
  69. package/schemas/git/git-worktree-capability.schema.json +19 -0
  70. package/schemas/git/git-worktree-manifest.schema.json +36 -0
package/README.md CHANGED
@@ -16,10 +16,18 @@ Set up this agent project with Sneakoscope Codex. Use [[mandarange/Sneakoscope-C
16
16
 
17
17
  ## Current Release
18
18
 
19
- SKS **2.0.5** is a P0 Codex App Fast UI and MAD Zellij worker-pane closure patch on top of the 2.0 execution layer. `sks --mad` now relies on launch-time Fast/high overrides instead of user-level Codex config rewrites, safe Fast UI repair runs through `sks doctor --fix`, provider badges read env/auth/config.toml consistently, and interactive MAD worker panes attach to the real Zellij session as scheduler slots spawn.
19
+ SKS **2.0.7** adds Git worktree parallel coding foundations for Naruto/MAD-SKS-style high-throughput work. Write-capable Git missions can now declare `git-worktree` mode, allocate worker worktrees outside the main checkout by default, export full-index diffs into patch envelopes, apply them through an integration worktree queue, and retain dirty worktrees instead of deleting evidence. Non-Git projects degrade to patch-envelope-only without calling `git worktree`.
20
20
 
21
21
  What changed:
22
22
 
23
+ - Git capability checks detect repo roots, Git dirs, worktree support, and safe cache roots while blocking in-repo worktree roots unless `SKS_ALLOW_IN_REPO_WORKTREES=1` is explicit.
24
+ - Worker worktree allocation creates isolated branches/paths under `$SKS_WORKTREE_ROOT`, `$XDG_CACHE_HOME/sks/worktrees`, or `~/.cache/sks/worktrees`; main checkouts stay untouched until integration.
25
+ - Worktree diff export records status, changed files, full-index binary diffs, and `git-worktree-diff` patch envelopes for parent-owned integration.
26
+ - Integration worktrees apply worker diffs with `git apply --3way`, while cleanup removes only clean worktrees and retains dirty ones with a lock artifact.
27
+ - Naruto work graphs, active-pool artifacts, Zellij dashboard titles, native worker intake, and GPT Final packs now carry worktree policy and WT/branch context.
28
+ - Product Design plugin readiness now checks both local and remote Codex App catalogs, auto-installs the remote plugin when needed, and records the installed/enabled skill surface.
29
+ - UI/design/PPT runtime routes prefer Product Design for research, ideation, audit, design QA, prototype, URL-to-code, image-to-code, share, and user-context steps.
30
+ - Naruto read-only runs force write mode off, propagate no-patch reasons through worker proof, and skip changed-file lease checks when no write-capable patch envelope exists.
23
31
  - `codex-sdk` is the default native agent backend for Team, QA, Research, Naruto, MAD-SKS, and direct agent runs, with every runtime task entering through `runCodexTask`.
24
32
  - Codex App UI snapshot, preservation, clobber guard, and doctor repair checks protect host-owned Fast UI/profile settings around `sks --mad`.
25
33
  - Provider context resolves `openai`, `codex-lb`, and `codex-app` with badge/fallback surfaces while avoiding private Codex App UI mutation.
@@ -78,7 +86,7 @@ Broader release checks still live behind `npm run release:check`. Detailed relea
78
86
  sks xai docs
79
87
  ```
80
88
 
81
- - **Quieter update prompts.** The "update available" choice is shown once per conversation and then stays quiet for a short window (default 8 min, `SKS_UPDATE_OFFER_THROTTLE_MS`) instead of repeating on every prompt. The check compares npm latest against source metadata, PATH `sks --version`, and the global npm package, so a completed `npm i -g sneakoscope` clears stale pending/accepted offers.
89
+ - **CLI-only SKS update notices.** Codex App hooks no longer stop normal work to ask for an SKS update. CLI launch surfaces such as `sks --mad` print a non-blocking latest-version notice, `sks update-check` / `sks update check` show the explicit status, and `sks doctor --fix` runs the guarded global SKS update path before repair.
82
90
 
83
91
  ## Retention And Cleanup
84
92
 
@@ -359,7 +367,7 @@ sks --mad --allow-package-install --allow-service-control --allow-network --yes
359
367
 
360
368
  This syncs existing codex-lb provider auth, creates/uses the `sks-mad-high` xhigh maintenance profile, opens the MAD-SKS permission gate for that Zellij run, starts a same-mission read-only native agent swarm, and launches a Codex CLI layout whose right-side lanes read that MAD ledger. Bare `sks --mad` grants target-project file and shell scope only; add explicit `--allow-*` flags for packages, services, network, Computer Use, browser use, generated assets, file permissions, DB writes, or other high-risk scopes. MAD-SKS is not a DB-only unlock: it is explicit user authorization to widen approved target-project scopes. Catastrophic database wipe/all-row/project-management safeguards remain active, and the pipeline contract still forbids unrequested fallback implementation code.
361
369
 
362
- Before launching, SKS checks npm for a newer `sneakoscope`; answer `y` to update or `n` to continue. Use `--yes` to approve missing dependency installs automatically. Tune MAD swarm startup with `--mad-agents <n>`, `--mad-swarm-work-items <n>`, and `--mad-swarm-backend <backend>`; `--no-mad-swarm` keeps only the cockpit UI if you need a temporary fallback.
370
+ Before launching, SKS checks npm for a newer `sneakoscope` and prints a non-blocking update notice when one is available; use `sks update now` or `sks doctor --fix` when you want SKS to update itself. Use `--yes` to approve missing dependency installs automatically. Tune MAD swarm startup with `--mad-agents <n>`, `--mad-swarm-work-items <n>`, and `--mad-swarm-backend <backend>`; `--no-mad-swarm` keeps only the cockpit UI if you need a temporary fallback.
363
371
 
364
372
  ### Team Missions
365
373
 
@@ -659,7 +667,7 @@ npm ls -g sneakoscope --depth=0
659
667
  sks doctor --fix
660
668
  ```
661
669
 
662
- Update prompts compare npm latest against the effective installed version from source metadata, PATH `sks --version`, and global npm package metadata. `sks update now` installs through npm global mode instead of mutating the current project's dependencies. If a global update succeeded but an old shim remains earlier on PATH, `sks doctor --fix` can remove duplicate global installs and refresh the managed setup.
670
+ CLI update checks compare npm latest against the effective installed version from source metadata, PATH `sks --version`, and global npm package metadata. Codex App hooks do not force update choices during ordinary work. `sks update now` installs through npm global mode instead of mutating the current project's dependencies, and `sks doctor --fix` runs that guarded global update path before setup/config repair. If a global update succeeded but an old shim remains earlier on PATH, `sks doctor --fix` can remove duplicate global installs and refresh the managed setup.
663
671
 
664
672
  ### Zellij is missing
665
673
 
@@ -76,7 +76,7 @@ dependencies = [
76
76
 
77
77
  [[package]]
78
78
  name = "sks-core"
79
- version = "2.0.5"
79
+ version = "2.0.7"
80
80
  dependencies = [
81
81
  "serde_json",
82
82
  ]
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "sks-core"
3
- version = "2.0.5"
3
+ version = "2.0.7"
4
4
  edition = "2021"
5
5
 
6
6
  [dependencies]
@@ -4,7 +4,7 @@ use std::io::{self, Read, Seek, SeekFrom};
4
4
  fn main() {
5
5
  let mut args = std::env::args().skip(1);
6
6
  match args.next().as_deref() {
7
- Some("--version") => println!("sks-rs 2.0.5"),
7
+ Some("--version") => println!("sks-rs 2.0.7"),
8
8
  Some("compact-info") => {
9
9
  let mut input = String::new();
10
10
  let _ = io::stdin().read_to_string(&mut input);
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schema": "sks.dist-build-stamp.v1",
3
3
  "package_name": "sneakoscope",
4
- "package_version": "2.0.5",
5
- "source_digest": "696706cb9d175c4200c967130fbc5e680f4b6c905ec6e7fad5b1671827bcb4cb",
6
- "source_file_count": 1948,
7
- "built_at_source_time": 1780613485190
4
+ "package_version": "2.0.7",
5
+ "source_digest": "fd9b0bc3d9a5a09bcd2b90988719b11e844448de39f28cf27e98c1b4cae82a0f",
6
+ "source_file_count": 1979,
7
+ "built_at_source_time": 1780686416915
8
8
  }
package/dist/bin/sks.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- const FAST_PACKAGE_VERSION = '2.0.5';
2
+ const FAST_PACKAGE_VERSION = '2.0.7';
3
3
  const args = process.argv.slice(2);
4
4
  try {
5
5
  if (args[0] === '--agent' && args[1] === 'worker') {
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "schema": "sks.dist-build.v2",
3
- "version": "2.0.5",
4
- "package_version": "2.0.5",
3
+ "version": "2.0.7",
4
+ "package_version": "2.0.7",
5
5
  "typescript": true,
6
6
  "mjs_runtime_files": 0,
7
- "compiled_file_count": 1014,
8
- "compiled_js_count": 1014,
7
+ "compiled_file_count": 1043,
8
+ "compiled_js_count": 1043,
9
9
  "compiled_dts_count": 0,
10
- "source_digest": "696706cb9d175c4200c967130fbc5e680f4b6c905ec6e7fad5b1671827bcb4cb",
11
- "source_file_count": 1948,
12
- "source_files_hash": "50906865545a9076d7b2736ee379a428160a8869749b85ee79c4ddab8eab4b12",
13
- "source_list_hash": "50906865545a9076d7b2736ee379a428160a8869749b85ee79c4ddab8eab4b12",
10
+ "source_digest": "fd9b0bc3d9a5a09bcd2b90988719b11e844448de39f28cf27e98c1b4cae82a0f",
11
+ "source_file_count": 1979,
12
+ "source_files_hash": "319ce7cd86fb58c825b14eb717a1d7b9c85be197206852aee9646fbfd5d356c6",
13
+ "source_list_hash": "319ce7cd86fb58c825b14eb717a1d7b9c85be197206852aee9646fbfd5d356c6",
14
14
  "src_mjs_runtime_files": 0,
15
15
  "dist_stamp_schema": "sks.dist-build-stamp.v1",
16
16
  "files": [
@@ -342,6 +342,19 @@
342
342
  "core/git-hygiene/shared-memory-security.js",
343
343
  "core/git-hygiene/validators.js",
344
344
  "core/git-simple.js",
345
+ "core/git/git-integration-worktree.js",
346
+ "core/git/git-repo-detection.js",
347
+ "core/git/git-worktree-cache-policy.js",
348
+ "core/git/git-worktree-capability.js",
349
+ "core/git/git-worktree-cleanup.js",
350
+ "core/git/git-worktree-conflict-resolver.js",
351
+ "core/git/git-worktree-diff.js",
352
+ "core/git/git-worktree-manager.js",
353
+ "core/git/git-worktree-merge-queue.js",
354
+ "core/git/git-worktree-patch-envelope.js",
355
+ "core/git/git-worktree-pool.js",
356
+ "core/git/git-worktree-root.js",
357
+ "core/git/git-worktree-runner.js",
345
358
  "core/goal-workflow.js",
346
359
  "core/gx-renderer.js",
347
360
  "core/harness-conflicts.js",
@@ -473,6 +486,8 @@
473
486
  "core/ppt-review/slide-issue-extraction.js",
474
487
  "core/ppt.js",
475
488
  "core/preflight/parallel-preflight-engine.js",
489
+ "core/product-design-app-server.js",
490
+ "core/product-design-plugin.js",
476
491
  "core/prompt-context-builder.js",
477
492
  "core/prompt/prompt-placeholder-guard.js",
478
493
  "core/proof-field.js",
@@ -812,6 +827,13 @@
812
827
  "scripts/flagship-proof-graph-v3-check.js",
813
828
  "scripts/flagship-proof-graph-v4-check.js",
814
829
  "scripts/git-precommit-fixture-check.js",
830
+ "scripts/git-worktree-cache-performance-check.js",
831
+ "scripts/git-worktree-capability-check.js",
832
+ "scripts/git-worktree-cleanup-check.js",
833
+ "scripts/git-worktree-diff-export-check.js",
834
+ "scripts/git-worktree-manager-check.js",
835
+ "scripts/git-worktree-merge-queue-check.js",
836
+ "scripts/git-worktree-pool-performance-check.js",
815
837
  "scripts/goal-mode-official-default-check.js",
816
838
  "scripts/gpt-final-arbiter-check.js",
817
839
  "scripts/gpt-final-arbiter-performance-check.js",
@@ -839,6 +861,7 @@
839
861
  "scripts/legacy-upgrade-matrix-check.js",
840
862
  "scripts/lib/codex-sdk-gate-lib.js",
841
863
  "scripts/lib/ensure-dist-fresh.js",
864
+ "scripts/lib/git-worktree-fixture.js",
842
865
  "scripts/lib/mad-sks-actual-executor-check-lib.js",
843
866
  "scripts/lib/native-cli-session-swarm-check-lib.js",
844
867
  "scripts/lib/real-codex-parallel-gate.js",
@@ -886,11 +909,15 @@
886
909
  "scripts/naruto-concurrency-governor-check.js",
887
910
  "scripts/naruto-gpt-final-pack-check.js",
888
911
  "scripts/naruto-parallel-patch-apply-check.js",
912
+ "scripts/naruto-readonly-routing-check.js",
889
913
  "scripts/naruto-real-local-gpt-final-smoke.js",
890
914
  "scripts/naruto-role-distribution-check.js",
891
915
  "scripts/naruto-shadow-clone-swarm-check.js",
892
916
  "scripts/naruto-verification-pool-check.js",
893
917
  "scripts/naruto-work-graph-check.js",
918
+ "scripts/naruto-worktree-coding-check.js",
919
+ "scripts/naruto-worktree-gpt-final-check.js",
920
+ "scripts/naruto-worktree-zellij-ui-check.js",
894
921
  "scripts/naruto-zellij-massive-ui-check.js",
895
922
  "scripts/non-recursive-pipeline-check.js",
896
923
  "scripts/npm-publish-performance-check.js",
@@ -916,6 +943,8 @@
916
943
  "scripts/prepublish-fast-check.js",
917
944
  "scripts/prepublish-release-check-or-fast.js",
918
945
  "scripts/priority-full-closure-check.js",
946
+ "scripts/product-design-auto-install-check.js",
947
+ "scripts/product-design-plugin-routing-check.js",
919
948
  "scripts/prompt-placeholder-guard-check.js",
920
949
  "scripts/provider-badge-context-check.js",
921
950
  "scripts/provider-context-config-toml-check.js",
@@ -13,6 +13,7 @@ import { context7ConfigToml, DOLLAR_SKILL_NAMES, GETDESIGN_REFERENCE, hasContext
13
13
  import { checkZellijCapability } from '../core/zellij/zellij-capability.js';
14
14
  import { reconcileCodexAppUpgradeProcesses } from '../core/codex-app.js';
15
15
  import { recordCodexLbHealthEvent } from '../core/codex-lb-circuit.js';
16
+ import { runSksUpdateCheck, runSksUpdateNow } from '../core/update-check.js';
16
17
  import { loadCodexLbEnv, writeCodexLbKeychain, codexLbMetadataPath } from '../core/codex-lb/codex-lb-env.js';
17
18
  import { buildCodexLbSetupPlan, codexLbPersistenceSummary, installCodexLbShellProfileSnippet, selectedCodexLbPersistenceModes } from '../core/codex-lb/codex-lb-setup.js';
18
19
  const DEFAULT_CODEX_APP_PLUGINS = [
@@ -2231,6 +2232,28 @@ export async function maybePromptCodexUpdateForLaunch(args = [], opts = {}) {
2231
2232
  return { status: 'skipped_by_user', latest: latest.version || null, current, command, bin: codex.bin || null };
2232
2233
  return installCodexLatest(command, latest.version, current);
2233
2234
  }
2235
+ export async function maybePromptSksUpdateForLaunch(args = [], opts = {}) {
2236
+ if (hasFlag(args, '--json') || hasFlag(args, '--skip-sks-update') || process.env.SKS_SKIP_SKS_UPDATE === '1')
2237
+ return { status: 'skipped' };
2238
+ const label = opts.label || 'SKS launch';
2239
+ const check = await runSksUpdateCheck({ timeoutMs: 5000 }).catch((err) => ({
2240
+ ok: false,
2241
+ update_available: false,
2242
+ latest: null,
2243
+ current: PACKAGE_VERSION,
2244
+ command: null,
2245
+ error: err?.message || String(err)
2246
+ }));
2247
+ if (!check.update_available) {
2248
+ return { status: check.error ? 'unavailable' : 'current', latest: check.latest || null, current: check.current || PACKAGE_VERSION, error: check.error || null };
2249
+ }
2250
+ const command = check.command || `sks update now --version ${check.latest}`;
2251
+ if (shouldAutoApproveInstall(args)) {
2252
+ return runSksUpdateNow({ version: check.latest, timeoutMs: 10 * 60 * 1000, maxOutputBytes: 128 * 1024 });
2253
+ }
2254
+ console.log(`SKS update available before ${label}: ${check.current} -> ${check.latest}. Run when ready: ${command}`);
2255
+ return { status: 'available', latest: check.latest || null, current: check.current || PACKAGE_VERSION, command };
2256
+ }
2234
2257
  export function shouldAutoApproveInstall(args = [], env = process.env) {
2235
2258
  if (hasFlag(args, '--from-postinstall') && env.SKS_POSTINSTALL_AUTO_INSTALL_CLI_TOOLS !== '1')
2236
2259
  return false;
@@ -1,11 +1,31 @@
1
1
  import { flag } from '../cli/args.js';
2
2
  import { printJson } from '../cli/output.js';
3
- import { codexAccessTokenStatus, codexAppIntegrationStatus, codexChromeExtensionStatus, formatCodexAppStatus } from '../core/codex-app.js';
3
+ import { codexAccessTokenStatus, codexAppIntegrationStatus, codexChromeExtensionStatus, codexProductDesignPluginStatus, formatCodexAppStatus, formatCodexProductDesignPluginStatus } from '../core/codex-app.js';
4
4
  import { codexAppRemoteControlCommand } from '../cli/codex-app-command.js';
5
5
  export async function run(_command, args = []) {
6
6
  const action = args[0] || 'check';
7
7
  if (action === 'remote-control' || action === 'remote')
8
8
  return codexAppRemoteControlCommand(args.slice(1));
9
+ if (action === 'product-design' || action === 'design-product' || action === 'ensure-product-design') {
10
+ const checkOnly = flag(args, '--check-only') || flag(args, '--no-install');
11
+ const status = await codexProductDesignPluginStatus({
12
+ autoInstallProductDesign: !checkOnly && (action === 'product-design'
13
+ || action === 'design-product'
14
+ || action === 'ensure-product-design'
15
+ || flag(args, '--install')
16
+ || flag(args, '--auto-install'))
17
+ });
18
+ if (flag(args, '--json')) {
19
+ printJson(status);
20
+ if (!status.ok)
21
+ process.exitCode = 1;
22
+ return;
23
+ }
24
+ console.log(formatCodexProductDesignPluginStatus(status));
25
+ if (!status.ok)
26
+ process.exitCode = 1;
27
+ return;
28
+ }
9
29
  if (action === 'chrome-extension' || action === 'chrome') {
10
30
  const status = await codexChromeExtensionStatus();
11
31
  if (flag(args, '--json')) {
@@ -32,7 +52,9 @@ export async function run(_command, args = []) {
32
52
  return;
33
53
  }
34
54
  if (action === 'check' || action === 'status') {
35
- const status = await codexAppIntegrationStatus();
55
+ const status = await codexAppIntegrationStatus({
56
+ autoInstallProductDesign: flag(args, '--install-product-design') || flag(args, '--auto-install-product-design')
57
+ });
36
58
  if (flag(args, '--json')) {
37
59
  printJson(status);
38
60
  if (!status.ok)
@@ -44,7 +66,7 @@ export async function run(_command, args = []) {
44
66
  process.exitCode = 1;
45
67
  return;
46
68
  }
47
- console.error('Usage: sks codex-app check|status|chrome-extension|pat status|remote-control [--json]');
69
+ console.error('Usage: sks codex-app check|status|product-design [--check-only]|ensure-product-design|chrome-extension|pat status|remote-control [--json]');
48
70
  process.exitCode = 1;
49
71
  }
50
72
  //# sourceMappingURL=codex-app.js.map
@@ -18,10 +18,23 @@ import { appendMigrationEvents, hashConfigText } from '../core/migration/migrati
18
18
  import { repairCodexAppFastUi } from '../core/codex-app/codex-app-fast-ui-repair.js';
19
19
  import { resolveProviderContext } from '../core/provider/provider-context.js';
20
20
  import { readLocalModelConfig } from '../core/agents/ollama-worker-config.js';
21
+ import { runSksUpdateNow } from '../core/update-check.js';
21
22
  export async function run(_command, args = []) {
23
+ const doctorFix = flag(args, '--fix');
22
24
  let setupRepair = null;
25
+ const sksUpdate = doctorFix
26
+ ? await runSksUpdateNow({
27
+ timeoutMs: 10 * 60 * 1000,
28
+ maxOutputBytes: 128 * 1024
29
+ }).catch((err) => ({
30
+ schema: 'sks.update-now.v1',
31
+ ok: false,
32
+ status: 'failed',
33
+ error: err?.message || String(err)
34
+ }))
35
+ : null;
23
36
  let migrationPreFix = null;
24
- if (flag(args, '--fix')) {
37
+ if (doctorFix) {
25
38
  // Snapshot config content before ANY mutation so the migration journal can
26
39
  // record real before/after hashes for the whole --fix transaction.
27
40
  migrationPreFix = await captureCodexConfigSnapshot();
@@ -97,7 +110,6 @@ export async function run(_command, args = []) {
97
110
  model_provider: null
98
111
  }
99
112
  }));
100
- const doctorFix = flag(args, '--fix');
101
113
  const explicitCodexAppUiRepair = flag(args, '--repair-codex-app-ui') || flag(args, '--yes');
102
114
  const codexAppUiPlan = await repairCodexAppFastUi(root, {
103
115
  apply: false,
@@ -165,7 +177,7 @@ export async function run(_command, args = []) {
165
177
  const zellijReadiness = buildZellijReadiness(root, zellij, ready);
166
178
  const result = {
167
179
  schema: 'sks.doctor-status.v1',
168
- ok: ready.ready,
180
+ ok: ready.ready && (!sksUpdate || sksUpdate.ok !== false),
169
181
  root,
170
182
  node: { ok: Number(process.versions.node.split('.')[0]) >= 20, version: process.version },
171
183
  codex,
@@ -189,7 +201,7 @@ export async function run(_command, args = []) {
189
201
  ready,
190
202
  sneakoscope: { ok: await exists(`${root}/.sneakoscope`) },
191
203
  package: { bytes: pkgBytes, human: formatBytes(pkgBytes) },
192
- repair: { setup: setupRepair, codex_config: configRepair, migration_journal: migrationJournal, global_sks_installs: globalSksInstallCleanup }
204
+ repair: { sks_update: sksUpdate, setup: setupRepair, codex_config: configRepair, migration_journal: migrationJournal, global_sks_installs: globalSksInstallCleanup }
193
205
  };
194
206
  if (flag(args, '--json')) {
195
207
  printJson(result);
@@ -265,6 +277,9 @@ export async function run(_command, args = []) {
265
277
  if (migrationJournal?.journal_path) {
266
278
  console.log(`Migration journal: ${migrationJournal.journal_path} (${migrationJournal.event_count} events, ${migrationJournal.mutations_without_rollback} without rollback)`);
267
279
  }
280
+ if (sksUpdate) {
281
+ console.log(`SKS update: ${sksUpdate.status}${sksUpdate.latest ? ` latest ${sksUpdate.latest}` : ''}${sksUpdate.error ? ` (${sksUpdate.error})` : ''}`);
282
+ }
268
283
  if (globalSksInstallCleanup) {
269
284
  console.log(`Global SKS installs: kept ${globalSksInstallCleanup.kept?.length ?? 0}, removed ${globalSksInstallCleanup.removed?.filter((entry) => entry.ok).length ?? 0}, source repo exempt ${globalSksInstallCleanup.candidates?.filter((entry) => entry.source_repo_exempt).length ?? 0}`);
270
285
  }
@@ -1,9 +1,9 @@
1
1
  import { madHighCommand } from '../core/commands/mad-sks-command.js';
2
- import { ensureMadLaunchDependencies, formatMadLaunchDependencyAction, maybePromptCodexUpdateForLaunch, maybePromptCodexLbSetupForLaunch } from '../cli/install-helpers.js';
2
+ import { ensureMadLaunchDependencies, formatMadLaunchDependencyAction, maybePromptCodexUpdateForLaunch, maybePromptCodexLbSetupForLaunch, maybePromptSksUpdateForLaunch } from '../cli/install-helpers.js';
3
3
  import { PACKAGE_VERSION } from '../core/fsx.js';
4
4
  export async function run(_command, args = []) {
5
5
  return madHighCommand(['--mad-sks', ...args], {
6
- maybePromptSksUpdateForLaunch: async () => ({ status: 'skipped' }),
6
+ maybePromptSksUpdateForLaunch,
7
7
  maybePromptCodexUpdateForLaunch,
8
8
  ensureMadLaunchDependencies,
9
9
  printDepsInstallAction: (action) => console.error(formatMadLaunchDependencyAction(action)),
@@ -52,6 +52,10 @@ import { CODEX_AGENT_WORKER_RESULT_SCHEMA_ID, codexAgentWorkerResultSchema } fro
52
52
  import { resolveLocalCollaborationPolicy, localCollaborationParticipated } from '../local-llm/local-collaboration-policy.js';
53
53
  import { runFinalGptReviewStage } from '../pipeline/final-gpt-review-stage.js';
54
54
  import { selectFinalGptPatchSource } from '../pipeline/final-gpt-patch-stage.js';
55
+ import { allocateWorkerWorktree } from '../git/git-worktree-manager.js';
56
+ import { exportGitWorktreeDiff } from '../git/git-worktree-diff.js';
57
+ import { buildGitWorktreePatchEnvelope } from '../git/git-worktree-patch-envelope.js';
58
+ import { cleanupGitWorktree } from '../git/git-worktree-cleanup.js';
55
59
  export async function runNativeAgentOrchestrator(opts = {}) {
56
60
  const root = path.resolve(opts.root || process.cwd());
57
61
  const prompt = String(opts.prompt || 'Native agent run');
@@ -117,6 +121,7 @@ export async function runNativeAgentOrchestrator(opts = {}) {
117
121
  const strategyCompiled = compileStrategy({
118
122
  prompt,
119
123
  route,
124
+ ...(writeCapable ? {} : { writeTargets: [] }),
120
125
  agentCount: roster.agent_count,
121
126
  visualRequired
122
127
  });
@@ -235,12 +240,13 @@ export async function runNativeAgentOrchestrator(opts = {}) {
235
240
  rate_limit_delay_ms: backend === 'codex-sdk' ? 250 : 0,
236
241
  resource_pressure_warnings: roster.agent_count > roster.concurrency ? ['agents_exceed_concurrency_batches'] : []
237
242
  });
243
+ const effectiveWriteMode = writeCapable ? opts.writeMode || 'off' : 'off';
238
244
  const parallelWritePolicy = {
239
245
  schema: 'sks.agent-parallel-write-policy.v1',
240
246
  generated_at: nowIso(),
241
247
  route,
242
248
  route_command: routeCommand,
243
- write_mode: opts.writeMode || 'off',
249
+ write_mode: effectiveWriteMode,
244
250
  apply_patches: opts.applyPatches === true,
245
251
  dry_run_patches: opts.dryRunPatches === true,
246
252
  max_write_agents: Number(opts.maxWriteAgents || 0),
@@ -251,6 +257,21 @@ export async function runNativeAgentOrchestrator(opts = {}) {
251
257
  strategy_gate: strategyRef
252
258
  };
253
259
  await writeJsonAtomic(path.join(ledgerRoot, 'agent-parallel-write-policy.json'), parallelWritePolicy);
260
+ const gitWorktreePolicy = opts.gitWorktreePolicy || null;
261
+ const gitWorktreeRuntime = {
262
+ schema: 'sks.agent-git-worktree-runtime.v1',
263
+ generated_at: nowIso(),
264
+ ok: true,
265
+ mode: gitWorktreePolicy?.mode || 'patch-envelope-only',
266
+ required: gitWorktreePolicy?.required === true,
267
+ main_repo_root: gitWorktreePolicy?.main_repo_root || root,
268
+ worktree_root: gitWorktreePolicy?.worktree_root || null,
269
+ allocations: [],
270
+ diffs: [],
271
+ cleanup: [],
272
+ blockers: []
273
+ };
274
+ await writeJsonAtomic(path.join(ledgerRoot, 'agent-git-worktree-runtime.json'), gitWorktreeRuntime);
254
275
  const nativeCliSwarm = createNativeCliSessionSwarmRecorder(ledgerRoot, {
255
276
  missionId,
256
277
  requestedAgents: Number(opts.agents || roster.agent_count || targetActiveSlots),
@@ -278,6 +299,17 @@ export async function runNativeAgentOrchestrator(opts = {}) {
278
299
  goalModeRef,
279
300
  launchSession: async ({ agent, workItem }) => {
280
301
  const slice = workItem.slice || { id: workItem.id, description: workItem.description || prompt };
302
+ const workerWorktree = await prepareWorkerGitWorktree({
303
+ root,
304
+ ledgerRoot,
305
+ missionId,
306
+ agent,
307
+ slice,
308
+ policy: gitWorktreePolicy,
309
+ runtime: gitWorktreeRuntime
310
+ });
311
+ const runtimeAgent = workerWorktree ? { ...agent, worktree: workerWorktree.context } : agent;
312
+ const runtimeSlice = workerWorktree ? { ...slice, worktree: workerWorktree.context } : slice;
281
313
  await openAgentSession(ledgerRoot, agent);
282
314
  await heartbeatAgentSession(ledgerRoot, agent);
283
315
  await appendAgentCodexCockpitHookEvent(dir, {
@@ -301,10 +333,21 @@ export async function runNativeAgentOrchestrator(opts = {}) {
301
333
  generationIndex: agent.generation_index,
302
334
  requireGeneration: true
303
335
  });
304
- const backendOpts = { ...opts, missionId, agentRoot: ledgerRoot, cwd: root, route, prompt, fastMode: fastModePolicy.fast_mode, serviceTier: fastModePolicy.service_tier };
336
+ const backendOpts = { ...opts, missionId, agentRoot: ledgerRoot, cwd: workerWorktree?.context.path || root, route, prompt, fastMode: fastModePolicy.fast_mode, serviceTier: fastModePolicy.service_tier, ...(workerWorktree ? { worktree: workerWorktree.context } : {}) };
305
337
  const result = opts.nativeCliSwarm === false
306
- ? await runAgentByBackend(backend, agent, slice, backendOpts)
307
- : await nativeCliSwarm.launchWorker({ agent, slice, opts: backendOpts });
338
+ ? await runAgentByBackend(backend, runtimeAgent, runtimeSlice, backendOpts)
339
+ : await nativeCliSwarm.launchWorker({ agent: runtimeAgent, slice: runtimeSlice, opts: backendOpts });
340
+ if (workerWorktree)
341
+ await finalizeWorkerGitWorktree({
342
+ root,
343
+ ledgerRoot,
344
+ missionId,
345
+ agent: runtimeAgent,
346
+ slice: runtimeSlice,
347
+ result,
348
+ workerWorktree,
349
+ runtime: gitWorktreeRuntime
350
+ });
308
351
  const terminalClose = await closeAgentTerminalSession(ledgerRoot, agent, {
309
352
  exitCode: result.status === 'done' ? 0 : 1,
310
353
  status: result.status,
@@ -442,6 +485,7 @@ export async function runNativeAgentOrchestrator(opts = {}) {
442
485
  ...(nativeCliSessionProof.ok ? [] : nativeCliSessionProof.blockers),
443
486
  ...(noSubagentScalingPolicy.ok ? [] : noSubagentScalingPolicy.blockers),
444
487
  ...(fastModePropagation.ok ? [] : fastModePropagation.blockers),
488
+ ...(gitWorktreeRuntime.required === true && gitWorktreeRuntime.ok === false ? gitWorktreeRuntime.blockers || ['git_worktree_runtime_not_ok'] : []),
445
489
  ...(localParticipated && gptFinalArbiter?.ok !== true ? gptFinalArbiter?.blockers || ['gpt_final_arbiter_not_ok'] : []),
446
490
  ...(localParticipated && finalGptPatchStage?.ok === false ? finalGptPatchStage.blockers || ['final_gpt_patch_stage_not_ok'] : []),
447
491
  ...(patchSwarm.ok ? [] : patchSwarm.blockers),
@@ -478,6 +522,7 @@ export async function runNativeAgentOrchestrator(opts = {}) {
478
522
  noSubagentScalingPolicy,
479
523
  fastModePolicy,
480
524
  fastModePropagation,
525
+ gitWorktreeRuntime,
481
526
  triwikiContext,
482
527
  selectedCoreSkill,
483
528
  localCollaborationPolicy,
@@ -522,6 +567,7 @@ export async function runNativeAgentOrchestrator(opts = {}) {
522
567
  no_subagent_scaling_policy: noSubagentScalingPolicy,
523
568
  fast_mode_policy: fastModePolicy,
524
569
  fast_mode_propagation: fastModePropagation,
570
+ git_worktree_runtime: gitWorktreeRuntime,
525
571
  local_collaboration_policy: localCollaborationPolicy,
526
572
  gpt_final_arbiter: gptFinalArbiter,
527
573
  final_gpt_patch_stage: finalGptPatchStage,
@@ -546,6 +592,97 @@ function withFinalGptPatchEnvelopes(results, patchEnvelopes = []) {
546
592
  next[0] = { ...next[0], patch_envelopes: patchEnvelopes };
547
593
  return next;
548
594
  }
595
+ async function prepareWorkerGitWorktree(input) {
596
+ if (input.policy?.mode !== 'git-worktree')
597
+ return null;
598
+ const sliceHasWritePaths = Array.isArray(input.slice.write_paths) && input.slice.write_paths.length > 0;
599
+ const agentWriteCapable = input.agent.write_allowed === true || /write|lease|required/i.test(String(input.agent.write_policy || ''));
600
+ if (!sliceHasWritePaths && !agentWriteCapable)
601
+ return null;
602
+ const generationIndex = Math.max(1, Math.floor(Number(input.agent.generation_index || 1)));
603
+ const allocation = await allocateWorkerWorktree({
604
+ repoRoot: input.policy.main_repo_root || input.root,
605
+ missionId: input.missionId,
606
+ workerId: String(input.agent.id || input.slice.id || 'worker'),
607
+ slotId: String(input.agent.slot_id || input.agent.id || 'slot-001'),
608
+ generationIndex
609
+ });
610
+ const artifactDir = path.join(input.ledgerRoot, input.agent.session_artifact_dir || path.join('sessions', input.agent.id || input.slice.id || 'worker'), 'worker');
611
+ await writeJsonAtomic(path.join(artifactDir, 'git-worktree-allocation.json'), allocation);
612
+ input.runtime.allocations.push({
613
+ agent_id: input.agent.id,
614
+ slice_id: input.slice.id,
615
+ ok: allocation.ok,
616
+ worktree_path: allocation.worktree_path,
617
+ branch: allocation.branch,
618
+ blockers: allocation.blockers
619
+ });
620
+ input.runtime.blockers.push(...allocation.blockers);
621
+ input.runtime.ok = input.runtime.blockers.length === 0;
622
+ await writeJsonAtomic(path.join(input.ledgerRoot, 'agent-git-worktree-runtime.json'), input.runtime);
623
+ if (!allocation.ok)
624
+ return null;
625
+ return {
626
+ allocation,
627
+ artifactDir,
628
+ context: {
629
+ id: `${allocation.slot_id}-gen-${allocation.generation_index}`,
630
+ path: allocation.worktree_path,
631
+ branch: allocation.branch,
632
+ main_repo_root: allocation.main_repo_root
633
+ }
634
+ };
635
+ }
636
+ async function finalizeWorkerGitWorktree(input) {
637
+ const allocation = input.workerWorktree.allocation;
638
+ const diff = await exportGitWorktreeDiff({
639
+ mainRepoRoot: allocation.main_repo_root || input.root,
640
+ worktreePath: allocation.worktree_path,
641
+ missionId: input.missionId,
642
+ workerId: String(input.agent.id || input.slice.id || 'worker')
643
+ });
644
+ await writeJsonAtomic(path.join(input.workerWorktree.artifactDir, 'git-worktree-diff.json'), diff);
645
+ input.runtime.diffs.push({
646
+ agent_id: input.agent.id,
647
+ slice_id: input.slice.id,
648
+ ok: diff.ok,
649
+ clean: diff.clean,
650
+ changed_files: diff.changed_files,
651
+ diff_bytes: diff.diff_bytes,
652
+ blockers: diff.blockers
653
+ });
654
+ if (!diff.clean && diff.ok) {
655
+ const envelope = buildGitWorktreePatchEnvelope({
656
+ diff,
657
+ agentId: String(input.agent.id || 'agent'),
658
+ sessionId: String(input.agent.session_id || ''),
659
+ slotId: String(input.agent.slot_id || ''),
660
+ generationIndex: Math.max(1, Math.floor(Number(input.agent.generation_index || 1)))
661
+ });
662
+ input.result.patch_envelopes = [...(Array.isArray(input.result.patch_envelopes) ? input.result.patch_envelopes : []), envelope];
663
+ input.result.changed_files = [...new Set([...(input.result.changed_files || []), ...diff.changed_files])];
664
+ input.result.artifacts = [...new Set([...(input.result.artifacts || []), path.relative(input.ledgerRoot, path.join(input.workerWorktree.artifactDir, 'git-worktree-diff.json'))])];
665
+ input.result.git_worktree_diff = diff;
666
+ }
667
+ const cleanup = await cleanupGitWorktree({
668
+ repoRoot: allocation.main_repo_root || input.root,
669
+ worktreePath: allocation.worktree_path,
670
+ branch: allocation.branch,
671
+ deleteBranch: diff.clean
672
+ });
673
+ await writeJsonAtomic(path.join(input.workerWorktree.artifactDir, 'git-worktree-cleanup.json'), cleanup);
674
+ input.runtime.cleanup.push({
675
+ agent_id: input.agent.id,
676
+ slice_id: input.slice.id,
677
+ action: cleanup.action,
678
+ clean: cleanup.clean,
679
+ retention_lock_path: cleanup.retention_lock_path,
680
+ blockers: cleanup.blockers
681
+ });
682
+ input.runtime.blockers.push(...diff.blockers, ...cleanup.blockers);
683
+ input.runtime.ok = input.runtime.blockers.length === 0;
684
+ await writeJsonAtomic(path.join(input.ledgerRoot, 'agent-git-worktree-runtime.json'), input.runtime);
685
+ }
549
686
  async function legacyCodexExecBlockedRun(input) {
550
687
  const blockers = ['legacy_codex_exec_runtime_removed'];
551
688
  const ledgerRoot = path.join(input.dir, 'agents');
@@ -779,7 +916,7 @@ async function runAgentPatchSwarmRuntime(root, ledgerRoot, input) {
779
916
  await queueStore.persistSnapshot();
780
917
  const journalSummary = await queueStore.journal.writeSummary();
781
918
  const proof = buildAgentPatchProof({ ...proofInput, transactionJournal: journalSummary });
782
- const zeroPatchBlockers = input.writeCapable && input.parallelWritePolicy?.write_mode === 'parallel' && pendingEntries.length === 0 && input.parallelWritePolicy?.dry_run_patches !== true
919
+ const zeroPatchBlockers = input.writeCapable && input.parallelWritePolicy?.write_mode === 'parallel' && pendingEntries.length === 0 && input.dryRun !== true && input.parallelWritePolicy?.dry_run_patches !== true
783
920
  ? ['write_mode_parallel_zero_patch_envelopes']
784
921
  : [];
785
922
  const blockers = [
@@ -870,6 +1007,8 @@ function normalizeVisualLaneCount(value, fallback, maxAgentCount) {
870
1007
  return Math.max(1, Math.min(maxAgentCount, Math.floor(raw)));
871
1008
  }
872
1009
  function isWriteCapableRun(opts) {
1010
+ if (opts.readonly === true)
1011
+ return false;
873
1012
  return opts.applyPatches === true || opts.dryRunPatches === true || (opts.writeMode !== undefined && opts.writeMode !== 'off');
874
1013
  }
875
1014
  function defaultRouteCommand(route) {
@@ -991,6 +1130,7 @@ async function runAgentByBackend(backend, agent, slice, opts) {
991
1130
  ...sdkWorkerResult,
992
1131
  backend: 'codex-sdk',
993
1132
  patch_envelopes: patchEnvelopes,
1133
+ ...(patchEnvelopes.length ? {} : { no_patch_reason: buildDirectNoPatchReason(slice, opts) }),
994
1134
  codex_child_report: sdkReport,
995
1135
  codex_sdk_thread: sdkReport,
996
1136
  model_authored_patch_envelopes: patchEnvelopes.length > 0,
@@ -1027,10 +1167,24 @@ function buildDirectSdkWorkerPrompt(slice) {
1027
1167
  '',
1028
1168
  write.length
1029
1169
  ? `Write-capable slice. Return JSON matching ${CODEX_AGENT_WORKER_RESULT_SCHEMA_ID}; include patch_envelopes for write_paths=${JSON.stringify(write)}. Each patch envelope must include schema, source "model_authored", agent_id, session_id, slot_id, generation_index, task_slice_id, lease_id, allowed_paths, operations, and rationale. Each operation must include op, path, search, replace, content, and diff; use empty strings for operation fields that do not apply.`
1030
- : `Read-only slice. Return JSON matching ${CODEX_AGENT_WORKER_RESULT_SCHEMA_ID}.`,
1170
+ : `Read-only slice. Return JSON matching ${CODEX_AGENT_WORKER_RESULT_SCHEMA_ID}; do not report pre-existing repository dirtiness as changed_files.`,
1031
1171
  'Required JSON fields: status, summary, findings, changed_files, patch_envelopes, verification, rollback_notes, blockers.'
1032
1172
  ].join('\n');
1033
1173
  }
1174
+ function buildDirectNoPatchReason(slice, opts) {
1175
+ const writePathCount = sdkWritePaths(slice, opts).length;
1176
+ return {
1177
+ schema: 'sks.native-cli-worker-no-patch-reason.v1',
1178
+ generated_at: nowIso(),
1179
+ ok: writePathCount === 0,
1180
+ reason: writePathCount ? 'write_capable_task_without_backend_patch_envelope' : 'read_only_or_no_write_paths',
1181
+ route_justification: writePathCount ? 'backend returned no patch envelopes for a write-capable task' : 'task has no write paths',
1182
+ read_only_or_noop_evidence: writePathCount === 0,
1183
+ task_slice_id: slice?.id || null,
1184
+ backend: 'codex-sdk',
1185
+ blockers: writePathCount ? ['write_capable_no_patch_envelope'] : []
1186
+ };
1187
+ }
1034
1188
  function sdkWritePaths(slice, opts) {
1035
1189
  return [
1036
1190
  ...(Array.isArray(slice?.write_paths) ? slice.write_paths : []),