sneakoscope 4.4.0 → 4.6.1

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 (84) hide show
  1. package/README.md +28 -2
  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/bin/sks.js +1 -1
  6. package/dist/cli/command-registry.js +1 -0
  7. package/dist/core/agents/agent-runner-ollama.js +2 -0
  8. package/dist/core/agents/native-worker-backend-router.js +3 -0
  9. package/dist/core/bench.js +115 -0
  10. package/dist/core/code-structure.js +399 -11
  11. package/dist/core/codex-control/codex-fake-sdk-adapter.js +67 -9
  12. package/dist/core/codex-control/gpt-final-arbiter.js +4 -1
  13. package/dist/core/codex-control/gpt-final-review-schema.js +58 -0
  14. package/dist/core/codex-native/core-skill-manifest.js +23 -0
  15. package/dist/core/commands/bench-command.js +11 -2
  16. package/dist/core/commands/code-structure-command.js +34 -2
  17. package/dist/core/commands/run-command.js +92 -2
  18. package/dist/core/commands/seo-command.js +130 -0
  19. package/dist/core/db-safety.js +8 -6
  20. package/dist/core/feature-fixtures.js +6 -0
  21. package/dist/core/feature-registry.js +3 -1
  22. package/dist/core/fsx.js +1 -1
  23. package/dist/core/hooks-runtime.js +8 -0
  24. package/dist/core/init.js +8 -6
  25. package/dist/core/lean-engineering-policy.js +159 -0
  26. package/dist/core/mad-db/mad-db-policy-resolver.js +23 -2
  27. package/dist/core/pipeline-internals/runtime-core.js +15 -5
  28. package/dist/core/proof/auto-finalize.js +3 -2
  29. package/dist/core/proof/proof-schema.js +2 -1
  30. package/dist/core/proof/proof-writer.js +1 -0
  31. package/dist/core/proof/route-adapter.js +4 -2
  32. package/dist/core/proof/route-finalizer.js +35 -3
  33. package/dist/core/routes.js +75 -9
  34. package/dist/core/search-visibility/adapter-registry.js +26 -0
  35. package/dist/core/search-visibility/adapters/next-app.js +6 -0
  36. package/dist/core/search-visibility/adapters/next-pages.js +6 -0
  37. package/dist/core/search-visibility/adapters/static-site.js +6 -0
  38. package/dist/core/search-visibility/analyzers.js +377 -0
  39. package/dist/core/search-visibility/artifacts.js +183 -0
  40. package/dist/core/search-visibility/discovery.js +347 -0
  41. package/dist/core/search-visibility/index.js +199 -0
  42. package/dist/core/search-visibility/mission.js +67 -0
  43. package/dist/core/search-visibility/mutation.js +314 -0
  44. package/dist/core/search-visibility/types.js +2 -0
  45. package/dist/core/search-visibility/verifier.js +60 -0
  46. package/dist/core/version.js +1 -1
  47. package/dist/scripts/check-architecture.js +40 -7
  48. package/dist/scripts/check-command-module-budget.js +43 -5
  49. package/dist/scripts/check-pipeline-budget.js +17 -30
  50. package/dist/scripts/check-publish-tag.js +33 -6
  51. package/dist/scripts/check-route-modularity.js +25 -33
  52. package/dist/scripts/check-runtime-schemas.js +22 -0
  53. package/dist/scripts/config-managed-merge-callsite-coverage-check.js +2 -2
  54. package/dist/scripts/core-skill-immutable-sync-check.js +3 -2
  55. package/dist/scripts/core-skill-integrity-blackbox.js +3 -2
  56. package/dist/scripts/core-skill-manifest-check.js +7 -2
  57. package/dist/scripts/geo-claim-evidence-check.js +18 -0
  58. package/dist/scripts/geo-cli-blackbox-check.js +18 -0
  59. package/dist/scripts/geo-crawler-policy-check.js +16 -0
  60. package/dist/scripts/geo-llms-txt-optional-check.js +19 -0
  61. package/dist/scripts/gpt-final-arbiter-check.js +4 -1
  62. package/dist/scripts/mad-db-direct-apply-migration-hook-check.js +50 -0
  63. package/dist/scripts/release-dag-full-coverage-check.js +1 -0
  64. package/dist/scripts/release-parallel-check.js +15 -0
  65. package/dist/scripts/release-registry-check.js +33 -14
  66. package/dist/scripts/search-visibility-gate-lib.js +124 -0
  67. package/dist/scripts/seo-audit-fixture-check.js +16 -0
  68. package/dist/scripts/seo-canonical-locale-check.js +19 -0
  69. package/dist/scripts/seo-cli-blackbox-check.js +18 -0
  70. package/dist/scripts/seo-geo-feature-fixture-quality-check.js +18 -0
  71. package/dist/scripts/seo-geo-geo-disambiguation-check.js +12 -0
  72. package/dist/scripts/seo-geo-no-unsupported-ranking-claims-check.js +18 -0
  73. package/dist/scripts/seo-geo-route-identity-check.js +12 -0
  74. package/dist/scripts/seo-geo-skill-rich-content-check.js +22 -0
  75. package/dist/scripts/seo-mutation-rollback-check.js +23 -0
  76. package/dist/scripts/seo-no-mutation-by-default-check.js +17 -0
  77. package/dist/scripts/seo-structured-data-visible-content-check.js +19 -0
  78. package/dist/scripts/sks-3-1-5-directive-check-lib.js +10 -1
  79. package/package.json +21 -2
  80. package/schemas/search-visibility/finding-ledger.schema.json +36 -0
  81. package/schemas/search-visibility/gate.schema.json +22 -0
  82. package/schemas/search-visibility/mutation-plan.schema.json +27 -0
  83. package/schemas/search-visibility/site-inventory.schema.json +21 -0
  84. package/schemas/search-visibility/verification-report.schema.json +23 -0
@@ -1,3 +1,4 @@
1
+ import { LEAN_SOLUTION_RUNGS } from '../lean-engineering-policy.js';
1
2
  export const GPT_FINAL_ARBITER_RESULT_SCHEMA_ID = 'sks.gpt-final-arbiter-result.v1';
2
3
  export const GPT_FINAL_ARBITER_INPUT_SCHEMA = 'sks.gpt-final-arbiter-input.v1';
