@1aboveio/skills 0.13.0 → 0.15.0
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 +13 -11
- package/package.json +1 -1
- package/runtime/skills/distribution/generated/recipes.json +17 -17
- package/runtime/skills/distribution/scripts/bundles.mjs +113 -25
- package/runtime/skills/engineering/engineering-runtime/scripts/invocation-policy.mjs +187 -0
- package/runtime/skills/engineering/engineering-runtime/scripts/workflow-coherence.mjs +19 -0
- package/runtime/skills/engineering/engineering-runtime/scripts/workflow-policy.mjs +9 -3
- package/skills/cicd-pipeline/mergify/references/watch-contract.md +7 -0
- package/skills/cicd-pipeline/mergify/scripts/watch-pr-delivery-core.mjs +60 -2
- package/skills/engineering/engineering-runtime/coherence/workflow.json +23 -16
- package/skills/engineering/engineering-runtime/scripts/invocation-policy.mjs +187 -0
- package/skills/engineering/engineering-runtime/scripts/workflow-coherence.mjs +19 -0
- package/skills/engineering/engineering-runtime/scripts/workflow-policy.mjs +9 -3
- package/skills/engineering/resolve-issues/generated/workflow-repair-policy.json +20 -13
- package/skills/engineering/resolve-issues/scripts/run-state.mjs +14 -6
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
4
|
+
import { homedir } from 'node:os';
|
|
5
|
+
import { dirname, join } from 'node:path';
|
|
6
|
+
|
|
7
|
+
import { isMainModule } from './main-module.mjs';
|
|
8
|
+
|
|
9
|
+
export const MANUAL_ONLY = 'manual-only';
|
|
10
|
+
const SKILL_NAME = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
11
|
+
|
|
12
|
+
function frontmatterBounds(contents) {
|
|
13
|
+
const match = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/.exec(contents);
|
|
14
|
+
if (!match) throw new Error('SKILL.md must begin with YAML frontmatter');
|
|
15
|
+
return { body: match[1], end: match[0].length };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function manualOnlySkill(contents) {
|
|
19
|
+
const frontmatter = frontmatterBounds(contents);
|
|
20
|
+
const declarations = frontmatter.body.match(/^disable-model-invocation\s*:\s*.*$/gm) ?? [];
|
|
21
|
+
if (declarations.length > 1) throw new Error('SKILL.md repeats disable-model-invocation');
|
|
22
|
+
|
|
23
|
+
const body = declarations.length === 1
|
|
24
|
+
? frontmatter.body.replace(/^disable-model-invocation\s*:\s*.*$/m, 'disable-model-invocation: true')
|
|
25
|
+
: `${frontmatter.body}\ndisable-model-invocation: true`;
|
|
26
|
+
return `---\n${body}\n---\n${contents.slice(frontmatter.end)}`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function manualOnlyOpenAi(contents) {
|
|
30
|
+
const policyHeaders = contents.match(/^policy\s*:\s*$/gm) ?? [];
|
|
31
|
+
if (policyHeaders.length > 1) throw new Error('agents/openai.yaml repeats policy');
|
|
32
|
+
if (policyHeaders.length === 0) {
|
|
33
|
+
const prefix = contents.length === 0 || contents.endsWith('\n') ? contents : `${contents}\n`;
|
|
34
|
+
return `${prefix}policy:\n allow_implicit_invocation: false\n`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const lines = contents.split('\n');
|
|
38
|
+
const policyIndex = lines.findIndex((line) => /^policy\s*:\s*$/.test(line));
|
|
39
|
+
let policyEnd = lines.length;
|
|
40
|
+
for (let index = policyIndex + 1; index < lines.length; index += 1) {
|
|
41
|
+
if (/^[^\s#][^:]*\s*:/.test(lines[index])) {
|
|
42
|
+
policyEnd = index;
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const declarations = lines
|
|
47
|
+
.slice(policyIndex + 1, policyEnd)
|
|
48
|
+
.filter((line) => /^\s+allow_implicit_invocation\s*:/.test(line));
|
|
49
|
+
if (declarations.length > 1) throw new Error('agents/openai.yaml repeats policy.allow_implicit_invocation');
|
|
50
|
+
if (declarations.length === 1) {
|
|
51
|
+
return lines.map((line, index) => (
|
|
52
|
+
index > policyIndex && index < policyEnd && /^\s+allow_implicit_invocation\s*:/.test(line)
|
|
53
|
+
? ' allow_implicit_invocation: false'
|
|
54
|
+
: line
|
|
55
|
+
)).join('\n');
|
|
56
|
+
}
|
|
57
|
+
lines.splice(policyIndex + 1, 0, ' allow_implicit_invocation: false');
|
|
58
|
+
return lines.join('\n');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function manualOnlyPolicyFindings(skillDirectory) {
|
|
62
|
+
const findings = [];
|
|
63
|
+
const skillPath = join(skillDirectory, 'SKILL.md');
|
|
64
|
+
const openaiPath = join(skillDirectory, 'agents', 'openai.yaml');
|
|
65
|
+
if (!existsSync(skillPath)) return ['SKILL.md is missing'];
|
|
66
|
+
const skill = readFileSync(skillPath, 'utf8');
|
|
67
|
+
const declarations = frontmatterBounds(skill).body.match(/^disable-model-invocation\s*:\s*true\s*$/gm) ?? [];
|
|
68
|
+
if (declarations.length !== 1) findings.push('SKILL.md must declare disable-model-invocation: true exactly once');
|
|
69
|
+
if (!existsSync(openaiPath)) {
|
|
70
|
+
findings.push('agents/openai.yaml is missing');
|
|
71
|
+
} else {
|
|
72
|
+
const openai = readFileSync(openaiPath, 'utf8');
|
|
73
|
+
const policies = openai.match(/^policy\s*:\s*$/gm) ?? [];
|
|
74
|
+
const implicit = openai.match(/^\s+allow_implicit_invocation\s*:\s*false\s*$/gm) ?? [];
|
|
75
|
+
if (policies.length !== 1 || implicit.length !== 1) {
|
|
76
|
+
findings.push('agents/openai.yaml must declare policy.allow_implicit_invocation: false exactly once');
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return findings;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Restore the reviewed upstream bytes for source-integrity hashing. The installed tree must first
|
|
83
|
+
// pass manualOnlyPolicyFindings; these exact trailing additions are the only intentional divergence.
|
|
84
|
+
export function reviewedSourceInjections(skillDirectory, policy) {
|
|
85
|
+
if (policy !== MANUAL_ONLY) return [];
|
|
86
|
+
const findings = manualOnlyPolicyFindings(skillDirectory);
|
|
87
|
+
if (findings.length > 0) throw new Error(findings.join('; '));
|
|
88
|
+
const skill = readFileSync(join(skillDirectory, 'SKILL.md'), 'utf8')
|
|
89
|
+
.replace(/^disable-model-invocation: true\n/m, '');
|
|
90
|
+
const openai = readFileSync(join(skillDirectory, 'agents', 'openai.yaml'), 'utf8')
|
|
91
|
+
.replace(/policy:\n allow_implicit_invocation: false\n?$/, '');
|
|
92
|
+
return [
|
|
93
|
+
{ relativePath: 'SKILL.md', contents: skill },
|
|
94
|
+
{ relativePath: 'agents/openai.yaml', contents: openai },
|
|
95
|
+
];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function clearInvocationPolicy({ installName, policy, skillsRoot = join(homedir(), '.agents', 'skills') }) {
|
|
99
|
+
if (!SKILL_NAME.test(installName ?? '')) throw new Error(`invalid installed skill name ${JSON.stringify(installName)}`);
|
|
100
|
+
if (policy !== MANUAL_ONLY) throw new Error(`unsupported invocation policy ${JSON.stringify(policy)}`);
|
|
101
|
+
const skillDirectory = join(skillsRoot, installName);
|
|
102
|
+
const skillPath = join(skillDirectory, 'SKILL.md');
|
|
103
|
+
const openaiPath = join(skillDirectory, 'agents', 'openai.yaml');
|
|
104
|
+
if (!existsSync(skillPath) || !existsSync(openaiPath)) throw new Error(`installed skill is incomplete: ${skillDirectory}`);
|
|
105
|
+
const skill = readFileSync(skillPath, 'utf8');
|
|
106
|
+
const openai = readFileSync(openaiPath, 'utf8');
|
|
107
|
+
const hasSkillPolicy = /^disable-model-invocation\s*:/m.test(frontmatterBounds(skill).body);
|
|
108
|
+
const hasOpenAiPolicy = /^policy\s*:\s*$/m.test(openai) || /^\s+allow_implicit_invocation\s*:/m.test(openai);
|
|
109
|
+
if (!hasSkillPolicy && !hasOpenAiPolicy) {
|
|
110
|
+
return { installName, policy: 'implicit', skillDirectory, changed: false };
|
|
111
|
+
}
|
|
112
|
+
const injected = Object.fromEntries(reviewedSourceInjections(skillDirectory, policy)
|
|
113
|
+
.map((entry) => [entry.relativePath, entry.contents]));
|
|
114
|
+
writeFileSync(skillPath, injected['SKILL.md']);
|
|
115
|
+
writeFileSync(openaiPath, injected['agents/openai.yaml']);
|
|
116
|
+
return { installName, policy: 'implicit', skillDirectory, changed: true };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function applyInvocationPolicy({ installName, policy, skillsRoot = join(homedir(), '.agents', 'skills') }) {
|
|
120
|
+
if (!SKILL_NAME.test(installName ?? '')) throw new Error(`invalid installed skill name ${JSON.stringify(installName)}`);
|
|
121
|
+
if (policy !== MANUAL_ONLY) throw new Error(`unsupported invocation policy ${JSON.stringify(policy)}`);
|
|
122
|
+
const skillDirectory = join(skillsRoot, installName);
|
|
123
|
+
const skillPath = join(skillDirectory, 'SKILL.md');
|
|
124
|
+
if (!existsSync(skillPath)) throw new Error(`installed skill is missing: ${skillPath}`);
|
|
125
|
+
const openaiPath = join(skillDirectory, 'agents', 'openai.yaml');
|
|
126
|
+
|
|
127
|
+
const skillBefore = readFileSync(skillPath, 'utf8');
|
|
128
|
+
const skillAfter = manualOnlySkill(skillBefore);
|
|
129
|
+
if (skillAfter !== skillBefore) writeFileSync(skillPath, skillAfter);
|
|
130
|
+
|
|
131
|
+
mkdirSync(dirname(openaiPath), { recursive: true });
|
|
132
|
+
const openaiBefore = existsSync(openaiPath) ? readFileSync(openaiPath, 'utf8') : '';
|
|
133
|
+
const openaiAfter = manualOnlyOpenAi(openaiBefore);
|
|
134
|
+
if (openaiAfter !== openaiBefore) writeFileSync(openaiPath, openaiAfter);
|
|
135
|
+
|
|
136
|
+
const findings = manualOnlyPolicyFindings(skillDirectory);
|
|
137
|
+
if (findings.length > 0) throw new Error(findings.join('; '));
|
|
138
|
+
return { installName, policy, skillDirectory, changed: skillAfter !== skillBefore || openaiAfter !== openaiBefore };
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function selectionEnablesTdd(env = process.env) {
|
|
142
|
+
const stateHome = env.XDG_STATE_HOME || join(env.HOME || homedir(), '.local', 'state');
|
|
143
|
+
const path = join(stateHome, '1aboveio-skills', 'selection.json');
|
|
144
|
+
if (!existsSync(path)) return true;
|
|
145
|
+
try {
|
|
146
|
+
const state = JSON.parse(readFileSync(path, 'utf8'));
|
|
147
|
+
if (state.tddImplicitInvocation === undefined) return true;
|
|
148
|
+
if (typeof state.tddImplicitInvocation !== 'boolean') {
|
|
149
|
+
throw new Error('tddImplicitInvocation must be a boolean');
|
|
150
|
+
}
|
|
151
|
+
return state.tddImplicitInvocation;
|
|
152
|
+
} catch (error) {
|
|
153
|
+
throw new Error(`cannot read TDD invocation selection at ${path}: ${error.message}`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export function reconcileInvocationPolicy(options) {
|
|
158
|
+
return selectionEnablesTdd(options.env)
|
|
159
|
+
? clearInvocationPolicy(options)
|
|
160
|
+
: applyInvocationPolicy(options);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function parseArgs(argv) {
|
|
164
|
+
const parsed = { installName: null, policy: null, skillsRoot: undefined, fromSelection: false };
|
|
165
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
166
|
+
const arg = argv[index];
|
|
167
|
+
if (arg === '--skill') parsed.installName = argv[++index];
|
|
168
|
+
else if (arg === '--policy') parsed.policy = argv[++index];
|
|
169
|
+
else if (arg === '--skills-root') parsed.skillsRoot = argv[++index];
|
|
170
|
+
else if (arg === '--from-selection') parsed.fromSelection = true;
|
|
171
|
+
else throw new Error(`unknown argument ${arg}`);
|
|
172
|
+
}
|
|
173
|
+
return parsed;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (isMainModule(import.meta.url)) {
|
|
177
|
+
try {
|
|
178
|
+
const parsed = parseArgs(process.argv.slice(2));
|
|
179
|
+
const result = parsed.fromSelection
|
|
180
|
+
? reconcileInvocationPolicy(parsed)
|
|
181
|
+
: applyInvocationPolicy(parsed);
|
|
182
|
+
process.stdout.write(`${JSON.stringify(result)}\n`);
|
|
183
|
+
} catch (error) {
|
|
184
|
+
process.stderr.write(`invocation-policy: ${error.message}\n`);
|
|
185
|
+
process.exitCode = 1;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
@@ -3,12 +3,14 @@ import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
|
3
3
|
import { isAbsolute, join, normalize, relative } from 'node:path';
|
|
4
4
|
import { isDeepStrictEqual } from 'node:util';
|
|
5
5
|
|
|
6
|
+
import { reviewedSourceInjections } from './invocation-policy.mjs';
|
|
6
7
|
import { isMainModule } from './main-module.mjs';
|
|
7
8
|
import {
|
|
8
9
|
HARNESS_RUNTIME_DEPENDENCY_MEMBERS,
|
|
9
10
|
MATT_DEPENDENCY_MEMBERS,
|
|
10
11
|
WORKFLOW_OWNED_MEMBERS,
|
|
11
12
|
WORKFLOW_TRUSTED_DEPENDENCY_CLOSURES,
|
|
13
|
+
WORKFLOW_TRUSTED_INVOCATION_POLICIES,
|
|
12
14
|
WORKFLOW_TRUSTED_REPAIR_RECIPE,
|
|
13
15
|
WORKFLOW_TRUSTED_SOURCES,
|
|
14
16
|
} from './workflow-policy.mjs';
|
|
@@ -18,6 +20,7 @@ export {
|
|
|
18
20
|
MATT_DEPENDENCY_MEMBERS,
|
|
19
21
|
WORKFLOW_OWNED_MEMBERS,
|
|
20
22
|
WORKFLOW_TRUSTED_DEPENDENCY_CLOSURES,
|
|
23
|
+
WORKFLOW_TRUSTED_INVOCATION_POLICIES,
|
|
21
24
|
WORKFLOW_TRUSTED_REPAIR_RECIPE,
|
|
22
25
|
} from './workflow-policy.mjs';
|
|
23
26
|
|
|
@@ -67,6 +70,9 @@ export const WORKFLOW_TRUSTED_POLICY = deepFreeze({
|
|
|
67
70
|
ownership: 'dependency',
|
|
68
71
|
sourcePath: MATT_MEMBER_PATHS[installName],
|
|
69
72
|
expectedSource: MATT_SOURCE,
|
|
73
|
+
...(WORKFLOW_TRUSTED_INVOCATION_POLICIES.find((entry) => entry.installName === installName)
|
|
74
|
+
? { invocationPolicy: WORKFLOW_TRUSTED_INVOCATION_POLICIES.find((entry) => entry.installName === installName).policy }
|
|
75
|
+
: {}),
|
|
70
76
|
})),
|
|
71
77
|
...HARNESS_RUNTIME_DEPENDENCY_MEMBERS.map((installName) => ({
|
|
72
78
|
installName,
|
|
@@ -331,6 +337,9 @@ function validateManifest(manifest) {
|
|
|
331
337
|
|| JSON.stringify(member.digestExcludes) !== JSON.stringify(expectedExcludes)) {
|
|
332
338
|
add('member-digest-contract-invalid', memberName, 'The member digest exclusion contract is missing or malformed.');
|
|
333
339
|
}
|
|
340
|
+
if (member.invocationPolicy !== expectedMember.invocationPolicy) {
|
|
341
|
+
add('member-invocation-policy-invalid', memberName, 'The runtime invocation policy is missing or malformed.');
|
|
342
|
+
}
|
|
334
343
|
}
|
|
335
344
|
}
|
|
336
345
|
|
|
@@ -465,8 +474,18 @@ export function verifyWorkflowCoherence(options = {}) {
|
|
|
465
474
|
// for a member to claim a scope its source does not have — and `validateManifest` has already
|
|
466
475
|
// proved `expectedSource` deep-equal to the trusted policy above (`member-source-identity-
|
|
467
476
|
// invalid` returns before this loop), so the derivation reads trusted data, not manifest claims.
|
|
477
|
+
let policyInjections = [];
|
|
478
|
+
if (layout === 'installed' && member.invocationPolicy) {
|
|
479
|
+
try {
|
|
480
|
+
policyInjections = reviewedSourceInjections(directory, member.invocationPolicy);
|
|
481
|
+
} catch {
|
|
482
|
+
// An unchanged upstream tree is the explicit "implicit enabled" option. Partial or
|
|
483
|
+
// malformed overlays still fail because their raw digest matches neither allowed state.
|
|
484
|
+
}
|
|
485
|
+
}
|
|
468
486
|
actualDigest = contentDigest(directory, {
|
|
469
487
|
exclude: member.digestExcludes,
|
|
488
|
+
injected: policyInjections,
|
|
470
489
|
published: firstPartySource(member.expectedSource),
|
|
471
490
|
});
|
|
472
491
|
} catch (error) {
|
|
@@ -47,6 +47,10 @@ export const MATT_DEPENDENCY_MEMBERS = deepFreeze([
|
|
|
47
47
|
// runtime imports. Order follows the coherence member census so the same tree always
|
|
48
48
|
// produces the same identity. A generated file remains part of its owner's content
|
|
49
49
|
// digest and is also named explicitly so a scoped verdict reports every identity used.
|
|
50
|
+
export const WORKFLOW_TRUSTED_INVOCATION_POLICIES = deepFreeze([
|
|
51
|
+
{ installName: 'tdd', policy: 'manual-only' },
|
|
52
|
+
]);
|
|
53
|
+
|
|
50
54
|
export const WORKFLOW_TRUSTED_DEPENDENCY_CLOSURES = deepFreeze([
|
|
51
55
|
{
|
|
52
56
|
invokedSkill: 'resolve-issues',
|
|
@@ -93,7 +97,7 @@ export const WORKFLOW_TRUSTED_SOURCES = deepFreeze({
|
|
|
93
97
|
id: 'first-party',
|
|
94
98
|
type: 'first-party',
|
|
95
99
|
package: '@1aboveio/skills',
|
|
96
|
-
version: '0.
|
|
100
|
+
version: '0.15.0',
|
|
97
101
|
},
|
|
98
102
|
matt: {
|
|
99
103
|
id: 'matt-pocock',
|
|
@@ -106,6 +110,7 @@ export const WORKFLOW_TRUSTED_SOURCES = deepFreeze({
|
|
|
106
110
|
|
|
107
111
|
const nativeSkillsPrefix = 'npx skills@1.5.22 add';
|
|
108
112
|
const nativeSkillsTarget = '--global --agent universal claude-code --skill';
|
|
113
|
+
const invocationPolicyCommand = 'node "$HOME/.agents/skills/engineering-runtime/scripts/invocation-policy.mjs" --skill tdd --policy manual-only --from-selection';
|
|
109
114
|
const externalArchiveLimits = 'SKILLS_DOWNLOAD_MAX_BYTES=33554432 SKILLS_EXTRACT_MAX_FILES=4096 SKILLS_EXTRACT_MAX_BYTES=67108864';
|
|
110
115
|
const mattMembersArgument = MATT_DEPENDENCY_MEMBERS.join(' ');
|
|
111
116
|
const firstPartyMembers = deepFreeze([
|
|
@@ -135,9 +140,10 @@ export const WORKFLOW_TRUSTED_REPAIR_RECIPE = deepFreeze({
|
|
|
135
140
|
revision: WORKFLOW_TRUSTED_SOURCES.matt.revision,
|
|
136
141
|
installPath: null,
|
|
137
142
|
members: MATT_DEPENDENCY_MEMBERS,
|
|
143
|
+
invocationPolicies: WORKFLOW_TRUSTED_INVOCATION_POLICIES,
|
|
138
144
|
commands: [{
|
|
139
145
|
transport: 'https',
|
|
140
|
-
command: `${externalArchiveLimits} ${nativeSkillsPrefix} https://codeload.github.com/mattpocock/skills/tar.gz/${WORKFLOW_TRUSTED_SOURCES.matt.revision} ${nativeSkillsTarget} ${mattMembersArgument}`,
|
|
146
|
+
command: `${externalArchiveLimits} ${nativeSkillsPrefix} https://codeload.github.com/mattpocock/skills/tar.gz/${WORKFLOW_TRUSTED_SOURCES.matt.revision} ${nativeSkillsTarget} ${mattMembersArgument} --yes && ${invocationPolicyCommand}`,
|
|
141
147
|
}],
|
|
142
148
|
onFailure: {
|
|
143
149
|
action: 'stop',
|
|
@@ -154,7 +160,7 @@ export const WORKFLOW_TRUSTED_REPAIR_RECIPE = deepFreeze({
|
|
|
154
160
|
commands: [
|
|
155
161
|
{
|
|
156
162
|
transport: 'npm',
|
|
157
|
-
command: `npx ${publicPackageRelease} install --group engineering-workflow`,
|
|
163
|
+
command: `npx ${publicPackageRelease} install --group engineering-workflow --yes`,
|
|
158
164
|
},
|
|
159
165
|
],
|
|
160
166
|
onFailure: {
|
|
@@ -178,7 +178,8 @@
|
|
|
178
178
|
"repository": "https://github.com/mattpocock/skills",
|
|
179
179
|
"revision": "ed37663cc5fbef691ddfecd080dff42f7e7e350d",
|
|
180
180
|
"reviewedTree": "04b0fcb78e3de7c58744fcba2528354cc64ab988"
|
|
181
|
-
}
|
|
181
|
+
},
|
|
182
|
+
"invocationPolicy": "manual-only"
|
|
182
183
|
},
|
|
183
184
|
{
|
|
184
185
|
"installName": "to-spec",
|
|
@@ -236,7 +237,7 @@
|
|
|
236
237
|
"id": "first-party",
|
|
237
238
|
"type": "first-party",
|
|
238
239
|
"package": "@1aboveio/skills",
|
|
239
|
-
"version": "0.
|
|
240
|
+
"version": "0.15.0"
|
|
240
241
|
}
|
|
241
242
|
},
|
|
242
243
|
{
|
|
@@ -247,7 +248,7 @@
|
|
|
247
248
|
"id": "first-party",
|
|
248
249
|
"type": "first-party",
|
|
249
250
|
"package": "@1aboveio/skills",
|
|
250
|
-
"version": "0.
|
|
251
|
+
"version": "0.15.0"
|
|
251
252
|
}
|
|
252
253
|
},
|
|
253
254
|
{
|
|
@@ -258,7 +259,7 @@
|
|
|
258
259
|
"id": "first-party",
|
|
259
260
|
"type": "first-party",
|
|
260
261
|
"package": "@1aboveio/skills",
|
|
261
|
-
"version": "0.
|
|
262
|
+
"version": "0.15.0"
|
|
262
263
|
}
|
|
263
264
|
},
|
|
264
265
|
{
|
|
@@ -269,7 +270,7 @@
|
|
|
269
270
|
"id": "first-party",
|
|
270
271
|
"type": "first-party",
|
|
271
272
|
"package": "@1aboveio/skills",
|
|
272
|
-
"version": "0.
|
|
273
|
+
"version": "0.15.0"
|
|
273
274
|
}
|
|
274
275
|
},
|
|
275
276
|
{
|
|
@@ -280,7 +281,7 @@
|
|
|
280
281
|
"id": "first-party",
|
|
281
282
|
"type": "first-party",
|
|
282
283
|
"package": "@1aboveio/skills",
|
|
283
|
-
"version": "0.
|
|
284
|
+
"version": "0.15.0"
|
|
284
285
|
}
|
|
285
286
|
},
|
|
286
287
|
{
|
|
@@ -291,7 +292,7 @@
|
|
|
291
292
|
"id": "first-party",
|
|
292
293
|
"type": "first-party",
|
|
293
294
|
"package": "@1aboveio/skills",
|
|
294
|
-
"version": "0.
|
|
295
|
+
"version": "0.15.0"
|
|
295
296
|
}
|
|
296
297
|
},
|
|
297
298
|
{
|
|
@@ -302,7 +303,7 @@
|
|
|
302
303
|
"id": "first-party",
|
|
303
304
|
"type": "first-party",
|
|
304
305
|
"package": "@1aboveio/skills",
|
|
305
|
-
"version": "0.
|
|
306
|
+
"version": "0.15.0"
|
|
306
307
|
}
|
|
307
308
|
},
|
|
308
309
|
{
|
|
@@ -313,7 +314,7 @@
|
|
|
313
314
|
"id": "first-party",
|
|
314
315
|
"type": "first-party",
|
|
315
316
|
"package": "@1aboveio/skills",
|
|
316
|
-
"version": "0.
|
|
317
|
+
"version": "0.15.0"
|
|
317
318
|
}
|
|
318
319
|
},
|
|
319
320
|
{
|
|
@@ -324,7 +325,7 @@
|
|
|
324
325
|
"id": "first-party",
|
|
325
326
|
"type": "first-party",
|
|
326
327
|
"package": "@1aboveio/skills",
|
|
327
|
-
"version": "0.
|
|
328
|
+
"version": "0.15.0"
|
|
328
329
|
}
|
|
329
330
|
}
|
|
330
331
|
],
|
|
@@ -404,10 +405,16 @@
|
|
|
404
405
|
"triage",
|
|
405
406
|
"wayfinder"
|
|
406
407
|
],
|
|
408
|
+
"invocationPolicies": [
|
|
409
|
+
{
|
|
410
|
+
"installName": "tdd",
|
|
411
|
+
"policy": "manual-only"
|
|
412
|
+
}
|
|
413
|
+
],
|
|
407
414
|
"commands": [
|
|
408
415
|
{
|
|
409
416
|
"transport": "https",
|
|
410
|
-
"command": "SKILLS_DOWNLOAD_MAX_BYTES=33554432 SKILLS_EXTRACT_MAX_FILES=4096 SKILLS_EXTRACT_MAX_BYTES=67108864 npx skills@1.5.22 add https://codeload.github.com/mattpocock/skills/tar.gz/ed37663cc5fbef691ddfecd080dff42f7e7e350d --global --agent universal claude-code --skill batch-grill-me code-review codebase-design diagnosing-bugs domain-modeling grill-me grill-with-docs grilling handoff improve-codebase-architecture prototype research resolving-merge-conflicts setup-matt-pocock-skills tdd to-spec to-tickets triage wayfinder"
|
|
417
|
+
"command": "SKILLS_DOWNLOAD_MAX_BYTES=33554432 SKILLS_EXTRACT_MAX_FILES=4096 SKILLS_EXTRACT_MAX_BYTES=67108864 npx skills@1.5.22 add https://codeload.github.com/mattpocock/skills/tar.gz/ed37663cc5fbef691ddfecd080dff42f7e7e350d --global --agent universal claude-code --skill batch-grill-me code-review codebase-design diagnosing-bugs domain-modeling grill-me grill-with-docs grilling handoff improve-codebase-architecture prototype research resolving-merge-conflicts setup-matt-pocock-skills tdd to-spec to-tickets triage wayfinder --yes && node \"$HOME/.agents/skills/engineering-runtime/scripts/invocation-policy.mjs\" --skill tdd --policy manual-only --from-selection"
|
|
411
418
|
}
|
|
412
419
|
],
|
|
413
420
|
"onFailure": {
|
|
@@ -419,7 +426,7 @@
|
|
|
419
426
|
"sourceId": "first-party",
|
|
420
427
|
"sourceType": "first-party",
|
|
421
428
|
"package": "@1aboveio/skills",
|
|
422
|
-
"version": "0.
|
|
429
|
+
"version": "0.15.0",
|
|
423
430
|
"installPath": null,
|
|
424
431
|
"members": [
|
|
425
432
|
"harness-runtime",
|
|
@@ -435,7 +442,7 @@
|
|
|
435
442
|
"commands": [
|
|
436
443
|
{
|
|
437
444
|
"transport": "npm",
|
|
438
|
-
"command": "npx @1aboveio/skills@0.
|
|
445
|
+
"command": "npx @1aboveio/skills@0.15.0 install --group engineering-workflow --yes"
|
|
439
446
|
}
|
|
440
447
|
],
|
|
441
448
|
"onFailure": {
|
|
@@ -3412,7 +3412,7 @@ export const WORKFLOW_PREFLIGHT_COMMANDS = Object.freeze([
|
|
|
3412
3412
|
|
|
3413
3413
|
const WORKFLOW_VERIFIER_URL = new URL('../../engineering-runtime/scripts/workflow-coherence.mjs', import.meta.url)
|
|
3414
3414
|
const WORKFLOW_FALLBACK_POLICY_URL = new URL('../generated/workflow-repair-policy.json', import.meta.url)
|
|
3415
|
-
export const WORKFLOW_TRUSTED_FALLBACK_POLICY_SHA256 = '
|
|
3415
|
+
export const WORKFLOW_TRUSTED_FALLBACK_POLICY_SHA256 = '7dde46aecddba9c837b567cf73d69eed42390912fa6d76c6d7468773f0d59a2d'
|
|
3416
3416
|
const WORKFLOW_REPAIR_RECIPE_REFERENCE = Object.freeze({
|
|
3417
3417
|
id: 'engineering-workflow-dependency-first',
|
|
3418
3418
|
generatedFrom: 'skills/distribution/generated/recipes.json',
|
|
@@ -3449,7 +3449,8 @@ const WORKFLOW_NATIVE_TARGET = '--global --agent universal claude-code --skill'
|
|
|
3449
3449
|
// The archive limits are part of the external command, not decoration: dropping them is how a
|
|
3450
3450
|
// pinned-archive install becomes an unbounded download.
|
|
3451
3451
|
const WORKFLOW_EXTERNAL_ARCHIVE_LIMITS = 'SKILLS_DOWNLOAD_MAX_BYTES=33554432 SKILLS_EXTRACT_MAX_FILES=4096 SKILLS_EXTRACT_MAX_BYTES=67108864'
|
|
3452
|
-
const WORKFLOW_RUNTIME_DEPENDENCY_INSTALL = ' && npm
|
|
3452
|
+
const WORKFLOW_RUNTIME_DEPENDENCY_INSTALL = ' && npm --prefix "$(realpath "$HOME/.agents/skills/engineering-runtime/scripts")" ci --ignore-scripts'
|
|
3453
|
+
const WORKFLOW_INVOCATION_POLICY_COMMAND = 'node "$HOME/.agents/skills/engineering-runtime/scripts/invocation-policy.mjs" --skill tdd --policy manual-only --from-selection'
|
|
3453
3454
|
const WORKFLOW_MATT_REPOSITORY = 'https://github.com/mattpocock/skills'
|
|
3454
3455
|
// Restated, not derived. Advancing the Matt pin already edits this file (the fallback policy hash
|
|
3455
3456
|
// below moves with it), so pinning the revision here costs no drift a maintainer was not already paying.
|
|
@@ -3467,8 +3468,9 @@ const WORKFLOW_FIRST_PARTY_MEMBERS = Object.freeze([
|
|
|
3467
3468
|
])
|
|
3468
3469
|
const WORKFLOW_RECIPE_FIELDS = Object.freeze(['id', 'generatedFrom', 'groupId', 'lifecycle', 'renderTarget', 'stopOnFailure', 'steps'])
|
|
3469
3470
|
const WORKFLOW_COMMAND_FIELDS = Object.freeze(['transport', 'command'])
|
|
3470
|
-
const
|
|
3471
|
-
const
|
|
3471
|
+
const WORKFLOW_INVOCATION_POLICY_FIELDS = Object.freeze(['installName', 'policy'])
|
|
3472
|
+
const WORKFLOW_EXTERNAL_STEP_FIELDS = Object.freeze(['sourceId', 'sourceType', 'repository', 'revision', 'installPath', 'members', 'invocationPolicies', 'commands', 'onFailure'])
|
|
3473
|
+
const WORKFLOW_CHECKOUT_STEP_FIELDS = Object.freeze(['sourceId', 'sourceType', 'repository', 'revision', 'installPath', 'members', 'commands', 'onFailure'])
|
|
3472
3474
|
const WORKFLOW_PACKAGE_STEP_FIELDS = Object.freeze(['sourceId', 'sourceType', 'package', 'version', 'installPath', 'members', 'commands', 'onFailure'])
|
|
3473
3475
|
|
|
3474
3476
|
function exactFields(record, fields) {
|
|
@@ -3501,6 +3503,7 @@ function validStepFailure(step) {
|
|
|
3501
3503
|
|
|
3502
3504
|
function validMattStep(step) {
|
|
3503
3505
|
const members = WORKFLOW_MATT_MEMBERS.join(' ')
|
|
3506
|
+
const invocationPolicy = step?.invocationPolicies?.[0]
|
|
3504
3507
|
return exactFields(step, WORKFLOW_EXTERNAL_STEP_FIELDS)
|
|
3505
3508
|
&& step.sourceId === 'matt-pocock'
|
|
3506
3509
|
&& step.sourceType === 'external'
|
|
@@ -3508,9 +3511,14 @@ function validMattStep(step) {
|
|
|
3508
3511
|
&& step.revision === WORKFLOW_MATT_REVISION
|
|
3509
3512
|
&& step.installPath === null
|
|
3510
3513
|
&& exactList(step.members, WORKFLOW_MATT_MEMBERS)
|
|
3514
|
+
&& Array.isArray(step.invocationPolicies)
|
|
3515
|
+
&& step.invocationPolicies.length === 1
|
|
3516
|
+
&& exactFields(invocationPolicy, WORKFLOW_INVOCATION_POLICY_FIELDS)
|
|
3517
|
+
&& invocationPolicy.installName === 'tdd'
|
|
3518
|
+
&& invocationPolicy.policy === 'manual-only'
|
|
3511
3519
|
&& exactCommands(step.commands, [{
|
|
3512
3520
|
transport: 'https',
|
|
3513
|
-
command: `${WORKFLOW_EXTERNAL_ARCHIVE_LIMITS} ${WORKFLOW_NATIVE_CLI} add https://codeload.github.com/mattpocock/skills/tar.gz/${WORKFLOW_MATT_REVISION} ${WORKFLOW_NATIVE_TARGET} ${members}`,
|
|
3521
|
+
command: `${WORKFLOW_EXTERNAL_ARCHIVE_LIMITS} ${WORKFLOW_NATIVE_CLI} add https://codeload.github.com/mattpocock/skills/tar.gz/${WORKFLOW_MATT_REVISION} ${WORKFLOW_NATIVE_TARGET} ${members} --yes && ${WORKFLOW_INVOCATION_POLICY_COMMAND}`,
|
|
3514
3522
|
}])
|
|
3515
3523
|
&& validStepFailure(step)
|
|
3516
3524
|
}
|
|
@@ -3544,7 +3552,7 @@ const WORKFLOW_REPAIR_RENDER_TARGETS = new Map(Object.entries({
|
|
|
3544
3552
|
&& WORKFLOW_EXACT_PACKAGE_VERSION.test(step.version ?? '')
|
|
3545
3553
|
&& exactCommands(step.commands, [{
|
|
3546
3554
|
transport: 'npm',
|
|
3547
|
-
command: `npx ${WORKFLOW_PUBLIC_PACKAGE}@${step.version} install --group ${recipe.groupId}`,
|
|
3555
|
+
command: `npx ${WORKFLOW_PUBLIC_PACKAGE}@${step.version} install --group ${recipe.groupId} --yes`,
|
|
3548
3556
|
}]),
|
|
3549
3557
|
}),
|
|
3550
3558
|
}))
|