@cloverleaf/reference-impl 0.13.1 → 0.13.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/.claude-plugin/plugin.json +1 -1
- package/VERSION +1 -1
- package/dist/cli.mjs +58 -45
- package/lib/cli.ts +62 -47
- package/package.json +1 -1
- package/prompts/ui-reviewer.md +14 -4
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cloverleaf",
|
|
3
3
|
"description": "Cloverleaf reference implementation — Claude Code skills for task scaffolding and the Delivery pipeline (implementer, documenter, reviewer, UI reviewer with multi-viewport visual diff, QA, merge, release).",
|
|
4
|
-
"version": "0.13.
|
|
4
|
+
"version": "0.13.2",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Renato D'Arrigo",
|
|
7
7
|
"email": "renato.darrigo@gmail.com"
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.13.
|
|
1
|
+
0.13.2
|
package/dist/cli.mjs
CHANGED
|
@@ -87,57 +87,70 @@ function die(msg, code = 1) {
|
|
|
87
87
|
process.stderr.write(msg + '\n');
|
|
88
88
|
process.exit(code);
|
|
89
89
|
}
|
|
90
|
+
/**
|
|
91
|
+
* The command list, shared by both exits below. `--help` is a request and its
|
|
92
|
+
* answer is this text, so it goes to stdout and exits 0; every error path keeps
|
|
93
|
+
* stderr and exit 2, so a caller piping stdout gets the list or nothing.
|
|
94
|
+
*/
|
|
95
|
+
const USAGE_TEXT = 'Usage: cloverleaf-cli <command> [args...]\n' +
|
|
96
|
+
'Commands:\n' +
|
|
97
|
+
' load-task <repoRoot> <taskId>\n' +
|
|
98
|
+
' infer-project <repoRoot>\n' +
|
|
99
|
+
' next-task-id <repoRoot> [--project=<p>]\n' +
|
|
100
|
+
' advance-status <repoRoot> <taskId> <toStatus> <actor> [gate]\n' +
|
|
101
|
+
' write-feedback <repoRoot> <taskId> <envelopeJsonPath>\n' +
|
|
102
|
+
' latest-feedback <repoRoot> <taskId>\n' +
|
|
103
|
+
' emit-gate-decision <repoRoot> <workItemId> <gate> <decision> <actor> [--comment=<str>]\n' +
|
|
104
|
+
' ui-review-config --repo-root <repoRoot>\n' +
|
|
105
|
+
' read-ui-review-state <repoRoot> <taskId>\n' +
|
|
106
|
+
' write-ui-review-state <repoRoot> <taskId> <baselines_pending>\n' +
|
|
107
|
+
' write-baseline <repoRoot> <taskId> <browser> <slug> <viewport> <sourceFile>\n' +
|
|
108
|
+
' plugin-root\n' +
|
|
109
|
+
' load-rfc <repoRoot> <id>\n' +
|
|
110
|
+
' save-rfc <repoRoot> <filePath>\n' +
|
|
111
|
+
' advance-rfc <repoRoot> <id> <toStatus> <agent|human> [gate]\n' +
|
|
112
|
+
' rfc-tasks <repoRoot> <rfcId> [--pretty]\n' +
|
|
113
|
+
' load-spike <repoRoot> <id>\n' +
|
|
114
|
+
' save-spike <repoRoot> <filePath>\n' +
|
|
115
|
+
' advance-spike <repoRoot> <id> <toStatus> <agent|human>\n' +
|
|
116
|
+
' load-plan <repoRoot> <id>\n' +
|
|
117
|
+
' save-plan <repoRoot> <filePath>\n' +
|
|
118
|
+
' advance-plan <repoRoot> <id> <toStatus> <agent|human> [gate]\n' +
|
|
119
|
+
' materialise-tasks <repoRoot> <planId>\n' +
|
|
120
|
+
' next-work-item-id <repoRoot> <project>\n' +
|
|
121
|
+
' discovery-config --repo-root <repoRoot>\n' +
|
|
122
|
+
' prep-worktree <mainRoot> <worktreePath>\n' +
|
|
123
|
+
' qa-report <runs.json> <out.html>\n' +
|
|
124
|
+
' dag-ready-tasks <repoRoot> <planId> <maxConcurrent>\n' +
|
|
125
|
+
' dag-detect-cycle <repoRoot> <planId>\n' +
|
|
126
|
+
' walk-state-read <repoRoot> <planId>\n' +
|
|
127
|
+
' walk-state-write <repoRoot> <walkStateJsonPath>\n' +
|
|
128
|
+
' walker-default-concurrency [--explain]\n' +
|
|
129
|
+
' check-scope <repoRoot> <taskId> --branch <branchName>\n' +
|
|
130
|
+
' extend-scope <repoRoot> <taskId> --add <file>... --reason <text>\n' +
|
|
131
|
+
' secret-scan <repoRoot> --branch <branch>\n' +
|
|
132
|
+
' classify-security <repoRoot> <taskId> [--branch <branch>]\n' +
|
|
133
|
+
' council-plan <repoRoot> <taskId> [gateKey] [--changed-files=a,b,c]\n' +
|
|
134
|
+
' aggregate-verdicts <membersJson> <rule> [--weighted-threshold=N]\n' +
|
|
135
|
+
' apply-council-verdict <repoRoot> <taskId> <gate> <councilVerdictJson>\n' +
|
|
136
|
+
' chair-context <chairMemberInputsJson>\n' +
|
|
137
|
+
' chair-verdict <chairRawJson> <membersJson>\n' +
|
|
138
|
+
' set-task-field <repoRoot> <taskId> <field> <value>\n' +
|
|
139
|
+
' validate-council <repoRoot>\n';
|
|
90
140
|
function usage(msg) {
|
|
91
141
|
if (msg)
|
|
92
142
|
process.stderr.write(msg + '\n');
|
|
93
|
-
process.stderr.write(
|
|
94
|
-
'Commands:\n' +
|
|
95
|
-
' load-task <repoRoot> <taskId>\n' +
|
|
96
|
-
' infer-project <repoRoot>\n' +
|
|
97
|
-
' next-task-id <repoRoot> [--project=<p>]\n' +
|
|
98
|
-
' advance-status <repoRoot> <taskId> <toStatus> <actor> [gate]\n' +
|
|
99
|
-
' write-feedback <repoRoot> <taskId> <envelopeJsonPath>\n' +
|
|
100
|
-
' latest-feedback <repoRoot> <taskId>\n' +
|
|
101
|
-
' emit-gate-decision <repoRoot> <workItemId> <gate> <decision> <actor> [--comment=<str>]\n' +
|
|
102
|
-
' ui-review-config --repo-root <repoRoot>\n' +
|
|
103
|
-
' read-ui-review-state <repoRoot> <taskId>\n' +
|
|
104
|
-
' write-ui-review-state <repoRoot> <taskId> <baselines_pending>\n' +
|
|
105
|
-
' write-baseline <repoRoot> <taskId> <browser> <slug> <viewport> <sourceFile>\n' +
|
|
106
|
-
' plugin-root\n' +
|
|
107
|
-
' load-rfc <repoRoot> <id>\n' +
|
|
108
|
-
' save-rfc <repoRoot> <filePath>\n' +
|
|
109
|
-
' advance-rfc <repoRoot> <id> <toStatus> <agent|human> [gate]\n' +
|
|
110
|
-
' rfc-tasks <repoRoot> <rfcId> [--pretty]\n' +
|
|
111
|
-
' load-spike <repoRoot> <id>\n' +
|
|
112
|
-
' save-spike <repoRoot> <filePath>\n' +
|
|
113
|
-
' advance-spike <repoRoot> <id> <toStatus> <agent|human>\n' +
|
|
114
|
-
' load-plan <repoRoot> <id>\n' +
|
|
115
|
-
' save-plan <repoRoot> <filePath>\n' +
|
|
116
|
-
' advance-plan <repoRoot> <id> <toStatus> <agent|human> [gate]\n' +
|
|
117
|
-
' materialise-tasks <repoRoot> <planId>\n' +
|
|
118
|
-
' next-work-item-id <repoRoot> <project>\n' +
|
|
119
|
-
' discovery-config --repo-root <repoRoot>\n' +
|
|
120
|
-
' prep-worktree <mainRoot> <worktreePath>\n' +
|
|
121
|
-
' qa-report <runs.json> <out.html>\n' +
|
|
122
|
-
' dag-ready-tasks <repoRoot> <planId> <maxConcurrent>\n' +
|
|
123
|
-
' dag-detect-cycle <repoRoot> <planId>\n' +
|
|
124
|
-
' walk-state-read <repoRoot> <planId>\n' +
|
|
125
|
-
' walk-state-write <repoRoot> <walkStateJsonPath>\n' +
|
|
126
|
-
' walker-default-concurrency [--explain]\n' +
|
|
127
|
-
' check-scope <repoRoot> <taskId> --branch <branchName>\n' +
|
|
128
|
-
' extend-scope <repoRoot> <taskId> --add <file>... --reason <text>\n' +
|
|
129
|
-
' secret-scan <repoRoot> --branch <branch>\n' +
|
|
130
|
-
' classify-security <repoRoot> <taskId> [--branch <branch>]\n' +
|
|
131
|
-
' council-plan <repoRoot> <taskId> [gateKey] [--changed-files=a,b,c]\n' +
|
|
132
|
-
' aggregate-verdicts <membersJson> <rule> [--weighted-threshold=N]\n' +
|
|
133
|
-
' apply-council-verdict <repoRoot> <taskId> <gate> <councilVerdictJson>\n' +
|
|
134
|
-
' chair-context <chairMemberInputsJson>\n' +
|
|
135
|
-
' chair-verdict <chairRawJson> <membersJson>\n' +
|
|
136
|
-
' set-task-field <repoRoot> <taskId> <field> <value>\n' +
|
|
137
|
-
' validate-council <repoRoot>\n');
|
|
143
|
+
process.stderr.write(USAGE_TEXT);
|
|
138
144
|
process.exit(2);
|
|
139
145
|
}
|
|
146
|
+
function help() {
|
|
147
|
+
process.stdout.write(USAGE_TEXT);
|
|
148
|
+
process.exit(0);
|
|
149
|
+
}
|
|
140
150
|
const [, , command, ...rest] = process.argv;
|
|
151
|
+
if (command === '--help' || command === '-h') {
|
|
152
|
+
help();
|
|
153
|
+
}
|
|
141
154
|
if (!command) {
|
|
142
155
|
usage('Error: no command given');
|
|
143
156
|
}
|
package/lib/cli.ts
CHANGED
|
@@ -92,60 +92,75 @@ function die(msg: string, code = 1): never {
|
|
|
92
92
|
process.exit(code);
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
+
/**
|
|
96
|
+
* The command list, shared by both exits below. `--help` is a request and its
|
|
97
|
+
* answer is this text, so it goes to stdout and exits 0; every error path keeps
|
|
98
|
+
* stderr and exit 2, so a caller piping stdout gets the list or nothing.
|
|
99
|
+
*/
|
|
100
|
+
const USAGE_TEXT =
|
|
101
|
+
'Usage: cloverleaf-cli <command> [args...]\n' +
|
|
102
|
+
'Commands:\n' +
|
|
103
|
+
' load-task <repoRoot> <taskId>\n' +
|
|
104
|
+
' infer-project <repoRoot>\n' +
|
|
105
|
+
' next-task-id <repoRoot> [--project=<p>]\n' +
|
|
106
|
+
' advance-status <repoRoot> <taskId> <toStatus> <actor> [gate]\n' +
|
|
107
|
+
' write-feedback <repoRoot> <taskId> <envelopeJsonPath>\n' +
|
|
108
|
+
' latest-feedback <repoRoot> <taskId>\n' +
|
|
109
|
+
' emit-gate-decision <repoRoot> <workItemId> <gate> <decision> <actor> [--comment=<str>]\n' +
|
|
110
|
+
' ui-review-config --repo-root <repoRoot>\n' +
|
|
111
|
+
' read-ui-review-state <repoRoot> <taskId>\n' +
|
|
112
|
+
' write-ui-review-state <repoRoot> <taskId> <baselines_pending>\n' +
|
|
113
|
+
' write-baseline <repoRoot> <taskId> <browser> <slug> <viewport> <sourceFile>\n' +
|
|
114
|
+
' plugin-root\n' +
|
|
115
|
+
' load-rfc <repoRoot> <id>\n' +
|
|
116
|
+
' save-rfc <repoRoot> <filePath>\n' +
|
|
117
|
+
' advance-rfc <repoRoot> <id> <toStatus> <agent|human> [gate]\n' +
|
|
118
|
+
' rfc-tasks <repoRoot> <rfcId> [--pretty]\n' +
|
|
119
|
+
' load-spike <repoRoot> <id>\n' +
|
|
120
|
+
' save-spike <repoRoot> <filePath>\n' +
|
|
121
|
+
' advance-spike <repoRoot> <id> <toStatus> <agent|human>\n' +
|
|
122
|
+
' load-plan <repoRoot> <id>\n' +
|
|
123
|
+
' save-plan <repoRoot> <filePath>\n' +
|
|
124
|
+
' advance-plan <repoRoot> <id> <toStatus> <agent|human> [gate]\n' +
|
|
125
|
+
' materialise-tasks <repoRoot> <planId>\n' +
|
|
126
|
+
' next-work-item-id <repoRoot> <project>\n' +
|
|
127
|
+
' discovery-config --repo-root <repoRoot>\n' +
|
|
128
|
+
' prep-worktree <mainRoot> <worktreePath>\n' +
|
|
129
|
+
' qa-report <runs.json> <out.html>\n' +
|
|
130
|
+
' dag-ready-tasks <repoRoot> <planId> <maxConcurrent>\n' +
|
|
131
|
+
' dag-detect-cycle <repoRoot> <planId>\n' +
|
|
132
|
+
' walk-state-read <repoRoot> <planId>\n' +
|
|
133
|
+
' walk-state-write <repoRoot> <walkStateJsonPath>\n' +
|
|
134
|
+
' walker-default-concurrency [--explain]\n' +
|
|
135
|
+
' check-scope <repoRoot> <taskId> --branch <branchName>\n' +
|
|
136
|
+
' extend-scope <repoRoot> <taskId> --add <file>... --reason <text>\n' +
|
|
137
|
+
' secret-scan <repoRoot> --branch <branch>\n' +
|
|
138
|
+
' classify-security <repoRoot> <taskId> [--branch <branch>]\n' +
|
|
139
|
+
' council-plan <repoRoot> <taskId> [gateKey] [--changed-files=a,b,c]\n' +
|
|
140
|
+
' aggregate-verdicts <membersJson> <rule> [--weighted-threshold=N]\n' +
|
|
141
|
+
' apply-council-verdict <repoRoot> <taskId> <gate> <councilVerdictJson>\n' +
|
|
142
|
+
' chair-context <chairMemberInputsJson>\n' +
|
|
143
|
+
' chair-verdict <chairRawJson> <membersJson>\n' +
|
|
144
|
+
' set-task-field <repoRoot> <taskId> <field> <value>\n' +
|
|
145
|
+
' validate-council <repoRoot>\n';
|
|
146
|
+
|
|
95
147
|
function usage(msg?: string): never {
|
|
96
148
|
if (msg) process.stderr.write(msg + '\n');
|
|
97
|
-
process.stderr.write(
|
|
98
|
-
'Usage: cloverleaf-cli <command> [args...]\n' +
|
|
99
|
-
'Commands:\n' +
|
|
100
|
-
' load-task <repoRoot> <taskId>\n' +
|
|
101
|
-
' infer-project <repoRoot>\n' +
|
|
102
|
-
' next-task-id <repoRoot> [--project=<p>]\n' +
|
|
103
|
-
' advance-status <repoRoot> <taskId> <toStatus> <actor> [gate]\n' +
|
|
104
|
-
' write-feedback <repoRoot> <taskId> <envelopeJsonPath>\n' +
|
|
105
|
-
' latest-feedback <repoRoot> <taskId>\n' +
|
|
106
|
-
' emit-gate-decision <repoRoot> <workItemId> <gate> <decision> <actor> [--comment=<str>]\n' +
|
|
107
|
-
' ui-review-config --repo-root <repoRoot>\n' +
|
|
108
|
-
' read-ui-review-state <repoRoot> <taskId>\n' +
|
|
109
|
-
' write-ui-review-state <repoRoot> <taskId> <baselines_pending>\n' +
|
|
110
|
-
' write-baseline <repoRoot> <taskId> <browser> <slug> <viewport> <sourceFile>\n' +
|
|
111
|
-
' plugin-root\n' +
|
|
112
|
-
' load-rfc <repoRoot> <id>\n' +
|
|
113
|
-
' save-rfc <repoRoot> <filePath>\n' +
|
|
114
|
-
' advance-rfc <repoRoot> <id> <toStatus> <agent|human> [gate]\n' +
|
|
115
|
-
' rfc-tasks <repoRoot> <rfcId> [--pretty]\n' +
|
|
116
|
-
' load-spike <repoRoot> <id>\n' +
|
|
117
|
-
' save-spike <repoRoot> <filePath>\n' +
|
|
118
|
-
' advance-spike <repoRoot> <id> <toStatus> <agent|human>\n' +
|
|
119
|
-
' load-plan <repoRoot> <id>\n' +
|
|
120
|
-
' save-plan <repoRoot> <filePath>\n' +
|
|
121
|
-
' advance-plan <repoRoot> <id> <toStatus> <agent|human> [gate]\n' +
|
|
122
|
-
' materialise-tasks <repoRoot> <planId>\n' +
|
|
123
|
-
' next-work-item-id <repoRoot> <project>\n' +
|
|
124
|
-
' discovery-config --repo-root <repoRoot>\n' +
|
|
125
|
-
' prep-worktree <mainRoot> <worktreePath>\n' +
|
|
126
|
-
' qa-report <runs.json> <out.html>\n' +
|
|
127
|
-
' dag-ready-tasks <repoRoot> <planId> <maxConcurrent>\n' +
|
|
128
|
-
' dag-detect-cycle <repoRoot> <planId>\n' +
|
|
129
|
-
' walk-state-read <repoRoot> <planId>\n' +
|
|
130
|
-
' walk-state-write <repoRoot> <walkStateJsonPath>\n' +
|
|
131
|
-
' walker-default-concurrency [--explain]\n' +
|
|
132
|
-
' check-scope <repoRoot> <taskId> --branch <branchName>\n' +
|
|
133
|
-
' extend-scope <repoRoot> <taskId> --add <file>... --reason <text>\n' +
|
|
134
|
-
' secret-scan <repoRoot> --branch <branch>\n' +
|
|
135
|
-
' classify-security <repoRoot> <taskId> [--branch <branch>]\n' +
|
|
136
|
-
' council-plan <repoRoot> <taskId> [gateKey] [--changed-files=a,b,c]\n' +
|
|
137
|
-
' aggregate-verdicts <membersJson> <rule> [--weighted-threshold=N]\n' +
|
|
138
|
-
' apply-council-verdict <repoRoot> <taskId> <gate> <councilVerdictJson>\n' +
|
|
139
|
-
' chair-context <chairMemberInputsJson>\n' +
|
|
140
|
-
' chair-verdict <chairRawJson> <membersJson>\n' +
|
|
141
|
-
' set-task-field <repoRoot> <taskId> <field> <value>\n' +
|
|
142
|
-
' validate-council <repoRoot>\n'
|
|
143
|
-
);
|
|
149
|
+
process.stderr.write(USAGE_TEXT);
|
|
144
150
|
process.exit(2);
|
|
145
151
|
}
|
|
146
152
|
|
|
153
|
+
function help(): never {
|
|
154
|
+
process.stdout.write(USAGE_TEXT);
|
|
155
|
+
process.exit(0);
|
|
156
|
+
}
|
|
157
|
+
|
|
147
158
|
const [, , command, ...rest] = process.argv;
|
|
148
159
|
|
|
160
|
+
if (command === '--help' || command === '-h') {
|
|
161
|
+
help();
|
|
162
|
+
}
|
|
163
|
+
|
|
149
164
|
if (!command) {
|
|
150
165
|
usage('Error: no command given');
|
|
151
166
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloverleaf/reference-impl",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.2",
|
|
4
4
|
"description": "Reference implementation of the Cloverleaf methodology as Claude Code skills. Implements the Tight Loop (Implementer + Reviewer).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
package/prompts/ui-reviewer.md
CHANGED
|
@@ -145,10 +145,18 @@ Do not attempt to launch a missing engine — fail fast with `verdict: "escalate
|
|
|
145
145
|
# to see why and return verdict `escalate` — do NOT continue with a broken install.
|
|
146
146
|
npx astro preferences disable devToolbar > /tmp/ui-devtoolbar.log 2>&1; echo "EXIT=$?"
|
|
147
147
|
# Non-zero EXIT means this UI directory is not an Astro project — see below.
|
|
148
|
-
npm run dev -- --port={{preview_port}} &
|
|
148
|
+
setsid npm run dev -- --port={{preview_port}} < /dev/null > /tmp/ui-dev-server.log 2>&1 &
|
|
149
149
|
SERVER_PID=$!
|
|
150
150
|
```
|
|
151
151
|
|
|
152
|
+
`setsid` is load-bearing, not decoration. `$SERVER_PID` is **npm's** PID and
|
|
153
|
+
npm runs the dev server as a child, so `kill $SERVER_PID` reaps npm and leaves
|
|
154
|
+
the server holding the port, reparented to init. `setsid` puts the whole tree
|
|
155
|
+
in its own process group whose id equals `$SERVER_PID`, which is what lets
|
|
156
|
+
step 13 kill the group. Keep the two together: without `setsid` the group kill
|
|
157
|
+
silently matches nothing, because the job would otherwise sit in your shell's
|
|
158
|
+
group and `$SERVER_PID` would not be a group id at all.
|
|
159
|
+
|
|
152
160
|
`astro preferences disable devToolbar` is **project-scoped** by default: it writes into `$WT/site/.astro/`, which step 13 deletes along with the worktree, so it turns the toolbar off for this capture alone and nothing outside this run changes. Never pass `--global` — that writes to the operator's home directory and silently changes every other Astro project on the machine. Disable it before backgrounding the server, not after: Astro decides whether to inject the toolbar when the dev server boots.
|
|
153
161
|
|
|
154
162
|
If that command exits non-zero, this UI directory is **not an Astro project**. Disable that toolchain's own dev overlay instead. If it has none you can turn off, emit an `info` finding recording that the baseline may contain dev-only UI rather than capturing a contaminated one silently.
|
|
@@ -159,7 +167,7 @@ Do not attempt to launch a missing engine — fail fast with `verdict: "escalate
|
|
|
159
167
|
node "$DRIVER" > /tmp/ui-driver.log 2>&1; echo "EXIT=$?"
|
|
160
168
|
```
|
|
161
169
|
|
|
162
|
-
4. Wait up to 30s for `http://localhost:{{preview_port}}/` to respond 200. If the server fails to start in 30s, run teardown (step 13) — `kill
|
|
170
|
+
4. Wait up to 30s for `http://localhost:{{preview_port}}/` to respond 200. If the server fails to start in 30s, read `/tmp/ui-dev-server.log` to see why, run teardown (step 13) — `kill -- -$SERVER_PID`, never a command-line pattern — and return verdict `escalate`.
|
|
163
171
|
|
|
164
172
|
5. Determine the site base path:
|
|
165
173
|
1. Check `{{repo_root}}/.cloverleaf/config/astro-base.json`. Expected shape: `{ "base": "<path>" }`. If present, use the `base` field verbatim and skip to step 6. (Consumer override — checked before parsing astro config.)
|
|
@@ -252,13 +260,15 @@ Do not attempt to launch a missing engine — fail fast with `verdict: "escalate
|
|
|
252
260
|
|
|
253
261
|
13. Teardown:
|
|
254
262
|
```bash
|
|
255
|
-
kill
|
|
263
|
+
kill -- -$SERVER_PID 2>/dev/null || true
|
|
256
264
|
rm -f "$DRIVER"
|
|
257
265
|
cd {{repo_root}}
|
|
258
266
|
git worktree remove --force "$WT"
|
|
259
267
|
```
|
|
260
268
|
|
|
261
|
-
Kill the
|
|
269
|
+
Kill the process **group** you created in step 3, never by command-line pattern. The leading `-` in `-$SERVER_PID` is what makes this a group kill; `kill $SERVER_PID` without it reaps only npm and leaves the dev server orphaned on the port, which the next run then fails to bind.
|
|
270
|
+
|
|
271
|
+
`pkill -f "astro dev"` also matches the command line of the shell running it, so the shell kills itself: exit 144, and every command after it in the same compound statement — the `rm -f` and `git worktree remove` above — silently never runs.
|
|
262
272
|
|
|
263
273
|
Delete every driver script you wrote, wherever you put it. `git worktree remove --force` only clears what lives inside `$WT`; a driver written anywhere else survives the run and leaks into the next one.
|
|
264
274
|
|