@aiwg/cli 2026.8.7 → 2026.8.10
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 +23 -8
- package/THIRD_PARTY_NOTICES.md +35 -0
- package/agentic/code/providers/capability-matrix.yaml +3 -3
- package/bin/aiwg.mjs +125 -0
- package/dist/src/artifacts/backends/graphology-backend.js +4 -3
- package/dist/src/artifacts/backends/sqlite-backend.js +4 -5
- package/dist/src/artifacts/cli.js +2 -2
- package/dist/src/artifacts/corpus-tools/cli.js +27 -0
- package/dist/src/artifacts/corpus-tools/profile-embed.js +3 -2
- package/dist/src/artifacts/corpus-tools/retrieval-lab.js +356 -0
- package/dist/src/artifacts/discover-facets.js +2 -2
- package/dist/src/artifacts/embedding-index.js +9 -8
- package/dist/src/artifacts/graph-backend.js +2 -2
- package/dist/src/artifacts/move.js +40 -2
- package/dist/src/artifacts/query-engine.js +21 -7
- package/dist/src/artifacts/repair.js +47 -0
- package/dist/src/artifacts/types.js +3 -3
- package/dist/src/cli/command-log.js +2 -2
- package/dist/src/cli/handlers/artifacts.js +50 -1
- package/dist/src/cli/handlers/cost-report.js +71 -0
- package/dist/src/cli/handlers/evidence.js +78 -0
- package/dist/src/cli/handlers/help.js +9 -0
- package/dist/src/cli/handlers/index.js +8 -3
- package/dist/src/cli/handlers/local-executor.js +4 -3
- package/dist/src/cli/handlers/refresh.js +20 -8
- package/dist/src/cli/handlers/regenerate.js +22 -12
- package/dist/src/cli/handlers/serve.js +15 -36
- package/dist/src/cli/handlers/sessions.js +5 -4
- package/dist/src/cli/handlers/setup-manifest.js +8 -1
- package/dist/src/cli/handlers/subcommands.js +30 -0
- package/dist/src/cli/handlers/use.js +272 -89
- package/dist/src/cli/handlers/utilities.js +149 -0
- package/dist/src/cli/handlers/workspace.js +10 -0
- package/dist/src/cli/help-generator.js +2 -1
- package/dist/src/cli/regenerate-selector.js +94 -0
- package/dist/src/cli/router.js +4 -1
- package/dist/src/cli/services/deployment-verification.js +596 -0
- package/dist/src/cli/skill-usage.js +2 -2
- package/dist/src/cli/workflow-orchestrator.js +1 -1
- package/dist/src/cli/workspace-signals.js +2 -2
- package/dist/src/config/aiwg-config.js +54 -27
- package/dist/src/config/cli.js +3 -3
- package/dist/src/config/project-artifacts-health.js +2 -0
- package/dist/src/config/project-artifacts-health.mjs +123 -0
- package/dist/src/config/project-artifacts-runtime.mjs +16 -0
- package/dist/src/config/project-artifacts.js +2 -1
- package/dist/src/cost/fleet-report.js +329 -0
- package/dist/src/evidence/bundle.js +256 -0
- package/dist/src/extensions/commands/definitions.js +77 -25
- package/dist/src/extensions/deployment-registration.js +6 -4
- package/dist/src/extensions/project-local-doctor.js +10 -6
- package/dist/src/extensions/project-local-gitignore.js +8 -4
- package/dist/src/extensions/project-quickref.js +197 -10
- package/dist/src/features/catalog.js +26 -0
- package/dist/src/features/cli.js +1 -3
- package/dist/src/features/runtime.js +17 -1
- package/dist/src/issues/cli.js +91 -7
- package/dist/src/mcp/server.mjs +1 -1
- package/dist/src/ops/registry.js +2 -2
- package/dist/src/policy/authorization.js +2 -2
- package/dist/src/providers/capability-matrix.yaml +3 -3
- package/dist/src/providers/provider-definitions.js +7 -5
- package/dist/src/providers/provider-definitions.mjs +1 -1
- package/dist/src/serve/pty-bridge.js +2 -8
- package/dist/src/serve/screen-reader.js +3 -6
- package/dist/src/smiths/context-pipeline/aiwg-md.js +2 -2
- package/dist/src/smiths/context-pipeline/finalization.js +18 -5
- package/dist/src/smiths/context-pipeline/generator.js +2 -2
- package/dist/src/smiths/context-pipeline/workspace-context.js +16 -17
- package/package.json +2 -1
- package/tools/agents/deploy-agents.mjs +10 -11
- package/tools/agents/providers/base.mjs +47 -5
- package/tools/agents/providers/openclaw.mjs +5 -2
- package/tools/agents/providers/windsurf.mjs +13 -24
- package/tools/plugin/package-plugins.mjs +53 -0
- package/tools/skills/deploy-skills-codex.mjs +21 -5
|
@@ -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,
|
|
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,
|
|
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 =
|
|
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
|
-
|
|
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 {
|
|
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,
|
|
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 {
|
|
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
|
|
310
|
-
const rel = path.relative(projectPath,
|
|
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](${
|
|
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
|
-
//
|
|
325
|
-
|
|
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
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
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,
|
|
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.
|
|
3
|
+
"version": "2026.8.10",
|
|
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,
|
|
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
|
-
*
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
@@ -44,6 +44,48 @@ export function ensureDir(d, dryRun = false) {
|
|
|
44
44
|
if (!fs.existsSync(d)) fs.mkdirSync(d, { recursive: true });
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
const PROJECT_ARTIFACT_ENV_KEYS = [
|
|
48
|
+
'AIWG_ARTIFACTS_PATH',
|
|
49
|
+
'AIWG_PROJECT_ARTIFACTS_PATH',
|
|
50
|
+
'AIWG_PROJECT_AIWG_DIR',
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
function expandProjectArtifactPath(value, projectDir) {
|
|
54
|
+
const trimmed = value.trim();
|
|
55
|
+
if (trimmed === '~') return os.homedir();
|
|
56
|
+
if (trimmed.startsWith('~/')) return path.resolve(os.homedir(), trimmed.slice(2));
|
|
57
|
+
return path.isAbsolute(trimmed) ? trimmed : path.resolve(projectDir, trimmed);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function parseProjectArtifactLocation(contents) {
|
|
61
|
+
for (const rawLine of contents.split(/\r?\n/)) {
|
|
62
|
+
let line = rawLine.trim();
|
|
63
|
+
if (!line || line.startsWith('#')) continue;
|
|
64
|
+
if (line.startsWith('export ')) line = line.slice('export '.length).trim();
|
|
65
|
+
const assignment = line.match(/^AIWG_ARTIFACTS_PATH\s*=\s*(.+)$/);
|
|
66
|
+
if (assignment) line = assignment[1].trim();
|
|
67
|
+
if ((line.startsWith('"') && line.endsWith('"')) || (line.startsWith("'") && line.endsWith("'"))) {
|
|
68
|
+
line = line.slice(1, -1);
|
|
69
|
+
}
|
|
70
|
+
return line || null;
|
|
71
|
+
}
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Resolve the artifact corpus root used for framework-generated workspace data. */
|
|
76
|
+
export function resolveFrameworkWorkspaceRoot(projectDir, env = process.env) {
|
|
77
|
+
for (const key of PROJECT_ARTIFACT_ENV_KEYS) {
|
|
78
|
+
const value = env[key];
|
|
79
|
+
if (typeof value === 'string' && value.trim()) return expandProjectArtifactPath(value, projectDir);
|
|
80
|
+
}
|
|
81
|
+
const pointer = path.join(projectDir, '.aiwg-location');
|
|
82
|
+
if (fs.existsSync(pointer)) {
|
|
83
|
+
const configured = parseProjectArtifactLocation(fs.readFileSync(pointer, 'utf8'));
|
|
84
|
+
if (configured) return expandProjectArtifactPath(configured, projectDir);
|
|
85
|
+
}
|
|
86
|
+
return path.join(projectDir, '.aiwg');
|
|
87
|
+
}
|
|
88
|
+
|
|
47
89
|
/**
|
|
48
90
|
* List markdown files in a directory (non-recursive)
|
|
49
91
|
*/
|
|
@@ -1441,7 +1483,7 @@ export function deploySkillDir(skillDir, destDir, opts) {
|
|
|
1441
1483
|
* Creates .aiwg/frameworks/{framework-id}/ directories
|
|
1442
1484
|
*/
|
|
1443
1485
|
export function initializeFrameworkWorkspace(target, mode, dryRun, srcRoot = null) {
|
|
1444
|
-
const aiwgBase =
|
|
1486
|
+
const aiwgBase = resolveFrameworkWorkspaceRoot(target);
|
|
1445
1487
|
const frameworksDir = path.join(aiwgBase, 'frameworks');
|
|
1446
1488
|
const sharedDir = path.join(aiwgBase, 'shared');
|
|
1447
1489
|
|
|
@@ -1498,7 +1540,7 @@ export function initializeFrameworkWorkspace(target, mode, dryRun, srcRoot = nul
|
|
|
1498
1540
|
}
|
|
1499
1541
|
for (const entry of fw.memoryCreates || []) {
|
|
1500
1542
|
if (entry && typeof entry.path === 'string') {
|
|
1501
|
-
console.log(`[dry-run] ${path.join(
|
|
1543
|
+
console.log(`[dry-run] ${path.join(aiwgBase, entry.path.slice('.aiwg/'.length))}${entry.path.endsWith('/') ? '/' : ''}`);
|
|
1502
1544
|
}
|
|
1503
1545
|
}
|
|
1504
1546
|
}
|
|
@@ -1515,7 +1557,7 @@ export function initializeFrameworkWorkspace(target, mode, dryRun, srcRoot = nul
|
|
|
1515
1557
|
for (const subdir of fw.subdirs) {
|
|
1516
1558
|
ensureDir(path.join(fwBase, subdir));
|
|
1517
1559
|
}
|
|
1518
|
-
initializeMemoryCreates(
|
|
1560
|
+
initializeMemoryCreates(aiwgBase, fw.path, fw.memoryCreates);
|
|
1519
1561
|
}
|
|
1520
1562
|
|
|
1521
1563
|
// Initialize registry.json if it doesn't exist
|
|
@@ -1536,12 +1578,12 @@ export function initializeFrameworkWorkspace(target, mode, dryRun, srcRoot = nul
|
|
|
1536
1578
|
}
|
|
1537
1579
|
|
|
1538
1580
|
|
|
1539
|
-
function initializeMemoryCreates(
|
|
1581
|
+
function initializeMemoryCreates(aiwgBase, frameworkPath, creates) {
|
|
1540
1582
|
for (const entry of creates || []) {
|
|
1541
1583
|
if (!entry || typeof entry.path !== 'string') continue;
|
|
1542
1584
|
if (!entry.path.startsWith('.aiwg/')) continue;
|
|
1543
1585
|
|
|
1544
|
-
const targetPath = path.join(
|
|
1586
|
+
const targetPath = path.join(aiwgBase, entry.path.slice('.aiwg/'.length));
|
|
1545
1587
|
const isDirectory = entry.path.endsWith('/') || path.extname(entry.path) === '';
|
|
1546
1588
|
if (isDirectory) {
|
|
1547
1589
|
ensureDir(targetPath);
|
|
@@ -209,10 +209,13 @@ function deploySkills(skillDirs, opts) {
|
|
|
209
209
|
? skillDirs
|
|
210
210
|
: skillDirs.filter(d => isKernelSkill(d));
|
|
211
211
|
if (crossAgentSkills.length > 0) {
|
|
212
|
-
|
|
212
|
+
// Honor the deployment target selected by `aiwg use --target`. Provider
|
|
213
|
+
// subprocesses execute from AIWG_ROOT so process.cwd() would otherwise
|
|
214
|
+
// mutate the framework checkout during isolated/project-targeted deploys.
|
|
215
|
+
const crossAgentDir = path.join(opts.target || process.cwd(), '.agents', 'skills');
|
|
213
216
|
ensureDir(crossAgentDir, opts.dryRun);
|
|
214
217
|
if (!opts.dryRun) {
|
|
215
|
-
console.log(`Deploying cross-agent skills to ${path.relative(process.cwd(), crossAgentDir)}...`);
|
|
218
|
+
console.log(`Deploying cross-agent skills to ${path.relative(opts.target || process.cwd(), crossAgentDir)}...`);
|
|
216
219
|
} else {
|
|
217
220
|
console.log(`[dry-run] Would deploy cross-agent skills to .agents/skills/`);
|
|
218
221
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Devin Desktop Provider (Windsurf compatibility adapter)
|
|
3
3
|
*
|
|
4
|
-
* Deploys agents to
|
|
4
|
+
* Deploys agents to the Devin Desktop/Windsurf compatibility surface with
|
|
5
|
+
* aggregated AGENTS.md and trigger-frontmatter rules. The provider id and
|
|
6
|
+
* generated `.windsurf/` paths remain stable for backward compatibility.
|
|
5
7
|
*
|
|
6
8
|
* Deployment paths:
|
|
7
9
|
* - Output: AGENTS.md (aggregated agents)
|
|
@@ -18,7 +20,7 @@
|
|
|
18
20
|
* - Capabilities tags for tools
|
|
19
21
|
* - Workflow format for commands
|
|
20
22
|
* - Conventional skills and rules deployment
|
|
21
|
-
* -
|
|
23
|
+
* - Devin Desktop selectors resolve to the stable `windsurf` provider id
|
|
22
24
|
*/
|
|
23
25
|
|
|
24
26
|
import realFs from 'fs';
|
|
@@ -57,7 +59,7 @@ import {
|
|
|
57
59
|
// ============================================================================
|
|
58
60
|
|
|
59
61
|
export const name = 'windsurf';
|
|
60
|
-
export const aliases = [];
|
|
62
|
+
export const aliases = ['devin', 'devin-desktop', 'devin-local', 'cascade'];
|
|
61
63
|
|
|
62
64
|
export const paths = {
|
|
63
65
|
agents: '.windsurf/agents/', // Discrete mirrors alongside AGENTS.md
|
|
@@ -85,17 +87,6 @@ export const capabilities = {
|
|
|
85
87
|
yamlFormat: false
|
|
86
88
|
};
|
|
87
89
|
|
|
88
|
-
// ============================================================================
|
|
89
|
-
// Warning Display
|
|
90
|
-
// ============================================================================
|
|
91
|
-
|
|
92
|
-
function displayWarning() {
|
|
93
|
-
console.log('\n' + '='.repeat(70));
|
|
94
|
-
console.log('[EXPERIMENTAL] Windsurf provider support is experimental and untested.');
|
|
95
|
-
console.log('Please report issues: https://github.com/jmagly/aiwg/issues');
|
|
96
|
-
console.log('='.repeat(70) + '\n');
|
|
97
|
-
}
|
|
98
|
-
|
|
99
90
|
// ============================================================================
|
|
100
91
|
// Content Transformation
|
|
101
92
|
// ============================================================================
|
|
@@ -253,12 +244,12 @@ export function generateAgentsMd(files, destPath, opts) {
|
|
|
253
244
|
// refuses to replace the legacy 2MB+ aggregate writer's output.
|
|
254
245
|
lines.push('<!-- aiwg-managed -->');
|
|
255
246
|
lines.push('');
|
|
256
|
-
lines.push('> AIWG Agent Directory for
|
|
247
|
+
lines.push('> AIWG Agent Directory for Devin Desktop');
|
|
257
248
|
lines.push('');
|
|
258
249
|
lines.push('<!--');
|
|
259
|
-
lines.push('
|
|
260
|
-
lines.push('
|
|
261
|
-
lines.push(' See: https://docs.
|
|
250
|
+
lines.push(' Generated by AIWG for Devin Desktop using the Windsurf compatibility adapter.');
|
|
251
|
+
lines.push(' Devin Desktop reads this file for directory-scoped AI instructions.');
|
|
252
|
+
lines.push(' See: https://docs.devin.ai/onboard-devin/agents-md');
|
|
262
253
|
lines.push('-->');
|
|
263
254
|
lines.push('');
|
|
264
255
|
lines.push('## Table of Contents');
|
|
@@ -316,10 +307,10 @@ export function generateWindsurfRules(srcRoot, target, opts) {
|
|
|
316
307
|
lines.push('trigger: always_on');
|
|
317
308
|
lines.push('---');
|
|
318
309
|
lines.push('');
|
|
319
|
-
lines.push('# AIWG Orchestration for
|
|
310
|
+
lines.push('# AIWG Orchestration for Devin Desktop');
|
|
320
311
|
lines.push('');
|
|
321
312
|
lines.push('<!--');
|
|
322
|
-
lines.push('
|
|
313
|
+
lines.push(' Generated by AIWG for Devin Desktop using the Windsurf compatibility adapter.');
|
|
323
314
|
lines.push(' This file provides orchestration context for AIWG SDLC workflows.');
|
|
324
315
|
lines.push(' trigger: always_on — included in system prompt on every message.');
|
|
325
316
|
lines.push('-->');
|
|
@@ -556,9 +547,7 @@ export async function deploy(opts) {
|
|
|
556
547
|
dryRun
|
|
557
548
|
} = opts;
|
|
558
549
|
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
console.log(`\n=== Windsurf Provider (EXPERIMENTAL) ===`);
|
|
550
|
+
console.log(`\n=== Devin Desktop Provider (windsurf compatibility adapter) ===`);
|
|
562
551
|
console.log(`Target: ${target}`);
|
|
563
552
|
console.log(`Mode: ${mode}`);
|
|
564
553
|
const normalizedMode = normalizeDeploymentMode(mode);
|
|
@@ -657,6 +657,13 @@ for (const [id, sourceRoot] of MANIFEST_PLUGIN_SOURCES) {
|
|
|
657
657
|
keywords: manifest.keywords || manifest.tags || [],
|
|
658
658
|
category: manifest.category || 'productivity',
|
|
659
659
|
sourceRoot,
|
|
660
|
+
declaredSkills: manifest.skills ?? [],
|
|
661
|
+
// Plugin payloads run outside the checkout. Rewrite references back to
|
|
662
|
+
// their packaged root, including the pre-rename Ralph path retained by
|
|
663
|
+
// older Agent Loop documentation.
|
|
664
|
+
selfReferenceRoots: id === 'agent-loop'
|
|
665
|
+
? [sourceRoot, 'agentic/code/addons/ralph']
|
|
666
|
+
: [sourceRoot],
|
|
660
667
|
};
|
|
661
668
|
}
|
|
662
669
|
|
|
@@ -774,6 +781,48 @@ function copyDir(src, dest, dryRun = false, filter = null) {
|
|
|
774
781
|
return copied;
|
|
775
782
|
}
|
|
776
783
|
|
|
784
|
+
function rewritePackagedSelfReferences(pluginDir, roots = []) {
|
|
785
|
+
if (roots.length === 0) return;
|
|
786
|
+
const pending = [pluginDir];
|
|
787
|
+
while (pending.length > 0) {
|
|
788
|
+
const current = pending.pop();
|
|
789
|
+
for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
|
|
790
|
+
const target = path.join(current, entry.name);
|
|
791
|
+
if (entry.isDirectory()) {
|
|
792
|
+
pending.push(target);
|
|
793
|
+
} else if (entry.isFile() && /\.(?:md|json|ya?ml)$/.test(entry.name)) {
|
|
794
|
+
const original = fs.readFileSync(target, 'utf8');
|
|
795
|
+
let rewritten = original;
|
|
796
|
+
for (const root of roots) {
|
|
797
|
+
rewritten = rewritten.replaceAll(`${root}/`, '${CLAUDE_PLUGIN_ROOT}/');
|
|
798
|
+
}
|
|
799
|
+
if (rewritten !== original) fs.writeFileSync(target, rewritten, 'utf8');
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
function pruneUndeclaredPackagedSkills(pluginDir, declaredSkills) {
|
|
806
|
+
if (!declaredSkills) return;
|
|
807
|
+
const skillsRoot = path.join(pluginDir, 'skills');
|
|
808
|
+
if (!fs.existsSync(skillsRoot)) return;
|
|
809
|
+
const allowed = new Set(declaredSkills);
|
|
810
|
+
for (const entry of fs.readdirSync(skillsRoot, { withFileTypes: true })) {
|
|
811
|
+
if (entry.isDirectory() && !allowed.has(entry.name)) {
|
|
812
|
+
fs.rmSync(path.join(skillsRoot, entry.name), { recursive: true, force: true });
|
|
813
|
+
} else if (entry.isFile() && entry.name.endsWith('.md')) {
|
|
814
|
+
const skillId = path.basename(entry.name, '.md');
|
|
815
|
+
if (allowed.has(skillId)) {
|
|
816
|
+
const skillDir = path.join(skillsRoot, skillId);
|
|
817
|
+
fs.mkdirSync(skillDir, { recursive: true });
|
|
818
|
+
fs.renameSync(path.join(skillsRoot, entry.name), path.join(skillDir, 'SKILL.md'));
|
|
819
|
+
} else {
|
|
820
|
+
fs.rmSync(path.join(skillsRoot, entry.name), { force: true });
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
|
|
777
826
|
// Clean plugin directory (except .claude-plugin)
|
|
778
827
|
function cleanPlugin(pluginDir) {
|
|
779
828
|
if (!fs.existsSync(pluginDir)) return;
|
|
@@ -811,6 +860,10 @@ function packagePlugin(name, config, options) {
|
|
|
811
860
|
console.log(` 📁 Copying self-contained source from ${config.sourceRoot}...`);
|
|
812
861
|
const count = copyDir(config.sourceRoot, pluginDir, options.dryRun);
|
|
813
862
|
console.log(` ${count} files`);
|
|
863
|
+
if (!options.dryRun) {
|
|
864
|
+
rewritePackagedSelfReferences(pluginDir, config.selfReferenceRoots);
|
|
865
|
+
pruneUndeclaredPackagedSkills(pluginDir, config.declaredSkills);
|
|
866
|
+
}
|
|
814
867
|
}
|
|
815
868
|
|
|
816
869
|
// Copy sources
|
|
@@ -417,6 +417,17 @@ function isFullAiwgSourceRoot(srcRoot) {
|
|
|
417
417
|
const repoRoot = path.resolve(scriptDir, '..', '..');
|
|
418
418
|
const srcRoot = source || repoRoot;
|
|
419
419
|
const fullAiwgSourceRoot = isFullAiwgSourceRoot(srcRoot);
|
|
420
|
+
const copyStandardSkills = cfg.copyStandardSkills === true;
|
|
421
|
+
// Component-scoped deploys (the way `aiwg use all` installs selected
|
|
422
|
+
// frameworks/addons) still need a complete inventory for stale-skill
|
|
423
|
+
// reconciliation. AIWG_ROOT is supplied by the orchestrator for exactly
|
|
424
|
+
// this purpose; fall back to the script checkout for standalone use.
|
|
425
|
+
const configuredAiwgRoot = process.env.AIWG_ROOT
|
|
426
|
+
? path.resolve(process.env.AIWG_ROOT)
|
|
427
|
+
: repoRoot;
|
|
428
|
+
const inventoryRoot = fullAiwgSourceRoot
|
|
429
|
+
? srcRoot
|
|
430
|
+
: (!copyStandardSkills && isFullAiwgSourceRoot(configuredAiwgRoot) ? configuredAiwgRoot : null);
|
|
420
431
|
|
|
421
432
|
console.log(`Deploying skills to Codex`);
|
|
422
433
|
console.log(` Source: ${srcRoot}`);
|
|
@@ -440,8 +451,6 @@ function isFullAiwgSourceRoot(srcRoot) {
|
|
|
440
451
|
// Codex normally deploys to the project `.agents/skills/` target, so the
|
|
441
452
|
// kernel/standard split is enforced at filter time rather than via separate
|
|
442
453
|
// destination directories. The standalone legacy default remains supported.
|
|
443
|
-
const copyStandardSkills = cfg.copyStandardSkills === true;
|
|
444
|
-
|
|
445
454
|
// Track every AIWG-managed source skill name so we can scope post-deploy
|
|
446
455
|
// cleanup to skills AIWG ships — never delete user-authored or
|
|
447
456
|
// third-party skills sitting alongside.
|
|
@@ -457,10 +466,13 @@ function isFullAiwgSourceRoot(srcRoot) {
|
|
|
457
466
|
// so full-root cleanup can remove stale AIWG skills. Component-scoped
|
|
458
467
|
// cleanup is limited below to names owned by that component, preserving
|
|
459
468
|
// user-authored skills alongside the generated set.
|
|
460
|
-
for (const { dir } of getSkillDirectories(srcRoot, 'all')) {
|
|
469
|
+
for (const { dir } of getSkillDirectories(inventoryRoot || srcRoot, 'all')) {
|
|
461
470
|
const allSkills = findSkillDirs(dir);
|
|
462
471
|
for (const s of allSkills) {
|
|
463
472
|
allManagedNames.add(path.basename(s));
|
|
473
|
+
if (!copyStandardSkills && isKernelSkill(s)) {
|
|
474
|
+
desiredNames.add(path.basename(s));
|
|
475
|
+
}
|
|
464
476
|
// Also record the frontmatter `name:` since Codex uses that as the
|
|
465
477
|
// target dir. Best-effort — ignore parse errors.
|
|
466
478
|
try {
|
|
@@ -469,7 +481,11 @@ function isFullAiwgSourceRoot(srcRoot) {
|
|
|
469
481
|
if (fmMatch) {
|
|
470
482
|
const nameMatch = fmMatch[1].match(/^\s*name:\s*(.+?)\s*$/m);
|
|
471
483
|
if (nameMatch) {
|
|
472
|
-
|
|
484
|
+
const deployedName = stripWrappingQuotes(nameMatch[1]);
|
|
485
|
+
allManagedNames.add(deployedName);
|
|
486
|
+
if (!copyStandardSkills && isKernelSkill(s)) {
|
|
487
|
+
desiredNames.add(deployedName);
|
|
488
|
+
}
|
|
473
489
|
}
|
|
474
490
|
}
|
|
475
491
|
} catch { /* ignore */ }
|
|
@@ -528,7 +544,7 @@ function isFullAiwgSourceRoot(srcRoot) {
|
|
|
528
544
|
// the .aiwg-managed marker. Treat only the exact historical names as
|
|
529
545
|
// managed so malformed legacy frontmatter cannot survive an upgrade.
|
|
530
546
|
let isAiwgManaged = allManagedNames.has(name) || LEGACY_RENAMED_SKILLS.has(name);
|
|
531
|
-
if (!isAiwgManaged && fullAiwgSourceRoot) {
|
|
547
|
+
if (!isAiwgManaged && (fullAiwgSourceRoot || inventoryRoot)) {
|
|
532
548
|
// Check for the .aiwg-managed marker file (preferred — survives
|
|
533
549
|
// frontmatter transforms) or fall back to namespace check.
|
|
534
550
|
const markerFile = path.join(target, name, '.aiwg-managed');
|