@aiwg/cli 2026.10.3 → 2026.10.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/agentic/code/providers/model-capabilities.v1.json +96 -4
- package/bin/aiwg.mjs +50 -27
- package/dist/src/agents/grounding/knowledge-base.js +54 -6
- package/dist/src/artifacts/browser-export.js +6 -0
- package/dist/src/artifacts/checksum-manifest.js +1 -0
- package/dist/src/artifacts/citation-parser.js +173 -92
- package/dist/src/artifacts/citation-table-schema.js +59 -0
- package/dist/src/artifacts/cli.js +57 -18
- package/dist/src/artifacts/corpus-tools/snapshot.js +1 -1
- package/dist/src/artifacts/corpus-tools/vision-extract.js +2 -0
- package/dist/src/artifacts/corpus-views/ref-parser.js +3 -1
- package/dist/src/artifacts/corpus-views/renderers.js +3 -3
- package/dist/src/artifacts/discovery-eval.js +6 -4
- package/dist/src/artifacts/fortemi-core-query-adapter.js +36 -3
- package/dist/src/artifacts/fortemi-core-sync.js +166 -40
- package/dist/src/artifacts/framework-capabilities.js +171 -0
- package/dist/src/artifacts/framework-index-paths.js +73 -0
- package/dist/src/artifacts/framework-source-digest.js +75 -0
- package/dist/src/artifacts/index-builder.js +285 -131
- package/dist/src/artifacts/index-files.js +132 -18
- package/dist/src/artifacts/index-preparation.js +65 -0
- package/dist/src/artifacts/index-refresh.js +576 -0
- package/dist/src/artifacts/index-worker.mjs +9 -0
- package/dist/src/artifacts/prebuilt-build-lock.js +47 -4
- package/dist/src/artifacts/prebuilt-deployment.js +46 -0
- package/dist/src/artifacts/prebuilt-relations.js +80 -0
- package/dist/src/artifacts/query-engine.js +410 -88
- package/dist/src/artifacts/relations-builder.js +168 -0
- package/dist/src/artifacts/relations.js +148 -0
- package/dist/src/artifacts/stats.js +5 -2
- package/dist/src/artifacts/types.js +30 -10
- package/dist/src/artifacts/watcher.js +1 -1
- package/dist/src/auth/credential-store.js +2 -1
- package/dist/src/catalog/cli.d.mts +5 -0
- package/dist/src/channel/manager.d.mts +20 -0
- package/dist/src/channel/manager.mjs +19 -2
- package/dist/src/cli/command-log.js +2 -3
- package/dist/src/cli/handlers/artifacts.js +3 -0
- package/dist/src/cli/handlers/auth.js +3 -0
- package/dist/src/cli/handlers/decision.js +19 -12
- package/dist/src/cli/handlers/diagnose.js +4 -3
- package/dist/src/cli/handlers/effect.js +4 -2
- package/dist/src/cli/handlers/evidence.js +3 -0
- package/dist/src/cli/handlers/help.js +1 -0
- package/dist/src/cli/handlers/index.js +3 -1
- package/dist/src/cli/handlers/init.js +4 -1
- package/dist/src/cli/handlers/lint.js +5 -0
- package/dist/src/cli/handlers/models.js +104 -8
- package/dist/src/cli/handlers/ralph-launcher.js +2 -0
- package/dist/src/cli/handlers/refresh.js +12 -0
- package/dist/src/cli/handlers/resource-versions.js +3 -0
- package/dist/src/cli/handlers/rules.js +195 -0
- package/dist/src/cli/handlers/run.js +4 -0
- package/dist/src/cli/handlers/runtime-info.js +1 -2
- package/dist/src/cli/handlers/script-runner.js +3 -2
- package/dist/src/cli/handlers/session.js +19 -9
- package/dist/src/cli/handlers/sessions.js +98 -37
- package/dist/src/cli/handlers/setup-manifest.js +39 -44
- package/dist/src/cli/handlers/setup.js +41 -0
- package/dist/src/cli/handlers/steward.js +11 -0
- package/dist/src/cli/handlers/subcommands.js +57 -2
- package/dist/src/cli/handlers/uhp.js +3 -0
- package/dist/src/cli/handlers/use.js +294 -55
- package/dist/src/cli/handlers/utilities.js +26 -10
- package/dist/src/cli/handlers/version.js +8 -0
- package/dist/src/cli/router.js +15 -1
- package/dist/src/cli/services/deployment-verification.js +19 -9
- package/dist/src/cli/services/provider-prepare-coordinator.js +24 -0
- package/dist/src/cli/skill-usage.js +2 -3
- package/dist/src/config/aiwg-config.js +87 -37
- package/dist/src/config/cli.js +111 -5
- package/dist/src/config/decision-config.js +106 -0
- package/dist/src/config/dispatch-thresholds-runtime.d.mts +8 -0
- package/dist/src/config/gitignore.js +50 -0
- package/dist/src/config/index-graph-overrides.js +36 -0
- package/dist/src/config/project-artifacts-health.d.mts +37 -0
- package/dist/src/config/project-artifacts-runtime.d.mts +16 -0
- package/dist/src/config/user-config-dir.d.mts +9 -0
- package/dist/src/config/user-config.js +2 -1
- package/dist/src/config/user-registry.js +23 -15
- package/dist/src/config/user-state.js +7 -0
- package/dist/src/decision/ask.js +203 -43
- package/dist/src/decision/context-live-qualification.js +2 -2
- package/dist/src/decision/driver.js +13 -8
- package/dist/src/decision/ensemble/aggregate.js +23 -10
- package/dist/src/decision/ensemble-study/multifact-study.js +6 -2
- package/dist/src/decision/entry.js +2 -15
- package/dist/src/decision/graph-live-qualification.js +1 -1
- package/dist/src/decision/graph-run.js +9 -0
- package/dist/src/decision/heldout/collector.js +5 -13
- package/dist/src/decision/heldout/contract.js +30 -7
- package/dist/src/decision/jev-credentials.js +15 -1
- package/dist/src/decision/ledger.js +131 -28
- package/dist/src/decision/offload-activation.js +137 -0
- package/dist/src/decision/offload.js +550 -0
- package/dist/src/decision/qualification/load.js +4 -6
- package/dist/src/decision/savings.js +28 -0
- package/dist/src/effects/types.js +5 -1
- package/dist/src/effects/verifiers/git.js +2 -1
- package/dist/src/effects/verifiers/tracker.js +26 -14
- package/dist/src/extensions/commands/definitions.js +19 -0
- package/dist/src/extensions/project-local-remove.js +36 -1
- package/dist/src/gates/providers/loader.js +61 -17
- package/dist/src/installation/manager-command.d.mts +18 -0
- package/dist/src/installation/manager-command.mjs +14 -2
- package/dist/src/installation/manager.d.mts +49 -0
- package/dist/src/installation/manager.mjs +78 -3
- package/dist/src/issues/cli.js +4 -4
- package/dist/src/issues/local.js +24 -5
- package/dist/src/jobs/runner.js +1 -1
- package/dist/src/lint/cli.js +27 -1
- package/dist/src/lint/loader.js +2 -1
- package/dist/src/lint/reporters.js +25 -1
- package/dist/src/lint/rule-activation.js +43 -0
- package/dist/src/lint/runner.js +137 -62
- package/dist/src/mcp/adapters/codex-runtime.js +7 -1
- package/dist/src/mcp/claude-install.d.mts +4 -0
- package/dist/src/mcp/claude-install.mjs +41 -0
- package/dist/src/mcp/cli.d.mts +1 -0
- package/dist/src/mcp/cli.mjs +36 -11
- package/dist/src/mcp/config-file.d.mts +28 -0
- package/dist/src/mcp/config-layers.d.mts +36 -0
- package/dist/src/mcp/credentials.d.mts +51 -0
- package/dist/src/mcp/grok-build-config.d.mts +6 -0
- package/dist/src/mcp/grok-build-config.mjs +1 -1
- package/dist/src/mcp/helpers.mjs +3 -2
- package/dist/src/mcp/muse-native-config.d.mts +13 -0
- package/dist/src/mcp/muse-native-config.mjs +1 -0
- package/dist/src/mcp/omp-config.d.mts +4 -0
- package/dist/src/mcp/registry.js +3 -93
- package/dist/src/mcp/registry.mjs +3 -87
- package/dist/src/mcp/server-config.d.mts +3 -0
- package/dist/src/mcp/server-config.mjs +89 -0
- package/dist/src/mcp/server.mjs +37 -375
- package/dist/src/mcp/test-simple.mjs +2 -1
- package/dist/src/mcp/toml-editor.d.mts +4 -0
- package/dist/src/mcp/toml-string.d.mts +1 -0
- package/dist/src/mcp/toml-strip-mcp.d.mts +1 -0
- package/dist/src/mcp/tool-filters.d.mts +46 -0
- package/dist/src/mcp/tools/decision.mjs +12 -11
- package/dist/src/mcp/tools/discovery.mjs +62 -55
- package/dist/src/mcp/tools/orchestration.mjs +12 -2
- package/dist/src/mcp/tools/worker-invoke.mjs +1340 -23
- package/dist/src/mcp/tools/worker-shell-control.mjs +52 -21
- package/dist/src/mcp/tools/worker-stdin-runtime.mjs +3 -3
- package/dist/src/mission-protocol/codecs.js +8 -0
- package/dist/src/models/config-loader.js +7 -2
- package/dist/src/models/model-capabilities.v1.json +96 -4
- package/dist/src/models/model-discovery.js +76 -11
- package/dist/src/models/provider-policy.js +101 -7
- package/dist/src/models/qualification-digest.d.mts +2 -0
- package/dist/src/models/resolver.js +12 -10
- package/dist/src/models/worker-direct-readiness.js +65 -44
- package/dist/src/models/worker-dispatch-offer.js +3 -3
- package/dist/src/models/worker-health-readiness.js +16 -3
- package/dist/src/models/worker-installation.d.mts +39 -0
- package/dist/src/models/worker-installation.mjs +135 -28
- package/dist/src/process/bounded-child-process.d.mts +84 -0
- package/dist/src/process/bounded-child-process.mjs +43 -0
- package/dist/src/process/cli-command.d.mts +16 -0
- package/dist/src/process/signal-child.d.mts +6 -0
- package/dist/src/process/signal-child.mjs +7 -0
- package/dist/src/providers/deployed-skill-roots.js +20 -0
- package/dist/src/providers/grok-build-acp.js +3 -2
- package/dist/src/providers/grok-build-headless.js +12 -4
- package/dist/src/providers/grok-build-inspect.js +1 -0
- package/dist/src/providers/omp-agent.d.mts +8 -0
- package/dist/src/providers/omp-diagnostics.d.mts +1 -0
- package/dist/src/providers/omp-paths.d.mts +13 -0
- package/dist/src/providers/skill-ownership.d.mts +9 -0
- package/dist/src/providers/skill-ownership.mjs +88 -0
- package/dist/src/rules/activation.d.mts +21 -0
- package/dist/src/rules/activation.mjs +62 -0
- package/dist/src/rules/inline-budget.d.mts +26 -0
- package/dist/src/rules/inline-budget.mjs +66 -0
- package/dist/src/rules/inventory.js +129 -0
- package/dist/src/rules/pin-state.js +73 -0
- package/dist/src/rules/pins.d.mts +14 -0
- package/dist/src/rules/pins.mjs +112 -0
- package/dist/src/rules/rule-frontmatter.schema.json +197 -0
- package/dist/src/serve/mission-conductor.js +38 -12
- package/dist/src/serve/worker-invocation.js +30 -10
- package/dist/src/serve/worker-stdin-frame.d.mts +10 -0
- package/dist/src/sessions/adapter-registry.js +23 -19
- package/dist/src/sessions/adapters/claude.js +39 -54
- package/dist/src/sessions/adapters/codex.js +82 -67
- package/dist/src/sessions/adapters/copilot.js +4 -4
- package/dist/src/sessions/adapters/cursor.js +40 -57
- package/dist/src/sessions/adapters/deepseek-harness.js +15 -12
- package/dist/src/sessions/adapters/factory.js +31 -27
- package/dist/src/sessions/adapters/generic.js +10 -10
- package/dist/src/sessions/adapters/grok-build.js +11 -11
- package/dist/src/sessions/adapters/grokbot.js +24 -13
- package/dist/src/sessions/adapters/hermes.js +27 -29
- package/dist/src/sessions/adapters/muse-workspace-scan.js +31 -11
- package/dist/src/sessions/adapters/muse.js +150 -93
- package/dist/src/sessions/adapters/omp.js +28 -14
- package/dist/src/sessions/adapters/openclaw.js +89 -79
- package/dist/src/sessions/adapters/opencode.js +27 -24
- package/dist/src/sessions/adapters/openhuman.js +18 -60
- package/dist/src/sessions/adapters/pi.js +29 -14
- package/dist/src/sessions/adapters/warp.js +3 -3
- package/dist/src/sessions/adapters/windsurf.js +19 -48
- package/dist/src/sessions/batch-contracts.js +16 -8
- package/dist/src/sessions/batch-import.js +51 -11
- package/dist/src/sessions/contracts.js +40 -2
- package/dist/src/sessions/coverage-preflight.js +1 -1
- package/dist/src/sessions/export-plan.js +4 -3
- package/dist/src/sessions/import-exclusions.js +72 -0
- package/dist/src/sessions/import-lease.js +34 -18
- package/dist/src/sessions/import-limits.js +37 -0
- package/dist/src/sessions/importer.js +60 -38
- package/dist/src/sessions/lifecycle.js +45 -0
- package/dist/src/sessions/policy.js +133 -25
- package/dist/src/sessions/readers.js +106 -53
- package/dist/src/sessions/refresh.js +106 -0
- package/dist/src/sessions/repository.js +147 -52
- package/dist/src/sessions/workspace-discovery.js +221 -56
- package/dist/src/sessions/workspace-matching.js +107 -0
- package/dist/src/skills/importer.js +1 -0
- package/dist/src/smiths/context-pipeline/aiwg-md.js +11 -0
- package/dist/src/smiths/context-pipeline/claude-hook.js +3 -26
- package/dist/src/smiths/context-pipeline/finalization.js +6 -5
- package/dist/src/smiths/context-pipeline/generator.js +21 -4
- package/dist/src/smiths/context-pipeline/parallelism-section.js +7 -15
- package/dist/src/smiths/mcpsmith/generator.js +1 -1
- package/dist/src/smiths/toolsmith/runtime-discovery.d.mts +10 -0
- package/dist/src/smiths/toolsmith/runtime-discovery.mjs +76 -18
- package/dist/src/storage/backends/fs.js +18 -76
- package/dist/src/storage/backends/obsidian.js +1 -0
- package/dist/src/tracker/forge-http.js +2 -1
- package/dist/src/update/checker.d.mts +2 -0
- package/dist/src/update/service.d.mts +22 -0
- package/dist/src/utils/contained-filesystem.d.mts +37 -0
- package/dist/src/utils/contained-filesystem.mjs +683 -0
- package/dist/src/utils/temporary-directory.js +36 -0
- package/dist/src/writing/example-generator.js +1 -1
- package/package.json +15 -2
- package/schemas/models/model-catalog.v1.schema.json +1 -1
- package/schemas/models/provider-model-capabilities.v1.schema.json +6 -0
- package/tools/_resolve-impl.mjs +13 -15
- package/tools/agents/deploy-agents.mjs +49 -21
- package/tools/agents/providers/base.mjs +104 -134
- package/tools/agents/providers/claude.mjs +8 -24
- package/tools/agents/providers/codex.mjs +18 -11
- package/tools/agents/providers/cursor.mjs +19 -18
- package/tools/agents/providers/grok-build.mjs +3 -1
- package/tools/agents/providers/hermes.mjs +57 -47
- package/tools/agents/providers/openclaw.mjs +9 -4
- package/tools/agents/providers/pi.mjs +5 -0
- package/tools/agents/providers/provider-prepare-channel.mjs +5 -0
- package/tools/agents/providers/provider-prepare-coordinator.mjs +247 -0
- package/tools/agents/providers/provider-prepare-worker.mjs +84 -0
- package/tools/agents/providers/rule-delivery.mjs +1270 -0
- package/tools/agents/providers/warp.mjs +20 -26
- package/tools/cli/decision-offload-report.mjs +40 -0
- package/tools/cli/workspace-status.mjs +862 -0
- package/tools/plugin/package-plugins.mjs +186 -8
- package/tools/plugin/plugin-links.mjs +114 -0
- package/tools/providers/antigravity-transport.mjs +3 -1
- package/tools/providers/deepseek-harness-transport.mjs +6 -4
- package/tools/providers/omp-transport.mjs +4 -2
- package/tools/ralph-external/lib/consecutive-failure-circuit-breaker.mjs +1748 -0
- package/tools/ralph-external/lib/launcher-recovery-evidence.mjs +55 -0
- package/tools/ralph-external/lib/omp-adapter.mjs +1 -1
- package/tools/ralph-external/lib/pi-adapter.mjs +0 -4
- package/tools/ralph-external/lib/provider-adapter.mjs +59 -1
- package/tools/ralph-external/lib/recovery-probe.mjs +72 -0
- package/tools/ralph-external/lib/session-environment.mjs +28 -0
- package/tools/ralph-external/lib/systemd-client.mjs +49 -0
- package/tools/ralph-external/lib/worker-health.mjs +1 -1
- package/tools/ralph-external/lib/worker-isolation.mjs +717 -0
- package/tools/ralph-external/lib/worker-overflow.mjs +50 -0
- package/tools/ralph-external/process-monitor.mjs +1017 -0
- package/tools/ralph-external/session-launcher.mjs +5057 -0
- package/tools/scripts/resource-preflight.mjs +541 -0
- package/tools/skills/deploy-skills-codex.mjs +2 -1
- 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 };
|