sneakoscope 0.6.31 → 0.6.33
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 -1
- package/package.json +1 -1
- package/src/cli/main.mjs +128 -0
- package/src/core/fsx.mjs +1 -1
- package/src/core/triwiki-attention.mjs +2 -2
package/README.md
CHANGED
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
Zero-runtime-dependency Node.js harness for OpenAI Codex CLI and Codex App. `sks` adds prompt routing, hooks, Team/Ralph/AutoResearch, Context7 evidence, H-Proof/Honest Mode, bounded state, and trust-scored TriWiki continuity.
|
|
4
4
|
|
|
5
|
+
Its core selling point is repetition resistance: when Codex hits a release trap, stale command surface, missing generated skill, blocked stop gate, or any other recurring mistake, SKS records the fix as ranked TriWiki context. The next run hydrates that high-priority memory before acting, so the harness is pushed toward checking the known failure mode first instead of rediscovering it from scratch.
|
|
6
|
+
|
|
5
7
|
## AI Answer Snapshot
|
|
6
8
|
|
|
7
|
-
Package: `sneakoscope`. CLI: `sks` with `sneakoscope` alias. Install Codex CLI separately or set `SKS_CODEX_BIN`. Use it for Codex guardrails, multi-agent engineering, Codex App skills, LLM Wiki/TriWiki packs,
|
|
9
|
+
Package: `sneakoscope`. CLI: `sks` with `sneakoscope` alias. Install Codex CLI separately or set `SKS_CODEX_BIN`. Use it for Codex guardrails, multi-agent engineering, Codex App skills, LLM Wiki/TriWiki packs, evidence-checked completion, and a workflow memory that makes repeated mistakes harder to repeat.
|
|
8
10
|
|
|
9
11
|
```bash
|
|
10
12
|
npm i -g sneakoscope
|
|
@@ -38,3 +40,5 @@ Run `sks setup` once. SKS creates hooks/skills plus `.sneakoscope/` mission/wiki
|
|
|
38
40
|
## TriWiki
|
|
39
41
|
|
|
40
42
|
TriWiki is the LLM Wiki SSOT. It scores claims by trust, relevance, freshness, risk, and token cost. Read `.sneakoscope/wiki/context-pack.json` before each route stage, hydrate low-trust claims from source/hash/RGBA anchors, refresh or pack after changes, and validate before handoffs/final claims. `sks wiki refresh --prune` also removes stale, oversized, or low-trust artifacts.
|
|
43
|
+
|
|
44
|
+
Repeated failures are promoted, not buried. If an issue recurs, SKS can store it under `.sneakoscope/memory`, assign it higher trust/required weight, and surface it ahead of lower-priority mission notes. That is how known fixes such as "check npm latest before publishing", "refresh generated Codex App skills after adding a dollar route", or "write the active stop-gate artifact before final answer" become first-class operating knowledge.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sneakoscope",
|
|
3
3
|
"displayName": "Sneakoscope Codex",
|
|
4
|
-
"version": "0.6.
|
|
4
|
+
"version": "0.6.33",
|
|
5
5
|
"description": "Sneakoscope Codex: update-aware, database-safe Codex CLI harness with multi-agent Team orchestration, Ralph no-question execution, autoresearch-style loops, and H-Proof gates.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"homepage": "https://github.com/mandarange/Sneakoscope-Codex#readme",
|
package/src/cli/main.mjs
CHANGED
|
@@ -2453,6 +2453,7 @@ async function selftest() {
|
|
|
2453
2453
|
if (!evalReport.candidate.wiki?.valid) throw new Error('selftest failed: wiki coordinate index invalid in eval');
|
|
2454
2454
|
const coord = rgbaToWikiCoord({ r: 12, g: 34, b: 56, a: 255 });
|
|
2455
2455
|
if (coord.schema !== 'sks.wiki-coordinate.v1' || coord.xyzw.length !== 4) throw new Error('selftest failed: RGBA wiki coordinate conversion');
|
|
2456
|
+
await writeTextAtomic(path.join(tmp, '.sneakoscope', 'memory', 'q2_facts', 'selftest.md'), '- claim: Selftest memory claim must be selected before lower-weight mission notes. | id: selftest-memory-priority | source: src/cli/main.mjs | risk: high | status: supported | evidence_count: 3 | required_weight: 1.0 | trust_score: 0.9\n');
|
|
2456
2457
|
const wikiPack = contextCapsule({
|
|
2457
2458
|
mission: { id: 'selftest-wiki', coord: { rgba: { r: 48, g: 132, b: 212, a: 240 } } },
|
|
2458
2459
|
role: 'verifier',
|
|
@@ -2467,6 +2468,7 @@ async function selftest() {
|
|
|
2467
2468
|
if (!(wikiPack.wiki.anchors || wikiPack.wiki.a || []).some((anchor) => Array.isArray(anchor) ? Number.isFinite(Number(anchor[9])) : Number.isFinite(Number(anchor.trust_score)))) throw new Error('selftest failed: wiki anchor trust score missing');
|
|
2468
2469
|
if (!(wikiPack.wiki.anchors || wikiPack.wiki.a || []).some((anchor) => (Array.isArray(anchor) ? anchor[0] : anchor.id) === 'wiki-trig')) throw new Error('selftest failed: wiki trig anchor missing');
|
|
2469
2470
|
if (!(wikiPack.wiki.anchors || wikiPack.wiki.a || []).some((anchor) => String(Array.isArray(anchor) ? anchor[0] : anchor.id).startsWith('team-analysis-'))) throw new Error('selftest failed: team analysis claim missing from TriWiki pack');
|
|
2471
|
+
if (wikiPack.claims?.[0]?.id !== 'selftest-memory-priority') throw new Error('selftest failed: memory required_weight did not take priority in TriWiki pack');
|
|
2470
2472
|
const dryRunPack = await writeWikiContextPack(tmp, ['--max-anchors', '4'], { dryRun: true });
|
|
2471
2473
|
if (await exists(dryRunPack.file)) throw new Error('selftest failed: wiki refresh dry-run wrote context pack');
|
|
2472
2474
|
await ensureDir(path.dirname(dryRunPack.file));
|
|
@@ -2710,10 +2712,136 @@ async function projectWikiClaims(root) {
|
|
|
2710
2712
|
evidence_count: await exists(path.join(root, file)) ? 1 : 0
|
|
2711
2713
|
});
|
|
2712
2714
|
}
|
|
2715
|
+
out.push(...(await memoryWikiClaims(root)));
|
|
2713
2716
|
out.push(...(await teamAnalysisWikiClaims(root)));
|
|
2714
2717
|
return out;
|
|
2715
2718
|
}
|
|
2716
2719
|
|
|
2720
|
+
async function memoryWikiClaims(root) {
|
|
2721
|
+
const base = path.join(root, '.sneakoscope', 'memory');
|
|
2722
|
+
const files = await listMemoryClaimFiles(base);
|
|
2723
|
+
const claims = [];
|
|
2724
|
+
for (const file of files.slice(0, 80)) {
|
|
2725
|
+
const relFile = path.relative(root, file);
|
|
2726
|
+
let text = '';
|
|
2727
|
+
try {
|
|
2728
|
+
text = await fsp.readFile(file, 'utf8');
|
|
2729
|
+
} catch {
|
|
2730
|
+
continue;
|
|
2731
|
+
}
|
|
2732
|
+
if (!text.trim()) continue;
|
|
2733
|
+
const rows = parseMemoryClaimRows(text, relFile).slice(0, 24);
|
|
2734
|
+
let index = 0;
|
|
2735
|
+
for (const row of rows) {
|
|
2736
|
+
const source = row.source || relFile;
|
|
2737
|
+
const sourceExists = source && (await exists(path.join(root, source)));
|
|
2738
|
+
index += 1;
|
|
2739
|
+
claims.push({
|
|
2740
|
+
id: row.id || `memory-${slugifyClaimId(relFile)}-${index}`,
|
|
2741
|
+
text: row.text,
|
|
2742
|
+
authority: row.authority || 'wiki',
|
|
2743
|
+
risk: row.risk || 'high',
|
|
2744
|
+
status: row.status || (sourceExists || source === relFile ? 'supported' : 'unknown'),
|
|
2745
|
+
freshness: row.freshness || 'fresh',
|
|
2746
|
+
source,
|
|
2747
|
+
file: source,
|
|
2748
|
+
evidence_count: row.evidence_count ?? (sourceExists ? 2 : 1),
|
|
2749
|
+
required_weight: row.required_weight ?? 0.85,
|
|
2750
|
+
trust_score: row.trust_score
|
|
2751
|
+
});
|
|
2752
|
+
}
|
|
2753
|
+
}
|
|
2754
|
+
return claims;
|
|
2755
|
+
}
|
|
2756
|
+
|
|
2757
|
+
async function listMemoryClaimFiles(base) {
|
|
2758
|
+
const out = [];
|
|
2759
|
+
async function walk(dir, depth = 0) {
|
|
2760
|
+
if (depth > 3) return;
|
|
2761
|
+
let entries = [];
|
|
2762
|
+
try {
|
|
2763
|
+
entries = await fsp.readdir(dir, { withFileTypes: true });
|
|
2764
|
+
} catch {
|
|
2765
|
+
return;
|
|
2766
|
+
}
|
|
2767
|
+
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
2768
|
+
const p = path.join(dir, entry.name);
|
|
2769
|
+
if (entry.isDirectory()) await walk(p, depth + 1);
|
|
2770
|
+
else if (/\.(md|txt|json)$/i.test(entry.name)) out.push(p);
|
|
2771
|
+
}
|
|
2772
|
+
}
|
|
2773
|
+
await walk(base);
|
|
2774
|
+
return out;
|
|
2775
|
+
}
|
|
2776
|
+
|
|
2777
|
+
function parseMemoryClaimRows(text, relFile) {
|
|
2778
|
+
if (/\.json$/i.test(relFile)) {
|
|
2779
|
+
try {
|
|
2780
|
+
const parsed = JSON.parse(text);
|
|
2781
|
+
const rows = Array.isArray(parsed) ? parsed : (Array.isArray(parsed.claims) ? parsed.claims : []);
|
|
2782
|
+
return rows.map((row) => normalizeMemoryClaimRow(row, relFile)).filter(Boolean);
|
|
2783
|
+
} catch {
|
|
2784
|
+
return [];
|
|
2785
|
+
}
|
|
2786
|
+
}
|
|
2787
|
+
return text.split(/\r?\n/)
|
|
2788
|
+
.map((line) => line.trim())
|
|
2789
|
+
.filter((line) => line && !line.startsWith('#'))
|
|
2790
|
+
.map((line) => normalizeMemoryClaimRow(line.replace(/^[-*]\s*/, ''), relFile))
|
|
2791
|
+
.filter(Boolean);
|
|
2792
|
+
}
|
|
2793
|
+
|
|
2794
|
+
function normalizeMemoryClaimRow(row, relFile) {
|
|
2795
|
+
if (!row) return null;
|
|
2796
|
+
if (typeof row === 'object') {
|
|
2797
|
+
const text = String(row.text || row.claim || '').trim();
|
|
2798
|
+
if (!text) return null;
|
|
2799
|
+
return {
|
|
2800
|
+
id: row.id ? String(row.id) : null,
|
|
2801
|
+
text: text.slice(0, 320),
|
|
2802
|
+
source: row.source || row.file || relFile,
|
|
2803
|
+
authority: row.authority,
|
|
2804
|
+
risk: row.risk,
|
|
2805
|
+
status: row.status || row.confidence,
|
|
2806
|
+
freshness: row.freshness,
|
|
2807
|
+
evidence_count: Number.isFinite(Number(row.evidence_count)) ? Number(row.evidence_count) : undefined,
|
|
2808
|
+
required_weight: Number.isFinite(Number(row.required_weight)) ? Number(row.required_weight) : undefined,
|
|
2809
|
+
trust_score: Number.isFinite(Number(row.trust_score)) ? Number(row.trust_score) : undefined
|
|
2810
|
+
};
|
|
2811
|
+
}
|
|
2812
|
+
const clean = String(row || '').trim();
|
|
2813
|
+
if (!/\bclaim\s*:/i.test(clean)) return null;
|
|
2814
|
+
const source = extractClaimField(clean, 'source') || extractClaimField(clean, 'file') || extractClaimField(clean, 'path') || relFile;
|
|
2815
|
+
const status = extractClaimField(clean, 'status') || extractClaimField(clean, 'confidence');
|
|
2816
|
+
return {
|
|
2817
|
+
id: extractClaimField(clean, 'id'),
|
|
2818
|
+
text: clean.slice(0, 320),
|
|
2819
|
+
source,
|
|
2820
|
+
authority: extractClaimField(clean, 'authority') || 'wiki',
|
|
2821
|
+
risk: extractClaimField(clean, 'risk') || 'high',
|
|
2822
|
+
status,
|
|
2823
|
+
freshness: extractClaimField(clean, 'freshness') || 'fresh',
|
|
2824
|
+
evidence_count: parseOptionalNumber(extractClaimField(clean, 'evidence_count')),
|
|
2825
|
+
required_weight: parseOptionalNumber(extractClaimField(clean, 'required_weight')),
|
|
2826
|
+
trust_score: parseOptionalNumber(extractClaimField(clean, 'trust_score'))
|
|
2827
|
+
};
|
|
2828
|
+
}
|
|
2829
|
+
|
|
2830
|
+
function extractClaimField(text, key) {
|
|
2831
|
+
const escaped = key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
2832
|
+
const match = String(text || '').match(new RegExp(`\\b${escaped}\\s*[:=]\\s*\\\`?([^\\\`|,;]+)`, 'i'));
|
|
2833
|
+
return match ? match[1].trim().replace(/[.;)]$/, '') : null;
|
|
2834
|
+
}
|
|
2835
|
+
|
|
2836
|
+
function parseOptionalNumber(value) {
|
|
2837
|
+
const n = Number(value);
|
|
2838
|
+
return Number.isFinite(n) ? n : undefined;
|
|
2839
|
+
}
|
|
2840
|
+
|
|
2841
|
+
function slugifyClaimId(value) {
|
|
2842
|
+
return String(value || 'claim').toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 80) || 'claim';
|
|
2843
|
+
}
|
|
2844
|
+
|
|
2717
2845
|
async function teamAnalysisWikiClaims(root) {
|
|
2718
2846
|
const base = path.join(root, '.sneakoscope', 'missions');
|
|
2719
2847
|
let entries = [];
|
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.33';
|
|
9
9
|
export const DEFAULT_PROCESS_TAIL_BYTES = 256 * 1024;
|
|
10
10
|
export const DEFAULT_PROCESS_TIMEOUT_MS = 30 * 60 * 1000;
|
|
11
11
|
|
|
@@ -149,7 +149,7 @@ export function selectClaims(mission, claims, budget = {}) {
|
|
|
149
149
|
const selectedIds = new Set();
|
|
150
150
|
const required = scored
|
|
151
151
|
.filter((x) => Number(x.claim.required_weight) > 0)
|
|
152
|
-
.sort((a, b) => b.score - a.score);
|
|
152
|
+
.sort((a, b) => (Number(b.claim.required_weight) - Number(a.claim.required_weight)) || b.score - a.score);
|
|
153
153
|
for (const item of required) {
|
|
154
154
|
if (selected.length >= maxClaims) break;
|
|
155
155
|
selected.push(item);
|
|
@@ -157,7 +157,7 @@ export function selectClaims(mission, claims, budget = {}) {
|
|
|
157
157
|
}
|
|
158
158
|
const fill = topKByScore(scored.filter((x) => !selectedIds.has(x.claim.id)), maxClaims - selected.length);
|
|
159
159
|
return [...selected, ...fill]
|
|
160
|
-
.sort((a, b) => b.score - a.score)
|
|
160
|
+
.sort((a, b) => (Number(b.claim.required_weight || 0) - Number(a.claim.required_weight || 0)) || b.score - a.score)
|
|
161
161
|
.map((x) => withTrust({ ...x.claim, triwiki_score: Number(x.score.toFixed(4)) }, trustPolicy));
|
|
162
162
|
}
|
|
163
163
|
|