sneakoscope 2.0.1 → 2.0.4

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 (74) hide show
  1. package/README.md +26 -5
  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 +28 -8
  8. package/dist/cli/command-registry.js +2 -0
  9. package/dist/commands/doctor.js +29 -3
  10. package/dist/core/agents/agent-command-surface.js +13 -3
  11. package/dist/core/agents/agent-orchestrator.js +92 -4
  12. package/dist/core/agents/agent-output-validator.js +2 -1
  13. package/dist/core/agents/agent-patch-proof.js +5 -0
  14. package/dist/core/agents/agent-patch-schema.js +2 -1
  15. package/dist/core/agents/agent-proof-evidence.js +26 -0
  16. package/dist/core/agents/agent-roster.js +1 -1
  17. package/dist/core/agents/agent-runner-ollama.js +411 -0
  18. package/dist/core/agents/agent-schema.js +1 -1
  19. package/dist/core/agents/intelligent-work-graph.js +45 -3
  20. package/dist/core/agents/native-cli-session-swarm.js +8 -1
  21. package/dist/core/agents/native-cli-worker.js +1 -1
  22. package/dist/core/agents/native-worker-backend-router.js +44 -2
  23. package/dist/core/agents/ollama-worker-config.js +118 -0
  24. package/dist/core/auto-review.js +39 -6
  25. package/dist/core/codex-app/codex-app-fast-ui-repair.js +42 -3
  26. package/dist/core/codex-control/codex-fake-sdk-adapter.js +20 -0
  27. package/dist/core/codex-control/codex-output-schemas.js +5 -1
  28. package/dist/core/codex-control/gpt-final-arbiter.js +160 -0
  29. package/dist/core/codex-control/gpt-final-context-compressor.js +17 -0
  30. package/dist/core/codex-control/gpt-final-proof-pack.js +120 -0
  31. package/dist/core/codex-control/gpt-final-review-schema.js +71 -0
  32. package/dist/core/commands/basic-cli.js +36 -1
  33. package/dist/core/commands/local-model-command.js +120 -0
  34. package/dist/core/commands/mad-sks-command.js +58 -9
  35. package/dist/core/commands/naruto-command.js +77 -5
  36. package/dist/core/commands/run-command.js +33 -1
  37. package/dist/core/commands/team-command.js +31 -2
  38. package/dist/core/doctor/doctor-readiness-matrix.js +19 -0
  39. package/dist/core/feature-fixtures.js +5 -0
  40. package/dist/core/fsx.js +1 -1
  41. package/dist/core/git-simple.js +143 -4
  42. package/dist/core/hooks-runtime.js +1 -1
  43. package/dist/core/init.js +2 -0
  44. package/dist/core/local-llm/local-collaboration-policy.js +93 -0
  45. package/dist/core/local-llm/local-llm-config.js +15 -0
  46. package/dist/core/pipeline/final-gpt-patch-stage.js +31 -0
  47. package/dist/core/pipeline/final-gpt-review-stage.js +5 -0
  48. package/dist/core/provider/provider-context.js +72 -9
  49. package/dist/core/retention.js +11 -0
  50. package/dist/core/routes.js +21 -1
  51. package/dist/core/safety/mutation-guard.js +2 -0
  52. package/dist/core/team-live.js +7 -1
  53. package/dist/core/update-check.js +215 -25
  54. package/dist/core/verification/verification-worker-pool.js +12 -0
  55. package/dist/core/version.js +1 -1
  56. package/dist/core/zellij/zellij-worker-pane-manager.js +19 -2
  57. package/dist/scripts/agent-ast-aware-work-graph-check.js +1 -1
  58. package/dist/scripts/codex-sdk-team-naruto-agent-pipeline-check.js +2 -1
  59. package/dist/scripts/doctor-fixes-codex-app-fast-ui-check.js +12 -2
  60. package/dist/scripts/gpt-final-arbiter-check.js +63 -0
  61. package/dist/scripts/gpt-final-arbiter-performance-check.js +36 -0
  62. package/dist/scripts/local-collab-gpt-final-availability-check.js +58 -0
  63. package/dist/scripts/local-collab-no-local-only-final-check.js +27 -0
  64. package/dist/scripts/local-collab-policy-check.js +17 -0
  65. package/dist/scripts/mad-sks-app-ui-no-mutation-check.js +92 -0
  66. package/dist/scripts/mad-sks-zellij-default-pane-worker-check.js +37 -0
  67. package/dist/scripts/mad-sks-zellij-launch-check.js +2 -1
  68. package/dist/scripts/provider-context-config-toml-check.js +63 -0
  69. package/dist/scripts/release-gate-existence-audit.js +4 -0
  70. package/dist/scripts/runtime-no-mjs-scripts-check.js +3 -2
  71. package/dist/scripts/zellij-worker-pane-manager-check.js +3 -0
  72. package/dist/scripts/zellij-worker-pane-manager-single-owner-check.js +39 -0
  73. package/package.json +13 -4
  74. package/schemas/local-llm/local-collaboration-policy.schema.json +57 -0
