agentic-workflow-manager 8.2.1 → 8.3.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/dist/src/commands/hooks/claude.js +47 -31
- package/dist/src/commands/hooks/index.js +1 -1
- package/dist/src/commands/hooks/shared.js +1 -1
- package/dist/src/commands/registry/add.js +6 -1
- package/dist/src/commands/registry/index.js +3 -0
- package/dist/src/core/context/orchestrator.js +15 -2
- package/dist/src/core/context/provider.js +29 -1
- package/dist/src/core/context/regenerate.js +29 -0
- package/dist/src/core/orchestrators.js +142 -0
- package/dist/tests/commands/hooks/install-symlink-fallback.test.js +13 -4
- package/dist/tests/commands/hooks/install.test.js +121 -3
- package/dist/tests/commands/hooks/resync.test.js +40 -2
- package/dist/tests/commands/registry/add.test.js +104 -0
- package/dist/tests/core/context/orchestrator.test.js +86 -0
- package/dist/tests/core/context/provider.test.js +137 -0
- package/dist/tests/core/context/regenerate.test.js +56 -0
- package/dist/tests/core/orchestrators.test.js +236 -0
- package/package.json +1 -1
|
@@ -0,0 +1,236 @@
|
|
|
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 os_1 = __importDefault(require("os"));
|
|
9
|
+
const orchestrators_1 = require("../../src/core/orchestrators");
|
|
10
|
+
const registries_1 = require("../../src/core/registries");
|
|
11
|
+
function registryWith(manifest) {
|
|
12
|
+
const root = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'awm-orch-'));
|
|
13
|
+
fs_1.default.writeFileSync(path_1.default.join(root, 'awm-registry.json'), JSON.stringify(manifest));
|
|
14
|
+
return root;
|
|
15
|
+
}
|
|
16
|
+
describe('readDeclaredOrchestrators', () => {
|
|
17
|
+
const created = [];
|
|
18
|
+
afterEach(() => {
|
|
19
|
+
for (const r of created.splice(0))
|
|
20
|
+
fs_1.default.rmSync(r, { recursive: true, force: true });
|
|
21
|
+
});
|
|
22
|
+
it('lee una declaracion valida', () => {
|
|
23
|
+
const root = registryWith({
|
|
24
|
+
minCliVersion: '8.1.5',
|
|
25
|
+
orchestrator: { name: 'mi-proceso', appliesWhen: 'cuando arranco una tarea', terminatesTo: 'development-process' },
|
|
26
|
+
});
|
|
27
|
+
created.push(root);
|
|
28
|
+
const { orchestrators, diagnostics } = (0, orchestrators_1.readDeclaredOrchestrators)(root);
|
|
29
|
+
expect(diagnostics).toEqual([]);
|
|
30
|
+
expect(orchestrators).toHaveLength(1);
|
|
31
|
+
expect(orchestrators[0]).toEqual({
|
|
32
|
+
name: 'mi-proceso',
|
|
33
|
+
appliesWhen: 'cuando arranco una tarea',
|
|
34
|
+
terminatesTo: 'development-process',
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
it('un registry sin bloque orchestrator no declara nada y no es un error', () => {
|
|
38
|
+
const root = registryWith({ minCliVersion: '8.1.5' });
|
|
39
|
+
created.push(root);
|
|
40
|
+
const { orchestrators, diagnostics } = (0, orchestrators_1.readDeclaredOrchestrators)(root);
|
|
41
|
+
expect(orchestrators).toEqual([]);
|
|
42
|
+
expect(diagnostics).toEqual([]);
|
|
43
|
+
});
|
|
44
|
+
it('un registry sin manifiesto no declara nada y no es un error', () => {
|
|
45
|
+
const root = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'awm-orch-'));
|
|
46
|
+
created.push(root);
|
|
47
|
+
const { orchestrators, diagnostics } = (0, orchestrators_1.readDeclaredOrchestrators)(root);
|
|
48
|
+
expect(orchestrators).toEqual([]);
|
|
49
|
+
expect(diagnostics).toEqual([]);
|
|
50
|
+
});
|
|
51
|
+
it('rechaza una declaracion malformada SIN lanzar, reportandola', () => {
|
|
52
|
+
const root = registryWith({ orchestrator: { name: 'mi-proceso' } }); // falta appliesWhen y terminatesTo
|
|
53
|
+
created.push(root);
|
|
54
|
+
const { orchestrators, diagnostics } = (0, orchestrators_1.readDeclaredOrchestrators)(root);
|
|
55
|
+
expect(orchestrators).toEqual([]);
|
|
56
|
+
expect(diagnostics).toHaveLength(1);
|
|
57
|
+
expect(diagnostics[0]).toMatch(/appliesWhen/);
|
|
58
|
+
});
|
|
59
|
+
it('rechaza un campo string en blanco (solo espacios) sin lanzar, reportandolo', () => {
|
|
60
|
+
const root = registryWith({ orchestrator: { name: 'x', appliesWhen: ' ', terminatesTo: 'none' } });
|
|
61
|
+
created.push(root);
|
|
62
|
+
const { orchestrators, diagnostics } = (0, orchestrators_1.readDeclaredOrchestrators)(root);
|
|
63
|
+
expect(orchestrators).toEqual([]);
|
|
64
|
+
expect(diagnostics).toHaveLength(1);
|
|
65
|
+
expect(diagnostics[0]).toMatch(/appliesWhen/);
|
|
66
|
+
});
|
|
67
|
+
it('orchestrator: null se rechaza SIN lanzar, reportandolo', () => {
|
|
68
|
+
const root = registryWith({ orchestrator: null });
|
|
69
|
+
created.push(root);
|
|
70
|
+
const { orchestrators, diagnostics } = (0, orchestrators_1.readDeclaredOrchestrators)(root);
|
|
71
|
+
expect(orchestrators).toEqual([]);
|
|
72
|
+
expect(diagnostics).toHaveLength(1);
|
|
73
|
+
expect(diagnostics[0]).toMatch(/must be an object/i);
|
|
74
|
+
});
|
|
75
|
+
it('orchestrator: [] (array) se rechaza SIN lanzar — prueba que Array.isArray se chequea, no solo === null', () => {
|
|
76
|
+
const root = registryWith({ orchestrator: [] });
|
|
77
|
+
created.push(root);
|
|
78
|
+
const { orchestrators, diagnostics } = (0, orchestrators_1.readDeclaredOrchestrators)(root);
|
|
79
|
+
expect(orchestrators).toEqual([]);
|
|
80
|
+
expect(diagnostics).toHaveLength(1);
|
|
81
|
+
expect(diagnostics[0]).toMatch(/must be an object/i);
|
|
82
|
+
});
|
|
83
|
+
it('orchestrator: 5 (numero) se rechaza SIN lanzar', () => {
|
|
84
|
+
const root = registryWith({ orchestrator: 5 });
|
|
85
|
+
created.push(root);
|
|
86
|
+
const { orchestrators, diagnostics } = (0, orchestrators_1.readDeclaredOrchestrators)(root);
|
|
87
|
+
expect(orchestrators).toEqual([]);
|
|
88
|
+
expect(diagnostics).toHaveLength(1);
|
|
89
|
+
expect(diagnostics[0]).toMatch(/must be an object/i);
|
|
90
|
+
});
|
|
91
|
+
it('orchestrator: "x" (string) se rechaza SIN lanzar', () => {
|
|
92
|
+
const root = registryWith({ orchestrator: 'x' });
|
|
93
|
+
created.push(root);
|
|
94
|
+
const { orchestrators, diagnostics } = (0, orchestrators_1.readDeclaredOrchestrators)(root);
|
|
95
|
+
expect(orchestrators).toEqual([]);
|
|
96
|
+
expect(diagnostics).toHaveLength(1);
|
|
97
|
+
expect(diagnostics[0]).toMatch(/must be an object/i);
|
|
98
|
+
});
|
|
99
|
+
it('rechaza un campo que excede la longitud maxima permitida', () => {
|
|
100
|
+
const root = registryWith({
|
|
101
|
+
orchestrator: { name: 'x'.repeat(501), appliesWhen: 'y', terminatesTo: 'none' },
|
|
102
|
+
});
|
|
103
|
+
created.push(root);
|
|
104
|
+
const { orchestrators, diagnostics } = (0, orchestrators_1.readDeclaredOrchestrators)(root);
|
|
105
|
+
expect(orchestrators).toEqual([]);
|
|
106
|
+
expect(diagnostics).toHaveLength(1);
|
|
107
|
+
expect(diagnostics[0]).toMatch(/"name"/);
|
|
108
|
+
expect(diagnostics[0]).toMatch(/500|maximum|exceeds/i);
|
|
109
|
+
});
|
|
110
|
+
it('acepta un campo justo en el limite de longitud maxima (500 caracteres)', () => {
|
|
111
|
+
const root = registryWith({
|
|
112
|
+
orchestrator: { name: 'x'.repeat(500), appliesWhen: 'y', terminatesTo: 'none' },
|
|
113
|
+
});
|
|
114
|
+
created.push(root);
|
|
115
|
+
const { orchestrators, diagnostics } = (0, orchestrators_1.readDeclaredOrchestrators)(root);
|
|
116
|
+
expect(diagnostics).toEqual([]);
|
|
117
|
+
expect(orchestrators).toHaveLength(1);
|
|
118
|
+
});
|
|
119
|
+
it('un manifiesto con JSON invalido se reporta, no explota', () => {
|
|
120
|
+
const root = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'awm-orch-'));
|
|
121
|
+
created.push(root);
|
|
122
|
+
fs_1.default.writeFileSync(path_1.default.join(root, 'awm-registry.json'), '{ no es json');
|
|
123
|
+
const { orchestrators, diagnostics } = (0, orchestrators_1.readDeclaredOrchestrators)(root);
|
|
124
|
+
expect(orchestrators).toEqual([]);
|
|
125
|
+
expect(diagnostics).toHaveLength(1);
|
|
126
|
+
});
|
|
127
|
+
it('una declaracion invalida no invalida las validas de otros registries', () => {
|
|
128
|
+
const bad = registryWith({ orchestrator: { name: 'roto' } });
|
|
129
|
+
const good = registryWith({
|
|
130
|
+
orchestrator: { name: 'sano', appliesWhen: 'siempre', terminatesTo: 'none' },
|
|
131
|
+
});
|
|
132
|
+
created.push(bad, good);
|
|
133
|
+
const all = [bad, good].map(orchestrators_1.readDeclaredOrchestrators);
|
|
134
|
+
expect(all[0].orchestrators).toEqual([]);
|
|
135
|
+
expect(all[1].orchestrators).toHaveLength(1);
|
|
136
|
+
expect(all[1].diagnostics).toEqual([]);
|
|
137
|
+
});
|
|
138
|
+
it('rechaza campos de precedencia: no son vocabulario del framework', () => {
|
|
139
|
+
const root = registryWith({
|
|
140
|
+
orchestrator: { name: 'x', appliesWhen: 'y', terminatesTo: 'none', precedence: 1 },
|
|
141
|
+
});
|
|
142
|
+
created.push(root);
|
|
143
|
+
const { orchestrators, diagnostics } = (0, orchestrators_1.readDeclaredOrchestrators)(root);
|
|
144
|
+
expect(orchestrators).toEqual([]);
|
|
145
|
+
expect(diagnostics[0]).toMatch(/unknown field "precedence"/i);
|
|
146
|
+
});
|
|
147
|
+
it('rechaza una declaracion que traiga secretos', () => {
|
|
148
|
+
const root = registryWith({
|
|
149
|
+
orchestrator: { name: 'x', appliesWhen: 'y', terminatesTo: 'none', token: 'ghp_abc' },
|
|
150
|
+
});
|
|
151
|
+
created.push(root);
|
|
152
|
+
const { diagnostics } = (0, orchestrators_1.readDeclaredOrchestrators)(root);
|
|
153
|
+
expect(diagnostics[0]).toMatch(/unknown field "token"/i);
|
|
154
|
+
});
|
|
155
|
+
it('rechaza un manifiesto simlinkeado sin lanzar, reportandolo', () => {
|
|
156
|
+
const root = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'awm-orch-'));
|
|
157
|
+
const outside = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'awm-orch-outside-'));
|
|
158
|
+
created.push(root, outside);
|
|
159
|
+
fs_1.default.writeFileSync(path_1.default.join(outside, 'awm-registry.json'), JSON.stringify({
|
|
160
|
+
orchestrator: { name: 'sano', appliesWhen: 'siempre', terminatesTo: 'none' },
|
|
161
|
+
}));
|
|
162
|
+
fs_1.default.symlinkSync(path_1.default.join(outside, 'awm-registry.json'), path_1.default.join(root, 'awm-registry.json'));
|
|
163
|
+
const { orchestrators, diagnostics } = (0, orchestrators_1.readDeclaredOrchestrators)(root);
|
|
164
|
+
expect(orchestrators).toEqual([]);
|
|
165
|
+
expect(diagnostics).toHaveLength(1);
|
|
166
|
+
expect(diagnostics[0]).toMatch(/symbolic link/i);
|
|
167
|
+
});
|
|
168
|
+
it('un nombre de campo con salto de linea no forja lineas de log adicionales', () => {
|
|
169
|
+
const root = registryWith({
|
|
170
|
+
orchestrator: { name: 'x', appliesWhen: 'y', terminatesTo: 'none', 'evil\nfake-warning: injected line': true },
|
|
171
|
+
});
|
|
172
|
+
created.push(root);
|
|
173
|
+
const { orchestrators, diagnostics } = (0, orchestrators_1.readDeclaredOrchestrators)(root);
|
|
174
|
+
expect(orchestrators).toEqual([]);
|
|
175
|
+
expect(diagnostics).toHaveLength(1);
|
|
176
|
+
expect(diagnostics[0].split('\n')).toHaveLength(1);
|
|
177
|
+
expect(diagnostics[0]).toContain(JSON.stringify('evil\nfake-warning: injected line'));
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
describe('collectDeclaredOrchestrators', () => {
|
|
181
|
+
// Isolates AWM_HOME per CLAUDE.md's testing rule ("ningun test puede tocar el ~/.awm
|
|
182
|
+
// real") — mirrors the pattern in tests/core/context/orchestrator.test.ts.
|
|
183
|
+
let isolatedAwmHome;
|
|
184
|
+
let originalAwmHomeEnv;
|
|
185
|
+
beforeEach(() => {
|
|
186
|
+
isolatedAwmHome = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'awm-orch-collect-home-'));
|
|
187
|
+
originalAwmHomeEnv = process.env.AWM_HOME;
|
|
188
|
+
process.env.AWM_HOME = isolatedAwmHome;
|
|
189
|
+
});
|
|
190
|
+
afterEach(() => {
|
|
191
|
+
if (originalAwmHomeEnv === undefined)
|
|
192
|
+
delete process.env.AWM_HOME;
|
|
193
|
+
else
|
|
194
|
+
process.env.AWM_HOME = originalAwmHomeEnv;
|
|
195
|
+
fs_1.default.rmSync(isolatedAwmHome, { recursive: true, force: true });
|
|
196
|
+
});
|
|
197
|
+
function writeManifest(name, manifest) {
|
|
198
|
+
const root = (0, registries_1.registryContentRoot)(name);
|
|
199
|
+
fs_1.default.mkdirSync(root, { recursive: true });
|
|
200
|
+
fs_1.default.writeFileSync(path_1.default.join(root, 'awm-registry.json'), JSON.stringify(manifest));
|
|
201
|
+
}
|
|
202
|
+
it('dedupea por nombre entre dos registries, conservando el primero por orden de registries.json', () => {
|
|
203
|
+
(0, registries_1.writeRegistriesConfig)([
|
|
204
|
+
{ name: 'first', remote: 'unused' },
|
|
205
|
+
{ name: 'second', remote: 'unused' },
|
|
206
|
+
]);
|
|
207
|
+
writeManifest('first', {
|
|
208
|
+
orchestrator: { name: 'shared', appliesWhen: 'primero', terminatesTo: 'a' },
|
|
209
|
+
});
|
|
210
|
+
writeManifest('second', {
|
|
211
|
+
orchestrator: { name: 'shared', appliesWhen: 'segundo', terminatesTo: 'b' },
|
|
212
|
+
});
|
|
213
|
+
const { declared, diagnostics } = (0, orchestrators_1.collectDeclaredOrchestrators)();
|
|
214
|
+
expect(declared).toHaveLength(1);
|
|
215
|
+
expect(declared[0]).toEqual({ name: 'shared', appliesWhen: 'primero', terminatesTo: 'a' });
|
|
216
|
+
expect(diagnostics).toHaveLength(1);
|
|
217
|
+
expect(diagnostics[0]).toMatch(/shared/);
|
|
218
|
+
expect(diagnostics[0]).toMatch(/duplicate|shadow/i);
|
|
219
|
+
});
|
|
220
|
+
it('no dedupea orquestadores con nombres distintos, ambos se conservan', () => {
|
|
221
|
+
(0, registries_1.writeRegistriesConfig)([
|
|
222
|
+
{ name: 'first', remote: 'unused' },
|
|
223
|
+
{ name: 'second', remote: 'unused' },
|
|
224
|
+
]);
|
|
225
|
+
writeManifest('first', {
|
|
226
|
+
orchestrator: { name: 'uno', appliesWhen: 'x', terminatesTo: 'a' },
|
|
227
|
+
});
|
|
228
|
+
writeManifest('second', {
|
|
229
|
+
orchestrator: { name: 'dos', appliesWhen: 'y', terminatesTo: 'b' },
|
|
230
|
+
});
|
|
231
|
+
const { declared, diagnostics } = (0, orchestrators_1.collectDeclaredOrchestrators)();
|
|
232
|
+
expect(declared).toHaveLength(2);
|
|
233
|
+
expect(declared.map((d) => d.name).sort()).toEqual(['dos', 'uno']);
|
|
234
|
+
expect(diagnostics).toEqual([]);
|
|
235
|
+
});
|
|
236
|
+
});
|