@aiwg/cli 2026.8.8 → 2026.8.11

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 (70) hide show
  1. package/README.md +23 -8
  2. package/THIRD_PARTY_NOTICES.md +35 -0
  3. package/agentic/code/providers/capability-matrix.yaml +3 -3
  4. package/bin/aiwg.mjs +125 -0
  5. package/dist/src/agents/packaged-agent-inventory.js +37 -1
  6. package/dist/src/artifacts/backends/graphology-backend.js +4 -3
  7. package/dist/src/artifacts/backends/sqlite-backend.js +4 -5
  8. package/dist/src/artifacts/cli.js +2 -2
  9. package/dist/src/artifacts/corpus-tools/cli.js +27 -0
  10. package/dist/src/artifacts/corpus-tools/profile-embed.js +3 -2
  11. package/dist/src/artifacts/corpus-tools/retrieval-lab.js +356 -0
  12. package/dist/src/artifacts/discover-facets.js +2 -2
  13. package/dist/src/artifacts/embedding-index.js +9 -8
  14. package/dist/src/artifacts/fortemi-core-sync.js +23 -8
  15. package/dist/src/artifacts/graph-backend.js +2 -2
  16. package/dist/src/artifacts/query-engine.js +21 -7
  17. package/dist/src/artifacts/repair.js +47 -0
  18. package/dist/src/artifacts/types.js +3 -3
  19. package/dist/src/cli/command-log.js +2 -2
  20. package/dist/src/cli/handlers/artifacts.js +50 -1
  21. package/dist/src/cli/handlers/cost-report.js +71 -0
  22. package/dist/src/cli/handlers/evidence.js +78 -0
  23. package/dist/src/cli/handlers/help.js +9 -0
  24. package/dist/src/cli/handlers/index.js +8 -3
  25. package/dist/src/cli/handlers/local-executor.js +4 -3
  26. package/dist/src/cli/handlers/refresh.js +63 -17
  27. package/dist/src/cli/handlers/regenerate.js +3 -3
  28. package/dist/src/cli/handlers/serve.js +15 -36
  29. package/dist/src/cli/handlers/setup-manifest.js +8 -1
  30. package/dist/src/cli/handlers/use.js +272 -70
  31. package/dist/src/cli/handlers/utilities.js +149 -0
  32. package/dist/src/cli/handlers/workspace.js +10 -0
  33. package/dist/src/cli/help-generator.js +2 -1
  34. package/dist/src/cli/router.js +4 -1
  35. package/dist/src/cli/services/deployment-verification.js +596 -0
  36. package/dist/src/cli/skill-usage.js +2 -2
  37. package/dist/src/cli/workflow-orchestrator.js +1 -1
  38. package/dist/src/cli/workspace-signals.js +2 -2
  39. package/dist/src/config/aiwg-config.js +54 -27
  40. package/dist/src/config/cli.js +3 -3
  41. package/dist/src/config/project-artifacts-health.js +2 -0
  42. package/dist/src/config/project-artifacts-health.mjs +123 -0
  43. package/dist/src/config/project-artifacts-runtime.mjs +16 -0
  44. package/dist/src/config/project-artifacts.js +2 -1
  45. package/dist/src/cost/fleet-report.js +329 -0
  46. package/dist/src/evidence/bundle.js +256 -0
  47. package/dist/src/extensions/commands/definitions.js +77 -25
  48. package/dist/src/extensions/deployment-registration.js +6 -4
  49. package/dist/src/features/catalog.js +26 -0
  50. package/dist/src/features/cli.js +1 -3
  51. package/dist/src/features/runtime.js +17 -1
  52. package/dist/src/issues/cli.js +91 -7
  53. package/dist/src/mcp/server.mjs +1 -1
  54. package/dist/src/ops/registry.js +2 -2
  55. package/dist/src/policy/authorization.js +2 -2
  56. package/dist/src/providers/capability-matrix.yaml +3 -3
  57. package/dist/src/providers/provider-definitions.js +7 -5
  58. package/dist/src/providers/provider-definitions.mjs +1 -1
  59. package/dist/src/serve/pty-bridge.js +2 -8
  60. package/dist/src/serve/screen-reader.js +3 -6
  61. package/dist/src/smiths/context-pipeline/aiwg-md.js +2 -2
  62. package/dist/src/smiths/context-pipeline/finalization.js +18 -5
  63. package/dist/src/smiths/context-pipeline/generator.js +2 -2
  64. package/dist/src/smiths/context-pipeline/workspace-context.js +16 -17
  65. package/package.json +2 -1
  66. package/tools/agents/deploy-agents.mjs +10 -11
  67. package/tools/agents/providers/base.mjs +61 -7
  68. package/tools/agents/providers/openclaw.mjs +5 -2
  69. package/tools/agents/providers/windsurf.mjs +13 -24
  70. package/tools/skills/deploy-skills-codex.mjs +21 -5
