agentic-workflow-manager 9.7.0 → 9.7.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.
Files changed (41) hide show
  1. package/dist/src/commands/doctor.js +4 -2
  2. package/dist/src/commands/export.js +3 -0
  3. package/dist/src/commands/init.js +10 -2
  4. package/dist/src/commands/registry/add.js +2 -2
  5. package/dist/src/commands/registry/index.js +7 -5
  6. package/dist/src/commands/registry/install-bundles.js +4 -4
  7. package/dist/src/commands/registry/status.js +4 -4
  8. package/dist/src/commands/sync.js +2 -1
  9. package/dist/src/commands/update.js +3 -1
  10. package/dist/src/core/bundle-install.js +2 -2
  11. package/dist/src/core/bundles.js +92 -19
  12. package/dist/src/core/diagnostics/context.js +1 -1
  13. package/dist/src/core/export/index.js +1 -1
  14. package/dist/src/core/export/resolve.js +2 -2
  15. package/dist/src/core/reconciliation.js +1 -1
  16. package/dist/src/index.js +46 -39
  17. package/dist/src/utils/grouping.js +1 -1
  18. package/dist/src/utils/registry-view.js +1 -1
  19. package/dist/tests/commands/bundle-diagnostics.test.js +145 -0
  20. package/dist/tests/commands/export.test.js +28 -2
  21. package/dist/tests/commands/multi-agent-targeting.test.js +1 -1
  22. package/dist/tests/core/bundle-install.test.js +36 -0
  23. package/dist/tests/core/bundles-multiroot.test.js +24 -2
  24. package/dist/tests/core/bundles-overrides.test.js +10 -7
  25. package/dist/tests/core/bundles.test.js +75 -6
  26. package/dist/tests/core/diagnostics/context.test.js +2 -2
  27. package/dist/tests/core/export/engine.test.js +1 -1
  28. package/dist/tests/core/export/resolve.test.js +1 -1
  29. package/dist/tests/core/init/mutation-targets.test.js +1 -1
  30. package/dist/tests/core/init/orchestrator.test.js +1 -1
  31. package/dist/tests/core/init/steps.test.js +1 -1
  32. package/dist/tests/core/reconciliation.test.js +1 -1
  33. package/dist/tests/core/sync-profile-atomicity.test.js +1 -1
  34. package/dist/tests/structural/bundle-skill-reference-contract.test.js +56 -0
  35. package/dist/tests/utils/grouping.test.js +2 -2
  36. package/dist/tests/utils/registry-view.test.js +7 -7
  37. package/package.json +1 -1
  38. package/prebuilds/darwin-arm64/secure_fs.node +0 -0
  39. package/prebuilds/darwin-x64/secure_fs.node +0 -0
  40. package/prebuilds/win32-arm64/secure_fs.node +0 -0
  41. package/prebuilds/win32-x64/secure_fs.node +0 -0
@@ -21,7 +21,7 @@ function makeFixture() {
21
21
  }));
22
22
  fs_1.default.writeFileSync(path_1.default.join(content, 'bundles', 'dev', 'bundle.json'), JSON.stringify({
23
23
  name: 'dev', version: '1.0.0', description: 'Dev core', scope: 'baseline', dependsOn: [],
24
- skills: ['brainstorming', { name: 'architecture-advisor', onSignal: true }],
24
+ skills: ['brainstorming', 'architecture-advisor'],
25
25
  workflows: ['development-process'], agents: ['development-process'],
26
26
  }));
