sneakoscope 3.1.5 → 3.1.7

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.
Files changed (58) hide show
  1. package/README.md +9 -37
  2. package/crates/sks-core/Cargo.lock +1 -1
  3. package/crates/sks-core/Cargo.toml +1 -1
  4. package/crates/sks-core/src/main.rs +1 -1
  5. package/dist/.sks-build-stamp.json +4 -4
  6. package/dist/bin/sks.js +1 -1
  7. package/dist/cli/command-registry.js +1 -2
  8. package/dist/commands/codex-app.js +20 -12
  9. package/dist/commands/codex-native.js +84 -0
  10. package/dist/commands/doctor.js +90 -2
  11. package/dist/core/codex-app/codex-agent-role-sync.js +15 -5
  12. package/dist/core/codex-app/codex-app-execution-profile.js +38 -16
  13. package/dist/core/codex-app/codex-app-harness-matrix.js +4 -117
  14. package/dist/core/codex-app/codex-hook-lifecycle.js +4 -1
  15. package/dist/core/codex-app/codex-init-deep.js +66 -4
  16. package/dist/core/codex-app/codex-skill-sync.js +13 -8
  17. package/dist/core/codex-control/codex-0138-capability.js +5 -2
  18. package/dist/core/codex-native/codex-native-capability-cache.js +21 -0
  19. package/dist/core/codex-native/codex-native-feature-broker.js +250 -0
  20. package/dist/core/codex-native/codex-native-feature-matrix.js +31 -0
  21. package/dist/core/codex-native/codex-native-harness-compat.js +54 -0
  22. package/dist/core/{codex-app/lazycodex-interop-policy.js → codex-native/codex-native-interop-policy.js} +13 -15
  23. package/dist/core/codex-native/codex-native-invocation-router.js +112 -0
  24. package/dist/core/codex-native/codex-native-pattern-analysis.js +68 -0
  25. package/dist/core/codex-native/codex-native-reference-cache.js +98 -0
  26. package/dist/core/codex-native/codex-native-reference-evidence.js +2 -0
  27. package/dist/core/codex-native/codex-native-reference-source.js +149 -0
  28. package/dist/core/codex-native/codex-native-rename-map.js +25 -0
  29. package/dist/core/codex-native/codex-native-repair-transaction.js +150 -0
  30. package/dist/core/codex-plugins/codex-plugin-json.js +5 -2
  31. package/dist/core/commands/mad-sks-command.js +16 -0
  32. package/dist/core/feature-fixtures.js +2 -4
  33. package/dist/core/feature-registry.js +1 -1
  34. package/dist/core/fsx.js +1 -1
  35. package/dist/core/image/image-artifact-path-contract.js +18 -1
  36. package/dist/core/loops/loop-owner-inference.js +3 -0
  37. package/dist/core/loops/loop-planner.js +8 -2
  38. package/dist/core/loops/loop-worker-prompts.js +2 -0
  39. package/dist/core/loops/loop-worker-runtime.js +42 -7
  40. package/dist/core/qa-loop.js +24 -1
  41. package/dist/core/research.js +36 -3
  42. package/dist/core/routes.js +2 -3
  43. package/dist/core/version.js +1 -1
  44. package/dist/scripts/codex-native-runtime-e2e-fixture.js +75 -0
  45. package/dist/scripts/loop-worker-fixture-child.js +2 -1
  46. package/dist/scripts/sks-3-1-4-directive-check-lib.js +1 -30
  47. package/dist/scripts/sks-3-1-5-directive-check-lib.js +4 -33
  48. package/dist/scripts/sks-3-1-6-directive-check-lib.js +522 -0
  49. package/dist/scripts/sks-3-1-7-directive-check-lib.js +58 -0
  50. package/package.json +44 -13
  51. package/dist/cli/hermes-command.js +0 -99
  52. package/dist/cli/openclaw-command.js +0 -83
  53. package/dist/commands/hermes.js +0 -5
  54. package/dist/commands/openclaw.js +0 -3
  55. package/dist/core/codex-app/lazycodex-analysis.js +0 -72
  56. package/dist/core/codex-app/lazycodex-live-analyzer.js +0 -98
  57. package/dist/core/hermes.js +0 -192
  58. package/dist/core/openclaw.js +0 -171