@@ -8,6 +8,10 @@
8
8
  * @issue #1462
9
9
  */
10
10
  import { readFile, writeFile } from 'fs/promises';
11
+ import path from 'node:path';
12
+ import { pathToFileURL } from 'node:url';
13
+ import { getFrameworkRoot } from '../channel/manager.mjs';
14
+ import { readAiwgConfig } from '../config/aiwg-config.js';
11
15
  import { LocalIssueProviderCore, buildExternalIssueSnapshotFromLocal, buildLocalIssueConflictReport, createLiveIssueClient, localIssueRoot, parseCommentIdMappings, parseExternalIssueSnapshot, } from './index.js';
12
16
  export async function main(args, cwd = process.cwd()) {
13
17
  const parsed = parseArgs(args);
@@ -27,7 +31,11 @@ export async function main(args, cwd = process.cwd()) {
27
31
  return;
28
32
  case 'new':
29
33
  case 'create':
30
- await handleNew(issues, parsed);
34
+ await handleNew(issues, parsed, cwd);
35
+ return;
36
+ case 'plan':
37
+ case 'draft-plan':
38
+ await handlePlan(parsed, cwd);
31
39
  return;
32
40
  case 'list':
33
41
  case 'ls':
@@ -68,12 +76,19 @@ async function handleInit(issues, args) {
68
76
  });
69
77
  printJsonOrText(args, config, `Initialized local issues with prefix ${config.issue_key.prefix}`);
70
78
  }
