sneakoscope 0.6.6 → 0.6.8
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 +11 -4
- package/package.json +1 -1
- package/src/cli/main.mjs +42 -16
- package/src/core/evaluation.mjs +1 -1
- package/src/core/fsx.mjs +1 -1
- package/src/core/init.mjs +95 -12
package/README.md
CHANGED
|
@@ -28,6 +28,14 @@ sks setup
|
|
|
28
28
|
sks doctor --fix
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
+
Use local-only setup when the generated SKS files must never appear in git status:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
sks setup --local-only
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
This writes repo-local excludes to `.git/info/exclude` for `.sneakoscope/`, `.codex/`, `.agents/`, and `AGENTS.md`. If `AGENTS.md` already exists, local-only setup does not modify it.
|
|
38
|
+
|
|
31
39
|
The npm package name is `sneakoscope`; the command is branded as SKS and exposed as lowercase `sks` for shell portability. The package also exposes a `sneakoscope` command alias, so `sks setup` and `sneakoscope setup` are equivalent.
|
|
32
40
|
Global installation is the default and recommended setup. During `sks setup` or `sks init`, SKS resolves the global binary when possible and writes that absolute path into `.codex/hooks.json`, which avoids PATH issues in GUI or hook execution environments. For a project-only install, use `npm i -D sneakoscope` and initialize hooks with `npx sks setup --install-scope project`; this writes hook commands that call the local `node_modules/sneakoscope` binary.
|
|
33
41
|
|
|
@@ -378,10 +386,10 @@ sks aliases
|
|
|
378
386
|
sks --help
|
|
379
387
|
sneakoscope --help
|
|
380
388
|
|
|
381
|
-
sks setup [--install-scope global|project] [--force] [--json]
|
|
389
|
+
sks setup [--install-scope global|project] [--local-only] [--force] [--json]
|
|
382
390
|
sks fix-path [--install-scope global|project] [--json]
|
|
383
|
-
sks doctor [--fix] [--json] [--install-scope global|project]
|
|
384
|
-
sks init [--force] [--install-scope global|project]
|
|
391
|
+
sks doctor [--fix] [--local-only] [--json] [--install-scope global|project]
|
|
392
|
+
sks init [--force] [--local-only] [--install-scope global|project]
|
|
385
393
|
sks selftest [--mock]
|
|
386
394
|
|
|
387
395
|
sks ralph prepare "task"
|
|
@@ -589,7 +597,6 @@ sks hproof check latest
|
|
|
589
597
|
.codex/skills/ Codex App local project skills
|
|
590
598
|
.codex/agents/ Codex App custom agents for Team mode
|
|
591
599
|
.codex/SNEAKOSCOPE.md Codex App quick reference
|
|
592
|
-
.agents/skills/ Sneakoscope Codex helper skills
|
|
593
600
|
AGENTS.md managed repository rules block
|
|
594
601
|
```
|
|
595
602
|
|
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.8",
|
|
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
|
@@ -50,10 +50,10 @@ const COMMAND_CATALOG = [
|
|
|
50
50
|
{ name: 'dollar-commands', usage: 'sks dollar-commands [--json]', description: 'List Codex App $ commands such as $DF.' },
|
|
51
51
|
{ name: 'df', usage: 'sks df', description: 'Explain $DF fast design/content fix mode.' },
|
|
52
52
|
{ name: 'aliases', usage: 'sks aliases', description: 'Show command aliases and npm binary names.' },
|
|
53
|
-
{ name: 'setup', usage: 'sks setup [--install-scope global|project] [--force] [--json]', description: 'Initialize SKS state, Codex App files, hooks, skills, and rules.' },
|
|
53
|
+
{ name: 'setup', usage: 'sks setup [--install-scope global|project] [--local-only] [--force] [--json]', description: 'Initialize SKS state, Codex App files, hooks, skills, and rules.' },
|
|
54
54
|
{ name: 'fix-path', usage: 'sks fix-path [--install-scope global|project] [--json]', description: 'Refresh hook commands with the resolved SKS binary path.' },
|
|
55
|
-
{ name: 'doctor', usage: 'sks doctor [--fix] [--json] [--install-scope global|project]', description: 'Check Node, Codex CLI, install scope, hooks, skills, DB guard, and Codex App files.' },
|
|
56
|
-
{ name: 'init', usage: 'sks init [--force] [--install-scope global|project]', description: 'Initialize the local SKS control surface.' },
|
|
55
|
+
{ name: 'doctor', usage: 'sks doctor [--fix] [--local-only] [--json] [--install-scope global|project]', description: 'Check Node, Codex CLI, install scope, hooks, skills, DB guard, and Codex App files.' },
|
|
56
|
+
{ name: 'init', usage: 'sks init [--force] [--local-only] [--install-scope global|project]', description: 'Initialize the local SKS control surface.' },
|
|
57
57
|
{ name: 'selftest', usage: 'sks selftest [--mock]', description: 'Run local smoke tests without calling a model.' },
|
|
58
58
|
{ name: 'ralph', usage: 'sks ralph prepare|answer|run|status ...', description: 'Run mandatory-clarification Ralph missions with a no-question execution loop.' },
|
|
59
59
|
{ name: 'research', usage: 'sks research prepare|run|status ...', description: 'Run frontier-style research missions with novelty and falsification gates.' },
|
|
@@ -134,10 +134,10 @@ Usage:
|
|
|
134
134
|
sks dollar-commands [--json]
|
|
135
135
|
sks df
|
|
136
136
|
sks aliases
|
|
137
|
-
sks setup [--install-scope global|project] [--force] [--json]
|
|
137
|
+
sks setup [--install-scope global|project] [--local-only] [--force] [--json]
|
|
138
138
|
sks fix-path [--install-scope global|project] [--json]
|
|
139
|
-
sks doctor [--fix] [--json] [--install-scope global|project]
|
|
140
|
-
sks init [--install-scope global|project]
|
|
139
|
+
sks doctor [--fix] [--local-only] [--json] [--install-scope global|project]
|
|
140
|
+
sks init [--install-scope global|project] [--local-only]
|
|
141
141
|
sks selftest [--mock]
|
|
142
142
|
sks ralph prepare "task"
|
|
143
143
|
sks ralph answer <mission-id|latest> <answers.json>
|
|
@@ -326,6 +326,11 @@ Project-only install:
|
|
|
326
326
|
npm i -D sneakoscope
|
|
327
327
|
npx sks setup --install-scope project
|
|
328
328
|
|
|
329
|
+
Local-only install artifacts:
|
|
330
|
+
sks setup --local-only
|
|
331
|
+
# writes generated SKS files but excludes .sneakoscope/, .codex/, .agents/, AGENTS.md through .git/info/exclude
|
|
332
|
+
# existing AGENTS.md is not modified in local-only mode
|
|
333
|
+
|
|
329
334
|
GitHub install for unreleased commits:
|
|
330
335
|
npm i -g git+${REPOSITORY_URL}
|
|
331
336
|
`);
|
|
@@ -563,6 +568,11 @@ Project-only install:
|
|
|
563
568
|
npm i -D sneakoscope
|
|
564
569
|
npx sks setup --install-scope project
|
|
565
570
|
|
|
571
|
+
Local-only install artifacts:
|
|
572
|
+
sks setup --local-only
|
|
573
|
+
# excludes .sneakoscope/, .codex/, .agents/, AGENTS.md through .git/info/exclude
|
|
574
|
+
# existing AGENTS.md is not modified in local-only mode
|
|
575
|
+
|
|
566
576
|
GitHub install for unreleased commits:
|
|
567
577
|
npm i -g git+${REPOSITORY_URL}
|
|
568
578
|
sks setup
|
|
@@ -750,8 +760,9 @@ Render and verify:
|
|
|
750
760
|
async function setup(args) {
|
|
751
761
|
const root = await projectRoot();
|
|
752
762
|
const installScope = installScopeFromArgs(args);
|
|
763
|
+
const localOnly = flag(args, '--local-only');
|
|
753
764
|
const globalCommand = await globalSksCommand();
|
|
754
|
-
const res = await initProject(root, { force: flag(args, '--force'), installScope, globalCommand });
|
|
765
|
+
const res = await initProject(root, { force: flag(args, '--force'), installScope, globalCommand, localOnly });
|
|
755
766
|
const install = await installStatus(root, installScope, { globalCommand });
|
|
756
767
|
const hooksPath = path.join(root, '.codex', 'hooks.json');
|
|
757
768
|
const result = {
|
|
@@ -767,6 +778,7 @@ async function setup(args) {
|
|
|
767
778
|
agents_rules: path.join(root, 'AGENTS.md')
|
|
768
779
|
},
|
|
769
780
|
created: res.created,
|
|
781
|
+
local_only: localOnly,
|
|
770
782
|
next: ['sks selftest --mock', 'sks doctor', 'sks commands']
|
|
771
783
|
};
|
|
772
784
|
if (flag(args, '--json')) return console.log(JSON.stringify(result, null, 2));
|
|
@@ -774,9 +786,10 @@ async function setup(args) {
|
|
|
774
786
|
console.log(`Project: ${root}`);
|
|
775
787
|
console.log(`Install: ${install.ok ? 'ok' : 'missing'} ${install.scope} (${install.command_prefix})`);
|
|
776
788
|
console.log(`Hooks: ${path.relative(root, hooksPath)}`);
|
|
789
|
+
if (localOnly) console.log('Git: local-only (.git/info/exclude; existing AGENTS.md not modified)');
|
|
777
790
|
console.log(`Codex App: .codex/config.toml, .codex/hooks.json, .codex/skills, .codex/agents, .codex/SNEAKOSCOPE.md`);
|
|
778
791
|
console.log(`Prompt: default optimization pipeline, $DF fast design/content route`);
|
|
779
|
-
console.log(`Skills: .codex/skills
|
|
792
|
+
console.log(`Skills: .codex/skills`);
|
|
780
793
|
console.log(`Next: sks selftest --mock; sks commands; sks dollar-commands`);
|
|
781
794
|
if (!install.ok && install.scope === 'global') console.log('\nGlobal command missing. Run: npm i -g sneakoscope');
|
|
782
795
|
if (!install.ok && install.scope === 'project') console.log('\nProject package missing. Run: npm i -D sneakoscope');
|
|
@@ -789,7 +802,7 @@ async function fixPath(args) {
|
|
|
789
802
|
? installScopeFromArgs(args)
|
|
790
803
|
: normalizeInstallScope(manifest?.installation?.scope || 'global');
|
|
791
804
|
const globalCommand = await globalSksCommand();
|
|
792
|
-
await initProject(root, { installScope, globalCommand });
|
|
805
|
+
await initProject(root, { installScope, globalCommand, localOnly: flag(args, '--local-only') || Boolean(manifest?.git?.local_only) });
|
|
793
806
|
const install = await installStatus(root, installScope, { globalCommand });
|
|
794
807
|
const result = {
|
|
795
808
|
root,
|
|
@@ -814,7 +827,8 @@ async function doctor(args) {
|
|
|
814
827
|
: null;
|
|
815
828
|
if (flag(args, '--fix')) {
|
|
816
829
|
const fixScope = requestedScope || 'global';
|
|
817
|
-
await
|
|
830
|
+
const existingManifest = await readJson(path.join(root, '.sneakoscope', 'manifest.json'), null);
|
|
831
|
+
await initProject(root, { installScope: fixScope, globalCommand: await globalSksCommand(), localOnly: flag(args, '--local-only') || Boolean(existingManifest?.git?.local_only) });
|
|
818
832
|
}
|
|
819
833
|
const codex = await getCodexInfo();
|
|
820
834
|
const rust = await rustInfo();
|
|
@@ -840,7 +854,7 @@ async function doctor(args) {
|
|
|
840
854
|
sneakoscope: { ok: await exists(path.join(root, '.sneakoscope')) },
|
|
841
855
|
db_guard: { ok: dbPolicyExists && dbScan.ok, policy: dbPolicyExists ? await loadDbSafetyPolicy(root) : null, scan: dbScan },
|
|
842
856
|
hooks: { ok: await exists(path.join(root, '.codex', 'hooks.json')) },
|
|
843
|
-
skills: { ok:
|
|
857
|
+
skills: { ok: await exists(path.join(root, '.codex', 'skills')) },
|
|
844
858
|
codex_app: {
|
|
845
859
|
...codexApp,
|
|
846
860
|
ok: codexApp.config.ok && codexApp.hooks.ok && codexApp.skills.ok && codexApp.agents.ok && codexApp.quick_reference.ok && codexApp.agents_rules.ok
|
|
@@ -859,7 +873,7 @@ async function doctor(args) {
|
|
|
859
873
|
console.log(`DB Guard: ${result.db_guard.ok ? 'ok' : 'blocked'} ${dbScan.findings?.length || 0} finding(s)`);
|
|
860
874
|
console.log(`Hooks: ${result.hooks.ok ? 'ok' : 'missing .codex/hooks.json'}`);
|
|
861
875
|
console.log(`Codex App: ${result.codex_app.ok ? 'ok' : 'missing app files'} .codex/config.toml .codex/hooks.json .codex/skills .codex/agents .codex/SNEAKOSCOPE.md`);
|
|
862
|
-
console.log(`Skills: ${result.skills.ok ? 'ok' : 'missing .codex/skills
|
|
876
|
+
console.log(`Skills: ${result.skills.ok ? 'ok' : 'missing .codex/skills'}`);
|
|
863
877
|
console.log(`Package: ${result.package.human}`);
|
|
864
878
|
console.log(`Storage: ${storage.total_human || '0 B'}`);
|
|
865
879
|
console.log(`Ready: ${result.ready ? 'yes' : 'no'}`);
|
|
@@ -872,10 +886,12 @@ async function doctor(args) {
|
|
|
872
886
|
async function init(args) {
|
|
873
887
|
const root = await projectRoot();
|
|
874
888
|
const installScope = installScopeFromArgs(args);
|
|
889
|
+
const localOnly = flag(args, '--local-only');
|
|
875
890
|
const globalCommand = await globalSksCommand();
|
|
876
|
-
const res = await initProject(root, { force: flag(args, '--force'), installScope, globalCommand });
|
|
891
|
+
const res = await initProject(root, { force: flag(args, '--force'), installScope, globalCommand, localOnly });
|
|
877
892
|
console.log(`Initialized Sneakoscope Codex in ${root}`);
|
|
878
893
|
console.log(`Install scope: ${installScope} (${sksCommandPrefix(installScope, { globalCommand })})`);
|
|
894
|
+
if (localOnly) console.log('Git mode: local-only (.git/info/exclude)');
|
|
879
895
|
for (const x of res.created) console.log(`- ${x}`);
|
|
880
896
|
}
|
|
881
897
|
|
|
@@ -1207,6 +1223,16 @@ async function selftest() {
|
|
|
1207
1223
|
await initProject(projectScopeTmp, { installScope: 'project' });
|
|
1208
1224
|
const projectHooks = await readJson(path.join(projectScopeTmp, '.codex', 'hooks.json'));
|
|
1209
1225
|
if (projectHooks.hooks.PreToolUse[0].hooks[0].command !== 'node ./node_modules/sneakoscope/bin/sks.mjs hook pre-tool') throw new Error('selftest failed: project install hook command missing');
|
|
1226
|
+
const localOnlyTmp = tmpdir();
|
|
1227
|
+
await ensureDir(path.join(localOnlyTmp, '.git'));
|
|
1228
|
+
await writeTextAtomic(path.join(localOnlyTmp, 'AGENTS.md'), 'existing local rules\n');
|
|
1229
|
+
await initProject(localOnlyTmp, { localOnly: true });
|
|
1230
|
+
const localExclude = await safeReadText(path.join(localOnlyTmp, '.git', 'info', 'exclude'));
|
|
1231
|
+
if (!localExclude.includes('.codex/') || !localExclude.includes('AGENTS.md')) throw new Error('selftest failed: local-only git excludes missing');
|
|
1232
|
+
const localAgents = await safeReadText(path.join(localOnlyTmp, 'AGENTS.md'));
|
|
1233
|
+
if (localAgents.trim() !== 'existing local rules') throw new Error('selftest failed: local-only modified existing AGENTS.md');
|
|
1234
|
+
const localManifest = await readJson(path.join(localOnlyTmp, '.sneakoscope', 'manifest.json'));
|
|
1235
|
+
if (!localManifest.git?.local_only) throw new Error('selftest failed: local-only manifest missing');
|
|
1210
1236
|
if (!isTransientNpmBinPath('/tmp/.npm/_npx/abc/node_modules/.bin/sks')) throw new Error('selftest failed: npx bin path not recognized as transient');
|
|
1211
1237
|
if (!isTransientNpmBinPath('/tmp/.npm-cache/_cacache/tmp/git-cloneabc/bin/sks.mjs')) throw new Error('selftest failed: npm cache git clone path not recognized as transient');
|
|
1212
1238
|
if (isTransientNpmBinPath('/usr/local/bin/sks')) throw new Error('selftest failed: stable global bin marked transient');
|
|
@@ -1226,8 +1252,8 @@ async function selftest() {
|
|
|
1226
1252
|
if (oldSksBin === undefined) delete process.env.SKS_BIN;
|
|
1227
1253
|
else process.env.SKS_BIN = oldSksBin;
|
|
1228
1254
|
}
|
|
1229
|
-
const
|
|
1230
|
-
if (
|
|
1255
|
+
const legacySkillMirrorExists = await exists(path.join(tmp, '.agents', 'skills', 'research-discovery', 'SKILL.md'));
|
|
1256
|
+
if (legacySkillMirrorExists) throw new Error('selftest failed: legacy .agents/skills mirror still installed');
|
|
1231
1257
|
const codexAppSkillExists = await exists(path.join(tmp, '.codex', 'skills', 'research-discovery', 'SKILL.md'));
|
|
1232
1258
|
if (!codexAppSkillExists) throw new Error('selftest failed: Codex App skill not installed');
|
|
1233
1259
|
const dfSkillExists = await exists(path.join(tmp, '.codex', 'skills', 'DF', 'SKILL.md'));
|
|
@@ -1413,7 +1439,7 @@ async function projectWikiClaims(root) {
|
|
|
1413
1439
|
const claims = [
|
|
1414
1440
|
['wiki-hooks', '.codex/hooks.json routes UserPromptSubmit, tool, permission, and Stop events through SKS guards.', '.codex/hooks.json', 'code', 'high'],
|
|
1415
1441
|
['wiki-config', '.codex/config.toml enables Codex App profiles, multi-agent support, and Team agent limits.', '.codex/config.toml', 'code', 'high'],
|
|
1416
|
-
['wiki-skills', '.codex/skills
|
|
1442
|
+
['wiki-skills', '.codex/skills provides local routes for DF, Team, Ralph, Research, AutoResearch, DB, GX, wiki, and evaluation workflows.', '.codex/skills', 'code', 'medium'],
|
|
1417
1443
|
['wiki-agents', '.codex/agents defines Team planning, implementation, DB safety, and QA reviewer roles.', '.codex/agents', 'code', 'medium'],
|
|
1418
1444
|
['wiki-policy', '.sneakoscope/policy.json stores update-check, honest-mode, retention, database, performance, and prompt-pipeline policy.', '.sneakoscope/policy.json', 'contract', 'high'],
|
|
1419
1445
|
['wiki-memory', '.sneakoscope/memory stores Q0 raw, Q1 evidence, Q2 facts, Q3 tags, and Q4 control bits for hydratable context.', '.sneakoscope/memory', 'wiki', 'high'],
|
package/src/core/evaluation.mjs
CHANGED
|
@@ -38,7 +38,7 @@ export function defaultEvaluationScenario() {
|
|
|
38
38
|
['req-wiki-rgba', 'TriWiki stores RGBA-derived trigonometric wiki anchors so compressed context remains hydratable by id, hash, source, and coordinate.', 'code', 'high', 1.2],
|
|
39
39
|
['req-retention', 'runtime logs and mission artifacts are bounded through retention policy and sks gc.', 'code', 'medium', 0.85],
|
|
40
40
|
['req-selftest', 'selftest covers contract sealing, DB guard blocking, done-gate evaluation, GX render/validate/drift, snapshot, and retention report.', 'test', 'high', 1.1],
|
|
41
|
-
['req-skill', 'sks init installs local
|
|
41
|
+
['req-skill', 'sks init installs one canonical local skill set under .codex/skills so project workflows can trigger domain-specific guidance without duplicate commands.', 'code', 'medium', 0.9],
|
|
42
42
|
['req-design', 'design artifact work should gather design context, build an HTML artifact, expose variations when useful, and verify rendered output.', 'code', 'medium', 0.8]
|
|
43
43
|
];
|
|
44
44
|
const noise = [
|
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.8';
|
|
9
9
|
export const DEFAULT_PROCESS_TAIL_BYTES = 256 * 1024;
|
|
10
10
|
export const DEFAULT_PROCESS_TIMEOUT_MS = 30 * 60 * 1000;
|
|
11
11
|
|
package/src/core/init.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
-
import
|
|
2
|
+
import fsp from 'node:fs/promises';
|
|
3
|
+
import { ensureDir, readJson, readText, writeJsonAtomic, writeTextAtomic, mergeManagedBlock, nowIso, PACKAGE_VERSION, exists } from './fsx.mjs';
|
|
3
4
|
import { DEFAULT_RETENTION_POLICY } from './retention.mjs';
|
|
4
5
|
import { DEFAULT_DB_SAFETY_POLICY } from './db-safety.mjs';
|
|
5
6
|
|
|
@@ -98,12 +99,15 @@ A task is not done until relevant tests are run or justified, unsupported critic
|
|
|
98
99
|
export async function initProject(root, opts = {}) {
|
|
99
100
|
const created = [];
|
|
100
101
|
const installScope = normalizeInstallScope(opts.installScope || 'global');
|
|
102
|
+
const localOnly = Boolean(opts.localOnly);
|
|
101
103
|
const hookCommandPrefix = opts.hookCommandPrefix || sksCommandPrefix(installScope, { globalCommand: opts.globalCommand });
|
|
102
104
|
const sine = path.join(root, '.sneakoscope');
|
|
103
105
|
const dirs = [
|
|
104
|
-
'.sneakoscope/state', '.sneakoscope/missions', '.sneakoscope/db', '.sneakoscope/bus', '.sneakoscope/hproof', '.sneakoscope/db', '.sneakoscope/wiki', '.sneakoscope/memory/q0_raw', '.sneakoscope/memory/q1_evidence', '.sneakoscope/memory/q2_facts', '.sneakoscope/memory/q3_tags', '.sneakoscope/memory/q4_bits', '.sneakoscope/gx/cartridges', '.sneakoscope/model/fingerprints', '.sneakoscope/genome/candidates', '.sneakoscope/trajectories/raw', '.sneakoscope/locks', '.sneakoscope/tmp', '.sneakoscope/arenas', '.sneakoscope/reports', '.codex', '.codex/skills', '.codex/agents'
|
|
106
|
+
'.sneakoscope/state', '.sneakoscope/missions', '.sneakoscope/db', '.sneakoscope/bus', '.sneakoscope/hproof', '.sneakoscope/db', '.sneakoscope/wiki', '.sneakoscope/memory/q0_raw', '.sneakoscope/memory/q1_evidence', '.sneakoscope/memory/q2_facts', '.sneakoscope/memory/q3_tags', '.sneakoscope/memory/q4_bits', '.sneakoscope/gx/cartridges', '.sneakoscope/model/fingerprints', '.sneakoscope/genome/candidates', '.sneakoscope/trajectories/raw', '.sneakoscope/locks', '.sneakoscope/tmp', '.sneakoscope/arenas', '.sneakoscope/reports', '.codex', '.codex/skills', '.codex/agents'
|
|
105
107
|
];
|
|
106
108
|
for (const d of dirs) await ensureDir(path.join(root, d));
|
|
109
|
+
const localExclude = localOnly ? await ensureLocalOnlyGitExclude(root) : null;
|
|
110
|
+
if (localExclude?.path) created.push(`${path.relative(root, localExclude.path)} local-only excludes`);
|
|
107
111
|
|
|
108
112
|
await writeJsonAtomic(path.join(sine, 'manifest.json'), {
|
|
109
113
|
package: 'sneakoscope',
|
|
@@ -138,6 +142,11 @@ export async function initProject(root, opts = {}) {
|
|
|
138
142
|
channel_map: { r: 'domainAngle', g: 'layerRadius', b: 'phase', a: 'concentration' },
|
|
139
143
|
continuity_model: 'selected_text_plus_hydratable_rgba_trig_anchors'
|
|
140
144
|
},
|
|
145
|
+
git: {
|
|
146
|
+
local_only: localOnly,
|
|
147
|
+
exclude_path: localExclude?.path ? path.relative(root, localExclude.path) : null,
|
|
148
|
+
excluded_patterns: localExclude?.patterns || []
|
|
149
|
+
},
|
|
141
150
|
database_safety: 'destructive_db_operations_denied_always',
|
|
142
151
|
gx_renderer: 'deterministic_svg_html'
|
|
143
152
|
});
|
|
@@ -157,7 +166,13 @@ export async function initProject(root, opts = {}) {
|
|
|
157
166
|
const policy = await readJson(policyPath, {});
|
|
158
167
|
await writeJsonAtomic(policyPath, {
|
|
159
168
|
...policy,
|
|
160
|
-
installation: installPolicy(installScope, hookCommandPrefix)
|
|
169
|
+
installation: installPolicy(installScope, hookCommandPrefix),
|
|
170
|
+
git: {
|
|
171
|
+
...(policy.git || {}),
|
|
172
|
+
local_only: localOnly || Boolean(policy.git?.local_only),
|
|
173
|
+
exclude_path: localExclude?.path ? path.relative(root, localExclude.path) : policy.git?.exclude_path || null,
|
|
174
|
+
excluded_patterns: localExclude?.patterns || policy.git?.excluded_patterns || []
|
|
175
|
+
}
|
|
161
176
|
});
|
|
162
177
|
}
|
|
163
178
|
|
|
@@ -165,6 +180,11 @@ export async function initProject(root, opts = {}) {
|
|
|
165
180
|
return {
|
|
166
181
|
schema_version: 1,
|
|
167
182
|
installation: installPolicy(scope, commandPrefix),
|
|
183
|
+
git: {
|
|
184
|
+
local_only: localOnly,
|
|
185
|
+
exclude_path: localExclude?.path ? path.relative(root, localExclude.path) : null,
|
|
186
|
+
excluded_patterns: localExclude?.patterns || []
|
|
187
|
+
},
|
|
168
188
|
retention: DEFAULT_RETENTION_POLICY,
|
|
169
189
|
update_check: {
|
|
170
190
|
enabled: true,
|
|
@@ -244,8 +264,13 @@ export async function initProject(root, opts = {}) {
|
|
|
244
264
|
created.push('.sneakoscope/state/current.json');
|
|
245
265
|
}
|
|
246
266
|
|
|
247
|
-
|
|
248
|
-
|
|
267
|
+
const agentsMdPath = path.join(root, 'AGENTS.md');
|
|
268
|
+
if (localOnly && await exists(agentsMdPath)) {
|
|
269
|
+
created.push('AGENTS.md skipped (local-only existing file)');
|
|
270
|
+
} else {
|
|
271
|
+
await mergeManagedBlock(agentsMdPath, 'Sneakoscope Codex GX MANAGED BLOCK', AGENTS_BLOCK);
|
|
272
|
+
created.push('AGENTS.md managed block');
|
|
273
|
+
}
|
|
249
274
|
|
|
250
275
|
await writeTextAtomic(path.join(root, '.codex', 'config.toml'), `[features]\ncodex_hooks = true\nmulti_agent = true\n\n[agents]\nmax_threads = 6\nmax_depth = 1\n\n[agents.team_consensus]\ndescription = "Planning and debate agent for SKS Team mode. Maps options, constraints, risks, and proposes the agreed objective before implementation starts."\nconfig_file = "./agents/team-consensus.toml"\nnickname_candidates = ["Consensus", "Atlas"]\n\n[agents.implementation_worker]\ndescription = "Implementation worker for SKS Team mode. Owns a clearly bounded write set and coordinates with other workers without reverting their edits."\nconfig_file = "./agents/implementation-worker.toml"\nnickname_candidates = ["Builder", "Mason"]\n\n[agents.db_safety_reviewer]\ndescription = "Read-only database safety reviewer for SQL, migrations, RLS, destructive-operation risk, and rollback safety."\nconfig_file = "./agents/db-safety-reviewer.toml"\nnickname_candidates = ["Sentinel", "Ledger"]\n\n[agents.qa_reviewer]\ndescription = "Read-only verification reviewer for correctness, tests, regressions, and missing evidence."\nconfig_file = "./agents/qa-reviewer.toml"\nnickname_candidates = ["Verifier", "Scout"]\n\n[profiles.sks-ralph]\nmodel = "gpt-5.5"\napproval_policy = "never"\nsandbox_mode = "workspace-write"\nmodel_reasoning_effort = "high"\n\n[profiles.sks-research]\nmodel = "gpt-5.5"\napproval_policy = "never"\nsandbox_mode = "workspace-write"\nmodel_reasoning_effort = "xhigh"\n\n[profiles.sks-team]\nmodel = "gpt-5.5"\napproval_policy = "on-request"\nsandbox_mode = "workspace-write"\nmodel_reasoning_effort = "high"\n\n[profiles.sks-default]\nmodel = "gpt-5.5"\napproval_policy = "on-request"\nsandbox_mode = "workspace-write"\nmodel_reasoning_effort = "medium"\n`);
|
|
251
276
|
created.push('.codex/config.toml');
|
|
@@ -264,14 +289,40 @@ export async function initProject(root, opts = {}) {
|
|
|
264
289
|
});
|
|
265
290
|
created.push(`.codex/hooks.json (${installScope})`);
|
|
266
291
|
|
|
267
|
-
await installSkills(root);
|
|
292
|
+
const skillInstall = await installSkills(root);
|
|
268
293
|
created.push('.codex/skills/*');
|
|
269
|
-
created.push(
|
|
294
|
+
if (skillInstall.removed_legacy_agent_skill_dirs.length) created.push(`.agents/skills legacy mirrors removed (${skillInstall.removed_legacy_agent_skill_dirs.length})`);
|
|
270
295
|
await installCodexAgents(root);
|
|
271
296
|
created.push('.codex/agents/*');
|
|
272
297
|
return { created };
|
|
273
298
|
}
|
|
274
299
|
|
|
300
|
+
async function ensureLocalOnlyGitExclude(root) {
|
|
301
|
+
const gitDir = await resolveGitDir(root);
|
|
302
|
+
if (!gitDir) return { path: null, patterns: [] };
|
|
303
|
+
const patterns = ['.sneakoscope/', '.codex/', '.agents/', 'AGENTS.md'];
|
|
304
|
+
const excludePath = path.join(gitDir, 'info', 'exclude');
|
|
305
|
+
await ensureDir(path.dirname(excludePath));
|
|
306
|
+
const markerStart = '# Sneakoscope Codex local-only generated files';
|
|
307
|
+
const current = await readText(excludePath, '');
|
|
308
|
+
if (!current.includes(markerStart)) {
|
|
309
|
+
const block = `${markerStart}\n${patterns.join('\n')}\n`;
|
|
310
|
+
await writeTextAtomic(excludePath, `${current.trimEnd()}${current.trim() ? '\n\n' : ''}${block}`);
|
|
311
|
+
}
|
|
312
|
+
return { path: excludePath, patterns };
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
async function resolveGitDir(root) {
|
|
316
|
+
const dotGit = path.join(root, '.git');
|
|
317
|
+
if (!(await exists(dotGit))) return null;
|
|
318
|
+
const text = await readText(dotGit, null);
|
|
319
|
+
if (typeof text === 'string') {
|
|
320
|
+
const match = text.match(/^gitdir:\s*(.+)\s*$/m);
|
|
321
|
+
if (match) return path.resolve(root, match[1]);
|
|
322
|
+
}
|
|
323
|
+
return dotGit;
|
|
324
|
+
}
|
|
325
|
+
|
|
275
326
|
function codexAppQuickReference(scope, commandPrefix) {
|
|
276
327
|
return `# Sneakoscope Codex for Codex App
|
|
277
328
|
|
|
@@ -385,12 +436,44 @@ async function installSkills(root) {
|
|
|
385
436
|
'design-artifact-expert': `---\nname: design-artifact-expert\ndescription: Create or revise high-fidelity HTML, UI, prototype, deck-like, or visual design artifacts using project design context, variations, and rendered verification.\n---\n\nUse when the user asks for design, UI, prototype, HTML artifact, landing page, deck-like visual work, interaction design, or visual refinement.\n\nWorkflow:\n1. Understand the artifact, audience, constraints, fidelity, variants, and existing brand/design system.\n2. Inspect local code, assets, screenshots, or design-system docs before inventing visuals. If context exists, follow its visual vocabulary.\n3. Build the actual usable screen or artifact first; avoid empty landing-page framing unless the task is explicitly marketing.\n4. Use descriptive HTML filenames. Keep large artifacts split into small support files when needed.\n5. For screens/slides, add data-screen-label attributes for comment context. Slide labels are 1-indexed.\n6. Preserve state for decks, videos, or multi-step prototypes with localStorage when refresh continuity matters.\n7. Expose a small Tweaks surface for useful variants such as layout, density, color, copy, or interaction options.\n8. Verify the artifact renders cleanly in a browser or preview. For design tasks, set done-gate.json design_verification_required/present fields and cite evidence.\n\nQuality bar:\n- Root design decisions in available assets and components.\n- Use restrained, domain-appropriate layout and typography.\n- Avoid text overlap, unreadable controls, decorative clutter, one-note palettes, and placeholder-only deliverables.\n- Prefer icons and familiar controls for tool actions, and make repeated UI dimensions stable.\n`
|
|
386
437
|
};
|
|
387
438
|
for (const [name, content] of Object.entries(skills)) {
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
await writeTextAtomic(path.join(dir, 'SKILL.md'), `${content.trim()}\n`);
|
|
392
|
-
}
|
|
439
|
+
const dir = path.join(root, '.codex', 'skills', name);
|
|
440
|
+
await ensureDir(dir);
|
|
441
|
+
await writeTextAtomic(path.join(dir, 'SKILL.md'), `${content.trim()}\n`);
|
|
393
442
|
}
|
|
443
|
+
return { removed_legacy_agent_skill_dirs: await removeLegacyAgentSkillMirrors(root, Object.keys(skills)) };
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
async function removeLegacyAgentSkillMirrors(root, skillNames) {
|
|
447
|
+
const legacyRoot = path.join(root, '.agents', 'skills');
|
|
448
|
+
if (!(await exists(legacyRoot))) return [];
|
|
449
|
+
const removed = [];
|
|
450
|
+
for (const name of skillNames) {
|
|
451
|
+
const dir = path.join(legacyRoot, name);
|
|
452
|
+
const skillPath = path.join(dir, 'SKILL.md');
|
|
453
|
+
const text = await readText(skillPath, null);
|
|
454
|
+
if (!isGeneratedSksLegacySkill(text, name)) continue;
|
|
455
|
+
await fsp.rm(dir, { recursive: true, force: true });
|
|
456
|
+
removed.push(path.relative(root, dir));
|
|
457
|
+
}
|
|
458
|
+
await removeDirIfEmpty(legacyRoot);
|
|
459
|
+
await removeDirIfEmpty(path.join(root, '.agents'));
|
|
460
|
+
return removed;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
function isGeneratedSksLegacySkill(text, name) {
|
|
464
|
+
if (typeof text !== 'string') return false;
|
|
465
|
+
return text.startsWith('---') && new RegExp(`^name:\\s*${escapeRegExp(name)}\\s*$`, 'm').test(text);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
function escapeRegExp(value) {
|
|
469
|
+
return String(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
async function removeDirIfEmpty(dir) {
|
|
473
|
+
try {
|
|
474
|
+
const entries = await fsp.readdir(dir);
|
|
475
|
+
if (!entries.length) await fsp.rmdir(dir);
|
|
476
|
+
} catch {}
|
|
394
477
|
}
|
|
395
478
|
|
|
396
479
|
async function installCodexAgents(root) {
|