sneakoscope 3.1.4 → 3.1.5
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 +1 -1
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/crates/sks-core/src/main.rs +1 -1
- package/dist/.sks-build-stamp.json +4 -4
- package/dist/bin/sks.js +1 -1
- package/dist/cli/install-helpers.js +56 -4
- package/dist/commands/codex-lb.js +12 -9
- package/dist/core/codex-app/codex-agent-role-sync.js +63 -11
- package/dist/core/codex-app/codex-agent-type-probe.js +202 -0
- package/dist/core/codex-app/codex-app-execution-profile.js +15 -8
- package/dist/core/codex-app/codex-app-fast-ui-repair.js +7 -4
- package/dist/core/codex-app/codex-app-harness-matrix.js +78 -26
- package/dist/core/codex-app/codex-app-types.js +21 -0
- package/dist/core/codex-app/codex-hook-approval-probe.js +188 -0
- package/dist/core/codex-app/codex-hook-lifecycle.js +31 -10
- package/dist/core/codex-app/codex-init-deep.js +97 -4
- package/dist/core/codex-app/codex-skill-sync.js +75 -3
- package/dist/core/codex-app/lazycodex-analysis.js +31 -10
- package/dist/core/codex-app/lazycodex-interop-policy.js +13 -2
- package/dist/core/codex-app/lazycodex-live-analyzer.js +98 -0
- package/dist/core/commands/mad-sks-command.js +37 -2
- package/dist/core/commands/qa-loop-command.js +3 -2
- package/dist/core/commands/research-command.js +2 -2
- package/dist/core/doctor/doctor-zellij-repair.js +5 -1
- package/dist/core/feature-fixtures.js +1 -0
- package/dist/core/feature-registry.js +4 -1
- package/dist/core/fsx.js +1 -1
- package/dist/core/init.js +4 -1
- package/dist/core/loops/loop-continuation-enforcer.js +11 -3
- package/dist/core/loops/loop-planner.js +21 -4
- package/dist/core/loops/loop-worker-runtime.js +23 -7
- package/dist/core/naruto/naruto-loop-worker-router.js +11 -2
- package/dist/core/qa-loop.js +39 -4
- package/dist/core/research/research-cycle-runner.js +1 -0
- package/dist/core/research/research-stage-runner.js +9 -2
- package/dist/core/research.js +35 -1
- package/dist/core/version.js +1 -1
- package/dist/core/zellij/homebrew-policy.js +0 -1
- package/dist/core/zellij/zellij-self-heal-types.js +45 -0
- package/dist/core/zellij/zellij-self-heal.js +69 -8
- package/dist/core/zellij/zellij-update.js +9 -1
- package/dist/scripts/sks-3-1-5-directive-check-lib.js +347 -0
- package/package.json +26 -2
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
1
|
import fs from 'node:fs/promises';
|
|
3
2
|
import path from 'node:path';
|
|
4
3
|
import os from 'node:os';
|
|
@@ -72,20 +71,93 @@ function skillName(value) {
|
|
|
72
71
|
return value.replace(/^\$/, '').toLowerCase();
|
|
73
72
|
}
|
|
74
73
|
function skillContent(name) {
|
|
74
|
+
const profile = skillProfile(name);
|
|
75
75
|
const body = [
|
|
76
76
|
'---',
|
|
77
77
|
`name: ${name}`,
|
|
78
|
-
`description: SKS managed Codex App route bridge for
|
|
78
|
+
`description: SKS managed Codex App route bridge for ${profile.command}.`,
|
|
79
79
|
'---',
|
|
80
80
|
'',
|
|
81
81
|
'<!-- BEGIN SKS MANAGED SKILL -->',
|
|
82
|
-
`
|
|
82
|
+
`Command: ${profile.command}`,
|
|
83
|
+
`Purpose: ${profile.purpose}`,
|
|
84
|
+
`Use when: ${profile.when}`,
|
|
85
|
+
`Evidence: ${profile.evidence}`,
|
|
86
|
+
'Safety: keep route state bounded, preserve user and LazyCodex/OmO skills, and stop on hard blockers instead of fabricating fallback behavior.',
|
|
87
|
+
`Fallback: ${profile.fallback}`,
|
|
83
88
|
`checksum: ${hash(name)}`,
|
|
84
89
|
'<!-- END SKS MANAGED SKILL -->',
|
|
85
90
|
''
|
|
86
91
|
].join('\n');
|
|
87
92
|
return body;
|
|
88
93
|
}
|
|
94
|
+
function skillProfile(name) {
|
|
95
|
+
const table = {
|
|
96
|
+
loop: {
|
|
97
|
+
command: '$Loop',
|
|
98
|
+
purpose: 'compile persisted route work into bounded loop plans with continuation evidence.',
|
|
99
|
+
when: 'a mission needs stage-by-stage execution, memory hints, or resume-safe artifacts.',
|
|
100
|
+
evidence: '.sneakoscope/loops/** plus codex-app-execution-profile.json',
|
|
101
|
+
fallback: 'use message-role routing when native agent_type is not verified.'
|
|
102
|
+
},
|
|
103
|
+
naruto: {
|
|
104
|
+
command: '$Naruto',
|
|
105
|
+
purpose: 'fan out bounded native worker lanes for high-scale review or implementation.',
|
|
106
|
+
when: 'parallel lanes are explicitly selected by the route and parent integration remains owner.',
|
|
107
|
+
evidence: 'naruto work graph, worker ledgers, and execution profile payloads.',
|
|
108
|
+
fallback: 'degrade to message-role workers without dropping proof artifacts.'
|
|
109
|
+
},
|
|
110
|
+
'qa-loop': {
|
|
111
|
+
command: '$QA-LOOP',
|
|
112
|
+
purpose: 'dogfood UI/API behavior with gate artifacts and current execution profile.',
|
|
113
|
+
when: 'route completion needs human-proxy verification or app handoff checks.',
|
|
114
|
+
evidence: 'qa-loop gate/result ledgers and codex-app-execution-profile.json.',
|
|
115
|
+
fallback: 'record the unavailable surface as blocked rather than inventing visual proof.'
|
|
116
|
+
},
|
|
117
|
+
research: {
|
|
118
|
+
command: '$Research',
|
|
119
|
+
purpose: 'run evidence-bound research cycles with source routing and synthesis ledgers.',
|
|
120
|
+
when: 'the request depends on discovery, evaluation, or external-source claims.',
|
|
121
|
+
evidence: 'research plan, source ledger, cycle record, and execution profile routing.',
|
|
122
|
+
fallback: 'mark unavailable source tools explicitly and avoid unsupported live-accuracy claims.'
|
|
123
|
+
},
|
|
124
|
+
dfix: {
|
|
125
|
+
command: '$DFix',
|
|
126
|
+
purpose: 'perform tiny direct fixes without the full Team route.',
|
|
127
|
+
when: 'copy/config/docs/labels/spacing/translation/mechanical edits are truly narrow.',
|
|
128
|
+
evidence: 'focused diff plus DFix Honest check.',
|
|
129
|
+
fallback: 'route broad implementation through Team/Loop instead.'
|
|
130
|
+
},
|
|
131
|
+
'image-ux-review': {
|
|
132
|
+
command: '$Image-UX-Review',
|
|
133
|
+
purpose: 'produce generated annotated UI review images and extract issue ledgers.',
|
|
134
|
+
when: 'visual UX critique is requested from screenshots or app captures.',
|
|
135
|
+
evidence: 'source inventory, generated annotation images, extracted issue ledger.',
|
|
136
|
+
fallback: 'block if raster annotation cannot be produced.'
|
|
137
|
+
},
|
|
138
|
+
'computer-use': {
|
|
139
|
+
command: '$Computer-Use',
|
|
140
|
+
purpose: 'operate native macOS desktop apps through the fast Computer Use lane.',
|
|
141
|
+
when: 'the task depends on non-web desktop UI or OS settings.',
|
|
142
|
+
evidence: 'desktop interaction notes/screenshots where available.',
|
|
143
|
+
fallback: 'use Browser/Chrome only for web targets.'
|
|
144
|
+
},
|
|
145
|
+
'init-deep': {
|
|
146
|
+
command: '$Init-Deep',
|
|
147
|
+
purpose: 'refresh project-local memory, directory AGENTS sections, and loop memory hints.',
|
|
148
|
+
when: 'a route needs deeper local context or directory-specific instruction recall.',
|
|
149
|
+
evidence: '.sneakoscope/context/AGENTS.generated.md and managed directory AGENTS blocks.',
|
|
150
|
+
fallback: 'preserve user content and skip directories that cannot be safely updated.'
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
return table[name] || {
|
|
154
|
+
command: `$${name}`,
|
|
155
|
+
purpose: 'bridge an SKS managed Codex App route.',
|
|
156
|
+
when: 'the matching SKS route is explicitly requested.',
|
|
157
|
+
evidence: '.sneakoscope route artifacts.',
|
|
158
|
+
fallback: 'record blockers with evidence.'
|
|
159
|
+
};
|
|
160
|
+
}
|
|
89
161
|
function hash(value) {
|
|
90
162
|
return crypto.createHash('sha256').update(`sks-skill:${value}`).digest('hex').slice(0, 12);
|
|
91
163
|
}
|
|
@@ -1,7 +1,14 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
1
|
import path from 'node:path';
|
|
3
|
-
import { nowIso, writeJsonAtomic } from '../fsx.js';
|
|
4
|
-
|
|
2
|
+
import { nowIso, writeJsonAtomic, writeTextAtomic } from '../fsx.js';
|
|
3
|
+
import { analyzeLazyCodexLiveSource } from './lazycodex-live-analyzer.js';
|
|
4
|
+
export function buildLazyCodexPatternAnalysis(live = null) {
|
|
5
|
+
const liveByPattern = new Map(live?.patterns.map((row) => [row.id, row]) || []);
|
|
6
|
+
const liveEvidenceByPattern = new Map();
|
|
7
|
+
for (const row of live?.evidence || []) {
|
|
8
|
+
const list = liveEvidenceByPattern.get(row.pattern_id) || [];
|
|
9
|
+
list.push(`${row.file}:${row.lines?.join('-') || 'unknown'}:${row.snippet_hash}`);
|
|
10
|
+
liveEvidenceByPattern.set(row.pattern_id, list);
|
|
11
|
+
}
|
|
5
12
|
return {
|
|
6
13
|
schema: 'sks.lazycodex-pattern-analysis.v1',
|
|
7
14
|
source_repo: 'code-yeongyu/lazycodex',
|
|
@@ -21,28 +28,42 @@ export function buildLazyCodexPatternAnalysis() {
|
|
|
21
28
|
pattern('hook-continuation', 'Hook lifecycle and continuation enforcer', ['Directive: UserPromptSubmit, PreToolUse, PostToolUse, Stop, Notification map to pipeline actions.'], 'adopt', 'Map lifecycle and add Loop continuation proof adapter.', ['src/core/codex-app/codex-hook-lifecycle.ts', 'src/core/loops/loop-continuation-enforcer.ts']),
|
|
22
29
|
pattern('skill-mcp-slashcommand', 'Skill MCP and slashcommand tool', ['Directive: OmO exposes skill MCP and slashcommand tools.'], 'adapt', 'Report MCP candidates and SKS route skill availability without assuming external plugin behavior.', ['src/core/codex-app/codex-app-harness-matrix.ts']),
|
|
23
30
|
pattern('lsp-ast-grep', 'LSP/AST-grep optional tooling', ['Directive: LSP/AST-grep are optional-but-first-class loop gates/tools.'], 'watch', 'Use as future specialist gates; do not add unrequested fallback tooling now.', ['src/core/loops/loop-gate-selector.ts'])
|
|
24
|
-
]
|
|
25
|
-
|
|
31
|
+
].map((row) => {
|
|
32
|
+
const livePattern = liveByPattern.get(row.id);
|
|
33
|
+
const liveEvidence = liveEvidenceByPattern.get(row.id) || [];
|
|
34
|
+
return {
|
|
35
|
+
...row,
|
|
36
|
+
evidence: [...row.evidence, ...liveEvidence],
|
|
37
|
+
confidence: liveEvidence.length ? 'high' : livePattern ? 'medium' : 'low',
|
|
38
|
+
live_evidence: liveEvidence
|
|
39
|
+
};
|
|
40
|
+
}),
|
|
41
|
+
blockers: live?.blockers || [],
|
|
42
|
+
warnings: live?.warnings || (live ? [] : ['live_analysis_not_available_static_only']),
|
|
43
|
+
live_analysis_path: live ? '.sneakoscope/reports/lazycodex-live-analysis.json' : null
|
|
26
44
|
};
|
|
27
45
|
}
|
|
28
46
|
export async function writeLazyCodexPatternAnalysis(root) {
|
|
29
|
-
const
|
|
47
|
+
const live = await analyzeLazyCodexLiveSource({ root, writeReport: true }).catch(() => null);
|
|
48
|
+
const report = buildLazyCodexPatternAnalysis(live);
|
|
30
49
|
await writeJsonAtomic(path.join(root, '.sneakoscope', 'reports', 'lazycodex-analysis.json'), report);
|
|
50
|
+
await writeTextAtomic(path.join(root, 'docs', 'lazycodex-analysis.md'), renderLazyCodexAnalysisMarkdown(report)).catch(() => undefined);
|
|
31
51
|
return report;
|
|
32
52
|
}
|
|
33
53
|
export function renderLazyCodexAnalysisMarkdown(report) {
|
|
34
|
-
const rows = report.patterns.map((p) => `| ${p.id} | ${p.sks_adoption} | ${p.rationale.replace(/\|/g, '\\|')} |`).join('\n');
|
|
54
|
+
const rows = report.patterns.map((p) => `| ${p.id} | ${p.sks_adoption} | ${p.confidence} | ${p.live_evidence.length} | ${p.rationale.replace(/\|/g, '\\|')} |`).join('\n');
|
|
35
55
|
return [
|
|
36
56
|
'# LazyCodex / OmO Pattern Analysis',
|
|
37
57
|
'',
|
|
38
58
|
`Source repo: \`${report.source_repo}\``,
|
|
39
59
|
`Analyzed at: \`${report.analyzed_at}\``,
|
|
60
|
+
`Live analysis: \`${report.live_analysis_path || 'not available'}\``,
|
|
40
61
|
'',
|
|
41
|
-
'| Pattern | Adoption | Rationale |',
|
|
42
|
-
'
|
|
62
|
+
'| Pattern | Adoption | Confidence | Live Evidence | Rationale |',
|
|
63
|
+
'|---|---|---|---:|---|',
|
|
43
64
|
rows,
|
|
44
65
|
'',
|
|
45
|
-
'This artifact
|
|
66
|
+
'This artifact combines static directive mapping with hashed current-source evidence when available. Long source excerpts are intentionally omitted.'
|
|
46
67
|
].join('\n');
|
|
47
68
|
}
|
|
48
69
|
function pattern(id, title, evidence, sks_adoption, rationale, target_modules) {
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
1
|
import fs from 'node:fs/promises';
|
|
3
2
|
import path from 'node:path';
|
|
4
3
|
import os from 'node:os';
|
|
@@ -6,7 +5,7 @@ import { nowIso, writeJsonAtomic } from '../fsx.js';
|
|
|
6
5
|
import { buildCodexPluginInventory } from '../codex-plugins/codex-plugin-json.js';
|
|
7
6
|
export async function buildLazyCodexInteropPolicy(input) {
|
|
8
7
|
const root = path.resolve(input.root);
|
|
9
|
-
const inventory = input.inventory || await buildCodexPluginInventory().catch((err) => ({ plugins: [], blockers: [
|
|
8
|
+
const inventory = normalizeInventory(input.inventory || await buildCodexPluginInventory().catch((err) => ({ plugins: [], blockers: [messageOf(err)] })));
|
|
10
9
|
const codexHome = input.codexHome || process.env.CODEX_HOME || path.join(os.homedir(), '.codex');
|
|
11
10
|
const skillNames = await discoverSkillNames([path.join(root, '.agents', 'skills'), path.join(codexHome, 'skills')]);
|
|
12
11
|
const pluginIds = (inventory.plugins || []).map((plugin) => `${plugin.id || ''} ${plugin.name || ''}`.toLowerCase());
|
|
@@ -36,6 +35,18 @@ export async function buildLazyCodexInteropPolicy(input) {
|
|
|
36
35
|
await writeJsonAtomic(path.join(root, '.sneakoscope', 'reports', 'lazycodex-interop-policy.json'), report).catch(() => undefined);
|
|
37
36
|
return report;
|
|
38
37
|
}
|
|
38
|
+
function normalizeInventory(value) {
|
|
39
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
40
|
+
return { plugins: [], blockers: [] };
|
|
41
|
+
const record = value;
|
|
42
|
+
return {
|
|
43
|
+
plugins: Array.isArray(record.plugins) ? record.plugins.map((plugin) => plugin && typeof plugin === 'object' ? plugin : {}) : [],
|
|
44
|
+
blockers: Array.isArray(record.blockers) ? record.blockers.map(String) : []
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function messageOf(err) {
|
|
48
|
+
return err instanceof Error ? err.message : String(err);
|
|
49
|
+
}
|
|
39
50
|
async function discoverSkillNames(roots) {
|
|
40
51
|
const names = new Set();
|
|
41
52
|
for (const root of roots) {
|
|
@@ -0,0 +1,98 @@
|
|
|
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
|
+
const PATTERNS = [
|
|
6
|
+
{ id: 'npx-no-global-install', claim: 'npx install command evidence', re: /\bnpx\b.+(?:lazycodex|omo|oh-my-openagent)/i },
|
|
7
|
+
{ id: 'codex-marketplace-plugin', claim: 'Codex marketplace install command evidence', re: /\bcodex\b.+(?:plugin|marketplace).+(?:add|install)/i },
|
|
8
|
+
{ id: 'startup-review-hooks', claim: 'Hook approval statement evidence', re: /\bhook\b.+\b(approval|approve|review|trusted|trust)\b/i },
|
|
9
|
+
{ id: 'doctor-health-report', claim: 'Doctor health report evidence', re: /\bdoctor\b.+\b(health|report|check)\b/i },
|
|
10
|
+
{ id: 'plan-start-loop', claim: '$ulw-loop/$ulw-plan/$start-work command evidence', re: /\$(?:ulw-loop|ulw-plan|start-work)\b/ },
|
|
11
|
+
{ id: 'init-deep-agents', claim: '$init-deep evidence', re: /\$init-deep\b|init-deep/i },
|
|
12
|
+
{ id: 'native-agent-type', claim: 'agent_type fallback evidence', re: /\bagent_type\b|message[- ]role|fallback/i }
|
|
13
|
+
];
|
|
14
|
+
export async function analyzeLazyCodexLiveSource(input) {
|
|
15
|
+
const root = path.resolve(input.root);
|
|
16
|
+
const sourceDir = input.sourceDir ? path.resolve(input.sourceDir) : path.join(root, '.sneakoscope', 'cache', 'lazycodex');
|
|
17
|
+
const files = ['README.md', 'package.json', 'bin/lazycodex-ai.js', '.gitmodules'];
|
|
18
|
+
const evidence = [];
|
|
19
|
+
const blockers = [];
|
|
20
|
+
for (const rel of files) {
|
|
21
|
+
const file = path.join(sourceDir, rel);
|
|
22
|
+
const text = await fs.readFile(file, 'utf8').catch(() => '');
|
|
23
|
+
if (!text)
|
|
24
|
+
continue;
|
|
25
|
+
evidence.push(...extractEvidence(rel, text));
|
|
26
|
+
}
|
|
27
|
+
if (!evidence.length)
|
|
28
|
+
blockers.push(`lazycodex_source_evidence_missing:${sourceDir}`);
|
|
29
|
+
const sourceSha = await gitSha(sourceDir);
|
|
30
|
+
const patterns = PATTERNS.map((pattern) => ({
|
|
31
|
+
id: pattern.id,
|
|
32
|
+
title: pattern.claim,
|
|
33
|
+
evidence: evidence.filter((row) => row.pattern_id === pattern.id).map((row) => `${row.file}:${row.lines?.join('-') || 'unknown'}:${row.snippet_hash}`),
|
|
34
|
+
sks_adoption: pattern.id === 'native-agent-type' || pattern.id === 'startup-review-hooks' || pattern.id === 'doctor-health-report' ? 'adopt' : 'adapt',
|
|
35
|
+
rationale: evidence.some((row) => row.pattern_id === pattern.id) ? 'Live source evidence found and hashed.' : 'No live source evidence found; keep static analysis as lower confidence.',
|
|
36
|
+
target_modules: []
|
|
37
|
+
}));
|
|
38
|
+
const report = {
|
|
39
|
+
schema: 'sks.lazycodex-live-analysis.v1',
|
|
40
|
+
generated_at: nowIso(),
|
|
41
|
+
source_repo: 'code-yeongyu/lazycodex',
|
|
42
|
+
source_ref: input.sourceRef || sourceSha || 'local-snapshot',
|
|
43
|
+
source_sha: sourceSha,
|
|
44
|
+
evidence,
|
|
45
|
+
patterns,
|
|
46
|
+
blockers,
|
|
47
|
+
warnings: blockers.length ? ['live_evidence_incomplete_static_fallback_required'] : []
|
|
48
|
+
};
|
|
49
|
+
if (input.writeReport !== false) {
|
|
50
|
+
await writeJsonAtomic(path.join(root, '.sneakoscope', 'reports', 'lazycodex-live-analysis.json'), report).catch(() => undefined);
|
|
51
|
+
await writeTextAtomic(path.join(root, 'docs', 'lazycodex-analysis.md'), renderLazyCodexLiveMarkdown(report)).catch(() => undefined);
|
|
52
|
+
}
|
|
53
|
+
return report;
|
|
54
|
+
}
|
|
55
|
+
export function extractEvidence(file, text) {
|
|
56
|
+
const lines = text.split(/\r?\n/);
|
|
57
|
+
const rows = [];
|
|
58
|
+
for (const pattern of PATTERNS) {
|
|
59
|
+
const index = lines.findIndex((line) => pattern.re.test(line));
|
|
60
|
+
if (index < 0)
|
|
61
|
+
continue;
|
|
62
|
+
const snippet = lines.slice(Math.max(0, index - 1), Math.min(lines.length, index + 2)).join('\n').slice(0, 500);
|
|
63
|
+
rows.push({
|
|
64
|
+
pattern_id: pattern.id,
|
|
65
|
+
file,
|
|
66
|
+
lines: [index + 1, index + 1],
|
|
67
|
+
snippet_hash: createHash('sha256').update(snippet).digest('hex'),
|
|
68
|
+
claim: pattern.claim,
|
|
69
|
+
confidence: 'high'
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
return rows;
|
|
73
|
+
}
|
|
74
|
+
export function renderLazyCodexLiveMarkdown(report) {
|
|
75
|
+
const rows = report.evidence.map((row) => `| ${row.pattern_id} | ${row.file} | ${row.lines?.join('-') || '-'} | ${row.snippet_hash.slice(0, 16)} | ${row.confidence} |`).join('\n');
|
|
76
|
+
return [
|
|
77
|
+
'# LazyCodex / OmO Pattern Analysis',
|
|
78
|
+
'',
|
|
79
|
+
`Source repo: \`${report.source_repo}\``,
|
|
80
|
+
`Source ref: \`${report.source_ref}\``,
|
|
81
|
+
`Source sha: \`${report.source_sha || 'unknown'}\``,
|
|
82
|
+
`Generated at: \`${report.generated_at}\``,
|
|
83
|
+
'',
|
|
84
|
+
'| Pattern | File | Lines | Snippet Hash | Confidence |',
|
|
85
|
+
'|---|---|---:|---|---|',
|
|
86
|
+
rows || '| none | - | - | - | low |',
|
|
87
|
+
'',
|
|
88
|
+
'Long source excerpts are intentionally omitted; release artifacts store line anchors and snippet hashes only.'
|
|
89
|
+
].join('\n');
|
|
90
|
+
}
|
|
91
|
+
async function gitSha(sourceDir) {
|
|
92
|
+
const head = await fs.readFile(path.join(sourceDir, '.git', 'HEAD'), 'utf8').catch(() => '');
|
|
93
|
+
const ref = head.match(/^ref:\s*(.+)$/m)?.[1];
|
|
94
|
+
if (ref)
|
|
95
|
+
return (await fs.readFile(path.join(sourceDir, '.git', ref), 'utf8').catch(() => '')).trim() || null;
|
|
96
|
+
return /^[0-9a-f]{40}$/i.test(head.trim()) ? head.trim() : null;
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=lazycodex-live-analyzer.js.map
|
|
@@ -28,17 +28,52 @@ export async function madHighCommand(args = [], deps = {}) {
|
|
|
28
28
|
if (subcommand)
|
|
29
29
|
return madSksSubcommand(subcommand, args.filter((arg) => String(arg) !== subcommand));
|
|
30
30
|
const cleanArgs = stripMadLaunchOnlyArgs(args);
|
|
31
|
-
|
|
31
|
+
const rawArgs = (args || []).map((arg) => String(arg));
|
|
32
|
+
const dryRun = rawArgs.includes('--dry-run');
|
|
33
|
+
if (args.includes('--json') && !dryRun) {
|
|
32
34
|
const profile = buildMadHighLaunchProfileNoWrite();
|
|
33
35
|
return console.log(JSON.stringify(profile, null, 2));
|
|
34
36
|
}
|
|
35
37
|
const update = { status: 'notice_only', non_blocking: true };
|
|
36
|
-
const rawArgs = (args || []).map((arg) => String(arg));
|
|
37
38
|
const headlessZellij = rawArgs.includes('--headless') || process.env.SKS_MAD_ALLOW_HEADLESS === '1';
|
|
38
39
|
const skipZellijRepair = rawArgs.includes('--skip-zellij-repair') || rawArgs.includes('--no-auto-install-zellij');
|
|
39
40
|
const launchRoot = process.cwd();
|
|
40
41
|
if (!(await exists(path.join(launchRoot, '.sneakoscope'))))
|
|
41
42
|
await initProject(launchRoot, {});
|
|
43
|
+
if (dryRun) {
|
|
44
|
+
const zellijPlan = skipZellijRepair
|
|
45
|
+
? { schema: 'sks.zellij-self-heal.v1', ok: true, status: 'skipped', dry_run: true, planned_mutations: [], command: null, blockers: [], warnings: ['zellij_repair_skipped'] }
|
|
46
|
+
: await repairZellijForSks({
|
|
47
|
+
root: launchRoot,
|
|
48
|
+
requestedBy: 'sks --mad',
|
|
49
|
+
fixRequested: true,
|
|
50
|
+
autoApprove: rawArgs.includes('--yes') || rawArgs.includes('-y'),
|
|
51
|
+
interactive: false,
|
|
52
|
+
installHomebrew: rawArgs.includes('--install-homebrew'),
|
|
53
|
+
allowHeadlessFallback: headlessZellij,
|
|
54
|
+
dryRun: true
|
|
55
|
+
});
|
|
56
|
+
const report = {
|
|
57
|
+
schema: 'sks.mad-sks-zellij-dry-run.v1',
|
|
58
|
+
ok: zellijPlan.ok === true,
|
|
59
|
+
status: zellijPlan.ok === true ? 'dry_run' : 'repair_required',
|
|
60
|
+
generated_at: nowIso(),
|
|
61
|
+
launch_skipped: true,
|
|
62
|
+
zellij_repair: zellijPlan
|
|
63
|
+
};
|
|
64
|
+
await writeJsonAtomic(path.join(launchRoot, '.sneakoscope', 'reports', 'mad-sks-zellij-dry-run.json'), report);
|
|
65
|
+
if (rawArgs.includes('--json'))
|
|
66
|
+
console.log(JSON.stringify(report, null, 2));
|
|
67
|
+
else {
|
|
68
|
+
console.log(`SKS MAD dry-run: launch_skipped=true status=${report.status}`);
|
|
69
|
+
const planned = Array.isArray(zellijPlan.planned_mutations) ? zellijPlan.planned_mutations : [];
|
|
70
|
+
for (const row of planned)
|
|
71
|
+
console.log(`- plan: ${row.command}`);
|
|
72
|
+
if (zellijPlan.command && planned.length === 0)
|
|
73
|
+
console.log(`- run: ${zellijPlan.command}`);
|
|
74
|
+
}
|
|
75
|
+
return report;
|
|
76
|
+
}
|
|
42
77
|
const codexUpdate = deps.maybePromptCodexUpdateForLaunch ? await deps.maybePromptCodexUpdateForLaunch(args, { label: 'MAD launch' }) : { status: 'skipped' };
|
|
43
78
|
if (codexUpdate.status === 'failed' || codexUpdate.status === 'updated_not_reflected') {
|
|
44
79
|
console.error(`Codex CLI update failed: ${codexUpdate.error || 'updated version was not visible on PATH'}`);
|
|
@@ -146,6 +146,7 @@ async function qaLoopRun(args) {
|
|
|
146
146
|
const mock = flag(args, '--mock');
|
|
147
147
|
const qaGate = await readJson(path.join(dir, 'qa-gate.json'), {});
|
|
148
148
|
const reportFile = qaGate.qa_report_file;
|
|
149
|
+
const executionProfile = await readJson(path.join(dir, 'qa-loop', 'execution-profile.json'), null);
|
|
149
150
|
const uiRequired = qaUiRequired(contract.answers || {});
|
|
150
151
|
const capabilityArtifact = await writeCodex0138CapabilityArtifacts(root, { missionId: id }).catch((err) => ({ error: err?.message || String(err), report: null }));
|
|
151
152
|
const usageArtifact = await writeCodexAccountUsageArtifacts(root, { missionId: id }).catch((err) => ({ error: err?.message || String(err), snapshot: null }));
|
|
@@ -273,7 +274,7 @@ async function qaLoopRun(args) {
|
|
|
273
274
|
await appendJsonlBounded(path.join(dir, 'events.jsonl'), { ts: nowIso(), type: 'qaloop.run.started', maxCycles, mock });
|
|
274
275
|
const nativeAgentPlan = await readJson(path.join(dir, 'qa-agent-plan.json'), null);
|
|
275
276
|
const nativeRoster = requestedAgents === 3 ? nativeAgentPlan : null;
|
|
276
|
-
const nativeAgentRun = await runNativeAgentOrchestrator({ root, missionId: id, route: '$QA-LOOP', prompt: mission.prompt || 'QA-LOOP run', backend: mock ? 'fake' : 'codex-sdk', mock, agents: requestedAgents, targetActiveSlots, desiredWorkItemCount, minimumWorkItems, maxQueueExpansion, concurrency: Math.min(requestedAgents, 5), readonly: !(applyPatches && writeMode !== 'off'), profile, writeMode: writeMode, applyPatches, dryRunPatches, maxWriteAgents, roster: nativeRoster, routeCommand: 'sks qa-loop run', routeBlackboxKind: 'actual_qa_command' });
|
|
277
|
+
const nativeAgentRun = await runNativeAgentOrchestrator({ root, missionId: id, route: '$QA-LOOP', prompt: mission.prompt || 'QA-LOOP run', backend: mock ? 'fake' : 'codex-sdk', mock, agents: requestedAgents, targetActiveSlots, desiredWorkItemCount, minimumWorkItems, maxQueueExpansion, concurrency: Math.min(requestedAgents, 5), readonly: !(applyPatches && writeMode !== 'off'), profile, writeMode: writeMode, applyPatches, dryRunPatches, maxWriteAgents, roster: nativeRoster, routeCommand: 'sks qa-loop run', routeBlackboxKind: 'actual_qa_command', env: { SKS_CODEX_APP_EXECUTION_PROFILE: executionProfile?.mode || 'unknown', SKS_CODEX_AGENT_ROLE_STRATEGY: executionProfile?.agent_role_strategy || 'message-role' } });
|
|
277
278
|
await writeJsonAtomic(path.join(dir, 'qa-native-agent-run.json'), nativeAgentRun);
|
|
278
279
|
await appendJsonlBounded(path.join(dir, 'events.jsonl'), { ts: nowIso(), type: 'qaloop.native_agents.completed', backend: nativeAgentRun.backend, ok: nativeAgentRun.ok, proof: nativeAgentRun.proof?.status });
|
|
279
280
|
if (mock) {
|
|
@@ -343,7 +344,7 @@ async function qaLoopRun(args) {
|
|
|
343
344
|
for (let cycle = 1; cycle <= maxCycles; cycle += 1) {
|
|
344
345
|
const cycleDir = path.join(dir, 'qa-loop', `cycle-${cycle}`);
|
|
345
346
|
const outputFile = path.join(cycleDir, 'final.md');
|
|
346
|
-
const prompt = buildQaLoopPrompt({ id, mission, contract, cycle, previous: last, reportFile, imagePathContract: imagePathContract?.contract || null, appHandoff });
|
|
347
|
+
const prompt = buildQaLoopPrompt({ id, mission, contract, cycle, previous: last, reportFile, imagePathContract: imagePathContract?.contract || null, appHandoff, executionProfile });
|
|
347
348
|
await appendJsonlBounded(path.join(dir, 'events.jsonl'), { ts: nowIso(), type: 'qaloop.cycle.start', cycle });
|
|
348
349
|
const result = await runCodexExec({ root, prompt, outputFile, json: true, profile, logDir: cycleDir });
|
|
349
350
|
await writeJsonAtomic(path.join(cycleDir, 'process.json'), { code: result.code, stdout_tail: result.stdout, stderr_tail: result.stderr, stdout_bytes: result.stdoutBytes, stderr_bytes: result.stderrBytes, truncated: result.truncated, timed_out: result.timedOut });
|
|
@@ -52,7 +52,7 @@ async function researchPrepare(args) {
|
|
|
52
52
|
const context7Required = routeNeedsContext7(route, prompt);
|
|
53
53
|
const reasoning = routeReasoning(route, prompt);
|
|
54
54
|
const autoresearch = flag(args, '--autoresearch');
|
|
55
|
-
const plan = await writeResearchPlan(dir, prompt, { depth: readFlagValue(args, '--depth', 'frontier'), missionId: id, autoresearch });
|
|
55
|
+
const plan = await writeResearchPlan(dir, prompt, { root, depth: readFlagValue(args, '--depth', 'frontier'), missionId: id, autoresearch });
|
|
56
56
|
const pipelinePlan = await writePipelinePlan(dir, { missionId: id, route, task: prompt, required: context7Required, ambiguity: { required: false, status: 'direct_research_cli' } });
|
|
57
57
|
await writeJsonAtomic(path.join(dir, 'route-context.json'), {
|
|
58
58
|
route: route.id,
|
|
@@ -114,7 +114,7 @@ async function researchRun(args) {
|
|
|
114
114
|
const { dir, mission } = await loadMission(root, id);
|
|
115
115
|
const planPath = path.join(dir, 'research-plan.json');
|
|
116
116
|
if (!(await exists(planPath)))
|
|
117
|
-
await writeResearchPlan(dir, mission.prompt || '', { missionId: id, autoresearch: flag(args, '--autoresearch') });
|
|
117
|
+
await writeResearchPlan(dir, mission.prompt || '', { root, missionId: id, autoresearch: flag(args, '--autoresearch') });
|
|
118
118
|
const plan = await readJson(planPath);
|
|
119
119
|
const dbScan = await scanDbSafety(root);
|
|
120
120
|
if (!dbScan.ok) {
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
1
|
import { repairZellijForSks } from '../zellij/zellij-self-heal.js';
|
|
3
2
|
export async function runDoctorZellijRepair(input) {
|
|
4
3
|
const args = (input.args || []).map(String);
|
|
@@ -10,6 +9,7 @@ export async function runDoctorZellijRepair(input) {
|
|
|
10
9
|
fixRequested: true,
|
|
11
10
|
autoApprove: args.includes('--yes') || args.includes('-y'),
|
|
12
11
|
installHomebrew: args.includes('--install-homebrew') || process.env.SKS_ALLOW_HOMEBREW_INSTALL === '1',
|
|
12
|
+
dryRun: args.includes('--dry-run'),
|
|
13
13
|
interactive: Boolean(process.stdin.isTTY && process.stdout.isTTY && process.env.SKS_NO_QUESTION !== '1'),
|
|
14
14
|
allowHeadlessFallback: false,
|
|
15
15
|
env: process.env
|
|
@@ -18,6 +18,10 @@ export async function runDoctorZellijRepair(input) {
|
|
|
18
18
|
export function doctorZellijRepairConsoleLine(result) {
|
|
19
19
|
if (!result)
|
|
20
20
|
return null;
|
|
21
|
+
if (result.dry_run) {
|
|
22
|
+
const planned = result.planned_mutations.map((row) => row.command).join(' && ') || result.command || 'none';
|
|
23
|
+
return `Zellij repair: dry_run planned ${planned}`;
|
|
24
|
+
}
|
|
21
25
|
if (result.strategy === 'none-current')
|
|
22
26
|
return `Zellij repair: current ${result.after.version || ''}`.trim();
|
|
23
27
|
if (result.ok && (result.strategy === 'brew-install-zellij' || result.strategy === 'brew-install-homebrew-then-zellij')) {
|
|
@@ -107,6 +107,7 @@ const FIXTURES = Object.freeze({
|
|
|
107
107
|
'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'),
|
|
108
108
|
'route-ux-review': fixture('mock', '$UX-Review image UX alias route', ['image-ux-generated-review-ledger.json', 'image-voxel-ledger.json'], 'pass'),
|
|
109
109
|
'route-db': fixture('execute_and_validate_artifacts', 'sks db check --sql "SELECT 1" --json', ['completion-proof.json', 'db-operation-report.json'], 'pass'),
|
|
110
|
+
'route-mad-db': fixture('mock', '$MAD-DB one-cycle DB break-glass route contract', ['mad-db-capability.json', 'mad-db-ledger.jsonl', 'completion-proof.json'], 'pass'),
|
|
110
111
|
'route-wiki': fixture('execute_and_validate_artifacts', 'sks wiki image-ingest test/fixtures/images/one-by-one.png --json', [{ path: 'completion-proof.json', schema: 'sks.completion-proof.v1' }, { path: 'image-voxel-ledger.json', schema: 'sks.image-voxel-ledger.v1' }], 'pass'),
|
|
111
112
|
'route-gx': fixture('execute_and_validate_artifacts', 'sks gx validate fixture --mock --json', ['completion-proof.json', { path: 'image-voxel-ledger.json', schema: 'sks.image-voxel-ledger.v1' }, 'gx-validation.json'], 'pass'),
|
|
112
113
|
'route-sks': fixture('mock', '$SKS control-surface route', ['completion-proof.json'], 'pass'),
|
|
@@ -2,7 +2,7 @@ import fs from 'node:fs/promises';
|
|
|
2
2
|
import { existsSync } from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { spawnSync } from 'node:child_process';
|
|
5
|
-
import { COMMAND_CATALOG, DOLLAR_COMMAND_ALIASES, DOLLAR_COMMANDS } from './routes.js';
|
|
5
|
+
import { COMMAND_CATALOG, DOLLAR_COMMAND_ALIASES, DOLLAR_COMMANDS, ROUTES } from './routes.js';
|
|
6
6
|
import { FEATURE_QUALITY_LEVELS, fixtureForFeature, fixtureSummary, validateFeatureFixtures } from './feature-fixtures.js';
|
|
7
7
|
import { runFeatureFixture, writeFeatureFixtureReports } from './feature-fixture-runner.js';
|
|
8
8
|
import { PACKAGE_VERSION, exists, nowIso, packageRoot, readJson, readText, runProcess, writeJsonAtomic, writeTextAtomic } from './fsx.js';
|
|
@@ -46,6 +46,9 @@ export async function buildFeatureRegistry({ root = packageRoot(), generatedAt =
|
|
|
46
46
|
}
|
|
47
47
|
for (const route of DOLLAR_COMMANDS)
|
|
48
48
|
features.push(routeFeature(route));
|
|
49
|
+
for (const route of ROUTES.filter((entry) => entry.hidden === true)) {
|
|
50
|
+
features.push(routeFeature(route));
|
|
51
|
+
}
|
|
49
52
|
features.push(nativeAgentIntakeFeature());
|
|
50
53
|
features.push(agentProofEvidenceFeature());
|
|
51
54
|
for (const skillName of skillNames) {
|
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 = '3.1.
|
|
8
|
+
export const PACKAGE_VERSION = '3.1.5';
|
|
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() {
|
package/dist/core/init.js
CHANGED
|
@@ -639,7 +639,10 @@ export async function initProject(root, opts = {}) {
|
|
|
639
639
|
const baseUrl = globalConfig.match(/(^|\n)\[model_providers\.codex-lb\][\s\S]*?\n\s*base_url\s*=\s*"([^"]+)"/)?.[2] || parseCodexLbEnvBaseUrl(envText);
|
|
640
640
|
if (!parseCodexLbEnvKey(envText) || !baseUrl)
|
|
641
641
|
return next;
|
|
642
|
-
|
|
642
|
+
const shouldSelectCodexLb = selectedRe.test(next) || selectedRe.test(globalConfig);
|
|
643
|
+
next = shouldSelectCodexLb
|
|
644
|
+
? upsertTopLevelTomlString(next, 'model_provider', 'codex-lb')
|
|
645
|
+
: removeTopLevelTomlKeyIfValue(next, 'model_provider', 'codex-lb');
|
|
643
646
|
next = upsertTomlTable(next, 'model_providers.codex-lb', `[model_providers.codex-lb]\nname = "OpenAI"\nbase_url = "${baseUrl}"\nwire_api = "responses"\nenv_key = "CODEX_LB_API_KEY"\nsupports_websockets = true\nrequires_openai_auth = false`);
|
|
644
647
|
return `${next.trim()}\n`;
|
|
645
648
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
1
|
import path from 'node:path';
|
|
3
2
|
import { nowIso, readJson, writeJsonAtomic } from '../fsx.js';
|
|
4
3
|
import { loopPlanPath } from './loop-artifacts.js';
|
|
@@ -8,9 +7,9 @@ export async function evaluateLoopContinuation(input) {
|
|
|
8
7
|
const blockers = [];
|
|
9
8
|
if (!plan)
|
|
10
9
|
blockers.push('loop_plan_missing');
|
|
11
|
-
const nodes = plan
|
|
10
|
+
const nodes = loopNodes(plan);
|
|
12
11
|
const proofs = await Promise.all(nodes.map((node) => readJson(path.join(root, '.sneakoscope', 'missions', input.missionId, 'loops', node.loop_id, 'loop-proof.json'), null)));
|
|
13
|
-
const completed = proofs.filter((proof) => proof
|
|
12
|
+
const completed = proofs.filter((proof) => isRecord(proof) && proof.status === 'completed').length;
|
|
14
13
|
const incomplete = Math.max(0, nodes.length - completed);
|
|
15
14
|
const shouldContinue = Boolean(plan && incomplete > 0 && blockers.length === 0);
|
|
16
15
|
const report = {
|
|
@@ -29,4 +28,13 @@ export async function evaluateLoopContinuation(input) {
|
|
|
29
28
|
await writeJsonAtomic(path.join(root, '.sneakoscope', 'missions', input.missionId, 'loop-continuation-enforcer.json'), report).catch(() => undefined);
|
|
30
29
|
return report;
|
|
31
30
|
}
|
|
31
|
+
function loopNodes(value) {
|
|
32
|
+
if (!isRecord(value) || !isRecord(value.graph) || !Array.isArray(value.graph.nodes))
|
|
33
|
+
return [];
|
|
34
|
+
return value.graph.nodes
|
|
35
|
+
.filter((node) => isRecord(node) && typeof node.loop_id === 'string');
|
|
36
|
+
}
|
|
37
|
+
function isRecord(value) {
|
|
38
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
39
|
+
}
|
|
32
40
|
//# sourceMappingURL=loop-continuation-enforcer.js.map
|
|
@@ -5,7 +5,7 @@ import { selectLoopGates } from './loop-gate-selector.js';
|
|
|
5
5
|
import { inferLoopOwnerScope } from './loop-owner-inference.js';
|
|
6
6
|
import { classifyLoopRisk } from './loop-risk-classifier.js';
|
|
7
7
|
import { defaultLoopBudget, validateLoopPlan } from './loop-schema.js';
|
|
8
|
-
import { readInitDeepMemory } from '../codex-app/codex-init-deep.js';
|
|
8
|
+
import { readInitDeepMemory, readInitDeepMemoryHints } from '../codex-app/codex-init-deep.js';
|
|
9
9
|
export async function planLoopsFromRequest(input) {
|
|
10
10
|
const parallelism = input.parallelism || 'balanced';
|
|
11
11
|
const maxLoops = Math.max(1, Math.min(32, input.maxLoops || 8));
|
|
@@ -53,6 +53,11 @@ export async function planLoopsFromRequest(input) {
|
|
|
53
53
|
})
|
|
54
54
|
};
|
|
55
55
|
const nodes = [...actionNodes, integrationNode];
|
|
56
|
+
const memoryHints = await readInitDeepMemoryHints(input.root, scopePathsForNodes(nodes)).catch(() => []);
|
|
57
|
+
const nodesWithMemory = nodes.map((node) => {
|
|
58
|
+
const hints = memoryHints.filter((hint) => hintAppliesToNode(hint, node)).slice(0, 5);
|
|
59
|
+
return hints.length ? { ...node, memory_hints: hints } : node;
|
|
60
|
+
});
|
|
56
61
|
const plan = {
|
|
57
62
|
schema: 'sks.loop-plan.v1',
|
|
58
63
|
mission_id: input.missionId,
|
|
@@ -64,12 +69,12 @@ export async function planLoopsFromRequest(input) {
|
|
|
64
69
|
confidence: actionNodes.length ? 'high' : 'medium'
|
|
65
70
|
},
|
|
66
71
|
graph: {
|
|
67
|
-
nodes,
|
|
72
|
+
nodes: nodesWithMemory,
|
|
68
73
|
edges: actionNodes.map((node) => ({ from: node.loop_id, to: integrationNode.loop_id, reason: 'integration_after_loop_proof' }))
|
|
69
74
|
},
|
|
70
75
|
global_budget: defaultLoopBudget({
|
|
71
|
-
max_iterations: Math.max(...
|
|
72
|
-
max_subagents:
|
|
76
|
+
max_iterations: Math.max(...nodesWithMemory.map((node) => node.budget.max_iterations)),
|
|
77
|
+
max_subagents: nodesWithMemory.reduce((sum, node) => sum + node.budget.max_subagents, 0)
|
|
73
78
|
}),
|
|
74
79
|
safety: {
|
|
75
80
|
no_unrequested_fallback_code: true,
|
|
@@ -176,4 +181,16 @@ function dynamicCheckerWorkerCount(input) {
|
|
|
176
181
|
function titleFromDomain(domainId) {
|
|
177
182
|
return domainId === 'loop-general-coding' ? 'General coding loop' : `${domainId} loop`;
|
|
178
183
|
}
|
|
184
|
+
function scopePathsForNodes(nodes) {
|
|
185
|
+
return nodes.flatMap((node) => [
|
|
186
|
+
...node.owner_scope.files,
|
|
187
|
+
...node.owner_scope.directories
|
|
188
|
+
]).filter(Boolean);
|
|
189
|
+
}
|
|
190
|
+
function hintAppliesToNode(hint, node) {
|
|
191
|
+
if (hint.scope === '.')
|
|
192
|
+
return true;
|
|
193
|
+
const scopes = [...node.owner_scope.files, ...node.owner_scope.directories].map((value) => value.replace(/^\.?\//, ''));
|
|
194
|
+
return scopes.some((scope) => scope === hint.scope || scope.startsWith(`${hint.scope}/`) || hint.scope.startsWith(`${scope}/`));
|
|
195
|
+
}
|
|
179
196
|
//# sourceMappingURL=loop-planner.js.map
|