@aiwg/cli 2026.10.11 → 2026.10.12
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/agentic/code/providers/model-catalog.v1.json +62 -12
- package/dist/src/artifacts/binding-target-identity.js +51 -0
- package/dist/src/artifacts/checksum-manifest.js +12 -0
- package/dist/src/artifacts/index-builder.js +11 -13
- package/dist/src/artifacts/query-engine.js +5 -11
- package/dist/src/cli/handlers/doctor-help.js +32 -0
- package/dist/src/cli/handlers/gates.js +19 -18
- package/dist/src/cli/handlers/models-help.js +7 -1
- package/dist/src/cli/handlers/models.js +3 -3
- package/dist/src/cli/handlers/script-runner.js +8 -5
- package/dist/src/cli/handlers/sessions-intake.js +104 -0
- package/dist/src/cli/handlers/sessions-tail.js +172 -0
- package/dist/src/cli/handlers/sessions.js +90 -17
- package/dist/src/cli/handlers/skill-lint.js +10 -3
- package/dist/src/cli/handlers/steward-format.js +100 -0
- package/dist/src/cli/handlers/steward-models.js +167 -0
- package/dist/src/cli/handlers/steward.js +14 -248
- package/dist/src/cli/handlers/use-bundle-counts.js +61 -0
- package/dist/src/cli/handlers/use.js +6 -24
- package/dist/src/cli/handlers/utilities.js +1 -24
- package/dist/src/config/aiwg-config.js +7 -0
- package/dist/src/config/authorization-cli.js +89 -0
- package/dist/src/config/cli-help.js +1 -0
- package/dist/src/config/cli.js +10 -1
- package/dist/src/decision/context-pruning.js +3 -2
- package/dist/src/decision/ensemble/contract.js +15 -4
- package/dist/src/decision/ensemble-study/artifacts.js +5 -0
- package/dist/src/decision/ensemble-study/score.js +4 -3
- package/dist/src/decision/feature-export.js +3 -9
- package/dist/src/decision/heldout/collector.js +2 -2
- package/dist/src/decision/issue-triage/runtime.js +3 -9
- package/dist/src/decision/qualification/gates.js +2 -1
- package/dist/src/decision/qualification/release.js +2 -3
- package/dist/src/decision/routing/report.js +2 -0
- package/dist/src/decision/sdlc-screening.js +3 -5
- package/dist/src/decision/sensitivity/comparative.js +5 -3
- package/dist/src/gates/driver.js +6 -77
- package/dist/src/gates/evaluate.js +4 -11
- package/dist/src/gates/floors.js +4 -4
- package/dist/src/gates/index.js +3 -3
- package/dist/src/gates/lattice.js +26 -0
- package/dist/src/gates/registry.js +2 -1
- package/dist/src/gates/report.js +19 -7
- package/dist/src/issues/audit-cli.js +120 -0
- package/dist/src/issues/audit-contract.js +64 -0
- package/dist/src/issues/audit-local.js +82 -0
- package/dist/src/issues/audit.js +220 -0
- package/dist/src/lint/rule-activation.js +15 -1
- package/dist/src/mission-protocol/canonical-codecs.js +270 -0
- package/dist/src/mission-protocol/codecs.js +22 -550
- package/dist/src/mission-protocol/health-trace-codecs.js +281 -0
- package/dist/src/mission-protocol/health-trace-types.js +43 -0
- package/dist/src/mission-protocol/types.js +2 -42
- package/dist/src/mission-protocol/worker-contract-json.js +10 -0
- package/dist/src/mission-protocol/worker-review-gate.js +100 -0
- package/dist/src/mission-protocol/worker-review-types.js +2 -0
- package/dist/src/models/catalog-policy.js +280 -0
- package/dist/src/models/index.js +1 -1
- package/dist/src/models/model-catalog.v1.json +62 -12
- package/dist/src/models/policy-resources.js +28 -0
- package/dist/src/models/provider-policy.js +10 -546
- package/dist/src/models/resolver.js +26 -8
- package/dist/src/models/router.js +15 -2
- package/dist/src/models/tiers.js +13 -0
- package/dist/src/models/types.js +0 -6
- package/dist/src/models/worker-ceilings.js +59 -0
- package/dist/src/models/worker-invocation-policy.js +243 -0
- package/dist/src/models/worker-plan.js +16 -20
- package/dist/src/models/worker-review-lineage.js +55 -0
- package/dist/src/models/worker-selection-policy.js +1 -1
- package/dist/src/models/wrapper-route.js +6 -3
- package/dist/src/policy/authorization.js +1 -0
- package/dist/src/policy/hitl-config.js +63 -0
- package/dist/src/policy/hitl-receipt.js +57 -0
- package/dist/src/policy/hitl-types.js +4 -0
- package/dist/src/policy/hitl-validation.js +39 -0
- package/dist/src/policy/hitl.js +117 -0
- package/dist/src/rules/activation.d.mts +1 -1
- package/dist/src/rules/activation.mjs +0 -2
- package/dist/src/rules/gate-coverage.js +51 -0
- package/dist/src/rules/gate-references.js +16 -0
- package/dist/src/rules/rule-frontmatter.schema.json +7 -1
- package/dist/src/serve/worker-command-request.js +21 -4
- package/dist/src/serve/worker-invocation-preparation.js +3 -1
- package/dist/src/serve/worker-invocation.js +21 -10
- package/dist/src/sessions/adapters/deepseek-harness.js +1 -0
- package/dist/src/sessions/adapters/grok-build-native.js +269 -0
- package/dist/src/sessions/adapters/grok-build.js +57 -15
- package/dist/src/sessions/adapters/hermes-state-db.js +252 -0
- package/dist/src/sessions/adapters/hermes.js +42 -3
- package/dist/src/sessions/adapters/warp.js +1 -1
- package/dist/src/sessions/batch-import.js +10 -0
- package/dist/src/sessions/contracts.js +1 -1
- package/dist/src/sessions/discovery-capability.js +38 -0
- package/dist/src/sessions/importer.js +8 -251
- package/dist/src/sessions/index.js +4 -0
- package/dist/src/sessions/intake-dir.js +71 -0
- package/dist/src/sessions/normalization.js +251 -0
- package/dist/src/sessions/tail.js +91 -0
- package/dist/src/sessions/workspace-discovery.js +83 -7
- package/dist/src/skills/agent-skill-sidecar.js +28 -0
- package/dist/src/skills/agent-skills.js +4 -23
- package/dist/src/skills/claude-surface-projection.js +25 -0
- package/dist/src/skills/deployer.js +8 -288
- package/dist/src/skills/deployment-policy.js +114 -0
- package/dist/src/skills/deployment-projection.js +152 -0
- package/dist/src/skills/deployment-types.js +20 -0
- package/dist/src/skills/exporter.js +8 -31
- package/dist/src/skills/provider-extensions.js +41 -0
- package/dist/src/skills/run.js +6 -0
- package/dist/src/skills/types.js +0 -8
- package/dist/src/skills/validator-resources.js +68 -0
- package/dist/src/skills/validator.js +6 -55
- package/dist/src/storage/cli-migrate-options.js +125 -0
- package/dist/src/storage/cli-migrate.js +111 -0
- package/dist/src/storage/cli.js +4 -279
- package/dist/src/storage/migration-adapter.js +79 -0
- package/dist/src/storage/migration-copy.js +108 -0
- package/dist/src/storage/migration-protocol.js +14 -197
- package/dist/src/storage/migration-records.js +186 -0
- package/dist/src/storage/migration-types.js +12 -0
- package/dist/src/tracker/forge-http.js +2 -1
- package/dist/src/tracker/issue-pages.js +110 -0
- package/package.json +1 -1
- package/schemas/mission-v1.schema.json +335 -0
- package/schemas/models/model-catalog.v1.schema.json +19 -1
- package/schemas/models/model-config-compatibility.v1.schema.json +3 -2
- package/schemas/models/model-policy.v1.schema.json +1 -1
- package/tools/agents/providers/provider-paths-staging.mjs +35 -0
- package/tools/agents/providers/rule-binding-targets.mjs +146 -0
- package/tools/agents/providers/rule-delivery.mjs +7 -126
- package/vscode-extension/schemas/aiwg.config.v1.json +177 -0
|
@@ -7,296 +7,15 @@
|
|
|
7
7
|
* @implements #1879
|
|
8
8
|
*/
|
|
9
9
|
import fs from 'node:fs';
|
|
10
|
-
import os from 'node:os';
|
|
11
10
|
import path from 'node:path';
|
|
12
11
|
import { randomUUID } from 'node:crypto';
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import { AGENT_SKILLS_SIDECAR_SCHEMA, AIWG_SKILL_CONTROL_FIELDS, createAgentSkillSidecar, projectStrictAgentSkill, } from './agent-skills.js';
|
|
12
|
+
import { normalizeProviderDefinitionId } from '../providers/provider-definitions.js';
|
|
13
|
+
import { AGENT_SKILLS_SIDECAR_SCHEMA } from './agent-skills.js';
|
|
16
14
|
import { getImportedAgentSkill } from './importer.js';
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
|
|
21
|
-
export const AGENT_SKILL_MANAGED_MARKER = '.aiwg-managed';
|
|
22
|
-
export const AGENT_SKILL_DEPLOYMENT_SIDECAR = '.aiwg-agent-skill.json';
|
|
23
|
-
const MARKER_CONTENT = 'aiwg-agent-skill-v1\n';
|
|
24
|
-
const STANDARD_FIELDS = [
|
|
25
|
-
'name',
|
|
26
|
-
'description',
|
|
27
|
-
'license',
|
|
28
|
-
'compatibility',
|
|
29
|
-
'metadata',
|
|
30
|
-
'allowed-tools',
|
|
31
|
-
];
|
|
32
|
-
const AIWG_FIELDS = new Set(AIWG_SKILL_CONTROL_FIELDS);
|
|
33
|
-
export class AgentSkillDeploymentError extends Error {
|
|
34
|
-
code;
|
|
35
|
-
constructor(code, message) {
|
|
36
|
-
super(message);
|
|
37
|
-
this.code = code;
|
|
38
|
-
this.name = 'AgentSkillDeploymentError';
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
function fail(code, message) {
|
|
42
|
-
throw new AgentSkillDeploymentError(code, message);
|
|
43
|
-
}
|
|
44
|
-
function assertPortableName(name) {
|
|
45
|
-
if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(name) || name.length > 64) {
|
|
46
|
-
fail('AS_DEPLOY_NAME', 'Agent Skill name must be 1-64 lowercase ASCII letters, numbers, or single hyphens');
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
function resolvedHome(options) {
|
|
50
|
-
return path.resolve(options.homeDir ?? os.homedir());
|
|
51
|
-
}
|
|
52
|
-
function resolvePolicy(target, options) {
|
|
53
|
-
const provider = normalizeProviderDefinitionId(target);
|
|
54
|
-
if (!provider) {
|
|
55
|
-
fail('AS_DEPLOY_PROVIDER', `unknown Agent Skills target "${target}"`);
|
|
56
|
-
}
|
|
57
|
-
const definition = getProviderDefinition(provider);
|
|
58
|
-
if (!definition) {
|
|
59
|
-
fail('AS_DEPLOY_PROVIDER', `provider definition is unavailable for "${provider}"`);
|
|
60
|
-
}
|
|
61
|
-
const namespace = definition.skillNamespace;
|
|
62
|
-
const reasons = [];
|
|
63
|
-
const warnings = [];
|
|
64
|
-
let root = namespace.pathType === 'home-dir'
|
|
65
|
-
? path.join(resolvedHome(options), namespace.skillsBaseDir)
|
|
66
|
-
: path.join(path.resolve(options.projectDir), namespace.skillsBaseDir);
|
|
67
|
-
let status = 'native';
|
|
68
|
-
let supported = true;
|
|
69
|
-
let appendToDescription;
|
|
70
|
-
let maxDescriptionLength;
|
|
71
|
-
switch (provider) {
|
|
72
|
-
case 'codex':
|
|
73
|
-
root = path.join(path.resolve(options.projectDir), '.agents', 'skills');
|
|
74
|
-
status = 'projected';
|
|
75
|
-
maxDescriptionLength = namespace.maxDescriptionLength;
|
|
76
|
-
reasons.push('uses the project .agents/skills compatibility surface without the legacy 100/500 truncation transform');
|
|
77
|
-
break;
|
|
78
|
-
case 'factory':
|
|
79
|
-
status = 'projected';
|
|
80
|
-
appendToDescription = namespace.appendToDescription;
|
|
81
|
-
reasons.push('applies the Factory description guidance before strict validation');
|
|
82
|
-
break;
|
|
83
|
-
case 'grokbot': {
|
|
84
|
-
// Fail closed: never invent ~/grokbot-skills or ~/.grokbot.
|
|
85
|
-
const configured = resolveGrokbotSkillsDir(process.env);
|
|
86
|
-
if (!configured) {
|
|
87
|
-
root = '';
|
|
88
|
-
status = 'unsupported';
|
|
89
|
-
supported = false;
|
|
90
|
-
reasons.push(grokbotMissingRootRemediation(process.env));
|
|
91
|
-
warnings.push(`Agent Skills deploy for grokbot requires absolute ${GROKBOT_SKILLS_DIR_ENV}; no filesystem root was invented`);
|
|
92
|
-
}
|
|
93
|
-
else {
|
|
94
|
-
root = configured;
|
|
95
|
-
reasons.push(`uses the operator-configured ${GROKBOT_SKILLS_DIR_ENV} skills root with strict managed ownership markers`);
|
|
96
|
-
}
|
|
97
|
-
break;
|
|
98
|
-
}
|
|
99
|
-
case 'hermes':
|
|
100
|
-
if (options.homeDir === undefined) {
|
|
101
|
-
root = resolveHermesHomePath('skills');
|
|
102
|
-
}
|
|
103
|
-
reasons.push('uses the active HERMES_HOME skills surface with strict managed ownership markers');
|
|
104
|
-
break;
|
|
105
|
-
case 'muse': {
|
|
106
|
-
// ADR scope rule 1: the default projection is the project
|
|
107
|
-
// <repo>/.agents/skills surface shared with antigravity, codex, and
|
|
108
|
-
// deepseek-harness. Muse reads both that root and its XDG user root,
|
|
109
|
-
// so writing the user root by default would list a skill twice.
|
|
110
|
-
// #234: an explicit user scope resolves the documented XDG root at
|
|
111
|
-
// deploy time — never ~/.muse — and bad XDG metadata fails closed.
|
|
112
|
-
if (options.scope === 'user') {
|
|
113
|
-
const home = resolvedHome(options);
|
|
114
|
-
const configured = resolveMuseXdgSkillsDir(process.env, home);
|
|
115
|
-
if (!configured) {
|
|
116
|
-
root = '';
|
|
117
|
-
status = 'unsupported';
|
|
118
|
-
supported = false;
|
|
119
|
-
reasons.push(museXdgSkillsDirRemediation(process.env, home));
|
|
120
|
-
warnings.push('Agent Skills deploy for muse requires a valid absolute XDG_CONFIG_HOME; no filesystem root was invented');
|
|
121
|
-
}
|
|
122
|
-
else {
|
|
123
|
-
root = configured;
|
|
124
|
-
reasons.push('uses the Muse XDG user skills root ($XDG_CONFIG_HOME/muse/skills, default ~/.config/muse/skills) with strict managed ownership markers');
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
else {
|
|
128
|
-
reasons.push('uses the project .agents/skills surface shared with antigravity, codex, and deepseek-harness');
|
|
129
|
-
}
|
|
130
|
-
break;
|
|
131
|
-
}
|
|
132
|
-
case 'openhuman':
|
|
133
|
-
status = 'projected';
|
|
134
|
-
reasons.push('uses the verified global one-level ~/.openhuman/skills layout');
|
|
135
|
-
break;
|
|
136
|
-
case 'windsurf':
|
|
137
|
-
status = 'projected';
|
|
138
|
-
reasons.push('uses one skill bundle directly below the one-level .windsurf/skills surface');
|
|
139
|
-
break;
|
|
140
|
-
default:
|
|
141
|
-
reasons.push('provider exposes a native recursive Agent Skills bundle surface');
|
|
142
|
-
}
|
|
143
|
-
return {
|
|
144
|
-
provider,
|
|
145
|
-
root,
|
|
146
|
-
status,
|
|
147
|
-
supported,
|
|
148
|
-
reasons,
|
|
149
|
-
warnings,
|
|
150
|
-
appendToDescription,
|
|
151
|
-
maxDescriptionLength,
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
|
-
function assertSafeSourceEntry(sourceRoot, absolutePath, relativePath) {
|
|
155
|
-
const stat = fs.lstatSync(absolutePath);
|
|
156
|
-
if (stat.isSymbolicLink()) {
|
|
157
|
-
fail('AS_DEPLOY_SOURCE_SYMLINK', `managed source contains a symlink: ${relativePath}`);
|
|
158
|
-
}
|
|
159
|
-
if (!stat.isDirectory() && !stat.isFile()) {
|
|
160
|
-
fail('AS_DEPLOY_SOURCE_SPECIAL_FILE', `managed source contains a non-regular entry: ${relativePath}`);
|
|
161
|
-
}
|
|
162
|
-
const real = fs.realpathSync.native(absolutePath);
|
|
163
|
-
const relativeReal = path.relative(fs.realpathSync.native(sourceRoot), real);
|
|
164
|
-
if (relativeReal.startsWith('..') || path.isAbsolute(relativeReal)) {
|
|
165
|
-
fail('AS_DEPLOY_SOURCE_ESCAPE', `managed source entry escapes its root: ${relativePath}`);
|
|
166
|
-
}
|
|
167
|
-
return stat;
|
|
168
|
-
}
|
|
169
|
-
function collectSourceEntries(sourceRoot) {
|
|
170
|
-
const entries = [];
|
|
171
|
-
const walk = (current, prefix) => {
|
|
172
|
-
for (const entry of fs.readdirSync(current, { withFileTypes: true })
|
|
173
|
-
.sort((left, right) => left.name.localeCompare(right.name))) {
|
|
174
|
-
const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
175
|
-
if (relativePath === AGENT_SKILL_MANAGED_MARKER
|
|
176
|
-
|| relativePath === AGENT_SKILL_DEPLOYMENT_SIDECAR) {
|
|
177
|
-
fail('AS_DEPLOY_RESERVED_PATH', `managed source uses reserved deployment path "${relativePath}"`);
|
|
178
|
-
}
|
|
179
|
-
const absolutePath = path.join(current, entry.name);
|
|
180
|
-
const stat = assertSafeSourceEntry(sourceRoot, absolutePath, relativePath);
|
|
181
|
-
if (stat.isDirectory()) {
|
|
182
|
-
entries.push({ kind: 'directory', relativePath });
|
|
183
|
-
walk(absolutePath, relativePath);
|
|
184
|
-
}
|
|
185
|
-
else {
|
|
186
|
-
entries.push({
|
|
187
|
-
kind: 'file',
|
|
188
|
-
relativePath,
|
|
189
|
-
bytes: fs.readFileSync(absolutePath),
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
};
|
|
194
|
-
walk(sourceRoot, '');
|
|
195
|
-
return entries;
|
|
196
|
-
}
|
|
197
|
-
function projectionDocument(record) {
|
|
198
|
-
const sourceEntries = collectSourceEntries(record.managedLocation);
|
|
199
|
-
const skillFile = sourceEntries.find((entry) => (entry.kind === 'file' && entry.relativePath === 'SKILL.md'));
|
|
200
|
-
if (!skillFile) {
|
|
201
|
-
fail('AS_DEPLOY_SKILL_MISSING', 'managed source does not contain SKILL.md');
|
|
202
|
-
}
|
|
203
|
-
const content = new TextDecoder('utf-8', { fatal: true }).decode(skillFile.bytes);
|
|
204
|
-
const validation = validateAgentSkillContent(content, {
|
|
205
|
-
profile: record.validationProfile,
|
|
206
|
-
file: path.join(record.managedLocation, 'SKILL.md'),
|
|
207
|
-
directoryName: record.name,
|
|
208
|
-
skillRoot: record.managedLocation,
|
|
209
|
-
checkResources: true,
|
|
210
|
-
});
|
|
211
|
-
if (!validation.valid || !validation.frontmatter) {
|
|
212
|
-
fail('AS_DEPLOY_SOURCE_INVALID', `managed source for "${record.name}" no longer passes ${record.validationProfile} validation`);
|
|
213
|
-
}
|
|
214
|
-
const standard = {};
|
|
215
|
-
for (const key of STANDARD_FIELDS) {
|
|
216
|
-
const value = validation.frontmatter[key];
|
|
217
|
-
if (value !== undefined) {
|
|
218
|
-
Object.assign(standard, { [key]: structuredClone(value) });
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
const aiwg = {};
|
|
222
|
-
for (const [key, value] of Object.entries(validation.frontmatter)) {
|
|
223
|
-
if (AIWG_FIELDS.has(key)) {
|
|
224
|
-
aiwg[key] = structuredClone(value);
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
return {
|
|
228
|
-
document: {
|
|
229
|
-
standard: standard,
|
|
230
|
-
body: validation.body,
|
|
231
|
-
resources: [],
|
|
232
|
-
aiwg,
|
|
233
|
-
unknownFields: [],
|
|
234
|
-
},
|
|
235
|
-
sourceEntries,
|
|
236
|
-
};
|
|
237
|
-
}
|
|
238
|
-
function strictSkillBytes(record, policy, document) {
|
|
239
|
-
const metadata = projectStrictAgentSkill(document);
|
|
240
|
-
if (policy.appendToDescription) {
|
|
241
|
-
const suffix = policy.appendToDescription;
|
|
242
|
-
if (!metadata.description.endsWith(suffix)) {
|
|
243
|
-
metadata.description = `${metadata.description.trimEnd()} ${suffix}`;
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
if (policy.maxDescriptionLength !== undefined
|
|
247
|
-
&& metadata.description.length > policy.maxDescriptionLength) {
|
|
248
|
-
policy.status = 'degraded';
|
|
249
|
-
policy.supported = false;
|
|
250
|
-
policy.reasons.push(`description length ${metadata.description.length} exceeds the provider limit ${policy.maxDescriptionLength}; no truncation was applied`);
|
|
251
|
-
return undefined;
|
|
252
|
-
}
|
|
253
|
-
const frontmatter = stringify(metadata, {
|
|
254
|
-
lineWidth: 0,
|
|
255
|
-
sortMapEntries: false,
|
|
256
|
-
}).trimEnd();
|
|
257
|
-
const content = `---\n${frontmatter}\n---\n${document.body}`;
|
|
258
|
-
const validation = validateAgentSkillContent(content, {
|
|
259
|
-
profile: 'strict',
|
|
260
|
-
file: 'SKILL.md',
|
|
261
|
-
directoryName: record.name,
|
|
262
|
-
});
|
|
263
|
-
if (!validation.valid) {
|
|
264
|
-
policy.status = 'degraded';
|
|
265
|
-
policy.supported = false;
|
|
266
|
-
policy.reasons.push(`provider projection failed strict validation: ${validation.diagnostics
|
|
267
|
-
.filter((item) => item.severity === 'error')
|
|
268
|
-
.map((item) => item.code)
|
|
269
|
-
.join(', ')}`);
|
|
270
|
-
return undefined;
|
|
271
|
-
}
|
|
272
|
-
return Buffer.from(content, 'utf8');
|
|
273
|
-
}
|
|
274
|
-
function sidecarBytes(record, policy, document) {
|
|
275
|
-
const portableSidecar = createAgentSkillSidecar(document, {
|
|
276
|
-
sourceKind: record.source.kind,
|
|
277
|
-
locator: record.source.locator,
|
|
278
|
-
...(record.source.kind === 'git'
|
|
279
|
-
? {
|
|
280
|
-
requestedRevision: record.source.requestedRevision,
|
|
281
|
-
resolvedRevision: record.source.resolvedRevision,
|
|
282
|
-
}
|
|
283
|
-
: {}),
|
|
284
|
-
sourceDigest: record.digest,
|
|
285
|
-
importedAt: record.importedAt,
|
|
286
|
-
aiwgVersion: record.aiwgVersion,
|
|
287
|
-
}, record.validationProfile, record.trust);
|
|
288
|
-
return Buffer.from(`${JSON.stringify({
|
|
289
|
-
schemaVersion: 1,
|
|
290
|
-
kind: 'aiwg-managed-agent-skill-projection',
|
|
291
|
-
name: record.name,
|
|
292
|
-
provider: policy.provider,
|
|
293
|
-
projectionStatus: policy.status,
|
|
294
|
-
sourceDigest: record.digest,
|
|
295
|
-
reasons: policy.reasons,
|
|
296
|
-
warnings: policy.warnings,
|
|
297
|
-
portable: portableSidecar,
|
|
298
|
-
}, null, 2)}\n`, 'utf8');
|
|
299
|
-
}
|
|
15
|
+
import { resolvePolicy } from './deployment-policy.js';
|
|
16
|
+
import { projectionDocument, providerSkillBytes, sidecarBytes } from './deployment-projection.js';
|
|
17
|
+
import { AGENT_SKILL_MANAGED_MARKER, AGENT_SKILL_DEPLOYMENT_SIDECAR, MARKER_CONTENT, assertPortableName, fail, } from './deployment-types.js';
|
|
18
|
+
export { AGENT_SKILL_MANAGED_MARKER, AGENT_SKILL_DEPLOYMENT_SIDECAR, AgentSkillDeploymentError, } from './deployment-types.js';
|
|
300
19
|
function readDeploymentSidecar(targetPath, expectedName, expectedProvider) {
|
|
301
20
|
try {
|
|
302
21
|
const sidecarPath = path.join(targetPath, AGENT_SKILL_DEPLOYMENT_SIDECAR);
|
|
@@ -356,7 +75,8 @@ function buildProjectionPlan(record, options) {
|
|
|
356
75
|
};
|
|
357
76
|
}
|
|
358
77
|
const { document, sourceEntries } = projectionDocument(record);
|
|
359
|
-
const
|
|
78
|
+
const original = sourceEntries.find((entry) => (entry.kind === 'file' && entry.relativePath === 'SKILL.md'));
|
|
79
|
+
const skillBytes = providerSkillBytes(record, policy, document, original?.bytes);
|
|
360
80
|
if (!skillBytes) {
|
|
361
81
|
return {
|
|
362
82
|
policy,
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
// Resolve existing provider skill roots and projection limits.
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { getProviderDefinition, normalizeProviderDefinitionId } from '../providers/provider-definitions.js';
|
|
5
|
+
import { resolveHermesHomePath } from '../providers/hermes-home.js';
|
|
6
|
+
import { museXdgSkillsDirRemediation, resolveMuseXdgSkillsDir } from '../providers/muse-paths.js';
|
|
7
|
+
import { GROKBOT_SKILLS_DIR_ENV, grokbotMissingRootRemediation, resolveGrokbotSkillsDir } from '../providers/grokbot-paths.js';
|
|
8
|
+
import { fail } from './deployment-types.js';
|
|
9
|
+
function resolvedHome(options) {
|
|
10
|
+
return path.resolve(options.homeDir ?? os.homedir());
|
|
11
|
+
}
|
|
12
|
+
export function resolvePolicy(target, options) {
|
|
13
|
+
const provider = normalizeProviderDefinitionId(target);
|
|
14
|
+
if (!provider) {
|
|
15
|
+
fail('AS_DEPLOY_PROVIDER', `unknown Agent Skills target "${target}"`);
|
|
16
|
+
}
|
|
17
|
+
const definition = getProviderDefinition(provider);
|
|
18
|
+
if (!definition) {
|
|
19
|
+
fail('AS_DEPLOY_PROVIDER', `provider definition is unavailable for "${provider}"`);
|
|
20
|
+
}
|
|
21
|
+
const namespace = definition.skillNamespace;
|
|
22
|
+
const reasons = [];
|
|
23
|
+
const warnings = [];
|
|
24
|
+
let root = namespace.pathType === 'home-dir'
|
|
25
|
+
? path.join(resolvedHome(options), namespace.skillsBaseDir)
|
|
26
|
+
: path.join(path.resolve(options.projectDir), namespace.skillsBaseDir);
|
|
27
|
+
let status = 'native';
|
|
28
|
+
let supported = true;
|
|
29
|
+
let appendToDescription;
|
|
30
|
+
let maxDescriptionLength;
|
|
31
|
+
switch (provider) {
|
|
32
|
+
case 'codex':
|
|
33
|
+
root = path.join(path.resolve(options.projectDir), '.agents', 'skills');
|
|
34
|
+
status = 'projected';
|
|
35
|
+
maxDescriptionLength = namespace.maxDescriptionLength;
|
|
36
|
+
reasons.push('uses the project .agents/skills compatibility surface without the legacy 100/500 truncation transform');
|
|
37
|
+
break;
|
|
38
|
+
case 'factory':
|
|
39
|
+
status = 'projected';
|
|
40
|
+
appendToDescription = namespace.appendToDescription;
|
|
41
|
+
reasons.push('applies the Factory description guidance before strict validation');
|
|
42
|
+
break;
|
|
43
|
+
case 'grokbot': {
|
|
44
|
+
// Fail closed: never invent ~/grokbot-skills or ~/.grokbot.
|
|
45
|
+
const configured = resolveGrokbotSkillsDir(process.env);
|
|
46
|
+
if (!configured) {
|
|
47
|
+
root = '';
|
|
48
|
+
status = 'unsupported';
|
|
49
|
+
supported = false;
|
|
50
|
+
reasons.push(grokbotMissingRootRemediation(process.env));
|
|
51
|
+
warnings.push(`Agent Skills deploy for grokbot requires absolute ${GROKBOT_SKILLS_DIR_ENV}; no filesystem root was invented`);
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
root = configured;
|
|
55
|
+
reasons.push(`uses the operator-configured ${GROKBOT_SKILLS_DIR_ENV} skills root with strict managed ownership markers`);
|
|
56
|
+
}
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
case 'hermes':
|
|
60
|
+
if (options.homeDir === undefined) {
|
|
61
|
+
root = resolveHermesHomePath('skills');
|
|
62
|
+
}
|
|
63
|
+
reasons.push('uses the active HERMES_HOME skills surface with strict managed ownership markers');
|
|
64
|
+
break;
|
|
65
|
+
case 'muse': {
|
|
66
|
+
// ADR scope rule 1: the default projection is the project
|
|
67
|
+
// <repo>/.agents/skills surface shared with antigravity, codex, and
|
|
68
|
+
// deepseek-harness. Muse reads both that root and its XDG user root,
|
|
69
|
+
// so writing the user root by default would list a skill twice.
|
|
70
|
+
// #234: an explicit user scope resolves the documented XDG root at
|
|
71
|
+
// deploy time — never ~/.muse — and bad XDG metadata fails closed.
|
|
72
|
+
if (options.scope === 'user') {
|
|
73
|
+
const home = resolvedHome(options);
|
|
74
|
+
const configured = resolveMuseXdgSkillsDir(process.env, home);
|
|
75
|
+
if (!configured) {
|
|
76
|
+
root = '';
|
|
77
|
+
status = 'unsupported';
|
|
78
|
+
supported = false;
|
|
79
|
+
reasons.push(museXdgSkillsDirRemediation(process.env, home));
|
|
80
|
+
warnings.push('Agent Skills deploy for muse requires a valid absolute XDG_CONFIG_HOME; no filesystem root was invented');
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
root = configured;
|
|
84
|
+
reasons.push('uses the Muse XDG user skills root ($XDG_CONFIG_HOME/muse/skills, default ~/.config/muse/skills) with strict managed ownership markers');
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
reasons.push('uses the project .agents/skills surface shared with antigravity, codex, and deepseek-harness');
|
|
89
|
+
}
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
case 'openhuman':
|
|
93
|
+
status = 'projected';
|
|
94
|
+
reasons.push('uses the verified global one-level ~/.openhuman/skills layout');
|
|
95
|
+
break;
|
|
96
|
+
case 'windsurf':
|
|
97
|
+
status = 'projected';
|
|
98
|
+
reasons.push('uses one skill bundle directly below the one-level .windsurf/skills surface');
|
|
99
|
+
break;
|
|
100
|
+
default:
|
|
101
|
+
reasons.push('provider exposes a native recursive Agent Skills bundle surface');
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
provider,
|
|
105
|
+
root,
|
|
106
|
+
status,
|
|
107
|
+
supported,
|
|
108
|
+
reasons,
|
|
109
|
+
warnings,
|
|
110
|
+
appendToDescription,
|
|
111
|
+
maxDescriptionLength,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=deployment-policy.js.map
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
// Project immutable managed sources into provider frontmatter and sidecars.
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { stringify } from 'yaml';
|
|
5
|
+
import { createAgentSkillSidecar, projectStrictAgentSkill } from './agent-skills.js';
|
|
6
|
+
import { agentSkillDocument, claudeSkillExtensions } from './provider-extensions.js';
|
|
7
|
+
import { validateAgentSkillContent } from './validator.js';
|
|
8
|
+
import { AGENT_SKILL_MANAGED_MARKER, AGENT_SKILL_DEPLOYMENT_SIDECAR, fail, } from './deployment-types.js';
|
|
9
|
+
function assertSafeSourceEntry(sourceRoot, absolutePath, relativePath) {
|
|
10
|
+
const stat = fs.lstatSync(absolutePath);
|
|
11
|
+
if (stat.isSymbolicLink()) {
|
|
12
|
+
fail('AS_DEPLOY_SOURCE_SYMLINK', `managed source contains a symlink: ${relativePath}`);
|
|
13
|
+
}
|
|
14
|
+
if (!stat.isDirectory() && !stat.isFile()) {
|
|
15
|
+
fail('AS_DEPLOY_SOURCE_SPECIAL_FILE', `managed source contains a non-regular entry: ${relativePath}`);
|
|
16
|
+
}
|
|
17
|
+
const real = fs.realpathSync.native(absolutePath);
|
|
18
|
+
const relativeReal = path.relative(fs.realpathSync.native(sourceRoot), real);
|
|
19
|
+
if (relativeReal.startsWith('..') || path.isAbsolute(relativeReal)) {
|
|
20
|
+
fail('AS_DEPLOY_SOURCE_ESCAPE', `managed source entry escapes its root: ${relativePath}`);
|
|
21
|
+
}
|
|
22
|
+
return stat;
|
|
23
|
+
}
|
|
24
|
+
function collectSourceEntries(sourceRoot) {
|
|
25
|
+
const entries = [];
|
|
26
|
+
const walk = (current, prefix) => {
|
|
27
|
+
for (const entry of fs.readdirSync(current, { withFileTypes: true })
|
|
28
|
+
.sort((left, right) => left.name.localeCompare(right.name))) {
|
|
29
|
+
const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
30
|
+
if (relativePath === AGENT_SKILL_MANAGED_MARKER
|
|
31
|
+
|| relativePath === AGENT_SKILL_DEPLOYMENT_SIDECAR) {
|
|
32
|
+
fail('AS_DEPLOY_RESERVED_PATH', `managed source uses reserved deployment path "${relativePath}"`);
|
|
33
|
+
}
|
|
34
|
+
const absolutePath = path.join(current, entry.name);
|
|
35
|
+
const stat = assertSafeSourceEntry(sourceRoot, absolutePath, relativePath);
|
|
36
|
+
if (stat.isDirectory()) {
|
|
37
|
+
entries.push({ kind: 'directory', relativePath });
|
|
38
|
+
walk(absolutePath, relativePath);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
entries.push({
|
|
42
|
+
kind: 'file',
|
|
43
|
+
relativePath,
|
|
44
|
+
bytes: fs.readFileSync(absolutePath),
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
walk(sourceRoot, '');
|
|
50
|
+
return entries;
|
|
51
|
+
}
|
|
52
|
+
export function projectionDocument(record) {
|
|
53
|
+
const sourceEntries = collectSourceEntries(record.managedLocation);
|
|
54
|
+
const skillFile = sourceEntries.find((entry) => (entry.kind === 'file' && entry.relativePath === 'SKILL.md'));
|
|
55
|
+
if (!skillFile) {
|
|
56
|
+
fail('AS_DEPLOY_SKILL_MISSING', 'managed source does not contain SKILL.md');
|
|
57
|
+
}
|
|
58
|
+
const content = new TextDecoder('utf-8', { fatal: true }).decode(skillFile.bytes);
|
|
59
|
+
const validation = validateAgentSkillContent(content, {
|
|
60
|
+
profile: record.validationProfile,
|
|
61
|
+
file: path.join(record.managedLocation, 'SKILL.md'),
|
|
62
|
+
directoryName: record.name,
|
|
63
|
+
skillRoot: record.managedLocation,
|
|
64
|
+
checkResources: true,
|
|
65
|
+
});
|
|
66
|
+
if (!validation.valid || !validation.frontmatter) {
|
|
67
|
+
fail('AS_DEPLOY_SOURCE_INVALID', `managed source for "${record.name}" no longer passes ${record.validationProfile} validation`);
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
document: agentSkillDocument(validation.frontmatter, validation.body),
|
|
71
|
+
sourceEntries,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
export function providerSkillBytes(record, policy, document, originalBytes) {
|
|
75
|
+
const metadata = { ...projectStrictAgentSkill(document) };
|
|
76
|
+
const extensions = claudeSkillExtensions(document);
|
|
77
|
+
if (policy.provider === 'claude') {
|
|
78
|
+
Object.assign(metadata, extensions);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
for (const field of Object.keys(extensions).sort()) {
|
|
82
|
+
const warning = `Claude skill field "${field}" is dropped for provider "${policy.provider}"; retained in providers.claude sidecar only`;
|
|
83
|
+
policy.warnings.push(warning);
|
|
84
|
+
policy.reasons.push(warning);
|
|
85
|
+
policy.status = 'degraded';
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (policy.appendToDescription) {
|
|
89
|
+
const suffix = policy.appendToDescription;
|
|
90
|
+
if (!metadata.description.endsWith(suffix)) {
|
|
91
|
+
metadata.description = `${metadata.description.trimEnd()} ${suffix}`;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (policy.maxDescriptionLength !== undefined
|
|
95
|
+
&& metadata.description.length > policy.maxDescriptionLength) {
|
|
96
|
+
policy.status = 'degraded';
|
|
97
|
+
policy.supported = false;
|
|
98
|
+
policy.reasons.push(`description length ${metadata.description.length} exceeds the provider limit ${policy.maxDescriptionLength}; no truncation was applied`);
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
const frontmatter = stringify(metadata, {
|
|
102
|
+
lineWidth: 0,
|
|
103
|
+
sortMapEntries: false,
|
|
104
|
+
}).trimEnd();
|
|
105
|
+
const content = `---\n${frontmatter}\n---\n${document.body}`;
|
|
106
|
+
const validation = validateAgentSkillContent(content, {
|
|
107
|
+
profile: policy.provider === 'claude' ? 'compatible' : 'strict',
|
|
108
|
+
file: 'SKILL.md',
|
|
109
|
+
directoryName: record.name,
|
|
110
|
+
});
|
|
111
|
+
if (!validation.valid) {
|
|
112
|
+
policy.status = 'degraded';
|
|
113
|
+
policy.supported = false;
|
|
114
|
+
policy.reasons.push(`provider projection failed validation: ${validation.diagnostics
|
|
115
|
+
.filter((item) => item.severity === 'error')
|
|
116
|
+
.map((item) => item.code)
|
|
117
|
+
.join(', ')}`);
|
|
118
|
+
return undefined;
|
|
119
|
+
}
|
|
120
|
+
if (policy.provider === 'claude' && originalBytes
|
|
121
|
+
&& document.unknownFields.length === 0
|
|
122
|
+
&& Object.keys(document.aiwg).every((key) => Object.hasOwn(extensions, key)))
|
|
123
|
+
return Buffer.from(originalBytes);
|
|
124
|
+
return Buffer.from(content, 'utf8');
|
|
125
|
+
}
|
|
126
|
+
export function sidecarBytes(record, policy, document) {
|
|
127
|
+
const portableSidecar = createAgentSkillSidecar(document, {
|
|
128
|
+
sourceKind: record.source.kind,
|
|
129
|
+
locator: record.source.locator,
|
|
130
|
+
...(record.source.kind === 'git'
|
|
131
|
+
? {
|
|
132
|
+
requestedRevision: record.source.requestedRevision,
|
|
133
|
+
resolvedRevision: record.source.resolvedRevision,
|
|
134
|
+
}
|
|
135
|
+
: {}),
|
|
136
|
+
sourceDigest: record.digest,
|
|
137
|
+
importedAt: record.importedAt,
|
|
138
|
+
aiwgVersion: record.aiwgVersion,
|
|
139
|
+
}, record.validationProfile, record.trust);
|
|
140
|
+
return Buffer.from(`${JSON.stringify({
|
|
141
|
+
schemaVersion: 1,
|
|
142
|
+
kind: 'aiwg-managed-agent-skill-projection',
|
|
143
|
+
name: record.name,
|
|
144
|
+
provider: policy.provider,
|
|
145
|
+
projectionStatus: policy.status,
|
|
146
|
+
sourceDigest: record.digest,
|
|
147
|
+
reasons: policy.reasons,
|
|
148
|
+
warnings: policy.warnings,
|
|
149
|
+
portable: portableSidecar,
|
|
150
|
+
}, null, 2)}\n`, 'utf8');
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=deployment-projection.js.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const AGENT_SKILL_MANAGED_MARKER = '.aiwg-managed';
|
|
2
|
+
export const AGENT_SKILL_DEPLOYMENT_SIDECAR = '.aiwg-agent-skill.json';
|
|
3
|
+
export const MARKER_CONTENT = 'aiwg-agent-skill-v1\n';
|
|
4
|
+
export class AgentSkillDeploymentError extends Error {
|
|
5
|
+
code;
|
|
6
|
+
constructor(code, message) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.code = code;
|
|
9
|
+
this.name = 'AgentSkillDeploymentError';
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export function fail(code, message) {
|
|
13
|
+
throw new AgentSkillDeploymentError(code, message);
|
|
14
|
+
}
|
|
15
|
+
export function assertPortableName(name) {
|
|
16
|
+
if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(name) || name.length > 64) {
|
|
17
|
+
fail('AS_DEPLOY_NAME', 'Agent Skill name must be 1-64 lowercase ASCII letters, numbers, or single hyphens');
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=deployment-types.js.map
|
|
@@ -13,18 +13,10 @@ import fs from 'node:fs';
|
|
|
13
13
|
import path from 'node:path';
|
|
14
14
|
import { fileURLToPath } from 'node:url';
|
|
15
15
|
import { stringify } from 'yaml';
|
|
16
|
-
import { AGENT_SKILLS_BASELINE,
|
|
16
|
+
import { AGENT_SKILLS_BASELINE, projectStrictAgentSkill, } from './agent-skills.js';
|
|
17
17
|
import { validateAgentSkillContent } from './validator.js';
|
|
18
|
+
import { agentSkillDocument } from './provider-extensions.js';
|
|
18
19
|
export const AGENT_SKILL_EXPORT_SIDECAR = '.aiwg-agent-skill-export.json';
|
|
19
|
-
const STANDARD_FIELDS = [
|
|
20
|
-
'name',
|
|
21
|
-
'description',
|
|
22
|
-
'license',
|
|
23
|
-
'compatibility',
|
|
24
|
-
'metadata',
|
|
25
|
-
'allowed-tools',
|
|
26
|
-
];
|
|
27
|
-
const AIWG_FIELDS = new Set(AIWG_SKILL_CONTROL_FIELDS);
|
|
28
20
|
export class AgentSkillExportError extends Error {
|
|
29
21
|
code;
|
|
30
22
|
constructor(code, message) {
|
|
@@ -160,28 +152,11 @@ function parseDocument(sourceRoot, entries) {
|
|
|
160
152
|
.map((item) => item.code)
|
|
161
153
|
.join(', ')}`);
|
|
162
154
|
}
|
|
163
|
-
const
|
|
164
|
-
for (const key of STANDARD_FIELDS) {
|
|
165
|
-
const value = validation.frontmatter[key];
|
|
166
|
-
if (value !== undefined)
|
|
167
|
-
Object.assign(standard, { [key]: structuredClone(value) });
|
|
168
|
-
}
|
|
169
|
-
const aiwg = {};
|
|
170
|
-
for (const [key, value] of Object.entries(validation.frontmatter)) {
|
|
171
|
-
if (AIWG_FIELDS.has(key)) {
|
|
172
|
-
aiwg[key] = structuredClone(value);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
155
|
+
const document = agentSkillDocument(validation.frontmatter, validation.body);
|
|
175
156
|
return {
|
|
176
|
-
document
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
resources: [],
|
|
180
|
-
aiwg,
|
|
181
|
-
unknownFields: [],
|
|
182
|
-
},
|
|
183
|
-
description: validation.frontmatter.description,
|
|
184
|
-
omittedAiwgFields: Object.keys(aiwg).sort(),
|
|
157
|
+
document,
|
|
158
|
+
description: document.standard.description,
|
|
159
|
+
omittedAiwgFields: Object.keys(document.aiwg).sort(),
|
|
185
160
|
};
|
|
186
161
|
}
|
|
187
162
|
function strictSkillEntry(sourceRoot, document) {
|
|
@@ -332,6 +307,7 @@ export function exportAgentSkillDirectory(sourceDir, options) {
|
|
|
332
307
|
sourceDigest,
|
|
333
308
|
exportDigest: '',
|
|
334
309
|
omittedAiwgFields,
|
|
310
|
+
...(document.providers ? { providers: document.providers } : {}),
|
|
335
311
|
fileCount: 0,
|
|
336
312
|
totalBytes: 0,
|
|
337
313
|
exportedAt,
|
|
@@ -360,6 +336,7 @@ export function exportAgentSkillDirectory(sourceDir, options) {
|
|
|
360
336
|
sourceDigest,
|
|
361
337
|
exportDigest,
|
|
362
338
|
omittedAiwgFields,
|
|
339
|
+
...(document.providers ? { providers: document.providers } : {}),
|
|
363
340
|
fileCount,
|
|
364
341
|
totalBytes,
|
|
365
342
|
exportedAt,
|