mustflow 2.23.0 → 2.24.2
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 +12 -2
- package/dist/cli/commands/adapters.js +11 -9
- package/dist/cli/commands/api.js +263 -113
- package/dist/cli/commands/check.js +11 -7
- package/dist/cli/commands/classify.js +16 -42
- package/dist/cli/commands/context.js +18 -31
- package/dist/cli/commands/contract-lint.js +12 -7
- package/dist/cli/commands/dashboard.js +65 -114
- package/dist/cli/commands/docs.js +43 -26
- package/dist/cli/commands/doctor.js +11 -7
- package/dist/cli/commands/evidence.js +642 -0
- package/dist/cli/commands/explain-verify.js +1 -59
- package/dist/cli/commands/explain.js +84 -36
- package/dist/cli/commands/handoff.js +13 -17
- package/dist/cli/commands/impact.js +14 -20
- package/dist/cli/commands/index.js +15 -9
- package/dist/cli/commands/init.js +56 -70
- package/dist/cli/commands/line-endings.js +15 -9
- package/dist/cli/commands/map.js +30 -42
- package/dist/cli/commands/next.js +300 -0
- package/dist/cli/commands/onboard.js +136 -0
- package/dist/cli/commands/run.js +47 -42
- package/dist/cli/commands/search.js +43 -69
- package/dist/cli/commands/status.js +9 -6
- package/dist/cli/commands/update.js +16 -10
- package/dist/cli/commands/upgrade.js +9 -6
- package/dist/cli/commands/verify/args.js +55 -249
- package/dist/cli/commands/verify.js +2 -1
- package/dist/cli/commands/version-sources.js +9 -6
- package/dist/cli/commands/version.js +9 -6
- package/dist/cli/commands/workspace.js +564 -0
- package/dist/cli/i18n/en.js +60 -1
- package/dist/cli/i18n/es.js +60 -1
- package/dist/cli/i18n/fr.js +60 -1
- package/dist/cli/i18n/hi.js +60 -1
- package/dist/cli/i18n/ko.js +60 -1
- package/dist/cli/i18n/zh.js +60 -1
- package/dist/cli/index.js +28 -25
- package/dist/cli/lib/agent-context.js +8 -9
- package/dist/cli/lib/command-registry.js +24 -0
- package/dist/cli/lib/dashboard-html/client-script.js +1 -1
- package/dist/cli/lib/local-index/database-path.js +5 -0
- package/dist/cli/lib/local-index/database-read.js +88 -0
- package/dist/cli/lib/local-index/effect-graph-read-model.js +112 -0
- package/dist/cli/lib/local-index/freshness.js +60 -0
- package/dist/cli/lib/local-index/index.js +12 -1866
- package/dist/cli/lib/local-index/path-surface-read-model.js +134 -0
- package/dist/cli/lib/local-index/populate.js +474 -0
- package/dist/cli/lib/local-index/schema.js +413 -0
- package/dist/cli/lib/local-index/search-read-model.js +533 -0
- package/dist/cli/lib/local-index/search-text.js +79 -0
- package/dist/cli/lib/option-parser.js +93 -0
- package/dist/cli/lib/repo-map.js +2 -2
- package/dist/cli/lib/run-plan.js +5 -22
- package/dist/core/change-verification.js +11 -5
- package/dist/core/command-effects.js +1 -3
- package/dist/core/command-intent-eligibility.js +14 -0
- package/dist/core/command-preconditions.js +8 -4
- package/dist/core/command-run-constraints.js +43 -0
- package/dist/core/public-json-contracts.js +57 -0
- package/dist/core/test-selection.js +8 -2
- package/dist/core/verification-plan.js +32 -4
- package/package.json +1 -1
- package/schemas/README.md +16 -0
- package/schemas/api-serve-response.schema.json +89 -0
- package/schemas/change-verification-report.schema.json +4 -1
- package/schemas/contract-lint-report.schema.json +1 -0
- package/schemas/evidence-report.schema.json +287 -0
- package/schemas/explain-report.schema.json +4 -0
- package/schemas/next-report.schema.json +121 -0
- package/schemas/onboard-commands-report.schema.json +100 -0
- package/schemas/workspace-command-catalog.schema.json +172 -0
- package/schemas/workspace-status.schema.json +141 -0
- package/schemas/workspace-verification-plan.schema.json +195 -0
- package/templates/default/manifest.toml +1 -1
|
@@ -0,0 +1,564 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { createClassifyOutput } from './classify.js';
|
|
3
|
+
import { createChangeVerificationReport, } from '../../core/change-verification.js';
|
|
4
|
+
import { createVerificationPlanId } from '../../core/verification-plan-id.js';
|
|
5
|
+
import { printUsageError, renderHelp } from '../lib/cli-output.js';
|
|
6
|
+
import { t } from '../lib/i18n.js';
|
|
7
|
+
import { formatCliOptionParseError, hasCliOptionToken, hasParsedCliOption, parseCliOptions, } from '../lib/option-parser.js';
|
|
8
|
+
import { resolveMustflowRoot } from '../lib/project-root.js';
|
|
9
|
+
import { getRepoMapConfig, discoverNestedRepositories } from '../lib/repo-map.js';
|
|
10
|
+
import { createRunPlan } from '../lib/run-plan.js';
|
|
11
|
+
import { readCommandContract, readString, readStringArray } from '../../core/config-loading.js';
|
|
12
|
+
const WORKSPACE_STATUS_SCHEMA_VERSION = '1';
|
|
13
|
+
const WORKSPACE_COMMAND_CATALOG_SCHEMA_VERSION = '1';
|
|
14
|
+
const WORKSPACE_VERIFICATION_PLAN_SCHEMA_VERSION = '1';
|
|
15
|
+
const WORKSPACE_STATUS_OPTIONS = [{ name: '--json', kind: 'boolean' }];
|
|
16
|
+
const WORKSPACE_COMMAND_CATALOG_OPTIONS = [{ name: '--json', kind: 'boolean' }];
|
|
17
|
+
const WORKSPACE_VERIFY_OPTIONS = [
|
|
18
|
+
{ name: '--changed', kind: 'boolean' },
|
|
19
|
+
{ name: '--plan-only', kind: 'boolean' },
|
|
20
|
+
{ name: '--json', kind: 'boolean' },
|
|
21
|
+
];
|
|
22
|
+
function getWorkspaceHelp(lang = 'en') {
|
|
23
|
+
return renderHelp({
|
|
24
|
+
usage: 'mf workspace <action> [options]',
|
|
25
|
+
summary: t(lang, 'workspace.help.summary'),
|
|
26
|
+
commands: [
|
|
27
|
+
{ label: 'status', description: t(lang, 'workspace.help.action.status') },
|
|
28
|
+
{ label: 'command-catalog', description: t(lang, 'workspace.help.action.commandCatalog') },
|
|
29
|
+
{ label: 'verify', description: t(lang, 'workspace.help.action.verify') },
|
|
30
|
+
],
|
|
31
|
+
options: [
|
|
32
|
+
{ label: '--changed', description: t(lang, 'classify.help.option.changed') },
|
|
33
|
+
{ label: '--plan-only', description: t(lang, 'verify.help.option.planOnly') },
|
|
34
|
+
{ label: '--json', description: t(lang, 'cli.option.json') },
|
|
35
|
+
{ label: '-h, --help', description: t(lang, 'cli.option.help') },
|
|
36
|
+
],
|
|
37
|
+
examples: [
|
|
38
|
+
'mf workspace status --json',
|
|
39
|
+
'mf workspace command-catalog --json',
|
|
40
|
+
'mf workspace verify --changed --plan-only --json',
|
|
41
|
+
],
|
|
42
|
+
exitCodes: [
|
|
43
|
+
{ label: '0', description: t(lang, 'workspace.help.exit.ok') },
|
|
44
|
+
{ label: '1', description: t(lang, 'cli.common.invalidInput') },
|
|
45
|
+
],
|
|
46
|
+
}, lang);
|
|
47
|
+
}
|
|
48
|
+
function createWorkspaceStatusPolicy() {
|
|
49
|
+
return {
|
|
50
|
+
mode: 'read_only',
|
|
51
|
+
grants_command_authority: false,
|
|
52
|
+
parent_root_grants_child_authority: false,
|
|
53
|
+
command_authority_per_root: '.mustflow/config/commands.toml',
|
|
54
|
+
run_entrypoint_per_root: 'mf run <intent>',
|
|
55
|
+
executes_commands: false,
|
|
56
|
+
raw_commands_included: false,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function createWorkspaceVerificationPlanPolicy() {
|
|
60
|
+
return {
|
|
61
|
+
...createWorkspaceStatusPolicy(),
|
|
62
|
+
plan_command_per_root: 'mf verify --changed --plan-only --json',
|
|
63
|
+
selected_intents_run_via: 'mf run <intent>',
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function getIntentNames(intents) {
|
|
67
|
+
return Object.keys(intents).sort((left, right) => left.localeCompare(right));
|
|
68
|
+
}
|
|
69
|
+
function summarizeCommandSurface(repositoryRoot, repository) {
|
|
70
|
+
if (!repository.commandContract) {
|
|
71
|
+
return {
|
|
72
|
+
path: null,
|
|
73
|
+
exists: false,
|
|
74
|
+
parse_error: null,
|
|
75
|
+
total_intents: null,
|
|
76
|
+
runnable_count: null,
|
|
77
|
+
runnable_intents: [],
|
|
78
|
+
blocked_count: null,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
const contract = readCommandContract(repositoryRoot);
|
|
83
|
+
const intentNames = getIntentNames(contract.intents);
|
|
84
|
+
const runnableIntents = intentNames.filter((intentName) => createRunPlan(repositoryRoot, contract, intentName).ok);
|
|
85
|
+
return {
|
|
86
|
+
path: repository.commandContract,
|
|
87
|
+
exists: true,
|
|
88
|
+
parse_error: null,
|
|
89
|
+
total_intents: intentNames.length,
|
|
90
|
+
runnable_count: runnableIntents.length,
|
|
91
|
+
runnable_intents: runnableIntents,
|
|
92
|
+
blocked_count: Math.max(0, intentNames.length - runnableIntents.length),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
return {
|
|
97
|
+
path: repository.commandContract,
|
|
98
|
+
exists: true,
|
|
99
|
+
parse_error: error instanceof Error ? error.message : String(error),
|
|
100
|
+
total_intents: null,
|
|
101
|
+
runnable_count: null,
|
|
102
|
+
runnable_intents: [],
|
|
103
|
+
blocked_count: null,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function repositoryStatus(commandSurface) {
|
|
108
|
+
if (!commandSurface.exists) {
|
|
109
|
+
return 'contract_missing';
|
|
110
|
+
}
|
|
111
|
+
if (commandSurface.parse_error) {
|
|
112
|
+
return 'contract_invalid';
|
|
113
|
+
}
|
|
114
|
+
return 'mustflow_ready';
|
|
115
|
+
}
|
|
116
|
+
function summarizeRepository(projectRoot, repository) {
|
|
117
|
+
const repositoryRoot = path.resolve(projectRoot, repository.relativePath);
|
|
118
|
+
const commandSurface = summarizeCommandSurface(repositoryRoot, repository);
|
|
119
|
+
const issues = commandSurface.parse_error ? [commandSurface.parse_error] : [];
|
|
120
|
+
return {
|
|
121
|
+
relative_path: repository.relativePath,
|
|
122
|
+
status: repositoryStatus(commandSurface),
|
|
123
|
+
git_repository: true,
|
|
124
|
+
mustflow: repository.mustflow,
|
|
125
|
+
agent_rules: repository.agentRules ?? null,
|
|
126
|
+
repo_map: repository.repoMap ?? null,
|
|
127
|
+
mustflow_config: repository.mustflowConfig ?? null,
|
|
128
|
+
command_contract: commandSurface,
|
|
129
|
+
context_index: repository.contextIndex ?? null,
|
|
130
|
+
skill_index: repository.skillIndex ?? null,
|
|
131
|
+
root_document_count: repository.rootDocuments.length,
|
|
132
|
+
machine_contract_count: repository.machineContracts.length,
|
|
133
|
+
manifest_count: repository.manifests.length,
|
|
134
|
+
command_adapter_count: repository.commandAdapters.length,
|
|
135
|
+
editing_policy_count: repository.editingPolicies.length,
|
|
136
|
+
issues,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
function createWorkspaceStatusOutput() {
|
|
140
|
+
const projectRoot = resolveMustflowRoot();
|
|
141
|
+
const config = getRepoMapConfig(projectRoot);
|
|
142
|
+
const nestedRepositories = discoverNestedRepositories(projectRoot, { ...config.map, includeNested: true }, config.workspace);
|
|
143
|
+
const repositories = nestedRepositories.map((repository) => summarizeRepository(projectRoot, repository));
|
|
144
|
+
const issues = config.workspace.enabled && config.workspace.roots.length > 0 && repositories.length === 0
|
|
145
|
+
? ['No nested git repositories were discovered under configured workspace roots.']
|
|
146
|
+
: [];
|
|
147
|
+
return {
|
|
148
|
+
schema_version: WORKSPACE_STATUS_SCHEMA_VERSION,
|
|
149
|
+
command: 'workspace status',
|
|
150
|
+
mustflow_root: projectRoot,
|
|
151
|
+
workspace: {
|
|
152
|
+
enabled: config.workspace.enabled,
|
|
153
|
+
roots: config.workspace.roots,
|
|
154
|
+
max_depth: config.workspace.maxDepth,
|
|
155
|
+
max_repositories: config.workspace.maxRepositories,
|
|
156
|
+
follow_symlinks: config.workspace.followSymlinks,
|
|
157
|
+
stop_at_repository_root: config.workspace.stopAtRepositoryRoot,
|
|
158
|
+
},
|
|
159
|
+
policy: createWorkspaceStatusPolicy(),
|
|
160
|
+
repository_count: repositories.length,
|
|
161
|
+
repositories,
|
|
162
|
+
issues,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
function readWorkspaceRepositories(projectRoot) {
|
|
166
|
+
const config = getRepoMapConfig(projectRoot);
|
|
167
|
+
return {
|
|
168
|
+
config,
|
|
169
|
+
repositories: discoverNestedRepositories(projectRoot, { ...config.map, includeNested: true }, config.workspace),
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
function workspaceConfigOutput(config) {
|
|
173
|
+
return {
|
|
174
|
+
enabled: config.workspace.enabled,
|
|
175
|
+
roots: config.workspace.roots,
|
|
176
|
+
max_depth: config.workspace.maxDepth,
|
|
177
|
+
max_repositories: config.workspace.maxRepositories,
|
|
178
|
+
follow_symlinks: config.workspace.followSymlinks,
|
|
179
|
+
stop_at_repository_root: config.workspace.stopAtRepositoryRoot,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
function createWorkspaceIssues(config, repositoryCount) {
|
|
183
|
+
return config.workspace.enabled && config.workspace.roots.length > 0 && repositoryCount === 0
|
|
184
|
+
? ['No nested git repositories were discovered under configured workspace roots.']
|
|
185
|
+
: [];
|
|
186
|
+
}
|
|
187
|
+
function readIntentStrings(rawIntent, key) {
|
|
188
|
+
return readStringArray(intentTable(rawIntent), key) ?? [];
|
|
189
|
+
}
|
|
190
|
+
function readIntentBoolean(rawIntent, key) {
|
|
191
|
+
const value = intentTable(rawIntent)[key];
|
|
192
|
+
return typeof value === 'boolean' ? value : null;
|
|
193
|
+
}
|
|
194
|
+
function intentTable(rawIntent) {
|
|
195
|
+
return rawIntent && typeof rawIntent === 'object' && !Array.isArray(rawIntent) ? rawIntent : {};
|
|
196
|
+
}
|
|
197
|
+
function readIntentString(rawIntent, key) {
|
|
198
|
+
return readString(intentTable(rawIntent), key) ?? null;
|
|
199
|
+
}
|
|
200
|
+
function safeRunCommand(intentName) {
|
|
201
|
+
return /^[A-Za-z0-9_-]+$/u.test(intentName) ? `mf run ${intentName}` : null;
|
|
202
|
+
}
|
|
203
|
+
function createCatalogIntent(repositoryRoot, repositoryPath, contract, intentName) {
|
|
204
|
+
const rawIntent = contract.intents[intentName];
|
|
205
|
+
try {
|
|
206
|
+
const plan = createRunPlan(repositoryRoot, contract, intentName);
|
|
207
|
+
return {
|
|
208
|
+
name: intentName,
|
|
209
|
+
description: readIntentString(rawIntent, 'description'),
|
|
210
|
+
status: plan.intentStatus,
|
|
211
|
+
lifecycle: plan.lifecycle,
|
|
212
|
+
run_policy: plan.runPolicy,
|
|
213
|
+
runnable: plan.ok,
|
|
214
|
+
reason_code: plan.reasonCode,
|
|
215
|
+
detail: plan.detail,
|
|
216
|
+
run_command: plan.ok ? `mf run ${intentName}` : null,
|
|
217
|
+
run_from_repository: repositoryPath,
|
|
218
|
+
timeout_seconds: plan.timeoutSeconds,
|
|
219
|
+
required_after: readIntentStrings(rawIntent, 'required_after'),
|
|
220
|
+
writes: plan.writes ?? [],
|
|
221
|
+
network: readIntentBoolean(rawIntent, 'network'),
|
|
222
|
+
destructive: readIntentBoolean(rawIntent, 'destructive'),
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
catch (error) {
|
|
226
|
+
return {
|
|
227
|
+
name: intentName,
|
|
228
|
+
description: readIntentString(rawIntent, 'description'),
|
|
229
|
+
status: readIntentString(rawIntent, 'status'),
|
|
230
|
+
lifecycle: readIntentString(rawIntent, 'lifecycle'),
|
|
231
|
+
run_policy: readIntentString(rawIntent, 'run_policy'),
|
|
232
|
+
runnable: false,
|
|
233
|
+
reason_code: 'plan_error',
|
|
234
|
+
detail: error instanceof Error ? error.message : String(error),
|
|
235
|
+
run_command: null,
|
|
236
|
+
run_from_repository: repositoryPath,
|
|
237
|
+
timeout_seconds: null,
|
|
238
|
+
required_after: readIntentStrings(rawIntent, 'required_after'),
|
|
239
|
+
writes: readIntentStrings(rawIntent, 'writes'),
|
|
240
|
+
network: readIntentBoolean(rawIntent, 'network'),
|
|
241
|
+
destructive: readIntentBoolean(rawIntent, 'destructive'),
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
function createCatalogRepository(projectRoot, repository) {
|
|
246
|
+
const repositoryRoot = path.resolve(projectRoot, repository.relativePath);
|
|
247
|
+
const commandSurface = summarizeCommandSurface(repositoryRoot, repository);
|
|
248
|
+
if (!repository.commandContract) {
|
|
249
|
+
return {
|
|
250
|
+
relative_path: repository.relativePath,
|
|
251
|
+
status: 'contract_missing',
|
|
252
|
+
command_contract: commandSurface,
|
|
253
|
+
intent_count: 0,
|
|
254
|
+
runnable_count: 0,
|
|
255
|
+
blocked_count: 0,
|
|
256
|
+
intents: [],
|
|
257
|
+
issues: ['Command contract is missing.'],
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
let contract;
|
|
261
|
+
try {
|
|
262
|
+
contract = readCommandContract(repositoryRoot);
|
|
263
|
+
}
|
|
264
|
+
catch (error) {
|
|
265
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
266
|
+
return {
|
|
267
|
+
relative_path: repository.relativePath,
|
|
268
|
+
status: 'contract_invalid',
|
|
269
|
+
command_contract: commandSurface,
|
|
270
|
+
intent_count: 0,
|
|
271
|
+
runnable_count: 0,
|
|
272
|
+
blocked_count: 0,
|
|
273
|
+
intents: [],
|
|
274
|
+
issues: [message],
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
const intents = getIntentNames(contract.intents).map((intentName) => createCatalogIntent(repositoryRoot, repository.relativePath, contract, intentName));
|
|
278
|
+
const runnableCount = intents.filter((intent) => intent.runnable).length;
|
|
279
|
+
return {
|
|
280
|
+
relative_path: repository.relativePath,
|
|
281
|
+
status: 'available',
|
|
282
|
+
command_contract: commandSurface,
|
|
283
|
+
intent_count: intents.length,
|
|
284
|
+
runnable_count: runnableCount,
|
|
285
|
+
blocked_count: Math.max(0, intents.length - runnableCount),
|
|
286
|
+
intents,
|
|
287
|
+
issues: [],
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
function createWorkspaceCommandCatalogOutput() {
|
|
291
|
+
const projectRoot = resolveMustflowRoot();
|
|
292
|
+
const { config, repositories: nestedRepositories } = readWorkspaceRepositories(projectRoot);
|
|
293
|
+
const repositories = nestedRepositories.map((repository) => createCatalogRepository(projectRoot, repository));
|
|
294
|
+
return {
|
|
295
|
+
schema_version: WORKSPACE_COMMAND_CATALOG_SCHEMA_VERSION,
|
|
296
|
+
command: 'workspace command-catalog',
|
|
297
|
+
mustflow_root: projectRoot,
|
|
298
|
+
workspace: workspaceConfigOutput(config),
|
|
299
|
+
policy: createWorkspaceStatusPolicy(),
|
|
300
|
+
repository_count: repositories.length,
|
|
301
|
+
total_intent_count: repositories.reduce((total, repository) => total + repository.intent_count, 0),
|
|
302
|
+
runnable_intent_count: repositories.reduce((total, repository) => total + repository.runnable_count, 0),
|
|
303
|
+
repositories,
|
|
304
|
+
issues: createWorkspaceIssues(config, repositories.length),
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
function createUnavailableVerificationRepository(repository, commandSurface, status, classification, issue) {
|
|
308
|
+
return {
|
|
309
|
+
relative_path: repository.relativePath,
|
|
310
|
+
status,
|
|
311
|
+
command_contract: commandSurface,
|
|
312
|
+
changed_file_count: classification ? classification.summary.fileCount : null,
|
|
313
|
+
changed_files: classification ? classification.files : [],
|
|
314
|
+
verification_plan_id: null,
|
|
315
|
+
requirement_count: 0,
|
|
316
|
+
candidate_count: 0,
|
|
317
|
+
selected_intent_count: 0,
|
|
318
|
+
gap_count: 0,
|
|
319
|
+
selected_intents: [],
|
|
320
|
+
gaps: [],
|
|
321
|
+
issues: [issue],
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
function selectedIntentsForVerificationReport(repositoryPath, report) {
|
|
325
|
+
return report.schedule.entries.map((entry) => ({
|
|
326
|
+
intent: entry.intent,
|
|
327
|
+
run_command: safeRunCommand(entry.intent),
|
|
328
|
+
run_from_repository: repositoryPath,
|
|
329
|
+
locks: entry.locks,
|
|
330
|
+
conflict_count: entry.conflicts.length,
|
|
331
|
+
}));
|
|
332
|
+
}
|
|
333
|
+
function createVerificationRepository(projectRoot, repository) {
|
|
334
|
+
const repositoryRoot = path.resolve(projectRoot, repository.relativePath);
|
|
335
|
+
const commandSurface = summarizeCommandSurface(repositoryRoot, repository);
|
|
336
|
+
let classification = null;
|
|
337
|
+
try {
|
|
338
|
+
classification = createClassifyOutput(repositoryRoot, 'changed', []);
|
|
339
|
+
}
|
|
340
|
+
catch (error) {
|
|
341
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
342
|
+
return createUnavailableVerificationRepository(repository, commandSurface, 'git_unavailable', null, message);
|
|
343
|
+
}
|
|
344
|
+
if (!repository.commandContract) {
|
|
345
|
+
return createUnavailableVerificationRepository(repository, commandSurface, 'contract_missing', classification, 'Command contract is missing.');
|
|
346
|
+
}
|
|
347
|
+
let contract;
|
|
348
|
+
try {
|
|
349
|
+
contract = readCommandContract(repositoryRoot);
|
|
350
|
+
}
|
|
351
|
+
catch (error) {
|
|
352
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
353
|
+
return createUnavailableVerificationRepository(repository, commandSurface, 'contract_invalid', classification, message);
|
|
354
|
+
}
|
|
355
|
+
let report;
|
|
356
|
+
try {
|
|
357
|
+
report = createChangeVerificationReport(classification, contract, repositoryRoot);
|
|
358
|
+
}
|
|
359
|
+
catch (error) {
|
|
360
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
361
|
+
return createUnavailableVerificationRepository(repository, commandSurface, 'plan_unavailable', classification, message);
|
|
362
|
+
}
|
|
363
|
+
return {
|
|
364
|
+
relative_path: repository.relativePath,
|
|
365
|
+
status: 'available',
|
|
366
|
+
command_contract: commandSurface,
|
|
367
|
+
changed_file_count: classification.summary.fileCount,
|
|
368
|
+
changed_files: classification.files,
|
|
369
|
+
verification_plan_id: createVerificationPlanId(report, contract),
|
|
370
|
+
requirement_count: report.requirements.length,
|
|
371
|
+
candidate_count: report.candidates.length,
|
|
372
|
+
selected_intent_count: report.schedule.entries.length,
|
|
373
|
+
gap_count: report.gaps.length,
|
|
374
|
+
selected_intents: selectedIntentsForVerificationReport(repository.relativePath, report),
|
|
375
|
+
gaps: report.gaps.map((gap) => ({
|
|
376
|
+
reason: gap.reason,
|
|
377
|
+
files: gap.files,
|
|
378
|
+
surfaces: gap.surfaces,
|
|
379
|
+
detail: gap.detail,
|
|
380
|
+
})),
|
|
381
|
+
issues: [],
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
function createWorkspaceVerificationPlanOutput() {
|
|
385
|
+
const projectRoot = resolveMustflowRoot();
|
|
386
|
+
const { config, repositories: nestedRepositories } = readWorkspaceRepositories(projectRoot);
|
|
387
|
+
const repositories = nestedRepositories.map((repository) => createVerificationRepository(projectRoot, repository));
|
|
388
|
+
return {
|
|
389
|
+
schema_version: WORKSPACE_VERIFICATION_PLAN_SCHEMA_VERSION,
|
|
390
|
+
command: 'workspace verify',
|
|
391
|
+
mustflow_root: projectRoot,
|
|
392
|
+
workspace: workspaceConfigOutput(config),
|
|
393
|
+
policy: createWorkspaceVerificationPlanPolicy(),
|
|
394
|
+
repository_count: repositories.length,
|
|
395
|
+
total_changed_file_count: repositories.reduce((total, repository) => total + (repository.changed_file_count ?? 0), 0),
|
|
396
|
+
total_requirement_count: repositories.reduce((total, repository) => total + repository.requirement_count, 0),
|
|
397
|
+
total_selected_intent_count: repositories.reduce((total, repository) => total + repository.selected_intent_count, 0),
|
|
398
|
+
total_gap_count: repositories.reduce((total, repository) => total + repository.gap_count, 0),
|
|
399
|
+
repositories,
|
|
400
|
+
issues: createWorkspaceIssues(config, repositories.length),
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
function renderWorkspaceStatus(output) {
|
|
404
|
+
const lines = [
|
|
405
|
+
'mustflow workspace status',
|
|
406
|
+
`mustflow root: ${output.mustflow_root}`,
|
|
407
|
+
`workspace enabled: ${output.workspace.enabled ? 'yes' : 'no'}`,
|
|
408
|
+
`configured roots: ${output.workspace.roots.length > 0 ? output.workspace.roots.join(', ') : 'none'}`,
|
|
409
|
+
`repositories: ${output.repository_count}`,
|
|
410
|
+
'',
|
|
411
|
+
];
|
|
412
|
+
if (output.repositories.length === 0) {
|
|
413
|
+
lines.push('No nested repositories discovered.');
|
|
414
|
+
return lines.join('\n');
|
|
415
|
+
}
|
|
416
|
+
for (const repository of output.repositories) {
|
|
417
|
+
lines.push(`- ${repository.relative_path} (${repository.status})`);
|
|
418
|
+
lines.push(` mustflow: ${repository.mustflow ? 'yes' : 'no'}`);
|
|
419
|
+
lines.push(` command contract: ${repository.command_contract.path ?? 'missing'}`);
|
|
420
|
+
if (repository.command_contract.runnable_count !== null) {
|
|
421
|
+
lines.push(` runnable intents: ${repository.command_contract.runnable_count}`);
|
|
422
|
+
}
|
|
423
|
+
if (repository.issues.length > 0) {
|
|
424
|
+
lines.push(` issues: ${repository.issues.join('; ')}`);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
return lines.join('\n');
|
|
428
|
+
}
|
|
429
|
+
function renderWorkspaceCommandCatalog(output) {
|
|
430
|
+
const lines = [
|
|
431
|
+
'mustflow workspace command-catalog',
|
|
432
|
+
`mustflow root: ${output.mustflow_root}`,
|
|
433
|
+
`workspace enabled: ${output.workspace.enabled ? 'yes' : 'no'}`,
|
|
434
|
+
`repositories: ${output.repository_count}`,
|
|
435
|
+
`intents: ${output.total_intent_count}`,
|
|
436
|
+
`runnable intents: ${output.runnable_intent_count}`,
|
|
437
|
+
'',
|
|
438
|
+
];
|
|
439
|
+
if (output.repositories.length === 0) {
|
|
440
|
+
lines.push('No nested repositories discovered.');
|
|
441
|
+
return lines.join('\n');
|
|
442
|
+
}
|
|
443
|
+
for (const repository of output.repositories) {
|
|
444
|
+
lines.push(`- ${repository.relative_path} (${repository.status})`);
|
|
445
|
+
if (repository.intents.length === 0) {
|
|
446
|
+
lines.push(` intents: none`);
|
|
447
|
+
}
|
|
448
|
+
for (const intent of repository.intents) {
|
|
449
|
+
lines.push(` - ${intent.name}: ${intent.runnable ? 'runnable' : intent.reason_code ?? 'blocked'}`);
|
|
450
|
+
}
|
|
451
|
+
if (repository.issues.length > 0) {
|
|
452
|
+
lines.push(` issues: ${repository.issues.join('; ')}`);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
return lines.join('\n');
|
|
456
|
+
}
|
|
457
|
+
function renderWorkspaceVerificationPlan(output) {
|
|
458
|
+
const lines = [
|
|
459
|
+
'mustflow workspace verify',
|
|
460
|
+
`mustflow root: ${output.mustflow_root}`,
|
|
461
|
+
`workspace enabled: ${output.workspace.enabled ? 'yes' : 'no'}`,
|
|
462
|
+
`repositories: ${output.repository_count}`,
|
|
463
|
+
`changed files: ${output.total_changed_file_count}`,
|
|
464
|
+
`requirements: ${output.total_requirement_count}`,
|
|
465
|
+
`selected intents: ${output.total_selected_intent_count}`,
|
|
466
|
+
`gaps: ${output.total_gap_count}`,
|
|
467
|
+
'',
|
|
468
|
+
];
|
|
469
|
+
if (output.repositories.length === 0) {
|
|
470
|
+
lines.push('No nested repositories discovered.');
|
|
471
|
+
return lines.join('\n');
|
|
472
|
+
}
|
|
473
|
+
for (const repository of output.repositories) {
|
|
474
|
+
lines.push(`- ${repository.relative_path} (${repository.status})`);
|
|
475
|
+
lines.push(` changed files: ${repository.changed_file_count ?? 'unknown'}`);
|
|
476
|
+
lines.push(` selected intents: ${repository.selected_intent_count}`);
|
|
477
|
+
for (const selected of repository.selected_intents) {
|
|
478
|
+
lines.push(` - ${selected.intent}: ${selected.run_command ?? 'unavailable'}`);
|
|
479
|
+
}
|
|
480
|
+
if (repository.gaps.length > 0) {
|
|
481
|
+
lines.push(` gaps: ${repository.gaps.map((gap) => gap.reason).join(', ')}`);
|
|
482
|
+
}
|
|
483
|
+
if (repository.issues.length > 0) {
|
|
484
|
+
lines.push(` issues: ${repository.issues.join('; ')}`);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
return lines.join('\n');
|
|
488
|
+
}
|
|
489
|
+
function runWorkspaceStatus(args, reporter, lang) {
|
|
490
|
+
if (hasCliOptionToken(args, '--help', ['-h'])) {
|
|
491
|
+
reporter.stdout(getWorkspaceHelp(lang));
|
|
492
|
+
return 0;
|
|
493
|
+
}
|
|
494
|
+
const parsed = parseCliOptions(args, WORKSPACE_STATUS_OPTIONS);
|
|
495
|
+
if (parsed.error) {
|
|
496
|
+
printUsageError(reporter, formatCliOptionParseError(parsed.error, lang), 'mf workspace --help', getWorkspaceHelp(lang), lang);
|
|
497
|
+
return 1;
|
|
498
|
+
}
|
|
499
|
+
const output = createWorkspaceStatusOutput();
|
|
500
|
+
reporter.stdout(hasParsedCliOption(parsed, '--json') ? JSON.stringify(output, null, 2) : renderWorkspaceStatus(output));
|
|
501
|
+
return 0;
|
|
502
|
+
}
|
|
503
|
+
function runWorkspaceCommandCatalog(args, reporter, lang) {
|
|
504
|
+
if (hasCliOptionToken(args, '--help', ['-h'])) {
|
|
505
|
+
reporter.stdout(getWorkspaceHelp(lang));
|
|
506
|
+
return 0;
|
|
507
|
+
}
|
|
508
|
+
const parsed = parseCliOptions(args, WORKSPACE_COMMAND_CATALOG_OPTIONS);
|
|
509
|
+
if (parsed.error) {
|
|
510
|
+
printUsageError(reporter, formatCliOptionParseError(parsed.error, lang), 'mf workspace --help', getWorkspaceHelp(lang), lang);
|
|
511
|
+
return 1;
|
|
512
|
+
}
|
|
513
|
+
const output = createWorkspaceCommandCatalogOutput();
|
|
514
|
+
reporter.stdout(hasParsedCliOption(parsed, '--json') ? JSON.stringify(output, null, 2) : renderWorkspaceCommandCatalog(output));
|
|
515
|
+
return 0;
|
|
516
|
+
}
|
|
517
|
+
function runWorkspaceVerify(args, reporter, lang) {
|
|
518
|
+
if (hasCliOptionToken(args, '--help', ['-h'])) {
|
|
519
|
+
reporter.stdout(getWorkspaceHelp(lang));
|
|
520
|
+
return 0;
|
|
521
|
+
}
|
|
522
|
+
const parsed = parseCliOptions(args, WORKSPACE_VERIFY_OPTIONS);
|
|
523
|
+
if (parsed.error) {
|
|
524
|
+
printUsageError(reporter, formatCliOptionParseError(parsed.error, lang), 'mf workspace --help', getWorkspaceHelp(lang), lang);
|
|
525
|
+
return 1;
|
|
526
|
+
}
|
|
527
|
+
if (!hasParsedCliOption(parsed, '--changed')) {
|
|
528
|
+
printUsageError(reporter, t(lang, 'workspace.error.verifyRequiresChanged'), 'mf workspace --help', getWorkspaceHelp(lang), lang);
|
|
529
|
+
return 1;
|
|
530
|
+
}
|
|
531
|
+
if (!hasParsedCliOption(parsed, '--plan-only')) {
|
|
532
|
+
printUsageError(reporter, t(lang, 'workspace.error.verifyRequiresPlanOnly'), 'mf workspace --help', getWorkspaceHelp(lang), lang);
|
|
533
|
+
return 1;
|
|
534
|
+
}
|
|
535
|
+
const output = createWorkspaceVerificationPlanOutput();
|
|
536
|
+
reporter.stdout(hasParsedCliOption(parsed, '--json') ? JSON.stringify(output, null, 2) : renderWorkspaceVerificationPlan(output));
|
|
537
|
+
return 0;
|
|
538
|
+
}
|
|
539
|
+
export function runWorkspace(args, reporter, lang = 'en') {
|
|
540
|
+
if (hasCliOptionToken(args, '--help', ['-h'])) {
|
|
541
|
+
reporter.stdout(getWorkspaceHelp(lang));
|
|
542
|
+
return 0;
|
|
543
|
+
}
|
|
544
|
+
const [action, ...rest] = args;
|
|
545
|
+
if (!action) {
|
|
546
|
+
printUsageError(reporter, t(lang, 'workspace.error.missingAction'), 'mf workspace --help', getWorkspaceHelp(lang), lang);
|
|
547
|
+
return 1;
|
|
548
|
+
}
|
|
549
|
+
if (action.startsWith('-')) {
|
|
550
|
+
printUsageError(reporter, t(lang, 'cli.error.unknownOption', { option: action }), 'mf workspace --help', getWorkspaceHelp(lang), lang);
|
|
551
|
+
return 1;
|
|
552
|
+
}
|
|
553
|
+
if (action === 'status') {
|
|
554
|
+
return runWorkspaceStatus(rest, reporter, lang);
|
|
555
|
+
}
|
|
556
|
+
if (action === 'command-catalog') {
|
|
557
|
+
return runWorkspaceCommandCatalog(rest, reporter, lang);
|
|
558
|
+
}
|
|
559
|
+
if (action === 'verify') {
|
|
560
|
+
return runWorkspaceVerify(rest, reporter, lang);
|
|
561
|
+
}
|
|
562
|
+
printUsageError(reporter, t(lang, 'workspace.error.unknownAction', { action }), 'mf workspace --help', getWorkspaceHelp(lang), lang);
|
|
563
|
+
return 1;
|
|
564
|
+
}
|
package/dist/cli/i18n/en.js
CHANGED
|
@@ -30,6 +30,10 @@ export const enMessages = {
|
|
|
30
30
|
"command.check.summary": "Validate mustflow files",
|
|
31
31
|
"command.classify.summary": "Classify changed files and public surfaces",
|
|
32
32
|
"command.contractLint.summary": "Lint the command contract",
|
|
33
|
+
"command.onboard.summary": "Suggest review-only command onboarding snippets",
|
|
34
|
+
"command.next.summary": "Choose the next safe mustflow action",
|
|
35
|
+
"command.evidence.summary": "Summarize verification requirements, evidence, and gaps",
|
|
36
|
+
"command.workspace.summary": "Inspect configured workspace roots and nested repository contracts",
|
|
33
37
|
"command.status.summary": "Show local mustflow install status",
|
|
34
38
|
"command.update.summary": "Preview or apply mustflow workflow updates",
|
|
35
39
|
"command.upgrade.summary": "Check the package version and safely update installed workflow files",
|
|
@@ -82,6 +86,58 @@ export const enMessages = {
|
|
|
82
86
|
"contractLint.label.coverageFindings": "Coverage findings",
|
|
83
87
|
"contractLint.label.suggestions": "Suggestions",
|
|
84
88
|
"contractLint.label.issues": "Issues",
|
|
89
|
+
"onboard.help.summary": "Suggest review-only command-intent snippets from existing package, Make, and just command files.",
|
|
90
|
+
"onboard.help.exit.ok": "Command onboarding suggestions were inspected",
|
|
91
|
+
"onboard.help.exit.fail": "The onboard command received invalid input",
|
|
92
|
+
"onboard.title": "mustflow command onboarding",
|
|
93
|
+
"onboard.label.suggestions": "Suggestions",
|
|
94
|
+
"onboard.label.reviewOnly": "Review only",
|
|
95
|
+
"onboard.label.writesFiles": "Writes files",
|
|
96
|
+
"onboard.label.reviewSnippets": "Review-only snippets",
|
|
97
|
+
"onboard.label.nextSteps": "Next steps",
|
|
98
|
+
"onboard.error.missingAction": "Specify an onboard action: commands",
|
|
99
|
+
"onboard.error.unknownAction": "Unknown onboard action: {action}",
|
|
100
|
+
"next.help.summary": "Inspect the current root and changed files, then print the next safe mustflow action.",
|
|
101
|
+
"next.help.exit.ok": "Next action was inspected",
|
|
102
|
+
"next.help.exit.fail": "The next action could not be inspected",
|
|
103
|
+
"next.title": "mustflow next",
|
|
104
|
+
"next.label.status": "Status",
|
|
105
|
+
"next.label.nextAction": "Next action",
|
|
106
|
+
"next.label.reason": "Reason",
|
|
107
|
+
"next.label.selectedIntents": "Selected verification",
|
|
108
|
+
"next.label.gaps": "Gaps",
|
|
109
|
+
"next.section.gaps": "Verification gaps",
|
|
110
|
+
"next.section.blockers": "Blockers",
|
|
111
|
+
"next.section.warnings": "Warnings",
|
|
112
|
+
"next.section.commands": "Recommended commands",
|
|
113
|
+
"evidence.help.summary": "Summarize changed-file verification requirements, latest evidence, receipts, and remaining gaps without running commands.",
|
|
114
|
+
"evidence.help.option.changed": "Build an evidence report from the current changed-file verification plan",
|
|
115
|
+
"evidence.help.option.latest": "Summarize only the latest stored run or verify evidence",
|
|
116
|
+
"evidence.help.option.plan": "Build an evidence report from a saved mustflow verification-plan JSON file",
|
|
117
|
+
"evidence.help.option.export": "Write the evidence report JSON to a path inside the mustflow root",
|
|
118
|
+
"evidence.help.exit.ok": "Evidence was inspected",
|
|
119
|
+
"evidence.help.exit.fail": "Evidence could not be inspected",
|
|
120
|
+
"evidence.error.conflictingInputs": "Choose only one evidence source: --changed, --latest, or --plan",
|
|
121
|
+
"evidence.title": "mustflow evidence",
|
|
122
|
+
"evidence.label.status": "Status",
|
|
123
|
+
"evidence.label.source": "Source",
|
|
124
|
+
"evidence.label.plan": "Verification plan",
|
|
125
|
+
"evidence.label.requirements": "Requirements",
|
|
126
|
+
"evidence.label.selectedIntents": "Selected intents",
|
|
127
|
+
"evidence.label.gaps": "Gaps",
|
|
128
|
+
"evidence.label.latest": "Latest evidence",
|
|
129
|
+
"evidence.section.requirements": "Requirements",
|
|
130
|
+
"evidence.section.gaps": "Gaps",
|
|
131
|
+
"evidence.section.remainingRisks": "Remaining risks",
|
|
132
|
+
"workspace.help.summary": "Inspect configured workspace roots and nested repository contract readiness without granting command authority.",
|
|
133
|
+
"workspace.help.action.status": "Print discovered nested repositories and their local mustflow contract status",
|
|
134
|
+
"workspace.help.action.commandCatalog": "Print per-repository command intent availability without raw command strings",
|
|
135
|
+
"workspace.help.action.verify": "Print per-repository changed-file verification plans without running commands",
|
|
136
|
+
"workspace.help.exit.ok": "Workspace status was inspected",
|
|
137
|
+
"workspace.error.missingAction": "Specify a workspace action: status, command-catalog, or verify",
|
|
138
|
+
"workspace.error.unknownAction": "Unknown workspace action: {action}",
|
|
139
|
+
"workspace.error.verifyRequiresChanged": "workspace verify requires --changed",
|
|
140
|
+
"workspace.error.verifyRequiresPlanOnly": "workspace verify requires --plan-only",
|
|
85
141
|
"context.help.summary": "Print the agent context for the current mustflow root.",
|
|
86
142
|
"context.help.option.json": "Print machine-readable context JSON",
|
|
87
143
|
"context.help.option.cacheProfile": "Print a prompt-cache profile: stable, task, volatile, or all",
|
|
@@ -95,11 +151,14 @@ export const enMessages = {
|
|
|
95
151
|
"api.help.action.diffRisk": "Print a compact changed-file risk and verification summary",
|
|
96
152
|
"api.help.action.health": "Print a compact workspace health summary",
|
|
97
153
|
"api.help.action.locks": "Print active mf run locks for multi-session coordination",
|
|
154
|
+
"api.help.action.serve": "Serve read-only API reports over newline-delimited stdio",
|
|
155
|
+
"api.help.option.stdio": "Read newline-delimited JSON requests from stdin and print JSON responses",
|
|
98
156
|
"api.help.exit.ok": "The API report was inspected and printed",
|
|
99
|
-
"api.error.missingAction": "Specify an api action: workspace-summary, command-catalog, verification-plan, latest-evidence, diff-risk, health, or
|
|
157
|
+
"api.error.missingAction": "Specify an api action: workspace-summary, command-catalog, verification-plan, latest-evidence, diff-risk, health, locks, or serve",
|
|
100
158
|
"api.error.unknownAction": "Unknown api action: {action}",
|
|
101
159
|
"api.error.actionRequiresJson": "{action} requires --json",
|
|
102
160
|
"api.error.actionRequiresChanged": "{action} currently requires --changed",
|
|
161
|
+
"api.error.serveRequiresStdio": "api serve requires --stdio",
|
|
103
162
|
"label.installed": "Installed",
|
|
104
163
|
"label.mustflowRoot": "mustflow root",
|
|
105
164
|
"label.commandContract": "Command specification",
|