mustflow 2.18.2 → 2.18.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.
- package/README.md +2 -0
- package/dist/cli/commands/run/builtin-dispatch.js +92 -0
- package/dist/cli/commands/run/executor.js +149 -0
- package/dist/cli/commands/run/output.js +59 -0
- package/dist/cli/commands/run/process-tree.js +91 -0
- package/dist/cli/commands/run/receipt.js +42 -0
- package/dist/cli/commands/run.js +17 -382
- package/dist/cli/commands/verify/args.js +262 -0
- package/dist/cli/commands/verify.js +1 -262
- package/dist/cli/i18n/en.js +1 -0
- package/dist/cli/i18n/es.js +1 -0
- package/dist/cli/i18n/fr.js +1 -0
- package/dist/cli/i18n/hi.js +1 -0
- package/dist/cli/i18n/ko.js +1 -0
- package/dist/cli/i18n/zh.js +1 -0
- package/dist/cli/index.js +6 -72
- package/dist/cli/lib/command-registry.js +27 -0
- package/dist/cli/lib/dashboard-export.js +2 -1
- package/dist/cli/lib/dashboard-html/locale-bootstrap.js +3 -2
- package/dist/cli/lib/dashboard-html/template.js +5 -4
- package/dist/cli/lib/html-json.js +11 -0
- package/dist/cli/lib/local-index/index.js +166 -14
- package/dist/cli/lib/run-plan.js +6 -0
- package/dist/core/check-issues.js +1 -0
- package/dist/core/command-contract-rules.js +0 -3
- package/dist/core/command-contract-validation.js +42 -4
- package/dist/core/command-intent-eligibility.js +4 -4
- package/dist/core/contract-lint.js +3 -3
- package/package.json +1 -1
- package/templates/default/i18n.toml +7 -1
- package/templates/default/locales/en/.mustflow/skills/INDEX.md +2 -1
- package/templates/default/locales/en/.mustflow/skills/routes.toml +6 -0
- package/templates/default/locales/en/.mustflow/skills/source-anchor-authoring/SKILL.md +147 -0
- package/templates/default/manifest.toml +8 -1
package/dist/cli/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { realpathSync } from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
5
|
-
import { COMMAND_DEFINITIONS } from './lib/command-registry.js';
|
|
5
|
+
import { COMMAND_DEFINITIONS, findCommandDefinition } from './lib/command-registry.js';
|
|
6
6
|
import { renderCliError, renderHelp } from './lib/cli-output.js';
|
|
7
7
|
import { DEFAULT_CLI_LANG, SUPPORTED_CLI_LANGS, isCliLang, t } from './lib/i18n.js';
|
|
8
8
|
import { consoleReporter } from './lib/reporter.js';
|
|
@@ -102,77 +102,11 @@ export async function runCli(argv, reporter = consoleReporter) {
|
|
|
102
102
|
reporter.stdout(getTopLevelHelp(parsed.lang));
|
|
103
103
|
return 0;
|
|
104
104
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
return (
|
|
110
|
-
}
|
|
111
|
-
if (command === 'adapters') {
|
|
112
|
-
return (await import('./commands/adapters.js')).runAdapters(args, reporter, parsed.lang);
|
|
113
|
-
}
|
|
114
|
-
if (command === 'check') {
|
|
115
|
-
return (await import('./commands/check.js')).runCheck(args, reporter, parsed.lang);
|
|
116
|
-
}
|
|
117
|
-
if (command === 'classify') {
|
|
118
|
-
return (await import('./commands/classify.js')).runClassify(args, reporter, parsed.lang);
|
|
119
|
-
}
|
|
120
|
-
if (command === 'contract-lint') {
|
|
121
|
-
return (await import('./commands/contract-lint.js')).runContractLint(args, reporter, parsed.lang);
|
|
122
|
-
}
|
|
123
|
-
if (command === 'status') {
|
|
124
|
-
return (await import('./commands/status.js')).runStatus(args, reporter, parsed.lang);
|
|
125
|
-
}
|
|
126
|
-
if (command === 'update') {
|
|
127
|
-
return (await import('./commands/update.js')).runUpdate(args, reporter, parsed.lang);
|
|
128
|
-
}
|
|
129
|
-
if (command === 'upgrade') {
|
|
130
|
-
return (await import('./commands/upgrade.js')).runUpgrade(args, reporter, parsed.lang);
|
|
131
|
-
}
|
|
132
|
-
if (command === 'map') {
|
|
133
|
-
return (await import('./commands/map.js')).runMap(args, reporter, parsed.lang);
|
|
134
|
-
}
|
|
135
|
-
if (command === 'line-endings') {
|
|
136
|
-
return (await import('./commands/line-endings.js')).runLineEndings(args, reporter, parsed.lang);
|
|
137
|
-
}
|
|
138
|
-
if (command === 'run') {
|
|
139
|
-
return (await import('./commands/run.js')).runRun(args, reporter, parsed.lang);
|
|
140
|
-
}
|
|
141
|
-
if (command === 'context') {
|
|
142
|
-
return (await import('./commands/context.js')).runContext(args, reporter, parsed.lang);
|
|
143
|
-
}
|
|
144
|
-
if (command === 'doctor') {
|
|
145
|
-
return (await import('./commands/doctor.js')).runDoctor(args, reporter, parsed.lang);
|
|
146
|
-
}
|
|
147
|
-
if (command === 'docs') {
|
|
148
|
-
return (await import('./commands/docs.js')).runDocs(args, reporter, parsed.lang);
|
|
149
|
-
}
|
|
150
|
-
if (command === 'handoff') {
|
|
151
|
-
return (await import('./commands/handoff.js')).runHandoff(args, reporter, parsed.lang);
|
|
152
|
-
}
|
|
153
|
-
if (command === 'index') {
|
|
154
|
-
return (await import('./commands/index.js')).runIndex(args, reporter, parsed.lang);
|
|
155
|
-
}
|
|
156
|
-
if (command === 'search') {
|
|
157
|
-
return (await import('./commands/search.js')).runSearch(args, reporter, parsed.lang);
|
|
158
|
-
}
|
|
159
|
-
if (command === 'dashboard') {
|
|
160
|
-
return (await import('./commands/dashboard.js')).runDashboard(args, reporter, parsed.lang);
|
|
161
|
-
}
|
|
162
|
-
if (command === 'version-sources') {
|
|
163
|
-
return (await import('./commands/version-sources.js')).runVersionSources(args, reporter, parsed.lang);
|
|
164
|
-
}
|
|
165
|
-
if (command === 'verify') {
|
|
166
|
-
return (await import('./commands/verify.js')).runVerify(args, reporter, parsed.lang);
|
|
167
|
-
}
|
|
168
|
-
if (command === 'explain') {
|
|
169
|
-
return (await import('./commands/explain.js')).runExplain(args, reporter, parsed.lang);
|
|
170
|
-
}
|
|
171
|
-
if (command === 'impact') {
|
|
172
|
-
return (await import('./commands/impact.js')).runImpact(args, reporter, parsed.lang);
|
|
173
|
-
}
|
|
174
|
-
if (command === 'help') {
|
|
175
|
-
return (await import('./commands/help.js')).runHelp(args, reporter, parsed.lang);
|
|
105
|
+
const commandId = command === '--version' || command === '-v' ? 'version' : command;
|
|
106
|
+
const commandDefinition = findCommandDefinition(commandId);
|
|
107
|
+
if (commandDefinition) {
|
|
108
|
+
const runner = await commandDefinition.loadRunner();
|
|
109
|
+
return runner(args, reporter, parsed.lang);
|
|
176
110
|
}
|
|
177
111
|
reporter.stderr(renderCliError(t(parsed.lang, 'cli.error.unknownCommand', { command }), 'mf --help', parsed.lang));
|
|
178
112
|
reporter.stdout(getTopLevelHelp(parsed.lang));
|
|
@@ -3,120 +3,147 @@ export const COMMAND_DEFINITIONS = [
|
|
|
3
3
|
id: 'adapters',
|
|
4
4
|
usage: 'mf adapters',
|
|
5
5
|
summaryKey: 'command.adapters.summary',
|
|
6
|
+
loadRunner: async () => (await import('../commands/adapters.js')).runAdapters,
|
|
6
7
|
},
|
|
7
8
|
{
|
|
8
9
|
id: 'init',
|
|
9
10
|
usage: 'mf init',
|
|
10
11
|
summaryKey: 'command.init.summary',
|
|
12
|
+
loadRunner: async () => (await import('../commands/init.js')).runInit,
|
|
11
13
|
},
|
|
12
14
|
{
|
|
13
15
|
id: 'check',
|
|
14
16
|
usage: 'mf check',
|
|
15
17
|
summaryKey: 'command.check.summary',
|
|
18
|
+
loadRunner: async () => (await import('../commands/check.js')).runCheck,
|
|
16
19
|
},
|
|
17
20
|
{
|
|
18
21
|
id: 'classify',
|
|
19
22
|
usage: 'mf classify',
|
|
20
23
|
summaryKey: 'command.classify.summary',
|
|
24
|
+
loadRunner: async () => (await import('../commands/classify.js')).runClassify,
|
|
21
25
|
},
|
|
22
26
|
{
|
|
23
27
|
id: 'contract-lint',
|
|
24
28
|
usage: 'mf contract-lint',
|
|
25
29
|
summaryKey: 'command.contractLint.summary',
|
|
30
|
+
loadRunner: async () => (await import('../commands/contract-lint.js')).runContractLint,
|
|
26
31
|
},
|
|
27
32
|
{
|
|
28
33
|
id: 'status',
|
|
29
34
|
usage: 'mf status',
|
|
30
35
|
summaryKey: 'command.status.summary',
|
|
36
|
+
loadRunner: async () => (await import('../commands/status.js')).runStatus,
|
|
31
37
|
},
|
|
32
38
|
{
|
|
33
39
|
id: 'update',
|
|
34
40
|
usage: 'mf update',
|
|
35
41
|
summaryKey: 'command.update.summary',
|
|
42
|
+
loadRunner: async () => (await import('../commands/update.js')).runUpdate,
|
|
36
43
|
},
|
|
37
44
|
{
|
|
38
45
|
id: 'upgrade',
|
|
39
46
|
usage: 'mf upgrade',
|
|
40
47
|
summaryKey: 'command.upgrade.summary',
|
|
48
|
+
loadRunner: async () => (await import('../commands/upgrade.js')).runUpgrade,
|
|
41
49
|
},
|
|
42
50
|
{
|
|
43
51
|
id: 'map',
|
|
44
52
|
usage: 'mf map',
|
|
45
53
|
summaryKey: 'command.map.summary',
|
|
54
|
+
loadRunner: async () => (await import('../commands/map.js')).runMap,
|
|
46
55
|
},
|
|
47
56
|
{
|
|
48
57
|
id: 'line-endings',
|
|
49
58
|
usage: 'mf line-endings',
|
|
50
59
|
summaryKey: 'command.lineEndings.summary',
|
|
60
|
+
loadRunner: async () => (await import('../commands/line-endings.js')).runLineEndings,
|
|
51
61
|
},
|
|
52
62
|
{
|
|
53
63
|
id: 'run',
|
|
54
64
|
usage: 'mf run',
|
|
55
65
|
summaryKey: 'command.run.summary',
|
|
66
|
+
loadRunner: async () => (await import('../commands/run.js')).runRun,
|
|
56
67
|
},
|
|
57
68
|
{
|
|
58
69
|
id: 'context',
|
|
59
70
|
usage: 'mf context',
|
|
60
71
|
summaryKey: 'command.context.summary',
|
|
72
|
+
loadRunner: async () => (await import('../commands/context.js')).runContext,
|
|
61
73
|
},
|
|
62
74
|
{
|
|
63
75
|
id: 'doctor',
|
|
64
76
|
usage: 'mf doctor',
|
|
65
77
|
summaryKey: 'command.doctor.summary',
|
|
78
|
+
loadRunner: async () => (await import('../commands/doctor.js')).runDoctor,
|
|
66
79
|
},
|
|
67
80
|
{
|
|
68
81
|
id: 'docs',
|
|
69
82
|
usage: 'mf docs',
|
|
70
83
|
summaryKey: 'command.docs.summary',
|
|
84
|
+
loadRunner: async () => (await import('../commands/docs.js')).runDocs,
|
|
71
85
|
},
|
|
72
86
|
{
|
|
73
87
|
id: 'handoff',
|
|
74
88
|
usage: 'mf handoff',
|
|
75
89
|
summaryKey: 'command.handoff.summary',
|
|
90
|
+
loadRunner: async () => (await import('../commands/handoff.js')).runHandoff,
|
|
76
91
|
},
|
|
77
92
|
{
|
|
78
93
|
id: 'index',
|
|
79
94
|
usage: 'mf index',
|
|
80
95
|
summaryKey: 'command.index.summary',
|
|
96
|
+
loadRunner: async () => (await import('../commands/index.js')).runIndex,
|
|
81
97
|
},
|
|
82
98
|
{
|
|
83
99
|
id: 'search',
|
|
84
100
|
usage: 'mf search',
|
|
85
101
|
summaryKey: 'command.search.summary',
|
|
102
|
+
loadRunner: async () => (await import('../commands/search.js')).runSearch,
|
|
86
103
|
},
|
|
87
104
|
{
|
|
88
105
|
id: 'dashboard',
|
|
89
106
|
usage: 'mf dashboard',
|
|
90
107
|
summaryKey: 'command.dashboard.summary',
|
|
108
|
+
loadRunner: async () => (await import('../commands/dashboard.js')).runDashboard,
|
|
91
109
|
},
|
|
92
110
|
{
|
|
93
111
|
id: 'version',
|
|
94
112
|
usage: 'mf version',
|
|
95
113
|
summaryKey: 'command.version.summary',
|
|
114
|
+
loadRunner: async () => (await import('../commands/version.js')).runVersion,
|
|
96
115
|
},
|
|
97
116
|
{
|
|
98
117
|
id: 'version-sources',
|
|
99
118
|
usage: 'mf version-sources',
|
|
100
119
|
summaryKey: 'command.versionSources.summary',
|
|
120
|
+
loadRunner: async () => (await import('../commands/version-sources.js')).runVersionSources,
|
|
101
121
|
},
|
|
102
122
|
{
|
|
103
123
|
id: 'verify',
|
|
104
124
|
usage: 'mf verify',
|
|
105
125
|
summaryKey: 'command.verify.summary',
|
|
126
|
+
loadRunner: async () => (await import('../commands/verify.js')).runVerify,
|
|
106
127
|
},
|
|
107
128
|
{
|
|
108
129
|
id: 'explain',
|
|
109
130
|
usage: 'mf explain',
|
|
110
131
|
summaryKey: 'command.explain.summary',
|
|
132
|
+
loadRunner: async () => (await import('../commands/explain.js')).runExplain,
|
|
111
133
|
},
|
|
112
134
|
{
|
|
113
135
|
id: 'impact',
|
|
114
136
|
usage: 'mf impact',
|
|
115
137
|
summaryKey: 'command.impact.summary',
|
|
138
|
+
loadRunner: async () => (await import('../commands/impact.js')).runImpact,
|
|
116
139
|
},
|
|
117
140
|
{
|
|
118
141
|
id: 'help',
|
|
119
142
|
usage: 'mf help',
|
|
120
143
|
summaryKey: 'command.help.summary',
|
|
144
|
+
loadRunner: async () => (await import('../commands/help.js')).runHelp,
|
|
121
145
|
},
|
|
122
146
|
];
|
|
147
|
+
export function findCommandDefinition(command) {
|
|
148
|
+
return COMMAND_DEFINITIONS.find((definition) => definition.id === command);
|
|
149
|
+
}
|
|
@@ -4,6 +4,7 @@ import { createDashboardCompletionVerdict } from '../../core/completion-verdict.
|
|
|
4
4
|
import { createDashboardEvidenceModel } from '../../core/verification-evidence.js';
|
|
5
5
|
import { redactSecretLikeText } from '../../core/secret-redaction.js';
|
|
6
6
|
import { ensureFileTargetInsideWithoutSymlinks, ensureInside, toPosixPath, writeUtf8FileInsideWithoutSymlinks, } from './filesystem.js';
|
|
7
|
+
import { safeJsonForInlineScript } from './html-json.js';
|
|
7
8
|
export class DashboardExportPathError extends Error {
|
|
8
9
|
targetPath;
|
|
9
10
|
constructor(targetPath) {
|
|
@@ -634,7 +635,7 @@ export function renderDashboardExportHtml(snapshot) {
|
|
|
634
635
|
const graphSummary = asRecord(harnessVerification.decision_graph_summary);
|
|
635
636
|
const harnessRunHistory = asRecord(harnessReport.run_history);
|
|
636
637
|
const harnessDocsReview = asRecord(harnessReport.docs_review);
|
|
637
|
-
const embeddedJson =
|
|
638
|
+
const embeddedJson = safeJsonForInlineScript(snapshot);
|
|
638
639
|
return `<!doctype html>
|
|
639
640
|
<html lang="en">
|
|
640
641
|
<head>
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { getDashboardLocaleBundle } from '../dashboard-locale.js';
|
|
2
|
+
import { safeJsonForInlineScript } from '../html-json.js';
|
|
2
3
|
export function createDashboardLocaleBootstrap() {
|
|
3
4
|
const localeBundle = getDashboardLocaleBundle();
|
|
4
5
|
return {
|
|
5
|
-
serializedLocaleBundle:
|
|
6
|
-
serializedAvailableLocales:
|
|
6
|
+
serializedLocaleBundle: safeJsonForInlineScript(localeBundle),
|
|
7
|
+
serializedAvailableLocales: safeJsonForInlineScript(localeBundle.locales),
|
|
7
8
|
};
|
|
8
9
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { safeJsonForInlineScript } from '../html-json.js';
|
|
1
2
|
import { renderDashboardClientScript } from './client-script.js';
|
|
2
3
|
import { createDashboardLocaleBootstrap } from './locale-bootstrap.js';
|
|
3
4
|
import { renderDashboardStyles } from './styles.js';
|
|
@@ -12,10 +13,10 @@ function escapeHtml(value) {
|
|
|
12
13
|
export function renderDashboardHtml(snapshot, token, statusSnapshot, docReviewSnapshot) {
|
|
13
14
|
const root = escapeHtml(snapshot.projectRoot);
|
|
14
15
|
const preferencesPath = escapeHtml(snapshot.preferencesPath);
|
|
15
|
-
const serializedSnapshot =
|
|
16
|
-
const serializedStatusSnapshot =
|
|
17
|
-
const serializedDocReviewSnapshot =
|
|
18
|
-
const serializedToken =
|
|
16
|
+
const serializedSnapshot = safeJsonForInlineScript(snapshot);
|
|
17
|
+
const serializedStatusSnapshot = safeJsonForInlineScript(statusSnapshot);
|
|
18
|
+
const serializedDocReviewSnapshot = safeJsonForInlineScript(docReviewSnapshot);
|
|
19
|
+
const serializedToken = safeJsonForInlineScript(token);
|
|
19
20
|
const { serializedLocaleBundle, serializedAvailableLocales } = createDashboardLocaleBootstrap();
|
|
20
21
|
return `<!doctype html>
|
|
21
22
|
<html lang="en">
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
const INLINE_SCRIPT_JSON_ESCAPES = {
|
|
2
|
+
'<': '\\u003C',
|
|
3
|
+
'>': '\\u003E',
|
|
4
|
+
'&': '\\u0026',
|
|
5
|
+
'\u2028': '\\u2028',
|
|
6
|
+
'\u2029': '\\u2029',
|
|
7
|
+
};
|
|
8
|
+
export function safeJsonForInlineScript(value) {
|
|
9
|
+
const json = JSON.stringify(value);
|
|
10
|
+
return (json ?? 'null').replace(/[<>&\u2028\u2029]/gu, (character) => INLINE_SCRIPT_JSON_ESCAPES[character]);
|
|
11
|
+
}
|
|
@@ -284,7 +284,21 @@ function collectCommandIntents(projectRoot) {
|
|
|
284
284
|
}
|
|
285
285
|
return intents;
|
|
286
286
|
}
|
|
287
|
+
function normalizeIndexedFileSourceScope(value) {
|
|
288
|
+
const sourceScope = toSearchString(value);
|
|
289
|
+
if (sourceScope === 'source_anchor' || sourceScope === 'state') {
|
|
290
|
+
return sourceScope;
|
|
291
|
+
}
|
|
292
|
+
return 'workflow';
|
|
293
|
+
}
|
|
287
294
|
function readIndexedFileRecord(projectRoot, relativePath, sourceScope, contentHash = null) {
|
|
295
|
+
const metadata = readIndexedFileMetadataRecord(projectRoot, relativePath, sourceScope);
|
|
296
|
+
return {
|
|
297
|
+
...metadata,
|
|
298
|
+
contentHash: contentHash ?? sha256Bytes(readFileSync(path.join(projectRoot, ...relativePath.split('/')))),
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
function readIndexedFileMetadataRecord(projectRoot, relativePath, sourceScope) {
|
|
288
302
|
const fullPath = path.join(projectRoot, ...relativePath.split('/'));
|
|
289
303
|
const stats = statSync(fullPath);
|
|
290
304
|
return {
|
|
@@ -292,7 +306,6 @@ function readIndexedFileRecord(projectRoot, relativePath, sourceScope, contentHa
|
|
|
292
306
|
sourceScope,
|
|
293
307
|
sizeBytes: stats.size,
|
|
294
308
|
mtimeMs: Math.round(stats.mtimeMs),
|
|
295
|
-
contentHash: contentHash ?? sha256Bytes(readFileSync(fullPath)),
|
|
296
309
|
};
|
|
297
310
|
}
|
|
298
311
|
function collectIndexedFileRecords(projectRoot, documents, sourceAnchors) {
|
|
@@ -305,8 +318,21 @@ function collectIndexedFileRecords(projectRoot, documents, sourceAnchors) {
|
|
|
305
318
|
records.set(anchorPath, readIndexedFileRecord(projectRoot, anchorPath, 'source_anchor'));
|
|
306
319
|
}
|
|
307
320
|
}
|
|
321
|
+
if (existsSync(path.join(projectRoot, ...LATEST_RUN_STATE_RELATIVE_PATH.split('/')))) {
|
|
322
|
+
records.set(LATEST_RUN_STATE_RELATIVE_PATH, readIndexedFileRecord(projectRoot, LATEST_RUN_STATE_RELATIVE_PATH, 'state'));
|
|
323
|
+
}
|
|
308
324
|
return [...records.values()].sort((left, right) => left.path.localeCompare(right.path));
|
|
309
325
|
}
|
|
326
|
+
function collectFastPreflightIndexedFileMetadataRecords(projectRoot, includeSource) {
|
|
327
|
+
if (includeSource) {
|
|
328
|
+
return null;
|
|
329
|
+
}
|
|
330
|
+
const records = getExistingIndexablePaths(projectRoot).map((relativePath) => readIndexedFileMetadataRecord(projectRoot, relativePath, 'workflow'));
|
|
331
|
+
if (existsSync(path.join(projectRoot, ...LATEST_RUN_STATE_RELATIVE_PATH.split('/')))) {
|
|
332
|
+
records.push(readIndexedFileMetadataRecord(projectRoot, LATEST_RUN_STATE_RELATIVE_PATH, 'state'));
|
|
333
|
+
}
|
|
334
|
+
return records.sort((left, right) => left.path.localeCompare(right.path));
|
|
335
|
+
}
|
|
310
336
|
function normalizeSearchText(value) {
|
|
311
337
|
return value.trim().replace(/\s+/g, ' ');
|
|
312
338
|
}
|
|
@@ -1948,6 +1974,86 @@ function populateDatabase(database, capabilities, documents, skills, skillRoutes
|
|
|
1948
1974
|
populatePathSurfaceReadModel(database);
|
|
1949
1975
|
populateSearchTables(database, capabilities, documents, skills, skillRoutes, commandIntents, sourceAnchors);
|
|
1950
1976
|
}
|
|
1977
|
+
function readCount(database, tableName) {
|
|
1978
|
+
if (!hasTable(database, tableName)) {
|
|
1979
|
+
return 0;
|
|
1980
|
+
}
|
|
1981
|
+
const [row] = queryRows(database, `SELECT COUNT(*) AS count FROM ${tableName}`);
|
|
1982
|
+
const count = row?.count;
|
|
1983
|
+
return typeof count === 'number' && Number.isFinite(count) ? count : 0;
|
|
1984
|
+
}
|
|
1985
|
+
function readStoredIndexedPaths(database) {
|
|
1986
|
+
if (!hasTable(database, 'documents')) {
|
|
1987
|
+
return [];
|
|
1988
|
+
}
|
|
1989
|
+
return queryRows(database, 'SELECT path FROM documents ORDER BY path')
|
|
1990
|
+
.map((row) => toSearchString(row.path))
|
|
1991
|
+
.filter(Boolean);
|
|
1992
|
+
}
|
|
1993
|
+
function createStoredLocalIndexResult(projectRoot, databasePath, dryRun, indexMode, database, capabilities) {
|
|
1994
|
+
return {
|
|
1995
|
+
schema_version: LOCAL_INDEX_SCHEMA_VERSION,
|
|
1996
|
+
command: 'index',
|
|
1997
|
+
ok: true,
|
|
1998
|
+
mustflow_root: path.resolve(projectRoot),
|
|
1999
|
+
database_path: databasePath,
|
|
2000
|
+
dry_run: dryRun,
|
|
2001
|
+
wrote_files: false,
|
|
2002
|
+
index_mode: indexMode,
|
|
2003
|
+
reused_existing: true,
|
|
2004
|
+
rebuild_reason: null,
|
|
2005
|
+
document_count: readCount(database, 'documents'),
|
|
2006
|
+
skill_count: readCount(database, 'skills'),
|
|
2007
|
+
skill_route_count: readCount(database, 'skill_routes'),
|
|
2008
|
+
command_intent_count: readCount(database, 'command_intents'),
|
|
2009
|
+
command_effect_count: readCount(database, 'command_effects'),
|
|
2010
|
+
verification_evidence_summary_count: readCount(database, 'verification_evidence_summaries'),
|
|
2011
|
+
verification_plan_count: readCount(database, 'verification_plans'),
|
|
2012
|
+
acceptance_criteria_count: readCount(database, 'acceptance_criteria'),
|
|
2013
|
+
criterion_coverage_count: readCount(database, 'criterion_coverage'),
|
|
2014
|
+
verification_receipt_summary_count: readCount(database, 'verification_receipt_summaries'),
|
|
2015
|
+
command_receipt_summary_count: readCount(database, 'command_receipt_summaries'),
|
|
2016
|
+
verification_coverage_state_count: readCount(database, 'verification_coverage_states'),
|
|
2017
|
+
verification_risk_signal_count: readCount(database, 'verification_risk_signals'),
|
|
2018
|
+
validation_ratchet_signal_count: readCount(database, 'validation_ratchet_signals'),
|
|
2019
|
+
completion_verdict_summary_count: readCount(database, 'completion_verdict_summaries'),
|
|
2020
|
+
repro_route_count: readCount(database, 'repro_routes'),
|
|
2021
|
+
repro_observation_count: readCount(database, 'repro_observations'),
|
|
2022
|
+
failure_fingerprint_count: readCount(database, 'verification_failure_fingerprints'),
|
|
2023
|
+
source_index_enabled: readMetadataValue(database, 'source_index_enabled') === 'true',
|
|
2024
|
+
source_anchor_count: readCount(database, 'source_anchors'),
|
|
2025
|
+
source_anchor_risk_signal_count: readCount(database, 'source_anchor_risk_signals'),
|
|
2026
|
+
search_backend: capabilities.backend,
|
|
2027
|
+
search_fts5_available: capabilities.fts5Available,
|
|
2028
|
+
content_mode: LOCAL_INDEX_CONTENT_MODE,
|
|
2029
|
+
store_full_content: LOCAL_INDEX_STORE_FULL_CONTENT,
|
|
2030
|
+
max_snippet_bytes_per_document: MAX_SNIPPET_BYTES_PER_DOCUMENT,
|
|
2031
|
+
excluded_raw_data_kinds: [...LOCAL_INDEX_EXCLUDED_RAW_DATA_KINDS],
|
|
2032
|
+
indexed_file_count: readCount(database, 'indexed_files'),
|
|
2033
|
+
indexed_paths: readStoredIndexedPaths(database),
|
|
2034
|
+
};
|
|
2035
|
+
}
|
|
2036
|
+
function indexedFileMetadataMatch(database, currentFiles) {
|
|
2037
|
+
const rows = queryRows(database, 'SELECT path, source_scope, size_bytes, mtime_ms, parser_version FROM indexed_files ORDER BY path');
|
|
2038
|
+
if (rows.length !== currentFiles.length) {
|
|
2039
|
+
return false;
|
|
2040
|
+
}
|
|
2041
|
+
const currentByPath = new Map(currentFiles.map((file) => [file.path, file]));
|
|
2042
|
+
for (const row of rows) {
|
|
2043
|
+
const storedPath = toSearchString(row.path);
|
|
2044
|
+
const current = currentByPath.get(storedPath);
|
|
2045
|
+
if (!current) {
|
|
2046
|
+
return false;
|
|
2047
|
+
}
|
|
2048
|
+
if (normalizeIndexedFileSourceScope(row.source_scope) !== current.sourceScope ||
|
|
2049
|
+
row.size_bytes !== current.sizeBytes ||
|
|
2050
|
+
row.mtime_ms !== current.mtimeMs ||
|
|
2051
|
+
toSearchString(row.parser_version) !== LOCAL_INDEX_PARSER_VERSION) {
|
|
2052
|
+
return false;
|
|
2053
|
+
}
|
|
2054
|
+
}
|
|
2055
|
+
return true;
|
|
2056
|
+
}
|
|
1951
2057
|
function indexedFilesMatch(database, currentFiles) {
|
|
1952
2058
|
const rows = queryRows(database, 'SELECT path, source_scope, content_hash, parser_version FROM indexed_files ORDER BY path');
|
|
1953
2059
|
if (rows.length !== currentFiles.length) {
|
|
@@ -1960,7 +2066,7 @@ function indexedFilesMatch(database, currentFiles) {
|
|
|
1960
2066
|
if (!current) {
|
|
1961
2067
|
return false;
|
|
1962
2068
|
}
|
|
1963
|
-
if (
|
|
2069
|
+
if (normalizeIndexedFileSourceScope(row.source_scope) !== current.sourceScope ||
|
|
1964
2070
|
toSearchString(row.content_hash) !== current.contentHash ||
|
|
1965
2071
|
toSearchString(row.parser_version) !== LOCAL_INDEX_PARSER_VERSION) {
|
|
1966
2072
|
return false;
|
|
@@ -1968,6 +2074,44 @@ function indexedFilesMatch(database, currentFiles) {
|
|
|
1968
2074
|
}
|
|
1969
2075
|
return true;
|
|
1970
2076
|
}
|
|
2077
|
+
async function readIncrementalPreflightReuse(SQL, databasePath, projectRoot, currentFiles, sourceScopeHash, dryRun, indexMode) {
|
|
2078
|
+
if (!currentFiles) {
|
|
2079
|
+
return { result: null, rebuildReason: null };
|
|
2080
|
+
}
|
|
2081
|
+
if (!existsSync(databasePath)) {
|
|
2082
|
+
return { result: null, rebuildReason: 'missing_index' };
|
|
2083
|
+
}
|
|
2084
|
+
let database;
|
|
2085
|
+
try {
|
|
2086
|
+
database = new SQL.Database(readFileSync(databasePath));
|
|
2087
|
+
if (readStoredSchemaVersion(database) !== LOCAL_INDEX_SCHEMA_VERSION) {
|
|
2088
|
+
return { result: null, rebuildReason: 'schema_version_mismatch' };
|
|
2089
|
+
}
|
|
2090
|
+
if (readMetadataValue(database, 'parser_version') !== LOCAL_INDEX_PARSER_VERSION) {
|
|
2091
|
+
return { result: null, rebuildReason: 'parser_version_mismatch' };
|
|
2092
|
+
}
|
|
2093
|
+
if (readMetadataValue(database, 'source_scope_hash') !== sourceScopeHash) {
|
|
2094
|
+
return { result: null, rebuildReason: 'source_scope_mismatch' };
|
|
2095
|
+
}
|
|
2096
|
+
if (!hasTable(database, 'indexed_files')) {
|
|
2097
|
+
return { result: null, rebuildReason: 'indexed_files_missing' };
|
|
2098
|
+
}
|
|
2099
|
+
if (!indexedFileMetadataMatch(database, currentFiles)) {
|
|
2100
|
+
return { result: null, rebuildReason: 'file_fingerprint_mismatch' };
|
|
2101
|
+
}
|
|
2102
|
+
const capabilities = readStoredSearchCapabilities(database);
|
|
2103
|
+
return {
|
|
2104
|
+
result: createStoredLocalIndexResult(projectRoot, databasePath, dryRun, indexMode, database, capabilities),
|
|
2105
|
+
rebuildReason: null,
|
|
2106
|
+
};
|
|
2107
|
+
}
|
|
2108
|
+
catch {
|
|
2109
|
+
return { result: null, rebuildReason: 'unreadable_index' };
|
|
2110
|
+
}
|
|
2111
|
+
finally {
|
|
2112
|
+
database?.close();
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
1971
2115
|
async function readIncrementalReuseDecision(SQL, databasePath, currentFiles, sourceScopeHash) {
|
|
1972
2116
|
if (!existsSync(databasePath)) {
|
|
1973
2117
|
return { reusable: false, rebuildReason: 'missing_index', capabilities: null };
|
|
@@ -2015,13 +2159,28 @@ export async function createLocalIndex(projectRoot, options = {}) {
|
|
|
2015
2159
|
const dryRun = options.dryRun === true;
|
|
2016
2160
|
const incremental = options.incremental === true;
|
|
2017
2161
|
const indexMode = incremental ? 'incremental' : 'full';
|
|
2162
|
+
const sourceConfig = readLocalIndexSourceConfig(projectRoot);
|
|
2163
|
+
const includeSource = options.includeSource === true || sourceConfig.enabledByDefault;
|
|
2164
|
+
const sourceScopeHash = getSourceScopeHash(includeSource, sourceConfig);
|
|
2165
|
+
let capabilities = searchCapabilities(false);
|
|
2166
|
+
let reusedExisting = false;
|
|
2167
|
+
let rebuildReason = null;
|
|
2168
|
+
const SQL = await loadSqlJs();
|
|
2169
|
+
const capabilityDatabase = new SQL.Database();
|
|
2170
|
+
capabilities = detectLocalSearchCapabilities(capabilityDatabase);
|
|
2171
|
+
capabilityDatabase.close();
|
|
2172
|
+
if (incremental) {
|
|
2173
|
+
const preflightFiles = collectFastPreflightIndexedFileMetadataRecords(projectRoot, includeSource);
|
|
2174
|
+
const preflightReuse = await readIncrementalPreflightReuse(SQL, databasePath, projectRoot, preflightFiles, sourceScopeHash, dryRun, indexMode);
|
|
2175
|
+
if (preflightReuse.result) {
|
|
2176
|
+
return preflightReuse.result;
|
|
2177
|
+
}
|
|
2178
|
+
rebuildReason = preflightReuse.rebuildReason;
|
|
2179
|
+
}
|
|
2018
2180
|
const documents = collectDocuments(projectRoot);
|
|
2019
2181
|
const skills = collectSkills(documents);
|
|
2020
2182
|
const skillRoutes = collectSkillRoutes(projectRoot);
|
|
2021
2183
|
const commandIntents = collectCommandIntents(projectRoot);
|
|
2022
|
-
const sourceConfig = readLocalIndexSourceConfig(projectRoot);
|
|
2023
|
-
const includeSource = options.includeSource === true || sourceConfig.enabledByDefault;
|
|
2024
|
-
const sourceScopeHash = getSourceScopeHash(includeSource, sourceConfig);
|
|
2025
2184
|
const previousSourceAnchors = includeSource
|
|
2026
2185
|
? await readPreviousSourceAnchorSnapshots(databasePath).catch(() => [])
|
|
2027
2186
|
: [];
|
|
@@ -2033,17 +2192,10 @@ export async function createLocalIndex(projectRoot, options = {}) {
|
|
|
2033
2192
|
: [];
|
|
2034
2193
|
const verificationEvidence = createVerificationEvidenceIndex(projectRoot);
|
|
2035
2194
|
const indexedFiles = collectIndexedFileRecords(projectRoot, documents, sourceAnchors);
|
|
2036
|
-
let capabilities = searchCapabilities(false);
|
|
2037
|
-
let reusedExisting = false;
|
|
2038
|
-
let rebuildReason = null;
|
|
2039
|
-
const SQL = await loadSqlJs();
|
|
2040
|
-
const capabilityDatabase = new SQL.Database();
|
|
2041
|
-
capabilities = detectLocalSearchCapabilities(capabilityDatabase);
|
|
2042
|
-
capabilityDatabase.close();
|
|
2043
2195
|
if (incremental) {
|
|
2044
2196
|
const reuseDecision = await readIncrementalReuseDecision(SQL, databasePath, indexedFiles, sourceScopeHash);
|
|
2045
2197
|
reusedExisting = reuseDecision.reusable;
|
|
2046
|
-
rebuildReason = reuseDecision.rebuildReason;
|
|
2198
|
+
rebuildReason = reuseDecision.rebuildReason ?? rebuildReason;
|
|
2047
2199
|
capabilities = reuseDecision.capabilities ?? capabilities;
|
|
2048
2200
|
}
|
|
2049
2201
|
if (!dryRun && !reusedExisting) {
|
|
@@ -2110,7 +2262,7 @@ function getStalePaths(projectRoot, database) {
|
|
|
2110
2262
|
const indexedPaths = new Set(indexedRows.map((row) => toSearchString(row.path)));
|
|
2111
2263
|
for (const row of indexedRows) {
|
|
2112
2264
|
const indexedPath = toSearchString(row.path);
|
|
2113
|
-
const sourceScope =
|
|
2265
|
+
const sourceScope = normalizeIndexedFileSourceScope(row.source_scope);
|
|
2114
2266
|
try {
|
|
2115
2267
|
const current = readIndexedFileRecord(projectRoot, indexedPath, sourceScope);
|
|
2116
2268
|
if (current.contentHash !== toSearchString(row.content_hash)) {
|
package/dist/cli/lib/run-plan.js
CHANGED
|
@@ -78,6 +78,9 @@ function readEffectiveMaxOutputBytes(contract, intent) {
|
|
|
78
78
|
readPositiveInteger(contract.defaults, 'max_output_bytes') ??
|
|
79
79
|
DEFAULT_COMMAND_MAX_OUTPUT_BYTES;
|
|
80
80
|
}
|
|
81
|
+
function readEffectiveKillAfterSeconds(contract) {
|
|
82
|
+
return readPositiveInteger(contract.defaults, 'kill_after_seconds') ?? 5;
|
|
83
|
+
}
|
|
81
84
|
function getMaxOutputBytesLimitDetail(contract, intent) {
|
|
82
85
|
const intentValue = readPositiveInteger(intent, 'max_output_bytes');
|
|
83
86
|
if (intentValue !== undefined) {
|
|
@@ -103,6 +106,7 @@ function readRunIntentMetadata(contract, intent) {
|
|
|
103
106
|
kind: readString(intent, 'kind') ?? null,
|
|
104
107
|
configuredCwd,
|
|
105
108
|
timeoutSeconds: readPositiveInteger(intent, 'timeout_seconds') ?? null,
|
|
109
|
+
killAfterSeconds: readEffectiveKillAfterSeconds(contract),
|
|
106
110
|
maxOutputBytes: readEffectiveMaxOutputBytes(contract, intent),
|
|
107
111
|
successExitCodes: getSuccessExitCodes(intent),
|
|
108
112
|
commandArgv,
|
|
@@ -138,6 +142,7 @@ function createBlockedRunPlan(contract, intentName, intent, eligibility, reasonC
|
|
|
138
142
|
cwd: null,
|
|
139
143
|
relativeCwd: null,
|
|
140
144
|
timeoutSeconds: metadata?.timeoutSeconds ?? null,
|
|
145
|
+
killAfterSeconds: metadata?.killAfterSeconds ?? null,
|
|
141
146
|
maxOutputBytes: metadata?.maxOutputBytes ?? null,
|
|
142
147
|
successExitCodes: metadata?.successExitCodes ?? null,
|
|
143
148
|
commandArgv: metadata?.commandArgv,
|
|
@@ -199,6 +204,7 @@ export function createRunPlan(projectRoot, contract, intentName, options = {}) {
|
|
|
199
204
|
cwd,
|
|
200
205
|
relativeCwd: getRelativeProjectPath(projectRoot, cwd),
|
|
201
206
|
timeoutSeconds: metadata.timeoutSeconds,
|
|
207
|
+
killAfterSeconds: metadata.killAfterSeconds,
|
|
202
208
|
maxOutputBytes: metadata.maxOutputBytes,
|
|
203
209
|
successExitCodes: metadata.successExitCodes,
|
|
204
210
|
commandArgv,
|
|
@@ -15,6 +15,7 @@ const CHECK_ISSUE_ID_RULES = [
|
|
|
15
15
|
['mustflow.command_contract.effect_path_escape', /^Strict: Command effect path must stay inside the current root:/u],
|
|
16
16
|
['mustflow.command_contract.shared_writes_without_effects', /^Strict warning: configured agent-runnable intents .+ share path:.+ through writes without explicit effects or resource locks$/u],
|
|
17
17
|
['mustflow.command_contract.broad_env_inheritance', /^Strict warning: configured agent-runnable intent [^\s]+ (?:implicitly inherits the host environment|uses env_policy = "inherit")/u],
|
|
18
|
+
['mustflow.command_contract.project_local_bin_bare_executable', /^Strict warning: configured agent-runnable intent [^\s]+ uses bare executable "[^"]+" that matches project-local node_modules\/\.bin/u],
|
|
18
19
|
['mustflow.prompt_cache.required', /^Strict: \[prompt_cache\] table is required$/u],
|
|
19
20
|
['mustflow.prompt_cache.volatile_in_stable', /^Strict: \[prompt_cache\.layers\.stable\]\.read must not include volatile path /u],
|
|
20
21
|
['mustflow.refresh.hash_method_required', /^Strict: \[refresh\]\.default_method should be "hash_check" for cache-friendly refresh$/u],
|
|
@@ -45,9 +45,6 @@ export function commandIntentHasCommandSource(intent) {
|
|
|
45
45
|
export function shellCommandHasBlockedBackgroundPattern(command) {
|
|
46
46
|
return BACKGROUND_SHELL_PATTERNS.some((pattern) => pattern.test(command));
|
|
47
47
|
}
|
|
48
|
-
export function commandIntentHasBlockedShellBackgroundPattern(intent) {
|
|
49
|
-
return intent.mode === 'shell' && typeof intent.cmd === 'string' && shellCommandHasBlockedBackgroundPattern(intent.cmd);
|
|
50
|
-
}
|
|
51
48
|
function normalizeExecutableName(value) {
|
|
52
49
|
return path.basename(value).replace(/\.(?:cmd|exe|ps1)$/iu, '').toLowerCase();
|
|
53
50
|
}
|