3
4
  const reviewItemSchema = {
@@ -20,6 +21,32 @@ const patchDecisionSchema = {
20
21
  },
21
22
  additionalProperties: false
22
23
  };
24
+ const leanReviewSchema = {
25
+ type: 'object',
26
+ required: [
27
+ 'status',
28
+ 'selected_rung',
29
+ 'unnecessary_files',
30
+ 'unnecessary_dependencies',
31
+ 'unnecessary_abstractions',
32
+ 'fallback_findings',
33
+ 'root_cause_review',
34
+ 'verification_minimum_present',
35
+ 'net_lines'
36
+ ],
37
+ properties: {
38
+ status: { enum: ['pass', 'modified', 'rejected', 'needs_more_work'] },
39
+ selected_rung: { enum: [...LEAN_SOLUTION_RUNGS, 'unknown'] },
40
+ unnecessary_files: { type: 'array', items: { type: 'string' } },
41
+ unnecessary_dependencies: { type: 'array', items: { type: 'string' } },
42
+ unnecessary_abstractions: { type: 'array', items: { type: 'string' } },
43
+ fallback_findings: { type: 'array', items: { type: 'string' } },
44
+ root_cause_review: { type: 'array', items: { type: 'string' } },
45
+ verification_minimum_present: { type: 'boolean' },
46
+ net_lines: { type: ['number', 'null'] }
47
+ },
48
+ additionalProperties: false
49
+ };
23
50
  export const gptFinalArbiterResultSchema = {
24
51
  type: 'object',
25
52
  required: [
@@ -34,6 +61,7 @@ export const gptFinalArbiterResultSchema = {
34
61
  'verification_plan',
35
62
  'rollback_notes',
36
63
  'blockers',
64
+ 'lean_review',
37
65
  'confidence'
38
66
  ],
39
67
  properties: {
@@ -48,6 +76,7 @@ export const gptFinalArbiterResultSchema = {
48
76
  verification_plan: { type: 'array', items: { type: 'string' } },
49
77
  rollback_notes: { type: 'array', items: { type: 'string' } },
50
78
  blockers: { type: 'array', items: { type: 'string' } },
79
+ lean_review: leanReviewSchema,
51
80
  confidence: { enum: ['low', 'medium', 'high'] }
52
81
  },
53
82
  additionalProperties: false
@@ -66,6 +95,7 @@ export function normalizeGptFinalArbiterResult(value) {
66
95
  verification_plan: stringArray(value?.verification_plan),
67
96
  rollback_notes: stringArray(value?.rollback_notes),
68
97
  blockers: stringArray(value?.blockers),
98
+ lean_review: normalizeLeanReview(value?.lean_review, status),
69
99
  confidence: normalizeConfidence(value?.confidence)
70
100
  };
71
101
  }
@@ -107,6 +137,34 @@ function patchDecisionItems(value) {
107
137
  function stringArray(value) {
108
138
  return Array.isArray(value) ? value.map((entry) => String(entry || '').trim()).filter(Boolean) : [];
109
139
  }
140
+ function normalizeLeanReview(value, arbiterStatus) {
141
+ const raw = typeof value === 'object' && value !== null ? value : {};
142
+ return {
143
+ status: normalizeLeanReviewStatus(raw.status, arbiterStatus),
144
+ selected_rung: normalizeLeanRung(raw.selected_rung),
145
+ unnecessary_files: stringArray(raw.unnecessary_files),
146
+ unnecessary_dependencies: stringArray(raw.unnecessary_dependencies),
147
+ unnecessary_abstractions: stringArray(raw.unnecessary_abstractions),
148
+ fallback_findings: stringArray(raw.fallback_findings),
149
+ root_cause_review: stringArray(raw.root_cause_review),
150
+ verification_minimum_present: typeof raw.verification_minimum_present === 'boolean' ? raw.verification_minimum_present : arbiterStatus === 'approved' || arbiterStatus === 'modified',
151
+ net_lines: Number.isFinite(Number(raw.net_lines)) ? Number(raw.net_lines) : null
152
+ };
153
+ }
154
+ function normalizeLeanReviewStatus(value, arbiterStatus) {
155
+ if (value === 'pass' || value === 'modified' || value === 'rejected' || value === 'needs_more_work')
156
+ return value;
157
+ if (arbiterStatus === 'approved')
158
+ return 'pass';
159
+ if (arbiterStatus === 'modified')
160
+ return 'modified';
161
+ if (arbiterStatus === 'rejected')
162
+ return 'rejected';
163
+ return 'needs_more_work';
164
+ }
165
+ function normalizeLeanRung(value) {
166
+ return typeof value === 'string' && [...LEAN_SOLUTION_RUNGS, 'unknown'].includes(value) ? value : 'unknown';
167
+ }
110
168
  function normalizeSeverity(value) {
111
169
  return value === 'low' || value === 'medium' || value === 'high' ? value : 'medium';
112
170
  }
@@ -1,4 +1,5 @@
1
1
  import { PACKAGE_VERSION, nowIso, sha256 } from '../fsx.js';
2
+ import { leanPolicyReference } from '../lean-engineering-policy.js';
2
3
  import { canonicalSkillName } from './skill-name-canonicalizer.js';
3
4
  export const CORE_SKILL_TEMPLATE_VERSION = 'sks-core-skill-template.v1';
4
5
  export const CORE_SKILL_MANAGED_BEGIN = '<!-- BEGIN SKS IMMUTABLE CORE SKILL -->';
@@ -83,6 +84,26 @@ const CORE_SKILL_DEFINITIONS = [
83
84
  when: 'Use when deeper local context or directory-specific recall is required.',
84
85
  evidence: '.sneakoscope/context/AGENTS.generated.md and managed memory artifacts.',
85
86
  fallback: 'Preserve user content and skip directories that cannot be safely updated.'
87
+ },
88
+ {
89
+ id: 'sks-core-search-visibility-core',
90
+ canonical_name: 'search-visibility-core',
91
+ display_name: 'search-visibility-core',
92
+ route: '$SEO-GEO-OPTIMIZER',
93
+ purpose: 'provide the shared search-visibility kernel for SEO and GEO audit, plan, explicit apply, verify, rollback, and Completion Proof without ranking, traffic, or citation guarantees.',
94
+ when: 'Use when $SEO-GEO-OPTIMIZER or sks seo-geo-optimizer needs typed mode-specific evidence, gates, artifacts, or safe mutation planning.',
95
+ evidence: 'search-visibility artifacts, seo-gate.json or geo-gate.json, mutation-plan.json, rollback-manifest.json, verification-report.json, and completion-proof.json.',
96
+ fallback: 'Keep unsupported frameworks plan-only, record unverified production/browser/Search Console/AI citation outcomes, and never invent guarantee evidence.'
97
+ },
98
+ {
99
+ id: 'sks-core-seo-geo-optimizer',
100
+ canonical_name: 'seo-geo-optimizer',
101
+ display_name: 'seo-geo-optimizer',
102
+ route: '$SEO-GEO-OPTIMIZER',
103
+ purpose: 'run the unified SEO/GEO optimizer route for Search Engine Optimization and Generative Engine Optimization, not geolocation or GeoIP, with no ranking, traffic, indexing, rich-result, answer inclusion, or AI citation guarantee.',
104
+ when: 'Use the CLI entrypoint: sks seo-geo-optimizer doctor|audit|plan|apply|verify|status|rollback|fixture --mode seo|geo for SEO and GEO visibility work.',
105
+ evidence: 'site inventory, route graph, seo-findings.json or geo-findings.json, claim-evidence-ledger.json, ai-crawler-policy.json, llms-txt-plan.json, verification report, route gate, and Completion Proof.',
106
+ fallback: 'Do not auto-allow training crawlers or fabricate AI answer visibility; mark missing live outcomes unverified and keep recovery on the unified optimizer route.'
86
107
  }
87
108
  ];
88
109
  export function coreSkillDefinitions() {
@@ -97,6 +118,7 @@ export function renderCoreSkillTemplate(name) {
97
118
  const skill = CORE_SKILL_DEFINITIONS.find((entry) => entry.canonical_name === canonical);
98
119
  if (!skill)
99
120
  throw new Error(`Unknown SKS core skill: ${name}`);
121
+ const lean = leanPolicyReference();
100
122
  return [
101
123
  '---',
102
124
  `name: ${skill.display_name}`,
@@ -117,6 +139,7 @@ export function renderCoreSkillTemplate(name) {
117
139
  `Command: ${skill.route}`,
118
140
  `Purpose: ${skill.purpose}`,
119
141
  `Use when: ${skill.when}`,
142
+ `Lean policy: ${lean.policy_id}/${lean.policy_hash}`,
120
143
  `Proof paths: ${skill.evidence}`,
121
144
  'Safety rules: preserve user-authored skills, keep route state bounded, and stop on hard blockers instead of fabricating fallback behavior.',
122
145
  `Failure recovery: ${skill.fallback}`,
@@ -1,4 +1,4 @@
1
- import { TRUST_VALIDATE_BENCH_COMMAND, benchRoot, runCoreBench } from '../bench.js';
1
+ import { TRUST_VALIDATE_BENCH_COMMAND, benchRoot, runCoreBench, runLeanPolicyBench } from '../bench.js';
2
2
  import { runProcess } from '../fsx.js';
3
3
  import { flag, readFlagValue } from './command-utils.js';
4
4
  export async function benchCommand(args = []) {
@@ -18,9 +18,18 @@ export async function benchCommand(args = []) {
18
18
  }
19
19
  if (action === 'route-fixtures')
20
20
  return commandBench('sks.route-fixture-bench.v1', ['all-features', 'selftest', '--mock', '--execute-fixtures', '--strict-artifacts', '--json'], args);
21
+ if (action === 'lean-policy') {
22
+ const report = await runLeanPolicyBench(root);
23
+ if (!report.ok)
24
+ process.exitCode = 1;
25
+ if (flag(args, '--json'))
26
+ return console.log(JSON.stringify(report, null, 2));
27
+ console.log(`lean-policy: ${report.ok ? 'pass' : 'blocked'}`);
28
+ return report;
29
+ }
21
30
  if (action === 'blackbox')
22
31
  return commandBench('sks.blackbox-bench.v1', ['blackbox-matrix-placeholder'], args);
23
- console.error('Usage: sks bench core|route-fixtures|blackbox|trust-kernel [--json] [--iterations N]');
32
+ console.error('Usage: sks bench core|route-fixtures|lean-policy|blackbox|trust-kernel [--json] [--iterations N]');
24
33
  process.exitCode = 2;
25
34
  }
26
35
  async function commandBench(schema, commandArgs, args = []) {
@@ -4,18 +4,50 @@ import { flag } from './command-utils.js';
4
4
  export async function codeStructureCommand(sub, args = []) {
5
5
  const action = sub || 'scan';
6
6
  if (action !== 'scan') {
7
- console.error('Usage: sks code-structure scan [--json]');
7
+ console.error('Usage: sks code-structure scan [--json] [--all] [--changed [ref|file[,file]]] [--changed-since <ref>]');
8
8
  process.exitCode = 1;
9
9
  return;
10
10
  }
11
11
  const root = await sksRoot();
12
- const report = await scanCodeStructure(root, { includeOk: flag(args, '--all') });
12
+ const changedArg = valueAfter(args, '--changed');
13
+ const changedSince = valueAfter(args, '--changed-since');
14
+ const changedArgLooksLikeFiles = Boolean(changedArg && (changedArg.includes(',')
15
+ || changedArg.includes('/')
16
+ || changedArg.includes('\\')
17
+ || /\.(js|ts|tsx|jsx|mjs|cjs|json|md|rs|toml)$/i.test(changedArg)));
18
+ const changedFiles = changedArg && changedArgLooksLikeFiles
19
+ ? changedArg.split(',').map((file) => file.trim()).filter(Boolean)
20
+ : [];
21
+ const report = await scanCodeStructure(root, {
22
+ includeOk: flag(args, '--all'),
23
+ changed: flag(args, '--changed') ? (changedFiles.length ? true : changedArg || true) : false,
24
+ changedSince,
25
+ changedFiles
26
+ });
13
27
  if (flag(args, '--json'))
14
28
  return console.log(JSON.stringify(report, null, 2));
15
29
  console.log('SKS Code Structure');
30
+ if (report.changed_scope?.mode !== 'full') {
31
+ console.log(`Changed scope: ${report.changed_scope.changed_files.length} files, ${report.changed_scope.net_lines} net lines`);
32
+ console.log(`Lean semantic review: ${report.semantic_review?.status || 'unknown'}`);
33
+ }
16
34
  for (const file of report.files.slice(0, 20))
17
35
  console.log(`${file.status} ${file.line_count} ${file.path}`);
18
36
  if (report.remaining_risks.length)
19
37
  console.log(`Risks: ${report.remaining_risks.join(', ')}`);
38
+ if (report.semantic_review?.findings?.length) {
39
+ for (const finding of report.semantic_review.findings.slice(0, 8)) {
40
+ console.log(`Lean ${finding.severity}: ${finding.file ? `${finding.file}: ` : ''}${finding.summary}`);
41
+ }
42
+ }
43
+ }
44
+ function valueAfter(args, name) {
45
+ const index = args.indexOf(name);
46
+ if (index === -1)
47
+ return null;
48
+ const value = args[index + 1];
49
+ if (!value || String(value).startsWith('--'))
50
+ return null;
51
+ return String(value);
20
52
  }
21
53
  //# sourceMappingURL=code-structure-command.js.map
@@ -4,6 +4,7 @@ import { createMission, missionDir, setCurrent } from '../mission.js';
4
4
  import { maybeFinalizeRoute } from '../proof/auto-finalize.js';
5
5
  import { routePrompt } from '../routes.js';
6
6
  import { latestTrustReport } from '../trust-kernel/trust-report.js';
7
+ import { normalizeTrustStatus, TRUST_REPORT_SCHEMA, trustKernelMetadata } from '../trust-kernel/trust-kernel-schema.js';
7
8
  import { flag, positionalArgs } from './command-utils.js';
8
9
  export async function runCommand(args = []) {
9
10
  const root = await projectRoot();
@@ -135,8 +136,11 @@ async function executeRunRoute(root, context) {
135
136
  blockers: execution.ok ? [] : execution.blockers,
136
137
  unverified: execution.unverified,
137
138
  command: { cmd: execution.command || `sks run "${prompt}" --execute`, status: execution.exit_code ?? (execution.ok ? 0 : 2) },
139
+ lightweightEvidence: execution.execution_kind === 'safe_deterministic',
138
140
  });
139
- const trust = await loadTrustReport(root, id);
141
+ const trust = execution.execution_kind === 'safe_deterministic'
142
+ ? await writeLightweightTrustReport(root, id, route.command, statusHint, proof.ok)
143
+ : await loadTrustReport(root, id);
140
144
  const autoVerification = auto ? await runAutoVerification(root, id) : null;
141
145
  const autoOk = autoVerification?.ok ?? true;
142
146
  await setCurrent(root, {
@@ -231,7 +235,7 @@ async function executeRouteCommand(root, route, prompt, { auto = false } = {}) {
231
235
  return routeExecutionResult(route, ['sks', ...commandArgs].join(' '), result, {
232
236
  okStatus: 'completed',
233
237
  trustStatus: 'verified_partial',
234
- executionKind: route.command === '$DB' || route.command === '$Wiki' || route.command === '$Fast-Mode' || route.command === '$with-local-llm-on' || route.command === '$Commit' || route.command === '$Commit-And-Push' ? 'safe_deterministic' : 'mock_safe',
238
+ executionKind: route.command === '$DB' || route.command === '$Wiki' || route.command === '$Fast-Mode' || route.command === '$with-local-llm-on' || route.command === '$Commit' || route.command === '$Commit-And-Push' || route.command === '$Ultra-Search' || route.command === '$SEO-GEO-OPTIMIZER' ? 'safe_deterministic' : 'mock_safe',
235
239
  });
236
240
  }
237
241
  async function runAutoVerification(root, missionId) {
@@ -342,6 +346,10 @@ function runNextAction(route, id, args) {
342
346
  function safeRouteExecutionArgs(route, prompt, { auto = false } = {}) {
343
347
  if (route.command === '$DB')
344
348
  return ['db', 'check', '--sql', 'SELECT 1', '--json'];
349
+ if (route.command === '$Ultra-Search')
350
+ return ultraSearchExecutionArgs(prompt);
351
+ if (route.command === '$SEO-GEO-OPTIMIZER')
352
+ return ['seo-geo-optimizer', searchVisibilityActionFromPrompt(prompt), '--mode', searchVisibilityModeFromPrompt(prompt), '--target', searchVisibilityTargetFromPrompt(prompt), '--offline', '--json'];
345
353
  if (route.command === '$Wiki')
346
354
  return ['wiki', 'refresh', '--json'];
347
355
  if (route.command === '$Fast-Mode')
@@ -354,6 +362,58 @@ function safeRouteExecutionArgs(route, prompt, { auto = false } = {}) {
354
362
  return ['commit-and-push', '--json'];
355
363
  return ['team', prompt, '--mock', '--json', ...(auto ? ['--no-open-zellij'] : [])];
356
364
  }
365
+ function ultraSearchExecutionArgs(prompt = '') {
366
+ const stripped = stripUltraSearchPrompt(prompt);
367
+ const lower = stripped.toLowerCase();
368
+ if (!stripped || /^(?:doctor|check|status)\b/.test(lower))
369
+ return ['ultra-search', 'doctor', '--json'];
370
+ if (/^(?:x|x-search|x_search)\b/.test(lower)) {
371
+ const query = stripped.replace(/^(?:x|x-search|x_search)\b[:\s-]*/i, '').trim() || 'source intelligence fixture';
372
+ return ['ultra-search', 'x', query, '--json'];
373
+ }
374
+ const url = stripped.match(/\bhttps?:\/\/\S+/)?.[0];
375
+ if (/^(?:fetch|url)\b/.test(lower) || url)
376
+ return ['ultra-search', 'fetch', url || stripped.replace(/^(?:fetch|url)\b[:\s-]*/i, '').trim() || 'https://example.com', '--json'];
377
+ const query = stripped.replace(/^run\b[:\s-]*/i, '').trim() || 'source intelligence fixture';
378
+ return ['ultra-search', 'run', query, '--mode', 'balanced', '--json'];
379
+ }
380
+ function stripUltraSearchPrompt(prompt = '') {
381
+ return String(prompt || '')
382
+ .trim()
383
+ .replace(/^\[\$Ultra-Search\]\([^)]+\)(?:\s|:)?\s*/i, '')
384
+ .replace(/^\[\$UltraSearch\]\([^)]+\)(?:\s|:)?\s*/i, '')
385
+ .replace(/^\$Ultra-Search(?:\s|:)?\s*/i, '')
386
+ .replace(/^\$UltraSearch(?:\s|:)?\s*/i, '')
387
+ .trim();
388
+ }
389
+ function searchVisibilityActionFromPrompt(prompt = '') {
390
+ const text = String(prompt || '').toLowerCase();
391
+ if (/\bdoctor\b|진단/.test(text))
392
+ return 'doctor';
393
+ if (/\bverify\b|검증/.test(text))
394
+ return 'fixture';
395
+ if (/\bplan\b|계획/.test(text))
396
+ return 'audit';
397
+ if (/\bapply\b|--apply\b|적용/.test(text))
398
+ return 'audit';
399
+ return 'audit';
400
+ }
401
+ function searchVisibilityTargetFromPrompt(prompt = '') {
402
+ const text = String(prompt || '').toLowerCase();
403
+ if (/\bpackage\b|npm|readme|github/.test(text))
404
+ return 'package';
405
+ if (/\bdocs?\b|documentation/.test(text))
406
+ return 'docs';
407
+ if (/\bwebsite\b|site\b|페이지|사이트/.test(text))
408
+ return 'website';
409
+ return 'auto';
410
+ }
411
+ function searchVisibilityModeFromPrompt(prompt = '') {
412
+ const text = String(prompt || '');
413
+ if (/generative\s+engine\s+optimization|AI\s+(?:answer|search)\s+(?:visibility|discoverability)|LLM\s+(?:citation|answer|visibility|discoverability)|answerability|entity\s+(?:facts?|clarity)|claim\s+evidence|crawler\s+policy|OAI-SearchBot|GPTBot|ChatGPT-User|Claude-SearchBot|ClaudeBot|Claude-User|llms\.txt|AI\s*검색\s*가시성|AI\s*답변\s*가시성|생성형\s*엔진\s*최적화/i.test(text))
414
+ return 'geo';
415
+ return 'seo';
416
+ }
357
417
  function fastModeActionFromPrompt(prompt = '') {
358
418
  const text = String(prompt || '');
359
419
  const lower = text.toLowerCase();
@@ -468,4 +528,34 @@ async function loadTrustReport(root, missionId) {
468
528
  return report;
469
529
  return { status: 'not_verified', ok: false, issues: ['trust_report_invalid'] };
470
530
  }
531
+ async function writeLightweightTrustReport(root, missionId, route, statusHint, proofOk) {
532
+ const status = normalizeTrustStatus(statusHint);
533
+ const issues = proofOk ? [] : ['completion_proof_not_ok'];
534
+ const report = {
535
+ schema: TRUST_REPORT_SCHEMA,
536
+ ...trustKernelMetadata(),
537
+ ok: proofOk && !['blocked', 'failed', 'not_verified'].includes(status),
538
+ mission_id: missionId,
539
+ route,
540
+ status,
541
+ proof_status: status,
542
+ evidence_status: 'verified_partial',
543
+ route_contract_status: 'verified_partial',
544
+ issues,
545
+ route_state_machine: {
546
+ state: 'trust_report',
547
+ lightweight: true,
548
+ reason: 'safe_deterministic_run_wrapper'
549
+ },
550
+ evidence: {
551
+ completion_proof: `.sneakoscope/missions/${missionId}/completion-proof.json`,
552
+ route_contract: null,
553
+ evidence_index: null,
554
+ evidence_records: 0,
555
+ lightweight: true
556
+ }
557
+ };
558
+ await writeJsonAtomic(path.join(missionDir(root, missionId), 'trust-report.json'), report);
559
+ return report;
560
+ }
471
561
  //# sourceMappingURL=run-command.js.map
@@ -0,0 +1,130 @@
1
+ import path from 'node:path';
2
+ import { printJson } from '../../cli/output.js';
3
+ import { projectRoot } from '../fsx.js';
4
+ import { flag, readOption } from './command-utils.js';
5
+ import { runSearchVisibilityApply, runSearchVisibilityAudit, runSearchVisibilityDoctor, runSearchVisibilityFixture, runSearchVisibilityPlan, runSearchVisibilityRollback, runSearchVisibilityStatus, runSearchVisibilityVerify, } from '../search-visibility/index.js';
6
+ export async function seoCommand(args = []) {
7
+ return runSearchVisibilityCommand('seo', args, 'seo');
8
+ }
9
+ export async function seoGeoOptimizerCommand(args = []) {
10
+ const normalized = normalizeOptimizerArgs(args);
11
+ return runSearchVisibilityCommand(normalized.mode, normalized.args, 'seo-geo-optimizer');
12
+ }
13
+ export async function runSearchVisibilityCommand(mode, args = [], displayCommand = mode) {
14
+ const action = args[0] || 'doctor';
15
+ const rest = args.slice(1);
16
+ const options = await parseOptions(rest);
17
+ let result;
18
+ if (action === 'doctor')
19
+ result = await runSearchVisibilityDoctor(mode, options);
20
+ else if (action === 'audit')
21
+ result = await runSearchVisibilityAudit(mode, options);
22
+ else if (action === 'plan')
23
+ result = await runSearchVisibilityPlan(mode, firstPositional(rest) || 'latest', options);
24
+ else if (action === 'apply')
25
+ result = await runSearchVisibilityApply(mode, firstPositional(rest) || 'latest', options);
26
+ else if (action === 'verify')
27
+ result = await runSearchVisibilityVerify(mode, firstPositional(rest) || 'latest', options);
28
+ else if (action === 'status')
29
+ result = await runSearchVisibilityStatus(mode, firstPositional(rest) || 'latest', options);
30
+ else if (action === 'rollback')
31
+ result = await runSearchVisibilityRollback(mode, firstPositional(rest) || 'latest', options);
32
+ else if (action === 'fixture')
33
+ result = await runSearchVisibilityFixture(mode, options);
34
+ else
35
+ return usage(mode, 2, displayCommand);
36
+ if (isBlocked(result))
37
+ process.exitCode = 1;
38
+ if (options.json) {
39
+ printJson(result);
40
+ return result;
41
+ }
42
+ printHuman(mode, action, result);
43
+ return result;
44
+ }
45
+ function normalizeOptimizerArgs(args) {
46
+ const out = [...args];
47
+ const first = String(out[0] || '').toLowerCase();
48
+ if (first === 'seo' || first === 'geo')
49
+ return { mode: first, args: out.slice(1) };
50
+ const modeIndex = out.findIndex((item) => item === '--mode');
51
+ if (modeIndex >= 0) {
52
+ const value = String(out[modeIndex + 1] || '').toLowerCase();
53
+ out.splice(modeIndex, value ? 2 : 1);
54
+ return { mode: value === 'geo' ? 'geo' : 'seo', args: out };
55
+ }
56
+ if (out.includes('--include-llms-txt') || out.includes('--observe-queries') || out.includes('--query-file'))
57
+ return { mode: 'geo', args: out };
58
+ return { mode: 'seo', args: out };
59
+ }
60
+ async function parseOptions(args) {
61
+ const root = path.resolve(readOption(args, '--root', await projectRoot()));
62
+ return {
63
+ root,
64
+ url: readOption(args, '--url', null),
65
+ target: targetOption(readOption(args, '--target', 'auto')),
66
+ framework: frameworkOption(readOption(args, '--framework', 'auto')),
67
+ offline: flag(args, '--offline'),
68
+ strict: flag(args, '--strict'),
69
+ json: flag(args, '--json'),
70
+ apply: flag(args, '--apply'),
71
+ yes: flag(args, '--yes'),
72
+ allowDirtyTouched: flag(args, '--allow-dirty-touched'),
73
+ browser: flag(args, '--browser'),
74
+ includeLlmsTxt: flag(args, '--include-llms-txt'),
75
+ observeQueries: flag(args, '--observe-queries'),
76
+ queryFile: readOption(args, '--query-file', null),
77
+ scope: String(readOption(args, '--scope', '') || '').split(',').map((item) => item.trim()).filter(Boolean),
78
+ };
79
+ }
80
+ function targetOption(value) {
81
+ return ['auto', 'website', 'docs', 'package'].includes(value) ? value : 'auto';
82
+ }
83
+ function frameworkOption(value) {
84
+ return ['auto', 'next-app', 'next-pages', 'static', 'package', 'unsupported'].includes(value) ? value : 'auto';
85
+ }
86
+ function firstPositional(args) {
87
+ const valueFlags = new Set(['--root', '--url', '--target', '--framework', '--scope', '--query-file']);
88
+ for (let i = 0; i < args.length; i += 1) {
89
+ const value = args[i] || '';
90
+ if (valueFlags.has(value)) {
91
+ i += 1;
92
+ continue;
93
+ }
94
+ if (!value.startsWith('--'))
95
+ return value;
96
+ }
97
+ return null;
98
+ }
99
+ function usage(mode, exitCode, displayCommand = mode) {
100
+ if (displayCommand === 'seo-geo-optimizer') {
101
+ console.error('Usage: sks seo-geo-optimizer [seo|geo] doctor|audit|plan|apply|verify|status|rollback|fixture [mission|latest] [--mode seo|geo] [--root <path>] [--url <origin>] [--target auto|website|docs|package] [--framework auto|next-app|next-pages|static] [--offline] [--strict] [--json]');
102
+ console.error(' sks seo-geo-optimizer apply <mission|latest> --mode seo|geo --apply [--include-llms-txt] [--scope <rule-or-path,...>] [--yes] [--json]');
103
+ console.error(' sks seo-geo-optimizer rollback <mission|latest> --mode seo|geo --apply [--yes] [--json]');
104
+ process.exitCode = exitCode;
105
+ return { schema: 'sks.search-visibility.usage.v1', ok: false, status: 'blocked', mode, command: displayCommand, reason: 'invalid_subcommand' };
106
+ }
107
+ const applyFlag = mode === 'geo' ? ' [--include-llms-txt]' : '';
108
+ console.error(`Usage: sks ${mode} doctor|audit|plan|apply|verify|status|rollback|fixture [mission|latest] [--root <path>] [--url <origin>] [--target auto|website|docs|package] [--framework auto|next-app|next-pages|static] [--offline] [--strict] [--json]`);
109
+ console.error(` sks ${mode} apply <mission|latest> --apply${applyFlag} [--scope <rule-or-path,...>] [--yes] [--json]`);
110
+ console.error(` sks ${mode} rollback <mission|latest> --apply [--yes] [--json]`);
111
+ process.exitCode = exitCode;
112
+ return { schema: 'sks.search-visibility.usage.v1', ok: false, status: 'blocked', mode, reason: 'invalid_subcommand' };
113
+ }
114
+ function isBlocked(value) {
115
+ if (!value || typeof value !== 'object')
116
+ return false;
117
+ const rec = value;
118
+ return rec.ok === false || rec.status === 'blocked';
119
+ }
120
+ function printHuman(mode, action, value) {
121
+ const rec = value && typeof value === 'object' ? value : {};
122
+ console.log(`SKS ${mode.toUpperCase()} ${action}: ${rec.status || (rec.ok === false ? 'blocked' : 'ok')}`);
123
+ if (rec.mission_id)
124
+ console.log(`Mission: ${rec.mission_id}`);
125
+ if (rec.artifacts_dir)
126
+ console.log(`Artifacts: ${rec.artifacts_dir}`);
127
+ if (Array.isArray(rec.blockers) && rec.blockers.length)
128
+ console.log(`Blockers: ${rec.blockers.join(', ')}`);
129
+ }
130
+ //# sourceMappingURL=seo-command.js.map
@@ -510,15 +510,16 @@ export async function checkDbOperation(root, state, payload, { duringNoQuestion
510
510
  const contract = await loadMissionContract(root, state);
511
511
  const classification = classifyToolPayload(payload);
512
512
  const madDb = await resolveMadDbMutationPolicy(root, state, classification);
513
- if (madDb.allowed === true && state?.mission_id) {
513
+ if (madDb.allowed === true && madDb.mission_id) {
514
514
  const madDbDecision = madDb;
515
+ const madDbMissionId = String(madDbDecision.mission_id);
515
516
  const sqlText = classification.sql?.statements?.length ? String(classification.sql.statements.join('\n')) : null;
516
517
  const sqlHash = sqlText ? sha256(sqlText) : null;
517
518
  const toolCallId = extractCanonicalToolCallId(payload) || `payload-${sha256(JSON.stringify({ tool: classification.toolName || '', sqlHash, level: classification.level })).slice(0, 16)}`;
518
519
  const operationClasses = madDbDecision.operation_classes?.length ? madDbDecision.operation_classes : madDbOperationClassesFromClassification(classification);
519
520
  const reservation = await reserveMadDbOperation({
520
521
  root,
521
- missionId: String(state.mission_id),
522
+ missionId: madDbMissionId,
522
523
  capability: madDbDecision.capability,
523
524
  toolCallId,
524
525
  toolName: classification.toolName || 'unknown_database_tool',
@@ -527,12 +528,12 @@ export async function checkDbOperation(root, state, payload, { duringNoQuestion
527
528
  });
528
529
  await transitionMadDbOperation({
529
530
  root,
530
- missionId: String(state.mission_id),
531
+ missionId: madDbMissionId,
531
532
  toolCallId,
532
533
  state: 'started'
533
534
  });
534
535
  const lifecycleHook = {
535
- mission_id: String(state.mission_id),
536
+ mission_id: madDbMissionId,
536
537
  operation_id: reservation.operation.operation_id,
537
538
  tool_call_id: toolCallId,
538
539
  cycle_id: madDbDecision.cycle_id || null,
@@ -560,12 +561,13 @@ export async function checkDbOperation(root, state, payload, { duringNoQuestion
560
561
  lifecycle_result_pending: true,
561
562
  ledger_result_hook: lifecycleHook,
562
563
  operation_classes: operationClasses,
564
+ state_source: madDbDecision.state_source || 'hook_state',
563
565
  counters: reservation.capability.counters,
564
566
  idempotent_reservation_reused: reservation.reused
565
567
  }
566
568
  };
567
- await appendMadDbLedgerEvent(root, state.mission_id, { type: 'db_mutation.allowed', cycle_id: madDbDecision.cycle_id, mode: madDbDecision.mode, classification, operation_id: reservation.operation.operation_id, tool_call_id: toolCallId });
568
- await appendJsonlBounded(path.join(missionDir(root, state.mission_id), 'db-safety.jsonl'), { ts: nowIso(), decision });
569
+ await appendMadDbLedgerEvent(root, madDbMissionId, { type: 'db_mutation.allowed', cycle_id: madDbDecision.cycle_id, mode: madDbDecision.mode, classification, operation_id: reservation.operation.operation_id, tool_call_id: toolCallId });
570
+ await appendJsonlBounded(path.join(missionDir(root, madDbMissionId), 'db-safety.jsonl'), { ts: nowIso(), decision });
569
571
  return decision;
570
572
  }
571
573
  const madSks = await madSksOverrideState(root, state);
@@ -36,6 +36,7 @@ const FIXTURES = Object.freeze({
36
36
  'cli-selftest': fixture('execute', 'sks selftest --mock', [], 'pass'),
37
37
  'cli-git': fixture('execute', 'sks git policy --json', [], 'pass'),
38
38
  'cli-goal': fixture('mock', 'sks goal status latest --json', ['goal-workflow.json'], 'pass'),
39
+ 'cli-seo-geo-optimizer': fixture('execute_and_validate_artifacts', 'sks seo-geo-optimizer fixture --mode seo --json', ['search-visibility/site-inventory.json', 'search-visibility/seo-findings.json', 'search-visibility/verification-report.json', 'seo-gate.json', 'completion-proof.json'], 'pass'),
39
40
  'cli-research': fixture('mock', 'sks research status latest --json', ['research-gate.json', 'completion-proof.json'], 'pass'),
40
41
  'cli-qa-loop': fixture('mock', 'sks qa-loop status latest --json', ['qa-loop-proof.json', 'completion-proof.json'], 'pass'),
41
42
  'cli-ppt': fixture('mock', 'sks ppt fixture --mock --json', ['ppt-imagegen-review-gate.json', 'completion-proof.json'], 'pass'),
@@ -75,6 +76,7 @@ const FIXTURES = Object.freeze({
75
76
  'cli-commit': fixture('mock', 'sks commit --dry-run', [], 'pass'),
76
77
  'cli-commit-and-push': fixture('mock', 'sks commit-and-push --dry-run', [], 'pass'),
77
78
  'cli-context7': fixture('real_optional', 'sks context7 check --json', [], 'pass'),
79
+ 'cli-ultra-search': fixture('execute', 'sks ultra-search doctor --json', [], 'pass'),
78
80
  'cli-xai': fixture('real_optional', 'sks xai check --json', [], 'pass'),
79
81
  'cli-all-features': fixture('mock', 'sks all-features complete --json', [`.sneakoscope/reports/all-feature-completion-${PACKAGE_VERSION}.json`], 'pass'),
80
82
  'cli-init': fixture('mock', 'sks init --local-only --dry-run', [], 'pass'),
@@ -88,6 +90,7 @@ const FIXTURES = Object.freeze({
88
90
  'skill-imagegen': fixture('mock', 'sks image-ux-review fixture --mock --json', ['image-ux-generated-review-ledger.json', 'image-voxel-ledger.json'], 'pass'),
89
91
  'skill-gx-visual-validate': fixture('mock', 'sks gx validate fixture --mock --json', ['gx-validation.json'], 'pass'),
90
92
  'skill-context7-docs': fixture('real_optional', 'sks context7 check --json', [], 'pass'),
93
+ 'skill-seo-geo-optimizer': fixture('mock', 'sks seo-geo-optimizer fixture --mode geo --json', ['search-visibility/site-inventory.json', 'search-visibility/geo-findings.json', 'geo-gate.json', 'completion-proof.json'], 'pass'),
91
94
  'cli-proof': fixture('execute_and_validate_artifacts', 'sks proof smoke --json', ['.sneakoscope/proof/latest.json'], 'pass'),
92
95
  'cli-trust': fixture('execute_and_validate_artifacts', 'sks trust report latest --json', ['trust-report.json'], 'pass'),
93
96
  'cli-wrongness': fixture('execute_and_validate_artifacts', 'sks wrongness add --kind missing_evidence --claim "fixture wrongness" --json', ['.sneakoscope/wiki/wrongness-ledger.json'], 'pass'),
@@ -104,6 +107,9 @@ const FIXTURES = Object.freeze({
104
107
  'route-dfix': fixture('execute_and_validate_artifacts', 'sks dfix fixture --json', ['completion-proof.json', 'dfix-gate.json', 'dfix-verification.json'], 'pass'),
105
108
  'route-answer': fixture('mock', '$Answer answer-only route policy', [], 'pass'),
106
109
  'route-goal': fixture('mock', '$Goal bridge route', ['goal-workflow.json', 'completion-proof.json'], 'pass'),
110
+ 'route-ultra-search': fixture('execute', 'sks run "$Ultra-Search source intelligence fixture" --execute --json', [], 'pass'),
111
+ 'route-ultrasearch': fixture('execute', 'sks run "$UltraSearch source intelligence fixture" --execute --json', [], 'pass'),
112
+ 'route-seo-geo-optimizer': fixture('execute_and_validate_artifacts', 'sks seo-geo-optimizer fixture --mode geo --json', ['search-visibility/site-inventory.json', 'search-visibility/geo-findings.json', 'search-visibility/verification-report.json', 'geo-gate.json', 'completion-proof.json'], 'pass'),
107
113
  'route-autoresearch': fixture('mock', '$AutoResearch fixture route', ['research-gate.json', 'completion-proof.json'], 'pass'),
108
114
  'route-mad-sks': fixture('mock', '$MAD-SKS permission gate route', ['mad-sks-gate.json', 'completion-proof.json'], 'pass'),
109
115
  'route-from-chat-img': fixture('mock', '$From-Chat-IMG visual work order route', ['from-chat-img-work-order.md', 'image-voxel-ledger.json', 'completion-proof.json'], 'pass'),
@@ -461,6 +461,7 @@ const SAFE_EXECUTABLE_FIXTURE_ARGS = Object.freeze({
461
461
  'cli-fix-path': ['fix-path', '--json'],
462
462
  'cli-selftest': ['selftest', '--mock'],
463
463
  'cli-git': ['git', 'policy', '--json'],
464
+ 'cli-seo-geo-optimizer': ['seo-geo-optimizer', 'fixture', '--mode', 'seo', '--json'],
464
465
  'cli-paths': ['paths', 'managed', '--json'],
465
466
  'cli-rollback': ['rollback', 'list', '--json'],
466
467
  'cli-proof-field': ['proof-field', 'scan', '--json', '--intent', 'fixture'],
@@ -493,6 +494,7 @@ const SAFE_EXECUTABLE_FIXTURE_ARGS = Object.freeze({
493
494
  'route-image-ux-review': ['image-ux-review', 'fixture', '--mock', '--json'],
494
495
  'route-computer-use': ['computer-use', 'import-fixture', '--mock', '--json'],
495
496
  'route-dfix': ['dfix', 'fixture', '--json'],
497
+ 'route-seo-geo-optimizer': ['seo-geo-optimizer', 'fixture', '--mode', 'geo', '--json'],
496
498
  'route-fast-mode': ['fast-mode', 'status', '--json'],
497
499
  'route-db': ['db', 'check', '--sql', 'SELECT 1', '--json'],
498
500
  'route-wiki': ['wiki', 'image-ingest', 'test/fixtures/images/one-by-one.png', '--json'],
@@ -826,7 +828,7 @@ function commandCategory(name) {
826
828
  function commandMaturity(name) {
827
829
  if (['help', 'version', 'commands', 'usage', 'root', 'quickstart', 'setup', 'doctor', 'selftest', 'update-check', 'fast-mode'].includes(name))
828
830
  return 'stable';
829
- if (['codex', 'codex-app', 'codex-native', 'codex-lb', 'hooks', 'features', 'all-features', 'wiki', 'wrongness', 'team', 'pipeline', 'goal', 'db', 'guard', 'computer-use', 'mad-sks'].includes(name))
831
+ if (['codex', 'codex-app', 'codex-native', 'codex-lb', 'hooks', 'features', 'all-features', 'wiki', 'wrongness', 'team', 'pipeline', 'goal', 'db', 'guard', 'computer-use', 'mad-sks', 'seo-geo-optimizer'].includes(name))
830
832
  return 'beta';
831
833
  return 'labs';
832
834
  }
package/dist/core/fsx.js 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
  import { fileURLToPath } from 'node:url';
8
- export const PACKAGE_VERSION = '4.4.0';
8
+ export const PACKAGE_VERSION = '4.6.1';
9
9
  export const DEFAULT_PROCESS_TAIL_BYTES = 256 * 1024;
10
10
  export const DEFAULT_PROCESS_TIMEOUT_MS = 30 * 60 * 1000;
11
11
  export function nowIso() {
@@ -12,6 +12,7 @@ import { localizedFinalizationReason } from './language-preference.js';
12
12
  import { classifyToolError } from './evaluation.js';
13
13
  import { REQUIRED_CODEX_MODEL, isForbiddenCodexModel } from './codex-model-guard.js';
14
14
  import { dollarCommand, routeRequiresSubagents, stripVisibleDecisionAnswerBlocks } from './routes.js';
15
+ import { leanEngineeringCompactText } from './lean-engineering-policy.js';
15
16
  import { appendMissionStatus } from './recallpulse.js';
16
17
  import { scanAgentTextForRecursion } from './agents/agent-recursion-guard.js';
17
18
  import { evaluateLoopContinuation } from './loops/loop-continuation-enforcer.js';
@@ -194,8 +195,15 @@ export async function evaluateHookPayload(name, payload = {}, opts = {}) {
194
195
  return hookPermission(root, state, payload, noQuestion);
195
196
  if (name === 'stop')
196
197
  return hookStop(root, state, payload, noQuestion);
198
+ if (name === 'subagent-start')
199
+ return hookSubagentStart(root, state);
197
200
  return { continue: true };
198
201
  }
202
+ async function hookSubagentStart(root, state) {
203
+ const active = await activeRouteContext(root, state).catch(() => '');
204
+ const additionalContext = [leanEngineeringCompactText(), active].filter(Boolean).join('\n\n');
205
+ return { continue: true, additionalContext };
206
+ }
199
207
  function blockForbiddenClientModel(payload = {}) {
200
208
  const model = forbiddenClientModelFromPayload(payload);
201
209
  if (!model || !isForbiddenCodexModel(model))