@@ -0,0 +1,54 @@
1
+ import { buildCodexNativeFeatureMatrix } from './codex-native-feature-broker.js';
2
+ export async function buildCodexAppHarnessMatrixFromNative(input) {
3
+ const matrix = await buildCodexNativeFeatureMatrix(input);
4
+ return codexAppHarnessMatrixFromNative(matrix);
5
+ }
6
+ export function codexAppHarnessMatrixFromNative(matrix) {
7
+ const hookApproval = probeRecord(matrix.probes.hook_approval);
8
+ const agentType = probeRecord(matrix.probes.agent_type);
9
+ const hookState = typeof hookApproval.approval_state === 'string' ? hookApproval.approval_state : 'unknown';
10
+ return {
11
+ schema: 'sks.codex-app-harness-matrix.v1',
12
+ generated_at: matrix.generated_at,
13
+ ok: matrix.ok,
14
+ codex_cli: {
15
+ available: matrix.codex_cli.available,
16
+ version: matrix.codex_cli.version
17
+ },
18
+ app_features: {
19
+ plugin_json: matrix.features.plugin_json.ok,
20
+ marketplace_add: matrix.features.plugin_marketplace.ok,
21
+ marketplace_upgrade: matrix.features.plugin_marketplace.ok,
22
+ startup_review_detectable: hookState !== 'unknown',
23
+ hook_approval_state_detectable: hookState !== 'unknown',
24
+ hook_approval_state: hookState === 'approved'
25
+ || hookState === 'pending_review'
26
+ || hookState === 'modified_requires_reapproval'
27
+ || hookState === 'not_installed'
28
+ ? hookState
29
+ : 'unknown',
30
+ skill_picker_ready: matrix.features.skill_picker.ok,
31
+ agent_type_supported: matrix.features.agent_type.ok,
32
+ mcp_inventory_ready: matrix.features.mcp_inventory.ok,
33
+ app_handoff_ready: matrix.features.app_handoff.ok,
34
+ image_path_exposure_ready: matrix.features.image_path_exposure.ok
35
+ },
36
+ sks_integrations: {
37
+ dollar_skills_synced: matrix.features.skill_sync.ok,
38
+ agent_roles_synced: matrix.features.agent_roles.ok,
39
+ hooks_synced: hookState === 'approved',
40
+ init_deep_available: matrix.features.project_memory.ok,
41
+ loop_mesh_app_profile_available: true
42
+ },
43
+ probes: {
44
+ hook_approval: hookApproval,
45
+ agent_type: agentType
46
+ },
47
+ blockers: matrix.blockers,
48
+ warnings: matrix.warnings
49
+ };
50
+ }
51
+ function probeRecord(value) {
52
+ return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
53
+ }
54
+ //# sourceMappingURL=codex-native-harness-compat.js.map
@@ -1,38 +1,36 @@
1
- import fs from 'node:fs/promises';
2
1
  import path from 'node:path';
3
2
  import os from 'node:os';
3
+ import fs from 'node:fs/promises';
4
4
  import { nowIso, writeJsonAtomic } from '../fsx.js';
5
5
  import { buildCodexPluginInventory } from '../codex-plugins/codex-plugin-json.js';
