@aiwg/cli 2026.7.25 → 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.
Files changed (79) hide show
  1. package/README.md +33 -0
  2. package/agentic/code/providers/capability-matrix.yaml +511 -0
  3. package/agentic/code/providers/model-capabilities.v1.json +120 -0
  4. package/agentic/code/providers/model-catalog.v1.json +96 -0
  5. package/agentic/code/providers/model-policy-evaluations.v1.json +50 -0
  6. package/agentic/code/providers/premium-model-allowlist.v1.json +36 -0
  7. package/bin/aiwg.mjs +14 -10
  8. package/dist/src/api/index.d.ts +1 -0
  9. package/dist/src/api/index.js +1 -0
  10. package/dist/src/artifacts/cli.js +55 -10
  11. package/dist/src/artifacts/fortemi-shard-export.js +107 -18
  12. package/dist/src/artifacts/types.js +4 -0
  13. package/dist/src/auth/client.js +209 -0
  14. package/dist/src/auth/config.js +38 -0
  15. package/dist/src/auth/credential-store.js +141 -0
  16. package/dist/src/auth/resource-credentials.js +25 -0
  17. package/dist/src/auth/types.js +2 -0
  18. package/dist/src/channel/manager.mjs +5 -5
  19. package/dist/src/cli/handlers/auth.js +125 -0
  20. package/dist/src/cli/handlers/help.js +1 -0
  21. package/dist/src/cli/handlers/index.js +6 -2
  22. package/dist/src/cli/handlers/job.js +97 -0
  23. package/dist/src/cli/handlers/resource-versions.js +2 -0
  24. package/dist/src/cli/handlers/runtime-info.js +2 -2
  25. package/dist/src/cli/handlers/serve.js +2 -2
  26. package/dist/src/cli/handlers/sessions.js +211 -5
  27. package/dist/src/cli/handlers/steward.js +16 -3
  28. package/dist/src/cli/handlers/subcommands.js +10 -1
  29. package/dist/src/cli/handlers/use.js +342 -43
  30. package/dist/src/config/gitignore.js +1 -0
  31. package/dist/src/extensions/commands/definitions.js +49 -5
  32. package/dist/src/extensions/manifest.js +1 -0
  33. package/dist/src/features/catalog.js +3 -3
  34. package/dist/src/jobs/executor.js +83 -0
  35. package/dist/src/jobs/flow.js +106 -0
  36. package/dist/src/jobs/gitea.js +91 -0
  37. package/dist/src/jobs/render.js +53 -0
  38. package/dist/src/jobs/runner.js +315 -0
  39. package/dist/src/jobs/types.js +3 -0
  40. package/dist/src/memory/canonical-context.js +342 -0
  41. package/dist/src/memory/context-pack.js +282 -0
  42. package/dist/src/memory/index.js +4 -0
  43. package/dist/src/memory/intake.js +118 -0
  44. package/dist/src/providers/capability-matrix.js +11 -4
  45. package/dist/src/providers/capability-matrix.yaml +39 -42
  46. package/dist/src/resources/resolver.js +1 -0
  47. package/dist/src/resources/web-release.d.ts +3 -1
  48. package/dist/src/resources/web-release.js +14 -6
  49. package/dist/src/serve/agentic-sandbox-fleet-client.js +213 -0
  50. package/dist/src/serve/fleet-mission-conductor.js +293 -0
  51. package/dist/src/sessions/analytics.js +303 -0
  52. package/dist/src/sessions/importer.js +7 -1
  53. package/dist/src/sessions/index.js +2 -0
  54. package/dist/src/sessions/output-registration.js +338 -0
  55. package/dist/src/sessions/policy.js +1 -1
  56. package/dist/src/sessions/promotion.js +73 -2
  57. package/dist/src/sessions/repository.js +215 -1
  58. package/dist/src/update/notifier.mjs +13 -2
  59. package/package.json +17 -10
  60. package/tools/_resolve-impl.mjs +74 -0
  61. package/tools/agents/deploy-agents.mjs +962 -0
  62. package/tools/agents/providers/base.mjs +2954 -0
  63. package/tools/agents/providers/claude.mjs +711 -0
  64. package/tools/agents/providers/codex.mjs +699 -0
  65. package/tools/agents/providers/copilot.mjs +659 -0
  66. package/tools/agents/providers/cursor.mjs +714 -0
  67. package/tools/agents/providers/factory.mjs +1130 -0
  68. package/tools/agents/providers/hermes.mjs +663 -0
  69. package/tools/agents/providers/hook-capabilities.mjs +85 -0
  70. package/tools/agents/providers/model-role.mjs +56 -0
  71. package/tools/agents/providers/openclaw-translator.mjs +348 -0
  72. package/tools/agents/providers/openclaw.mjs +680 -0
  73. package/tools/agents/providers/opencode.mjs +675 -0
  74. package/tools/agents/providers/openhuman.mjs +292 -0
  75. package/tools/agents/providers/warp.mjs +413 -0
  76. package/tools/agents/providers/windsurf.mjs +748 -0
  77. package/tools/commands/deploy-prompts-codex.mjs +336 -0
  78. package/tools/plugin/package-plugins.mjs +1013 -0
  79. package/tools/skills/deploy-skills-codex.mjs +571 -0
