@indy2kro/agentenv 0.1.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.
Files changed (99) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +320 -0
  3. package/dist/adapters/base.d.ts +58 -0
  4. package/dist/adapters/base.d.ts.map +1 -0
  5. package/dist/adapters/base.js +16 -0
  6. package/dist/adapters/base.js.map +1 -0
  7. package/dist/adapters/claude.d.ts +21 -0
  8. package/dist/adapters/claude.d.ts.map +1 -0
  9. package/dist/adapters/claude.js +198 -0
  10. package/dist/adapters/claude.js.map +1 -0
  11. package/dist/adapters/codex.d.ts +23 -0
  12. package/dist/adapters/codex.d.ts.map +1 -0
  13. package/dist/adapters/codex.js +134 -0
  14. package/dist/adapters/codex.js.map +1 -0
  15. package/dist/adapters/copilot.d.ts +21 -0
  16. package/dist/adapters/copilot.d.ts.map +1 -0
  17. package/dist/adapters/copilot.js +111 -0
  18. package/dist/adapters/copilot.js.map +1 -0
  19. package/dist/adapters/detect.d.ts +23 -0
  20. package/dist/adapters/detect.d.ts.map +1 -0
  21. package/dist/adapters/detect.js +80 -0
  22. package/dist/adapters/detect.js.map +1 -0
  23. package/dist/adapters/index.d.ts +12 -0
  24. package/dist/adapters/index.d.ts.map +1 -0
  25. package/dist/adapters/index.js +11 -0
  26. package/dist/adapters/index.js.map +1 -0
  27. package/dist/adapters/opencode.d.ts +19 -0
  28. package/dist/adapters/opencode.d.ts.map +1 -0
  29. package/dist/adapters/opencode.js +107 -0
  30. package/dist/adapters/opencode.js.map +1 -0
  31. package/dist/commands/apply.d.ts +21 -0
  32. package/dist/commands/apply.d.ts.map +1 -0
  33. package/dist/commands/apply.js +113 -0
  34. package/dist/commands/apply.js.map +1 -0
  35. package/dist/commands/configure.d.ts +13 -0
  36. package/dist/commands/configure.d.ts.map +1 -0
  37. package/dist/commands/configure.js +151 -0
  38. package/dist/commands/configure.js.map +1 -0
  39. package/dist/commands/setup.d.ts +11 -0
  40. package/dist/commands/setup.d.ts.map +1 -0
  41. package/dist/commands/setup.js +78 -0
  42. package/dist/commands/setup.js.map +1 -0
  43. package/dist/commands/status.d.ts +3 -0
  44. package/dist/commands/status.d.ts.map +1 -0
  45. package/dist/commands/status.js +152 -0
  46. package/dist/commands/status.js.map +1 -0
  47. package/dist/config/schema.d.ts +142 -0
  48. package/dist/config/schema.d.ts.map +1 -0
  49. package/dist/config/schema.js +778 -0
  50. package/dist/config/schema.js.map +1 -0
  51. package/dist/config/scopes.d.ts +14 -0
  52. package/dist/config/scopes.d.ts.map +1 -0
  53. package/dist/config/scopes.js +32 -0
  54. package/dist/config/scopes.js.map +1 -0
  55. package/dist/generate/agentsmd.d.ts +44 -0
  56. package/dist/generate/agentsmd.d.ts.map +1 -0
  57. package/dist/generate/agentsmd.js +340 -0
  58. package/dist/generate/agentsmd.js.map +1 -0
  59. package/dist/index.d.ts +7 -0
  60. package/dist/index.d.ts.map +1 -0
  61. package/dist/index.js +24 -0
  62. package/dist/index.js.map +1 -0
  63. package/dist/integrations/base.d.ts +33 -0
  64. package/dist/integrations/base.d.ts.map +1 -0
  65. package/dist/integrations/base.js +9 -0
  66. package/dist/integrations/base.js.map +1 -0
  67. package/dist/integrations/index.d.ts +7 -0
  68. package/dist/integrations/index.d.ts.map +1 -0
  69. package/dist/integrations/index.js +5 -0
  70. package/dist/integrations/index.js.map +1 -0
  71. package/dist/integrations/superpowers.d.ts +48 -0
  72. package/dist/integrations/superpowers.d.ts.map +1 -0
  73. package/dist/integrations/superpowers.js +191 -0
  74. package/dist/integrations/superpowers.js.map +1 -0
  75. package/dist/shell/detector.d.ts +92 -0
  76. package/dist/shell/detector.d.ts.map +1 -0
  77. package/dist/shell/detector.js +714 -0
  78. package/dist/shell/detector.js.map +1 -0
  79. package/dist/toolchain/fallbacks.d.ts +77 -0
  80. package/dist/toolchain/fallbacks.d.ts.map +1 -0
  81. package/dist/toolchain/fallbacks.js +238 -0
  82. package/dist/toolchain/fallbacks.js.map +1 -0
  83. package/dist/toolchain/gh.d.ts +20 -0
  84. package/dist/toolchain/gh.d.ts.map +1 -0
  85. package/dist/toolchain/gh.js +36 -0
  86. package/dist/toolchain/gh.js.map +1 -0
  87. package/dist/toolchain/mise.d.ts +92 -0
  88. package/dist/toolchain/mise.d.ts.map +1 -0
  89. package/dist/toolchain/mise.js +346 -0
  90. package/dist/toolchain/mise.js.map +1 -0
  91. package/dist/toolchain/rtk.d.ts +27 -0
  92. package/dist/toolchain/rtk.d.ts.map +1 -0
  93. package/dist/toolchain/rtk.js +76 -0
  94. package/dist/toolchain/rtk.js.map +1 -0
  95. package/dist/wizard/build.d.ts +24 -0
  96. package/dist/wizard/build.d.ts.map +1 -0
  97. package/dist/wizard/build.js +64 -0
  98. package/dist/wizard/build.js.map +1 -0
  99. package/package.json +80 -0
