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.
- package/README.md +26 -5
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/crates/sks-core/src/main.rs +1 -1
- package/dist/.sks-build-stamp.json +4 -4
- package/dist/bin/sks.js +1 -1
- package/dist/build-manifest.json +28 -8
- package/dist/cli/command-registry.js +2 -0
- package/dist/commands/doctor.js +29 -3
- package/dist/core/agents/agent-command-surface.js +13 -3
- package/dist/core/agents/agent-orchestrator.js +92 -4
- package/dist/core/agents/agent-output-validator.js +2 -1
- package/dist/core/agents/agent-patch-proof.js +5 -0
- package/dist/core/agents/agent-patch-schema.js +2 -1
- package/dist/core/agents/agent-proof-evidence.js +26 -0
- package/dist/core/agents/agent-roster.js +1 -1
- package/dist/core/agents/agent-runner-ollama.js +411 -0
- package/dist/core/agents/agent-schema.js +1 -1
- package/dist/core/agents/intelligent-work-graph.js +45 -3
- package/dist/core/agents/native-cli-session-swarm.js +8 -1
- package/dist/core/agents/native-cli-worker.js +1 -1
- package/dist/core/agents/native-worker-backend-router.js +44 -2
- package/dist/core/agents/ollama-worker-config.js +118 -0
- package/dist/core/auto-review.js +39 -6
- package/dist/core/codex-app/codex-app-fast-ui-repair.js +42 -3
- package/dist/core/codex-control/codex-fake-sdk-adapter.js +20 -0
- package/dist/core/codex-control/codex-output-schemas.js +5 -1
- package/dist/core/codex-control/gpt-final-arbiter.js +160 -0
- package/dist/core/codex-control/gpt-final-context-compressor.js +17 -0
- package/dist/core/codex-control/gpt-final-proof-pack.js +120 -0
- package/dist/core/codex-control/gpt-final-review-schema.js +71 -0
- package/dist/core/commands/basic-cli.js +36 -1
- package/dist/core/commands/local-model-command.js +120 -0
- package/dist/core/commands/mad-sks-command.js +58 -9
- package/dist/core/commands/naruto-command.js +77 -5
- package/dist/core/commands/run-command.js +33 -1
- package/dist/core/commands/team-command.js +31 -2
- package/dist/core/doctor/doctor-readiness-matrix.js +19 -0
- package/dist/core/feature-fixtures.js +5 -0
- package/dist/core/fsx.js +1 -1
- package/dist/core/git-simple.js +143 -4
- package/dist/core/hooks-runtime.js +1 -1
- package/dist/core/init.js +2 -0
- package/dist/core/local-llm/local-collaboration-policy.js +93 -0
- package/dist/core/local-llm/local-llm-config.js +15 -0
- package/dist/core/pipeline/final-gpt-patch-stage.js +31 -0
- package/dist/core/pipeline/final-gpt-review-stage.js +5 -0
- package/dist/core/provider/provider-context.js +72 -9
- package/dist/core/retention.js +11 -0
- package/dist/core/routes.js +21 -1
- package/dist/core/safety/mutation-guard.js +2 -0
- package/dist/core/team-live.js +7 -1
- package/dist/core/update-check.js +215 -25
- package/dist/core/verification/verification-worker-pool.js +12 -0
- package/dist/core/version.js +1 -1
- package/dist/core/zellij/zellij-worker-pane-manager.js +19 -2
- package/dist/scripts/agent-ast-aware-work-graph-check.js +1 -1
- package/dist/scripts/codex-sdk-team-naruto-agent-pipeline-check.js +2 -1
- package/dist/scripts/doctor-fixes-codex-app-fast-ui-check.js +12 -2
- package/dist/scripts/gpt-final-arbiter-check.js +63 -0
- package/dist/scripts/gpt-final-arbiter-performance-check.js +36 -0
- package/dist/scripts/local-collab-gpt-final-availability-check.js +58 -0
- package/dist/scripts/local-collab-no-local-only-final-check.js +27 -0
- package/dist/scripts/local-collab-policy-check.js +17 -0
- package/dist/scripts/mad-sks-app-ui-no-mutation-check.js +92 -0
- package/dist/scripts/mad-sks-zellij-default-pane-worker-check.js +37 -0
- package/dist/scripts/mad-sks-zellij-launch-check.js +2 -1
- package/dist/scripts/provider-context-config-toml-check.js +63 -0
- package/dist/scripts/release-gate-existence-audit.js +4 -0
- package/dist/scripts/runtime-no-mjs-scripts-check.js +3 -2
- package/dist/scripts/zellij-worker-pane-manager-check.js +3 -0
- package/dist/scripts/zellij-worker-pane-manager-single-owner-check.js +39 -0
- package/package.json +13 -4
- package/schemas/local-llm/local-collaboration-policy.schema.json +57 -0
|
@@ -2,8 +2,10 @@ import path from 'node:path';
|
|
|
2
2
|
import { createMission, findLatestMission, loadMission } from '../mission.js';
|
|
3
3
|
import { readJson, sksRoot } from '../fsx.js';
|
|
4
4
|
import { runNativeAgentOrchestrator } from '../agents/agent-orchestrator.js';
|
|
5
|
+
import { classifyOllamaWorkerSlice } from '../agents/agent-runner-ollama.js';
|
|
5
6
|
import { buildNarutoCloneRoster, systemSafeNarutoConcurrency } from '../agents/agent-roster.js';
|
|
6
7
|
import { DEFAULT_NARUTO_CLONES, MAX_NARUTO_AGENT_COUNT } from '../agents/agent-schema.js';
|
|
8
|
+
import { resolveOllamaWorkerConfig } from '../agents/ollama-worker-config.js';
|
|
7
9
|
import { attachZellijSessionInteractive, launchZellijLayout } from '../zellij/zellij-launcher.js';
|
|
8
10
|
const NARUTO_RESULT_SCHEMA = 'sks.naruto-command-result.v1';
|
|
9
11
|
const NARUTO_ROUTE = '$Naruto';
|
|
@@ -32,7 +34,9 @@ async function narutoRun(parsed) {
|
|
|
32
34
|
// The clone roster is the full work fan-out; live concurrency is throttled to a
|
|
33
35
|
// system-safe number so naruto never spawns the whole count at once unless an
|
|
34
36
|
// explicit operator override asks for a higher target.
|
|
35
|
-
const
|
|
37
|
+
const localWorker = await resolveNarutoLocalWorkerMode(parsed);
|
|
38
|
+
const schedulerBackend = localWorker.auto_select_eligible ? 'ollama' : parsed.backend;
|
|
39
|
+
const safe = systemSafeNarutoConcurrency({ backend: schedulerBackend });
|
|
36
40
|
const activeSlots = Math.max(1, Math.min(roster.agent_count, parsed.concurrency || safe.cap));
|
|
37
41
|
const mission = await createMission(root, { mode: 'naruto', prompt: parsed.prompt });
|
|
38
42
|
const ledgerRoot = path.join(mission.dir, 'agents');
|
|
@@ -75,6 +79,11 @@ async function narutoRun(parsed) {
|
|
|
75
79
|
narutoMode: true,
|
|
76
80
|
clones: roster.agent_count,
|
|
77
81
|
backend: parsed.backend,
|
|
82
|
+
backendExplicit: parsed.backendExplicit,
|
|
83
|
+
noOllama: parsed.noOllama,
|
|
84
|
+
ollamaEnabled: parsed.ollamaEnabled,
|
|
85
|
+
ollamaModel: parsed.ollamaModel,
|
|
86
|
+
ollamaBaseUrl: parsed.ollamaBaseUrl,
|
|
78
87
|
mock: parsed.mock,
|
|
79
88
|
real: parsed.real,
|
|
80
89
|
readonly: parsed.readonly,
|
|
@@ -86,6 +95,7 @@ async function narutoRun(parsed) {
|
|
|
86
95
|
json: parsed.json
|
|
87
96
|
});
|
|
88
97
|
const clones = result.roster?.agent_count ?? roster.agent_count;
|
|
98
|
+
const localWorkerSummary = summarizeNarutoLocalWorkerResult(localWorker, result);
|
|
89
99
|
const summary = {
|
|
90
100
|
schema: NARUTO_RESULT_SCHEMA,
|
|
91
101
|
ok: result.ok === true,
|
|
@@ -99,6 +109,7 @@ async function narutoRun(parsed) {
|
|
|
99
109
|
target_active_slots: result.target_active_slots ?? activeSlots,
|
|
100
110
|
concurrency_capped: clones > (result.target_active_slots ?? activeSlots),
|
|
101
111
|
system: { cores: safe.cores, free_gb: safe.free_gb, safe_concurrency: safe.cap, heavy_backend: safe.heavy },
|
|
112
|
+
local_worker: localWorkerSummary,
|
|
102
113
|
proof: result.proof?.status || 'missing',
|
|
103
114
|
run: result,
|
|
104
115
|
zellij: null
|
|
@@ -116,6 +127,19 @@ async function narutoRun(parsed) {
|
|
|
116
127
|
console.log('Zellij: optional live panes unavailable (' + ((summary.zellij.warnings || []).join('; ') || summary.zellij.capability?.status || 'unknown') + ')');
|
|
117
128
|
});
|
|
118
129
|
}
|
|
130
|
+
function summarizeNarutoLocalWorkerResult(localWorker, result) {
|
|
131
|
+
const backendCounts = {};
|
|
132
|
+
const rows = Array.isArray(result?.results) ? result.results : [];
|
|
133
|
+
for (const row of rows) {
|
|
134
|
+
const selected = String(row?.backend_router_report?.selected_backend || row?.backend || 'unknown');
|
|
135
|
+
backendCounts[selected] = (backendCounts[selected] || 0) + 1;
|
|
136
|
+
}
|
|
137
|
+
return {
|
|
138
|
+
...localWorker,
|
|
139
|
+
selected_worker_count: backendCounts.ollama || 0,
|
|
140
|
+
backend_counts: backendCounts
|
|
141
|
+
};
|
|
142
|
+
}
|
|
119
143
|
async function narutoStatus(parsed) {
|
|
120
144
|
const root = await sksRoot();
|
|
121
145
|
const id = parsed.missionId && parsed.missionId !== 'latest' ? parsed.missionId : await findLatestMission(root);
|
|
@@ -149,7 +173,7 @@ async function narutoHelp(parsed) {
|
|
|
149
173
|
mode: 'NARUTO',
|
|
150
174
|
description: 'Shadow Clone Swarm: fan out up to ' + MAX_NARUTO_AGENT_COUNT + ' parallel clone sessions.',
|
|
151
175
|
usage: [
|
|
152
|
-
'sks naruto run "<task>" [--clones N] [--backend codex-sdk|fake] [--work-items N] [--real] [--readonly] [--json]',
|
|
176
|
+
'sks naruto run "<task>" [--clones N] [--backend codex-sdk|fake|ollama] [--local-model|--no-ollama] [--work-items N] [--real] [--readonly] [--json]',
|
|
153
177
|
'sks naruto status [--mission <id>] [--json]'
|
|
154
178
|
],
|
|
155
179
|
defaults: { clones: DEFAULT_NARUTO_CLONES, max_clones: MAX_NARUTO_AGENT_COUNT, backend: 'codex-sdk' }
|
|
@@ -171,18 +195,23 @@ function parseNarutoArgs(args = []) {
|
|
|
171
195
|
const clones = clampClones(requestedClones);
|
|
172
196
|
const workItems = clampWorkItems(Number(readOption(args, '--work-items', clones)), clones);
|
|
173
197
|
const concurrency = normalizeConcurrency(readOption(args, '--concurrency', readOption(args, '--target-active-slots', null)), clones);
|
|
174
|
-
const
|
|
198
|
+
const useOllama = hasFlag(args, '--ollama') || hasFlag(args, '--local-model');
|
|
199
|
+
const noOllama = hasFlag(args, '--no-ollama') || hasFlag(args, '--no-local-model');
|
|
200
|
+
const backendExplicit = hasOption(args, '--backend') || useOllama || noOllama;
|
|
201
|
+
const backend = String(readOption(args, '--backend', hasFlag(args, '--mock') ? 'fake' : useOllama && !noOllama ? 'ollama' : 'codex-sdk'));
|
|
175
202
|
const mock = hasFlag(args, '--mock') || backend === 'fake';
|
|
176
203
|
const real = hasFlag(args, '--real');
|
|
177
204
|
const readonly = hasFlag(args, '--readonly') || hasFlag(args, '--read-only');
|
|
178
205
|
const writeModeRaw = String(readOption(args, '--write-mode', hasFlag(args, '--parallel-write') ? 'parallel' : '') || '');
|
|
179
206
|
const writeMode = (['proof-safe', 'parallel', 'serial', 'off'].includes(writeModeRaw) ? writeModeRaw : null);
|
|
180
207
|
const missionId = String(readOption(args, '--mission', readOption(args, '--mission-id', 'latest')));
|
|
208
|
+
const ollamaModel = String(readOption(args, '--ollama-model', readOption(args, '--local-model-model', '')) || '') || null;
|
|
209
|
+
const ollamaBaseUrl = String(readOption(args, '--ollama-base-url', readOption(args, '--local-model-base-url', '')) || '') || null;
|
|
181
210
|
const noOpenZellij = hasFlag(args, '--no-open-zellij') || hasFlag(args, '--no-zellij');
|
|
182
211
|
const attach = hasFlag(args, '--attach');
|
|
183
|
-
const valueFlags = new Set(['--clones', '--agents', '--work-items', '--concurrency', '--target-active-slots', '--backend', '--write-mode', '--mission', '--mission-id']);
|
|
212
|
+
const valueFlags = new Set(['--clones', '--agents', '--work-items', '--concurrency', '--target-active-slots', '--backend', '--write-mode', '--mission', '--mission-id', '--ollama-model', '--local-model-model', '--ollama-base-url', '--local-model-base-url']);
|
|
184
213
|
const prompt = positionalArgs(rest, valueFlags).join(' ').trim() || 'Naruto shadow clone swarm run';
|
|
185
|
-
return { action, prompt, clones, workItems, concurrency, backend, mock, real, readonly, writeMode, json, missionId, noOpenZellij, attach };
|
|
214
|
+
return { action, prompt, clones, workItems, concurrency, backend, backendExplicit, mock, real, readonly, ollamaEnabled: useOllama && !noOllama, noOllama, ollamaModel, ollamaBaseUrl, writeMode, json, missionId, noOpenZellij, attach };
|
|
186
215
|
}
|
|
187
216
|
function clampClones(value) {
|
|
188
217
|
if (!Number.isFinite(value) || value < 1)
|
|
@@ -212,6 +241,9 @@ function readOption(args, name, fallback) {
|
|
|
212
241
|
const prefixed = args.find((arg) => String(arg).startsWith(name + '='));
|
|
213
242
|
return prefixed ? prefixed.slice(name.length + 1) : fallback;
|
|
214
243
|
}
|
|
244
|
+
function hasOption(args, name) {
|
|
245
|
+
return args.includes(name) || args.some((arg) => String(arg).startsWith(name + '='));
|
|
246
|
+
}
|
|
215
247
|
function positionalArgs(args, valueFlags) {
|
|
216
248
|
const out = [];
|
|
217
249
|
for (let i = 0; i < args.length; i += 1) {
|
|
@@ -233,4 +265,44 @@ function emit(parsed, result, text) {
|
|
|
233
265
|
text();
|
|
234
266
|
return result;
|
|
235
267
|
}
|
|
268
|
+
async function resolveNarutoLocalWorkerMode(parsed) {
|
|
269
|
+
const configInput = {
|
|
270
|
+
ollamaEnabled: parsed.ollamaEnabled,
|
|
271
|
+
model: parsed.ollamaModel,
|
|
272
|
+
baseUrl: parsed.ollamaBaseUrl
|
|
273
|
+
};
|
|
274
|
+
if (parsed.backend === 'ollama')
|
|
275
|
+
configInput.backend = 'ollama';
|
|
276
|
+
const config = await resolveOllamaWorkerConfig(configInput).catch(() => null);
|
|
277
|
+
const policy = classifyOllamaWorkerSlice({
|
|
278
|
+
id: 'naruto-local-worker-probe',
|
|
279
|
+
role: parsed.readonly ? 'collector' : 'implementer',
|
|
280
|
+
description: parsed.prompt,
|
|
281
|
+
write_paths: parsed.readonly ? [] : ['<lease-scoped-worker-path>']
|
|
282
|
+
}, { route: NARUTO_ROUTE, agent: { role: parsed.readonly ? 'collector' : 'implementer' } });
|
|
283
|
+
const autoSelectEligible = parsed.backend === 'codex-sdk'
|
|
284
|
+
&& parsed.backendExplicit !== true
|
|
285
|
+
&& parsed.noOllama !== true
|
|
286
|
+
&& config?.ok === true
|
|
287
|
+
&& config.enabled === true
|
|
288
|
+
&& policy.ok === true;
|
|
289
|
+
return {
|
|
290
|
+
schema: 'sks.naruto-local-worker-mode.v1',
|
|
291
|
+
enabled: config?.enabled === true,
|
|
292
|
+
provider: config?.provider || 'ollama',
|
|
293
|
+
model: config?.model || null,
|
|
294
|
+
requested_backend: parsed.backend,
|
|
295
|
+
backend_explicit: parsed.backendExplicit,
|
|
296
|
+
auto_select_eligible: autoSelectEligible,
|
|
297
|
+
worker_only: true,
|
|
298
|
+
no_strategy_planning_design: true,
|
|
299
|
+
policy,
|
|
300
|
+
blockers: [
|
|
301
|
+
...(config?.blockers || (config ? [] : ['ollama_worker_config_unavailable'])),
|
|
302
|
+
...(policy.blockers || []),
|
|
303
|
+
...(parsed.backendExplicit ? ['backend_explicit'] : []),
|
|
304
|
+
...(parsed.noOllama ? ['no_ollama_requested'] : [])
|
|
305
|
+
]
|
|
306
|
+
};
|
|
307
|
+
}
|
|
236
308
|
//# sourceMappingURL=naruto-command.js.map
|
|
@@ -231,7 +231,7 @@ async function executeRouteCommand(root, route, prompt, { auto = false } = {}) {
|
|
|
231
231
|
return routeExecutionResult(route, ['sks', ...commandArgs].join(' '), result, {
|
|
232
232
|
okStatus: 'completed',
|
|
233
233
|
trustStatus: 'verified_partial',
|
|
234
|
-
executionKind: route.command === '$DB' || route.command === '$Wiki' || route.command === '$Fast-Mode' ? 'safe_deterministic' : 'mock_safe',
|
|
234
|
+
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' ? 'safe_deterministic' : 'mock_safe',
|
|
235
235
|
});
|
|
236
236
|
}
|
|
237
237
|
async function runAutoVerification(root, missionId) {
|
|
@@ -346,6 +346,12 @@ function safeRouteExecutionArgs(route, prompt, { auto = false } = {}) {
|
|
|
346
346
|
return ['wiki', 'refresh', '--json'];
|
|
347
347
|
if (route.command === '$Fast-Mode')
|
|
348
348
|
return ['fast-mode', fastModeActionFromPrompt(prompt), '--json'];
|
|
349
|
+
if (route.command === '$with-local-llm-on')
|
|
350
|
+
return ['with-local-llm', localModelActionFromPrompt(prompt), '--json'];
|
|
351
|
+
if (route.command === '$Commit')
|
|
352
|
+
return ['commit', '--json'];
|
|
353
|
+
if (route.command === '$Commit-And-Push')
|
|
354
|
+
return ['commit-and-push', '--json'];
|
|
349
355
|
return ['team', prompt, '--mock', '--json', ...(auto ? ['--no-open-zellij'] : [])];
|
|
350
356
|
}
|
|
351
357
|
function fastModeActionFromPrompt(prompt = '') {
|
|
@@ -372,6 +378,32 @@ function fastModeActionFromPrompt(prompt = '') {
|
|
|
372
378
|
return 'clear';
|
|
373
379
|
return 'status';
|
|
374
380
|
}
|
|
381
|
+
function localModelActionFromPrompt(prompt = '') {
|
|
382
|
+
const text = String(prompt || '');
|
|
383
|
+
const lower = text.toLowerCase();
|
|
384
|
+
if (/\$with-local-llm-off\b/.test(lower))
|
|
385
|
+
return 'disable';
|
|
386
|
+
if (/\$with-local-llm-on\b/.test(lower))
|
|
387
|
+
return 'enable';
|
|
388
|
+
const routeMatch = /\$with-local-llm\b/i.exec(text);
|
|
389
|
+
if (!routeMatch)
|
|
390
|
+
return 'status';
|
|
391
|
+
const afterRoute = text
|
|
392
|
+
.slice(routeMatch.index + routeMatch[0].length)
|
|
393
|
+
.replace(/^[\s:=\-]+/, '')
|
|
394
|
+
.trimStart()
|
|
395
|
+
.toLowerCase();
|
|
396
|
+
const token = afterRoute.match(/^[^\s?!.,;:()"'`]+/)?.[0] || '';
|
|
397
|
+
if (['off', 'disable', 'disabled', '끄기', '꺼', '꺼줘'].includes(token) || token.startsWith('끄') || token.startsWith('꺼'))
|
|
398
|
+
return 'disable';
|
|
399
|
+
if (['on', 'enable', 'enabled', '켜기', '켜', '켜줘'].includes(token) || token.startsWith('켜'))
|
|
400
|
+
return 'enable';
|
|
401
|
+
if (['model', 'set-model', 'set'].includes(token))
|
|
402
|
+
return 'set-model';
|
|
403
|
+
if (['status', 'state', 'check', '확인', '상태'].includes(token))
|
|
404
|
+
return 'status';
|
|
405
|
+
return 'status';
|
|
406
|
+
}
|
|
375
407
|
function destructiveDbPrompt(prompt = '') {
|
|
376
408
|
return /\b(drop|truncate|delete\s+from|update\s+\w+\s+set|reset|db\s+push|disable\s+rls)\b/i.test(prompt);
|
|
377
409
|
}
|
|
@@ -26,7 +26,11 @@ export async function team(args = []) {
|
|
|
26
26
|
const jsonOutput = flag(args, '--json');
|
|
27
27
|
const mock = flag(args, '--mock');
|
|
28
28
|
const openZellij = !mock && !jsonOutput && !flag(args, '--no-open-zellij') && !flag(args, '--no-zellij');
|
|
29
|
-
const
|
|
29
|
+
const useOllama = flag(args, '--ollama') || flag(args, '--local-model');
|
|
30
|
+
const noOllama = flag(args, '--no-ollama') || flag(args, '--no-local-model');
|
|
31
|
+
const ollamaModel = readFlagValue(args, '--ollama-model', readFlagValue(args, '--local-model-model', '')) || null;
|
|
32
|
+
const ollamaBaseUrl = readFlagValue(args, '--ollama-base-url', readFlagValue(args, '--local-model-base-url', '')) || null;
|
|
33
|
+
const cleanCreateArgs = stripTeamCreateControlArgs(args);
|
|
30
34
|
const opts = parseTeamCreateArgs(cleanCreateArgs);
|
|
31
35
|
const { prompt, agentSessions, roleCounts, roster } = opts;
|
|
32
36
|
const targetActiveSlots = readBoundedIntegerFlag(args, '--target-active-slots', roster.bundle_size, 1, 20);
|
|
@@ -40,7 +44,7 @@ export async function team(args = []) {
|
|
|
40
44
|
const dryRunPatches = flag(args, '--dry-run-patches') || flag(args, '--dryrun-patches');
|
|
41
45
|
const maxWriteAgents = readBoundedIntegerFlag(args, '--max-write-agents', Math.min(roster.bundle_size, 5), 1, 20);
|
|
42
46
|
if (!prompt) {
|
|
43
|
-
console.error('Usage: sks team "task" [20:agents] [executor:5 reviewer:6 user:1] [--agents N] [--work-items N] [--target-active-slots N] [--profile NAME] [--write-mode off|proof-safe|parallel|serial] [--apply-patches] [--no-open-zellij] [--json] [--mock]');
|
|
47
|
+
console.error('Usage: sks team "task" [20:agents] [executor:5 reviewer:6 user:1] [--agents N] [--work-items N] [--target-active-slots N] [--profile NAME] [--write-mode off|proof-safe|parallel|serial] [--apply-patches] [--ollama|--no-ollama] [--no-open-zellij] [--json] [--mock]');
|
|
44
48
|
process.exitCode = 1;
|
|
45
49
|
return;
|
|
46
50
|
}
|
|
@@ -111,6 +115,10 @@ export async function team(args = []) {
|
|
|
111
115
|
applyPatches,
|
|
112
116
|
dryRunPatches,
|
|
113
117
|
maxWriteAgents,
|
|
118
|
+
ollamaEnabled: useOllama && !noOllama,
|
|
119
|
+
noOllama,
|
|
120
|
+
ollamaModel,
|
|
121
|
+
ollamaBaseUrl,
|
|
114
122
|
routeCommand: 'sks team',
|
|
115
123
|
routeBlackboxKind: 'actual_team_command'
|
|
116
124
|
});
|
|
@@ -198,6 +206,27 @@ export function parseTeamCreateArgs(args) {
|
|
|
198
206
|
const normalized = normalizeTeamSpec({ agentSessions: spec.agentSessions, roleCounts: spec.roleCounts, prompt });
|
|
199
207
|
return { prompt, agentSessions: normalized.agentSessions, roleCounts: normalized.roleCounts, roster: normalized.roster };
|
|
200
208
|
}
|
|
209
|
+
function stripTeamCreateControlArgs(args = []) {
|
|
210
|
+
const booleanFlags = new Set([
|
|
211
|
+
'--open-zellij', '--zellij-open', '--no-open-zellij', '--no-zellij', '--no-attach',
|
|
212
|
+
'--mock', '--ollama', '--local-model', '--no-ollama', '--no-local-model'
|
|
213
|
+
]);
|
|
214
|
+
const valueFlags = new Set(['--ollama-model', '--local-model-model', '--ollama-base-url', '--local-model-base-url']);
|
|
215
|
+
const out = [];
|
|
216
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
217
|
+
const arg = String(args[i]);
|
|
218
|
+
if (booleanFlags.has(arg))
|
|
219
|
+
continue;
|
|
220
|
+
if (valueFlags.has(arg)) {
|
|
221
|
+
i += 1;
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
if ([...valueFlags].some((flagName) => arg.startsWith(flagName + '=')))
|
|
225
|
+
continue;
|
|
226
|
+
out.push(args[i]);
|
|
227
|
+
}
|
|
228
|
+
return out;
|
|
229
|
+
}
|
|
201
230
|
export function buildTeamPlan(id, prompt, opts = {}) {
|
|
202
231
|
const spec = normalizeTeamSpec({ ...opts, prompt });
|
|
203
232
|
const { agentSessions, roleCounts, roster } = spec;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { nowIso, writeJsonAtomic } from '../fsx.js';
|
|
3
|
+
import { resolveLocalCollaborationPolicy } from '../local-llm/local-collaboration-policy.js';
|
|
3
4
|
export const DOCTOR_READINESS_MATRIX_SCHEMA = 'sks.doctor-readiness-matrix.v1';
|
|
4
5
|
export async function writeDoctorReadinessMatrix(root, input = {}) {
|
|
5
6
|
const matrix = buildDoctorReadinessMatrix(input);
|
|
@@ -47,10 +48,18 @@ export function buildDoctorReadinessMatrix(input = {}) {
|
|
|
47
48
|
warnings.add('codex_app_needs_setup_optional_for_cli');
|
|
48
49
|
if (input.codex_app_ui?.fast_selector === 'manual_action_required')
|
|
49
50
|
warnings.add('codex_app_fast_selector_manual_action_required');
|
|
51
|
+
if (input.codex_app_ui?.requires_confirmation === true)
|
|
52
|
+
blockers.add('codex_app_fast_ui_repair_requires_confirmation');
|
|
50
53
|
if (input.codex_app_ui?.fast_selector === 'repaired')
|
|
51
54
|
warnings.add('codex_app_fast_selector_repaired_restart_app_if_needed');
|
|
52
55
|
if (input.codex_lb?.ok === false)
|
|
53
56
|
warnings.add(`codex_lb_${input.codex_lb?.circuit?.state || 'blocked'}`);
|
|
57
|
+
const localCollaborationPolicy = resolveLocalCollaborationPolicy({ mode: input.local_collaboration?.mode || null });
|
|
58
|
+
const gptFinalAvailable = input.local_collaboration?.gpt_final_arbiter_available === undefined
|
|
59
|
+
? codexBinOk
|
|
60
|
+
: input.local_collaboration.gpt_final_arbiter_available === true;
|
|
61
|
+
if (localCollaborationPolicy.gpt_final_required && !gptFinalAvailable)
|
|
62
|
+
blockers.add('gpt_final_arbiter_unavailable');
|
|
54
63
|
const codexConfigNode = nodeRead.ok !== false && codexConfig.ok !== false;
|
|
55
64
|
const codexConfigChild = childRead.ok !== false && codexConfig.ok !== false;
|
|
56
65
|
const cliReady = codexBinOk && codexConfigNode && codexConfigChild && cliConfigOk;
|
|
@@ -58,6 +67,8 @@ export function buildDoctorReadinessMatrix(input = {}) {
|
|
|
58
67
|
const nextActions = normalizeList(input.operator_actions || codexConfig.operator_actions);
|
|
59
68
|
if (!nextActions.length && blockers.size)
|
|
60
69
|
nextActions.push('Run `sks doctor --fix`, then run `sks mad repair-config --apply` if config-load still fails.');
|
|
70
|
+
if (input.codex_app_ui?.requires_confirmation === true)
|
|
71
|
+
nextActions.push(input.codex_app_ui.next_action || 'Run `sks doctor --fix --repair-codex-app-ui` after reviewing the repair plan.');
|
|
61
72
|
if (!zellijReadyForInteractive)
|
|
62
73
|
nextActions.push('Install Zellij for `sks --mad` and interactive lane UI. On macOS: `brew install zellij`.');
|
|
63
74
|
return {
|
|
@@ -88,6 +99,14 @@ export function buildDoctorReadinessMatrix(input = {}) {
|
|
|
88
99
|
hooks_ready: input.hooks_ready !== false,
|
|
89
100
|
codex_app_ready: input.codex_app?.ok === true,
|
|
90
101
|
codex_app_required_for_cli: false,
|
|
102
|
+
local_collaboration: {
|
|
103
|
+
mode: localCollaborationPolicy.mode,
|
|
104
|
+
local_backend: input.local_collaboration?.local_backend || input.local_model?.provider || 'ollama',
|
|
105
|
+
local_model: input.local_collaboration?.local_model || input.local_model?.model || null,
|
|
106
|
+
final_arbiter: gptFinalAvailable ? 'GPT available' : 'missing',
|
|
107
|
+
final_apply_allowed: localCollaborationPolicy.gpt_final_required ? gptFinalAvailable : localCollaborationPolicy.mode === 'disabled',
|
|
108
|
+
blockers: localCollaborationPolicy.gpt_final_required && !gptFinalAvailable ? ['gpt_final_arbiter_unavailable'] : localCollaborationPolicy.blockers
|
|
109
|
+
},
|
|
91
110
|
ready: blockers.size === 0 && cliReady,
|
|
92
111
|
primary_blocker: [...blockers][0] || null,
|
|
93
112
|
blockers: [...blockers],
|
|
@@ -25,6 +25,7 @@ const FIXTURES = Object.freeze({
|
|
|
25
25
|
'cli-status': fixture('execute', 'sks status --json', [], 'pass'),
|
|
26
26
|
'cli-usage': fixture('execute', 'sks usage overview', [], 'pass'),
|
|
27
27
|
'cli-quickstart': fixture('execute', 'sks quickstart', [], 'pass'),
|
|
28
|
+
'cli-update': fixture('mock', 'sks update now --dry-run --json', [], 'pass'),
|
|
28
29
|
'cli-update-check': fixture('static', 'sks update-check --json', [], 'pass'),
|
|
29
30
|
'cli-guard': fixture('execute', 'sks guard check --json', [], 'pass'),
|
|
30
31
|
'cli-conflicts': fixture('execute', 'sks conflicts check --json', [], 'pass'),
|
|
@@ -55,6 +56,7 @@ const FIXTURES = Object.freeze({
|
|
|
55
56
|
'cli-stats': fixture('execute', 'sks stats --json', [], 'pass'),
|
|
56
57
|
'cli-dollar-commands': fixture('execute', 'sks dollar-commands --json', [], 'pass'),
|
|
57
58
|
'cli-fast-mode': fixture('execute', 'sks fast-mode status --json', [], 'pass'),
|
|
59
|
+
'cli-with-local-llm': fixture('execute', 'sks with-local-llm status --json', [], 'pass'),
|
|
58
60
|
'cli-dfix': fixture('execute_and_validate_artifacts', 'sks dfix fixture --json', ['completion-proof.json', 'dfix-gate.json', 'dfix-verification.json'], 'pass'),
|
|
59
61
|
'cli-wiki': fixture('execute_and_validate_artifacts', 'sks wiki image-ingest test/fixtures/images/one-by-one.png --json', [{ path: '.sneakoscope/wiki/image-voxel-ledger.json', schema: 'sks.image-voxel-ledger.v1', require_anchors: false }], 'pass'),
|
|
60
62
|
'cli-db': fixture('execute', 'sks db policy', [], 'pass'),
|
|
@@ -111,6 +113,9 @@ const FIXTURES = Object.freeze({
|
|
|
111
113
|
'route-fast-mode': fixture('execute', 'sks fast-mode status --json', [], 'pass'),
|
|
112
114
|
'route-fast-on': fixture('mock', '$Fast-On covered by hermetic fast-mode blackbox toggle test', [], 'pass'),
|
|
113
115
|
'route-fast-off': fixture('mock', '$Fast-Off covered by hermetic fast-mode blackbox toggle test', [], 'pass'),
|
|
116
|
+
'route-local-model': fixture('execute', 'sks with-local-llm status --json', [], 'pass'),
|
|
117
|
+
'route-with-local-llm-on': fixture('mock', '$with-local-llm-on covered by hermetic local-model dollar-command blackbox toggle test', [], 'pass'),
|
|
118
|
+
'route-with-local-llm-off': fixture('mock', '$with-local-llm-off covered by hermetic local-model dollar-command blackbox toggle test', [], 'pass'),
|
|
114
119
|
'route-help': fixture('mock', '$Help lightweight route', [], 'pass'),
|
|
115
120
|
'route-commit': fixture('mock', '$Commit git route', ['completion-proof.json'], 'pass'),
|
|
116
121
|
'route-commit-and-push': fixture('mock', '$Commit-And-Push git route', ['completion-proof.json'], 'pass'),
|
package/dist/core/fsx.js
CHANGED
|
@@ -5,7 +5,7 @@ import os from 'node:os';
|
|
|
5
5
|
import crypto from 'node:crypto';
|
|
6
6
|
import { spawn } from 'node:child_process';
|
|
7
7
|
import { fileURLToPath } from 'node:url';
|
|
8
|
-
export const PACKAGE_VERSION = '2.0.
|
|
8
|
+
export const PACKAGE_VERSION = '2.0.4';
|
|
9
9
|
export const DEFAULT_PROCESS_TAIL_BYTES = 256 * 1024;
|
|
10
10
|
export const DEFAULT_PROCESS_TIMEOUT_MS = 30 * 60 * 1000;
|
|
11
11
|
export function nowIso() {
|
package/dist/core/git-simple.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { runProcess, projectRoot, isGitRepo, nowIso, sha256 } from './fsx.js';
|
|
2
3
|
import { redactSecrets } from './secret-redaction.js';
|
|
4
|
+
import { runOllamaAgent } from './agents/agent-runner-ollama.js';
|
|
5
|
+
import { resolveOllamaWorkerConfig } from './agents/ollama-worker-config.js';
|
|
3
6
|
const TRAILER = 'Co-authored-by: Codex <noreply@openai.com>';
|
|
4
7
|
export async function simpleGitCommitCommand(args = [], opts = {}) {
|
|
5
8
|
const root = await projectRoot();
|
|
@@ -16,17 +19,24 @@ export async function simpleGitCommitCommand(args = [], opts = {}) {
|
|
|
16
19
|
export async function simpleGitCommit(root, { message = null, push = false } = {}) {
|
|
17
20
|
if (!await isGitRepo(root))
|
|
18
21
|
return { schema: 'sks.simple-git.v1', ok: false, reason: 'not_git_repo', root };
|
|
19
|
-
const before = await
|
|
22
|
+
const [before, branch, head] = await Promise.all([
|
|
23
|
+
git(root, ['status', '--short']),
|
|
24
|
+
git(root, ['branch', '--show-current']),
|
|
25
|
+
git(root, ['rev-parse', '--short', 'HEAD'])
|
|
26
|
+
]);
|
|
20
27
|
const changed = statusLines(before.stdout);
|
|
21
28
|
if (!changed.length)
|
|
22
29
|
return { schema: 'sks.simple-git.v1', ok: false, reason: 'no_changes', root };
|
|
30
|
+
const localWorker = message
|
|
31
|
+
? localWorkerSkipped('message_provided')
|
|
32
|
+
: await draftCommitMessageWithLocalWorker(root, changed, { push, branch: branch.stdout.trim(), head: head.stdout.trim() });
|
|
23
33
|
const add = await git(root, ['add', '-A']);
|
|
24
34
|
if (add.code !== 0)
|
|
25
35
|
return failure(root, 'git_add_failed', before, add);
|
|
26
36
|
const stagedCheck = await git(root, ['diff', '--cached', '--quiet']);
|
|
27
37
|
if (stagedCheck.code === 0)
|
|
28
38
|
return { schema: 'sks.simple-git.v1', ok: false, reason: 'no_staged_changes', root, changed };
|
|
29
|
-
const commitMessage = ensureCodexTrailer(message || buildCommitMessage(changed));
|
|
39
|
+
const commitMessage = ensureCodexTrailer(message || localWorker.message || buildCommitMessage(changed));
|
|
30
40
|
const commit = await git(root, ['commit', '-m', commitTitle(commitMessage), '-m', commitBody(commitMessage)]);
|
|
31
41
|
if (commit.code !== 0)
|
|
32
42
|
return failure(root, 'git_commit_failed', before, commit);
|
|
@@ -46,7 +56,8 @@ export async function simpleGitCommit(root, { message = null, push = false } = {
|
|
|
46
56
|
commit: commitSummary(commit),
|
|
47
57
|
hash: hash.stdout.trim(),
|
|
48
58
|
pushed: Boolean(push && pushResult?.code === 0),
|
|
49
|
-
push: pushResult ? { ok: pushResult.code === 0, stdout: pushResult.stdout.trim(), stderr: pushResult.stderr.trim() } : null
|
|
59
|
+
push: pushResult ? { ok: pushResult.code === 0, stdout: pushResult.stdout.trim(), stderr: pushResult.stderr.trim() } : null,
|
|
60
|
+
local_worker: localWorker.report
|
|
50
61
|
});
|
|
51
62
|
}
|
|
52
63
|
function failure(root, reason, before, failed, extra = {}) {
|
|
@@ -77,6 +88,134 @@ function buildCommitMessage(changed = []) {
|
|
|
77
88
|
const more = changed.length > 12 ? `\n- ...and ${changed.length - 12} more` : '';
|
|
78
89
|
return `chore: update project changes\n\nSummary: ${summary}\n\nChanged files:\n${files}${more}`;
|
|
79
90
|
}
|
|
91
|
+
async function draftCommitMessageWithLocalWorker(root, changed, context) {
|
|
92
|
+
const disabled = String(process.env.SKS_SIMPLE_GIT_LOCAL_LLM || '').trim() === '0';
|
|
93
|
+
if (disabled)
|
|
94
|
+
return { message: null, report: localWorkerSkipped('disabled_by_SKS_SIMPLE_GIT_LOCAL_LLM') };
|
|
95
|
+
const config = await resolveOllamaWorkerConfig().catch((error) => null);
|
|
96
|
+
if (!config?.ok || config.enabled !== true) {
|
|
97
|
+
return {
|
|
98
|
+
message: null,
|
|
99
|
+
report: {
|
|
100
|
+
schema: 'sks.simple-git-local-worker.v1',
|
|
101
|
+
generated_at: nowIso(),
|
|
102
|
+
ok: false,
|
|
103
|
+
used: false,
|
|
104
|
+
enabled: config?.enabled === true,
|
|
105
|
+
provider: config?.provider || 'ollama',
|
|
106
|
+
model: config?.model || null,
|
|
107
|
+
worker_only: true,
|
|
108
|
+
parent_owned_git_mutation: true,
|
|
109
|
+
task: context.push ? 'commit-and-push-message-draft' : 'commit-message-draft',
|
|
110
|
+
blockers: config?.blockers || ['ollama_worker_config_unavailable']
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
const runId = sha256(`${nowIso()}:${context.branch}:${context.head}:${changed.join('\n')}`).slice(0, 12);
|
|
115
|
+
const workerDirRel = path.join('.git', 'sks-local-workers', 'simple-git', runId);
|
|
116
|
+
const slice = {
|
|
117
|
+
id: `simple-git-message-${runId}`,
|
|
118
|
+
role: 'collector',
|
|
119
|
+
domain: 'git',
|
|
120
|
+
description: [
|
|
121
|
+
'simple collect summarize git status for a commit message draft only',
|
|
122
|
+
'Do not run git commands. Do not choose whether to commit or push.',
|
|
123
|
+
'Return summary as a conventional commit title and proposed_changes as short body bullets.',
|
|
124
|
+
`Action: ${context.push ? 'commit-and-push' : 'commit'}`,
|
|
125
|
+
`Branch: ${context.branch || 'unknown'}`,
|
|
126
|
+
`HEAD before commit: ${context.head || 'unknown'}`,
|
|
127
|
+
'Changed status lines:',
|
|
128
|
+
...changed.slice(0, 80)
|
|
129
|
+
].join('\n')
|
|
130
|
+
};
|
|
131
|
+
const agent = {
|
|
132
|
+
id: 'simple_git_local_worker',
|
|
133
|
+
session_id: `simple-git-${runId}`,
|
|
134
|
+
slot_id: 'local-worker',
|
|
135
|
+
generation_index: 1,
|
|
136
|
+
persona_id: 'local_git_summarizer',
|
|
137
|
+
role: 'collector'
|
|
138
|
+
};
|
|
139
|
+
const result = await runOllamaAgent(agent, slice, {
|
|
140
|
+
missionId: `simple-git-${runId}`,
|
|
141
|
+
agentRoot: root,
|
|
142
|
+
cwd: root,
|
|
143
|
+
workerDirRel,
|
|
144
|
+
route: context.push ? '$Commit-And-Push' : '$Commit',
|
|
145
|
+
fastMode: true,
|
|
146
|
+
serviceTier: 'fast',
|
|
147
|
+
ollamaTimeoutMs: Number(process.env.SKS_SIMPLE_GIT_OLLAMA_TIMEOUT_MS || 15000)
|
|
148
|
+
}).catch((error) => ({
|
|
149
|
+
status: 'blocked',
|
|
150
|
+
summary: '',
|
|
151
|
+
findings: [],
|
|
152
|
+
proposed_changes: [],
|
|
153
|
+
artifacts: [],
|
|
154
|
+
blockers: [error instanceof Error ? error.message : String(error)]
|
|
155
|
+
}));
|
|
156
|
+
const message = result.status === 'done' ? localWorkerCommitMessage(result, changed) : null;
|
|
157
|
+
return {
|
|
158
|
+
message,
|
|
159
|
+
report: {
|
|
160
|
+
schema: 'sks.simple-git-local-worker.v1',
|
|
161
|
+
generated_at: nowIso(),
|
|
162
|
+
ok: result.status === 'done',
|
|
163
|
+
used: Boolean(message),
|
|
164
|
+
enabled: true,
|
|
165
|
+
provider: config.provider,
|
|
166
|
+
model: config.model,
|
|
167
|
+
worker_only: true,
|
|
168
|
+
parent_owned_git_mutation: true,
|
|
169
|
+
task: context.push ? 'commit-and-push-message-draft' : 'commit-message-draft',
|
|
170
|
+
artifacts: result.artifacts || [],
|
|
171
|
+
summary: result.summary || null,
|
|
172
|
+
blockers: result.blockers || [],
|
|
173
|
+
fallback: message ? null : 'deterministic_commit_message'
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
function localWorkerCommitMessage(result, changed) {
|
|
178
|
+
const title = normalizeCommitTitle(result.summary);
|
|
179
|
+
if (!title)
|
|
180
|
+
return null;
|
|
181
|
+
const bodyLines = [
|
|
182
|
+
'Summary: local Ollama worker drafted this message from git status; parent SKS performed all git mutations.',
|
|
183
|
+
'',
|
|
184
|
+
...stringArray(result.proposed_changes || result.findings).slice(0, 8).map((line) => `- ${line}`),
|
|
185
|
+
...(stringArray(result.proposed_changes || result.findings).length ? [] : changed.slice(0, 8).map((line) => `- ${line}`))
|
|
186
|
+
];
|
|
187
|
+
return `${title}\n\n${bodyLines.join('\n')}`;
|
|
188
|
+
}
|
|
189
|
+
function normalizeCommitTitle(value) {
|
|
190
|
+
const raw = String(value || '').split(/\r?\n/)[0]?.trim() || '';
|
|
191
|
+
const stripped = raw.replace(/^["']|["']$/g, '').replace(/\s+/g, ' ').trim();
|
|
192
|
+
if (!stripped)
|
|
193
|
+
return '';
|
|
194
|
+
const conventional = /^[a-z][a-z0-9-]*(\([^)]+\))?:\s+\S/.test(stripped);
|
|
195
|
+
const title = conventional ? stripped : `chore: ${stripped.replace(/^(commit message|summary)\s*:\s*/i, '')}`;
|
|
196
|
+
return title.length > 96 ? title.slice(0, 93).trimEnd() + '...' : title;
|
|
197
|
+
}
|
|
198
|
+
function stringArray(value) {
|
|
199
|
+
return Array.isArray(value) ? value.map((line) => String(line || '').trim()).filter(Boolean) : [];
|
|
200
|
+
}
|
|
201
|
+
function localWorkerSkipped(reason) {
|
|
202
|
+
return {
|
|
203
|
+
message: null,
|
|
204
|
+
report: {
|
|
205
|
+
schema: 'sks.simple-git-local-worker.v1',
|
|
206
|
+
generated_at: nowIso(),
|
|
207
|
+
ok: true,
|
|
208
|
+
used: false,
|
|
209
|
+
enabled: false,
|
|
210
|
+
provider: 'ollama',
|
|
211
|
+
model: null,
|
|
212
|
+
worker_only: true,
|
|
213
|
+
parent_owned_git_mutation: true,
|
|
214
|
+
task: 'commit-message-draft',
|
|
215
|
+
blockers: [reason]
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
}
|
|
80
219
|
function ensureCodexTrailer(message = '') {
|
|
81
220
|
const withoutDuplicate = String(message || '')
|
|
82
221
|
.split(/\r?\n/)
|
|
@@ -1036,7 +1036,7 @@ function updateOfferThrottleMs() {
|
|
|
1036
1036
|
return 8 * 60 * 1000;
|
|
1037
1037
|
}
|
|
1038
1038
|
function sksUpdateInstallCommand(version) {
|
|
1039
|
-
return `
|
|
1039
|
+
return `sks update now --version ${version}`;
|
|
1040
1040
|
}
|
|
1041
1041
|
function copyStableUpdateChoiceText(latest, current = null) {
|
|
1042
1042
|
const installed = current ? `installed ${current}, latest ${latest}` : `latest ${latest}`;
|
package/dist/core/init.js
CHANGED
|
@@ -1024,6 +1024,8 @@ export async function installSkills(root) {
|
|
|
1024
1024
|
'fast-mode': `---\nname: fast-mode\ndescription: Dollar-command route for $Fast-Mode, $Fast-On, and $Fast-Off project-local Fast mode toggles.\n---\n\nUse when the user invokes $Fast-Mode, $Fast-On, $Fast-Off, or asks to turn SKS Fast mode on/off for dollar commands. Prefer \`sks fast-mode on|off|status|clear --json\`. The command writes only .sneakoscope/state/fast-mode.json in the active project. Explicit runtime flags still win: \`--fast\`, \`--no-fast\`, and \`--service-tier standard|fast\` override the saved preference for that run. Finish with a short status and Honest Mode; do not start Team or broad implementation for a toggle-only request.\n`,
|
|
1025
1025
|
'fast-on': `---\nname: fast-on\ndescription: Alias for $Fast-On project-local SKS Fast mode enablement.\n---\n\nUse the same rules as fast-mode. Run or instruct \`sks fast-mode on --json\`, then report the active state, state file, and the fact that explicit per-run flags still override the saved preference.\n`,
|
|
1026
1026
|
'fast-off': `---\nname: fast-off\ndescription: Alias for $Fast-Off project-local SKS Fast mode disablement.\n---\n\nUse the same rules as fast-mode. Run or instruct \`sks fast-mode off --json\`, then report the active state, state file, and the fact that explicit per-run flags still override the saved preference.\n`,
|
|
1027
|
+
'with-local-llm-on': `---\nname: with-local-llm-on\ndescription: Dollar-command route for $with-local-llm-on local Ollama worker enablement.\n---\n\nUse when the user invokes $with-local-llm-on or asks to enable the optional local Ollama worker backend. Prefer \`sks with-local-llm on --json\`. The command writes the machine-local config at \`~/.sneakoscope/local-model.json\`. Default off means SKS stays GPT-only until this command enables local workers. Enabled mode only lets policy-eligible simple code patch-envelope or read-only collection worker slices use Ollama; GPT/Codex still owns strategy, planning, design, review, verification, safety, and integration. \`--no-ollama\` and \`SKS_OLLAMA_WORKERS=0\` still force local workers off for a run. Finish with a short status and Honest Mode; do not start Team for a toggle-only request.\n`,
|
|
1028
|
+
'with-local-llm-off': `---\nname: with-local-llm-off\ndescription: Dollar-command route for $with-local-llm-off local Ollama worker disablement.\n---\n\nUse when the user invokes $with-local-llm-off or asks to disable the optional local Ollama worker backend. Prefer \`sks with-local-llm off --json\`. The command writes the machine-local config at \`~/.sneakoscope/local-model.json\`. Disabled mode keeps SKS GPT-only by default. Strategy, planning, design, review, verification, safety, and integration remain GPT/Codex-owned regardless of this toggle. Finish with a short status and Honest Mode; do not start Team for a toggle-only request.\n`,
|
|
1027
1029
|
'wiki': `---\nname: wiki\ndescription: Dollar-command route for $Wiki TriWiki refresh, pack, validate, and prune commands.\n---\n\nUse for $Wiki or Korean wiki-refresh requests. Refresh/update/갱신: run sks wiki refresh, then validate .sneakoscope/wiki/context-pack.json. Pack: run sks wiki pack, then validate. Prune/clean/정리: use sks wiki refresh --prune, or sks wiki prune --dry-run for inspection. Report claims, anchors, trust, attention.use_first/hydrate_first, validation, and blockers. Do not start ambiguity-gated implementation, subagents, or unrelated work.\n`,
|
|
1028
1030
|
'team': `---\nname: team\ndescription: SKS Team orchestration for $Team/code work; $From-Chat-IMG is the explicit chat-image alias.\n---\n\nUse for $Team/code work. Auto-seal the route contract from prompt, TriWiki/current-code defaults, and conservative policy; do not surface a prequestion sheet. Read pipeline-plan.json or run sks pipeline plan to see the runtime lane, kept/skipped stages, and verification before implementation. Write team-roster.json; team-gate.json needs team_roster_confirmed=true. executor:N means N native analysis agents, N debate voices, then fresh N executors. ${MIN_TEAM_REVIEW_POLICY_TEXT} After consensus, compile team-graph.json, team-runtime-tasks.json, team-decomposition-report.json, and team-inbox/ so worker handoff uses concrete runtime task ids with role/path/domain/lane hints. Refresh/validate TriWiki before debate, implementation, review, and final; consume attention.use_first and hydrate attention.hydrate_first before risky decisions. ${outcomeRubricPolicyText()} ${speedLanePolicyText()} ${solutionScoutPolicyText('fix this broken behavior')} ${skillDreamPolicyText()} Log events and use sks team message for bounded inter-agent communication in transcript/lane panes. Color-coded Zellij lanes distinguish overview/native-analysis/planning/execution/review/safety sessions in one Zellij window using split panes when Zellij is available. $Team/$team plus sks --mad uses the MAD-SKS permission gate module: user-authorized target-project scopes such as files, shell, packages, services, network, browser/Computer Use, generated assets, file permissions, migrations, normal DB writes, Supabase MCP writes, direct SQL, and schema cleanup are open only for the active invocation; catastrophic wipe/all-row/project-management, credential exfiltration, persistent security weakening, and unrequested fallback guards remain. End with cleanup-zellij or a cleanup event so follow panes show cleanup and stop; pass team-session-cleanup.json, then reflection and Honest Mode. Parent integrates/verifies.\n\n${chatCaptureIntakeText()}\n`,
|
|
1029
1031
|
'from-chat-img': `---\nname: from-chat-img\ndescription: Explicit $From-Chat-IMG Team alias for chat screenshot plus attachment analysis.\n---\n\nUse only for From-Chat-IMG/$From-Chat-IMG. It enters the normal Team pipeline. Treat uploads as chat screenshot plus originals. For web/browser/webapp targets use Codex Chrome Extension first; for native Mac/non-web app surfaces use Codex Computer Use visual inspection when available. List requirements first, match regions to attachments with confidence, write ${FROM_CHAT_IMG_COVERAGE_ARTIFACT}, ${FROM_CHAT_IMG_CHECKLIST_ARTIFACT}, ${FROM_CHAT_IMG_TEMP_TRIWIKI_ARTIFACT}, and ${FROM_CHAT_IMG_QA_LOOP_ARTIFACT}, then continue Team gates, review, reflection, and Honest Mode. ${CODEX_WEB_VERIFICATION_POLICY} ${CODEX_COMPUTER_USE_ONLY_POLICY} The ledger must account for every visible customer request, screenshot image region, and separate attachment; ${FROM_CHAT_IMG_CHECKLIST_ARTIFACT} must have a checked item for each request, image-region/attachment match, work item, scoped QA-LOOP, and verification step; ${FROM_CHAT_IMG_TEMP_TRIWIKI_ARTIFACT} stores temporary TriWiki-backed session context with expires_after_sessions=${FROM_CHAT_IMG_TEMP_TRIWIKI_SESSIONS}. ${FROM_CHAT_IMG_QA_LOOP_ARTIFACT} must prove QA-LOOP ran over the exact customer-request work-order range after implementation, with every work item covered, post-fix verification complete, and zero unresolved findings. team-gate.json cannot pass From-Chat-IMG completion until unresolved_items is empty, every checklist box is checked, and scoped_qa_loop_completed=true.\n`,
|