@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,680 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenClaw Provider
|
|
3
|
+
*
|
|
4
|
+
* OpenClaw uses an MCP-based integration model similar to Hermes, but with
|
|
5
|
+
* native support for all 5 artifact types including behaviors.
|
|
6
|
+
*
|
|
7
|
+
* What this provider deploys (all user-global, home directory):
|
|
8
|
+
* - Agents: ~/.openclaw/agents/ (native agent definitions)
|
|
9
|
+
* - Commands: ~/.openclaw/commands/ (slash commands)
|
|
10
|
+
* - Skills: ~/.openclaw/skills/ (NLP-triggered capabilities)
|
|
11
|
+
* - Rules: ~/.openclaw/rules/ (context-loaded constraints)
|
|
12
|
+
* - Behaviors: ~/.openclaw/behaviors/ (reactive capabilities with hooks + scripts)
|
|
13
|
+
*
|
|
14
|
+
* OpenClaw is the first platform to support behaviors natively.
|
|
15
|
+
* Behaviors are a new AIWG artifact type: reactive capabilities with scripts
|
|
16
|
+
* and event hooks that fire automatically when system events occur.
|
|
17
|
+
*
|
|
18
|
+
* See: docs/openclaw-guide.md
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import realFs from 'fs';
|
|
22
|
+
import { createRequire } from 'module';
|
|
23
|
+
const _require = createRequire(import.meta.url);
|
|
24
|
+
let fs;
|
|
25
|
+
try { const gfs = _require('graceful-fs'); gfs.gracefulify(realFs); fs = realFs; } catch { fs = realFs; }
|
|
26
|
+
import path from 'path';
|
|
27
|
+
import os from 'os';
|
|
28
|
+
import {
|
|
29
|
+
ensureDir,
|
|
30
|
+
listMdFiles,
|
|
31
|
+
listMdFilesRecursive,
|
|
32
|
+
listSkillDirs,
|
|
33
|
+
deploySkillDir,
|
|
34
|
+
deploySkillsWithKernelRouting,
|
|
35
|
+
isKernelSkill,
|
|
36
|
+
pruneStaleAiwgSkills,
|
|
37
|
+
computeAllKernelNames,
|
|
38
|
+
deployFiles,
|
|
39
|
+
getAddonAgentFiles,
|
|
40
|
+
getAddonCommandFiles,
|
|
41
|
+
getAddonSkillDirs,
|
|
42
|
+
getAddonRuleFiles,
|
|
43
|
+
assembleRulesIndex,
|
|
44
|
+
normalizeDeploymentMode,
|
|
45
|
+
collectFrameworkArtifacts,
|
|
46
|
+
listOnDemandRuleFiles,
|
|
47
|
+
writeOnDemandRuleIndex,
|
|
48
|
+
cleanupOldRuleFiles,
|
|
49
|
+
filterAgentFiles,
|
|
50
|
+
filterCommandsAgainstSkills,
|
|
51
|
+
createAgentsMdFromTemplate,
|
|
52
|
+
createManagedMdFromTemplate,
|
|
53
|
+
} from './base.mjs';
|
|
54
|
+
|
|
55
|
+
// ============================================================================
|
|
56
|
+
// Provider Configuration
|
|
57
|
+
// ============================================================================
|
|
58
|
+
|
|
59
|
+
export const name = 'openclaw';
|
|
60
|
+
export const aliases = [];
|
|
61
|
+
|
|
62
|
+
const openclawHome = path.join(os.homedir(), '.openclaw');
|
|
63
|
+
|
|
64
|
+
export const paths = {
|
|
65
|
+
agents: path.join(openclawHome, 'agents'),
|
|
66
|
+
commands: path.join(openclawHome, 'commands'),
|
|
67
|
+
// Skills sequestered under ~/.openclaw/.aiwg/skills/ — index-driven discovery (#1212).
|
|
68
|
+
skills: path.join(openclawHome, '.aiwg', 'skills'),
|
|
69
|
+
rules: path.join(openclawHome, 'rules'),
|
|
70
|
+
behaviors: path.join(openclawHome, 'behaviors'),
|
|
71
|
+
// Per ADR-3 + PUW-008/009: native hook loader path.
|
|
72
|
+
// BEHAVIOR.md → HOOK.md + handler.js translation lands here.
|
|
73
|
+
hooks: path.join(openclawHome, 'hooks'),
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// Kernel skills (always-loaded) deploy to the platform-native dir.
|
|
77
|
+
// Critical for OpenClaw: DEFAULT_MAX_SKILLS_IN_PROMPT = 150, so the
|
|
78
|
+
// kernel set must come in under that floor (~9 today, well under cap).
|
|
79
|
+
export const kernelSkillsPath = path.join(openclawHome, 'skills');
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Resolve OpenClaw's active agent workspace directory — the dir whose `AGENTS.md`
|
|
83
|
+
* OpenClaw injects into every session's system prompt (docs/concepts/context.md,
|
|
84
|
+
* agent-workspace.md). Honors `agents.defaults.workspace` in
|
|
85
|
+
* `~/.openclaw/openclaw.json` and the `OPENCLAW_PROFILE` env var; defaults to
|
|
86
|
+
* `~/.openclaw/workspace`. The deployed agents/skills/rules under `~/.openclaw/`
|
|
87
|
+
* are inert until the Discover-First bridge lands in this workspace AGENTS.md. (#1562)
|
|
88
|
+
*/
|
|
89
|
+
export function resolveOpenClawWorkspace() {
|
|
90
|
+
const profile = process.env.OPENCLAW_PROFILE;
|
|
91
|
+
let workspace = profile && profile !== 'default'
|
|
92
|
+
? path.join(openclawHome, `workspace-${profile}`)
|
|
93
|
+
: path.join(openclawHome, 'workspace');
|
|
94
|
+
try {
|
|
95
|
+
const cfgPath = path.join(openclawHome, 'openclaw.json');
|
|
96
|
+
if (fs.existsSync(cfgPath)) {
|
|
97
|
+
const cfg = JSON.parse(fs.readFileSync(cfgPath, 'utf8'));
|
|
98
|
+
const ws = cfg?.agents?.defaults?.workspace;
|
|
99
|
+
if (typeof ws === 'string' && ws.trim()) {
|
|
100
|
+
workspace = ws.startsWith('~')
|
|
101
|
+
? path.join(os.homedir(), ws.slice(1).replace(/^[/\\]/, ''))
|
|
102
|
+
: ws;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
} catch { /* malformed config — fall back to the default workspace */ }
|
|
106
|
+
return workspace;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export const support = {
|
|
110
|
+
agents: 'native',
|
|
111
|
+
// OpenClaw slash commands are built-in + plugin/skill-registered (e.g. `/prose`);
|
|
112
|
+
// there is NO user `~/.openclaw/commands/*.md` slash-command scan (field-validated:
|
|
113
|
+
// AIWG-deployed command files never appear in `/commands`). AIWG commands surface
|
|
114
|
+
// via the AGENTS.md bridge + `aiwg discover --type command` / `aiwg show command`,
|
|
115
|
+
// exactly like Codex (static enum, ADR-1) and OpenHuman.
|
|
116
|
+
commands: 'aggregated',
|
|
117
|
+
skills: 'native',
|
|
118
|
+
rules: 'native',
|
|
119
|
+
behaviors: 'native',
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
export const capabilities = {
|
|
123
|
+
skills: true,
|
|
124
|
+
rules: true,
|
|
125
|
+
behaviors: true,
|
|
126
|
+
aggregatedOutput: false,
|
|
127
|
+
yamlFormat: false,
|
|
128
|
+
homeDirectoryDeploy: true,
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
// ============================================================================
|
|
132
|
+
// Model Mapping (global inheritance)
|
|
133
|
+
// ============================================================================
|
|
134
|
+
|
|
135
|
+
export function mapModel(shorthand, modelCfg, modelsConfig) {
|
|
136
|
+
return undefined;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// ============================================================================
|
|
140
|
+
// Content Transformation (passthrough — OpenClaw uses AIWG native format)
|
|
141
|
+
// ============================================================================
|
|
142
|
+
|
|
143
|
+
export function transformAgent(srcPath, content, opts) {
|
|
144
|
+
// AIWG's catalog intentionally records configured/<role> placeholders for
|
|
145
|
+
// OpenClaw because the exact provider/model namespace is installation
|
|
146
|
+
// specific. Omit the portable Markdown pin and inherit the configured
|
|
147
|
+
// OpenClaw subagent model rather than emitting a misleading Claude alias.
|
|
148
|
+
return content.replace(/^model:\s*.+\n/m, '');
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function transformCommand(srcPath, content, opts) {
|
|
152
|
+
return content;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// ============================================================================
|
|
156
|
+
// Deployment Functions
|
|
157
|
+
// ============================================================================
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Deploy agents to ~/.openclaw/agents/
|
|
161
|
+
*/
|
|
162
|
+
function deployAgents(agentFiles, opts) {
|
|
163
|
+
ensureDir(paths.agents, opts.dryRun);
|
|
164
|
+
return deployFiles(agentFiles, paths.agents, { ...opts, injectPlatform: true }, transformAgent);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Deploy commands to ~/.openclaw/commands/
|
|
169
|
+
*/
|
|
170
|
+
function deployCommands(commandFiles, opts) {
|
|
171
|
+
ensureDir(paths.commands, opts.dryRun);
|
|
172
|
+
return deployFiles(commandFiles, paths.commands, opts, transformCommand);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Deploy skills with kernel-vs-standard routing (#1212/#1216).
|
|
177
|
+
*
|
|
178
|
+
* OpenClaw is the binding constraint for the kernel pivot:
|
|
179
|
+
* `DEFAULT_MAX_SKILLS_IN_PROMPT = 150` is the hard cap. The kernel set
|
|
180
|
+
* (one quickref per installed framework + core utility set, ~9 today)
|
|
181
|
+
* fits comfortably under that floor regardless of how many frameworks
|
|
182
|
+
* are installed.
|
|
183
|
+
*
|
|
184
|
+
* - kernel skills → ~/.openclaw/skills/aiwg/<name> (platform-native, always-loaded)
|
|
185
|
+
* - standard → ~/.openclaw/.aiwg/skills/<name> (index-discoverable, hidden from flat scan)
|
|
186
|
+
*
|
|
187
|
+
* The legacy `aiwg/` 2-level namespacing under the kernel path (PUW-025
|
|
188
|
+
* #1126) is preserved to avoid collision with non-AIWG ClaWHub installs.
|
|
189
|
+
*/
|
|
190
|
+
function deploySkills(skillDirs, opts) {
|
|
191
|
+
// Standard: ~/.openclaw/.aiwg/skills/ (no extra aiwg/ — the .aiwg/ segment IS the namespace)
|
|
192
|
+
const standardDestDir = paths.skills;
|
|
193
|
+
// Kernel: ~/.openclaw/skills/aiwg/ (preserves legacy 2-level namespacing)
|
|
194
|
+
const kernelDestDir = path.join(kernelSkillsPath, 'aiwg');
|
|
195
|
+
|
|
196
|
+
deploySkillsWithKernelRouting(skillDirs, standardDestDir, kernelDestDir, opts);
|
|
197
|
+
|
|
198
|
+
// PUW-012 (#1113): also deploy to project-local .agents/skills/ as a
|
|
199
|
+
// cross-agent compatibility path. OpenClaw's primary deploy is the
|
|
200
|
+
// home-dir namespaced layout above; this secondary surface lets other
|
|
201
|
+
// tools running on the same project pick up the same skills via the
|
|
202
|
+
// universal `.agents/skills/<name>/SKILL.md` discovery convention.
|
|
203
|
+
//
|
|
204
|
+
// Honors #1217 no-copy default: filter to kernel-only unless the
|
|
205
|
+
// operator opts in via env var so standard skills stay at $AIWG_ROOT
|
|
206
|
+
// and are reached via `aiwg discover`.
|
|
207
|
+
const copyStandardSkills = opts?.copyStandardSkills === true;
|
|
208
|
+
const crossAgentSkills = copyStandardSkills
|
|
209
|
+
? skillDirs
|
|
210
|
+
: skillDirs.filter(d => isKernelSkill(d));
|
|
211
|
+
if (crossAgentSkills.length > 0) {
|
|
212
|
+
const crossAgentDir = path.join(process.cwd(), '.agents', 'skills');
|
|
213
|
+
ensureDir(crossAgentDir, opts.dryRun);
|
|
214
|
+
if (!opts.dryRun) {
|
|
215
|
+
console.log(`Deploying cross-agent skills to ${path.relative(process.cwd(), crossAgentDir)}...`);
|
|
216
|
+
} else {
|
|
217
|
+
console.log(`[dry-run] Would deploy cross-agent skills to .agents/skills/`);
|
|
218
|
+
}
|
|
219
|
+
for (const skillDir of crossAgentSkills) {
|
|
220
|
+
deploySkillDir(skillDir, crossAgentDir, opts);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Deploy rules to ~/.openclaw/rules/
|
|
227
|
+
*/
|
|
228
|
+
function deployRules(ruleFiles, opts) {
|
|
229
|
+
ensureDir(paths.rules, opts.dryRun);
|
|
230
|
+
cleanupOldRuleFiles(paths.rules, opts);
|
|
231
|
+
return deployFiles(ruleFiles, paths.rules, opts, transformCommand);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Deploy behaviors to ~/.openclaw/behaviors/
|
|
236
|
+
*
|
|
237
|
+
* Behaviors are directories containing BEHAVIOR.md and a scripts/ subdirectory.
|
|
238
|
+
* Each behavior directory is copied wholesale to the target.
|
|
239
|
+
*/
|
|
240
|
+
function deployBehaviors(behaviorDirs, opts) {
|
|
241
|
+
ensureDir(paths.behaviors, opts.dryRun);
|
|
242
|
+
|
|
243
|
+
let count = 0;
|
|
244
|
+
for (const behaviorDir of behaviorDirs) {
|
|
245
|
+
const behaviorName = path.basename(behaviorDir);
|
|
246
|
+
const destDir = path.join(paths.behaviors, behaviorName);
|
|
247
|
+
|
|
248
|
+
if (opts.dryRun) {
|
|
249
|
+
console.log(`[dry-run] Would deploy behavior: ${behaviorName} -> ${destDir}`);
|
|
250
|
+
count++;
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
ensureDir(destDir);
|
|
255
|
+
|
|
256
|
+
// Copy BEHAVIOR.md
|
|
257
|
+
const behaviorMd = path.join(behaviorDir, 'BEHAVIOR.md');
|
|
258
|
+
if (fs.existsSync(behaviorMd)) {
|
|
259
|
+
fs.copyFileSync(behaviorMd, path.join(destDir, 'BEHAVIOR.md'));
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Copy scripts/ directory if it exists
|
|
263
|
+
const scriptsDir = path.join(behaviorDir, 'scripts');
|
|
264
|
+
if (fs.existsSync(scriptsDir)) {
|
|
265
|
+
const destScriptsDir = path.join(destDir, 'scripts');
|
|
266
|
+
ensureDir(destScriptsDir);
|
|
267
|
+
|
|
268
|
+
for (const entry of fs.readdirSync(scriptsDir)) {
|
|
269
|
+
const srcFile = path.join(scriptsDir, entry);
|
|
270
|
+
const destFile = path.join(destScriptsDir, entry);
|
|
271
|
+
if (fs.statSync(srcFile).isFile()) {
|
|
272
|
+
fs.copyFileSync(srcFile, destFile);
|
|
273
|
+
// Preserve executable permission for scripts
|
|
274
|
+
try { fs.chmodSync(destFile, 0o755); } catch { /* ignore on platforms without chmod */ }
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
count++;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// PUW-008/PUW-009 (#1109/#1110) per ADR-3: also emit hook artifacts to
|
|
283
|
+
// ~/.openclaw/hooks/<name>/ so OpenClaw's native hook loader picks them
|
|
284
|
+
// up. The behaviors/ writes above keep happening per always-deploy
|
|
285
|
+
// invariant (operator visibility); hooks/ is the discovery bridge.
|
|
286
|
+
try {
|
|
287
|
+
ensureDir(paths.hooks, opts.dryRun);
|
|
288
|
+
if (!opts.dryRun) {
|
|
289
|
+
// Lazy import so this is only loaded when behaviors exist.
|
|
290
|
+
import('./openclaw-translator.mjs').then((mod) => {
|
|
291
|
+
let translated = 0;
|
|
292
|
+
const skipped = [];
|
|
293
|
+
for (const behaviorDir of behaviorDirs) {
|
|
294
|
+
const behaviorName = path.basename(behaviorDir);
|
|
295
|
+
const hookDir = path.join(paths.hooks, behaviorName);
|
|
296
|
+
const r = mod.translateBehaviorToHook(behaviorDir, hookDir, opts);
|
|
297
|
+
if (r.ok) translated++;
|
|
298
|
+
else skipped.push({ name: behaviorName, reason: r.reason });
|
|
299
|
+
}
|
|
300
|
+
if (translated > 0) {
|
|
301
|
+
console.log(`Translated ${translated} behavior(s) to OpenClaw hooks at ${paths.hooks}`);
|
|
302
|
+
}
|
|
303
|
+
if (skipped.length > 0 && opts.verbose) {
|
|
304
|
+
for (const s of skipped) {
|
|
305
|
+
console.log(`Skipped hook translation for ${s.name}: ${s.reason}`);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}).catch((err) => {
|
|
309
|
+
console.warn(`Warning: hook translation failed: ${err.message || err}`);
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
} catch (err) {
|
|
313
|
+
console.warn(`Warning: hook deployment skipped: ${err.message || err}`);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return count;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// ============================================================================
|
|
320
|
+
// Behavior Discovery
|
|
321
|
+
// ============================================================================
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* List behavior directories (directories containing BEHAVIOR.md)
|
|
325
|
+
*/
|
|
326
|
+
function listBehaviorDirs(dir) {
|
|
327
|
+
if (!fs.existsSync(dir)) return [];
|
|
328
|
+
return fs
|
|
329
|
+
.readdirSync(dir, { withFileTypes: true })
|
|
330
|
+
.filter((e) => e.isDirectory() && fs.existsSync(path.join(dir, e.name, 'BEHAVIOR.md')))
|
|
331
|
+
.map((e) => path.join(dir, e.name));
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Collect behavior directories from all sources:
|
|
336
|
+
* - Cross-framework: agentic/code/behaviors/
|
|
337
|
+
* - Per-addon: agentic/code/addons/<name>/behaviors/
|
|
338
|
+
* - Per-framework: agentic/code/frameworks/<name>/behaviors/
|
|
339
|
+
* - Direct component source: <srcRoot>/behaviors/
|
|
340
|
+
*/
|
|
341
|
+
function collectBehaviorDirs(srcRoot, mode) {
|
|
342
|
+
const dirs = [];
|
|
343
|
+
const seen = new Set();
|
|
344
|
+
const addDirs = (items) => {
|
|
345
|
+
for (const dir of items) {
|
|
346
|
+
if (seen.has(dir)) continue;
|
|
347
|
+
seen.add(dir);
|
|
348
|
+
dirs.push(dir);
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
// Direct component source, used by addon-only deploys such as:
|
|
353
|
+
// aiwg use aiwg-fleet --provider openclaw
|
|
354
|
+
addDirs(listBehaviorDirs(path.join(srcRoot, 'behaviors')));
|
|
355
|
+
|
|
356
|
+
// Cross-framework behaviors
|
|
357
|
+
const globalBehaviorsDir = path.join(srcRoot, 'agentic', 'code', 'behaviors');
|
|
358
|
+
addDirs(listBehaviorDirs(globalBehaviorsDir));
|
|
359
|
+
|
|
360
|
+
// Addon behaviors
|
|
361
|
+
const addonsDir = path.join(srcRoot, 'agentic', 'code', 'addons');
|
|
362
|
+
if (fs.existsSync(addonsDir)) {
|
|
363
|
+
for (const entry of fs.readdirSync(addonsDir, { withFileTypes: true })) {
|
|
364
|
+
if (!entry.isDirectory()) continue;
|
|
365
|
+
const addonBehaviorsDir = path.join(addonsDir, entry.name, 'behaviors');
|
|
366
|
+
addDirs(listBehaviorDirs(addonBehaviorsDir));
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Per-framework behaviors
|
|
371
|
+
const frameworksDir = path.join(srcRoot, 'agentic', 'code', 'frameworks');
|
|
372
|
+
if (fs.existsSync(frameworksDir)) {
|
|
373
|
+
for (const entry of fs.readdirSync(frameworksDir, { withFileTypes: true })) {
|
|
374
|
+
if (!entry.isDirectory()) continue;
|
|
375
|
+
const fwBehaviorsDir = path.join(frameworksDir, entry.name, 'behaviors');
|
|
376
|
+
addDirs(listBehaviorDirs(fwBehaviorsDir));
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
return dirs;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// ============================================================================
|
|
384
|
+
// File Extension
|
|
385
|
+
// ============================================================================
|
|
386
|
+
|
|
387
|
+
export function getFileExtension(type) {
|
|
388
|
+
return '.md';
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// ============================================================================
|
|
392
|
+
// Main Deploy Function
|
|
393
|
+
// ============================================================================
|
|
394
|
+
|
|
395
|
+
export async function deploy(opts) {
|
|
396
|
+
const {
|
|
397
|
+
srcRoot,
|
|
398
|
+
mode,
|
|
399
|
+
deployCommands: shouldDeployCommands,
|
|
400
|
+
deploySkills: shouldDeploySkills,
|
|
401
|
+
deployRules: shouldDeployRules,
|
|
402
|
+
commandsOnly,
|
|
403
|
+
skillsOnly,
|
|
404
|
+
rulesOnly,
|
|
405
|
+
dryRun,
|
|
406
|
+
} = opts;
|
|
407
|
+
|
|
408
|
+
const verbose = opts.verbose || false;
|
|
409
|
+
|
|
410
|
+
if (!opts.quiet) {
|
|
411
|
+
console.log(`\n=== OpenClaw Provider ===`);
|
|
412
|
+
console.log(`Deploy target: ${openclawHome}`);
|
|
413
|
+
console.log(`Mode: ${mode}`);
|
|
414
|
+
console.log(`Architecture: OpenClaw -> MCP -> AIWG`);
|
|
415
|
+
console.log('');
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
const normalizedMode = normalizeDeploymentMode(mode);
|
|
419
|
+
|
|
420
|
+
// ── Collect source artifacts ──────────────────────────────────────────────
|
|
421
|
+
const agentFiles = [];
|
|
422
|
+
const commandFiles = [];
|
|
423
|
+
const skillDirs = [];
|
|
424
|
+
const ruleFiles = [];
|
|
425
|
+
|
|
426
|
+
// Addon artifacts
|
|
427
|
+
if (normalizedMode === 'general' || normalizedMode === 'sdlc' || normalizedMode === 'both' || normalizedMode === 'all') {
|
|
428
|
+
agentFiles.push(...getAddonAgentFiles(srcRoot));
|
|
429
|
+
if (shouldDeployCommands || commandsOnly) commandFiles.push(...getAddonCommandFiles(srcRoot));
|
|
430
|
+
if (shouldDeploySkills || skillsOnly) skillDirs.push(...getAddonSkillDirs(srcRoot));
|
|
431
|
+
if (shouldDeployRules || rulesOnly) ruleFiles.push(...getAddonRuleFiles(srcRoot));
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// Framework artifacts
|
|
435
|
+
const frameworkArtifacts = collectFrameworkArtifacts(srcRoot, normalizedMode, {
|
|
436
|
+
includeAgents: true,
|
|
437
|
+
includeCommands: shouldDeployCommands || commandsOnly,
|
|
438
|
+
includeSkills: shouldDeploySkills || skillsOnly,
|
|
439
|
+
includeRules: shouldDeployRules || rulesOnly,
|
|
440
|
+
recursiveCommands: true,
|
|
441
|
+
consolidatedSdlcRules: true,
|
|
442
|
+
});
|
|
443
|
+
agentFiles.push(...frameworkArtifacts.agents);
|
|
444
|
+
commandFiles.push(...frameworkArtifacts.commands);
|
|
445
|
+
skillDirs.push(...frameworkArtifacts.skills);
|
|
446
|
+
ruleFiles.push(...frameworkArtifacts.rules);
|
|
447
|
+
|
|
448
|
+
// Behaviors (OpenClaw-specific — first provider to support this)
|
|
449
|
+
const behaviorDirs = collectBehaviorDirs(srcRoot, normalizedMode);
|
|
450
|
+
|
|
451
|
+
// ── Deploy ────────────────────────────────────────────────────────────────
|
|
452
|
+
const counts = { agents: 0, commands: 0, skills: 0, rules: 0, behaviors: 0 };
|
|
453
|
+
|
|
454
|
+
// Agents
|
|
455
|
+
if (!commandsOnly && !skillsOnly && !rulesOnly) {
|
|
456
|
+
const filteredAgents = filterAgentFiles(agentFiles, opts);
|
|
457
|
+
if (verbose) console.log(`\nDeploying ${filteredAgents.length} agents to ${paths.agents}...`);
|
|
458
|
+
deployAgents(filteredAgents, opts);
|
|
459
|
+
counts.agents = filteredAgents.length;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// Commands (filter collisions with skills)
|
|
463
|
+
const filteredCommands = (shouldDeploySkills || skillsOnly)
|
|
464
|
+
? filterCommandsAgainstSkills(commandFiles, skillDirs)
|
|
465
|
+
: commandFiles;
|
|
466
|
+
|
|
467
|
+
if (shouldDeployCommands || commandsOnly) {
|
|
468
|
+
if (verbose) console.log(`\nDeploying ${filteredCommands.length} commands to ${paths.commands}...`);
|
|
469
|
+
deployCommands(filteredCommands, opts);
|
|
470
|
+
counts.commands = filteredCommands.length;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// Skills
|
|
474
|
+
if (shouldDeploySkills || skillsOnly) {
|
|
475
|
+
if (verbose) console.log(`\nDeploying ${skillDirs.length} skills to ${paths.skills}/aiwg/ (PUW-025 namespaced)...`);
|
|
476
|
+
deploySkills(skillDirs, opts);
|
|
477
|
+
counts.skills = skillDirs.length;
|
|
478
|
+
|
|
479
|
+
// Holistic post-deploy cleanup of stale AIWG-managed kernel
|
|
480
|
+
// skills (renamed/removed sources). Runs once per deploy with the
|
|
481
|
+
// global desired-kernel set so it survives multi-call orchestration
|
|
482
|
+
// (`aiwg use` invokes deploy-agents.mjs once per framework + addon
|
|
483
|
+
// batch).
|
|
484
|
+
{
|
|
485
|
+
const _kernelDestDir = path.isAbsolute(kernelSkillsPath)
|
|
486
|
+
? kernelSkillsPath
|
|
487
|
+
: path.join(target, kernelSkillsPath);
|
|
488
|
+
pruneStaleAiwgSkills(_kernelDestDir, computeAllKernelNames(srcRoot), opts);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// Rules
|
|
493
|
+
if (shouldDeployRules || rulesOnly) {
|
|
494
|
+
const assembled = assembleRulesIndex(srcRoot);
|
|
495
|
+
if (assembled) {
|
|
496
|
+
const { tmpdir } = await import('os');
|
|
497
|
+
const tmpDir = fs.mkdtempSync(path.join(tmpdir(), 'aiwg-rules-assembly-'));
|
|
498
|
+
const assembledPath = path.join(tmpDir, 'RULES-INDEX.md');
|
|
499
|
+
fs.writeFileSync(assembledPath, assembled);
|
|
500
|
+
|
|
501
|
+
const finalRuleFiles = [
|
|
502
|
+
assembledPath,
|
|
503
|
+
...ruleFiles.filter(f => path.basename(f) !== 'RULES-INDEX.md'),
|
|
504
|
+
];
|
|
505
|
+
|
|
506
|
+
if (verbose) console.log(`\nDeploying assembled RULES-INDEX.md + ${finalRuleFiles.length - 1} additional rule files...`);
|
|
507
|
+
deployRules(finalRuleFiles, opts);
|
|
508
|
+
counts.rules = finalRuleFiles.length;
|
|
509
|
+
|
|
510
|
+
try { fs.rmSync(tmpDir, { recursive: true, force: true }); } catch { /* ignore */ }
|
|
511
|
+
} else {
|
|
512
|
+
if (verbose) console.log(`\nDeploying ${ruleFiles.length} rules to ${paths.rules}...`);
|
|
513
|
+
deployRules(ruleFiles, opts);
|
|
514
|
+
counts.rules = ruleFiles.length;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// On-demand index (#1675): list the MEDIUM/LOW rules tier-gated out of the
|
|
518
|
+
// always-on set so agents can fetch them via `aiwg show rule`. OpenClaw's
|
|
519
|
+
// rule dir is the (absolute) home path, so no target join.
|
|
520
|
+
const onDemandCount = writeOnDemandRuleIndex(paths.rules, listOnDemandRuleFiles(srcRoot), opts);
|
|
521
|
+
if (verbose && onDemandCount > 0) {
|
|
522
|
+
console.log(` On-demand rules (not inlined): ${onDemandCount} → RULES-ONDEMAND.md`);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// Behaviors (always deploy if available — OpenClaw is the first native behaviors platform)
|
|
527
|
+
if (!commandsOnly && !skillsOnly && !rulesOnly && behaviorDirs.length > 0) {
|
|
528
|
+
if (verbose) console.log(`\nDeploying ${behaviorDirs.length} behaviors to ${paths.behaviors}...`);
|
|
529
|
+
counts.behaviors = deployBehaviors(behaviorDirs, opts);
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// ── Context bridge (#1562) ──────────────────────────────────────────────
|
|
533
|
+
// Inject the AIWG Discover-First bridge into OpenClaw's session-loaded
|
|
534
|
+
// workspace AGENTS.md. OpenClaw loads <workspace>/AGENTS.md into every
|
|
535
|
+
// session's system prompt; without this, the deployed agents/skills/rules
|
|
536
|
+
// sit inert on disk (no discover-first, no rule routing — the agent treats
|
|
537
|
+
// `address-issues` as a generic query). Preserves operator content outside
|
|
538
|
+
// the AIWG-managed section.
|
|
539
|
+
// Run once, only from the batch whose srcRoot actually contains the framework
|
|
540
|
+
// template (the repo/framework root). `aiwg use` invokes deploy() once per
|
|
541
|
+
// framework AND per addon with different srcRoots; addon batches would warn
|
|
542
|
+
// "template not found". The idempotent skip in createAgentsMdFromTemplate also
|
|
543
|
+
// guards against double-injection across batches.
|
|
544
|
+
if (!commandsOnly && !skillsOnly && !rulesOnly) {
|
|
545
|
+
const templatePath = path.join(
|
|
546
|
+
srcRoot, 'agentic', 'code', 'frameworks', 'sdlc-complete', 'templates', 'openclaw', 'AGENTS.md.aiwg-template',
|
|
547
|
+
);
|
|
548
|
+
if (fs.existsSync(templatePath)) {
|
|
549
|
+
const workspace = resolveOpenClawWorkspace();
|
|
550
|
+
ensureDir(workspace, dryRun);
|
|
551
|
+
if (verbose || !opts.quiet) {
|
|
552
|
+
console.log(`\nBridging AIWG context into ${path.join(workspace, 'AGENTS.md')} + SOUL.md (#1562/#1572)...`);
|
|
553
|
+
}
|
|
554
|
+
// AGENTS.md = methodology/rules; SOUL.md = the AIWG Steward identity (the
|
|
555
|
+
// discover-first reflex baked in as *who the agent is*, which a weak model
|
|
556
|
+
// adopts far more reliably than a rules appendix). Both update in place.
|
|
557
|
+
createAgentsMdFromTemplate(workspace, srcRoot, 'openclaw/AGENTS.md.aiwg-template', dryRun);
|
|
558
|
+
createManagedMdFromTemplate(workspace, 'SOUL.md', srcRoot, 'openclaw/SOUL.md.aiwg-template', dryRun, {
|
|
559
|
+
sectionMarker: '<!-- AIWG Steward Identity -->',
|
|
560
|
+
legacyHeading: '## AIWG Steward',
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
// ── Summary ───────────────────────────────────────────────────────────────
|
|
566
|
+
if (verbose) {
|
|
567
|
+
console.log('\n=== OpenClaw deployment complete ===\n');
|
|
568
|
+
} else {
|
|
569
|
+
const parts = [];
|
|
570
|
+
if (counts.agents > 0) parts.push(`${counts.agents} agents`);
|
|
571
|
+
if (counts.commands > 0) parts.push(`${counts.commands} commands`);
|
|
572
|
+
if (counts.skills > 0) parts.push(`${counts.skills} skills`);
|
|
573
|
+
if (counts.rules > 0) parts.push(`${counts.rules} rules`);
|
|
574
|
+
if (counts.behaviors > 0) parts.push(`${counts.behaviors} behaviors`);
|
|
575
|
+
if (parts.length > 0) {
|
|
576
|
+
console.log(` Deployed: ${parts.join(' ')}`);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// ── Post-deployment hint ──────────────────────────────────────────────────
|
|
581
|
+
if (!opts.quiet) {
|
|
582
|
+
console.log('');
|
|
583
|
+
console.log('All artifacts deployed to ~/.openclaw/ (user-global).');
|
|
584
|
+
console.log('Next: configure ~/.openclaw/config.yaml to connect AIWG MCP server.');
|
|
585
|
+
console.log('See: docs/openclaw-guide.md');
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
// ============================================================================
|
|
590
|
+
// Plugin Bundle Generation (OpenClaw / ClawHub)
|
|
591
|
+
// ============================================================================
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* Generate a `clawhub.json` manifest for the ClawHub registry.
|
|
595
|
+
*
|
|
596
|
+
* ClawHub is OpenClaw's central package registry. AIWG generates the manifest
|
|
597
|
+
* alongside file deployment; publishing to ClawHub is a separate workflow
|
|
598
|
+
* (not yet implemented — publish via ClawHub CLI manually for now).
|
|
599
|
+
*
|
|
600
|
+
* @param {string} targetDir - Where to write clawhub.json
|
|
601
|
+
* @param {{ dryRun?: boolean, srcRoot?: string, name?: string, version?: string, description?: string, tags?: string[] }} opts
|
|
602
|
+
*/
|
|
603
|
+
export function generatePluginBundle(targetDir, opts = {}) {
|
|
604
|
+
const {
|
|
605
|
+
dryRun = false,
|
|
606
|
+
srcRoot = process.cwd(),
|
|
607
|
+
name: pluginName = 'aiwg-plugin',
|
|
608
|
+
version: overrideVersion,
|
|
609
|
+
description = 'AIWG plugin for OpenClaw',
|
|
610
|
+
tags = ['aiwg', 'multi-agent'],
|
|
611
|
+
} = opts;
|
|
612
|
+
|
|
613
|
+
// Resolve version
|
|
614
|
+
let version = overrideVersion;
|
|
615
|
+
if (!version) {
|
|
616
|
+
try {
|
|
617
|
+
const pkgPath = path.join(srcRoot, 'package.json');
|
|
618
|
+
if (fs.existsSync(pkgPath)) {
|
|
619
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
620
|
+
version = pkg.version || 'unknown';
|
|
621
|
+
} else {
|
|
622
|
+
version = 'unknown';
|
|
623
|
+
}
|
|
624
|
+
} catch {
|
|
625
|
+
version = 'unknown';
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
const manifest = {
|
|
630
|
+
name: pluginName,
|
|
631
|
+
version,
|
|
632
|
+
description,
|
|
633
|
+
author: 'AIWG Contributors',
|
|
634
|
+
license: 'MIT',
|
|
635
|
+
homepage: 'https://aiwg.io',
|
|
636
|
+
repository: 'https://github.com/jmagly/aiwg',
|
|
637
|
+
tags,
|
|
638
|
+
contents: {
|
|
639
|
+
agents: '~/.openclaw/agents/',
|
|
640
|
+
commands: '~/.openclaw/commands/',
|
|
641
|
+
skills: '~/.openclaw/skills/',
|
|
642
|
+
rules: '~/.openclaw/rules/',
|
|
643
|
+
behaviors: '~/.openclaw/behaviors/',
|
|
644
|
+
},
|
|
645
|
+
};
|
|
646
|
+
|
|
647
|
+
const manifestPath = path.join(targetDir, 'clawhub.json');
|
|
648
|
+
|
|
649
|
+
if (dryRun) {
|
|
650
|
+
console.log(`[dry-run] Would create ${manifestPath}`);
|
|
651
|
+
return { manifestPath, manifest };
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
if (!fs.existsSync(targetDir)) {
|
|
655
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
656
|
+
}
|
|
657
|
+
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + '\n', 'utf-8');
|
|
658
|
+
|
|
659
|
+
console.log(`Generated OpenClaw ClawHub manifest: ${manifestPath}`);
|
|
660
|
+
return { manifestPath, manifest };
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
// ============================================================================
|
|
664
|
+
// Default Export
|
|
665
|
+
// ============================================================================
|
|
666
|
+
|
|
667
|
+
export default {
|
|
668
|
+
name,
|
|
669
|
+
aliases,
|
|
670
|
+
paths,
|
|
671
|
+
kernelSkillsPath,
|
|
672
|
+
support,
|
|
673
|
+
capabilities,
|
|
674
|
+
transformAgent,
|
|
675
|
+
transformCommand,
|
|
676
|
+
mapModel,
|
|
677
|
+
getFileExtension,
|
|
678
|
+
generatePluginBundle,
|
|
679
|
+
deploy,
|
|
680
|
+
};
|