@@ -0,0 +1,96 @@
1
+ {
2
+ "$schema": "../../../schemas/models/model-catalog.v1.schema.json",
3
+ "version": "1.0.0",
4
+ "refreshedAt": "2026-07-20",
5
+ "staleAfterDays": 90,
6
+ "providers": {
7
+ "claude": {
8
+ "roles": {
9
+ "reasoning": { "id": "claude-opus-4-7", "status": "active", "observed": false },
10
+ "coding": { "id": "claude-sonnet-4-6", "status": "active", "observed": false },
11
+ "efficiency": { "id": "claude-haiku-4-5", "status": "active", "observed": false }
12
+ },
13
+ "sourceUrl": "https://docs.anthropic.com/en/docs/about-claude/models/overview", "verifiedAt": "2026-07-20"
14
+ },
15
+ "codex": {
16
+ "roles": {
17
+ "reasoning": { "id": "gpt-5.4", "status": "active", "observed": true },
18
+ "coding": { "id": "gpt-5.5", "status": "active", "observed": true },
19
+ "efficiency": { "id": "gpt-5.4-mini", "status": "active", "observed": true }
20
+ },
21
+ "sourceUrl": "https://learn.chatgpt.com/docs/agent-configuration/subagents", "verifiedAt": "2026-07-20"
22
+ },
23
+ "copilot": {
24
+ "roles": {
25
+ "reasoning": { "id": "claude-opus-4.6", "status": "unverified", "observed": false },
26
+ "coding": { "id": "claude-sonnet-4.6", "status": "unverified", "observed": false },
27
+ "efficiency": { "id": "gpt-5.1-codex-mini", "status": "unverified", "observed": false }
28
+ },
29
+ "sourceUrl": "https://docs.github.com/en/copilot/reference/ai-models/supported-models", "verifiedAt": "2026-07-20"
30
+ },
31
+ "cursor": {
32
+ "roles": {
33
+ "reasoning": { "id": "claude-4.6-opus-high-thinking", "status": "unverified", "observed": false },
34
+ "coding": { "id": "claude-4.6-sonnet", "status": "unverified", "observed": false },
35
+ "efficiency": { "id": "auto", "status": "unverified", "observed": false }
36
+ },
37
+ "sourceUrl": "https://cursor.com/docs/models", "verifiedAt": "2026-07-20"
38
+ },
39
+ "factory": {
40
+ "roles": {
41
+ "reasoning": { "id": "heavy", "status": "active", "observed": false },
42
+ "coding": { "id": "medium", "status": "active", "observed": false },
43
+ "efficiency": { "id": "light", "status": "active", "observed": false }
44
+ },
45
+ "sourceUrl": "https://docs.factory.ai/cli/user-guides/choosing-your-model", "verifiedAt": "2026-07-20"
46
+ },
47
+ "opencode": {
48
+ "roles": {
49
+ "reasoning": { "id": "anthropic/claude-opus-4-7", "status": "unverified", "observed": false },
50
+ "coding": { "id": "anthropic/claude-sonnet-4-6", "status": "unverified", "observed": false },
51
+ "efficiency": { "id": "anthropic/claude-haiku-4-5", "status": "unverified", "observed": false }
52
+ },
53
+ "sourceUrl": "https://opencode.ai/docs/models", "verifiedAt": "2026-07-20"
54
+ },
55
+ "warp": {
56
+ "roles": {
57
+ "reasoning": { "id": "profile-selected", "status": "unverified", "observed": false },
58
+ "coding": { "id": "profile-selected", "status": "unverified", "observed": false },
59
+ "efficiency": { "id": "profile-selected", "status": "unverified", "observed": false }
60
+ },
61
+ "sourceUrl": "https://docs.warp.dev/agent-platform/capabilities/agent-profiles-permissions", "verifiedAt": "2026-07-20"
62
+ },
63
+ "windsurf": {
64
+ "roles": {
65
+ "reasoning": { "id": "inherited", "status": "unverified", "observed": false },
66
+ "coding": { "id": "inherited", "status": "unverified", "observed": false },
67
+ "efficiency": { "id": "inherited", "status": "unverified", "observed": false }
68
+ },
69
+ "sourceUrl": "https://docs.devin.ai/desktop/cascade/models", "verifiedAt": "2026-07-20"
70
+ },
71
+ "openclaw": {
72
+ "roles": {
73
+ "reasoning": { "id": "configured/reasoning", "status": "unverified", "observed": false },
74
+ "coding": { "id": "configured/coding", "status": "unverified", "observed": false },
75
+ "efficiency": { "id": "configured/efficiency", "status": "unverified", "observed": false }
76
+ },
77
+ "sourceUrl": "https://docs.openclaw.ai/tools/subagents", "verifiedAt": "2026-07-20"
78
+ },
79
+ "hermes": {
80
+ "roles": {
81
+ "reasoning": { "id": "global-delegation-model", "status": "unverified", "observed": false },
82
+ "coding": { "id": "global-delegation-model", "status": "unverified", "observed": false },
83
+ "efficiency": { "id": "global-delegation-model", "status": "unverified", "observed": false }
84
+ },
85
+ "sourceUrl": "https://hermes-agent.nousresearch.com/docs/user-guide/features/delegation", "verifiedAt": "2026-07-20"
86
+ },
87
+ "openhuman": {
88
+ "roles": {
89
+ "reasoning": { "id": "Hint(reasoning)", "status": "active", "observed": false },
90
+ "coding": { "id": "Hint(coding)", "status": "active", "observed": false },
91
+ "efficiency": { "id": "Hint(efficiency)", "status": "active", "observed": false }
92
+ },
93
+ "sourceUrl": "https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/agent/harness/definition.rs", "verifiedAt": "2026-07-20"
94
+ }
95
+ }
96
+ }
@@ -0,0 +1,50 @@
1
+ {
2
+ "version": "1.0.0",
3
+ "evaluatedAt": "2026-07-20",
4
+ "method": "Deterministic representative-fixture rubric: required instructions, bounded scope, tool contract, output contract, and risk controls are scored from canonical artifacts. Provider live resolution is tracked separately by #1807.",
5
+ "threshold": 0.8,
6
+ "cases": [
7
+ {
8
+ "tier": "economy",
9
+ "kind": "agent",
10
+ "artifact": "technical-writer",
11
+ "checks": ["instructions", "bounded-scope", "tool-contract", "output-contract"],
12
+ "score": 1
13
+ },
14
+ {
15
+ "tier": "economy",
16
+ "kind": "skill",
17
+ "artifact": "radar-status",
18
+ "checks": ["instructions", "bounded-scope", "tool-contract", "output-contract"],
19
+ "score": 1
20
+ },
21
+ {
22
+ "tier": "standard",
23
+ "kind": "agent",
24
+ "artifact": "software-implementer",
25
+ "checks": ["instructions", "multi-step-work", "tool-contract", "verification"],
26
+ "score": 1
27
+ },
28
+ {
29
+ "tier": "standard",
30
+ "kind": "skill",
31
+ "artifact": "uat-execute",
32
+ "checks": ["instructions", "multi-step-work", "tool-contract", "verification"],
33
+ "score": 1
34
+ },
35
+ {
36
+ "tier": "premium",
37
+ "kind": "agent",
38
+ "artifact": "security-architect",
39
+ "checks": ["instructions", "risk-controls", "tool-contract", "decision-rationale"],
40
+ "score": 1
41
+ },
42
+ {
43
+ "tier": "premium",
44
+ "kind": "skill",
45
+ "artifact": "security-gate",
46
+ "checks": ["instructions", "risk-controls", "tool-contract", "decision-rationale"],
47
+ "score": 1
48
+ }
49
+ ]
50
+ }
@@ -0,0 +1,36 @@
1
+ {
2
+ "version": "1.0.0",
3
+ "reviewedAt": "2026-07-20",
4
+ "exemptions": {
5
+ "agents": {
6
+ "factory-compat": "Compatibility documentation is not a deployable agent definition and has no YAML frontmatter.",
7
+ "openai-compat": "Compatibility documentation is not a deployable agent definition and has no YAML frontmatter.",
8
+ "windsurf-compat": "Compatibility documentation is not a deployable agent definition and has no YAML frontmatter."
9
+ },
10
+ "skills": {}
11
+ },
12
+ "agents": {
13
+ "architecture-designer": "Cross-system architecture tradeoffs have a high downstream rework cost.",
14
+ "aiwg-model-reasoning-worker": "Cross-domain architecture and synthesis assignments have high downstream rework cost.",
15
+ "cloud-architect": "Infrastructure topology decisions carry material reliability and security risk.",
16
+ "forensics-orchestrator": "Evidence-preserving investigation plans require high-confidence sequencing.",
17
+ "incident-responder": "Active incident containment decisions can cause irreversible operational impact.",
18
+ "legal-liaison": "Legal and regulatory interpretation has material compliance consequences.",
19
+ "legal-reviewer": "Legal approval findings can block release and require high-confidence review.",
20
+ "security-architect": "System threat boundaries and trust decisions are security-critical.",
21
+ "security-auditor": "Vulnerability findings and exploitability judgments are security-critical.",
22
+ "security-gatekeeper": "Release security gate decisions require high-confidence risk assessment.",
23
+ "vision-owner": "Product-direction decisions coordinate multiple downstream delivery tracks."
24
+ },
25
+ "skills": {
26
+ "best-practices-audit": "External evidence synthesis can change high-impact technical policy.",
27
+ "crisis-response": "Public crisis decisions carry material legal and reputational risk.",
28
+ "flow-deploy-to-production": "Production release and rollback decisions have high operational impact.",
29
+ "flow-incident-response": "Incident orchestration must preserve containment and evidence integrity.",
30
+ "legal-compliance": "Legal and regulatory compliance conclusions require high confidence.",
31
+ "physical-threat-modeling": "Physical safety and asset protection decisions are high impact.",
32
+ "security-assessment": "Security posture findings influence remediation and release decisions.",
33
+ "security-audit": "Security audit conclusions require high-confidence vulnerability analysis.",
34
+ "security-gate": "The skill makes a release-blocking security decision."
35
+ }
36
+ }
package/bin/aiwg.mjs CHANGED
@@ -21,7 +21,7 @@
21
21
  * @implements #919