71
- async function handleNew(issues, args) {
79
+ async function loadIssueComposer() {
80
+ const frameworkRoot = await getFrameworkRoot();
81
+ const modulePath = path.join(frameworkRoot, 'tools', 'issues', 'policy-boundary-composer.mjs');
82
+ return import(pathToFileURL(modulePath).href);
83
+ }
84
+ async function buildIssuePlan(args, cwd) {
72
85
  const title = stringFlag(args, 'title') ?? args.positional[1];
73
86
  if (!title)
74
- throw new Error('Usage: aiwg issue new --title <title> [--body <text>|--body-file <path>]');
87
+ throw new Error('Usage: aiwg issue plan --title <title> [--body <text>|--body-file <path>]');
75
88
  const body = await readBody(args);
76
- const issue = await issues.createIssue({
89
+ const config = await readAiwgConfig(cwd);
90
+ const composer = await loadIssueComposer();
91
+ return composer.planIssueDraft({
77
92
  title,
78
93
  body,
79
94
  type: stringFlag(args, 'type'),
@@ -81,8 +96,76 @@ async function handleNew(issues, args) {
81
96
  labels: csvFlag(args, 'label'),
82
97
  assignees: csvFlag(args, 'assignee'),
83
98
  author: stringFlag(args, 'author'),
84
- });
85
- printJsonOrText(args, issue, `Created ${issue.fields.id}: ${issue.fields.title}`);
99
+ }, { threatAssessment: config?.security?.threatAssessment });
100
+ }
101
+ async function handlePlan(args, cwd) {
102
+ const plan = await buildIssuePlan(args, cwd);
103
+ console.log(JSON.stringify(plan, null, 2));
104
+ }
105
+ async function handleNew(issues, args, cwd) {
106
+ const plan = await buildIssuePlan(args, cwd);
107
+ if (plan.disposition === 'blocked') {
108
+ throw new Error(`Issue draft rejected by ${plan.blockingRule}; no issue was written. Run aiwg issue plan to inspect suggested segments.`);
109
+ }
110
+ const authorizationDigest = stringFlag(args, 'authorize-policy');
111
+ if (plan.authorizationRequired && authorizationDigest !== plan.digest) {
112
+ throw new Error(`Issue draft requires explicit policy authorization. Review 'aiwg issue plan', then pass --authorize-policy ${plan.digest}. No issue was written.`);
113
+ }
114
+ const composer = await loadIssueComposer();
115
+ const records = new Map();
116
+ const adapter = {
117
+ async findByMarker(marker) {
118
+ const listed = await issues.listIssues({ limit: 10_000 });
119
+ for (const entry of listed.issues) {
120
+ const record = await issues.getIssue(entry.id, { body: true, comments: 0 });
121
+ if (record.body.includes(marker)) {
122
+ records.set(record.fields.id, record);
123
+ return { id: record.fields.id, reference: record.fields.id, body: record.body };
124
+ }
125
+ }
126
+ return null;
127
+ },
128
+ async create(draft) {
129
+ const record = await issues.createIssue({
130
+ title: draft.title,
131
+ body: draft.body,
132
+ type: stringFlag(args, 'type'),
133
+ priority: stringFlag(args, 'priority'),
134
+ labels: draft.labels,
135
+ assignees: draft.assignees,
136
+ author: stringFlag(args, 'author'),
137
+ });
138
+ records.set(record.fields.id, record);
139
+ return { id: record.fields.id, reference: record.fields.id, body: record.body };
140
+ },
141
+ async update(issue, patch) {
142
+ const record = records.get(issue.id) ?? await issues.getIssue(issue.id, { body: true, comments: 0 });
143
+ const references = [...patch.body.matchAll(/(?:Depends on|Related):\s+([A-Z][A-Z0-9_-]*-\d+)/g)]
144
+ .map((match) => match[1])
145
+ .filter((reference) => reference !== issue.id);
146
+ await issues.updateIssueFields(issue.id, {
147
+ links: {
148
+ ...record.fields.links,
149
+ related: [...new Set([...record.fields.links.related, ...references])],
150
+ parent: patch.body.match(/Depends on:\s+([A-Z][A-Z0-9_-]*-\d+)/)?.[1] ?? record.fields.links.parent,
151
+ },
152
+ });
153
+ await issues.commentIssue(issue.id, patch.body.slice(patch.body.indexOf('## Related')), {
154
+ author: stringFlag(args, 'author') ?? 'aiwg',
155
+ });
156
+ },
157
+ };
158
+ const result = await composer.executeIssuePlan(plan, adapter, { authorizationDigest });
159
+ if (result.status !== 'created') {
160
+ throw new Error(`Issue creation ${result.status}: ${result.error ?? 'review the recovery envelope'}\n${JSON.stringify(result.recovery ?? result, null, 2)}`);
161
+ }
162
+ if (args.flags.has('json') || result.created.length > 1) {
163
+ console.log(JSON.stringify(result, null, 2));
164
+ }
165
+ else {
166
+ const issue = result.created[0];
167
+ console.log(`Created ${issue.reference}: ${plan.segments[0].title}`);
168
+ }
86
169
  }
87
170
  async function handleList(issues, args) {
88
171
  const limit = stringFlag(args, 'limit');
@@ -313,7 +396,8 @@ function csvFlag(args, name) {
313
396
  function printUsage() {
314
397
  console.log(`Usage:
315
398
  aiwg issue init [--prefix KEY] [--padding N]
316
- aiwg issue new --title "..." [--body "..."] [--body-file path]
399
+ aiwg issue plan --title "..." [--body "..."] [--body-file path] [--json]
400
+ aiwg issue new --title "..." [--body "..."] [--body-file path] [--authorize-policy DIGEST]
317
401
  aiwg issue list [--status open] [--label bug] [--search text] [--limit 20] [--json]
318
402
  aiwg issue show <KEY> [--comments last:10|all]
319
403
  aiwg issue import --from gitea|github --snapshot-file path [--json]
@@ -7,7 +7,7 @@
7
7
  * @see https://modelcontextprotocol.io/specification/2025-11-25
8
8
  * @architecture @.aiwg/architecture/software-architecture-doc.md - Section 2.1 CLI Layer
9
9
  * @reference @docs/references/REF-066-mcp-specification-2025.md
10
- * @cli @docs/CLI_USAGE.md - mcp serve command
10
+ * @cli @docs/cli/agent-usage.md - mcp serve command
11
11
  * @frameworks @agentic/code/frameworks/sdlc-complete/
12
12
  * @frameworks @agentic/code/frameworks/media-marketing-kit/
13
13
  */
@@ -13,7 +13,7 @@ import { existsSync, readFileSync, readdirSync, statSync } from 'fs';
13
13
  import { execSync } from 'child_process';
14
14
  import { resolveConfigDir } from '../config/user-config.js';
15
15
  import { resolveWorkspace } from '../config/workspace.js';
16
- import { projectAiwgPath } from '../config/project-artifacts.js';
16
+ import { projectControlPath } from '../config/project-artifacts.js';
17
17
  /**
18
18
  * Extension type abbreviations
19
19
  */
@@ -455,7 +455,7 @@ export class OpsRegistry {
455
455
  throw new Error(`Workspace "${name}" not found`);
456
456
  }
457
457
  let canonical = null;
458
- const canonicalConfigPath = projectAiwgPath(workspace.home, 'aiwg.config');
458
+ const canonicalConfigPath = projectControlPath(workspace.home, 'aiwg.config');
459
459
  let canonicalDeclared = false;
460
460
  if (existsSync(canonicalConfigPath)) {
461
461
  try {
@@ -9,7 +9,7 @@
9
9
  import { access, copyFile, mkdir, readFile, rename } from 'node:fs/promises';
10
10
  import { isAbsolute, join, relative, resolve } from 'node:path';
11
11
  import { load as loadYaml } from 'js-yaml';
12
- import { projectAiwgPath } from '../config/project-artifacts.js';
12
+ import { projectAiwgPath, projectControlPath } from '../config/project-artifacts.js';
13
13
  /**
14
14
  * Export provider mappings without inventing provider grants. Stable IDs,
15
15
  * principals, scopes, resources, actions, and conditions survive unchanged;
@@ -375,7 +375,7 @@ export async function archiveLegacyPermissionManifests(projectDir) {
375
375
  return archived;
376
376
  }
377
377
  export async function backupConfig(projectDir) {
378
- const source = projectAiwgPath(projectDir, 'aiwg.config');
378
+ const source = projectControlPath(projectDir, 'aiwg.config');
379
379
  const backupDir = projectAiwgPath(projectDir, 'backups');
380
380
  await mkdir(backupDir, { recursive: true });
381
381
  const content = await readFile(source);
@@ -323,9 +323,9 @@ providers:
323
323
  aggregated_output: true
324
324
 
325
325
  windsurf:
326
- display_name: Windsurf
327
- status: experimental
328
- # Windsurf is an IDE extension requiring VS Code host — daemon not supported
326
+ display_name: Devin Desktop
327
+ status: stable
328
+ # Devin Desktop uses the Windsurf compatibility surface and requires a VS Code host — daemon not supported
329
329
  daemon_tier: unsupported
330
330
  daemon_pty_adapter: false
331
331
  artifact_paths:
@@ -704,11 +704,13 @@ const BUILT_IN_SEEDS = [
704
704
  },
705
705
  {
706
706
  id: 'windsurf',
707
- aliases: ['devin-desktop', 'devin-local', 'cascade'],
707
+ displayName: 'Devin Desktop',
708
+ status: 'stable',
709
+ aliases: ['devin', 'devin-desktop', 'devin-local', 'cascade'],
708
710
  builtIn: true,
709
711
  surfaces: {
710
- primary: 'windsurf',
711
- compatibility: ['devin-desktop', 'devin-local', 'cascade'],
712
+ primary: 'devin',
713
+ compatibility: ['devin-desktop', 'windsurf', 'devin-local', 'cascade'],
712
714
  precedence: ['.devin/rules/', '.windsurf/rules/', 'AGENTS.md', '.windsurfrules'],
713
715
  related: [
714
716
  {
@@ -716,7 +718,7 @@ const BUILT_IN_SEEDS = [
716
718
  displayName: 'Devin Desktop',
717
719
  relationship: 'same-provider',
718
720
  deployable: true,
719
- aliases: ['windsurf', 'cascade'],
721
+ aliases: ['devin', 'windsurf', 'cascade'],
720
722
  paths: {
721
723
  rules: ['.devin/rules/*.md', '.windsurf/rules/*.md'],
722
724
  skills: [],
@@ -724,7 +726,7 @@ const BUILT_IN_SEEDS = [
724
726
  legacy: ['.windsurfrules'],
725
727
  },
726
728
  notes: [
727
- 'Devin Desktop is the renamed Windsurf local IDE surface; --provider windsurf remains the deployable compatibility id.',
729
+ 'Devin Desktop is the current product name; --provider devin is preferred and --provider windsurf remains a deprecated compatibility id.',
728
730
  '.devin/rules is preferred by Devin Desktop, but AIWG keeps .devin/ as ignored local provider output and currently emits the compatibility surface through .windsurf/ plus AGENTS.md.',
729
731
  ],
730
732
  },
@@ -70,7 +70,7 @@ const MCP_INJECTION_DEFINITIONS = [
70
70
  },
71
71
  {
72
72
  id: 'windsurf',
73
- aliases: ['devin-desktop'],
73
+ aliases: ['devin', 'devin-desktop', 'devin-local', 'cascade'],
74
74
  mcp: {
75
75
  providerId: 'windsurf',
76
76
  includeInSupportedProviders: true,
@@ -288,16 +288,10 @@ async function spawnSandboxWsPty(session, agentId, wsEndpoint, opts) {
288
288
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
289
289
  let wsMod;
290
290
  try {
291
- wsMod = await (new Function('m', 'return import(m)'))('ws');
291
+ wsMod = await loadFeaturePackage('ws');
292
292
  }
293
293
  catch {
294
- try {
295
- // @ts-expect-error Optional runtime dependency; this workspace does not ship @types/ws.
296
- wsMod = await import('ws');
297
- }
298
- catch {
299
- throw new Error('ws package required for sandbox PTY bridge. Run: npm install ws');
300
- }
294
+ throw new Error('WebSocket support is required for the sandbox PTY bridge; run `aiwg features install webserver`');
301
295
  }
302
296
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
303
297
  const WS = wsMod.WebSocket ?? wsMod.default?.WebSocket ?? wsMod.default;
@@ -10,15 +10,12 @@
10
10
  * @see src/serve/pty-bridge.ts — PTY WebSocket bridge
11
11
  */
12
12
  import { EventEmitter } from 'events';
13
- import { createRequire } from 'module';
14
- // @xterm/headless is an optional dependency. Load it once at module level
15
- // using createRequire so we can import the CJS bundle from ESM.
16
- const _require = createRequire(import.meta.url);
13
+ import { requireFeaturePackage } from '../features/runtime.js';
17
14
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
18
15
  let XtermTerminal;
19
16
  try {
20
17
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
21
- const xterm = _require('@xterm/headless');
18
+ const xterm = requireFeaturePackage('@xterm/headless');
22
19
  XtermTerminal = xterm.Terminal;
23
20
  }
24
21
  catch {
@@ -48,7 +45,7 @@ export class ScreenReader {
48
45
  disposed = false;
49
46
  constructor(opts) {
50
47
  if (!XtermTerminal) {
51
- throw new Error('@xterm/headless is required for ScreenReader. Install it: npm install @xterm/headless');
48
+ throw new Error('Terminal screen parsing is unavailable; run `aiwg features install terminal`');
52
49
  }
53
50
  this._rows = opts?.rows ?? 24;
54
51
  this._cols = opts?.cols ?? 80;
@@ -16,7 +16,7 @@
16
16
  */
17
17
  import { promises as fs } from 'node:fs';
18
18
  import * as path from 'node:path';
19
- import { projectAiwgPath } from '../../config/project-artifacts.js';
19
+ import { projectControlPath } from '../../config/project-artifacts.js';
20
20
  import { buildParallelismSection, replaceOrAppendParallelismBlock } from './parallelism-section.js';
21
21
  import { buildContextFinalizationBlock, replaceOrAppendFinalizationBlock } from './finalization.js';
22
22
  import { buildExternalLinksSection, replaceOrAppendExternalLinksBlock, } from './external-links-section.js';
@@ -79,7 +79,7 @@ export async function buildAiwgMdContent(projectPath, stagedClaudeMdContent) {
79
79
  return replaceOrAppendExternalLinksBlock(withFinalization, externalLinksSection);
80
80
  }
81
81
  // Fallback stub.
82
- const normalizedPath = projectAiwgPath(projectPath, 'AIWG.md');
82
+ const normalizedPath = projectControlPath(projectPath, 'AIWG.md');
83
83
  const normalizedRelative = path.relative(projectPath, normalizedPath).replace(/\\/g, '/');
84
84
  const normalizedLink = normalizedRelative.startsWith('.') ? normalizedRelative : `./${normalizedRelative}`;
85
85
  const stub = [
@@ -10,7 +10,7 @@ import * as path from 'node:path';
10
10
  import { execFile } from 'node:child_process';
11
11
  import { promisify } from 'node:util';
12
12
  import { readAiwgConfig } from '../../config/aiwg-config.js';
13
- import { projectAiwgPath } from '../../config/project-artifacts.js';
13
+ import { projectAiwgPath, projectControlPath, resolveProjectAiwgDir, } from '../../config/project-artifacts.js';
14
14
  import { renderTrackerProtocol, resolveTrackerAuthority } from '../../tracker/capability-protocol.js';
15
15
  import { buildExternalLinksSection, replaceOrAppendExternalLinksBlock, } from './external-links-section.js';
16
16
  export const FINALIZATION_START = '<!-- aiwg-context-finalization:START -->';
@@ -61,7 +61,7 @@ export async function buildContextFinalizationBlock(projectPath) {
61
61
  const installed = Object.entries(config?.installed ?? {});
62
62
  const installedNames = installed.map(([name]) => name);
63
63
  const providerDeployments = new Set();
64
- const normalizedAiwgMdPath = displayProjectPath(projectPath, projectAiwgPath(projectPath, 'AIWG.md'));
64
+ const normalizedAiwgMdPath = displayProjectPath(projectPath, projectControlPath(projectPath, 'AIWG.md'));
65
65
  const normalizedAiwgMdLabel = `\`${normalizedAiwgMdPath}\``;
66
66
  for (const [, entry] of installed) {
67
67
  for (const provider of Object.keys(entry.deployedTo ?? {})) {
@@ -114,7 +114,7 @@ export function replaceOrAppendFinalizationBlock(content, block) {
114
114
  export async function buildNormalizedAiwgMd(projectPath, existing = '') {
115
115
  const block = await buildContextFinalizationBlock(projectPath);
116
116
  const externalLinksSection = await buildExternalLinksSection(projectPath);
117
- const normalizedAiwgMdPath = displayProjectPath(projectPath, projectAiwgPath(projectPath, 'AIWG.md'));
117
+ const normalizedAiwgMdPath = displayProjectPath(projectPath, projectControlPath(projectPath, 'AIWG.md'));
118
118
  const base = existing.trim().length > 0
119
119
  ? existing
120
120
  : [
@@ -132,7 +132,7 @@ export async function buildNormalizedAiwgMd(projectPath, existing = '') {
132
132
  return replaceOrAppendExternalLinksBlock(withFinalization, externalLinksSection);
133
133
  }
134
134
  export async function writeNormalizedAiwgMd(projectPath) {
135
- const targetPath = projectAiwgPath(projectPath, 'AIWG.md');
135
+ const targetPath = projectControlPath(projectPath, 'AIWG.md');
136
136
  let existing = '';
137
137
  try {
138
138
  existing = await fs.readFile(targetPath, 'utf8');
@@ -143,7 +143,20 @@ export async function writeNormalizedAiwgMd(projectPath) {
143
143
  }
144
144
  const content = await buildNormalizedAiwgMd(projectPath, existing);
145
145
  await fs.mkdir(path.dirname(targetPath), { recursive: true });
146
- await fs.writeFile(targetPath, content.endsWith('\n') ? content : `${content}\n`, 'utf8');
146
+ const normalizedContent = content.endsWith('\n') ? content : `${content}\n`;
147
+ await fs.writeFile(targetPath, normalizedContent, 'utf8');
148
+ const artifactRoot = resolveProjectAiwgDir(projectPath);
149
+ const artifactPath = projectAiwgPath(projectPath, 'AIWG.md');
150
+ if (artifactPath !== targetPath) {
151
+ try {
152
+ await fs.access(artifactRoot);
153
+ await fs.writeFile(artifactPath, normalizedContent, 'utf8');
154
+ }
155
+ catch (error) {
156
+ if (error.code !== 'ENOENT')
157
+ throw error;
158
+ }
159
+ }
147
160
  return targetPath;
148
161
  }
149
162
  //# sourceMappingURL=finalization.js.map
@@ -21,7 +21,7 @@ import { shouldEmitAgentsMd, shouldEmitAiwgMd, shouldEmitClaudeMdHook } from './
21
21
  import { ensureClaudeMdHook } from './claude-hook.js';
22
22
  import { ensureManagedHook } from './managed-hook.js';
23
23
  import { buildProviderBootstrapBlock, ensureWorkspaceContext, registerProviderContext, } from './workspace-context.js';
24
- import { projectAiwgPath } from '../../config/project-artifacts.js';
24
+ import { projectControlPath } from '../../config/project-artifacts.js';
25
25
  const SECTION_TITLES = {
26
26
  agents: 'Agents',
27
27
  rules: 'Rules',
@@ -137,7 +137,7 @@ export async function buildAgentsMd(opts) {
137
137
  parts.push('');
138
138
  parts.push('See [AIWG.md](./AIWG.md) for the full AIWG framework context');
139
139
  parts.push('(active frameworks, addons, agents, behaviors, rules).');
140
- const configRelative = path.relative(opts.projectPath, projectAiwgPath(opts.projectPath, 'aiwg.config')).replace(/\\/g, '/');
140
+ const configRelative = path.relative(opts.projectPath, projectControlPath(opts.projectPath, 'aiwg.config')).replace(/\\/g, '/');
141
141
  const configLink = configRelative.startsWith('.') ? configRelative : `./${configRelative}`;
142
142
  parts.push(`Tracker and delivery source of truth: [${configRelative}](${configLink}).`);
143
143
  parts.push('');
@@ -12,7 +12,8 @@ import * as path from 'node:path';
12
12
  import { buildAiwgMdContent } from './aiwg-md.js';
13
13
  import { buildNormalizedAiwgMd } from './finalization.js';
14
14
  import { getProviderDefinition, listProviderDefinitions, } from '../../providers/provider-definitions.js';
15
- import { projectAiwgPath, resolveProjectAiwgDir } from '../../config/project-artifacts.js';
15
+ import { readAiwgConfig } from '../../config/aiwg-config.js';
16
+ import { projectAiwgPath, projectControlPath, resolveProjectAiwgDir, } from '../../config/project-artifacts.js';
16
17
  export const WORKSPACE_MANAGED_START = '<!-- AIWG:workspace-context:start -->';
17
18
  export const WORKSPACE_MANAGED_END = '<!-- AIWG:workspace-context:end -->';
18
19
  export const WORKSPACE_OPERATOR_START = '<!-- AIWG:workspace-operator:start -->';
@@ -306,8 +307,8 @@ function providerForPath(relativePath) {
306
307
  }
307
308
  return null;
308
309
  }
309
- function projectArtifactMarkdownPath(projectPath, ...segments) {
310
- const rel = path.relative(projectPath, projectAiwgPath(projectPath, ...segments)).replace(/\\/g, '/');
310
+ function projectControlMarkdownPath(projectPath, ...segments) {
311
+ const rel = path.relative(projectPath, projectControlPath(projectPath, ...segments)).replace(/\\/g, '/');
311
312
  if (rel === '')
312
313
  return '.';
313
314
  return rel.startsWith('.') ? rel : `./${rel}`;
@@ -315,14 +316,15 @@ function projectArtifactMarkdownPath(projectPath, ...segments) {
315
316
  function workspaceLinks(projectPath, providerFiles = []) {
316
317
  const links = new Set([
317
318
  '[AIWG framework context](./AIWG.md)',
318
- `[AIWG project configuration](${projectArtifactMarkdownPath(projectPath, 'aiwg.config')})`,
319
+ `[AIWG project configuration](${projectControlMarkdownPath(projectPath, 'aiwg.config')})`,
319
320
  ]);
320
321
  if (providerFiles.length > 0) {
321
322
  for (const file of providerFiles)
322
323
  links.add(`[Provider-specific context](./${file.replace(/\\/g, '/')})`);
323
324
  }
324
- // The quickref source is linked, not copied into each provider directory.
325
- links.add(`[Project-local quickref](${projectArtifactMarkdownPath(projectPath, 'quickref.json')}) (when configured)`);
325
+ // Keep the logical project path portable in committed context. Runtime
326
+ // quickref readers follow `.aiwg-location` to the external corpus.
327
+ links.add('[Project-local quickref](.aiwg/quickref.json) (when configured)');
326
328
  return [...links];
327
329
  }
328
330
  export function buildWorkspaceManagedBlock(projectPath, providerFiles = []) {
@@ -692,16 +694,13 @@ function providerMigrationContent(source) {
692
694
  ].join('\n');
693
695
  }
694
696
  async function configuredProviders(projectPath) {
695
- const config = await readOptional(projectAiwgPath(projectPath, 'aiwg.config'));
696
- if (config) {
697
- try {
698
- const parsed = JSON.parse(config);
699
- if (Array.isArray(parsed.providers))
700
- return parsed.providers.filter((provider) => typeof provider === 'string');
701
- }
702
- catch {
703
- // Audit/migration reports malformed provider config through the normal CLI path.
704
- }
697
+ try {
698
+ const config = await readAiwgConfig(projectPath);
699
+ if (config)
700
+ return config.providers.filter((provider) => typeof provider === 'string');
701
+ }
702
+ catch {
703
+ // Audit/migration reports malformed provider config through the normal CLI path.
705
704
  }
706
705
  return ['claude'];
707
706
  }
@@ -783,7 +782,7 @@ async function stageMigrationWrites(projectPath, audit, options = {}) {
783
782
  writes.set(configPath, `${JSON.stringify(config, null, 2)}\n`);
784
783
  }
785
784
  if (options.includeGeneratedContext) {
786
- const normalizedPath = projectOutputPath(projectPath, projectAiwgPath(projectPath, 'AIWG.md'));
785
+ const normalizedPath = projectOutputPath(projectPath, projectControlPath(projectPath, 'AIWG.md'));
787
786
  const existingNormalized = await readOptional(path.join(projectPath, normalizedPath)) ?? '';
788
787
  writes.set(normalizedPath, await buildNormalizedAiwgMd(projectPath, existingNormalized));
789
788
  const stagedClaude = writes.has('CLAUDE.md')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiwg/cli",
3
- "version": "2026.8.8",
3
+ "version": "2026.8.11",
4
4
  "description": "Lightweight AIWG CLI for signed, versioned web-backed resources.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -24,6 +24,7 @@
24
24
  },
25
25
  "files": [
26
26
  "LICENSE",
27
+ "THIRD_PARTY_NOTICES.md",
27
28
  "bin/",
28
29
  "agentic/code/providers/",
29
30
  "dist/src/",
@@ -20,7 +20,7 @@
20
20
  * --rules-only Deploy only rules (skip agents)
21
21
  * --dry-run Show what would be deployed without writing
22
22
  * --force Overwrite existing files
23
- * --provider <name> Target provider: claude (default), openai, codex, cursor, opencode, copilot, factory, warp, windsurf, hermes, or openclaw
23
+ * --provider <name> Target provider: claude (default), openai, codex, cursor, opencode, copilot, factory, warp, devin, hermes, or openclaw
24
24
  * --model <name> Override model for all tiers (blanket)
25
25
  * --reasoning-model <name> Override model for reasoning tasks
26
26
  * --coding-model <name> Override model for coding tasks
@@ -49,7 +49,8 @@
49
49
  * copilot - GitHub Copilot - .github/agents/, .github/commands/, .github/skills/, .github/copilot-rules/
50
50
  * cursor - Cursor IDE - .cursor/agents/, .cursor/commands/, .cursor/skills/, .cursor/rules/
51
51
  * warp - Warp Terminal - .warp/agents/, .warp/commands/, .warp/skills/, .warp/rules/ + WARP.md
52
- * windsurf - Windsurf - .windsurf/agents/, .windsurf/workflows/, .windsurf/skills/, .windsurf/rules/
52
+ * devin - Devin Desktop - .windsurf/agents/, .windsurf/workflows/, .windsurf/skills/, .windsurf/rules/
53
+ * windsurf - Deprecated alias for devin
53
54
  * openclaw - OpenClaw - ~/.openclaw/agents/, ~/.openclaw/commands/, ~/.openclaw/skills/, ~/.openclaw/rules/, ~/.openclaw/behaviors/
54
55
  *
55
56
  * Defaults:
@@ -103,6 +104,7 @@ function getDeployVersion(srcRoot) {
103
104
 
104
105
  const PROVIDER_ALIASES = {
105
106
  'openai': 'codex',
107
+ 'devin': 'windsurf',
106
108
  'devin-desktop': 'windsurf',
107
109
  'devin-local': 'windsurf',
108
110
  'cascade': 'windsurf',
@@ -111,15 +113,9 @@ const PROVIDER_ALIASES = {
111
113
  const AVAILABLE_PROVIDERS = ['claude', 'factory', 'codex', 'opencode', 'copilot', 'cursor', 'warp', 'windsurf', 'hermes', 'openclaw', 'openhuman'];
112
114
 
113
115
  const UNSUPPORTED_PROVIDER_HINTS = {
114
- devin: [
115
- 'Devin Desktop is supported through the Windsurf compatibility adapter:',
116
- ' aiwg use sdlc --provider windsurf',
117
- ' aiwg use sdlc --provider devin-desktop',
118
- 'Devin CLI has distinct rules/skills surfaces and is recorded as future-provider metadata; AIWG does not emit .devin/ provider output yet.',
119
- ],
120
116
  'devin-cli': [
121
117
  'Devin CLI has distinct rules/skills surfaces and is not a deployable AIWG provider yet.',
122
- 'Use --provider windsurf or --provider devin-desktop for Devin Desktop/Windsurf local IDE deployments.',
118
+ 'Use --provider devin for Devin Desktop deployments.',
123
119
  ],
124
120
  };
125
121
 
@@ -531,7 +527,7 @@ Providers (all deploy agents, commands, skills, and rules):
531
527
  Paths: .cursor/agents/, .cursor/commands/, .cursor/skills/, .cursor/rules/
532
528
  warp - Warp Terminal
533
529
  Paths: .warp/agents/, .warp/commands/, .warp/skills/, .warp/rules/ + WARP.md
534
- windsurf - Windsurf
530
+ devin - Devin Desktop (preferred; aliases: devin-desktop, windsurf)
535
531
  Paths: .windsurf/agents/, .windsurf/workflows/, .windsurf/skills/, .windsurf/rules/
536
532
  hermes - Hermes Agent (MCP-based integration)
537
533
  Skills: ~/.hermes/skills/ (user-global) | Agents: AGENTS.md (lean routing guide)
@@ -865,7 +861,10 @@ export async function main() {
865
861
  srcRoot,
866
862
  target: cfg.target,
867
863
  mode: cfg.mode,
868
- provider: cfg.provider,
864
+ // Provider aliases are selectors, not artifact identities. Passing the
865
+ // canonical id keeps generated frontmatter byte-identical for devin,
866
+ // devin-desktop, and the deprecated windsurf selector.
867
+ provider: resolveProvider(cfg.provider),
869
868
  dryRun: cfg.dryRun,
870
869
  force: cfg.force,
871
870
  reasoningModel: cfg.reasoningModel,