@sjawhar/pi-legion-envoy 7.11.0 → 7.11.2
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/dist/legion.js
CHANGED
|
@@ -18042,7 +18042,7 @@ function endInjectedUserTurns(sessionID) {
|
|
|
18042
18042
|
// package.json
|
|
18043
18043
|
var package_default = {
|
|
18044
18044
|
name: "@sjawhar/pi-legion-envoy",
|
|
18045
|
-
version: "7.11.
|
|
18045
|
+
version: "7.11.2",
|
|
18046
18046
|
license: "Apache-2.0",
|
|
18047
18047
|
type: "module",
|
|
18048
18048
|
omp: {
|
|
@@ -18055,7 +18055,7 @@ var package_default = {
|
|
|
18055
18055
|
]
|
|
18056
18056
|
},
|
|
18057
18057
|
legion: {
|
|
18058
|
-
daemonApiVersion:
|
|
18058
|
+
daemonApiVersion: 13
|
|
18059
18059
|
},
|
|
18060
18060
|
repository: {
|
|
18061
18061
|
type: "git",
|
|
@@ -18421,7 +18421,8 @@ var LegionGrantRequest = exports_external.strictObject({
|
|
|
18421
18421
|
sessionId: nonEmptyString,
|
|
18422
18422
|
secret: nonEmptyString,
|
|
18423
18423
|
tree: nonEmptyString,
|
|
18424
|
-
issue: nonEmptyString
|
|
18424
|
+
issue: nonEmptyString,
|
|
18425
|
+
push: exports_external.boolean().optional()
|
|
18425
18426
|
});
|
|
18426
18427
|
var LegionControllerGrantRequest = exports_external.strictObject({
|
|
18427
18428
|
sessionId: nonEmptyString,
|
|
@@ -19413,8 +19414,24 @@ function splitShellCommands(command) {
|
|
|
19413
19414
|
endCommand();
|
|
19414
19415
|
return commands;
|
|
19415
19416
|
}
|
|
19417
|
+
function commandMatch(words, name, sequence) {
|
|
19418
|
+
for (let index = 0;index < words.length; index += 1) {
|
|
19419
|
+
const word = words[index];
|
|
19420
|
+
if (word !== name && !word?.endsWith(`/${name}`))
|
|
19421
|
+
continue;
|
|
19422
|
+
if (sequence.every((expected, offset) => words[index + 1 + offset] === expected))
|
|
19423
|
+
return index;
|
|
19424
|
+
}
|
|
19425
|
+
return -1;
|
|
19426
|
+
}
|
|
19427
|
+
function isPushInvocation(words) {
|
|
19428
|
+
return commandMatch(words, "legion", ["push"]) !== -1;
|
|
19429
|
+
}
|
|
19430
|
+
function commandsRunPush(commands) {
|
|
19431
|
+
return commands?.some(isPushInvocation) ?? false;
|
|
19432
|
+
}
|
|
19416
19433
|
function jjLogRewriteInvocation(words) {
|
|
19417
|
-
const jj = words
|
|
19434
|
+
const jj = commandMatch(words, "jj", []);
|
|
19418
19435
|
if (jj === -1)
|
|
19419
19436
|
return;
|
|
19420
19437
|
const args = words.slice(jj + 1);
|
|
@@ -19430,7 +19447,7 @@ var LEGION_HANDOFF_COMPLETE_MENTION = /\blegion[\s"'`,[\]]+handoff[\s"'`,[\]]+co
|
|
|
19430
19447
|
var LEGION_HANDOFF_COMPLETE = {
|
|
19431
19448
|
mention: (text) => LEGION_HANDOFF_COMPLETE_MENTION.test(text) ? "legion handoff complete" : undefined,
|
|
19432
19449
|
invocation: (words) => {
|
|
19433
|
-
const legion = words
|
|
19450
|
+
const legion = commandMatch(words, "legion", ["handoff", "complete"]);
|
|
19434
19451
|
return legion === -1 ? undefined : words.slice(legion).join(" ");
|
|
19435
19452
|
},
|
|
19436
19453
|
refusal: (attempt) => `refused \`${attempt}\`: a phase is completed with the \`legion\` tool's ` + "`handoff_complete`, never a shell command: the tool call is what records the phase " + "complete. A root architect or a `task` subagent has no handoff actions: it leaves the " + "completion to the worker. Text that only names the command (a commit message, a PR body) " + "reads as the command: pass it in a file."
|
|
@@ -19477,14 +19494,13 @@ function nonFileWriteScheme(toolCall) {
|
|
|
19477
19494
|
return;
|
|
19478
19495
|
return NON_FILE_WRITE_URL.exec(writeTarget(toolCall.input.path))?.[1]?.toLowerCase();
|
|
19479
19496
|
}
|
|
19480
|
-
function paneRuleRefusal(toolCall, rules) {
|
|
19497
|
+
function paneRuleRefusal(toolCall, rules, commands) {
|
|
19481
19498
|
if (rules.length === 0)
|
|
19482
19499
|
return;
|
|
19483
19500
|
const { toolName, input } = toolCall;
|
|
19484
19501
|
if (toolName === "bash") {
|
|
19485
19502
|
if (typeof input.command !== "string")
|
|
19486
19503
|
return;
|
|
19487
|
-
const commands = splitShellCommands(input.command);
|
|
19488
19504
|
for (const rule of rules) {
|
|
19489
19505
|
const attempt = refusedCommand(input.command, commands, rule);
|
|
19490
19506
|
if (attempt !== undefined)
|
|
@@ -19584,7 +19600,8 @@ function legionExtension(pi) {
|
|
|
19584
19600
|
toolName: toolCall.toolName
|
|
19585
19601
|
});
|
|
19586
19602
|
paneRules ??= PANE_RULES[classifySession(process.env).kind] ?? [];
|
|
19587
|
-
const
|
|
19603
|
+
const commands = toolCall.toolName === "bash" && typeof toolCall.input.command === "string" ? splitShellCommands(toolCall.input.command) : undefined;
|
|
19604
|
+
const refusal = paneRuleRefusal(toolCall, paneRules, commands);
|
|
19588
19605
|
if (refusal !== undefined)
|
|
19589
19606
|
return { block: true, reason: refusal };
|
|
19590
19607
|
if (await checkSubagentSession(context))
|
|
@@ -19614,7 +19631,8 @@ function legionExtension(pi) {
|
|
|
19614
19631
|
tree: active.tree,
|
|
19615
19632
|
issue: active.issue,
|
|
19616
19633
|
sessionId: sessionID,
|
|
19617
|
-
secret: active.secret
|
|
19634
|
+
secret: active.secret,
|
|
19635
|
+
push: commandsRunPush(commands)
|
|
19618
19636
|
}));
|
|
19619
19637
|
});
|
|
19620
19638
|
pi.on("message_start", async (event, context) => {
|
package/dist/skills/AGENTS.md
CHANGED
|
@@ -14,7 +14,7 @@ event intake, process lifecycle, credentials, and role delivery.
|
|
|
14
14
|
| `legion-architect/` | root and sub-architects | tree ownership, decomposition, waves, gates, integration, sign-off |
|
|
15
15
|
| `legion-controller/` | the controller root process | wake routing, keeping the admission slots full from `todo`, the daily report, escalation |
|
|
16
16
|
| `legion-oracle/` | any role doing research | repository-grounded research |
|
|
17
|
-
| `legion-retro/` | the implementer, at retro | the pre-merge retrospective
|
|
17
|
+
| `legion-retro/` | the implementer, at retro | the pre-merge retrospective, its Dispatch message, and the PR body content the pull request's changed paths require at its commit |
|
|
18
18
|
| `legion-worker/` | planner, implementer, tester, reviewer, merger | the phase contracts: handoffs, GitHub identity, PR body and READY discipline, the merge-gate order |
|
|
19
19
|
| `thermonuclear-code-quality/` | the `thermonuclear-code-quality` agent | the maintainability rubric of the reviewer's pair (adapted from the MIT-licensed Thermos plugin in `cursor/plugins`; `LICENSE` beside it) |
|
|
20
20
|
| `thermonuclear-deep-review/` | the `thermonuclear-deep-review` agent, and the reviewer (the Security Guidelines) | the correctness rubric of the reviewer's pair, with its tagged, diff-triggered Security Guidelines and the attack on the PR body's claims (adapted from the MIT-licensed Thermos plugin in `cursor/plugins`; `LICENSE` beside it) |
|
|
@@ -235,8 +235,13 @@ ends, never after an idle window). You start nothing for it. Never `envoy_publis
|
|
|
235
235
|
worker's role topic to start retro: a suspended role is not running to receive it.
|
|
236
236
|
|
|
237
237
|
Wait for the implementer to report its durable retro result. Retro output is
|
|
238
|
-
`docs/solutions
|
|
239
|
-
|
|
238
|
+
`docs/solutions/`, the PR body content the repository's instructions derive from the pull
|
|
239
|
+
request's changed paths at that commit, and one `dispatch_message` on the issue; it must not create
|
|
240
|
+
a `.legion` file or rewrite the reviewer-approved head. When the implementer reports that it cannot
|
|
241
|
+
compute that body content or do the work a line of it affirms, that its read of the body failed or
|
|
242
|
+
came back empty, or that GitHub refused the body, it has pushed nothing and is still in its phase.
|
|
243
|
+
Answer it with `envoy_publish` to its role topic, saying how from the deployment instructions;
|
|
244
|
+
when they do not say, open a `dispatch_ask` naming the missing instruction.
|
|
240
245
|
|
|
241
246
|
## 6. Architect sign-off and merge
|
|
242
247
|
|
|
@@ -9,8 +9,9 @@ Retro is mandatory for every issue that passed review. The daemon starts the imp
|
|
|
9
9
|
once the reviewer approves, so the person with implementation context performs the retrospective,
|
|
10
10
|
and the skill obtains a separate fresh-eyes perspective. Retro runs before merge.
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
The `scripts/e2e/` proofs this skill names are in sjawhar/legion, the Legion repository, which
|
|
13
|
+
need not be the repository you are working in. `docs/solutions/` and `.legion/` are on the issue
|
|
14
|
+
branch of the repository you are working in.
|
|
14
15
|
|
|
15
16
|
## Merge-gate ordering
|
|
16
17
|
|
|
@@ -22,8 +23,9 @@ retrospective's durable output.
|
|
|
22
23
|
merge. The daemon strips whatever `.legion/` main still carries from the next issue's branch
|
|
23
24
|
before any of its roles start, so that tree's own merge carries the removal onto the default
|
|
24
25
|
branch; no operator sweep follows.
|
|
25
|
-
3. Run this retro: commit durable learnings to `docs/solutions
|
|
26
|
-
the
|
|
26
|
+
3. Run this retro: commit durable learnings to `docs/solutions/`, bring the pull request body's
|
|
27
|
+
path-derived content up to date for that commit before you push it, and post the retro
|
|
28
|
+
message on the Dispatch issue.
|
|
27
29
|
Retro writes **no `.legion` file**, so it never changes the approved head's handoffs.
|
|
28
30
|
4. The merger verifies the tip is the approved head plus commits that change only
|
|
29
31
|
`docs/solutions/` — `jj diff --from <approved-sha> --to <tip-sha> --summary`, quoted in READY —
|
|
@@ -103,8 +105,50 @@ related_issues:
|
|
|
103
105
|
---
|
|
104
106
|
```
|
|
105
107
|
|
|
106
|
-
Commit the documentation on the existing issue branch
|
|
107
|
-
|
|
108
|
+
Commit the documentation on the existing issue branch. Do not create a replacement branch or
|
|
109
|
+
bookmark.
|
|
110
|
+
|
|
111
|
+
Before you push that commit, bring up to date the body content the repository derives from the
|
|
112
|
+
pull request's changed paths. A repository can require such content, a line naming a checklist
|
|
113
|
+
for each class of changed path for instance, and have a required check read it from the PR body.
|
|
114
|
+
When your commit adds a `docs/solutions/` path the approved body never accounted for, that check
|
|
115
|
+
fails at your head; the merger reports a stale body rather than rewriting it, and READY refuses a
|
|
116
|
+
head whose required check failed, so the tree stops at the merger.
|
|
117
|
+
|
|
118
|
+
1. Read what the deployment instructions in your system prompt, and the agent guide and pull
|
|
119
|
+
request template of the repository you are working in, derive from the changed paths. When
|
|
120
|
+
they derive nothing, skip steps 2 to 4 and go on to the push.
|
|
121
|
+
2. Compute it the way they say, for the paths the pull request changes at your commit, the files
|
|
122
|
+
GitHub lists on it:
|
|
123
|
+
`cd -- "$LEGION_WORKSPACE" && jj -R "$LEGION_WORKSPACE" git fetch && jj -R "$LEGION_WORKSPACE" diff --from 'fork_point(<base>@origin | <commit>)' --to <commit> --name-only`
|
|
124
|
+
lists them, `<base>` the pull request's base branch and `<commit>` your docs commit. Where a
|
|
125
|
+
line affirms work, such as a checklist completed for a class of path, do that work for the
|
|
126
|
+
paths your commit adds before you write the line: the line is a claim. That work may change
|
|
127
|
+
files under `docs/solutions/` only, folded into your docs commit before the push, since
|
|
128
|
+
anything else above the approved head voids the approval.
|
|
129
|
+
3. In a fresh `mktemp -d` directory outside `$LEGION_WORKSPACE` (the one place you write outside
|
|
130
|
+
it: jj snapshots a file inside it, and a fixed name in a shared `/tmp` can hold another
|
|
131
|
+
session's body), save the live body twice, and stop unless the read succeeded and is not
|
|
132
|
+
empty. `pipefail` makes a failed read fail the line (without it the line exits with `tee`'s
|
|
133
|
+
status and leaves two empty files), and the parentheses keep it to that line, since your
|
|
134
|
+
session's shell persists and a later `cmd | head` would exit 141 under it:
|
|
135
|
+
`cd -- "$LEGION_WORKSPACE" && ( set -o pipefail && legion gh -- api repos/{owner}/{repo}/pulls/{number} --jq .body | tee <dir>/before.md <dir>/body.md ) && test -s <dir>/before.md`.
|
|
136
|
+
In `<dir>/body.md`, change the lines it carries for that content and add any it lacks where
|
|
137
|
+
the instructions place them; other phases' lines stay as they wrote them.
|
|
138
|
+
4. Write it back before the push:
|
|
139
|
+
`cd -- "$LEGION_WORKSPACE" && legion gh -- api --method PATCH repos/{owner}/{repo}/pulls/{number} -F body=@<dir>/body.md`.
|
|
140
|
+
The checks the push starts read the body as it stands then; an edit after the push re-runs
|
|
141
|
+
only a check that also starts on an edited body. Such a check re-runs now at the approved
|
|
142
|
+
head, on a diff without your commit, and may fail there; READY reads the head your push makes.
|
|
143
|
+
|
|
144
|
+
When you cannot compute that content, cannot do the work a line affirms within
|
|
145
|
+
`docs/solutions/`, the body read fails or comes back empty, or GitHub refuses the body, push
|
|
146
|
+
nothing and do not complete: tell the architect with `envoy_publish` to its role topic, naming
|
|
147
|
+
what failed.
|
|
148
|
+
|
|
149
|
+
Then push the commit with `legion push`. When the push is refused after step 4 wrote the body,
|
|
150
|
+
write `<dir>/before.md` back the same way before you report the refusal to the architect, so the
|
|
151
|
+
body matches the head GitHub has. After the push, post one Dispatch message on
|
|
108
152
|
the issue — `issue` is your `LEGION_ISSUE`; Legion issues live on Dispatch, never on a GitHub
|
|
109
153
|
issue, and the `gh` shim refuses every GitHub-issue write — naming the documents, the
|
|
110
154
|
one-to-three most useful takeaways, the two proofs you read, and the production check that
|
|
@@ -130,10 +174,11 @@ dispatch_message({
|
|
|
130
174
|
})
|
|
131
175
|
```
|
|
132
176
|
|
|
133
|
-
The
|
|
134
|
-
handoff, phase artifact, local feedback log, or
|
|
135
|
-
`.legion/`. Report completion with the `legion`
|
|
136
|
-
summary: two sentences for the architect) — no
|
|
177
|
+
The `docs/solutions/` commit, the PR body lines that commit makes stale, and the Dispatch message
|
|
178
|
+
are the only retro outputs. Never write a handoff, phase artifact, local feedback log, or
|
|
179
|
+
completion label, and add or change nothing under `.legion/`. Report completion with the `legion`
|
|
180
|
+
tool's `handoff_complete` alone (its summary: two sentences for the architect) — no
|
|
181
|
+
`handoff_write`.
|
|
137
182
|
|
|
138
183
|
## Completion check
|
|
139
184
|
|
|
@@ -142,6 +187,9 @@ Before returning, verify all of the following:
|
|
|
142
187
|
- The reviewer-approved head remains below the retro documentation commit, and the reviewer's
|
|
143
188
|
approval of that head stands: the merger accepts the approved head plus this commit.
|
|
144
189
|
- The learning documents and the Dispatch message both exist (never a GitHub issue comment).
|
|
190
|
+
- The PR body carries what the repository's instructions derive from the pull request's changed
|
|
191
|
+
paths at the retro head, written back before that head's push, and each line that affirms work
|
|
192
|
+
affirms work you did; or the instructions derive nothing.
|
|
145
193
|
- Both proofs were read, and any gap in either is recorded as a learning.
|
|
146
194
|
- No `.legion` file was created or modified by retro.
|
|
147
195
|
- The fresh-eyes analysis was considered alongside the implementer's context.
|
|
@@ -12,7 +12,8 @@ removes `.legion/` before the merge: the approved head carries it. The daemon st
|
|
|
12
12
|
operator sweep follows. After the approval, only retro's `docs/solutions/` commit leaves it
|
|
13
13
|
standing on its own (*Retro*, below). A conflict-forced merge goes back to the reviewer for a
|
|
14
14
|
confirmation or a new round, as the fingerprint decides (*The reviewer*, below, and
|
|
15
|
-
`skill://legion-worker/references/conflicts-and-rewrites.md`), and any other change
|
|
15
|
+
`skill://legion-worker/references/conflicts-and-rewrites.md`), and any other change to the head
|
|
16
|
+
voids it.
|
|
16
17
|
|
|
17
18
|
## The reviewer
|
|
18
19
|
|
|
@@ -68,6 +69,12 @@ review posted without a completion leaves the issue in reviewing until you finis
|
|
|
68
69
|
reviewer. Anything else above the approved head does void it, and the merger tells the
|
|
69
70
|
architect the head must return to review instead of completing. A conflict-forced rebase
|
|
70
71
|
after retro moves those documents with the branch; retro never re-runs.
|
|
72
|
+
- **Retro brings the PR body's path-derived content up to date before its push.** Whatever the
|
|
73
|
+
repository's instructions derive from the pull request's changed paths (a checklist named for
|
|
74
|
+
each class of path, read by a required check), retro recomputes for the whole diff at its commit
|
|
75
|
+
and writes into the live body before `legion push` (`skill://legion-retro`), since the merger
|
|
76
|
+
reports a stale body rather than rewriting it. A body edit changes no commit, so the approval
|
|
77
|
+
stands.
|
|
71
78
|
|
|
72
79
|
## The merger
|
|
73
80
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sjawhar/pi-legion-envoy",
|
|
3
|
-
"version": "7.11.
|
|
3
|
+
"version": "7.11.2",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"omp": {
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
]
|
|
14
14
|
},
|
|
15
15
|
"legion": {
|
|
16
|
-
"daemonApiVersion":
|
|
16
|
+
"daemonApiVersion": 13
|
|
17
17
|
},
|
|
18
18
|
"repository": {
|
|
19
19
|
"type": "git",
|