agile-context-engineering 0.2.2 → 0.5.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 (146) hide show
  1. package/.claude-plugin/plugin.json +10 -0
  2. package/CHANGELOG.md +82 -0
  3. package/README.md +27 -18
  4. package/agents/ace-product-owner.md +1 -1
  5. package/agents/ace-technical-application-architect.md +28 -0
  6. package/agents/ace-wiki-mapper.md +144 -29
  7. package/bin/install.js +67 -63
  8. package/hooks/ace-check-update.js +17 -9
  9. package/package.json +7 -5
  10. package/shared/lib/ace-core.js +308 -0
  11. package/shared/lib/ace-core.test.js +308 -0
  12. package/shared/lib/ace-github.js +753 -0
  13. package/shared/lib/ace-story.js +400 -0
  14. package/shared/lib/ace-story.test.js +250 -0
  15. package/{agile-context-engineering → shared}/utils/ui-formatting.md +299 -299
  16. package/skills/execute-story/SKILL.md +110 -0
  17. package/skills/execute-story/script.js +305 -0
  18. package/skills/execute-story/script.test.js +261 -0
  19. package/skills/execute-story/walkthrough-template.xml +255 -0
  20. package/{agile-context-engineering/workflows/execute-story.xml → skills/execute-story/workflow.xml} +83 -9
  21. package/skills/help/SKILL.md +69 -0
  22. package/skills/help/script.js +318 -0
  23. package/skills/help/script.test.js +183 -0
  24. package/{agile-context-engineering/workflows/help.xml → skills/help/workflow.xml} +8 -8
  25. package/skills/init-coding-standards/SKILL.md +72 -0
  26. package/{agile-context-engineering/templates/wiki/coding-standards.xml → skills/init-coding-standards/coding-standards-template.xml} +38 -0
  27. package/skills/init-coding-standards/script.js +59 -0
  28. package/skills/init-coding-standards/script.test.js +70 -0
  29. package/{agile-context-engineering/workflows/init-coding-standards.xml → skills/init-coding-standards/workflow.xml} +4 -9
  30. package/skills/map-cross-cutting/SKILL.md +89 -0
  31. package/skills/map-cross-cutting/workflow.xml +330 -0
  32. package/skills/map-guide/SKILL.md +89 -0
  33. package/skills/map-guide/workflow.xml +320 -0
  34. package/skills/map-pattern/SKILL.md +89 -0
  35. package/skills/map-pattern/workflow.xml +331 -0
  36. package/skills/map-story/SKILL.md +127 -0
  37. package/skills/map-story/templates/guide.xml +137 -0
  38. package/skills/map-story/templates/pattern.xml +159 -0
  39. package/skills/map-story/templates/system-cross-cutting.xml +197 -0
  40. package/skills/map-story/templates/walkthrough.xml +255 -0
  41. package/{agile-context-engineering/workflows/map-story.xml → skills/map-story/workflow.xml} +258 -9
  42. package/skills/map-subsystem/SKILL.md +111 -0
  43. package/skills/map-subsystem/script.js +60 -0
  44. package/skills/map-subsystem/script.test.js +68 -0
  45. package/skills/map-subsystem/templates/decizions.xml +115 -0
  46. package/skills/map-subsystem/templates/guide.xml +137 -0
  47. package/{agile-context-engineering/templates/wiki → skills/map-subsystem/templates}/module-discovery.xml +3 -3
  48. package/skills/map-subsystem/templates/pattern.xml +159 -0
  49. package/skills/map-subsystem/templates/system-cross-cutting.xml +197 -0
  50. package/skills/map-subsystem/templates/system.xml +381 -0
  51. package/skills/map-subsystem/templates/walkthrough.xml +255 -0
  52. package/{agile-context-engineering/workflows/map-subsystem.xml → skills/map-subsystem/workflow.xml} +17 -21
  53. package/skills/map-sys-doc/SKILL.md +90 -0
  54. package/skills/map-sys-doc/system.xml +381 -0
  55. package/skills/map-sys-doc/workflow.xml +336 -0
  56. package/skills/map-system/SKILL.md +85 -0
  57. package/skills/map-system/script.js +84 -0
  58. package/skills/map-system/script.test.js +73 -0
  59. package/skills/map-system/templates/wiki-readme.xml +297 -0
  60. package/{agile-context-engineering/workflows/map-system.xml → skills/map-system/workflow.xml} +11 -16
  61. package/skills/map-walkthrough/SKILL.md +92 -0
  62. package/skills/map-walkthrough/walkthrough.xml +255 -0
  63. package/skills/map-walkthrough/workflow.xml +457 -0
  64. package/skills/plan-backlog/SKILL.md +75 -0
  65. package/skills/plan-backlog/script.js +136 -0
  66. package/skills/plan-backlog/script.test.js +83 -0
  67. package/{agile-context-engineering/workflows/plan-backlog.xml → skills/plan-backlog/workflow.xml} +13 -21
  68. package/skills/plan-feature/SKILL.md +76 -0
  69. package/skills/plan-feature/script.js +148 -0
  70. package/skills/plan-feature/script.test.js +80 -0
  71. package/{agile-context-engineering/workflows/plan-feature.xml → skills/plan-feature/workflow.xml} +21 -29
  72. package/skills/plan-product-vision/SKILL.md +75 -0
  73. package/skills/plan-product-vision/script.js +60 -0
  74. package/skills/plan-product-vision/script.test.js +69 -0
  75. package/{agile-context-engineering/workflows/plan-product-vision.xml → skills/plan-product-vision/workflow.xml} +4 -9
  76. package/skills/plan-story/SKILL.md +116 -0
  77. package/skills/plan-story/script.js +326 -0
  78. package/skills/plan-story/script.test.js +240 -0
  79. package/skills/plan-story/story-template.xml +451 -0
  80. package/{agile-context-engineering/workflows/plan-story.xml → skills/plan-story/workflow.xml} +1285 -909
  81. package/skills/research-external-solution/SKILL.md +107 -0
  82. package/skills/research-external-solution/script.js +238 -0
  83. package/skills/research-external-solution/script.test.js +134 -0
  84. package/{agile-context-engineering/workflows/research-external-solution.xml → skills/research-external-solution/workflow.xml} +4 -6
  85. package/skills/research-integration-solution/SKILL.md +98 -0
  86. package/{agile-context-engineering/templates/product/story-integration-solution.xml → skills/research-integration-solution/integration-solution-template.xml} +1 -0
  87. package/skills/research-integration-solution/script.js +231 -0
  88. package/skills/research-integration-solution/script.test.js +134 -0
  89. package/{agile-context-engineering/workflows/research-integration-solution.xml → skills/research-integration-solution/workflow.xml} +4 -5
  90. package/skills/research-story-wiki/SKILL.md +92 -0
  91. package/skills/research-story-wiki/script.js +231 -0
  92. package/skills/research-story-wiki/script.test.js +138 -0
  93. package/{agile-context-engineering/templates/product/story-wiki.xml → skills/research-story-wiki/story-wiki-template.xml} +4 -0
  94. package/{agile-context-engineering/workflows/research-story-wiki.xml → skills/research-story-wiki/workflow.xml} +5 -6
  95. package/skills/research-technical-solution/SKILL.md +103 -0
  96. package/skills/research-technical-solution/script.js +231 -0
  97. package/skills/research-technical-solution/script.test.js +134 -0
  98. package/{agile-context-engineering/workflows/research-technical-solution.xml → skills/research-technical-solution/workflow.xml} +4 -5
  99. package/skills/review-story/SKILL.md +100 -0
  100. package/skills/review-story/script.js +257 -0
  101. package/skills/review-story/script.test.js +169 -0
  102. package/skills/review-story/story-template.xml +451 -0
  103. package/{agile-context-engineering/workflows/review-story.xml → skills/review-story/workflow.xml} +1 -3
  104. package/skills/update/SKILL.md +53 -0
  105. package/{agile-context-engineering/workflows/update.xml → skills/update/workflow.xml} +237 -207
  106. package/agile-context-engineering/src/ace-tools.js +0 -2881
  107. package/agile-context-engineering/src/ace-tools.test.js +0 -1089
  108. package/agile-context-engineering/templates/_command.md +0 -54
  109. package/agile-context-engineering/templates/_workflow.xml +0 -17
  110. package/agile-context-engineering/templates/config.json +0 -0
  111. package/agile-context-engineering/templates/product/integration-solution.xml +0 -0
  112. package/agile-context-engineering/templates/wiki/wiki-readme.xml +0 -276
  113. package/commands/ace/execute-story.md +0 -137
  114. package/commands/ace/help.md +0 -93
  115. package/commands/ace/init-coding-standards.md +0 -83
  116. package/commands/ace/map-story.md +0 -156
  117. package/commands/ace/map-subsystem.md +0 -138
  118. package/commands/ace/map-system.md +0 -92
  119. package/commands/ace/plan-backlog.md +0 -83
  120. package/commands/ace/plan-feature.md +0 -89
  121. package/commands/ace/plan-product-vision.md +0 -81
  122. package/commands/ace/plan-story.md +0 -145
  123. package/commands/ace/research-external-solution.md +0 -138
  124. package/commands/ace/research-integration-solution.md +0 -135
  125. package/commands/ace/research-story-wiki.md +0 -116
  126. package/commands/ace/research-technical-solution.md +0 -147
  127. package/commands/ace/review-story.md +0 -109
  128. package/commands/ace/update.md +0 -54
  129. /package/{agile-context-engineering → shared}/utils/questioning.xml +0 -0
  130. /package/{agile-context-engineering/templates/product/story.xml → skills/execute-story/story-template.xml} +0 -0
  131. /package/{agile-context-engineering/templates/wiki → skills/map-cross-cutting}/system-cross-cutting.xml +0 -0
  132. /package/{agile-context-engineering/templates/wiki → skills/map-guide}/guide.xml +0 -0
  133. /package/{agile-context-engineering/templates/wiki → skills/map-pattern}/pattern.xml +0 -0
  134. /package/{agile-context-engineering/templates/wiki → skills/map-story/templates}/decizions.xml +0 -0
  135. /package/{agile-context-engineering/templates/wiki → skills/map-story/templates}/system.xml +0 -0
  136. /package/{agile-context-engineering/templates/wiki → skills/map-story/templates}/tech-debt-index.xml +0 -0
  137. /package/{agile-context-engineering/templates/wiki → skills/map-subsystem/templates}/subsystem-architecture.xml +0 -0
  138. /package/{agile-context-engineering/templates/wiki → skills/map-subsystem/templates}/subsystem-structure.xml +0 -0
  139. /package/{agile-context-engineering/templates/wiki → skills/map-system/templates}/system-architecture.xml +0 -0
  140. /package/{agile-context-engineering/templates/wiki → skills/map-system/templates}/system-structure.xml +0 -0
  141. /package/{agile-context-engineering/templates/wiki → skills/map-system/templates}/testing-framework.xml +0 -0
  142. /package/{agile-context-engineering/templates/product/product-backlog.xml → skills/plan-backlog/product-backlog-template.xml} +0 -0
  143. /package/{agile-context-engineering/templates/product/feature.xml → skills/plan-feature/feature-template.xml} +0 -0
  144. /package/{agile-context-engineering/templates/product/product-vision.xml → skills/plan-product-vision/product-vision-template.xml} +0 -0
  145. /package/{agile-context-engineering/templates/product/external-solution.xml → skills/research-external-solution/external-solution-template.xml} +0 -0
  146. /package/{agile-context-engineering/templates/product/story-technical-solution.xml → skills/research-technical-solution/technical-solution-template.xml} +0 -0
