forge-workflow 0.1.0-beta.3 → 0.1.0-beta.4
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/AGENTS.md +1 -1
- package/bin/forge-cmd.js +1 -1
- package/bin/forge.js +5 -0
- package/docs/reference/COMMANDS.md +3 -1
- package/docs/reference/shepherd.md +72 -1
- package/lib/adapters/greptile-review-adapter.js +1 -1
- package/lib/adapters/pr-state-adapter.js +103 -8
- package/lib/agents-config.js +5 -0
- package/lib/commands/_issue.js +31 -46
- package/lib/commands/_manifest.js +1 -1
- package/lib/commands/_resolve-command-opts.js +36 -29
- package/lib/commands/claim.js +2 -4
- package/lib/commands/hooks.js +155 -8
- package/lib/commands/plan.js +23 -115
- package/lib/commands/prime.js +8 -1
- package/lib/commands/release.js +1 -2
- package/lib/commands/serve.js +5 -2
- package/lib/commands/setup.js +0 -1
- package/lib/commands/shepherd.js +36 -3
- package/lib/commands/skill.js +275 -0
- package/lib/commands/status.js +37 -32
- package/lib/commands/test.js +32 -0
- package/lib/commands/worktree.js +27 -1
- package/lib/core/runtime-graph.js +88 -14
- package/lib/forge-issues.js +13 -464
- package/lib/harness-capability-matrix.js +2 -2
- package/lib/hook-renderer.js +54 -12
- package/lib/issue-backend.js +42 -3
- package/lib/kernel/broker.js +28 -0
- package/lib/kernel/migrations.js +30 -2
- package/lib/kernel/schema.js +35 -0
- package/lib/kernel/sqlite-driver.js +150 -0
- package/lib/memory-recall.js +151 -0
- package/lib/orientation.js +303 -6
- package/lib/pr-bundle.js +6 -2
- package/lib/pr-monitor/reconcile-executor.js +659 -0
- package/lib/pr-monitor/reconcile-tick.js +138 -0
- package/lib/pr-monitor/reconcile.js +0 -0
- package/lib/pr-monitor/render-sticky.js +14 -0
- package/lib/pr-monitor/shepherd-lease.js +243 -0
- package/lib/pr-monitor/upsert-sticky.js +1 -1
- package/lib/pr-monitor/watch-lifecycle.js +1 -1
- package/lib/pr-pull.js +70 -15
- package/lib/project-memory.js +8 -0
- package/lib/rules-sync.js +4 -0
- package/lib/runtime-health.js +15 -46
- package/lib/skill-eval.js +750 -0
- package/lib/status/identity.js +46 -0
- package/lib/status/presenter.js +0 -35
- package/lib/status/snapshot.js +11 -16
- package/lib/upgrade-safety.js +8 -9
- package/lib/using-forge.js +315 -0
- package/lib/workflow/enforce-stage.js +5 -5
- package/lib/workflow/state-manager.js +23 -23
- package/package.json +1 -1
- package/rules/using-forge.md +24 -0
- package/scripts/forge-team/index.sh +0 -5
- package/scripts/forge-team/tests/dispatcher.test.sh +1 -1
- package/scripts/forge-team/tests/workflow-integration.test.sh +0 -1
- package/scripts/test.js +8 -1
- package/skills/claim-safety/SKILL.md +4 -0
- package/skills/claim-safety/evals/scorecard.json +41 -0
- package/skills/coverage.json +83 -0
- package/skills/dev/SKILL.md +4 -0
- package/skills/dev/evals/scorecard.json +41 -0
- package/skills/gates/SKILL.md +80 -0
- package/skills/gates/evals/evals.json +38 -0
- package/skills/gates/evals/scorecard.json +41 -0
- package/skills/hermes-forge/SKILL.md +1 -0
- package/skills/hermes-forge/evals/scorecard.json +41 -0
- package/skills/issue-basics/SKILL.md +1 -0
- package/skills/issue-basics/evals/scorecard.json +41 -0
- package/skills/kernel/SKILL.md +38 -0
- package/skills/kernel/evals/scorecard.json +41 -0
- package/skills/memory/SKILL.md +16 -1
- package/skills/memory/evals/scorecard.json +41 -0
- package/skills/parallel-deep-research/SKILL.md +1 -0
- package/skills/parallel-deep-research/evals/scorecard.json +41 -0
- package/skills/plan/SKILL.md +6 -0
- package/skills/plan/evals/scorecard.json +41 -0
- package/skills/portability/SKILL.md +47 -0
- package/skills/portability/evals/evals.json +34 -0
- package/skills/portability/evals/scorecard.json +41 -0
- package/skills/research/SKILL.md +1 -0
- package/skills/research/evals/scorecard.json +41 -0
- package/skills/review/SKILL.md +6 -0
- package/skills/review/evals/scorecard.json +41 -0
- package/skills/rollback/SKILL.md +1 -0
- package/skills/rollback/evals/scorecard.json +41 -0
- package/skills/setup/SKILL.md +91 -0
- package/skills/setup/evals/evals.json +42 -0
- package/skills/setup/evals/scorecard.json +41 -0
- package/skills/shepherd/SKILL.md +76 -36
- package/skills/shepherd/evals/evals.json +21 -9
- package/skills/shepherd/evals/scorecard.json +41 -0
- package/skills/ship/SKILL.md +6 -0
- package/skills/ship/evals/scorecard.json +41 -0
- package/skills/smith/SKILL.md +8 -0
- package/skills/smith/evals/scorecard.json +41 -0
- package/skills/sonarcloud/SKILL.md +1 -0
- package/skills/sonarcloud/evals/scorecard.json +41 -0
- package/skills/sonarcloud-analysis/SKILL.md +1 -0
- package/skills/sonarcloud-analysis/evals/scorecard.json +41 -0
- package/skills/status/SKILL.md +3 -0
- package/skills/status/evals/scorecard.json +41 -0
- package/skills/triage-ready/SKILL.md +2 -0
- package/skills/triage-ready/evals/scorecard.json +41 -0
- package/skills/using-forge/SKILL.md +104 -0
- package/skills/using-forge/evals/scorecard.json +41 -0
- package/skills/validate/SKILL.md +4 -0
- package/skills/validate/evals/scorecard.json +41 -0
- package/skills/verify/SKILL.md +4 -0
- package/skills/verify/evals/scorecard.json +41 -0
- package/skills/worktree/SKILL.md +87 -0
- package/skills/worktree/evals/evals.json +38 -0
- package/skills/worktree/evals/scorecard.json +41 -0
- package/lib/adapters/beads-issue-adapter.js +0 -127
- package/lib/beads-nudge.js +0 -91
- package/lib/commands/board.js +0 -64
- package/lib/status/beads-snapshot.js +0 -145
- package/scripts/forge-team/lib/dashboard.sh +0 -316
- package/scripts/forge-team/tests/dashboard.test.sh +0 -155
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: using-forge
|
|
3
|
+
description: >
|
|
4
|
+
Forge dispatch skill: the session bootstrap that makes Forge's skills auto-trigger as a
|
|
5
|
+
reasoning-driven SYSTEM. Reach for this FIRST, before ANY response (including clarifying
|
|
6
|
+
questions, codebase exploration, or file reads), whenever there is even a 1% chance a Forge
|
|
7
|
+
skill applies. Carries the 1%-rule, the announce-before-acting rule, an anti-rationalization
|
|
8
|
+
red-flags table, a subagent escape hatch, and an intent-to-skill routing table: add/build/scope
|
|
9
|
+
a feature to plan; implement an existing task to dev; fix a failing test or bug to dev (debug
|
|
10
|
+
first); type-check/lint/tests to validate; push and open a PR to ship; address PR/review
|
|
11
|
+
feedback to review; post-merge health to verify; what to work on to triage-ready; where am I /
|
|
12
|
+
current stage to status; create/close/search an issue to issue-basics; drive one issue
|
|
13
|
+
end-to-end to smith; monitor a PR to shepherd. Run `forge skill for "<situation>"` for a
|
|
14
|
+
deterministic fallback. NOT itself a stage; the kernel skill is the fuller umbrella index
|
|
15
|
+
once oriented.
|
|
16
|
+
allowed-tools: Read, Bash(forge:*)
|
|
17
|
+
terminal: true
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
<SUBAGENT-STOP>
|
|
21
|
+
If you were dispatched as a subagent to execute one specific task, ignore this skill and do
|
|
22
|
+
the task you were given. This dispatch rule is for the top-level session, not for a scoped
|
|
23
|
+
subagent — otherwise every subagent would re-enter routing instead of working.
|
|
24
|
+
</SUBAGENT-STOP>
|
|
25
|
+
|
|
26
|
+
<EXTREMELY-IMPORTANT>
|
|
27
|
+
If you think there is even a 1% chance a Forge skill applies to what you are doing, you MUST
|
|
28
|
+
invoke that skill BEFORE any response or action.
|
|
29
|
+
|
|
30
|
+
IF A SKILL APPLIES TO YOUR TASK, YOU DO NOT HAVE A CHOICE. YOU MUST USE IT.
|
|
31
|
+
|
|
32
|
+
This is not negotiable. You cannot rationalize your way out of it.
|
|
33
|
+
</EXTREMELY-IMPORTANT>
|
|
34
|
+
|
|
35
|
+
## The rule
|
|
36
|
+
|
|
37
|
+
**Invoke the relevant or requested Forge skill BEFORE any response or action** — including
|
|
38
|
+
clarifying questions, exploring the codebase, or checking files. If it turns out wrong for the
|
|
39
|
+
situation, you don't have to follow it — but you check first.
|
|
40
|
+
|
|
41
|
+
Then **announce** `Using [skill] to [purpose]` and follow the skill exactly. If it has a
|
|
42
|
+
checklist or HARD-GATE, create one todo per item.
|
|
43
|
+
|
|
44
|
+
When unsure WHICH skill fits, either consult the routing table below or run
|
|
45
|
+
`forge skill for "<what you are about to do>"` for a deterministic best-fit answer.
|
|
46
|
+
|
|
47
|
+
## Intent → skill routing table
|
|
48
|
+
|
|
49
|
+
Process skills set the approach first; then the stage/implementation skill carries it out.
|
|
50
|
+
|
|
51
|
+
| When the situation is… | Start with |
|
|
52
|
+
|------------------------|-----------|
|
|
53
|
+
| "Add / build / scope a new feature", "let's build X" | `plan` |
|
|
54
|
+
| Implement a task that a plan already defined | `dev` |
|
|
55
|
+
| "Fix this bug" / a failing test / unexpected behavior | `dev` (if `superpowers:systematic-debugging` is installed, apply its technique first, then implement via `dev`) |
|
|
56
|
+
| Run type-check, lint, security, or tests | `validate` |
|
|
57
|
+
| Push the branch and open a PR | `ship` |
|
|
58
|
+
| Address PR / review-agent feedback (CodeRabbit, Greptile, CI) | `review` |
|
|
59
|
+
| Post-merge health check, close issues | `verify` |
|
|
60
|
+
| "What should I work on?" — rank the ready queue | `triage-ready` |
|
|
61
|
+
| "Where am I?" / current stage / stale or active work | `status` |
|
|
62
|
+
| Create / update / close / search / comment on ONE issue | `issue-basics` |
|
|
63
|
+
| Claim an issue and PROVE the lease before mutating | `claim-safety` |
|
|
64
|
+
| Drive one issue plan→merged-PR under human gates | `smith` |
|
|
65
|
+
| Monitor / shepherd a PR's CI + checks (never merges) | `shepherd` |
|
|
66
|
+
| Deep, multi-source web research | `research` / `parallel-deep-research` |
|
|
67
|
+
| "How does Forge fit together?" / which verb for X | `kernel` |
|
|
68
|
+
| Safe revert / undo a merged change | `rollback` |
|
|
69
|
+
|
|
70
|
+
If two skills seem to apply, run the process skill first (`plan`, `systematic-debugging`), then
|
|
71
|
+
the doing skill. `kernel` is the umbrella index — use it to orient, not to do the work.
|
|
72
|
+
|
|
73
|
+
## Red flags — these thoughts mean STOP, you are rationalizing
|
|
74
|
+
|
|
75
|
+
| Thought | Reality |
|
|
76
|
+
|---------|---------|
|
|
77
|
+
| "This is just a simple question" | Questions are tasks. Check for a skill. |
|
|
78
|
+
| "I need more context first" | The skill check comes BEFORE clarifying questions. |
|
|
79
|
+
| "Let me explore the codebase first" | Skills tell you HOW to explore. Check first. |
|
|
80
|
+
| "I can check git/files quickly" | Files lack conversation context. Check for a skill. |
|
|
81
|
+
| "This doesn't need a formal stage" | If a skill exists, use it. |
|
|
82
|
+
| "I remember what that skill says" | Skills evolve. Read the current version. |
|
|
83
|
+
| "The skill is overkill here" | Simple things become complex. Use it. |
|
|
84
|
+
| "I'll just do this one thing first" | Check BEFORE doing anything. |
|
|
85
|
+
| "It's obviously a plan/dev/ship" | Then invoking it costs nothing. Invoke it. |
|
|
86
|
+
|
|
87
|
+
## Subagent escape hatch
|
|
88
|
+
|
|
89
|
+
You do NOT have to hold every skill in your own context. When a task is independent and
|
|
90
|
+
bounded, dispatch a subagent with the specific skill and a self-contained brief, and keep only
|
|
91
|
+
its conclusion. Parallelize independent tasks (see `superpowers:dispatching-parallel-agents`).
|
|
92
|
+
Reviewers/verifiers that find nothing must say so and name what they inspected.
|
|
93
|
+
|
|
94
|
+
## Agent-agnostic
|
|
95
|
+
|
|
96
|
+
This skill syncs to every harness mirror (`.agents/`, `.claude/`, `.codex/`, `.cursor/`,
|
|
97
|
+
`.hermes/`), and the same routing guidance is reachable via `forge skill for "<situation>"` on
|
|
98
|
+
any harness — never branch on harness identity. On Claude the SessionStart hook injects this
|
|
99
|
+
text automatically; elsewhere, AGENTS.md points here and the CLI router is the fallback.
|
|
100
|
+
|
|
101
|
+
## User instructions win
|
|
102
|
+
|
|
103
|
+
CLAUDE.md / AGENTS.md and direct user requests take precedence over skills, which in turn
|
|
104
|
+
override default behavior. Skip a skill only when your human partner explicitly tells you to.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill": "using-forge",
|
|
3
|
+
"fixtures": "no-fixtures",
|
|
4
|
+
"static": {
|
|
5
|
+
"token_cost": {
|
|
6
|
+
"desc_chars": 1016,
|
|
7
|
+
"body_lines": 87,
|
|
8
|
+
"score": 42
|
|
9
|
+
},
|
|
10
|
+
"caps": {
|
|
11
|
+
"desc_within": true,
|
|
12
|
+
"body_within": true,
|
|
13
|
+
"score": 100
|
|
14
|
+
},
|
|
15
|
+
"description_quality": {
|
|
16
|
+
"has_trigger_cues": false,
|
|
17
|
+
"has_disambiguation_cues": true,
|
|
18
|
+
"adequate_length": true,
|
|
19
|
+
"score": 60
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"router_reachability": {
|
|
23
|
+
"has_curated_rule": false,
|
|
24
|
+
"router_exempt": false,
|
|
25
|
+
"fixtures": "no-fixtures",
|
|
26
|
+
"fixtures_total": 0,
|
|
27
|
+
"fixtures_best_hit": 0,
|
|
28
|
+
"reachable": null,
|
|
29
|
+
"keyword_alignment": null
|
|
30
|
+
},
|
|
31
|
+
"behavioral": {
|
|
32
|
+
"trigger_recall": null,
|
|
33
|
+
"trigger_precision": null,
|
|
34
|
+
"disambiguation": null,
|
|
35
|
+
"chain_correctness": null,
|
|
36
|
+
"outcome_quality": null,
|
|
37
|
+
"variance": null,
|
|
38
|
+
"note": "behavioral — W5 (LLM judge): semantic recall/precision/chain/outcome/variance, not deterministic"
|
|
39
|
+
},
|
|
40
|
+
"composite": 63
|
|
41
|
+
}
|
package/skills/validate/SKILL.md
CHANGED
|
@@ -13,6 +13,8 @@ description: >
|
|
|
13
13
|
is `/ship`), it does not answer PR review feedback from Greptile / SonarCloud / CodeRabbit
|
|
14
14
|
(that is `/review`), and it is not the post-merge CI health check (that is `/verify`).
|
|
15
15
|
allowed-tools: Bash, Read, Grep, Glob
|
|
16
|
+
next: ship
|
|
17
|
+
terminal: false
|
|
16
18
|
---
|
|
17
19
|
|
|
18
20
|
> **Note:** Three things share the "validate" name in Forge:
|
|
@@ -24,6 +26,8 @@ Run comprehensive validation including type checking, linting, code review, secu
|
|
|
24
26
|
|
|
25
27
|
# Validate
|
|
26
28
|
|
|
29
|
+
> **Chain (HARD-GATE):** the ONLY skill you invoke after `validate` is `ship`. Do not open the PR until validation shows fresh passing output in THIS session.
|
|
30
|
+
|
|
27
31
|
This skill validates all code before creating a pull request.
|
|
28
32
|
|
|
29
33
|
## Usage
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill": "validate",
|
|
3
|
+
"fixtures": "present",
|
|
4
|
+
"static": {
|
|
5
|
+
"token_cost": {
|
|
6
|
+
"desc_chars": 995,
|
|
7
|
+
"body_lines": 300,
|
|
8
|
+
"score": 21
|
|
9
|
+
},
|
|
10
|
+
"caps": {
|
|
11
|
+
"desc_within": true,
|
|
12
|
+
"body_within": true,
|
|
13
|
+
"score": 100
|
|
14
|
+
},
|
|
15
|
+
"description_quality": {
|
|
16
|
+
"has_trigger_cues": false,
|
|
17
|
+
"has_disambiguation_cues": true,
|
|
18
|
+
"adequate_length": true,
|
|
19
|
+
"score": 60
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"router_reachability": {
|
|
23
|
+
"has_curated_rule": true,
|
|
24
|
+
"router_exempt": false,
|
|
25
|
+
"fixtures": "present",
|
|
26
|
+
"fixtures_total": 5,
|
|
27
|
+
"fixtures_best_hit": 3,
|
|
28
|
+
"reachable": true,
|
|
29
|
+
"keyword_alignment": 0.6
|
|
30
|
+
},
|
|
31
|
+
"behavioral": {
|
|
32
|
+
"trigger_recall": null,
|
|
33
|
+
"trigger_precision": null,
|
|
34
|
+
"disambiguation": null,
|
|
35
|
+
"chain_correctness": null,
|
|
36
|
+
"outcome_quality": null,
|
|
37
|
+
"variance": null,
|
|
38
|
+
"note": "behavioral — W5 (LLM judge): semantic recall/precision/chain/outcome/variance, not deterministic"
|
|
39
|
+
},
|
|
40
|
+
"composite": 56
|
|
41
|
+
}
|
package/skills/verify/SKILL.md
CHANGED
|
@@ -12,12 +12,16 @@ description: >
|
|
|
12
12
|
rollback to undo an already-shipped change, status to merely report the current stage
|
|
13
13
|
without acting, and issue-basics to close an issue unrelated to a just-merged PR.
|
|
14
14
|
allowed-tools: Bash, Read, Grep, Glob
|
|
15
|
+
next: ship
|
|
16
|
+
terminal: false
|
|
15
17
|
---
|
|
16
18
|
|
|
17
19
|
Verify that the merge landed correctly and everything is running properly after merge.
|
|
18
20
|
|
|
19
21
|
# Verify
|
|
20
22
|
|
|
23
|
+
> **Chain (HARD-GATE):** the successor depends on the change classification (source of truth: lib/workflow/stages.js) — in the post-merge flow (Critical/Standard/Refactor/Simple/Hotfix) NOTHING follows `verify`; the `docs` flow alone reuses `verify` → `ship` as a pre-ship content check. `verify` is therefore not universally terminal — a chain-aware harness MUST gate on classification, not blindly follow `next`.
|
|
24
|
+
|
|
21
25
|
This skill runs AFTER the user has merged the PR. It checks system health — not documentation (that was handled by the pre-merge gate embedded in `/ship` and `/review`).
|
|
22
26
|
|
|
23
27
|
## Usage
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill": "verify",
|
|
3
|
+
"fixtures": "present",
|
|
4
|
+
"static": {
|
|
5
|
+
"token_cost": {
|
|
6
|
+
"desc_chars": 890,
|
|
7
|
+
"body_lines": 287,
|
|
8
|
+
"score": 28
|
|
9
|
+
},
|
|
10
|
+
"caps": {
|
|
11
|
+
"desc_within": true,
|
|
12
|
+
"body_within": true,
|
|
13
|
+
"score": 100
|
|
14
|
+
},
|
|
15
|
+
"description_quality": {
|
|
16
|
+
"has_trigger_cues": true,
|
|
17
|
+
"has_disambiguation_cues": true,
|
|
18
|
+
"adequate_length": true,
|
|
19
|
+
"score": 100
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"router_reachability": {
|
|
23
|
+
"has_curated_rule": true,
|
|
24
|
+
"router_exempt": false,
|
|
25
|
+
"fixtures": "present",
|
|
26
|
+
"fixtures_total": 6,
|
|
27
|
+
"fixtures_best_hit": 2,
|
|
28
|
+
"reachable": true,
|
|
29
|
+
"keyword_alignment": 0.33
|
|
30
|
+
},
|
|
31
|
+
"behavioral": {
|
|
32
|
+
"trigger_recall": null,
|
|
33
|
+
"trigger_precision": null,
|
|
34
|
+
"disambiguation": null,
|
|
35
|
+
"chain_correctness": null,
|
|
36
|
+
"outcome_quality": null,
|
|
37
|
+
"variance": null,
|
|
38
|
+
"note": "behavioral — W5 (LLM judge): semantic recall/precision/chain/outcome/variance, not deterministic"
|
|
39
|
+
},
|
|
40
|
+
"composite": 78
|
|
41
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: worktree
|
|
3
|
+
description: >
|
|
4
|
+
Forge's worktree lifecycle. `forge worktree create <slug>` makes an isolated checkout
|
|
5
|
+
under `.worktrees/`, creates its branch (feat/<slug>), links a kernel issue (`--issue`),
|
|
6
|
+
and installs dependencies — all in one step; `forge worktree list`/`remove <slug>` manage
|
|
7
|
+
them; `forge clean` removes worktrees whose branches already merged (squash-aware) and
|
|
8
|
+
fast-forwards the default branch. Use when the user says "work on this in an isolated
|
|
9
|
+
branch/worktree", "spin up a worktree", "parallel work on another PR or issue", "clean up
|
|
10
|
+
merged worktrees", or "why did my worktree miss its dependencies". Footgun it prevents:
|
|
11
|
+
raw `git worktree add` only creates a branch — it skips the issue link and dep install —
|
|
12
|
+
so always use `forge worktree create`; and `forge clean` is cwd-scoped, run it from the
|
|
13
|
+
primary repo root, never inside a worktree. NOT for opening/pushing a PR or the branch
|
|
14
|
+
(ship), NOT for planning a feature (plan), NOT the stages that create a worktree as one
|
|
15
|
+
step — this is the worktree lifecycle itself.
|
|
16
|
+
allowed-tools: Bash, Read, Grep, Glob
|
|
17
|
+
terminal: true
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
Forge worktrees give a task its own isolated checkout so concurrent work never collides on `HEAD` or a shared branch. Use `forge worktree`, not raw `git worktree add` — the Forge command does the extra setup that a bare branch add skips.
|
|
21
|
+
|
|
22
|
+
# Worktree lifecycle
|
|
23
|
+
|
|
24
|
+
## When to use
|
|
25
|
+
|
|
26
|
+
- "Work on this in an isolated branch / worktree", "spin up a worktree for this".
|
|
27
|
+
- Parallel work: a second PR or issue you want to develop without disturbing the current checkout.
|
|
28
|
+
- "Clean up the merged worktrees" — reclaim worktrees whose branches already landed.
|
|
29
|
+
- Diagnosing "why did my new worktree miss its dependencies / node_modules".
|
|
30
|
+
|
|
31
|
+
This skill is the **worktree lifecycle itself**. The `plan`/`ship` stages may create a worktree as one internal step, but managing worktrees directly — create, list, remove, clean — is this skill.
|
|
32
|
+
|
|
33
|
+
## Create
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
forge worktree create <slug>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Creates the worktree at `.worktrees/<slug>` and, in one step:
|
|
40
|
+
|
|
41
|
+
- creates its branch (default `feat/<slug>`),
|
|
42
|
+
- links a kernel issue to the worktree when `--issue <id>` is given (records issue → worktree),
|
|
43
|
+
- installs dependencies with the repo's detected package manager (bun/pnpm/yarn/npm).
|
|
44
|
+
|
|
45
|
+
Flags:
|
|
46
|
+
|
|
47
|
+
| Flag | Purpose |
|
|
48
|
+
| --- | --- |
|
|
49
|
+
| `--branch <name>` | Custom branch name (default `feat/<slug>`). |
|
|
50
|
+
| `--base <ref>` | Base ref the new branch forks from (default the repo default branch, e.g. `origin/main`). |
|
|
51
|
+
| `--issue <id>` | Kernel issue id to link this worktree to. |
|
|
52
|
+
| `--work-folder <path>` | Repo-relative work-folder this issue owns (drops a `.forge-issue` marker). |
|
|
53
|
+
|
|
54
|
+
The kernel issue store lives in the git common dir that every worktree shares, so a new worktree already sees the same kernel — no per-worktree issue-store bootstrap is needed.
|
|
55
|
+
|
|
56
|
+
## The footgun this skill prevents
|
|
57
|
+
|
|
58
|
+
**Never use raw `git worktree add`** for Forge work. It only creates a branch + checkout — it does **not** link the kernel issue and does **not** install dependencies. A worktree made that way is why deps end up missing. Always use `forge worktree create`.
|
|
59
|
+
|
|
60
|
+
## List / remove
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
forge worktree list # read the worktree registry from the kernel
|
|
64
|
+
forge worktree remove <slug> # git worktree remove for that slug
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Clean merged worktrees
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
forge clean # remove worktrees whose branches merged; FF the default branch
|
|
71
|
+
forge clean --dry-run # show what would be removed, change nothing
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
`forge clean` is **squash-aware** — it detects branches that merged via squash, not just fast-forward merges.
|
|
75
|
+
|
|
76
|
+
### `forge clean` footguns
|
|
77
|
+
|
|
78
|
+
- **cwd-scoped.** Run it from the **primary repo root**, never from inside a `.worktrees/<slug>` checkout. There is no default-branch guard, so a bare run from the wrong place can remove a clean checkout.
|
|
79
|
+
- **Fast-forward the local default branch first** so squash-merge detection sees the merged state.
|
|
80
|
+
- **Windows long paths.** `node_modules` inside a worktree can exceed the Windows path limit; if `git worktree remove --force` de-registers the worktree but the directory lingers, delete it with a PowerShell extended-path (`\\?\`) remove.
|
|
81
|
+
|
|
82
|
+
## Adjacent skills
|
|
83
|
+
|
|
84
|
+
- Opening or pushing a PR / the branch → `ship`.
|
|
85
|
+
- Planning a feature (which may create a worktree as a step) → `plan`.
|
|
86
|
+
- Watching an open PR toward merge → `shepherd`.
|
|
87
|
+
- Everyday issue create/update/close → `issue-basics`.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"query": "Spin up an isolated worktree so I can work on this branch separately.",
|
|
4
|
+
"should_trigger": true
|
|
5
|
+
},
|
|
6
|
+
{
|
|
7
|
+
"query": "Create a worktree for this issue with its branch and dependencies installed.",
|
|
8
|
+
"should_trigger": true
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"query": "I want to work on another PR in parallel without disturbing my current checkout.",
|
|
12
|
+
"should_trigger": true
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"query": "Clean up all the merged worktrees and fast-forward main.",
|
|
16
|
+
"should_trigger": true
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"query": "Why did my new worktree miss its node_modules dependencies?",
|
|
20
|
+
"should_trigger": true
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"query": "Set up an isolated branch checkout under .worktrees for this task.",
|
|
24
|
+
"should_trigger": true
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"query": "Open a pull request and push my feature branch.",
|
|
28
|
+
"should_trigger": false
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"query": "Plan this new feature and break it into a task list.",
|
|
32
|
+
"should_trigger": false
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"query": "Watch my PR and tell me why it isn't merging.",
|
|
36
|
+
"should_trigger": false
|
|
37
|
+
}
|
|
38
|
+
]
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill": "worktree",
|
|
3
|
+
"fixtures": "present",
|
|
4
|
+
"static": {
|
|
5
|
+
"token_cost": {
|
|
6
|
+
"desc_chars": 1013,
|
|
7
|
+
"body_lines": 70,
|
|
8
|
+
"score": 44
|
|
9
|
+
},
|
|
10
|
+
"caps": {
|
|
11
|
+
"desc_within": true,
|
|
12
|
+
"body_within": true,
|
|
13
|
+
"score": 100
|
|
14
|
+
},
|
|
15
|
+
"description_quality": {
|
|
16
|
+
"has_trigger_cues": true,
|
|
17
|
+
"has_disambiguation_cues": true,
|
|
18
|
+
"adequate_length": true,
|
|
19
|
+
"score": 100
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"router_reachability": {
|
|
23
|
+
"has_curated_rule": true,
|
|
24
|
+
"router_exempt": false,
|
|
25
|
+
"fixtures": "present",
|
|
26
|
+
"fixtures_total": 6,
|
|
27
|
+
"fixtures_best_hit": 6,
|
|
28
|
+
"reachable": true,
|
|
29
|
+
"keyword_alignment": 1
|
|
30
|
+
},
|
|
31
|
+
"behavioral": {
|
|
32
|
+
"trigger_recall": null,
|
|
33
|
+
"trigger_precision": null,
|
|
34
|
+
"disambiguation": null,
|
|
35
|
+
"chain_correctness": null,
|
|
36
|
+
"outcome_quality": null,
|
|
37
|
+
"variance": null,
|
|
38
|
+
"note": "behavioral — W5 (LLM judge): semantic recall/precision/chain/outcome/variance, not deterministic"
|
|
39
|
+
},
|
|
40
|
+
"composite": 83
|
|
41
|
+
}
|
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const {
|
|
4
|
-
IssueAdapter,
|
|
5
|
-
decideIssueAuthority,
|
|
6
|
-
normalizeIssueStatus,
|
|
7
|
-
} = require('../issue-adapter.js');
|
|
8
|
-
|
|
9
|
-
class BeadsIssueAdapter extends IssueAdapter {
|
|
10
|
-
constructor(options = {}) {
|
|
11
|
-
super({
|
|
12
|
-
id: options.id || 'beads',
|
|
13
|
-
kind: 'issue',
|
|
14
|
-
name: options.name || 'Beads Issue Adapter',
|
|
15
|
-
version: options.version || '0.1.0',
|
|
16
|
-
});
|
|
17
|
-
this.runBeadsOperation = options.runBeadsOperation;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
run(operation, args = [], context = {}) {
|
|
21
|
-
if (typeof this.runBeadsOperation !== 'function') {
|
|
22
|
-
throw new TypeError('runBeadsOperation is not configured on this adapter');
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return this.runBeadsOperation(operation, args, context, context.deps || {});
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
list(args = [], context = {}) {
|
|
29
|
-
return this.run('list', args, context);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
ready(args = [], context = {}) {
|
|
33
|
-
return this.run('ready', args, context);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
read(args = [], context = {}) {
|
|
37
|
-
return this.run('show', args, context);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
show(args = [], context = {}) {
|
|
41
|
-
return this.read(args, context);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
create(args = [], context = {}) {
|
|
45
|
-
return this.run('create', args, context);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
search(args = [], context = {}) {
|
|
49
|
-
return this.run('search', args, context);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
stats(args = [], context = {}) {
|
|
53
|
-
return this.run('stats', args, context);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
update(args = [], context = {}) {
|
|
57
|
-
return this.run('update', args, context);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
close(args = [], context = {}) {
|
|
61
|
-
return this.run('close', args, context);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
comment(args = [], context = {}) {
|
|
65
|
-
return this.run('comment', args, context);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
depAdd(args = [], context = {}) {
|
|
69
|
-
return this.run('dep.add', args, context);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
depRemove(args = [], context = {}) {
|
|
73
|
-
return this.run('dep.remove', args, context);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// De-bead parity: claim/release and the KAP-7/KAP-12 derived reads now route
|
|
77
|
-
// through the backend abstraction (the translations moved out of _issue.js). The
|
|
78
|
-
// beads layer maps claim -> `update <id> --claim`; release has no bd equivalent
|
|
79
|
-
// and returns the Kernel-only contract error (handled in runBeadsOperation).
|
|
80
|
-
claim(args = [], context = {}) {
|
|
81
|
-
return this.run('claim', args, context);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
release(args = [], context = {}) {
|
|
85
|
-
return this.run('release', args, context);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
blocked(args = [], context = {}) {
|
|
89
|
-
return this.run('blocked', args, context);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
stale(args = [], context = {}) {
|
|
93
|
-
return this.run('stale', args, context);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
orphans(args = [], context = {}) {
|
|
97
|
-
return this.run('orphans', args, context);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
lint(args = [], context = {}) {
|
|
101
|
-
return this.run('lint', args, context);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Lease ownership is Kernel-only; the beads layer returns the Kernel-only contract
|
|
105
|
-
// error (handled in runBeadsOperation), matching the release passthrough.
|
|
106
|
-
owns(args = [], context = {}) {
|
|
107
|
-
return this.run('owns', args, context);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// Active-lease listing is Kernel-only (kernel_claims); the beads layer returns the
|
|
111
|
-
// Kernel-only contract error (handled in runBeadsOperation), matching owns/release.
|
|
112
|
-
claims(args = [], context = {}) {
|
|
113
|
-
return this.run('claims', args, context);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
mapStatus(status, context = {}) {
|
|
117
|
-
return normalizeIssueStatus(status, context);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
decideAuthority(change = {}, context = {}) {
|
|
121
|
-
return decideIssueAuthority(change, context);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
module.exports = {
|
|
126
|
-
BeadsIssueAdapter,
|
|
127
|
-
};
|
package/lib/beads-nudge.js
DELETED
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const { resolveIssueBackend } = require('./issue-backend');
|
|
4
|
-
const { detectBeadsJsonlSource } = require('./beads-detect');
|
|
5
|
-
|
|
6
|
-
// Unmigrated legacy-store nudge (kernel issue a5399f3d — upgrade-safety
|
|
7
|
-
// 0.0.10 -> current).
|
|
8
|
-
//
|
|
9
|
-
// The 0.0.10 -> current upgrade flipped the DEFAULT issue backend to the Kernel,
|
|
10
|
-
// but the legacy -> kernel migration fires ONLY from `forge setup`/`init`, never
|
|
11
|
-
// lazily on the issue path. So a user who runs `bun update` then `forge list` /
|
|
12
|
-
// `forge ready` reads an EMPTY kernel and their 0.0.10 issues APPEAR GONE — the
|
|
13
|
-
// data is safe on disk, just invisible, with no hint. This helper closes that
|
|
14
|
-
// footgun: when the resolved backend is the (default) Kernel, a read comes back
|
|
15
|
-
// empty, AND a legacy jsonl store still exists, it prints a one-time guided hint.
|
|
16
|
-
//
|
|
17
|
-
// It lives in this NEUTRAL module (not lib/commands/_issue.js) on purpose: the
|
|
18
|
-
// message text names the retired backend + its `.beads` store, and _issue.js is a
|
|
19
|
-
// D20 release-readiness hot-path surface that must stay free of those tokens (the
|
|
20
|
-
// bd-call-site audit + kernel-backed checks scan it). Keeping the strings here
|
|
21
|
-
// lets the hot path call a token-free helper.
|
|
22
|
-
//
|
|
23
|
-
// Best-effort and non-blocking: it NEVER throws and never alters the read result.
|
|
24
|
-
// Fires at most once per project root per process to avoid spam.
|
|
25
|
-
const NUDGE_READS = new Set(['list', 'ready']);
|
|
26
|
-
const warnedRoots = new Set();
|
|
27
|
-
|
|
28
|
-
// A kernel read looks empty when its contract data carries no issues. Handles the
|
|
29
|
-
// issue.list/issue.ready shape ({ issues, count }) plus a bare array / null, and
|
|
30
|
-
// is deliberately conservative: anything it cannot confirm as empty is treated as
|
|
31
|
-
// NON-empty so a user with real kernel issues is never nagged.
|
|
32
|
-
function kernelReadLooksEmpty(result) {
|
|
33
|
-
if (!result || typeof result !== 'object' || result.ok !== true) {
|
|
34
|
-
return false;
|
|
35
|
-
}
|
|
36
|
-
const data = result.data;
|
|
37
|
-
if (data === null || data === undefined) {
|
|
38
|
-
return true;
|
|
39
|
-
}
|
|
40
|
-
if (Array.isArray(data)) {
|
|
41
|
-
return data.length === 0;
|
|
42
|
-
}
|
|
43
|
-
if (Array.isArray(data.issues)) {
|
|
44
|
-
return data.issues.length === 0;
|
|
45
|
-
}
|
|
46
|
-
if (Array.isArray(data.items)) {
|
|
47
|
-
return data.items.length === 0;
|
|
48
|
-
}
|
|
49
|
-
if (typeof data.count === 'number') {
|
|
50
|
-
return data.count === 0;
|
|
51
|
-
}
|
|
52
|
-
return false;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function maybeWarnUnmigratedBeads(subcommand, result, projectRoot, rawOpts = {}) {
|
|
56
|
-
try {
|
|
57
|
-
if (!NUDGE_READS.has(subcommand) || !projectRoot) {
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
if (warnedRoots.has(projectRoot)) {
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
const env = rawOpts.env || process.env;
|
|
64
|
-
// Resolve the EFFECTIVE backend (default kernel). An explicit opt-in to the
|
|
65
|
-
// retired backend means the user chose it deliberately — nothing to nudge.
|
|
66
|
-
const backend = resolveIssueBackend({ deps: rawOpts, env, projectRoot, warn: () => {} });
|
|
67
|
-
if (backend !== 'kernel' || !kernelReadLooksEmpty(result)) {
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
if (!detectBeadsJsonlSource(projectRoot)) {
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
warnedRoots.add(projectRoot);
|
|
74
|
-
console.error(
|
|
75
|
-
'\n[forge] Legacy issue data detected (.beads/*.jsonl) but the Kernel issue store is empty.\n'
|
|
76
|
-
+ 'Forge now defaults to the Kernel backend (breaking change since 0.0.10). Your legacy\n'
|
|
77
|
-
+ 'issues are safe on disk but will not appear until migrated. To migrate:\n'
|
|
78
|
-
+ ' forge migrate --from beads # import your legacy issues into the Kernel\n'
|
|
79
|
-
+ ' forge setup # (re)wire hooks + provision the Kernel store\n'
|
|
80
|
-
+ 'Prefer to stay on the legacy backend? Set `issueBackend: beads` in .forge/config.yaml '
|
|
81
|
-
+ '(or FORGE_ISSUE_BACKEND=beads).\n',
|
|
82
|
-
);
|
|
83
|
-
} catch (_err) { // NOSONAR S2486 — line-scoped; must sit on the catch line
|
|
84
|
-
/* the nudge is a best-effort hint; it must never break a read */
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
module.exports = {
|
|
89
|
-
maybeWarnUnmigratedBeads,
|
|
90
|
-
kernelReadLooksEmpty,
|
|
91
|
-
};
|