minutework 0.1.8 → 0.1.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/EXTERNAL_ALPHA.md +3 -0
  2. package/README.md +2 -0
  3. package/assets/claude-local/CLAUDE.md.template +60 -0
  4. package/assets/claude-local/skills/README.md +14 -0
  5. package/assets/claude-local/skills/ai-capability-defaults.md +21 -0
  6. package/assets/claude-local/skills/app-pack-authoring.md +16 -0
  7. package/assets/claude-local/skills/capability-gap-reporting.md +26 -0
  8. package/assets/claude-local/skills/email-ingress-and-thread-routing.md +36 -0
  9. package/assets/claude-local/skills/generated-workspace-architecture.md +55 -0
  10. package/assets/claude-local/skills/layering-and-import-modes.md +28 -0
  11. package/assets/claude-local/skills/ontology-mapping.md +16 -1
  12. package/assets/claude-local/skills/runtime-capability-inventory.md +46 -0
  13. package/assets/claude-local/skills/shadow-participation-and-guest-threads.md +33 -0
  14. package/assets/claude-local/skills/sidecar-generation.md +4 -0
  15. package/dist/cli.js +0 -0
  16. package/dist/index.js +12 -1
  17. package/dist/index.js.map +1 -1
  18. package/dist/init.js +5 -64
  19. package/dist/init.js.map +1 -1
  20. package/dist/orchestrator-context.js +15 -1
  21. package/dist/orchestrator-context.js.map +1 -1
  22. package/dist/orchestrator-state.d.ts +12 -5
  23. package/dist/orchestrator-state.js +1 -1
  24. package/dist/orchestrator-state.js.map +1 -1
  25. package/dist/orchestrator.js +6 -2
  26. package/dist/orchestrator.js.map +1 -1
  27. package/dist/reporting.js +1 -1
  28. package/dist/reporting.js.map +1 -1
  29. package/dist/sandbox.js +1 -1
  30. package/dist/sandbox.js.map +1 -1
  31. package/dist/workspace-assets.d.ts +56 -0
  32. package/dist/workspace-assets.js +409 -0
  33. package/dist/workspace-assets.js.map +1 -0
  34. package/dist/workspace.d.ts +8 -0
  35. package/dist/workspace.js +158 -0
  36. package/dist/workspace.js.map +1 -0
  37. package/package.json +7 -5
  38. package/vendor/workspace-mcp/cli.d.ts +2 -0
  39. package/vendor/workspace-mcp/cli.js +5 -0
  40. package/vendor/workspace-mcp/cli.js.map +1 -0
  41. package/vendor/workspace-mcp/context.d.ts +89 -0
  42. package/vendor/workspace-mcp/context.js +1840 -0
  43. package/vendor/workspace-mcp/context.js.map +1 -0
  44. package/vendor/workspace-mcp/index.d.ts +5 -0
  45. package/vendor/workspace-mcp/index.js +4 -0
  46. package/vendor/workspace-mcp/index.js.map +1 -0
  47. package/vendor/workspace-mcp/runtime-state.d.ts +23 -0
  48. package/vendor/workspace-mcp/runtime-state.js +88 -0
  49. package/vendor/workspace-mcp/runtime-state.js.map +1 -0
  50. package/vendor/workspace-mcp/server.d.ts +17 -0
  51. package/vendor/workspace-mcp/server.js +94 -0
  52. package/vendor/workspace-mcp/server.js.map +1 -0
  53. package/vendor/workspace-mcp/types.d.ts +1527 -0
  54. package/vendor/workspace-mcp/types.js +432 -0
  55. package/vendor/workspace-mcp/types.js.map +1 -0
