@ikon85/agent-workflow-kit 0.27.0 → 0.27.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/README.md CHANGED
@@ -330,6 +330,10 @@ still reference. Flags: `--force` (overwrite pre-existing files on `init`),
330
330
 
331
331
  ## Release notes
332
332
 
333
+ ### 0.27.1
334
+
335
+ - changed: `scripts/release-parity.mjs`
336
+
333
337
  ### 0.27.0
334
338
 
335
339
  - removed: `.agents/skills/zoom-out/SKILL.md`
@@ -1,5 +1,5 @@
1
1
  {
2
- "kitVersion": "0.27.0",
2
+ "kitVersion": "0.27.1",
3
3
  "files": [
4
4
  {
5
5
  "path": ".agents/skills/ask-matt/SKILL.md",
@@ -2539,7 +2539,7 @@
2539
2539
  "path": "scripts/release-parity.mjs",
2540
2540
  "kind": "script",
2541
2541
  "installRole": "consumer",
2542
- "sha256": "faa4a1d1116d58f69c8541a586ebd6076ebc3f7af7809e9e4a53928f49298365",
2542
+ "sha256": "32f61bf776974be63a017e90f428d4dab6098a9e2ba0c25c865d18ea3bd3e89e",
2543
2543
  "mode": 420,
2544
2544
  "origin": "kit"
2545
2545
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ikon85/agent-workflow-kit",
3
- "version": "0.27.0",
3
+ "version": "0.27.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": {
@@ -50,3 +50,37 @@ export function assertReleaseParity(identities) {
50
50
  }
51
51
  return identities.local;
52
52
  }
53
+
54
+ // Consumer-side identity: an unpacked installation cannot be re-packed
55
+ // byte-identically (npm normalizes at publish/unpack), so the installed copy
56
+ // proves itself by content — never by a fresh tarball hash.
57
+ const INSTALLED_FIELDS = ['name', 'version', 'manifestSha256'];
58
+
59
+ export async function installedIdentityFromDir(kitRoot) {
60
+ const packageJson = JSON.parse(await readFile(`${kitRoot}/package.json`, 'utf8'));
61
+ const manifest = await readFile(`${kitRoot}/agent-workflow-kit.package.json`);
62
+ if (packageJson.version !== JSON.parse(manifest).kitVersion) {
63
+ throw new Error('installed package and manifest versions mismatch');
64
+ }
65
+ return {
66
+ name: packageJson.name,
67
+ version: packageJson.version,
68
+ manifestSha256: digest('sha256', manifest),
69
+ };
70
+ }
71
+
72
+ export function assertConsumerReleaseParity({ installed, npm, github }) {
73
+ validate('npm', npm);
74
+ validate('github', github);
75
+ for (const field of FIELDS) {
76
+ if (github[field] !== npm[field]) throw new Error(`github ${field} mismatch`);
77
+ }
78
+ if (!installed || typeof installed !== 'object') throw new Error('installed release identity missing');
79
+ for (const field of INSTALLED_FIELDS) {
80
+ if (typeof installed[field] !== 'string' || !installed[field]) {
81
+ throw new Error(`installed ${field} missing`);
82
+ }
83
+ if (installed[field] !== npm[field]) throw new Error(`installed ${field} mismatch`);
84
+ }
85
+ return npm;
86
+ }
@@ -51,3 +51,40 @@ test('a package tarball produces a deterministic content identity', async () =>
51
51
  assert.deepEqual(first, second);
52
52
  } finally { await rm(root, { recursive: true, force: true }); }
53
53
  });
54
+
55
+ test('consumer parity proves npm↔github and matches the installed copy without re-packing', async () => {
56
+ const { assertConsumerReleaseParity } = await import('./release-parity.mjs');
57
+ const installed = { name: identity.name, version: identity.version, manifestSha256: identity.manifestSha256 };
58
+ assert.deepEqual(
59
+ assertConsumerReleaseParity({ installed, npm: { ...identity }, github: { ...identity } }),
60
+ identity,
61
+ );
62
+ assert.throws(() => assertConsumerReleaseParity({
63
+ installed, npm: { ...identity }, github: { ...identity, tarballIntegrity: 'different' },
64
+ }), /github tarballIntegrity mismatch/);
65
+ assert.throws(() => assertConsumerReleaseParity({
66
+ installed: { ...installed, manifestSha256: 'different' },
67
+ npm: { ...identity }, github: { ...identity },
68
+ }), /installed manifestSha256 mismatch/);
69
+ assert.throws(() => assertConsumerReleaseParity({
70
+ installed: { ...installed, version: '9.9.9' },
71
+ npm: { ...identity }, github: { ...identity },
72
+ }), /installed version mismatch/);
73
+ });
74
+
75
+ test('an installed kit directory yields a content identity without npm pack', async () => {
76
+ const { installedIdentityFromDir } = await import('./release-parity.mjs');
77
+ const root = await mkdtemp(join(tmpdir(), 'release-parity-installed-'));
78
+ try {
79
+ const manifest = JSON.stringify({ kitVersion: '1.2.3', files: [] });
80
+ await writeFile(join(root, 'package.json'), JSON.stringify({ name: identity.name, version: '1.2.3' }));
81
+ await writeFile(join(root, 'agent-workflow-kit.package.json'), manifest);
82
+ const installed = await installedIdentityFromDir(root);
83
+ assert.equal(installed.name, identity.name);
84
+ assert.equal(installed.version, '1.2.3');
85
+ assert.equal(typeof installed.manifestSha256, 'string');
86
+ assert.equal('tarballIntegrity' in installed, false);
87
+ } finally {
88
+ await rm(root, { recursive: true, force: true });
89
+ }
90
+ });
package/src/cli.mjs CHANGED
@@ -7,6 +7,7 @@ import { update } from './commands/update.mjs';
7
7
  import { diff } from './commands/diff.mjs';
8
8
  import { uninstall } from './commands/uninstall.mjs';
9
9
  import { createCommandAdapter } from '../scripts/release-state.mjs';
10
+ import { installedIdentityFromDir } from '../scripts/release-parity.mjs';
10
11
 
11
12
  const KIT_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..');
12
13
  const consumerRoot = process.cwd();
@@ -87,8 +88,10 @@ async function readUpdateRelease() {
87
88
  env: { ...process.env, GH_REPO: 'iKon85/agent-workflow-kit' },
88
89
  });
