sneakoscope 0.6.87 → 0.6.89

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 CHANGED
@@ -90,6 +90,8 @@ sks bootstrap
90
90
 
91
91
  `sks` commands work even when no project root is present. Project-aware commands use the nearest `.sneakoscope`, `.dcodex`, or `.git` root; if none exists, SKS uses a per-user global runtime root. `sks bootstrap` still initializes the current project when you want project-local hooks, skills, and TriWiki state.
92
92
 
93
+ Project setup writes shared `.gitignore` entries for generated SKS files: `.sneakoscope/`, `.codex/`, `.agents/`, and managed `AGENTS.md`. Use `sks setup --local-only` when you want those excludes kept only in `.git/info/exclude`.
94
+
93
95
  ### One-Shot Install
94
96
 
95
97
  Use this when you do not want to keep a global install:
@@ -256,6 +258,8 @@ Generated app files include:
256
258
  | `.codex/config.toml` | Codex profiles, agents, and MCP configuration. |
257
259
  | `.sneakoscope/` | Runtime state, missions, wiki packs, policies, and artifacts. |
258
260
 
261
+ Default setup adds these generated SKS paths to the project `.gitignore`; `--local-only` uses `.git/info/exclude` instead.
262
+
259
263
  Use `sks dollar-commands` to confirm that terminal discovery and Codex App prompt commands agree.
260
264
 
261
265
  TriWiki is intentionally sparse: `sks wiki sweep` records demote, soft-forget, archive, delete, promote-to-skill, and promote-to-rule candidates instead of injecting every old claim into future prompts. `sks harness fixture` validates the broader Harness Growth Factory contract: deliberate forgetting fixtures, skill card metadata, experiment schema, tool-error taxonomy, permission profiles, MultiAgentV2 defaults, and Warp cockpit view coverage. `sks code-structure scan` flags handwritten files above 1000/2000/3000-line thresholds so new logic can be extracted before command files become harder to maintain.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sneakoscope",
3
3
  "displayName": "ㅅㅋㅅ",
4
- "version": "0.6.87",
4
+ "version": "0.6.89",
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
@@ -1385,6 +1385,7 @@ async function codexAppHelp(args = []) {
1385
1385
  `Skills: project=${skills.project.ok ? 'ok' : `missing ${skills.project.missing.length}`} global=${skills.global.ok ? 'ok' : `missing ${skills.global.missing.length}`}`, '',
1386
1386
  'Setup:', ' sks bootstrap', ' sks deps check', ' sks codex-app check', ' sks warp check', '',
1387
1387
  'Generated files:', ' .codex/config.toml', ' .codex/hooks.json', ' .agents/skills/', ' .codex/agents/', ' .codex/SNEAKOSCOPE.md', ' AGENTS.md', '',
1388
+ 'Git ignore:', ' default setup writes .gitignore entries for .sneakoscope/, .codex/, .agents/, AGENTS.md', ' --local-only writes those patterns to .git/info/exclude instead', '',
1388
1389
  'Prompt routes:', formatDollarCommandsCompact(' ')
1389
1390
  ].join('\n'));
1390
1391
  }
