@ikon85/agent-workflow-kit 0.26.0 → 0.26.1
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/.agents/skills/kit-update/SKILL.md +4 -4
- package/.agents/skills/setup-workflow/SKILL.md +3 -3
- package/.agents/skills/setup-workflow/safety-guardrails.md +3 -3
- package/.claude/skills/kit-update/SKILL.md +4 -4
- package/.claude/skills/setup-workflow/SKILL.md +3 -3
- package/.claude/skills/setup-workflow/safety-guardrails.md +3 -3
- package/README.md +10 -0
- package/agent-workflow-kit.package.json +8 -8
- package/package.json +1 -1
- package/scripts/release-delta-guard.mjs +32 -2
- package/scripts/release-delta-guard.test.mjs +37 -3
|
@@ -7,10 +7,10 @@ description: "Preview and transactionally apply a parity-verified agent-workflow
|
|
|
7
7
|
|
|
8
8
|
Update an installed consumer only from the public scoped package
|
|
9
9
|
`@ikon85/agent-workflow-kit`. The unscoped `agent-workflow-kit` package belongs
|
|
10
|
-
to another publisher and is never a valid update source. Existing
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
to another publisher and is never a valid update source. Existing direct
|
|
11
|
+
GitHub installs remain valid, but updates must use the scoped npm release so
|
|
12
|
+
the shared release-parity checker can prove that npm and the matching GitHub
|
|
13
|
+
release contain the same artifact.
|
|
14
14
|
|
|
15
15
|
## Workflow
|
|
16
16
|
|
|
@@ -239,9 +239,9 @@ repository?"* Offer exactly **Yes**, **Later**, and **No**.
|
|
|
239
239
|
- **Disable** — remove only exact kit-owned Agent commands and Git wiring
|
|
240
240
|
first, then set `enabled: false`.
|
|
241
241
|
|
|
242
|
-
|
|
243
|
-
opt-in/manual: never copy its
|
|
244
|
-
without repository evidence and user confirmation.
|
|
242
|
+
The seed's reference consumer uses the parity-on profile. Other consumers are
|
|
243
|
+
opt-in/manual: never copy its package-manager, path, shim, surface, or outage
|
|
244
|
+
decisions without repository evidence and user confirmation.
|
|
245
245
|
|
|
246
246
|
### 3. Section B — Triage labels
|
|
247
247
|
|
|
@@ -49,9 +49,9 @@ all-or-nothing switch.
|
|
|
49
49
|
## Profile shape
|
|
50
50
|
|
|
51
51
|
Reconcile only `safetyGuardrails`; preserve every sibling section, unknown key,
|
|
52
|
-
and consumer-owned value. The following is the
|
|
53
|
-
not a universal default. Other consumers choose each `enabled` value
|
|
54
|
-
only facts proven in their repository.
|
|
52
|
+
and consumer-owned value. The following is the reference consumer's parity-on
|
|
53
|
+
example, not a universal default. Other consumers choose each `enabled` value
|
|
54
|
+
and fill only facts proven in their repository.
|
|
55
55
|
|
|
56
56
|
```json
|
|
57
57
|
{
|
|
@@ -7,10 +7,10 @@ description: "Preview and transactionally apply a parity-verified agent-workflow
|
|
|
7
7
|
|
|
8
8
|
Update an installed consumer only from the public scoped package
|
|
9
9
|
`@ikon85/agent-workflow-kit`. The unscoped `agent-workflow-kit` package belongs
|
|
10
|
-
to another publisher and is never a valid update source. Existing
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
to another publisher and is never a valid update source. Existing direct
|
|
11
|
+
GitHub installs remain valid, but updates must use the scoped npm release so
|
|
12
|
+
the shared release-parity checker can prove that npm and the matching GitHub
|
|
13
|
+
release contain the same artifact.
|
|
14
14
|
|
|
15
15
|
## Workflow
|
|
16
16
|
|
|
@@ -239,9 +239,9 @@ repository?"* Offer exactly **Yes**, **Later**, and **No**.
|
|
|
239
239
|
- **Disable** — remove only exact kit-owned Agent commands and Git wiring
|
|
240
240
|
first, then set `enabled: false`.
|
|
241
241
|
|
|
242
|
-
|
|
243
|
-
opt-in/manual: never copy its
|
|
244
|
-
without repository evidence and user confirmation.
|
|
242
|
+
The seed's reference consumer uses the parity-on profile. Other consumers are
|
|
243
|
+
opt-in/manual: never copy its package-manager, path, shim, surface, or outage
|
|
244
|
+
decisions without repository evidence and user confirmation.
|
|
245
245
|
|
|
246
246
|
### 3. Section B — Triage labels
|
|
247
247
|
|
|
@@ -49,9 +49,9 @@ all-or-nothing switch.
|
|
|
49
49
|
## Profile shape
|
|
50
50
|
|
|
51
51
|
Reconcile only `safetyGuardrails`; preserve every sibling section, unknown key,
|
|
52
|
-
and consumer-owned value. The following is the
|
|
53
|
-
not a universal default. Other consumers choose each `enabled` value
|
|
54
|
-
only facts proven in their repository.
|
|
52
|
+
and consumer-owned value. The following is the reference consumer's parity-on
|
|
53
|
+
example, not a universal default. Other consumers choose each `enabled` value
|
|
54
|
+
and fill only facts proven in their repository.
|
|
55
55
|
|
|
56
56
|
```json
|
|
57
57
|
{
|
package/README.md
CHANGED
|
@@ -332,6 +332,16 @@ still reference. Flags: `--force` (overwrite pre-existing files on `init`),
|
|
|
332
332
|
|
|
333
333
|
## Release notes
|
|
334
334
|
|
|
335
|
+
### 0.26.1
|
|
336
|
+
|
|
337
|
+
- changed: `.agents/skills/kit-update/SKILL.md`
|
|
338
|
+
- changed: `.agents/skills/setup-workflow/SKILL.md`
|
|
339
|
+
- changed: `.agents/skills/setup-workflow/safety-guardrails.md`
|
|
340
|
+
- changed: `.claude/skills/kit-update/SKILL.md`
|
|
341
|
+
- changed: `.claude/skills/setup-workflow/SKILL.md`
|
|
342
|
+
- changed: `.claude/skills/setup-workflow/safety-guardrails.md`
|
|
343
|
+
- changed: `scripts/release-delta-guard.mjs`
|
|
344
|
+
|
|
335
345
|
### 0.26.0
|
|
336
346
|
|
|
337
347
|
- added: `.agents/skills/setup-workflow/safety-guardrails.md`
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"kitVersion": "0.26.
|
|
2
|
+
"kitVersion": "0.26.1",
|
|
3
3
|
"files": [
|
|
4
4
|
{
|
|
5
5
|
"path": ".agents/skills/ask-matt/SKILL.md",
|
|
@@ -339,7 +339,7 @@
|
|
|
339
339
|
"kind": "skill",
|
|
340
340
|
"ownerSkill": "kit-update",
|
|
341
341
|
"surface": "codex",
|
|
342
|
-
"sha256": "
|
|
342
|
+
"sha256": "dbf2c0f696bd0ce45c5c2a315d40a3bbb99635e4ab384c788ac59f799fbf43de",
|
|
343
343
|
"mode": 420,
|
|
344
344
|
"origin": "kit"
|
|
345
345
|
},
|
|
@@ -573,7 +573,7 @@
|
|
|
573
573
|
"kind": "skill",
|
|
574
574
|
"ownerSkill": "setup-workflow",
|
|
575
575
|
"surface": "codex",
|
|
576
|
-
"sha256": "
|
|
576
|
+
"sha256": "8e223f625dcf941142005cbd9a4b6214358f68e3fd63cbee7bffef35b8b14ec8",
|
|
577
577
|
"mode": 420,
|
|
578
578
|
"origin": "kit"
|
|
579
579
|
},
|
|
@@ -582,7 +582,7 @@
|
|
|
582
582
|
"kind": "skill",
|
|
583
583
|
"ownerSkill": "setup-workflow",
|
|
584
584
|
"surface": "codex",
|
|
585
|
-
"sha256": "
|
|
585
|
+
"sha256": "e4cac2f46ac5f16e9a1473b38a64979a7d767ccdc834a2540d20c733305ea2e7",
|
|
586
586
|
"mode": 420,
|
|
587
587
|
"origin": "kit"
|
|
588
588
|
},
|
|
@@ -1499,7 +1499,7 @@
|
|
|
1499
1499
|
"kind": "skill",
|
|
1500
1500
|
"ownerSkill": "kit-update",
|
|
1501
1501
|
"surface": "claude",
|
|
1502
|
-
"sha256": "
|
|
1502
|
+
"sha256": "dbf2c0f696bd0ce45c5c2a315d40a3bbb99635e4ab384c788ac59f799fbf43de",
|
|
1503
1503
|
"mode": 420,
|
|
1504
1504
|
"origin": "kit"
|
|
1505
1505
|
},
|
|
@@ -1751,7 +1751,7 @@
|
|
|
1751
1751
|
"kind": "skill",
|
|
1752
1752
|
"ownerSkill": "setup-workflow",
|
|
1753
1753
|
"surface": "claude",
|
|
1754
|
-
"sha256": "
|
|
1754
|
+
"sha256": "8e223f625dcf941142005cbd9a4b6214358f68e3fd63cbee7bffef35b8b14ec8",
|
|
1755
1755
|
"mode": 420,
|
|
1756
1756
|
"origin": "kit"
|
|
1757
1757
|
},
|
|
@@ -1760,7 +1760,7 @@
|
|
|
1760
1760
|
"kind": "skill",
|
|
1761
1761
|
"ownerSkill": "setup-workflow",
|
|
1762
1762
|
"surface": "claude",
|
|
1763
|
-
"sha256": "
|
|
1763
|
+
"sha256": "e4cac2f46ac5f16e9a1473b38a64979a7d767ccdc834a2540d20c733305ea2e7",
|
|
1764
1764
|
"mode": 420,
|
|
1765
1765
|
"origin": "kit"
|
|
1766
1766
|
},
|
|
@@ -2303,7 +2303,7 @@
|
|
|
2303
2303
|
{
|
|
2304
2304
|
"path": "scripts/release-delta-guard.mjs",
|
|
2305
2305
|
"kind": "script",
|
|
2306
|
-
"sha256": "
|
|
2306
|
+
"sha256": "a51f30e7261e4efb0b5c5002f39353ef8e8be18a2bd39ad7139f1ce7a2e7e18d",
|
|
2307
2307
|
"mode": 420,
|
|
2308
2308
|
"origin": "kit"
|
|
2309
2309
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ikon85/agent-workflow-kit",
|
|
3
|
-
"version": "0.26.
|
|
3
|
+
"version": "0.26.1",
|
|
4
4
|
"description": "Portable AI-agent workflow skills (plan → execute → land → learn) for Claude Code & Codex — grilling, TDD, diagnosis, two-axis code review, cross-model Codex review, design & domain-modeling, plus a skill router (ask-matt). npx init/update/diff/uninstall.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/** Block shipped changes whose version or checked manifest does not match a fresh build. */
|
|
2
2
|
import { execFileSync } from 'node:child_process';
|
|
3
|
-
import { mkdtemp, readFile, rm } from 'node:fs/promises';
|
|
3
|
+
import { mkdir, mkdtemp, readFile, rm } from 'node:fs/promises';
|
|
4
4
|
import { tmpdir } from 'node:os';
|
|
5
5
|
import { dirname, join } from 'node:path';
|
|
6
6
|
import { fileURLToPath } from 'node:url';
|
|
7
|
+
import { sha256 } from '../src/lib/hash.mjs';
|
|
7
8
|
import { buildKit } from './build-kit.mjs';
|
|
8
9
|
|
|
9
10
|
const byPath = (manifest) => new Map(manifest.files.map((entry) => [entry.path, entry.sha256]));
|
|
@@ -41,6 +42,7 @@ function bumpKind(before, after) {
|
|
|
41
42
|
export function assessRelease(input) {
|
|
42
43
|
const delta = manifestDelta(input.baseManifest, input.builtManifest);
|
|
43
44
|
const checkedDrift = manifestDelta(input.checkedManifest, input.builtManifest);
|
|
45
|
+
const payloadDrift = manifestDelta(input.builtManifest, input.payloadManifest);
|
|
44
46
|
const errors = [];
|
|
45
47
|
if (hasDelta(delta) && input.currentVersion === input.baseVersion) {
|
|
46
48
|
errors.push(`shipped delta has no version bump (${describe(delta)}); version remains ${input.currentVersion}`);
|
|
@@ -52,6 +54,9 @@ export function assessRelease(input) {
|
|
|
52
54
|
if (checkedDrift.added.length || checkedDrift.changed.length) {
|
|
53
55
|
errors.push(`checked manifest is stale (${describe(checkedDrift)})`);
|
|
54
56
|
}
|
|
57
|
+
if (hasDelta(payloadDrift)) {
|
|
58
|
+
errors.push(`npm package payload does not match built manifest (${describe(payloadDrift)})`);
|
|
59
|
+
}
|
|
55
60
|
const recommendedBump = recommendBump(delta);
|
|
56
61
|
const actual = bumpKind(input.baseVersion, input.currentVersion);
|
|
57
62
|
const rank = { patch: 1, minor: 2, major: 3 };
|
|
@@ -68,18 +73,43 @@ function gitShowJson(repoRoot, ref, path) {
|
|
|
68
73
|
return JSON.parse(execFileSync('git', ['show', `${ref}:${path}`], { cwd: repoRoot, encoding: 'utf8' }));
|
|
69
74
|
}
|
|
70
75
|
|
|
76
|
+
export async function packedPayloadManifest({ repoRoot, manifest }) {
|
|
77
|
+
const tempRoot = await mkdtemp(join(tmpdir(), 'awkit-package-payload-'));
|
|
78
|
+
try {
|
|
79
|
+
const packOutput = execFileSync(
|
|
80
|
+
'npm', ['pack', '--json', '--ignore-scripts', '--pack-destination', tempRoot],
|
|
81
|
+
{ cwd: repoRoot, encoding: 'utf8' },
|
|
82
|
+
);
|
|
83
|
+
const [{ filename }] = JSON.parse(packOutput);
|
|
84
|
+
const unpackDir = join(tempRoot, 'unpacked');
|
|
85
|
+
await mkdir(unpackDir);
|
|
86
|
+
execFileSync('tar', ['-xzf', join(tempRoot, filename), '-C', unpackDir]);
|
|
87
|
+
return {
|
|
88
|
+
kitVersion: manifest.kitVersion,
|
|
89
|
+
files: await Promise.all(manifest.files.map(async (entry) => ({
|
|
90
|
+
...entry,
|
|
91
|
+
sha256: sha256(await readFile(join(unpackDir, 'package', entry.path))),
|
|
92
|
+
}))),
|
|
93
|
+
};
|
|
94
|
+
} finally {
|
|
95
|
+
await rm(tempRoot, { recursive: true, force: true });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
71
99
|
export async function checkReleaseDelta({ repoRoot, baseRef = 'origin/main' } = {}) {
|
|
72
100
|
repoRoot ??= join(dirname(fileURLToPath(import.meta.url)), '..');
|
|
73
101
|
const distDir = await mkdtemp(join(tmpdir(), 'awkit-release-guard-'));
|
|
74
102
|
try {
|
|
75
103
|
await buildKit({ repoRoot, distDir });
|
|
76
104
|
const currentPackage = JSON.parse(await readFile(join(repoRoot, 'package.json'), 'utf8'));
|
|
105
|
+
const builtManifest = JSON.parse(await readFile(join(distDir, 'agent-workflow-kit.package.json'), 'utf8'));
|
|
77
106
|
return assessRelease({
|
|
78
107
|
baseVersion: gitShowJson(repoRoot, baseRef, 'package.json').version,
|
|
79
108
|
currentVersion: currentPackage.version,
|
|
80
109
|
baseManifest: gitShowJson(repoRoot, baseRef, 'agent-workflow-kit.package.json'),
|
|
81
110
|
checkedManifest: JSON.parse(await readFile(join(repoRoot, 'agent-workflow-kit.package.json'), 'utf8')),
|
|
82
|
-
builtManifest
|
|
111
|
+
builtManifest,
|
|
112
|
+
payloadManifest: await packedPayloadManifest({ repoRoot, manifest: builtManifest }),
|
|
83
113
|
});
|
|
84
114
|
} finally { await rm(distDir, { recursive: true, force: true }); }
|
|
85
115
|
}
|
|
@@ -1,8 +1,29 @@
|
|
|
1
1
|
import { test } from 'node:test';
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
|
-
import {
|
|
3
|
+
import { mkdtemp, readFile, rm } from 'node:fs/promises';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { dirname, join } from 'node:path';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
import { buildKit } from './build-kit.mjs';
|
|
8
|
+
import {
|
|
9
|
+
assessRelease, manifestDelta, packedPayloadManifest,
|
|
10
|
+
} from './release-delta-guard.mjs';
|
|
4
11
|
|
|
5
12
|
const file = (path, sha256) => ({ path, sha256 });
|
|
13
|
+
const repoRoot = join(dirname(fileURLToPath(import.meta.url)), '..');
|
|
14
|
+
|
|
15
|
+
test('fresh build manifest hashes match the actual npm package payload', async () => {
|
|
16
|
+
const tempRoot = await mkdtemp(join(tmpdir(), 'awkit-packed-manifest-test-'));
|
|
17
|
+
try {
|
|
18
|
+
const distDir = join(tempRoot, 'dist');
|
|
19
|
+
await buildKit({ repoRoot, distDir });
|
|
20
|
+
const built = JSON.parse(await readFile(join(distDir, 'agent-workflow-kit.package.json'), 'utf8'));
|
|
21
|
+
const packed = await packedPayloadManifest({ repoRoot, manifest: built });
|
|
22
|
+
assert.deepEqual(manifestDelta(built, packed), { added: [], removed: [], changed: [] });
|
|
23
|
+
} finally {
|
|
24
|
+
await rm(tempRoot, { recursive: true, force: true });
|
|
25
|
+
}
|
|
26
|
+
});
|
|
6
27
|
|
|
7
28
|
test('an unbumped shipped change is blocked with its concrete delta', () => {
|
|
8
29
|
const result = assessRelease({
|
|
@@ -10,6 +31,7 @@ test('an unbumped shipped change is blocked with its concrete delta', () => {
|
|
|
10
31
|
baseManifest: { kitVersion: '1.2.3', files: [file('skill.md', 'old')] },
|
|
11
32
|
builtManifest: { kitVersion: '1.2.3', files: [file('skill.md', 'new')] },
|
|
12
33
|
checkedManifest: { kitVersion: '1.2.3', files: [file('skill.md', 'old')] },
|
|
34
|
+
payloadManifest: { kitVersion: '1.2.3', files: [file('skill.md', 'new')] },
|
|
13
35
|
});
|
|
14
36
|
assert.equal(result.ok, false);
|
|
15
37
|
assert.deepEqual(result.delta.changed, ['skill.md']);
|
|
@@ -23,6 +45,7 @@ test('dead checked-manifest entries are rejected', () => {
|
|
|
23
45
|
baseManifest: { kitVersion: '1.2.3', files: [] },
|
|
24
46
|
builtManifest: { kitVersion: '1.3.0', files: [] },
|
|
25
47
|
checkedManifest: { kitVersion: '1.3.0', files: [file('gone.md', 'old')] },
|
|
48
|
+
payloadManifest: { kitVersion: '1.3.0', files: [] },
|
|
26
49
|
});
|
|
27
50
|
assert.equal(result.ok, false);
|
|
28
51
|
assert.match(result.errors.join('\n'), /dead entry: gone\.md/);
|
|
@@ -33,7 +56,7 @@ test('a matching regenerated manifest and semantic bump pass', () => {
|
|
|
33
56
|
const result = assessRelease({
|
|
34
57
|
baseVersion: '1.2.3', currentVersion: '1.3.0',
|
|
35
58
|
baseManifest: { kitVersion: '1.2.3', files: [] }, builtManifest: current,
|
|
36
|
-
checkedManifest: current,
|
|
59
|
+
checkedManifest: current, payloadManifest: current,
|
|
37
60
|
});
|
|
38
61
|
assert.equal(result.ok, true);
|
|
39
62
|
assert.equal(result.recommendedBump, 'minor');
|
|
@@ -45,9 +68,20 @@ test('shipped content rejects downgrade and malformed version transitions', () =
|
|
|
45
68
|
const result = assessRelease({
|
|
46
69
|
baseVersion: '1.2.3', currentVersion,
|
|
47
70
|
baseManifest: { kitVersion: '1.2.3', files: [file('skill.md', 'old')] },
|
|
48
|
-
builtManifest: current, checkedManifest: current,
|
|
71
|
+
builtManifest: current, checkedManifest: current, payloadManifest: current,
|
|
49
72
|
});
|
|
50
73
|
assert.equal(result.ok, false, currentVersion);
|
|
51
74
|
assert.match(result.errors.join('\n'), /invalid version transition: 1\.2\.3 ->/);
|
|
52
75
|
}
|
|
53
76
|
});
|
|
77
|
+
|
|
78
|
+
test('actual npm payload drift is blocked even when checked and built manifests match', () => {
|
|
79
|
+
const scrubbed = { kitVersion: '1.2.3', files: [file('skill.md', 'scrubbed')] };
|
|
80
|
+
const result = assessRelease({
|
|
81
|
+
baseVersion: '1.2.3', currentVersion: '1.2.3',
|
|
82
|
+
baseManifest: scrubbed, builtManifest: scrubbed, checkedManifest: scrubbed,
|
|
83
|
+
payloadManifest: { kitVersion: '1.2.3', files: [file('skill.md', 'source')] },
|
|
84
|
+
});
|
|
85
|
+
assert.equal(result.ok, false);
|
|
86
|
+
assert.match(result.errors.join('\n'), /npm package payload.*changed: skill\.md/);
|
|
87
|
+
});
|