@skyf0xx/hedgehog 6.2.4 → 6.2.5
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/bin/cli.mjs +24 -6
- package/package.json +1 -1
- package/src/db/worktree.mjs +27 -0
- package/src/hosts/gemini/gemini-extension.json +1 -1
package/bin/cli.mjs
CHANGED
|
@@ -21,7 +21,7 @@ import { spawn, execFileSync } from 'node:child_process';
|
|
|
21
21
|
import { dbInit, DB_PATH, dbAbsPath, openDb, openDbAt } from '../src/db/init.mjs';
|
|
22
22
|
import { loadCore, lintCore, isModuleAxis } from '../src/db/core.mjs';
|
|
23
23
|
import { planTasks, CORE_INTENT_ID } from '../src/db/plan.mjs';
|
|
24
|
-
import { addIntent, INTENTS_DIR } from '../src/db/intent.mjs';
|
|
24
|
+
import { addIntent, INTENTS_DIR, intentFilePath } from '../src/db/intent.mjs';
|
|
25
25
|
import {
|
|
26
26
|
nextTask,
|
|
27
27
|
formatNext,
|
|
@@ -595,9 +595,13 @@ ${bold('Usage')}
|
|
|
595
595
|
npx @skyf0xx/hedgehog abandon <intent-id> --reason "<why>"
|
|
596
596
|
drop an intent that will never finish: records why,
|
|
597
597
|
resets its tasks to planned on trunk, removes its
|
|
598
|
-
worktree and branch
|
|
598
|
+
worktree and branch — the id stays taken; to change
|
|
599
|
+
an abandoned intent, edit its .hedgehog/intents/<id>.json
|
|
600
|
+
by hand and run 'hedgehog plan', not 'intent add' again
|
|
599
601
|
npx @skyf0xx/hedgehog intent add [flags] add an intent (rules/requirements/dependencies)
|
|
600
|
-
npx @skyf0xx/hedgehog intent add --file <path> add an intent from a JSON file
|
|
602
|
+
npx @skyf0xx/hedgehog intent add --file <path> add an intent from a JSON file — fails on an id already
|
|
603
|
+
in the build graph, including one reset by 'hedgehog
|
|
604
|
+
abandon' (still 'planned', not removed); see 'abandon' above
|
|
601
605
|
npx @skyf0xx/hedgehog next print the task packet for one ready task
|
|
602
606
|
npx @skyf0xx/hedgehog show <task-id> print the task packet for any task, at any status
|
|
603
607
|
npx @skyf0xx/hedgehog claim --owner <owner> [--count <n>] atomically claim up to n ready tasks
|
|
@@ -1555,7 +1559,11 @@ async function planCommand(args = []) {
|
|
|
1555
1559
|
}
|
|
1556
1560
|
|
|
1557
1561
|
for (const { intentId, branch, path } of worktreesCreated) {
|
|
1558
|
-
console.log(
|
|
1562
|
+
console.log(
|
|
1563
|
+
` ${green('worktree')} ${bold(intentId)} ${dim(`${branch} → ${path}`)}\n` +
|
|
1564
|
+
` ${dim(`compiling here, not onto trunk, because every intent ${intentId} depends_on is already complete —`)}\n` +
|
|
1565
|
+
` ${dim(`see \`hedgehog merge ${intentId}\`/\`hedgehog abandon ${intentId}\` to close it out later`)}`,
|
|
1566
|
+
);
|
|
1559
1567
|
}
|
|
1560
1568
|
|
|
1561
1569
|
// Compiles the new worktree's own graph from inside it — a plain
|
|
@@ -1728,8 +1736,15 @@ async function intentCommand(args) {
|
|
|
1728
1736
|
try {
|
|
1729
1737
|
intent = await addIntent(db, record);
|
|
1730
1738
|
} catch (err) {
|
|
1739
|
+
const idTaken = /UNIQUE constraint failed: intents\.id/.test(err.message);
|
|
1731
1740
|
console.error(
|
|
1732
|
-
`${red('Failed to add intent:')} ${err.message}\n\
|
|
1741
|
+
`${red('Failed to add intent:')} ${err.message}\n\n` +
|
|
1742
|
+
(idTaken
|
|
1743
|
+
? `${bold(record.id)} already exists in the build graph — this includes an intent \`hedgehog abandon\`\n` +
|
|
1744
|
+
`reset to 'planned' rather than removed. To change it, edit ${intentFilePath(record.id)}\n` +
|
|
1745
|
+
`by hand and run \`hedgehog plan\` to recompile; don't run \`intent add\` again for this id.\n\n`
|
|
1746
|
+
: '') +
|
|
1747
|
+
`Usage: hedgehog intent add --id <id> --goal <goal> --outcome <outcome> [--rule <r>]... [--depends-on <id>]...\n or: hedgehog intent add --file <path.json>\n`,
|
|
1733
1748
|
);
|
|
1734
1749
|
process.exitCode = 1;
|
|
1735
1750
|
return;
|
|
@@ -3705,7 +3720,10 @@ async function abandonCommand(args) {
|
|
|
3705
3720
|
console.log(
|
|
3706
3721
|
`\n${bold('Abandoned.')} ${dim(`${id} is reset to planned on trunk. Commit ${ABANDONED_DIR}/${id.toLowerCase()}.json —`)}\n` +
|
|
3707
3722
|
`${dim('the build graph is derived and gitignored, and an uncommitted abandonment')}\n` +
|
|
3708
|
-
`${dim('is reverted by the next `hedgehog db rebuild`.')}\n
|
|
3723
|
+
`${dim('is reverted by the next `hedgehog db rebuild`.')}\n\n` +
|
|
3724
|
+
`${dim(`${bold(id)} still exists in the build graph at 'planned' — do not run \`hedgehog intent add\` for`)}\n` +
|
|
3725
|
+
`${dim(`this id again (it will fail: the id is already taken). To change the intent, edit`)}\n` +
|
|
3726
|
+
`${dim(`${intentFilePath(id)} by hand and run \`hedgehog plan\` to recompile it.`)}\n`,
|
|
3709
3727
|
);
|
|
3710
3728
|
}
|
|
3711
3729
|
|
package/package.json
CHANGED
package/src/db/worktree.mjs
CHANGED
|
@@ -431,6 +431,26 @@ function resetIntentTasksToPlanned(db, intentId) {
|
|
|
431
431
|
.all(intentId);
|
|
432
432
|
}
|
|
433
433
|
|
|
434
|
+
// Deletes every `intent_dependencies` row naming `intentId` on either
|
|
435
|
+
// side. An abandoned intent is reset to `planned` with no compiled tasks
|
|
436
|
+
// on trunk, so a `depends_on` edge pointing at it (something else declared
|
|
437
|
+
// as depending on it) or away from it (its own declared dependency) is
|
|
438
|
+
// both stale the moment abandonment lands: `eligibleIntents` (this file)
|
|
439
|
+
// reads that table directly to decide worktree eligibility, and a row
|
|
440
|
+
// surviving abandonment lets an unrelated intent read as "its dependency
|
|
441
|
+
// is satisfied" against an intent that no longer has any real status to
|
|
442
|
+
// satisfy anything with, or lets this intent re-read as eligible against a
|
|
443
|
+
// dependency it no longer declares. Re-adding the same id later
|
|
444
|
+
// (`hedgehog intent add --file` with a JSON that still declares the
|
|
445
|
+
// dependency) recreates the row from that file, same as any first-time
|
|
446
|
+
// add — this only clears what abandonment made stale, not what a future
|
|
447
|
+
// add might legitimately restate.
|
|
448
|
+
function clearIntentDependencies(db, intentId) {
|
|
449
|
+
db.prepare(
|
|
450
|
+
'DELETE FROM intent_dependencies WHERE intent_id = ? OR depends_on_intent_id = ?',
|
|
451
|
+
).run(intentId, intentId);
|
|
452
|
+
}
|
|
453
|
+
|
|
434
454
|
// Resets every task of `intentId` to `planned` on whichever DB `db` is
|
|
435
455
|
// open against (trunk, by the CLI's own contract — see abandonCommand),
|
|
436
456
|
// clears any lease, and reopens the intent itself to `planned` so a later
|
|
@@ -440,6 +460,11 @@ function resetIntentTasksToPlanned(db, intentId) {
|
|
|
440
460
|
// tasks that don't exist yet). Mirrors reconcile.mjs#applyReconciliation's
|
|
441
461
|
// shape: the committed file is written first, this is applied second.
|
|
442
462
|
//
|
|
463
|
+
// Also clears every `intent_dependencies` row naming this intent, on
|
|
464
|
+
// either side (clearIntentDependencies, above) — an abandoned intent's own
|
|
465
|
+
// declared dependency and any other intent's dependency on it are both
|
|
466
|
+
// stale the instant it resets to `planned` with no shipped work.
|
|
467
|
+
//
|
|
443
468
|
// Refuses an intent already `complete` (merged, real shipped work) —
|
|
444
469
|
// same reasoning as reconcile.mjs#confirmReconciliation refusing an
|
|
445
470
|
// already-complete task: applyAbandonment resets tasks to 'planned'
|
|
@@ -458,6 +483,7 @@ export function applyAbandonment(db, intentId) {
|
|
|
458
483
|
);
|
|
459
484
|
}
|
|
460
485
|
const resetTasks = resetIntentTasksToPlanned(db, intentId);
|
|
486
|
+
clearIntentDependencies(db, intentId);
|
|
461
487
|
|
|
462
488
|
if (intent) {
|
|
463
489
|
db.prepare("UPDATE intents SET status = 'planned' WHERE id = ?").run(intentId);
|
|
@@ -501,6 +527,7 @@ export function replayAbandonments(db, abandonments) {
|
|
|
501
527
|
}
|
|
502
528
|
setPlanned.run(intentId);
|
|
503
529
|
resetIntentTasksToPlanned(db, intentId);
|
|
530
|
+
clearIntentDependencies(db, intentId);
|
|
504
531
|
replayed.push({ intentId, reason: record.reason });
|
|
505
532
|
}
|
|
506
533
|
return { replayed, orphaned };
|