@@ -1564,6 +1565,7 @@ async function setup(args) {
1564
1565
  console.log(`Hooks: ${path.relative(root, hooksPath)}`);
1565
1566
  console.log(`Version: ${versioningInfo.enabled ? (versioningInfo.hook_installed ? 'auto-bump enabled' : 'auto-bump hook missing') : 'not enabled'}${versioningInfo.package_version ? ` (${versioningInfo.package_version})` : ''}`);
1566
1567
  if (localOnly) console.log('Git: local-only (.git/info/exclude; user AGENTS preserved, SKS managed block refreshed)');
1568
+ else console.log('Git: .gitignore ignores SKS generated files');
1567
1569
  console.log(`Codex App: .codex/config.toml, .codex/hooks.json, .agents/skills, .codex/agents, .codex/SNEAKOSCOPE.md`);
1568
1570
  console.log(`Global $: ${globalSkills.status === 'installed' ? 'ok' : globalSkills.status} ${globalSkills.root || ''}`.trimEnd());
1569
1571
  console.log(`App tools: ${appRuntime.ok ? 'ok' : 'needs setup'} Codex App=${appRuntime.app.installed ? 'ok' : 'missing'} Browser Use=${appRuntime.mcp.has_browser_use ? 'ok' : 'missing'} Computer Use=${appRuntime.mcp.has_computer_use ? 'ok' : 'missing'}`);
@@ -1759,6 +1761,7 @@ async function init(args) {
1759
1761
  console.log(`Initialized ㅅㅋㅅ in ${root}`);
1760
1762
  console.log(`Install scope: ${installScope} (${sksCommandPrefix(installScope, { globalCommand })})`);
1761
1763
  if (localOnly) console.log('Git mode: local-only (.git/info/exclude)');
1764
+ else console.log('Git mode: shared .gitignore');
1762
1765
  for (const x of res.created) console.log(`- ${x}`);
1763
1766
  }
1764
1767
 
@@ -1967,9 +1970,11 @@ async function selftest() {
1967
1970
  await writeJsonAtomic(path.join(postinstallBootstrapTmp, 'package.json'), { name: 'postinstall-bootstrap-smoke', version: '0.0.0' });
1968
1971
  const postinstallBootstrap = await runProcess(process.execPath, [path.join(packageRoot(), 'bin', 'sks.mjs'), 'postinstall'], { cwd: postinstallBootstrapTmp, input: 'y\n', env: { INIT_CWD: postinstallBootstrapTmp, HOME: path.join(postinstallBootstrapTmp, 'home'), SKS_SKIP_POSTINSTALL_SHIM: '1', SKS_SKIP_POSTINSTALL_CONTEXT7: '1', SKS_SKIP_POSTINSTALL_GLOBAL_SKILLS: '1', SKS_SKIP_CLI_TOOLS: '1', SKS_POSTINSTALL_PROMPT: '1' }, timeoutMs: 30000, maxOutputBytes: 256 * 1024 });
1969
1972
  if (postinstallBootstrap.code !== 0 || !String(postinstallBootstrap.stdout || '').includes('SKS Ready')) throw new Error(`selftest failed: approved postinstall bootstrap did not run: ${postinstallBootstrap.stderr}`);
1970
- for (const rel of ['.agents/skills/team/SKILL.md', '.codex/config.toml', '.codex/hooks.json', '.sneakoscope/harness-guard.json', '.codex/SNEAKOSCOPE.md', 'AGENTS.md']) {
1973
+ for (const rel of ['.agents/skills/team/SKILL.md', '.codex/config.toml', '.codex/hooks.json', '.sneakoscope/harness-guard.json', '.codex/SNEAKOSCOPE.md', 'AGENTS.md', '.gitignore']) {
1971
1974
  if (!(await exists(path.join(postinstallBootstrapTmp, rel)))) throw new Error(`selftest failed: bootstrap did not create ${rel}`);
1972
1975
  }
1976
+ const postinstallBootstrapGitignore = await safeReadText(path.join(postinstallBootstrapTmp, '.gitignore'));
1977
+ if (!postinstallBootstrapGitignore.includes('.sneakoscope/') || !postinstallBootstrapGitignore.includes('.codex/') || !postinstallBootstrapGitignore.includes('.agents/') || !postinstallBootstrapGitignore.includes('AGENTS.md')) throw new Error('selftest failed: bootstrap did not ignore SKS generated files');
1973
1978
  const bootstrapJsonTmp = tmpdir();
1974
1979
  await writeJsonAtomic(path.join(bootstrapJsonTmp, 'package.json'), { name: 'bootstrap-json-smoke', version: '0.0.0' });
1975
1980
  const bootstrapJson = await runProcess(process.execPath, [path.join(packageRoot(), 'bin', 'sks.mjs'), 'bootstrap', '--json'], { cwd: bootstrapJsonTmp, env: { HOME: path.join(bootstrapJsonTmp, 'home'), SKS_SKIP_POSTINSTALL_GLOBAL_SKILLS: '1', SKS_SKIP_CLI_TOOLS: '1' }, timeoutMs: 30000, maxOutputBytes: 256 * 1024 });
@@ -2090,10 +2095,19 @@ async function selftest() {
2090
2095
  await initProject(localOnlyTmp, { localOnly: true });
2091
2096
  const localExclude = await safeReadText(path.join(localOnlyTmp, '.git', 'info', 'exclude'));
2092
2097
  if (!localExclude.includes('.codex/') || !localExclude.includes('AGENTS.md')) throw new Error('selftest failed: local-only git excludes missing');
2098
+ if (await exists(path.join(localOnlyTmp, '.gitignore'))) throw new Error('selftest failed: local-only wrote shared .gitignore');
2093
2099
  const localAgents = await safeReadText(path.join(localOnlyTmp, 'AGENTS.md'));
2094
2100
  if (localAgents.trim() !== 'existing local rules') throw new Error('selftest failed: local-only modified existing AGENTS.md');
2095
2101
  const localManifest = await readJson(path.join(localOnlyTmp, '.sneakoscope', 'manifest.json'));
2096
2102
  if (!localManifest.git?.local_only) throw new Error('selftest failed: local-only manifest missing');
2103
+ const gitignoreTmp = tmpdir();
2104
+ await writeTextAtomic(path.join(gitignoreTmp, '.gitignore'), 'node_modules/\n.sneakoscope/\n');
2105
+ await initProject(gitignoreTmp, {});
2106
+ const gitignoreText = await safeReadText(path.join(gitignoreTmp, '.gitignore'));
2107
+ if (!gitignoreText.includes('node_modules/') || !gitignoreText.includes('# BEGIN Sneakoscope Codex generated files') || !gitignoreText.includes('.codex/') || !gitignoreText.includes('.agents/') || !gitignoreText.includes('AGENTS.md')) throw new Error('selftest failed: shared .gitignore did not preserve existing entries and add SKS patterns');
2108
+ await initProject(gitignoreTmp, {});
2109
+ const gitignoreTextSecond = await safeReadText(path.join(gitignoreTmp, '.gitignore'));
2110
+ if ((gitignoreTextSecond.match(/BEGIN Sneakoscope Codex generated files/g) || []).length !== 1) throw new Error('selftest failed: shared .gitignore managed block duplicated');
2097
2111
  const managedAgentsTmp = tmpdir();
2098
2112
  await ensureDir(path.join(managedAgentsTmp, '.git'));
2099
2113
  await writeTextAtomic(path.join(managedAgentsTmp, 'AGENTS.md'), '<!-- BEGIN Sneakoscope Codex GX MANAGED BLOCK -->\nold managed rules\n<!-- END Sneakoscope Codex GX MANAGED BLOCK -->\n');
@@ -89,6 +89,8 @@ Local-only install artifacts:
89
89
  # writes generated SKS files but excludes .sneakoscope/, .codex/, .agents/, AGENTS.md through .git/info/exclude
90
90
  # user-owned AGENTS.md is preserved; an existing SKS managed block is refreshed
91
91
 
92
+ Default project setup writes the same SKS generated-file patterns into the project .gitignore.
93
+
92
94
  GitHub install for unreleased commits:
93
95
  npm i -g git+${REPOSITORY_URL}
94
96
  sks bootstrap
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.87';
8
+ export const PACKAGE_VERSION = '0.6.89';
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
@@ -9,6 +9,8 @@ import { installVersionGitHook } from './version-manager.mjs';
9
9
  import { CODEX_COMPUTER_USE_ONLY_POLICY, DOLLAR_COMMANDS, DOLLAR_COMMAND_ALIASES, DOLLAR_SKILL_NAMES, FROM_CHAT_IMG_CHECKLIST_ARTIFACT, FROM_CHAT_IMG_COVERAGE_ARTIFACT, FROM_CHAT_IMG_QA_LOOP_ARTIFACT, FROM_CHAT_IMG_TEMP_TRIWIKI_ARTIFACT, FROM_CHAT_IMG_TEMP_TRIWIKI_SESSIONS, RECOMMENDED_MCP_SERVERS, RECOMMENDED_SKILLS, chatCaptureIntakeText, context7ConfigToml, stackCurrentDocsPolicyText, triwikiContextTracking, triwikiContextTrackingText, triwikiStagePolicyText } from './routes.mjs';
10
10
 
11
11
  const REFLECTION_MEMORY_PATH = '.sneakoscope/memory/q2_facts/post-route-reflection.md';
12
+ const SKS_GENERATED_GIT_PATTERNS = ['.sneakoscope/', '.codex/', '.agents/', 'AGENTS.md'];
13
+
12
14
  function reflectionInstructionText(commandPrefix = 'sks') {
13
15
  return `Post-route reflection: full routes load \`reflection\` after work/tests and before final; DFix/Answer/Help/Wiki/SKS discovery are exempt. Write reflection.md; record only real misses/gaps, or no_issue_acknowledged. For lessons, append TriWiki claim rows to ${REFLECTION_MEMORY_PATH}. Run "${commandPrefix} wiki refresh" or pack, validate, then pass reflection-gate.json.`;
14
16
  }
@@ -107,7 +109,9 @@ export async function initProject(root, opts = {}) {
107
109
  ];
108
110
  for (const d of dirs) await ensureDir(path.join(root, d));
109
111
  const localExclude = localOnly ? await ensureLocalOnlyGitExclude(root) : null;
112
+ const sharedIgnore = localOnly ? null : await ensureSharedGitIgnore(root);
110
113
  if (localExclude?.path) created.push(`${path.relative(root, localExclude.path)} local-only excludes`);
114
+ if (sharedIgnore?.changed) created.push(`${path.relative(root, sharedIgnore.path)} SKS generated files ignore`);
111
115
 
112
116
  await writeJsonAtomic(path.join(sine, 'manifest.json'), {
113
117
  package: 'sneakoscope',
@@ -165,6 +169,8 @@ export async function initProject(root, opts = {}) {
165
169
  },
166
170
  git: {
167
171
  local_only: localOnly,
172
+ ignore_path: sharedIgnore?.path ? path.relative(root, sharedIgnore.path) : null,
173
+ ignored_patterns: sharedIgnore?.patterns || [],
168
174
  exclude_path: localExclude?.path ? path.relative(root, localExclude.path) : null,
169
175
  excluded_patterns: localExclude?.patterns || [],
170
176
  versioning: {
@@ -198,6 +204,8 @@ export async function initProject(root, opts = {}) {
198
204
  git: {
199
205
  ...(policy.git || {}),
200
206
  local_only: localOnly || Boolean(policy.git?.local_only),
207
+ ignore_path: sharedIgnore?.path ? path.relative(root, sharedIgnore.path) : policy.git?.ignore_path || null,
208
+ ignored_patterns: sharedIgnore?.patterns || policy.git?.ignored_patterns || [],
201
209
  exclude_path: localExclude?.path ? path.relative(root, localExclude.path) : policy.git?.exclude_path || null,
202
210
  excluded_patterns: localExclude?.patterns || policy.git?.excluded_patterns || [],
203
211
  versioning: {
@@ -267,6 +275,8 @@ export async function initProject(root, opts = {}) {
267
275
  installation: installPolicy(scope, commandPrefix),
268
276
  git: {
269
277
  local_only: localOnly,
278
+ ignore_path: sharedIgnore?.path ? path.relative(root, sharedIgnore.path) : null,
279
+ ignored_patterns: sharedIgnore?.patterns || [],
270
280
  exclude_path: localExclude?.path ? path.relative(root, localExclude.path) : null,
271
281
  excluded_patterns: localExclude?.patterns || [],
272
282
  versioning: {
@@ -445,10 +455,33 @@ policy = "Deny destructive database operations, credential exfiltration, persist
445
455
  return { created };
446
456
  }
447
457
 
458
+ async function ensureSharedGitIgnore(root) {
459
+ const patterns = SKS_GENERATED_GIT_PATTERNS;
460
+ const ignorePath = path.join(root, '.gitignore');
461
+ const markerStart = '# BEGIN Sneakoscope Codex generated files';
462
+ const markerEnd = '# END Sneakoscope Codex generated files';
463
+ const managedBlock = `${markerStart}\n${patterns.join('\n')}\n${markerEnd}\n`;
464
+ const current = await readText(ignorePath, '');
465
+ if (current.includes(markerStart)) {
466
+ const re = new RegExp(`${escapeRegExp(markerStart)}[\\s\\S]*?${escapeRegExp(markerEnd)}\\n?`);
467
+ const next = current.replace(re, managedBlock);
468
+ if (next !== current) await writeTextAtomic(ignorePath, next.endsWith('\n') ? next : `${next}\n`);
469
+ return { path: ignorePath, patterns, changed: next !== current };
470
+ }
471
+ const existing = new Set(current.split(/\r?\n/).map((line) => line.trim()).filter(Boolean));
472
+ const missing = patterns.filter((pattern) => !existing.has(pattern));
473
+ if (!missing.length) return { path: ignorePath, patterns, changed: false };
474
+ const block = missing.length === patterns.length
475
+ ? managedBlock
476
+ : `${markerStart}\n${missing.join('\n')}\n${markerEnd}\n`;
477
+ await writeTextAtomic(ignorePath, `${current.trimEnd()}${current.trim() ? '\n\n' : ''}${block}`);
478
+ return { path: ignorePath, patterns, changed: true };
479
+ }
480
+
448
481
  async function ensureLocalOnlyGitExclude(root) {
449
482
  const gitDir = await resolveGitDir(root);
450
483
  if (!gitDir) return { path: null, patterns: [] };
451
- const patterns = ['.sneakoscope/', '.codex/', '.agents/', 'AGENTS.md'];
484
+ const patterns = SKS_GENERATED_GIT_PATTERNS;
452
485
  const excludePath = path.join(gitDir, 'info', 'exclude');
453
486
  await ensureDir(path.dirname(excludePath));
454
487
  const markerStart = '# Sneakoscope Codex local-only generated files';