@aiwg/cli 2026.8.19 → 2026.8.25
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/THIRD_PARTY_NOTICES.md +12 -0
- package/dist/src/api/index.d.ts +2 -0
- package/dist/src/api/index.js +2 -0
- package/dist/src/artifacts/backend-runtime.js +26 -0
- package/dist/src/artifacts/backends/sqlite-backend.js +204 -28
- package/dist/src/artifacts/dep-graph.js +27 -5
- package/dist/src/artifacts/graph-backend.js +2 -2
- package/dist/src/artifacts/graph-query.js +21 -9
- package/dist/src/artifacts/index-builder.js +15 -0
- package/dist/src/artifacts/index-status.js +4 -1
- package/dist/src/artifacts/stats.js +4 -1
- package/dist/src/artifacts/types.js +13 -1
- package/dist/src/cli/handlers/artifact-verify.js +3 -0
- package/dist/src/cli/handlers/help.js +2 -0
- package/dist/src/cli/handlers/index.js +6 -2
- package/dist/src/cli/handlers/mission.js +27 -0
- package/dist/src/cli/handlers/refresh.js +37 -2
- package/dist/src/cli/handlers/runtime-info.js +29 -0
- package/dist/src/cli/handlers/steward.js +12 -0
- package/dist/src/cli/handlers/subcommands.js +26 -20
- package/dist/src/cli/handlers/uhp.js +88 -0
- package/dist/src/cli/handlers/utilities.js +3 -0
- package/dist/src/cli/router.js +27 -0
- package/dist/src/config/aiwg-config.js +10 -0
- package/dist/src/extensions/commands/definitions.js +38 -0
- package/dist/src/installation/manager-command.mjs +10 -1
- package/dist/src/mission-protocol/codecs.js +265 -0
- package/dist/src/mission-protocol/index.js +3 -0
- package/dist/src/mission-protocol/types.js +2 -0
- package/dist/src/storage/backend-contract.js +64 -0
- package/dist/src/storage/index.js +2 -0
- package/dist/src/storage/migration-protocol.js +378 -0
- package/dist/src/uhp/client.js +374 -0
- package/dist/src/uhp/config.js +130 -0
- package/dist/src/uhp/errors.js +63 -0
- package/dist/src/uhp/index.js +7 -0
- package/dist/src/uhp/mission.js +111 -0
- package/dist/src/uhp/sse.js +76 -0
- package/dist/src/uhp/types.js +2 -0
- package/dist/src/update/service.mjs +2 -5
- package/package.json +1 -1
|
@@ -195,6 +195,11 @@ export const BUILTIN_GRAPH_CONFIGS = {
|
|
|
195
195
|
* @implements #426
|
|
196
196
|
*/
|
|
197
197
|
export const GRAPH_CONFIGS = { ...BUILTIN_GRAPH_CONFIGS };
|
|
198
|
+
let projectGraphBackend;
|
|
199
|
+
/** Resolve backend precedence: graph override, project default, then JSON. */
|
|
200
|
+
export function resolveGraphBackendType(graph) {
|
|
201
|
+
return (graph ? GRAPH_CONFIGS[graph]?.graphBackend : undefined) ?? projectGraphBackend ?? 'json';
|
|
202
|
+
}
|
|
198
203
|
function freshBuiltinGraphConfig(name) {
|
|
199
204
|
const config = BUILTIN_GRAPH_CONFIGS[name];
|
|
200
205
|
return {
|
|
@@ -459,6 +464,7 @@ export function loadUserGraphConfigs(cwd, diagnostics) {
|
|
|
459
464
|
// Reset on every project load so a prior cwd cannot leak its override or
|
|
460
465
|
// detected Python package roots into a later build in the same process.
|
|
461
466
|
GRAPH_CONFIGS.codebase = detectPythonCodebaseConfig(cwd, freshBuiltinGraphConfig('codebase'));
|
|
467
|
+
projectGraphBackend = undefined;
|
|
462
468
|
// Load module-declared graphs first (frameworks/addons)
|
|
463
469
|
const moduleLoaded = loadModuleGraphConfigs(cwd, diagnostics);
|
|
464
470
|
const loaded = [...moduleLoaded];
|
|
@@ -469,12 +475,16 @@ export function loadUserGraphConfigs(cwd, diagnostics) {
|
|
|
469
475
|
let graphs;
|
|
470
476
|
let graphOverrides;
|
|
471
477
|
let fromDeprecatedYaml = false;
|
|
478
|
+
let canonicalIndexPresent = false;
|
|
472
479
|
// (a) Canonical: .aiwg/aiwg.config (JSON).
|
|
473
480
|
try {
|
|
474
481
|
const aiwgConfigPath = projectControlPath(cwd, 'aiwg.config');
|
|
475
482
|
if (fs.existsSync(aiwgConfigPath)) {
|
|
476
483
|
const parsed = JSON.parse(fs.readFileSync(aiwgConfigPath, 'utf-8'));
|
|
477
484
|
const idx = parsed.index;
|
|
485
|
+
canonicalIndexPresent = idx !== undefined;
|
|
486
|
+
if (idx?.graphBackend === 'json' || idx?.graphBackend === 'graphology' || idx?.graphBackend === 'sqlite')
|
|
487
|
+
projectGraphBackend = idx.graphBackend;
|
|
478
488
|
const g = idx?.graphs;
|
|
479
489
|
if (g && typeof g === 'object')
|
|
480
490
|
graphs = g;
|
|
@@ -493,12 +503,14 @@ export function loadUserGraphConfigs(cwd, diagnostics) {
|
|
|
493
503
|
});
|
|
494
504
|
}
|
|
495
505
|
// (b) Fallback: legacy .aiwg/config.yaml.
|
|
496
|
-
if (!graphs && !graphOverrides) {
|
|
506
|
+
if (!canonicalIndexPresent && !graphs && !graphOverrides) {
|
|
497
507
|
try {
|
|
498
508
|
const configPath = projectAiwgPath(cwd, 'config.yaml');
|
|
499
509
|
if (fs.existsSync(configPath)) {
|
|
500
510
|
const config = loadYaml(fs.readFileSync(configPath, 'utf-8'));
|
|
501
511
|
const idx = config?.index;
|
|
512
|
+
if (idx?.graphBackend === 'json' || idx?.graphBackend === 'graphology' || idx?.graphBackend === 'sqlite')
|
|
513
|
+
projectGraphBackend = idx.graphBackend;
|
|
502
514
|
const g = idx?.graphs;
|
|
503
515
|
if (g && typeof g === 'object') {
|
|
504
516
|
graphs = g;
|
|
@@ -102,6 +102,9 @@ export const artifactVerifyHandler = {
|
|
|
102
102
|
description: 'Verify cross-asset DSSE provenance and manage trust roots',
|
|
103
103
|
category: 'utility',
|
|
104
104
|
aliases: [],
|
|
105
|
+
async help() {
|
|
106
|
+
return { exitCode: 0, message: usage() };
|
|
107
|
+
},
|
|
105
108
|
async execute(ctx) {
|
|
106
109
|
if (ctx.args.includes('--help') || ctx.args.includes('-h') || ctx.args.length === 0)
|
|
107
110
|
return { exitCode: 0, message: usage() };
|
|
@@ -71,6 +71,8 @@ function displayHelp() {
|
|
|
71
71
|
['runtime-info', 'Show runtime environment summary'],
|
|
72
72
|
['runtime-info --discover', 'Full tool discovery and catalog generation'],
|
|
73
73
|
['runtime-info --check <tool>', 'Check specific tool availability'],
|
|
74
|
+
['runtime-info --transports', 'Show configured transport capabilities separately from providers'],
|
|
75
|
+
['uhp <operation> --profile <name>', 'Inspect or smoke-test an explicit experimental UHP endpoint profile'],
|
|
74
76
|
]);
|
|
75
77
|
helpGroup('CATALOG', [
|
|
76
78
|
['catalog list', 'List all models in catalog'],
|
|
@@ -32,6 +32,7 @@ import { regenerateHandler, regenerateHandlers } from './regenerate.js';
|
|
|
32
32
|
import { workspaceContextHandler, workspaceContextHandlers } from './workspace-context.js';
|
|
33
33
|
import { artifactsHandler, artifactsHandlers } from './artifacts.js';
|
|
34
34
|
import { mcHandler, mcHandlers } from './mc.js';
|
|
35
|
+
import { missionHandlers } from './mission.js';
|
|
35
36
|
import { sdlcAccelerateHandler } from './sdlc-accelerate.js';
|
|
36
37
|
import { teamHandler, teamHandlers } from './team.js';
|
|
37
38
|
import { installHandler } from './install.js';
|
|
@@ -64,6 +65,7 @@ import { evidenceHandler } from './evidence.js';
|
|
|
64
65
|
import { artifactVerifyHandler } from './artifact-verify.js';
|
|
65
66
|
import { outputModeHandler } from './output-mode.js';
|
|
66
67
|
import { installationHandler } from './installation.js';
|
|
68
|
+
import { uhpHandler } from './uhp.js';
|
|
67
69
|
// Re-export individual handlers
|
|
68
70
|
export {
|
|
69
71
|
// Maintenance
|
|
@@ -75,7 +77,7 @@ newBundleHandler, quickrefHandler, newProjectHandler, sessionHandler, sessionsHa
|
|
|
75
77
|
// Workspace
|
|
76
78
|
statusHandler, wizardHandler, migrateWorkspaceHandler, rollbackWorkspaceHandler,
|
|
77
79
|
// Subcommands
|
|
78
|
-
mcpHandler, catalogHandler, modelsHandler, versionsHandler, indexHandler, artifactsHandler, corpusHandler, discoverHandler, showHandler, featuresHandler, skillsHandler, configHandler, opsHandler, storageHandler, activityLogHandler, commandLogHandler, skillUsageHandler, kbHandler, memoryHandler, reflectionsHandler, provenanceHandler, researchStoreHandler, researchQueryHandler, runtimeInfoHandler, agentcardHandler,
|
|
80
|
+
mcpHandler, catalogHandler, modelsHandler, versionsHandler, indexHandler, artifactsHandler, corpusHandler, discoverHandler, showHandler, featuresHandler, skillsHandler, configHandler, opsHandler, storageHandler, activityLogHandler, commandLogHandler, skillUsageHandler, kbHandler, memoryHandler, reflectionsHandler, provenanceHandler, researchStoreHandler, researchQueryHandler, runtimeInfoHandler, agentcardHandler, uhpHandler,
|
|
79
81
|
// Agentic Tools (RLM)
|
|
80
82
|
chunkHandler, fanoutHandler, rlmPrepHandler, rlmSearchHandler, rlmStatusCliHandler, rlmCacheHandler,
|
|
81
83
|
// Utilities
|
|
@@ -111,7 +113,7 @@ repoAccessHandler,
|
|
|
111
113
|
// Lint
|
|
112
114
|
lintHandler, };
|
|
113
115
|
// Re-export handler arrays
|
|
114
|
-
export { workspaceHandlers, utilityHandlers, scaffoldingHandlers, ralphHandlers, subcommandHandlers, mcHandlers, teamHandlers, stewardHandlers, regenerateHandlers, workspaceContextHandlers, artifactsHandlers, daemonHandlers, sandboxHandlers, repoAccessHandlers, };
|
|
116
|
+
export { workspaceHandlers, utilityHandlers, scaffoldingHandlers, ralphHandlers, subcommandHandlers, mcHandlers, missionHandlers, teamHandlers, stewardHandlers, regenerateHandlers, workspaceContextHandlers, artifactsHandlers, daemonHandlers, sandboxHandlers, repoAccessHandlers, };
|
|
115
117
|
/**
|
|
116
118
|
* All registered command handlers
|
|
117
119
|
*
|
|
@@ -170,6 +172,7 @@ export const allHandlers = [
|
|
|
170
172
|
skillsHandler,
|
|
171
173
|
runtimeInfoHandler,
|
|
172
174
|
agentcardHandler,
|
|
175
|
+
uhpHandler,
|
|
173
176
|
// Utilities
|
|
174
177
|
prefillCardsHandler,
|
|
175
178
|
contributeStartHandler,
|
|
@@ -188,6 +191,7 @@ export const allHandlers = [
|
|
|
188
191
|
...ralphHandlers,
|
|
189
192
|
// Mission Control
|
|
190
193
|
...mcHandlers,
|
|
194
|
+
...missionHandlers,
|
|
191
195
|
// Agent Teams
|
|
192
196
|
...teamHandlers,
|
|
193
197
|
// Steward (capability awareness)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const usage = `Usage: aiwg mission migrate [--dry-run] [--root <workspace>]
|
|
2
|
+
aiwg mission migrate --apply --target mission.aiwg.io/v1 [--id <id>]
|
|
3
|
+
aiwg mission migrate --verify <id> | --resume <id> | --rollback <id>
|
|
4
|
+
|
|
5
|
+
Preview is the default. Apply is backup-first and requires an explicit target.`;
|
|
6
|
+
export const missionHandler = {
|
|
7
|
+
id: 'mission',
|
|
8
|
+
name: 'Mission Protocol',
|
|
9
|
+
description: 'Preview, apply, verify, resume, or roll back Mission Protocol migrations',
|
|
10
|
+
category: 'orchestration',
|
|
11
|
+
aliases: [],
|
|
12
|
+
async execute(ctx) {
|
|
13
|
+
if (ctx.args[0] !== 'migrate' || ctx.args.includes('--help') || ctx.args.includes('-h')) {
|
|
14
|
+
return { exitCode: ctx.args[0] === 'migrate' ? 0 : 2, message: usage, rawOutput: true };
|
|
15
|
+
}
|
|
16
|
+
const { spawn } = await import('node:child_process');
|
|
17
|
+
const path = await import('node:path');
|
|
18
|
+
const script = path.join(ctx.frameworkRoot, 'tools/mission-protocol/migrate.mjs');
|
|
19
|
+
return await new Promise(resolve => {
|
|
20
|
+
const child = spawn(process.execPath, [script, ...ctx.args.slice(1), '--root', ctx.cwd], { stdio: 'inherit', signal: ctx.signal });
|
|
21
|
+
child.once('error', error => resolve({ exitCode: 1, error, message: error.message }));
|
|
22
|
+
child.once('exit', code => resolve({ exitCode: code ?? 1 }));
|
|
23
|
+
});
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
export const missionHandlers = [missionHandler];
|
|
27
|
+
//# sourceMappingURL=mission.js.map
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*
|
|
11
11
|
* @implements @agentic/code/frameworks/sdlc-complete/rules/self-maintenance.md
|
|
12
12
|
* @source @src/cli/router.ts
|
|
13
|
-
* @issue #482, #557, #694
|
|
13
|
+
* @issue #173, #174, #482, #557, #694
|
|
14
14
|
*/
|
|
15
15
|
import { promises as fs } from 'fs';
|
|
16
16
|
import path from 'path';
|
|
@@ -151,6 +151,30 @@ export function collectModelDeployArgs(args) {
|
|
|
151
151
|
}
|
|
152
152
|
return forwarded;
|
|
153
153
|
}
|
|
154
|
+
const REFRESH_HELP = `Usage: aiwg refresh [options]
|
|
155
|
+
|
|
156
|
+
Update AIWG, re-deploy installed frameworks, and run health verification.
|
|
157
|
+
|
|
158
|
+
Options:
|
|
159
|
+
--dry-run Preview changes without updating or deploying
|
|
160
|
+
--quiet Suppress progress output
|
|
161
|
+
--skip-update Skip the installation update
|
|
162
|
+
--packages-only Refresh remote packages only
|
|
163
|
+
--provider <name> Override provider auto-detection
|
|
164
|
+
--channel <name> Select the update channel (stable or main)
|
|
165
|
+
--frameworks <list> Re-deploy a comma-separated installed subset
|
|
166
|
+
--model <name> Override all deployed agent model tiers
|
|
167
|
+
--reasoning-model <name> Override the reasoning model tier
|
|
168
|
+
--coding-model <name> Override the coding model tier
|
|
169
|
+
--efficiency-model <name> Override the efficiency model tier
|
|
170
|
+
--filter <pattern> Limit model deployment by agent name
|
|
171
|
+
--filter-role <role> Limit model deployment by role
|
|
172
|
+
--model-tier <tier> Limit model deployment by tier
|
|
173
|
+
--save Save model overrides to the project
|
|
174
|
+
--save-user Save model overrides to user configuration
|
|
175
|
+
-h, --help Show this help without running refresh
|
|
176
|
+
|
|
177
|
+
Alias: aiwg sync (deprecated)`;
|
|
154
178
|
/**
|
|
155
179
|
* Refresh command handler (formerly sync)
|
|
156
180
|
*/
|
|
@@ -160,6 +184,9 @@ export const refreshHandler = {
|
|
|
160
184
|
description: 'Refresh AIWG to latest version and re-deploy installed frameworks',
|
|
161
185
|
category: 'maintenance',
|
|
162
186
|
aliases: ['--refresh', 'sync', '--sync'],
|
|
187
|
+
async help() {
|
|
188
|
+
return { exitCode: 0, message: REFRESH_HELP, rawOutput: true };
|
|
189
|
+
},
|
|
163
190
|
async execute(ctx) {
|
|
164
191
|
const dryRun = hasFlag(ctx.args, '--dry-run');
|
|
165
192
|
const quiet = hasFlag(ctx.args, '--quiet');
|
|
@@ -451,6 +478,10 @@ export const refreshHandler = {
|
|
|
451
478
|
if (dryRun) {
|
|
452
479
|
ui.info('Dry run complete — no changes made');
|
|
453
480
|
}
|
|
481
|
+
else if (updateFailure) {
|
|
482
|
+
ui.warn(`Refresh completed with installation update failure (exit ${updateFailure.exitCode}); ` +
|
|
483
|
+
're-deployment continued, but AIWG may still be on the previous version.');
|
|
484
|
+
}
|
|
454
485
|
else {
|
|
455
486
|
ui.success('Refresh complete');
|
|
456
487
|
}
|
|
@@ -459,7 +490,11 @@ export const refreshHandler = {
|
|
|
459
490
|
// Quiet mode: JSON output
|
|
460
491
|
if (quiet) {
|
|
461
492
|
const output = JSON.stringify({
|
|
462
|
-
status: dryRun
|
|
493
|
+
status: dryRun
|
|
494
|
+
? 'dry-run'
|
|
495
|
+
: updateFailure
|
|
496
|
+
? 'refreshed-with-update-failure'
|
|
497
|
+
: 'refreshed',
|
|
463
498
|
provider: detectedProvider,
|
|
464
499
|
frameworks,
|
|
465
500
|
skipUpdate,
|
|
@@ -53,8 +53,37 @@ async function handleRuntimeInfo(args, cwd = process.cwd()) {
|
|
|
53
53
|
const hasCheck = checkIndex >= 0;
|
|
54
54
|
const hasCapabilities = args.includes('--capabilities');
|
|
55
55
|
const hasProviders = args.includes('--providers');
|
|
56
|
+
const hasTransports = args.includes('--transports');
|
|
56
57
|
const featureIndex = args.indexOf('--feature');
|
|
57
58
|
const hasFeature = featureIndex >= 0;
|
|
59
|
+
if (hasTransports) {
|
|
60
|
+
const { readAiwgConfig } = await import('../../config/aiwg-config.js');
|
|
61
|
+
const config = await readAiwgConfig(cwd);
|
|
62
|
+
const profiles = Object.entries(config?.uhp?.profiles ?? {}).map(([name, profile]) => ({
|
|
63
|
+
name,
|
|
64
|
+
transport: 'uhp',
|
|
65
|
+
protocolVersion: profile.version,
|
|
66
|
+
experimental: true,
|
|
67
|
+
configured: true,
|
|
68
|
+
enabled: config?.uhp?.enabled === true,
|
|
69
|
+
endpointOrigin: new URL(profile.endpoint).origin,
|
|
70
|
+
credentialSource: profile.credential.source,
|
|
71
|
+
credentialReference: profile.credential.name,
|
|
72
|
+
}));
|
|
73
|
+
const output = { providersAreTransports: false, transports: { uhp: { experimental: true, enabled: config?.uhp?.enabled === true, profiles } } };
|
|
74
|
+
if (hasJson)
|
|
75
|
+
console.log(JSON.stringify(output, null, 2));
|
|
76
|
+
else {
|
|
77
|
+
console.log('\nTransport Inventory');
|
|
78
|
+
console.log('===================');
|
|
79
|
+
console.log('UHP is a remote execution transport, not an AIWG provider.');
|
|
80
|
+
if (!profiles.length)
|
|
81
|
+
console.log(' UHP: not configured (experimental)');
|
|
82
|
+
for (const profile of profiles)
|
|
83
|
+
console.log(` UHP/${profile.name}: ${profile.enabled ? 'enabled' : 'disabled'}, ${profile.protocolVersion}, ${profile.endpointOrigin}`);
|
|
84
|
+
}
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
58
87
|
// --- Capability matrix queries (no RuntimeDiscovery needed) ---
|
|
59
88
|
if (hasProviders) {
|
|
60
89
|
const { collectProviderInventory } = await import('../../providers/provider-inventory.js');
|
|
@@ -182,6 +182,7 @@ async function handleSteward(args, ctx) {
|
|
|
182
182
|
aiwg steward capabilities --feature <name> Provider support matrix for a feature
|
|
183
183
|
aiwg steward capabilities --all Full matrix (all providers x features)
|
|
184
184
|
aiwg steward find --capability <name> Routing advice for your current provider
|
|
185
|
+
aiwg steward transports Report transport capabilities separately from providers
|
|
185
186
|
aiwg steward models [--complex|--high-impact] Model policy/discovery routing advice
|
|
186
187
|
aiwg steward models --route --capability-type <agent|skill|rule|workflow>
|
|
187
188
|
--capability <id> --assignment <text> [--provider <name>] [--json]
|
|
@@ -263,6 +264,17 @@ async function handleSteward(args, ctx) {
|
|
|
263
264
|
exitCode: EXIT_CODES.USAGE,
|
|
264
265
|
});
|
|
265
266
|
}
|
|
267
|
+
if (subcommand === 'transports') {
|
|
268
|
+
const projectDir = ctx ? getProjectDir(ctx, args) : process.cwd();
|
|
269
|
+
const config = await readAiwgConfig(projectDir);
|
|
270
|
+
const profiles = Object.keys(config?.uhp?.profiles ?? {});
|
|
271
|
+
console.log('\n Remote execution transports');
|
|
272
|
+
console.log(' ───────────────────────────');
|
|
273
|
+
console.log(` UHP ${config?.uhp?.enabled ? 'enabled' : 'disabled'} (experimental client transport; not a provider capability)`);
|
|
274
|
+
console.log(` Profiles: ${profiles.length ? profiles.join(', ') : 'none'}`);
|
|
275
|
+
console.log(' Routing: explicit `aiwg uhp <operation> --profile <name>`; no UHP↔A2A fallback.');
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
266
278
|
const matrix = loadCapabilityMatrix();
|
|
267
279
|
if (subcommand === 'capabilities') {
|
|
268
280
|
const providerFlag = args.indexOf('--provider');
|
|
@@ -1139,34 +1139,40 @@ export const pluginStatusHandler = {
|
|
|
1139
1139
|
*
|
|
1140
1140
|
* Delegates to tools/plugin/package-plugins.mjs
|
|
1141
1141
|
*/
|
|
1142
|
+
function packagePluginHelp() {
|
|
1143
|
+
return {
|
|
1144
|
+
exitCode: 0,
|
|
1145
|
+
message: [
|
|
1146
|
+
"aiwg package-plugin — package a project-local or built-in marketplace wrapper",
|
|
1147
|
+
"",
|
|
1148
|
+
"Usage:",
|
|
1149
|
+
" aiwg package-plugin <name> [--source <path>] [--output <path>] [--provider <name>] [--clean] [--dry-run]",
|
|
1150
|
+
" aiwg package-plugin --plugin <name> [options] # compatibility form",
|
|
1151
|
+
"",
|
|
1152
|
+
"Options:",
|
|
1153
|
+
" --source <path> explicit project-local wrapper source (must stay inside the project)",
|
|
1154
|
+
" --output <path> standalone archive output (default: dist/plugins)",
|
|
1155
|
+
" --provider <name> claude, codex, or all for standalone wrappers; built-ins retain all formats",
|
|
1156
|
+
" --clean clean generated plugin output before packaging",
|
|
1157
|
+
" --dry-run, -n preview without writing",
|
|
1158
|
+
" --help, -h show this help",
|
|
1159
|
+
"",
|
|
1160
|
+
"Project-local wrappers are discovered under .aiwg/plugins and packaged as deterministic archives.",
|
|
1161
|
+
].join("\n"),
|
|
1162
|
+
};
|
|
1163
|
+
}
|
|
1142
1164
|
export const packagePluginHandler = {
|
|
1143
1165
|
id: "package-plugin",
|
|
1144
1166
|
name: "Package Plugin",
|
|
1145
1167
|
description: "Package a plugin for distribution",
|
|
1146
1168
|
category: "plugin",
|
|
1147
1169
|
aliases: ["-package-plugin", "--package-plugin"],
|
|
1170
|
+
async help() {
|
|
1171
|
+
return packagePluginHelp();
|
|
1172
|
+
},
|
|
1148
1173
|
async execute(ctx) {
|
|
1149
1174
|
if (ctx.args.includes("--help") || ctx.args.includes("-h")) {
|
|
1150
|
-
return
|
|
1151
|
-
exitCode: 0,
|
|
1152
|
-
message: [
|
|
1153
|
-
"aiwg package-plugin — package a project-local or built-in marketplace wrapper",
|
|
1154
|
-
"",
|
|
1155
|
-
"Usage:",
|
|
1156
|
-
" aiwg package-plugin <name> [--source <path>] [--output <path>] [--provider <name>] [--clean] [--dry-run]",
|
|
1157
|
-
" aiwg package-plugin --plugin <name> [options] # compatibility form",
|
|
1158
|
-
"",
|
|
1159
|
-
"Options:",
|
|
1160
|
-
" --source <path> explicit project-local wrapper source (must stay inside the project)",
|
|
1161
|
-
" --output <path> standalone archive output (default: dist/plugins)",
|
|
1162
|
-
" --provider <name> claude, codex, or all for standalone wrappers; built-ins retain all formats",
|
|
1163
|
-
" --clean clean generated plugin output before packaging",
|
|
1164
|
-
" --dry-run, -n preview without writing",
|
|
1165
|
-
" --help, -h show this help",
|
|
1166
|
-
"",
|
|
1167
|
-
"Project-local wrappers are discovered under .aiwg/plugins and packaged as deterministic archives.",
|
|
1168
|
-
].join("\n"),
|
|
1169
|
-
};
|
|
1175
|
+
return packagePluginHelp();
|
|
1170
1176
|
}
|
|
1171
1177
|
const hasExplicitPlugin = ctx.args.includes("--plugin") || ctx.args.includes("-p");
|
|
1172
1178
|
const positional = ctx.args[0] && !ctx.args[0].startsWith("-")
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { readAiwgConfig } from '../../config/aiwg-config.js';
|
|
2
|
+
import { resolveUhpProfile } from '../../uhp/config.js';
|
|
3
|
+
import { UhpClient } from '../../uhp/client.js';
|
|
4
|
+
import { projectUhpResponseToCanonicalMission, projectUhpResponseToMission, unknownUhpMissionEvidence } from '../../uhp/mission.js';
|
|
5
|
+
import { UhpError } from '../../uhp/errors.js';
|
|
6
|
+
function valueAfter(args, name) {
|
|
7
|
+
const index = args.indexOf(name);
|
|
8
|
+
return index >= 0 ? args[index + 1] : undefined;
|
|
9
|
+
}
|
|
10
|
+
function usage() {
|
|
11
|
+
return `Usage: aiwg uhp <discover|harnesses|models|run> --profile <name> [options]
|
|
12
|
+
|
|
13
|
+
Experimental UHP ${'2026-08-11'} client (client support only; no server conformance claim)
|
|
14
|
+
|
|
15
|
+
discover Inspect unauthenticated capability discovery
|
|
16
|
+
harnesses List configured remote harnesses
|
|
17
|
+
models [--harness <id>] List global or per-harness model availability
|
|
18
|
+
run --input <text> [--harness <id>] [--model <id>] [--stream]
|
|
19
|
+
Run an explicit smoke task
|
|
20
|
+
|
|
21
|
+
All commands require --profile. Credentials are resolved from the profile's
|
|
22
|
+
secret reference at request time; bearer values are never accepted as CLI arguments.`;
|
|
23
|
+
}
|
|
24
|
+
async function executeUhp(ctx) {
|
|
25
|
+
const [operation] = ctx.args;
|
|
26
|
+
if (!operation || operation === 'help' || operation === '--help')
|
|
27
|
+
return { exitCode: 0, message: usage(), rawOutput: true };
|
|
28
|
+
const profileName = valueAfter(ctx.args, '--profile');
|
|
29
|
+
if (!profileName)
|
|
30
|
+
return { exitCode: 2, message: 'UHP routing requires explicit --profile <name> selection.' };
|
|
31
|
+
const config = await readAiwgConfig(ctx.cwd);
|
|
32
|
+
const profile = resolveUhpProfile(config?.uhp, profileName);
|
|
33
|
+
const client = new UhpClient(profileName, profile);
|
|
34
|
+
if (operation === 'discover')
|
|
35
|
+
return { exitCode: 0, message: JSON.stringify(await client.discover(), null, 2), rawOutput: true };
|
|
36
|
+
if (operation === 'harnesses')
|
|
37
|
+
return { exitCode: 0, message: JSON.stringify(await client.listHarnesses(), null, 2), rawOutput: true };
|
|
38
|
+
if (operation === 'models')
|
|
39
|
+
return { exitCode: 0, message: JSON.stringify(await client.listModels(valueAfter(ctx.args, '--harness')), null, 2), rawOutput: true };
|
|
40
|
+
if (operation !== 'run')
|
|
41
|
+
return { exitCode: 2, message: `Unknown UHP operation '${operation}'.\n${usage()}`, rawOutput: true };
|
|
42
|
+
const input = valueAfter(ctx.args, '--input');
|
|
43
|
+
if (!input)
|
|
44
|
+
return { exitCode: 2, message: 'aiwg uhp run requires --input <text>.' };
|
|
45
|
+
const request = {
|
|
46
|
+
input,
|
|
47
|
+
...(valueAfter(ctx.args, '--model') ? { model: valueAfter(ctx.args, '--model') } : {}),
|
|
48
|
+
metadata: { ...(valueAfter(ctx.args, '--harness') ? { harness_id: valueAfter(ctx.args, '--harness') } : {}) },
|
|
49
|
+
};
|
|
50
|
+
if (ctx.args.includes('--stream')) {
|
|
51
|
+
const events = [];
|
|
52
|
+
let terminal;
|
|
53
|
+
try {
|
|
54
|
+
for await (const event of client.streamResponse(request, { signal: ctx.signal })) {
|
|
55
|
+
events.push(event);
|
|
56
|
+
if (event.response)
|
|
57
|
+
terminal = event.response;
|
|
58
|
+
}
|
|
59
|
+
const canonical = terminal ? projectUhpResponseToCanonicalMission(profileName, terminal, request, events.at(-1)) : undefined;
|
|
60
|
+
return { exitCode: 0, message: JSON.stringify({ events, evidence: terminal ? projectUhpResponseToMission(profileName, terminal, request, events.at(-1)) : unknownUhpMissionEvidence(profileName, 'Stream ended without a response'), ...(canonical ? { mission: canonical.value, adapter: { sourceVersion: canonical.sourceVersion, warnings: canonical.warnings, lossReport: canonical.lossReport } } : {}) }, null, 2), rawOutput: true };
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
if (error instanceof UhpError && error.options.remoteState === 'unknown') {
|
|
64
|
+
return { exitCode: 1, message: JSON.stringify({ error: { code: error.code, message: error.message }, evidence: unknownUhpMissionEvidence(profileName, error.message, terminal?.id, events.at(-1)?.sequence_number) }, null, 2), rawOutput: true };
|
|
65
|
+
}
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const response = await client.createResponse(request, { signal: ctx.signal });
|
|
70
|
+
const canonical = projectUhpResponseToCanonicalMission(profileName, response, request);
|
|
71
|
+
return { exitCode: 0, message: JSON.stringify({ response, evidence: projectUhpResponseToMission(profileName, response, request), mission: canonical.value, adapter: { sourceVersion: canonical.sourceVersion, warnings: canonical.warnings, lossReport: canonical.lossReport } }, null, 2), rawOutput: true };
|
|
72
|
+
}
|
|
73
|
+
export const uhpHandler = {
|
|
74
|
+
id: 'uhp',
|
|
75
|
+
name: 'Unified Harness Protocol',
|
|
76
|
+
description: 'Inspect and smoke-test an explicitly selected experimental UHP endpoint profile',
|
|
77
|
+
category: 'toolsmith',
|
|
78
|
+
aliases: [],
|
|
79
|
+
async execute(ctx) {
|
|
80
|
+
try {
|
|
81
|
+
return await executeUhp(ctx);
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
return { exitCode: error instanceof UhpError || error instanceof Error ? 1 : 1, message: error instanceof Error ? error.message : 'UHP operation failed' };
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
//# sourceMappingURL=uhp.js.map
|
|
@@ -466,6 +466,9 @@ export const doctorHandler = {
|
|
|
466
466
|
description: 'Run health diagnostics',
|
|
467
467
|
category: 'maintenance',
|
|
468
468
|
aliases: ['-doctor', '--doctor'],
|
|
469
|
+
async help() {
|
|
470
|
+
return { exitCode: 0, message: DOCTOR_HELP, rawOutput: true };
|
|
471
|
+
},
|
|
469
472
|
async execute(ctx) {
|
|
470
473
|
if (ctx.args.includes('--help') || ctx.args.includes('-h')) {
|
|
471
474
|
return { exitCode: 0, message: DOCTOR_HELP, rawOutput: true };
|
package/dist/src/cli/router.js
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* @tests @test/unit/cli/router.test.ts
|
|
11
11
|
* @issue #33
|
|
12
12
|
* @issue #58
|
|
13
|
+
* @issue #174
|
|
13
14
|
*/
|
|
14
15
|
import { loadRegistry } from '../extensions/loader.js';
|
|
15
16
|
import { getFrameworkRoot } from '../channel/manager.mjs';
|
|
@@ -134,6 +135,32 @@ export async function run(args, options = {}) {
|
|
|
134
135
|
ui.error(`No handler found for command: ${commandId}`);
|
|
135
136
|
process.exit(1);
|
|
136
137
|
}
|
|
138
|
+
// Help must be intercepted before hooks and the normal handler path. Some
|
|
139
|
+
// commands mutate project or installation state, so passing an unrecognised
|
|
140
|
+
// help flag through to execute() is unsafe (#174).
|
|
141
|
+
if (commandArgs.includes('--help') || commandArgs.includes('-h')) {
|
|
142
|
+
const ctx = await buildContext(commandArgs, args, options);
|
|
143
|
+
if (handler.help) {
|
|
144
|
+
const result = await handler.help(ctx);
|
|
145
|
+
if (result.message) {
|
|
146
|
+
if (result.rawOutput) {
|
|
147
|
+
process.stdout.write(result.message.endsWith('\n') ? result.message : `${result.message}\n`);
|
|
148
|
+
}
|
|
149
|
+
else if (result.exitCode !== 0) {
|
|
150
|
+
ui.error(result.message);
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
ui.info(result.message);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (result.exitCode !== 0)
|
|
157
|
+
process.exit(result.exitCode);
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
ui.info(`No detailed help for \`aiwg ${commandId}\`. Run \`aiwg help\` for the command overview.`);
|
|
161
|
+
}
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
137
164
|
// Build context for handler and hooks
|
|
138
165
|
const ctx = await buildContext(commandArgs, args, options);
|
|
139
166
|
// Build hook context
|
|
@@ -17,6 +17,7 @@ import { validateAuthorization, } from '../policy/authorization.js';
|
|
|
17
17
|
import { projectAiwgPath, projectControlPath, resolveProjectAiwgDir, } from './project-artifacts.js';
|
|
18
18
|
import { defaultThreatAssessmentConfig, validateThreatAssessmentConfig, } from '../security/threat-assessment-config.js';
|
|
19
19
|
import { defaultArtifactOutputs, validateArtifactOutputs } from '../artifacts/output-policy.js';
|
|
20
|
+
import { validateUhpConfig } from '../uhp/config.js';
|
|
20
21
|
const CONFIG_FILENAME = 'aiwg.config';
|
|
21
22
|
/**
|
|
22
23
|
* Operations that a workspace may authorize for one member repository.
|
|
@@ -159,6 +160,9 @@ export function validateIndexConfig(index) {
|
|
|
159
160
|
return ['index: must be an object'];
|
|
160
161
|
}
|
|
161
162
|
const indexObject = index;
|
|
163
|
+
if (indexObject.graphBackend !== undefined && !GRAPH_BACKENDS.includes(indexObject.graphBackend)) {
|
|
164
|
+
errors.push(`index.graphBackend: must be one of ${GRAPH_BACKENDS.join(' | ')}`);
|
|
165
|
+
}
|
|
162
166
|
const isStringArray = (v) => Array.isArray(v) && v.every((x) => typeof x === 'string');
|
|
163
167
|
const graphOverrides = indexObject.graphOverrides;
|
|
164
168
|
if (graphOverrides !== undefined) {
|
|
@@ -772,6 +776,9 @@ export async function readAiwgConfig(projectDir) {
|
|
|
772
776
|
const artifactOutputErrors = validateArtifactOutputs(parsed.artifact_outputs);
|
|
773
777
|
if (artifactOutputErrors.length > 0)
|
|
774
778
|
throw new Error(`Invalid .aiwg/aiwg.config:\n${artifactOutputErrors.join('\n')}`);
|
|
779
|
+
const uhpErrors = validateUhpConfig(parsed.uhp);
|
|
780
|
+
if (uhpErrors.length > 0)
|
|
781
|
+
throw new Error(`Invalid .aiwg/aiwg.config:\n${uhpErrors.join('\n')}`);
|
|
775
782
|
return parsed;
|
|
776
783
|
}
|
|
777
784
|
/**
|
|
@@ -787,6 +794,9 @@ export async function writeAiwgConfig(projectDir, config) {
|
|
|
787
794
|
const artifactOutputErrors = validateArtifactOutputs(config.artifact_outputs);
|
|
788
795
|
if (artifactOutputErrors.length > 0)
|
|
789
796
|
throw new Error(`Invalid .aiwg/aiwg.config:\n${artifactOutputErrors.join('\n')}`);
|
|
797
|
+
const uhpErrors = validateUhpConfig(config.uhp);
|
|
798
|
+
if (uhpErrors.length > 0)
|
|
799
|
+
throw new Error(`Invalid .aiwg/aiwg.config:\n${uhpErrors.join('\n')}`);
|
|
790
800
|
const localPath = getConfigPath(projectDir);
|
|
791
801
|
const artifactDir = resolveProjectAiwgDir(projectDir);
|
|
792
802
|
const artifactPath = join(artifactDir, CONFIG_FILENAME);
|
|
@@ -1663,6 +1663,24 @@ export const agentcardCommand = {
|
|
|
1663
1663
|
},
|
|
1664
1664
|
},
|
|
1665
1665
|
};
|
|
1666
|
+
export const uhpCommand = {
|
|
1667
|
+
id: 'uhp',
|
|
1668
|
+
type: 'command',
|
|
1669
|
+
name: 'Unified Harness Protocol',
|
|
1670
|
+
description: 'Inspect discovery and run smoke tasks through an explicit experimental UHP endpoint profile',
|
|
1671
|
+
version: '1.0.0',
|
|
1672
|
+
capabilities: ['cli', 'uhp', 'remote-harness', 'transport'],
|
|
1673
|
+
keywords: ['uhp', 'unified harness protocol', 'remote harness', 'responses'],
|
|
1674
|
+
category: 'toolsmith',
|
|
1675
|
+
platforms: { claude: 'full', generic: 'full' },
|
|
1676
|
+
deployment: { pathTemplate: '.{platform}/commands/{id}.md', core: true },
|
|
1677
|
+
metadata: {
|
|
1678
|
+
type: 'command',
|
|
1679
|
+
template: 'utility',
|
|
1680
|
+
argumentHint: '<discover|harnesses|models|run> --profile <name> [--harness <id>] [--model <id>] [--input <text>] [--stream]',
|
|
1681
|
+
allowedTools: ['Read', 'Bash'],
|
|
1682
|
+
},
|
|
1683
|
+
};
|
|
1666
1684
|
// Utility Commands
|
|
1667
1685
|
export const prefillCardsCommand = {
|
|
1668
1686
|
id: 'prefill-cards',
|
|
@@ -2371,6 +2389,24 @@ export const mcCommand = {
|
|
|
2371
2389
|
},
|
|
2372
2390
|
},
|
|
2373
2391
|
};
|
|
2392
|
+
export const missionCommand = {
|
|
2393
|
+
id: 'mission',
|
|
2394
|
+
type: 'command',
|
|
2395
|
+
name: 'Mission Protocol',
|
|
2396
|
+
description: 'Preview, apply, verify, resume, and roll back versioned Mission workspace migrations',
|
|
2397
|
+
version: '1.0.0',
|
|
2398
|
+
capabilities: ['cli', 'orchestration', 'mission-protocol', 'migration', 'rollback'],
|
|
2399
|
+
keywords: ['mission', 'migrate', 'migration', 'preview', 'resume', 'rollback'],
|
|
2400
|
+
category: 'orchestration',
|
|
2401
|
+
platforms: { claude: 'full', generic: 'full' },
|
|
2402
|
+
deployment: { pathTemplate: '.{platform}/commands/{id}.md', core: true },
|
|
2403
|
+
metadata: {
|
|
2404
|
+
type: 'command',
|
|
2405
|
+
template: 'orchestration',
|
|
2406
|
+
allowedTools: ['Bash', 'Read', 'Write'],
|
|
2407
|
+
argumentHint: 'migrate [--dry-run|--apply|--verify|--resume|--rollback]',
|
|
2408
|
+
},
|
|
2409
|
+
};
|
|
2374
2410
|
// Steward Commands
|
|
2375
2411
|
export const stewardCommand = {
|
|
2376
2412
|
id: 'steward',
|
|
@@ -3631,6 +3667,7 @@ export const commandDefinitions = [
|
|
|
3631
3667
|
// Toolsmith (1)
|
|
3632
3668
|
runtimeInfoCommand,
|
|
3633
3669
|
agentcardCommand,
|
|
3670
|
+
uhpCommand,
|
|
3634
3671
|
// Utility (5)
|
|
3635
3672
|
prefillCardsCommand,
|
|
3636
3673
|
contributeStartCommand,
|
|
@@ -3663,6 +3700,7 @@ export const commandDefinitions = [
|
|
|
3663
3700
|
ralphConfigCommand,
|
|
3664
3701
|
// Mission Control (1)
|
|
3665
3702
|
mcCommand,
|
|
3703
|
+
missionCommand,
|
|
3666
3704
|
// Steward (1)
|
|
3667
3705
|
stewardCommand,
|
|
3668
3706
|
// Agent Teams (1)
|
|
@@ -21,11 +21,20 @@ export function resolveManagerCommand(file, args, options = {}) {
|
|
|
21
21
|
return {
|
|
22
22
|
file: commandInterpreter,
|
|
23
23
|
args: ['/d', '/s', '/c', command],
|
|
24
|
+
// The payload above already contains cmd.exe-native quoting. Instruct
|
|
25
|
+
// Node not to apply MSVCRT escaping to those embedded quotes (#173).
|
|
26
|
+
windowsVerbatimArguments: true,
|
|
24
27
|
};
|
|
25
28
|
}
|
|
26
29
|
|
|
27
30
|
export function executeManagerCommand(file, args, options = {}) {
|
|
28
31
|
const invocation = resolveManagerCommand(file, args, options);
|
|
29
32
|
const execute = options.execute ?? execFileSync;
|
|
30
|
-
|
|
33
|
+
const execOptions = options.execOptions
|
|
34
|
+
? { ...options.execOptions }
|
|
35
|
+
: { stdio: 'inherit' };
|
|
36
|
+
if (invocation.windowsVerbatimArguments === true) {
|
|
37
|
+
execOptions.windowsVerbatimArguments = true;
|
|
38
|
+
}
|
|
39
|
+
return execute(invocation.file, invocation.args, execOptions);
|
|
31
40
|
}
|