@jaybeeuu/agent-cortex 1.42.0 → 1.43.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/.agents/skills/style-versioning/SKILL.md +73 -2
- package/.github/workflows/ci.yml +47 -27
- package/AGENTS.md +23 -2
- package/CHANGELOG.md +166 -0
- package/README.md +125 -73
- package/agents/README.md +17 -4
- package/bin/agent-cortex.mjs +48 -30
- package/bin/installers/pi.mjs +885 -237
- package/docs/claude-hooks.md +5 -4
- package/docs/commit-signing.md +231 -0
- package/docs/ideas/enable-signed-commit-enforcement-changesets-bot-commits-must-be-signed.md +10 -1
- package/docs/ideas/extension-manifests.md +5 -0
- package/docs/research/disable-model-invocation.md +103 -0
- package/docs/research/skill-design-research.md +2 -2
- package/docs/skills/skill-anatomy.md +5 -1
- package/docs/technical-direction/harness-config.md +25 -1
- package/docs/technical-direction/migration-execution-labels.md +17 -9
- package/extensions/agent-modes/discover.test.ts +19 -0
- package/extensions/agent-modes/discover.ts +25 -3
- package/extensions/session-start/README.md +38 -0
- package/extensions/session-start/index.test.ts +122 -0
- package/extensions/session-start/index.ts +27 -0
- package/extensions/session-start/package.json +19 -0
- package/extensions/session-start/tsconfig.json +12 -0
- package/extensions/skill-stats/pnpm-lock.yaml +329 -0
- package/extensions/subagent/README.md +1 -1
- package/extensions/subagent/agents.test.ts +176 -0
- package/extensions/subagent/agents.ts +102 -11
- package/extensions/subagent/index.ts +2 -1
- package/extensions/subagent/package.json +1 -1
- package/lib/cli.mjs +35 -9
- package/package.json +6 -5
- package/pi/settings.json +1 -7
- package/plugin.json +1 -1
- package/pnpm-workspace.yaml +6 -0
- package/scripts/check-changeset.mjs +42 -0
- package/scripts/lib/release-gates.mjs +61 -0
- package/scripts/lib/verify-worker-tree.mjs +107 -0
- package/scripts/resolve-publish-command.mjs +17 -0
- package/skills/planning/create-task/scripts/pnpm-lock.yaml +342 -0
- package/skills/productivity/validate-agent-dir/scripts/pnpm-lock.yaml +329 -0
- package/skills/workflow/git-workflow/SKILL.md +35 -1
- package/skills/workflow/ralph/REFERENCE.md +17 -1
- package/skills/workflow/ralph/SKILL.md +4 -1
- package/skills/workflow/run-pipeline-stage/SKILL.md +11 -4
- package/skills/workflow/run-pipeline-stage/playbooks/code.md +2 -0
- package/skills/workflow/run-pipeline-stage/playbooks/document.md +3 -0
- package/skills/workflow/run-pipeline-stage/playbooks/fix.md +2 -0
- package/skills/workflow/run-pipeline-stage/prompts/stage-runner.md +11 -0
- package/skills/workflow/run-pipeline-stage/scripts/pnpm-lock.yaml +342 -0
- package/test/cli.test.mjs +26 -29
- package/test/helpers/fake-pi.mjs +132 -0
- package/test/pi-installer.test.mjs +345 -286
- package/test/pi-register.test.mjs +342 -0
- package/test/pi-sweep.test.mjs +619 -0
- package/test/release-gates.test.mjs +190 -0
- package/test/verify-worker-tree.test.mjs +120 -0
- package/token-map.README.md +7 -6
- package/token-map.json +6 -5
- package/extensions/session-start.ts +0 -44
- package/lib/pi-packages.mjs +0 -169
- package/test/pi-packages.test.mjs +0 -295
package/README.md
CHANGED
|
@@ -105,6 +105,13 @@ does its own checkout and `pnpm install` rather than sharing build artifacts fro
|
|
|
105
105
|
the `setup` job — pnpm workspace symlinks don't survive artifact upload/download,
|
|
106
106
|
so artifact sharing would break the workspace resolution that the build depends on.
|
|
107
107
|
|
|
108
|
+
The root `pnpm-workspace.yaml` carries a strict `allowBuilds` map: on pnpm 12 (what CI
|
|
109
|
+
pins) any dependency with install or build scripts that is not named there aborts
|
|
110
|
+
`pnpm install` with `ERR_PNPM_IGNORED_BUILDS`. pnpm 10 only warns, so a newly pulled-in
|
|
111
|
+
native dependency surfaces as a red `Setup` job before it surfaces locally. Adding one
|
|
112
|
+
therefore means deciding its build policy in that map — `false` unless it genuinely
|
|
113
|
+
needs to compile, with a comment saying why.
|
|
114
|
+
|
|
108
115
|
The repo commits no generated `claude/` plugin output, so the `claude-plugin-check` job
|
|
109
116
|
validates the Claude plugin **materialiser** instead of diffing a committed mirror:
|
|
110
117
|
it runs `node bin/agent-cortex.mjs install claude --output <tmp dir>` and checks the
|
|
@@ -114,10 +121,14 @@ and no symlinks leak into the copied tree. The Copilot drift check
|
|
|
114
121
|
(`pnpm build:copilot` + `git diff --exit-code -- 'agents/*.agent.md'`) still guards the
|
|
115
122
|
committed flat agent files.
|
|
116
123
|
|
|
117
|
-
A separate `changeset-check` job runs
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
124
|
+
A separate `changeset-check` job runs on every pull request and fails it unless the
|
|
125
|
+
PR adds a `.changeset/*.md` file. **Publishing is the default and `no-publish` is the
|
|
126
|
+
only opt-out**, so shipping nothing has to be a deliberate, reviewable act rather
|
|
127
|
+
than an accident. Two exemptions: the changesets bot's Version Packages PR (it
|
|
128
|
+
consumes changesets and adds none) and PRs labelled `no-publish`. The `no-publish`
|
|
129
|
+
label must exist in the repository's label settings — a one-off repo-settings step
|
|
130
|
+
that no PR can ship. The `style-versioning` skill documents the format of a
|
|
131
|
+
changeset.
|
|
121
132
|
|
|
122
133
|
On pushes to `main`, a `release` job (gated on `lint`, `test`, and
|
|
123
134
|
`claude-plugin-check`) runs changesets to open a `chore: version packages` PR when
|
|
@@ -125,90 +136,131 @@ changesets are pending, then publishes to npm once it lands. The version step ru
|
|
|
125
136
|
`pnpm version-packages` — bumping `package.json`, syncing `plugin.json`, and
|
|
126
137
|
regenerating the committed Copilot agent files (the Claude plugin is materialised at
|
|
127
138
|
install time with the package version, so it has no committed output to regenerate)
|
|
128
|
-
so the drift gates stay green; the
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
139
|
+
so the drift gates stay green; the publish step runs `pnpm publish-package` (pack +
|
|
140
|
+
provenance publish), or
|
|
141
|
+
`pnpm publish-noop` when the merged PR carried `no-publish`. Labels are invisible on
|
|
142
|
+
a push event, so the job resolves the PR merged for the pushed commit through the
|
|
143
|
+
GitHub API and reads its labels — no extra secret or scope, since the job already
|
|
144
|
+
holds `GITHUB_TOKEN` and `pull-requests: write`. A direct push with no merged PR
|
|
145
|
+
resolves to no labels and therefore publishes, so attempting to publish an
|
|
146
|
+
already-published version still fails loudly; there is deliberately no idempotency
|
|
147
|
+
guard. Publishing authenticates via npm Trusted Publishing (OIDC) — no npm token or
|
|
148
|
+
GitHub secret is needed, only the one-time npm-side setup on npmjs.com (package →
|
|
132
149
|
Access → Trusted Publishing for the `jaybeeuu/agent-cortex` repo). Releases
|
|
133
150
|
are sourced entirely from `main`.
|
|
134
151
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
lacks, every value already present in the live file wins, and keys the template
|
|
147
|
-
does not know about are preserved — so personal config (and anything pi itself
|
|
148
|
-
wrote) survives re-install. The one key the CLI owns outright is `packages`: it
|
|
149
|
-
is rebuilt from the template with the repo path entry resolved against the live
|
|
150
|
-
settings file.
|
|
151
|
-
|
|
152
|
-
Two consequences follow from that precedence:
|
|
153
|
-
|
|
154
|
-
- **Template changes to non-`packages` keys never reach an existing install.**
|
|
155
|
-
Because the live value always wins, editing a default in `pi/settings.json`
|
|
156
|
-
only affects a fresh install (or a manually deleted live file) — edit the live
|
|
157
|
-
file to adopt a new default.
|
|
158
|
-
- **`pi install npm:<pkg>` entries do not survive re-install.** `packages` is
|
|
159
|
-
rebuilt from the template, so any `npm:` package pi added to the live file is
|
|
160
|
-
dropped on the next `agent-cortex install pi`. Declare long-lived packages in
|
|
161
|
-
the repo template instead.
|
|
162
|
-
|
|
163
|
-
An unparseable live `settings.json` is warned about and overwritten.
|
|
164
|
-
`keybindings.json` follows a checksum rule: written from the template, refreshed
|
|
165
|
-
when the template changes, and left untouched once you edit it by hand (delete
|
|
166
|
-
it to re-adopt the template).
|
|
167
|
-
|
|
168
|
-
### Pi package dependencies
|
|
169
|
-
|
|
170
|
-
These packages are declared in the package manifest (`package.json` → `pi.packages`) and
|
|
171
|
-
provisioned by `agent-cortex install pi`, so a clean `~/.pi` gets the tools they provide
|
|
172
|
-
without a manual `pi install`. They are also declared in the template `pi/settings.json`,
|
|
173
|
-
so the materialised `settings.json` registers them with pi.
|
|
174
|
-
|
|
175
|
-
| Package | Version | Purpose |
|
|
176
|
-
|---|---|---|
|
|
177
|
-
| [`pi-web-access`](https://www.npmjs.com/package/pi-web-access) | 0.10.7 | Web search, URL fetching, GitHub repo access, PDF/YouTube/video analysis (`fetch_content`) |
|
|
178
|
-
| [`pi-questions`](https://www.npmjs.com/package/pi-questions) | latest | Structured interactive questions (`ask_questions`) |
|
|
179
|
-
|
|
180
|
-
Pass `--no-provision` to skip the package install step (e.g. offline machines); a
|
|
181
|
-
missing `pi` CLI or failed install warns and leaves the rest of the install intact.
|
|
152
|
+
Merge the version PR whenever its plan and diff look right, but approve its CI
|
|
153
|
+
first: `changesets/action` pushes that branch with `GITHUB_TOKEN`, so GitHub parks
|
|
154
|
+
the CI run the push creates behind **“Approve workflows to run”** — a run with no
|
|
155
|
+
jobs, which reports nothing, so the merge box shows `BLOCKED` with an empty check
|
|
156
|
+
list even though no gate has run or failed. Approving it from the merge box (or
|
|
157
|
+
`gh api -X POST repos/{owner}/{repo}/actions/runs/<run-id>/approve`) starts the five
|
|
158
|
+
required checks. Do it after the last merge to `main`, not once at the start of a
|
|
159
|
+
release: every subsequent merge force-updates the version branch, and a new head
|
|
160
|
+
brings a new parked run. This stays manual by design — letting the bot's push
|
|
161
|
+
trigger CI on its own means a personal access token or GitHub App token, a
|
|
162
|
+
long-lived credential in repository secrets to save one click per release.
|
|
182
163
|
|
|
183
|
-
|
|
184
|
-
(replaces the former `pi-notify` dependency). It sends an OSC desktop
|
|
185
|
-
notification on multi-turn tasks, labelled with the tmux session:window.pane
|
|
186
|
-
if available, or the project directory name otherwise.
|
|
164
|
+
## Installation
|
|
187
165
|
|
|
188
|
-
### Pi
|
|
166
|
+
### Pi package staging (`agent-cortex install pi`)
|
|
189
167
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
168
|
+
`agent-cortex install pi` materialises agent-cortex as a self-contained pi
|
|
169
|
+
package in `~/.agent-cortex/pi` — the pi analogue of `~/.agent-cortex/claude` —
|
|
170
|
+
and registers that directory with pi through pi's own `pi install`. Nothing is
|
|
171
|
+
generated into pi's own directories (`~/.pi/agent/…`): pi owns its settings
|
|
172
|
+
file, so the installer only reads it to decide the plan and lets the `pi` CLI
|
|
173
|
+
write it. Once the staged package is registered, the artifacts an older
|
|
174
|
+
installer rendered there are swept (see below).
|
|
194
175
|
|
|
195
176
|
```sh
|
|
196
|
-
agent-cortex install pi
|
|
197
|
-
# → ~/.pi/agent/agents/<name>.agent.md (ralph, plan, ralph-plan, strategy)
|
|
198
|
-
# → ~/.pi/agent/skills/ (token-substituted skill tree)
|
|
199
|
-
# → ~/.pi/agent/settings.json + keybindings.json (merged from the repo templates)
|
|
177
|
+
agent-cortex install pi # stage ~/.agent-cortex/pi + register it with pi
|
|
200
178
|
```
|
|
201
179
|
|
|
202
|
-
|
|
180
|
+
Registration is idempotent by state: the staged path is registered once (a
|
|
181
|
+
re-run is a true no-op — a local package source is read from disk on every pi
|
|
182
|
+
start, so nothing needs refreshing), and a source pointing at a raw agent-cortex
|
|
183
|
+
checkout is removed first, so exactly one agent-cortex source stays configured.
|
|
184
|
+
The installer decides that plan by reading pi's settings at
|
|
185
|
+
`~/.pi/agent/settings.json`, so a pi agent dir relocated with
|
|
186
|
+
`PI_CODING_AGENT_DIR` is not inspected — a superseded raw-repo source configured
|
|
187
|
+
there has to be removed by hand with `pi remove <path>`.
|
|
188
|
+
|
|
189
|
+
Under the old model the same installer rendered agents and skills into
|
|
190
|
+
`~/.pi/agent/{agents,skills}`. Those copies are a second source of the same
|
|
191
|
+
resource — the skill-collision diagnostics the staged model exists to remove —
|
|
192
|
+
so once registration succeeds the installer removes the ones it can attribute to
|
|
193
|
+
agent-cortex: `agents/<name>.agent.md` and `skills/<group>/<name>` for agents and
|
|
194
|
+
skills this package ships or once shipped, and a `.agent-cortex-install.json`
|
|
195
|
+
path record left there listing those copies. It is a one-time migration:
|
|
196
|
+
attribution is by name, which cannot tell a rendered copy from a file you wrote
|
|
197
|
+
under the same name, so once it has finished it records that in
|
|
198
|
+
`~/.agent-cortex/legacy-sweep.json` — keyed by the pi user directory, never
|
|
199
|
+
inside it — and never removes anything there again. A completed migration
|
|
200
|
+
leaves `~/.pi/agent` with no agent-cortex file in it. Only a migration that
|
|
201
|
+
removed every copy it attributed, from directories it could read, is recorded:
|
|
202
|
+
a copy that could not be deleted (permissions, `EBUSY`) or a directory that
|
|
203
|
+
could not be listed is warned about and retried on the next install, and a
|
|
204
|
+
record that cannot be written is a warning, not a failed install. Anything
|
|
205
|
+
whose name this package does not ship is left alone, and
|
|
206
|
+
`~/.pi/agent/extensions` is never touched at all — no
|
|
207
|
+
installer has written there, so its contents are yours. `--output <dir>` and
|
|
208
|
+
`--dry-run` sweep nothing.
|
|
209
|
+
The sweep follows the settings file the installer reads (`~/.pi/agent`): a pi
|
|
210
|
+
agent dir relocated with `PI_CODING_AGENT_DIR` keeps its copies.
|
|
211
|
+
|
|
212
|
+
The staged package contains:
|
|
213
|
+
|
|
214
|
+
| Path | Contents |
|
|
215
|
+
| --- | --- |
|
|
216
|
+
| `package.json` | pi manifest — `keywords: ["pi-package"]`, `pi.extensions` + `pi.skills`; the version tracks the package |
|
|
217
|
+
| `agents/` | composed flat agents (ralph, plan, ralph-plan, strategy) with `{{TOOL:...}}` / `{{PATH:...}}` substituted — the shipped `agent-modes` extension resolves them from the package root |
|
|
218
|
+
| `skills/` | token-substituted skill copies (real files, never symlinks) |
|
|
219
|
+
| `extensions/` | copy of the repo `extensions/` tree |
|
|
220
|
+
| `token-map.json` | the token map `agent-modes` reads from the package root |
|
|
221
|
+
| `node_modules/` | the required third-party pi packages and their transitive closure |
|
|
222
|
+
|
|
223
|
+
The third-party packages supply the tools the agents need — `pi-questions`
|
|
224
|
+
(`ask_questions`) and `pi-web-access` (`fetch_content`). They are ordinary
|
|
225
|
+
runtime dependencies of this package and travel inside the staged tree, so pi
|
|
226
|
+
loads them from a local directory without any npm resolution of its own. An
|
|
227
|
+
unresolvable dependency fails the install rather than staging a package that
|
|
228
|
+
cannot provide those tools. Re-staging clears the children the installer owns, so
|
|
229
|
+
a re-run never accumulates stale copies.
|
|
203
230
|
|
|
204
231
|
| Flag | Meaning |
|
|
205
232
|
| --- | --- |
|
|
206
|
-
| `--dry-run` | Show what would be
|
|
207
|
-
| `--output <dir>` |
|
|
233
|
+
| `--dry-run` | Show what would be staged and registered without writing or spawning anything |
|
|
234
|
+
| `--output <dir>` | Stage into `<dir>` only — no registration (tests/CI) |
|
|
208
235
|
| `--plugin-root <dir>` | Override the plugin root used for `{{PATH:...}}` tokens (default: token-map.json's pi value — use it for checkout or symlinked installs) |
|
|
209
|
-
| `--
|
|
236
|
+
| `--require-register` | Fail the install when the `pi` CLI cannot drive registration; by default a missing CLI warns and prints the commands to run |
|
|
237
|
+
|
|
238
|
+
If the `pi` CLI is not on `PATH`, the staged package is correct but unregistered:
|
|
239
|
+
the installer warns and prints the exact commands (`pi install
|
|
240
|
+
~/.agent-cortex/pi`, plus a `pi remove` for any superseded raw-repo source).
|
|
241
|
+
|
|
242
|
+
A superseded raw-repo source that `pi remove` cannot drop fails the install
|
|
243
|
+
(non-zero, naming the source still configured): pi would load both sources, so
|
|
244
|
+
reporting success would leave the two-source state registration exists to
|
|
245
|
+
prevent. Nothing is installed on that path — remove the source by hand and re-run.
|
|
246
|
+
|
|
247
|
+
Re-run `agent-cortex install pi` after every `git pull` to refresh the staged
|
|
248
|
+
package; registration is already in place, so that re-run changes nothing in
|
|
249
|
+
pi's settings. Verify with `pi list` and `pi config` (restart pi to apply).
|
|
250
|
+
|
|
251
|
+
### Desktop notifications
|
|
252
|
+
|
|
253
|
+
Desktop notifications are handled by the local `extensions/notify/` extension
|
|
254
|
+
(replaces the former `pi-notify` dependency). It sends an OSC desktop
|
|
255
|
+
notification on multi-turn tasks, labelled with the tmux session:window.pane
|
|
256
|
+
if available, or the project directory name otherwise.
|
|
257
|
+
|
|
258
|
+
### Pi config files
|
|
210
259
|
|
|
211
|
-
|
|
260
|
+
`pi/settings.json` and `pi/keybindings.json` are committed **reference
|
|
261
|
+
templates** for your own pi configuration. They are not installer output: pi's
|
|
262
|
+
`settings.json` and `keybindings.json` are pi's own user config, so
|
|
263
|
+
`agent-cortex install pi` leaves them alone.
|
|
212
264
|
|
|
213
265
|
### Copilot plugin (separate)
|
|
214
266
|
|
package/agents/README.md
CHANGED
|
@@ -153,6 +153,16 @@ composes agent prompts on the fly from `agent.md` + `pi/frontmatter.json`, subst
|
|
|
153
153
|
tokens against `token-map.json`'s pi column with no install step. See
|
|
154
154
|
`extensions/agent-modes/README.md`.
|
|
155
155
|
|
|
156
|
+
### Rendered tool lists are comma-separated
|
|
157
|
+
|
|
158
|
+
pi's `parseFrontmatter` returns the `tools` scalar as one string, and its consumers split
|
|
159
|
+
that string on commas — the subagent extension forwards it straight to `pi --tools`, whose
|
|
160
|
+
parser is comma-separated too. Generated pi agents therefore render
|
|
161
|
+
`tools: "bash, read, task"`; a space-separated list collapses to a single bogus name and
|
|
162
|
+
leaves the agent with an allowlist matching no tool. Both parsers
|
|
163
|
+
(`extensions/subagent/agents.ts`, `extensions/agent-modes/discover.ts`) accept whitespace
|
|
164
|
+
as well, so either form round-trips safely.
|
|
165
|
+
|
|
156
166
|
## Composition Rules
|
|
157
167
|
|
|
158
168
|
### For Harnesses with Sections
|
|
@@ -216,8 +226,11 @@ flat `agents/*.agent.md` files are built from them:
|
|
|
216
226
|
from `agents-native/ralph.md`, copied verbatim).
|
|
217
227
|
- The pi harness delivers two ways: the `agent-modes` extension composes `agent.md` +
|
|
218
228
|
`pi/frontmatter.json` at runtime against `token-map.json`, and `agent-cortex install pi`
|
|
219
|
-
|
|
220
|
-
|
|
229
|
+
renders flat pi agents + token-substituted skills into a self-contained package at
|
|
230
|
+
`~/.agent-cortex/pi`, which pi loads once registered (see README "Pi package staging
|
|
231
|
+
(`agent-cortex install pi`)"). `agent-modes` and the `subagent` extension both read agents
|
|
232
|
+
beside themselves,
|
|
233
|
+
so the staged package needs no `~/.pi/agent` writes.
|
|
221
234
|
|
|
222
235
|
Never edit the generated flat materialised files by hand — edit the composable directory.
|
|
223
236
|
Generation is install-time: `claude` ships
|
|
@@ -229,8 +242,8 @@ in `claude-extras/`), and CI validates the materialiser with structural checks o
|
|
|
229
242
|
install; `copilot` ships via
|
|
230
243
|
`bin/installers/copilot.mjs`, the shared code path behind both `agent-cortex install
|
|
231
244
|
copilot` and `scripts/build-copilot-agents.mjs`; `pi` ships via
|
|
232
|
-
`bin/installers/pi.mjs`, which
|
|
233
|
-
|
|
245
|
+
`bin/installers/pi.mjs`, which stages a self-contained pi package (manifest, rendered
|
|
246
|
+
agents, token-substituted skills, extensions) at `~/.agent-cortex/pi`.
|
|
234
247
|
|
|
235
248
|
## Validation
|
|
236
249
|
|
package/bin/agent-cortex.mjs
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { parseArgs, buildHelpText, validateHarness } from "../lib/cli.mjs";
|
|
4
|
-
import {
|
|
4
|
+
import { join, dirname } from "node:path";
|
|
5
|
+
import { installPi, registerPi, sweepLegacyPiUserDirs } from "./installers/pi.mjs";
|
|
5
6
|
|
|
6
7
|
const argv = process.argv.slice(2);
|
|
7
8
|
const parsed = parseArgs(argv);
|
|
@@ -29,18 +30,41 @@ if (parsed.command === "install") {
|
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
if (parsed.harness === "pi") {
|
|
33
|
+
// A plain `agent-cortex install pi` stages the package into the home
|
|
34
|
+
// install root (~/.agent-cortex/pi) and registers that directory with pi
|
|
35
|
+
// through pi's own `pi install` (idempotent by state: a re-run is a no-op
|
|
36
|
+
// update); `--output <dir>` is the stage-only form (tests/CI) and prints
|
|
37
|
+
// the registration command instead.
|
|
32
38
|
try {
|
|
33
39
|
const result = await installPi({
|
|
34
40
|
dryRun: parsed.dryRun ?? false,
|
|
35
|
-
// Third-party pi packages are only provisioned for a real install:
|
|
36
|
-
// --output is the generate-only form, --no-provision opts an offline
|
|
37
|
-
// machine out explicitly.
|
|
38
|
-
provisionPackages: parsed.output === undefined && !parsed.noProvision,
|
|
39
41
|
warn: () => {}, // warnings surface once in the printed summary
|
|
40
42
|
...(parsed.output ? { output: parsed.output } : {}),
|
|
41
43
|
...(parsed.pluginRoot ? { pluginRoot: parsed.pluginRoot } : {}),
|
|
42
44
|
});
|
|
43
45
|
printPiInstall(result);
|
|
46
|
+
if (parsed.output === undefined) {
|
|
47
|
+
const registration = await registerPi({
|
|
48
|
+
output: result.output,
|
|
49
|
+
dryRun: parsed.dryRun ?? false,
|
|
50
|
+
requireRegister: parsed.requireRegister ?? false,
|
|
51
|
+
warn: (msg) => process.stdout.write(` ⚠ ${msg}\n`),
|
|
52
|
+
});
|
|
53
|
+
// The old installer's rendered copies are only stale once the staged
|
|
54
|
+
// package is what pi loads — and nothing is deleted on a dry run. The
|
|
55
|
+
// flag is passed through as well, so the sweep's own guarantee does not
|
|
56
|
+
// depend on this guard staying in place.
|
|
57
|
+
if (registration.registered && !(parsed.dryRun ?? false)) {
|
|
58
|
+
printPiSweep(
|
|
59
|
+
await sweepLegacyPiUserDirs({
|
|
60
|
+
piUserDir: dirname(registration.settingsPath),
|
|
61
|
+
dryRun: parsed.dryRun ?? false,
|
|
62
|
+
}),
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
} else {
|
|
66
|
+
process.stdout.write(` → register it with pi: pi install ${result.output}\n`);
|
|
67
|
+
}
|
|
44
68
|
} catch (err) {
|
|
45
69
|
process.stderr.write(`Install failed: ${err.message}\n`);
|
|
46
70
|
process.exit(1);
|
|
@@ -106,20 +130,32 @@ if (parsed.command === "install") {
|
|
|
106
130
|
process.exit(0);
|
|
107
131
|
}
|
|
108
132
|
|
|
133
|
+
function printPiSweep({ removed }) {
|
|
134
|
+
if (removed.length === 0) return;
|
|
135
|
+
process.stdout.write(
|
|
136
|
+
` ✓ Removed ${removed.length} artifact(s) the pre-staged-model installer rendered into pi's user directory (one-time migration — the staged package is the source from now on)\n`,
|
|
137
|
+
);
|
|
138
|
+
for (const path of removed) {
|
|
139
|
+
process.stdout.write(` - ${path}\n`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
109
143
|
function printPiInstall(result) {
|
|
110
144
|
process.stdout.write(`Installing agent-cortex for "pi" harness…\n`);
|
|
145
|
+
process.stdout.write(` ✓ Staged pi package → ${result.output}\n`);
|
|
111
146
|
for (const agent of result.agents) {
|
|
112
147
|
process.stdout.write(` ✓ ${agent.name}.agent.md → ${agent.filePath}\n`);
|
|
113
148
|
}
|
|
114
149
|
process.stdout.write(` ✓ Substituted ${result.skills.md} markdown file(s) across ${result.skills.skills} skill(s) → ${result.skills.dir}\n`);
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
process.stdout.write(` ✓ ${
|
|
150
|
+
process.stdout.write(` ✓ Copied ${result.extensions.files} extension file(s) → ${result.extensions.dir}\n`);
|
|
151
|
+
for (const name of result.packages.bundled) {
|
|
152
|
+
process.stdout.write(` ✓ Bundled pi package ${name} → ${join(result.packages.dir, name)}\n`);
|
|
153
|
+
}
|
|
154
|
+
const transitive = result.packages.staged.length - result.packages.bundled.length;
|
|
155
|
+
if (transitive > 0) {
|
|
156
|
+
process.stdout.write(` ✓ Copied ${transitive} transitive dependency tree(s) → ${result.packages.dir}\n`);
|
|
122
157
|
}
|
|
158
|
+
process.stdout.write(` ✓ ${result.dryRun ? "Would write" : "Wrote"} ${result.manifest.path} (version ${result.manifest.manifest.version})\n`);
|
|
123
159
|
for (const warning of result.warnings) {
|
|
124
160
|
process.stdout.write(` ⚠ ${warning}\n`);
|
|
125
161
|
}
|
|
@@ -127,21 +163,3 @@ function printPiInstall(result) {
|
|
|
127
163
|
process.stdout.write("(dry-run — nothing written)\n");
|
|
128
164
|
}
|
|
129
165
|
}
|
|
130
|
-
|
|
131
|
-
/** Report one managed pi config file (settings.json / keybindings.json). */
|
|
132
|
-
function printManagedConfig(label, info) {
|
|
133
|
-
if (!info) return;
|
|
134
|
-
if (info.action === "would-remove-symlink") {
|
|
135
|
-
process.stdout.write(` → would remove symlink ${info.path} and write ${label}\n`);
|
|
136
|
-
} else if (info.action === "would-write") {
|
|
137
|
-
process.stdout.write(` → would write ${label} → ${info.path}\n`);
|
|
138
|
-
} else if (info.action === "unchanged") {
|
|
139
|
-
process.stdout.write(` · ${label} → ${info.path} (unchanged)\n`);
|
|
140
|
-
} else if (info.action === "skipped") {
|
|
141
|
-
process.stdout.write(` ⚠ ${label} modified after install — left untouched → ${info.path}\n`);
|
|
142
|
-
} else if (info.action === "removed-symlink") {
|
|
143
|
-
process.stdout.write(` ✓ ${label} → ${info.path} (removed legacy symlink)\n`);
|
|
144
|
-
} else {
|
|
145
|
-
process.stdout.write(` ✓ ${label} → ${info.path}\n`);
|
|
146
|
-
}
|
|
147
|
-
}
|