@@ -0,0 +1,93 @@
1
+ import { nowIso } from '../fsx.js';
2
+ export const LOCAL_COLLABORATION_POLICY_SCHEMA = 'sks.local-collaboration-policy.v1';
3
+ export const LOCAL_COLLABORATION_FINAL_GATE_SCHEMA = 'sks.local-collaboration-final-gate.v1';
4
+ export const DEFAULT_LOCAL_COLLABORATION_MODE = 'local-parallel-gpt-final';
5
+ export const LOCAL_COLLABORATION_MODES = [
6
+ 'disabled',
7
+ 'local-draft-gpt-final',
8
+ 'local-worker-gpt-orchestrator',
9
+ 'local-parallel-gpt-final',
10
+ 'local-only-draft'
11
+ ];
12
+ export function resolveLocalCollaborationPolicy(input = {}) {
13
+ const env = input.env || process.env;
14
+ const requested = firstText(input.mode, env.SKS_LOCAL_COLLAB_MODE, DEFAULT_LOCAL_COLLABORATION_MODE);
15
+ const mode = normalizeLocalCollaborationMode(requested);
16
+ const invalid = mode ? [] : [`invalid_local_collaboration_mode:${requested}`];
17
+ const resolvedMode = mode || DEFAULT_LOCAL_COLLABORATION_MODE;
18
+ return {
19
+ schema: LOCAL_COLLABORATION_POLICY_SCHEMA,
20
+ generated_at: nowIso(),
21
+ mode: resolvedMode,
22
+ default_mode: DEFAULT_LOCAL_COLLABORATION_MODE,
23
+ local_llm_role: resolvedMode === 'disabled' ? 'disabled' : 'draft_worker',
24
+ gpt_final_required: resolvedMode !== 'disabled' && resolvedMode !== 'local-only-draft',
25
+ gpt_final_backend_must_be_remote: resolvedMode !== 'disabled',
26
+ local_only_draft: resolvedMode === 'local-only-draft',
27
+ final_accepted_statuses: ['approved', 'modified'],
28
+ final_patch_source_when_enabled: 'gpt_final_arbiter',
29
+ blockers: [
30
+ ...invalid,
31
+ ...(resolvedMode === 'local-only-draft' ? ['needs_gpt_final_review'] : [])
32
+ ]
33
+ };
34
+ }
35
+ export function evaluateLocalCollaborationFinalGate(input = {}) {
36
+ const policy = input.policy || resolveLocalCollaborationPolicy(input.mode === undefined ? {} : { mode: input.mode });
37
+ const localParticipated = input.localParticipated !== false && policy.mode !== 'disabled';
38
+ const status = normalizeGptFinalStatus(input.gptFinalStatus);
39
+ const requiresGptFinal = policy.gpt_final_required && localParticipated;
40
+ const gptBackend = String(input.gptFinalBackend || '');
41
+ const remoteBackendOk = !gptBackend || !isLocalBackendName(gptBackend);
42
+ const blockers = [
43
+ ...policy.blockers,
44
+ ...(requiresGptFinal && input.gptFinalAvailable === false ? ['gpt_final_arbiter_unavailable'] : []),
45
+ ...(requiresGptFinal && !status ? ['gpt_final_arbiter_missing'] : []),
46
+ ...(requiresGptFinal && status && !policy.final_accepted_statuses.includes(status) ? [`gpt_final_status_not_accepted:${status}`] : []),
47
+ ...(requiresGptFinal && !remoteBackendOk ? ['gpt_final_backend_must_not_be_local_llm'] : []),
48
+ ...(policy.local_only_draft && input.applyPatches === true ? ['local_only_draft_apply_blocked'] : [])
49
+ ];
50
+ const accepted = blockers.length === 0 && (!requiresGptFinal || status === 'approved' || status === 'modified');
51
+ return {
52
+ schema: LOCAL_COLLABORATION_FINAL_GATE_SCHEMA,
53
+ generated_at: nowIso(),
54
+ ok: accepted,
55
+ mode: policy.mode,
56
+ local_participated: localParticipated,
57
+ gpt_final_required: requiresGptFinal,
58
+ gpt_final_status: status,
59
+ gpt_final_backend: gptBackend || null,
60
+ final_status: accepted ? 'accepted' : policy.local_only_draft ? 'draft_only' : 'blocked',
61
+ apply_allowed: accepted && policy.local_only_draft !== true,
62
+ release_proof_allowed: accepted,
63
+ final_patch_source: accepted && policy.mode !== 'disabled' ? policy.final_patch_source_when_enabled : 'not_applicable',
64
+ blockers
65
+ };
66
+ }
67
+ export function localCollaborationParticipated(results = []) {
68
+ return results.some((result) => {
69
+ const backend = String(result?.backend_router_report?.selected_backend || result?.backend || '').toLowerCase();
70
+ return backend === 'ollama' || backend === 'local-llm' || backend === 'local_llm';
71
+ });
72
+ }
73
+ export function normalizeLocalCollaborationMode(value) {
74
+ const text = String(value ?? '').trim();
75
+ return LOCAL_COLLABORATION_MODES.includes(text) ? text : null;
76
+ }
77
+ export function normalizeGptFinalStatus(value) {
78
+ const text = String(value ?? '').trim();
79
+ return text === 'approved' || text === 'modified' || text === 'rejected' || text === 'needs_more_work' ? text : null;
80
+ }
81
+ function firstText(...values) {
82
+ for (const value of values) {
83
+ const text = String(value ?? '').trim();
84
+ if (text)
85
+ return text;
86
+ }
87
+ return '';
88
+ }
89
+ function isLocalBackendName(value) {
90
+ const text = value.toLowerCase();
91
+ return text === 'ollama' || text === 'local-llm' || text === 'local_llm';
92
+ }
93
+ //# sourceMappingURL=local-collaboration-policy.js.map
@@ -0,0 +1,15 @@
1
+ import { resolveOllamaWorkerConfig } from '../agents/ollama-worker-config.js';
2
+ export async function resolveLocalLlmConfig(input = {}) {
3
+ const config = await resolveOllamaWorkerConfig(input);
4
+ return {
5
+ schema: 'sks.local-llm-config.v1',
6
+ ok: config.ok,
7
+ enabled: config.enabled,
8
+ provider: config.provider,
9
+ model: config.model,
10
+ base_url: config.base_url,
11
+ worker_only: true,
12
+ blockers: config.blockers
13
+ };
14
+ }
15
+ //# sourceMappingURL=local-llm-config.js.map
@@ -0,0 +1,31 @@
1
+ export function selectFinalGptPatchSource(gptFinal, localPatchEnvelopes = []) {
2
+ const status = String(gptFinal?.result?.status || gptFinal?.status || '');
3
+ if (status === 'modified') {
4
+ return {
5
+ schema: 'sks.final-gpt-patch-stage.v1',
6
+ ok: true,
7
+ final_patch_source: 'gpt_final_arbiter',
8
+ patch_envelopes: Array.isArray(gptFinal?.result?.modified_patch_envelopes) ? gptFinal.result.modified_patch_envelopes : [],
9
+ blockers: []
10
+ };
11
+ }
12
+ if (status === 'approved') {
13
+ return {
14
+ schema: 'sks.final-gpt-patch-stage.v1',
15
+ ok: true,
16
+ final_patch_source: 'gpt_final_arbiter',
17
+ patch_envelopes: Array.isArray(gptFinal?.result?.accepted_patch_envelopes) && gptFinal.result.accepted_patch_envelopes.length
18
+ ? gptFinal.result.accepted_patch_envelopes
19
+ : localPatchEnvelopes,
20
+ blockers: []
21
+ };
22
+ }
23
+ return {
24
+ schema: 'sks.final-gpt-patch-stage.v1',
25
+ ok: false,
26
+ final_patch_source: 'blocked',
27
+ patch_envelopes: [],
28
+ blockers: ['gpt_final_not_approved']
29
+ };
30
+ }
31
+ //# sourceMappingURL=final-gpt-patch-stage.js.map
@@ -0,0 +1,5 @@
1
+ import { runGptFinalArbiter } from '../codex-control/gpt-final-arbiter.js';
2
+ export async function runFinalGptReviewStage(input, opts = {}) {
3
+ return runGptFinalArbiter(input, opts);
4
+ }
5
+ //# sourceMappingURL=final-gpt-review-stage.js.map
@@ -1,15 +1,22 @@
1
1
  import path from 'node:path';
