@hone-ai/cli 1.11.0 → 1.12.1

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/hone-cli.js CHANGED
@@ -1087,13 +1087,24 @@ program
1087
1087
  // Write generated skills to disk
1088
1088
  console.log('Writing generated skills...');
1089
1089
  if (result.skills) {
1090
+ let preservedCount = 0;
1090
1091
  for (const [skillName, content] of Object.entries(result.skills)) {
1091
1092
  if (!content || content.length < 50) continue;
1092
1093
  const skillDir = path.join(repoRoot, '.github', 'skills', skillName);
1093
1094
  const skillFile = path.join(skillDir, 'SKILL.md');
1094
1095
  fs.mkdirSync(skillDir, { recursive: true });
1095
- fs.writeFileSync(skillFile, content);
1096
- console.log(` ✓ .github/skills/${skillName}/SKILL.md`);
1096
+ const { merged, preserved } = splicePreserveRepoSpecific(skillFile, content);
1097
+ fs.writeFileSync(skillFile, merged);
1098
+ if (preserved) {
1099
+ preservedCount++;
1100
+ console.log(` ✓ .github/skills/${skillName}/SKILL.md (preserved adopter REPO-SPECIFIC content)`);
1101
+ } else {
1102
+ console.log(` ✓ .github/skills/${skillName}/SKILL.md`);
1103
+ }
1104
+ }
1105
+ if (preservedCount > 0) {
1106
+ console.log(`\n Preserved adopter REPO-SPECIFIC content in ${preservedCount} skill(s).`);
1107
+ console.log(` (HC-019y-followup-1: derive no longer overwrites curated REPO-SPECIFIC sections)`);
1097
1108
  }
1098
1109
  }
1099
1110
 
@@ -2599,6 +2610,14 @@ function sleep(ms) {
2599
2610
  return new Promise(resolve => setTimeout(resolve, ms));
2600
2611
  }
2601
2612
 
2613
+ // HC-019y-followup-1: splice helper extracted to cli/lib/ so tests
2614
+ // can require it without dragging in the commander entry point.
2615
+ // (Previous attempt to gate `program.parse` behind `require.main ===
2616
+ // module` broke the CLI in 1.12.0 because bin/hone.js does
2617
+ // `require('../hone-cli.js')` — making require.main the bin wrapper,
2618
+ // not hone-cli.js, so the gate never fired and parse never ran.)
2619
+ const { splicePreserveRepoSpecific } = require('./lib/splice-repo-specific');
2620
+
2602
2621
  // ── STATUS command (H-009) ────────────────────────────────────────────────────
2603
2622
  // Show pipeline state for a story (or use --all for every in-flight story).
2604
2623
  // Reads .github/pipeline/<STORY-ID>/metadata.yml + filesystem; uses
@@ -0,0 +1,61 @@
1
+ 'use strict';
2
+ /**
3
+ * HC-019y-followup-1: when `hone derive` writes a SKILL.md, splice the
4
+ * existing adopter REPO-SPECIFIC content over whatever the LLM produced
5
+ * for that section. The semantic guarantee: adopter-curated content
6
+ * (everything below the `<!-- REPO-SPECIFIC -->` marker) is NEVER lost
7
+ * across derive runs.
8
+ *
9
+ * Pre-fix: derive wrote the file blind (fs.writeFileSync(skillFile,
10
+ * content)). 5 OptionsFlow regression tests broke after a single derive
11
+ * because 378 lines of E33-A/E34-B-added content disappeared.
12
+ *
13
+ * Extracted to its own file (cli/lib/) so it can be require()'d by
14
+ * tests without dragging in cli/hone-cli.js's commander entry point.
15
+ * The earlier attempt to gate `program.parse` behind
16
+ * `require.main === module` broke the CLI in production because
17
+ * bin/hone.js calls `require('../hone-cli.js')` — making require.main
18
+ * the bin/hone.js wrapper, not hone-cli.js itself, so the gate never
19
+ * fired and `program.parse` never ran. Lesson: don't gate side-effect
20
+ * top-level code; extract testable helpers to their own modules.
21
+ */
22
+ const fs = require('node:fs');
23
+
24
+ /**
25
+ * Splice adopter REPO-SPECIFIC tail into derive output.
26
+ *
27
+ * @param {string} existingPath absolute path of the file being overwritten
28
+ * @param {string} newContent the content from `hone derive` server response
29
+ * @returns {{ merged: string, preserved: boolean }}
30
+ * merged: bytes to write
31
+ * preserved: true if adopter REPO-SPECIFIC content was preserved
32
+ * across the splice; false for fresh writes
33
+ */
34
+ function splicePreserveRepoSpecific(existingPath, newContent) {
35
+ const MARKER = '<!-- REPO-SPECIFIC';
36
+ if (!fs.existsSync(existingPath)) {
37
+ // Fresh adopter — no existing file. Write new content as-is.
38
+ return { merged: newContent, preserved: false };
39
+ }
40
+ const existing = fs.readFileSync(existingPath, 'utf8');
41
+ const oldIdx = existing.indexOf(MARKER);
42
+ if (oldIdx < 0) {
43
+ // Existing file has no REPO-SPECIFIC marker (likely a non-skill or
44
+ // legacy file). Nothing to preserve.
45
+ return { merged: newContent, preserved: false };
46
+ }
47
+ const newIdx = newContent.indexOf(MARKER);
48
+ if (newIdx < 0) {
49
+ // New content has no REPO-SPECIFIC section. Append the existing one.
50
+ return { merged: newContent.trimEnd() + '\n\n' + existing.slice(oldIdx), preserved: true };
51
+ }
52
+ // Both have REPO-SPECIFIC. Take new content up to the marker, then
53
+ // append everything from existing's marker to EOF. This preserves
54
+ // adopter-curated content while updating the body before the marker.
55
+ return {
56
+ merged: newContent.slice(0, newIdx) + existing.slice(oldIdx),
57
+ preserved: true,
58
+ };
59
+ }
60
+
61
+ module.exports = { splicePreserveRepoSpecific };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hone-ai/cli",
3
- "version": "1.11.0",
3
+ "version": "1.12.1",
4
4
  "description": "Hone AI — Enterprise SDLC Pipeline CLI",
5
5
  "main": "hone-cli.js",
6
6
  "bin": {