sneakoscope 0.6.66 → 0.6.69
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 +10 -4
- package/package.json +1 -1
- package/src/cli/main.mjs +127 -46
- package/src/core/cmux-ui.mjs +3 -3
- package/src/core/fsx.mjs +18 -2
- package/src/core/init.mjs +4 -2
- package/src/core/pipeline.mjs +2 -2
- package/src/core/routes.mjs +4 -2
- package/src/core/team-live.mjs +72 -0
package/README.md
CHANGED
|
@@ -6,18 +6,21 @@ Sneakoscope Codex (`sks`, displayed as `ㅅㅋㅅ`) is a Codex CLI/App harness f
|
|
|
6
6
|
|
|
7
7
|
## Quick Start
|
|
8
8
|
|
|
9
|
-
Install globally,
|
|
9
|
+
Install globally, then run `sks` from either a project or any global shell location:
|
|
10
10
|
|
|
11
11
|
```sh
|
|
12
12
|
npm i -g sneakoscope
|
|
13
|
+
sks root
|
|
13
14
|
sks bootstrap
|
|
14
15
|
sks
|
|
15
16
|
```
|
|
16
17
|
|
|
18
|
+
`sks root` tells you whether SKS found a project root or is using the per-user global runtime root. Outside a repo/project marker, runtime commands such as `sks`, `sks deps check`, `sks pipeline status`, and `sks team ...` use that global root instead of writing `.sneakoscope` into the random current directory.
|
|
19
|
+
|
|
17
20
|
If you only want a one-shot run without keeping `sks` installed globally:
|
|
18
21
|
|
|
19
22
|
```sh
|
|
20
|
-
npx -y -p sneakoscope sks
|
|
23
|
+
npx -y -p sneakoscope sks root
|
|
21
24
|
```
|
|
22
25
|
|
|
23
26
|
For a repo-local install:
|
|
@@ -89,10 +92,11 @@ Use this when you want `sks` available from any repo:
|
|
|
89
92
|
|
|
90
93
|
```sh
|
|
91
94
|
npm i -g sneakoscope
|
|
95
|
+
sks root
|
|
92
96
|
sks bootstrap
|
|
93
97
|
```
|
|
94
98
|
|
|
95
|
-
`sks bootstrap` initializes the current project
|
|
99
|
+
`sks` commands work even when no project root is present. Project-aware commands use the nearest `.sneakoscope`, `.dcodex`, or `.git` root; if none exists, SKS uses a per-user global runtime root. `sks bootstrap` still initializes the current project when you want project-local hooks, skills, and TriWiki state.
|
|
96
100
|
|
|
97
101
|
### One-Shot Install
|
|
98
102
|
|
|
@@ -185,11 +189,12 @@ Answer `y` to install `sneakoscope@latest`, then rerun `sks --mad`. Answer `n` t
|
|
|
185
189
|
```sh
|
|
186
190
|
sks team "implement this feature" executor:3 reviewer:1
|
|
187
191
|
sks team watch latest
|
|
192
|
+
sks team lane latest --agent analysis_scout_1 --follow
|
|
188
193
|
sks team status latest
|
|
189
194
|
sks team log latest
|
|
190
195
|
```
|
|
191
196
|
|
|
192
|
-
Team mode prepares the mission, records live events, compiles runtime tasks and worker inboxes, and opens cmux live lanes when cmux is available.
|
|
197
|
+
Team mode prepares the mission, records live events, compiles runtime tasks and worker inboxes, and opens cmux live lanes when cmux is available. `sks team lane` renders one agent's status, assigned runtime tasks, recent events, and a fallback global tail for multi-pane monitoring.
|
|
193
198
|
|
|
194
199
|
### QA, Ralph, Research, DB, Wiki, GX
|
|
195
200
|
|
|
@@ -374,6 +379,7 @@ OMX/DCodex conflicts intentionally block setup/doctor until the user approves cl
|
|
|
374
379
|
```sh
|
|
375
380
|
sks pipeline status --json
|
|
376
381
|
sks team watch latest
|
|
382
|
+
sks team lane latest --agent parent_orchestrator --follow
|
|
377
383
|
sks wiki validate .sneakoscope/wiki/context-pack.json
|
|
378
384
|
```
|
|
379
385
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sneakoscope",
|
|
3
3
|
"displayName": "ㅅㅋㅅ",
|
|
4
|
-
"version": "0.6.
|
|
4
|
+
"version": "0.6.69",
|
|
5
5
|
"description": "Sneakoscope Codex: database-safe Codex CLI/App harness with Team, Ralph, AutoResearch, TriWiki, and Honest Mode.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"homepage": "https://github.com/mandarange/Sneakoscope-Codex#readme",
|
package/src/cli/main.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import os from 'node:os';
|
|
|
3
3
|
import fsp from 'node:fs/promises';
|
|
4
4
|
import readline from 'node:readline/promises';
|
|
5
5
|
import { stdin as input, stdout as output } from 'node:process';
|
|
6
|
-
import { projectRoot, readJson, writeJsonAtomic, writeTextAtomic, appendJsonlBounded, nowIso, exists, ensureDir, tmpdir, packageRoot, dirSize, formatBytes, which, runProcess, PACKAGE_VERSION } from '../core/fsx.mjs';
|
|
6
|
+
import { projectRoot, readJson, writeJsonAtomic, writeTextAtomic, appendJsonlBounded, nowIso, exists, ensureDir, tmpdir, packageRoot, dirSize, formatBytes, which, runProcess, PACKAGE_VERSION, sksRoot, globalSksRoot, findProjectRoot } from '../core/fsx.mjs';
|
|
7
7
|
import { initProject, installSkills, normalizeInstallScope, sksCommandPrefix } from '../core/init.mjs';
|
|
8
8
|
import { getCodexInfo, runCodexExec } from '../core/codex-adapter.mjs';
|
|
9
9
|
import { createMission, loadMission, findLatestMission, missionDir, setCurrent, stateFile } from '../core/mission.mjs';
|
|
@@ -28,7 +28,7 @@ import { rgbaKey, rgbaToWikiCoord, validateWikiCoordinateIndex } from '../core/w
|
|
|
28
28
|
import { COMMAND_CATALOG, DOLLAR_COMMAND_ALIASES, DOLLAR_COMMANDS, DOLLAR_SKILL_NAMES, FROM_CHAT_IMG_CHECKLIST_ARTIFACT, FROM_CHAT_IMG_COVERAGE_ARTIFACT, FROM_CHAT_IMG_QA_LOOP_ARTIFACT, FROM_CHAT_IMG_TEMP_TRIWIKI_ARTIFACT, FROM_CHAT_IMG_TEMP_TRIWIKI_SESSIONS, RECOMMENDED_SKILLS, ROUTES, USAGE_TOPICS, context7ConfigToml, hasContext7ConfigText, hasFromChatImgSignal, looksLikeAnswerOnlyRequest, reflectionRequiredForRoute, reasoningInstruction, routePrompt, routeReasoning, routeRequiresSubagents, stackCurrentDocsPolicy, triwikiContextTracking } from '../core/routes.mjs';
|
|
29
29
|
import { context7Evidence, evaluateStop, recordContext7Evidence, recordSubagentEvidence } from '../core/pipeline.mjs';
|
|
30
30
|
import { TEAM_DECOMPOSITION_ARTIFACT, TEAM_GRAPH_ARTIFACT, TEAM_INBOX_DIR, TEAM_RUNTIME_TASKS_ARTIFACT, teamRuntimePlanMetadata, teamRuntimeRequiredArtifacts, validateTeamRuntimeArtifacts, writeTeamRuntimeArtifacts } from '../core/team-dag.mjs';
|
|
31
|
-
import { appendTeamEvent, formatRoleCounts, initTeamLive, normalizeTeamSpec, parseTeamSpecArgs, parseTeamSpecText, readTeamDashboard, readTeamLive, readTeamTranscriptTail } from '../core/team-live.mjs';
|
|
31
|
+
import { appendTeamEvent, formatRoleCounts, initTeamLive, normalizeTeamSpec, parseTeamSpecArgs, parseTeamSpecText, readTeamDashboard, readTeamLive, readTeamTranscriptTail, renderTeamAgentLane } from '../core/team-live.mjs';
|
|
32
32
|
import { CODEX_APP_DOCS_URL, codexAppIntegrationStatus, formatCodexAppStatus } from '../core/codex-app.mjs';
|
|
33
33
|
import { CMUX_BREW_COMMAND, CMUX_BREW_UPGRADE_COMMAND, buildCmuxLaunchPlan, defaultCmuxWorkspaceName, ensureCmuxInstalled, formatCmuxBanner, launchCmuxTeamView, launchCmuxUi, platformCmuxInstallHint, runCmuxStatus, sanitizeCmuxWorkspaceName, cmuxAvailable } from '../core/cmux-ui.mjs';
|
|
34
34
|
import { autoReviewProfileName, autoReviewStatus, autoReviewSummary, enableAutoReview, disableAutoReview, enableMadHighProfile, madHighProfileName } from '../core/auto-review.mjs';
|
|
@@ -63,6 +63,7 @@ export async function main(args) {
|
|
|
63
63
|
if (cmd === 'help') return help(tail);
|
|
64
64
|
if (cmd === 'commands') return commands(tail);
|
|
65
65
|
if (cmd === 'usage') return usage(tail);
|
|
66
|
+
if (cmd === 'root') return rootCommand(tail);
|
|
66
67
|
if (cmd === 'quickstart') return quickstart();
|
|
67
68
|
if (cmd === 'codex-app') return codexAppHelp(tail);
|
|
68
69
|
if (cmd === 'bootstrap') return bootstrap(tail);
|
|
@@ -112,6 +113,7 @@ Usage:
|
|
|
112
113
|
sks wizard
|
|
113
114
|
sks commands [--json]
|
|
114
115
|
sks usage [${USAGE_TOPICS}]
|
|
116
|
+
sks root [--json]
|
|
115
117
|
sks quickstart
|
|
116
118
|
sks bootstrap [--install-scope global|project] [--local-only] [--json]
|
|
117
119
|
sks deps check|install [cmux|codex|context7|all] [--yes] [--json]
|
|
@@ -145,7 +147,7 @@ Usage:
|
|
|
145
147
|
sks ralph run <mission-id|latest> [--mock] [--max-cycles N]
|
|
146
148
|
sks ralph status <mission-id|latest>
|
|
147
149
|
sks team "task" [executor:5 reviewer:2 user:1] [--json]
|
|
148
|
-
sks team log|tail|watch|status [mission-id|latest]
|
|
150
|
+
sks team log|tail|watch|lane|status [mission-id|latest]
|
|
149
151
|
sks team event [mission-id|latest] --agent <name> --phase <phase> --message "..."
|
|
150
152
|
sks research prepare "topic" [--depth frontier]
|
|
151
153
|
sks research run <mission-id|latest> [--mock] [--max-cycles N]
|
|
@@ -504,6 +506,27 @@ function commands(args = []) {
|
|
|
504
506
|
console.log(`\nCanonical Codex App picker skills: ${DOLLAR_COMMAND_ALIASES.map((x) => x.app_skill).join(', ')}`);
|
|
505
507
|
}
|
|
506
508
|
|
|
509
|
+
async function rootCommand(args = []) {
|
|
510
|
+
const project = await findProjectRoot();
|
|
511
|
+
const global = globalSksRoot();
|
|
512
|
+
const active = await sksRoot();
|
|
513
|
+
const result = {
|
|
514
|
+
cwd: process.cwd(),
|
|
515
|
+
mode: project ? 'project' : 'global',
|
|
516
|
+
active_root: active,
|
|
517
|
+
project_root: project,
|
|
518
|
+
global_root: global,
|
|
519
|
+
using_global_root: !project
|
|
520
|
+
};
|
|
521
|
+
if (flag(args, '--json')) return console.log(JSON.stringify(result, null, 2));
|
|
522
|
+
console.log('SKS Root\n');
|
|
523
|
+
console.log(`Mode: ${result.mode}`);
|
|
524
|
+
console.log(`Active root: ${active}`);
|
|
525
|
+
console.log(`Project: ${project || 'none'}`);
|
|
526
|
+
console.log(`Global root: ${global}`);
|
|
527
|
+
if (!project) console.log('\nNo project marker was found here, so SKS will use the per-user global runtime root. Run `sks bootstrap` to initialize the current directory as a project.');
|
|
528
|
+
}
|
|
529
|
+
|
|
507
530
|
function dollarCommands(args = []) {
|
|
508
531
|
if (flag(args, '--json')) return console.log(JSON.stringify({ dollar_commands: DOLLAR_COMMANDS, app_skill_aliases: DOLLAR_COMMAND_ALIASES }, null, 2));
|
|
509
532
|
console.log('ㅅㅋㅅ $ Commands\n');
|
|
@@ -549,8 +572,9 @@ Rules:
|
|
|
549
572
|
}
|
|
550
573
|
|
|
551
574
|
async function context7(sub = 'check', args = []) {
|
|
552
|
-
const root = await projectRoot();
|
|
553
575
|
const action = sub || 'check';
|
|
576
|
+
const setupScope = action === 'setup' ? readOption(args, '--scope', flag(args, '--global') ? 'global' : 'project') : null;
|
|
577
|
+
const root = action === 'setup' && setupScope === 'project' ? await projectRoot() : await sksRoot();
|
|
554
578
|
if (action === 'check') {
|
|
555
579
|
const result = await checkContext7(root);
|
|
556
580
|
if (flag(args, '--json')) return console.log(JSON.stringify(result, null, 2));
|
|
@@ -636,7 +660,7 @@ async function context7(sub = 'check', args = []) {
|
|
|
636
660
|
return;
|
|
637
661
|
}
|
|
638
662
|
if (action === 'setup') {
|
|
639
|
-
const scope =
|
|
663
|
+
const scope = setupScope;
|
|
640
664
|
const transport = readOption(args, '--transport', flag(args, '--remote') ? 'remote' : 'local');
|
|
641
665
|
if (!['project', 'global'].includes(scope)) throw new Error('Invalid Context7 scope. Use project or global.');
|
|
642
666
|
if (!['local', 'remote'].includes(transport)) throw new Error('Invalid Context7 transport. Use local or remote.');
|
|
@@ -673,7 +697,7 @@ function printContext7DocsResult(result, opts = {}) {
|
|
|
673
697
|
}
|
|
674
698
|
|
|
675
699
|
async function pipeline(sub = 'status', args = []) {
|
|
676
|
-
const root = await
|
|
700
|
+
const root = await sksRoot();
|
|
677
701
|
const action = sub || 'status';
|
|
678
702
|
if (action === 'answer') return pipelineAnswer(root, args);
|
|
679
703
|
const state = await readJson(stateFile(root), {});
|
|
@@ -1022,7 +1046,7 @@ async function cmuxCommand(sub = 'start', args = []) {
|
|
|
1022
1046
|
return;
|
|
1023
1047
|
}
|
|
1024
1048
|
if (action === 'check') {
|
|
1025
|
-
const root = await
|
|
1049
|
+
const root = await sksRoot();
|
|
1026
1050
|
const plan = await buildCmuxLaunchPlan({ root, session: readOption(args, '--session', null) });
|
|
1027
1051
|
if (flag(args, '--json')) return console.log(JSON.stringify(plan, null, 2));
|
|
1028
1052
|
console.log(formatCmuxBanner(plan.app));
|
|
@@ -1111,14 +1135,14 @@ async function ensureMadLaunchDependencies(args = []) {
|
|
|
1111
1135
|
const cmux = await cmuxAvailable().catch(() => ({ ok: false }));
|
|
1112
1136
|
if (!cmux.ok && !cmux.bin) actions.push(await installCmuxDependency(args));
|
|
1113
1137
|
}
|
|
1114
|
-
const status = await depsStatus(await
|
|
1138
|
+
const status = await depsStatus(await sksRoot());
|
|
1115
1139
|
return { ready: Boolean(status.codex_cli.ok && (status.cmux.ok || status.cmux.bin)), actions, status };
|
|
1116
1140
|
}
|
|
1117
1141
|
|
|
1118
1142
|
async function deps(sub = 'check', args = []) {
|
|
1119
1143
|
const action = sub || 'check';
|
|
1120
1144
|
if (action === 'check' || action === 'status') {
|
|
1121
|
-
const root = await
|
|
1145
|
+
const root = await sksRoot();
|
|
1122
1146
|
const status = await depsStatus(root);
|
|
1123
1147
|
if (flag(args, '--json')) return console.log(JSON.stringify(status, null, 2));
|
|
1124
1148
|
printDepsStatus(status);
|
|
@@ -1131,7 +1155,7 @@ async function deps(sub = 'check', args = []) {
|
|
|
1131
1155
|
}
|
|
1132
1156
|
|
|
1133
1157
|
async function depsStatus(root = null, opts = {}) {
|
|
1134
|
-
root ||= await
|
|
1158
|
+
root ||= await sksRoot();
|
|
1135
1159
|
const npmBin = await which('npm').catch(() => null);
|
|
1136
1160
|
const codex = opts.codex || await getCodexInfo().catch(() => ({}));
|
|
1137
1161
|
const app = opts.codexApp || await codexAppIntegrationStatus({ codex });
|
|
@@ -1195,7 +1219,7 @@ function printDepsStatus(status) {
|
|
|
1195
1219
|
}
|
|
1196
1220
|
|
|
1197
1221
|
async function depsInstall(args = []) {
|
|
1198
|
-
const root = await
|
|
1222
|
+
const root = await sksRoot();
|
|
1199
1223
|
const target = positionalArgs(args)[0] || 'all';
|
|
1200
1224
|
const wants = target === 'all' ? ['codex', 'context7', 'cmux'] : [target];
|
|
1201
1225
|
const actions = [];
|
|
@@ -1308,9 +1332,15 @@ function quickstart() {
|
|
|
1308
1332
|
|
|
1309
1333
|
First install and bootstrap this project:
|
|
1310
1334
|
npm i -g sneakoscope
|
|
1335
|
+
sks root
|
|
1311
1336
|
sks bootstrap
|
|
1312
1337
|
sks
|
|
1313
1338
|
|
|
1339
|
+
Use outside a project:
|
|
1340
|
+
sks root
|
|
1341
|
+
sks deps check
|
|
1342
|
+
sks team "global mission"
|
|
1343
|
+
|
|
1314
1344
|
If cmux is missing:
|
|
1315
1345
|
sks deps install cmux
|
|
1316
1346
|
|
|
@@ -1411,12 +1441,13 @@ Examples:
|
|
|
1411
1441
|
function usage(args = []) {
|
|
1412
1442
|
const topic = String(args[0] || 'overview').toLowerCase();
|
|
1413
1443
|
const blocks = {
|
|
1414
|
-
overview: ['ㅅㅋㅅ Usage', '', 'Discover:', ' sks commands', ' sks quickstart', ' sks bootstrap', ' sks deps check', ' sks codex-app check', ' sks cmux check', ' sks dollar-commands', '', `Topics: ${USAGE_TOPICS}`],
|
|
1415
|
-
install: ['Install', '', ' npm i -g sneakoscope', ' sks bootstrap', ' sks', '', 'Fallback:', ' npx -y -p sneakoscope sks
|
|
1444
|
+
overview: ['ㅅㅋㅅ Usage', '', 'Discover:', ' sks commands', ' sks quickstart', ' sks root', ' sks bootstrap', ' sks deps check', ' sks codex-app check', ' sks cmux check', ' sks dollar-commands', '', `Topics: ${USAGE_TOPICS}`],
|
|
1445
|
+
install: ['Install', '', ' npm i -g sneakoscope', ' sks root', ' sks', '', 'Project bootstrap:', ' sks bootstrap', '', 'Fallback:', ' npx -y -p sneakoscope sks root', '', 'Project:', ' npm i -D sneakoscope', ' npx sks setup --install-scope project'],
|
|
1416
1446
|
bootstrap: ['Bootstrap', '', ' sks bootstrap', ' sks setup --bootstrap', '', 'Creates project SKS files, Codex App skills/hooks/config, state/guard files, then checks Codex App, Context7, and cmux.'],
|
|
1447
|
+
root: ['Root', '', ' sks root [--json]', '', 'Inside a project, SKS uses that project root. Outside any project marker, runtime commands use the per-user global SKS root instead of writing .sneakoscope into the current random folder.'],
|
|
1417
1448
|
deps: ['Dependencies', '', ' sks deps check [--json]', ' sks deps install [cmux|codex|context7|all] [--yes]', '', 'cmux on macOS uses Homebrew only after approval.'],
|
|
1418
1449
|
cmux: ['cmux', '', ' sks', ' sks cmux check', ' sks cmux status --once', ' sks deps install cmux'],
|
|
1419
|
-
team: ['Team', '', ' sks team "task" executor:5 reviewer:2 user:1', ' sks team watch latest', '', '$Team runs questions -> contract -> scouts -> TriWiki attention -> debate -> runtime graph/inbox -> fresh executors -> review -> cleanup -> reflection -> Honest.'],
|
|
1450
|
+
team: ['Team', '', ' sks team "task" executor:5 reviewer:2 user:1', ' sks team watch latest', ' sks team lane latest --agent analysis_scout_1 --follow', '', '$Team runs questions -> contract -> scouts -> TriWiki attention -> debate -> runtime graph/inbox -> fresh executors -> review -> cleanup -> reflection -> Honest.'],
|
|
1420
1451
|
'qa-loop': ['QA-LOOP', '', ' sks qa-loop prepare "QA this app"', ' sks qa-loop answer <MISSION_ID> answers.json', ' sks qa-loop run <MISSION_ID> --max-cycles 8', '', 'Report: YYYY-MM-DD-v<version>-qa-report.md'],
|
|
1421
1452
|
ralph: ['Ralph', '', ' sks ralph prepare "task"', ' sks ralph answer <MISSION_ID> answers.json', ' sks ralph run <MISSION_ID> --max-cycles 8'],
|
|
1422
1453
|
'codex-app': ['Codex App', '', ' sks bootstrap', ' sks codex-app check', ' sks dollar-commands', ' cat .codex/SNEAKOSCOPE.md'],
|
|
@@ -1608,7 +1639,7 @@ async function fixPath(args) {
|
|
|
1608
1639
|
}
|
|
1609
1640
|
|
|
1610
1641
|
async function doctor(args) {
|
|
1611
|
-
const root = await
|
|
1642
|
+
const root = await sksRoot();
|
|
1612
1643
|
const requestedScope = args.includes('--install-scope') || flag(args, '--project') || flag(args, '--global')
|
|
1613
1644
|
? installScopeFromArgs(args)
|
|
1614
1645
|
: null;
|
|
@@ -1731,7 +1762,7 @@ async function checkRequiredSkills(root, skillRoot = path.join(root, '.agents',
|
|
|
1731
1762
|
}
|
|
1732
1763
|
|
|
1733
1764
|
async function codexAppSkillReadiness(root = null) {
|
|
1734
|
-
root ||= await
|
|
1765
|
+
root ||= await sksRoot();
|
|
1735
1766
|
const project = await checkRequiredSkills(root);
|
|
1736
1767
|
const global = await checkRequiredSkills(null, globalCodexSkillsRoot());
|
|
1737
1768
|
return { ok: project.ok || global.ok, project, global };
|
|
@@ -1892,7 +1923,7 @@ function qaRoute() {
|
|
|
1892
1923
|
}
|
|
1893
1924
|
|
|
1894
1925
|
async function qaLoopPrepare(args) {
|
|
1895
|
-
const root = await
|
|
1926
|
+
const root = await sksRoot();
|
|
1896
1927
|
if (!(await exists(path.join(root, '.sneakoscope')))) await initProject(root, {});
|
|
1897
1928
|
const prompt = promptOf(args);
|
|
1898
1929
|
if (!prompt) throw new Error('Missing QA target prompt.');
|
|
@@ -1912,7 +1943,7 @@ async function qaLoopPrepare(args) {
|
|
|
1912
1943
|
}
|
|
1913
1944
|
|
|
1914
1945
|
async function qaLoopAnswer(args) {
|
|
1915
|
-
const root = await
|
|
1946
|
+
const root = await sksRoot();
|
|
1916
1947
|
const [missionArg, answerFile] = args;
|
|
1917
1948
|
const id = await resolveMissionId(root, missionArg);
|
|
1918
1949
|
if (!id || !answerFile) throw new Error('Usage: sks qa-loop answer <mission-id|latest> <answers.json>');
|
|
@@ -1937,7 +1968,7 @@ async function qaLoopAnswer(args) {
|
|
|
1937
1968
|
}
|
|
1938
1969
|
|
|
1939
1970
|
async function qaLoopRun(args) {
|
|
1940
|
-
const root = await
|
|
1971
|
+
const root = await sksRoot();
|
|
1941
1972
|
const id = await resolveMissionId(root, args[0]);
|
|
1942
1973
|
if (!id) throw new Error('Usage: sks qa-loop run <mission-id|latest> [--mock] [--max-cycles N]');
|
|
1943
1974
|
const { dir, mission } = await loadMission(root, id);
|
|
@@ -2002,7 +2033,7 @@ async function qaLoopRun(args) {
|
|
|
2002
2033
|
}
|
|
2003
2034
|
|
|
2004
2035
|
async function qaLoopStatus(args) {
|
|
2005
|
-
const root = await
|
|
2036
|
+
const root = await sksRoot();
|
|
2006
2037
|
const id = await resolveMissionId(root, args[0]);
|
|
2007
2038
|
if (!id) throw new Error('Usage: sks qa-loop status <mission-id|latest>');
|
|
2008
2039
|
const { dir, mission } = await loadMission(root, id);
|
|
@@ -2019,7 +2050,7 @@ async function qaLoopStatus(args) {
|
|
|
2019
2050
|
}
|
|
2020
2051
|
|
|
2021
2052
|
async function researchPrepare(args) {
|
|
2022
|
-
const root = await
|
|
2053
|
+
const root = await sksRoot();
|
|
2023
2054
|
if (!(await exists(path.join(root, '.sneakoscope')))) await initProject(root, {});
|
|
2024
2055
|
const prompt = positionalArgs(args).join(' ').trim();
|
|
2025
2056
|
if (!prompt) throw new Error('Missing research topic.');
|
|
@@ -2033,7 +2064,7 @@ async function researchPrepare(args) {
|
|
|
2033
2064
|
}
|
|
2034
2065
|
|
|
2035
2066
|
async function researchRun(args) {
|
|
2036
|
-
const root = await
|
|
2067
|
+
const root = await sksRoot();
|
|
2037
2068
|
const id = await resolveMissionId(root, args[0]);
|
|
2038
2069
|
if (!id) throw new Error('Usage: sks research run <mission-id|latest> [--mock] [--max-cycles N]');
|
|
2039
2070
|
const { dir, mission } = await loadMission(root, id);
|
|
@@ -2095,7 +2126,7 @@ async function researchRun(args) {
|
|
|
2095
2126
|
}
|
|
2096
2127
|
|
|
2097
2128
|
async function researchStatus(args) {
|
|
2098
|
-
const root = await
|
|
2129
|
+
const root = await sksRoot();
|
|
2099
2130
|
const id = await resolveMissionId(root, args[0]);
|
|
2100
2131
|
if (!id) throw new Error('Usage: sks research status <mission-id|latest>');
|
|
2101
2132
|
const { dir, mission } = await loadMission(root, id);
|
|
@@ -2106,7 +2137,7 @@ async function researchStatus(args) {
|
|
|
2106
2137
|
}
|
|
2107
2138
|
|
|
2108
2139
|
async function ralphPrepare(args) {
|
|
2109
|
-
const root = await
|
|
2140
|
+
const root = await sksRoot();
|
|
2110
2141
|
if (!(await exists(path.join(root, '.sneakoscope')))) await initProject(root, {});
|
|
2111
2142
|
const prompt = promptOf(args);
|
|
2112
2143
|
if (!prompt) throw new Error('Missing task prompt.');
|
|
@@ -2124,7 +2155,7 @@ async function ralphPrepare(args) {
|
|
|
2124
2155
|
}
|
|
2125
2156
|
|
|
2126
2157
|
async function ralphAnswer(args) {
|
|
2127
|
-
const root = await
|
|
2158
|
+
const root = await sksRoot();
|
|
2128
2159
|
const [missionArg, answerFile] = args;
|
|
2129
2160
|
const id = await resolveMissionId(root, missionArg);
|
|
2130
2161
|
if (!id || !answerFile) throw new Error('Usage: sks ralph answer <mission-id|latest> <answers.json>');
|
|
@@ -2145,7 +2176,7 @@ async function ralphAnswer(args) {
|
|
|
2145
2176
|
}
|
|
2146
2177
|
|
|
2147
2178
|
async function ralphRun(args) {
|
|
2148
|
-
const root = await
|
|
2179
|
+
const root = await sksRoot();
|
|
2149
2180
|
const id = await resolveMissionId(root, args[0]);
|
|
2150
2181
|
if (!id) throw new Error('Usage: sks ralph run <mission-id|latest> [--mock]');
|
|
2151
2182
|
const { dir, mission } = await loadMission(root, id);
|
|
@@ -2229,7 +2260,7 @@ async function ralphRunMock(root, id, dir) {
|
|
|
2229
2260
|
}
|
|
2230
2261
|
|
|
2231
2262
|
async function ralphStatus(args) {
|
|
2232
|
-
const root = await
|
|
2263
|
+
const root = await sksRoot();
|
|
2233
2264
|
const id = await resolveMissionId(root, args[0]);
|
|
2234
2265
|
if (!id) throw new Error('Usage: sks ralph status <mission-id|latest>');
|
|
2235
2266
|
const { dir, mission } = await loadMission(root, id);
|
|
@@ -2321,6 +2352,18 @@ async function selftest() {
|
|
|
2321
2352
|
const depsCheck = await runProcess(process.execPath, [path.join(packageRoot(), 'bin', 'sks.mjs'), 'deps', 'check', '--json'], { cwd: bootstrapJsonTmp, env: { HOME: path.join(bootstrapJsonTmp, 'home') }, timeoutMs: 20000, maxOutputBytes: 256 * 1024 });
|
|
2322
2353
|
const depsResult = JSON.parse(depsCheck.stdout);
|
|
2323
2354
|
if (!depsResult.node?.ok || !('cmux' in depsResult) || !('homebrew' in depsResult)) throw new Error('selftest failed: deps check json missing expected fields');
|
|
2355
|
+
const globalCwd = tmpdir();
|
|
2356
|
+
const globalRuntimeRoot = path.join(tmpdir(), 'sks-global-root');
|
|
2357
|
+
const globalRootProbe = await runProcess(process.execPath, [path.join(packageRoot(), 'bin', 'sks.mjs'), 'root', '--json'], { cwd: globalCwd, env: { SKS_GLOBAL_ROOT: globalRuntimeRoot }, timeoutMs: 15000, maxOutputBytes: 64 * 1024 });
|
|
2358
|
+
const globalRootResult = JSON.parse(globalRootProbe.stdout);
|
|
2359
|
+
if (globalRootResult.mode !== 'global' || globalRootResult.active_root !== globalRuntimeRoot || globalRootResult.project_root !== null) throw new Error('selftest failed: global root probe did not use SKS_GLOBAL_ROOT outside a project');
|
|
2360
|
+
const globalPipeline = await runProcess(process.execPath, [path.join(packageRoot(), 'bin', 'sks.mjs'), 'pipeline', 'status', '--json'], { cwd: globalCwd, env: { SKS_GLOBAL_ROOT: globalRuntimeRoot }, timeoutMs: 15000, maxOutputBytes: 64 * 1024 });
|
|
2361
|
+
const globalPipelineResult = JSON.parse(globalPipeline.stdout);
|
|
2362
|
+
if (globalPipelineResult.root !== globalRuntimeRoot) throw new Error('selftest failed: pipeline status did not use global runtime root outside a project');
|
|
2363
|
+
const globalTeam = await runProcess(process.execPath, [path.join(packageRoot(), 'bin', 'sks.mjs'), 'team', 'global path smoke', '--json'], { cwd: globalCwd, env: { SKS_GLOBAL_ROOT: globalRuntimeRoot }, timeoutMs: 30000, maxOutputBytes: 256 * 1024 });
|
|
2364
|
+
const globalTeamResult = JSON.parse(globalTeam.stdout);
|
|
2365
|
+
if (!String(globalTeamResult.mission_dir || '').startsWith(path.join(globalRuntimeRoot, '.sneakoscope', 'missions')) || !(await exists(path.join(globalRuntimeRoot, '.sneakoscope', 'manifest.json')))) throw new Error('selftest failed: team mission did not materialize under global runtime root');
|
|
2366
|
+
if (await exists(path.join(globalCwd, '.sneakoscope'))) throw new Error('selftest failed: global runtime command polluted the caller cwd with .sneakoscope');
|
|
2324
2367
|
const madProfilePath = path.join(tmp, 'mad-codex-config.toml');
|
|
2325
2368
|
const madProfile = await enableMadHighProfile({ configPath: madProfilePath });
|
|
2326
2369
|
const madProfileText = await safeReadText(madProfilePath);
|
|
@@ -2464,6 +2507,8 @@ async function selftest() {
|
|
|
2464
2507
|
if (!promptPipelineText.includes('From-Chat-IMG') || !promptPipelineText.includes('Do not assume ordinary image prompts are chat captures')) throw new Error('selftest failed: prompt pipeline missing explicit From-Chat-IMG gating');
|
|
2465
2508
|
const fromChatImgSkillText = await safeReadText(path.join(tmp, '.agents', 'skills', 'from-chat-img', 'SKILL.md'));
|
|
2466
2509
|
if (!fromChatImgSkillText.includes('normal Team pipeline') || !fromChatImgSkillText.includes('Computer Use/browser visual inspection') || !fromChatImgSkillText.includes(FROM_CHAT_IMG_CHECKLIST_ARTIFACT) || !fromChatImgSkillText.includes(FROM_CHAT_IMG_TEMP_TRIWIKI_ARTIFACT) || !fromChatImgSkillText.includes(FROM_CHAT_IMG_QA_LOOP_ARTIFACT)) throw new Error('selftest failed: from-chat-img skill missing Team/browser inspection checklist guidance');
|
|
2510
|
+
const fromChatImgSkillMeta = await safeReadText(path.join(tmp, '.agents', 'skills', 'from-chat-img', 'agents', 'openai.yaml'));
|
|
2511
|
+
if (!fromChatImgSkillMeta.includes('model_reasoning_effort: xhigh')) throw new Error('selftest failed: from-chat-img skill metadata is not xhigh');
|
|
2467
2512
|
for (const supportSkill of ['reasoning-router', 'pipeline-runner', 'context7-docs', 'seo-geo-optimizer', 'reflection', 'design-system-builder', 'design-ui-editor', 'imagegen']) {
|
|
2468
2513
|
if (!(await exists(path.join(tmp, '.agents', 'skills', supportSkill, 'SKILL.md')))) throw new Error(`selftest failed: ${supportSkill} skill not installed`);
|
|
2469
2514
|
}
|
|
@@ -2498,7 +2543,7 @@ async function selftest() {
|
|
|
2498
2543
|
if (!DOLLAR_DEFAULT_PIPELINE_TEXT.includes('$Team')) throw new Error('selftest failed: dollar-commands missing Team default routing guidance');
|
|
2499
2544
|
if (!DOLLAR_DEFAULT_PIPELINE_TEXT.includes('$From-Chat-IMG')) throw new Error('selftest failed: dollar-commands missing From-Chat-IMG guidance');
|
|
2500
2545
|
if (!DOLLAR_DEFAULT_PIPELINE_TEXT.includes('$MAD-SKS')) throw new Error('selftest failed: dollar-commands missing MAD-SKS scoped override guidance');
|
|
2501
|
-
if (!COMMAND_CATALOG.some((c) => c.name === 'context7') || !COMMAND_CATALOG.some((c) => c.name === 'pipeline') || !COMMAND_CATALOG.some((c) => c.name === 'qa-loop')) throw new Error('selftest failed: context7/pipeline/qa-loop commands missing from catalog');
|
|
2546
|
+
if (!COMMAND_CATALOG.some((c) => c.name === 'context7') || !COMMAND_CATALOG.some((c) => c.name === 'pipeline') || !COMMAND_CATALOG.some((c) => c.name === 'qa-loop') || !COMMAND_CATALOG.some((c) => c.name === 'root')) throw new Error('selftest failed: context7/pipeline/qa-loop/root commands missing from catalog');
|
|
2502
2547
|
const registryDollarCommands = DOLLAR_COMMANDS.map((c) => c.command);
|
|
2503
2548
|
const manifest = await readJson(path.join(tmp, '.sneakoscope', 'manifest.json'));
|
|
2504
2549
|
const policy = await readJson(path.join(tmp, '.sneakoscope', 'policy.json'));
|
|
@@ -2967,8 +3012,9 @@ async function selftest() {
|
|
|
2967
3012
|
if (roleTeamPlan.roster.development_team.filter((agent) => agent.role === 'executor').length !== 5) throw new Error('selftest failed: executor role count not reflected in development team');
|
|
2968
3013
|
if (!roleTeamPlan.roster.debate_team.some((agent) => /inconvenience/.test(agent.persona))) throw new Error('selftest failed: user friction persona missing from debate team');
|
|
2969
3014
|
const cmuxTeam = await launchCmuxTeamView({ root: tmp, missionId: teamId, plan: roleTeamPlan, json: true });
|
|
2970
|
-
if (!cmuxTeam.agents?.length || !cmuxTeam.agents.some((entry) => entry.agent === 'analysis_scout_1') || !cmuxTeam.agents.every((entry) => String(entry.command || '').includes('team
|
|
3015
|
+
if (!cmuxTeam.agents?.length || !cmuxTeam.agents.some((entry) => entry.agent === 'analysis_scout_1') || !cmuxTeam.agents.every((entry) => String(entry.command || '').includes('team lane') && String(entry.command || '').includes('--agent'))) throw new Error('selftest failed: Team cmux view did not expose agent live lanes');
|
|
2971
3016
|
if (routeReasoning(routePrompt('$Research frontier idea'), '$Research frontier idea').effort !== 'xhigh') throw new Error('selftest failed: research reasoning not xhigh');
|
|
3017
|
+
if (routeReasoning(routePrompt('$From-Chat-IMG 채팅 이미지 작업'), '$From-Chat-IMG 채팅 이미지 작업').effort !== 'xhigh') throw new Error('selftest failed: From-Chat-IMG reasoning not xhigh');
|
|
2972
3018
|
if (routeReasoning(routePrompt('$DB migration'), '$DB migration').effort !== 'high') throw new Error('selftest failed: logical reasoning not high');
|
|
2973
3019
|
if (routeReasoning(routePrompt('$DFix button label'), '$DFix button label').effort !== 'medium') throw new Error('selftest failed: simple reasoning not medium');
|
|
2974
3020
|
if (routePrompt('이 파이프라인은 왜 이렇게 동작해?')?.id !== 'Answer') throw new Error('selftest failed: question prompt did not route to Answer');
|
|
@@ -3004,6 +3050,10 @@ async function selftest() {
|
|
|
3004
3050
|
if (!teamLive.includes('selftest mapped options')) throw new Error('selftest failed: team live transcript missing event');
|
|
3005
3051
|
if (!teamLive.includes('Context tracking SSOT: TriWiki')) throw new Error('selftest failed: team live transcript missing TriWiki context tracking');
|
|
3006
3052
|
if (!(await readTeamTranscriptTail(teamDir, 1)).join('\n').includes('selftest mapped options')) throw new Error('selftest failed: team transcript tail missing event');
|
|
3053
|
+
const teamLane = await renderTeamAgentLane(teamDir, { missionId: teamId, agent: 'analysis_scout_1', lines: 4 });
|
|
3054
|
+
if (!teamLane.includes('SKS Team Agent Lane') || !teamLane.includes('analysis_scout_1') || !teamLane.includes('selftest mapped repo slice')) throw new Error('selftest failed: team agent lane missing agent event context');
|
|
3055
|
+
const teamLaneCli = await runProcess(process.execPath, [hookBin, 'team', 'lane', teamId, '--agent', 'analysis_scout_1', '--lines', '4'], { cwd: tmp, env: { SKS_DISABLE_UPDATE_CHECK: '1' }, timeoutMs: 15000, maxOutputBytes: 64 * 1024 });
|
|
3056
|
+
if (teamLaneCli.code !== 0 || !String(teamLaneCli.stdout || '').includes('SKS Team Agent Lane') || !String(teamLaneCli.stdout || '').includes('analysis_scout_1')) throw new Error('selftest failed: sks team lane CLI did not render an agent lane');
|
|
3007
3057
|
await writeTextAtomic(path.join(teamDir, 'team-analysis.md'), '- claim: analysis scout mapped route registry | source: src/core/routes.mjs | risk: high | confidence: supported\n');
|
|
3008
3058
|
const installUxSchema = buildQuestionSchema('SKS first install/bootstrap UX and Context7 MCP setup improvement');
|
|
3009
3059
|
const installUxSlotIds = installUxSchema.slots.map((s) => s.id);
|
|
@@ -3118,7 +3168,7 @@ async function selftest() {
|
|
|
3118
3168
|
}
|
|
3119
3169
|
|
|
3120
3170
|
async function profile(sub, args) {
|
|
3121
|
-
const root = await
|
|
3171
|
+
const root = await sksRoot();
|
|
3122
3172
|
if (sub === 'show') return console.log(JSON.stringify(await readJson(path.join(root, '.sneakoscope', 'model', 'current.json'), { model: 'gpt-5.5' }), null, 2));
|
|
3123
3173
|
if (sub === 'set') { await writeJsonAtomic(path.join(root, '.sneakoscope', 'model', 'current.json'), { model: args[0] || 'gpt-5.5', set_at: nowIso() }); return console.log(`Model profile set: ${args[0] || 'gpt-5.5'}`); }
|
|
3124
3174
|
console.error('Usage: sks profile show|set <model>');
|
|
@@ -3126,7 +3176,7 @@ async function profile(sub, args) {
|
|
|
3126
3176
|
|
|
3127
3177
|
async function hproof(sub, args) {
|
|
3128
3178
|
if (sub !== 'check') return console.error('Usage: sks hproof check [mission-id]');
|
|
3129
|
-
const root = await
|
|
3179
|
+
const root = await sksRoot();
|
|
3130
3180
|
const id = await resolveMissionId(root, args[0]);
|
|
3131
3181
|
if (!id) throw new Error('No mission found.');
|
|
3132
3182
|
console.log(JSON.stringify(await evaluateDoneGate(root, id), null, 2));
|
|
@@ -3138,7 +3188,7 @@ async function evalCommand(sub, args) {
|
|
|
3138
3188
|
return;
|
|
3139
3189
|
}
|
|
3140
3190
|
if (sub === 'thresholds') return console.log(JSON.stringify(DEFAULT_EVAL_THRESHOLDS, null, 2));
|
|
3141
|
-
const root = await
|
|
3191
|
+
const root = await sksRoot();
|
|
3142
3192
|
if (sub === 'run') {
|
|
3143
3193
|
const iterations = Number(readFlagValue(args, '--iterations', 200));
|
|
3144
3194
|
const report = runEvaluationBenchmark({ iterations });
|
|
@@ -3176,14 +3226,14 @@ async function wiki(sub, args = []) {
|
|
|
3176
3226
|
return;
|
|
3177
3227
|
}
|
|
3178
3228
|
if (sub === 'pack') {
|
|
3179
|
-
const root = await
|
|
3229
|
+
const root = await sksRoot();
|
|
3180
3230
|
const { pack, file } = await writeWikiContextPack(root, args);
|
|
3181
3231
|
if (flag(args, '--json')) return console.log(JSON.stringify({ ...pack, path: file }, null, 2));
|
|
3182
3232
|
printWikiPackSummary(root, file, pack);
|
|
3183
3233
|
return;
|
|
3184
3234
|
}
|
|
3185
3235
|
if (sub === 'refresh') {
|
|
3186
|
-
const root = await
|
|
3236
|
+
const root = await sksRoot();
|
|
3187
3237
|
const dryRun = flag(args, '--dry-run');
|
|
3188
3238
|
const { pack, file } = await writeWikiContextPack(root, args, { dryRun });
|
|
3189
3239
|
const validation = wikiValidationResult(pack);
|
|
@@ -3220,7 +3270,7 @@ async function wiki(sub, args = []) {
|
|
|
3220
3270
|
return;
|
|
3221
3271
|
}
|
|
3222
3272
|
if (sub === 'prune') {
|
|
3223
|
-
const root = await
|
|
3273
|
+
const root = await sksRoot();
|
|
3224
3274
|
const pruneResult = await pruneWikiArtifacts(root, { dryRun: flag(args, '--dry-run') });
|
|
3225
3275
|
if (flag(args, '--json')) {
|
|
3226
3276
|
return console.log(JSON.stringify({
|
|
@@ -3237,7 +3287,7 @@ async function wiki(sub, args = []) {
|
|
|
3237
3287
|
return;
|
|
3238
3288
|
}
|
|
3239
3289
|
if (sub === 'validate') {
|
|
3240
|
-
const root = await
|
|
3290
|
+
const root = await sksRoot();
|
|
3241
3291
|
const target = positionalArgs(args)[0] || path.join(root, '.sneakoscope', 'wiki', 'context-pack.json');
|
|
3242
3292
|
const pack = await readJson(path.resolve(target));
|
|
3243
3293
|
const { result, trustAnchors } = wikiValidationResult(pack);
|
|
@@ -3721,7 +3771,7 @@ function printEvalCompare(report, saved) {
|
|
|
3721
3771
|
async function memory(sub, args) { return gc(args || []); }
|
|
3722
3772
|
|
|
3723
3773
|
async function gc(args) {
|
|
3724
|
-
const root = await
|
|
3774
|
+
const root = await sksRoot();
|
|
3725
3775
|
const res = await enforceRetention(root, { dryRun: flag(args, '--dry-run') });
|
|
3726
3776
|
if (flag(args, '--json')) return console.log(JSON.stringify(res, null, 2));
|
|
3727
3777
|
console.log(flag(args, '--dry-run') ? 'ㅅㅋㅅ GC dry run' : 'ㅅㅋㅅ GC completed');
|
|
@@ -3731,7 +3781,7 @@ async function gc(args) {
|
|
|
3731
3781
|
}
|
|
3732
3782
|
|
|
3733
3783
|
async function stats(args) {
|
|
3734
|
-
const root = await
|
|
3784
|
+
const root = await sksRoot();
|
|
3735
3785
|
const report = await storageReport(root);
|
|
3736
3786
|
const pkgBytes = await dirSize(packageRoot()).catch(() => 0);
|
|
3737
3787
|
const out = { package: { bytes: pkgBytes, human: formatBytes(pkgBytes) }, storage: report };
|
|
@@ -3744,7 +3794,7 @@ async function stats(args) {
|
|
|
3744
3794
|
|
|
3745
3795
|
function positionalArgs(args = []) {
|
|
3746
3796
|
const out = [];
|
|
3747
|
-
const valueFlags = new Set(['--format', '--iterations', '--out', '--baseline', '--candidate', '--install-scope', '--max-cycles', '--depth', '--scope', '--transport', '--query', '--topic', '--tokens', '--timeout-ms', '--sql', '--command', '--project-ref', '--agent', '--phase', '--message', '--role', '--max-anchors']);
|
|
3797
|
+
const valueFlags = new Set(['--format', '--iterations', '--out', '--baseline', '--candidate', '--install-scope', '--max-cycles', '--depth', '--scope', '--transport', '--query', '--topic', '--tokens', '--timeout-ms', '--sql', '--command', '--project-ref', '--agent', '--phase', '--message', '--role', '--max-anchors', '--lines']);
|
|
3748
3798
|
for (let i = 0; i < args.length; i++) {
|
|
3749
3799
|
const arg = String(args[i]);
|
|
3750
3800
|
if (valueFlags.has(arg)) {
|
|
@@ -3806,7 +3856,7 @@ function defaultBeta(name) {
|
|
|
3806
3856
|
}
|
|
3807
3857
|
|
|
3808
3858
|
async function gx(sub, args) {
|
|
3809
|
-
const root = await
|
|
3859
|
+
const root = await sksRoot();
|
|
3810
3860
|
const name = cartridgeName(args);
|
|
3811
3861
|
const dir = cartridgeDir(root, name);
|
|
3812
3862
|
if (sub === 'init') {
|
|
@@ -3854,18 +3904,19 @@ async function gx(sub, args) {
|
|
|
3854
3904
|
}
|
|
3855
3905
|
|
|
3856
3906
|
async function team(args) {
|
|
3857
|
-
const teamSubcommands = new Set(['log', 'tail', 'watch', 'status', 'event']);
|
|
3907
|
+
const teamSubcommands = new Set(['log', 'tail', 'watch', 'lane', 'status', 'event']);
|
|
3858
3908
|
if (teamSubcommands.has(args[0])) return teamCommand(args[0], args.slice(1));
|
|
3859
3909
|
const opts = parseTeamCreateArgs(args);
|
|
3860
3910
|
const { prompt, agentSessions, roleCounts, roster } = opts;
|
|
3861
3911
|
if (!prompt) {
|
|
3862
3912
|
console.error('Usage: sks team "task" [executor:5 reviewer:2 user:1] [--agents N] [--json]');
|
|
3863
|
-
console.error(' sks team log|tail|watch|status [mission-id|latest]');
|
|
3913
|
+
console.error(' sks team log|tail|watch|lane|status [mission-id|latest]');
|
|
3864
3914
|
console.error(' sks team event [mission-id|latest] --agent <name> --phase <phase> --message "..."');
|
|
3865
3915
|
process.exitCode = 1;
|
|
3866
3916
|
return;
|
|
3867
3917
|
}
|
|
3868
|
-
const root = await
|
|
3918
|
+
const root = await sksRoot();
|
|
3919
|
+
if (!(await exists(path.join(root, '.sneakoscope')))) await initProject(root, {});
|
|
3869
3920
|
const { id, dir } = await createMission(root, { mode: 'team', prompt });
|
|
3870
3921
|
const schema = buildQuestionSchema(prompt);
|
|
3871
3922
|
await writeQuestions(dir, schema);
|
|
@@ -4061,6 +4112,7 @@ function buildTeamPlan(id, prompt, opts = {}) {
|
|
|
4061
4112
|
'sks team log <mission-id>',
|
|
4062
4113
|
'sks team tail <mission-id>',
|
|
4063
4114
|
'sks team watch <mission-id>',
|
|
4115
|
+
'sks team lane <mission-id> --agent <name> --follow',
|
|
4064
4116
|
'sks team event <mission-id> --agent <name> --phase <phase> --message "..."'
|
|
4065
4117
|
]
|
|
4066
4118
|
},
|
|
@@ -4135,7 +4187,7 @@ ${plan.roster.validation_team.map((agent) => `- ${agent.id}: ${agent.persona}`).
|
|
|
4135
4187
|
- Keep team-live.md readable for the user inside Codex App.
|
|
4136
4188
|
- Mirror every useful subagent status, debate result, handoff, review finding, and integration decision to team-transcript.jsonl.
|
|
4137
4189
|
- Use \`sks team event ${plan.mission_id} --agent <name> --phase <phase> --message "..."\` when recording a live event from the parent thread.
|
|
4138
|
-
- The user can inspect the flow with \`sks team log ${plan.mission_id}\`, \`sks team tail ${plan.mission_id}\`, or \`sks team
|
|
4190
|
+
- The user can inspect the flow with \`sks team log ${plan.mission_id}\`, \`sks team tail ${plan.mission_id}\`, \`sks team watch ${plan.mission_id}\`, or \`sks team lane ${plan.mission_id} --agent analysis_scout_1 --follow\`.
|
|
4139
4191
|
|
|
4140
4192
|
## Phases
|
|
4141
4193
|
|
|
@@ -4148,7 +4200,7 @@ ${plan.invariants.map((x) => `- ${x}`).join('\n')}
|
|
|
4148
4200
|
}
|
|
4149
4201
|
|
|
4150
4202
|
async function teamCommand(sub, args) {
|
|
4151
|
-
const root = await
|
|
4203
|
+
const root = await sksRoot();
|
|
4152
4204
|
const missionArg = args[0] && !String(args[0]).startsWith('--') ? args[0] : 'latest';
|
|
4153
4205
|
const id = await resolveMissionId(root, missionArg);
|
|
4154
4206
|
if (!id) {
|
|
@@ -4191,6 +4243,35 @@ async function teamCommand(sub, args) {
|
|
|
4191
4243
|
return;
|
|
4192
4244
|
}
|
|
4193
4245
|
if (sub === 'log') return console.log(await readTeamLive(dir));
|
|
4246
|
+
if (sub === 'lane') {
|
|
4247
|
+
const agent = readFlagValue(args, '--agent', 'parent_orchestrator');
|
|
4248
|
+
const phase = readFlagValue(args, '--phase', '');
|
|
4249
|
+
const lines = Number(readFlagValue(args, '--lines', '12'));
|
|
4250
|
+
const printLane = async () => {
|
|
4251
|
+
const text = await renderTeamAgentLane(dir, { missionId: id, agent, phase, lines });
|
|
4252
|
+
if (flag(args, '--json')) {
|
|
4253
|
+
console.log(JSON.stringify({ mission_id: id, agent, phase, lane: text }, null, 2));
|
|
4254
|
+
} else {
|
|
4255
|
+
if (flag(args, '--follow') && process.stdout.isTTY) console.clear();
|
|
4256
|
+
console.log(text);
|
|
4257
|
+
}
|
|
4258
|
+
return text;
|
|
4259
|
+
};
|
|
4260
|
+
let last = await printLane();
|
|
4261
|
+
if (flag(args, '--follow')) {
|
|
4262
|
+
for (;;) {
|
|
4263
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
4264
|
+
const next = await renderTeamAgentLane(dir, { missionId: id, agent, phase, lines });
|
|
4265
|
+
if (next !== last) {
|
|
4266
|
+
if (process.stdout.isTTY) console.clear();
|
|
4267
|
+
else console.log('\n--- team lane update ---\n');
|
|
4268
|
+
console.log(next);
|
|
4269
|
+
last = next;
|
|
4270
|
+
}
|
|
4271
|
+
}
|
|
4272
|
+
}
|
|
4273
|
+
return;
|
|
4274
|
+
}
|
|
4194
4275
|
if (sub === 'tail' || sub === 'watch') {
|
|
4195
4276
|
const lines = readFlagValue(args, '--lines', '20');
|
|
4196
4277
|
const printTail = async () => {
|
|
@@ -4213,7 +4294,7 @@ async function teamCommand(sub, args) {
|
|
|
4213
4294
|
}
|
|
4214
4295
|
|
|
4215
4296
|
async function db(sub, args) {
|
|
4216
|
-
const root = await
|
|
4297
|
+
const root = await sksRoot();
|
|
4217
4298
|
if (sub === 'policy') {
|
|
4218
4299
|
console.log(JSON.stringify(await loadDbSafetyPolicy(root), null, 2));
|
|
4219
4300
|
return;
|
package/src/core/cmux-ui.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import fsp from 'node:fs/promises';
|
|
3
3
|
import { spawnSync } from 'node:child_process';
|
|
4
|
-
import { exists, packageRoot,
|
|
4
|
+
import { exists, packageRoot, runProcess, sha256, sksRoot, which } from './fsx.mjs';
|
|
5
5
|
import { getCodexInfo } from './codex-adapter.mjs';
|
|
6
6
|
import { codexAppIntegrationStatus, formatCodexAppStatus } from './codex-app.mjs';
|
|
7
7
|
|
|
@@ -137,12 +137,12 @@ export function teamAgentCommand(root, missionId, agentId, phase) {
|
|
|
137
137
|
return [
|
|
138
138
|
`printf '%s\\n' ${shellEscape(`${SKS_CMUX_LOGO}\n\nTeam mission: ${missionId}\nAgent: ${agentId}\nPhase: ${phase}\n`)}`,
|
|
139
139
|
`cd ${shellEscape(root)}`,
|
|
140
|
-
`node ${shellEscape(path.join(packageRoot(), 'bin', 'sks.mjs'))} team
|
|
140
|
+
`node ${shellEscape(path.join(packageRoot(), 'bin', 'sks.mjs'))} team lane ${shellEscape(missionId)} --agent ${shellEscape(agentId)} --phase ${shellEscape(phase)} --follow --lines 12`
|
|
141
141
|
].join('; ');
|
|
142
142
|
}
|
|
143
143
|
|
|
144
144
|
export async function buildCmuxLaunchPlan(opts = {}) {
|
|
145
|
-
const root = path.resolve(opts.root || await
|
|
145
|
+
const root = path.resolve(opts.root || await sksRoot());
|
|
146
146
|
const workspace = sanitizeCmuxWorkspaceName(opts.workspace || opts.session || defaultCmuxWorkspaceName(root));
|
|
147
147
|
const sksBin = opts.sksBin || path.join(packageRoot(), 'bin', 'sks.mjs');
|
|
148
148
|
const codex = opts.codex || await getCodexInfo().catch(() => ({}));
|
package/src/core/fsx.mjs
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
|
|
|
8
|
-
export const PACKAGE_VERSION = '0.6.
|
|
8
|
+
export const PACKAGE_VERSION = '0.6.69';
|
|
9
9
|
export const DEFAULT_PROCESS_TAIL_BYTES = 256 * 1024;
|
|
10
10
|
export const DEFAULT_PROCESS_TIMEOUT_MS = 30 * 60 * 1000;
|
|
11
11
|
|
|
@@ -145,7 +145,7 @@ export async function findUp(start, names) {
|
|
|
145
145
|
}
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
-
export async function
|
|
148
|
+
export async function findProjectRoot(start = process.cwd()) {
|
|
149
149
|
const resolved = path.resolve(start);
|
|
150
150
|
const sine = await findUp(resolved, ['.sneakoscope', '.dcodex']);
|
|
151
151
|
if (sine) {
|
|
@@ -157,6 +157,22 @@ export async function projectRoot(start = process.cwd()) {
|
|
|
157
157
|
const root = path.dirname(git);
|
|
158
158
|
if (root !== path.parse(root).root) return root;
|
|
159
159
|
}
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function globalSksRoot() {
|
|
164
|
+
if (process.env.SKS_GLOBAL_ROOT) return path.resolve(process.env.SKS_GLOBAL_ROOT);
|
|
165
|
+
return path.join(process.env.HOME || os.homedir(), '.sneakoscope-global');
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export async function sksRoot(start = process.cwd()) {
|
|
169
|
+
return await findProjectRoot(start) || globalSksRoot();
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export async function projectRoot(start = process.cwd()) {
|
|
173
|
+
const resolved = path.resolve(start);
|
|
174
|
+
const root = await findProjectRoot(resolved);
|
|
175
|
+
if (root) return root;
|
|
160
176
|
return resolved;
|
|
161
177
|
}
|
|
162
178
|
|
package/src/core/init.mjs
CHANGED
|
@@ -474,8 +474,10 @@ function codexAppQuickReference(scope, commandPrefix) {
|
|
|
474
474
|
`Picker skills: ${DOLLAR_COMMAND_ALIASES.map((x) => x.app_skill).join(', ')}.`,
|
|
475
475
|
'Routing: Answer direct, DFix ultralight, execution routes ask only scope/safety/behavior/acceptance-changing questions before sealing answers.',
|
|
476
476
|
`Full routes write reflection.md, record lessons to ${REFLECTION_MEMORY_PATH}, refresh/pack TriWiki, validate, then final-answer with a user-visible completion summary plus Honest Mode.`,
|
|
477
|
+
`Runtime root: ${commandPrefix} root shows whether SKS is using the nearest project root or the per-user global SKS runtime root; outside any project marker, runtime commands use the global root instead of writing .sneakoscope into the current random directory.`,
|
|
477
478
|
`Context Tracking: TriWiki SSOT. Before each route phase read only the latest coordinate+voxel overlay pack at .sneakoscope/wiki/context-pack.json; coordinate-only legacy packs are invalid. Use attention.use_first for compact high-trust recall and hydrate attention.hydrate_first from source before risky/lower-trust decisions. During every stage hydrate low-trust claims from source/hash/RGBA anchors; after changes run ${commandPrefix} wiki refresh or pack; before handoff/final run ${commandPrefix} wiki validate .sneakoscope/wiki/context-pack.json.`,
|
|
478
479
|
stackCurrentDocsPolicyText(commandPrefix),
|
|
480
|
+
`Team lanes: ${commandPrefix} team lane latest --agent analysis_scout_1 --follow shows one agent's status, assigned runtime tasks, recent agent events, and fallback global tail.`,
|
|
479
481
|
`Runtime: open Codex App once, then run ${commandPrefix} bootstrap, ${commandPrefix} deps check, or ${commandPrefix} deps install cmux.`,
|
|
480
482
|
`Guard: generated harness files are immutable outside the engine source repo; check ${commandPrefix} guard check; conflicts use ${commandPrefix} conflicts prompt with human approval.`
|
|
481
483
|
].join('\n') + '\n';
|
|
@@ -498,7 +500,7 @@ export async function installSkills(root) {
|
|
|
498
500
|
'gx': `---\nname: gx\ndescription: Dollar-command route for $GX or $gx deterministic GX visual context cartridges.\n---\n\nUse when the user invokes $GX/$gx or asks for architecture/context visualization through SKS. Prefer sks gx init, render, validate, drift, and snapshot. vgraph.json remains the source of truth.\n`,
|
|
499
501
|
'help': `---\nname: help\ndescription: Dollar-command route for $Help or $help explaining installed SKS commands and workflows.\n---\n\nUse when the user invokes $Help/$help or asks what commands exist. Prefer concise output from sks commands, sks usage <topic>, sks quickstart, sks aliases, and sks codex-app.\n`,
|
|
500
502
|
'prompt-pipeline': `---\nname: prompt-pipeline\ndescription: Default SKS prompt optimization pipeline for execution prompts; Answer and DFix bypass it.\n---\n\nClassify intent: Answer only for real questions; question-shaped implicit instructions, complaints, and mandatory-policy statements route to Team. DFix handles tiny design/content; code defaults to Team unless safety/research/GX route fits. Infer goal, target, constraints, acceptance, risk, and smallest safe route. Ask only scope/safety/behavior/acceptance-changing questions; otherwise seal inferred answers. Code work surfaces route/guard/scopes, materializes team-roster.json from default or explicit counts before implementation, compiles concrete Team runtime graph/inbox artifacts after consensus, and parent owns integration/tests/Context7/Honest Mode.\n\n${chatCaptureIntakeText()}\n\nDesign: read design.md; if missing use design-system-builder; use imagegen for image/logo/raster. TriWiki context-tracking SSOT: .sneakoscope/wiki/context-pack.json; read only the latest coordinate+voxel overlay pack before every route stage, run sks wiki refresh/pack after changes, validate before handoffs/final.\n`,
|
|
501
|
-
'reasoning-router': `---\nname: reasoning-router\ndescription: Temporary SKS reasoning-effort routing for every command and pipeline route.\n---\n\nmedium: simple copy/color/discovery/setup/mechanical edits. high: logic, safety, architecture, DB, orchestration, refactor, multi-file work. xhigh: research, AutoResearch, falsification, benchmarks, SEO/GEO, open-ended discovery. Routing is temporary; return to default after the gate. Inspect with sks reasoning and sks pipeline status.\n`,
|
|
503
|
+
'reasoning-router': `---\nname: reasoning-router\ndescription: Temporary SKS reasoning-effort routing for every command and pipeline route.\n---\n\nmedium: simple copy/color/discovery/setup/mechanical edits. high: logic, safety, architecture, DB, orchestration, refactor, multi-file work. xhigh: research, AutoResearch, falsification, benchmarks, SEO/GEO, open-ended discovery, and From-Chat-IMG image work-order analysis. Routing is temporary; return to default after the gate. Inspect with sks reasoning and sks pipeline status.\n`,
|
|
502
504
|
'pipeline-runner': `---\nname: pipeline-runner\ndescription: Execute SKS dollar-command routes as stateful pipelines with mission artifacts, route gates, Context7 evidence, temporary reasoning routing, reflection, and Honest Mode.\n---\n\nEvery $ command is a route. Use current.json and mission artifacts, temporary reasoning, TriWiki before stages, source hydration, Context7 when required, Team cleanup before reflection, reflection for full routes, and completion summary plus Honest Mode before final. Surface guard/scopes, record evidence, refresh/pack/validate TriWiki, and check sks pipeline status/resume.\n`,
|
|
503
505
|
'context7-docs': `---\nname: context7-docs\ndescription: Enforce Context7 MCP documentation evidence for SKS routes that depend on external libraries, frameworks, APIs, MCPs, package managers, DB SDKs, or generated docs.\n---\n\nWhen required, resolve-library-id, then query-docs for the resolved id. Legacy get-library-docs evidence is accepted. Prefer sks context7 tools/resolve/docs/evidence and finish only after both evidence stages exist. Check setup with sks context7 check.\n`,
|
|
504
506
|
'seo-geo-optimizer': `---\nname: seo-geo-optimizer\ndescription: SEO/GEO support for README, npm, GitHub, keywords, snippets, schema, and AI-search visibility.\n---\n\nUse for SEO/GEO, package metadata, README ranking, snippets, schema, and AI search. Optimize README, package.json, docs, badges, topics, quickstart, examples, command discovery, exact names, keywords, and AI Answer Snapshot. Do not invent metrics; use $AutoResearch unless it is a tiny copy edit.\n`,
|
|
@@ -552,7 +554,7 @@ Context tracking:
|
|
|
552
554
|
}
|
|
553
555
|
|
|
554
556
|
async function writeSkillMetadata(dir, name) {
|
|
555
|
-
const effort = ['research', 'autoresearch', 'research-discovery', 'autoresearch-loop'].includes(name)
|
|
557
|
+
const effort = ['research', 'autoresearch', 'research-discovery', 'autoresearch-loop', 'from-chat-img'].includes(name)
|
|
556
558
|
? 'xhigh'
|
|
557
559
|
: (['dfix', 'sks', 'help'].includes(name) ? 'medium' : 'high');
|
|
558
560
|
await ensureDir(path.join(dir, 'agents'));
|
package/src/core/pipeline.mjs
CHANGED
|
@@ -305,14 +305,14 @@ async function prepareTeam(root, route, task, required) {
|
|
|
305
305
|
transcript: 'team-transcript.jsonl',
|
|
306
306
|
dashboard: 'team-dashboard.json',
|
|
307
307
|
cmux: 'CLI Team entrypoints open cmux live lanes for the visible Team agent budget when cmux is available.',
|
|
308
|
-
commands: ['sks team status latest', 'sks team log latest', 'sks team tail latest', 'sks team watch latest', 'sks team event latest --agent <name> --phase <phase> --message "..."']
|
|
308
|
+
commands: ['sks team status latest', 'sks team log latest', 'sks team tail latest', 'sks team watch latest', 'sks team lane latest --agent <name> --follow', 'sks team event latest --agent <name> --phase <phase> --message "..."']
|
|
309
309
|
},
|
|
310
310
|
required_artifacts: ['team-roster.json', 'team-analysis.md', ...(fromChatImgRequired ? [FROM_CHAT_IMG_COVERAGE_ARTIFACT, FROM_CHAT_IMG_CHECKLIST_ARTIFACT, FROM_CHAT_IMG_TEMP_TRIWIKI_ARTIFACT, FROM_CHAT_IMG_QA_LOOP_ARTIFACT] : []), 'team-consensus.md', ...teamRuntimeRequiredArtifacts(), 'team-review.md', 'team-gate.json', TEAM_SESSION_CLEANUP_ARTIFACT, 'reflection.md', 'reflection-gate.json', 'team-live.md', 'team-transcript.jsonl', 'team-dashboard.json', '.sneakoscope/wiki/context-pack.json', 'context7-evidence.jsonl']
|
|
311
311
|
};
|
|
312
312
|
await writeJsonAtomic(path.join(dir, 'team-plan.json'), plan);
|
|
313
313
|
await writeJsonAtomic(path.join(dir, 'team-roster.json'), { schema_version: 1, mission_id: id, role_counts: roleCounts, agent_sessions: agentSessions, bundle_size: roster.bundle_size, roster, confirmed: true, source: 'default_or_prompt_team_spec' });
|
|
314
314
|
const contextTracking = triwikiContextTracking();
|
|
315
|
-
await writeTextAtomic(path.join(dir, 'team-workflow.md'), `# SKS Team Workflow\n\nTask: ${cleanTask}\n\nAgent session budget: ${agentSessions}\nBundle size: ${roster.bundle_size}\nRole counts: ${formatRoleCounts(roleCounts)}\nReasoning: high for team logic, temporary for this route only.\nContext tracking: ${contextTracking.ssot} SSOT, ${contextTracking.default_pack}; use relevant TriWiki context before every work stage, refresh/validate after findings, and preserve hydratable source anchors.\n\n1. Run exactly ${roster.bundle_size} read-only analysis_scout_N agents and write team-analysis.md.\n2. Refresh/validate TriWiki before debate.\n3. Run exactly ${roster.bundle_size} debate participants, then write consensus and implementation slices.\n4. Compile ${TEAM_GRAPH_ARTIFACT}, ${TEAM_RUNTIME_TASKS_ARTIFACT}, ${TEAM_DECOMPOSITION_ARTIFACT}, and ${TEAM_INBOX_DIR} so worker handoff uses concrete runtime task ids.\n5. Close debate agents before starting a fresh ${roster.bundle_size}-person executor team.\n6. Review, integrate, verify, and record evidence.\n7. Close/clean remaining Team sessions, finalize live transcript state, and write ${TEAM_SESSION_CLEANUP_ARTIFACT} before reflection/final.\n\nLive visibility:\n- sks team log ${id}\n- sks team tail ${id}\n- sks team watch ${id}\n- sks team event ${id} --agent <name> --phase <phase> --message \"...\"\n`);
|
|
315
|
+
await writeTextAtomic(path.join(dir, 'team-workflow.md'), `# SKS Team Workflow\n\nTask: ${cleanTask}\n\nAgent session budget: ${agentSessions}\nBundle size: ${roster.bundle_size}\nRole counts: ${formatRoleCounts(roleCounts)}\nReasoning: high for team logic, temporary for this route only.\nContext tracking: ${contextTracking.ssot} SSOT, ${contextTracking.default_pack}; use relevant TriWiki context before every work stage, refresh/validate after findings, and preserve hydratable source anchors.\n\n1. Run exactly ${roster.bundle_size} read-only analysis_scout_N agents and write team-analysis.md.\n2. Refresh/validate TriWiki before debate.\n3. Run exactly ${roster.bundle_size} debate participants, then write consensus and implementation slices.\n4. Compile ${TEAM_GRAPH_ARTIFACT}, ${TEAM_RUNTIME_TASKS_ARTIFACT}, ${TEAM_DECOMPOSITION_ARTIFACT}, and ${TEAM_INBOX_DIR} so worker handoff uses concrete runtime task ids.\n5. Close debate agents before starting a fresh ${roster.bundle_size}-person executor team.\n6. Review, integrate, verify, and record evidence.\n7. Close/clean remaining Team sessions, finalize live transcript state, and write ${TEAM_SESSION_CLEANUP_ARTIFACT} before reflection/final.\n\nLive visibility:\n- sks team log ${id}\n- sks team tail ${id}\n- sks team watch ${id}\n- sks team lane ${id} --agent analysis_scout_1 --follow\n- sks team event ${id} --agent <name> --phase <phase> --message \"...\"\n`);
|
|
316
316
|
await initTeamLive(id, dir, cleanTask, { agentSessions, roleCounts, roster });
|
|
317
317
|
const runtime = await writeTeamRuntimeArtifacts(dir, plan, {});
|
|
318
318
|
await writeJsonAtomic(path.join(dir, 'team-gate.json'), { passed: false, team_roster_confirmed: true, analysis_artifact: false, triwiki_refreshed: false, triwiki_validated: false, consensus_artifact: false, ...runtime.gate_fields, implementation_team_fresh: false, review_artifact: false, integration_evidence: false, session_cleanup: false, context7_evidence: false, ...(fromChatImgRequired ? { from_chat_img_required: true, from_chat_img_request_coverage: false } : {}) });
|
package/src/core/routes.mjs
CHANGED
|
@@ -4,7 +4,7 @@ export const FROM_CHAT_IMG_CHECKLIST_ARTIFACT = 'from-chat-img-checklist.md';
|
|
|
4
4
|
export const FROM_CHAT_IMG_TEMP_TRIWIKI_ARTIFACT = 'from-chat-img-temp-triwiki.json';
|
|
5
5
|
export const FROM_CHAT_IMG_QA_LOOP_ARTIFACT = 'from-chat-img-qa-loop.json';
|
|
6
6
|
export const FROM_CHAT_IMG_TEMP_TRIWIKI_SESSIONS = 5;
|
|
7
|
-
export const USAGE_TOPICS = 'install|setup|bootstrap|deps|cmux|auto-review|team|qa-loop|ralph|research|db|codex-app|dfix|design|imagegen|dollar|context7|pipeline|reasoning|guard|conflicts|versioning|eval|hproof|gx|wiki';
|
|
7
|
+
export const USAGE_TOPICS = 'install|setup|bootstrap|root|deps|cmux|auto-review|team|qa-loop|ralph|research|db|codex-app|dfix|design|imagegen|dollar|context7|pipeline|reasoning|guard|conflicts|versioning|eval|hproof|gx|wiki';
|
|
8
8
|
|
|
9
9
|
export const RECOMMENDED_MCP_SERVERS = [
|
|
10
10
|
{
|
|
@@ -331,6 +331,7 @@ export const COMMAND_CATALOG = [
|
|
|
331
331
|
{ name: 'usage', usage: `sks usage [${USAGE_TOPICS}]`, description: 'Print copy-ready workflows for common tasks.' },
|
|
332
332
|
{ name: 'quickstart', usage: 'sks quickstart', description: 'Show the shortest safe setup and verification flow.' },
|
|
333
333
|
{ 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/cmux, and print ready true/false.' },
|
|
334
|
+
{ name: 'root', usage: 'sks root [--json]', description: 'Show whether SKS is using a project root or the per-user global SKS runtime root.' },
|
|
334
335
|
{ name: 'deps', usage: 'sks deps check|install [cmux|codex|context7|all] [--yes]', description: 'Check or guided-install Node/npm PATH, Codex CLI/App, Context7, Browser Use, Computer Use, cmux, and Homebrew on macOS.' },
|
|
335
336
|
{ name: 'codex-app', usage: 'sks codex-app [check|open]', description: 'Check Codex App install and first-party MCP/plugin readiness, then show app setup files and examples.' },
|
|
336
337
|
{ name: 'cmux', usage: 'sks cmux [check|status] [--workspace name]', description: 'Open the SKS cmux runtime with the ㅅㅋㅅ ASCII status pane and Codex CLI.' },
|
|
@@ -356,7 +357,7 @@ export const COMMAND_CATALOG = [
|
|
|
356
357
|
{ name: 'eval', usage: 'sks eval run|compare|thresholds ...', description: 'Run deterministic context-quality and performance evidence checks.' },
|
|
357
358
|
{ name: 'wiki', usage: 'sks wiki coords|pack|refresh|prune|validate ...', description: 'Build, refresh, prune, and validate RGBA/trig LLM Wiki context packs with attention.use_first and attention.hydrate_first for compact recall plus source hydration.' },
|
|
358
359
|
{ name: 'hproof', usage: 'sks hproof check [mission-id|latest]', description: 'Evaluate the H-Proof done gate for a mission.' },
|
|
359
|
-
{ name: 'team', usage: 'sks team "task" [executor:5 reviewer:2 user:1]|log|tail|watch|status|event ...', description: 'Create and observe a scout-first Team mission: parallel analysis, TriWiki attention, role debate, runtime graph/inbox handoff, then executor parallel development.' },
|
|
360
|
+
{ name: 'team', usage: 'sks team "task" [executor:5 reviewer:2 user:1]|log|tail|watch|lane|status|event ...', description: 'Create and observe a scout-first Team mission: parallel analysis, TriWiki attention, role debate, runtime graph/inbox handoff, then executor parallel development.' },
|
|
360
361
|
{ name: 'reasoning', usage: 'sks reasoning ["prompt"] [--json]', description: 'Show SKS temporary reasoning-effort routing: medium for simple tasks, high for logic, xhigh for research.' },
|
|
361
362
|
{ name: 'gx', usage: 'sks gx init|render|validate|drift|snapshot [name]', description: 'Create and verify deterministic SVG/HTML visual context cartridges.' },
|
|
362
363
|
{ name: 'profile', usage: 'sks profile show|set <model>', description: 'Inspect or set the current SKS model profile metadata.' },
|
|
@@ -527,6 +528,7 @@ export function subagentExecutionPolicyText(route, prompt = '') {
|
|
|
527
528
|
export function routeReasoning(route, prompt = '') {
|
|
528
529
|
const text = String(prompt || '');
|
|
529
530
|
const base = route?.reasoningPolicy || 'medium';
|
|
531
|
+
if (hasFromChatImgSignal(text)) return reasoning('xhigh', 'from_chat_img_image_work_order_analysis');
|
|
530
532
|
if (route?.id === 'Research' || route?.id === 'AutoResearch') return reasoning('xhigh', 'research_or_experiment_route');
|
|
531
533
|
if (/\b(research|autoresearch|hypothesis|falsify|novelty|frontier|benchmark|experiment|SEO|GEO|ranking|연구|실험|가설|검증)\b/i.test(text)) return reasoning('xhigh', 'research_level_prompt');
|
|
532
534
|
if (base === 'high' || /\b(architecture|design|migration|database|security|parallel|orchestrat|refactor|algorithm|logic|tradeoff|검토|설계|마이그레이션|보안|병렬|팀|논리)\b/i.test(text)) return reasoning('high', 'logical_or_safety_work');
|
package/src/core/team-live.mjs
CHANGED
|
@@ -3,6 +3,7 @@ import { appendJsonlBounded, nowIso, readJson, readText, writeJsonAtomic, writeT
|
|
|
3
3
|
import { triwikiContextTracking, triwikiContextTrackingText } from './routes.mjs';
|
|
4
4
|
|
|
5
5
|
const MAX_LIVE_BYTES = 192 * 1024;
|
|
6
|
+
const TEAM_RUNTIME_TASKS_ARTIFACT = 'team-runtime-tasks.json';
|
|
6
7
|
const DEFAULT_AGENTS = ['parent_orchestrator', 'analysis_scout', 'team_consensus', 'implementation_worker', 'db_safety_reviewer', 'qa_reviewer'];
|
|
7
8
|
export const DEFAULT_TEAM_ROLE_COUNTS = { user: 1, planner: 1, reviewer: 1, executor: 3 };
|
|
8
9
|
export const DEFAULT_MAX_TEAM_AGENT_SESSIONS = 6;
|
|
@@ -67,6 +68,7 @@ export function defaultTeamDashboard(id, prompt, opts = {}) {
|
|
|
67
68
|
log: `sks team log ${id}`,
|
|
68
69
|
tail: `sks team tail ${id}`,
|
|
69
70
|
watch: `sks team watch ${id}`,
|
|
71
|
+
lane: `sks team lane ${id} --agent <agent> --follow`,
|
|
70
72
|
event: `sks team event ${id} --agent <agent> --phase <phase> --message "..."`
|
|
71
73
|
},
|
|
72
74
|
agents: Object.fromEntries([...new Set([...DEFAULT_AGENTS, ...spec.roster.all_agents.map((agent) => agent.id)])].map((name) => [name, { status: 'pending', phase: null, last_seen: null }])),
|
|
@@ -115,6 +117,7 @@ sks team status ${id}
|
|
|
115
117
|
sks team log ${id}
|
|
116
118
|
sks team tail ${id}
|
|
117
119
|
sks team watch ${id}
|
|
120
|
+
sks team lane ${id} --agent analysis_scout_1 --follow
|
|
118
121
|
sks team event ${id} --agent analysis_scout_1 --phase parallel_analysis_scouting --message "mapped repo slice"
|
|
119
122
|
\`\`\`
|
|
120
123
|
|
|
@@ -337,6 +340,42 @@ export async function readTeamTranscriptTail(dir, count = 20) {
|
|
|
337
340
|
return text.split(/\n/).filter(Boolean).slice(-Math.max(1, Number(count) || 20));
|
|
338
341
|
}
|
|
339
342
|
|
|
343
|
+
export async function renderTeamAgentLane(dir, opts = {}) {
|
|
344
|
+
const agent = String(opts.agent || opts.agentId || 'parent_orchestrator');
|
|
345
|
+
const phase = opts.phase ? String(opts.phase) : null;
|
|
346
|
+
const lines = Math.max(1, Number(opts.lines) || 12);
|
|
347
|
+
const dashboard = await readTeamDashboard(dir);
|
|
348
|
+
const runtime = await readJson(path.join(dir, TEAM_RUNTIME_TASKS_ARTIFACT), null);
|
|
349
|
+
const missionId = opts.missionId || dashboard?.mission_id || runtime?.mission_id || path.basename(dir);
|
|
350
|
+
const status = dashboard?.agents?.[agent] || {};
|
|
351
|
+
const runtimeTasks = Array.isArray(runtime?.tasks) ? runtime.tasks : Array.isArray(runtime) ? runtime : [];
|
|
352
|
+
const assignedTasks = runtimeTasks.filter((task) => task?.worker === agent || task?.agent_hint === agent);
|
|
353
|
+
const eventWindow = await readTeamTranscriptTail(dir, Math.max(lines * 8, 80));
|
|
354
|
+
const agentEvents = eventWindow.map(parseTranscriptLine).filter((event) => event?.agent === agent).slice(-lines);
|
|
355
|
+
const globalTail = (await readTeamTranscriptTail(dir, lines)).map(parseTranscriptLine).filter(Boolean);
|
|
356
|
+
return [
|
|
357
|
+
`# SKS Team Agent Lane`,
|
|
358
|
+
'',
|
|
359
|
+
`Mission: ${missionId}`,
|
|
360
|
+
`Agent: ${agent}`,
|
|
361
|
+
`Requested phase: ${phase || 'any'}`,
|
|
362
|
+
'',
|
|
363
|
+
`## Agent Status`,
|
|
364
|
+
`- status: ${status.status || 'pending'}`,
|
|
365
|
+
`- phase: ${status.phase || 'unknown'}`,
|
|
366
|
+
`- last_seen: ${status.last_seen || 'never'}`,
|
|
367
|
+
'',
|
|
368
|
+
`## Assigned Runtime Tasks`,
|
|
369
|
+
...(runtime ? formatRuntimeTasks(assignedTasks) : ['- team-runtime-tasks.json not available yet.']),
|
|
370
|
+
'',
|
|
371
|
+
`## Recent Agent Events`,
|
|
372
|
+
...(agentEvents.length ? agentEvents.map(formatTranscriptEvent) : ['- No recent agent-specific events in the bounded tail.']),
|
|
373
|
+
'',
|
|
374
|
+
`## Fallback Global Tail`,
|
|
375
|
+
...(globalTail.length ? globalTail.map(formatTranscriptEvent) : ['- No transcript events yet.'])
|
|
376
|
+
].join('\n');
|
|
377
|
+
}
|
|
378
|
+
|
|
340
379
|
function normalizeEvent(event = {}) {
|
|
341
380
|
return {
|
|
342
381
|
ts: event.ts || nowIso(),
|
|
@@ -348,6 +387,39 @@ function normalizeEvent(event = {}) {
|
|
|
348
387
|
};
|
|
349
388
|
}
|
|
350
389
|
|
|
390
|
+
function parseTranscriptLine(line) {
|
|
391
|
+
try {
|
|
392
|
+
return JSON.parse(line);
|
|
393
|
+
} catch {
|
|
394
|
+
return { raw: String(line || '').slice(0, 1000) };
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
function formatTranscriptEvent(event = {}) {
|
|
399
|
+
if (event.raw) return `- ${event.raw}`;
|
|
400
|
+
const parts = [
|
|
401
|
+
event.ts || 'no-ts',
|
|
402
|
+
`[${event.phase || 'general'}]`,
|
|
403
|
+
event.agent || 'unknown',
|
|
404
|
+
event.type ? `(${event.type})` : null
|
|
405
|
+
].filter(Boolean);
|
|
406
|
+
const suffix = event.artifact ? ` (${event.artifact})` : '';
|
|
407
|
+
return `- ${parts.join(' ')}: ${String(event.message || '').slice(0, 500)}${suffix}`;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
function formatRuntimeTasks(tasks = []) {
|
|
411
|
+
if (!tasks.length) return ['- No assigned runtime tasks found.'];
|
|
412
|
+
return tasks.slice(0, 12).map((task) => {
|
|
413
|
+
const details = [
|
|
414
|
+
task.status || 'pending',
|
|
415
|
+
task.phase || task.role || 'team',
|
|
416
|
+
task.depends_on?.length ? `deps:${task.depends_on.join(',')}` : null,
|
|
417
|
+
task.file_paths?.length ? `files:${task.file_paths.slice(0, 3).join(',')}` : null
|
|
418
|
+
].filter(Boolean).join(' | ');
|
|
419
|
+
return `- ${task.task_id || 'task'} ${task.subject || task.symbolic_id || 'untitled'} (${details})`;
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
|
|
351
423
|
function trimLiveMarkdown(text) {
|
|
352
424
|
if (Buffer.byteLength(text) <= MAX_LIVE_BYTES) return text.endsWith('\n') ? text : `${text}\n`;
|
|
353
425
|
const marker = '## Live Events\n';
|