@@ -0,0 +1,778 @@
1
+ /**
2
+ * Agentenv Configuration Schema
3
+ * Defines the structure and validation for agentenv.toml
4
+ */
5
+ import * as fs from 'fs';
6
+ import * as path from 'path';
7
+ import toml from 'toml';
8
+ // Default configuration values
9
+ export const DEFAULT_CONFIG = {
10
+ scope: 'project',
11
+ agents: {
12
+ claude_code: true,
13
+ codex_cli: true,
14
+ copilot: true,
15
+ opencode: true,
16
+ },
17
+ tools: {
18
+ // Tier 1 - always enabled by default
19
+ ripgrep: true,
20
+ fd: true,
21
+ jq: true,
22
+ rtk: true,
23
+ // Tier 2 - enabled by default
24
+ ast_grep: true,
25
+ git_delta: true,
26
+ gh: true,
27
+ difftastic: true,
28
+ // Tier 3 - disabled by default
29
+ universal_ctags: false,
30
+ yq: false,
31
+ bat: false,
32
+ eza: false,
33
+ miller: false,
34
+ tokei: false,
35
+ hyperfine: false,
36
+ fzf: false,
37
+ just: false,
38
+ watchexec: false,
39
+ direnv: false,
40
+ },
41
+ rtk: {
42
+ enabled: true,
43
+ init: {
44
+ claude_code: true,
45
+ codex_cli: true,
46
+ copilot: true,
47
+ opencode: true,
48
+ },
49
+ },
50
+ tier0: {
51
+ check_enabled: true,
52
+ },
53
+ generate: {
54
+ marker_start: '<!-- agentenv-managed-start -->',
55
+ marker_end: '<!-- agentenv-managed-end -->',
56
+ files: ['AGENTS.md', 'CLAUDE.md'],
57
+ },
58
+ integrations: {
59
+ superpowers: {
60
+ enabled: false,
61
+ source: 'github:obra/superpowers',
62
+ ref: 'v6.3.0',
63
+ agents: ['claude_code', 'codex_cli', 'copilot', 'opencode'],
64
+ allow_hooks: false,
65
+ allow_external_requests: false,
66
+ },
67
+ },
68
+ };
69
+ // Known agent keys for validation
70
+ export const AGENT_KEYS = ['claude_code', 'codex_cli', 'copilot', 'opencode'];
71
+ // Known integration keys for validation
72
+ export const INTEGRATION_KEYS = ['superpowers'];
73
+ // Known tool keys for validation
74
+ export const TOOL_KEYS = [
75
+ 'ripgrep',
76
+ 'fd',
77
+ 'jq',
78
+ 'rtk',
79
+ 'ast_grep',
80
+ 'git_delta',
81
+ 'universal_ctags',
82
+ 'gh',
83
+ 'difftastic',
84
+ 'yq',
85
+ 'bat',
86
+ 'eza',
87
+ 'miller',
88
+ 'tokei',
89
+ 'hyperfine',
90
+ 'fzf',
91
+ 'just',
92
+ 'watchexec',
93
+ 'direnv',
94
+ ];
95
+ // Tool tiers for categorization
96
+ export const TOOL_TIERS = {
97
+ // Tier 1 - essential
98
+ ripgrep: 1,
99
+ fd: 1,
100
+ jq: 1,
101
+ rtk: 1,
102
+ // Tier 2 - AI-coding value-add
103
+ ast_grep: 2,
104
+ git_delta: 2,
105
+ universal_ctags: 2,
106
+ gh: 2,
107
+ difftastic: 2,
108
+ // Tier 3 - power-user
109
+ yq: 3,
110
+ bat: 3,
111
+ eza: 3,
112
+ miller: 3,
113
+ tokei: 3,
114
+ hyperfine: 3,
115
+ fzf: 3,
116
+ just: 3,
117
+ watchexec: 3,
118
+ direnv: 3,
119
+ };
120
+ // Binary name mappings (TOML key -> binary name)
121
+ export const BINARY_MAP = {
122
+ ripgrep: 'rg',
123
+ fd: 'fd',
124
+ jq: 'jq',
125
+ rtk: 'rtk',
126
+ ast_grep: 'sg',
127
+ git_delta: 'delta',
128
+ universal_ctags: 'ctags',
129
+ gh: 'gh',
130
+ difftastic: 'difft',
131
+ yq: 'yq',
132
+ bat: 'bat',
133
+ eza: 'eza',
134
+ miller: 'mlr',
135
+ tokei: 'tokei',
136
+ hyperfine: 'hyperfine',
137
+ fzf: 'fzf',
138
+ just: 'just',
139
+ watchexec: 'watchexec',
140
+ direnv: 'direnv',
141
+ };
142
+ // Tool descriptions
143
+ export const TOOL_DESCRIPTIONS = {
144
+ ripgrep: 'Fast text search (use instead of grep -r)',
145
+ fd: 'Fast, user-friendly file finder',
146
+ jq: 'Lightweight and flexible command-line JSON processor',
147
+ rtk: 'CLI proxy that reduces LLM token consumption by 60-90%',
148
+ ast_grep: 'Structural/AST-based code search and rewrite',
149
+ git_delta: 'Syntax-highlighted git diff pager',
150
+ universal_ctags: 'Universal ctags for code navigation',
151
+ difftastic: 'Structural diff tool that understands syntax',
152
+ gh: 'GitHub CLI for repository operations',
153
+ yq: 'YAML/TOML processor (jq for YAML)',
154
+ bat: 'cat clone with syntax highlighting and git integration',
155
+ eza: 'Modern replacement for ls',
156
+ miller: 'CSV/TSV data processing',
157
+ tokei: 'Fast code statistics (LOC, etc.)',
158
+ hyperfine: 'Command-line benchmarking tool',
159
+ fzf: 'Fuzzy finder with non-interactive filter mode',
160
+ just: 'Command runner for project recipes',
161
+ watchexec: 'File watcher that runs commands on changes',
162
+ direnv: 'Environment variable manager',
163
+ };
164
+ /**
165
+ * Load configuration from agentenv.toml file
166
+ * Searches in current directory, then user config directory
167
+ */
168
+ export function loadConfig(configPath) {
169
+ let pathToLoad = configPath;
170
+ if (!pathToLoad) {
171
+ // Try current directory
172
+ try {
173
+ if (fs.existsSync('agentenv.toml')) {
174
+ pathToLoad = 'agentenv.toml';
175
+ }
176
+ }
177
+ catch {
178
+ // Ignore
179
+ }
180
+ // Try user config directory
181
+ if (!pathToLoad) {
182
+ const home = process.env.HOME || process.env.USERPROFILE || '';
183
+ const userConfigPath = path.join(home, '.config', 'agentenv', 'agentenv.toml');
184
+ try {
185
+ if (fs.existsSync(userConfigPath)) {
186
+ pathToLoad = userConfigPath;
187
+ }
188
+ }
189
+ catch {
190
+ // Ignore
191
+ }
192
+ }
193
+ }
194
+ if (!pathToLoad) {
195
+ return { ...DEFAULT_CONFIG };
196
+ }
197
+ try {
198
+ const data = fs.readFileSync(pathToLoad, 'utf-8');
199
+ const parsed = toml.parse(data);
200
+ return mergeWithDefaults(normalizeConfig(parsed));
201
+ }
202
+ catch (err) {
203
+ throw new Error(`Invalid agentenv configuration at ${pathToLoad}: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
204
+ }
205
+ }
206
+ /**
207
+ * Save configuration to agentenv.toml file
208
+ */
209
+ export function saveConfig(config, configPath) {
210
+ try {
211
+ fs.mkdirSync(path.dirname(configPath), { recursive: true });
212
+ const tomlString = configToToml(config);
213
+ fs.writeFileSync(configPath, tomlString);
214
+ }
215
+ catch (err) {
216
+ throw new Error(`Failed to save config: ${err}`, { cause: err });
217
+ }
218
+ }
219
+ /**
220
+ * Convert configuration object to TOML string
221
+ */
222
+ export function configToToml(config) {
223
+ const lines = [];
224
+ // Scope
225
+ if (config.scope) {
226
+ lines.push(`scope = ${tomlString(config.scope)}`);
227
+ }
228
+ // Agents
229
+ if (config.agents) {
230
+ lines.push('\n[agents]');
231
+ if (config.agents.claude_code !== undefined)
232
+ lines.push(`claude_code = ${config.agents.claude_code}`);
233
+ if (config.agents.codex_cli !== undefined)
234
+ lines.push(`codex_cli = ${config.agents.codex_cli}`);
235
+ if (config.agents.copilot !== undefined)
236
+ lines.push(`copilot = ${config.agents.copilot}`);
237
+ if (config.agents.opencode !== undefined)
238
+ lines.push(`opencode = ${config.agents.opencode}`);
239
+ }
240
+ // Tools
241
+ if (config.tools) {
242
+ lines.push('\n[tools]');
243
+ const toolKeys = [
244
+ 'ripgrep',
245
+ 'fd',
246
+ 'jq',
247
+ 'rtk',
248
+ 'ast_grep',
249
+ 'git_delta',
250
+ 'universal_ctags',
251
+ 'gh',
252
+ 'difftastic',
253
+ 'yq',
254
+ 'bat',
255
+ 'eza',
256
+ 'miller',
257
+ 'tokei',
258
+ 'hyperfine',
259
+ 'fzf',
260
+ 'just',
261
+ 'watchexec',
262
+ 'direnv',
263
+ ];
264
+ for (const key of toolKeys) {
265
+ if (config.tools[key] !== undefined) {
266
+ lines.push(`${key} = ${config.tools[key]}`);
267
+ }
268
+ }
269
+ }
270
+ // Custom tools
271
+ if (config.custom_tools && config.custom_tools.length > 0) {
272
+ for (const ct of config.custom_tools) {
273
+ lines.push('\n[[custom_tools]]');
274
+ lines.push(`name = ${tomlString(ct.name)}`);
275
+ lines.push(`description = ${tomlString(ct.description)}`);
276
+ if (ct.already_installed !== undefined)
277
+ lines.push(`already_installed = ${ct.already_installed}`);
278
+ if (ct.mise_source)
279
+ lines.push(`mise_source = ${tomlString(ct.mise_source)}`);
280
+ if (ct.version)
281
+ lines.push(`version = ${tomlString(ct.version)}`);
282
+ if (ct.path_windows)
283
+ lines.push(`path_windows = ${tomlString(ct.path_windows)}`);
284
+ if (ct.path_macos)
285
+ lines.push(`path_macos = ${tomlString(ct.path_macos)}`);
286
+ if (ct.path_linux)
287
+ lines.push(`path_linux = ${tomlString(ct.path_linux)}`);
288
+ }
289
+ }
290
+ // RTK
291
+ if (config.rtk) {
292
+ lines.push('\n[rtk]');
293
+ if (config.rtk.enabled !== undefined)
294
+ lines.push(`enabled = ${config.rtk.enabled}`);
295
+ if (config.rtk.init) {
296
+ lines.push('\n[rtk.init]');
297
+ if (config.rtk.init.claude_code !== undefined)
298
+ lines.push(`claude_code = ${config.rtk.init.claude_code}`);
299
+ if (config.rtk.init.codex_cli !== undefined)
300
+ lines.push(`codex_cli = ${config.rtk.init.codex_cli}`);
301
+ if (config.rtk.init.copilot !== undefined)
302
+ lines.push(`copilot = ${config.rtk.init.copilot}`);
303
+ if (config.rtk.init.opencode !== undefined)
304
+ lines.push(`opencode = ${config.rtk.init.opencode}`);
305
+ }
306
+ }
307
+ // Tier0
308
+ if (config.tier0) {
309
+ lines.push('\n[tier0]');
310
+ if (config.tier0.check_enabled !== undefined)
311
+ lines.push(`check_enabled = ${config.tier0.check_enabled}`);
312
+ if (config.tier0.git_bash_path)
313
+ lines.push(`git_bash_path = ${tomlString(config.tier0.git_bash_path)}`);
314
+ }
315
+ // Generate
316
+ if (config.generate) {
317
+ lines.push('\n[generate]');
318
+ if (config.generate.marker_start)
319
+ lines.push(`marker_start = ${tomlString(config.generate.marker_start)}`);
320
+ if (config.generate.marker_end)
321
+ lines.push(`marker_end = ${tomlString(config.generate.marker_end)}`);
322
+ if (config.generate.files)
323
+ lines.push(`files = [${config.generate.files.map(tomlString).join(', ')}]`);
324
+ }
325
+ // Integrations
326
+ if (config.integrations?.superpowers) {
327
+ const superpowers = config.integrations.superpowers;
328
+ lines.push('\n[integrations.superpowers]');
329
+ if (superpowers.enabled !== undefined)
330
+ lines.push(`enabled = ${superpowers.enabled}`);
331
+ if (superpowers.source)
332
+ lines.push(`source = ${tomlString(superpowers.source)}`);
333
+ if (superpowers.ref)
334
+ lines.push(`ref = ${tomlString(superpowers.ref)}`);
335
+ if (superpowers.scope)
336
+ lines.push(`scope = ${tomlString(superpowers.scope)}`);
337
+ if (superpowers.agents)
338
+ lines.push(`agents = [${superpowers.agents.map(tomlString).join(', ')}]`);
339
+ if (superpowers.allow_hooks !== undefined)
340
+ lines.push(`allow_hooks = ${superpowers.allow_hooks}`);
341
+ if (superpowers.allow_external_requests !== undefined)
342
+ lines.push(`allow_external_requests = ${superpowers.allow_external_requests}`);
343
+ }
344
+ return lines.join('\n');
345
+ }
346
+ function tomlString(value) {
347
+ return JSON.stringify(value);
348
+ }
349
+ /**
350
+ * Merge provided config with defaults (missing properties get default values)
351
+ */
352
+ function mergeWithDefaults(config) {
353
+ const result = { ...DEFAULT_CONFIG };
354
+ // Merge scope
355
+ if (config.scope !== undefined) {
356
+ result.scope = config.scope;
357
+ }
358
+ // Merge agents
359
+ if (config.agents) {
360
+ result.agents = {
361
+ claude_code: config.agents.claude_code ?? DEFAULT_CONFIG.agents?.claude_code,
362
+ codex_cli: config.agents.codex_cli ?? DEFAULT_CONFIG.agents?.codex_cli,
363
+ copilot: config.agents.copilot ?? DEFAULT_CONFIG.agents?.copilot,
364
+ opencode: config.agents.opencode ?? DEFAULT_CONFIG.agents?.opencode,
365
+ };
366
+ }
367
+ // Merge tools
368
+ if (config.tools) {
369
+ result.tools = {
370
+ ripgrep: config.tools.ripgrep ?? DEFAULT_CONFIG.tools?.ripgrep,
371
+ fd: config.tools.fd ?? DEFAULT_CONFIG.tools?.fd,
372
+ jq: config.tools.jq ?? DEFAULT_CONFIG.tools?.jq,
373
+ rtk: config.tools.rtk ?? DEFAULT_CONFIG.tools?.rtk,
374
+ ast_grep: config.tools.ast_grep ?? DEFAULT_CONFIG.tools?.ast_grep,
375
+ git_delta: config.tools.git_delta ?? DEFAULT_CONFIG.tools?.git_delta,
376
+ universal_ctags: config.tools.universal_ctags ?? DEFAULT_CONFIG.tools?.universal_ctags,
377
+ gh: config.tools.gh ?? DEFAULT_CONFIG.tools?.gh,
378
+ difftastic: config.tools.difftastic ?? DEFAULT_CONFIG.tools?.difftastic,
379
+ yq: config.tools.yq ?? DEFAULT_CONFIG.tools?.yq,
380
+ bat: config.tools.bat ?? DEFAULT_CONFIG.tools?.bat,
381
+ eza: config.tools.eza ?? DEFAULT_CONFIG.tools?.eza,
382
+ miller: config.tools.miller ?? DEFAULT_CONFIG.tools?.miller,
383
+ tokei: config.tools.tokei ?? DEFAULT_CONFIG.tools?.tokei,
384
+ hyperfine: config.tools.hyperfine ?? DEFAULT_CONFIG.tools?.hyperfine,
385
+ fzf: config.tools.fzf ?? DEFAULT_CONFIG.tools?.fzf,
386
+ just: config.tools.just ?? DEFAULT_CONFIG.tools?.just,
387
+ watchexec: config.tools.watchexec ?? DEFAULT_CONFIG.tools?.watchexec,
388
+ direnv: config.tools.direnv ?? DEFAULT_CONFIG.tools?.direnv,
389
+ };
390
+ }
391
+ // Merge custom_tools
392
+ if (config.custom_tools) {
393
+ result.custom_tools = config.custom_tools;
394
+ }
395
+ // Merge rtk
396
+ if (config.rtk) {
397
+ result.rtk = {
398
+ enabled: config.rtk.enabled ?? DEFAULT_CONFIG.rtk?.enabled,
399
+ init: {
400
+ claude_code: config.rtk.init?.claude_code ?? DEFAULT_CONFIG.rtk?.init?.claude_code,
401
+ codex_cli: config.rtk.init?.codex_cli ?? DEFAULT_CONFIG.rtk?.init?.codex_cli,
402
+ copilot: config.rtk.init?.copilot ?? DEFAULT_CONFIG.rtk?.init?.copilot,
403
+ opencode: config.rtk.init?.opencode ?? DEFAULT_CONFIG.rtk?.init?.opencode,
404
+ },
405
+ };
406
+ }
407
+ // Merge tier0
408
+ if (config.tier0) {
409
+ result.tier0 = {
410
+ check_enabled: config.tier0.check_enabled ?? DEFAULT_CONFIG.tier0?.check_enabled,
411
+ git_bash_path: config.tier0.git_bash_path ?? DEFAULT_CONFIG.tier0?.git_bash_path,
412
+ };
413
+ }
414
+ // Merge generate
415
+ if (config.generate) {
416
+ result.generate = {
417
+ marker_start: config.generate.marker_start ?? DEFAULT_CONFIG.generate?.marker_start,
418
+ marker_end: config.generate.marker_end ?? DEFAULT_CONFIG.generate?.marker_end,
419
+ files: config.generate.files ?? DEFAULT_CONFIG.generate?.files,
420
+ };
421
+ }
422
+ // Merge integrations
423
+ if (config.integrations) {
424
+ result.integrations = {
425
+ superpowers: config.integrations.superpowers
426
+ ? {
427
+ enabled: config.integrations.superpowers.enabled ??
428
+ DEFAULT_CONFIG.integrations?.superpowers?.enabled,
429
+ source: config.integrations.superpowers.source ??
430
+ DEFAULT_CONFIG.integrations?.superpowers?.source,
431
+ ref: config.integrations.superpowers.ref ?? DEFAULT_CONFIG.integrations?.superpowers?.ref,
432
+ scope: config.integrations.superpowers.scope ??
433
+ DEFAULT_CONFIG.integrations?.superpowers?.scope,
434
+ agents: config.integrations.superpowers.agents ??
435
+ DEFAULT_CONFIG.integrations?.superpowers?.agents,
436
+ allow_hooks: config.integrations.superpowers.allow_hooks ??
437
+ DEFAULT_CONFIG.integrations?.superpowers?.allow_hooks,
438
+ allow_external_requests: config.integrations.superpowers.allow_external_requests ??
439
+ DEFAULT_CONFIG.integrations?.superpowers?.allow_external_requests,
440
+ }
441
+ : DEFAULT_CONFIG.integrations?.superpowers,
442
+ };
443
+ }
444
+ // Merge advanced
445
+ if (config.advanced) {
446
+ result.advanced = {
447
+ default_mode: config.advanced.default_mode ?? DEFAULT_CONFIG.advanced?.default_mode,
448
+ show_diff_preview: config.advanced.show_diff_preview ?? DEFAULT_CONFIG.advanced?.show_diff_preview,
449
+ };
450
+ }
451
+ return result;
452
+ }
453
+ /**
454
+ * Get enabled agents from config
455
+ */
456
+ export function getEnabledAgents(config) {
457
+ const agents = config.agents || DEFAULT_CONFIG.agents || {};
458
+ const enabled = [];
459
+ if (agents.claude_code)
460
+ enabled.push('claude_code');
461
+ if (agents.codex_cli)
462
+ enabled.push('codex_cli');
463
+ if (agents.copilot)
464
+ enabled.push('copilot');
465
+ if (agents.opencode)
466
+ enabled.push('opencode');
467
+ return enabled;
468
+ }
469
+ /**
470
+ * Normalize a config parsed from TOML into the canonical schema shape.
471
+ * Accepts the dotted `path.windows` / `version.windows` forms used in the
472
+ * docs example (§6.3) and flattens them to the `path_windows` keys the rest
473
+ * of the pipeline expects.
474
+ */
475
+ function normalizeConfig(config) {
476
+ if (!Array.isArray(config.custom_tools)) {
477
+ return config;
478
+ }
479
+ const normalized = {
480
+ ...config,
481
+ custom_tools: config.custom_tools.map((ct) => {
482
+ const result = { ...ct };
483
+ const paths = ct.path;
484
+ if (paths && typeof paths === 'object') {
485
+ if (paths.windows)
486
+ result.path_windows = paths.windows;
487
+ if (paths.macos)
488
+ result.path_macos = paths.macos;
489
+ if (paths.linux)
490
+ result.path_linux = paths.linux;
491
+ }
492
+ const versions = ct.version;
493
+ if (versions && typeof versions === 'object' && !Array.isArray(versions)) {
494
+ result.version = versions[process.platform];
495
+ }
496
+ return result;
497
+ }),
498
+ };
499
+ return normalized;
500
+ }
501
+ /**
502
+ * Validate a config and report problems, separating hard errors from warnings.
503
+ * The CLI refuses to proceed when there are errors; warnings are surfaced but
504
+ * non-fatal (e.g. a catalog tool that mise cannot install from its registry).
505
+ */
506
+ export function validateConfig(config) {
507
+ const errors = [];
508
+ const warnings = [];
509
+ if (config.scope !== undefined && config.scope !== 'project' && config.scope !== 'user') {
510
+ errors.push(`scope must be "project" or "user", got "${String(config.scope)}"`);
511
+ }
512
+ const agents = config.agents ?? {};
513
+ for (const key of Object.keys(agents)) {
514
+ if (!AGENT_KEYS.includes(key)) {
515
+ errors.push(`Unknown agent "${key}"`);
516
+ }
517
+ }
518
+ const tools = (config.tools ?? {});
519
+ for (const key of Object.keys(tools)) {
520
+ if (!TOOL_KEYS.includes(key)) {
521
+ errors.push(`Unknown tool "${key}"`);
522
+ }
523
+ }
524
+ if (Array.isArray(config.custom_tools)) {
525
+ for (const ct of config.custom_tools) {
526
+ if (!ct.name) {
527
+ errors.push('A custom tool entry is missing its "name"');
528
+ }
529
+ else if (ct.already_installed && !ct.path_windows && !ct.path_macos && !ct.path_linux) {
530
+ warnings.push(`custom tool "${ct.name}" is marked already_installed but has no OS path set`);
531
+ }
532
+ }
533
+ }
534
+ // Tools Phase 0 flagged as not resolvable from the mise registry.
535
+ const registryGapNote = {
536
+ universal_ctags: 'not in the mise registry; add a custom_tools fallback',
537
+ tokei: 'not in the mise registry; add a custom_tools fallback or local toolchain',
538
+ };
539
+ for (const [tool, note] of Object.entries(registryGapNote)) {
540
+ if (tools[tool]) {
541
+ warnings.push(`${tool}: ${note}`);
542
+ }
543
+ }
544
+ const integrations = (config.integrations ?? {});
545
+ for (const key of Object.keys(integrations)) {
546
+ if (!INTEGRATION_KEYS.includes(key)) {
547
+ errors.push(`Unknown integration "${key}"`);
548
+ }
549
+ }
550
+ const superpowers = config.integrations?.superpowers;
551
+ if (superpowers) {
552
+ if (superpowers.scope !== undefined &&
553
+ superpowers.scope !== 'project' &&
554
+ superpowers.scope !== 'user') {
555
+ errors.push(`integrations.superpowers.scope must be "project" or "user", got "${String(superpowers.scope)}"`);
556
+ }
557
+ if (superpowers.agents) {
558
+ for (const agent of superpowers.agents) {
559
+ if (!AGENT_KEYS.includes(agent)) {
560
+ errors.push(`integrations.superpowers.agents contains unknown agent "${agent}"`);
561
+ }
562
+ }
563
+ }
564
+ if (superpowers.source !== undefined && superpowers.source.trim() === '') {
565
+ errors.push('integrations.superpowers.source must not be empty');
566
+ }
567
+ if (superpowers.ref !== undefined) {
568
+ if (superpowers.ref.trim() === '') {
569
+ errors.push('integrations.superpowers.ref must not be empty');
570
+ }
571
+ else if (!/^[A-Za-z0-9._/-]+$/.test(superpowers.ref)) {
572
+ errors.push(`integrations.superpowers.ref "${superpowers.ref}" contains characters that are not valid in a git ref`);
573
+ }
574
+ else if (['main', 'master', 'HEAD'].includes(superpowers.ref)) {
575
+ warnings.push(`integrations.superpowers.ref "${superpowers.ref}" is a floating branch; pin to a tag or commit for reproducibility`);
576
+ }
577
+ }
578
+ }
579
+ return { errors, warnings };
580
+ }
581
+ /**
582
+ * Structurally diff two configs for the pre-write review in `agentenv
583
+ * configure` and for drift reporting in `agentenv status` (§6.5).
584
+ * Tools/agents flip toggles as added/removed; scalar settings as changed.
585
+ */
586
+ export function diffConfigs(oldConfig, newConfig) {
587
+ const entries = [];
588
+ const oldAgents = oldConfig.agents ?? {};
589
+ const newAgents = newConfig.agents ?? {};
590
+ const oldTools = (oldConfig.tools ?? {});
591
+ const newTools = (newConfig.tools ?? {});
592
+ if (oldConfig.scope !== newConfig.scope) {
593
+ entries.push({
594
+ kind: 'changed',
595
+ key: 'scope',
596
+ oldValue: oldConfig.scope,
597
+ newValue: newConfig.scope,
598
+ });
599
+ }
600
+ for (const key of AGENT_KEYS) {
601
+ const oldValue = oldAgents[key] === true;
602
+ const newValue = newAgents[key] === true;
603
+ if (oldValue !== newValue) {
604
+ entries.push({
605
+ kind: newValue ? 'added' : 'removed',
606
+ key: `agents.${key}`,
607
+ oldValue: oldValue,
608
+ newValue: newValue,
609
+ });
610
+ }
611
+ }
612
+ for (const key of TOOL_KEYS) {
613
+ const oldValue = oldTools[key] === true;
614
+ const newValue = newTools[key] === true;
615
+ if (oldValue !== newValue) {
616
+ entries.push({
617
+ kind: newValue ? 'added' : 'removed',
618
+ key: `tools.${key}`,
619
+ oldValue: oldValue,
620
+ newValue: newValue,
621
+ });
622
+ }
623
+ }
624
+ diffCustomTools(oldConfig.custom_tools ?? [], newConfig.custom_tools ?? [], entries);
625
+ diffNestedSettings([
626
+ ['rtk.enabled', oldConfig.rtk?.enabled, newConfig.rtk?.enabled],
627
+ ['rtk.init.claude_code', oldConfig.rtk?.init?.claude_code, newConfig.rtk?.init?.claude_code],
628
+ ['rtk.init.codex_cli', oldConfig.rtk?.init?.codex_cli, newConfig.rtk?.init?.codex_cli],
629
+ ['rtk.init.copilot', oldConfig.rtk?.init?.copilot, newConfig.rtk?.init?.copilot],
630
+ ['rtk.init.opencode', oldConfig.rtk?.init?.opencode, newConfig.rtk?.init?.opencode],
631
+ ['tier0.check_enabled', oldConfig.tier0?.check_enabled, newConfig.tier0?.check_enabled],
632
+ ['tier0.git_bash_path', oldConfig.tier0?.git_bash_path, newConfig.tier0?.git_bash_path],
633
+ ['generate.marker_start', oldConfig.generate?.marker_start, newConfig.generate?.marker_start],
634
+ ['generate.marker_end', oldConfig.generate?.marker_end, newConfig.generate?.marker_end],
635
+ ['generate.files', jsonOr(oldConfig.generate?.files), jsonOr(newConfig.generate?.files)],
636
+ ['advanced.default_mode', oldConfig.advanced?.default_mode, newConfig.advanced?.default_mode],
637
+ [
638
+ 'advanced.show_diff_preview',
639
+ oldConfig.advanced?.show_diff_preview,
640
+ newConfig.advanced?.show_diff_preview,
641
+ ],
642
+ [
643
+ 'integrations.superpowers.enabled',
644
+ oldConfig.integrations?.superpowers?.enabled,
645
+ newConfig.integrations?.superpowers?.enabled,
646
+ ],
647
+ [
648
+ 'integrations.superpowers.source',
649
+ oldConfig.integrations?.superpowers?.source,
650
+ newConfig.integrations?.superpowers?.source,
651
+ ],
652
+ [
653
+ 'integrations.superpowers.ref',
654
+ oldConfig.integrations?.superpowers?.ref,
655
+ newConfig.integrations?.superpowers?.ref,
656
+ ],
657
+ [
658
+ 'integrations.superpowers.scope',
659
+ oldConfig.integrations?.superpowers?.scope,
660
+ newConfig.integrations?.superpowers?.scope,
661
+ ],
662
+ [
663
+ 'integrations.superpowers.agents',
664
+ jsonOr(oldConfig.integrations?.superpowers?.agents),
665
+ jsonOr(newConfig.integrations?.superpowers?.agents),
666
+ ],
667
+ [
668
+ 'integrations.superpowers.allow_hooks',
669
+ oldConfig.integrations?.superpowers?.allow_hooks,
670
+ newConfig.integrations?.superpowers?.allow_hooks,
671
+ ],
672
+ [
673
+ 'integrations.superpowers.allow_external_requests',
674
+ oldConfig.integrations?.superpowers?.allow_external_requests,
675
+ newConfig.integrations?.superpowers?.allow_external_requests,
676
+ ],
677
+ ], entries);
678
+ return entries;
679
+ }
680
+ function diffCustomTools(oldTools, newTools, entries) {
681
+ const byName = (tools) => new Map(tools.map((tool) => [tool.name, tool]));
682
+ const oldByName = byName(oldTools);
683
+ const newByName = byName(newTools);
684
+ for (const [name, oldTool] of oldByName) {
685
+ if (!newByName.has(name)) {
686
+ entries.push({ kind: 'removed', key: `custom_tools.${name}`, oldValue: oldTool });
687
+ }
688
+ }
689
+ for (const [name, newTool] of newByName) {
690
+ if (!oldByName.has(name)) {
691
+ entries.push({ kind: 'added', key: `custom_tools.${name}`, newValue: newTool });
692
+ }
693
+ }
694
+ for (const [name, oldTool] of oldByName) {
695
+ if (!newByName.has(name))
696
+ continue;
697
+ const newTool = newByName.get(name);
698
+ const fields = [
699
+ ['description', oldTool.description, newTool.description],
700
+ ['already_installed', oldTool.already_installed, newTool.already_installed],
701
+ ['mise_source', oldTool.mise_source, newTool.mise_source],
702
+ ['version', oldTool.version, newTool.version],
703
+ ['path_windows', oldTool.path_windows, newTool.path_windows],
704
+ ['path_macos', oldTool.path_macos, newTool.path_macos],
705
+ ['path_linux', oldTool.path_linux, newTool.path_linux],
706
+ ];
707
+ for (const [field, oldValue, newValue] of fields) {
708
+ if (oldValue !== newValue) {
709
+ entries.push({
710
+ kind: 'changed',
711
+ key: `custom_tools.${name}.${field}`,
712
+ oldValue,
713
+ newValue,
714
+ });
715
+ }
716
+ }
717
+ }
718
+ }
719
+ function jsonOr(value) {
720
+ return value === undefined ? undefined : JSON.stringify(value);
721
+ }
722
+ function diffNestedSettings(pairs, entries) {
723
+ for (const [key, oldValue, newValue] of pairs) {
724
+ if (oldValue !== newValue) {
725
+ entries.push({ kind: 'changed', key, oldValue, newValue });
726
+ }
727
+ }
728
+ }
729
+ /**
730
+ * Get enabled tools from config
731
+ */
732
+ export function getEnabledTools(config) {
733
+ const tools = config.tools || DEFAULT_CONFIG.tools || {};
734
+ const enabled = [];
735
+ const toolKeys = [
736
+ 'ripgrep',
737
+ 'fd',
738
+ 'jq',
739
+ 'rtk',
740
+ 'ast_grep',
741
+ 'git_delta',
742
+ 'universal_ctags',
743
+ 'gh',
744
+ 'difftastic',
745
+ 'yq',
746
+ 'bat',
747
+ 'eza',
748
+ 'miller',
749
+ 'tokei',
750
+ 'hyperfine',
751
+ 'fzf',
752
+ 'just',
753
+ 'watchexec',
754
+ 'direnv',
755
+ ];
756
+ for (const key of toolKeys) {
757
+ if (tools[key]) {
758
+ enabled.push(key);
759
+ }
760
+ }
761
+ // Add custom tools
762
+ if (config.custom_tools) {
763
+ for (const ct of config.custom_tools) {
764
+ if (ct.already_installed || ct.mise_source) {
765
+ enabled.push(ct.name);
766
+ }
767
+ }
768
+ }
769
+ return enabled;
770
+ }
771
+ /**
772
+ * Resolve the effective scope for an integration: its own `scope` field when
773
+ * set, otherwise the agentenv-level scope, defaulting to "project".
774
+ */
775
+ export function resolveIntegrationScope(config, integration) {
776
+ return integration?.scope ?? config.scope ?? 'project';
777
+ }
778
+ //# sourceMappingURL=schema.js.map