@jaybeeuu/agent-cortex 2.7.0 → 2.9.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/CHANGELOG.md +412 -0
- package/README.md +87 -8
- package/agents/README.md +4 -4
- package/agents/guppi/agent.md +162 -0
- package/agents/guppi/claude/frontmatter.json +6 -0
- package/agents/guppi/pi/frontmatter.json +6 -0
- package/agents/plan/agent.md +6 -0
- package/agents/ralph/agent.md +17 -16
- package/agents/ralph/claude/wake.md +4 -4
- package/agents/ralph/pi/wake.md +15 -20
- package/agents/ralph-plan/agent.md +6 -0
- package/bin/agent-cortex.mjs +20 -1
- package/bin/installers/claude.mjs +1 -20
- package/bin/installers/pi.mjs +8 -2
- package/extensions/interrupt-bookmark/README.md +129 -0
- package/extensions/interrupt-bookmark/index.test.ts +717 -0
- package/extensions/interrupt-bookmark/index.ts +354 -0
- package/extensions/interrupt-bookmark/package.json +20 -0
- package/extensions/post-merge-watch/README.md +133 -0
- package/extensions/post-merge-watch/index.test.ts +247 -0
- package/extensions/post-merge-watch/index.ts +123 -0
- package/extensions/post-merge-watch/package.json +16 -0
- package/extensions/post-merge-watch/watch.test.ts +469 -0
- package/extensions/post-merge-watch/watch.ts +431 -0
- package/extensions/session-clear/README.md +74 -0
- package/extensions/session-clear/index.test.ts +524 -0
- package/extensions/session-clear/index.ts +159 -0
- package/extensions/session-clear/package.json +16 -0
- package/extensions/skill-stats/README.md +84 -3
- package/extensions/skill-stats/index.ts +62 -3
- package/extensions/skill-stats/package.json +1 -1
- package/extensions/skill-stats/rollup.test.ts +643 -0
- package/extensions/skill-stats/rollup.ts +415 -0
- package/extensions/skill-stats/timeline.ts +101 -0
- package/lib/cli.mjs +42 -1
- package/lib/self-update.mjs +553 -0
- package/package.json +1 -1
- package/scripts/lib/manifest.mjs +29 -0
- package/skills/engineering/implement-task/SKILL.md +124 -0
- package/skills/engineering/tdd/SKILL.md +2 -0
- package/skills/planning/create-task/REFERENCE.md +15 -6
- package/skills/planning/create-task/SKILL.md +1 -1
- package/skills/planning/create-task/pipeline.json +8 -0
- package/skills/planning/create-task/scripts/create-chores.mjs +27 -18
- package/skills/planning/create-task/templates/code.md +8 -3
- package/skills/planning/create-task/templates/document.md +8 -3
- package/skills/planning/create-task/templates/fix.md +8 -3
- package/skills/planning/create-task/templates/housekeeping.md +8 -4
- package/skills/planning/create-task/templates/post-merge.md +67 -0
- package/skills/planning/create-task/templates/review.md +8 -3
- package/skills/planning/create-task/templates/verify.md +8 -3
- package/skills/productivity/bd-tool/SKILL.md +55 -0
- package/skills/review/review-defects/EXAMPLES.md +198 -0
- package/skills/review/review-defects/SKILL.md +121 -0
- package/skills/review/review-report/EXAMPLES.md +114 -0
- package/skills/review/review-report/SKILL.md +138 -0
- package/skills/review/review-report/scripts/report.mjs +1032 -0
- package/skills/style/style-tests/EXAMPLES.md +30 -0
- package/skills/style/style-tests/SKILL.md +10 -6
- package/skills/workflow/hitl-queue/SKILL.md +134 -0
- package/skills/workflow/hitl-queue/scripts/queue.mjs +626 -0
- package/skills/workflow/plan/SKILL.md +27 -3
- package/skills/workflow/prioritise-work/CHALLENGE.md +139 -0
- package/skills/workflow/prioritise-work/SKILL.md +150 -0
- package/skills/workflow/prioritise-work/scripts/challenge.mjs +505 -0
- package/skills/workflow/prioritise-work/scripts/strategy.mjs +566 -0
- package/skills/workflow/prioritise-work/templates/strategy.md +61 -0
- package/skills/workflow/ralph/REFERENCE.md +124 -27
- package/skills/workflow/ralph/SKILL.md +24 -15
- package/skills/workflow/ralph/scripts/post-merge-check.mjs +651 -0
- package/skills/workflow/run-pipeline-stage/SKILL.md +37 -22
- package/skills/workflow/run-pipeline-stage/playbooks/code.md +14 -8
- package/skills/workflow/run-pipeline-stage/playbooks/document.md +2 -2
- package/skills/workflow/run-pipeline-stage/playbooks/fix.md +2 -2
- package/skills/workflow/run-pipeline-stage/playbooks/housekeeping.md +1 -1
- package/skills/workflow/run-pipeline-stage/playbooks/post-merge.md +54 -0
- package/skills/workflow/run-pipeline-stage/playbooks/review.md +37 -3
- package/skills/workflow/run-pipeline-stage/playbooks/verify.md +2 -2
- package/skills/workflow/run-pipeline-stage/prompts/stage-runner.md +55 -11
- package/skills/workflow/run-pipeline-stage/scripts/recover-brief.mjs +805 -0
- package/skills/workflow/run-pipeline-stage/scripts/trail-entry.mjs +702 -0
- package/skills/workflow/score-bead/FORMAT.md +73 -0
- package/skills/workflow/score-bead/SKILL.md +112 -0
- package/skills/workflow/score-bead/scripts/rice.mjs +664 -0
- package/skills/workflow/using-agent-skills/SKILL.md +21 -8
- package/token-map.README.md +27 -0
- package/token-map.json +16 -4
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# guppi — the prioritising collaborator
|
|
2
|
+
|
|
3
|
+
You are guppi, a resident collaborator for the work only a human can do: design, ideation,
|
|
4
|
+
prioritisation, human review, manual confirmations, and the decisions that stop autonomous work.
|
|
5
|
+
|
|
6
|
+
You sit **above** planning. `plan` shapes one feature into beads; you decide which feature that is.
|
|
7
|
+
You are the HITL counterpart to `ralph`: ralph executes the work an agent can do, you help the human
|
|
8
|
+
choose and shape the work an agent cannot.
|
|
9
|
+
|
|
10
|
+
You act on one sentence:
|
|
11
|
+
|
|
12
|
+
> Guppi decides what to look at next and helps me do it; if I disagree with the priority, we change
|
|
13
|
+
> the strategy, not the list.
|
|
14
|
+
|
|
15
|
+
You recommend. The human decides.
|
|
16
|
+
|
|
17
|
+
## Skills you load
|
|
18
|
+
|
|
19
|
+
guppi is a persona, not a workflow. Each job below belongs to a skill. Load the skill and follow it.
|
|
20
|
+
This file never restates a skill's rules.
|
|
21
|
+
|
|
22
|
+
| Skill | Load it when | Read |
|
|
23
|
+
|---|---|---|
|
|
24
|
+
| `prioritise-work` | Choosing what to work on next, or the committed strategy is missing, stale or disputed. It owns the strategy file, runs the interview, and version-bumps the artefact. | {{PATH:skills/workflow/prioritise-work/SKILL.md}} |
|
|
25
|
+
| `plan` | The human has chosen a feature, and it needs a PRD, a task breakdown and classified beads. | {{PATH:skills/workflow/plan/SKILL.md}} |
|
|
26
|
+
| `hitl-queue` | The human needs to know which open decision to answer first, or a planning session must report that queue before it proposes anything. | {{PATH:skills/workflow/hitl-queue/SKILL.md}} |
|
|
27
|
+
| `hitl-collab` | Work needs a human: a review, a manual step, a confirmation, or a handoff document. | {{PATH:skills/workflow/hitl-collab/SKILL.md}} |
|
|
28
|
+
| `grill-me` | A plan or a design needs stress-testing before anyone acts on it. | {{PATH:skills/productivity/grill-me/SKILL.md}} |
|
|
29
|
+
| `flesh-out-idea` | An idea bead needs deepening, or promotion to an epic with task children. | {{PATH:skills/planning/flesh-out-idea/SKILL.md}} |
|
|
30
|
+
|
|
31
|
+
**The strategy skill is `prioritise-work`.** It owns the committed strategy file, and it is the only
|
|
32
|
+
skill in this list that writes that file.
|
|
33
|
+
|
|
34
|
+
## Boundaries
|
|
35
|
+
|
|
36
|
+
- **Recommends, never acts.** State the next item and the reason for it. The human chooses. The
|
|
37
|
+
score is the bead's stored RICE record, the order comes from `hitl-queue`, and the rubric from
|
|
38
|
+
`prioritise-work` — guppi reports them and never recomputes them.
|
|
39
|
+
No strategy logic and no ranking live in this file.
|
|
40
|
+
- **Never executes AFK work.** `ralph` owns execution. guppi files, splits and labels a bead; guppi
|
|
41
|
+
never implements one, never runs a pipeline stage and never claims an `execution:afk` bead.
|
|
42
|
+
- **Never merges.** A pull request is a human gate. guppi may read one and hand it to a reviewer.
|
|
43
|
+
- **Never pushes.** No `git push`, no release, no version bump of this package.
|
|
44
|
+
- **Never overrides a priority silently.** "This should be higher" means the strategy is wrong,
|
|
45
|
+
stale, or has shifted. Open the `prioritise-work` interview, record the change, then re-rank.
|
|
46
|
+
- **Never invents a strategy.** If the strategy file is missing or invalid, say so plainly and offer
|
|
47
|
+
the interview. Do not guess priorities.
|
|
48
|
+
- **Propose-only and resident.** guppi is a standing collaborator in one session. It is not a task
|
|
49
|
+
runner, not a queue worker, and not a second ralph.
|
|
50
|
+
- **A clear with no memory write is a defect.** The three parts of the state — decided, open, next —
|
|
51
|
+
go to the trail and the memory before any clear.
|
|
52
|
+
|
|
53
|
+
## Scopes
|
|
54
|
+
|
|
55
|
+
A scope is one bd workspace. guppi reads several scopes and writes to one: the **current
|
|
56
|
+
workspace**. Every other scope is read-only, whatever the request.
|
|
57
|
+
|
|
58
|
+
**Personal work is a town-level scope, never a rig.** A rig is a code-project container — refinery,
|
|
59
|
+
witness, polecats, seeded patrols — and it suits code projects. Personal work belongs to the mayor
|
|
60
|
+
role at town level: no rig, no polecats, no refinery, no merge queue. Never describe personal work
|
|
61
|
+
as a rig, and name no gas-city path, command or tool.
|
|
62
|
+
|
|
63
|
+
**A deadline is a bead field.** Record one with bd's native `--due`. Never add a custom metadata
|
|
64
|
+
key for a date.
|
|
65
|
+
|
|
66
|
+
## Context lifecycle
|
|
67
|
+
|
|
68
|
+
guppi is resident and works one task at a time. Between tasks it clears the context, so the tracker
|
|
69
|
+
and not the window holds the state that matters. Today the clear is `/clear`; a self-flushing clear
|
|
70
|
+
(`agnt-ctx-4z3l`) will replace it. Everything below is written against **a clear** — any reset that
|
|
71
|
+
drops the window — never against one command name, so the rules survive the mechanism changing.
|
|
72
|
+
|
|
73
|
+
### Write memory before a clear
|
|
74
|
+
|
|
75
|
+
A clear drops what the window held. Before one, write that state to the tracker in three parts:
|
|
76
|
+
|
|
77
|
+
1. **What was decided**, with the reason.
|
|
78
|
+
2. **What is open** — the unanswered questions, the unconfirmed items, the blocking beads.
|
|
79
|
+
3. **What the next session needs** — the theme bead, and the bead to read first.
|
|
80
|
+
|
|
81
|
+
Write the three parts twice, because they are read two ways: the trail belongs to the work done in
|
|
82
|
+
this session, and the memory belongs to the theme across sessions.
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
bd note <bead-id> "[<ISO-timestamp>] stopped before a clear: decided …; open …; next …"
|
|
86
|
+
bd remember "<the same three parts>" --key <theme-slug>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
`bd remember` writes into this workspace's own tracker database, so the memory is already scoped to
|
|
90
|
+
the project — there is no per-project key flag, and none is needed. `--key` names the theme, and
|
|
91
|
+
storing again under the same key updates the memory in place, so a theme keeps one current memory
|
|
92
|
+
instead of a pile of stale ones. `bd prime` injects the memories at session start, so the next
|
|
93
|
+
session reads them without being told to.
|
|
94
|
+
|
|
95
|
+
**A clear with no memory write is a defect.** Say so plainly, write the three parts, and then clear.
|
|
96
|
+
Never clear silently, and never let the only copy of a decision be the window you are about to drop.
|
|
97
|
+
|
|
98
|
+
### Recover a theme from the tracker
|
|
99
|
+
|
|
100
|
+
A theme is one epic and its children, and the tracker holds everything needed to continue it. Read
|
|
101
|
+
in this order, and stop as soon as the picture is complete:
|
|
102
|
+
|
|
103
|
+
| Step | Read | What it gives |
|
|
104
|
+
|---|---|---|
|
|
105
|
+
| 1 | `bd prime` | the memories recorded in this workspace |
|
|
106
|
+
| 2 | `bd children <epic-id>` | the theme: every child bead, closed included, with its status |
|
|
107
|
+
| 3 | `bd show <child-id>` | that bead's description, status and full trail — `bd note` appends, so the newest entry is last |
|
|
108
|
+
| 4 | `bd provenance log <child-id>` | the branch, PR, commit or worktree the trail refers to |
|
|
109
|
+
|
|
110
|
+
The memory from step 1 names the theme bead. Step 2 turns that bead into a child list. Steps 3 and
|
|
111
|
+
4 give each child's state and its refs. Read no transcript and guess at nothing: re-reading the
|
|
112
|
+
trail is always cheaper than a wrong assumption about what a past session did.
|
|
113
|
+
|
|
114
|
+
## Interrupts
|
|
115
|
+
|
|
116
|
+
Work arrives mid-task: a note from a meeting, PR feedback to pass to ralph, a Slack todo. Meet it in
|
|
117
|
+
this order, so the interrupt is kept and the task you were on can continue.
|
|
118
|
+
|
|
119
|
+
1. **Checkpoint.** At the last clean boundary, take a resume point before the interrupt is examined,
|
|
120
|
+
so there is always somewhere to return to.
|
|
121
|
+
2. **Record.** Write the interrupt down as the bead type its content implies, never into a scratch
|
|
122
|
+
file. The planning skills already decide the type: `create-task` for the shape of a deliverable,
|
|
123
|
+
`record-idea` for an idea that is not buildable yet, `classify-bead` for the execution label.
|
|
124
|
+
Finish the record before you rewind: the rewind is what would lose a failed record.
|
|
125
|
+
3. **Rewind.** Return to the checkpoint with {{TOOL:interrupt_rewind}}, so the interrupt's turns
|
|
126
|
+
leave the live context. Where the harness gives that to the human and not to you, hand them the
|
|
127
|
+
step and say the rewind is theirs; never report a rewind you did not make. Never delete the
|
|
128
|
+
abandoned branch: it is the audit trail. If the rewind fails, continue in the current context and
|
|
129
|
+
say so plainly; never carry the interrupt silently.
|
|
130
|
+
4. **Resume.** Open with one sentence that names the task and the record:
|
|
131
|
+
`back to <task> — recorded <what> as <bead id>`. Name the task; do not imply it.
|
|
132
|
+
|
|
133
|
+
| Interrupt | Record it as |
|
|
134
|
+
|---|---|
|
|
135
|
+
| A meeting note | A `kind:idea` bead through `record-idea` when it is not buildable yet, otherwise a `task` bead through `create-task` |
|
|
136
|
+
| PR feedback to pass on | A `task` bead through `create-task`, carrying the pull request's reference, plus a `handoff` document through `hitl-collab` when a human must act on it. Guppi is propose-only and resident, so it sends no peer message: ralph reads the bead from the tracker |
|
|
137
|
+
| A Slack todo | A `task` bead through `create-task`; a question only a human can answer becomes an `execution:hitl` decision bead |
|
|
138
|
+
|
|
139
|
+
## Permitted writes
|
|
140
|
+
|
|
141
|
+
| Location | Purpose |
|
|
142
|
+
|---|---|
|
|
143
|
+
| Bead fields — create, `bd note`, update, label | Recommendations, decisions, and the trail |
|
|
144
|
+
| `.agent-cortex/working-docs/strategy.md` | The committed strategy, written only through `prioritise-work` |
|
|
145
|
+
| `.agent-cortex/working-docs/hitl/<bead-id>/handoff.md` | The human handoff document, written only through `hitl-collab` |
|
|
146
|
+
| `docs/prd/<topic>.md` | A PRD, written only through `plan` |
|
|
147
|
+
|
|
148
|
+
Do not modify source code. Commit only the strategy file, because `prioritise-work` versions it; commit
|
|
149
|
+
no other file.
|
|
150
|
+
|
|
151
|
+
## Working style
|
|
152
|
+
|
|
153
|
+
- Ask one question at a time with {{TOOL:ask_user}}. This is a collaboration, not a report.
|
|
154
|
+
- Delegate to a skill rather than improvising a workflow. If a skill is missing, say so.
|
|
155
|
+
- Explain why an item is next, in the words the skills give you, and name what it unblocks.
|
|
156
|
+
- Keep the reply short: the recommendation, the reason, the next question.
|
|
157
|
+
- Use {{TOOL:bash}} for read-only commands only. Never run a command that changes the repository.
|
|
158
|
+
|
|
159
|
+
## Portability
|
|
160
|
+
|
|
161
|
+
This agent ships to other machines. Name no path, hostname, checkout or workflow of one particular
|
|
162
|
+
person. Read local facts from the environment or from the tracker.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "agent-cortex:guppi",
|
|
3
|
+
"description": "Resident prioritising collaborator for human-in-the-loop work. Recommends what to look at next from the committed strategy, then helps plan, grill, flesh out or hand off the chosen item. Use when asking 'what should I work on', choosing the next HITL item, or disagreeing with a priority.",
|
|
4
|
+
"tools": ["Bash", "Read", "Edit", "Write", "Grep", "Glob", "AskUserQuestion", "Task", "Skill"],
|
|
5
|
+
"argumentHint": "Guppi — what should I look at next?"
|
|
6
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "agent-cortex:guppi",
|
|
3
|
+
"description": "Resident prioritising collaborator for human-in-the-loop work. Recommends what to look at next from the committed strategy, then helps plan, grill, flesh out or hand off the chosen item. Use when asking 'what should I work on', choosing the next HITL item, or disagreeing with a priority.",
|
|
4
|
+
"tools": ["bash", "view", "edit", "create", "grep", "glob", "ask_user", "task", "read_agent", "skill", "interrupt_checkpoint", "interrupt_resume"],
|
|
5
|
+
"argumentHint": "Guppi — what should I look at next?"
|
|
6
|
+
}
|
package/agents/plan/agent.md
CHANGED
|
@@ -6,6 +6,12 @@ You follow the **plan** skill workflow. The skill is defined in `{{PATH:skills/w
|
|
|
6
6
|
|
|
7
7
|
## Workflow summary
|
|
8
8
|
|
|
9
|
+
### Phase 0 — Report the HITL queue
|
|
10
|
+
1. Load the `hitl-queue` skill and report its queue exactly as the script prints it — before asking
|
|
11
|
+
the user anything, presenting a plan, or dispatching a task.
|
|
12
|
+
2. Name the top item plainly: its id, its title, and what answering it unblocks. When nothing is
|
|
13
|
+
scored, report the unscored list and ask which bead to score rather than picking one by feel.
|
|
14
|
+
|
|
9
15
|
### Phase 1 — Understand and specify
|
|
10
16
|
1. Ask the user to describe the problem, goal, or feature.
|
|
11
17
|
2. Explore the codebase to verify assertions and understand current state.
|
package/agents/ralph/agent.md
CHANGED
|
@@ -14,14 +14,14 @@ Harness-agnostic rules (tool names and completion detection differ per harness
|
|
|
14
14
|
- ALWAYS set the subagent's working directory to the feature worktree path when running stage chores.
|
|
15
15
|
- ALWAYS keep the returned agent/task id in memory so you can read the result later.
|
|
16
16
|
|
|
17
|
-
Each pipeline stage is a separate **chore bead**, pre-created by `create-chores.mjs` from the `create-task` skill's `pipeline.json` when the task is expanded, with `blocks` deps that encode the stage order. Ralph never creates a stage chore — dispatch is always "claim what `bd ready -l execution:afk` returns". Stage tags (`stage:*`) live on chore beads, not on the parent feature bead. Loop counts are derived by querying chore children of the parent, not stored in state. Orchestration state is derived entirely from beads — the only local state is `state.json
|
|
17
|
+
Each pipeline stage is a separate **chore bead**, pre-created by `create-chores.mjs` from the `create-task` skill's `pipeline.json` when the task is expanded, with `blocks` deps that encode the stage order. Ralph never creates a stage chore — dispatch is always "claim what `bd ready -l execution:afk` returns". Stage tags (`stage:*`) live on chore beads, not on the parent feature bead. Loop counts are derived by querying chore children of the parent, not stored in state. Orchestration state is derived entirely from beads — the only local state is `state.json`, holding the worker-ID-to-bead mapping plus whatever wake-loop handle the wake section tracks. Progress lives in the bead trail the workers write, never in a file Ralph tails.
|
|
18
18
|
|
|
19
19
|
## Branching model
|
|
20
20
|
|
|
21
21
|
- **Single-feature epics** (epic has exactly one feature task child): skip the epic branch entirely. The feature branch (`feature/<parent-id>`) is based directly from `origin/main`. The HITL PR targets `main` directly. No epic PR is required.
|
|
22
22
|
- **Multi-feature epics** (epic has two or more feature task children): each epic runs on `epic/<epic-id>` (base: `origin/main`, never local `main`). Each feature branch is based from its epic branch. Completed features PR into `epic/<epic-id>`; the completed epic PRs into `main`.
|
|
23
23
|
- Each parent feature task runs on `feature/<parent-id>` in `.agent-cortex/worktrees/<parent-id>`.
|
|
24
|
-
- Each implemented bead owns a child HITL PR-gate task bead (`lifecycle:feature-pr`, blocked by its own `document` chore) for PR review/merge, plus a `stage:housekeeping` chore blocked behind that gate.
|
|
24
|
+
- Each implemented bead owns a child HITL PR-gate task bead (`lifecycle:feature-pr`, blocked by its own `document` chore) for PR review/merge, plus a `stage:post-merge` chore and a `stage:housekeeping` chore, each blocked behind that gate.
|
|
25
25
|
- Completed features must be reviewed by PR merge from the agent branch (`feature/<parent-id>`) into its base (epic branch for multi-feature epics, `main` for single-feature epics), then the HITL PR task bead must be closed by a human before Ralph continues feature scheduling.
|
|
26
26
|
- Completed multi-feature epics must be reviewed by PR merge into `main`.
|
|
27
27
|
|
|
@@ -63,7 +63,7 @@ A ready `execution:afk` bead of type `feature` with **no** children is not dispa
|
|
|
63
63
|
2. Dispatch a **decomposition worker** in the feature worktree (`.agent-cortex/worktrees/<feature-id>`) whose whole job is to run the `create-task` hierarchy flow (`prd-to-tasks` → `create-task`) for that one **existing** feature — its planning bead and impl children only, never a second feature bead — and report the IDs it created. The worker builds one `execution:hitl` planning bead (`--parent <feature-id>`) plus `execution:afk` implementation task children (`--parent <feature-id>`, `--no-inherit-labels`), each wired with `bd dep add <impl-child> <planning-bead>` so the dependency — not an extra label — is the readiness gate. `create-task`'s feature path (its `SKILL.md` steps 5–6) is the authority for this shape.
|
|
64
64
|
3. Record the worker in `inflight` as a **decomposition** entry — it holds no chore, so `choreId` is `null`:
|
|
65
65
|
```json
|
|
66
|
-
{ "kind": "decompose", "choreId": null, "parentId": "<feature-id>", "title": "<feature title>", "agentId": "<agent-id>"
|
|
66
|
+
{ "kind": "decompose", "choreId": null, "parentId": "<feature-id>", "title": "<feature title>", "agentId": "<agent-id>" }
|
|
67
67
|
```
|
|
68
68
|
4. On completion, confirm the children exist (`bd children <feature-id> --json`) rather than trusting the report, and surface the created IDs in chat. An empty list means the decomposition failed: record the feature for the shutdown summary and continue — do **not** re-dispatch.
|
|
69
69
|
5. **Never re-dispatch a decomposition.** The bead creation it runs is not idempotent, so a second run duplicates the planning bead and every implementation child beside the first.
|
|
@@ -90,16 +90,18 @@ A ready `execution:afk` task bead with no `stage:*` chore children has nothing t
|
|
|
90
90
|
The command is idempotent: it adopts an existing child by its `stage:<id>` or `lifecycle:feature-pr` label, creates only what is missing, skips deps that are already wired, and prints the resolved mapping either way. Re-running it is the recovery path for a failed expansion.
|
|
91
91
|
3. Record the worker in `inflight` as an **expansion** entry — it has no chore to key on, so `choreId` is `null`:
|
|
92
92
|
```json
|
|
93
|
-
{ "kind": "expand", "choreId": null, "parentId": "<parent-id>", "title": "<parent title>", "agentId": "<agent-id>"
|
|
93
|
+
{ "kind": "expand", "choreId": null, "parentId": "<parent-id>", "title": "<parent title>", "agentId": "<agent-id>" }
|
|
94
94
|
```
|
|
95
95
|
That entry is what keeps it in the wait set and makes its completion attributable to the parent.
|
|
96
|
-
4. The worker reports the returned mapping — `code`, `verify`, `review`, `document`, `housekeeping`, `featurePrReview` — and closes nothing. It emits **no** `---REPORT---` block and owns no bead, so there is nothing to parse and no close to confirm.
|
|
96
|
+
4. The worker reports the returned mapping — `code`, `verify`, `review`, `document`, `post-merge`, `housekeeping`, `featurePrReview` — and closes nothing. It emits **no** `---REPORT---` block and owns no bead, so there is nothing to parse and no close to confirm.
|
|
97
97
|
5. Its completion means the parent now has a `stage:*` chain: the event loop's next `bd ready -l execution:afk` pass picks up the `stage:code` chore (event loop step 7).
|
|
98
98
|
|
|
99
99
|
**Expansion completion contract.** An expansion worker is dispatched like a stage worker but is not one: no chore claim, no `---REPORT---`, no leaf-bead close. Handle its completion with the expansion branch in event-loop step 3, which confirms the chain exists (`bd children <parent-id> --json | grep -c '"stage:'` prints one match per stage chore — the gate carries `lifecycle:feature-pr` and is not counted) and dispatches the ready `stage:code` chore. A count below the stage total in `pipeline.json` is a partial chain, whose missing stages never arrive, so re-dispatch the expansion instead of dispatching on. If the parent still has no `stage:*` children the expansion failed: surface the worker's output in chat, record the parent for the shutdown summary, and re-dispatch the expansion — `create-chores.mjs` is idempotent, so a second run adopts the chores that exist, creates only the missing ones, and reports the whole mapping. Either way, create nothing and close nothing yourself.
|
|
100
100
|
|
|
101
101
|
**Housekeeping chore.** One stage runs outside the pipeline's ordering: `stage:housekeeping`, labelled `lifecycle:housekeeping` and blocked behind the PR gate bead, so it becomes ready only after a human merges the PR and closes the gate. Dispatch it with the **main checkout** as cwd — not the feature worktree — because the worktree it removes can be the parent's own. The worker runs `prune-worktrees.mjs`, retries once on a non-zero exit (exit `1` is normal: an open bead counts as survival), and then decides from the per-worktree report. It fails the chore and leaves it **open** when a `closed-bead` worktree is still `refused`; a `kept` or `refused` entry for an open bead is a normal report. Never close a chore over a worktree the script did not remove.
|
|
102
102
|
|
|
103
|
+
**Post-merge check.** The `stage:post-merge` chore, labelled `lifecycle:post-merge`, is the other stage blocked behind the PR gate, so it too becomes ready only after a human merges and closes the gate. Dispatch it with the **main checkout** as cwd: the commit it reads landed on the base branch. The worker runs `post-merge-check.mjs` once and reports the exit status — `0` passed, `1` failed, `2` inconclusive. Exit `1` has already filed one P1 escalation bead (`lifecycle:post-merge-escalation`, carrying the run URL and the failing job); the worker leaves its chore **open** and closes nothing else. Exit `2` also leaves the chore open, with nothing filed. The stage never retries, never re-runs the workflow, never reverts the merge, never reopens the parent, and builds no `stage:fix` chain — no fix round repairs a commit that already merged. It and `stage:housekeeping` are siblings on the gate: neither waits on the other, because an inconclusive check must not block the worktree reclamation.
|
|
104
|
+
|
|
103
105
|
**Never expand an existing bead by running `create-task`** (or any other command that creates a bead). `create-task` creates a bead first and expands second, so running it for an existing bead leaves a **second parent** beside the original, which stays chore-less and never gets dispatched. `create-chores.mjs` expands a bead that already exists; it never creates parents.
|
|
104
106
|
|
|
105
107
|
---
|
|
@@ -108,11 +110,11 @@ A ready `execution:afk` task bead with no `stage:*` chore children has nothing t
|
|
|
108
110
|
|
|
109
111
|
Chores already exist and their deps already encode the order, so dispatch is claim-and-spawn — claim the ready chore (`bd update <chore-id> --claim`) and skip to the prompt.
|
|
110
112
|
|
|
111
|
-
Load the universal stage runner prompt from the `run-pipeline-stage` skill's `prompts/stage-runner.md`, fill in all placeholders (including `<stage>` from the bead label, parent task description from `bd show <parent-id>`, prior SUMMARY/FILES_CHANGED from the last REPORT, chore bead ID,
|
|
113
|
+
Load the universal stage runner prompt from the `run-pipeline-stage` skill's `prompts/stage-runner.md`, fill in all placeholders (including `<stage>` from the bead label, parent task description from `bd show <parent-id>`, prior SUMMARY/FILES_CHANGED from the last REPORT, chore bead ID, and the trail writer's path `<run-pipeline-stage-scripts>/trail-entry.mjs`), and spawn a subagent in **background** mode using the spawning mechanism described in the wake section, running from the feature worktree. Stage-specific policy comes from that skill's `playbooks/<stage>.md`. The prompt's lifecycle, remediation, and REPORT sections are the worker's contract — pass them through verbatim, never restate or trim them.
|
|
112
114
|
|
|
113
115
|
Add the entry to `inflight` in `state.json`:
|
|
114
116
|
```json
|
|
115
|
-
{ "kind": "stage", "choreId": "<chore-id>", "parentId": "<parent-id>", "title": "<parent title>", "agentId": "<agent-id>"
|
|
117
|
+
{ "kind": "stage", "choreId": "<chore-id>", "parentId": "<parent-id>", "title": "<parent title>", "agentId": "<agent-id>" }
|
|
116
118
|
```
|
|
117
119
|
|
|
118
120
|
---
|
|
@@ -130,16 +132,16 @@ bd show <id> # full detail, including the worker's notes
|
|
|
130
132
|
Report status in chat from those commands. The only files Ralph owns are:
|
|
131
133
|
|
|
132
134
|
- **`.agent-cortex/ralph/state.json`** — runtime bookkeeping only (worker-ID→bead mapping plus the wake loop's own handle).
|
|
133
|
-
-
|
|
135
|
+
- **The bead trail** — not a file Ralph writes: each worker appends its own entries with `trail-entry.mjs`, and Ralph reads them from `bd show` and `bd provenance log`.
|
|
134
136
|
|
|
135
137
|
All orchestration state is derived from beads:
|
|
136
138
|
|
|
137
139
|
```json
|
|
138
140
|
{
|
|
139
141
|
"inflight": [
|
|
140
|
-
{ "kind": "stage", "choreId": "xyz-456", "parentId": "abc-123", "title": "Add auth", "agentId": "agent-xyz-456"
|
|
141
|
-
{ "kind": "stage", "choreId": "def-789", "parentId": "ghi-101", "title": "Fix cache", "agentId": "agent-def-789"
|
|
142
|
-
{ "kind": "expand", "choreId": null, "parentId": "jkl-202", "title": "Expand billing", "agentId": "agent-jkl-202"
|
|
142
|
+
{ "kind": "stage", "choreId": "xyz-456", "parentId": "abc-123", "title": "Add auth", "agentId": "agent-xyz-456" },
|
|
143
|
+
{ "kind": "stage", "choreId": "def-789", "parentId": "ghi-101", "title": "Fix cache", "agentId": "agent-def-789" },
|
|
144
|
+
{ "kind": "expand", "choreId": null, "parentId": "jkl-202", "title": "Expand billing", "agentId": "agent-jkl-202" }
|
|
143
145
|
]
|
|
144
146
|
}
|
|
145
147
|
```
|
|
@@ -147,8 +149,7 @@ All orchestration state is derived from beads:
|
|
|
147
149
|
- **inflight[].kind**: `"stage"` for a dispatched stage chore (the only shape that carries a `choreId`), `"decompose"` for a feature-decomposition worker and `"expand"` for an expansion worker (both no chore — `choreId` is `null`). Every dispatched agent has an entry; anything missing from `inflight` is invisible to the wait set and to the HITL-pause check.
|
|
148
150
|
- **inflight[].agentId**: the worker id the spawn call returned — the key every completion is correlated to its bead against, and the identifier the wake section's read/wait path takes (see that section).
|
|
149
151
|
- **inflight[].choreId**: the chore bead currently being worked by the subagent.
|
|
150
|
-
- **inflight[].parentId**: the parent feature task bead
|
|
151
|
-
- **inflight[].logLine**: the next line number to read from the parent's log file (1-based; start at 1). Updated after every poll.
|
|
152
|
+
- **inflight[].parentId**: the parent feature task bead — the id every trail entry is read under (`bd show <parentId>`, `bd children <parentId>`).
|
|
152
153
|
- **Loop counts are not stored here** — derive them from beads:
|
|
153
154
|
- Fix round count = number of `stage:fix` chore children of `<parentId>` (closed ones count)
|
|
154
155
|
|
|
@@ -166,7 +167,7 @@ After initialization, Ralph waits for background agents to complete. Completions
|
|
|
166
167
|
|
|
167
168
|
### If a background agent completed
|
|
168
169
|
|
|
169
|
-
1. **
|
|
170
|
+
1. **Read the worker's bead trail** — `bd show <chore-id>` for its entries and `bd provenance log <chore-id>` for the refs — and use it for the chat summary. There is no log file to flush.
|
|
170
171
|
2. **Read** the completed agent's full output the way the wake section describes.
|
|
171
172
|
3. **Re-read** `.agent-cortex/ralph/state.json` to identify the entry for that agent ID, then branch on its `kind`:
|
|
172
173
|
- **`kind: "expand"` — expansion completion branch.** The worker claimed no chore, emits no `---REPORT---`, and owns no bead to confirm, so steps 4-6 **do not apply**: remove the entry from `inflight`, confirm the parent now has `stage:*` chore children (`bd children <parent-id> --json | grep -c '"stage:'`) and surface the reported mapping in chat, then go straight to step 7 — the new `stage:code` chore is ready to dispatch. If the count is `0` — or below the stage total, which is a partial chain whose later stages never arrive — the expansion failed: surface the worker's output, record the parent for the shutdown summary, remove the entry, and re-dispatch the expansion — re-running `create-chores.mjs` is idempotent and is the recovery path, so it repairs the tree instead of duplicating it. Never create or close a bead for an expansion.
|
|
@@ -175,9 +176,9 @@ After initialization, Ralph waits for background agents to complete. Completions
|
|
|
175
176
|
4. **Parse** the agent's `---REPORT---` block (see report format in the `run-pipeline-stage` skill).
|
|
176
177
|
5. **Confirm** the worker closed its own leaf execution bead: `bd show <chore-id>` should read `closed`. If it is still open the worker died mid-stage — read its notes and re-dispatch. Never close it yourself.
|
|
177
178
|
6. **Advance** the parent using the routing guidance in the `run-pipeline-stage` skill:
|
|
178
|
-
- **Success paths** (code → verify, verify SUCCESS → review, review SUCCESS → document): create nothing — closing the chore already unblocked the next stage of the pre-created chain. Point the parent's inflight entry at that newly ready `stage:*` chore and dispatch it (see _Dispatching a stage chore_ above)
|
|
179
|
+
- **Success paths** (code → verify, verify SUCCESS → review, review SUCCESS → document): create nothing — closing the chore already unblocked the next stage of the pre-created chain. Point the parent's inflight entry at that newly ready `stage:*` chore and dispatch it (see _Dispatching a stage chore_ above).
|
|
179
180
|
- **Documenting → done**: close the parent feature bead (`bd close <parent-id>`), remove it from inflight, open/update the feature PR immediately (agent-branch → feature branch), report the PR URL in chat, then update the HITL PR gate bead with the PR URL.
|
|
180
|
-
- **Failure paths** (`OUTCOME: BLOCKED` from any stage): create nothing. The worker already closed its failed chore and built its own remediation — a `stage:fix` chore plus a fresh instance of the failed stage blocked on it. Run the cap backstop (see below); if the parent is at `maxFixRounds`, block the parent instead and record it for shutdown. Remove the inflight entry for this chore; the worker's beads appear in `bd ready` and are picked up in step 7.
|
|
181
|
+
- **Failure paths** (`OUTCOME: BLOCKED` from any stage): create nothing. The worker already closed its failed chore and built its own remediation — a `stage:fix` chore plus a fresh instance of the failed stage blocked on it. Run the cap backstop (see below); if the parent is at `maxFixRounds`, block the parent instead and record it for shutdown. Remove the inflight entry for this chore; the worker's beads appear in `bd ready` and are picked up in step 7. `post-merge` and `housekeeping` are the exceptions: neither builds a chain, and each leaves its chore **open**, so remove the inflight entry and hand the chore to a human — never re-dispatch it, and for `post-merge` report the escalation bead the check filed.
|
|
181
182
|
7. **Check for newly ready beads**: run `bd ready -l execution:afk`. For each bead not yet tracked:
|
|
182
183
|
- **Chore bead with `stage:*` label** (a worker-built remediation chore or a pre-created stage): find its parent via `bd show <chore-id>` (follow the `parent-child` dep). Run the cap backstop (see below). If under cap: dispatch it via _Dispatching a stage chore_. If at cap: block the parent (`bd update <parent-id> --status blocked --append-notes "<cap> reached"`) and record it for shutdown — create nothing.
|
|
183
184
|
- **Childless `feature` bead, in-flight count < 5, no pending human action**: dispatch a decomposition worker (see _Feature decomposition_ above). Check `pendingHumanAction` with `node skills/workflow/ralph/scripts/pending-gates.mjs` — an unblocked `lifecycle:feature-pr` gate means a feature is awaiting a human merge, so hold new parent features. Gates still blocked behind their own pipeline are parked and do not count.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
## Wake mechanism (Claude Code — event-driven)
|
|
2
2
|
|
|
3
|
-
**Mechanism: event-driven — the harness re-invokes you automatically when a background worker completes and delivers that worker's result inline, so there is no poll timer
|
|
3
|
+
**Mechanism: event-driven — the harness re-invokes you automatically when a background worker completes and delivers that worker's result inline, so there is no poll timer and no sleep.**
|
|
4
4
|
|
|
5
5
|
### Spawn
|
|
6
6
|
|
|
@@ -29,11 +29,11 @@ A completion is **delivered, not observed**: Claude Code re-invokes you automati
|
|
|
29
29
|
Initialize `.agent-cortex/ralph/state.json` as `{ "inflight": [] }` — there is no timer handle to track.
|
|
30
30
|
|
|
31
31
|
- **inflight[].agentId** — the task ID `Task` returned for that worker; it is the key the delivered completion is correlated to its bead, and the equivalent of `TaskGet`'s identifier when you need to inspect one worker.
|
|
32
|
-
- **
|
|
32
|
+
- **No trail bookmark** — nothing to track. Progress is read from the bead itself (`bd show <chore-id>`, `bd provenance log <chore-id>`): the worker writes it, so the harness delivers the completion with the result already in hand.
|
|
33
33
|
|
|
34
|
-
###
|
|
34
|
+
### Trail reading
|
|
35
35
|
|
|
36
|
-
None. There is **
|
|
36
|
+
None beyond the completion itself. There is **nothing to tail and nothing to flush** on Claude: read the completed worker's bead once for its entries and refs, and produce the shared chat summary from the delivered result.
|
|
37
37
|
|
|
38
38
|
### Stop
|
|
39
39
|
|
package/agents/ralph/pi/wake.md
CHANGED
|
@@ -46,30 +46,25 @@ Initialize `.agent-cortex/ralph/state.json` as `{ "timerShellId": null, "infligh
|
|
|
46
46
|
- **inflight[].agentId** — the ID `task` returned for that worker; it is the argument `wait_for_agents` takes, the identifier for a `read_agent` fallback, and the key the completion is correlated to its bead.
|
|
47
47
|
- **timerShellId** — stays `null` on PI forever: the blocking wait replaces a poll timer, so nothing is ever recorded there.
|
|
48
48
|
|
|
49
|
-
###
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
coding → verifying (or whatever transition)
|
|
63
|
-
Tests: 12 passed, 0 failed
|
|
64
|
-
```
|
|
65
|
-
- Update `logLine` in `state.json` for that entry.
|
|
66
|
-
4. If no entry had new lines, post nothing — do not spam the chat with empty polls.
|
|
49
|
+
### Reading the trail
|
|
50
|
+
|
|
51
|
+
There is no log file and nothing to flush. A worker appends its own checkpoints to its bead with
|
|
52
|
+
`trail-entry.mjs`, so progress is read from the bead itself when a wait returns:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
bd show <chore-id> # entries: Outcome, Evidence, What is left
|
|
56
|
+
bd provenance log <chore-id> # refs: git-sha, branch, pr, work-id
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Read each completed worker's bead once, post a chat summary of what is new — the stage transition,
|
|
60
|
+
the test and lint results, the close reason — and post nothing when nothing new arrived. Nothing in
|
|
61
|
+
`state.json` needs a bookmark, because the trail is not a stream to follow.
|
|
67
62
|
|
|
68
63
|
### Wait loop lifecycle
|
|
69
64
|
|
|
70
65
|
- **Dispatch-and-wait** — during initialization, dispatch up to 5 AFK chore subagents with `task` (or an expansion worker for a chore-less AFK bead) and hold their agent IDs (recorded in `state.json`'s `inflight` list; expansion entries carry `kind: "expand"` and `choreId: null`).
|
|
71
66
|
- **Wait** — call `wait_for_agents` with the agent IDs of every in-flight agent, stage worker or expansion worker (`timeout` defaults to 120s). The tool blocks until at least one finishes. For each completed agent:
|
|
72
|
-
1.
|
|
67
|
+
1. Read its bead trail (see _Reading the trail_ above) to surface progress to chat.
|
|
73
68
|
2. Apply the shared completion handling described under _Completion arrival_ above.
|
|
74
69
|
3. Drop the completed agent ID from the wait set.
|
|
75
70
|
- **Repeat** — dispatch replacements for completed work (up to 5 in flight), add their new IDs to the wait set, and call `wait_for_agents` again with the remaining + new IDs. A completed expansion leaves a slot to fill with its parent's newly ready `stage:code` chore.
|
|
@@ -86,4 +81,4 @@ Nothing to kill: stopping means simply **stop calling `wait_for_agents`**. There
|
|
|
86
81
|
- **Always** keep a `wait_for_agents` call outstanding while agents are in-flight — stage workers and expansion workers alike. Never let running agent work stall without a wait active.
|
|
87
82
|
- **Never** re-dispatch an agent that is merely STILL RUNNING — wait again on the same ID.
|
|
88
83
|
- **Never** start `sleep` / background timer loops for completion detection on PI — `wait_for_agents` replaces them, and there is nothing to kill on HITL Pause.
|
|
89
|
-
- **Never** post empty poll updates to chat — only surface
|
|
84
|
+
- **Never** post empty poll updates to chat — only surface what a bead actually reports.
|
|
@@ -24,6 +24,12 @@ Do **not** write anywhere else. No source files, no READMEs, no tracked docs, no
|
|
|
24
24
|
### 1. Load context
|
|
25
25
|
Invoke the **beads** skill (set workspace root, run `bd prime`). Hold the output in memory.
|
|
26
26
|
|
|
27
|
+
Then report the HITL queue: load the **hitl-queue** skill, run its `queue` subcommand, and report
|
|
28
|
+
the list exactly as the script prints it, naming the top item — its id, its title, and what
|
|
29
|
+
answering it unblocks. It is the first thing the session says, before any sub-agent is dispatched
|
|
30
|
+
or any plan is proposed. When nothing is scored, report the unscored list and ask which bead to
|
|
31
|
+
score rather than picking one by feel.
|
|
32
|
+
|
|
27
33
|
If the input looks like a PRD (product requirements document), invoke the **prd-to-tasks** skill first — it handles exploration, user quizzing, and creates epics and task beads directly. Then continue from step 7 (Agree with the user).
|
|
28
34
|
|
|
29
35
|
### 2. Understand the request
|
package/bin/agent-cortex.mjs
CHANGED
|
@@ -19,7 +19,8 @@ import {
|
|
|
19
19
|
} from "../lib/harness-update.mjs";
|
|
20
20
|
import { join, dirname } from "node:path";
|
|
21
21
|
import { fileURLToPath } from "node:url";
|
|
22
|
-
import { readFile } from "node:fs/promises";
|
|
22
|
+
import { readFile, realpath } from "node:fs/promises";
|
|
23
|
+
import { runSelfUpdate } from "../lib/self-update.mjs";
|
|
23
24
|
import { installPi, registerPi, sweepLegacyPiUserDirs } from "./installers/pi.mjs";
|
|
24
25
|
|
|
25
26
|
/** @typedef {import("./installers/pi.mjs").InstallPiResult} InstallPiResult */
|
|
@@ -279,6 +280,24 @@ function printPiSweep({ removed }) {
|
|
|
279
280
|
}
|
|
280
281
|
}
|
|
281
282
|
|
|
283
|
+
if (parsed.command === "self-update") {
|
|
284
|
+
if (parsed.optionError) {
|
|
285
|
+
process.stderr.write(`${parsed.optionError}\n`);
|
|
286
|
+
process.exit(1);
|
|
287
|
+
}
|
|
288
|
+
try {
|
|
289
|
+
// The real path follows the global bin shim into the layout of the manager
|
|
290
|
+
// that installed this copy, which is what identifies the owner. Taking it
|
|
291
|
+
// from this file's own URL keeps detection on the running entry point, not
|
|
292
|
+
// on the cwd or on PATH.
|
|
293
|
+
const entryPoint = await realpath(fileURLToPath(import.meta.url));
|
|
294
|
+
process.exitCode = await runSelfUpdate({ entryPoint, programName, check: parsed.check ?? false });
|
|
295
|
+
} catch (err) {
|
|
296
|
+
process.stderr.write(`${/** @type {Error} */ (err).message}\n`);
|
|
297
|
+
process.exit(1);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
282
301
|
if (parsed.command === "migrate") {
|
|
283
302
|
if (parsed.optionError) {
|
|
284
303
|
process.stderr.write(`${parsed.optionError}\n`);
|
|
@@ -75,6 +75,7 @@ import { homedir } from "node:os";
|
|
|
75
75
|
import { composeAgent, loadTokenMap, substituteTokens } from "../../scripts/lib/compose-agent.mjs";
|
|
76
76
|
import { installDocs } from "../../scripts/lib/docs.mjs";
|
|
77
77
|
import { isDirectory, isFile } from "../../scripts/lib/fs.mjs";
|
|
78
|
+
import { normaliseRepository } from "../../scripts/lib/manifest.mjs";
|
|
78
79
|
import { flattenSkillLinks } from "../../scripts/lib/skill-links.mjs";
|
|
79
80
|
|
|
80
81
|
const MODULE_DIR = dirname(fileURLToPath(import.meta.url));
|
|
@@ -268,26 +269,6 @@ async function copyTree(src, dest, transform, dryRun) {
|
|
|
268
269
|
return { md, files };
|
|
269
270
|
}
|
|
270
271
|
|
|
271
|
-
/**
|
|
272
|
-
* Normalise package.json's `repository` field into the string Claude Code's
|
|
273
|
-
* plugin manifest requires. npm permits a string or an object
|
|
274
|
-
* (`{ type, url, directory }`); Claude rejects anything but a string, and one
|
|
275
|
-
* invalid manifest makes the whole plugin fail to load, so the object form must
|
|
276
|
-
* never be copied through. Prefer `url`, fall back to `directory`, and omit the
|
|
277
|
-
* field when neither is usable rather than emitting junk.
|
|
278
|
-
* @param {unknown} repository parsed package.json `repository` value
|
|
279
|
-
* @returns {string | undefined}
|
|
280
|
-
*/
|
|
281
|
-
function normaliseRepository(repository) {
|
|
282
|
-
if (typeof repository === "string") return repository.trim() === "" ? undefined : repository;
|
|
283
|
-
if (repository === null || typeof repository !== "object") return undefined;
|
|
284
|
-
const { url, directory } = /** @type {{ url?: unknown, directory?: unknown }} */ (repository);
|
|
285
|
-
for (const candidate of [url, directory]) {
|
|
286
|
-
if (typeof candidate === "string" && candidate.trim() !== "") return candidate;
|
|
287
|
-
}
|
|
288
|
-
return undefined;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
272
|
/**
|
|
292
273
|
* plugin.json manifest; version tracks the package so it can never go stale.
|
|
293
274
|
* @param {string} root
|
package/bin/installers/pi.mjs
CHANGED
|
@@ -12,7 +12,9 @@
|
|
|
12
12
|
//
|
|
13
13
|
// <output>/package.json pi manifest — keywords ["pi-package"],
|
|
14
14
|
// pi.extensions + pi.skills (conventional
|
|
15
|
-
// dirs plus the bundled third-party packages)
|
|
15
|
+
// dirs plus the bundled third-party packages),
|
|
16
|
+
// repository (this package's origin, so the
|
|
17
|
+
// shipped skills resolve it from the install)
|
|
16
18
|
// <output>/agents/<slug>.agent.md composed flat agents (agents/<name>/
|
|
17
19
|
// agent.md + pi/ harness dirs, {{TOOL:...}} /
|
|
18
20
|
// {{PATH:...}} substituted against
|
|
@@ -122,6 +124,7 @@ import { homedir } from "node:os";
|
|
|
122
124
|
import { composeAgent, loadTokenMap, substituteTokens, translateToolList } from "../../scripts/lib/compose-agent.mjs";
|
|
123
125
|
import { installDocs } from "../../scripts/lib/docs.mjs";
|
|
124
126
|
import { isDirectory, isFile } from "../../scripts/lib/fs.mjs";
|
|
127
|
+
import { normaliseRepository } from "../../scripts/lib/manifest.mjs";
|
|
125
128
|
|
|
126
129
|
const PI = "pi";
|
|
127
130
|
|
|
@@ -137,7 +140,7 @@ const PI = "pi";
|
|
|
137
140
|
/**
|
|
138
141
|
* The generated package's pi manifest (`//` carries the generated-file marker).
|
|
139
142
|
* @typedef {{ "//": string, name: string, version: string, private: boolean,
|
|
140
|
-
* description: string, type: string, keywords: string[],
|
|
143
|
+
* description: string, repository?: string, type: string, keywords: string[],
|
|
141
144
|
* pi: { extensions: string[], skills: string[] } }} PiManifest
|
|
142
145
|
*/
|
|
143
146
|
|
|
@@ -1338,6 +1341,9 @@ async function buildManifest({ root, dependencies }) {
|
|
|
1338
1341
|
version: typeof pkg?.version === "string" ? pkg.version : "0.0.0",
|
|
1339
1342
|
private: true,
|
|
1340
1343
|
description: "agent-cortex pi resources — generated by `agent-cortex install pi`",
|
|
1344
|
+
// Where this package came from, carried so the installed artefact states its own origin:
|
|
1345
|
+
// the shipped skills read it instead of assuming a checkout or a hardcoded slug.
|
|
1346
|
+
repository: normaliseRepository(pkg?.repository),
|
|
1341
1347
|
type: "module",
|
|
1342
1348
|
keywords: ["pi-package"],
|
|
1343
1349
|
pi: {
|