@hone-ai/cli 1.12.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 +8 -49
- package/lib/splice-repo-specific.js +61 -0
- package/package.json +1 -1
package/hone-cli.js
CHANGED
|
@@ -2610,49 +2610,13 @@ function sleep(ms) {
|
|
|
2610
2610
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
2611
2611
|
}
|
|
2612
2612
|
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
* Pre-fix: derive wrote the file blind (fs.writeFileSync(skillFile,
|
|
2621
|
-
* content)). 5 OptionsFlow regression tests broke after a single derive
|
|
2622
|
-
* because 378 lines of E33-A/E34-B-added content disappeared.
|
|
2623
|
-
*
|
|
2624
|
-
* Returns { merged, preserved }:
|
|
2625
|
-
* - merged: the bytes to write (always a string)
|
|
2626
|
-
* - preserved: true if adopter REPO-SPECIFIC content was preserved
|
|
2627
|
-
* across the splice; false for fresh writes
|
|
2628
|
-
*/
|
|
2629
|
-
function splicePreserveRepoSpecific(existingPath, newContent) {
|
|
2630
|
-
const MARKER = '<!-- REPO-SPECIFIC';
|
|
2631
|
-
if (!fs.existsSync(existingPath)) {
|
|
2632
|
-
// Fresh adopter — no existing file. Write new content as-is.
|
|
2633
|
-
return { merged: newContent, preserved: false };
|
|
2634
|
-
}
|
|
2635
|
-
const existing = fs.readFileSync(existingPath, 'utf8');
|
|
2636
|
-
const oldIdx = existing.indexOf(MARKER);
|
|
2637
|
-
if (oldIdx < 0) {
|
|
2638
|
-
// Existing file has no REPO-SPECIFIC marker (likely a non-skill or
|
|
2639
|
-
// legacy file). Nothing to preserve.
|
|
2640
|
-
return { merged: newContent, preserved: false };
|
|
2641
|
-
}
|
|
2642
|
-
const newIdx = newContent.indexOf(MARKER);
|
|
2643
|
-
if (newIdx < 0) {
|
|
2644
|
-
// New content has no REPO-SPECIFIC section. Append the existing one.
|
|
2645
|
-
return { merged: newContent.trimEnd() + '\n\n' + existing.slice(oldIdx), preserved: true };
|
|
2646
|
-
}
|
|
2647
|
-
// Both have REPO-SPECIFIC. Take new content up to the marker, then
|
|
2648
|
-
// append everything from existing's marker to EOF. This preserves
|
|
2649
|
-
// adopter-curated content while updating the body before the marker.
|
|
2650
|
-
return {
|
|
2651
|
-
merged: newContent.slice(0, newIdx) + existing.slice(oldIdx),
|
|
2652
|
-
preserved: true,
|
|
2653
|
-
};
|
|
2654
|
-
}
|
|
2655
|
-
module.exports.splicePreserveRepoSpecific = splicePreserveRepoSpecific;
|
|
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');
|
|
2656
2620
|
|
|
2657
2621
|
// ── STATUS command (H-009) ────────────────────────────────────────────────────
|
|
2658
2622
|
// Show pipeline state for a story (or use --all for every in-flight story).
|
|
@@ -5295,9 +5259,4 @@ program
|
|
|
5295
5259
|
.description('Hone AI — Enterprise SDLC Pipeline CLI')
|
|
5296
5260
|
.version(pkg.version);
|
|
5297
5261
|
|
|
5298
|
-
|
|
5299
|
-
// require()'d by tests for helpers like splicePreserveRepoSpecific
|
|
5300
|
-
// without auto-running the CLI's --help.
|
|
5301
|
-
if (require.main === module) {
|
|
5302
|
-
program.parse(process.argv);
|
|
5303
|
-
}
|
|
5262
|
+
program.parse(process.argv);
|
|
@@ -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 };
|