@hone-ai/cli 1.10.0 → 1.12.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/hone-cli.js +87 -39
- package/lib/refresh-knowledge.js +4 -1
- package/package.json +1 -1
package/hone-cli.js
CHANGED
|
@@ -387,7 +387,12 @@ program
|
|
|
387
387
|
process.exit(1);
|
|
388
388
|
}
|
|
389
389
|
|
|
390
|
-
// ── Phase 1b: Install
|
|
390
|
+
// ── Phase 1b: Install CLAUDE.md ──────────────────────────────────────────
|
|
391
|
+
// HC-019y: removed the install-time .github/agents/ -> .claude/agents/
|
|
392
|
+
// mirror. The bash setup script now writes agents directly to
|
|
393
|
+
// .claude/agents/ (the path Claude Code actually reads). Pre-fix the
|
|
394
|
+
// mirror ran only at install time, so post-install edits to
|
|
395
|
+
// .github/agents/ were silently dropped (OptionsFlow E34-A: 6+ PRs).
|
|
391
396
|
const repoRoot = process.cwd();
|
|
392
397
|
const claudeMdSrc = path.join(tmpDir, 'CLAUDE.md');
|
|
393
398
|
const claudeMdDst = path.join(repoRoot, 'CLAUDE.md');
|
|
@@ -402,20 +407,6 @@ program
|
|
|
402
407
|
}
|
|
403
408
|
}
|
|
404
409
|
|
|
405
|
-
// Create .claude/agents/ with symlinks or copies of .github/agents/
|
|
406
|
-
const ghAgentsDir = path.join(repoRoot, '.github', 'agents');
|
|
407
|
-
const claudeAgentsDir = path.join(repoRoot, '.claude', 'agents');
|
|
408
|
-
if (fs.existsSync(ghAgentsDir)) {
|
|
409
|
-
fs.mkdirSync(claudeAgentsDir, { recursive: true });
|
|
410
|
-
const agentFiles = fs.readdirSync(ghAgentsDir).filter(f => f.endsWith('.agent.md'));
|
|
411
|
-
for (const file of agentFiles) {
|
|
412
|
-
const src = path.join(ghAgentsDir, file);
|
|
413
|
-
const dst = path.join(claudeAgentsDir, file);
|
|
414
|
-
fs.copyFileSync(src, dst);
|
|
415
|
-
}
|
|
416
|
-
console.log(` ✓ .claude/agents/ created (${agentFiles.length} agents mirrored from .github/agents/)`);
|
|
417
|
-
}
|
|
418
|
-
|
|
419
410
|
// ── Phase 1c: Platform grounding (HC-013b-setup) ──────────────────────
|
|
420
411
|
// Runs AFTER bash script writes .pipeline-config.yml.
|
|
421
412
|
// Discovers metadata types, detects MCP, selects doc registry URLs,
|
|
@@ -1096,13 +1087,24 @@ program
|
|
|
1096
1087
|
// Write generated skills to disk
|
|
1097
1088
|
console.log('Writing generated skills...');
|
|
1098
1089
|
if (result.skills) {
|
|
1090
|
+
let preservedCount = 0;
|
|
1099
1091
|
for (const [skillName, content] of Object.entries(result.skills)) {
|
|
1100
1092
|
if (!content || content.length < 50) continue;
|
|
1101
1093
|
const skillDir = path.join(repoRoot, '.github', 'skills', skillName);
|
|
1102
1094
|
const skillFile = path.join(skillDir, 'SKILL.md');
|
|
1103
1095
|
fs.mkdirSync(skillDir, { recursive: true });
|
|
1104
|
-
|
|
1105
|
-
|
|
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)`);
|
|
1106
1108
|
}
|
|
1107
1109
|
}
|
|
1108
1110
|
|
|
@@ -1505,7 +1507,12 @@ program
|
|
|
1505
1507
|
}
|
|
1506
1508
|
}
|
|
1507
1509
|
|
|
1508
|
-
// ── 2. Sync agent prompts into .
|
|
1510
|
+
// ── 2. Sync agent prompts into .claude/agents/*.agent.md ───────────────
|
|
1511
|
+
// HC-019y: writes directly to .claude/agents/ (the path Claude Code
|
|
1512
|
+
// actually reads). Pre-fix `hone sync` wrote to .github/agents/ and
|
|
1513
|
+
// mirrored to .claude/agents/, but the dual-directory layout caused
|
|
1514
|
+
// OptionsFlow E34-A's silent-drop trap when adopters edited the wrong
|
|
1515
|
+
// path. Mirror eliminated; .github/agents/ is now legacy.
|
|
1509
1516
|
if (!opts.skillsOnly) {
|
|
1510
1517
|
const agents = [
|
|
1511
1518
|
'story-groomer',
|
|
@@ -1520,9 +1527,16 @@ program
|
|
|
1520
1527
|
];
|
|
1521
1528
|
|
|
1522
1529
|
console.log('\nSyncing agent prompts...');
|
|
1523
|
-
const agentsDir = path.join(process.cwd(), '.
|
|
1530
|
+
const agentsDir = path.join(process.cwd(), '.claude', 'agents');
|
|
1524
1531
|
fs.mkdirSync(agentsDir, { recursive: true });
|
|
1525
1532
|
|
|
1533
|
+
// Legacy-path migration warning (one-time, non-destructive).
|
|
1534
|
+
const legacyGhAgents = path.join(process.cwd(), '.github', 'agents');
|
|
1535
|
+
if (fs.existsSync(legacyGhAgents)) {
|
|
1536
|
+
console.log(' ⚠ Legacy .github/agents/ detected — Claude Code reads .claude/agents/');
|
|
1537
|
+
console.log(' Migrate any REPO-SPECIFIC edits then `rm -rf .github/agents/`');
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1526
1540
|
let synced = 0;
|
|
1527
1541
|
for (const agent of agents) {
|
|
1528
1542
|
try {
|
|
@@ -1531,7 +1545,7 @@ program
|
|
|
1531
1545
|
headers: { Accept: 'text/markdown' },
|
|
1532
1546
|
});
|
|
1533
1547
|
const dest = path.join(agentsDir, `${agent}.agent.md`);
|
|
1534
|
-
const rel =
|
|
1548
|
+
const rel = `.claude/agents/${agent}.agent.md`;
|
|
1535
1549
|
const result = safeWrite(dest, rel, r.data);
|
|
1536
1550
|
if (result !== 'skipped') {
|
|
1537
1551
|
console.log(` ✓ ${rel}`);
|
|
@@ -1539,28 +1553,13 @@ program
|
|
|
1539
1553
|
}
|
|
1540
1554
|
} catch (e) {
|
|
1541
1555
|
if (e.response?.status === 404) {
|
|
1542
|
-
console.log(` ⚠ agents/${agent}.agent.md — prompt not seeded yet (run seed-agent-prompts.js)`);
|
|
1556
|
+
console.log(` ⚠ .claude/agents/${agent}.agent.md — prompt not seeded yet (run seed-agent-prompts.js)`);
|
|
1543
1557
|
} else {
|
|
1544
|
-
console.log(` ⚠ agents/${agent}.agent.md — ${e.message}`);
|
|
1558
|
+
console.log(` ⚠ .claude/agents/${agent}.agent.md — ${e.message}`);
|
|
1545
1559
|
}
|
|
1546
1560
|
}
|
|
1547
1561
|
}
|
|
1548
1562
|
if (synced > 0) console.log(`\nSynced ${synced} agent prompts.`);
|
|
1549
|
-
|
|
1550
|
-
// Mirror to .claude/agents/ for Claude Code. This mirror is a derived
|
|
1551
|
-
// copy of .github/agents/, so it's safe to overwrite — local edits
|
|
1552
|
-
// belong in the source-of-truth dir. We still respect skipped files:
|
|
1553
|
-
// if the source was skipped, don't overwrite the mirror either.
|
|
1554
|
-
const claudeAgentsDir = path.join(process.cwd(), '.claude', 'agents');
|
|
1555
|
-
const ghAgentsDir = path.join(process.cwd(), '.github', 'agents');
|
|
1556
|
-
if (fs.existsSync(ghAgentsDir)) {
|
|
1557
|
-
fs.mkdirSync(claudeAgentsDir, { recursive: true });
|
|
1558
|
-
const files = fs.readdirSync(ghAgentsDir).filter(f => f.endsWith('.agent.md'));
|
|
1559
|
-
for (const f of files) {
|
|
1560
|
-
fs.copyFileSync(path.join(ghAgentsDir, f), path.join(claudeAgentsDir, f));
|
|
1561
|
-
}
|
|
1562
|
-
console.log(` ✓ .claude/agents/ mirrored (${files.length} agents)`);
|
|
1563
|
-
}
|
|
1564
1563
|
}
|
|
1565
1564
|
|
|
1566
1565
|
// ── 3. Sync copilot-instructions.md from server ──────────────────────
|
|
@@ -2029,7 +2028,7 @@ program
|
|
|
2029
2028
|
console.log('╚══════════════════════════════════════════════╝');
|
|
2030
2029
|
console.log('');
|
|
2031
2030
|
console.log(' Your repo now has:');
|
|
2032
|
-
console.log(' • 9 agents (.
|
|
2031
|
+
console.log(' • 9 agents (.claude/agents/)');
|
|
2033
2032
|
console.log(' • 11 enterprise skills + domain skills');
|
|
2034
2033
|
console.log(' • CI workflow (ai-review.yml)');
|
|
2035
2034
|
console.log(' • CLAUDE.md + copilot-instructions.md');
|
|
@@ -2611,6 +2610,50 @@ function sleep(ms) {
|
|
|
2611
2610
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
2612
2611
|
}
|
|
2613
2612
|
|
|
2613
|
+
/**
|
|
2614
|
+
* HC-019y-followup-1: when `hone derive` writes a SKILL.md, splice the
|
|
2615
|
+
* existing adopter REPO-SPECIFIC content over whatever the LLM produced
|
|
2616
|
+
* for that section. The semantic guarantee: adopter-curated content
|
|
2617
|
+
* (everything below the `<!-- REPO-SPECIFIC -->` marker) is NEVER lost
|
|
2618
|
+
* across derive runs.
|
|
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;
|
|
2656
|
+
|
|
2614
2657
|
// ── STATUS command (H-009) ────────────────────────────────────────────────────
|
|
2615
2658
|
// Show pipeline state for a story (or use --all for every in-flight story).
|
|
2616
2659
|
// Reads .github/pipeline/<STORY-ID>/metadata.yml + filesystem; uses
|
|
@@ -5252,4 +5295,9 @@ program
|
|
|
5252
5295
|
.description('Hone AI — Enterprise SDLC Pipeline CLI')
|
|
5253
5296
|
.version(pkg.version);
|
|
5254
5297
|
|
|
5255
|
-
|
|
5298
|
+
// HC-019y-followup-1: gate commander invocation so the file can be
|
|
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
|
+
}
|
package/lib/refresh-knowledge.js
CHANGED
|
@@ -26,9 +26,12 @@ const path = require('node:path');
|
|
|
26
26
|
const crypto = require('node:crypto');
|
|
27
27
|
const { execSync } = require('node:child_process');
|
|
28
28
|
|
|
29
|
+
// HC-019y (2026-05-29): removed `.github/agents` from refresh paths.
|
|
30
|
+
// Agents live exclusively at `.claude/agents/` (Claude Code's read path)
|
|
31
|
+
// after the install-time mirror was eliminated. `.github/agents/` is now
|
|
32
|
+
// a legacy directory adopters are expected to manually delete.
|
|
29
33
|
const KNOWLEDGE_DIRS = [
|
|
30
34
|
'.github/skills',
|
|
31
|
-
'.github/agents',
|
|
32
35
|
'.claude/agents',
|
|
33
36
|
'.github/copilot-instructions.md',
|
|
34
37
|
];
|