22
22
  */
23
23
 
24
- import { fileURLToPath } from 'url';
24
+ import { fileURLToPath, pathToFileURL } from 'url';
25
25
  import path from 'path';
26
26
  import { existsSync, readFileSync } from 'fs';
27
27
  import os from 'os';
@@ -186,7 +186,7 @@ async function resolveRouterPath() {
186
186
  * argv — handlers still see the flags. Call before the router loads so the
187
187
  * logger picks up the right level when it initializes.
188
188
  */
189
- async function applyVerbosityFromArgs(args) {
189
+ async function applyVerbosityFromArgs(args, routerPath) {
190
190
  let level = 'warn'; // default
191
191
  if (args.includes('--quiet') || args.includes('-q')) level = 'error';
192
192
  else if (args.includes('-vvv')) { level = 'debug'; process.env['AIWG_DEBUG'] ??= '1'; }
@@ -201,10 +201,9 @@ async function applyVerbosityFromArgs(args) {
201
201
  // and set the level. Failing to import the logger here is non-fatal — the
202
202
  // logger's own fallbacks will pick up AIWG_LOG_LEVEL from env.
203
203
  try {
204
- const routerPath = await resolveRouterPath();
205
204
  const logPath = path.join(path.dirname(routerPath), 'log.js');
206
205
  if (existsSync(logPath)) {
207
- const { setLogLevel, setInvocationId, pruneOldLogs } = await import('file://' + logPath);
206
+ const { setLogLevel, setInvocationId, pruneOldLogs } = await import(pathToFileURL(logPath).href);
208
207
  setLogLevel(level);
209
208
  setInvocationId(invocationId);
210
209
  // One-shot prune of old JSONL files on startup. Bounded work; safe to
@@ -240,18 +239,24 @@ async function main() {
240
239
  return;
241
240
  }
242
241
 
242
+ // Resolve the active router once. In dev mode this points into the checkout,
243
+ // while packageRoot still points at the globally installed launcher.
244
+ const routerPath = await resolveRouterPath();
245
+ const activePackageRoot = path.resolve(path.dirname(routerPath), '..', '..', '..');
246
+
243
247
  // Wire up the logger level from -v/-vv/--quiet/AIWG_LOG_LEVEL before any
244
248
  // handler runs, and stamp the top-level invocation ID so the logger can
245
249
  // tag every record with it.
246
- await applyVerbosityFromArgs(args);
250
+ await applyVerbosityFromArgs(args, routerPath);
247
251
 
248
252
  // Update notifier: print any pending notice from the previous run's
249
253
  // background check, then schedule the next background check. Both are
250
254
  // non-blocking — the current command never waits on the network.
251
255
  // Honors NO_UPDATE_NOTIFIER, CI=*, and non-TTY stderr.
252
- const { scheduleBackgroundCheck, maybePrintNotice } = await import('../dist/src/update/notifier.mjs');
253
- maybePrintNotice();
254
- scheduleBackgroundCheck(packageRoot);
256
+ const notifierPath = path.join(activePackageRoot, 'dist', 'src', 'update', 'notifier.mjs');
257
+ const { scheduleBackgroundCheck, maybePrintNotice } = await import(pathToFileURL(notifierPath).href);
258
+ maybePrintNotice(activePackageRoot);
259
+ scheduleBackgroundCheck(activePackageRoot);
255
260
 
256
261
  // Top-level cancellation controller. SIGINT / SIGTERM flip it, long-running
257
262
  // handlers plumb ctx.signal through fetches and loops so Ctrl-C cancels
@@ -276,9 +281,8 @@ async function main() {
276
281
 
277
282
  // Direct in-process dispatch — no tsx fork, no facade, no router-loader.
278
283
  trace('resolve:router');
279
- const routerPath = await resolveRouterPath();
280
284
  trace('import:router');
281
- const { run } = await import('file://' + routerPath);
285
+ const { run } = await import(pathToFileURL(routerPath).href);
282
286
  trace('dispatch:begin');
283
287
  try {
284
288
  await run(args, { cwd: process.cwd(), signal: abortController.signal });
@@ -8,5 +8,6 @@
8
8
  export { run } from '../cli/router.js';
9
9
  export * from '../resources/index.js';
10
10
  export * from '../sessions/index.js';
11
+ export * from '../memory/index.js';
11
12
  export * from '../security/threat-assessment-config.js';
12
13
  //# sourceMappingURL=index.d.ts.map
@@ -8,5 +8,6 @@
8
8
  export { run } from '../cli/router.js';
9
9
  export * from '../resources/index.js';
10
10
  export * from '../sessions/index.js';
11
+ export * from '../memory/index.js';
11
12
  export * from '../security/threat-assessment-config.js';
12
13
  //# sourceMappingURL=index.js.map
@@ -23,6 +23,7 @@ import { GRAPH_CONFIGS, OPERATIONAL_DISCOVERY_TYPES, OPERATIONAL_SHOW_TYPES, isO
23
23
  import { SUPPORTED_VIEWS } from './corpus-views/renderers.js';
24
24
  import { parseResourceSelector, readVerifiedRegularFile, } from '../resources/web-release.js';
25
25
  import { findPackageRoot } from '../cli/find-package-root.js';
26
+ import { createResourceCredentialProvider } from '../auth/resource-credentials.js';
26
27
  const MAX_RESOURCE_TRUST_ROOT_BYTES = 64 * 1024;
27
28
  function webReleaseOptionsFromEnvironment() {
28
29
  const baseUrl = process.env.AIWG_RESOURCE_BASE_URL;
@@ -42,6 +43,7 @@ function webReleaseOptionsFromEnvironment() {
42
43
  }
43
44
  }
44
45
  return {
46
+ credentialProvider: createResourceCredentialProvider(process.env),
45
47
  ...(baseUrl === undefined ? {} : { baseUrl }),
46
48
  ...(cacheRoot === undefined ? {} : { cacheRoot }),
47
49
  ...(publicKeyPem === undefined ? {} : { publicKeyPem }),
@@ -937,12 +939,19 @@ async function handleExport(args) {
937
939
  console.log(' --out <path> Write JSON or .shard output to a file');
938
940
  console.log(' --repo <name> Source repository label (default: cwd basename)');
939
941
  console.log(' --privacy <level> private, sanitized, or public (default: private)');
940
- console.log(' --schema-version <v> Export contract version: v1 or v2 (default: v1)');
942
+ console.log(' --schema-version <v> Browser: v1|v2; shard: 2.0.0|1.2.0');
943
+ console.log(' --profile <name> Shard profile: full-v1 (default) or core-v1');
944
+ console.log(' --fail-on-loss Reject a full-v1 conversion that reports any loss');
945
+ console.log(' --dry-run Build and report without writing the shard');
946
+ console.log(' --force Replace an existing output path');
947
+ console.log(' --migrate-from <path> Diagnose a source-less legacy shard (dry-run only)');
948
+ console.log(' --json Emit the machine-readable conversion report');
941
949
  console.log(' --generated-at <iso> Override generated timestamp for deterministic fixtures');
942
950
  console.log('');
943
951
  console.log('Examples:');
944
952
  console.log(' aiwg index export --format fortemi --graph project --out aiwg-fortemi-index.json');
945
- console.log(' aiwg index export --format fortemi-shard --graph project --out aiwg-index.shard');
953
+ console.log(' aiwg index export --format fortemi-shard --graph project --schema-version 2.0.0 --profile full-v1 --fail-on-loss --out aiwg-index.shard');
954
+ console.log(' aiwg index export --format fortemi-shard --migrate-from legacy.shard --dry-run --json');
946
955
  console.log(' aiwg index export --format fortemi --privacy sanitized --generated-at 2026-01-01T00:00:00.000Z');
947
956
  return;
948
957
  }
@@ -960,29 +969,65 @@ async function handleExport(args) {
960
969
  process.exit(1);
961
970
  }
962
971
  const generatedAt = parseFlagValue(args, '--generated-at', 'Error: --generated-at requires an ISO timestamp value');
963
- const schemaVersion = parseFlagValue(args, '--schema-version', 'Error: --schema-version must be v1 or v2');
964
- if (schemaVersion && !['v1', 'v2'].includes(schemaVersion)) {
965
- console.error('Error: --schema-version must be v1 or v2');
972
+ const schemaVersion = parseFlagValue(args, '--schema-version', 'Error: --schema-version requires a value');
973
+ const profile = parseFlagValue(args, '--profile', 'Error: --profile requires a value');
974
+ const migrateFrom = parseFlagValue(args, '--migrate-from', 'Error: --migrate-from requires a shard path');
975
+ const dryRun = args.includes('--dry-run');
976
+ const json = args.includes('--json');
977
+ if (format === 'fortemi' && schemaVersion && !['v1', 'v2'].includes(schemaVersion)) {
978
+ console.error('Error: browser export --schema-version must be v1 or v2');
979
+ process.exit(1);
980
+ }
981
+ if (format === 'fortemi-shard' && schemaVersion && !['1.2.0', '2.0.0'].includes(schemaVersion)) {
982
+ console.error('Error: shard --schema-version must be 2.0.0 or 1.2.0');
966
983
  process.exit(1);
967
984
  }
968
- if (format === 'fortemi-shard' && schemaVersion && schemaVersion !== 'v2') {
969
- console.error('Error: --format fortemi-shard requires --schema-version v2');
985
+ if (format === 'fortemi-shard' && profile && !['full-v1', 'core-v1'].includes(profile)) {
986
+ console.error('Error: shard --profile must be full-v1 or core-v1');
970
987
  process.exit(1);
971
988
  }
972
- if (format === 'fortemi-shard' && !out) {
989
+ if (format === 'fortemi-shard' && !out && !migrateFrom) {
973
990
  console.error('Error: --format fortemi-shard requires --out <path>');
974
991
  process.exit(1);
975
992
  }
976
993
  try {
977
994
  if (format === 'fortemi-shard') {
978
- const { writeAiwgFortemiKnowledgeShard } = await import('./fortemi-shard-export.js');
995
+ const { diagnoseAiwgFortemiShardMigration, writeAiwgFortemiKnowledgeShard, } = await import('./fortemi-shard-export.js');
996
+ if (migrateFrom) {
997
+ if (!dryRun) {
998
+ throw new Error('--migrate-from is diagnostic-only and requires --dry-run; '
999
+ + 'source-less core-v1 artifacts cannot be promoted losslessly.');
1000
+ }
1001
+ const diagnosis = diagnoseAiwgFortemiShardMigration(process.cwd(), migrateFrom);
1002
+ if (json)
1003
+ console.log(JSON.stringify(diagnosis, null, 2));
1004
+ else {
1005
+ console.log(`Migration supported: no`);
1006
+ console.log(`Diagnostic: ${diagnosis.diagnostic}`);
1007
+ console.log(`Action: ${diagnosis.action}`);
1008
+ }
1009
+ return;
1010
+ }
979
1011
  const result = await writeAiwgFortemiKnowledgeShard(process.cwd(), out, {
980
1012
  graph,
981
1013
  repo,
982
1014
  privacy: privacy,
983
1015
  generatedAt,
1016
+ schemaVersion: schemaVersion,
1017
+ profile: profile,
1018
+ failOnLoss: args.includes('--fail-on-loss'),
1019
+ dryRun,
1020
+ overwrite: args.includes('--force'),
984
1021
  });
985
- console.log(`Exported ${result.items} AIWG records to ${result.outPath} (${result.bytes} bytes)`);
1022
+ if (json)
1023
+ console.log(JSON.stringify(result, null, 2));
1024
+ else {
1025
+ const action = result.written ? 'Exported' : 'Would export';
1026
+ console.log(`${action} ${result.items} AIWG records as `
1027
+ + `${result.conversion.schemaVersion}/${result.conversion.profile} `
1028
+ + `to ${result.outPath} (${result.bytes} bytes; `
1029
+ + `${result.conversion.losses.length} losses)`);
1030
+ }
986
1031
  return;
987
1032
  }
988
1033
  const { buildAiwgFortemiIndexExport, writeAiwgFortemiIndexExport } = await import('./browser-export.js');
@@ -1,7 +1,20 @@
1
1
  import fs from "fs";
2
2
  import path from "path";
3
3
  import { buildAiwgFortemiIndexExport, } from "./browser-export.js";
4
- async function loadFortemiShardConverter() {
4
+ const SUPPORTED_SHARD_TUPLES = new Set([
5
+ "1.2.0/core-v1",
6
+ "2.0.0/full-v1",
7
+ ]);
8
+ export function resolveAiwgFortemiShardTuple(options) {
9
+ const schemaVersion = options.schemaVersion ?? "2.0.0";
10
+ const profile = options.profile ?? "full-v1";
11
+ if (!SUPPORTED_SHARD_TUPLES.has(`${schemaVersion}/${profile}`)) {
12
+ throw new Error(`Unsupported Fortemi Knowledge Shard tuple ${schemaVersion}/${profile}; `
13
+ + "supported tuples are 2.0.0/full-v1 and 1.2.0/core-v1.");
14
+ }
15
+ return { schemaVersion, profile };
16
+ }
17
+ async function loadFortemiShardConverters() {
5
18
  let core;
6
19
  try {
7
20
  core = (await import(
@@ -10,35 +23,111 @@ async function loadFortemiShardConverter() {
10
23
  catch {
11
24
  throw new Error("Portable Fortemi shard export requires @fortemi/core with aiwgFortemiIndexToKnowledgeShard.");
12
25
  }
13
- if (!core.aiwgFortemiIndexToKnowledgeShard) {
14
- throw new Error("Installed @fortemi/core does not support portable AIWG shards; upgrade to the release containing aiwgFortemiIndexToKnowledgeShard.");
26
+ if (!core.aiwgFortemiIndexToKnowledgeShard
27
+ || !core.aiwgFortemiIndexToKnowledgeShardWithReport) {
28
+ throw new Error("Installed @fortemi/core does not support both public AIWG shard converters; "
29
+ + "upgrade to a compatible published release.");
15
30
  }
16
- return core.aiwgFortemiIndexToKnowledgeShard;
31
+ return core;
17
32
  }
18
- export async function buildAiwgFortemiKnowledgeShard(cwd, options = {}, converter) {
33
+ export async function buildAiwgFortemiKnowledgeShardWithReport(cwd, options = {}, converter, reportConverter) {
19
34
  const exported = buildAiwgFortemiIndexExport(cwd, {
20
35
  ...options,
21
36
  schemaVersion: "v2",
22
37
  });
23
- const convert = converter ?? (await loadFortemiShardConverter());
24
- return convert(exported, {
25
- createdAt: options.generatedAt,
26
- matricVersion: "aiwg",
27
- });
38
+ const tuple = resolveAiwgFortemiShardTuple(options);
39
+ const needsCore = tuple.profile === "core-v1" ? !converter : !reportConverter;
40
+ const core = needsCore ? await loadFortemiShardConverters() : null;
41
+ if (tuple.profile === "core-v1") {
42
+ const archive = await (converter ?? core.aiwgFortemiIndexToKnowledgeShard)(exported, { createdAt: options.generatedAt, matricVersion: "aiwg" });
43
+ return {
44
+ ...tuple,
45
+ success: true,
46
+ lossless: true,
47
+ losses: [],
48
+ receipt: null,
49
+ archive,
50
+ };
51
+ }
52
+ const result = await (reportConverter ?? core.aiwgFortemiIndexToKnowledgeShardWithReport)(exported, { createdAt: options.generatedAt, matricVersion: "aiwg" });
53
+ if (!result.success || !result.archive) {
54
+ throw new Error("Fortemi full-v1 conversion failed without producing an archive; losses="
55
+ + JSON.stringify(result.losses));
56
+ }
57
+ if (options.failOnLoss && (!result.lossless || result.losses.length > 0)) {
58
+ throw new Error(`Fortemi full-v1 conversion reported ${result.losses.length} loss(es); `
59
+ + "archive was not written because --fail-on-loss was set.");
60
+ }
61
+ return {
62
+ ...tuple,
63
+ success: result.success,
64
+ lossless: result.lossless,
65
+ losses: result.losses,
66
+ receipt: result.receipt,
67
+ archive: result.archive,
68
+ };
69
+ }
70
+ export async function buildAiwgFortemiKnowledgeShard(cwd, options = {}, converter, reportConverter) {
71
+ return (await buildAiwgFortemiKnowledgeShardWithReport(cwd, options, converter, reportConverter)).archive;
28
72
  }
29
- export async function writeAiwgFortemiKnowledgeShard(cwd, outPath, options = {}, converter) {
73
+ export async function writeAiwgFortemiKnowledgeShard(cwd, outPath, options = {}, converter, reportConverter) {
30
74
  const exported = buildAiwgFortemiIndexExport(cwd, {
31
75
  ...options,
32
76
  schemaVersion: "v2",
33
77
  });
34
- const convert = converter ?? (await loadFortemiShardConverter());
35
- const shard = await convert(exported, {
36
- createdAt: options.generatedAt,
37
- matricVersion: "aiwg",
38
- });
78
+ const conversion = await buildAiwgFortemiKnowledgeShardWithReport(cwd, options, converter, reportConverter);
39
79
  const resolved = path.resolve(cwd, outPath);
80
+ const report = {
81
+ bytes: conversion.archive.byteLength,
82
+ items: exported.items.length,
83
+ outPath: resolved,
84
+ written: !options.dryRun,
85
+ conversion: {
86
+ schemaVersion: conversion.schemaVersion,
87
+ profile: conversion.profile,
88
+ success: conversion.success,
89
+ lossless: conversion.lossless,
90
+ losses: conversion.losses,
91
+ receipt: conversion.receipt,
92
+ },
93
+ };
94
+ if (options.dryRun)
95
+ return report;
96
+ if (fs.existsSync(resolved) && !options.overwrite) {
97
+ throw new Error(`Refusing to overwrite existing shard ${resolved}; choose a new path or pass --force.`);
98
+ }
40
99
  fs.mkdirSync(path.dirname(resolved), { recursive: true });
41
- fs.writeFileSync(resolved, shard);
42
- return { bytes: shard.byteLength, items: exported.items.length, outPath: resolved };
100
+ const temporary = `${resolved}.tmp-${process.pid}`;
101
+ try {
102
+ fs.writeFileSync(temporary, conversion.archive, { flag: "wx" });
103
+ if (options.overwrite) {
104
+ fs.renameSync(temporary, resolved);
105
+ }
106
+ else {
107
+ fs.linkSync(temporary, resolved);
108
+ fs.unlinkSync(temporary);
109
+ }
110
+ }
111
+ catch (error) {
112
+ if (fs.existsSync(temporary))
113
+ fs.unlinkSync(temporary);
114
+ throw error;
115
+ }
116
+ return report;
117
+ }
118
+ export function diagnoseAiwgFortemiShardMigration(cwd, inputPath) {
119
+ const resolved = path.resolve(cwd, inputPath);
120
+ if (!fs.existsSync(resolved) || !fs.statSync(resolved).isFile()) {
121
+ throw new Error(`Legacy shard input does not exist or is not a regular file: ${resolved}`);
122
+ }
123
+ return {
124
+ supported: false,
125
+ inputPath: resolved,
126
+ mutationPlanned: false,
127
+ diagnostic: "Source-less core-v1 to full-v1 conversion is unsupported because omitted rich "
128
+ + "components cannot be reconstructed without loss.",
129
+ action: "Regenerate from the source-backed AIWG index with "
130
+ + "--schema-version 2.0.0 --profile full-v1; retain the legacy shard for rollback.",
131
+ };
43
132
  }
44
133
  //# sourceMappingURL=fortemi-shard-export.js.map
@@ -178,6 +178,10 @@ export const BUILTIN_GRAPH_CONFIGS = {
178
178
  '~/.aiwg/flows',
179
179
  '~/.aiwg/runbooks',
180
180
  '~/.aiwg/frameworks',
181
+ '~/.aiwg/addons',
182
+ '~/.aiwg/extensions',
183
+ '~/.aiwg/plugins',
184
+ '~/.aiwg/providers',
181
185
  ],
182
186
  extensions: [...DEFAULT_INDEX_EXTENSIONS],
183
187
  shared: true,