27
27
  fs_1.default.writeFileSync(path_1.default.join(content, 'bundles', 'frontend', 'bundle.json'), JSON.stringify({
@@ -47,17 +47,86 @@ describe('readCatalog', () => {
47
47
  });
48
48
  });
49
49
  describe('discoverBundles', () => {
50
- it('loads each bundle and normalizes skill refs (string | object)', () => {
50
+ it('loads each bundle with canonical skill names', () => {
51
51
  const content = makeFixture();
52
52
  const bundles = (0, bundles_1.discoverBundles)(content);
53
53
  const dev = bundles.find((b) => b.name === 'dev');
54
- expect(dev.skills).toEqual([
55
- { name: 'brainstorming', onSignal: false },
56
- { name: 'architecture-advisor', onSignal: true },
57
- ]);
54
+ expect(dev.skills).toEqual(['brainstorming', 'architecture-advisor']);
58
55
  expect(dev.scope).toBe('baseline');
59
56
  expect(dev.dependsOn).toEqual([]);
60
57
  });
58
+ it('canonicalizes legacy skill objects and reports one bounded migration diagnostic per manifest', () => {
59
+ const content = makeFixture();
60
+ const manifest = path_1.default.join(content, 'bundles', 'dev', 'bundle.json');
61
+ fs_1.default.writeFileSync(manifest, JSON.stringify({
62
+ name: 'dev', version: '1.0.0', scope: 'baseline',
63
+ skills: [
64
+ { name: 'brainstorming', onSignal: true },
65
+ { name: 'architecture-advisor', onSignal: false },
66
+ ],
67
+ }));
68
+ const { inspectBundles } = require('../../src/core/bundles');
69
+ const result = inspectBundles(content);
70
+ expect(result.bundles[0].skills).toEqual(['brainstorming', 'architecture-advisor']);
71
+ expect(result.diagnostics).toHaveLength(1);
72
+ expect(result.diagnostics[0]).toContain('bundle.json');
73
+ expect(result.diagnostics[0]).toContain('skills: ["skill-name"]');
74
+ expect(result.diagnostics[0]).toMatch(/v10/i);
75
+ expect(result.diagnostics[0]).not.toMatch(/[\x00-\x1f\x7f-\x9f]/);
76
+ expect(result.diagnostics[0]).not.toContain('{"name"');
77
+ expect(result.diagnostics[0].length).toBeLessThanOrEqual(512);
78
+ });
79
+ it('forwards one deduplicated legacy diagnostic to an injected reporter', () => {
80
+ const content = makeFixture();
81
+ const manifest = path_1.default.join(content, 'bundles', 'dev', 'bundle.json');
82
+ fs_1.default.writeFileSync(manifest, JSON.stringify({
83
+ name: 'dev', version: '1.0.0', scope: 'baseline',
84
+ skills: [{ name: 'brainstorming', onSignal: true }, { name: 'architecture-advisor', onSignal: false }],
85
+ }));
86
+ const diagnostics = [];
87
+ const bundles = (0, bundles_1.discoverBundles)(content, (diagnostic) => diagnostics.push(diagnostic));
88
+ expect(bundles[0].skills).toEqual(['brainstorming', 'architecture-advisor']);
89
+ expect(diagnostics).toHaveLength(1);
90
+ });
91
+ it('creates a command-scoped warning reporter that suppresses repeated diagnostics', () => {
92
+ const warnings = [];
93
+ const report = (0, bundles_1.createBundleDiagnosticReporter)((warning) => warnings.push(warning));
94
+ report('legacy warning');
95
+ report('legacy warning');
96
+ expect(warnings).toEqual(['warning: legacy warning']);
97
+ });
98
+ it('sanitizes, bounds, and then deduplicates caller-provided diagnostics', () => {
99
+ const warnings = [];
100
+ const report = (0, bundles_1.createBundleDiagnosticReporter)((warning) => warnings.push(warning));
101
+ const payload = `legacy\x1b\n${'x'.repeat(600)}`;
102
+ report(payload);
103
+ report(`legacy ${'x'.repeat(600)}`);
104
+ expect(warnings).toHaveLength(1);
105
+ expect(warnings[0]).toMatch(/^warning: legacy /);
106
+ expect(warnings[0]).not.toMatch(/[\x00-\x1f\x7f-\x9f]/);
107
+ expect(warnings[0].length).toBeLessThanOrEqual('warning: '.length + 512);
108
+ });
109
+ it.each([
110
+ ['missing skills', undefined, /skills/],
111
+ ['a non-array skills value', 'brainstorming', /skills/],
112
+ ['an empty string skill', [''], /skills\[0\]/],
113
+ ['an array skill', [['brainstorming']], /skills\[0\]/],
114
+ ['a null skill', [null], /skills\[0\]/],
115
+ ['an object missing name', [{ onSignal: true }], /skills\[0\]/],
116
+ ['an object with an empty name', [{ name: '', onSignal: true }], /skills\[0\]/],
117
+ ['an object with a non-string name', [{ name: 1, onSignal: true }], /skills\[0\]/],
118
+ ['an object with a non-boolean onSignal', [{ name: 'brainstorming', onSignal: 'true' }], /skills\[0\]/],
119
+ ['an object with an unknown key', [{ name: 'brainstorming', extra: true }], /skills\[0\]/],
120
+ ])('rejects %s with a safe manifest identity', (_case, skills, location) => {
121
+ const content = makeFixture();
122
+ const manifest = path_1.default.join(content, 'bundles', 'dev', 'bundle.json');
123
+ const bundle = { name: 'dev', version: '1.0.0', scope: 'baseline' };
124
+ if (skills !== undefined)
125
+ bundle.skills = skills;
126
+ fs_1.default.writeFileSync(manifest, JSON.stringify(bundle));
127
+ expect(() => (0, bundles_1.discoverBundles)(content)).toThrow(/bundle\.json/);
128
+ expect(() => (0, bundles_1.discoverBundles)(content)).toThrow(location);
129
+ });
61
130
  it('returns [] when catalog is missing', () => {
62
131
  const empty = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'awm-empty-'));
63
132
  expect((0, bundles_1.discoverBundles)(empty)).toEqual([]);
@@ -9,7 +9,7 @@ const path_1 = __importDefault(require("path"));
9
9
  function bundle(name, scope, skills) {
10
10
  return {
11
11
  name, description: '', version: '1.0.0', scope, visibility: 'public',
12
- dependsOn: [], skills: skills.map((s) => ({ name: s, onSignal: false })),
12
+ dependsOn: [], skills,
13
13
  workflows: [], agents: [],
14
14
  };
15
15
  }
@@ -86,7 +86,7 @@ describe('gatherContext', () => {
86
86
  it('machine: devCore surfaces a missing per-agent artifact even when the shared skill link is already present', () => {
87
87
  const bundleWithAgent = () => ({
88
88
  name: 'dev-core', description: '', version: '1.0.0', scope: 'baseline', visibility: 'public',
89
- dependsOn: [], skills: [{ name: 'development-process', onSignal: false }],
89
+ dependsOn: [], skills: ['development-process'],
90
90
  workflows: [], agents: ['development-process'],
91
91
  });
92
92
  // Simulate OpenCode having already linked the shared ~/.agents/skills dir.
@@ -21,7 +21,7 @@ function makeRoot() {
21
21
  }));
22
22
  fs_1.default.writeFileSync(path_1.default.join(root, 'bundles/dev/bundle.json'), JSON.stringify({
23
23
  name: 'dev', version: '1.0.0', scope: 'baseline', dependsOn: [],
24
- skills: ['proc-skill', { name: 'mermaid', onSignal: true }, { name: 'ported', onSignal: true }],
24
+ skills: ['proc-skill', 'mermaid', 'ported'],
25
25
  workflows: [], agents: [],
26
26
  }));
27
27
  const mk = (name, fm) => {
@@ -17,7 +17,7 @@ function makeRoot() {
17
17
  }));
18
18
  fs_1.default.writeFileSync(path_1.default.join(root, 'bundles/dev/bundle.json'), JSON.stringify({
19
19
  name: 'dev', version: '1.0.0', scope: 'baseline', dependsOn: [],
20
- skills: ['proc-skill', { name: 'mermaid', onSignal: true }, { name: 'ported', onSignal: true }],
20
+ skills: ['proc-skill', 'mermaid', 'ported'],
21
21
  workflows: [], agents: [],
22
22
  }));
23
23
  const mk = (name, fm) => {
@@ -23,7 +23,7 @@ const tmp_1 = require("../../support/tmp");
23
23
  function bundle(name, scope, skills) {
24
24
  return {
25
25
  name, description: '', version: '1.0.0', scope, visibility: 'public',
26
- dependsOn: [], skills: skills.map((s) => ({ name: s, onSignal: false })),
26
+ dependsOn: [], skills,
27
27
  workflows: [], agents: [],
28
28
  };
29
29
  }
@@ -31,7 +31,7 @@ function seedRegistry(contentRoot) {
31
31
  fs_1.default.mkdirSync(path_1.default.join(contentRoot, 'bundles', 'dev'), { recursive: true });
32
32
  fs_1.default.writeFileSync(path_1.default.join(contentRoot, 'bundles', 'dev', 'bundle.json'), JSON.stringify({
33
33
  name: 'dev', version: '1.0.0', scope: 'baseline', dependsOn: [],
34
- skills: [{ name: 'brainstorming' }, { name: 'using-awm' }], workflows: [], agents: [],
34
+ skills: ['brainstorming', 'using-awm'], workflows: [], agents: [],
35
35
  }));
36
36
  // Seed .git in the content root so gatherMachine.registryCache.present is true.
37
37
  fs_1.default.mkdirSync(path_1.default.join(contentRoot, '.git'), { recursive: true });
@@ -12,7 +12,7 @@ const context_1 = require("../../../src/core/diagnostics/context");
12
12
  function bundle(name, scope, skills) {
13
13
  return {
14
14
  name, description: '', version: '1.0.0', scope, visibility: 'public',
15
- dependsOn: [], skills: skills.map((s) => ({ name: s, onSignal: false })),
15
+ dependsOn: [], skills,
16
16
  workflows: [], agents: [],
17
17
  };
18
18
  }
@@ -17,7 +17,7 @@ function writeBundle(contentRoot, name, scope, skills, dependsOn = []) {
17
17
  fs_1.default.mkdirSync(path_1.default.join(contentRoot, 'bundles', name), { recursive: true });
18
18
  fs_1.default.writeFileSync(path_1.default.join(contentRoot, 'bundles', name, 'bundle.json'), JSON.stringify({
19
19
  name, version: '1.0.0', scope, dependsOn,
20
- skills: skills.map((s) => ({ name: s })), workflows: [], agents: [],
20
+ skills, workflows: [], agents: [],
21
21
  }));
22
22
  for (const skill of skills) {
23
23
  fs_1.default.mkdirSync(path_1.default.join(contentRoot, 'skills', skill), { recursive: true });
@@ -27,7 +27,7 @@ const bundle_install_1 = require("../../src/core/bundle-install");
27
27
  function bundle(name, skills, contentRoot) {
28
28
  return {
29
29
  name, description: '', version: '1.0.0', scope: 'project', visibility: 'public',
30
- dependsOn: [], skills: skills.map(n => ({ name: n })), workflows: [], agents: [],
30
+ dependsOn: [], skills, workflows: [], agents: [],
31
31
  source: `bundles/${name}`, contentRoot,
32
32
  };
33
33
  }
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const fs_1 = __importDefault(require("fs"));
7
+ const path_1 = __importDefault(require("path"));
8
+ const ROOT = path_1.default.resolve(__dirname, '../../..');
9
+ const BUNDLES_SOURCE = path_1.default.join(ROOT, 'cli/src/core/bundles.ts');
10
+ const GUIDE = path_1.default.join(ROOT, 'docs/guides/authoring-a-registry-with-an-orchestrator.md');
11
+ const read = (file) => fs_1.default.readFileSync(file, 'utf8');
12
+ function sourceFiles(dir) {
13
+ return fs_1.default.readdirSync(dir, { withFileTypes: true }).flatMap((entry) => {
14
+ const file = path_1.default.join(dir, entry.name);
15
+ if (entry.isDirectory())
16
+ return sourceFiles(file);
17
+ return entry.isFile() && file.endsWith('.ts') ? [file] : [];
18
+ });
19
+ }
20
+ function jsonFences(markdown) {
21
+ return Array.from(markdown.matchAll(/```json\n([\s\S]*?)\n```/g), ([, json]) => json);
22
+ }
23
+ describe('bundle skill-reference contract', () => {
24
+ it('keeps active bundle consumers string-only, without BundleSkillRef', () => {
25
+ const source = read(BUNDLES_SOURCE);
26
+ const bundleSkillRefs = sourceFiles(path_1.default.join(ROOT, 'cli/src'))
27
+ .filter((file) => /\bBundleSkillRef\b/.test(read(file)));
28
+ expect(bundleSkillRefs).toEqual([]);
29
+ expect(source).toMatch(/interface BundleDefinition\s*\{[\s\S]*?\bskills:\s*string\[\];/);
30
+ });
31
+ it('uses canonical string skill references in the active authoring guide', () => {
32
+ const guide = read(GUIDE);
33
+ const bundleExample = jsonFences(guide).find((json) => JSON.parse(json).name === 'mi-proceso');
34
+ expect(bundleExample).toBeDefined();
35
+ const skills = JSON.parse(bundleExample).skills;
36
+ expect(skills).toEqual(['mi-proceso']);
37
+ expect(skills.every((skill) => typeof skill === 'string')).toBe(true);
38
+ });
39
+ it('places the CLI 9 migration boundary immediately after the bundle example', () => {
40
+ const guide = read(GUIDE);
41
+ const bundleFence = '```json\n{\n "name": "mi-proceso"';
42
+ const start = guide.indexOf(bundleFence);
43
+ const end = guide.indexOf('```', start) + 3;
44
+ const following = guide.slice(end, guide.indexOf('## 5.', end));
45
+ expect(start).toBeGreaterThanOrEqual(0);
46
+ expect(following).toMatch(/CLI 9[\s\S]*(?:legacy object|objeto legacy)[\s\S]*\{\s*name\s*,\s*onSignal:\s*true\s*\}/i);
47
+ expect(following).toMatch(/ignor(?:e|a)[\s\S]*boolean[\s\S]*onSignal/i);
48
+ expect(following).toMatch(/warning/i);
49
+ expect(following).toMatch(/CLI 10[\s\S]*(?:rejects?|rechaza)[\s\S]*(?:objects?|objetos?)/i);
50
+ });
51
+ it('keeps every JSON fence in the touched guide parseable', () => {
52
+ for (const json of jsonFences(read(GUIDE))) {
53
+ expect(() => JSON.parse(json)).not.toThrow();
54
+ }
55
+ });
56
+ });
@@ -7,10 +7,10 @@ const bundle = (over) => ({
7
7
  });
8
8
  const processes = [
9
9
  bundle({ name: 'core-dev', description: 'Core development skills',
10
- skills: [{ name: 'brainstorming', onSignal: false }, { name: 'shared-skill', onSignal: false }],
10
+ skills: ['brainstorming', 'shared-skill'],
11
11
  workflows: [], agents: [] }),
12
12
  bundle({ name: 'docs', description: 'Documentation skills',
13
- skills: [{ name: 'docs-assistant', onSignal: false }, { name: 'shared-skill', onSignal: false }],
13
+ skills: ['docs-assistant', 'shared-skill'],
14
14
  workflows: [], agents: [] }),
15
15
  ];
16
16
  const formatLabel = (c) => c.baseName;
@@ -10,10 +10,10 @@ const bundle = (over) => ({
10
10
  });
11
11
  const processes = [
12
12
  bundle({ name: 'core-dev', description: 'Dev lifecycle', scope: 'baseline',
13
- skills: [{ name: 'brainstorming', onSignal: false }, { name: 'shared', onSignal: false }],
13
+ skills: ['brainstorming', 'shared'],
14
14
  workflows: ['exec'], agents: ['plan'] }),
15
15
  bundle({ name: 'docs', description: 'Docs as code',
16
- skills: [{ name: 'shared', onSignal: false }] }),
16
+ skills: ['shared'] }),
17
17
  ];
18
18
  describe('buildPackageView', () => {
19
19
  it('groups artifacts under their package with correct counts and install names', () => {
@@ -108,7 +108,7 @@ describe('visibility', () => {
108
108
  it('marks a private bundle on its PackageView', () => {
109
109
  const priv = [
110
110
  bundle({ name: 'secret', description: 'private', visibility: 'private',
111
- skills: [{ name: 'a', onSignal: false }] }),
111
+ skills: ['a'] }),
112
112
  ];
113
113
  const view = (0, registry_view_1.buildPackageView)([skill('a')], [], [], priv);
114
114
  expect(view.find((p) => p.name === 'secret').visibility).toBe('private');
@@ -121,7 +121,7 @@ describe('visibility', () => {
121
121
  describe('packageSummaryLines width-awareness', () => {
122
122
  it('truncates the description column when a width is given', () => {
123
123
  const view = (0, registry_view_1.buildPackageView)([skill('brainstorming', 'x'.repeat(200))], [], [], [
124
- bundle({ name: 'core-dev', description: 'x'.repeat(200), skills: [{ name: 'brainstorming', onSignal: false }] }),
124
+ bundle({ name: 'core-dev', description: 'x'.repeat(200), skills: ['brainstorming'] }),
125
125
  ]);
126
126
  const lines = (0, registry_view_1.packageSummaryLines)(view, 60);
127
127
  for (const l of lines)
@@ -130,7 +130,7 @@ describe('packageSummaryLines width-awareness', () => {
130
130
  });
131
131
  it('does not truncate when no width is given (piped output)', () => {
132
132
  const view = (0, registry_view_1.buildPackageView)([skill('brainstorming', 'y'.repeat(200))], [], [], [
133
- bundle({ name: 'core-dev', description: 'z'.repeat(200), skills: [{ name: 'brainstorming', onSignal: false }] }),
133
+ bundle({ name: 'core-dev', description: 'z'.repeat(200), skills: ['brainstorming'] }),
134
134
  ]);
135
135
  const lines = (0, registry_view_1.packageSummaryLines)(view); // no width
136
136
  expect(lines.some((l) => l.includes('z'.repeat(200)))).toBe(true);
@@ -139,7 +139,7 @@ describe('packageSummaryLines width-awareness', () => {
139
139
  describe('artifactPickerItems', () => {
140
140
  it('prepends an "install entire package" sentinel item, then one per artifact', () => {
141
141
  const view = (0, registry_view_1.buildPackageView)([skill('a', 'desc a'), skill('b', 'desc b')], [], [], [
142
- bundle({ name: 'p', description: 'pkg', skills: [{ name: 'a', onSignal: false }, { name: 'b', onSignal: false }] }),
142
+ bundle({ name: 'p', description: 'pkg', skills: ['a', 'b'] }),
143
143
  ]);
144
144
  const items = (0, registry_view_1.artifactPickerItems)(view.find((p) => p.name === 'p'));
145
145
  expect(items[0].value).toBe(registry_view_1.ALL_SENTINEL);
@@ -150,7 +150,7 @@ describe('artifactPickerItems', () => {
150
150
  describe('packagePickerItems', () => {
151
151
  it('builds one item per package with a count+description summary', () => {
152
152
  const view = (0, registry_view_1.buildPackageView)([skill('a')], [], [], [
153
- bundle({ name: 'p', description: 'pkg desc', skills: [{ name: 'a', onSignal: false }] }),
153
+ bundle({ name: 'p', description: 'pkg desc', skills: ['a'] }),
154
154
  ]);
155
155
  const items = (0, registry_view_1.packagePickerItems)(view);
156
156
  expect(items[0].value).toBe('p');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentic-workflow-manager",
3
- "version": "9.7.0",
3
+ "version": "9.7.1",
4
4
  "main": "dist/src/index.js",
5
5
  "bin": {
6
6
  "awm": "./dist/src/index.js"
Binary file
Binary file