89
90
  try {
90
- const local = (await adapter.local()).identity;
91
- return { local, npm: await adapter.npm(local), github: await adapter.github(local) };
91
+ // Re-packing an unpacked install is never byte-identical to the registry
92
+ // tarball the installed copy proves itself by content identity instead.
93
+ const installed = await installedIdentityFromDir(KIT_ROOT);
94
+ return { installed, npm: await adapter.npm(installed), github: await adapter.github(installed) };
92
95
  } finally {
93
96
  await adapter.dispose();
94
97
  }
@@ -1,6 +1,6 @@
1
1
  import { readFile, rm } from 'node:fs/promises';
2
2
  import { join } from 'node:path';
3
- import { assertReleaseParity } from '../../scripts/release-parity.mjs';
3
+ import { assertConsumerReleaseParity } from '../../scripts/release-parity.mjs';
4
4
  import { activateCandidate, stageConsumer, verifyCandidate } from '../lib/updateCandidate.mjs';
5
5
  import { reconcile } from '../lib/updateReconcile.mjs';
6
6
  import {
@@ -86,7 +86,7 @@ async function applyTransaction(context) {
86
86
  }
87
87
 
88
88
  function verifyRelease(identities, kitVersion) {
89
- const release = assertReleaseParity(identities);
89
+ const release = assertConsumerReleaseParity(identities);
90
90
  if (release.name !== RELEASE_NAME) throw new Error(`invalid release origin: ${release.name}`);
91
91
  if (release.version !== kitVersion) {
92
92
  throw new Error(`release version ${release.version} does not match kit ${kitVersion}`);