sneakoscope 0.6.89 → 0.6.90

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sneakoscope",
3
3
  "displayName": "ㅅㅋㅅ",
4
- "version": "0.6.89",
4
+ "version": "0.6.90",
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
@@ -1924,6 +1924,24 @@ async function selftest() {
1924
1924
  if (trippedStop) throw new Error('selftest failed: compliance loop guard did not terminally trip');
1925
1925
  const loopBlocker = await readJson(path.join(loopMission.dir, 'hard-blocker.json'), null);
1926
1926
  if (loopBlocker?.reason !== 'compliance_loop_guard_tripped') throw new Error('selftest failed: compliance loop guard did not write hard blocker');
1927
+ await setCurrent(tmp, loopState);
1928
+ const dfixPromptHook = await runProcess(process.execPath, [path.join(packageRoot(), 'bin', 'sks.mjs'), 'hook', 'user-prompt-submit'], {
1929
+ cwd: tmp,
1930
+ input: JSON.stringify({ cwd: tmp, prompt: '$DFix Change the CTA label only' }),
1931
+ timeoutMs: 15000,
1932
+ maxOutputBytes: 64 * 1024
1933
+ });
1934
+ if (dfixPromptHook.code !== 0) throw new Error(`selftest failed: DFix prompt hook exited ${dfixPromptHook.code}: ${dfixPromptHook.stderr}`);
1935
+ const dfixStopHook = await runProcess(process.execPath, [path.join(packageRoot(), 'bin', 'sks.mjs'), 'hook', 'stop'], {
1936
+ cwd: tmp,
1937
+ input: JSON.stringify({ cwd: tmp, last_assistant_message: 'DFix 완료 요약: CTA 라벨만 변경했습니다. 검증: 대상 파일 확인 통과. 남은 문제: 없음.' }),
1938
+ timeoutMs: 15000,
1939
+ maxOutputBytes: 64 * 1024
1940
+ });
1941
+ if (dfixStopHook.code !== 0) throw new Error(`selftest failed: DFix stop hook exited ${dfixStopHook.code}: ${dfixStopHook.stderr}`);
1942
+ const dfixStop = JSON.parse(dfixStopHook.stdout || '{}');
1943
+ if (dfixStop.decision === 'block' || dfixStop.continue === false) throw new Error(`selftest failed: DFix stop hook was blocked: ${dfixStopHook.stdout}`);
1944
+ if (!String(dfixStop.systemMessage || '').includes('DFix ultralight finalization accepted')) throw new Error('selftest failed: DFix stop hook did not use the ultralight finalization bypass');
1927
1945
  await writeJsonAtomic(path.join(loopMission.dir, 'team-roster.json'), { schema_version: 1, mission_id: loopMission.id, confirmed: true });
1928
1946
  await writeJsonAtomic(path.join(loopMission.dir, 'team-session-cleanup.json'), { schema_version: 1, passed: true, all_sessions_closed: true, outstanding_sessions: 0, live_transcript_finalized: true });
1929
1947
  await writeJsonAtomic(path.join(loopMission.dir, 'team-gate.json'), { passed: true, team_roster_confirmed: true, analysis_artifact: true, triwiki_refreshed: true, triwiki_validated: true, consensus_artifact: true, implementation_team_fresh: true, review_artifact: true, integration_evidence: true, session_cleanup: true });
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.89';
8
+ export const PACKAGE_VERSION = '0.6.90';
9
9
  export const DEFAULT_PROCESS_TAIL_BYTES = 256 * 1024;
10
10
  export const DEFAULT_PROCESS_TIMEOUT_MS = 30 * 60 * 1000;
11
11
 
@@ -12,9 +12,11 @@ const TEAM_DIGEST_MESSAGE_CHARS = 180;
12
12
  const TEAM_DIGEST_CONTEXT_CHARS = 1600;
13
13
  const TEAM_DIGEST_SYSTEM_CHARS = 260;
14
14
  const STOP_REPEAT_GUARD_ARTIFACT = 'stop-hook-repeat-guard.json';
15
+ const LIGHT_ROUTE_STOP_ARTIFACT = 'light-route-stop.json';
15
16
  const STOP_REPEAT_GUARD_WINDOW_MS = 10 * 60 * 1000;
16
17
  const STOP_REPEAT_GUARD_MAX_ENTRIES = 25;
17
18
  const DEFAULT_STOP_REPEAT_GUARD_LIMIT = 2;
19
+ const LIGHT_ROUTE_STOP_WINDOW_MS = 10 * 60 * 1000;
18
20
 
19
21
  async function loadHookPayload() {
20
22
  const raw = await readStdin();
@@ -111,6 +113,7 @@ async function hookUserPrompt(root, state, payload, noQuestion) {
111
113
  const command = dollarCommand(prompt);
112
114
  const route = routePrompt(prompt);
113
115
  const bypassActiveRoute = route?.id === 'DFix' || route?.id === 'Answer';
116
+ if (route?.id === 'DFix') await recordLightRouteStop(root, route, payload, prompt);
114
117
  if (isClarificationAwaiting(state) && !looksLikeClarificationCancel(prompt)) {
115
118
  const activeContext = await activeRouteContext(root, state);
116
119
  const teamDigest = await teamLiveDigest(root, state);
@@ -222,6 +225,12 @@ async function hookPermission(root, state, payload, noQuestion) {
222
225
  }
223
226
 
224
227
  async function hookStop(root, state, payload, noQuestion) {
228
+ if (!noQuestion && await consumeLightRouteStop(root, payload)) {
229
+ return {
230
+ continue: true,
231
+ systemMessage: 'SKS: DFix ultralight finalization accepted; full-route Honest Mode loopback is not required.'
232
+ };
233
+ }
225
234
  const routeDecision = await evaluateStop(root, state, payload, { noQuestion });
226
235
  if (routeDecision) return routeDecision;
227
236
  if (!noQuestion) {
@@ -258,6 +267,46 @@ async function hookStop(root, state, payload, noQuestion) {
258
267
  };
259
268
  }
260
269
 
270
+ async function recordLightRouteStop(root, route = {}, payload = {}, prompt = '') {
271
+ const now = nowIso();
272
+ const expires = new Date(Date.parse(now) + LIGHT_ROUTE_STOP_WINDOW_MS).toISOString();
273
+ const file = path.join(root, '.sneakoscope', 'state', LIGHT_ROUTE_STOP_ARTIFACT);
274
+ await writeJsonAtomic(file, {
275
+ schema_version: 1,
276
+ route: route.id || null,
277
+ route_command: route.command || null,
278
+ mode: route.mode || null,
279
+ conversation_id: conversationId(payload),
280
+ prompt_hash: sha256(String(prompt || '')).slice(0, 16),
281
+ created_at: now,
282
+ expires_at: expires,
283
+ pending_stop_bypass: true,
284
+ stop_policy: 'dfix_ultralight_bypasses_full_route_honest_mode'
285
+ }).catch(() => null);
286
+ }
287
+
288
+ async function consumeLightRouteStop(root, payload = {}) {
289
+ const file = path.join(root, '.sneakoscope', 'state', LIGHT_ROUTE_STOP_ARTIFACT);
290
+ const record = await readJson(file, null).catch(() => null);
291
+ if (!record?.pending_stop_bypass) return false;
292
+ if (record.route !== 'DFix') return false;
293
+ const nowMs = Date.now();
294
+ const expiresMs = Date.parse(record.expires_at || '');
295
+ if (!Number.isFinite(expiresMs) || expiresMs < nowMs) return false;
296
+ const currentConversation = conversationId(payload);
297
+ if (record.conversation_id && explicitConversationId(payload) && record.conversation_id !== currentConversation) return false;
298
+ await writeJsonAtomic(file, {
299
+ ...record,
300
+ pending_stop_bypass: false,
301
+ consumed_at: nowIso()
302
+ }).catch(() => null);
303
+ return true;
304
+ }
305
+
306
+ function explicitConversationId(payload = {}) {
307
+ return payload.conversation_id || payload.thread_id || payload.session_id || payload.chat_id || null;
308
+ }
309
+
261
310
  async function finalizationRepeatDecision(root, state = {}, payload = {}, reason = '', kind = 'finalization') {
262
311
  const now = nowIso();
263
312
  const guardPath = path.join(root, '.sneakoscope', 'state', STOP_REPEAT_GUARD_ARTIFACT);
package/src/core/init.mjs CHANGED
@@ -527,7 +527,7 @@ function codexAppQuickReference(scope, commandPrefix) {
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, and redesign. 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, 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`,
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`,
@@ -81,7 +81,7 @@ export function dfixQuickContext(prompt, route = routePrompt(prompt)) {
81
81
  '2. Inspect only the files needed to locate that target.',
82
82
  '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.',
83
83
  '4. Run only cheap verification when useful, such as syntax check, focused test, or local render smoke.',
84
- '5. Final response: one short completion summary explaining what changed, plus verification or the exact blocker.'
84
+ '5. Final response: one short DFix completion summary explaining what changed, plus cheap verification or the exact blocker. Do not enter repeated full-route Honest Mode loops.'
85
85
  ].join('\n');
86
86
  }
87
87