@@ -0,0 +1,1840 @@
1
+ import { promises as fs } from "node:fs";
2
+ import path from "node:path";
3
+ import { analyzeCompilerWorkspace, CompileGraphSchema, discoverCompilerWorkspaceRoot, HostedPreviewReleaseMetadataSchema, inspectCompilerWorkspace, loadWorkspaceConfig as loadSharedWorkspaceConfig, resolveGeneratedArtifactPaths, resolveWorkspaceCliConfig, } from "@minutework/schema-compiler";
4
+ import { z } from "zod";
5
+ import { createSha256Digest, readOptionalLocalRuntimeActivityFile, resolveLocalRuntimeStatePaths, } from "./runtime-state.js";
6
+ import { CapabilityGapLayerSchema, CapabilityGapReportSchema, CapabilityGapReusabilitySchema, BindingPayloadSchema, PreviewDeployStateSchema, } from "./types.js";
7
+ const StoredAuthProfileSchema = z.object({
8
+ createdAt: z.string(),
9
+ expiresAt: z.string(),
10
+ id: z.string(),
11
+ platformBaseUrl: z.string(),
12
+ scopeTemplate: z.string(),
13
+ scopes: z.array(z.string()),
14
+ tenantId: z.string(),
15
+ tenantName: z.string(),
16
+ tenantSlug: z.string(),
17
+ token: z.string(),
18
+ tokenKind: z.string(),
19
+ user: z.object({
20
+ email: z.string(),
21
+ id: z.string(),
22
+ username: z.string(),
23
+ }),
24
+ });
25
+ export class WorkspaceMcpError extends Error {
26
+ }
27
+ export const WORKSPACE_MCP_TOOL_SPECS = [
28
+ {
29
+ description: "Return MinuteWork workspace metadata, local binding/auth status, starter flags, and top-level diagnostics.",
30
+ id: "minutework_workspace_snapshot",
31
+ },
32
+ {
33
+ description: "Return MinuteWork schema entrypoint status, validate report, compiled schema graph when available, and artifact readiness for compile/codegen outputs.",
34
+ id: "minutework_schema_status",
35
+ },
36
+ {
37
+ description: "Return local sandbox topology, compile-graph readiness, planned dev/test steps, and persisted local runtime activity derived from workspace state.",
38
+ id: "minutework_local_runtime_status",
39
+ },
40
+ {
41
+ description: "Return current MinuteWork deploy target context plus preview/live placeholder status objects.",
42
+ id: "minutework_deploy_status",
43
+ },
44
+ {
45
+ description: "Return aggregated MinuteWork workspace diagnostics and recommended next actions.",
46
+ id: "minutework_workspace_doctor",
47
+ },
48
+ {
49
+ description: "Return a truthful MinuteWork capability inventory for the generated workspace, including local manifest surface, preview/publication posture, blind spots, and gap-report contract details.",
50
+ id: "minutework_capability_inventory",
51
+ },
52
+ ];
53
+ export function getWorkspaceMcpToolDescription(id) {
54
+ return WORKSPACE_MCP_TOOL_SPECS.find((tool) => tool.id === id)?.description ?? id;
55
+ }
56
+ export function parseWorkspaceMcpCliArgs(args) {
57
+ let workspacePath;
58
+ for (let index = 0; index < args.length; index += 1) {
59
+ const arg = args[index];
60
+ if (arg === "--help" || arg === "-h") {
61
+ return { help: true };
62
+ }
63
+ if (arg === "--workspace") {
64
+ const next = args[index + 1];
65
+ if (!next || next.startsWith("--")) {
66
+ throw new WorkspaceMcpError('The "--workspace" option requires a path.');
67
+ }
68
+ workspacePath = next;
69
+ index += 1;
70
+ continue;
71
+ }
72
+ if (arg.startsWith("--workspace=")) {
73
+ workspacePath = arg.slice("--workspace=".length);
74
+ if (!workspacePath) {
75
+ throw new WorkspaceMcpError('The "--workspace" option requires a path.');
76
+ }
77
+ continue;
78
+ }
79
+ throw new WorkspaceMcpError(`Unknown mcp option: ${arg}`);
80
+ }
81
+ return {
82
+ help: false,
83
+ workspacePath,
84
+ };
85
+ }
86
+ export function renderWorkspaceMcpHelp() {
87
+ return `MinuteWork workspace MCP
88
+
89
+ Usage:
90
+ minutework mcp [--workspace <path>]
91
+
92
+ Starts a read-only local MCP server over stdio for MinuteWork-aware IDE tools.
93
+ `;
94
+ }
95
+ export async function loadWorkspaceContext(options = {}) {
96
+ const cwd = options.cwd ?? process.cwd();
97
+ const env = options.env ?? process.env;
98
+ const platform = options.platform ?? process.platform;
99
+ const workspaceRoot = await resolveWorkspaceRoot({
100
+ cwd,
101
+ workspacePath: options.workspacePath,
102
+ });
103
+ const configPath = path.join(workspaceRoot, "minutework.config.ts");
104
+ const config = await loadWorkspaceConfig(configPath);
105
+ const resolvedWorkspaceCliConfig = await resolveWorkspaceCliConfig({
106
+ env,
107
+ workspaceRoot,
108
+ });
109
+ const bindingPath = path.join(workspaceRoot, config.localState.directory, "binding.json");
110
+ const bindingState = await readOptionalWorkspaceBinding(bindingPath);
111
+ const statePaths = resolveCliStatePaths({ env, platform });
112
+ const authRecord = await loadCurrentAuthProfile(statePaths);
113
+ const cursorConfigPath = path.join(workspaceRoot, config.mcp.cursorConfigPath);
114
+ const claudeDesktopSamplePath = path.join(workspaceRoot, config.mcp.claudeDesktopSamplePath);
115
+ const schemaDirectoryPath = path.join(workspaceRoot, config.schema.directory);
116
+ const schemaEntrypointPath = path.join(workspaceRoot, config.schema.entrypoint);
117
+ const schemaMwPath = path.join(workspaceRoot, "schema.mw");
118
+ const tenantAppStarterPath = path.join(workspaceRoot, config.starters.tenantApp.path);
119
+ const sidecarStarterPath = path.join(workspaceRoot, config.starters.sidecar.path);
120
+ const artifactPaths = resolveGeneratedArtifactPaths(config.localState.directory);
121
+ const runtimeStatePaths = resolveLocalRuntimeStatePaths(workspaceRoot, config.localState.directory);
122
+ const previewDeployStatePath = path.join(workspaceRoot, config.localState.directory, "deploy", "preview", "status.json");
123
+ const capabilityGapReportPath = path.join(runtimeStatePaths.sandboxRoot, "capability-gap-report.json");
124
+ const compilerStaticAnalysis = await inspectCompilerWorkspace(workspaceRoot);
125
+ const compilerAnalysis = await analyzeCompilerWorkspace(workspaceRoot);
126
+ const [cursorConfigExists, claudeDesktopSampleExists, schemaDirectoryExists, schemaEntrypointExists, schemaMwExists, compileGraphArtifactExists, openapiArtifactExists, clientTypesArtifactExists, tenantAppStarterExists, sidecarStarterExists,] = await Promise.all([
127
+ exists(cursorConfigPath),
128
+ exists(claudeDesktopSamplePath),
129
+ exists(schemaDirectoryPath),
130
+ exists(schemaEntrypointPath),
131
+ exists(schemaMwPath),
132
+ exists(path.join(workspaceRoot, artifactPaths.compileGraphPath)),
133
+ exists(path.join(workspaceRoot, artifactPaths.openapiPath)),
134
+ exists(path.join(workspaceRoot, artifactPaths.clientTypesPath)),
135
+ exists(tenantAppStarterPath),
136
+ exists(sidecarStarterPath),
137
+ ]);
138
+ const persistedCompileGraphState = compileGraphArtifactExists
139
+ ? await readOptionalCompileGraph(path.join(workspaceRoot, artifactPaths.compileGraphPath))
140
+ : { corrupted: false, digest: null, graph: null };
141
+ const persistedCompileGraphCurrent = compilerAnalysis.compileGraph !== null &&
142
+ persistedCompileGraphState.graph !== null &&
143
+ JSON.stringify(persistedCompileGraphState.graph) === JSON.stringify(compilerAnalysis.compileGraph);
144
+ const [runtimeDevSessionState, runtimeTestRunState] = await Promise.all([
145
+ readOptionalLocalRuntimeActivityFile({
146
+ command: "dev",
147
+ filePath: runtimeStatePaths.devSessionPath,
148
+ }),
149
+ readOptionalLocalRuntimeActivityFile({
150
+ command: "test",
151
+ filePath: runtimeStatePaths.testRunPath,
152
+ }),
153
+ ]);
154
+ const previewDeployStateRecord = await readOptionalPreviewDeployState(previewDeployStatePath);
155
+ const capabilityGapReportRecord = await readOptionalCapabilityGapReport(capabilityGapReportPath);
156
+ const openapiArtifactState = await inspectGeneratedJsonArtifact(path.join(workspaceRoot, artifactPaths.openapiPath), compilerAnalysis.codegenArtifacts ? formatJson(compilerAnalysis.codegenArtifacts.openapi) : null);
157
+ const clientTypesArtifactState = await inspectGeneratedTextArtifact(path.join(workspaceRoot, artifactPaths.clientTypesPath), compilerAnalysis.codegenArtifacts?.clientTypes ?? null);
158
+ const authStatus = buildAuthStatus(authRecord, statePaths.authRoot);
159
+ const bindingStatus = buildBindingStatus(bindingPath, bindingState.binding, authStatus.profile);
160
+ const diagnostics = [
161
+ ...bindingState.diagnostics,
162
+ ...authRecord.diagnostics,
163
+ ...buildPlatformMismatchDiagnostics(authStatus.profile, resolvedWorkspaceCliConfig),
164
+ ...buildDiagnostics({
165
+ authStatus,
166
+ authStateMalformed: authRecord.diagnostics.length > 0,
167
+ bindingStatus,
168
+ bindingStateMalformed: bindingState.diagnostics.length > 0,
169
+ claudeDesktopSampleExists,
170
+ claudeDesktopSamplePath,
171
+ cursorConfigExists,
172
+ cursorConfigPath,
173
+ persistedCompileGraphCorrupted: persistedCompileGraphState.corrupted,
174
+ schemaDirectoryExists,
175
+ schemaDirectoryPath,
176
+ schemaEntrypointExists,
177
+ schemaEntrypointPath,
178
+ schemaMwExists,
179
+ schemaMwPath,
180
+ }),
181
+ ];
182
+ return {
183
+ authStatus,
184
+ bindingStatus,
185
+ capabilityGapReport: capabilityGapReportRecord.report,
186
+ capabilityGapReportCorrupted: capabilityGapReportRecord.corrupted,
187
+ capabilityGapReportPath,
188
+ claudeDesktopSampleExists,
189
+ claudeDesktopSamplePath,
190
+ clientTypesArtifactExists,
191
+ compileGraphArtifactExists,
192
+ compilerAnalysis,
193
+ compilerStaticAnalysis,
194
+ config,
195
+ configPath,
196
+ cursorConfigExists,
197
+ cursorConfigPath,
198
+ diagnostics,
199
+ openapiArtifactExists,
200
+ persistedCompileGraph: persistedCompileGraphState.graph,
201
+ persistedCompileGraphDigest: persistedCompileGraphCurrent ? persistedCompileGraphState.digest : null,
202
+ persistedCompileGraphCurrent,
203
+ persistedCompileGraphCorrupted: persistedCompileGraphState.corrupted,
204
+ persistedClientTypesCurrent: clientTypesArtifactState.current,
205
+ persistedOpenapiCorrupted: openapiArtifactState.corrupted,
206
+ persistedOpenapiCurrent: openapiArtifactState.current,
207
+ previewDeployState: previewDeployStateRecord.state,
208
+ previewDeployStateCorrupted: previewDeployStateRecord.corrupted,
209
+ previewDeployStatePath,
210
+ resolvedWorkspaceCliConfig,
211
+ runtimeDevSessionState,
212
+ runtimeStatePaths,
213
+ runtimeTestRunState,
214
+ schemaDirectoryExists,
215
+ schemaDirectoryPath,
216
+ schemaEntrypointExists,
217
+ schemaEntrypointPath,
218
+ schemaMwExists,
219
+ schemaMwPath,
220
+ sidecarStarterExists,
221
+ tenantAppStarterExists,
222
+ workspaceRoot,
223
+ };
224
+ }
225
+ export async function loadLocalRuntimeStatusContext(options = {}) {
226
+ const cwd = options.cwd ?? process.cwd();
227
+ const workspaceRoot = await resolveWorkspaceRoot({
228
+ cwd,
229
+ workspacePath: options.workspacePath,
230
+ });
231
+ const configPath = path.join(workspaceRoot, "minutework.config.ts");
232
+ const config = await loadWorkspaceConfig(configPath);
233
+ const compilerStaticAnalysis = await inspectCompilerWorkspace(workspaceRoot);
234
+ const artifactPaths = resolveGeneratedArtifactPaths(config.localState.directory);
235
+ const compileGraphPath = path.join(workspaceRoot, artifactPaths.compileGraphPath);
236
+ const runtimeStatePaths = resolveLocalRuntimeStatePaths(workspaceRoot, config.localState.directory);
237
+ const tenantAppStarterPath = path.join(workspaceRoot, config.starters.tenantApp.path);
238
+ const sidecarStarterPath = path.join(workspaceRoot, config.starters.sidecar.path);
239
+ const [compileGraphArtifactExists, tenantAppStarterExists, sidecarStarterExists] = await Promise.all([
240
+ exists(compileGraphPath),
241
+ exists(tenantAppStarterPath),
242
+ exists(sidecarStarterPath),
243
+ ]);
244
+ const persistedCompileGraphState = compileGraphArtifactExists
245
+ ? await readOptionalCompileGraph(compileGraphPath)
246
+ : { corrupted: false, digest: null, graph: null };
247
+ const persistedCompileGraphCurrent = isPersistedCompileGraphCurrent(persistedCompileGraphState.graph, compilerStaticAnalysis);
248
+ const [runtimeDevSessionState, runtimeTestRunState] = await Promise.all([
249
+ readOptionalLocalRuntimeActivityFile({
250
+ command: "dev",
251
+ filePath: runtimeStatePaths.devSessionPath,
252
+ }),
253
+ readOptionalLocalRuntimeActivityFile({
254
+ command: "test",
255
+ filePath: runtimeStatePaths.testRunPath,
256
+ }),
257
+ ]);
258
+ return {
259
+ compileGraphArtifactExists,
260
+ compilerStaticAnalysis,
261
+ config,
262
+ persistedCompileGraphDigest: persistedCompileGraphCurrent ? persistedCompileGraphState.digest : null,
263
+ persistedCompileGraphCorrupted: persistedCompileGraphState.corrupted,
264
+ persistedCompileGraphCurrent,
265
+ runtimeDevSessionState,
266
+ runtimeStatePaths,
267
+ runtimeTestRunState,
268
+ sidecarStarterExists,
269
+ tenantAppStarterExists,
270
+ workspaceRoot,
271
+ };
272
+ }
273
+ export function collectWorkspaceSnapshot(context) {
274
+ return {
275
+ authStatus: context.authStatus,
276
+ bindingStatus: context.bindingStatus,
277
+ config: buildNormalizedWorkspaceConfig(context),
278
+ configPath: context.configPath,
279
+ diagnostics: context.diagnostics,
280
+ ide: {
281
+ claudeDesktopSample: {
282
+ exists: context.claudeDesktopSampleExists,
283
+ path: context.claudeDesktopSamplePath,
284
+ },
285
+ cursorConfig: {
286
+ exists: context.cursorConfigExists,
287
+ path: context.cursorConfigPath,
288
+ },
289
+ },
290
+ resolvedPlatform: {
291
+ baseUrl: context.resolvedWorkspaceCliConfig.platformBaseUrl,
292
+ hasLegacyOverride: context.resolvedWorkspaceCliConfig.hasLegacyPlatformOverride,
293
+ source: context.resolvedWorkspaceCliConfig.platformBaseUrlSource,
294
+ },
295
+ schema: {
296
+ directory: context.config.schema.directory,
297
+ directoryExists: context.schemaDirectoryExists,
298
+ directoryPath: context.schemaDirectoryPath,
299
+ entrypoint: context.config.schema.entrypoint,
300
+ entrypointExists: context.schemaEntrypointExists,
301
+ entrypointPath: context.schemaEntrypointPath,
302
+ },
303
+ starters: [
304
+ buildStarter("tenant-app", context),
305
+ buildStarter("sidecar", context),
306
+ ],
307
+ workspaceName: context.config.workspace.name,
308
+ workspaceRoot: context.workspaceRoot,
309
+ };
310
+ }
311
+ function buildNormalizedWorkspaceConfig(context) {
312
+ return {
313
+ localState: {
314
+ directory: context.config.localState.directory,
315
+ directoryPath: path.join(context.workspaceRoot, context.config.localState.directory),
316
+ },
317
+ platform: context.config.platform
318
+ ? {
319
+ baseUrl: context.config.platform.baseUrl ?? null,
320
+ defaultEnvironment: context.config.platform.defaultEnvironment ?? null,
321
+ }
322
+ : null,
323
+ mcp: {
324
+ claudeDesktopSampleAbsolutePath: context.claudeDesktopSamplePath,
325
+ claudeDesktopSamplePath: context.config.mcp.claudeDesktopSamplePath,
326
+ cursorConfigAbsolutePath: context.cursorConfigPath,
327
+ cursorConfigPath: context.config.mcp.cursorConfigPath,
328
+ },
329
+ schema: {
330
+ directory: context.config.schema.directory,
331
+ directoryPath: context.schemaDirectoryPath,
332
+ entrypoint: context.config.schema.entrypoint,
333
+ entrypointPath: context.schemaEntrypointPath,
334
+ },
335
+ starters: {
336
+ sidecar: buildNormalizedStarterConfig("sidecar", context),
337
+ tenantApp: buildNormalizedStarterConfig("tenant-app", context),
338
+ },
339
+ workspace: {
340
+ name: context.config.workspace.name,
341
+ },
342
+ };
343
+ }
344
+ export function collectSchemaStatus(context) {
345
+ const prerequisiteCodes = collectSchemaPrerequisiteCodes(context);
346
+ const artifactPaths = resolveGeneratedArtifactPaths(context.config.localState.directory);
347
+ return {
348
+ artifacts: [
349
+ buildArtifactStatus(context, "compile_graph", artifactPaths.compileGraphPath, prerequisiteCodes),
350
+ buildArtifactStatus(context, "openapi", artifactPaths.openapiPath, prerequisiteCodes),
351
+ buildArtifactStatus(context, "client_types", artifactPaths.clientTypesPath, prerequisiteCodes),
352
+ ],
353
+ compileGraph: context.compilerAnalysis.compileGraph,
354
+ diagnostics: context.diagnostics,
355
+ directoryExists: context.schemaDirectoryExists,
356
+ entrypointExists: context.schemaEntrypointExists,
357
+ schemaDirectoryPath: context.schemaDirectoryPath,
358
+ schemaEntrypointPath: context.schemaEntrypointPath,
359
+ selectedInput: context.compilerAnalysis.validateReport.selectedInput,
360
+ validateReport: context.compilerAnalysis.validateReport,
361
+ workspaceRoot: context.workspaceRoot,
362
+ };
363
+ }
364
+ export function collectLocalRuntimeStatus(context) {
365
+ const artifactPaths = resolveGeneratedArtifactPaths(context.config.localState.directory);
366
+ const compileGraphPath = path.join(context.workspaceRoot, artifactPaths.compileGraphPath);
367
+ const hasSelectedInput = context.compilerStaticAnalysis.selectedInput !== null;
368
+ const hasCurrentCompileGraph = context.persistedCompileGraphCurrent;
369
+ const schemaPrerequisiteCodes = context.compilerStaticAnalysis.diagnostics
370
+ .filter((item) => item.severity !== "info")
371
+ .map((item) => item.code);
372
+ const compileGraphState = buildLocalRuntimeCompileGraphState(context, compileGraphPath);
373
+ const surfaces = [
374
+ buildSchemaCompileSurface(context, compileGraphPath),
375
+ buildStarterSurface(context, "tenant_app"),
376
+ buildStarterSurface(context, "sidecar"),
377
+ ];
378
+ const previewSurfaceIssueCodes = collectPreviewSurfaceIssueCodes(surfaces);
379
+ const hasMissingPreviewSurfacePath = previewSurfaceIssueCodes.some((code) => code === "workspace.runtime.tenant_app_missing" || code === "workspace.runtime.sidecar_missing");
380
+ const hasEnabledPreviewSurface = surfaces.some((surface) => (surface.id === "tenant_app" || surface.id === "sidecar") && surface.status === "ready");
381
+ const devActivity = buildLocalRuntimeActivitySummary("dev", context.runtimeDevSessionState, context.persistedCompileGraphDigest);
382
+ const testActivity = buildLocalRuntimeActivitySummary("test", context.runtimeTestRunState, context.persistedCompileGraphDigest);
383
+ return {
384
+ activity: {
385
+ dev: devActivity,
386
+ test: testActivity,
387
+ },
388
+ commands: [
389
+ {
390
+ canonicalPath: path.join(context.runtimeStatePaths.sandboxRoot, "dev"),
391
+ detail: hasSelectedInput
392
+ ? hasCurrentCompileGraph
393
+ ? hasEnabledPreviewSurface
394
+ ? "The local sandbox can start the enabled preview surfaces from the current canonical compile graph."
395
+ : hasMissingPreviewSurfacePath
396
+ ? "The local sandbox cannot start preview surfaces until the enabled starter paths exist."
397
+ : "The local sandbox does not have any enabled preview surfaces yet. Add a tenant-app or sidecar starter before running dev."
398
+ : "The local sandbox needs a current canonical compile graph before preview surfaces can start. Run `minutework compile` first."
399
+ : "The local sandbox cannot start until the workspace has exactly one supported schema input.",
400
+ name: "dev",
401
+ prerequisiteCodes: hasSelectedInput
402
+ ? hasCurrentCompileGraph
403
+ ? previewSurfaceIssueCodes
404
+ : ["workspace.runtime.compile_graph_missing", ...previewSurfaceIssueCodes]
405
+ : schemaPrerequisiteCodes,
406
+ status: hasCurrentCompileGraph && hasEnabledPreviewSurface ? "ready" : "blocked",
407
+ steps: [...buildSchemaPreflightSteps(context), ...buildLocalRuntimePreviewSteps(context)],
408
+ },
409
+ {
410
+ canonicalPath: path.join(context.runtimeStatePaths.sandboxRoot, "test"),
411
+ detail: hasCurrentCompileGraph
412
+ ? hasEnabledPreviewSurface
413
+ ? "The local sandbox can run schema smoke checks and starter-local tests from the current canonical compile graph."
414
+ : "The local sandbox can run schema smoke checks from the current canonical compile graph even without enabled preview surfaces."
415
+ : hasSelectedInput
416
+ ? "The local sandbox test loop needs a current canonical compile graph. Run `minutework compile` first."
417
+ : "The local sandbox test loop is blocked until the workspace has exactly one supported schema input.",
418
+ name: "test",
419
+ prerequisiteCodes: hasCurrentCompileGraph
420
+ ? []
421
+ : hasSelectedInput
422
+ ? ["workspace.runtime.compile_graph_missing"]
423
+ : schemaPrerequisiteCodes,
424
+ status: hasCurrentCompileGraph ? "ready" : "blocked",
425
+ steps: [...buildSchemaTestSteps(context), ...buildLocalRuntimeTestSteps(context)],
426
+ },
427
+ ],
428
+ compileGraph: compileGraphState,
429
+ prerequisites: {
430
+ hasCurrentCompileGraph,
431
+ hasEnabledPreviewSurface,
432
+ hasSelectedInput,
433
+ },
434
+ status: hasCurrentCompileGraph ? "ready" : "blocked",
435
+ surfaces,
436
+ topology: {
437
+ devSessionPath: context.runtimeStatePaths.devSessionPath,
438
+ localStateDirectoryPath: path.join(context.workspaceRoot, context.config.localState.directory),
439
+ sandboxRoot: context.runtimeStatePaths.sandboxRoot,
440
+ testRunPath: context.runtimeStatePaths.testRunPath,
441
+ },
442
+ workspaceRoot: context.workspaceRoot,
443
+ };
444
+ }
445
+ function buildLocalRuntimeActivitySummary(command, state, currentCompileGraphDigest) {
446
+ const emptyOutcome = {
447
+ errorCode: null,
448
+ exitCode: null,
449
+ failedStepId: null,
450
+ signal: null,
451
+ };
452
+ if (state.kind === "missing") {
453
+ return {
454
+ compileGraphDigest: null,
455
+ controllerPid: null,
456
+ detail: command === "dev"
457
+ ? "No local dev session has been recorded yet."
458
+ : "No local test run has been recorded yet.",
459
+ endedAt: null,
460
+ filePath: state.filePath,
461
+ graphStatus: "missing",
462
+ outcome: emptyOutcome,
463
+ startedAt: null,
464
+ status: "missing",
465
+ steps: [],
466
+ updatedAt: null,
467
+ };
468
+ }
469
+ if (state.kind === "corrupted" || !state.record) {
470
+ return {
471
+ compileGraphDigest: null,
472
+ controllerPid: null,
473
+ detail: command === "dev"
474
+ ? "The persisted local dev session file is malformed. The next `minutework dev` run will replace it."
475
+ : "The persisted local test run file is malformed. The next `minutework test` run will replace it.",
476
+ endedAt: null,
477
+ filePath: state.filePath,
478
+ graphStatus: "missing",
479
+ outcome: emptyOutcome,
480
+ startedAt: null,
481
+ status: "corrupted",
482
+ steps: [],
483
+ updatedAt: null,
484
+ };
485
+ }
486
+ const graphStatus = resolveLocalRuntimeActivityGraphStatus(state.record.compileGraphDigest, currentCompileGraphDigest);
487
+ const base = {
488
+ compileGraphDigest: state.record.compileGraphDigest,
489
+ controllerPid: state.record.controllerPid,
490
+ endedAt: state.record.endedAt,
491
+ filePath: state.filePath,
492
+ graphStatus,
493
+ outcome: state.record.outcome,
494
+ startedAt: state.record.startedAt,
495
+ steps: state.record.steps,
496
+ updatedAt: state.record.updatedAt,
497
+ };
498
+ if (state.stale) {
499
+ return {
500
+ ...base,
501
+ detail: command === "dev"
502
+ ? "The last local dev session still claimed to be running, but its recorded controller or child processes are no longer live."
503
+ : "The last local test run still claimed to be running, but its recorded controller or child processes are no longer live.",
504
+ status: "stale",
505
+ };
506
+ }
507
+ switch (state.record.status) {
508
+ case "running":
509
+ return {
510
+ ...base,
511
+ detail: command === "dev"
512
+ ? "The local dev session is currently running."
513
+ : "The local test run is currently running.",
514
+ status: "running",
515
+ };
516
+ case "exited":
517
+ return {
518
+ ...base,
519
+ detail: "The last local dev session exited cleanly.",
520
+ status: "exited",
521
+ };
522
+ case "passed":
523
+ return {
524
+ ...base,
525
+ detail: "The last local test run passed.",
526
+ status: "passed",
527
+ };
528
+ case "failed":
529
+ return {
530
+ ...base,
531
+ detail: command === "dev"
532
+ ? "The last local dev session failed."
533
+ : "The last local test run failed.",
534
+ status: "failed",
535
+ };
536
+ case "interrupted":
537
+ return {
538
+ ...base,
539
+ detail: command === "dev"
540
+ ? "The last local dev session ended due to an interrupt signal."
541
+ : "The last local test run ended due to an interrupt signal.",
542
+ status: "interrupted",
543
+ };
544
+ }
545
+ }
546
+ function resolveLocalRuntimeActivityGraphStatus(recordedDigest, currentCompileGraphDigest) {
547
+ if (!recordedDigest || !currentCompileGraphDigest) {
548
+ return "missing";
549
+ }
550
+ return recordedDigest === currentCompileGraphDigest ? "current" : "stale";
551
+ }
552
+ function buildLocalRuntimeCompileGraphState(context, compileGraphPath) {
553
+ if (context.persistedCompileGraphCorrupted) {
554
+ return {
555
+ detail: "The persisted compile graph is malformed. Regenerate it with `minutework compile` before depending on it.",
556
+ path: compileGraphPath,
557
+ status: "corrupted",
558
+ };
559
+ }
560
+ if (context.persistedCompileGraphCurrent) {
561
+ return {
562
+ detail: "The persisted compile graph matches the current selected-input and config digests.",
563
+ path: compileGraphPath,
564
+ status: "current",
565
+ };
566
+ }
567
+ if (context.compilerStaticAnalysis.selectedInput === null) {
568
+ return {
569
+ detail: "The compile graph is blocked until the workspace has exactly one supported schema input.",
570
+ path: compileGraphPath,
571
+ status: "blocked",
572
+ };
573
+ }
574
+ if (context.compileGraphArtifactExists) {
575
+ return {
576
+ detail: "A persisted compile graph exists on disk, but it is stale relative to the current config or selected-input digests. Refresh it with `minutework compile`.",
577
+ path: compileGraphPath,
578
+ status: "stale",
579
+ };
580
+ }
581
+ return {
582
+ detail: "No compile graph is persisted yet. Generate the canonical local sandbox artifact with `minutework compile`.",
583
+ path: compileGraphPath,
584
+ status: "missing",
585
+ };
586
+ }
587
+ function buildSchemaCompileSurface(context, compileGraphPath) {
588
+ if (context.persistedCompileGraphCurrent) {
589
+ return {
590
+ detail: "The canonical compile graph is current and ready for local sandbox commands.",
591
+ id: "schema_compile",
592
+ path: compileGraphPath,
593
+ status: "ready",
594
+ };
595
+ }
596
+ if (context.compilerStaticAnalysis.selectedInput === null) {
597
+ return {
598
+ detail: "The canonical compile graph is blocked until the workspace has exactly one supported schema input.",
599
+ id: "schema_compile",
600
+ path: compileGraphPath,
601
+ status: "blocked",
602
+ };
603
+ }
604
+ return {
605
+ detail: "The canonical compile graph must be refreshed from the current workspace source before sandbox commands can run.",
606
+ id: "schema_compile",
607
+ path: compileGraphPath,
608
+ status: "blocked",
609
+ };
610
+ }
611
+ function buildStarterSurface(context, id) {
612
+ const isTenantApp = id === "tenant_app";
613
+ const starter = isTenantApp ? context.config.starters.tenantApp : context.config.starters.sidecar;
614
+ const starterExists = isTenantApp ? context.tenantAppStarterExists : context.sidecarStarterExists;
615
+ const starterLabel = isTenantApp ? "tenant-app" : "sidecar";
616
+ const absolutePath = path.join(context.workspaceRoot, starter.path);
617
+ if (!starter.enabled) {
618
+ return {
619
+ detail: `The ${starterLabel} starter is not enabled for this workspace.`,
620
+ id,
621
+ path: absolutePath,
622
+ status: "disabled",
623
+ };
624
+ }
625
+ if (!starterExists) {
626
+ return {
627
+ detail: `The configured ${starterLabel} starter path is missing on disk. Restore it or materialize a new workspace before using this local sandbox surface.`,
628
+ id,
629
+ path: absolutePath,
630
+ status: "missing",
631
+ };
632
+ }
633
+ if (!context.persistedCompileGraphCurrent) {
634
+ return {
635
+ detail: `The ${starterLabel} surface is configured, but it waits on a current canonical compile graph before local sandbox commands run.`,
636
+ id,
637
+ path: absolutePath,
638
+ status: "blocked",
639
+ };
640
+ }
641
+ return {
642
+ detail: `The ${starterLabel} surface is configured and can participate in the local sandbox loop.`,
643
+ id,
644
+ path: absolutePath,
645
+ status: "ready",
646
+ };
647
+ }
648
+ function collectPreviewSurfaceIssueCodes(surfaces) {
649
+ const issueCodes = [];
650
+ const tenantAppSurface = surfaces.find((surface) => surface.id === "tenant_app");
651
+ const sidecarSurface = surfaces.find((surface) => surface.id === "sidecar");
652
+ if (tenantAppSurface?.status === "missing") {
653
+ issueCodes.push("workspace.runtime.tenant_app_missing");
654
+ }
655
+ if (sidecarSurface?.status === "missing") {
656
+ issueCodes.push("workspace.runtime.sidecar_missing");
657
+ }
658
+ if ((tenantAppSurface?.status ?? "disabled") === "disabled" && (sidecarSurface?.status ?? "disabled") === "disabled") {
659
+ issueCodes.push("workspace.runtime.preview_surface_missing");
660
+ }
661
+ return issueCodes;
662
+ }
663
+ function buildSchemaPreflightSteps(context) {
664
+ return [
665
+ {
666
+ args: ["validate"],
667
+ cwd: context.workspaceRoot,
668
+ detail: "Validate the selected schema input before starting local preview surfaces.",
669
+ id: "validate",
670
+ program: "minutework",
671
+ },
672
+ {
673
+ args: ["compile"],
674
+ cwd: context.workspaceRoot,
675
+ detail: "Refresh the canonical compile graph used by the local sandbox.",
676
+ id: "compile",
677
+ program: "minutework",
678
+ },
679
+ ];
680
+ }
681
+ function buildSchemaTestSteps(context) {
682
+ return [
683
+ ...buildSchemaPreflightSteps(context),
684
+ {
685
+ args: ["codegen"],
686
+ cwd: context.workspaceRoot,
687
+ detail: "Refresh deterministic OpenAPI and client-type outputs for local sandbox and test workflows.",
688
+ id: "codegen",
689
+ program: "minutework",
690
+ },
691
+ ];
692
+ }
693
+ function buildLocalRuntimePreviewSteps(context) {
694
+ const steps = [];
695
+ if (context.config.starters.tenantApp.enabled && context.tenantAppStarterExists) {
696
+ steps.push({
697
+ args: ["--dir", context.config.starters.tenantApp.path, "dev"],
698
+ cwd: context.workspaceRoot,
699
+ detail: "Start the tenant-app preview surface.",
700
+ id: "tenant_app_dev",
701
+ program: "pnpm",
702
+ });
703
+ }
704
+ if (context.config.starters.sidecar.enabled && context.sidecarStarterExists) {
705
+ steps.push({
706
+ args: ["run", "uvicorn", "fastapi_sidecar.main:app", "--reload"],
707
+ cwd: path.join(context.workspaceRoot, context.config.starters.sidecar.path),
708
+ detail: "Start the sidecar preview surface.",
709
+ id: "sidecar_dev",
710
+ program: "poetry",
711
+ });
712
+ }
713
+ return steps;
714
+ }
715
+ function buildLocalRuntimeTestSteps(context) {
716
+ const steps = [];
717
+ if (context.config.starters.tenantApp.enabled && context.tenantAppStarterExists) {
718
+ steps.push({
719
+ args: ["--dir", context.config.starters.tenantApp.path, "test"],
720
+ cwd: context.workspaceRoot,
721
+ detail: "Run the tenant-app local test suite.",
722
+ id: "tenant_app_test",
723
+ program: "pnpm",
724
+ });
725
+ }
726
+ if (context.config.starters.sidecar.enabled && context.sidecarStarterExists) {
727
+ steps.push({
728
+ args: ["run", "pytest"],
729
+ cwd: path.join(context.workspaceRoot, context.config.starters.sidecar.path),
730
+ detail: "Run the sidecar local test suite.",
731
+ id: "sidecar_test",
732
+ program: "poetry",
733
+ });
734
+ }
735
+ return steps;
736
+ }
737
+ export function collectDeployStatus(context) {
738
+ const binding = context.bindingStatus.binding;
739
+ const activeEnvironment = binding?.environment ?? null;
740
+ const compiledRelease = resolveCompiledPreviewReleaseMetadata(context.compilerAnalysis.compileGraph, binding?.propertyKey ?? null);
741
+ const previewState = resolvePreviewDeployStateForBinding(context.previewDeployState, binding);
742
+ const previewPrerequisiteCodes = collectDeployPrerequisiteCodes({
743
+ binding,
744
+ compiledRelease,
745
+ context,
746
+ });
747
+ const previewTarget = buildPreviewDeployTargetStatus({
748
+ binding,
749
+ compiledRelease,
750
+ context,
751
+ currentTarget: activeEnvironment === "preview",
752
+ previewState,
753
+ prerequisiteCodes: previewPrerequisiteCodes,
754
+ });
755
+ const liveTarget = buildLiveDeployTargetStatus(context, activeEnvironment === "live");
756
+ return {
757
+ status: previewTarget.status === "deployed"
758
+ ? "deployed"
759
+ : previewTarget.status === "failed"
760
+ ? "failed"
761
+ : previewTarget.status === "ready"
762
+ ? "ready"
763
+ : "blocked",
764
+ targetContext: {
765
+ activeEnvironment,
766
+ platformBaseUrl: binding?.platformBaseUrl ?? context.authStatus.profile?.platformBaseUrl ?? null,
767
+ propertyKey: binding?.propertyKey ?? null,
768
+ releaseClass: binding?.releaseClass ?? null,
769
+ tenantId: binding?.tenantId ?? context.authStatus.profile?.tenantId ?? null,
770
+ tenantSlug: binding?.tenantSlug ?? context.authStatus.profile?.tenantSlug ?? null,
771
+ workspaceName: binding?.workspaceName ?? context.authStatus.profile?.tenantName ?? null,
772
+ },
773
+ targets: [previewTarget, liveTarget],
774
+ workspaceRoot: context.workspaceRoot,
775
+ };
776
+ }
777
+ export function collectWorkspaceDoctor(context) {
778
+ const diagnostics = collectDoctorDiagnostics(context);
779
+ const recommendedActions = buildRecommendedActions(diagnostics);
780
+ return {
781
+ diagnostics,
782
+ recommendedActions,
783
+ summary: {
784
+ errorCount: diagnostics.filter((item) => item.severity === "error").length,
785
+ infoCount: diagnostics.filter((item) => item.severity === "info").length,
786
+ warningCount: diagnostics.filter((item) => item.severity === "warning").length,
787
+ },
788
+ workspaceRoot: context.workspaceRoot,
789
+ };
790
+ }
791
+ export function collectCapabilityInventory(context) {
792
+ const localRuntimeStatus = collectLocalRuntimeStatus(context);
793
+ const deployStatus = collectDeployStatus(context);
794
+ const previewTarget = deployStatus.targets.find((target) => target.environment === "preview") ??
795
+ buildPreviewDeployTargetStatus({
796
+ binding: null,
797
+ compiledRelease: null,
798
+ context,
799
+ currentTarget: false,
800
+ previewState: null,
801
+ prerequisiteCodes: [],
802
+ });
803
+ const liveTarget = deployStatus.targets.find((target) => target.environment === "live") ??
804
+ buildLiveDeployTargetStatus(context, false);
805
+ const enabledStarters = [
806
+ context.config.starters.tenantApp.enabled ? "tenant-app" : null,
807
+ context.config.starters.sidecar.enabled ? "sidecar" : null,
808
+ ].filter((starter) => starter !== null);
809
+ const compileGraph = context.compilerAnalysis.compileGraph;
810
+ const compileGraphDocumentCounts = {
811
+ actionManifests: compileGraph?.documents.actionManifests.length ?? 0,
812
+ appManifests: compileGraph?.documents.appManifests.length ?? 0,
813
+ flowManifests: compileGraph?.documents.flowManifests.length ?? 0,
814
+ ontologyMappingManifests: compileGraph?.documents.ontologyMappingManifests.length ?? 0,
815
+ projectionContracts: compileGraph?.documents.projectionContracts.length ?? 0,
816
+ queryManifests: compileGraph?.documents.queryManifests.length ?? 0,
817
+ releaseMetadata: compileGraph?.documents.releaseMetadata.length ?? 0,
818
+ routeManifests: compileGraph?.documents.routeManifests.length ?? 0,
819
+ schemaDefinitions: compileGraph?.documents.schemaDefinitions.length ?? 0,
820
+ skillManifests: compileGraph?.documents.skillManifests.length ?? 0,
821
+ };
822
+ const unknowns = buildCapabilityInventoryUnknowns(context);
823
+ return {
824
+ alphaConstraints: {
825
+ deferred: [
826
+ "additional local coding engines beyond Claude",
827
+ "sidecar/runtime-backed deploy workflows from the hosted alpha lane",
828
+ "runtime-local Builder host work initiated from the developer-local broker lane",
829
+ ],
830
+ detail: "Generated workspaces can inspect local authoring, compile-graph, and preview deploy posture, but they do not imply full live runtime descriptor or live-public deployment coverage.",
831
+ hostedPreviewDeploy: "Hosted preview/deploy remains narrower than local authoring: the current external alpha lane centers on tenant-app preview deploys and does not imply sidecar or runtime-backed deploy support.",
832
+ localAuthoring: "Developer-local MinuteWork authoring currently supports tenant-app, sidecar, or both through the broker plus the local sandbox/workspace MCP lane.",
833
+ supportedLocalAuthoringSurfaces: ["tenant-app", "sidecar", "both"],
834
+ },
835
+ authAndBindingInventory: {
836
+ authStatus: context.authStatus.status,
837
+ bindingStatus: context.bindingStatus.status,
838
+ detail: buildCapabilityInventoryAuthDetail(context),
839
+ matchesActiveAuthProfile: context.bindingStatus.matchesActiveAuthProfile,
840
+ platformBaseUrl: context.bindingStatus.binding?.platformBaseUrl ??
841
+ context.authStatus.profile?.platformBaseUrl ??
842
+ null,
843
+ },
844
+ capabilityGapReportContract: buildCapabilityGapReportContract(context),
845
+ compileGraphInventory: {
846
+ artifactPath: localRuntimeStatus.compileGraph.path,
847
+ detail: compileGraph !== null
848
+ ? `The compile graph is the authoritative local declarative inventory for schemas, manifests, and release metadata in this workspace. ${localRuntimeStatus.compileGraph.detail}`
849
+ : `No authoritative local compile graph is currently available. ${localRuntimeStatus.compileGraph.detail}`,
850
+ documentCounts: compileGraphDocumentCounts,
851
+ selectedInput: context.compilerAnalysis.validateReport.selectedInput,
852
+ status: localRuntimeStatus.compileGraph.status,
853
+ },
854
+ knownLocalSurface: {
855
+ builderGuidance: {
856
+ claudeMdPath: path.join(context.workspaceRoot, "CLAUDE.md"),
857
+ detail: "Generated Builder guidance is exported into the workspace root as CLAUDE.md plus the skills/ directory.",
858
+ skillsDirectoryPath: path.join(context.workspaceRoot, "skills"),
859
+ },
860
+ contractualDefaults: [
861
+ "`tenant-app` is the combined public plus private web starter.",
862
+ "`mw.core.site` is a first-party baseline runtime capability by architecture contract.",
863
+ "Generated workspaces are developer-local authoring clients, not live runtime descriptors.",
864
+ "Anonymous live delivery should prefer published snapshots or hosted release paths.",
865
+ ],
866
+ workspaceMcpTools: [...WORKSPACE_MCP_TOOL_SPECS],
867
+ },
868
+ publicationInventory: {
869
+ activeEnvironment: deployStatus.targetContext.activeEnvironment,
870
+ detail: "Preview/publication posture is derived from repo binding, compiled release metadata, and persisted preview receipts. Live deploy remains intentionally blocked in the current external alpha.",
871
+ liveTarget,
872
+ previewTarget,
873
+ },
874
+ recommendedNextChecks: buildCapabilityInventoryRecommendedChecks(context, localRuntimeStatus, unknowns),
875
+ starterSurfaceInventory: {
876
+ detail: enabledStarters.length > 0
877
+ ? `Enabled local implementation surfaces: ${enabledStarters.join(", ")}. Sandbox readiness depends on both starter paths and a current compile graph.`
878
+ : "No local implementation surfaces are enabled for this workspace.",
879
+ enabledStarters,
880
+ localRuntimeSurfaces: localRuntimeStatus.surfaces,
881
+ },
882
+ unknowns,
883
+ workspaceRoot: context.workspaceRoot,
884
+ };
885
+ }
886
+ function buildArtifactStatus(context, id, relativePathFromWorkspaceRoot, prerequisiteCodes) {
887
+ const canonicalPath = path.join(context.workspaceRoot, relativePathFromWorkspaceRoot);
888
+ const compileEligible = context.compilerAnalysis.validateReport.compileEligible;
889
+ const existsOnDisk = id === "compile_graph"
890
+ ? context.compileGraphArtifactExists
891
+ : id === "openapi"
892
+ ? context.openapiArtifactExists
893
+ : context.clientTypesArtifactExists;
894
+ if (id === "compile_graph" && existsOnDisk && context.persistedCompileGraphCorrupted) {
895
+ return {
896
+ canonicalPath,
897
+ detail: "A compile graph artifact exists on disk, but it is malformed. Regenerate it with `minutework compile`.",
898
+ exists: true,
899
+ id,
900
+ prerequisiteCodes,
901
+ status: "blocked",
902
+ };
903
+ }
904
+ if (id === "compile_graph" && existsOnDisk && context.persistedCompileGraphCurrent) {
905
+ return {
906
+ canonicalPath,
907
+ detail: "This generated artifact is present on disk for the current workspace.",
908
+ exists: true,
909
+ id,
910
+ prerequisiteCodes,
911
+ status: "available",
912
+ };
913
+ }
914
+ if (id === "compile_graph") {
915
+ return {
916
+ canonicalPath,
917
+ detail: compileEligible
918
+ ? existsOnDisk
919
+ ? "A compile graph exists on disk, but it no longer matches the current source digests. Rerun `minutework compile`."
920
+ : "The compile graph can be generated from the current local schema source by running `minutework compile`."
921
+ : existsOnDisk
922
+ ? "A compile graph exists on disk, but the current local schema source no longer validates cleanly."
923
+ : "The compile graph is blocked until the local schema source validates cleanly.",
924
+ exists: existsOnDisk,
925
+ id,
926
+ prerequisiteCodes,
927
+ status: compileEligible ? "ready" : "blocked",
928
+ };
929
+ }
930
+ if (!compileEligible) {
931
+ return {
932
+ canonicalPath,
933
+ detail: existsOnDisk
934
+ ? "A generated artifact exists on disk, but the current local schema source no longer validates cleanly."
935
+ : "Generated codegen artifacts are blocked until the local schema source validates cleanly.",
936
+ exists: existsOnDisk,
937
+ id,
938
+ prerequisiteCodes,
939
+ status: "blocked",
940
+ };
941
+ }
942
+ if (!context.persistedCompileGraphCurrent) {
943
+ return {
944
+ canonicalPath,
945
+ detail: existsOnDisk
946
+ ? "A generated artifact exists on disk, but the canonical compile graph is missing or stale. Rerun `minutework codegen`."
947
+ : "Generated codegen artifacts become available once the canonical compile graph is current and `minutework codegen` has been run.",
948
+ exists: existsOnDisk,
949
+ id,
950
+ prerequisiteCodes,
951
+ status: "ready",
952
+ };
953
+ }
954
+ const artifactIsCurrent = id === "openapi" ? context.persistedOpenapiCurrent : context.persistedClientTypesCurrent;
955
+ const artifactIsCorrupted = id === "openapi" ? context.persistedOpenapiCorrupted : false;
956
+ if (existsOnDisk && artifactIsCurrent) {
957
+ return {
958
+ canonicalPath,
959
+ detail: "This generated artifact is present on disk for the current compile graph.",
960
+ exists: true,
961
+ id,
962
+ prerequisiteCodes,
963
+ status: "available",
964
+ };
965
+ }
966
+ return {
967
+ canonicalPath,
968
+ detail: existsOnDisk
969
+ ? artifactIsCorrupted
970
+ ? "A generated OpenAPI artifact exists on disk, but it is malformed. Regenerate it with `minutework codegen`."
971
+ : "A generated artifact exists on disk, but it no longer matches the current compile graph. Rerun `minutework codegen`."
972
+ : "This generated artifact can be produced from the current compile graph by running `minutework codegen`.",
973
+ exists: existsOnDisk,
974
+ id,
975
+ prerequisiteCodes,
976
+ status: "ready",
977
+ };
978
+ }
979
+ function buildAuthStatus(authRecord, authRoot) {
980
+ if (!authRecord.profile) {
981
+ return {
982
+ authRoot,
983
+ currentProfilePath: authRecord.currentProfilePath,
984
+ expiresInSeconds: null,
985
+ profile: null,
986
+ profilePath: authRecord.profilePath,
987
+ status: "missing",
988
+ };
989
+ }
990
+ const profile = sanitizeAuthProfile(authRecord.profile);
991
+ const expiresAtMs = Date.parse(profile.expiresAt);
992
+ const expiresInSeconds = Number.isNaN(expiresAtMs)
993
+ ? null
994
+ : Math.max(Math.floor((expiresAtMs - Date.now()) / 1000), 0);
995
+ return {
996
+ authRoot,
997
+ currentProfilePath: authRecord.currentProfilePath,
998
+ expiresInSeconds,
999
+ profile,
1000
+ profilePath: authRecord.profilePath,
1001
+ status: expiresInSeconds === 0 ? "expired" : "active",
1002
+ };
1003
+ }
1004
+ function buildBindingStatus(bindingPath, binding, activeAuthProfile) {
1005
+ return {
1006
+ binding,
1007
+ exists: binding !== null,
1008
+ matchesActiveAuthProfile: binding && activeAuthProfile ? binding.authProfileId === activeAuthProfile.id : null,
1009
+ path: bindingPath,
1010
+ status: binding ? "linked" : "missing",
1011
+ };
1012
+ }
1013
+ function buildPlatformMismatchDiagnostics(activeAuthProfile, resolvedWorkspaceCliConfig) {
1014
+ if (!activeAuthProfile) {
1015
+ return [];
1016
+ }
1017
+ const normalizedAuthPlatformBaseUrl = activeAuthProfile.platformBaseUrl.replace(/\/+$/, "");
1018
+ if (normalizedAuthPlatformBaseUrl === resolvedWorkspaceCliConfig.platformBaseUrl) {
1019
+ return [];
1020
+ }
1021
+ return [
1022
+ {
1023
+ action: buildPlatformMismatchAction(resolvedWorkspaceCliConfig),
1024
+ code: "workspace.auth.platform_mismatch",
1025
+ detail: `The active CLI auth profile targets ${normalizedAuthPlatformBaseUrl}, but this workspace resolves the CLI platform from ${describeResolvedPlatformSource(resolvedWorkspaceCliConfig)} as ${resolvedWorkspaceCliConfig.platformBaseUrl}.`,
1026
+ severity: "warning",
1027
+ summary: "Active CLI auth profile targets a different platform.",
1028
+ },
1029
+ ];
1030
+ }
1031
+ function buildPreviewDeployTargetStatus(options) {
1032
+ const previewUrl = normalizeOptionalString(options.previewState?.currentPreviewUrl);
1033
+ const lastTerminalReceipt = options.previewState?.lastTerminalReceipt ?? null;
1034
+ if (!options.binding) {
1035
+ return {
1036
+ canonicalPath: options.context.previewDeployStatePath,
1037
+ compiledRelease: null,
1038
+ currentPreviewUrl: previewUrl,
1039
+ currentTarget: options.currentTarget,
1040
+ detail: "Preview deploy status is unavailable until the workspace is linked to a tenant and published-site property.",
1041
+ environment: "preview",
1042
+ lastRequestedAt: null,
1043
+ lastRequestedReceiptId: null,
1044
+ lastRequestedReleaseKey: null,
1045
+ lastTerminalReceipt,
1046
+ propertyKey: null,
1047
+ prerequisiteCodes: options.prerequisiteCodes,
1048
+ releaseClass: null,
1049
+ status: "blocked",
1050
+ };
1051
+ }
1052
+ if (!options.compiledRelease) {
1053
+ return {
1054
+ canonicalPath: options.context.previewDeployStatePath,
1055
+ compiledRelease: null,
1056
+ currentPreviewUrl: previewUrl,
1057
+ currentTarget: options.currentTarget,
1058
+ detail: "The linked preview target does not have current hosted release metadata. Re-run `minutework compile` after confirming tenant-app preview env defaults.",
1059
+ environment: "preview",
1060
+ lastRequestedAt: options.previewState?.lastRequestedAt ?? null,
1061
+ lastRequestedReceiptId: options.previewState?.lastRequestedReceiptId ?? null,
1062
+ lastRequestedReleaseKey: options.previewState?.lastRequestedReleaseKey ?? null,
1063
+ lastTerminalReceipt,
1064
+ propertyKey: options.binding.propertyKey,
1065
+ prerequisiteCodes: options.prerequisiteCodes,
1066
+ releaseClass: options.binding.releaseClass,
1067
+ status: "blocked",
1068
+ };
1069
+ }
1070
+ if (lastTerminalReceipt?.deploymentState === "activated" && previewUrl) {
1071
+ return {
1072
+ canonicalPath: options.context.previewDeployStatePath,
1073
+ compiledRelease: options.compiledRelease,
1074
+ currentPreviewUrl: previewUrl,
1075
+ currentTarget: options.currentTarget,
1076
+ detail: "The linked preview target has a recorded activated deploy and a current preview URL.",
1077
+ environment: "preview",
1078
+ lastRequestedAt: options.previewState?.lastRequestedAt ?? null,
1079
+ lastRequestedReceiptId: options.previewState?.lastRequestedReceiptId ?? null,
1080
+ lastRequestedReleaseKey: options.previewState?.lastRequestedReleaseKey ?? null,
1081
+ lastTerminalReceipt,
1082
+ propertyKey: options.binding.propertyKey,
1083
+ prerequisiteCodes: options.prerequisiteCodes,
1084
+ releaseClass: options.binding.releaseClass,
1085
+ status: "deployed",
1086
+ };
1087
+ }
1088
+ if (lastTerminalReceipt?.deploymentState === "failed" ||
1089
+ lastTerminalReceipt?.deploymentState === "rolled_back") {
1090
+ return {
1091
+ canonicalPath: options.context.previewDeployStatePath,
1092
+ compiledRelease: options.compiledRelease,
1093
+ currentPreviewUrl: previewUrl,
1094
+ currentTarget: options.currentTarget,
1095
+ detail: lastTerminalReceipt.deploymentState === "rolled_back"
1096
+ ? "The last recorded preview deploy rolled back to the previous preview target."
1097
+ : "The last recorded preview deploy failed.",
1098
+ environment: "preview",
1099
+ lastRequestedAt: options.previewState?.lastRequestedAt ?? null,
1100
+ lastRequestedReceiptId: options.previewState?.lastRequestedReceiptId ?? null,
1101
+ lastRequestedReleaseKey: options.previewState?.lastRequestedReleaseKey ?? null,
1102
+ lastTerminalReceipt,
1103
+ propertyKey: options.binding.propertyKey,
1104
+ prerequisiteCodes: options.prerequisiteCodes,
1105
+ releaseClass: options.binding.releaseClass,
1106
+ status: "failed",
1107
+ };
1108
+ }
1109
+ return {
1110
+ canonicalPath: options.context.previewDeployStatePath,
1111
+ compiledRelease: options.compiledRelease,
1112
+ currentPreviewUrl: previewUrl,
1113
+ currentTarget: options.currentTarget,
1114
+ detail: options.context.previewDeployStateCorrupted
1115
+ ? "The preview target is configured, but the persisted preview deploy state is malformed. The next deploy will replace it."
1116
+ : options.previewState?.lastRequestedReceiptId
1117
+ ? "The preview target is configured and has recorded deploy history."
1118
+ : "The preview target is configured and ready for `minutework deploy --preview`.",
1119
+ environment: "preview",
1120
+ lastRequestedAt: options.previewState?.lastRequestedAt ?? null,
1121
+ lastRequestedReceiptId: options.previewState?.lastRequestedReceiptId ?? null,
1122
+ lastRequestedReleaseKey: options.previewState?.lastRequestedReleaseKey ?? null,
1123
+ lastTerminalReceipt,
1124
+ propertyKey: options.binding.propertyKey,
1125
+ prerequisiteCodes: options.prerequisiteCodes,
1126
+ releaseClass: options.binding.releaseClass,
1127
+ status: "ready",
1128
+ };
1129
+ }
1130
+ function buildLiveDeployTargetStatus(context, currentTarget) {
1131
+ return {
1132
+ canonicalPath: path.join(context.workspaceRoot, context.config.localState.directory, "deploy", "live"),
1133
+ compiledRelease: null,
1134
+ currentPreviewUrl: null,
1135
+ currentTarget,
1136
+ detail: "`minutework deploy --live` remains intentionally blocked for the external alpha.",
1137
+ environment: "live",
1138
+ lastRequestedAt: null,
1139
+ lastRequestedReceiptId: null,
1140
+ lastRequestedReleaseKey: null,
1141
+ lastTerminalReceipt: null,
1142
+ propertyKey: context.bindingStatus.binding?.propertyKey ?? null,
1143
+ prerequisiteCodes: ["workspace.deploy.live_unsupported"],
1144
+ releaseClass: context.bindingStatus.binding?.releaseClass ?? null,
1145
+ status: "unsupported",
1146
+ };
1147
+ }
1148
+ function collectDeployPrerequisiteCodes(options) {
1149
+ const codes = [...collectPrerequisiteCodes(options.context)];
1150
+ if (options.binding && !options.compiledRelease) {
1151
+ codes.push("workspace.deploy.release_metadata_missing");
1152
+ }
1153
+ if (options.context.previewDeployStateCorrupted) {
1154
+ codes.push("workspace.deploy.preview_state_corrupted");
1155
+ }
1156
+ return Array.from(new Set(codes));
1157
+ }
1158
+ function resolveCompiledPreviewReleaseMetadata(compileGraph, propertyKey) {
1159
+ if (!compileGraph || !propertyKey) {
1160
+ return null;
1161
+ }
1162
+ for (const document of compileGraph.documents.releaseMetadata) {
1163
+ const parsed = HostedPreviewReleaseMetadataSchema.safeParse(document);
1164
+ if (parsed.success && parsed.data.propertyKey === propertyKey) {
1165
+ return mapCompiledRelease(parsed.data);
1166
+ }
1167
+ }
1168
+ return null;
1169
+ }
1170
+ function mapCompiledRelease(metadata) {
1171
+ return {
1172
+ releaseDigest: metadata.releaseDigest,
1173
+ releaseKey: metadata.releaseKey,
1174
+ runtimeContentRef: metadata.runtimeContentRef,
1175
+ sourceDigest: metadata.sourceDigest,
1176
+ starterId: metadata.starterId,
1177
+ workspaceName: metadata.workspaceName,
1178
+ };
1179
+ }
1180
+ function resolvePreviewDeployStateForBinding(previewDeployState, binding) {
1181
+ if (!previewDeployState || !binding) {
1182
+ return null;
1183
+ }
1184
+ if (previewDeployState.propertyKey !== binding.propertyKey ||
1185
+ previewDeployState.releaseClass !== binding.releaseClass) {
1186
+ return null;
1187
+ }
1188
+ return previewDeployState;
1189
+ }
1190
+ function normalizeOptionalString(value) {
1191
+ if (!value) {
1192
+ return null;
1193
+ }
1194
+ const trimmed = value.trim();
1195
+ return trimmed.length > 0 ? trimmed : null;
1196
+ }
1197
+ function buildDiagnostics(options) {
1198
+ const diagnostics = [];
1199
+ if (!options.cursorConfigExists) {
1200
+ diagnostics.push({
1201
+ action: "Restore `.cursor/mcp.json` or run `minutework workspace sync-assets` for this workspace.",
1202
+ code: "workspace.mcp.cursor_config_missing",
1203
+ detail: "Cursor MCP wiring is missing from the generated workspace contract.",
1204
+ path: options.cursorConfigPath,
1205
+ severity: "warning",
1206
+ summary: "Cursor MCP config is missing.",
1207
+ });
1208
+ }
1209
+ if (!options.claudeDesktopSampleExists) {
1210
+ diagnostics.push({
1211
+ action: "Restore `mcp/claude-desktop.sample.json` or run `minutework workspace sync-assets` for this workspace.",
1212
+ code: "workspace.mcp.claude_sample_missing",
1213
+ detail: "The generated Claude Desktop MCP sample file is missing.",
1214
+ path: options.claudeDesktopSamplePath,
1215
+ severity: "warning",
1216
+ summary: "Claude Desktop MCP sample is missing.",
1217
+ });
1218
+ }
1219
+ if (options.bindingStatus.status === "missing" && !options.bindingStateMalformed) {
1220
+ diagnostics.push({
1221
+ action: "Run `minutework link` from the workspace root.",
1222
+ code: "workspace.binding.missing",
1223
+ detail: "The repo-local binding file does not exist yet.",
1224
+ path: options.bindingStatus.path,
1225
+ severity: "warning",
1226
+ summary: "Workspace is not linked.",
1227
+ });
1228
+ }
1229
+ if (options.authStatus.status === "missing" && !options.authStateMalformed) {
1230
+ diagnostics.push({
1231
+ action: "Run `minutework login` on this machine.",
1232
+ code: "workspace.auth.missing",
1233
+ detail: "No active machine-local CLI auth profile is available.",
1234
+ path: options.authStatus.currentProfilePath,
1235
+ severity: "warning",
1236
+ summary: "No active CLI auth profile.",
1237
+ });
1238
+ }
1239
+ if (options.authStatus.status === "expired") {
1240
+ diagnostics.push({
1241
+ action: "Run `minutework login` to refresh the local auth profile.",
1242
+ code: "workspace.auth.expired",
1243
+ detail: "The active CLI auth profile is present but expired.",
1244
+ path: options.authStatus.profilePath ?? options.authStatus.currentProfilePath,
1245
+ severity: "warning",
1246
+ summary: "Active CLI auth profile is expired.",
1247
+ });
1248
+ }
1249
+ if (options.bindingStatus.status === "linked" &&
1250
+ options.authStatus.profile &&
1251
+ options.bindingStatus.matchesActiveAuthProfile === false) {
1252
+ diagnostics.push({
1253
+ action: "Run `minutework link` or switch the active CLI auth profile.",
1254
+ code: "workspace.auth.binding_mismatch",
1255
+ detail: "The repo binding points at a different auth profile than the current active CLI profile.",
1256
+ path: options.bindingStatus.path,
1257
+ severity: "warning",
1258
+ summary: "Binding and active auth profile do not match.",
1259
+ });
1260
+ }
1261
+ if (!options.schemaDirectoryExists && !options.schemaMwExists) {
1262
+ diagnostics.push({
1263
+ action: "Create the schema directory, add a root `schema.mw`, or update `minutework.config.ts`.",
1264
+ code: "workspace.schema.directory_missing",
1265
+ detail: "The configured schema directory does not exist and no root schema.mw alternative was found.",
1266
+ path: options.schemaDirectoryPath,
1267
+ severity: "warning",
1268
+ summary: "Schema directory is missing.",
1269
+ });
1270
+ }
1271
+ if (!options.schemaEntrypointExists && !options.schemaMwExists) {
1272
+ diagnostics.push({
1273
+ action: "Create the configured schema entrypoint, create a root `schema.mw`, or update `minutework.config.ts`.",
1274
+ code: "workspace.schema.entrypoint_missing",
1275
+ detail: "Neither the configured schema entrypoint nor a root schema.mw file exists for this workspace.",
1276
+ path: options.schemaEntrypointPath,
1277
+ severity: "warning",
1278
+ summary: "No supported schema authoring input is present.",
1279
+ });
1280
+ }
1281
+ if (options.persistedCompileGraphCorrupted) {
1282
+ diagnostics.push({
1283
+ action: "Delete the malformed compile graph or rerun `minutework compile` to regenerate it.",
1284
+ code: "workspace.schema.compile_graph_corrupted",
1285
+ detail: "The on-disk compile graph exists but could not be parsed.",
1286
+ severity: "warning",
1287
+ summary: "Compile graph artifact is corrupted.",
1288
+ });
1289
+ }
1290
+ return diagnostics;
1291
+ }
1292
+ function buildRecommendedActions(diagnostics) {
1293
+ const actions = new Set();
1294
+ for (const diagnostic of diagnostics) {
1295
+ if (diagnostic.code === "workspace.auth.missing" ||
1296
+ diagnostic.code === "workspace.auth.expired" ||
1297
+ diagnostic.code === "workspace.auth.pointer_malformed" ||
1298
+ diagnostic.code === "workspace.auth.profile_malformed") {
1299
+ actions.add("Run `minutework login` to refresh the machine-local auth profile.");
1300
+ continue;
1301
+ }
1302
+ if (diagnostic.code === "workspace.binding.missing" || diagnostic.code === "workspace.binding.malformed") {
1303
+ actions.add("Run `minutework link` to bind this repo to the active tenant/environment.");
1304
+ continue;
1305
+ }
1306
+ if (diagnostic.code === "workspace.schema.directory_missing" ||
1307
+ diagnostic.code === "workspace.schema.entrypoint_missing") {
1308
+ actions.add("Create the configured schema files, add a root `schema.mw`, or update `minutework.config.ts`.");
1309
+ continue;
1310
+ }
1311
+ if (diagnostic.code === "workspace.mcp.cursor_config_missing" ||
1312
+ diagnostic.code === "workspace.mcp.claude_sample_missing") {
1313
+ actions.add("Restore the generated MCP wiring files with `minutework workspace sync-assets`.");
1314
+ continue;
1315
+ }
1316
+ if (diagnostic.code === "workspace.auth.binding_mismatch") {
1317
+ actions.add("Align the active CLI auth profile with the repo binding via `minutework link`.");
1318
+ continue;
1319
+ }
1320
+ if (diagnostic.code === "workspace.auth.platform_mismatch") {
1321
+ actions.add("Run `minutework login` for the wired workspace platform, or remove any legacy platform override.");
1322
+ continue;
1323
+ }
1324
+ if (diagnostic.code === "workspace.schema.compile_graph_corrupted") {
1325
+ actions.add("Delete the malformed compile graph or rerun `minutework compile` to regenerate it.");
1326
+ continue;
1327
+ }
1328
+ if (diagnostic.code === "compiler.input.conflict") {
1329
+ actions.add("Remove either the configured TypeScript schema entrypoint or the root `schema.mw` file so the workspace has exactly one schema input.");
1330
+ continue;
1331
+ }
1332
+ if (diagnostic.code === "compiler.input.missing") {
1333
+ actions.add("Create the configured schema files, add a root `schema.mw`, or update `minutework.config.ts`.");
1334
+ continue;
1335
+ }
1336
+ if (diagnostic.code === "compiler.source.export_missing" ||
1337
+ diagnostic.code === "compiler.source.invalid" ||
1338
+ diagnostic.code === "compiler.source.load_failed" ||
1339
+ diagnostic.code === "compiler.source.parse_failed" ||
1340
+ diagnostic.code === "compiler.schema.duplicate_key") {
1341
+ actions.add("Fix the local schema authoring source until `minutework validate` succeeds.");
1342
+ continue;
1343
+ }
1344
+ if (diagnostic.code.startsWith("compiler.")) {
1345
+ actions.add("Fix the local schema authoring source until `minutework validate` succeeds.");
1346
+ }
1347
+ }
1348
+ return [...actions];
1349
+ }
1350
+ function buildCapabilityInventoryAuthDetail(context) {
1351
+ if (context.authStatus.status === "active" && context.bindingStatus.status === "linked") {
1352
+ return "CLI auth and repo binding are both present, so generated-workspace preview/publication context can be evaluated for this tenant/property pair.";
1353
+ }
1354
+ if (context.authStatus.status !== "active" && context.bindingStatus.status !== "linked") {
1355
+ return "The generated workspace is not authenticated or linked yet, so tenant-scoped runtime/publication state remains partially unknown.";
1356
+ }
1357
+ if (context.authStatus.status !== "active") {
1358
+ return "Repo binding exists, but the current machine-local CLI auth profile is missing or expired.";
1359
+ }
1360
+ return "CLI auth exists, but the repo-local binding is missing or malformed.";
1361
+ }
1362
+ function buildCapabilityGapReportContract(context) {
1363
+ if (context.capabilityGapReportCorrupted) {
1364
+ return {
1365
+ canonicalPath: context.capabilityGapReportPath,
1366
+ detail: "A capability gap report file exists, but it is malformed. Repair or remove it before relying on it as structured architecture evidence.",
1367
+ fileStatus: "corrupted",
1368
+ gapCount: null,
1369
+ layerValues: [...CapabilityGapLayerSchema.options],
1370
+ reusabilityValues: [...CapabilityGapReusabilitySchema.options],
1371
+ version: "MinuteWorkCapabilityGapReportV1",
1372
+ };
1373
+ }
1374
+ if (context.capabilityGapReport) {
1375
+ return {
1376
+ canonicalPath: context.capabilityGapReportPath,
1377
+ detail: "A structured capability gap report is already recorded for this workspace and can be extended with new reusable-substrate gaps.",
1378
+ fileStatus: "available",
1379
+ gapCount: context.capabilityGapReport.gaps.length,
1380
+ layerValues: [...CapabilityGapLayerSchema.options],
1381
+ reusabilityValues: [...CapabilityGapReusabilitySchema.options],
1382
+ version: "MinuteWorkCapabilityGapReportV1",
1383
+ };
1384
+ }
1385
+ return {
1386
+ canonicalPath: context.capabilityGapReportPath,
1387
+ detail: "No capability gap report has been recorded yet. Use this file to capture sanitized, reusable-substrate gaps discovered during early tenant builds.",
1388
+ fileStatus: "missing",
1389
+ gapCount: null,
1390
+ layerValues: [...CapabilityGapLayerSchema.options],
1391
+ reusabilityValues: [...CapabilityGapReusabilitySchema.options],
1392
+ version: "MinuteWorkCapabilityGapReportV1",
1393
+ };
1394
+ }
1395
+ function buildCapabilityInventoryUnknowns(context) {
1396
+ const unknowns = [
1397
+ {
1398
+ detail: "Generated workspaces do not expose a live runtime descriptor or capability registry yet.",
1399
+ id: "runtime_descriptor",
1400
+ nextCheck: "Use a linked runtime-capability surface when one exists; until then, treat live runtime descriptor state as unknown.",
1401
+ },
1402
+ {
1403
+ detail: "Installed runtime app-pack, skill, and sidecar activation state is not directly visible from workspace MCP today.",
1404
+ id: "runtime_installed_state",
1405
+ nextCheck: "Treat the compile graph as local desired-state inventory and verify installed state through linked runtime surfaces or human confirmation.",
1406
+ },
1407
+ {
1408
+ detail: "Provider-specific runtime AI readiness and secret-backed service availability are not directly inspectable from the generated workspace.",
1409
+ id: "runtime_provider_readiness",
1410
+ nextCheck: "Inspect linked runtime/provider surfaces or confirm with an operator before inventing new provider infrastructure.",
1411
+ },
1412
+ ];
1413
+ if (context.authStatus.status !== "active" || context.bindingStatus.status !== "linked") {
1414
+ unknowns.push({
1415
+ detail: "Because auth or repo binding is incomplete, the workspace cannot confidently resolve tenant-scoped runtime/publication state.",
1416
+ id: "linked_tenant_runtime",
1417
+ nextCheck: "Run `minutework login` and `minutework link`, then refresh the capability inventory.",
1418
+ });
1419
+ }
1420
+ return unknowns;
1421
+ }
1422
+ function buildCapabilityInventoryRecommendedChecks(context, localRuntimeStatus, unknowns) {
1423
+ const checks = new Set();
1424
+ if (context.authStatus.status !== "active") {
1425
+ checks.add("Run `minutework login` to refresh the machine-local auth profile.");
1426
+ }
1427
+ if (context.bindingStatus.status !== "linked") {
1428
+ checks.add("Run `minutework link` to bind this repo to the active tenant/environment.");
1429
+ }
1430
+ if (!context.persistedCompileGraphCurrent) {
1431
+ checks.add("Run `minutework compile` to refresh the authoritative local compile graph.");
1432
+ }
1433
+ if (context.persistedCompileGraphCurrent &&
1434
+ (!context.persistedOpenapiCurrent || !context.persistedClientTypesCurrent)) {
1435
+ checks.add("Run `minutework codegen` to refresh deterministic OpenAPI and client-type artifacts.");
1436
+ }
1437
+ if (localRuntimeStatus.surfaces.some((surface) => surface.status === "missing")) {
1438
+ checks.add("Restore the missing starter paths or scaffold a fresh workspace before relying on local sandbox surfaces.");
1439
+ }
1440
+ if (context.capabilityGapReportCorrupted) {
1441
+ checks.add("Repair or remove the malformed capability gap report before extending it.");
1442
+ }
1443
+ if (unknowns.length > 0) {
1444
+ checks.add("Treat live runtime descriptor/provider state as unknown until a linked runtime-capability surface exposes it.");
1445
+ }
1446
+ checks.add("If runtime/platform/gateway source trees are available locally, inspect them directly for implemented capability inventory before inventing new substrate.");
1447
+ checks.add("Use `minutework_capability_inventory` plus the more specific workspace MCP tools before assuming runtime capability coverage.");
1448
+ return [...checks];
1449
+ }
1450
+ function collectDoctorDiagnostics(context) {
1451
+ return [
1452
+ ...context.diagnostics,
1453
+ ...context.compilerAnalysis.validateReport.diagnostics.map((diagnostic) => buildCompilerDiagnostic(context, diagnostic)),
1454
+ ];
1455
+ }
1456
+ function buildCompilerDiagnostic(context, diagnostic) {
1457
+ return {
1458
+ action: undefined,
1459
+ code: diagnostic.code,
1460
+ detail: diagnostic.detail,
1461
+ path: diagnostic.path ? path.join(context.workspaceRoot, diagnostic.path) : undefined,
1462
+ severity: diagnostic.severity,
1463
+ summary: diagnostic.summary,
1464
+ };
1465
+ }
1466
+ function buildStarter(id, context) {
1467
+ const starter = buildNormalizedStarterConfig(id, context);
1468
+ return {
1469
+ ...starter,
1470
+ id,
1471
+ };
1472
+ }
1473
+ function buildNormalizedStarterConfig(id, context) {
1474
+ const starter = id === "tenant-app" ? context.config.starters.tenantApp : context.config.starters.sidecar;
1475
+ return {
1476
+ absolutePath: path.join(context.workspaceRoot, starter.path),
1477
+ enabled: starter.enabled,
1478
+ path: starter.path,
1479
+ };
1480
+ }
1481
+ function collectPrerequisiteCodes(context) {
1482
+ return [
1483
+ ...context.diagnostics.filter((item) => item.severity !== "info").map((item) => item.code),
1484
+ ...context.compilerAnalysis.validateReport.diagnostics
1485
+ .filter((item) => item.severity !== "info")
1486
+ .map((item) => item.code),
1487
+ ];
1488
+ }
1489
+ function collectSchemaPrerequisiteCodes(context) {
1490
+ return [
1491
+ ...context.diagnostics
1492
+ .filter((item) => item.severity !== "info" && item.code.startsWith("workspace.schema."))
1493
+ .map((item) => item.code),
1494
+ ...context.compilerAnalysis.validateReport.diagnostics
1495
+ .filter((item) => item.severity !== "info")
1496
+ .map((item) => item.code),
1497
+ ];
1498
+ }
1499
+ function describeResolvedPlatformSource(resolvedWorkspaceCliConfig) {
1500
+ switch (resolvedWorkspaceCliConfig.platformBaseUrlSource) {
1501
+ case "shellEnv":
1502
+ return "exported shell env MW_CLI_PLATFORM_BASE_URL";
1503
+ case "workspaceConfig":
1504
+ return "minutework.config.ts";
1505
+ case "workspaceDefault":
1506
+ return "the MinuteWork local platform wiring";
1507
+ default:
1508
+ return "the workspace";
1509
+ }
1510
+ }
1511
+ function buildPlatformMismatchAction(resolvedWorkspaceCliConfig) {
1512
+ return resolvedWorkspaceCliConfig.hasLegacyPlatformOverride
1513
+ ? `Run \`minutework login --platform ${resolvedWorkspaceCliConfig.platformBaseUrl}\` or remove the legacy platform override.`
1514
+ : "Run `minutework login` to refresh auth for the wired workspace platform.";
1515
+ }
1516
+ async function exists(targetPath) {
1517
+ try {
1518
+ await fs.stat(targetPath);
1519
+ return true;
1520
+ }
1521
+ catch (error) {
1522
+ const errno = error;
1523
+ if (errno.code === "ENOENT") {
1524
+ return false;
1525
+ }
1526
+ throw error;
1527
+ }
1528
+ }
1529
+ async function loadWorkspaceConfig(configPath) {
1530
+ try {
1531
+ return await loadSharedWorkspaceConfig(configPath);
1532
+ }
1533
+ catch (error) {
1534
+ throw new WorkspaceMcpError(`MinuteWork config could not be loaded from ${configPath}: ${error instanceof Error ? error.message : String(error)}`);
1535
+ }
1536
+ }
1537
+ async function loadCurrentAuthProfile(paths) {
1538
+ const diagnostics = [];
1539
+ const currentProfilePath = path.join(paths.authRoot, "current-profile.json");
1540
+ if (!(await exists(currentProfilePath))) {
1541
+ return {
1542
+ currentProfilePath,
1543
+ diagnostics,
1544
+ profile: null,
1545
+ profilePath: null,
1546
+ };
1547
+ }
1548
+ let pointer;
1549
+ try {
1550
+ pointer = await readJsonFile(currentProfilePath, z.object({
1551
+ profileId: z.string().min(1),
1552
+ platformBaseUrl: z.string().optional(),
1553
+ }), "CLI auth profile pointer");
1554
+ }
1555
+ catch (error) {
1556
+ if (error instanceof WorkspaceMcpError) {
1557
+ diagnostics.push({
1558
+ action: "Run `minutework login` to recreate the active CLI auth profile.",
1559
+ code: "workspace.auth.pointer_malformed",
1560
+ detail: error.message,
1561
+ path: currentProfilePath,
1562
+ severity: "warning",
1563
+ summary: "CLI auth profile pointer is malformed.",
1564
+ });
1565
+ return {
1566
+ currentProfilePath,
1567
+ diagnostics,
1568
+ profile: null,
1569
+ profilePath: null,
1570
+ };
1571
+ }
1572
+ throw error;
1573
+ }
1574
+ const profilePath = path.join(paths.authRoot, `${pointer.profileId}.json`);
1575
+ if (!(await exists(profilePath))) {
1576
+ return {
1577
+ currentProfilePath,
1578
+ diagnostics,
1579
+ profile: null,
1580
+ profilePath,
1581
+ };
1582
+ }
1583
+ try {
1584
+ const profile = await readJsonFile(profilePath, StoredAuthProfileSchema, "CLI auth profile");
1585
+ return {
1586
+ currentProfilePath,
1587
+ diagnostics,
1588
+ profile,
1589
+ profilePath,
1590
+ };
1591
+ }
1592
+ catch (error) {
1593
+ if (error instanceof WorkspaceMcpError) {
1594
+ diagnostics.push({
1595
+ action: "Run `minutework login` to refresh the malformed CLI auth profile.",
1596
+ code: "workspace.auth.profile_malformed",
1597
+ detail: error.message,
1598
+ path: profilePath,
1599
+ severity: "warning",
1600
+ summary: "CLI auth profile is malformed.",
1601
+ });
1602
+ return {
1603
+ currentProfilePath,
1604
+ diagnostics,
1605
+ profile: null,
1606
+ profilePath,
1607
+ };
1608
+ }
1609
+ throw error;
1610
+ }
1611
+ }
1612
+ async function readJsonFile(targetPath, schema, label) {
1613
+ let raw;
1614
+ try {
1615
+ raw = await fs.readFile(targetPath, "utf8");
1616
+ }
1617
+ catch (error) {
1618
+ const errno = error;
1619
+ if (errno.code === "ENOENT") {
1620
+ throw new WorkspaceMcpError(`${label} is missing at ${targetPath}.`);
1621
+ }
1622
+ throw error;
1623
+ }
1624
+ let parsedJson;
1625
+ try {
1626
+ parsedJson = JSON.parse(raw);
1627
+ }
1628
+ catch (error) {
1629
+ throw new WorkspaceMcpError(`${label} is malformed at ${targetPath}: ${error instanceof Error ? error.message : String(error)}`);
1630
+ }
1631
+ const parsed = schema.safeParse(parsedJson);
1632
+ if (!parsed.success) {
1633
+ throw new WorkspaceMcpError(`${label} is malformed at ${targetPath}: ${parsed.error.issues[0]?.message ?? "unknown error"}`);
1634
+ }
1635
+ return parsed.data;
1636
+ }
1637
+ async function readOptionalWorkspaceBinding(targetPath) {
1638
+ if (!(await exists(targetPath))) {
1639
+ return {
1640
+ binding: null,
1641
+ diagnostics: [],
1642
+ };
1643
+ }
1644
+ try {
1645
+ return {
1646
+ binding: await readJsonFile(targetPath, BindingPayloadSchema, "workspace binding"),
1647
+ diagnostics: [],
1648
+ };
1649
+ }
1650
+ catch (error) {
1651
+ if (error instanceof WorkspaceMcpError) {
1652
+ return {
1653
+ binding: null,
1654
+ diagnostics: [
1655
+ {
1656
+ action: "Run `minutework link` to recreate the repo-local binding.",
1657
+ code: "workspace.binding.malformed",
1658
+ detail: error.message,
1659
+ path: targetPath,
1660
+ severity: "warning",
1661
+ summary: "Workspace binding is malformed.",
1662
+ },
1663
+ ],
1664
+ };
1665
+ }
1666
+ throw error;
1667
+ }
1668
+ }
1669
+ async function readOptionalPreviewDeployState(targetPath) {
1670
+ if (!(await exists(targetPath))) {
1671
+ return {
1672
+ corrupted: false,
1673
+ state: null,
1674
+ };
1675
+ }
1676
+ try {
1677
+ return {
1678
+ corrupted: false,
1679
+ state: await readJsonFile(targetPath, PreviewDeployStateSchema, "preview deploy state"),
1680
+ };
1681
+ }
1682
+ catch (error) {
1683
+ if (error instanceof WorkspaceMcpError) {
1684
+ return {
1685
+ corrupted: true,
1686
+ state: null,
1687
+ };
1688
+ }
1689
+ throw error;
1690
+ }
1691
+ }
1692
+ async function readOptionalCapabilityGapReport(targetPath) {
1693
+ if (!(await exists(targetPath))) {
1694
+ return {
1695
+ corrupted: false,
1696
+ report: null,
1697
+ };
1698
+ }
1699
+ try {
1700
+ return {
1701
+ corrupted: false,
1702
+ report: await readJsonFile(targetPath, CapabilityGapReportSchema, "capability gap report"),
1703
+ };
1704
+ }
1705
+ catch (error) {
1706
+ if (error instanceof WorkspaceMcpError) {
1707
+ return {
1708
+ corrupted: true,
1709
+ report: null,
1710
+ };
1711
+ }
1712
+ throw error;
1713
+ }
1714
+ }
1715
+ async function readOptionalCompileGraph(targetPath) {
1716
+ try {
1717
+ const raw = await fs.readFile(targetPath, "utf8");
1718
+ return {
1719
+ corrupted: false,
1720
+ digest: createSha256Digest(raw),
1721
+ graph: CompileGraphSchema.parse(JSON.parse(raw)),
1722
+ };
1723
+ }
1724
+ catch (error) {
1725
+ if (error instanceof WorkspaceMcpError) {
1726
+ return {
1727
+ corrupted: true,
1728
+ digest: null,
1729
+ graph: null,
1730
+ };
1731
+ }
1732
+ if (error instanceof z.ZodError || error instanceof SyntaxError) {
1733
+ return {
1734
+ corrupted: true,
1735
+ digest: null,
1736
+ graph: null,
1737
+ };
1738
+ }
1739
+ throw error;
1740
+ }
1741
+ }
1742
+ function isPersistedCompileGraphCurrent(persistedGraph, staticAnalysis) {
1743
+ if (!persistedGraph || !staticAnalysis.selectedInput) {
1744
+ return false;
1745
+ }
1746
+ return (persistedGraph.sources.config.sha256 === staticAnalysis.configDigest &&
1747
+ persistedGraph.sources.selectedInput.kind === staticAnalysis.selectedInput.kind &&
1748
+ persistedGraph.sources.selectedInput.path === staticAnalysis.selectedInput.path &&
1749
+ persistedGraph.sources.selectedInput.sha256 === staticAnalysis.selectedInput.sha256);
1750
+ }
1751
+ async function inspectGeneratedJsonArtifact(targetPath, expectedContents) {
1752
+ if (!(await exists(targetPath))) {
1753
+ return {
1754
+ corrupted: false,
1755
+ current: false,
1756
+ };
1757
+ }
1758
+ const raw = await fs.readFile(targetPath, "utf8");
1759
+ try {
1760
+ JSON.parse(raw);
1761
+ }
1762
+ catch {
1763
+ return {
1764
+ corrupted: true,
1765
+ current: false,
1766
+ };
1767
+ }
1768
+ return {
1769
+ corrupted: false,
1770
+ current: expectedContents !== null && raw === expectedContents,
1771
+ };
1772
+ }
1773
+ async function inspectGeneratedTextArtifact(targetPath, expectedContents) {
1774
+ if (!(await exists(targetPath))) {
1775
+ return {
1776
+ current: false,
1777
+ };
1778
+ }
1779
+ const raw = await fs.readFile(targetPath, "utf8");
1780
+ return {
1781
+ current: expectedContents !== null && raw === expectedContents,
1782
+ };
1783
+ }
1784
+ function formatJson(payload) {
1785
+ return `${JSON.stringify(payload, null, 2)}\n`;
1786
+ }
1787
+ function resolveCliStatePaths(options) {
1788
+ const env = options?.env ?? process.env;
1789
+ const platform = options?.platform ?? process.platform;
1790
+ const home = env.HOME ?? env.USERPROFILE ?? "";
1791
+ const localAppData = env.LOCALAPPDATA ?? "";
1792
+ let minuteworkHome;
1793
+ if (platform === "darwin") {
1794
+ minuteworkHome = path.posix.join(home || "~", "Library", "Application Support", "MinuteWork");
1795
+ }
1796
+ else if (platform === "linux") {
1797
+ minuteworkHome = path.posix.join(home || "~", ".local", "share", "minutework");
1798
+ }
1799
+ else if (platform === "win32") {
1800
+ const windowsBase = localAppData ||
1801
+ (home ? path.win32.join(home, "AppData", "Local") : path.win32.join("~", "AppData", "Local"));
1802
+ minuteworkHome = path.win32.join(windowsBase, "MinuteWork");
1803
+ }
1804
+ else {
1805
+ throw new WorkspaceMcpError(`Unsupported platform for MinuteWork CLI state: ${platform}`);
1806
+ }
1807
+ return {
1808
+ authRoot: joinForPlatform(platform, minuteworkHome, "cli", "auth"),
1809
+ };
1810
+ }
1811
+ async function resolveWorkspaceRoot(options) {
1812
+ const startPath = options.workspacePath
1813
+ ? path.resolve(options.cwd, options.workspacePath)
1814
+ : path.resolve(options.cwd);
1815
+ try {
1816
+ return await discoverCompilerWorkspaceRoot(startPath);
1817
+ }
1818
+ catch (error) {
1819
+ throw new WorkspaceMcpError(error instanceof Error ? error.message : "No MinuteWork workspace was found from the current directory upward.");
1820
+ }
1821
+ }
1822
+ function sanitizeAuthProfile(profile) {
1823
+ return {
1824
+ createdAt: profile.createdAt,
1825
+ expiresAt: profile.expiresAt,
1826
+ id: profile.id,
1827
+ platformBaseUrl: profile.platformBaseUrl,
1828
+ scopeTemplate: profile.scopeTemplate,
1829
+ scopes: profile.scopes,
1830
+ tenantId: profile.tenantId,
1831
+ tenantName: profile.tenantName,
1832
+ tenantSlug: profile.tenantSlug,
1833
+ tokenKind: profile.tokenKind,
1834
+ user: profile.user,
1835
+ };
1836
+ }
1837
+ function joinForPlatform(platform, root, ...segments) {
1838
+ return platform === "win32" ? path.win32.join(root, ...segments) : path.posix.join(root, ...segments);
1839
+ }
1840
+ //# sourceMappingURL=context.js.map