sneakoscope 0.9.10 → 0.9.11
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 +1 -1
- package/src/cli/install-helpers.mjs +97 -7
- package/src/cli/main.mjs +2 -1
- package/src/core/fsx.mjs +1 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sneakoscope",
|
|
3
3
|
"displayName": "ㅅㅋㅅ",
|
|
4
|
-
"version": "0.9.
|
|
4
|
+
"version": "0.9.11",
|
|
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",
|
|
@@ -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 { ensureDir, exists, globalSksRoot, packageRoot, readText, runProcess, tmpdir, which, writeTextAtomic } from '../core/fsx.mjs';
|
|
6
|
+
import { ensureDir, exists, globalSksRoot, packageRoot, PACKAGE_VERSION, readText, runProcess, tmpdir, which, writeTextAtomic } from '../core/fsx.mjs';
|
|
7
7
|
import { getCodexInfo } from '../core/codex-adapter.mjs';
|
|
8
8
|
import { formatHarnessConflictReport, llmHarnessCleanupPrompt, scanHarnessConflicts } from '../core/harness-conflicts.mjs';
|
|
9
9
|
import { initProject, installSkills } from '../core/init.mjs';
|
|
@@ -32,6 +32,7 @@ export async function postinstall({ bootstrap }) {
|
|
|
32
32
|
console.log('\nSKS installed.');
|
|
33
33
|
const shim = await ensureSksCommandDuringInstall();
|
|
34
34
|
if (shim.status === 'present') console.log(`SKS command: available (${shim.command}).`);
|
|
35
|
+
else if (shim.status === 'repaired') console.log(`SKS command: stale PATH shim repaired (${shim.command}).`);
|
|
35
36
|
else if (shim.status === 'created') console.log(`SKS command: shim created at ${shim.command}.`);
|
|
36
37
|
else if (shim.status === 'created_not_on_path') console.log(`SKS command: shim created at ${shim.command}. Add ${path.dirname(shim.command)} to PATH, or run npx -y -p sneakoscope sks.`);
|
|
37
38
|
else if (shim.status === 'skipped') console.log(`SKS command: skipped (${shim.reason}).`);
|
|
@@ -1367,10 +1368,13 @@ function escapeRegExp(value) {
|
|
|
1367
1368
|
export async function ensureSksCommandDuringInstall(opts = {}) {
|
|
1368
1369
|
if (process.env.SKS_SKIP_POSTINSTALL_SHIM === '1' && !opts.force) return { status: 'skipped', reason: 'SKS_SKIP_POSTINSTALL_SHIM=1' };
|
|
1369
1370
|
const pathEnv = opts.pathEnv ?? process.env.PATH ?? '';
|
|
1370
|
-
const existing = await findCommandOnPath('sks', pathEnv);
|
|
1371
|
-
if (isStableSksBin(existing)) return { status: 'present', command: existing };
|
|
1372
1371
|
const nodeBin = opts.nodeBin || process.execPath;
|
|
1373
1372
|
const target = opts.target || path.join(packageRoot(), 'bin', 'sks.mjs');
|
|
1373
|
+
const repair = await reconcileSksPathShimsDuringInstall({ ...opts, pathEnv, nodeBin, target });
|
|
1374
|
+
if (repair.status === 'repaired') return { ...repair, command: repair.command || repair.repaired?.[0]?.path || target };
|
|
1375
|
+
if (repair.status === 'failed') return repair;
|
|
1376
|
+
const existing = await findCommandOnPath('sks', pathEnv);
|
|
1377
|
+
if (isStableSksBin(existing)) return { status: 'present', command: existing };
|
|
1374
1378
|
const dirs = candidateShimDirs(pathEnv, opts.home || process.env.HOME);
|
|
1375
1379
|
const script = process.platform === 'win32'
|
|
1376
1380
|
? `@echo off\r\n"${nodeBin}" "${target}" %*\r\n`
|
|
@@ -1394,6 +1398,80 @@ export async function ensureSksCommandDuringInstall(opts = {}) {
|
|
|
1394
1398
|
return { status: 'failed', error: lastError };
|
|
1395
1399
|
}
|
|
1396
1400
|
|
|
1401
|
+
export async function selftestSksShimRepair() {
|
|
1402
|
+
const staleShimTmp = tmpdir();
|
|
1403
|
+
const staleBin = path.join(staleShimTmp, 'old-prefix', 'bin');
|
|
1404
|
+
const stalePkg = path.join(staleShimTmp, 'old-prefix', 'lib', 'node_modules', 'sneakoscope');
|
|
1405
|
+
await ensureDir(path.join(stalePkg, 'bin'));
|
|
1406
|
+
await ensureDir(staleBin);
|
|
1407
|
+
await writeTextAtomic(path.join(stalePkg, 'package.json'), JSON.stringify({ name: 'sneakoscope', version: '0.0.1' }, null, 2));
|
|
1408
|
+
await writeTextAtomic(path.join(stalePkg, 'bin', 'sks.mjs'), '#!/usr/bin/env node\nconsole.log("sneakoscope 0.0.1");\n');
|
|
1409
|
+
await fsp.chmod(path.join(stalePkg, 'bin', 'sks.mjs'), 0o755).catch(() => {});
|
|
1410
|
+
await fsp.symlink(path.join(stalePkg, 'bin', 'sks.mjs'), path.join(staleBin, 'sks'));
|
|
1411
|
+
const repair = await ensureSksCommandDuringInstall({ force: true, pathEnv: staleBin, home: path.join(staleShimTmp, 'home') });
|
|
1412
|
+
if (repair.status !== 'repaired') throw new Error(`selftest: stale global sks shim was not repaired (${repair.status})`);
|
|
1413
|
+
const run = await runProcess(path.join(staleBin, 'sks'), ['--version'], { timeoutMs: 10000, maxOutputBytes: 16 * 1024 });
|
|
1414
|
+
if (run.code !== 0 || !String(run.stdout || '').includes(PACKAGE_VERSION)) throw new Error('selftest: repaired stale sks shim does not run current package version');
|
|
1415
|
+
return { ok: true, repaired: repair.repaired || [] };
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
async function reconcileSksPathShimsDuringInstall(opts = {}) {
|
|
1419
|
+
if (process.env.SKS_SKIP_POSTINSTALL_SHIM_REPAIR === '1' && !opts.force) return { status: 'skipped', reason: 'SKS_SKIP_POSTINSTALL_SHIM_REPAIR=1' };
|
|
1420
|
+
const target = opts.target || path.join(packageRoot(), 'bin', 'sks.mjs');
|
|
1421
|
+
const nodeBin = opts.nodeBin || process.execPath;
|
|
1422
|
+
const currentVersion = await installedPackageVersion(packageRoot());
|
|
1423
|
+
const commands = await findCommandsOnPath(['sks', 'sneakoscope'], opts.pathEnv ?? process.env.PATH ?? '');
|
|
1424
|
+
const repaired = [];
|
|
1425
|
+
const failed = [];
|
|
1426
|
+
for (const command of commands) {
|
|
1427
|
+
const info = await inspectSksPathShim(command.path, { target, currentVersion });
|
|
1428
|
+
if (!info.repairable) continue;
|
|
1429
|
+
const script = process.platform === 'win32'
|
|
1430
|
+
? `@echo off\r\n"${nodeBin}" "${target}" %*\r\n`
|
|
1431
|
+
: `#!/bin/sh\nexec "${nodeBin}" "${target}" "$@"\n`;
|
|
1432
|
+
try {
|
|
1433
|
+
await writeTextAtomic(command.path, script);
|
|
1434
|
+
if (process.platform !== 'win32') await fsp.chmod(command.path, 0o755).catch(() => {});
|
|
1435
|
+
repaired.push({ path: command.path, name: command.name, previous_version: info.version || null, target });
|
|
1436
|
+
} catch (err) {
|
|
1437
|
+
failed.push({ path: command.path, name: command.name, previous_version: info.version || null, error: err.message });
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
if (repaired.length) return { status: 'repaired', command: repaired[0].path, repaired, failed };
|
|
1441
|
+
if (failed.length) return { status: 'failed', error: failed.map((entry) => `${entry.path}: ${entry.error}`).join('; '), failed };
|
|
1442
|
+
return { status: 'present' };
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1445
|
+
async function inspectSksPathShim(candidate, opts = {}) {
|
|
1446
|
+
if (!candidate || isTransientNpmBinPath(candidate)) return { repairable: false, reason: 'transient_or_missing' };
|
|
1447
|
+
const target = path.resolve(opts.target || path.join(packageRoot(), 'bin', 'sks.mjs'));
|
|
1448
|
+
const resolved = await fsp.realpath(candidate).catch(() => candidate);
|
|
1449
|
+
if (path.resolve(resolved) === target) return { repairable: false, reason: 'current_target' };
|
|
1450
|
+
const packageDir = sksPackageRootForBin(resolved) || sksPackageRootForBin(candidate);
|
|
1451
|
+
if (!packageDir) return { repairable: false, reason: 'not_sneakoscope_bin' };
|
|
1452
|
+
const version = await installedPackageVersion(packageDir);
|
|
1453
|
+
const currentVersion = opts.currentVersion || await installedPackageVersion(packageRoot());
|
|
1454
|
+
if (!version || !currentVersion || compareVersions(version, currentVersion) >= 0) return { repairable: false, reason: 'not_older', version, current_version: currentVersion };
|
|
1455
|
+
return { repairable: true, version, current_version: currentVersion, package_dir: packageDir, resolved };
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
function sksPackageRootForBin(file) {
|
|
1459
|
+
const normalized = String(file || '').split(path.sep).join('/');
|
|
1460
|
+
const marker = '/node_modules/sneakoscope/bin/';
|
|
1461
|
+
const idx = normalized.lastIndexOf(marker);
|
|
1462
|
+
if (idx < 0) return null;
|
|
1463
|
+
return normalized.slice(0, idx + '/node_modules/sneakoscope'.length).split('/').join(path.sep);
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
async function installedPackageVersion(root) {
|
|
1467
|
+
const pkg = await readJsonMaybe(path.join(root, 'package.json'));
|
|
1468
|
+
return pkg?.version || (root === packageRoot() ? PACKAGE_VERSION : null);
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
async function readJsonMaybe(file) {
|
|
1472
|
+
try { return JSON.parse(await fsp.readFile(file, 'utf8')); } catch { return null; }
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1397
1475
|
function candidateShimDirs(pathEnv, home) {
|
|
1398
1476
|
const seen = new Set();
|
|
1399
1477
|
const out = [];
|
|
@@ -1413,14 +1491,26 @@ function candidateShimDirs(pathEnv, home) {
|
|
|
1413
1491
|
}
|
|
1414
1492
|
|
|
1415
1493
|
async function findCommandOnPath(name, pathEnv) {
|
|
1494
|
+
const found = await findCommandsOnPath([name], pathEnv);
|
|
1495
|
+
return found[0]?.path || null;
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
async function findCommandsOnPath(names, pathEnv) {
|
|
1416
1499
|
const suffixes = process.platform === 'win32' ? ['.cmd', '.exe', ''] : [''];
|
|
1500
|
+
const out = [];
|
|
1501
|
+
const seen = new Set();
|
|
1417
1502
|
for (const dir of String(pathEnv || '').split(path.delimiter).filter(Boolean)) {
|
|
1418
|
-
for (const
|
|
1419
|
-
const
|
|
1420
|
-
|
|
1503
|
+
for (const name of names) {
|
|
1504
|
+
for (const suffix of suffixes) {
|
|
1505
|
+
const candidate = path.join(dir, `${name}${suffix}`);
|
|
1506
|
+
const key = path.resolve(candidate);
|
|
1507
|
+
if (seen.has(key) || !await exists(candidate)) continue;
|
|
1508
|
+
seen.add(key);
|
|
1509
|
+
out.push({ name, path: candidate });
|
|
1510
|
+
}
|
|
1421
1511
|
}
|
|
1422
1512
|
}
|
|
1423
|
-
return
|
|
1513
|
+
return out;
|
|
1424
1514
|
}
|
|
1425
1515
|
|
|
1426
1516
|
async function ensureGlobalContext7DuringInstall() {
|
package/src/cli/main.mjs
CHANGED
|
@@ -81,7 +81,7 @@ import { OPENCLAW_SKILL_NAME, installOpenClawSkill } from '../core/openclaw.mjs'
|
|
|
81
81
|
import { buildTmuxLaunchPlan, buildTmuxOpenArgs, codexLaunchCommand, createTmuxSession, defaultCodexLaunchArgs, isTmuxShellSession, runTmuxLaunchPlanSyntaxCheck, shouldAutoAttachTmux, sksAsciiLogo, tmuxReadiness, tmuxStatusKind, defaultTmuxSessionName, formatTmuxBanner, launchMadTmuxUi, launchTmuxTeamView, launchTmuxUi, platformTmuxInstallHint, reconcileTmuxTeamCockpit, runTmuxStatus, sanitizeTmuxSessionName, sweepCodexLbTmuxSessions, sweepTmuxTeamSurfaces, teamLaneStyle } from '../core/tmux-ui.mjs';
|
|
82
82
|
import { autoReviewProfileName, autoReviewStatus, autoReviewSummary, enableAutoReview, disableAutoReview, enableMadHighProfile, madHighProfileName } from '../core/auto-review.mjs';
|
|
83
83
|
import { context7Command } from './context7-command.mjs';
|
|
84
|
-
import { askPostinstallQuestion, checkCodexLbResponseChain, checkContext7, checkRequiredSkills, codexLbChatgptBackupPath, codexLbStatus, configureCodexLb, ensureCodexCliTool, ensureGlobalCodexFastModeDuringInstall, ensureGlobalCodexSkillsDuringInstall, ensureProjectContext7Config, ensureRelatedCliTools, ensureSksCommandDuringInstall, ensureTmuxCliTool, formatCodexLbRepairResultText, formatCodexLbStatusText, globalCodexSkillsRoot, maybePromptCodexLbSetupForLaunch, maybePromptCodexUpdateForLaunch, postinstall, postinstallBootstrapDecision, releaseCodexLbAuthHold, repairCodexLbAuth, selftestCodexLb, shouldAutoApproveInstall, unselectCodexLbProvider } from './install-helpers.mjs';
|
|
84
|
+
import { askPostinstallQuestion, checkCodexLbResponseChain, checkContext7, checkRequiredSkills, codexLbChatgptBackupPath, codexLbStatus, configureCodexLb, ensureCodexCliTool, ensureGlobalCodexFastModeDuringInstall, ensureGlobalCodexSkillsDuringInstall, ensureProjectContext7Config, ensureRelatedCliTools, ensureSksCommandDuringInstall, ensureTmuxCliTool, formatCodexLbRepairResultText, formatCodexLbStatusText, globalCodexSkillsRoot, maybePromptCodexLbSetupForLaunch, maybePromptCodexUpdateForLaunch, postinstall, postinstallBootstrapDecision, releaseCodexLbAuthHold, repairCodexLbAuth, selftestCodexLb, selftestSksShimRepair, shouldAutoApproveInstall, unselectCodexLbProvider } from './install-helpers.mjs';
|
|
85
85
|
import { buildTeamPlan, codeStructureCommand, dbCommand, defaultBeta, defaultVGraph, evalCommand, gcCommand, goalCommand, gxCommand, harnessCommand, hproofCommand, madHighCommand as runMadHighCommand, memoryCommand, migrateWikiContextPack, parseTeamCreateArgs, perfCommand, profileCommand, projectWikiClaims, proofFieldCommand, qaLoopCommand, quickstartCommand, researchCommand, skillDreamCommand, statsCommand, team, teamWorkflowMarkdown, validateArtifactsCommand, wikiCommand, wikiVoxelRowCount, writeWikiContextPack } from './maintenance-commands.mjs';
|
|
86
86
|
import { openClawCommand } from './openclaw-command.mjs';
|
|
87
87
|
import { recallPulseCommand } from './recallpulse-command.mjs';
|
|
@@ -2330,6 +2330,7 @@ async function selftest() {
|
|
|
2330
2330
|
if (await exists(path.join(postinstallSetupHome, '.agents', 'skills', name, 'SKILL.md'))) throw new Error(`selftest: postinstall global skills shadow the first-party ${name} plugin`);
|
|
2331
2331
|
}
|
|
2332
2332
|
if (!(await exists(path.join(postinstallSetupTmp, 'home', '.agents', 'skills', 'getdesign-reference', 'SKILL.md')))) throw new Error('selftest: postinstall global getdesign-reference skill not installed');
|
|
2333
|
+
await selftestSksShimRepair();
|
|
2333
2334
|
const oldNoBootstrap = process.env.SKS_POSTINSTALL_NO_BOOTSTRAP;
|
|
2334
2335
|
process.env.SKS_POSTINSTALL_NO_BOOTSTRAP = '1';
|
|
2335
2336
|
const noBootstrapDecision = await postinstallBootstrapDecision(postinstallSetupTmp);
|
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.9.
|
|
8
|
+
export const PACKAGE_VERSION = '0.9.11';
|
|
9
9
|
export const DEFAULT_PROCESS_TAIL_BYTES = 256 * 1024;
|
|
10
10
|
export const DEFAULT_PROCESS_TIMEOUT_MS = 30 * 60 * 1000;
|
|
11
11
|
|