@aiwg/cli 2026.8.0 → 2026.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -0
- package/agentic/code/providers/capability-matrix.yaml +511 -0
- package/agentic/code/providers/model-capabilities.v1.json +120 -0
- package/agentic/code/providers/model-catalog.v1.json +96 -0
- package/agentic/code/providers/model-policy-evaluations.v1.json +50 -0
- package/agentic/code/providers/premium-model-allowlist.v1.json +36 -0
- package/bin/aiwg.mjs +14 -10
- package/dist/src/api/index.d.ts +1 -0
- package/dist/src/api/index.js +1 -0
- package/dist/src/artifacts/cli.js +2 -0
- package/dist/src/artifacts/types.js +4 -0
- package/dist/src/auth/client.js +209 -0
- package/dist/src/auth/config.js +38 -0
- package/dist/src/auth/credential-store.js +141 -0
- package/dist/src/auth/resource-credentials.js +25 -0
- package/dist/src/auth/types.js +2 -0
- package/dist/src/channel/manager.mjs +5 -5
- package/dist/src/cli/handlers/auth.js +125 -0
- package/dist/src/cli/handlers/help.js +1 -0
- package/dist/src/cli/handlers/index.js +3 -1
- package/dist/src/cli/handlers/install.js +42 -4
- package/dist/src/cli/handlers/marketplace.js +375 -122
- package/dist/src/cli/handlers/resource-versions.js +2 -0
- package/dist/src/cli/handlers/sessions.js +23 -5
- package/dist/src/cli/handlers/subcommands.js +10 -1
- package/dist/src/cli/handlers/use.js +342 -43
- package/dist/src/config/gitignore.js +1 -0
- package/dist/src/extensions/commands/definitions.js +19 -0
- package/dist/src/marketplace/exchange.js +602 -0
- package/dist/src/marketplace/provenance-types.js +19 -0
- package/dist/src/marketplace/provenance.js +834 -0
- package/dist/src/memory/canonical-context.js +342 -0
- package/dist/src/memory/context-pack.js +282 -0
- package/dist/src/memory/index.js +4 -0
- package/dist/src/memory/intake.js +118 -0
- package/dist/src/packages/adapters/git.js +79 -29
- package/dist/src/packages/package-discovery.js +81 -0
- package/dist/src/packages/package-registry.js +2 -0
- package/dist/src/packages/registry.js +119 -20
- package/dist/src/resources/resolver.js +1 -0
- package/dist/src/resources/web-release.d.ts +3 -1
- package/dist/src/resources/web-release.js +14 -6
- package/dist/src/serve/agentic-sandbox-fleet-client.js +213 -0
- package/dist/src/serve/fleet-mission-conductor.js +293 -0
- package/dist/src/sessions/index.js +1 -0
- package/dist/src/sessions/output-registration.js +338 -0
- package/dist/src/sessions/promotion.js +73 -2
- package/dist/src/sessions/repository.js +2 -1
- package/dist/src/update/notifier.mjs +13 -2
- package/package.json +8 -1
- package/tools/_resolve-impl.mjs +74 -0
- package/tools/agents/deploy-agents.mjs +962 -0
- package/tools/agents/providers/base.mjs +2954 -0
- package/tools/agents/providers/claude.mjs +711 -0
- package/tools/agents/providers/codex.mjs +699 -0
- package/tools/agents/providers/copilot.mjs +659 -0
- package/tools/agents/providers/cursor.mjs +714 -0
- package/tools/agents/providers/factory.mjs +1130 -0
- package/tools/agents/providers/hermes.mjs +663 -0
- package/tools/agents/providers/hook-capabilities.mjs +85 -0
- package/tools/agents/providers/model-role.mjs +56 -0
- package/tools/agents/providers/openclaw-translator.mjs +348 -0
- package/tools/agents/providers/openclaw.mjs +680 -0
- package/tools/agents/providers/opencode.mjs +675 -0
- package/tools/agents/providers/openhuman.mjs +292 -0
- package/tools/agents/providers/warp.mjs +413 -0
- package/tools/agents/providers/windsurf.mjs +748 -0
- package/tools/commands/deploy-prompts-codex.mjs +336 -0
- package/tools/plugin/package-plugins.mjs +1013 -0
- package/tools/skills/deploy-skills-codex.mjs +571 -0
|
@@ -0,0 +1,714 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cursor IDE Provider
|
|
3
|
+
*
|
|
4
|
+
* Deploys agents, commands, skills, and rules for Cursor IDE.
|
|
5
|
+
* Rules use native .cursor/rules/ support with MDC format.
|
|
6
|
+
* Other artifacts use conventional .cursor/ subdirectories.
|
|
7
|
+
*
|
|
8
|
+
* Deployment paths:
|
|
9
|
+
* - Agents: .cursor/agents/
|
|
10
|
+
* - Commands: .cursor/commands/
|
|
11
|
+
* - Skills: .cursor/skills/
|
|
12
|
+
* - Rules: .cursor/rules/
|
|
13
|
+
*
|
|
14
|
+
* Special features:
|
|
15
|
+
* - MDC format (.mdc extension) for rules
|
|
16
|
+
* - Glob pattern attachment for rules
|
|
17
|
+
* - $ARGUMENTS -> [arguments] conversion for rules
|
|
18
|
+
* - Delegates rules deployment to deploy-rules-cursor.mjs
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import realFs from 'fs';
|
|
22
|
+
import { createRequire } from 'module';
|
|
23
|
+
const _require = createRequire(import.meta.url);
|
|
24
|
+
const staticModelCatalog = _require('../../../agentic/code/providers/model-catalog.v1.json');
|
|
25
|
+
let fs;
|
|
26
|
+
try { const gfs = _require('graceful-fs'); gfs.gracefulify(realFs); fs = realFs; } catch { fs = realFs; }
|
|
27
|
+
import path from 'path';
|
|
28
|
+
import { spawn } from 'child_process';
|
|
29
|
+
import {
|
|
30
|
+
ensureDir,
|
|
31
|
+
listMdFiles,
|
|
32
|
+
listMdFilesRecursive,
|
|
33
|
+
listSkillDirs,
|
|
34
|
+
deployFiles,
|
|
35
|
+
deploySkillDir,
|
|
36
|
+
deploySkillsWithKernelRouting,
|
|
37
|
+
isKernelSkill,
|
|
38
|
+
pruneStaleAiwgSkills,
|
|
39
|
+
computeAllKernelNames,
|
|
40
|
+
filterAgentFiles,
|
|
41
|
+
getAddonAgentFiles,
|
|
42
|
+
getAddonCommandFiles,
|
|
43
|
+
getAddonSkillDirs,
|
|
44
|
+
getAddonRuleFiles,
|
|
45
|
+
createAgentsMdFromTemplate,
|
|
46
|
+
initializeFrameworkWorkspace,
|
|
47
|
+
normalizeDeploymentMode,
|
|
48
|
+
collectFrameworkArtifacts,
|
|
49
|
+
listOnDemandRuleFiles,
|
|
50
|
+
writeOnDemandRuleIndex,
|
|
51
|
+
cleanupOldRuleFiles,
|
|
52
|
+
filterCommandsAgainstSkills,
|
|
53
|
+
deploySoulCompanions,
|
|
54
|
+
loadRuntimeModelCatalog
|
|
55
|
+
} from './base.mjs';
|
|
56
|
+
const modelCatalog = loadRuntimeModelCatalog(staticModelCatalog);
|
|
57
|
+
|
|
58
|
+
// ============================================================================
|
|
59
|
+
// Provider Configuration
|
|
60
|
+
// ============================================================================
|
|
61
|
+
|
|
62
|
+
export const name = 'cursor';
|
|
63
|
+
export const aliases = [];
|
|
64
|
+
|
|
65
|
+
export const paths = {
|
|
66
|
+
agents: '.cursor/agents/',
|
|
67
|
+
commands: '.cursor/commands/',
|
|
68
|
+
// Skills sequestered under .cursor/.aiwg/skills/ — index-driven discovery (#1212).
|
|
69
|
+
skills: '.cursor/.aiwg/skills/',
|
|
70
|
+
rules: '.cursor/rules/'
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// Kernel skills (always-loaded) deploy to the platform-native dir.
|
|
74
|
+
export const kernelSkillsPath = '.cursor/skills/';
|
|
75
|
+
|
|
76
|
+
export const support = {
|
|
77
|
+
agents: 'conventional',
|
|
78
|
+
commands: 'conventional',
|
|
79
|
+
skills: 'conventional',
|
|
80
|
+
rules: 'native'
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export const capabilities = {
|
|
84
|
+
agents: true,
|
|
85
|
+
commands: true,
|
|
86
|
+
skills: true,
|
|
87
|
+
rules: true, // Rules-focused provider with native support
|
|
88
|
+
aggregatedOutput: false,
|
|
89
|
+
yamlFormat: false
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
// ============================================================================
|
|
93
|
+
// Content Transformation
|
|
94
|
+
// ============================================================================
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Transform agent content for Cursor
|
|
98
|
+
* Cursor uses conventional deployment - minimal transformation needed
|
|
99
|
+
*/
|
|
100
|
+
export function transformAgent(srcPath, content, opts) {
|
|
101
|
+
const model = content.match(/^model:\s*(.+)$/m)?.[1]?.trim().replace(/['"]/g, '');
|
|
102
|
+
if (!model) return content;
|
|
103
|
+
const roles = modelCatalog.providers.cursor.roles;
|
|
104
|
+
const mapped = /opus/i.test(model)
|
|
105
|
+
? roles.reasoning.id
|
|
106
|
+
: /haiku/i.test(model)
|
|
107
|
+
? roles.efficiency.id
|
|
108
|
+
: roles.coding.id;
|
|
109
|
+
return content.replace(/^model:\s*.+$/m, `model: ${mapped}`);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Transform command content for Cursor
|
|
114
|
+
* Cursor uses conventional deployment - minimal transformation needed
|
|
115
|
+
*/
|
|
116
|
+
export function transformCommand(srcPath, content, opts) {
|
|
117
|
+
return content;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Transform rule content for Cursor — inject MDC frontmatter with the
|
|
122
|
+
* activation mode mapping per ADR-2 (rule activation mode schema).
|
|
123
|
+
*
|
|
124
|
+
* Reads the source rule's `activation:` field (or defaults to alwaysApply
|
|
125
|
+
* per ADR-2 §2 default-preservation) and emits the corresponding MDC
|
|
126
|
+
* frontmatter fields:
|
|
127
|
+
* - alwaysApply (true): activation === 'alwaysApply' (default)
|
|
128
|
+
* - alwaysApply (false), no globs: activation === 'auto'
|
|
129
|
+
* - globs: activation === 'glob' + globs: '<pattern>'
|
|
130
|
+
* - alwaysApply (false), description: activation === 'manual'
|
|
131
|
+
*
|
|
132
|
+
* Per ADR-2 §5: until the live-Cursor smoke test gate is in CI, all rules
|
|
133
|
+
* deploy with alwaysApply: true regardless of source `activation` value
|
|
134
|
+
* (with a deploy-time warning when the source declared a non-alwaysApply
|
|
135
|
+
* mode). The deploy-time warning is emitted by the deployer, not this
|
|
136
|
+
* transform.
|
|
137
|
+
*/
|
|
138
|
+
export function transformRule(srcPath, content, opts) {
|
|
139
|
+
// Detect proper YAML frontmatter — must close within the first 30 lines.
|
|
140
|
+
// Files with `---` horizontal rules deeper in markdown content (like
|
|
141
|
+
// RULES-INDEX.md) can have multiple `---` lines without being frontmatter;
|
|
142
|
+
// the line-budget keeps us from misidentifying horizontal rules as frontmatter
|
|
143
|
+
// delimiters.
|
|
144
|
+
const lines = content.split('\n');
|
|
145
|
+
let fmEnd = -1;
|
|
146
|
+
if (lines[0]?.trim() === '---') {
|
|
147
|
+
for (let i = 1; i < Math.min(lines.length, 30); i++) {
|
|
148
|
+
if (lines[i].trim() === '---') {
|
|
149
|
+
fmEnd = i;
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
// YAML frontmatter is key:value pairs. If we see a markdown heading or
|
|
153
|
+
// bold/italic markers in what would be frontmatter, this is not YAML.
|
|
154
|
+
if (/^(#{1,6}\s|\*\*|^\* )/.test(lines[i])) {
|
|
155
|
+
fmEnd = -1;
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (fmEnd > 0) {
|
|
162
|
+
const existingFm = lines.slice(1, fmEnd).join('\n');
|
|
163
|
+
const body = lines.slice(fmEnd + 1).join('\n');
|
|
164
|
+
|
|
165
|
+
// If the operator already set alwaysApply, leave it alone.
|
|
166
|
+
if (/^\s*alwaysApply\s*:\s*\w+/m.test(existingFm)) {
|
|
167
|
+
return content;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// PUW-021 (#1122): if source frontmatter declares `globs:` (or
|
|
171
|
+
// `applyTo:` for Copilot-style rules), emit Cursor MDC `globs:` plus
|
|
172
|
+
// `alwaysApply: false`. The activation mode is implicitly `glob`.
|
|
173
|
+
const globsMatch = /^\s*globs?\s*:\s*(.+)$/m.exec(existingFm);
|
|
174
|
+
const applyToMatch = /^\s*applyTo\s*:\s*(.+)$/m.exec(existingFm);
|
|
175
|
+
if (globsMatch || applyToMatch) {
|
|
176
|
+
const globValue = (globsMatch?.[1] || applyToMatch?.[1] || '').trim().replace(/^['"]|['"]$/g, '');
|
|
177
|
+
let mergedFm = existingFm.trimEnd();
|
|
178
|
+
// Add globs field if absent (operator already set globs would have matched).
|
|
179
|
+
if (!globsMatch) {
|
|
180
|
+
mergedFm += `\nglobs: '${globValue}'`;
|
|
181
|
+
}
|
|
182
|
+
mergedFm += '\nalwaysApply: false';
|
|
183
|
+
return `---\n${mergedFm}\n---\n${body}`;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const updatedFm = existingFm.trimEnd() + '\nalwaysApply: true';
|
|
187
|
+
return `---\n${updatedFm}\n---\n${body}`;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// No (proper) frontmatter — prepend a minimal MDC block.
|
|
191
|
+
return `---\nalwaysApply: true\n---\n${content}`;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// ============================================================================
|
|
195
|
+
// Model Mapping (not applicable for Cursor)
|
|
196
|
+
// ============================================================================
|
|
197
|
+
|
|
198
|
+
export function mapModel(shorthand, modelCfg, modelsConfig) {
|
|
199
|
+
return shorthand;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// ============================================================================
|
|
203
|
+
// Deployment Functions
|
|
204
|
+
// ============================================================================
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Deploy agents to .cursor/agents/
|
|
208
|
+
*/
|
|
209
|
+
export function deployAgents(agentFiles, targetDir, opts) {
|
|
210
|
+
const destDir = path.join(targetDir, paths.agents);
|
|
211
|
+
ensureDir(destDir, opts.dryRun);
|
|
212
|
+
return deployFiles(agentFiles, destDir, { ...opts, injectPlatform: true }, transformAgent);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Deploy commands to .cursor/commands/
|
|
217
|
+
*/
|
|
218
|
+
export function deployCommands(commandFiles, targetDir, opts) {
|
|
219
|
+
const destDir = path.join(targetDir, paths.commands);
|
|
220
|
+
ensureDir(destDir, opts.dryRun);
|
|
221
|
+
return deployFiles(commandFiles, destDir, opts, transformCommand);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Deploy skills with kernel-vs-standard routing (#1212/#1216).
|
|
226
|
+
* - kernel skills → .cursor/skills/ (platform-native, always-loaded)
|
|
227
|
+
* - standard → .cursor/.aiwg/skills/ (index-discoverable)
|
|
228
|
+
*/
|
|
229
|
+
export function deploySkills(skillDirs, targetDir, opts) {
|
|
230
|
+
const standardDestDir = path.join(targetDir, paths.skills);
|
|
231
|
+
const kernelDestDir = path.join(targetDir, kernelSkillsPath);
|
|
232
|
+
deploySkillsWithKernelRouting(skillDirs, standardDestDir, kernelDestDir, opts);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Deploy rules via external script (native MDC support)
|
|
237
|
+
* Falls back to inline deployment if script not found
|
|
238
|
+
*/
|
|
239
|
+
export async function deployRulesViaScript(targetDir, srcRoot, opts) {
|
|
240
|
+
const scriptPath = path.join(srcRoot, 'tools', 'rules', 'deploy-rules-cursor.mjs');
|
|
241
|
+
|
|
242
|
+
if (!fs.existsSync(scriptPath)) {
|
|
243
|
+
console.warn(`Cursor rules deployment script not found at ${scriptPath}`);
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
console.log('Delegating rules deployment to deploy-rules-cursor.mjs...');
|
|
248
|
+
|
|
249
|
+
return new Promise((resolve, reject) => {
|
|
250
|
+
const args = ['--target', targetDir, '--source', srcRoot];
|
|
251
|
+
if (opts.dryRun) args.push('--dry-run');
|
|
252
|
+
if (opts.force) args.push('--force');
|
|
253
|
+
if (opts.mode) args.push('--mode', opts.mode);
|
|
254
|
+
|
|
255
|
+
const child = spawn('node', [scriptPath, ...args], {
|
|
256
|
+
stdio: 'inherit',
|
|
257
|
+
cwd: srcRoot
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
child.on('close', (code) => {
|
|
261
|
+
if (code === 0) resolve(true);
|
|
262
|
+
else reject(new Error(`deploy-rules-cursor.mjs exited with code ${code}`));
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
child.on('error', reject);
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Deploy rules to .cursor/rules/ (inline deployment)
|
|
271
|
+
* Used as fallback when external script is not available
|
|
272
|
+
*/
|
|
273
|
+
export function deployRulesInline(ruleFiles, targetDir, opts) {
|
|
274
|
+
const destDir = path.join(targetDir, paths.rules);
|
|
275
|
+
ensureDir(destDir, opts.dryRun);
|
|
276
|
+
// Migrate legacy AIWG `.md` rules → `.mdc`. Cursor's rules engine only loads
|
|
277
|
+
// `.mdc` files, so AIWG-managed `.md` rules from an older deploy are inert and
|
|
278
|
+
// would linger (cleanup matches by stem, keeping them). Remove the marker-
|
|
279
|
+
// bearing `.md` leftovers so the `.mdc` we emit below is the live copy.
|
|
280
|
+
if (!opts.dryRun && fs.existsSync(destDir)) {
|
|
281
|
+
for (const name of fs.readdirSync(destDir)) {
|
|
282
|
+
if (!name.endsWith('.md') || name === 'RULES-INDEX.md') continue;
|
|
283
|
+
const p = path.join(destDir, name);
|
|
284
|
+
try {
|
|
285
|
+
if (fs.readFileSync(p, 'utf8').includes('aiwg:managed')) {
|
|
286
|
+
fs.unlinkSync(p);
|
|
287
|
+
console.log(` migrated legacy rule to .mdc (removed ${name})`);
|
|
288
|
+
}
|
|
289
|
+
} catch { /* ignore unreadable file */ }
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
// #1143: skip cleanup when this deploy has 0 rules.
|
|
293
|
+
cleanupOldRuleFiles(destDir, { ...opts, incomingFiles: ruleFiles });
|
|
294
|
+
// transformRule injects native MDC frontmatter (globs → alwaysApply:false when
|
|
295
|
+
// the source declares them; else alwaysApply:true). Emit with the `.mdc`
|
|
296
|
+
// extension Cursor actually loads — getFileExtension('rule') === '.mdc'.
|
|
297
|
+
return deployFiles(ruleFiles, destDir, { ...opts, fileExtension: '.mdc' }, transformRule);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Deploy rules - tries external script first, falls back to inline
|
|
302
|
+
*/
|
|
303
|
+
export async function deployRules(ruleFilesOrTarget, targetDirOrSrcRoot, optsOrUndefined) {
|
|
304
|
+
// Handle both call signatures:
|
|
305
|
+
// 1. deployRules(targetDir, srcRoot, opts) - from old code
|
|
306
|
+
// 2. deployRules(ruleFiles, targetDir, opts) - from new code
|
|
307
|
+
|
|
308
|
+
// Check if first arg is array (new signature) or string (old signature)
|
|
309
|
+
if (Array.isArray(ruleFilesOrTarget)) {
|
|
310
|
+
// New signature: deployRules(ruleFiles, targetDir, opts)
|
|
311
|
+
return deployRulesInline(ruleFilesOrTarget, targetDirOrSrcRoot, optsOrUndefined);
|
|
312
|
+
} else {
|
|
313
|
+
// Old signature: deployRules(targetDir, srcRoot, opts)
|
|
314
|
+
// Try external script first
|
|
315
|
+
try {
|
|
316
|
+
const success = await deployRulesViaScript(ruleFilesOrTarget, targetDirOrSrcRoot, optsOrUndefined);
|
|
317
|
+
if (success) return;
|
|
318
|
+
} catch (err) {
|
|
319
|
+
console.warn('External rules script failed, using inline deployment');
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Fallback to inline - need to collect rule files
|
|
323
|
+
console.log('Using inline rules deployment...');
|
|
324
|
+
const ruleFiles = [];
|
|
325
|
+
const srcRoot = targetDirOrSrcRoot;
|
|
326
|
+
const opts = optsOrUndefined;
|
|
327
|
+
|
|
328
|
+
// Use consolidated index if available
|
|
329
|
+
const indexPath = getRulesIndexPath(srcRoot);
|
|
330
|
+
if (indexPath) {
|
|
331
|
+
ruleFiles.push(indexPath);
|
|
332
|
+
} else {
|
|
333
|
+
const sdlcRulesDir = path.join(srcRoot, 'agentic', 'code', 'frameworks', 'sdlc-complete', 'rules');
|
|
334
|
+
if (fs.existsSync(sdlcRulesDir)) {
|
|
335
|
+
ruleFiles.push(...listMdFiles(sdlcRulesDir));
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
deployRulesInline(ruleFiles, ruleFilesOrTarget, opts);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// ============================================================================
|
|
344
|
+
// AGENTS.md
|
|
345
|
+
// ============================================================================
|
|
346
|
+
|
|
347
|
+
export function createAgentsMd(target, srcRoot, dryRun) {
|
|
348
|
+
createAgentsMdFromTemplate(target, srcRoot, 'cursor/AGENTS.md.aiwg-template', dryRun);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// ============================================================================
|
|
352
|
+
// Cloud agent + worktree configuration (PUW-019 #1120)
|
|
353
|
+
// ============================================================================
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Deploy Cursor cloud-agent + worktree config templates.
|
|
357
|
+
*
|
|
358
|
+
* Per PUW-019: AIWG ships templates at
|
|
359
|
+
* agentic/code/frameworks/sdlc-complete/templates/cursor/environment.json.aiwg-template
|
|
360
|
+
* agentic/code/frameworks/sdlc-complete/templates/cursor/worktrees.json.aiwg-template
|
|
361
|
+
* but they were never wired into the deployer. This emits them at
|
|
362
|
+
* .cursor/environment.json
|
|
363
|
+
* .cursor/worktrees.json
|
|
364
|
+
* only when the operator-provided file is absent (always-deploy invariant
|
|
365
|
+
* §0.6 — we don't overwrite operator-authored config). When the file
|
|
366
|
+
* exists, deploy is skipped with a verbose-only note.
|
|
367
|
+
*/
|
|
368
|
+
export function deployCursorConfigTemplates(targetDir, srcRoot, opts) {
|
|
369
|
+
const cursorDir = path.join(targetDir, '.cursor');
|
|
370
|
+
ensureDir(cursorDir, opts?.dryRun);
|
|
371
|
+
|
|
372
|
+
const templates = [
|
|
373
|
+
{ src: 'cursor/environment.json.aiwg-template', dest: 'environment.json' },
|
|
374
|
+
{ src: 'cursor/worktrees.json.aiwg-template', dest: 'worktrees.json' },
|
|
375
|
+
];
|
|
376
|
+
|
|
377
|
+
for (const t of templates) {
|
|
378
|
+
const srcPath = path.join(
|
|
379
|
+
srcRoot,
|
|
380
|
+
'agentic',
|
|
381
|
+
'code',
|
|
382
|
+
'frameworks',
|
|
383
|
+
'sdlc-complete',
|
|
384
|
+
'templates',
|
|
385
|
+
t.src,
|
|
386
|
+
);
|
|
387
|
+
const destPath = path.join(cursorDir, t.dest);
|
|
388
|
+
|
|
389
|
+
if (!fs.existsSync(srcPath)) {
|
|
390
|
+
if (opts?.verbose) {
|
|
391
|
+
console.log(` cursor template not found, skipping: ${t.src}`);
|
|
392
|
+
}
|
|
393
|
+
continue;
|
|
394
|
+
}
|
|
395
|
+
if (fs.existsSync(destPath)) {
|
|
396
|
+
if (opts?.verbose) {
|
|
397
|
+
console.log(` ${t.dest} exists, preserving operator content`);
|
|
398
|
+
}
|
|
399
|
+
continue;
|
|
400
|
+
}
|
|
401
|
+
if (opts?.dryRun) {
|
|
402
|
+
console.log(`[dry-run] Would deploy cursor template ${t.src} -> .cursor/${t.dest}`);
|
|
403
|
+
continue;
|
|
404
|
+
}
|
|
405
|
+
fs.copyFileSync(srcPath, destPath);
|
|
406
|
+
if (opts?.verbose) {
|
|
407
|
+
console.log(`deployed cursor template: .cursor/${t.dest}`);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// ============================================================================
|
|
413
|
+
// Post-Deployment
|
|
414
|
+
// ============================================================================
|
|
415
|
+
|
|
416
|
+
export async function postDeploy(targetDir, opts) {
|
|
417
|
+
initializeFrameworkWorkspace(targetDir, opts.mode, opts.dryRun, opts.srcRoot);
|
|
418
|
+
|
|
419
|
+
if (opts.createAgentsMd) {
|
|
420
|
+
createAgentsMd(targetDir, opts.srcRoot, opts.dryRun);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// PUW-019 (#1120): wire environment.json + worktrees.json templates.
|
|
424
|
+
try {
|
|
425
|
+
deployCursorConfigTemplates(targetDir, opts.srcRoot, opts);
|
|
426
|
+
} catch (err) {
|
|
427
|
+
console.warn(
|
|
428
|
+
`Warning: cursor template deploy failed: ${err && err.message ? err.message : err}`,
|
|
429
|
+
);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// ============================================================================
|
|
434
|
+
// File Extension
|
|
435
|
+
// ============================================================================
|
|
436
|
+
|
|
437
|
+
export function getFileExtension(type) {
|
|
438
|
+
// Rules use .mdc for native Cursor support, everything else uses .md
|
|
439
|
+
if (type === 'rule' || type === 'rules') {
|
|
440
|
+
return '.mdc';
|
|
441
|
+
}
|
|
442
|
+
return '.md';
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// ============================================================================
|
|
446
|
+
// Main Deploy Function
|
|
447
|
+
// ============================================================================
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Main deployment function for Cursor provider
|
|
451
|
+
* Orchestrates deployment of agents, commands, skills, and rules
|
|
452
|
+
*/
|
|
453
|
+
export async function deploy(opts) {
|
|
454
|
+
const {
|
|
455
|
+
srcRoot,
|
|
456
|
+
target,
|
|
457
|
+
mode,
|
|
458
|
+
deployCommands: shouldDeployCommands,
|
|
459
|
+
deploySkills: shouldDeploySkills,
|
|
460
|
+
deployRules: shouldDeployRules,
|
|
461
|
+
commandsOnly,
|
|
462
|
+
skillsOnly,
|
|
463
|
+
rulesOnly,
|
|
464
|
+
dryRun,
|
|
465
|
+
createAgentsMd: shouldCreateAgentsMd
|
|
466
|
+
} = opts;
|
|
467
|
+
|
|
468
|
+
console.log(`\n=== Cursor IDE Provider ===`);
|
|
469
|
+
console.log(`Target: ${target}`);
|
|
470
|
+
console.log(`Mode: ${mode}`);
|
|
471
|
+
|
|
472
|
+
// Collect source files based on mode
|
|
473
|
+
const agentFiles = [];
|
|
474
|
+
const commandFiles = [];
|
|
475
|
+
const skillDirs = [];
|
|
476
|
+
const ruleFiles = [];
|
|
477
|
+
|
|
478
|
+
// Check for addon-style directory structure (direct agents/, commands/, skills/ subdirs)
|
|
479
|
+
// This handles deployment when --source points to an addon directory
|
|
480
|
+
const isAddonSource = fs.existsSync(path.join(srcRoot, 'agents')) ||
|
|
481
|
+
fs.existsSync(path.join(srcRoot, 'commands')) ||
|
|
482
|
+
fs.existsSync(path.join(srcRoot, 'skills'));
|
|
483
|
+
|
|
484
|
+
if (isAddonSource) {
|
|
485
|
+
// Deploy from addon-style directory structure
|
|
486
|
+
const addonAgentsDir = path.join(srcRoot, 'agents');
|
|
487
|
+
if (fs.existsSync(addonAgentsDir)) {
|
|
488
|
+
agentFiles.push(...listMdFiles(addonAgentsDir));
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
if (shouldDeployCommands || commandsOnly) {
|
|
492
|
+
const addonCommandsDir = path.join(srcRoot, 'commands');
|
|
493
|
+
if (fs.existsSync(addonCommandsDir)) {
|
|
494
|
+
commandFiles.push(...listMdFiles(addonCommandsDir));
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
if (shouldDeploySkills || skillsOnly) {
|
|
499
|
+
const addonSkillsDir = path.join(srcRoot, 'skills');
|
|
500
|
+
if (fs.existsSync(addonSkillsDir)) {
|
|
501
|
+
skillDirs.push(...listSkillDirs(addonSkillsDir));
|
|
502
|
+
|
|
503
|
+
// Holistic post-deploy cleanup of stale AIWG-managed kernel
|
|
504
|
+
// skills (renamed/removed sources). Uses the global kernel set
|
|
505
|
+
// (computeAllKernelNames walks all source frameworks/addons),
|
|
506
|
+
// not just this-call's skillDirs, because aiwg use invokes
|
|
507
|
+
// deploy-agents.mjs multiple times.
|
|
508
|
+
{
|
|
509
|
+
const _kernelDestDir = path.isAbsolute(kernelSkillsPath)
|
|
510
|
+
? kernelSkillsPath
|
|
511
|
+
: path.join(target, kernelSkillsPath);
|
|
512
|
+
pruneStaleAiwgSkills(_kernelDestDir, computeAllKernelNames(srcRoot), opts);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
if (shouldDeployRules || rulesOnly) {
|
|
518
|
+
const addonRulesDir = path.join(srcRoot, 'rules');
|
|
519
|
+
if (fs.existsSync(addonRulesDir)) {
|
|
520
|
+
ruleFiles.push(...listMdFiles(addonRulesDir));
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// All addons (dynamically discovered)
|
|
526
|
+
const normalizedMode = normalizeDeploymentMode(mode);
|
|
527
|
+
if (normalizedMode === 'general' || normalizedMode === 'sdlc' || normalizedMode === 'both' || normalizedMode === 'all') {
|
|
528
|
+
agentFiles.push(...getAddonAgentFiles(srcRoot));
|
|
529
|
+
|
|
530
|
+
if (shouldDeployCommands || commandsOnly) {
|
|
531
|
+
commandFiles.push(...getAddonCommandFiles(srcRoot));
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
if (shouldDeploySkills || skillsOnly) {
|
|
535
|
+
skillDirs.push(...getAddonSkillDirs(srcRoot));
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
if (shouldDeployRules || rulesOnly) {
|
|
539
|
+
ruleFiles.push(...getAddonRuleFiles(srcRoot));
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
const frameworkArtifacts = collectFrameworkArtifacts(srcRoot, normalizedMode, {
|
|
544
|
+
includeAgents: true,
|
|
545
|
+
includeCommands: shouldDeployCommands || commandsOnly,
|
|
546
|
+
includeSkills: shouldDeploySkills || skillsOnly,
|
|
547
|
+
includeRules: shouldDeployRules || rulesOnly,
|
|
548
|
+
recursiveCommands: true,
|
|
549
|
+
consolidatedSdlcRules: true
|
|
550
|
+
});
|
|
551
|
+
agentFiles.push(...frameworkArtifacts.agents);
|
|
552
|
+
const soulFiles = [...(frameworkArtifacts.souls || [])];
|
|
553
|
+
commandFiles.push(...frameworkArtifacts.commands);
|
|
554
|
+
skillDirs.push(...frameworkArtifacts.skills);
|
|
555
|
+
ruleFiles.push(...frameworkArtifacts.rules);
|
|
556
|
+
|
|
557
|
+
// Deploy based on flags
|
|
558
|
+
if (!commandsOnly && !skillsOnly && !rulesOnly) {
|
|
559
|
+
// Apply filters if specified
|
|
560
|
+
const filteredAgents = filterAgentFiles(agentFiles, opts);
|
|
561
|
+
if (opts.filter || opts.filterRole) {
|
|
562
|
+
console.log(`\nFiltered from ${agentFiles.length} to ${filteredAgents.length} agents`);
|
|
563
|
+
}
|
|
564
|
+
console.log(`\nDeploying ${filteredAgents.length} agents...`);
|
|
565
|
+
deployAgents(filteredAgents, target, opts);
|
|
566
|
+
|
|
567
|
+
// Deploy soul companion files alongside agents
|
|
568
|
+
if (soulFiles.length > 0) {
|
|
569
|
+
const destDir = path.join(target, paths.agents);
|
|
570
|
+
console.log(`\nDeploying ${soulFiles.length} soul files...`);
|
|
571
|
+
deploySoulCompanions(soulFiles, destDir, opts);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// Filter commands that collide with skills (skills take precedence)
|
|
576
|
+
const filteredCommands = (shouldDeploySkills || skillsOnly)
|
|
577
|
+
? filterCommandsAgainstSkills(commandFiles, skillDirs)
|
|
578
|
+
: commandFiles;
|
|
579
|
+
|
|
580
|
+
if (shouldDeployCommands || commandsOnly) {
|
|
581
|
+
console.log(`\nDeploying ${filteredCommands.length} commands...`);
|
|
582
|
+
deployCommands(filteredCommands, target, opts);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
if (shouldDeploySkills || skillsOnly) {
|
|
586
|
+
console.log(`\nDeploying ${skillDirs.length} skills...`);
|
|
587
|
+
deploySkills(skillDirs, target, opts);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
if (shouldDeployRules || rulesOnly) {
|
|
591
|
+
console.log(`\nDeploying ${ruleFiles.length} rules...`);
|
|
592
|
+
// Use inline deployment (external script relied on commands/ dirs which are now skills)
|
|
593
|
+
deployRulesInline(ruleFiles, target, opts);
|
|
594
|
+
|
|
595
|
+
// On-demand index (#1675): list the MEDIUM/LOW rules tier-gated out of the
|
|
596
|
+
// always-on set so agents can fetch them via `aiwg show rule`.
|
|
597
|
+
const onDemandCount = writeOnDemandRuleIndex(
|
|
598
|
+
path.join(target, paths.rules),
|
|
599
|
+
listOnDemandRuleFiles(srcRoot),
|
|
600
|
+
opts,
|
|
601
|
+
);
|
|
602
|
+
if (onDemandCount > 0) {
|
|
603
|
+
console.log(` On-demand rules (not inlined): ${onDemandCount} → RULES-ONDEMAND.md`);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// Post-deployment
|
|
608
|
+
await postDeploy(target, { ...opts, createAgentsMd: shouldCreateAgentsMd });
|
|
609
|
+
|
|
610
|
+
console.log('\n=== Cursor deployment complete ===\n');
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
// ============================================================================
|
|
614
|
+
// Default Export
|
|
615
|
+
// ============================================================================
|
|
616
|
+
|
|
617
|
+
// ============================================================================
|
|
618
|
+
// Plugin Bundle Generation (Cursor)
|
|
619
|
+
// ============================================================================
|
|
620
|
+
|
|
621
|
+
/**
|
|
622
|
+
* Generate a `.cursor-plugin/plugin.json` manifest for distributing AIWG as a
|
|
623
|
+
* Cursor plugin. Cursor's cursor.com/marketplace is centralized and partner-
|
|
624
|
+
* oriented; this manifest enables manual/local installation and future
|
|
625
|
+
* submission if the marketplace opens third-party submissions.
|
|
626
|
+
*
|
|
627
|
+
* Layout produced:
|
|
628
|
+
* <targetDir>/.cursor-plugin/plugin.json — the plugin manifest
|
|
629
|
+
*
|
|
630
|
+
* @param {string} targetDir - Plugin bundle root (typically agentic/code/plugins/<name>/)
|
|
631
|
+
* @param {{ dryRun?: boolean, srcRoot?: string, name?: string, version?: string, description?: string, contents?: object }} opts
|
|
632
|
+
*/
|
|
633
|
+
export function generatePluginBundle(targetDir, opts = {}) {
|
|
634
|
+
const {
|
|
635
|
+
dryRun = false,
|
|
636
|
+
srcRoot = process.cwd(),
|
|
637
|
+
name: pluginName = 'aiwg-plugin',
|
|
638
|
+
version: overrideVersion,
|
|
639
|
+
description = 'AIWG plugin for Cursor',
|
|
640
|
+
contents = {
|
|
641
|
+
agents: 'agents/',
|
|
642
|
+
commands: 'commands/',
|
|
643
|
+
skills: 'skills/',
|
|
644
|
+
rules: 'rules/',
|
|
645
|
+
},
|
|
646
|
+
} = opts;
|
|
647
|
+
|
|
648
|
+
// Resolve version: opts.version > package.json > 'unknown'
|
|
649
|
+
let version = overrideVersion;
|
|
650
|
+
if (!version) {
|
|
651
|
+
try {
|
|
652
|
+
const pkgPath = path.join(srcRoot, 'package.json');
|
|
653
|
+
if (fs.existsSync(pkgPath)) {
|
|
654
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
655
|
+
version = pkg.version || 'unknown';
|
|
656
|
+
} else {
|
|
657
|
+
version = 'unknown';
|
|
658
|
+
}
|
|
659
|
+
} catch {
|
|
660
|
+
version = 'unknown';
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
const pluginDir = path.join(targetDir, '.cursor-plugin');
|
|
665
|
+
const manifest = {
|
|
666
|
+
name: pluginName,
|
|
667
|
+
version,
|
|
668
|
+
displayName: pluginName.replace(/^aiwg-/, 'AIWG ').replace(/-/g, ' '),
|
|
669
|
+
description,
|
|
670
|
+
publisher: 'aiwg',
|
|
671
|
+
homepage: 'https://aiwg.io',
|
|
672
|
+
repository: 'https://github.com/jmagly/aiwg',
|
|
673
|
+
license: 'MIT',
|
|
674
|
+
contents,
|
|
675
|
+
};
|
|
676
|
+
|
|
677
|
+
if (dryRun) {
|
|
678
|
+
console.log(`[dry-run] Would create ${pluginDir}/plugin.json`);
|
|
679
|
+
return { pluginDir, manifest };
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
if (!fs.existsSync(pluginDir)) {
|
|
683
|
+
fs.mkdirSync(pluginDir, { recursive: true });
|
|
684
|
+
}
|
|
685
|
+
fs.writeFileSync(
|
|
686
|
+
path.join(pluginDir, 'plugin.json'),
|
|
687
|
+
JSON.stringify(manifest, null, 2) + '\n',
|
|
688
|
+
'utf-8'
|
|
689
|
+
);
|
|
690
|
+
|
|
691
|
+
console.log(`Generated Cursor plugin manifest: ${pluginDir}/plugin.json`);
|
|
692
|
+
return { pluginDir, manifest };
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
export default {
|
|
696
|
+
name,
|
|
697
|
+
aliases,
|
|
698
|
+
paths,
|
|
699
|
+
kernelSkillsPath,
|
|
700
|
+
support,
|
|
701
|
+
capabilities,
|
|
702
|
+
transformAgent,
|
|
703
|
+
transformCommand,
|
|
704
|
+
mapModel,
|
|
705
|
+
deployAgents,
|
|
706
|
+
deployCommands,
|
|
707
|
+
deploySkills,
|
|
708
|
+
deployRules,
|
|
709
|
+
createAgentsMd,
|
|
710
|
+
postDeploy,
|
|
711
|
+
getFileExtension,
|
|
712
|
+
generatePluginBundle,
|
|
713
|
+
deploy
|
|
714
|
+
};
|