@aiwg/cli 2026.10.10 → 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-capabilities.v1.json +78 -14
- 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 +287 -31
- 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 +18 -28
- 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/config/worker-context.js +337 -0
- 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/mcp/tools/worker-invoke.mjs +108 -10
- 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-capabilities.v1.json +78 -14
- 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 -539
- 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 +30 -1
- 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/process/aiwg-probe-command.d.mts +15 -0
- package/dist/src/process/aiwg-probe-command.mjs +37 -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/grokbot.js +7 -0
- 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 +44 -6
- package/dist/src/sessions/contracts.js +20 -1
- package/dist/src/sessions/coverage-preflight.js +7 -1
- package/dist/src/sessions/discovery-capability.js +38 -0
- package/dist/src/sessions/fortemi-export-mapping.js +33 -3
- package/dist/src/sessions/host-catalog.js +157 -0
- package/dist/src/sessions/image-blobs.js +141 -0
- package/dist/src/sessions/importer.js +9 -232
- package/dist/src/sessions/index.js +6 -0
- package/dist/src/sessions/intake-dir.js +71 -0
- package/dist/src/sessions/normalization.js +251 -0
- package/dist/src/sessions/refresh.js +25 -2
- package/dist/src/sessions/repository.js +164 -22
- package/dist/src/sessions/tail.js +91 -0
- package/dist/src/sessions/workspace-discovery.js +88 -9
- 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 +7 -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/tools/ralph-external/lib/muse-adapter.mjs +10 -0
- package/tools/ralph-external/lib/worker-context.mjs +237 -0
- package/tools/ralph-external/lib/worker-invocation-ancestry.mjs +132 -0
- package/tools/ralph-external/lib/worker-invocation-processes.mjs +123 -0
- package/tools/ralph-external/lib/worker-isolation-status.mjs +48 -0
- package/tools/ralph-external/lib/worker-isolation.mjs +17 -29
- package/tools/ralph-external/lib/worker-launch-cli.mjs +3 -1
- package/tools/ralph-external/lib/worker-overflow.mjs +48 -13
- package/tools/ralph-external/lib/worker-retry-evidence.mjs +103 -0
- package/tools/ralph-external/lib/worker-termination.mjs +82 -0
- package/tools/ralph-external/session-launcher.mjs +51 -96
- package/vscode-extension/schemas/aiwg.config.v1.json +177 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** Inspection only: account-enabled skills are a separate delivery mechanism. */
|
|
2
|
+
export function projectClaudeSkillSurfaces(body) {
|
|
3
|
+
const shell = /!`[^`]+`/.test(body);
|
|
4
|
+
return ['cli', 'desktop-code', 'cowork', 'cloud'].map((surface) => {
|
|
5
|
+
const local = surface === 'cli' || surface === 'desktop-code';
|
|
6
|
+
const shellStatus = local ? 'native' : surface === 'cowork' ? 'degraded' : 'unsupported';
|
|
7
|
+
return {
|
|
8
|
+
provider: 'claude',
|
|
9
|
+
surface,
|
|
10
|
+
status: local ? 'native' : 'unsupported',
|
|
11
|
+
...(shell ? { shellInterpolation: shellStatus } : {}),
|
|
12
|
+
reasons: [
|
|
13
|
+
local
|
|
14
|
+
? 'Reads filesystem SKILL.md bundles from .claude/skills.'
|
|
15
|
+
: 'Does not read local .claude/skills; requires skills enabled on the claude.ai account.',
|
|
16
|
+
...(shell ? [local
|
|
17
|
+
? '! shell interpolation is native; AIWG does not execute it during validation or deploy.'
|
|
18
|
+
: surface === 'cowork'
|
|
19
|
+
? '! shell interpolation is degraded to a policy notice for account-enabled Cowork skills.'
|
|
20
|
+
: '! shell interpolation from a local skill is unsupported on cloud.'] : []),
|
|
21
|
+
],
|
|
22
|
+
};
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=claude-surface-projection.js.map
|
|
@@ -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
|