agentic-workflow-manager 5.0.2 → 6.0.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.
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.WINDOWS_KNOWN_GAP = void 0;
7
7
  exports.homeDir = homeDir;
8
8
  exports.awmHome = awmHome;
9
+ exports.providerConfigHome = providerConfigHome;
9
10
  exports.platform = platform;
10
11
  exports.isWindowsNative = isWindowsNative;
11
12
  exports.platformLabel = platformLabel;
@@ -27,6 +28,25 @@ function homeDir() {
27
28
  function awmHome() {
28
29
  return process.env.AWM_HOME || path_1.default.join(homeDir(), '.awm');
29
30
  }
31
+ /**
32
+ * Raiz de configuracion de un agente, honrando SU propia variable de entorno.
33
+ *
34
+ * AWM honra `AWM_HOME` para si mismo (ver `awmHome` arriba) y durante mucho tiempo le
35
+ * nego esa misma cortesia a todos los agentes que administra: cada ruta de proveedor se
36
+ * armaba desde `homeDir()` y punto. En una maquina con `CODEX_HOME` apuntando a otro
37
+ * lado, AWM escribia el hook en `~/.codex/hooks.json` y Codex lo leia desde
38
+ * `$CODEX_HOME/hooks.json` — instalacion correcta, en el archivo que nadie mira. El hook
39
+ * quedaba instalado y mudo, y `doctor` lo reportaba presente porque miraba el mismo
40
+ * lugar equivocado donde lo habia escrito.
41
+ *
42
+ * `envVar` en `null` significa "este proveedor no declara override conocido": se usa el
43
+ * default y listo. No se inventan variables — una que no exista de verdad seria peor que
44
+ * ninguna, porque prometeria una configurabilidad que no funciona.
45
+ */
46
+ function providerConfigHome(envVar, defaultDir) {
47
+ const override = envVar ? process.env[envVar]?.trim() : undefined;
48
+ return override || path_1.default.join(homeDir(), defaultDir);
49
+ }
30
50
  /** Raw platform string (wrapper over process.platform for testability). */
31
51
  function platform() {
32
52
  return process.platform;
@@ -45,19 +45,42 @@ function unsupportedScopeError(artifactType, scope, providerLabel, reason) {
45
45
  return new Error(`${artifactType} ${scope} scope is not supported by ${providerLabel}` +
46
46
  (reason ? `: ${reason}` : '.'));
47
47
  }
48
+ /** El override declarado por cada agente. Una sola tabla: agregar un proveedor o
49
+ * descubrir la variable de otro es una edicion de UNA linea, y el guard estructural
50
+ * `provider-paths-honor-config-home` verifica que TODAS sus rutas la respeten. */
51
+ const CONFIG_HOME = {
52
+ // Confirmada contra el binario real: Codex 0.146.0 lee `$CODEX_HOME/hooks.json`.
53
+ // Una corrida del playbook en una maquina con CODEX_HOME seteado encontro el hook
54
+ // instalado donde Codex no mira. Ver docs/decisions.md D-011.
55
+ codex: { envVar: 'CODEX_HOME', dir: '.codex' },
56
+ // Los demas usan su directorio por defecto. `null` es una afirmacion honesta —
57
+ // "no le conocemos override"— y no un "no tiene". Cuando se confirme uno contra su
58
+ // binario, se cambia aca y el guard obliga a que todas sus rutas lo sigan.
59
+ 'claude-code': { envVar: null, dir: '.claude' },
60
+ opencode: { envVar: null, dir: '.config/opencode' },
61
+ cursor: { envVar: null, dir: '.cursor' },
62
+ copilot: { envVar: null, dir: '.github' },
63
+ antigravity: { envVar: null, dir: '.gemini/antigravity' },
64
+ };
65
+ function configHomeFor(agent) {
66
+ const declared = CONFIG_HOME[agent];
67
+ return { ...declared, resolved: (0, paths_1.providerConfigHome)(declared.envVar, declared.dir) };
68
+ }
48
69
  function providers() {
49
70
  const home = (0, paths_1.homeDir)();
50
71
  const awm = (0, paths_1.awmHome)();
72
+ const root = (agent) => configHomeFor(agent).resolved;
51
73
  return {
52
74
  antigravity: {
53
75
  label: 'Antigravity',
76
+ configHome: configHomeFor('antigravity'),
54
77
  skill: {
55
- global: path_1.default.join(home, '.gemini/antigravity/skills'),
78
+ global: path_1.default.join(root('antigravity'), 'skills'),
56
79
  local: '.agent/skills',
57
80
  renderer: 'link',
58
81
  },
59
82
  workflow: {
60
- global: path_1.default.join(home, '.gemini/antigravity/global_workflows'),
83
+ global: path_1.default.join(root('antigravity'), 'global_workflows'),
61
84
  local: '.agent/workflows',
62
85
  renderer: 'link',
63
86
  },
@@ -65,6 +88,7 @@ function providers() {
65
88
  },
66
89
  opencode: {
67
90
  label: 'OpenCode',
91
+ configHome: configHomeFor('opencode'),
68
92
  skill: {
69
93
  global: path_1.default.join(home, '.agents/skills'),
70
94
  local: '.agents/skills',
@@ -72,32 +96,33 @@ function providers() {
72
96
  },
73
97
  workflow: null,
74
98
  agent: {
75
- global: path_1.default.join(home, '.config/opencode/agents'),
99
+ global: path_1.default.join(root('opencode'), 'agents'),
76
100
  local: '.agents/profiles',
77
101
  renderer: 'link',
78
102
  },
79
103
  injection: {
80
104
  type: 'config-instructions',
81
- configPath: path_1.default.join(home, '.config/opencode/opencode.json'),
105
+ configPath: path_1.default.join(root('opencode'), 'opencode.json'),
82
106
  field: 'instructions',
83
107
  },
84
108
  },
85
109
  'claude-code': {
86
110
  label: 'Claude Code',
111
+ configHome: configHomeFor('claude-code'),
87
112
  skill: {
88
- global: path_1.default.join(home, '.claude/skills'),
113
+ global: path_1.default.join(root('claude-code'), 'skills'),
89
114
  local: '.claude/skills',
90
115
  renderer: 'link',
91
116
  },
92
117
  workflow: null,
93
118
  agent: {
94
- global: path_1.default.join(home, '.claude/agents'),
119
+ global: path_1.default.join(root('claude-code'), 'agents'),
95
120
  local: '.claude/agents',
96
121
  renderer: 'link',
97
122
  },
98
123
  hooks: {
99
124
  type: 'cc-settings-merge',
100
- settingsPath: path_1.default.join(home, '.claude/settings.json'),
125
+ settingsPath: path_1.default.join(root('claude-code'), 'settings.json'),
101
126
  scriptsDir: path_1.default.join(awm, 'hooks'),
102
127
  matcher: 'startup|clear|compact',
103
128
  eventName: 'SessionStart',
@@ -106,6 +131,7 @@ function providers() {
106
131
  },
107
132
  codex: {
108
133
  label: 'Codex',
134
+ configHome: configHomeFor('codex'),
109
135
  minimumVersion: '0.145.0',
110
136
  versionCommand: { command: 'codex', args: ['--version'], versionPattern: /^codex-cli (\d+\.\d+\.\d+)$/ },
111
137
  skill: {
@@ -115,27 +141,28 @@ function providers() {
115
141
  },
116
142
  workflow: null,
117
143
  agent: {
118
- global: path_1.default.join(home, '.codex/agents'),
144
+ global: path_1.default.join(root('codex'), 'agents'),
119
145
  local: '.codex/agents',
120
146
  renderer: 'codex-agent-toml',
121
147
  },
122
148
  hooks: {
123
149
  type: 'codex-hooks-json',
124
- settingsPath: path_1.default.join(home, '.codex/hooks.json'),
150
+ settingsPath: path_1.default.join(root('codex'), 'hooks.json'),
125
151
  scriptsDir: path_1.default.join(awm, 'hooks/codex'),
126
152
  matcher: 'startup|resume|clear|compact',
127
153
  eventName: 'SessionStart',
128
154
  },
129
155
  injection: {
130
156
  type: 'managed-agents-md',
131
- globalPath: path_1.default.join(home, '.codex/AGENTS.md'),
157
+ globalPath: path_1.default.join(root('codex'), 'AGENTS.md'),
132
158
  localFile: 'AGENTS.md',
133
159
  },
134
160
  },
135
161
  cursor: {
136
162
  label: 'Cursor',
163
+ configHome: configHomeFor('cursor'),
137
164
  skill: {
138
- global: path_1.default.join(home, '.cursor/rules'),
165
+ global: path_1.default.join(root('cursor'), 'rules'),
139
166
  local: '.cursor/rules',
140
167
  renderer: 'cursor-mdc',
141
168
  },
@@ -153,6 +180,7 @@ function providers() {
153
180
  },
154
181
  copilot: {
155
182
  label: 'Copilot',
183
+ configHome: configHomeFor('copilot'),
156
184
  skill: {
157
185
  global: null,
158
186
  globalUnsupportedReason: 'GitHub Copilot has no user-level skill discovery mechanism — skills must be installed per-project.',
@@ -26,7 +26,7 @@ function tmpRegistry() {
26
26
  }
27
27
  describe('InjectionOrchestrator (claude-code dispatch via HookMergeStrategy)', () => {
28
28
  const ccOverride = {
29
- label: 'Claude Code', skill: { global: '', local: '', renderer: 'link' }, workflow: null, agent: null,
29
+ label: 'Claude Code', configHome: { envVar: null, dir: '.test', resolved: '/tmp/test-config-home' }, skill: { global: '', local: '', renderer: 'link' }, workflow: null, agent: null,
30
30
  injection: { type: 'cc-settings-merge' },
31
31
  hooks: { type: 'cc-settings-merge', settingsPath: '', scriptsDir: '', matcher: '', eventName: '' },
32
32
  };
@@ -64,7 +64,7 @@ describe('InjectionOrchestrator (claude-code dispatch via HookMergeStrategy)', (
64
64
  it('throws when providerOverride has no injection (does not fall through to real agent config)', () => {
65
65
  const noInjection = {
66
66
  label: 'Test',
67
- skill: { global: '', local: '', renderer: 'link' },
67
+ configHome: { envVar: null, dir: '.test', resolved: '/tmp/test-config-home' }, skill: { global: '', local: '', renderer: 'link' },
68
68
  workflow: null,
69
69
  agent: null,
70
70
  };
@@ -85,7 +85,7 @@ describe('InjectionOrchestrator (opencode, real strategy)', () => {
85
85
  registryRoot = tmpRegistry();
86
86
  orch = new orchestrator_1.InjectionOrchestrator({
87
87
  providerOverride: {
88
- label: 'OpenCode', skill: { global: '', local: '', renderer: 'link' }, workflow: null, agent: null,
88
+ label: 'OpenCode', configHome: { envVar: null, dir: '.test', resolved: '/tmp/test-config-home' }, skill: { global: '', local: '', renderer: 'link' }, workflow: null, agent: null,
89
89
  injection: { type: 'config-instructions', configPath, field: 'instructions' },
90
90
  },
91
91
  contextPathOverride: absPath,
@@ -150,7 +150,7 @@ describe('InjectionOrchestrator (codex, managed AGENTS.md strategy)', () => {
150
150
  fs_1.default.writeFileSync(agentsPath, '# User rules\n');
151
151
  const orch = new orchestrator_1.InjectionOrchestrator({
152
152
  providerOverride: {
153
- label: 'Codex', skill: { global: '', local: '', renderer: 'link' }, workflow: null, agent: null,
153
+ label: 'Codex', configHome: { envVar: null, dir: '.test', resolved: '/tmp/test-config-home' }, skill: { global: '', local: '', renderer: 'link' }, workflow: null, agent: null,
154
154
  injection: { type: 'managed-agents-md', globalPath: agentsPath, localFile: 'AGENTS.md' },
155
155
  },
156
156
  contextPathOverride: contextPath,
@@ -202,7 +202,7 @@ describe('InjectionOrchestrator (local scope materializes under the project, not
202
202
  it('materializes to projectContextPath(projectRoot) for scope local, never to globalContextPath()', () => {
203
203
  const orch = new orchestrator_1.InjectionOrchestrator({
204
204
  providerOverride: {
205
- label: 'Copilot', skill: { global: null, local: '.github/instructions', renderer: 'link' }, workflow: null, agent: null,
205
+ label: 'Copilot', configHome: { envVar: null, dir: '.test', resolved: '/tmp/test-config-home' }, skill: { global: null, local: '.github/instructions', renderer: 'link' }, workflow: null, agent: null,
206
206
  injection: { type: 'managed-agents-md', globalPath: null, localFile: 'AGENTS.md' },
207
207
  },
208
208
  });
@@ -287,7 +287,7 @@ describe('CodexAgentsStrategy', () => {
287
287
  function codexProvider(globalPath) {
288
288
  return {
289
289
  label: 'Codex',
290
- skill: { global: '', local: '', renderer: 'link' },
290
+ configHome: { envVar: null, dir: '.test', resolved: '/tmp/test-config-home' }, skill: { global: '', local: '', renderer: 'link' },
291
291
  workflow: null,
292
292
  agent: null,
293
293
  injection: { type: 'managed-agents-md', globalPath, localFile: 'AGENTS.md' },
@@ -296,7 +296,7 @@ function codexProvider(globalPath) {
296
296
  function copilotProvider() {
297
297
  return {
298
298
  label: 'Copilot',
299
- skill: { global: null, local: '.github/instructions', renderer: 'link' },
299
+ configHome: { envVar: null, dir: '.test', resolved: '/tmp/test-config-home' }, skill: { global: null, local: '.github/instructions', renderer: 'link' },
300
300
  workflow: null,
301
301
  agent: null,
302
302
  injection: { type: 'managed-agents-md', globalPath: null, localFile: 'AGENTS.md' },
@@ -305,7 +305,7 @@ function copilotProvider() {
305
305
  function cursorProvider() {
306
306
  return {
307
307
  label: 'Cursor',
308
- skill: { global: '', local: '.cursor/rules', renderer: 'link' },
308
+ configHome: { envVar: null, dir: '.test', resolved: '/tmp/test-config-home' }, skill: { global: '', local: '.cursor/rules', renderer: 'link' },
309
309
  workflow: null,
310
310
  agent: null,
311
311
  injection: { type: 'managed-agents-md', globalPath: null, localFile: 'AGENTS.md' },
@@ -18,7 +18,7 @@ function setup(opencodeJson) {
18
18
  fs_1.default.mkdirSync(path_1.default.dirname(absPath), { recursive: true });
19
19
  fs_1.default.writeFileSync(absPath, 'CTX');
20
20
  const provider = {
21
- label: 'OpenCode', skill: { global: '', local: '', renderer: 'link' }, workflow: null, agent: null,
21
+ label: 'OpenCode', configHome: { envVar: null, dir: '.test', resolved: '/tmp/test-config-home' }, skill: { global: '', local: '', renderer: 'link' }, workflow: null, agent: null,
22
22
  injection: { type: 'config-instructions', configPath, field: 'instructions' },
23
23
  };
24
24
  const input = {
@@ -55,6 +55,8 @@ describe('Providers Routing', () => {
55
55
  it('declares the complete Codex capability metadata', () => {
56
56
  expect((0, providers_1.providerFor)('codex')).toEqual({
57
57
  label: 'Codex',
58
+ // El unico proveedor con override confirmado contra su binario real (D-011).
59
+ configHome: { envVar: 'CODEX_HOME', dir: '.codex', resolved: path_1.default.join(tmpHome, '.codex') },
58
60
  minimumVersion: '0.145.0',
59
61
  versionCommand: { command: 'codex', args: ['--version'], versionPattern: /^codex-cli (\d+\.\d+\.\d+)$/ },
60
62
  skill: {
@@ -86,6 +88,7 @@ describe('Providers Routing', () => {
86
88
  const graph = (0, providers_1.providers)();
87
89
  expect(graph.antigravity).toEqual({
88
90
  label: 'Antigravity',
91
+ configHome: { envVar: null, dir: '.gemini/antigravity', resolved: path_1.default.join(tmpHome, '.gemini/antigravity') },
89
92
  skill: {
90
93
  global: path_1.default.join(tmpHome, '.gemini/antigravity/skills'),
91
94
  local: '.agent/skills',
@@ -100,6 +103,7 @@ describe('Providers Routing', () => {
100
103
  });
101
104
  expect(graph.opencode).toEqual({
102
105
  label: 'OpenCode',
106
+ configHome: { envVar: null, dir: '.config/opencode', resolved: path_1.default.join(tmpHome, '.config/opencode') },
103
107
  skill: {
104
108
  global: path_1.default.join(tmpHome, '.agents/skills'),
105
109
  local: '.agents/skills',
@@ -119,6 +123,7 @@ describe('Providers Routing', () => {
119
123
  });
120
124
  expect(graph['claude-code']).toEqual({
121
125
  label: 'Claude Code',
126
+ configHome: { envVar: null, dir: '.claude', resolved: path_1.default.join(tmpHome, '.claude') },
122
127
  skill: {
123
128
  global: path_1.default.join(tmpHome, '.claude/skills'),
124
129
  local: '.claude/skills',
@@ -0,0 +1,102 @@
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
+ // Un proveedor que declara override de home tiene que respetarlo en TODAS sus rutas.
7
+ //
8
+ // El caso que lo motivo: en una maquina con `CODEX_HOME` apuntando fuera del home, AWM
9
+ // instalaba el hook en `~/.codex/hooks.json` mientras Codex leia
10
+ // `$CODEX_HOME/hooks.json`. Instalacion correcta, en el archivo que nadie mira — el hook
11
+ // quedaba mudo, y `doctor` lo reportaba presente porque miraba el mismo lugar equivocado
12
+ // donde lo habia escrito. Tres rutas afectadas (`hooks.json`, `agents/`, `AGENTS.md`), y
13
+ // arreglar una o dos habria dejado el bug vivo en la tercera.
14
+ //
15
+ // Por eso el guard es una PROPIEDAD sobre la tabla entera y no una lista de rutas: no se
16
+ // puede satisfacer arreglando un sitio, y un proveedor nuevo lo hereda sin que nadie se
17
+ // acuerde de agregarlo. Ver docs/decisions.md D-011.
18
+ const path_1 = __importDefault(require("path"));
19
+ const providers_1 = require("../../src/providers");
20
+ /** Toda ruta absoluta que la config de un proveedor declara, con su nombre de campo. */
21
+ function absolutePaths(config) {
22
+ const out = [];
23
+ const push = (field, value) => {
24
+ if (typeof value === 'string' && path_1.default.isAbsolute(value))
25
+ out.push({ field, value });
26
+ };
27
+ push('skill.global', config.skill.global);
28
+ push('workflow.global', config.workflow?.global);
29
+ push('agent.global', config.agent?.global);
30
+ push('hooks.settingsPath', config.hooks?.settingsPath);
31
+ if (config.injection?.type === 'config-instructions')
32
+ push('injection.configPath', config.injection.configPath);
33
+ if (config.injection?.type === 'managed-agents-md')
34
+ push('injection.globalPath', config.injection.globalPath);
35
+ return out;
36
+ }
37
+ /** Vuelve a pedir la tabla con `env` aplicado. `providers()` la reconstruye en cada
38
+ * llamada leyendo el entorno, asi que no hace falta resetear modulos. */
39
+ function providersWith(env) {
40
+ const previous = {};
41
+ for (const [k, v] of Object.entries(env)) {
42
+ previous[k] = process.env[k];
43
+ if (v === undefined)
44
+ delete process.env[k];
45
+ else
46
+ process.env[k] = v;
47
+ }
48
+ try {
49
+ return (0, providers_1.providers)();
50
+ }
51
+ finally {
52
+ for (const [k, v] of Object.entries(previous)) {
53
+ if (v === undefined)
54
+ delete process.env[k];
55
+ else
56
+ process.env[k] = v;
57
+ }
58
+ }
59
+ }
60
+ const HOME = path_1.default.join(path_1.default.sep, 'tmp', 'awm-guard-home');
61
+ const ELSEWHERE = path_1.default.join(path_1.default.sep, 'tmp', 'awm-guard-elsewhere');
62
+ describe('a provider that declares a config-home override honors it everywhere', () => {
63
+ for (const agent of providers_1.AGENT_TARGETS) {
64
+ const declared = providersWith({ HOME })[agent].configHome;
65
+ if (!declared.envVar)
66
+ continue;
67
+ it(`${agent}: setting ${declared.envVar} moves every path that lives under its own dir`, () => {
68
+ const base = providersWith({ HOME, [declared.envVar]: undefined })[agent];
69
+ const moved = providersWith({ HOME, [declared.envVar]: ELSEWHERE })[agent];
70
+ const defaultRoot = path_1.default.join(HOME, declared.dir);
71
+ const under = absolutePaths(base).filter((p) => p.value.startsWith(defaultRoot));
72
+ // Si esto diera 0, el test pasaria sin verificar nada — y ese es exactamente
73
+ // el modo de falla que hace inutil a un guard.
74
+ expect(under.length).toBeGreaterThan(0);
75
+ const movedByField = new Map(absolutePaths(moved).map((p) => [p.field, p.value]));
76
+ const stragglers = under.filter((p) => !movedByField.get(p.field)?.startsWith(ELSEWHERE));
77
+ expect(stragglers.map((p) => `${p.field} = ${movedByField.get(p.field)}`)).toEqual([]);
78
+ });
79
+ it(`${agent}: an empty or blank ${declared.envVar} falls back to the default, it does not resolve to nothing`, () => {
80
+ // Una variable exportada vacia es comun en scripts (`export CODEX_HOME=`) y
81
+ // tomarla al pie de la letra dejaria las rutas colgando de la raiz.
82
+ for (const blank of ['', ' ']) {
83
+ const cfg = providersWith({ HOME, [declared.envVar]: blank })[agent];
84
+ expect(cfg.configHome.resolved).toBe(path_1.default.join(HOME, declared.dir));
85
+ }
86
+ });
87
+ }
88
+ it('shared, cross-agent conventions are NOT moved by one agent’s override', () => {
89
+ // `~/.agents/skills` lo comparten Codex y OpenCode: es del ecosistema, no de un
90
+ // agente. Moverlo con CODEX_HOME desconectaria a OpenCode de sus propias skills.
91
+ const moved = providersWith({ HOME, CODEX_HOME: ELSEWHERE });
92
+ expect(moved.codex.skill.global).toBe(path_1.default.join(HOME, '.agents/skills'));
93
+ expect(moved.opencode.skill.global).toBe(path_1.default.join(HOME, '.agents/skills'));
94
+ });
95
+ it('every agent declares a config home, so a new provider cannot skip the question', () => {
96
+ const table = providersWith({ HOME });
97
+ for (const agent of providers_1.AGENT_TARGETS) {
98
+ expect(typeof table[agent].configHome.dir).toBe('string');
99
+ expect(table[agent].configHome.dir.length).toBeGreaterThan(0);
100
+ }
101
+ });
102
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentic-workflow-manager",
3
- "version": "5.0.2",
3
+ "version": "6.0.0",
4
4
  "main": "dist/src/index.js",
5
5
  "bin": {
6
6
  "awm": "./dist/src/index.js"