6
- export async function buildLazyCodexInteropPolicy(input) {
6
+ const RESERVED_EXTERNAL_ROUTE_SKILLS = ['ulw-loop', 'ulw-plan', 'start-work'];
7
+ export async function buildCodexNativeInteropPolicy(input) {
7
8
  const root = path.resolve(input.root);
8
9
  const inventory = normalizeInventory(input.inventory || await buildCodexPluginInventory().catch((err) => ({ plugins: [], blockers: [messageOf(err)] })));
9
10
  const codexHome = input.codexHome || process.env.CODEX_HOME || path.join(os.homedir(), '.codex');
10
11
  const skillNames = await discoverSkillNames([path.join(root, '.agents', 'skills'), path.join(codexHome, 'skills')]);
11
- const pluginIds = (inventory.plugins || []).map((plugin) => `${plugin.id || ''} ${plugin.name || ''}`.toLowerCase());
12
- const lazycodexInstalled = pluginIds.some((id) => id.includes('omo') || id.includes('lazycodex'))
13
- || ['ulw-loop', 'ulw-plan', 'start-work'].some((name) => skillNames.includes(name));
14
- const collisions = ['ulw-loop', 'ulw-plan', 'start-work'].filter((name) => skillNames.includes(name));
12
+ const pluginIds = (inventory.plugins || []).map((plugin) => `${plugin.id || ''} ${plugin.name || ''}`.toLowerCase()).filter(Boolean);
13
+ const preservedSkillNames = RESERVED_EXTERNAL_ROUTE_SKILLS.filter((name) => skillNames.includes(name));
15
14
  const report = {
16
- schema: 'sks.lazycodex-interop-policy.v1',
15
+ schema: 'sks.codex-native-interop-policy.v1',
17
16
  generated_at: nowIso(),
18
17
  ok: true,
19
18
  mode: input.mode || 'coexist',
20
- lazycodex_detected: lazycodexInstalled,
21
19
  detection: {
22
20
  plugin_inventory_ids: pluginIds,
23
21
  skill_names: skillNames,
24
- collisions
22
+ preserved_skill_names: preservedSkillNames
25
23
  },
26
24
  policy: {
27
- clobber_lazycodex_skills: false,
28
25
  clobber_user_skills: false,
29
- default_mode: 'coexist',
30
- explicit_handoff_required: true
26
+ clobber_external_route_assets: false,
27
+ explicit_handoff_required: true,
28
+ artifact_names_brand_neutral: true
31
29
  },
32
- actions: collisions.map((name) => `preserve_existing_skill:${name}`),
30
+ actions: preservedSkillNames.map((name) => `preserve_existing_skill:${name}`),
33
31
  blockers: []
34
32
  };
35
- await writeJsonAtomic(path.join(root, '.sneakoscope', 'reports', 'lazycodex-interop-policy.json'), report).catch(() => undefined);
33
+ await writeJsonAtomic(path.join(root, '.sneakoscope', 'reports', 'codex-native-interop-policy.json'), report).catch(() => undefined);
36
34
  return report;
37
35
  }
38
36
  function normalizeInventory(value) {
@@ -57,4 +55,4 @@ async function discoverSkillNames(roots) {
57
55
  }
58
56
  return [...names].sort();
59
57
  }
60
- //# sourceMappingURL=lazycodex-interop-policy.js.map
58
+ //# sourceMappingURL=codex-native-interop-policy.js.map
@@ -0,0 +1,112 @@
1
+ import path from 'node:path';
2
+ import { nowIso, writeJsonAtomic } from '../fsx.js';
3
+ import { buildCodexNativeFeatureMatrix } from './codex-native-feature-broker.js';
4
+ export async function resolveCodexNativeInvocationPlan(input) {
5
+ const root = path.resolve(input.root || process.cwd());
6
+ const matrix = input.matrix || await buildCodexNativeFeatureMatrix({
7
+ root,
8
+ missionDir: input.missionId ? path.join(root, '.sneakoscope', 'missions', input.missionId) : null
9
+ });
10
+ const plan = planFor(matrix, input.route, input.desiredCapability);
11
+ if (input.missionId) {
12
+ const filename = `codex-native-invocation-plan.${routeSlug(input.route)}.${capabilitySlug(input.desiredCapability)}.json`;
13
+ await writeJsonAtomic(path.join(root, '.sneakoscope', 'missions', input.missionId, filename), plan).catch(() => undefined);
14
+ }
15
+ await writeJsonAtomic(path.join(root, '.sneakoscope', 'reports', 'codex-native-invocation-plan.json'), plan).catch(() => undefined);
16
+ return plan;
17
+ }
18
+ function planFor(matrix, route, capability) {
19
+ const blockers = [];
20
+ const warnings = [];
21
+ let selected = 'sks-managed-artifact';
22
+ const artifacts = ['.sneakoscope/reports/codex-native-feature-matrix.json'];
23
+ const proofPolicy = ['record selected strategy and blockers before counting route evidence'];
24
+ if (capability === 'agent-role') {
25
+ if (matrix.invocation_defaults.loop_worker_role_strategy === 'agent_type') {
26
+ selected = 'codex-app-native';
27
+ proofPolicy.push('include native agent_type payload in worker proof');
28
+ }
29
+ else {
30
+ selected = 'message-role-fallback';
31
+ warnings.push('agent_type unavailable; message-role fallback active');
32
+ proofPolicy.push('include message-role fallback in worker proof');
33
+ }
34
+ }
35
+ else if (capability === 'visual-review') {
36
+ if (matrix.invocation_defaults.hook_evidence_policy === 'unknown-do-not-count')
37
+ warnings.push('hook-derived evidence will not count');
38
+ selected = matrix.invocation_defaults.qa_visual_review_strategy === 'app-handoff' ? 'codex-app-native' : 'sks-managed-artifact';
39
+ if (matrix.invocation_defaults.qa_visual_review_strategy === 'blocked') {
40
+ selected = 'blocked';
41
+ blockers.push('qa_visual_review_unavailable');
42
+ }
43
+ proofPolicy.push('do not pass visual review without accepted artifact or app handoff confirmation');
44
+ }
45
+ else if (capability === 'plugin-source' || capability === 'mcp-source') {
46
+ if (matrix.features.mcp_inventory.ok) {
47
+ selected = 'codex-app-native';
48
+ artifacts.push('.sneakoscope/mcp-plugin-server-candidates.json');
49
+ proofPolicy.push('treat remote MCP servers as candidates only until explicitly enabled');
50
+ }
51
+ else if (matrix.features.code_mode_web_search.ok) {
52
+ selected = 'codex-cli-headless';
53
+ warnings.push('plugin inventory unavailable; code/web source fallback selected');
54
+ }
55
+ else {
56
+ selected = 'sks-managed-artifact';
57
+ warnings.push('plugin inventory unavailable; local/source-ledger fallback selected');
58
+ }
59
+ }
60
+ else if (capability === 'web-search') {
61
+ selected = matrix.features.code_mode_web_search.ok ? 'codex-cli-headless' : 'sks-managed-artifact';
62
+ if (!matrix.features.code_mode_web_search.ok)
63
+ warnings.push('code-mode web search not verified');
64
+ }
65
+ else if (capability === 'image-followup') {
66
+ selected = matrix.features.image_path_exposure.ok ? 'codex-app-native' : 'sks-managed-artifact';
67
+ proofPolicy.push(matrix.features.image_path_exposure.ok ? 'include model-visible image path' : 'use saved artifact path contract');
68
+ }
69
+ else if (capability === 'hook-evidence') {
70
+ if (matrix.invocation_defaults.hook_evidence_policy !== 'approved-only') {
71
+ selected = 'blocked';
72
+ blockers.push('hook_approval_not_approved');
73
+ warnings.push('hook-derived proof cannot count');
74
+ }
75
+ else {
76
+ selected = 'codex-app-native';
77
+ }
78
+ proofPolicy.push('approved hooks only; unknown hook state does not count');
79
+ }
80
+ else if (capability === 'project-memory') {
81
+ selected = matrix.features.project_memory.ok ? 'sks-managed-artifact' : 'blocked';
82
+ if (!matrix.features.project_memory.ok)
83
+ blockers.push('project_memory_unavailable');
84
+ proofPolicy.push('project memory is guidance only and never expands write scope');
85
+ }
86
+ return {
87
+ schema: 'sks.codex-native-invocation-plan.v1',
88
+ generated_at: nowIso(),
89
+ ok: blockers.length === 0,
90
+ route,
91
+ desired_capability: capability,
92
+ selected_strategy: selected,
93
+ required_artifacts: artifacts,
94
+ proof_policy: proofPolicy,
95
+ env: {
96
+ SKS_CODEX_NATIVE_STRATEGY: selected,
97
+ SKS_CODEX_NATIVE_FEATURE_MATRIX: '.sneakoscope/reports/codex-native-feature-matrix.json',
98
+ SKS_CODEX_NATIVE_AGENT_ROLE_STRATEGY: matrix.invocation_defaults.loop_worker_role_strategy,
99
+ SKS_CODEX_NATIVE_HOOK_EVIDENCE_POLICY: matrix.invocation_defaults.hook_evidence_policy
100
+ },
101
+ blockers,
102
+ warnings,
103
+ feature_matrix_artifact: '.sneakoscope/reports/codex-native-feature-matrix.json'
104
+ };
105
+ }
106
+ function routeSlug(route) {
107
+ return route.replace(/^\$/, '').toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
108
+ }
109
+ function capabilitySlug(capability) {
110
+ return capability.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
111
+ }
112
+ //# sourceMappingURL=codex-native-invocation-router.js.map
@@ -0,0 +1,68 @@
1
+ import path from 'node:path';
2
+ import { createHash } from 'node:crypto';
3
+ import { nowIso, writeJsonAtomic, writeTextAtomic } from '../fsx.js';
4
+ import { analyzeCodexNativeReferenceSource, renderCodexNativeReferenceMarkdown } from './codex-native-reference-source.js';
5
+ const PATTERN_ROWS = [
6
+ row('no-global-optional-tooling', 'Optional tooling stays non-global by default', 'adapt', ['src/core/zellij/zellij-self-heal.ts'], ['Keep optional repair/install paths explicit and proof-backed.']),
7
+ row('plugin-lifecycle-state-separation', 'Plugin install and approval are separate states', 'adopt', ['src/core/codex-native/codex-native-feature-broker.ts'], ['Do not count installed tools as approved evidence.']),
8
+ row('hook-approval-gating', 'Hook approval gates counted evidence', 'adopt', ['src/core/codex-app/codex-hook-lifecycle.ts'], ['Unknown or pending hook approval does not count as proof.']),
9
+ row('skill-picker-route-bridge', 'SKS route skills bridge command picker usage', 'adapt', ['src/core/codex-app/codex-skill-sync.ts'], ['Managed skills expose route purpose, command, proof path, and failure recovery.']),
10
+ row('native-agent-role-probe', 'Native agent role payload is probed before use', 'adopt', ['src/core/codex-app/codex-agent-type-probe.ts'], ['Use agent_type only when the probe supports it.']),
11
+ row('message-role-fallback', 'Message-role fallback is explicit', 'adopt', ['src/core/codex-native/codex-native-invocation-router.ts'], ['Fallback remains valid but must be recorded in proof.']),
12
+ row('directory-local-memory', 'Directory-local memory is guidance', 'adapt', ['src/core/codex-app/codex-init-deep.ts', 'src/core/loops/loop-planner.ts'], ['Memory hints never widen owner scope.']),
13
+ row('plan-work-proof-separation', 'Plan, work, and proof stay separate', 'adopt', ['src/core/loops', 'src/core/naruto'], ['Route artifacts separate planning, execution, and verification.']),
14
+ row('continuation-enforcer', 'Continuation is stateful and bounded', 'adapt', ['src/core/loops/loop-continuation-enforcer.ts'], ['Use runtime proof when hook approval is not approved.']),
15
+ row('doctor-harness-matrix', 'Doctor merges readiness into one story', 'adopt', ['src/commands/doctor.ts'], ['Doctor should show Zellij, Codex Native, Loop, QA, and Research together.']),
16
+ row('mcp-tool-candidate-inventory', 'MCP plugin servers are candidates', 'adapt', ['src/core/mcp/mcp-plugin-inventory.ts'], ['Candidate-only inventory prevents accidental destructive auto-enable.']),
17
+ row('non-clobber-managed-assets', 'Managed assets avoid clobbering user files', 'adopt', ['src/core/codex-app/codex-skill-sync.ts', 'src/core/codex-app/codex-agent-role-sync.ts'], ['Skip unmarked user assets and include checksums for managed content.'])
18
+ ];
19
+ export async function buildCodexNativePatternAnalysis(input) {
20
+ const root = path.resolve(input.root);
21
+ const evidence = input.evidence || await analyzeCodexNativeReferenceSource({ root, sourceDir: input.sourceDir || null, writeReport: true }).catch(() => null);
22
+ const evidenceByPattern = new Map();
23
+ const confidenceByPattern = new Map();
24
+ for (const item of evidence?.evidence || []) {
25
+ const list = evidenceByPattern.get(item.pattern_id) || [];
26
+ list.push(item.snippet_hash);
27
+ evidenceByPattern.set(item.pattern_id, list);
28
+ confidenceByPattern.set(item.pattern_id, strongerConfidence(confidenceByPattern.get(item.pattern_id), item.confidence));
29
+ }
30
+ return {
31
+ schema: 'sks.codex-native-pattern-analysis.v1',
32
+ generated_at: nowIso(),
33
+ source_kind: 'external-reference-source',
34
+ source_ref: evidence?.source_ref || neutralSourceRef(input.sourceDir || '.sneakoscope/cache/codex-native-reference'),
35
+ source_sha: evidence?.source_sha || null,
36
+ source_url_hash: evidence?.source_url_hash || null,
37
+ cache_report_path: evidence?.cache_report_path || null,
38
+ patterns: PATTERN_ROWS.map((pattern) => {
39
+ const hashes = evidenceByPattern.get(pattern.id) || [];
40
+ return {
41
+ ...pattern,
42
+ confidence: confidenceByPattern.get(pattern.id) || (hashes.length ? 'medium' : evidence ? 'low' : 'low'),
43
+ evidence_hashes: hashes
44
+ };
45
+ }),
46
+ blockers: evidence?.blockers || ['source_snapshot_missing'],
47
+ warnings: evidence?.warnings || ['reference_evidence_unavailable']
48
+ };
49
+ }
50
+ function neutralSourceRef(value) {
51
+ return `source:${createHash('sha256').update(value).digest('hex').slice(0, 16)}`;
52
+ }
53
+ function strongerConfidence(current, next) {
54
+ const rank = { low: 0, medium: 1, high: 2 };
55
+ return current && rank[current] >= rank[next] ? current : next;
56
+ }
57
+ export async function writeCodexNativePatternAnalysis(root, input = {}) {
58
+ const evidence = await analyzeCodexNativeReferenceSource({ root, sourceDir: input.sourceDir || null, writeReport: true }).catch(() => null);
59
+ const report = await buildCodexNativePatternAnalysis({ root, evidence, sourceDir: input.sourceDir || null });
60
+ await writeJsonAtomic(path.join(root, '.sneakoscope', 'reports', 'codex-native-pattern-analysis.json'), report);
61
+ if (evidence)
62
+ await writeTextAtomic(path.join(root, 'docs', 'codex-native-patterns.md'), renderCodexNativeReferenceMarkdown(evidence)).catch(() => undefined);
63
+ return report;
64
+ }
65
+ function row(id, title, adoption, sks_target_modules, implementation_notes) {
66
+ return { id, title, adoption, sks_target_modules, implementation_notes };
67
+ }
68
+ //# sourceMappingURL=codex-native-pattern-analysis.js.map
@@ -0,0 +1,98 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { exists, nowIso, runProcess, sha256, writeJsonAtomic } from '../fsx.js';
4
+ export async function ensureCodexNativeReferenceSnapshot(input) {
5
+ const root = path.resolve(input.root);
6
+ const cacheDir = path.join(root, '.sneakoscope', 'cache', 'codex-native-reference');
7
+ const reportPath = path.join(root, '.sneakoscope', 'reports', 'codex-native-reference-cache.json');
8
+ const sourceUrl = input.sourceUrl ?? process.env.SKS_CODEX_NATIVE_REFERENCE_SOURCE_URL ?? null;
9
+ const sourceRef = input.ref || process.env.SKS_CODEX_NATIVE_REFERENCE_REF || 'HEAD';
10
+ const offline = input.offline === true || process.env.SKS_CODEX_NATIVE_REFERENCE_OFFLINE === '1' || !sourceUrl;
11
+ const blockers = [];
12
+ const warnings = [];
13
+ let refreshed = false;
14
+ if (!offline && sourceUrl) {
15
+ const parent = path.dirname(cacheDir);
16
+ await fs.mkdir(parent, { recursive: true });
17
+ const gitDir = path.join(cacheDir, '.git');
18
+ const timeoutMs = input.timeoutMs || 60_000;
19
+ if (!(await exists(gitDir))) {
20
+ if (await exists(cacheDir))
21
+ await fs.rm(cacheDir, { recursive: true, force: true });
22
+ const cloneArgs = [
23
+ 'clone',
24
+ '--depth',
25
+ '1',
26
+ '--filter=blob:none',
27
+ ...(sourceRef === 'HEAD' ? [] : ['--branch', sourceRef]),
28
+ sourceUrl,
29
+ cacheDir
30
+ ];
31
+ const cloned = await runProcess('git', cloneArgs, {
32
+ timeoutMs,
33
+ maxOutputBytes: 128 * 1024
34
+ }).catch((err) => ({ code: 1, stderr: messageOf(err), stdout: '' }));
35
+ if (cloned.code === 0)
36
+ refreshed = true;
37
+ else
38
+ blockers.push('source_snapshot_fetch_failed');
39
+ }
40
+ else if (input.refresh === true) {
41
+ const fetched = await runProcess('git', ['fetch', '--depth', '1', 'origin', sourceRef], {
42
+ cwd: cacheDir,
43
+ timeoutMs,
44
+ maxOutputBytes: 128 * 1024
45
+ }).catch((err) => ({ code: 1, stderr: messageOf(err), stdout: '' }));
46
+ if (fetched.code === 0) {
47
+ await runProcess('git', ['checkout', 'FETCH_HEAD'], { cwd: cacheDir, timeoutMs, maxOutputBytes: 64 * 1024 }).catch(() => null);
48
+ refreshed = true;
49
+ }
50
+ else {
51
+ blockers.push('source_snapshot_refresh_failed');
52
+ }
53
+ }
54
+ }
55
+ else {
56
+ warnings.push(sourceUrl ? 'reference_cache_offline_mode' : 'reference_source_url_missing_offline_cache_only');
57
+ }
58
+ const cacheExists = await hasTextFiles(cacheDir);
59
+ if (!cacheExists)
60
+ blockers.push('source_snapshot_missing');
61
+ const report = {
62
+ schema: 'sks.codex-native-reference-cache.v1',
63
+ generated_at: nowIso(),
64
+ ok: blockers.length === 0,
65
+ cache_dir: path.relative(root, cacheDir),
66
+ source_url_hash: sourceUrl ? sha256(sourceUrl) : null,
67
+ source_ref: sourceRef,
68
+ source_sha: await gitSha(cacheDir),
69
+ refreshed,
70
+ offline,
71
+ blockers: [...new Set(blockers)],
72
+ warnings: [...new Set(warnings)]
73
+ };
74
+ await writeJsonAtomic(reportPath, report).catch(() => undefined);
75
+ return report;
76
+ }
77
+ async function hasTextFiles(dir) {
78
+ const rows = await fs.readdir(dir, { withFileTypes: true }).catch(() => []);
79
+ for (const row of rows) {
80
+ if (row.name === '.git' || row.name === 'node_modules' || row.name === 'dist')
81
+ continue;
82
+ const full = path.join(dir, row.name);
83
+ if (row.isFile() && /\.(md|txt|json|toml|ya?ml|js|ts|mjs|cjs)$/i.test(row.name))
84
+ return true;
85
+ if (row.isDirectory() && await hasTextFiles(full))
86
+ return true;
87
+ }
88
+ return false;
89
+ }
90
+ async function gitSha(sourceDir) {
91
+ const run = await runProcess('git', ['rev-parse', 'HEAD'], { cwd: sourceDir, timeoutMs: 5000, maxOutputBytes: 4096 }).catch(() => null);
92
+ const sha = run?.code === 0 ? `${run.stdout || ''}`.trim() : '';
93
+ return /^[0-9a-f]{40}$/i.test(sha) ? sha : null;
94
+ }
95
+ function messageOf(err) {
96
+ return err instanceof Error ? err.message : String(err);
97
+ }
98
+ //# sourceMappingURL=codex-native-reference-cache.js.map
@@ -0,0 +1,2 @@
1
+ export { analyzeCodexNativeReferenceSource, extractCodexNativeEvidence, renderCodexNativeReferenceMarkdown } from './codex-native-reference-source.js';
2
+ //# sourceMappingURL=codex-native-reference-evidence.js.map
@@ -0,0 +1,149 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { createHash } from 'node:crypto';
4
+ import { nowIso, writeJsonAtomic, writeTextAtomic } from '../fsx.js';
5
+ import { ensureCodexNativeReferenceSnapshot } from './codex-native-reference-cache.js';
6
+ const PATTERNS = [
7
+ { id: 'no-global-optional-tooling', claim: 'optional tooling avoids mandatory global install', re: /\bnpx\b|no[- ]global|global install/i },
8
+ { id: 'plugin-lifecycle-state-separation', claim: 'plugin install state is separated from approval/readiness', re: /\bplugin\b.+\b(install|enable|marketplace|lifecycle)\b/i },
9
+ { id: 'hook-approval-gating', claim: 'hook approval is a separate counted evidence gate', re: /\bhook\b.+\b(approval|approve|review|trusted|trust)\b/i },
10
+ { id: 'skill-picker-route-bridge', claim: 'route skills are exposed through command picker style bridges', re: /\b(skill|command picker|slash command|\$[A-Za-z-]+)\b/i },
11
+ { id: 'native-agent-role-probe', claim: 'native agent role support is probed before use', re: /\bagent_type\b|agent role|spawn_agent/i },
12
+ { id: 'message-role-fallback', claim: 'message-role fallback is used when native role payload is unavailable', re: /message[- ]role|fallback/i },
13
+ { id: 'directory-local-memory', claim: 'directory-local memory is injected as context', re: /AGENTS\.md|directory[- ]local|project memory/i },
14
+ { id: 'plan-work-proof-separation', claim: 'planning, work, and proof are separated', re: /\b(plan|work|proof)\b/i },
15
+ { id: 'continuation-enforcer', claim: 'continuation is enforced through loop state', re: /continuation|resume|stop hook/i },
16
+ { id: 'doctor-harness-matrix', claim: 'doctor merges feature probes into one readiness matrix', re: /doctor|health|matrix|readiness/i },
17
+ { id: 'mcp-tool-candidate-inventory', claim: 'MCP tools are candidates until approved', re: /\bMCP\b|tool candidate|server candidate/i },
18
+ { id: 'non-clobber-managed-assets', claim: 'managed assets preserve user-authored content', re: /non[- ]clobber|managed|preserve user|checksum/i }
19
+ ];
20
+ export async function analyzeCodexNativeReferenceSource(input) {
21
+ const root = path.resolve(input.root);
22
+ const cacheInput = input.sourceRef ? { root, ref: input.sourceRef } : { root };
23
+ const cache = input.sourceDir
24
+ ? null
25
+ : await ensureCodexNativeReferenceSnapshot(cacheInput).catch((err) => ({
26
+ schema: 'sks.codex-native-reference-cache.v1',
27
+ generated_at: nowIso(),
28
+ ok: false,
29
+ cache_dir: '.sneakoscope/cache/codex-native-reference',
30
+ source_url_hash: null,
31
+ source_ref: input.sourceRef || 'HEAD',
32
+ source_sha: null,
33
+ refreshed: false,
34
+ offline: true,
35
+ blockers: [messageOf(err)],
36
+ warnings: ['reference_cache_exception']
37
+ }));
38
+ const sourceDir = input.sourceDir
39
+ ? path.resolve(input.sourceDir)
40
+ : path.join(root, cache?.cache_dir || '.sneakoscope/cache/codex-native-reference');
41
+ const confidence = input.sourceDir ? 'high' : cache?.ok ? cache.refreshed ? 'high' : 'medium' : 'low';
42
+ const files = await listTextFiles(sourceDir);
43
+ const evidence = [];
44
+ const blockers = [];
45
+ for (const file of files) {
46
+ const rel = path.relative(sourceDir, file).split(path.sep).join('/');
47
+ const text = await fs.readFile(file, 'utf8').catch(() => '');
48
+ if (text)
49
+ evidence.push(...extractCodexNativeEvidence(rel, text).map((row) => ({ ...row, confidence })));
50
+ }
51
+ if (!files.length || !evidence.length)
52
+ blockers.push('source_snapshot_missing');
53
+ const report = {
54
+ schema: 'sks.codex-native-reference-evidence.v1',
55
+ generated_at: nowIso(),
56
+ source_kind: 'external-reference-source',
57
+ source_ref: neutralSourceRef(input, cache, sourceDir),
58
+ source_sha: cache?.source_sha || await gitSha(sourceDir),
59
+ source_url_hash: cache?.source_url_hash || null,
60
+ cache_report_path: cache ? '.sneakoscope/reports/codex-native-reference-cache.json' : null,
61
+ cache,
62
+ evidence,
63
+ blockers: [...new Set([...blockers, ...(cache?.blockers || [])])],
64
+ warnings: [...new Set([...(cache?.warnings || []), ...(blockers.length ? ['reference_evidence_incomplete'] : [])])]
65
+ };
66
+ if (input.writeReport !== false) {
67
+ await writeJsonAtomic(path.join(root, '.sneakoscope', 'reports', 'codex-native-reference-evidence.json'), report).catch(() => undefined);
68
+ await writeTextAtomic(path.join(root, 'docs', 'codex-native-patterns.md'), renderCodexNativeReferenceMarkdown(report)).catch(() => undefined);
69
+ }
70
+ return report;
71
+ }
72
+ export function extractCodexNativeEvidence(file, text) {
73
+ const lines = text.split(/\r?\n/);
74
+ const rows = [];
75
+ for (const pattern of PATTERNS) {
76
+ const index = lines.findIndex((line) => pattern.re.test(line));
77
+ if (index < 0)
78
+ continue;
79
+ const snippet = lines.slice(Math.max(0, index - 1), Math.min(lines.length, index + 2)).join('\n').slice(0, 500);
80
+ rows.push({
81
+ pattern_id: pattern.id,
82
+ file,
83
+ line_range: [index + 1, index + 1],
84
+ snippet_hash: createHash('sha256').update(snippet).digest('hex'),
85
+ claim_id: pattern.claim,
86
+ confidence: 'high'
87
+ });
88
+ }
89
+ return rows;
90
+ }
91
+ export function renderCodexNativeReferenceMarkdown(report) {
92
+ const rows = report.evidence.map((row) => `| ${row.pattern_id} | ${row.file} | ${row.line_range?.join('-') || '-'} | ${row.snippet_hash.slice(0, 16)} | ${row.confidence} |`).join('\n');
93
+ return [
94
+ '# SKS Codex Native Patterns',
95
+ '',
96
+ 'This document records reference-derived Codex-native patterns in SKS-owned vocabulary. It stores line anchors and snippet hashes only.',
97
+ '',
98
+ `Generated at: \`${report.generated_at}\``,
99
+ `Source kind: \`${report.source_kind}\``,
100
+ `Source URL hash: \`${report.source_url_hash || 'none'}\``,
101
+ `Source SHA: \`${report.source_sha || 'none'}\``,
102
+ '',
103
+ '| Pattern | File | Lines | Snippet Hash | Confidence |',
104
+ '|---|---|---:|---|---|',
105
+ rows || '| none | - | - | - | low |',
106
+ ''
107
+ ].join('\n');
108
+ }
109
+ function neutralSourceRef(input, cache, sourceDir) {
110
+ if (cache)
111
+ return `cache:${createHash('sha256').update(`${cache.cache_dir}:${cache.source_ref}:${cache.source_sha || ''}`).digest('hex').slice(0, 16)}`;
112
+ if (input.sourceRef)
113
+ return `explicit:${createHash('sha256').update(input.sourceRef).digest('hex').slice(0, 16)}`;
114
+ return `explicit-source-dir:${createHash('sha256').update(sourceDir).digest('hex').slice(0, 16)}`;
115
+ }
116
+ async function listTextFiles(dir) {
117
+ const out = [];
118
+ await walk(dir, out, 500);
119
+ return out;
120
+ }
121
+ async function walk(dir, out, maxFiles) {
122
+ if (out.length >= maxFiles)
123
+ return;
124
+ const rows = await fs.readdir(dir, { withFileTypes: true }).catch(() => []);
125
+ for (const row of rows) {
126
+ if (out.length >= maxFiles)
127
+ return;
128
+ const full = path.join(dir, row.name);
129
+ if (row.isDirectory()) {
130
+ if (['.git', 'node_modules', 'dist'].includes(row.name))
131
+ continue;
132
+ await walk(full, out, maxFiles);
133
+ }
134
+ else if (row.isFile() && /\.(md|txt|json|toml|ya?ml|js|ts|mjs|cjs)$/i.test(row.name)) {
135
+ out.push(full);
136
+ }
137
+ }
138
+ }
139
+ async function gitSha(sourceDir) {
140
+ const head = await fs.readFile(path.join(sourceDir, '.git', 'HEAD'), 'utf8').catch(() => '');
141
+ const ref = head.match(/^ref:\s*(.+)$/m)?.[1];
142
+ if (ref)
143
+ return (await fs.readFile(path.join(sourceDir, '.git', ref), 'utf8').catch(() => '')).trim() || null;
144
+ return /^[0-9a-f]{40}$/i.test(head.trim()) ? head.trim() : null;
145
+ }
146
+ function messageOf(err) {
147
+ return err instanceof Error ? err.message : String(err);
148
+ }
149
+ //# sourceMappingURL=codex-native-reference-source.js.map
@@ -0,0 +1,25 @@
1
+ export const CODEX_NATIVE_RENAME_RULES = [
2
+ { from_hash: 'external-analysis', to: 'codex-native:pattern-analysis', surface: 'gate' },
3
+ { from_hash: 'external-evidence', to: 'codex-native:reference-evidence', surface: 'gate' },
4
+ { from_hash: 'external-interop', to: 'codex-native:interop-policy', surface: 'gate' },
5
+ { from_hash: 'app-harness', to: 'codex-native:harness-matrix', surface: 'gate' },
6
+ { from_hash: 'skill-sync', to: 'codex-native:skill-sync', surface: 'gate' },
7
+ { from_hash: 'agent-role-sync', to: 'codex-native:agent-role-sync', surface: 'gate' },
8
+ { from_hash: 'hook-lifecycle', to: 'codex-native:hook-lifecycle', surface: 'gate' },
9
+ { from_hash: 'execution-profile', to: 'codex-native:execution-profile', surface: 'gate' },
10
+ { from_hash: 'feature-broker', to: 'codex-native:feature-broker', surface: 'gate' },
11
+ { from_hash: 'invocation-router', to: 'codex-native:invocation-router', surface: 'gate' },
12
+ { from_hash: 'pattern-schema', to: 'sks.codex-native-pattern-analysis.v1', surface: 'schema' },
13
+ { from_hash: 'evidence-schema', to: 'sks.codex-native-reference-evidence.v1', surface: 'schema' },
14
+ { from_hash: 'matrix-schema', to: 'sks.codex-native-feature-matrix.v1', surface: 'schema' },
15
+ { from_hash: 'plan-schema', to: 'sks.codex-native-invocation-plan.v1', surface: 'schema' },
16
+ { from_hash: 'pattern-report', to: '.sneakoscope/reports/codex-native-pattern-analysis.json', surface: 'artifact' },
17
+ { from_hash: 'evidence-report', to: '.sneakoscope/reports/codex-native-reference-evidence.json', surface: 'artifact' },
18
+ { from_hash: 'matrix-report', to: '.sneakoscope/reports/codex-native-feature-matrix.json', surface: 'artifact' },
19
+ { from_hash: 'plan-report', to: '.sneakoscope/reports/codex-native-invocation-plan.json', surface: 'artifact' },
20
+ { from_hash: 'patterns-doc', to: 'docs/codex-native-patterns.md', surface: 'docs' }
21
+ ];
22
+ export function codexNativeRenameTargets() {
23
+ return CODEX_NATIVE_RENAME_RULES.map((rule) => rule.to);
24
+ }
25
+ //# sourceMappingURL=codex-native-rename-map.js.map