@aiwg/cli 2026.8.0 → 2026.8.1
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 +33 -0
- package/agentic/code/providers/capability-matrix.yaml +511 -0
- package/agentic/code/providers/model-capabilities.v1.json +120 -0
- package/agentic/code/providers/model-catalog.v1.json +96 -0
- package/agentic/code/providers/model-policy-evaluations.v1.json +50 -0
- package/agentic/code/providers/premium-model-allowlist.v1.json +36 -0
- package/bin/aiwg.mjs +14 -10
- package/dist/src/api/index.d.ts +1 -0
- package/dist/src/api/index.js +1 -0
- package/dist/src/artifacts/cli.js +2 -0
- package/dist/src/artifacts/types.js +4 -0
- package/dist/src/auth/client.js +209 -0
- package/dist/src/auth/config.js +38 -0
- package/dist/src/auth/credential-store.js +141 -0
- package/dist/src/auth/resource-credentials.js +25 -0
- package/dist/src/auth/types.js +2 -0
- package/dist/src/channel/manager.mjs +5 -5
- package/dist/src/cli/handlers/auth.js +125 -0
- package/dist/src/cli/handlers/help.js +1 -0
- package/dist/src/cli/handlers/index.js +3 -1
- package/dist/src/cli/handlers/resource-versions.js +2 -0
- package/dist/src/cli/handlers/sessions.js +23 -5
- package/dist/src/cli/handlers/subcommands.js +10 -1
- package/dist/src/cli/handlers/use.js +342 -43
- package/dist/src/config/gitignore.js +1 -0
- package/dist/src/extensions/commands/definitions.js +19 -0
- package/dist/src/memory/canonical-context.js +342 -0
- package/dist/src/memory/context-pack.js +282 -0
- package/dist/src/memory/index.js +4 -0
- package/dist/src/memory/intake.js +118 -0
- package/dist/src/resources/resolver.js +1 -0
- package/dist/src/resources/web-release.d.ts +3 -1
- package/dist/src/resources/web-release.js +14 -6
- package/dist/src/serve/agentic-sandbox-fleet-client.js +213 -0
- package/dist/src/serve/fleet-mission-conductor.js +293 -0
- package/dist/src/sessions/index.js +1 -0
- package/dist/src/sessions/output-registration.js +338 -0
- package/dist/src/sessions/promotion.js +73 -2
- package/dist/src/sessions/repository.js +2 -1
- package/dist/src/update/notifier.mjs +13 -2
- package/package.json +8 -1
- package/tools/_resolve-impl.mjs +74 -0
- package/tools/agents/deploy-agents.mjs +962 -0
- package/tools/agents/providers/base.mjs +2954 -0
- package/tools/agents/providers/claude.mjs +711 -0
- package/tools/agents/providers/codex.mjs +699 -0
- package/tools/agents/providers/copilot.mjs +659 -0
- package/tools/agents/providers/cursor.mjs +714 -0
- package/tools/agents/providers/factory.mjs +1130 -0
- package/tools/agents/providers/hermes.mjs +663 -0
- package/tools/agents/providers/hook-capabilities.mjs +85 -0
- package/tools/agents/providers/model-role.mjs +56 -0
- package/tools/agents/providers/openclaw-translator.mjs +348 -0
- package/tools/agents/providers/openclaw.mjs +680 -0
- package/tools/agents/providers/opencode.mjs +675 -0
- package/tools/agents/providers/openhuman.mjs +292 -0
- package/tools/agents/providers/warp.mjs +413 -0
- package/tools/agents/providers/windsurf.mjs +748 -0
- package/tools/commands/deploy-prompts-codex.mjs +336 -0
- package/tools/plugin/package-plugins.mjs +1013 -0
- package/tools/skills/deploy-skills-codex.mjs +571 -0
|
@@ -21,11 +21,13 @@ import { loadCliCommandsContribution, registerCliCommands, registerHooks, } from
|
|
|
21
21
|
import { translateSkillsToCommands, providerNeedsCommands } from '../../plugin/skill-command-translator.js';
|
|
22
22
|
import * as ui from '../ui.js';
|
|
23
23
|
import { readAiwgConfig, writeAiwgConfig, updateInstalled, hashManifest, emptyConfig, getProjectDir } from '../../config/aiwg-config.js';
|
|
24
|
+
import { appendGitignore } from '../../config/gitignore.js';
|
|
24
25
|
import { getLogger } from '../log.js';
|
|
25
26
|
import { installCockpit } from './cockpit.js';
|
|
26
27
|
import { initHandler } from './init.js';
|
|
27
28
|
import { checkCollisions, formatCollisionReport, hasBlockingCollisions, } from '../../smiths/skillsmith/collision-detector.js';
|
|
28
29
|
import { discoverProjectLocalBundles, } from '../../extensions/project-local-discovery.js';
|
|
30
|
+
import { PROJECT_LOCAL_TYPE_TO_DIR } from '../../extensions/project-local-paths.js';
|
|
29
31
|
import { buildUpstreamRegistry } from '../../extensions/upstream-registry.js';
|
|
30
32
|
import { resolveShadows, formatShadowReport, } from '../../extensions/shadow-resolver.js';
|
|
31
33
|
import { appendProjectLocalActivity, emitDiscoverEventsDeduped, } from '../../extensions/project-local-activity.js';
|
|
@@ -270,6 +272,55 @@ export function addonPath(frameworkRoot, name) {
|
|
|
270
272
|
const folderName = resolveAddonFolderName(name);
|
|
271
273
|
return path.join(frameworkRoot, 'agentic/code/addons', folderName);
|
|
272
274
|
}
|
|
275
|
+
/**
|
|
276
|
+
* Resolve a selected addon's required addon dependencies in deterministic
|
|
277
|
+
* dependency-first order. Optional dependencies remain descriptive and are
|
|
278
|
+
* never activated implicitly.
|
|
279
|
+
*/
|
|
280
|
+
export async function resolveRequiredAddonActivationOrder(frameworkRoot, selectedAddon) {
|
|
281
|
+
const visiting = new Set();
|
|
282
|
+
const visited = new Set();
|
|
283
|
+
const order = [];
|
|
284
|
+
const visit = async (requestedName, ancestry) => {
|
|
285
|
+
const name = resolveAddonFolderName(requestedName);
|
|
286
|
+
if (!/^[a-z0-9][a-z0-9-]*$/.test(name)) {
|
|
287
|
+
throw new Error(`Invalid required addon identifier '${requestedName}'`);
|
|
288
|
+
}
|
|
289
|
+
if (visited.has(name))
|
|
290
|
+
return;
|
|
291
|
+
if (visiting.has(name)) {
|
|
292
|
+
throw new Error(`Required addon dependency cycle: ${[...ancestry, name].join(' -> ')}`);
|
|
293
|
+
}
|
|
294
|
+
const source = addonPath(frameworkRoot, name);
|
|
295
|
+
let manifest;
|
|
296
|
+
try {
|
|
297
|
+
manifest = JSON.parse(await fs.readFile(path.join(source, 'manifest.json'), 'utf8'));
|
|
298
|
+
}
|
|
299
|
+
catch (error) {
|
|
300
|
+
throw new Error(`Cannot load required addon '${name}': ${error instanceof Error ? error.message : String(error)}`);
|
|
301
|
+
}
|
|
302
|
+
if (manifest.id !== name) {
|
|
303
|
+
throw new Error(`Required addon manifest identity mismatch: expected '${name}'`);
|
|
304
|
+
}
|
|
305
|
+
const required = manifest.dependencies?.required ?? [];
|
|
306
|
+
if (!Array.isArray(required) || required.some(item => typeof item !== 'string')) {
|
|
307
|
+
throw new Error(`Addon '${name}' has an invalid dependencies.required declaration`);
|
|
308
|
+
}
|
|
309
|
+
visiting.add(name);
|
|
310
|
+
for (const dependency of [...required].sort()) {
|
|
311
|
+
const dependencyName = resolveAddonFolderName(dependency);
|
|
312
|
+
if (!await isValidAddon(frameworkRoot, dependencyName)) {
|
|
313
|
+
throw new Error(`Addon '${name}' requires unavailable addon '${dependencyName}'`);
|
|
314
|
+
}
|
|
315
|
+
await visit(dependencyName, [...ancestry, name]);
|
|
316
|
+
}
|
|
317
|
+
visiting.delete(name);
|
|
318
|
+
visited.add(name);
|
|
319
|
+
order.push(name);
|
|
320
|
+
};
|
|
321
|
+
await visit(selectedAddon, []);
|
|
322
|
+
return order;
|
|
323
|
+
}
|
|
273
324
|
async function registerSourceCliCommands(opts) {
|
|
274
325
|
const contribution = await loadCliCommandsContribution(opts.source);
|
|
275
326
|
if (!contribution)
|
|
@@ -298,6 +349,25 @@ function getProviderPaths(provider) {
|
|
|
298
349
|
function getProviderKernelSkillsPath(provider) {
|
|
299
350
|
return getProviderKernelSkillPath(provider) || getProviderKernelSkillPath('claude');
|
|
300
351
|
}
|
|
352
|
+
const PROVIDER_GENERATED_GITIGNORE_PATTERNS = {
|
|
353
|
+
codex: ['.codex/', '.agents/'],
|
|
354
|
+
};
|
|
355
|
+
async function ensureProviderGeneratedDirsIgnored(projectRoot, provider, opts) {
|
|
356
|
+
if (opts.dryRun)
|
|
357
|
+
return;
|
|
358
|
+
const patterns = PROVIDER_GENERATED_GITIGNORE_PATTERNS[provider];
|
|
359
|
+
if (!patterns || patterns.length === 0)
|
|
360
|
+
return;
|
|
361
|
+
try {
|
|
362
|
+
const result = await appendGitignore(projectRoot, patterns);
|
|
363
|
+
if (opts.verbose && result.added.length > 0) {
|
|
364
|
+
ui.dim(` Gitignore: added ${result.added.join(', ')}`);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
catch (error) {
|
|
368
|
+
ui.warn(`Failed to update .gitignore for generated ${provider} artifacts: ${error instanceof Error ? error.message : String(error)}`);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
301
371
|
const MIRRORED_STANDARD_COMMAND_SKILLS = new Set([
|
|
302
372
|
'aiwg-setup-project',
|
|
303
373
|
'aiwg-update-claude',
|
|
@@ -460,7 +530,7 @@ function agenticNextSteps(openStep) {
|
|
|
460
530
|
return [
|
|
461
531
|
openStep,
|
|
462
532
|
'Ask the steward: "Check that AIWG is installed correctly and tell me what I can do here."',
|
|
463
|
-
'Regenerate:
|
|
533
|
+
'Regenerate: Invoke {{aiwg-regenerate}} in-session when context files need rebuilding.',
|
|
464
534
|
'Install runbook: docs/agentic-install-runbook.md',
|
|
465
535
|
'Diagnostics: aiwg doctor',
|
|
466
536
|
];
|
|
@@ -499,7 +569,11 @@ const NEXT_STEPS = {
|
|
|
499
569
|
};
|
|
500
570
|
export function nextStepsFor(framework, provider = 'claude') {
|
|
501
571
|
const providerKey = `${provider}/${framework}`;
|
|
502
|
-
|
|
572
|
+
const regenerateInvocation = provider === 'codex' || provider === 'openai'
|
|
573
|
+
? '$aiwg-regenerate'
|
|
574
|
+
: '/aiwg-regenerate';
|
|
575
|
+
const steps = NEXT_STEPS[providerKey] ?? NEXT_STEPS[framework] ?? NEXT_STEPS.sdlc;
|
|
576
|
+
return steps.map((step) => step.replace('{{aiwg-regenerate}}', regenerateInvocation));
|
|
503
577
|
}
|
|
504
578
|
function printNextSteps(framework, provider = 'claude') {
|
|
505
579
|
ui.section('Next steps:', nextStepsFor(framework, provider));
|
|
@@ -523,8 +597,8 @@ const SESSION_RELOAD_NOTICE = {
|
|
|
523
597
|
rationale: 'Claude Code reads .claude/agents/ at session start. A running session retains its old registry until reloaded.',
|
|
524
598
|
},
|
|
525
599
|
codex: {
|
|
526
|
-
action: 'Restart
|
|
527
|
-
rationale: 'Codex caches its agent and skill registry per session.
|
|
600
|
+
action: 'Restart/open Codex in this target workspace so it picks up newly deployed agents and .agents/skills entries.',
|
|
601
|
+
rationale: 'Codex caches its agent and skill registry per session. Project .agents/skills/ and .codex/agents/ are scanned from the Codex working directory up to the repo root on startup.',
|
|
528
602
|
},
|
|
529
603
|
copilot: {
|
|
530
604
|
action: 'Reload the VS Code window (`Developer: Reload Window`) so Copilot picks up the new .github/agents/ entries.',
|
|
@@ -1638,6 +1712,103 @@ async function deploySourceDirectory(opts) {
|
|
|
1638
1712
|
}
|
|
1639
1713
|
return result;
|
|
1640
1714
|
}
|
|
1715
|
+
async function assertNoSymlinks(root) {
|
|
1716
|
+
for (const entry of await fs.readdir(root, { withFileTypes: true })) {
|
|
1717
|
+
const candidate = path.join(root, entry.name);
|
|
1718
|
+
if (entry.isSymbolicLink()) {
|
|
1719
|
+
throw new Error(`Refusing symbolic link in external bundle source: ${candidate}`);
|
|
1720
|
+
}
|
|
1721
|
+
if (entry.isDirectory())
|
|
1722
|
+
await assertNoSymlinks(candidate);
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
async function installProjectLocalBundleInUserCatalog(bundle) {
|
|
1726
|
+
await assertNoSymlinks(bundle.bundlePath);
|
|
1727
|
+
const catalogRoot = path.join(os.homedir(), '.aiwg', PROJECT_LOCAL_TYPE_TO_DIR[bundle.type]);
|
|
1728
|
+
await fs.mkdir(catalogRoot, { recursive: true });
|
|
1729
|
+
const destination = path.join(catalogRoot, bundle.id);
|
|
1730
|
+
const stage = await fs.mkdtemp(path.join(catalogRoot, `.${bundle.id}-stage-`));
|
|
1731
|
+
const backup = path.join(catalogRoot, `.${bundle.id}-backup-${process.pid}`);
|
|
1732
|
+
let movedExisting = false;
|
|
1733
|
+
try {
|
|
1734
|
+
await fs.cp(bundle.bundlePath, stage, { recursive: true, force: true });
|
|
1735
|
+
try {
|
|
1736
|
+
await fs.rename(destination, backup);
|
|
1737
|
+
movedExisting = true;
|
|
1738
|
+
}
|
|
1739
|
+
catch (error) {
|
|
1740
|
+
if (error.code !== 'ENOENT')
|
|
1741
|
+
throw error;
|
|
1742
|
+
}
|
|
1743
|
+
await fs.rename(stage, destination);
|
|
1744
|
+
if (movedExisting)
|
|
1745
|
+
await fs.rm(backup, { recursive: true, force: true });
|
|
1746
|
+
return destination;
|
|
1747
|
+
}
|
|
1748
|
+
catch (error) {
|
|
1749
|
+
await fs.rm(stage, { recursive: true, force: true });
|
|
1750
|
+
if (movedExisting) {
|
|
1751
|
+
await fs.rm(destination, { recursive: true, force: true });
|
|
1752
|
+
await fs.rename(backup, destination).catch(() => { });
|
|
1753
|
+
}
|
|
1754
|
+
throw error;
|
|
1755
|
+
}
|
|
1756
|
+
}
|
|
1757
|
+
async function rebuildExternalBundleIndex(projectDir, graph, verbose) {
|
|
1758
|
+
const originalLog = console.log;
|
|
1759
|
+
if (!verbose)
|
|
1760
|
+
console.log = () => { };
|
|
1761
|
+
try {
|
|
1762
|
+
const [{ buildIndex }, { syncFortemiCoreIndex }] = await Promise.all([
|
|
1763
|
+
import('../../artifacts/index-builder.js'),
|
|
1764
|
+
import('../../artifacts/fortemi-core-sync.js'),
|
|
1765
|
+
]);
|
|
1766
|
+
await buildIndex(projectDir, { graph, force: true, explicit: false });
|
|
1767
|
+
syncFortemiCoreIndex(projectDir, { graph });
|
|
1768
|
+
}
|
|
1769
|
+
finally {
|
|
1770
|
+
console.log = originalLog;
|
|
1771
|
+
}
|
|
1772
|
+
ui.dim(` Refreshed ${graph}-scope capability index`);
|
|
1773
|
+
}
|
|
1774
|
+
async function mirrorProjectLocalBundleToUserScope(opts) {
|
|
1775
|
+
const paths = getProviderPaths(opts.provider);
|
|
1776
|
+
const resolveProjectPath = (value) => !value ? '' : path.isAbsolute(value) ? value : path.join(opts.target, value);
|
|
1777
|
+
const mirrored = await mirrorToUserScope(opts.provider, {
|
|
1778
|
+
agents: resolveProjectPath(paths.agents),
|
|
1779
|
+
skills: resolveProjectPath(paths.skills),
|
|
1780
|
+
kernelSkills: resolveProjectPath(getProviderKernelSkillsPath(opts.provider)),
|
|
1781
|
+
commands: resolveProjectPath(paths.commands),
|
|
1782
|
+
rules: resolveProjectPath(paths.rules),
|
|
1783
|
+
behaviors: resolveProjectPath(paths.behaviors),
|
|
1784
|
+
});
|
|
1785
|
+
const counts = {
|
|
1786
|
+
agents: mirrored.agents.count,
|
|
1787
|
+
commands: mirrored.commands.count,
|
|
1788
|
+
skills: mirrored.skills.count,
|
|
1789
|
+
rules: mirrored.rules.count,
|
|
1790
|
+
};
|
|
1791
|
+
const total = Object.values(counts).reduce((sum, count) => sum + count, 0);
|
|
1792
|
+
if (total === 0) {
|
|
1793
|
+
throw new Error(`External bundle '${opts.bundle.id}' produced no user-scope artifacts for ${opts.provider}`);
|
|
1794
|
+
}
|
|
1795
|
+
const { recordUserDeploy } = await import('../../config/user-registry.js');
|
|
1796
|
+
await recordUserDeploy({
|
|
1797
|
+
framework: opts.bundle.id,
|
|
1798
|
+
provider: opts.provider,
|
|
1799
|
+
version: opts.bundle.manifest.version,
|
|
1800
|
+
source: 'project-local',
|
|
1801
|
+
counts,
|
|
1802
|
+
entries: {
|
|
1803
|
+
agents: mirrored.agents.entries,
|
|
1804
|
+
commands: mirrored.commands.entries,
|
|
1805
|
+
skills: mirrored.skills.entries,
|
|
1806
|
+
rules: mirrored.rules.entries,
|
|
1807
|
+
behaviors: mirrored.behaviors.entries,
|
|
1808
|
+
},
|
|
1809
|
+
manifestHash: await hashManifest(path.join(opts.bundle.bundlePath, 'manifest.json')),
|
|
1810
|
+
});
|
|
1811
|
+
}
|
|
1641
1812
|
/**
|
|
1642
1813
|
* Use command handler
|
|
1643
1814
|
*
|
|
@@ -1702,23 +1873,31 @@ export class UseHandler {
|
|
|
1702
1873
|
if (scopeIdx >= 0 && remainingArgs[scopeIdx + 1] === 'project') {
|
|
1703
1874
|
return { exitCode: 1, message: 'Error: --global conflicts with --scope project' };
|
|
1704
1875
|
}
|
|
1705
|
-
if (!framework || !VALID_FRAMEWORKS.includes(framework)) {
|
|
1706
|
-
return {
|
|
1707
|
-
exitCode: 1,
|
|
1708
|
-
message: 'Error: --global currently supports framework targets; addons and project-local bundles require project deployment',
|
|
1709
|
-
};
|
|
1710
|
-
}
|
|
1711
1876
|
const contextTargetIdx = remainingArgs.indexOf('--target');
|
|
1712
1877
|
const contextTarget = path.resolve(contextTargetIdx >= 0 && remainingArgs[contextTargetIdx + 1]
|
|
1713
1878
|
? remainingArgs[contextTargetIdx + 1]
|
|
1714
1879
|
: (ctx.cwd || process.cwd()));
|
|
1880
|
+
const externalDiscovery = await discoverProjectLocalBundles(contextTarget);
|
|
1881
|
+
const externalBundle = framework
|
|
1882
|
+
? externalDiscovery.bundles.find(bundle => bundle.id === framework)
|
|
1883
|
+
: undefined;
|
|
1884
|
+
const isFrameworkTarget = Boolean(framework && VALID_FRAMEWORKS.includes(framework));
|
|
1885
|
+
if (!framework || (!isFrameworkTarget && !externalBundle)) {
|
|
1886
|
+
return {
|
|
1887
|
+
exitCode: 1,
|
|
1888
|
+
message: 'Error: --global target must be a bundled framework or a valid external project-local bundle',
|
|
1889
|
+
};
|
|
1890
|
+
}
|
|
1715
1891
|
const originalConfig = await readAiwgConfig(contextTarget);
|
|
1716
1892
|
const providers = configuredGlobalProviders(remainingArgs, originalConfig);
|
|
1717
1893
|
const dryRun = remainingArgs.includes('--dry-run');
|
|
1718
|
-
const stageRoot =
|
|
1719
|
-
? path.join(os.tmpdir(), 'aiwg-global-bootstrap-dry-run')
|
|
1720
|
-
: await fs.mkdtemp(path.join(os.tmpdir(), 'aiwg-global-bootstrap-'));
|
|
1894
|
+
const stageRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'aiwg-global-bootstrap-'));
|
|
1721
1895
|
try {
|
|
1896
|
+
if (externalBundle) {
|
|
1897
|
+
const stagedBundle = path.join(stageRoot, '.aiwg', PROJECT_LOCAL_TYPE_TO_DIR[externalBundle.type], externalBundle.id);
|
|
1898
|
+
await fs.mkdir(path.dirname(stagedBundle), { recursive: true });
|
|
1899
|
+
await fs.cp(externalBundle.bundlePath, stagedBundle, { recursive: true, force: true });
|
|
1900
|
+
}
|
|
1722
1901
|
for (const provider of providers) {
|
|
1723
1902
|
const innerArgs = [
|
|
1724
1903
|
framework,
|
|
@@ -1726,7 +1905,7 @@ export class UseHandler {
|
|
|
1726
1905
|
'--provider', provider,
|
|
1727
1906
|
'--scope', 'user',
|
|
1728
1907
|
'--target', stageRoot,
|
|
1729
|
-
'--no-project-local',
|
|
1908
|
+
...(externalBundle ? [] : ['--no-project-local']),
|
|
1730
1909
|
'--no-context-files',
|
|
1731
1910
|
'--no-hooks',
|
|
1732
1911
|
'--no-workspace-signals',
|
|
@@ -1745,14 +1924,13 @@ export class UseHandler {
|
|
|
1745
1924
|
}
|
|
1746
1925
|
}
|
|
1747
1926
|
finally {
|
|
1748
|
-
|
|
1749
|
-
await fs.rm(stageRoot, { recursive: true, force: true });
|
|
1927
|
+
await fs.rm(stageRoot, { recursive: true, force: true });
|
|
1750
1928
|
}
|
|
1751
1929
|
return {
|
|
1752
1930
|
exitCode: 0,
|
|
1753
1931
|
message: dryRun
|
|
1754
|
-
? `Global bootstrap preview complete; project context target: ${contextTarget}`
|
|
1755
|
-
: `Global bootstrap complete; user assets installed and
|
|
1932
|
+
? `Global ${externalBundle ? 'external bundle' : 'bootstrap'} preview complete; project context target: ${contextTarget}`
|
|
1933
|
+
: `Global ${externalBundle ? 'external bundle' : 'bootstrap'} complete; user assets installed and capability index refreshed`,
|
|
1756
1934
|
};
|
|
1757
1935
|
}
|
|
1758
1936
|
// Project-isolation warning (UC-NUA-002 / SAD §5.1). Fires once per CLI
|
|
@@ -1849,7 +2027,10 @@ export class UseHandler {
|
|
|
1849
2027
|
}
|
|
1850
2028
|
return { exitCode: 0 };
|
|
1851
2029
|
}
|
|
1852
|
-
|
|
2030
|
+
// Handler contexts already carry the active installation root. Respect it
|
|
2031
|
+
// so linked worktrees, embedded callers, and tests do not silently deploy
|
|
2032
|
+
// artifacts from a different channel checkout.
|
|
2033
|
+
const frameworkRoot = ctx.frameworkRoot || await getFrameworkRoot();
|
|
1853
2034
|
if (framework === 'all' && explicitTarget !== 'all' && !remainingArgs.includes('--no-workspace-signals')) {
|
|
1854
2035
|
const profileIdx = remainingArgs.findIndex((a) => a === '--profile');
|
|
1855
2036
|
const profile = profileIdx >= 0 && remainingArgs[profileIdx + 1]
|
|
@@ -1962,6 +2143,7 @@ export class UseHandler {
|
|
|
1962
2143
|
ui.warn(`${plResult.failed} project-local bundle(s) failed to deploy`);
|
|
1963
2144
|
}
|
|
1964
2145
|
}
|
|
2146
|
+
await ensureProviderGeneratedDirsIgnored(target, providerName, { dryRun, verbose });
|
|
1965
2147
|
if (!dryRun) {
|
|
1966
2148
|
try {
|
|
1967
2149
|
const registry = getRegistry();
|
|
@@ -2028,7 +2210,20 @@ export class UseHandler {
|
|
|
2028
2210
|
const dryRunSingle = remainingArgs.includes('--dry-run');
|
|
2029
2211
|
const verboseSingle = remainingArgs.includes('--verbose') || remainingArgs.includes('-v');
|
|
2030
2212
|
const targetIdxSingle = remainingArgs.findIndex(a => a === '--target');
|
|
2031
|
-
const targetSingle = targetIdxSingle >= 0 && remainingArgs[targetIdxSingle + 1] ? remainingArgs[targetIdxSingle + 1] :
|
|
2213
|
+
const targetSingle = targetIdxSingle >= 0 && remainingArgs[targetIdxSingle + 1] ? remainingArgs[targetIdxSingle + 1] : projectDir;
|
|
2214
|
+
let scopeSingle;
|
|
2215
|
+
try {
|
|
2216
|
+
scopeSingle = detectScope(remainingArgs);
|
|
2217
|
+
if (scopeSingle === 'project' && remainingArgs.includes('--user')) {
|
|
2218
|
+
scopeSingle = 'user';
|
|
2219
|
+
}
|
|
2220
|
+
}
|
|
2221
|
+
catch (error) {
|
|
2222
|
+
return {
|
|
2223
|
+
exitCode: 1,
|
|
2224
|
+
message: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
2225
|
+
};
|
|
2226
|
+
}
|
|
2032
2227
|
// Multi-provider expansion mirrors the framework path
|
|
2033
2228
|
let providersForSingle;
|
|
2034
2229
|
if (explicitProvider)
|
|
@@ -2037,9 +2232,38 @@ export class UseHandler {
|
|
|
2037
2232
|
providersForSingle = config.providers;
|
|
2038
2233
|
else
|
|
2039
2234
|
providersForSingle = ['claude'];
|
|
2235
|
+
const resolvedProviders = [];
|
|
2236
|
+
for (const requestedProvider of providersForSingle) {
|
|
2237
|
+
const provider = normalizeProviderDefinitionId(requestedProvider);
|
|
2238
|
+
if (!provider) {
|
|
2239
|
+
return {
|
|
2240
|
+
exitCode: 1,
|
|
2241
|
+
message: `External bundle '${match.id}' cannot deploy to unsupported provider '${requestedProvider}'. Choose a provider declared in its manifest.`,
|
|
2242
|
+
};
|
|
2243
|
+
}
|
|
2244
|
+
const support = match.manifest.platforms[provider]
|
|
2245
|
+
?? match.manifest.platforms.generic;
|
|
2246
|
+
if (!support || support === 'none') {
|
|
2247
|
+
const declared = Object.entries(match.manifest.platforms)
|
|
2248
|
+
.filter(([, level]) => level && level !== 'none')
|
|
2249
|
+
.map(([name]) => name)
|
|
2250
|
+
.join(', ');
|
|
2251
|
+
return {
|
|
2252
|
+
exitCode: 1,
|
|
2253
|
+
message: `External bundle '${match.id}' does not declare support for provider '${provider}'. Declared providers: ${declared || 'none'}.`,
|
|
2254
|
+
};
|
|
2255
|
+
}
|
|
2256
|
+
if (scopeSingle === 'user' && !USER_SCOPE_PATHS[provider]) {
|
|
2257
|
+
return {
|
|
2258
|
+
exitCode: 1,
|
|
2259
|
+
message: `External bundle '${match.id}' cannot install at user scope for provider '${provider}' because AIWG has no user-scope path contract for it.`,
|
|
2260
|
+
};
|
|
2261
|
+
}
|
|
2262
|
+
resolvedProviders.push(provider);
|
|
2263
|
+
}
|
|
2040
2264
|
let totalDeployed = 0;
|
|
2041
2265
|
let totalFailed = 0;
|
|
2042
|
-
for (const p of
|
|
2266
|
+
for (const p of resolvedProviders) {
|
|
2043
2267
|
const r = await deployProjectLocalBundles({
|
|
2044
2268
|
ctx, frameworkRoot, projectDir, provider: p, target: targetSingle,
|
|
2045
2269
|
dryRun: dryRunSingle, verbose: verboseSingle, quiet: !verboseSingle && !dryRunSingle,
|
|
@@ -2048,10 +2272,39 @@ export class UseHandler {
|
|
|
2048
2272
|
});
|
|
2049
2273
|
totalDeployed += r.deployed;
|
|
2050
2274
|
totalFailed += r.failed;
|
|
2275
|
+
if (r.deployed > 0 && scopeSingle === 'user' && !dryRunSingle) {
|
|
2276
|
+
try {
|
|
2277
|
+
await mirrorProjectLocalBundleToUserScope({
|
|
2278
|
+
bundle: match,
|
|
2279
|
+
provider: p,
|
|
2280
|
+
target: targetSingle,
|
|
2281
|
+
});
|
|
2282
|
+
}
|
|
2283
|
+
catch (error) {
|
|
2284
|
+
totalFailed += 1;
|
|
2285
|
+
ui.warn(`Failed to install external bundle '${match.id}' for user-scope provider '${p}': ${error instanceof Error ? error.message : String(error)}`);
|
|
2286
|
+
}
|
|
2287
|
+
}
|
|
2288
|
+
}
|
|
2289
|
+
if (totalDeployed > 0 && totalFailed === 0 && !dryRunSingle) {
|
|
2290
|
+
try {
|
|
2291
|
+
await rebuildExternalBundleIndex(projectDir, 'project', verboseSingle);
|
|
2292
|
+
if (scopeSingle === 'user') {
|
|
2293
|
+
await installProjectLocalBundleInUserCatalog(match);
|
|
2294
|
+
await rebuildExternalBundleIndex(projectDir, 'user', verboseSingle);
|
|
2295
|
+
}
|
|
2296
|
+
}
|
|
2297
|
+
catch (error) {
|
|
2298
|
+
totalFailed += 1;
|
|
2299
|
+
ui.warn(`External bundle '${match.id}' index refresh failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
2300
|
+
}
|
|
2051
2301
|
}
|
|
2052
2302
|
if (!verboseSingle && !dryRunSingle) {
|
|
2053
2303
|
ui.blank();
|
|
2054
|
-
|
|
2304
|
+
if (totalFailed === 0) {
|
|
2305
|
+
const scopeLabel = scopeSingle === 'user' ? 'project + user' : 'project';
|
|
2306
|
+
ui.success(`external ${match.type} '${match.id}' installed at ${scopeLabel} scope (${totalDeployed} provider(s)); capability index refreshed`);
|
|
2307
|
+
}
|
|
2055
2308
|
}
|
|
2056
2309
|
return {
|
|
2057
2310
|
exitCode: totalFailed > 0 ? 1 : 0,
|
|
@@ -2071,7 +2324,8 @@ export class UseHandler {
|
|
|
2071
2324
|
const provider = explicitAddonProvider ?? (config?.providers?.[0] ?? 'claude');
|
|
2072
2325
|
const targetIdx = remainingArgs.findIndex(a => a === '--target');
|
|
2073
2326
|
const target = targetIdx >= 0 && remainingArgs[targetIdx + 1] ? remainingArgs[targetIdx + 1] : process.cwd();
|
|
2074
|
-
const
|
|
2327
|
+
const dryRunAddon = remainingArgs.includes('--dry-run');
|
|
2328
|
+
const runner = createScriptRunner(frameworkRoot);
|
|
2075
2329
|
const addonBaseArgs = ['--deploy-commands', '--deploy-skills', '--deploy-rules'];
|
|
2076
2330
|
addonBaseArgs.push(...modelDeployArgs);
|
|
2077
2331
|
if (provider)
|
|
@@ -2082,7 +2336,47 @@ export class UseHandler {
|
|
|
2082
2336
|
if (remainingArgs.includes('--copy-all') || remainingArgs.includes('--copy-standard-skills')) {
|
|
2083
2337
|
addonBaseArgs.push('--copy-all');
|
|
2084
2338
|
}
|
|
2339
|
+
if (dryRunAddon)
|
|
2340
|
+
addonBaseArgs.push('--dry-run');
|
|
2085
2341
|
const kind = isExtension ? 'extension' : 'addon';
|
|
2342
|
+
const activationOrder = isAddon
|
|
2343
|
+
? await resolveRequiredAddonActivationOrder(frameworkRoot, framework)
|
|
2344
|
+
: [framework];
|
|
2345
|
+
const requiredAddons = activationOrder.slice(0, -1);
|
|
2346
|
+
for (const dependency of requiredAddons) {
|
|
2347
|
+
ui.blank();
|
|
2348
|
+
ui.header(` Deploying required ${dependency} addon...`);
|
|
2349
|
+
const dependencySource = addonPath(frameworkRoot, dependency);
|
|
2350
|
+
const dependencyResult = await runner.run('tools/agents/deploy-agents.mjs', [
|
|
2351
|
+
'--quiet', '--source', dependencySource,
|
|
2352
|
+
...addonBaseArgs,
|
|
2353
|
+
], { capture: true });
|
|
2354
|
+
if (dependencyResult.exitCode !== 0) {
|
|
2355
|
+
return {
|
|
2356
|
+
...dependencyResult,
|
|
2357
|
+
message: dependencyResult.message
|
|
2358
|
+
|| `Failed to deploy required addon '${dependency}'`,
|
|
2359
|
+
};
|
|
2360
|
+
}
|
|
2361
|
+
try {
|
|
2362
|
+
await registerSourceCliCommands({
|
|
2363
|
+
source: dependencySource,
|
|
2364
|
+
target,
|
|
2365
|
+
provider,
|
|
2366
|
+
dryRun: dryRunAddon,
|
|
2367
|
+
fallbackDescription: `${dependency} addon commands`,
|
|
2368
|
+
});
|
|
2369
|
+
}
|
|
2370
|
+
catch (error) {
|
|
2371
|
+
return {
|
|
2372
|
+
exitCode: 1,
|
|
2373
|
+
message: `Failed to register required addon '${dependency}' CLI commands: ${error instanceof Error ? error.message : String(error)}`,
|
|
2374
|
+
};
|
|
2375
|
+
}
|
|
2376
|
+
ui.success(dryRunAddon
|
|
2377
|
+
? `Required ${dependency} addon activation previewed`
|
|
2378
|
+
: `Required ${dependency} addon deployed`);
|
|
2379
|
+
}
|
|
2086
2380
|
ui.blank();
|
|
2087
2381
|
ui.header(` Deploying ${framework} ${kind}...`);
|
|
2088
2382
|
const addonSource = isExtension
|
|
@@ -2095,23 +2389,25 @@ export class UseHandler {
|
|
|
2095
2389
|
if (addonResult.exitCode !== 0) {
|
|
2096
2390
|
return addonResult;
|
|
2097
2391
|
}
|
|
2098
|
-
// Register
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2392
|
+
// Register only artifacts actually written by a confirmed deployment.
|
|
2393
|
+
if (!dryRunAddon) {
|
|
2394
|
+
try {
|
|
2395
|
+
const registry = getRegistry();
|
|
2396
|
+
const paths = getProviderPaths(provider);
|
|
2397
|
+
await registerDeployedExtensions(registry, {
|
|
2398
|
+
agentsPath: paths.agents,
|
|
2399
|
+
skillsPath: paths.skills,
|
|
2400
|
+
commandsPath: paths.commands,
|
|
2401
|
+
rulesPath: paths.rules,
|
|
2402
|
+
behaviorsPath: paths.behaviors,
|
|
2403
|
+
provider,
|
|
2404
|
+
cwd: target,
|
|
2405
|
+
});
|
|
2406
|
+
ui.success('Extension registration complete');
|
|
2407
|
+
}
|
|
2408
|
+
catch (error) {
|
|
2409
|
+
ui.warn(`Failed to register extensions: ${error instanceof Error ? error.message : String(error)}`);
|
|
2410
|
+
}
|
|
2115
2411
|
}
|
|
2116
2412
|
// Register CLI commands if addon declares them
|
|
2117
2413
|
try {
|
|
@@ -2119,7 +2415,7 @@ export class UseHandler {
|
|
|
2119
2415
|
source: addonSource,
|
|
2120
2416
|
target,
|
|
2121
2417
|
provider,
|
|
2122
|
-
dryRun:
|
|
2418
|
+
dryRun: dryRunAddon,
|
|
2123
2419
|
fallbackDescription: `${framework} addon commands`,
|
|
2124
2420
|
});
|
|
2125
2421
|
}
|
|
@@ -2177,7 +2473,7 @@ export class UseHandler {
|
|
|
2177
2473
|
}
|
|
2178
2474
|
}
|
|
2179
2475
|
// Write profile config to project namespace
|
|
2180
|
-
if (selectedProfile) {
|
|
2476
|
+
if (selectedProfile && !dryRunAddon) {
|
|
2181
2477
|
const namespace = topology.namespace || `.aiwg/${framework}`;
|
|
2182
2478
|
const configDir = path.join(target, namespace);
|
|
2183
2479
|
await fs.mkdir(configDir, { recursive: true });
|
|
@@ -2207,7 +2503,9 @@ export class UseHandler {
|
|
|
2207
2503
|
return wrapperValidation;
|
|
2208
2504
|
}
|
|
2209
2505
|
ui.blank();
|
|
2210
|
-
ui.success(
|
|
2506
|
+
ui.success(dryRunAddon
|
|
2507
|
+
? `${framework} addon activation preview complete`
|
|
2508
|
+
: `${framework} addon deployed`);
|
|
2211
2509
|
return {
|
|
2212
2510
|
exitCode: 0,
|
|
2213
2511
|
};
|
|
@@ -2484,6 +2782,7 @@ export class UseHandler {
|
|
|
2484
2782
|
ui.warn(`${plResult.failed} project-local bundle(s) failed to deploy`);
|
|
2485
2783
|
}
|
|
2486
2784
|
}
|
|
2785
|
+
await ensureProviderGeneratedDirsIgnored(target, provider, { dryRun, verbose });
|
|
2487
2786
|
const paths = getProviderPaths(provider);
|
|
2488
2787
|
if (!dryRun && !skipUtils) {
|
|
2489
2788
|
const wrapperValidation = await validateDeployedModelWrappers({
|
|
@@ -66,6 +66,24 @@ export const versionCommand = {
|
|
|
66
66
|
},
|
|
67
67
|
},
|
|
68
68
|
};
|
|
69
|
+
export const authCommand = {
|
|
70
|
+
id: 'auth',
|
|
71
|
+
type: 'command',
|
|
72
|
+
name: 'Authentication',
|
|
73
|
+
description: 'Log in, inspect access, and log out of paid AIWG web resources',
|
|
74
|
+
version: '1.0.0',
|
|
75
|
+
capabilities: ['cli', 'authentication', 'resources'],
|
|
76
|
+
keywords: ['auth', 'login', 'logout', 'status', 'releases'],
|
|
77
|
+
category: 'maintenance',
|
|
78
|
+
platforms: { claude: 'full', generic: 'full' },
|
|
79
|
+
deployment: { pathTemplate: '.{platform}/commands/{id}.md', core: true },
|
|
80
|
+
metadata: {
|
|
81
|
+
type: 'command',
|
|
82
|
+
template: 'utility',
|
|
83
|
+
argumentHint: '<login|status|logout> [--device] [--json] [--all]',
|
|
84
|
+
allowedTools: ['Bash'],
|
|
85
|
+
},
|
|
86
|
+
};
|
|
69
87
|
export const doctorCommand = {
|
|
70
88
|
id: 'doctor',
|
|
71
89
|
type: 'skill',
|
|
@@ -3461,6 +3479,7 @@ export const commandDefinitions = [
|
|
|
3461
3479
|
// Maintenance (7)
|
|
3462
3480
|
helpCommand,
|
|
3463
3481
|
versionCommand,
|
|
3482
|
+
authCommand,
|
|
3464
3483
|
doctorCommand,
|
|
3465
3484
|
updateCommand,
|
|
3466
3485
|
refreshCommand,
|