@aiwg/cli 2026.10.3 → 2026.10.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (279) hide show
  1. package/README.md +2 -2
  2. package/agentic/code/providers/model-capabilities.v1.json +96 -4
  3. package/bin/aiwg.mjs +50 -27
  4. package/dist/src/agents/grounding/knowledge-base.js +54 -6
  5. package/dist/src/artifacts/browser-export.js +6 -0
  6. package/dist/src/artifacts/checksum-manifest.js +1 -0
  7. package/dist/src/artifacts/citation-parser.js +173 -92
  8. package/dist/src/artifacts/citation-table-schema.js +59 -0
  9. package/dist/src/artifacts/cli.js +69 -18
  10. package/dist/src/artifacts/corpus-tools/snapshot.js +1 -1
  11. package/dist/src/artifacts/corpus-tools/vision-extract.js +2 -0
  12. package/dist/src/artifacts/corpus-views/ref-parser.js +3 -1
  13. package/dist/src/artifacts/corpus-views/renderers.js +3 -3
  14. package/dist/src/artifacts/discovery-eval.js +6 -4
  15. package/dist/src/artifacts/fortemi-core-query-adapter.js +36 -3
  16. package/dist/src/artifacts/fortemi-core-sync.js +166 -40
  17. package/dist/src/artifacts/framework-capabilities.js +171 -0
  18. package/dist/src/artifacts/framework-index-paths.js +73 -0
  19. package/dist/src/artifacts/framework-source-digest.js +75 -0
  20. package/dist/src/artifacts/index-builder.js +286 -131
  21. package/dist/src/artifacts/index-files.js +132 -18
  22. package/dist/src/artifacts/index-preparation.js +130 -0
  23. package/dist/src/artifacts/index-refresh.js +591 -0
  24. package/dist/src/artifacts/index-worker.mjs +11 -0
  25. package/dist/src/artifacts/prebuilt-build-lock.js +47 -4
  26. package/dist/src/artifacts/prebuilt-deployment.js +46 -0
  27. package/dist/src/artifacts/prebuilt-relations.js +80 -0
  28. package/dist/src/artifacts/query-engine.js +410 -88
  29. package/dist/src/artifacts/relations-builder.js +168 -0
  30. package/dist/src/artifacts/relations.js +148 -0
  31. package/dist/src/artifacts/stats.js +5 -2
  32. package/dist/src/artifacts/types.js +30 -10
  33. package/dist/src/artifacts/watcher.js +1 -1
  34. package/dist/src/auth/credential-store.js +2 -1
  35. package/dist/src/catalog/cli.d.mts +5 -0
  36. package/dist/src/channel/manager.d.mts +20 -0
  37. package/dist/src/channel/manager.mjs +19 -2
  38. package/dist/src/cli/command-log.js +2 -3
  39. package/dist/src/cli/handlers/artifacts.js +3 -0
  40. package/dist/src/cli/handlers/auth.js +3 -0
  41. package/dist/src/cli/handlers/decision.js +19 -12
  42. package/dist/src/cli/handlers/diagnose.js +4 -3
  43. package/dist/src/cli/handlers/effect.js +4 -2
  44. package/dist/src/cli/handlers/evidence.js +3 -0
  45. package/dist/src/cli/handlers/help.js +1 -0
  46. package/dist/src/cli/handlers/index.js +3 -1
  47. package/dist/src/cli/handlers/init.js +4 -1
  48. package/dist/src/cli/handlers/lint.js +5 -0
  49. package/dist/src/cli/handlers/models.js +104 -8
  50. package/dist/src/cli/handlers/ralph-launcher.js +2 -0
  51. package/dist/src/cli/handlers/refresh.js +12 -0
  52. package/dist/src/cli/handlers/resource-versions.js +3 -0
  53. package/dist/src/cli/handlers/rules.js +195 -0
  54. package/dist/src/cli/handlers/run.js +4 -0
  55. package/dist/src/cli/handlers/runtime-info.js +1 -2
  56. package/dist/src/cli/handlers/script-runner.js +3 -2
  57. package/dist/src/cli/handlers/session.js +19 -9
  58. package/dist/src/cli/handlers/sessions.js +137 -43
  59. package/dist/src/cli/handlers/setup-manifest.js +39 -44
  60. package/dist/src/cli/handlers/setup.js +41 -0
  61. package/dist/src/cli/handlers/steward.js +11 -0
  62. package/dist/src/cli/handlers/subcommands.js +57 -2
  63. package/dist/src/cli/handlers/uhp.js +3 -0
  64. package/dist/src/cli/handlers/use.js +294 -55
  65. package/dist/src/cli/handlers/utilities.js +26 -10
  66. package/dist/src/cli/handlers/version.js +8 -0
  67. package/dist/src/cli/router.js +15 -1
  68. package/dist/src/cli/services/deployment-verification.js +19 -9
  69. package/dist/src/cli/services/provider-prepare-coordinator.js +24 -0
  70. package/dist/src/cli/skill-usage.js +2 -3
  71. package/dist/src/config/aiwg-config.js +87 -37
  72. package/dist/src/config/cli.js +111 -5
  73. package/dist/src/config/decision-config.js +106 -0
  74. package/dist/src/config/dispatch-thresholds-runtime.d.mts +8 -0
  75. package/dist/src/config/gitignore.js +50 -0
  76. package/dist/src/config/index-graph-overrides.js +36 -0
  77. package/dist/src/config/project-artifacts-health.d.mts +37 -0
  78. package/dist/src/config/project-artifacts-runtime.d.mts +16 -0
  79. package/dist/src/config/user-config-dir.d.mts +9 -0
  80. package/dist/src/config/user-config.js +2 -1
  81. package/dist/src/config/user-registry.js +23 -15
  82. package/dist/src/config/user-state.js +7 -0
  83. package/dist/src/decision/ask.js +203 -43
  84. package/dist/src/decision/context-live-qualification.js +2 -2
  85. package/dist/src/decision/driver.js +13 -8
  86. package/dist/src/decision/ensemble/aggregate.js +23 -10
  87. package/dist/src/decision/ensemble-study/multifact-study.js +6 -2
  88. package/dist/src/decision/entry.js +2 -15
  89. package/dist/src/decision/graph-live-qualification.js +1 -1
  90. package/dist/src/decision/graph-run.js +9 -0
  91. package/dist/src/decision/heldout/collector.js +5 -13
  92. package/dist/src/decision/heldout/contract.js +30 -7
  93. package/dist/src/decision/jev-credentials.js +15 -1
  94. package/dist/src/decision/ledger.js +131 -28
  95. package/dist/src/decision/offload-activation.js +137 -0
  96. package/dist/src/decision/offload.js +550 -0
  97. package/dist/src/decision/qualification/load.js +4 -6
  98. package/dist/src/decision/savings.js +28 -0
  99. package/dist/src/effects/types.js +5 -1
  100. package/dist/src/effects/verifiers/git.js +2 -1
  101. package/dist/src/effects/verifiers/tracker.js +26 -14
  102. package/dist/src/extensions/commands/definitions.js +19 -0
  103. package/dist/src/extensions/project-local-remove.js +36 -1
  104. package/dist/src/gates/providers/loader.js +61 -17
  105. package/dist/src/installation/manager-command.d.mts +18 -0
  106. package/dist/src/installation/manager-command.mjs +14 -2
  107. package/dist/src/installation/manager.d.mts +49 -0
  108. package/dist/src/installation/manager.mjs +78 -3
  109. package/dist/src/issues/cli.js +4 -4
  110. package/dist/src/issues/local.js +24 -5
  111. package/dist/src/jobs/runner.js +1 -1
  112. package/dist/src/lint/cli.js +27 -1
  113. package/dist/src/lint/loader.js +2 -1
  114. package/dist/src/lint/reporters.js +25 -1
  115. package/dist/src/lint/rule-activation.js +43 -0
  116. package/dist/src/lint/runner.js +137 -62
  117. package/dist/src/mcp/adapters/codex-runtime.js +7 -1
  118. package/dist/src/mcp/claude-install.d.mts +4 -0
  119. package/dist/src/mcp/claude-install.mjs +41 -0
  120. package/dist/src/mcp/cli.d.mts +1 -0
  121. package/dist/src/mcp/cli.mjs +36 -11
  122. package/dist/src/mcp/config-file.d.mts +28 -0
  123. package/dist/src/mcp/config-layers.d.mts +36 -0
  124. package/dist/src/mcp/credentials.d.mts +51 -0
  125. package/dist/src/mcp/grok-build-config.d.mts +6 -0
  126. package/dist/src/mcp/grok-build-config.mjs +1 -1
  127. package/dist/src/mcp/helpers.mjs +3 -2
  128. package/dist/src/mcp/muse-native-config.d.mts +13 -0
  129. package/dist/src/mcp/muse-native-config.mjs +1 -0
  130. package/dist/src/mcp/omp-config.d.mts +4 -0
  131. package/dist/src/mcp/registry.js +3 -93
  132. package/dist/src/mcp/registry.mjs +3 -87
  133. package/dist/src/mcp/server-config.d.mts +3 -0
  134. package/dist/src/mcp/server-config.mjs +89 -0
  135. package/dist/src/mcp/server.mjs +37 -375
  136. package/dist/src/mcp/test-simple.mjs +2 -1
  137. package/dist/src/mcp/toml-editor.d.mts +4 -0
  138. package/dist/src/mcp/toml-string.d.mts +1 -0
  139. package/dist/src/mcp/toml-strip-mcp.d.mts +1 -0
  140. package/dist/src/mcp/tool-filters.d.mts +46 -0
  141. package/dist/src/mcp/tools/decision.mjs +12 -11
  142. package/dist/src/mcp/tools/discovery.mjs +62 -55
  143. package/dist/src/mcp/tools/orchestration.mjs +12 -2
  144. package/dist/src/mcp/tools/worker-invoke.mjs +1340 -23
  145. package/dist/src/mcp/tools/worker-shell-control.mjs +52 -21
  146. package/dist/src/mcp/tools/worker-stdin-runtime.mjs +3 -3
  147. package/dist/src/mission-protocol/codecs.js +8 -0
  148. package/dist/src/models/config-loader.js +7 -2
  149. package/dist/src/models/model-capabilities.v1.json +96 -4
  150. package/dist/src/models/model-discovery.js +76 -11
  151. package/dist/src/models/provider-policy.js +101 -7
  152. package/dist/src/models/qualification-digest.d.mts +2 -0
  153. package/dist/src/models/resolver.js +12 -10
  154. package/dist/src/models/worker-direct-readiness.js +65 -44
  155. package/dist/src/models/worker-dispatch-offer.js +3 -3
  156. package/dist/src/models/worker-health-readiness.js +16 -3
  157. package/dist/src/models/worker-installation.d.mts +39 -0
  158. package/dist/src/models/worker-installation.mjs +135 -28
  159. package/dist/src/process/bounded-child-process.d.mts +84 -0
  160. package/dist/src/process/bounded-child-process.mjs +43 -0
  161. package/dist/src/process/cli-command.d.mts +16 -0
  162. package/dist/src/process/signal-child.d.mts +6 -0
  163. package/dist/src/process/signal-child.mjs +7 -0
  164. package/dist/src/providers/deployed-skill-roots.js +20 -0
  165. package/dist/src/providers/grok-build-acp.js +3 -2
  166. package/dist/src/providers/grok-build-headless.js +12 -4
  167. package/dist/src/providers/grok-build-inspect.js +1 -0
  168. package/dist/src/providers/omp-agent.d.mts +8 -0
  169. package/dist/src/providers/omp-diagnostics.d.mts +1 -0
  170. package/dist/src/providers/omp-paths.d.mts +13 -0
  171. package/dist/src/providers/skill-ownership.d.mts +9 -0
  172. package/dist/src/providers/skill-ownership.mjs +88 -0
  173. package/dist/src/rules/activation.d.mts +21 -0
  174. package/dist/src/rules/activation.mjs +62 -0
  175. package/dist/src/rules/inline-budget.d.mts +26 -0
  176. package/dist/src/rules/inline-budget.mjs +66 -0
  177. package/dist/src/rules/inventory.js +129 -0
  178. package/dist/src/rules/pin-state.js +73 -0
  179. package/dist/src/rules/pins.d.mts +14 -0
  180. package/dist/src/rules/pins.mjs +112 -0
  181. package/dist/src/rules/rule-frontmatter.schema.json +197 -0
  182. package/dist/src/serve/mission-conductor.js +38 -12
  183. package/dist/src/serve/worker-invocation.js +30 -10
  184. package/dist/src/serve/worker-stdin-frame.d.mts +10 -0
  185. package/dist/src/sessions/adapter-registry.js +23 -19
  186. package/dist/src/sessions/adapters/claude.js +39 -54
  187. package/dist/src/sessions/adapters/codex.js +95 -67
  188. package/dist/src/sessions/adapters/copilot.js +4 -4
  189. package/dist/src/sessions/adapters/cursor.js +40 -57
  190. package/dist/src/sessions/adapters/deepseek-harness.js +15 -12
  191. package/dist/src/sessions/adapters/factory.js +31 -27
  192. package/dist/src/sessions/adapters/generic.js +10 -10
  193. package/dist/src/sessions/adapters/grok-build.js +11 -11
  194. package/dist/src/sessions/adapters/grokbot.js +24 -13
  195. package/dist/src/sessions/adapters/hermes.js +27 -29
  196. package/dist/src/sessions/adapters/muse-workspace-scan.js +31 -11
  197. package/dist/src/sessions/adapters/muse.js +150 -93
  198. package/dist/src/sessions/adapters/omp.js +28 -14
  199. package/dist/src/sessions/adapters/openclaw.js +89 -79
  200. package/dist/src/sessions/adapters/opencode.js +27 -24
  201. package/dist/src/sessions/adapters/openhuman.js +18 -60
  202. package/dist/src/sessions/adapters/pi.js +29 -14
  203. package/dist/src/sessions/adapters/warp.js +3 -3
  204. package/dist/src/sessions/adapters/windsurf.js +19 -48
  205. package/dist/src/sessions/batch-contracts.js +16 -8
  206. package/dist/src/sessions/batch-import.js +252 -12
  207. package/dist/src/sessions/contracts.js +50 -3
  208. package/dist/src/sessions/coverage-preflight.js +27 -2
  209. package/dist/src/sessions/export-plan.js +4 -3
  210. package/dist/src/sessions/import-exclusions.js +72 -0
  211. package/dist/src/sessions/import-lease.js +34 -18
  212. package/dist/src/sessions/import-limits.js +37 -0
  213. package/dist/src/sessions/importer.js +60 -38
  214. package/dist/src/sessions/lifecycle.js +45 -0
  215. package/dist/src/sessions/policy.js +145 -26
  216. package/dist/src/sessions/readers.js +106 -53
  217. package/dist/src/sessions/refresh.js +105 -0
  218. package/dist/src/sessions/repository.js +151 -54
  219. package/dist/src/sessions/workspace-discovery.js +292 -60
  220. package/dist/src/sessions/workspace-matching.js +107 -0
  221. package/dist/src/skills/importer.js +1 -0
  222. package/dist/src/smiths/context-pipeline/aiwg-md.js +11 -0
  223. package/dist/src/smiths/context-pipeline/claude-hook.js +3 -26
  224. package/dist/src/smiths/context-pipeline/finalization.js +6 -5
  225. package/dist/src/smiths/context-pipeline/generator.js +21 -4
  226. package/dist/src/smiths/context-pipeline/parallelism-section.js +7 -15
  227. package/dist/src/smiths/mcpsmith/generator.js +1 -1
  228. package/dist/src/smiths/toolsmith/runtime-discovery.d.mts +10 -0
  229. package/dist/src/smiths/toolsmith/runtime-discovery.mjs +76 -18
  230. package/dist/src/storage/backends/fs.js +18 -76
  231. package/dist/src/storage/backends/obsidian.js +1 -0
  232. package/dist/src/tracker/forge-http.js +2 -1
  233. package/dist/src/update/checker.d.mts +2 -0
  234. package/dist/src/update/service.d.mts +22 -0
  235. package/dist/src/utils/contained-filesystem.d.mts +37 -0
  236. package/dist/src/utils/contained-filesystem.mjs +683 -0
  237. package/dist/src/utils/temporary-directory.js +36 -0
  238. package/dist/src/writing/example-generator.js +1 -1
  239. package/package.json +15 -2
  240. package/schemas/models/model-catalog.v1.schema.json +1 -1
  241. package/schemas/models/provider-model-capabilities.v1.schema.json +6 -0
  242. package/tools/_resolve-impl.mjs +13 -15
  243. package/tools/agents/deploy-agents.mjs +49 -21
  244. package/tools/agents/providers/base.mjs +104 -134
  245. package/tools/agents/providers/claude.mjs +8 -24
  246. package/tools/agents/providers/codex.mjs +18 -11
  247. package/tools/agents/providers/cursor.mjs +19 -18
  248. package/tools/agents/providers/grok-build.mjs +3 -1
  249. package/tools/agents/providers/hermes.mjs +57 -47
  250. package/tools/agents/providers/openclaw.mjs +9 -4
  251. package/tools/agents/providers/pi.mjs +5 -0
  252. package/tools/agents/providers/provider-prepare-channel.mjs +5 -0
  253. package/tools/agents/providers/provider-prepare-coordinator.mjs +247 -0
  254. package/tools/agents/providers/provider-prepare-worker.mjs +84 -0
  255. package/tools/agents/providers/rule-delivery.mjs +1270 -0
  256. package/tools/agents/providers/warp.mjs +20 -26
  257. package/tools/cli/decision-offload-report.mjs +40 -0
  258. package/tools/cli/workspace-status.mjs +862 -0
  259. package/tools/plugin/package-plugins.mjs +186 -8
  260. package/tools/plugin/plugin-links.mjs +114 -0
  261. package/tools/providers/antigravity-transport.mjs +3 -1
  262. package/tools/providers/deepseek-harness-transport.mjs +6 -4
  263. package/tools/providers/omp-transport.mjs +4 -2
  264. package/tools/ralph-external/lib/consecutive-failure-circuit-breaker.mjs +1748 -0
  265. package/tools/ralph-external/lib/launcher-recovery-evidence.mjs +55 -0
  266. package/tools/ralph-external/lib/omp-adapter.mjs +1 -1
  267. package/tools/ralph-external/lib/pi-adapter.mjs +0 -4
  268. package/tools/ralph-external/lib/provider-adapter.mjs +59 -1
  269. package/tools/ralph-external/lib/recovery-probe.mjs +72 -0
  270. package/tools/ralph-external/lib/session-environment.mjs +28 -0
  271. package/tools/ralph-external/lib/systemd-client.mjs +49 -0
  272. package/tools/ralph-external/lib/worker-health.mjs +1 -1
  273. package/tools/ralph-external/lib/worker-isolation.mjs +717 -0
  274. package/tools/ralph-external/lib/worker-overflow.mjs +50 -0
  275. package/tools/ralph-external/process-monitor.mjs +1017 -0
  276. package/tools/ralph-external/session-launcher.mjs +5057 -0
  277. package/tools/scripts/resource-preflight.mjs +541 -0
  278. package/tools/skills/deploy-skills-codex.mjs +2 -1
  279. package/vscode-extension/schemas/aiwg.config.v1.json +1194 -231