2
- import { exists, nowIso, readJson, writeJsonAtomic } from '../fsx.js';
2
+ import { exists, nowIso, readJson, readText, writeJsonAtomic } from '../fsx.js';
3
3
  export const PROVIDER_CONTEXT_SCHEMA = 'sks.provider-context.v1';
4
4
  export async function resolveProviderContext(input = {}) {
5
5
  const env = input.env || process.env;
6
6
  const root = path.resolve(input.root || process.cwd());
7
7
  const codexHome = path.resolve(String(input.codexHome || env.CODEX_HOME || path.join(env.HOME || '', '.codex')));
8
+ const configText = await readText(path.join(codexHome, 'config.toml'), '').catch(() => '');
9
+ const configModelProvider = readTopLevelTomlString(configText, 'model_provider');
10
+ const codexLbProviderBlockPresent = hasCodexLbProviderBlock(configText);
11
+ const codexLbEnvKey = codexLbProviderEnvKey(configText) || (codexLbProviderBlockPresent ? 'CODEX_LB_API_KEY' : null);
12
+ const codexLbRequiresOpenAiAuth = codexLbProviderRequiresOpenAiAuth(configText);
13
+ const codexLbProviderValid = codexLbProviderBlockPresent && (codexLbRequiresOpenAiAuth === false || codexLbRequiresOpenAiAuth == null);
8
14
  const openaiKey = Boolean(String(env.OPENAI_API_KEY || '').trim());
9
- const lbKey = Boolean(String(env.CODEX_LB_API_KEY || '').trim());
15
+ const lbKey = Boolean(String((codexLbEnvKey ? env[codexLbEnvKey] : env.CODEX_LB_API_KEY) || env.CODEX_LB_API_KEY || '').trim());
10
16
  const envProvider = String(env.SKS_MODEL_PROVIDER || env.CODEX_MODEL_PROVIDER || env.OPENAI_MODEL_PROVIDER || '').trim();
11
- const modelProvider = String(input.modelProvider || envProvider || '').trim() || null;
12
- const lbExplicit = modelProvider === 'codex-lb' || env.SKS_PROVIDER === 'codex-lb' || env.SKS_USE_CODEX_LB === '1';
17
+ const modelProvider = String(input.modelProvider || envProvider || configModelProvider || '').trim() || null;
18
+ const envLbExplicit = env.SKS_PROVIDER === 'codex-lb' || env.SKS_USE_CODEX_LB === '1';
19
+ const lbExplicit = modelProvider === 'codex-lb' || envLbExplicit;
13
20
  const auth = await readJson(path.join(codexHome, 'auth.json'), null).catch(() => null);
14
21
  const appAuthPresent = Boolean(auth) || await exists(path.join(codexHome, 'auth.json'));
15
22
  const conflict = (lbKey && openaiKey && !lbExplicit && !modelProvider) || (modelProvider === 'codex-lb' && !lbKey && openaiKey);
@@ -17,17 +24,23 @@ export async function resolveProviderContext(input = {}) {
17
24
  let authMode = 'unknown';
18
25
  let source = 'unknown';
19
26
  let confidence = 'low';
20
- if (lbExplicit && lbKey) {
27
+ if (envLbExplicit && lbKey) {
21
28
  provider = 'codex-lb';
22
29
  authMode = 'codex_lb_key';
23
30
  source = 'codex_lb';
24
31
  confidence = 'high';
25
32
  }
26
- else if (modelProvider === 'codex-lb') {
33
+ else if (modelProvider === 'codex-lb' && codexLbProviderValid && lbKey) {
27
34
  provider = 'codex-lb';
28
- authMode = lbKey ? 'codex_lb_key' : 'unknown';
35
+ authMode = 'codex_lb_key';
29
36
  source = 'config';
30
- confidence = lbKey ? 'medium' : 'low';
37
+ confidence = 'high';
38
+ }
39
+ else if (modelProvider === 'codex-lb' && codexLbProviderValid) {
40
+ provider = 'codex-lb';
41
+ authMode = 'unknown';
42
+ source = 'config';
43
+ confidence = 'low';
31
44
  }
32
45
  else if (openaiKey) {
33
46
  provider = 'openai';
@@ -43,6 +56,7 @@ export async function resolveProviderContext(input = {}) {
43
56
  }
44
57
  const warnings = [
45
58
  ...(conflict ? ['provider_conflict'] : []),
59
+ ...(modelProvider === 'codex-lb' && !codexLbProviderValid && !envLbExplicit ? ['codex_lb_provider_config_missing_or_invalid'] : []),
46
60
  ...(provider === 'codex-lb' && !lbKey ? ['codex_lb_selected_without_key'] : [])
47
61
  ];
48
62
  return {
@@ -61,7 +75,11 @@ export async function resolveProviderContext(input = {}) {
61
75
  codex_lb_key_present: lbKey,
62
76
  codex_lb_explicit: lbExplicit,
63
77
  codex_app_auth_present: appAuthPresent,
64
- model_provider: modelProvider
78
+ model_provider: modelProvider,
79
+ codex_lb_provider_block_present: codexLbProviderBlockPresent,
80
+ codex_lb_env_key: codexLbEnvKey,
81
+ codex_lb_requires_openai_auth: codexLbRequiresOpenAiAuth,
82
+ codex_lb_available: codexLbProviderValid && lbKey
65
83
  }
66
84
  };
67
85
  }
@@ -79,4 +97,49 @@ function normalizeServiceTier(value) {
79
97
  return 'standard';
80
98
  return 'unknown';
81
99
  }
100
+ export function readTopLevelTomlString(text, key) {
101
+ const lines = String(text || '').split(/\r?\n/);
102
+ for (const line of lines) {
103
+ if (/^\s*\[/.test(line))
104
+ break;
105
+ const match = line.match(new RegExp(`^\\s*${escapeRegExp(key)}\\s*=\\s*"([^"]*)"\\s*(?:#.*)?$`));
106
+ if (match?.[1] != null)
107
+ return match[1];
108
+ }
109
+ return null;
110
+ }
111
+ export function hasCodexLbProviderBlock(text) {
112
+ return codexLbProviderBody(text) != null;
113
+ }
114
+ export function codexLbProviderEnvKey(text) {
115
+ const body = codexLbProviderBody(text);
116
+ return body == null ? null : readTopLevelTomlString(body, 'env_key');
117
+ }
118
+ export function codexLbProviderRequiresOpenAiAuth(text) {
119
+ const body = codexLbProviderBody(text);
120
+ if (body == null)
121
+ return null;
122
+ const match = body.match(/^\s*requires_openai_auth\s*=\s*(true|false)\s*(?:#.*)?$/m);
123
+ return match?.[1] === 'true' ? true : match?.[1] === 'false' ? false : null;
124
+ }
125
+ function codexLbProviderBody(text) {
126
+ const lines = String(text || '').split(/\r?\n/);
127
+ const out = [];
128
+ let inTable = false;
129
+ for (const line of lines) {
130
+ const table = line.match(/^\s*\[([^\]]+)\]\s*(?:#.*)?$/)?.[1]?.trim();
131
+ if (table) {
132
+ if (inTable)
133
+ break;
134
+ inTable = table === 'model_providers.codex-lb' || table === 'model_providers."codex-lb"' || table === '"model_providers"."codex-lb"';
135
+ continue;
136
+ }
137
+ if (inTable)
138
+ out.push(line);
139
+ }
140
+ return inTable ? out.join('\n') : null;
141
+ }
142
+ function escapeRegExp(value) {
143
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
144
+ }
82
145
  //# sourceMappingURL=provider-context.js.map
@@ -52,6 +52,10 @@ const DISPOSABLE_MISSION_DIRS = Object.freeze([
52
52
  'research/cycles',
53
53
  'research/tmp'
54
54
  ]);
55
+ const DISPOSABLE_MISSION_FILES = Object.freeze([
56
+ 'agents/agent-intelligent-work-graph.json',
57
+ 'agents/agent-intelligent-work-graph-v2.json'
58
+ ]);
55
59
  const MISSION_CLOSE_GATES = Object.freeze([
56
60
  'team-gate.json',
57
61
  'reflection-gate.json',
@@ -235,6 +239,9 @@ async function compactClosedMissionWorkdirs(root, policy, dryRun, actions, opts
235
239
  const target = path.join(mission.path, rel);
236
240
  await removePath('remove_closed_mission_workdir', target, dryRun, actions, { mission: mission.id, rel, reason: 'closed_mission_disposable_workdir' });
237
241
  }
242
+ for (const rel of DISPOSABLE_MISSION_FILES) {
243
+ await removePath('remove_closed_mission_large_file', path.join(mission.path, rel), dryRun, actions, { mission: mission.id, rel, reason: 'closed_mission_disposable_large_file' });
244
+ }
238
245
  await pruneMissionDisposableLogs(mission, dryRun, actions);
239
246
  }
240
247
  }
@@ -272,6 +279,9 @@ async function compactOldMissionWithDurableArtifacts(mission, dryRun, actions, r
272
279
  for (const rel of DISPOSABLE_MISSION_DIRS) {
273
280
  await removePath('remove_old_mission_workdir', path.join(mission.path, rel), dryRun, actions, { mission: mission.id, rel, reason });
274
281
  }
282
+ for (const rel of DISPOSABLE_MISSION_FILES) {
283
+ await removePath('remove_old_mission_large_file', path.join(mission.path, rel), dryRun, actions, { mission: mission.id, rel, reason });
284
+ }
275
285
  await pruneMissionDisposableLogs(mission, dryRun, actions);
276
286
  }
277
287
  async function canCompactActiveMission(mission, opts = {}) {
@@ -545,6 +555,7 @@ export async function enforceRetention(root, opts = {}) {
545
555
  action_count: actions.length,
546
556
  protected_durable_context: DURABLE_RETENTION_CLASSES,
547
557
  disposable_mission_dirs: DISPOSABLE_MISSION_DIRS,
558
+ disposable_mission_files: DISPOSABLE_MISSION_FILES,
548
559
  prune_report_logs: Boolean(opts.pruneReportLogs || policy.prune_disposable_report_logs),
549
560
  completed_mission_id: opts.completedMissionId || null,
550
561
  actions
@@ -335,6 +335,22 @@ export const ROUTES = [
335
335
  cliEntrypoint: 'sks fast-mode on|off|status|clear [--json]',
336
336
  examples: ['$Fast-On', '$Fast-Off', '$Fast-Mode status']
337
337
  },
338
+ {
339
+ id: 'LocalModel',
340
+ command: '$with-local-llm-on',
341
+ mode: 'LOCAL_MODEL',
342
+ route: 'local Ollama worker toggle',
343
+ description: 'Turn the optional local Ollama worker backend on or off. Default off keeps SKS GPT-only; enabled mode lets eligible simple code/collection worker slices use Ollama while GPT/Codex owns strategy, design, review, verification, and integration.',
344
+ requiredSkills: ['with-local-llm-on', 'honest-mode'],
345
+ dollarAliases: ['$with-local-llm-off'],
346
+ appSkillAliases: ['with-local-llm-off'],
347
+ lifecycle: ['global_local_model_toggle', 'worker_only_policy_status', 'honest_mode'],
348
+ context7Policy: 'not_required',
349
+ reasoningPolicy: 'low',
350
+ stopGate: 'none',
351
+ cliEntrypoint: 'sks with-local-llm on|off|status|set-model [--json]',
352
+ examples: ['$with-local-llm-on', '$with-local-llm-off', 'sks with-local-llm status --json']
353
+ },
338
354
  {
339
355
  id: 'Team',
340
356
  command: '$Team',
@@ -534,7 +550,7 @@ export const ROUTES = [
534
550
  requiredSkills: ['mad-sks', 'db-safety-guard', 'pipeline-runner', 'context7-docs', REFLECTION_SKILL_NAME, 'honest-mode'],
535
551
  lifecycle: ['explicit_invocation', 'auto_sealed_permission_scope', 'scoped_permission_override', 'catastrophic_guard', 'permission_deactivation', 'post_route_reflection', 'honest_mode'],
536
552
  context7Policy: 'required',
537
- reasoningPolicy: 'high',
553
+ reasoningPolicy: 'xhigh',
538
554
  stopGate: 'mad-sks-gate.json',
539
555
  cliEntrypoint: 'Codex App prompt route only: $MAD-SKS <task>',
540
556
  examples: ['$MAD-SKS $Team target project maintenance with package/service/file and DB scopes', '$DB Supabase 점검 $MAD-SKS']
@@ -607,6 +623,7 @@ export const COMMAND_CATALOG = [
607
623
  { name: 'quickstart', usage: 'sks quickstart', description: 'Show the shortest safe setup and verification flow.' },
608
624
  { name: 'bootstrap', usage: 'sks bootstrap [--install-scope global|project] [--local-only] [--json]', description: 'Initialize the current project, install SKS Codex App files/skills, check Context7/Codex App/Zellij, and print ready true/false.' },
609
625
  { name: 'root', usage: 'sks root [--json]', description: 'Show whether SKS is using a project root or the per-user global SKS runtime root.' },
626
+ { name: 'update', usage: 'sks update check|now [--version <version>] [--json] [--dry-run]', description: 'Check for SKS updates or install the requested package version through npm global mode.' },
610
627
  { name: 'deps', usage: 'sks deps check [--json] [--yes]', description: 'Check Node/npm, Codex CLI, and Zellij readiness; pass --yes to repair missing Codex CLI/Zellij tooling when supported.' },
611
628
  { name: 'codex', usage: 'sks codex compatibility|version|doctor|schema [--json]', description: 'Check Codex CLI rust-v0.136.0 compatibility, installed version, 0.136 capabilities, inherited 0.135/0.134/0.133 behavior, and vendored hook schema snapshot freshness.' },
612
629
  { name: 'codex-app', usage: 'sks codex-app [check|chrome-extension|pat status|remote-control]', description: 'Check Codex App install, Codex Chrome Extension web verification readiness, PAT-safe status, first-party MCP/plugin readiness, and Codex CLI 0.130.0+ remote-control availability.' },
@@ -621,6 +638,7 @@ export const COMMAND_CATALOG = [
621
638
  { name: 'auto-review', usage: 'sks auto-review status|enable|start [--high] | sks --Auto-review --high', description: 'Enable Codex automatic approval review and launch SKS Zellij with the auto-review profile.' },
622
639
  { name: 'dollar-commands', usage: 'sks dollar-commands [--json]', description: 'List Codex App $ commands such as $DFix and $Team.' },
623
640
  { name: 'fast-mode', usage: 'sks fast-mode on|off|status|clear [--json]', description: 'Toggle the project-local Fast mode default used by $Fast-On, $Fast-Off, and native-agent routes.' },
641
+ { name: 'with-local-llm', usage: 'sks with-local-llm on|off|status|set-model [--json]', description: 'Toggle the optional local Ollama worker backend used by $with-local-llm-on/$with-local-llm-off and eligible simple worker slices.' },
624
642
  { name: 'commit', usage: 'sks commit [--message "msg"] [--json]', description: 'Stage current changes, summarize them, and create a simple git commit without the full SKS pipeline.' },
625
643
  { name: 'commit-and-push', usage: 'sks commit-and-push [--message "msg"] [--json]', description: 'Stage current changes, create a simple git commit, and push without the full SKS pipeline.' },
626
644
  { name: 'dfix', usage: 'sks dfix', description: 'Explain $DFix ultralight direct-fix mode.' },
@@ -1007,6 +1025,8 @@ export function routeReasoning(route, prompt = '') {
1007
1025
  const base = ALLOWED_REASONING_EFFORTS.has(route?.reasoningPolicy) ? route.reasoningPolicy : 'medium';
1008
1026
  if (hasFromChatImgSignal(text))
1009
1027
  return reasoning('xhigh', 'from_chat_img_image_work_order_analysis');
1028
+ if (/(?:^|\s)sks\s+--mad\b|(?:^|\s)--mad\b|\$MAD-SKS\b|\bmad-sks\b|\bmadsks\b/i.test(text))
1029
+ return reasoning('xhigh', 'mad_sks_or_mad_launch_default');
1010
1030
  if (route?.id === 'Team')
1011
1031
  return teamRouteReasoning(text);
1012
1032
  if (route?.id === 'Research' || route?.id === 'AutoResearch')
@@ -162,6 +162,8 @@ export async function guardedProcessKill(ctx, pid, opts = {}) {
162
162
  export async function guardedPackageInstall(ctx, spec, opts) {
163
163
  return guard(ctx, 'package_install', spec, opts, async () => {
164
164
  const runOpts = { timeoutMs: opts.timeoutMs ?? 600000 };
165
+ if (opts.cwd !== undefined)
166
+ runOpts.cwd = opts.cwd;
165
167
  if (opts.env !== undefined)
166
168
  runOpts.env = opts.env;
167
169
  if (opts.maxOutputBytes !== undefined)
@@ -360,7 +360,13 @@ export function parseTeamSpecArgs(args = []) {
360
360
  i++;
361
361
  continue;
362
362
  }
363
- if (arg === '--json' || arg === '--open-zellij' || arg === '--zellij-open' || arg === '--no-open-zellij' || arg === '--no-zellij' || arg === '--no-attach' || arg === '--separate-session' || arg === '--new-session' || arg === '--legacy-team-session')
363
+ if (/^--(?:ollama-model|local-model-model|ollama-base-url|local-model-base-url)$/.test(arg)) {
364
+ i++;
365
+ continue;
366
+ }
367
+ if (/^--(?:ollama-model|local-model-model|ollama-base-url|local-model-base-url)=/.test(arg))
368
+ continue;
369
+ if (arg === '--json' || arg === '--open-zellij' || arg === '--zellij-open' || arg === '--no-open-zellij' || arg === '--no-zellij' || arg === '--no-attach' || arg === '--separate-session' || arg === '--new-session' || arg === '--legacy-team-session' || arg === '--ollama' || arg === '--local-model' || arg === '--no-ollama' || arg === '--no-local-model')
364
370
  continue;
365
371
  const consumed = consumeTeamSpecText(arg, { roleCounts, explicitExecutor, explicitSession });
366
372
  roleCounts = consumed.roleCounts;