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.
- package/dist/src/core/paths.js +20 -0
- package/dist/src/providers/index.js +39 -11
- package/dist/tests/core/context/orchestrator.test.js +5 -5
- package/dist/tests/core/context/strategies/codex-agents.test.js +3 -3
- package/dist/tests/core/context/strategies/config-instructions.test.js +1 -1
- package/dist/tests/providers/index.test.js +5 -0
- package/dist/tests/structural/provider-paths-honor-config-home.test.js +102 -0
- package/package.json +1 -1
package/dist/src/core/paths.js
CHANGED
|
@@ -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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
+
});
|