package/bin/install.js CHANGED
@@ -39,8 +39,8 @@ const RUNTIMES = {
39
39
  },
40
40
  };
41
41
 
42
- // The folder name installed inside the config directory (e.g. ~/.claude/agile-context-engineering/)
43
- const ACE_DIR_NAME = 'agile-context-engineering';
42
+ // Legacy folder name only used for cleanup of pre-plugin installations
43
+ const ACE_LEGACY_DIR_NAME = 'agile-context-engineering';
44
44
 
45
45
  function log(message, color = '') {
46
46
  console.log(`${color}${message}${colors.reset}`);
@@ -184,7 +184,7 @@ function getBasePath(runtime, scope) {
184
184
  }
185
185
 
186
186
  // File extensions that contain path references needing runtime transformation
187
- const TRANSFORMABLE_EXTENSIONS = new Set(['.md', '.xml']);
187
+ const TRANSFORMABLE_EXTENSIONS = new Set(['.md', '.xml', '.js']);
188
188
 
189
189
  // Transform file content for a target runtime (replaces .claude/ paths with target runtime paths)
190
190
  function transformForRuntime(content, runtime) {
@@ -225,77 +225,73 @@ function copyDir(src, dest, runtime) {
225
225
  function installForRuntime(runtime, scope, packageDir) {
226
226
  const config = RUNTIMES[runtime];
227
227
  const basePath = getBasePath(runtime, scope);
228
- const commandsPath = path.join(basePath, config.commandsDir);
229
228
  const agentsPath = path.join(basePath, config.agentsDir);
230
- const acePath = path.join(basePath, ACE_DIR_NAME);
231
229
 
232
- // Source directories
233
- const srcCommands = path.join(packageDir, 'commands');
230
+ // Source directories (plugin structure)
231
+ const srcSkills = path.join(packageDir, 'skills');
232
+ const srcShared = path.join(packageDir, 'shared');
233
+ const srcPlugin = path.join(packageDir, '.claude-plugin');
234
234
  const srcAgents = path.join(packageDir, 'agents');
235
- const srcTemplates = path.join(packageDir, 'agile-context-engineering', 'templates');
236
- const srcUtils = path.join(packageDir, 'agile-context-engineering', 'utils');
237
- const srcWorkflows = path.join(packageDir, 'agile-context-engineering', 'workflows');
238
- const srcTools = path.join(packageDir, 'agile-context-engineering', 'src');
239
235
 
240
236
  log(`\nInstalling ACE for ${config.name}...`, colors.cyan);
241
237
  log(` Target: ${basePath}`, colors.dim);
242
238
 
243
- // Clean previous ACE installation to remove stale files from renamed/deleted commands
244
- const aceCommandsPath = path.join(commandsPath, 'ace');
239
+ // Clean previous ACE installation to remove stale files
240
+ const aceCommandsPath = path.join(basePath, config.commandsDir, 'ace');
245
241
  if (fs.existsSync(aceCommandsPath)) {
246
242
  fs.rmSync(aceCommandsPath, { recursive: true });
247
243
  }
248
244
  if (fs.existsSync(agentsPath)) {
249
- // Only remove ace-* agent files, preserve non-ACE agents
250
245
  for (const f of fs.readdirSync(agentsPath)) {
251
246
  if (f.startsWith('ace-')) {
252
247
  fs.rmSync(path.join(agentsPath, f), { recursive: true });
253
248
  }
254
249
  }
255
250
  }
256
- if (fs.existsSync(acePath)) {
257
- fs.rmSync(acePath, { recursive: true });
251
+ // Clean old agile-context-engineering directory from pre-plugin installs
252
+ const legacyAcePath = path.join(basePath, ACE_LEGACY_DIR_NAME);
253
+ if (fs.existsSync(legacyAcePath)) {
254
+ fs.rmSync(legacyAcePath, { recursive: true });
255
+ }
256
+ // Clean skills/shared directories from previous plugin installs
257
+ const skillsPath = path.join(basePath, 'skills');
258
+ const sharedPath = path.join(basePath, 'shared');
259
+ const pluginPath = path.join(basePath, '.claude-plugin');
260
+ if (fs.existsSync(skillsPath)) {
261
+ fs.rmSync(skillsPath, { recursive: true });
262
+ }
263
+ if (fs.existsSync(sharedPath)) {
264
+ fs.rmSync(sharedPath, { recursive: true });
265
+ }
266
+ if (fs.existsSync(pluginPath)) {
267
+ fs.rmSync(pluginPath, { recursive: true });
258
268
  }
259
269
 
260
270
  // Create directories
261
- fs.mkdirSync(commandsPath, { recursive: true });
262
271
  fs.mkdirSync(agentsPath, { recursive: true });
263
- fs.mkdirSync(acePath, { recursive: true });
264
-
265
- // Copy commands (transform paths for target runtime)
266
- if (fs.existsSync(srcCommands)) {
267
- copyDir(srcCommands, commandsPath, runtime);
268
- log(` ✓ Commands installed`, colors.green);
269
- }
270
272
 
271
- // Copy agents (transform paths for target runtime)
272
- if (fs.existsSync(srcAgents)) {
273
- copyDir(srcAgents, agentsPath, runtime);
274
- log(` ✓ Agents installed`, colors.green);
273
+ // Copy skills (plugin skill directories)
274
+ if (fs.existsSync(srcSkills)) {
275
+ copyDir(srcSkills, skillsPath, runtime);
276
+ log(` ✓ Skills installed`, colors.green);
275
277
  }
276
278
 
277
- // Copy templates into agile-context-engineering/
278
- if (fs.existsSync(srcTemplates)) {
279
- copyDir(srcTemplates, path.join(acePath, 'templates'), runtime);
280
- log(` ✓ Templates installed`, colors.green);
279
+ // Copy shared libs (ace-core.js, ace-story.js, ace-github.js, utils)
280
+ if (fs.existsSync(srcShared)) {
281
+ copyDir(srcShared, sharedPath, runtime);
282
+ log(` ✓ Shared libs installed`, colors.green);
281
283
  }
282
284
 
283
- // Copy utils into agile-context-engineering/
284
- if (fs.existsSync(srcUtils)) {
285
- copyDir(srcUtils, path.join(acePath, 'utils'), runtime);
286
- log(` ✓ Utils installed`, colors.green);
285
+ // Copy plugin manifest
286
+ if (fs.existsSync(srcPlugin)) {
287
+ copyDir(srcPlugin, pluginPath, runtime);
288
+ log(` ✓ Plugin manifest installed`, colors.green);
287
289
  }
288
290
 
289
- // Copy workflows into agile-context-engineering/
290
- if (fs.existsSync(srcWorkflows)) {
291
- copyDir(srcWorkflows, path.join(acePath, 'workflows'), runtime);
292
- log(` ✓ Workflows installed`, colors.green);
293
- }
294
-
295
- // Copy src (ace-tools) into agile-context-engineering/
296
- if (fs.existsSync(srcTools)) {
297
- copyDir(srcTools, path.join(acePath, 'src'), runtime);
298
- log(` ✓ Tools installed`, colors.green);
291
+ // Copy agents (transform paths for target runtime)
292
+ if (fs.existsSync(srcAgents)) {
293
+ copyDir(srcAgents, agentsPath, runtime);
294
+ log(` ✓ Agents installed`, colors.green);
299
295
  }
300
296
 
301
297
  // Copy hooks
@@ -314,10 +310,19 @@ function installForRuntime(runtime, scope, packageDir) {
314
310
  log(` ✓ Hooks installed`, colors.green);
315
311
  }
316
312
 
317
- // Write VERSION file for update checking
318
- const versionFile = path.join(acePath, 'VERSION');
313
+ // Write VERSION file for update checking (in shared/ alongside libs)
314
+ const versionFile = path.join(sharedPath, 'VERSION');
315
+ if (!fs.existsSync(sharedPath)) fs.mkdirSync(sharedPath, { recursive: true });
319
316
  fs.writeFileSync(versionFile, VERSION, 'utf-8');
320
317
 
318
+ // Copy CHANGELOG.md
319
+ const changelogSrc = path.join(packageDir, 'CHANGELOG.md');
320
+ const changelogDest = path.join(sharedPath, 'CHANGELOG.md');
321
+ if (fs.existsSync(changelogSrc)) {
322
+ fs.copyFileSync(changelogSrc, changelogDest);
323
+ log(` ✓ CHANGELOG.md installed`, colors.green);
324
+ }
325
+
321
326
  return basePath;
322
327
  }
323
328
 
@@ -478,29 +483,28 @@ async function main() {
478
483
  log(`\nInstalled structure:`, colors.cyan);
479
484
  for (const { path: p } of installedPaths) {
480
485
  log(` ${p}/`, colors.dim);
481
- log(` commands/ace/ Slash commands`, colors.dim);
486
+ log(` skills/ Skill directories (SKILL.md + workflow + templates + script.js)`, colors.dim);
487
+ log(` shared/ Shared libraries (ace-core.js, ace-story.js, ace-github.js)`, colors.dim);
482
488
  log(` agents/ Agent definitions`, colors.dim);
483
489
  log(` hooks/ Statusline & update hooks`, colors.dim);
484
- log(` ${ACE_DIR_NAME}/`, colors.dim);
485
- log(` templates/ Project & artifact templates`, colors.dim);
486
- log(` utils/ Formatting & utility guides`, colors.dim);
487
- log(` workflows/ Workflow definitions`, colors.dim);
490
+ log(` .claude-plugin/ Plugin manifest`, colors.dim);
488
491
  }
489
492
 
490
- log(`\nAvailable commands:`, colors.cyan);
491
- log(` /ace:help Check project status and next steps`, colors.dim);
492
- log(` /ace:plan-project Plan your project with epics and features`, colors.dim);
493
- log(` /ace:plan-epic Plan an epic with features and stories`, colors.dim);
494
- log(` /ace:plan-feature Plan a feature with stories`, colors.dim);
495
- log(` /ace:plan-story Plan a story with tasks`, colors.dim);
496
- log(` /ace:refine-story Refine a story for execution`, colors.dim);
497
- log(` /ace:execute-story Execute a story`, colors.dim);
498
- log(` /ace:verify-story Verify a completed story`, colors.dim);
493
+ log(`\nAvailable skills:`, colors.cyan);
494
+ log(` /ace:help Check project status and next steps`, colors.dim);
495
+ log(` /ace:plan-product-vision Create or update the product vision`, colors.dim);
496
+ log(` /ace:plan-backlog Plan the product backlog`, colors.dim);
497
+ log(` /ace:plan-feature Plan a feature with stories`, colors.dim);
498
+ log(` /ace:plan-story Plan a story specification`, colors.dim);
499
+ log(` /ace:execute-story Execute a planned story`, colors.dim);
500
+ log(` /ace:map-system Map system-wide architecture`, colors.dim);
501
+ log(` /ace:map-subsystem Map a subsystem's internals`, colors.dim);
502
+ log(` /ace:init-coding-standards Generate coding standards`, colors.dim);
499
503
 
500
504
  log(`\nGet started:`, colors.cyan);
501
505
  log(` 1. Navigate to your project directory`, colors.dim);
502
506
  log(` 2. Run /ace:help to initialize ACE`, colors.dim);
503
- log(` 3. Run /ace:plan-project to start planning\n`, colors.dim);
507
+ log(` 3. Run /ace:plan-product-vision to define your product\n`, colors.dim);
504
508
  }
505
509
 
506
510
  main().catch((err) => {
@@ -13,8 +13,11 @@ const cacheDir = path.join(homeDir, '.claude', 'cache');
13
13
  const cacheFile = path.join(cacheDir, 'ace-update-check.json');
14
14
 
15
15
  // VERSION file locations (check project first, then global)
16
- const projectVersionFile = path.join(cwd, '.claude', 'agile-context-engineering', 'VERSION');
17
- const globalVersionFile = path.join(homeDir, '.claude', 'agile-context-engineering', 'VERSION');
16
+ // New plugin structure: shared/VERSION; legacy: agile-context-engineering/VERSION
17
+ const projectVersionFile = path.join(cwd, '.claude', 'shared', 'VERSION');
18
+ const globalVersionFile = path.join(homeDir, '.claude', 'shared', 'VERSION');
19
+ const legacyProjectVersionFile = path.join(cwd, '.claude', 'agile-context-engineering', 'VERSION');
20
+ const legacyGlobalVersionFile = path.join(homeDir, '.claude', 'agile-context-engineering', 'VERSION');
18
21
 
19
22
  // Ensure cache directory exists
20
23
  if (!fs.existsSync(cacheDir)) {
@@ -27,16 +30,21 @@ const child = spawn(process.execPath, ['-e', `
27
30
  const { execSync } = require('child_process');
28
31
 
29
32
  const cacheFile = ${JSON.stringify(cacheFile)};
30
- const projectVersionFile = ${JSON.stringify(projectVersionFile)};
31
- const globalVersionFile = ${JSON.stringify(globalVersionFile)};
33
+ const versionFiles = [
34
+ ${JSON.stringify(projectVersionFile)},
35
+ ${JSON.stringify(globalVersionFile)},
36
+ ${JSON.stringify(legacyProjectVersionFile)},
37
+ ${JSON.stringify(legacyGlobalVersionFile)},
38
+ ];
32
39
 
33
- // Check project directory first (local install), then global
40
+ // Check new paths first, then legacy
34
41
  let installed = '0.0.0';
35
42
  try {
36
- if (fs.existsSync(projectVersionFile)) {
37
- installed = fs.readFileSync(projectVersionFile, 'utf8').trim();
38
- } else if (fs.existsSync(globalVersionFile)) {
39
- installed = fs.readFileSync(globalVersionFile, 'utf8').trim();
43
+ for (const vf of versionFiles) {
44
+ if (fs.existsSync(vf)) {
45
+ installed = fs.readFileSync(vf, 'utf8').trim();
46
+ break;
47
+ }
40
48
  }
41
49
  } catch (e) {}
42
50
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agile-context-engineering",
3
- "version": "0.2.2",
3
+ "version": "0.5.0",
4
4
  "description": "ACE - Agile Context Engineering: A spec-driven development system for Claude Code and Crush (formerly OpenCode) with Agile workflows",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -9,13 +9,15 @@
9
9
  },
10
10
  "files": [
11
11
  "bin",
12
- "commands",
12
+ "skills",
13
+ "shared",
13
14
  "agents",
14
15
  "hooks",
15
- "agile-context-engineering"
16
+ ".claude-plugin",
17
+ "CHANGELOG.md"
16
18
  ],
17
19
  "scripts": {
18
- "test": "node --test agile-context-engineering/src/ace-tools.test.js"
20
+ "test": "node --test shared/lib/*.test.js skills/*/script.test.js"
19
21
  },
20
22
  "keywords": [
21
23
  "claude-code",
@@ -32,7 +34,7 @@
32
34
  "license": "MIT",
33
35
  "repository": {
34
36
  "type": "git",
35
- "url": "https://github.com/agile-context-engineering/ace"
37
+ "url": "https://github.com/Quantarcane/ACE"
36
38
  },
37
39
  "engines": {
38
40
  "node": ">=16.7.0"
@@ -0,0 +1,308 @@
1
+ /**
2
+ * ACE Core — Universal helpers shared across all ACE skills.
3
+ *
4
+ * Extracted from ace-tools.js monolith. Contains: config loading, model resolution,
5
+ * path checks, environment detection, slug/timestamp generation, settings management,
6
+ * and process-level output/error helpers.
7
+ *
8
+ * Usage: const { loadConfig, resolveModel, output, error } = require('./ace-core');
9
+ */
10
+
11
+ const fs = require('fs');
12
+ const path = require('path');
13
+
14
+ // ─── Model Profile Table ─────────────────────────────────────────────────────
15
+
16
+ const MODEL_PROFILES = {
17
+ 'ace-product-owner': { quality: 'opus', balanced: 'sonnet', budget: 'sonnet' },
18
+ 'ace-project-researcher': { quality: 'opus', balanced: 'sonnet', budget: 'haiku' },
19
+ 'ace-research-synthesizer': { quality: 'sonnet', balanced: 'sonnet', budget: 'haiku' },
20
+ 'ace-wiki-mapper': { quality: 'sonnet', balanced: 'sonnet', budget: 'haiku' },
21
+ 'ace-code-integration-analyst': { quality: 'opus', balanced: 'opus', budget: 'sonnet' },
22
+ 'ace-code-discovery-analyst': { quality: 'opus', balanced: 'opus', budget: 'sonnet' },
23
+ 'ace-executor': { quality: 'opus', balanced: 'sonnet', budget: 'sonnet' },
24
+ 'ace-code-reviewer': { quality: 'sonnet', balanced: 'sonnet', budget: 'haiku' },
25
+ };
26
+
27
+ // ─── Settings Defaults ───────────────────────────────────────────────────────
28
+
29
+ const SETTINGS_DEFAULTS = {
30
+ model_profile: 'balanced',
31
+ commit_docs: true,
32
+ agent_teams: false,
33
+ github_project: {
34
+ enabled: false,
35
+ gh_installed: false,
36
+ repo: '',
37
+ project_number: null,
38
+ owner: '',
39
+ },
40
+ };
41
+
42
+ // ─── Process Output ──────────────────────────────────────────────────────────
43
+
44
+ function output(result, raw, rawValue) {
45
+ if (raw && rawValue !== undefined) {
46
+ process.stdout.write(String(rawValue));
47
+ } else {
48
+ process.stdout.write(JSON.stringify(result, null, 2));
49
+ }
50
+ process.exit(0);
51
+ }
52
+
53
+ function error(message) {
54
+ process.stderr.write('Error: ' + message + '\n');
55
+ process.exit(1);
56
+ }
57
+
58
+ // ─── Config ──────────────────────────────────────────────────────────────────
59
+
60
+ function loadConfig(cwd) {
61
+ const configPath = path.join(cwd, '.ace', 'config.json');
62
+ const defaults = {
63
+ version: '0.1.0',
64
+ projectName: '',
65
+ description: '',
66
+ storage: 'local',
67
+ model_profile: 'quality',
68
+ commit_docs: true,
69
+ github: {
70
+ enabled: false,
71
+ repo: null,
72
+ labels: {
73
+ epic: 'ace:epic',
74
+ feature: 'ace:feature',
75
+ story: 'ace:story',
76
+ task: 'ace:task',
77
+ },
78
+ },
79
+ createdAt: '',
80
+ };
81
+
82
+ try {
83
+ const raw = fs.readFileSync(configPath, 'utf-8');
84
+ const parsed = JSON.parse(raw);
85
+ return {
86
+ version: parsed.version ?? defaults.version,
87
+ projectName: parsed.projectName ?? defaults.projectName,
88
+ description: parsed.description ?? defaults.description,
89
+ storage: parsed.storage ?? defaults.storage,
90
+ model_profile: parsed.model_profile ?? defaults.model_profile,
91
+ commit_docs: parsed.commit_docs ?? defaults.commit_docs,
92
+ github: {
93
+ enabled: parsed.github?.enabled ?? defaults.github.enabled,
94
+ repo: parsed.github?.repo ?? defaults.github.repo,
95
+ labels: {
96
+ epic: parsed.github?.labels?.epic ?? defaults.github.labels.epic,
97
+ feature: parsed.github?.labels?.feature ?? defaults.github.labels.feature,
98
+ story: parsed.github?.labels?.story ?? defaults.github.labels.story,
99
+ task: parsed.github?.labels?.task ?? defaults.github.labels.task,
100
+ },
101
+ },
102
+ createdAt: parsed.createdAt ?? defaults.createdAt,
103
+ };
104
+ } catch {
105
+ return defaults;
106
+ }
107
+ }
108
+
109
+ // ─── Path Helpers ────────────────────────────────────────────────────────────
110
+
111
+ function pathExists(cwd, targetPath) {
112
+ const fullPath = path.isAbsolute(targetPath) ? targetPath : path.join(cwd, targetPath);
113
+ try {
114
+ fs.statSync(fullPath);
115
+ return true;
116
+ } catch {
117
+ return false;
118
+ }
119
+ }
120
+
121
+ function safeReadFile(filePath) {
122
+ try { return fs.readFileSync(filePath, 'utf-8'); }
123
+ catch { return null; }
124
+ }
125
+
126
+ // ─── Slug & Timestamp ────────────────────────────────────────────────────────
127
+
128
+ function generateSlug(text) {
129
+ if (!text) return null;
130
+ return text.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
131
+ }
132
+
133
+ function currentTimestamp(format) {
134
+ const now = new Date();
135
+ switch (format) {
136
+ case 'date':
137
+ return now.toISOString().split('T')[0];
138
+ case 'filename':
139
+ return now.toISOString().replace(/[:.]/g, '-').replace('T', '_').split('Z')[0];
140
+ case 'full':
141
+ default:
142
+ return now.toISOString();
143
+ }
144
+ }
145
+
146
+ // ─── Model Resolution ────────────────────────────────────────────────────────
147
+
148
+ function resolveModel(cwd, agentType) {
149
+ const config = loadConfig(cwd);
150
+ const profile = config.model_profile || 'balanced';
151
+ const agentModels = MODEL_PROFILES[agentType];
152
+ if (!agentModels) return 'sonnet';
153
+ return agentModels[profile] || agentModels['balanced'] || 'sonnet';
154
+ }
155
+
156
+ // ─── Code & Environment Detection ────────────────────────────────────────────
157
+
158
+ /**
159
+ * Detect existing code files by walking up to maxDepth levels.
160
+ */
161
+ function detectCodeFiles(cwd, maxDepth) {
162
+ const codeExtensions = new Set(['.cs', '.ts', '.js', '.py', '.go', '.rs', '.swift', '.java', '.tsx', '.jsx']);
163
+ const ignoreDirs = new Set(['node_modules', '.git', '.ace', '.gsd', 'dist', 'build', '__pycache__']);
164
+ const found = [];
165
+
166
+ function walk(dir, depth) {
167
+ if (depth > maxDepth || found.length >= 5) return;
168
+ let entries;
169
+ try {
170
+ entries = fs.readdirSync(dir, { withFileTypes: true });
171
+ } catch {
172
+ return;
173
+ }
174
+ for (const entry of entries) {
175
+ if (found.length >= 5) return;
176
+ if (entry.isDirectory()) {
177
+ if (!ignoreDirs.has(entry.name)) {
178
+ walk(path.join(dir, entry.name), depth + 1);
179
+ }
180
+ } else if (entry.isFile()) {
181
+ const ext = path.extname(entry.name);
182
+ if (codeExtensions.has(ext)) {
183
+ found.push(path.join(dir, entry.name));
184
+ }
185
+ }
186
+ }
187
+ }
188
+
189
+ walk(cwd, 0);
190
+ return found;
191
+ }
192
+
193
+ /**
194
+ * Detect whether the project is brownfield (existing code/manifests) or greenfield.
195
+ */
196
+ function detectBrownfieldStatus(cwd) {
197
+ const codeFiles = detectCodeFiles(cwd, 3);
198
+ const hasExistingCode = codeFiles.length > 0;
199
+
200
+ const packageFiles = [
201
+ 'package.json', 'requirements.txt', 'pyproject.toml', 'Cargo.toml',
202
+ 'go.mod', 'Package.swift', 'pom.xml', 'build.gradle',
203
+ ];
204
+
205
+ const hasDotnetProject = (() => {
206
+ try {
207
+ const rootFiles = fs.readdirSync(cwd);
208
+ return rootFiles.some(f => f.endsWith('.sln') || f.endsWith('.csproj'));
209
+ } catch {
210
+ return false;
211
+ }
212
+ })();
213
+
214
+ const hasPackageFile = packageFiles.some(f => pathExists(cwd, f)) || hasDotnetProject;
215
+ const isBrownfield = hasExistingCode || hasPackageFile;
216
+
217
+ return {
218
+ has_existing_code: hasExistingCode,
219
+ has_package_file: hasPackageFile,
220
+ is_brownfield: isBrownfield,
221
+ is_greenfield: !isBrownfield,
222
+ };
223
+ }
224
+
225
+ // ─── Settings ────────────────────────────────────────────────────────────────
226
+
227
+ function loadSettings(cwd) {
228
+ const settingsPath = path.join(cwd, '.ace', 'settings.json');
229
+ try {
230
+ const raw = fs.readFileSync(settingsPath, 'utf-8');
231
+ const parsed = JSON.parse(raw);
232
+ return {
233
+ model_profile: parsed.model_profile ?? SETTINGS_DEFAULTS.model_profile,
234
+ commit_docs: parsed.commit_docs ?? SETTINGS_DEFAULTS.commit_docs,
235
+ agent_teams: parsed.agent_teams ?? SETTINGS_DEFAULTS.agent_teams,
236
+ github_project: {
237
+ enabled: parsed.github_project?.enabled ?? SETTINGS_DEFAULTS.github_project.enabled,
238
+ gh_installed: parsed.github_project?.gh_installed ?? SETTINGS_DEFAULTS.github_project.gh_installed,
239
+ repo: parsed.github_project?.repo ?? SETTINGS_DEFAULTS.github_project.repo,
240
+ project_number: parsed.github_project?.project_number ?? SETTINGS_DEFAULTS.github_project.project_number,
241
+ owner: parsed.github_project?.owner ?? SETTINGS_DEFAULTS.github_project.owner,
242
+ },
243
+ };
244
+ } catch {
245
+ return JSON.parse(JSON.stringify(SETTINGS_DEFAULTS));
246
+ }
247
+ }
248
+
249
+ function writeSettings(cwd, settings) {
250
+ const aceDir = path.join(cwd, '.ace');
251
+ if (!fs.existsSync(aceDir)) {
252
+ fs.mkdirSync(aceDir, { recursive: true });
253
+ }
254
+ const settingsPath = path.join(aceDir, 'settings.json');
255
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf-8');
256
+ }
257
+
258
+ // ─── Shell Execution ─────────────────────────────────────────────────────────
259
+
260
+ /**
261
+ * Parse key=value arguments into an object.
262
+ */
263
+ function parseKeyValueArgs(args) {
264
+ const result = {};
265
+ for (const arg of args) {
266
+ const eqIndex = arg.indexOf('=');
267
+ if (eqIndex === -1) continue;
268
+ result[arg.substring(0, eqIndex)] = arg.substring(eqIndex + 1);
269
+ }
270
+ return result;
271
+ }
272
+
273
+ /**
274
+ * Run a shell command and return trimmed stdout. Returns null on failure.
275
+ */
276
+ function execCommand(cmd, cwd) {
277
+ const { execSync } = require('child_process');
278
+ try {
279
+ return execSync(cmd, {
280
+ cwd,
281
+ shell: 'bash',
282
+ stdio: ['pipe', 'pipe', 'pipe'],
283
+ encoding: 'utf-8',
284
+ timeout: 30000,
285
+ }).trim();
286
+ } catch {
287
+ return null;
288
+ }
289
+ }
290
+
291
+ module.exports = {
292
+ MODEL_PROFILES,
293
+ SETTINGS_DEFAULTS,
294
+ output,
295
+ error,
296
+ loadConfig,
297
+ pathExists,
298
+ safeReadFile,
299
+ generateSlug,
300
+ currentTimestamp,
301
+ resolveModel,
302
+ detectCodeFiles,
303
+ detectBrownfieldStatus,
304
+ loadSettings,
305
+ writeSettings,
306
+ parseKeyValueArgs,
307
+ execCommand,
308
+ };