@@ -0,0 +1,862 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * CLI Workspace Status Tool
4
+ *
5
+ * Shows workspace health, installed frameworks, and migration status.
6
+ *
7
+ * @module tools/cli/workspace-status
8
+ * @version 1.0.0
9
+ * @since 2025-12-09
10
+ *
11
+ * @usage
12
+ * # Show workspace status
13
+ * aiwg -status
14
+ *
15
+ * # Show detailed framework health
16
+ * aiwg -status --verbose
17
+ *
18
+ * # Output as JSON
19
+ * aiwg -status --json
20
+ */
21
+
22
+ import fs from 'fs/promises';
23
+ import { importImpl } from '../_resolve-impl.mjs';
24
+ import { collectDecisionOffloadReport, formatDecisionOffloadReport } from './decision-offload-report.mjs';
25
+ import { existsSync } from 'fs';
26
+ import http from 'http';
27
+ import os from 'os';
28
+ import path from 'path';
29
+ import { fileURLToPath, pathToFileURL } from 'url';
30
+
31
+ const __filename = fileURLToPath(import.meta.url);
32
+ const __dirname = path.dirname(__filename);
33
+ const packageRoot = path.resolve(__dirname, '../..');
34
+ const runtimeRoot = existsSync(path.join(packageRoot, 'src')) ? 'src' : 'dist/src';
35
+ const [{ resolveProjectAiwgDir }, { auditProjectArtifactHealth }] = await Promise.all([
36
+ import(pathToFileURL(path.join(packageRoot, runtimeRoot, 'config/project-artifacts-runtime.mjs')).href),
37
+ import(pathToFileURL(path.join(packageRoot, runtimeRoot, 'config/project-artifacts-health.mjs')).href),
38
+ ]);
39
+
40
+ // ===========================
41
+ // CLI Helpers
42
+ // ===========================
43
+
44
+ /**
45
+ * Display help message
46
+ */
47
+ function displayHelp() {
48
+ console.log(`
49
+ AIWG - Workspace Status Tool
50
+
51
+ Shows workspace health, installed frameworks, and migration status.
52
+
53
+ USAGE
54
+ aiwg -status [options] [project-root]
55
+ node tools/cli/workspace-status.mjs [options] [project-root]
56
+
57
+ OPTIONS
58
+ --verbose, -v Show detailed information
59
+ --json Output as JSON
60
+ --probe Output deterministic first-run verification probe
61
+ --export <fmt> Export fleet status payload: json | ndjson
62
+ --serve Serve fleet status JSON over loopback HTTP
63
+ --bind <addr> Bind address for --serve (default: 127.0.0.1)
64
+ --port <port> Port for --serve (default: 7387)
65
+ --fleet-id <id> Machine identifier for fleet exports
66
+ --activity-hours <n> Activity-log lookback window (default: 24)
67
+ --help, -h Show this help message
68
+
69
+ ARGUMENTS
70
+ project-root Path to project root (default: current directory)
71
+
72
+ EXAMPLES
73
+ # Show workspace status
74
+ aiwg -status
75
+
76
+ # Show detailed framework health
77
+ aiwg -status --verbose
78
+
79
+ # Output as JSON for scripting
80
+ aiwg -status --json
81
+
82
+ # Output deterministic first-run verification probe
83
+ aiwg status --probe --json
84
+
85
+ # Export fleet status for cockpit ingestion
86
+ aiwg status --export json --fleet-id eride
87
+ aiwg status --export ndjson
88
+
89
+ # Serve fleet status on loopback for polling
90
+ aiwg status --serve --port 7387 --fleet-id eride
91
+ `);
92
+ }
93
+
94
+ /**
95
+ * Parse command-line arguments
96
+ */
97
+ function parseArgs(args) {
98
+ const options = {
99
+ verbose: false,
100
+ json: false,
101
+ probe: false,
102
+ exportFormat: null,
103
+ serve: false,
104
+ bind: '127.0.0.1',
105
+ port: 7387,
106
+ fleetId: null,
107
+ activityHours: 24,
108
+ projectRoot: process.cwd(),
109
+ help: false
110
+ };
111
+
112
+ for (let i = 0; i < args.length; i++) {
113
+ const arg = args[i];
114
+
115
+ if (arg === '--verbose' || arg === '-v') {
116
+ options.verbose = true;
117
+ } else if (arg === '--json') {
118
+ options.json = true;
119
+ } else if (arg === '--probe') {
120
+ options.probe = true;
121
+ options.json = true;
122
+ } else if (arg === '--export' && args[i + 1]) {
123
+ options.exportFormat = args[++i];
124
+ } else if (arg === '--serve') {
125
+ options.serve = true;
126
+ } else if (arg === '--bind' && args[i + 1]) {
127
+ options.bind = args[++i];
128
+ } else if (arg === '--port' && args[i + 1]) {
129
+ const port = Number(args[++i]);
130
+ if (Number.isInteger(port) && port >= 0 && port < 65536) options.port = port;
131
+ } else if (arg === '--fleet-id' && args[i + 1]) {
132
+ options.fleetId = args[++i];
133
+ } else if (arg === '--activity-hours' && args[i + 1]) {
134
+ const hours = Number(args[++i]);
135
+ if (Number.isFinite(hours) && hours >= 0) options.activityHours = hours;
136
+ } else if (arg === '--help' || arg === '-h') {
137
+ options.help = true;
138
+ } else if (!arg.startsWith('--') && !arg.startsWith('-')) {
139
+ options.projectRoot = path.resolve(arg);
140
+ }
141
+ }
142
+
143
+ return options;
144
+ }
145
+
146
+ async function readJsonFile(filePath) {
147
+ try {
148
+ return JSON.parse(await fs.readFile(filePath, 'utf8'));
149
+ } catch {
150
+ return null;
151
+ }
152
+ }
153
+
154
+ async function readJsonFileDetailed(filePath) {
155
+ try {
156
+ const content = await fs.readFile(filePath, 'utf8');
157
+ return { exists: true, valid: true, data: JSON.parse(content), error: null };
158
+ } catch (error) {
159
+ if (error && error.code === 'ENOENT') {
160
+ return { exists: false, valid: false, data: null, error: null };
161
+ }
162
+ return {
163
+ exists: true,
164
+ valid: false,
165
+ data: null,
166
+ error: error instanceof Error ? error.message : String(error),
167
+ };
168
+ }
169
+ }
170
+
171
+ /**
172
+ * Format bytes as human-readable string
173
+ */
174
+ function formatBytes(bytes) {
175
+ if (bytes === 0) return '0 Bytes';
176
+
177
+ const k = 1024;
178
+ const sizes = ['Bytes', 'KB', 'MB', 'GB'];
179
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
180
+
181
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
182
+ }
183
+
184
+ /**
185
+ * Get status emoji
186
+ */
187
+ function statusEmoji(status) {
188
+ switch (status) {
189
+ case 'healthy':
190
+ return '✅';
191
+ case 'warning':
192
+ return '⚠️';
193
+ case 'error':
194
+ return '❌';
195
+ default:
196
+ return '❓';
197
+ }
198
+ }
199
+
200
+ function normalizeRegistryFrameworks(registry) {
201
+ if (!registry || !registry.frameworks) return [];
202
+
203
+ const entries = Array.isArray(registry.frameworks)
204
+ ? registry.frameworks.map((data) => [data?.id, data])
205
+ : Object.entries(registry.frameworks);
206
+
207
+ return entries
208
+ .filter(([id]) => typeof id === 'string' && id.length > 0)
209
+ .map(([id, data]) => ({
210
+ id,
211
+ type: data.type || 'framework',
212
+ version: data.version || 'unknown',
213
+ health: data.health || 'healthy',
214
+ installDate: data.installed || data.installedAt || data['install-date'] || 'unknown'
215
+ }));
216
+ }
217
+
218
+ async function countDirEntries(dirPath) {
219
+ try {
220
+ const entries = await fs.readdir(dirPath, { withFileTypes: true });
221
+ return entries.length;
222
+ } catch {
223
+ return 0;
224
+ }
225
+ }
226
+
227
+ async function collectProviderDeployments(projectRoot) {
228
+ const providers = [
229
+ { name: 'claude-code', path: '.claude', artifactDirs: ['agents', 'commands', 'skills', 'rules', 'behaviors'] },
230
+ { name: 'codex', path: '.codex', artifactDirs: ['agents', 'prompts', 'skills', 'rules'] },
231
+ { name: 'copilot', path: '.github', artifactDirs: ['prompts', 'instructions'] },
232
+ { name: 'cursor', path: '.cursor', artifactDirs: ['agents', 'commands', 'skills', 'rules'] },
233
+ { name: 'opencode', path: '.opencode', artifactDirs: ['agent', 'command', 'skill', 'rule'] },
234
+ { name: 'warp', path: '.warp', artifactDirs: ['agents', 'commands', 'skills', 'rules'] },
235
+ { name: 'universal', path: '.agents', artifactDirs: ['agents', 'commands', 'skills', 'rules'] }
236
+ ];
237
+
238
+ const deployments = [];
239
+ for (const provider of providers) {
240
+ const providerPath = path.join(projectRoot, provider.path);
241
+ try {
242
+ await fs.access(providerPath);
243
+ } catch {
244
+ continue;
245
+ }
246
+
247
+ const counts = {};
248
+ for (const dir of provider.artifactDirs) {
249
+ const count = await countDirEntries(path.join(providerPath, dir));
250
+ if (count > 0) counts[dir] = count;
251
+ }
252
+ const manifest = await readJsonFile(path.join(providerPath, '.aiwg-manifest.json'));
253
+ deployments.push({
254
+ name: provider.name,
255
+ path: provider.path,
256
+ counts,
257
+ lastRefresh: manifest?.generatedAt || manifest?.deployedAt || manifest?.timestamp || null
258
+ });
259
+ }
260
+ return deployments;
261
+ }
262
+
263
+ async function collectProjectLocalBundles(aiwgPath, installedFrameworkIds = new Set()) {
264
+ const bundleDirs = ['extensions', 'addons', 'frameworks', 'plugins'];
265
+ const bundles = [];
266
+ for (const type of bundleDirs) {
267
+ const typeDir = path.join(aiwgPath, type);
268
+ let entries;
269
+ try {
270
+ entries = await fs.readdir(typeDir, { withFileTypes: true });
271
+ } catch {
272
+ continue;
273
+ }
274
+
275
+ const names = entries
276
+ .filter(entry => entry.isDirectory())
277
+ .filter(entry => type !== 'frameworks' || !installedFrameworkIds.has(entry.name))
278
+ .map(entry => entry.name)
279
+ .sort();
280
+ if (names.length > 0) {
281
+ bundles.push({ type, count: names.length, names });
282
+ }
283
+ }
284
+ return bundles;
285
+ }
286
+
287
+ function parseActivityLogLine(line) {
288
+ const match = /^##\s+\[(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2})\]\s+([a-z]+)\s+\|\s+(.+?)\s*$/.exec(line);
289
+ if (!match) return null;
290
+ const [, rawTimestamp, operation, summary] = match;
291
+ const isoTimestamp = `${rawTimestamp.replace(' ', 'T')}:00.000Z`;
292
+ const date = new Date(isoTimestamp);
293
+ if (Number.isNaN(date.getTime())) return null;
294
+ return { timestamp: isoTimestamp, rawTimestamp, operation, summary };
295
+ }
296
+
297
+ async function collectActivityLog(projectRoot, hours) {
298
+ const aiwgRoot = resolveProjectAiwgDir(projectRoot);
299
+ const candidates = [
300
+ path.join(aiwgRoot, 'activity.log'),
301
+ path.join(aiwgRoot, 'activity-log', 'activity.log')
302
+ ];
303
+ let content = null;
304
+ let source = null;
305
+ for (const candidate of candidates) {
306
+ try {
307
+ content = await fs.readFile(candidate, 'utf8');
308
+ source = path.relative(projectRoot, candidate);
309
+ break;
310
+ } catch {
311
+ // Try next historical location.
312
+ }
313
+ }
314
+ if (!content) return { source: null, entries: [] };
315
+
316
+ const cutoff = hours > 0 ? Date.now() - hours * 60 * 60 * 1000 : 0;
317
+ const entries = content
318
+ .split('\n')
319
+ .map(parseActivityLogLine)
320
+ .filter(Boolean)
321
+ .filter(entry => cutoff === 0 || new Date(entry.timestamp).getTime() >= cutoff)
322
+ .slice(-100);
323
+
324
+ return { source, entries };
325
+ }
326
+
327
+ async function collectActiveOperations(projectRoot) {
328
+ const aiwgRoot = resolveProjectAiwgDir(projectRoot);
329
+ const checks = [
330
+ { name: 'mission-control', path: path.join(aiwgRoot, 'mc') },
331
+ { name: 'ralph', path: path.join(aiwgRoot, 'ralph') },
332
+ { name: 'daemon', path: path.join(aiwgRoot, 'daemon') }
333
+ ];
334
+ const operations = [];
335
+ for (const check of checks) {
336
+ try {
337
+ const entries = await fs.readdir(check.path, { withFileTypes: true });
338
+ const count = entries.filter(entry => !entry.name.startsWith('.')).length;
339
+ if (count > 0) {
340
+ operations.push({
341
+ subsystem: check.name,
342
+ path: path.relative(projectRoot, check.path),
343
+ count
344
+ });
345
+ }
346
+ } catch {
347
+ // No active state for this subsystem.
348
+ }
349
+ }
350
+ return operations;
351
+ }
352
+
353
+ async function buildWorkspaceStatus(projectRoot) {
354
+ const aiwgPath = resolveProjectAiwgDir(projectRoot);
355
+ const artifactHealth = auditProjectArtifactHealth(projectRoot);
356
+ const { rulePinState } = await importImpl(import.meta.url, 'rules/pin-state.js');
357
+ const pins = rulePinState(projectRoot, packageRoot);
358
+ const result = {
359
+ rule_pins: { rules: pins.rules, warnings: pins.warnings },
360
+ decisionOffload: await collectDecisionOffloadReport(projectRoot),
361
+ workspace: {
362
+ path: aiwgPath,
363
+ exists: false,
364
+ isLegacy: false,
365
+ isFrameworkScoped: false
366
+ },
367
+ frameworks: [],
368
+ providerDeployments: [],
369
+ projectLocalBundles: [],
370
+ health: {
371
+ overall: 'unknown',
372
+ issues: []
373
+ },
374
+ migration: {
375
+ status: 'unknown',
376
+ backupsAvailable: 0
377
+ },
378
+ artifactHealth,
379
+ };
380
+
381
+ if (artifactHealth.severity !== 'ok') {
382
+ result.health.issues.push({
383
+ severity: artifactHealth.severity,
384
+ message: `External artifact corpus: ${artifactHealth.classification}.`,
385
+ action: artifactHealth.action,
386
+ });
387
+ result.health.overall = artifactHealth.severity;
388
+ }
389
+
390
+ try {
391
+ await fs.access(aiwgPath);
392
+ result.workspace.exists = true;
393
+ } catch {
394
+ result.workspace.exists = false;
395
+ }
396
+
397
+ if (!result.workspace.exists) return result;
398
+
399
+ const frameworksDir = path.join(aiwgPath, 'frameworks');
400
+ // SDLC-canonical artifact dirs. These coexist with `.aiwg/frameworks/` by
401
+ // design when sdlc-complete is deployed (documented in CLAUDE.md). They only
402
+ // indicate a genuinely-legacy (pre-#54) workspace when `.aiwg/frameworks/`
403
+ // is absent. See issue #1516.
404
+ const sdlcCanonicalDirs = ['intake', 'requirements', 'architecture', 'planning', 'testing'];
405
+ // Non-SDLC top-level dirs that the pre-#1516 deploy scaffolded by mistake.
406
+ // If any of these exist, the workspace pre-dates the manifest fix and a
407
+ // migration is needed to move them under `.aiwg/frameworks/<id>/`.
408
+ const orphanLegacyDirs = ['forensics', 'kb', 'media', 'media-curator', 'marketing', 'security-engineering'];
409
+
410
+ try {
411
+ await fs.access(frameworksDir);
412
+ result.workspace.isFrameworkScoped = true;
413
+ } catch {
414
+ result.workspace.isFrameworkScoped = false;
415
+ }
416
+
417
+ let hasSdlcDirs = false;
418
+ for (const dir of sdlcCanonicalDirs) {
419
+ try {
420
+ await fs.access(path.join(aiwgPath, dir));
421
+ hasSdlcDirs = true;
422
+ break;
423
+ } catch {
424
+ // Directory doesn't exist.
425
+ }
426
+ }
427
+
428
+ let hasOrphanLegacy = false;
429
+ for (const dir of orphanLegacyDirs) {
430
+ try {
431
+ await fs.access(path.join(aiwgPath, dir));
432
+ hasOrphanLegacy = true;
433
+ break;
434
+ } catch {
435
+ // Directory doesn't exist.
436
+ }
437
+ }
438
+
439
+ // isLegacy now only fires when the workspace shape genuinely warrants a
440
+ // migration: either pre-#54 SDLC-only top-level layout, or orphan non-SDLC
441
+ // top-level dirs left over from a pre-#1516 deploy.
442
+ if (hasSdlcDirs && !result.workspace.isFrameworkScoped) {
443
+ result.workspace.isLegacy = true;
444
+ } else if (hasOrphanLegacy) {
445
+ result.workspace.isLegacy = true;
446
+ }
447
+
448
+ if (result.workspace.isFrameworkScoped && !result.workspace.isLegacy) {
449
+ result.migration.status = 'completed';
450
+ } else if (result.workspace.isLegacy && !result.workspace.isFrameworkScoped) {
451
+ result.migration.status = 'pending';
452
+ } else if (result.workspace.isLegacy && result.workspace.isFrameworkScoped) {
453
+ result.migration.status = 'partial';
454
+ } else {
455
+ result.migration.status = 'none';
456
+ }
457
+
458
+ const parentDir = path.dirname(aiwgPath);
459
+ try {
460
+ const entries = await fs.readdir(parentDir);
461
+ result.migration.backupsAvailable = entries.filter(
462
+ name => name.startsWith('.aiwg.backup.')
463
+ ).length;
464
+ } catch {
465
+ result.migration.backupsAvailable = 0;
466
+ }
467
+
468
+ const registryPath = path.join(frameworksDir, 'registry.json');
469
+ const registryRead = await readJsonFileDetailed(registryPath);
470
+ if (registryRead.valid) {
471
+ result.frameworks = normalizeRegistryFrameworks(registryRead.data);
472
+ } else if (registryRead.exists) {
473
+ result.health.issues.push({
474
+ severity: 'warning',
475
+ message: 'Framework registry exists but could not be parsed.',
476
+ action: 'Re-run aiwg use for the intended framework.',
477
+ });
478
+ }
479
+
480
+ const configRead = await readJsonFileDetailed(path.join(aiwgPath, 'aiwg.config'));
481
+ if (configRead.exists && !configRead.valid) {
482
+ result.health.issues.push({
483
+ severity: 'warning',
484
+ message: 'AIWG project config exists but could not be parsed.',
485
+ action: 'Review .aiwg/aiwg.config or re-run aiwg init.',
486
+ });
487
+ }
488
+
489
+ result.providerDeployments = await collectProviderDeployments(projectRoot);
490
+ result.projectLocalBundles = await collectProjectLocalBundles(
491
+ aiwgPath,
492
+ new Set(result.frameworks.map(framework => framework.id))
493
+ );
494
+
495
+ if (result.frameworks.length === 0) {
496
+ result.health.overall = 'unknown';
497
+ } else {
498
+ const hasErrors = result.frameworks.some(f => f.health === 'error');
499
+ const hasWarnings = result.frameworks.some(f => f.health === 'warning');
500
+
501
+ if (hasErrors) {
502
+ result.health.overall = 'error';
503
+ } else if (hasWarnings) {
504
+ result.health.overall = 'warning';
505
+ } else {
506
+ result.health.overall = 'healthy';
507
+ }
508
+ }
509
+
510
+ if (result.health.issues.some((issue) => issue.severity === 'warning') && result.health.overall !== 'error') {
511
+ result.health.overall = 'warning';
512
+ }
513
+
514
+ if (result.workspace.isLegacy && !result.workspace.isFrameworkScoped) {
515
+ result.health.issues.push({
516
+ severity: 'warning',
517
+ message: 'Legacy workspace structure detected. Consider migrating.',
518
+ action: 'aiwg -migrate-workspace'
519
+ });
520
+ }
521
+
522
+ return result;
523
+ }
524
+
525
+ async function buildVerificationProbe(projectRoot) {
526
+ const workspace = await buildWorkspaceStatus(projectRoot);
527
+ const frameworkCount = workspace.frameworks.length;
528
+ const providerCount = workspace.providerDeployments.length;
529
+ const malformedConfig = workspace.health.issues.some((issue) => /could not be parsed/.test(issue.message));
530
+ const artifactNeedsRepair = workspace.artifactHealth.external_configured && workspace.artifactHealth.severity !== 'ok';
531
+ const ready = workspace.workspace.exists && frameworkCount > 0 && providerCount > 0;
532
+ const partial = workspace.workspace.exists && (frameworkCount > 0 || providerCount > 0 || malformedConfig);
533
+
534
+ const userRegistryOverride = Boolean(process.env.AIWG_USER_REGISTRY_PATH && String(process.env.AIWG_USER_REGISTRY_PATH).trim());
535
+ if (userRegistryOverride) {
536
+ workspace.health.issues.push({
537
+ severity: 'warning',
538
+ message: 'AIWG_USER_REGISTRY_PATH is set (test override active); user registry is not writing to default ~/.aiwg/installed.json',
539
+ action: 'unset AIWG_USER_REGISTRY_PATH',
540
+ });
541
+ if (workspace.health.overall !== 'error') {
542
+ workspace.health.overall = 'warning';
543
+ }
544
+ }
545
+
546
+ return {
547
+ schema: 'aiwg.status.probe.v1',
548
+ rule_pins: workspace.rule_pins,
549
+ generated_at: new Date().toISOString(),
550
+ project_root: path.resolve(projectRoot),
551
+ engaged: ready && !artifactNeedsRepair,
552
+ status: malformedConfig || artifactNeedsRepair ? 'needs-repair' : ready ? 'ready' : partial ? 'partial' : 'not-configured',
553
+ checks: {
554
+ workspace_exists: workspace.workspace.exists,
555
+ framework_count: frameworkCount,
556
+ provider_deployment_count: providerCount,
557
+ health: workspace.health.overall,
558
+ malformed_config: malformedConfig,
559
+ artifact_health: workspace.artifactHealth.classification,
560
+ external_artifact_reachable: workspace.artifactHealth.external_reachable,
561
+ user_registry_path_override: userRegistryOverride,
562
+ },
563
+ verification: {
564
+ required: true,
565
+ action: artifactNeedsRepair
566
+ ? workspace.artifactHealth.action
567
+ : ready
568
+ ? 'AIWG workspace and provider deployment are detected.'
569
+ : malformedConfig
570
+ ? 'Repair the malformed AIWG config or registry, then run this probe again.'
571
+ : 'Run the guided path, deploy one framework, then run this probe again.',
572
+ command: 'aiwg status --probe --json',
573
+ next_command: artifactNeedsRepair
574
+ ? 'aiwg artifacts repair --dry-run'
575
+ : ready ? null : malformedConfig ? 'aiwg doctor --project-local' : 'aiwg wizard --dry-run',
576
+ },
577
+ agent_response_guidance: {
578
+ when_asked_if_aiwg_is_engaged:
579
+ 'Run or read `aiwg status --probe --json`, report engaged/status/project_root/provider_deployments plainly, and do not add AIWG attribution to generated user files, commits, comments, or headers.',
580
+ no_attribution_default: true,
581
+ passive_footer_default: false,
582
+ },
583
+ workspace,
584
+ };
585
+ }
586
+
587
+ async function readAiwgVersion() {
588
+ const candidates = [
589
+ path.join(__dirname, '..', '..', 'package.json'),
590
+ path.join(process.cwd(), 'package.json')
591
+ ];
592
+ for (const candidate of candidates) {
593
+ const pkg = await readJsonFile(candidate);
594
+ if (pkg?.version) return pkg.version;
595
+ }
596
+ return 'unknown';
597
+ }
598
+
599
+ async function buildFleetStatusExport(projectRoot, options = {}) {
600
+ const workspace = await buildWorkspaceStatus(projectRoot);
601
+ const activityLog = await collectActivityLog(projectRoot, options.activityHours ?? 24);
602
+ const activeOperations = await collectActiveOperations(projectRoot);
603
+
604
+ return {
605
+ schema: 'aiwg.fleet.status.v1',
606
+ generated_at: new Date().toISOString(),
607
+ machine: {
608
+ fleet_id: options.fleetId || process.env.AIWG_FLEET_ID || os.hostname(),
609
+ hostname: os.hostname(),
610
+ platform: process.platform,
611
+ arch: process.arch
612
+ },
613
+ aiwg: {
614
+ version: await readAiwgVersion(),
615
+ workspace_path: workspace.workspace.path
616
+ },
617
+ workspace,
618
+ frameworks: workspace.frameworks,
619
+ provider_deployments: workspace.providerDeployments,
620
+ activity_log: activityLog,
621
+ active_operations: activeOperations,
622
+ health: {
623
+ overall: workspace.health.overall,
624
+ flags: workspace.health.issues.map(issue => ({
625
+ severity: issue.severity,
626
+ message: issue.message,
627
+ action: issue.action || null
628
+ }))
629
+ },
630
+ security: {
631
+ bind_default: '127.0.0.1',
632
+ contains_secrets: false,
633
+ transport: 'pull'
634
+ }
635
+ };
636
+ }
637
+
638
+ function writeExportPayload(payload, format) {
639
+ if (format === 'json') {
640
+ console.log(JSON.stringify(payload, null, 2));
641
+ } else if (format === 'ndjson') {
642
+ console.log(JSON.stringify(payload));
643
+ } else {
644
+ throw new Error(`Unsupported export format: ${format}. Expected json or ndjson.`);
645
+ }
646
+ }
647
+
648
+ async function startStatusServer(options) {
649
+ const server = http.createServer(async (req, res) => {
650
+ if (req.method !== 'GET' || !['/', '/status', '/status.json'].includes(req.url || '/')) {
651
+ res.writeHead(404, { 'content-type': 'application/json' });
652
+ res.end(JSON.stringify({ error: 'not_found' }));
653
+ return;
654
+ }
655
+
656
+ try {
657
+ const payload = await buildFleetStatusExport(options.projectRoot, options);
658
+ res.writeHead(200, {
659
+ 'content-type': 'application/json',
660
+ 'cache-control': 'no-store'
661
+ });
662
+ res.end(JSON.stringify(payload, null, 2));
663
+ } catch (error) {
664
+ console.error('[workspace-status] status request failed', error);
665
+ res.writeHead(500, { 'content-type': 'application/json' });
666
+ res.end(JSON.stringify({ error: 'status_unavailable', message: 'Workspace status is unavailable.' }));
667
+ }
668
+ });
669
+
670
+ await new Promise((resolve, reject) => {
671
+ server.once('error', reject);
672
+ server.listen(options.port, options.bind, resolve);
673
+ });
674
+ return server;
675
+ }
676
+
677
+ // ===========================
678
+ // Main Status Flow
679
+ // ===========================
680
+
681
+ /**
682
+ * Main status command
683
+ */
684
+ async function workspaceStatus(args) {
685
+ try {
686
+ // Parse arguments
687
+ const options = parseArgs(args);
688
+
689
+ if (options.help) {
690
+ displayHelp();
691
+ return;
692
+ }
693
+
694
+ if (options.exportFormat && !['json', 'ndjson'].includes(options.exportFormat)) {
695
+ throw new Error(`Unsupported export format: ${options.exportFormat}. Expected json or ndjson.`);
696
+ }
697
+
698
+ if (options.serve) {
699
+ const server = await startStatusServer(options);
700
+ const address = server.address();
701
+ const port = typeof address === 'object' && address ? address.port : options.port;
702
+ console.log(`AIWG fleet status server listening on http://${options.bind}:${port}/status`);
703
+ return server;
704
+ }
705
+
706
+ if (options.exportFormat) {
707
+ const payload = await buildFleetStatusExport(options.projectRoot, options);
708
+ writeExportPayload(payload, options.exportFormat);
709
+ return;
710
+ }
711
+
712
+ if (options.probe) {
713
+ const probe = await buildVerificationProbe(options.projectRoot);
714
+ console.log(JSON.stringify(probe, null, 2));
715
+ return;
716
+ }
717
+
718
+ const result = await buildWorkspaceStatus(options.projectRoot);
719
+
720
+ if (!result.workspace.exists) {
721
+ if (options.json) {
722
+ console.log(JSON.stringify(result, null, 2));
723
+ } else {
724
+ console.log('\nAIWG - Workspace Status');
725
+ console.log('='.repeat(60));
726
+ console.log('');
727
+ console.log('No .aiwg/ workspace found in this project.');
728
+ console.log('');
729
+ console.log('To create a workspace:');
730
+ console.log(' aiwg use sdlc # Install SDLC framework');
731
+ console.log(' aiwg use marketing # Install Marketing framework');
732
+ console.log(' aiwg use all # Install all frameworks');
733
+ }
734
+ return;
735
+ }
736
+
737
+ // Output results
738
+ if (options.json) {
739
+ console.log(JSON.stringify(result, null, 2));
740
+ return;
741
+ }
742
+
743
+ // Text output
744
+ console.log('\nAIWG - Workspace Status');
745
+ console.log('='.repeat(60));
746
+ console.log('');
747
+
748
+ for (const rule of result.rule_pins.rules) console.log(`${rule.name}: pinned kernel (canonical ${rule.canonical_activation})`);
749
+ for (const warning of result.rule_pins.warnings) console.log(warning);
750
+
751
+ for (const line of formatDecisionOffloadReport(result.decisionOffload)) console.log(line);
752
+ console.log('');
753
+
754
+ // Workspace info
755
+ console.log('Workspace:');
756
+ console.log(` Path: ${result.workspace.path}`);
757
+ console.log(` Structure: ${result.workspace.isFrameworkScoped ? 'Framework-scoped' : 'Legacy'}`);
758
+ console.log('');
759
+
760
+ // Migration status
761
+ console.log('Migration:');
762
+ switch (result.migration.status) {
763
+ case 'completed':
764
+ console.log(' Status: ✅ Completed');
765
+ break;
766
+ case 'pending':
767
+ console.log(' Status: ⚠️ Pending (legacy structure)');
768
+ console.log(' Action: Run "aiwg -migrate-workspace" to migrate');
769
+ break;
770
+ case 'partial':
771
+ console.log(' Status: ⚠️ Partial (mixed structure)');
772
+ console.log(' Action: Run "aiwg -migrate-workspace" to complete');
773
+ break;
774
+ default:
775
+ console.log(' Status: ❓ Unknown');
776
+ }
777
+ if (result.migration.backupsAvailable > 0) {
778
+ console.log(` Backups: ${result.migration.backupsAvailable} available`);
779
+ }
780
+ console.log('');
781
+
782
+ // Frameworks
783
+ console.log('Installed Frameworks (registry):');
784
+ if (result.frameworks.length === 0) {
785
+ console.log(' (none)');
786
+ console.log('');
787
+ console.log(' To install frameworks:');
788
+ console.log(' aiwg use sdlc');
789
+ console.log(' aiwg use marketing');
790
+ } else {
791
+ for (const framework of result.frameworks) {
792
+ const emoji = statusEmoji(framework.health);
793
+ console.log(` ${emoji} ${framework.id}`);
794
+ if (options.verbose) {
795
+ console.log(` Type: ${framework.type}`);
796
+ console.log(` Version: ${framework.version}`);
797
+ console.log(` Health: ${framework.health}`);
798
+ console.log(` Installed: ${framework.installDate}`);
799
+ }
800
+ }
801
+ }
802
+ console.log('');
803
+
804
+ // Provider deployment summary
805
+ console.log('Provider Deployments:');
806
+ if (result.providerDeployments.length === 0) {
807
+ console.log(' (none detected)');
808
+ } else {
809
+ for (const deployment of result.providerDeployments) {
810
+ const counts = Object.entries(deployment.counts)
811
+ .map(([kind, count]) => `${kind}: ${count}`)
812
+ .join(' ');
813
+ console.log(` ${deployment.name.padEnd(16)} ${deployment.path.padEnd(12)} ${counts || '(no artifacts counted)'}`);
814
+ }
815
+ }
816
+ console.log('');
817
+
818
+ // Project-local bundle summary
819
+ console.log('Project-local Bundles:');
820
+ if (result.projectLocalBundles.length === 0) {
821
+ console.log(' (none detected)');
822
+ } else {
823
+ for (const group of result.projectLocalBundles) {
824
+ console.log(` ${group.type.padEnd(12)} ${group.count} (${group.names.join(', ')})`);
825
+ }
826
+ }
827
+ console.log('');
828
+
829
+ // Health summary
830
+ console.log('Health:');
831
+ console.log(` Overall: ${statusEmoji(result.health.overall)} ${result.health.overall}`);
832
+ if (result.health.issues.length > 0) {
833
+ console.log(' Issues:');
834
+ for (const issue of result.health.issues) {
835
+ console.log(` ${statusEmoji(issue.severity)} ${issue.message}`);
836
+ if (issue.action) {
837
+ console.log(` → ${issue.action}`);
838
+ }
839
+ }
840
+ }
841
+ console.log('');
842
+
843
+ } catch (error) {
844
+ console.error(`\nError: ${error.message}`);
845
+ if (error.stack && process.env.DEBUG) {
846
+ console.error(error.stack);
847
+ }
848
+ process.exit(1);
849
+ }
850
+ }
851
+
852
+ // ===========================
853
+ // CLI Entry Point
854
+ // ===========================
855
+
856
+ if (process.argv[1] === fileURLToPath(import.meta.url)) {
857
+ const args = process.argv.slice(2);
858
+ await workspaceStatus(args);
859
+ }
860
+
861
+ export { workspaceStatus };
862
+ export { buildFleetStatusExport, buildVerificationProbe, buildWorkspaceStatus, startStatusServer };