sneakoscope 0.6.93 → 0.6.98
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 +5 -5
- package/package.json +1 -1
- package/src/cli/main.mjs +56 -13
- package/src/cli/maintenance-commands.mjs +22 -4
- package/src/core/fsx.mjs +1 -1
- package/src/core/hooks-runtime.mjs +13 -22
- package/src/core/init.mjs +5 -5
- package/src/core/pipeline.mjs +1 -1
- package/src/core/routes.mjs +2 -2
- package/src/core/team-live.mjs +1 -1
- package/src/core/triwiki-attention.mjs +24 -2
- package/src/core/warp-ui.mjs +58 -7
- package/src/core/wiki-coordinate.mjs +12 -4
package/README.md
CHANGED
|
@@ -43,7 +43,7 @@ sks selftest --mock
|
|
|
43
43
|
|
|
44
44
|
| Area | What it does |
|
|
45
45
|
| --- | --- |
|
|
46
|
-
| CLI runtime | `sks
|
|
46
|
+
| CLI runtime | `sks warp open` and `sks --mad` explicitly launch Codex CLI with Warp; bare `sks` only prints help/readiness surfaces. |
|
|
47
47
|
| Codex App commands | Installs generated skills so `$Team`, `$From-Chat-IMG`, `$DFix`, `$QA-LOOP`, `$Goal`, `$DB`, `$Wiki`, `$Help`, and related routes are visible in prompt workflows. |
|
|
48
48
|
| Team orchestration | Runs substantial work through ambiguity handling, scouts, TriWiki refresh, debate, runtime task graphs, worker inboxes, implementation, review, cleanup, reflection, and Honest Mode. |
|
|
49
49
|
| From-Chat-IMG | Turns chat screenshots plus original attachments into source-bound work orders, then requires scoped QA evidence before completion. |
|
|
@@ -155,12 +155,12 @@ sks fix-path
|
|
|
155
155
|
### Open Codex CLI With Warp
|
|
156
156
|
|
|
157
157
|
```sh
|
|
158
|
-
sks
|
|
158
|
+
sks warp open
|
|
159
159
|
sks warp check
|
|
160
160
|
sks warp status --once
|
|
161
161
|
```
|
|
162
162
|
|
|
163
|
-
`sks` writes a Warp Launch Configuration for Codex CLI and opens it through Warp's public URI scheme when running in
|
|
163
|
+
`sks warp open` writes a Warp Launch Configuration for Codex CLI and opens it through Warp's public URI scheme only when that is explicitly requested. When it is already running inside Warp, SKS runs Codex in the current terminal session instead of opening another Warp window. `sks` and `sks warp check` are diagnostic/help surfaces and do not start a workspace.
|
|
164
164
|
|
|
165
165
|
### MAD Warp Launch
|
|
166
166
|
|
|
@@ -277,7 +277,7 @@ Use these inside Codex App or another agent prompt. They are prompt commands, no
|
|
|
277
277
|
| --- | --- |
|
|
278
278
|
| `$Team` | You want implementation, code changes, or substantial repo work. |
|
|
279
279
|
| `$From-Chat-IMG` | You have a chat screenshot plus original attachments and want each visible request mapped to work. |
|
|
280
|
-
| `$DFix` | You need a tiny design/content edit such as copy, label, color, spacing, or translation. |
|
|
280
|
+
| `$DFix` | You need a tiny design/content edit such as copy, label, color, spacing, or translation, with no Team/TriWiki/reflection recording and only a one-line DFix Honest check. |
|
|
281
281
|
| `$Answer` | You want an answer only and no implementation should start. |
|
|
282
282
|
| `$SKS` | You need setup, status, usage, or workflow help. |
|
|
283
283
|
| `$QA-LOOP` | You want UI/API dogfooding, safe fixes, and rechecks. |
|
|
@@ -308,7 +308,7 @@ sks selftest --mock
|
|
|
308
308
|
|
|
309
309
|
```sh
|
|
310
310
|
sks warp check
|
|
311
|
-
sks
|
|
311
|
+
sks warp open
|
|
312
312
|
```
|
|
313
313
|
|
|
314
314
|
For the high-reasoning full-access profile:
|
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.98",
|
|
5
5
|
"description": "Sneakoscope Codex: database-safe Codex CLI/App harness with Team, Goal, 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
|
@@ -43,7 +43,7 @@ import { buildPromptContext } from '../core/prompt-context-builder.mjs';
|
|
|
43
43
|
import { renderTeamDashboardState, writeTeamDashboardState } from '../core/team-dashboard-renderer.mjs';
|
|
44
44
|
import { GOAL_WORKFLOW_ARTIFACT } from '../core/goal-workflow.mjs';
|
|
45
45
|
import { CODEX_APP_DOCS_URL, codexAppIntegrationStatus, formatCodexAppStatus } from '../core/codex-app.mjs';
|
|
46
|
-
import { buildWarpLaunchConfigYaml, buildWarpLaunchPlan, buildWarpOpenArgs, runWarpLaunchConfigSyntaxCheck, warpReadiness, warpStatusKind, defaultWarpWorkspaceName, formatWarpBanner, launchWarpTeamView, launchWarpUi, platformWarpInstallHint, runWarpStatus, sanitizeWarpWorkspaceName, teamLaneStyle, writeWarpLaunchConfig } from '../core/warp-ui.mjs';
|
|
46
|
+
import { buildWarpLaunchConfigYaml, buildWarpLaunchPlan, buildWarpOpenArgs, isWarpShellSession, runWarpLaunchConfigSyntaxCheck, warpOpenLaunchDecision, warpReadiness, warpStatusKind, defaultWarpWorkspaceName, formatWarpBanner, launchWarpTeamView, launchWarpUi, platformWarpInstallHint, runWarpStatus, sanitizeWarpWorkspaceName, teamLaneStyle, writeWarpLaunchConfig } from '../core/warp-ui.mjs';
|
|
47
47
|
import { autoReviewProfileName, autoReviewStatus, autoReviewSummary, enableAutoReview, disableAutoReview, enableMadHighProfile, madHighProfileName } from '../core/auto-review.mjs';
|
|
48
48
|
import { buildTeamPlan, codeStructureCommand, dbCommand, defaultBeta, defaultVGraph, evalCommand, gcCommand, goalCommand, gxCommand, harnessCommand, hproofCommand, memoryCommand, migrateWikiContextPack, parseTeamCreateArgs, perfCommand, profileCommand, projectWikiClaims, proofFieldCommand, qaLoopCommand, quickstartCommand, researchCommand, statsCommand, team, teamWorkflowMarkdown, validateArtifactsCommand, wikiCommand, wikiVoxelRowCount, writeWikiContextPack } from './maintenance-commands.mjs';
|
|
49
49
|
|
|
@@ -65,12 +65,12 @@ export async function main(args) {
|
|
|
65
65
|
if (isAutoReviewFlag(args[0])) return autoReviewCommand('start', args.slice(1));
|
|
66
66
|
const [cmd, sub, ...rest] = args;
|
|
67
67
|
const tail = sub === undefined ? [] : [sub, ...rest];
|
|
68
|
-
if (!cmd) return
|
|
68
|
+
if (!cmd) return help();
|
|
69
69
|
if (cmd === '--help' || cmd === '-h') return help();
|
|
70
70
|
if (cmd === '--version' || cmd === '-v' || cmd === 'version') return version();
|
|
71
71
|
if (cmd === 'postinstall') return postinstall();
|
|
72
72
|
if (cmd === 'wizard' || cmd === 'ui') return wizard(tail);
|
|
73
|
-
if (cmd === 'warp') return
|
|
73
|
+
if (cmd === 'warp') return !sub || String(sub).startsWith('--') ? warpCommand('check', tail) : warpCommand(sub, rest);
|
|
74
74
|
if (cmd === 'auto-review' || cmd === 'autoreview') return autoReviewCommand(sub, rest);
|
|
75
75
|
if (cmd === 'update-check') return updateCheck(tail);
|
|
76
76
|
if (cmd === 'help') return help(tail);
|
|
@@ -139,7 +139,7 @@ Usage:
|
|
|
139
139
|
sks --mad [--high]
|
|
140
140
|
sks auto-review status|enable|start [--high]
|
|
141
141
|
sks --Auto-review [--high]
|
|
142
|
-
sks warp [--workspace name]
|
|
142
|
+
sks warp open [--workspace name]
|
|
143
143
|
sks warp status [--once]
|
|
144
144
|
sks dollar-commands [--json]
|
|
145
145
|
sks dfix
|
|
@@ -218,10 +218,6 @@ function shouldShowWizard() {
|
|
|
218
218
|
return Boolean(input.isTTY && output.isTTY && process.env.SKS_NO_WIZARD !== '1' && process.env.CI !== 'true');
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
-
function shouldLaunchWarpUi() {
|
|
222
|
-
return Boolean(input.isTTY && output.isTTY && process.env.SKS_NO_WARP !== '1' && process.env.CI !== 'true');
|
|
223
|
-
}
|
|
224
|
-
|
|
225
221
|
function isAutoReviewFlag(value) {
|
|
226
222
|
return /^--?auto[-_]?review$/i.test(String(value || ''));
|
|
227
223
|
}
|
|
@@ -593,12 +589,13 @@ Examples:
|
|
|
593
589
|
$DFix Change the CTA label to "Start"
|
|
594
590
|
|
|
595
591
|
Purpose:
|
|
596
|
-
Fast design/content fixes only. DFix bypasses the general SKS prompt pipeline and uses an ultralight task list.
|
|
592
|
+
Fast design/content fixes only. DFix bypasses the general SKS prompt pipeline and uses an ultralight, no-record task list.
|
|
597
593
|
|
|
598
594
|
Rules:
|
|
599
595
|
List the exact micro-edits, inspect only needed files, apply only those edits.
|
|
600
|
-
Do not run mission state, ambiguity gates, TriWiki
|
|
596
|
+
Do not run mission state, ambiguity gates, TriWiki/TriFix/reflection/state recording, Context7 routing, subagents, Goal, Research, eval, or broad redesign.
|
|
601
597
|
Run only cheap verification when useful.
|
|
598
|
+
Start the final answer with "DFix 완료 요약:" and include one "DFix 솔직모드:" line for verified, not verified, and remaining issues.
|
|
602
599
|
`);
|
|
603
600
|
}
|
|
604
601
|
|
|
@@ -1104,7 +1101,7 @@ async function warpCommand(sub = 'start', args = []) {
|
|
|
1104
1101
|
if (flag(args, '--json')) console.log(JSON.stringify(result, null, 2));
|
|
1105
1102
|
return;
|
|
1106
1103
|
}
|
|
1107
|
-
console.error('Usage: sks warp
|
|
1104
|
+
console.error('Usage: sks warp open|start|check|status|banner [--workspace name]');
|
|
1108
1105
|
process.exitCode = 1;
|
|
1109
1106
|
}
|
|
1110
1107
|
|
|
@@ -1421,7 +1418,7 @@ function usage(args = []) {
|
|
|
1421
1418
|
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 warp.'],
|
|
1422
1419
|
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.'],
|
|
1423
1420
|
deps: ['Dependencies', '', ' sks deps check [--json]', ' sks deps install [warp|codex|context7|all] [--yes]', '', 'warp on macOS uses Homebrew only after approval.'],
|
|
1424
|
-
warp: ['warp', '', ' sks', ' sks warp check', ' sks warp status --once', ' sks deps install warp'],
|
|
1421
|
+
warp: ['warp', '', ' sks warp open', ' sks warp check', ' sks warp status --once', ' sks deps install warp', '', 'Warp launch is explicit. Running bare `sks` prints help and never opens Warp by itself.'],
|
|
1425
1422
|
team: ['Team', '', ' sks team "task" executor:5 reviewer:2 user:1', ' sks team watch latest', ' sks team lane latest --agent analysis_scout_1 --follow', ' sks team message latest --from analysis_scout_1 --to executor_1 --message "handoff note"', ' sks team cleanup-warp latest', '', '$Team runs questions -> contract -> scouts -> TriWiki attention -> debate -> runtime graph/inbox -> fresh executors -> review -> cleanup -> reflection -> Honest.'],
|
|
1426
1423
|
'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'],
|
|
1427
1424
|
goal: ['Goal', '', ' sks goal create "task"', ' sks goal status latest', ' sks goal pause latest', ' sks goal resume latest', ' sks goal clear latest'],
|
|
@@ -1932,9 +1929,10 @@ async function selftest() {
|
|
|
1932
1929
|
maxOutputBytes: 64 * 1024
|
|
1933
1930
|
});
|
|
1934
1931
|
if (dfixPromptHook.code !== 0) throw new Error(`selftest failed: DFix prompt hook exited ${dfixPromptHook.code}: ${dfixPromptHook.stderr}`);
|
|
1932
|
+
if (await exists(path.join(tmp, '.sneakoscope', 'state', 'light-route-stop.json'))) throw new Error('selftest failed: DFix prompt hook created persistent light-route state');
|
|
1935
1933
|
const dfixStopHook = await runProcess(process.execPath, [path.join(packageRoot(), 'bin', 'sks.mjs'), 'hook', 'stop'], {
|
|
1936
1934
|
cwd: tmp,
|
|
1937
|
-
input: JSON.stringify({ cwd: tmp, last_assistant_message: 'DFix 완료 요약: CTA 라벨만
|
|
1935
|
+
input: JSON.stringify({ cwd: tmp, last_assistant_message: 'DFix 완료 요약: CTA 라벨만 변경했습니다.\nDFix 솔직모드: 검증=대상 파일 확인 통과, 미검증=없음, 남은 문제=없음.' }),
|
|
1938
1936
|
timeoutMs: 15000,
|
|
1939
1937
|
maxOutputBytes: 64 * 1024
|
|
1940
1938
|
});
|
|
@@ -2024,10 +2022,23 @@ async function selftest() {
|
|
|
2024
2022
|
if (!warpSyntax.ok || !warpLaunchYaml.includes('name: "sks-mad-selftest"') || !warpLaunchYaml.includes('commands:')) throw new Error('selftest failed: MAD Warp launch configuration was not generated with name and command');
|
|
2025
2023
|
const warpOpenArgs = buildWarpOpenArgs(workspacePlan);
|
|
2026
2024
|
if (!warpOpenArgs.includes('open') || !warpOpenArgs.some((arg) => String(arg).includes('warp://launch/sks-mad-selftest.yaml'))) throw new Error('selftest failed: MAD Warp launch URI is not stable by workspace name');
|
|
2025
|
+
if (!isWarpShellSession({ TERM_PROGRAM: 'WarpTerminal' })) throw new Error('selftest failed: Warp shell session env was not detected');
|
|
2026
|
+
const warpNestedDecision = warpOpenLaunchDecision({ env: { TERM_PROGRAM: 'WarpTerminal' } });
|
|
2027
|
+
if (warpNestedDecision.open || !warpNestedDecision.current_session) throw new Error('selftest failed: nested Warp launch was not redirected to current session');
|
|
2027
2028
|
const oldWarpConfigDir = process.env.SKS_WARP_LAUNCH_CONFIG_DIR;
|
|
2028
2029
|
process.env.SKS_WARP_LAUNCH_CONFIG_DIR = path.join(tmp, 'warp-launch-configs');
|
|
2029
2030
|
const writtenWarpConfig = await writeWarpLaunchConfig({ ...workspacePlan, command: 'codex', title: 'sks-mad-selftest' }, [{ cwd: tmp, command: 'codex --profile sks-mad-high', focused: true }]);
|
|
2030
2031
|
if (!(await exists(writtenWarpConfig.config_path)) || !writtenWarpConfig.record.launch_uri.includes('warp://launch/')) throw new Error('selftest failed: Warp launch configuration was not persisted for URI launch');
|
|
2032
|
+
const currentSessionLaunch = await launchWarpUi(['--workspace', 'sks-current-session-selftest'], {
|
|
2033
|
+
root: tmp,
|
|
2034
|
+
codex: { bin: 'printf', version: 'mock' },
|
|
2035
|
+
app: { ok: true, guidance: [] },
|
|
2036
|
+
warp: { ok: true, version: 'Warp.app' },
|
|
2037
|
+
env: { TERM_PROGRAM: 'WarpTerminal' },
|
|
2038
|
+
dryRunCurrentSession: true,
|
|
2039
|
+
quiet: true
|
|
2040
|
+
});
|
|
2041
|
+
if (!currentSessionLaunch.opened?.current_session || currentSessionLaunch.opened?.skipped) throw new Error('selftest failed: Warp shell launch did not stay in the current session');
|
|
2031
2042
|
if (oldWarpConfigDir === undefined) delete process.env.SKS_WARP_LAUNCH_CONFIG_DIR;
|
|
2032
2043
|
else process.env.SKS_WARP_LAUNCH_CONFIG_DIR = oldWarpConfigDir;
|
|
2033
2044
|
if (warpStatusKind({ ok: false, bin: null }) !== 'missing') throw new Error('selftest failed: missing warp was not labeled missing');
|
|
@@ -2525,7 +2536,10 @@ async function selftest() {
|
|
|
2525
2536
|
if (hookDfixJson.hookSpecificOutput?.additionalContext?.includes('Mission:')) throw new Error('selftest failed: $DFix created route mission state');
|
|
2526
2537
|
if (!hookDfixJson.hookSpecificOutput?.additionalContext?.includes('DFix ultralight pipeline active')) throw new Error('selftest failed: $DFix hook missing ultralight pipeline guidance');
|
|
2527
2538
|
if (!hookDfixJson.hookSpecificOutput?.additionalContext?.includes('Task list:')) throw new Error('selftest failed: $DFix hook missing micro task list');
|
|
2539
|
+
if (!hookDfixJson.hookSpecificOutput?.additionalContext?.includes('DFix 완료 요약')) throw new Error('selftest failed: $DFix hook missing no-record final marker guidance');
|
|
2540
|
+
if (!hookDfixJson.hookSpecificOutput?.additionalContext?.includes('DFix 솔직모드')) throw new Error('selftest failed: $DFix hook missing lightweight Honest Mode guidance');
|
|
2528
2541
|
if (!hookDfixJson.systemMessage?.includes('DFix ultralight')) throw new Error('selftest failed: $DFix hook missing ultralight system message');
|
|
2542
|
+
if (await exists(path.join(hookDfixTmp, '.sneakoscope', 'state', 'light-route-stop.json'))) throw new Error('selftest failed: $DFix hook created persistent light-route state');
|
|
2529
2543
|
const hookDfixState = await readJson(stateFile(hookDfixTmp), {});
|
|
2530
2544
|
if (String(hookDfixState.phase || '').includes('CLARIFICATION_AWAITING_ANSWERS')) throw new Error('selftest failed: $DFix state entered clarification gate');
|
|
2531
2545
|
const inferredDfixPayload = JSON.stringify({ cwd: hookTeamTmp, prompt: '버튼 라벨 바꿔줘' });
|
|
@@ -2808,6 +2822,8 @@ async function selftest() {
|
|
|
2808
2822
|
if (maxTextParsed.agentSessions !== 6 || maxTextParsed.roleCounts.executor !== 6) throw new Error('selftest failed: team max-agent text parsing');
|
|
2809
2823
|
const roleParsed = parseTeamCreateArgs(['executor:5', 'reviewer:2', 'user:1', '작업']);
|
|
2810
2824
|
if (roleParsed.roleCounts.executor !== 5 || roleParsed.roleCounts.reviewer !== 2 || roleParsed.agentSessions !== 5 || roleParsed.prompt !== '작업') throw new Error('selftest failed: team role-count parsing');
|
|
2825
|
+
const openWarpFlagParsed = parseTeamCreateArgs(['--open-warp', '작업']);
|
|
2826
|
+
if (openWarpFlagParsed.prompt !== '작업') throw new Error('selftest failed: team --open-warp leaked into prompt');
|
|
2811
2827
|
const roleTeamPlan = buildTeamPlan(teamId, '역할 팀 테스트', { roleCounts: roleParsed.roleCounts });
|
|
2812
2828
|
if (roleTeamPlan.roster.debate_team.length !== 5) throw new Error('selftest failed: executor role count not reflected in debate team size');
|
|
2813
2829
|
if (roleTeamPlan.roster.analysis_team.length !== 5) throw new Error('selftest failed: executor role count not reflected in analysis scout team');
|
|
@@ -2985,6 +3001,33 @@ async function selftest() {
|
|
|
2985
3001
|
if (!wikiPack.claims?.some((claim) => claim.id === 'selftest-memory-priority')) throw new Error('selftest failed: memory required_weight claim was not selected in TriWiki pack');
|
|
2986
3002
|
if (!wikiPack.claims?.some((claim) => claim.id === 'wiki-stack-current-docs-policy')) throw new Error('selftest failed: stack current-docs policy claim missing from TriWiki pack');
|
|
2987
3003
|
if (!wikiPack.claims?.some((claim) => claim.id === 'wiki-stack-current-docs-vercel-duration')) throw new Error('selftest failed: Vercel duration current-docs claim missing from TriWiki pack');
|
|
3004
|
+
const cacheHitPack = contextCapsule({
|
|
3005
|
+
mission: { id: 'cache-hit-selftest', coord: { rgba: { r: 24, g: 24, b: 24, a: 255 } } },
|
|
3006
|
+
role: 'worker',
|
|
3007
|
+
claims: [
|
|
3008
|
+
{ id: 'cache-hit-core-1', text: 'Selected high-similarity claim must keep an anchor for attention cache hits.', authority: 'code', risk: 'low', status: 'supported', freshness: 'fresh', trust_score: 0.82, coord: { rgba: { r: 24, g: 24, b: 24, a: 255 } } },
|
|
3009
|
+
{ id: 'cache-hit-core-2', text: 'Second selected high-similarity claim must keep an anchor for attention cache hits.', authority: 'code', risk: 'low', status: 'supported', freshness: 'fresh', trust_score: 0.82, coord: { rgba: { r: 24, g: 24, b: 25, a: 255 } } },
|
|
3010
|
+
...Array.from({ length: 8 }, (_, i) => ({ id: `cache-hit-distractor-${i}`, text: `High-priority distractor ${i}`, authority: 'code', risk: 'critical', status: 'supported', freshness: 'fresh', trust_score: 0.99, coord: { rgba: { r: 180 + i, g: 180, b: 180, a: 255 } } }))
|
|
3011
|
+
],
|
|
3012
|
+
q4: { mode: 'cache-hit-selftest' },
|
|
3013
|
+
q3: ['triwiki', 'cache-hit'],
|
|
3014
|
+
budget: { maxClaims: 2, maxWikiAnchors: 2, maxAttentionUse: 2 }
|
|
3015
|
+
});
|
|
3016
|
+
const cacheHitUseIds = new Set(cacheHitPack.attention?.use_first?.map((row) => row[0]) || []);
|
|
3017
|
+
if (!cacheHitUseIds.has('cache-hit-core-1') || !cacheHitUseIds.has('cache-hit-core-2')) throw new Error('selftest failed: selected TriWiki claims were not pinned as attention cache-hit anchors');
|
|
3018
|
+
const primingPack = contextCapsule({
|
|
3019
|
+
mission: { id: 'positive-recall-selftest', coord: { rgba: { r: 64, g: 96, b: 128, a: 255 } } },
|
|
3020
|
+
role: 'worker',
|
|
3021
|
+
claims: [
|
|
3022
|
+
{ id: 'positive-recall-guard', text: 'Do not imagine elephant during TriWiki recall.', authority: 'code', risk: 'high', status: 'supported', freshness: 'fresh', required_weight: 1.4, trust_score: 0.95, coord: { rgba: { r: 64, g: 96, b: 128, a: 255 } } }
|
|
3023
|
+
],
|
|
3024
|
+
q4: { mode: 'positive-recall-selftest' },
|
|
3025
|
+
q3: ['triwiki', 'positive-recall'],
|
|
3026
|
+
budget: { maxClaims: 1, maxWikiAnchors: 1, maxAttentionUse: 1, maxAttentionHydrate: 1 }
|
|
3027
|
+
});
|
|
3028
|
+
const primingClaim = primingPack.claims?.find((claim) => claim.id === 'positive-recall-guard');
|
|
3029
|
+
if (!primingClaim || /elephant|do\s+not/i.test(primingClaim.text || '') || primingClaim.text_policy !== 'positive_recall_negation_suppressed') throw new Error('selftest failed: TriWiki compact recall did not suppress negative priming text');
|
|
3030
|
+
if (!primingPack.attention?.hydrate_first?.some((row) => row[0] === 'positive-recall-guard' && String(row[1]).includes('negative_priming'))) throw new Error('selftest failed: negative priming claim was not source-hydration gated');
|
|
2988
3031
|
const dryRunPack = await writeWikiContextPack(tmp, ['--max-anchors', '4'], { dryRun: true });
|
|
2989
3032
|
if (wikiVoxelRowCount(dryRunPack.pack.wiki) !== 4) throw new Error('selftest failed: dry-run wiki pack did not build voxel rows');
|
|
2990
3033
|
if (await exists(dryRunPack.file)) throw new Error('selftest failed: wiki refresh dry-run wrote context pack');
|
|
@@ -901,6 +901,19 @@ export async function projectWikiClaims(root) {
|
|
|
901
901
|
required_weight: 1.45,
|
|
902
902
|
trust_score: 0.95
|
|
903
903
|
});
|
|
904
|
+
out.push({
|
|
905
|
+
id: 'wiki-positive-recall-priming-guard',
|
|
906
|
+
text: 'TriWiki compact recall should phrase selected guidance as the positive target behavior; anti-goal or failure-pattern wording should stay hydratable by source/hash instead of being pasted into the active recall text.',
|
|
907
|
+
authority: 'code',
|
|
908
|
+
risk: 'high',
|
|
909
|
+
status: 'supported',
|
|
910
|
+
freshness: 'fresh',
|
|
911
|
+
source: 'src/core/triwiki-attention.mjs',
|
|
912
|
+
file: 'src/core/triwiki-attention.mjs',
|
|
913
|
+
evidence_count: 3,
|
|
914
|
+
required_weight: 1.42,
|
|
915
|
+
trust_score: 0.94
|
|
916
|
+
});
|
|
904
917
|
out.push(...(await memoryWikiClaims(root)));
|
|
905
918
|
out.push(...(await userRequestSignalWikiClaims(root)));
|
|
906
919
|
out.push(...(await teamAnalysisWikiClaims(root)));
|
|
@@ -1348,10 +1361,12 @@ export async function gxCommand(sub, args) {
|
|
|
1348
1361
|
export async function team(args) {
|
|
1349
1362
|
const teamSubcommands = new Set(['log', 'tail', 'watch', 'lane', 'status', 'dashboard', 'event', 'message', 'cleanup-warp']);
|
|
1350
1363
|
if (teamSubcommands.has(args[0])) return teamCommand(args[0], args.slice(1));
|
|
1351
|
-
const
|
|
1364
|
+
const openWarp = flag(args, '--open-warp') || flag(args, '--warp-open');
|
|
1365
|
+
const cleanCreateArgs = args.filter((arg) => !['--open-warp', '--warp-open'].includes(String(arg)));
|
|
1366
|
+
const opts = parseTeamCreateArgs(cleanCreateArgs);
|
|
1352
1367
|
const { prompt, agentSessions, roleCounts, roster } = opts;
|
|
1353
1368
|
if (!prompt) {
|
|
1354
|
-
console.error('Usage: sks team "task" [executor:5 reviewer:2 user:1] [--agents N] [--json]');
|
|
1369
|
+
console.error('Usage: sks team "task" [executor:5 reviewer:2 user:1] [--agents N] [--open-warp] [--json]');
|
|
1355
1370
|
console.error(' sks team log|tail|watch|lane|status|message|cleanup-warp [mission-id|latest]');
|
|
1356
1371
|
console.error(' sks team event [mission-id|latest] --agent <name> --phase <phase> --message "..."');
|
|
1357
1372
|
console.error(' sks team message [mission-id|latest] --from <agent> --to <agent|all> --message "..."');
|
|
@@ -1414,7 +1429,7 @@ export async function team(args) {
|
|
|
1414
1429
|
questions: path.join(dir, 'questions.md'),
|
|
1415
1430
|
codex_agents: ['analysis_scout', 'team_consensus', 'implementation_worker', 'db_safety_reviewer', 'qa_reviewer']
|
|
1416
1431
|
};
|
|
1417
|
-
result.warp = await launchWarpTeamView({ root, missionId: id, plan, promptFile: result.workflow, json: flag(args, '--json') });
|
|
1432
|
+
result.warp = await launchWarpTeamView({ root, missionId: id, plan, promptFile: result.workflow, json: flag(args, '--json') || !openWarp });
|
|
1418
1433
|
if (flag(args, '--json')) return console.log(JSON.stringify(result, null, 2));
|
|
1419
1434
|
console.log(`Team mission created: ${id}`);
|
|
1420
1435
|
console.log(`Plan: ${path.relative(root, result.plan)}`);
|
|
@@ -1424,7 +1439,10 @@ export async function team(args) {
|
|
|
1424
1439
|
console.log(`Runtime graph: ${path.relative(root, result.team_graph)}`);
|
|
1425
1440
|
console.log(`Worker inbox: ${path.relative(root, result.worker_inbox_dir)}`);
|
|
1426
1441
|
console.log(`Live: ${path.relative(root, result.live)}`);
|
|
1427
|
-
if (result.warp.ready)
|
|
1442
|
+
if (result.warp.ready) {
|
|
1443
|
+
const warpState = result.warp.created ? 'opened' : 'not opened; use --open-warp for a launch configuration';
|
|
1444
|
+
console.log(`warp: ${warpState} ${result.warp.opened_lane_count || result.warp.agents.length} agent lane(s) in ${result.warp.workspace_ref || result.warp.workspace}`);
|
|
1445
|
+
}
|
|
1428
1446
|
else console.log(`warp: blocked (${Array.from(new Set(result.warp.blockers || [])).join('; ')})`);
|
|
1429
1447
|
console.log(`Watch: sks team watch ${id}`);
|
|
1430
1448
|
console.log('Use $Team in Codex App or the Warp launch view from this CLI flow to run scouts, debate/consensus, runtime graph/inbox handoff, then a fresh implementation team with disjoint ownership.');
|
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.98';
|
|
9
9
|
export const DEFAULT_PROCESS_TAIL_BYTES = 256 * 1024;
|
|
10
10
|
export const DEFAULT_PROCESS_TIMEOUT_MS = 30 * 60 * 1000;
|
|
11
11
|
|
|
@@ -16,7 +16,6 @@ const LIGHT_ROUTE_STOP_ARTIFACT = 'light-route-stop.json';
|
|
|
16
16
|
const STOP_REPEAT_GUARD_WINDOW_MS = 10 * 60 * 1000;
|
|
17
17
|
const STOP_REPEAT_GUARD_MAX_ENTRIES = 25;
|
|
18
18
|
const DEFAULT_STOP_REPEAT_GUARD_LIMIT = 2;
|
|
19
|
-
const LIGHT_ROUTE_STOP_WINDOW_MS = 10 * 60 * 1000;
|
|
20
19
|
|
|
21
20
|
async function loadHookPayload() {
|
|
22
21
|
const raw = await readStdin();
|
|
@@ -114,7 +113,6 @@ async function hookUserPrompt(root, state, payload, noQuestion) {
|
|
|
114
113
|
const route = routePrompt(prompt);
|
|
115
114
|
const bypassActiveRoute = route?.id === 'DFix' || route?.id === 'Answer';
|
|
116
115
|
const goalOverlay = activeGoalOverlayContext(state, route);
|
|
117
|
-
if (route?.id === 'DFix') await recordLightRouteStop(root, route, payload, prompt);
|
|
118
116
|
if (isClarificationAwaiting(state) && !looksLikeClarificationCancel(prompt)) {
|
|
119
117
|
const activeContext = await activeRouteContext(root, state);
|
|
120
118
|
const teamDigest = await teamLiveDigest(root, state);
|
|
@@ -237,7 +235,8 @@ async function hookPermission(root, state, payload, noQuestion) {
|
|
|
237
235
|
}
|
|
238
236
|
|
|
239
237
|
async function hookStop(root, state, payload, noQuestion) {
|
|
240
|
-
|
|
238
|
+
const last = extractLastMessage(payload);
|
|
239
|
+
if (!noQuestion && (hasDfixLightCompletion(last) || await consumeLightRouteStop(root, payload))) {
|
|
241
240
|
return {
|
|
242
241
|
continue: true,
|
|
243
242
|
systemMessage: 'SKS: DFix ultralight finalization accepted; full-route Honest Mode loopback is not required.'
|
|
@@ -246,7 +245,6 @@ async function hookStop(root, state, payload, noQuestion) {
|
|
|
246
245
|
const routeDecision = await evaluateStop(root, state, payload, { noQuestion });
|
|
247
246
|
if (routeDecision) return routeDecision;
|
|
248
247
|
if (!noQuestion) {
|
|
249
|
-
const last = extractLastMessage(payload);
|
|
250
248
|
if (!hasHonestMode(last)) {
|
|
251
249
|
const reason = 'SKS Honest Mode is required before finishing. Re-check the actual goal, verify evidence/tests, state gaps honestly, and only then provide the final answer. Include a short "SKS Honest Mode" or "솔직모드" section.';
|
|
252
250
|
const repeatDecision = await finalizationRepeatDecision(root, state, payload, reason, 'honest_mode_missing');
|
|
@@ -279,24 +277,6 @@ async function hookStop(root, state, payload, noQuestion) {
|
|
|
279
277
|
};
|
|
280
278
|
}
|
|
281
279
|
|
|
282
|
-
async function recordLightRouteStop(root, route = {}, payload = {}, prompt = '') {
|
|
283
|
-
const now = nowIso();
|
|
284
|
-
const expires = new Date(Date.parse(now) + LIGHT_ROUTE_STOP_WINDOW_MS).toISOString();
|
|
285
|
-
const file = path.join(root, '.sneakoscope', 'state', LIGHT_ROUTE_STOP_ARTIFACT);
|
|
286
|
-
await writeJsonAtomic(file, {
|
|
287
|
-
schema_version: 1,
|
|
288
|
-
route: route.id || null,
|
|
289
|
-
route_command: route.command || null,
|
|
290
|
-
mode: route.mode || null,
|
|
291
|
-
conversation_id: conversationId(payload),
|
|
292
|
-
prompt_hash: sha256(String(prompt || '')).slice(0, 16),
|
|
293
|
-
created_at: now,
|
|
294
|
-
expires_at: expires,
|
|
295
|
-
pending_stop_bypass: true,
|
|
296
|
-
stop_policy: 'dfix_ultralight_bypasses_full_route_honest_mode'
|
|
297
|
-
}).catch(() => null);
|
|
298
|
-
}
|
|
299
|
-
|
|
300
280
|
async function consumeLightRouteStop(root, payload = {}) {
|
|
301
281
|
const file = path.join(root, '.sneakoscope', 'state', LIGHT_ROUTE_STOP_ARTIFACT);
|
|
302
282
|
const record = await readJson(file, null).catch(() => null);
|
|
@@ -315,6 +295,17 @@ async function consumeLightRouteStop(root, payload = {}) {
|
|
|
315
295
|
return true;
|
|
316
296
|
}
|
|
317
297
|
|
|
298
|
+
function hasDfixLightCompletion(text) {
|
|
299
|
+
const s = String(text || '');
|
|
300
|
+
const marker = /^\s*(?:\*\*)?\s*(?:\$?DFix|dfix)\s*(?:완료\s*요약|completion\s+summary)\s*[::]/im.test(s);
|
|
301
|
+
if (!marker) return false;
|
|
302
|
+
const honest = /^\s*(?:\*\*)?\s*(?:\$?DFix|dfix)\s*(?:솔직모드|honest(?:\s+mode)?)\s*[::]/im.test(s);
|
|
303
|
+
if (!honest) return false;
|
|
304
|
+
const verification = /(검증|확인|통과|verified|verification|checked|evidence|근거)/i.test(s);
|
|
305
|
+
const gap = /(미검증|남은|문제|gap|remaining|not verified|not run|blocker|차단|불가|없음|none)/i.test(s);
|
|
306
|
+
return verification && gap;
|
|
307
|
+
}
|
|
308
|
+
|
|
318
309
|
function explicitConversationId(payload = {}) {
|
|
319
310
|
return payload.conversation_id || payload.thread_id || payload.session_id || payload.chat_id || null;
|
|
320
311
|
}
|
package/src/core/init.mjs
CHANGED
|
@@ -90,7 +90,7 @@ function isSksManagedHook(hook) {
|
|
|
90
90
|
return hook.type === 'command' && /\bhook\s+(?:user-prompt-submit|pre-tool|post-tool|permission-request|stop)\b/.test(command) && /\b(?:sks|sneakoscope|sks\.mjs)\b/.test(command);
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
const AGENTS_BLOCK = "\n# Sneakoscope Codex Managed Rules\n\nThis repository uses Sneakoscope Codex.\n\n## Core Rules\n\n- Codex native `/goal` workflows are the persisted continuation surface; Ralph is removed from the user-facing SKS surface.\n- Keep runtime state bounded: raw logs go to files, prompts get tails/summaries, and `sks gc` may prune stale artifacts.\n- Before substantive work, SKS checks npm for a newer package. If newer, ask update-now vs skip-for-this-conversation.\n- Versioning is managed by the SKS pre-commit hook; check `sks versioning status`. Bypass only with `SKS_DISABLE_VERSIONING=1`.\n- Installed harness files are immutable to LLM edits: `.codex/*`, `.agents/skills/`, `.codex/agents/`, `.sneakoscope/*policy*.json`, `AGENTS.md`, and `node_modules/sneakoscope`. The Sneakoscope engine source repo is the only automatic exception.\n- OMX/DCodex conflicts block setup/doctor. Show `sks conflicts prompt`; cleanup requires explicit human approval.\n- Do not stop at a plan when implementation was requested. Finish, verify, or report the hard blocker.\n- Do not create unrequested fallback implementation code. If the requested path is impossible, block with evidence instead of inventing substitute behavior.\n\n## Routes\n\n- General execution/code-changing prompts default to `$Team`: analysis scouts, TriWiki refresh/validate, read-only debate, consensus, concrete runtime task graph/inboxes, fresh executor team, review, integration, Honest Mode.\n- `$Computer-Use` / `$CU` is the maximum-speed Codex Computer Use lane for UI/browser/visual tasks: skip Team debate and upfront TriWiki loops, use Codex Computer Use directly, then refresh/validate TriWiki and run Honest Mode at final closeout.\n- `$Goal` is a fast bridge/overlay for Codex native `/goal` create/pause/resume/clear persistence controls; implementation continues through the selected SKS execution route.\n- TriWiki recall must stay bounded. Use `sks wiki sweep` to record demote, soft-forget, archive, delete, promote-to-skill, and promote-to-rule candidates instead of injecting every old claim.\n- Team missions must keep schema-backed evidence current: `work-order-ledger.json`, `effort-decision.json`, `team-dashboard-state.json`, and route-specific visual/dogfood artifacts where applicable. Use `sks validate-artifacts latest` before claiming those artifacts pass.\n- `$DFix` is only for tiny design/content edits and bypasses the main pipeline. `$Answer`, `$Help`, and `$Wiki` stay lightweight.\n- For code work, surface route/guard/write scopes first, split independent worker scopes when available, and keep parent-owned integration and verification.\n- Design work reads `design.md`; if missing, use `design-system-builder`. Image/logo/raster assets use `imagegen`.\n- Research, AutoResearch, performance, token, accuracy, SEO/GEO, or workflow-improvement claims need experiment/eval evidence. Do not claim live model accuracy without a scored dataset.\n- Treat handwritten files above 3000 lines as split-review risks. Run `sks code-structure scan` and prefer extraction before adding substantial logic.\n\n## Evidence And Context\n\n- Context7 is required for external libraries, APIs, MCPs, package managers, SDKs, and generated docs: resolve-library-id then query-docs.\n- When tech stack, framework, package, runtime, or deployment-platform versions change, use Context7 or official vendor web docs, record current syntax/security/limit guidance as high-priority TriWiki claims, then refresh and validate before coding.\n- TriWiki is the context-tracking SSOT for long-running missions, Team handoffs, and context-pressure recovery. Read `.sneakoscope/wiki/context-pack.json` before each stage, use `attention.use_first` for compact high-trust recall, hydrate `attention.hydrate_first` from source before risky or lower-trust decisions, refresh after findings or artifact changes, and validate before handoffs/final claims.\n- Source priority: current code/tests/config, decision contract, vgraph, beta, GX render/snapshot metadata, LLM Wiki coordinate index, then model knowledge only if allowed.\n- Final response before stop: summarize what was done, what changed for the user/repo, what was verified, and what remains unverified or blocked; then run Honest Mode. Say what passed and what was not verified.\n- `$From-Chat-IMG` uses forensic visual effort, not ordinary Team effort. Completion is blocked until source inventory, visual mapping, work-order coverage, scoped dogfood/QA, and post-fix verification artifacts are present and valid.\n\n## Safety\n\n- Database access is high risk. Use read-only inspection by default; live data mutation is out of scope unless a sealed contract allows local or branch-only migration files.\n- MAD and MAD-SKS widen only explicit scoped permissions; they still do not authorize unrequested fallback implementation code.\n- Task completion requires relevant tests or justification, zero unsupported critical claims, accepted visual/wiki drift, and final evidence.\n\n## Codex App\n\nUse `.codex/SNEAKOSCOPE.md`, generated `.agents/skills`, `.codex/hooks.json`, and SKS dollar commands (`$sks`, `$team`, `$computer-use`, `$cu`, `$goal`, `$dfix`, `$qa-loop`, etc.) as the app control surface.\n";
|
|
93
|
+
const AGENTS_BLOCK = "\n# Sneakoscope Codex Managed Rules\n\nThis repository uses Sneakoscope Codex.\n\n## Core Rules\n\n- Codex native `/goal` workflows are the persisted continuation surface; Ralph is removed from the user-facing SKS surface.\n- Keep runtime state bounded: raw logs go to files, prompts get tails/summaries, and `sks gc` may prune stale artifacts.\n- Before substantive work, SKS checks npm for a newer package. If newer, ask update-now vs skip-for-this-conversation.\n- Versioning is managed by the SKS pre-commit hook; check `sks versioning status`. Bypass only with `SKS_DISABLE_VERSIONING=1`.\n- Installed harness files are immutable to LLM edits: `.codex/*`, `.agents/skills/`, `.codex/agents/`, `.sneakoscope/*policy*.json`, `AGENTS.md`, and `node_modules/sneakoscope`. The Sneakoscope engine source repo is the only automatic exception.\n- OMX/DCodex conflicts block setup/doctor. Show `sks conflicts prompt`; cleanup requires explicit human approval.\n- Do not stop at a plan when implementation was requested. Finish, verify, or report the hard blocker.\n- Do not create unrequested fallback implementation code. If the requested path is impossible, block with evidence instead of inventing substitute behavior.\n\n## Routes\n\n- General execution/code-changing prompts default to `$Team`: analysis scouts, TriWiki refresh/validate, read-only debate, consensus, concrete runtime task graph/inboxes, fresh executor team, review, integration, Honest Mode.\n- `$Computer-Use` / `$CU` is the maximum-speed Codex Computer Use lane for UI/browser/visual tasks: skip Team debate and upfront TriWiki loops, use Codex Computer Use directly, then refresh/validate TriWiki and run Honest Mode at final closeout.\n- `$Goal` is a fast bridge/overlay for Codex native `/goal` create/pause/resume/clear persistence controls; implementation continues through the selected SKS execution route.\n- TriWiki recall must stay bounded. Use `sks wiki sweep` to record demote, soft-forget, archive, delete, promote-to-skill, and promote-to-rule candidates instead of injecting every old claim.\n- Team missions must keep schema-backed evidence current: `work-order-ledger.json`, `effort-decision.json`, `team-dashboard-state.json`, and route-specific visual/dogfood artifacts where applicable. Use `sks validate-artifacts latest` before claiming those artifacts pass.\n- `$DFix` is only for tiny design/content edits and bypasses the main pipeline, Team, TriWiki/TriFix/reflection recording, and persistent route state; it still uses a one-line DFix-specific Honest check before final. `$Answer`, `$Help`, and `$Wiki` stay lightweight.\n- For code work, surface route/guard/write scopes first, split independent worker scopes when available, and keep parent-owned integration and verification.\n- Design work reads `design.md`; if missing, use `design-system-builder`. Image/logo/raster assets use `imagegen`.\n- Research, AutoResearch, performance, token, accuracy, SEO/GEO, or workflow-improvement claims need experiment/eval evidence. Do not claim live model accuracy without a scored dataset.\n- Treat handwritten files above 3000 lines as split-review risks. Run `sks code-structure scan` and prefer extraction before adding substantial logic.\n\n## Evidence And Context\n\n- Context7 is required for external libraries, APIs, MCPs, package managers, SDKs, and generated docs: resolve-library-id then query-docs.\n- When tech stack, framework, package, runtime, or deployment-platform versions change, use Context7 or official vendor web docs, record current syntax/security/limit guidance as high-priority TriWiki claims, then refresh and validate before coding.\n- TriWiki is the context-tracking SSOT for long-running missions, Team handoffs, and context-pressure recovery. Read `.sneakoscope/wiki/context-pack.json` before each stage, use `attention.use_first` for compact high-trust recall, hydrate `attention.hydrate_first` from source before risky or lower-trust decisions, refresh after findings or artifact changes, and validate before handoffs/final claims.\n- Source priority: current code/tests/config, decision contract, vgraph, beta, GX render/snapshot metadata, LLM Wiki coordinate index, then model knowledge only if allowed.\n- Final response before stop: summarize what was done, what changed for the user/repo, what was verified, and what remains unverified or blocked; then run Honest Mode. Say what passed and what was not verified.\n- `$From-Chat-IMG` uses forensic visual effort, not ordinary Team effort. Completion is blocked until source inventory, visual mapping, work-order coverage, scoped dogfood/QA, and post-fix verification artifacts are present and valid.\n\n## Safety\n\n- Database access is high risk. Use read-only inspection by default; live data mutation is out of scope unless a sealed contract allows local or branch-only migration files.\n- MAD and MAD-SKS widen only explicit scoped permissions; they still do not authorize unrequested fallback implementation code.\n- Task completion requires relevant tests or justification, zero unsupported critical claims, accepted visual/wiki drift, and final evidence.\n\n## Codex App\n\nUse `.codex/SNEAKOSCOPE.md`, generated `.agents/skills`, `.codex/hooks.json`, and SKS dollar commands (`$sks`, `$team`, `$computer-use`, `$cu`, `$goal`, `$dfix`, `$qa-loop`, etc.) as the app control surface.\n";
|
|
94
94
|
|
|
95
95
|
export async function initProject(root, opts = {}) {
|
|
96
96
|
const created = [];
|
|
@@ -514,20 +514,20 @@ function codexAppQuickReference(scope, commandPrefix) {
|
|
|
514
514
|
'dollar-commands:',
|
|
515
515
|
...DOLLAR_COMMANDS.map((c) => `- \`${c.command}\`: ${c.route}`),
|
|
516
516
|
`Picker skills: ${DOLLAR_COMMAND_ALIASES.map((x) => x.app_skill).join(', ')}.`,
|
|
517
|
-
'Routing: Answer direct, DFix ultralight, execution routes ask only scope/safety/behavior/acceptance-changing questions before sealing answers.',
|
|
517
|
+
'Routing: Answer direct, DFix ultralight no-record, execution routes ask only scope/safety/behavior/acceptance-changing questions before sealing answers.',
|
|
518
518
|
`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.`,
|
|
519
519
|
`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.`,
|
|
520
520
|
`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.`,
|
|
521
521
|
stackCurrentDocsPolicyText(commandPrefix),
|
|
522
|
-
`Team Warp view: ${commandPrefix} team "task"
|
|
523
|
-
`Runtime: open Codex App once, then run ${commandPrefix} bootstrap, ${commandPrefix} deps check, or ${commandPrefix}
|
|
522
|
+
`Team Warp view: ${commandPrefix} team "task" prepares live watch/lane commands without opening Warp by default; add --open-warp when you explicitly want a Warp Launch Configuration with an overview watch pane plus color-coded split per-agent lanes; ${commandPrefix} team lane latest --agent analysis_scout_1 --follow shows one agent's status, assigned runtime tasks, recent agent events, direct messages, and fallback global tail; ${commandPrefix} team message latest --from analysis_scout_1 --to executor_1 --message "handoff note" mirrors bounded agent communication into transcript/lane panes; ${commandPrefix} team cleanup-warp latest marks the SKS launch record complete and asks follow panes to show a cleanup summary then stop.`,
|
|
523
|
+
`Runtime: open Codex App once, then run ${commandPrefix} bootstrap, ${commandPrefix} deps check, or ${commandPrefix} warp open when you explicitly want a Warp/Codex CLI launch.`,
|
|
524
524
|
`Guard: generated harness files are immutable outside the engine source repo; check ${commandPrefix} guard check; conflicts use ${commandPrefix} conflicts prompt with human approval.`
|
|
525
525
|
].join('\n') + '\n';
|
|
526
526
|
}
|
|
527
527
|
|
|
528
528
|
export async function installSkills(root) {
|
|
529
529
|
const skills = {
|
|
530
|
-
'dfix': `---\nname: dfix\ndescription: Ultralight fast design/content fix mode for $DFix or $dfix requests and inferred simple edits such as text color, copy, labels, spacing, or translation.\n---\n\nUse for tiny copy/color/label/spacing/translation edits. List exact micro-edits, inspect only needed files, apply only those edits, and run cheap verification. Bypass broad SKS routing, Goal, Research, eval, redesign, and repeated full-route Honest Mode loops. Read \`design.md\` for UI work when present; use imagegen for image/logo/raster assets.\n`,
|
|
530
|
+
'dfix': `---\nname: dfix\ndescription: Ultralight fast design/content fix mode for $DFix or $dfix requests and inferred simple edits such as text color, copy, labels, spacing, or translation.\n---\n\nUse for tiny copy/color/label/spacing/translation edits. List exact micro-edits, inspect only needed files, apply only those edits, and run cheap verification. Bypass broad SKS routing, mission state, TriWiki/TriFix/reflection/state recording, Goal, Research, eval, redesign, and repeated full-route Honest Mode loops. Start the final answer with \`DFix 완료 요약:\` and include one \`DFix 솔직모드:\` line covering verified, not verified, and remaining issues. Read \`design.md\` for UI work when present; use imagegen for image/logo/raster assets.\n`,
|
|
531
531
|
'answer': `---\nname: answer\ndescription: Answer-only research route for ordinary questions that should not start implementation.\n---\n\nUse for explanations, comparisons, status, facts, source-backed research, or docs guidance. Use repo/TriWiki first for project-local facts; hydrate low-trust claims from source. Browse or use Context7 for current external package/API/framework/MCP docs. End with a concise answer summary plus Honest Mode; do not create missions, subagents, or file edits.\n`,
|
|
532
532
|
'sks': `---\nname: sks\ndescription: General Sneakoscope Codex command route for $SKS or $sks usage, setup, status, and workflow help.\n---\n\nUse local SKS commands: bootstrap, deps, commands, quickstart, codex-app, context7, guard, conflicts, reasoning, wiki, pipeline. Promote code-changing work to Team unless Answer/DFix/Help/Wiki/safety route fits. Surface route/guard/scope, use TriWiki, do not edit installed harness files outside this engine repo, and require human-approved conflict cleanup.\n`,
|
|
533
533
|
'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`,
|
package/src/core/pipeline.mjs
CHANGED
|
@@ -82,7 +82,7 @@ export function dfixQuickContext(prompt, route = routePrompt(prompt)) {
|
|
|
82
82
|
'2. Inspect only the files needed to locate that target.',
|
|
83
83
|
'3. Apply only the listed design/content edit; for UI/UX micro-edits read design.md when present, and use imagegen for any image/logo/raster asset.',
|
|
84
84
|
'4. Run only cheap verification when useful, such as syntax check, focused test, or local render smoke.',
|
|
85
|
-
'5. Final response:
|
|
85
|
+
'5. Final response: start with `DFix 완료 요약:` and include one `DFix 솔직모드:` line with verified / not verified / remaining issue status. Do not create TriWiki/TriFix/reflection/state records and do not enter repeated full-route Honest Mode loops.'
|
|
86
86
|
].join('\n');
|
|
87
87
|
}
|
|
88
88
|
|
package/src/core/routes.mjs
CHANGED
|
@@ -150,7 +150,7 @@ export const ROUTES = [
|
|
|
150
150
|
command: '$DFix',
|
|
151
151
|
mode: 'DFIX',
|
|
152
152
|
route: 'fast design/content fix',
|
|
153
|
-
description: 'Small UI/content edits such as text color, copy, label, spacing, or translation. Bypasses the general SKS pipeline and runs an ultralight task-list path.',
|
|
153
|
+
description: 'Small UI/content edits such as text color, copy, label, spacing, or translation. Bypasses the general SKS pipeline and runs an ultralight, no-record task-list path.',
|
|
154
154
|
requiredSkills: ['dfix'],
|
|
155
155
|
lifecycle: ['micro_task_list', 'targeted_inspection', 'listed_edits_only', 'cheap_verification'],
|
|
156
156
|
context7Policy: 'optional',
|
|
@@ -372,7 +372,7 @@ export const COMMAND_CATALOG = [
|
|
|
372
372
|
{ name: 'root', usage: 'sks root [--json]', description: 'Show whether SKS is using a project root or the per-user global SKS runtime root.' },
|
|
373
373
|
{ name: 'deps', usage: 'sks deps check|install [warp|codex|context7|all] [--yes]', description: 'Check or guided-install Node/npm PATH, Codex CLI/App, Context7, Browser Use, Computer Use, warp, and Homebrew on macOS.' },
|
|
374
374
|
{ 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.' },
|
|
375
|
-
{ name: 'warp', usage: 'sks warp
|
|
375
|
+
{ name: 'warp', usage: 'sks warp open|check|status [--workspace name]', description: 'Explicitly open the SKS warp runtime, or check/status without launching Warp.' },
|
|
376
376
|
{ name: 'mad', usage: 'sks --mad [--high]', description: 'Open a one-shot warp Codex CLI workspace with the SKS MAD full-access auto-review profile.' },
|
|
377
377
|
{ name: 'auto-review', usage: 'sks auto-review status|enable|start [--high] | sks --Auto-review --high', description: 'Enable Codex automatic approval review and launch SKS warp with the auto-review profile.' },
|
|
378
378
|
{ name: 'dollar-commands', usage: 'sks dollar-commands [--json]', description: 'List Codex App $ commands such as $DFix and $Team.' },
|
package/src/core/team-live.mjs
CHANGED
|
@@ -219,7 +219,7 @@ export function parseTeamSpecArgs(args = []) {
|
|
|
219
219
|
i++;
|
|
220
220
|
continue;
|
|
221
221
|
}
|
|
222
|
-
if (arg === '--json') continue;
|
|
222
|
+
if (arg === '--json' || arg === '--open-warp' || arg === '--warp-open') continue;
|
|
223
223
|
cleanArgs.push(args[i]);
|
|
224
224
|
}
|
|
225
225
|
return { cleanArgs, ...normalizeTeamSpec({ roleCounts, agentSessions: explicitSession }) };
|
|
@@ -156,9 +156,28 @@ function attentionRow(claim, anchor, reason = '') {
|
|
|
156
156
|
return reason ? [claim.id, reason] : [claim.id, anchor?.rgba, anchor?.h];
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
+
const NEGATIVE_PRIMING_RE = /\b(do\s+not|don't|dont|never|avoid|forbid(?:den)?|must\s+not|unsupported|conflicted)\b|하지\s*마|하지\s*말|말아야|금지|안\s*(?:돼|됨|된다)|비\s*상식/i;
|
|
160
|
+
|
|
161
|
+
export function negativePrimingRisk(claim = {}) {
|
|
162
|
+
return NEGATIVE_PRIMING_RE.test(String(claim.text || claim.claim || ''));
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export function positiveRecallText(claim = {}) {
|
|
166
|
+
const text = String(claim.text || claim.claim || '').trim();
|
|
167
|
+
if (!negativePrimingRisk({ ...claim, text })) return text;
|
|
168
|
+
const route = `${claim.id || ''} ${claim.source || ''} ${claim.file || ''} ${text}`.toLowerCase();
|
|
169
|
+
if (/dfix/.test(route)) return 'Keep DFix on the ultralight route with a concise completion summary and cheap verification.';
|
|
170
|
+
if (/computer[-_\s]?use|playwright|selenium|puppeteer|browser automation|chrome mcp/.test(route)) return 'Use Codex Computer Use as the UI/browser evidence source for visual verification claims.';
|
|
171
|
+
if (/fallback|substitute|compatibility shim|mock behavior/.test(route)) return 'Implement the requested path directly and block with evidence when that path is impossible.';
|
|
172
|
+
if (/clarification|ambiguity|question|ask|질문|모호/.test(route)) return 'Infer safely from current code and TriWiki, then ask only scope-changing questions.';
|
|
173
|
+
if (/triwiki|wiki|cache|attention|hydrate|memory|메모리/.test(route)) return 'Use positive TriWiki target recall: selected cache-hit anchors first, with source hydration before risky claims.';
|
|
174
|
+
return `Follow the positive target behavior for ${claim.id || claim.source || 'this guardrail'}; hydrate source before acting on the guardrail.`;
|
|
175
|
+
}
|
|
176
|
+
|
|
159
177
|
function hydrateReason(claim = {}) {
|
|
160
178
|
const action = trustAction(claim);
|
|
161
179
|
if (action !== 'use') return `trust_action:${action}`;
|
|
180
|
+
if (negativePrimingRisk(claim)) return 'negative_priming:hydrate_source';
|
|
162
181
|
if (['high', 'critical'].includes(claim.risk)) return `risk:${claim.risk}`;
|
|
163
182
|
if (claim.status !== 'supported') return `status:${claim.status || 'unknown'}`;
|
|
164
183
|
return '';
|
|
@@ -227,7 +246,8 @@ export function contextCapsule({ mission, role = 'worker', contractHash = null,
|
|
|
227
246
|
claims: claimsWithTrust,
|
|
228
247
|
q4,
|
|
229
248
|
q3,
|
|
230
|
-
maxAnchors: budget.maxWikiAnchors ?? (role.includes('verifier') ? 16 : 7)
|
|
249
|
+
maxAnchors: budget.maxWikiAnchors ?? (role.includes('verifier') ? 16 : 7),
|
|
250
|
+
pinAnchorIds: selected.map((claim) => claim.id)
|
|
231
251
|
});
|
|
232
252
|
const wiki = budget.verboseWiki ? fullWiki : compactWikiCoordinateIndex(fullWiki);
|
|
233
253
|
const anchorRows = Array.isArray(wiki.a) ? wiki.a : [];
|
|
@@ -251,13 +271,15 @@ export function contextCapsule({ mission, role = 'worker', contractHash = null,
|
|
|
251
271
|
}),
|
|
252
272
|
claims: selected.map((c) => {
|
|
253
273
|
const anchor = anchorsById.get(c.id);
|
|
274
|
+
const text = positiveRecallText(c);
|
|
254
275
|
const row = {
|
|
255
276
|
id: c.id,
|
|
256
|
-
text
|
|
277
|
+
text,
|
|
257
278
|
source: c.source,
|
|
258
279
|
rgba: anchor?.rgba,
|
|
259
280
|
h: anchor?.h
|
|
260
281
|
};
|
|
282
|
+
if (text !== String(c.text || '')) row.text_policy = 'positive_recall_negation_suppressed';
|
|
261
283
|
if (budget.verboseClaims) {
|
|
262
284
|
row.status = c.status;
|
|
263
285
|
row.risk = c.risk;
|
package/src/core/warp-ui.mjs
CHANGED
|
@@ -64,6 +64,31 @@ export function warpLaunchUri(configPathOrFilename) {
|
|
|
64
64
|
return `warp://launch/${encodeURIComponent(filename)}`;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
export function isWarpShellSession(env = process.env) {
|
|
68
|
+
if (truthyEnv(env.WARP_IS_LOCAL_SHELL_SESSION)) return true;
|
|
69
|
+
if (truthyEnv(env.WARP_SESSION_ID)) return true;
|
|
70
|
+
return String(env.TERM_PROGRAM || '') === 'WarpTerminal';
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function warpOpenLaunchDecision(opts = {}) {
|
|
74
|
+
const args = Array.isArray(opts.args) ? opts.args : [];
|
|
75
|
+
const env = opts.env || process.env;
|
|
76
|
+
if (opts.forceOpen === true || opts.open === true || args.includes('--open') || args.includes('--force-open') || truthyEnv(env.SKS_WARP_FORCE_OPEN) || truthyEnv(env.SKS_WARP_OPEN)) {
|
|
77
|
+
return { open: true, reason: 'forced' };
|
|
78
|
+
}
|
|
79
|
+
if (opts.skipOpen === true || opts.noOpen === true || opts.open === false || args.includes('--no-open') || truthyEnv(env.SKS_WARP_SKIP_OPEN) || truthyEnv(env.SKS_WARP_NO_OPEN)) {
|
|
80
|
+
return { open: false, reason: 'opening disabled by option/env' };
|
|
81
|
+
}
|
|
82
|
+
if (isWarpShellSession(env)) {
|
|
83
|
+
return { open: false, current_session: true, reason: 'already inside Warp shell session' };
|
|
84
|
+
}
|
|
85
|
+
return { open: true, reason: 'default' };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function truthyEnv(value) {
|
|
89
|
+
return value !== undefined && value !== null && !/^(?:0|false|no|off)$/i.test(String(value).trim());
|
|
90
|
+
}
|
|
91
|
+
|
|
67
92
|
export async function findWarpApp() {
|
|
68
93
|
const env = process.env.SKS_WARP_APP || process.env.WARP_APP;
|
|
69
94
|
if (env && await exists(env)) return env;
|
|
@@ -222,7 +247,7 @@ export function formatWarpBanner(status = null) {
|
|
|
222
247
|
' $DFix $Answer $SKS $Team $QA-LOOP $Goal $Research $AutoResearch $DB $GX $Wiki $Help',
|
|
223
248
|
'',
|
|
224
249
|
'CLI-first runtime:',
|
|
225
|
-
' sks
|
|
250
|
+
' sks warp open explicitly open a Warp Codex CLI launch configuration',
|
|
226
251
|
' sks --mad open one-shot MAD full-access auto-review launch configuration',
|
|
227
252
|
' sks team "task" prepare Team mission and Warp split-pane live view',
|
|
228
253
|
'',
|
|
@@ -317,18 +342,20 @@ export async function writeWarpLaunchConfig(plan = {}, panes = []) {
|
|
|
317
342
|
return { config_path: configPath, yaml, record };
|
|
318
343
|
}
|
|
319
344
|
|
|
320
|
-
export async function openWarpLaunchConfig(configPath) {
|
|
345
|
+
export async function openWarpLaunchConfig(configPath, opts = {}) {
|
|
321
346
|
const uri = warpLaunchUri(configPath);
|
|
347
|
+
const decision = warpOpenLaunchDecision(opts);
|
|
348
|
+
if (!decision.open) return { ok: false, skipped: true, reason: decision.reason, uri, stdout: '', stderr: '' };
|
|
322
349
|
if (process.platform === 'darwin') {
|
|
323
350
|
const run = await runProcess('open', [uri], { timeoutMs: 5000, maxOutputBytes: 16 * 1024 }).catch((err) => ({ code: 1, stderr: err.message, stdout: '' }));
|
|
324
|
-
return { ok: run.code === 0, uri, stdout: run.stdout || '', stderr: run.stderr || '' };
|
|
351
|
+
return { ok: run.code === 0, skipped: false, reason: decision.reason, uri, stdout: run.stdout || '', stderr: run.stderr || '' };
|
|
325
352
|
}
|
|
326
353
|
const opener = await which('xdg-open').catch(() => null);
|
|
327
354
|
if (opener) {
|
|
328
355
|
const run = await runProcess(opener, [uri], { timeoutMs: 5000, maxOutputBytes: 16 * 1024 }).catch((err) => ({ code: 1, stderr: err.message, stdout: '' }));
|
|
329
|
-
return { ok: run.code === 0, uri, stdout: run.stdout || '', stderr: run.stderr || '' };
|
|
356
|
+
return { ok: run.code === 0, skipped: false, reason: decision.reason, uri, stdout: run.stdout || '', stderr: run.stderr || '' };
|
|
330
357
|
}
|
|
331
|
-
return { ok: false, uri, stderr: 'No platform URI opener found' };
|
|
358
|
+
return { ok: false, skipped: false, reason: decision.reason, uri, stdout: '', stderr: 'No platform URI opener found' };
|
|
332
359
|
}
|
|
333
360
|
|
|
334
361
|
export async function launchWarpUi(args = [], opts = {}) {
|
|
@@ -344,16 +371,40 @@ export async function launchWarpUi(args = [], opts = {}) {
|
|
|
344
371
|
if (args.includes('--status-only')) return { plan };
|
|
345
372
|
const command = codexLaunchCommand(plan.root, plan.codex.bin, plan.codexArgs);
|
|
346
373
|
const written = await writeWarpLaunchConfig({ ...plan, command }, [{ cwd: plan.root, command, focused: true }]);
|
|
347
|
-
const
|
|
374
|
+
const decision = warpOpenLaunchDecision({ ...opts, args });
|
|
375
|
+
const opened = decision.current_session
|
|
376
|
+
? runWarpCommandInCurrentSession(command, { cwd: plan.root, dryRun: opts.dryRunCurrentSession || opts.dryRun })
|
|
377
|
+
: await openWarpLaunchConfig(written.config_path, { ...opts, args });
|
|
348
378
|
if (!args.includes('--quiet')) {
|
|
349
379
|
console.log(`SKS Warp launch configuration: ${written.config_path}`);
|
|
350
380
|
console.log(`Warp URI: ${written.record.launch_uri}`);
|
|
351
|
-
if (opened.
|
|
381
|
+
if (opened.current_session) console.log(`Warp: current session (${opened.reason || 'already inside Warp shell session'})`);
|
|
382
|
+
else if (opened.ok) console.log('Warp: opened');
|
|
383
|
+
else if (opened.skipped) console.log(`Warp: skipped (${opened.reason || 'opening disabled'})`);
|
|
352
384
|
else if (!opened.skipped) console.log(`Warp: not opened (${opened.stderr || 'URI opener failed'})`);
|
|
353
385
|
}
|
|
354
386
|
return { plan, created: true, config_path: written.config_path, launch_uri: written.record.launch_uri, opened };
|
|
355
387
|
}
|
|
356
388
|
|
|
389
|
+
function runWarpCommandInCurrentSession(command, opts = {}) {
|
|
390
|
+
if (opts.dryRun) return { ok: true, current_session: true, skipped: false, reason: 'dry run current Warp session', command };
|
|
391
|
+
const shell = process.env.SHELL || '/bin/sh';
|
|
392
|
+
const run = spawnSync(shell, ['-lc', command], {
|
|
393
|
+
cwd: opts.cwd || process.cwd(),
|
|
394
|
+
stdio: 'inherit',
|
|
395
|
+
env: process.env
|
|
396
|
+
});
|
|
397
|
+
return {
|
|
398
|
+
ok: run.status === 0,
|
|
399
|
+
current_session: true,
|
|
400
|
+
skipped: false,
|
|
401
|
+
reason: 'ran in current Warp shell session',
|
|
402
|
+
code: run.status,
|
|
403
|
+
signal: run.signal || null,
|
|
404
|
+
stderr: run.error?.message || ''
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
|
|
357
408
|
function printWarpLaunchBlocked(plan, opts = {}) {
|
|
358
409
|
if (opts.concise) {
|
|
359
410
|
console.error('SKS Warp launch blocked.');
|
|
@@ -166,10 +166,13 @@ export function wikiAnchorFromClaim(claim = {}, index = 0) {
|
|
|
166
166
|
};
|
|
167
167
|
}
|
|
168
168
|
|
|
169
|
-
export function buildWikiCoordinateIndex({ mission = {}, claims = [], q4 = {}, q3 = [], maxAnchors = 24 } = {}) {
|
|
169
|
+
export function buildWikiCoordinateIndex({ mission = {}, claims = [], q4 = {}, q3 = [], maxAnchors = 24, pinAnchorIds = [] } = {}) {
|
|
170
170
|
const missionCoord = normalizeWikiCoord(mission.coord || {}, mission.id || JSON.stringify(q3 || []));
|
|
171
171
|
const claimsById = new Map((claims || []).map((claim, index) => [String(claim.id || `claim-${index + 1}`), claim]));
|
|
172
|
-
const
|
|
172
|
+
const limit = Math.max(0, Number(maxAnchors) || 0);
|
|
173
|
+
const pinned = new Set((pinAnchorIds || []).map((id) => String(id)).filter(Boolean));
|
|
174
|
+
const compareAnchor = (a, b) => b.pri - a.pri || b.sim - a.sim || a.id.localeCompare(b.id);
|
|
175
|
+
const candidates = (claims || [])
|
|
173
176
|
.map((claim, index) => {
|
|
174
177
|
const anchor = wikiAnchorFromClaim(claim, index);
|
|
175
178
|
const coord = { domainAngle: anchor.c[0], layerRadius: anchor.c[1], phase: anchor.c[2], concentration: anchor.c[3] };
|
|
@@ -178,8 +181,13 @@ export function buildWikiCoordinateIndex({ mission = {}, claims = [], q4 = {}, q
|
|
|
178
181
|
const risk = riskScore(claim.risk);
|
|
179
182
|
return { ...anchor, sim: round6(wikiCoordSimilarity(missionCoord, coord)), pri: round4((required * 0.52) + (trust * 0.3) + (risk * 0.18)) };
|
|
180
183
|
})
|
|
181
|
-
.sort(
|
|
182
|
-
|
|
184
|
+
.sort(compareAnchor);
|
|
185
|
+
const pinnedAnchors = candidates.filter((anchor) => pinned.has(anchor.id)).sort(compareAnchor).slice(0, limit);
|
|
186
|
+
const pinnedAnchorIds = new Set(pinnedAnchors.map((anchor) => anchor.id));
|
|
187
|
+
const anchors = [
|
|
188
|
+
...pinnedAnchors,
|
|
189
|
+
...candidates.filter((anchor) => !pinnedAnchorIds.has(anchor.id)).slice(0, Math.max(0, limit - pinnedAnchors.length))
|
|
190
|
+
];
|
|
183
191
|
return {
|
|
184
192
|
schema: WIKI_COORD_SCHEMA,
|
|
185
193
|
channel_map: {
|