pi-minimal 0.1.0 → 0.2.1
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/.github/dependabot.yml +11 -0
- package/.github/workflows/publish.yml +178 -14
- package/.github/workflows/release.yml +42 -0
- package/CHANGELOG.md +63 -0
- package/README.md +26 -1
- package/SECURITY.md +21 -0
- package/dist/activity.js +0 -3
- package/dist/cwd.js +92 -52
- package/dist/footer.js +26 -27
- package/dist/git-state.js +63 -0
- package/dist/index.js +48 -6
- package/dist/tool-rows.js +143 -12
- package/dist/tools.js +143 -77
- package/dist/transcript.js +50 -0
- package/dist/turn-rule.js +119 -0
- package/package.json +14 -3
- package/release-notes.md +30 -0
- package/src/activity.ts +0 -4
- package/src/cwd.ts +99 -51
- package/src/footer.ts +32 -32
- package/src/git-state.ts +71 -0
- package/src/index.ts +50 -6
- package/src/tool-rows.ts +170 -19
- package/src/tools.ts +156 -106
- package/src/transcript.ts +51 -0
- package/src/turn-rule.ts +135 -0
- package/test/activity.test.ts +4 -15
- package/test/footer.test.ts +38 -0
- package/test/git-state.test.ts +52 -0
- package/test/index.test.ts +40 -4
- package/test/package.test.ts +3 -2
- package/test/theme.test.ts +45 -0
- package/test/tool-rows.test.ts +109 -0
- package/test/tools.test.ts +49 -42
- package/test/transcript.test.ts +22 -0
- package/test/turn-rule.test.ts +33 -0
- package/themes/minimal-dark.json +84 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# To get started with Dependabot version updates, you'll need to specify which
|
|
2
|
+
# package ecosystems to update and where the package manifests are located.
|
|
3
|
+
# Please see the documentation for all configuration options:
|
|
4
|
+
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
|
|
5
|
+
|
|
6
|
+
version: 2
|
|
7
|
+
updates:
|
|
8
|
+
- package-ecosystem: "" # See documentation for possible values
|
|
9
|
+
directory: "/" # Location of package manifests
|
|
10
|
+
schedule:
|
|
11
|
+
interval: "weekly"
|
|
@@ -1,29 +1,193 @@
|
|
|
1
1
|
name: Publish
|
|
2
2
|
|
|
3
|
+
# Publishes automatically when the version in package.json changes on main:
|
|
4
|
+
# verifies the build, tags vX.Y.Z, creates a GitHub Release (CHANGELOG section +
|
|
5
|
+
# auto-generated notes), records a deployment to the "npm" environment, and
|
|
6
|
+
# publishes to npm via trusted publishing (OIDC; no token). Pushes that do not
|
|
7
|
+
# change the version are a no-op.
|
|
8
|
+
#
|
|
9
|
+
# This file name is registered with npm as the trusted publisher for pi-minimal.
|
|
10
|
+
# Renaming it breaks publishing until npm's trusted-publisher config is updated.
|
|
11
|
+
|
|
3
12
|
on:
|
|
4
|
-
|
|
5
|
-
|
|
13
|
+
push:
|
|
14
|
+
branches: [main]
|
|
15
|
+
workflow_dispatch:
|
|
16
|
+
|
|
17
|
+
concurrency:
|
|
18
|
+
group: publish
|
|
19
|
+
cancel-in-progress: false
|
|
6
20
|
|
|
7
21
|
permissions:
|
|
8
|
-
contents:
|
|
22
|
+
contents: write
|
|
23
|
+
deployments: write
|
|
9
24
|
id-token: write
|
|
25
|
+
pull-requests: read
|
|
10
26
|
|
|
11
27
|
jobs:
|
|
12
|
-
|
|
28
|
+
detect:
|
|
29
|
+
name: Detect version change
|
|
13
30
|
runs-on: ubuntu-latest
|
|
14
|
-
|
|
31
|
+
outputs:
|
|
32
|
+
publish: ${{ steps.check.outputs.publish }}
|
|
33
|
+
tag_exists: ${{ steps.check.outputs.tag_exists }}
|
|
34
|
+
version: ${{ steps.check.outputs.version }}
|
|
35
|
+
tag: ${{ steps.check.outputs.tag }}
|
|
36
|
+
previous_tag: ${{ steps.check.outputs.previous_tag }}
|
|
15
37
|
steps:
|
|
16
38
|
- uses: actions/checkout@v4
|
|
39
|
+
with:
|
|
40
|
+
fetch-depth: 0
|
|
17
41
|
- uses: actions/setup-node@v4
|
|
18
42
|
with:
|
|
19
43
|
node-version: 22.19.0
|
|
20
|
-
|
|
21
|
-
cache: npm
|
|
22
|
-
- run: npm ci
|
|
23
|
-
- run: npm run lint
|
|
24
|
-
- run: npm test
|
|
25
|
-
- run: npm run build
|
|
26
|
-
- run: git diff --exit-code -- dist
|
|
27
|
-
- run: npm publish --access public
|
|
44
|
+
- id: check
|
|
28
45
|
env:
|
|
29
|
-
|
|
46
|
+
GH_TOKEN: ${{ github.token }}
|
|
47
|
+
run: |
|
|
48
|
+
set -euo pipefail
|
|
49
|
+
name=$(node -p "require('./package.json').name")
|
|
50
|
+
version=$(node -p "require('./package.json').version")
|
|
51
|
+
tag="v$version"
|
|
52
|
+
echo "version=$version" >> "$GITHUB_OUTPUT"
|
|
53
|
+
echo "tag=$tag" >> "$GITHUB_OUTPUT"
|
|
54
|
+
previous_tag=$(git tag --list 'v*' --sort=-v:refname | grep -vx "$tag" | head -n1 || true)
|
|
55
|
+
echo "previous_tag=$previous_tag" >> "$GITHUB_OUTPUT"
|
|
56
|
+
|
|
57
|
+
tag_exists=false
|
|
58
|
+
if git rev-parse -q --verify "refs/tags/$tag" >/dev/null; then tag_exists=true; fi
|
|
59
|
+
echo "tag_exists=$tag_exists" >> "$GITHUB_OUTPUT"
|
|
60
|
+
|
|
61
|
+
if npm view "$name@$version" version >/dev/null 2>&1; then
|
|
62
|
+
echo "::notice::$name@$version is already on npm; nothing to publish."
|
|
63
|
+
echo "publish=false" >> "$GITHUB_OUTPUT"; exit 0
|
|
64
|
+
fi
|
|
65
|
+
echo "publish=true" >> "$GITHUB_OUTPUT"
|
|
66
|
+
if [ "$tag_exists" = true ]; then
|
|
67
|
+
echo "::notice::$tag exists but $name@$version is not on npm; publishing (re-run)."
|
|
68
|
+
else
|
|
69
|
+
echo "::notice::New version $version detected (previous tag: ${previous_tag:-none})."
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
release:
|
|
73
|
+
name: Tag, release, publish ${{ needs.detect.outputs.tag }}
|
|
74
|
+
needs: detect
|
|
75
|
+
if: needs.detect.outputs.publish == 'true'
|
|
76
|
+
runs-on: ubuntu-latest
|
|
77
|
+
environment:
|
|
78
|
+
name: npm
|
|
79
|
+
url: https://www.npmjs.com/package/pi-minimal/v/${{ needs.detect.outputs.version }}
|
|
80
|
+
env:
|
|
81
|
+
VERSION: ${{ needs.detect.outputs.version }}
|
|
82
|
+
TAG: ${{ needs.detect.outputs.tag }}
|
|
83
|
+
PREVIOUS_TAG: ${{ needs.detect.outputs.previous_tag }}
|
|
84
|
+
GH_TOKEN: ${{ github.token }}
|
|
85
|
+
steps:
|
|
86
|
+
- uses: actions/checkout@v4
|
|
87
|
+
with:
|
|
88
|
+
fetch-depth: 0
|
|
89
|
+
# No registry-url here: setup-node would write an .npmrc with a placeholder
|
|
90
|
+
# NODE_AUTH_TOKEN, and npm then sends that instead of exchanging the OIDC
|
|
91
|
+
# token, which the registry rejects with a 404. Trusted publishing needs
|
|
92
|
+
# no token configured at all.
|
|
93
|
+
- uses: actions/setup-node@v4
|
|
94
|
+
with:
|
|
95
|
+
node-version: 22.19.0
|
|
96
|
+
cache: npm
|
|
97
|
+
|
|
98
|
+
- name: Verify
|
|
99
|
+
run: |
|
|
100
|
+
npm ci
|
|
101
|
+
npm run lint
|
|
102
|
+
npm run typecheck
|
|
103
|
+
npm test
|
|
104
|
+
npm run build
|
|
105
|
+
git diff --exit-code -- dist
|
|
106
|
+
|
|
107
|
+
- name: Compose release notes
|
|
108
|
+
id: notes
|
|
109
|
+
run: |
|
|
110
|
+
set -euo pipefail
|
|
111
|
+
notes=release-notes.md
|
|
112
|
+
: > "$notes"
|
|
113
|
+
|
|
114
|
+
# 1. Hand-written section from CHANGELOG.md, if present ("## [X.Y.Z] ..." until next "## ").
|
|
115
|
+
if [ -f CHANGELOG.md ]; then
|
|
116
|
+
section=$(awk -v v="$VERSION" '
|
|
117
|
+
/^## \[/ { if (found) exit; found = index($0, "[" v "]") > 0; next }
|
|
118
|
+
found && /^\[[^]]+\]: / { exit }
|
|
119
|
+
found { print }' CHANGELOG.md | sed -e '/./,$!d' -e :a -e '/^\n*$/{$d;N;ba' -e '}')
|
|
120
|
+
if [ -n "$section" ]; then
|
|
121
|
+
printf '%s\n\n' "$section" >> "$notes"
|
|
122
|
+
else
|
|
123
|
+
echo "::warning::CHANGELOG.md has no section for [$VERSION]; using generated notes only."
|
|
124
|
+
fi
|
|
125
|
+
fi
|
|
126
|
+
|
|
127
|
+
# 2. GitHub-generated notes (merged PRs / commits since the previous tag).
|
|
128
|
+
body=$(gh api -X POST "repos/${GITHUB_REPOSITORY}/releases/generate-notes" \
|
|
129
|
+
-f tag_name="$TAG" -f target_commitish="$GITHUB_SHA" \
|
|
130
|
+
${PREVIOUS_TAG:+-f previous_tag_name="$PREVIOUS_TAG"} --jq .body)
|
|
131
|
+
printf '%s\n\n' "$body" >> "$notes"
|
|
132
|
+
|
|
133
|
+
# 3. Install footer.
|
|
134
|
+
cat >> "$notes" <<EOF
|
|
135
|
+
---
|
|
136
|
+
**Install / upgrade**
|
|
137
|
+
\`\`\`bash
|
|
138
|
+
pi install npm:pi-minimal@$VERSION # or: pi update --extensions
|
|
139
|
+
\`\`\`
|
|
140
|
+
EOF
|
|
141
|
+
echo "file=$notes" >> "$GITHUB_OUTPUT"
|
|
142
|
+
echo "### Release notes for $TAG" >> "$GITHUB_STEP_SUMMARY"
|
|
143
|
+
cat "$notes" >> "$GITHUB_STEP_SUMMARY"
|
|
144
|
+
|
|
145
|
+
- name: Create tag and GitHub Release
|
|
146
|
+
run: |
|
|
147
|
+
set -euo pipefail
|
|
148
|
+
if [ "${{ needs.detect.outputs.tag_exists }}" != true ]; then
|
|
149
|
+
git config user.name "github-actions[bot]"
|
|
150
|
+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
151
|
+
git tag -a "$TAG" -m "pi-minimal $VERSION" "$GITHUB_SHA"
|
|
152
|
+
git push origin "refs/tags/$TAG"
|
|
153
|
+
else
|
|
154
|
+
echo "Tag $TAG already exists; not re-tagging."
|
|
155
|
+
fi
|
|
156
|
+
if gh release view "$TAG" >/dev/null 2>&1; then
|
|
157
|
+
echo "Release $TAG already exists; updating notes."
|
|
158
|
+
gh release edit "$TAG" --notes-file "${{ steps.notes.outputs.file }}"
|
|
159
|
+
else
|
|
160
|
+
gh release create "$TAG" \
|
|
161
|
+
--target "$GITHUB_SHA" \
|
|
162
|
+
--title "pi-minimal $VERSION" \
|
|
163
|
+
--notes-file "${{ steps.notes.outputs.file }}" \
|
|
164
|
+
--verify-tag
|
|
165
|
+
fi
|
|
166
|
+
|
|
167
|
+
- name: Upgrade npm for trusted publishing
|
|
168
|
+
# Node 22 bundles npm 10.x; OIDC trusted publishing needs npm >= 11.5.1.
|
|
169
|
+
# npm 12 requires Node >= 22.22.2, so stay on the 11.x line for Node 22.19.
|
|
170
|
+
run: |
|
|
171
|
+
npm install -g npm@11
|
|
172
|
+
npm --version
|
|
173
|
+
|
|
174
|
+
- name: Publish to npm (trusted publishing)
|
|
175
|
+
# No NODE_AUTH_TOKEN: npm authenticates with the job's OIDC token and
|
|
176
|
+
# generates provenance automatically.
|
|
177
|
+
run: |
|
|
178
|
+
npm config get registry
|
|
179
|
+
npm whoami 2>/dev/null || echo "no token configured (expected for OIDC)"
|
|
180
|
+
npm publish --access public
|
|
181
|
+
|
|
182
|
+
- name: Attach package tarball to release
|
|
183
|
+
run: |
|
|
184
|
+
npm pack --silent
|
|
185
|
+
gh release upload "$TAG" ./pi-minimal-"$VERSION".tgz --clobber
|
|
186
|
+
|
|
187
|
+
- name: Summary
|
|
188
|
+
run: |
|
|
189
|
+
{
|
|
190
|
+
echo "### Published"
|
|
191
|
+
echo "- Release: ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/releases/tag/$TAG"
|
|
192
|
+
echo "- npm: https://www.npmjs.com/package/pi-minimal/v/$VERSION"
|
|
193
|
+
} >> "$GITHUB_STEP_SUMMARY"
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
name: Release (manual fallback)
|
|
2
|
+
|
|
3
|
+
# Normal releases are fully automatic: bump the version in package.json on main
|
|
4
|
+
# and .github/workflows/publish.yml tags, releases, and publishes via trusted
|
|
5
|
+
# publishing. Use this only to re-run the npm publish for an existing release
|
|
6
|
+
# when the automated path fails; it authenticates with the NPM_TOKEN secret.
|
|
7
|
+
|
|
8
|
+
on:
|
|
9
|
+
workflow_dispatch:
|
|
10
|
+
inputs:
|
|
11
|
+
tag:
|
|
12
|
+
description: "Existing release tag to publish (e.g. v0.2.0)"
|
|
13
|
+
required: true
|
|
14
|
+
|
|
15
|
+
permissions:
|
|
16
|
+
contents: read
|
|
17
|
+
id-token: write
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
publish:
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
environment:
|
|
23
|
+
name: npm
|
|
24
|
+
url: https://www.npmjs.com/package/pi-minimal
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@v4
|
|
27
|
+
with:
|
|
28
|
+
ref: ${{ inputs.tag }}
|
|
29
|
+
- uses: actions/setup-node@v4
|
|
30
|
+
with:
|
|
31
|
+
node-version: 22.19.0
|
|
32
|
+
registry-url: https://registry.npmjs.org
|
|
33
|
+
cache: npm
|
|
34
|
+
- run: npm ci
|
|
35
|
+
- run: npm run lint
|
|
36
|
+
- run: npm test
|
|
37
|
+
- run: npm run build
|
|
38
|
+
- run: git diff --exit-code -- dist
|
|
39
|
+
- run: npm install -g npm@11
|
|
40
|
+
- run: npm publish --access public --provenance
|
|
41
|
+
env:
|
|
42
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions follow
|
|
5
|
+
[Semantic Versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
Releases are cut automatically: when the `version` in `package.json` changes on
|
|
8
|
+
`main`, the Publish workflow tags it, publishes to npm, and uses the matching
|
|
9
|
+
`## [x.y.z]` section below as the top of the release notes.
|
|
10
|
+
|
|
11
|
+
## [Unreleased]
|
|
12
|
+
|
|
13
|
+
## [0.2.1] - 2026-10-22
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
- Automated releases: when `version` in `package.json` changes on `main`, the
|
|
17
|
+
Publish workflow verifies the build, tags `vX.Y.Z`, creates a GitHub Release
|
|
18
|
+
(CHANGELOG section + generated notes + package tarball), records a deployment
|
|
19
|
+
to the `npm` environment, and publishes to npm via OIDC trusted publishing.
|
|
20
|
+
- `CHANGELOG.md` and a "Releasing" section in the README.
|
|
21
|
+
- The Publish workflow upgrades npm to >= 11.5.1 (required for OIDC trusted
|
|
22
|
+
publishing) and is re-runnable: an existing tag whose version is missing
|
|
23
|
+
from npm is published without re-tagging.
|
|
24
|
+
- `repository`, `homepage`, and `bugs` fields in `package.json`, required for
|
|
25
|
+
npm provenance verification.
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
- The release-triggered publish workflow is now a manual, token-based fallback
|
|
29
|
+
(`release.yml`) for re-publishing an existing tag.
|
|
30
|
+
|
|
31
|
+
## [0.2.0] - 2026-10-22
|
|
32
|
+
|
|
33
|
+
### Added
|
|
34
|
+
- One-line tool rows for the built-in tools with a status glyph (`◐` running,
|
|
35
|
+
`✓` ok, `✗` failed), a fixed verb column, and a right-aligned outcome
|
|
36
|
+
(`34 lines · 1.0s`, `exit 1`, `+3 −1`, `4 matches`, `2.1 KB`).
|
|
37
|
+
- Failed rows show the first line of output underneath; `ctrl+o` expands the
|
|
38
|
+
full output as a `│`-ruled block.
|
|
39
|
+
- A dim turn rule (`── 15:51 · 12s · 4 tools ──`) after every agent run,
|
|
40
|
+
stored as a custom entry that never enters model context.
|
|
41
|
+
- `┊` gutter on thinking paragraphs.
|
|
42
|
+
- Status line shows the git branch and dirty count (`⎇ main ±1`) and colours
|
|
43
|
+
the context meter amber at 70 % and red at 90 %.
|
|
44
|
+
- Footer key hints appear only when idle with an empty editor; `/compact` is
|
|
45
|
+
suggested under context pressure.
|
|
46
|
+
- `minimal-dark` theme shipped with the package.
|
|
47
|
+
|
|
48
|
+
### Changed
|
|
49
|
+
- Tool renderers are attached with `registerToolRenderer` at extension load
|
|
50
|
+
instead of re-registering the built-in tools in `session_start`, so resumed
|
|
51
|
+
transcripts render the same way as new messages.
|
|
52
|
+
- Requires pi `>=1.4.0`.
|
|
53
|
+
|
|
54
|
+
## [0.1.0] - 2026-09-23
|
|
55
|
+
|
|
56
|
+
### Added
|
|
57
|
+
- Initial release: header/footer chrome, compact tool activity rows, working
|
|
58
|
+
status line, Herdr sibling status, Gauntlet widget hiding.
|
|
59
|
+
|
|
60
|
+
[Unreleased]: https://github.com/raviy06/pi-minimal/compare/v0.2.1...HEAD
|
|
61
|
+
[0.2.1]: https://github.com/raviy06/pi-minimal/compare/v0.2.0...v0.2.1
|
|
62
|
+
[0.2.0]: https://github.com/raviy06/pi-minimal/compare/v0.1.0...v0.2.0
|
|
63
|
+
[0.1.0]: https://github.com/raviy06/pi-minimal/releases/tag/v0.1.0
|
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@ A Pi coding-agent package.
|
|
|
5
5
|
## Requirements
|
|
6
6
|
|
|
7
7
|
- Node.js `>=22.19.0`
|
|
8
|
-
- Pi `>=
|
|
8
|
+
- Pi `>=1.4.0` (uses `registerToolRenderer`, `registerEntryRenderer`, `registerMarkdownTransformer`)
|
|
9
9
|
|
|
10
10
|
No other Pi extension is required. Pi supplies the peer packages used by this extension.
|
|
11
11
|
|
|
@@ -16,12 +16,27 @@ Optional integrations:
|
|
|
16
16
|
|
|
17
17
|
## Installation
|
|
18
18
|
|
|
19
|
+
```bash
|
|
20
|
+
pi install npm:pi-minimal
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Git installation also works:
|
|
24
|
+
|
|
19
25
|
```bash
|
|
20
26
|
pi install git:github.com/raviy06/pi-minimal
|
|
21
27
|
```
|
|
22
28
|
|
|
23
29
|
Do not load this package beside the live `grok-chrome` extension. Disable or remove `grok-chrome` before enabling `pi-minimal`.
|
|
24
30
|
|
|
31
|
+
## What it changes
|
|
32
|
+
|
|
33
|
+
- **Tool rows** – every built-in tool call (`bash`, `read`, `edit`, `write`, `grep`, `find`, `ls`) renders as one line: status glyph (`◐` running, `✓` ok, `✗` failed), verb, item, and a right-aligned outcome (`34 lines · 1.0s`, `exit 1`, `+3 −1`, `4 matches`). Failed rows show the first line of output underneath; `ctrl+o` expands full output in a `│`-ruled block.
|
|
34
|
+
- **Turn rule** – after each agent run a dim `── 15:51 · 12s · 4 tools ──` separator is appended (stored as a custom entry; never sent to the model).
|
|
35
|
+
- **Thinking gutter** – thinking paragraphs are prefixed with `┊` so they read as secondary.
|
|
36
|
+
- **Status line** – one line above the editor: model · thinking level · context meter (amber at 70 %, red at 90 %) · cwd · `⎇ branch ±dirty` · Herdr siblings.
|
|
37
|
+
- **Footer** – key hints appear only when the agent is idle and the editor is empty; `/compact` is suggested under context pressure.
|
|
38
|
+
- **Theme** – ships `minimal-dark`: neutral tool backgrounds, visible user-message tint, dimmer thinking text, violet headings (yellow is reserved for warnings), and code-fence lines coloured like the background so fenced code reads as a padded block. Select it in `/settings` → Theme, or set `"theme": "minimal-dark"`.
|
|
39
|
+
|
|
25
40
|
## Configuration
|
|
26
41
|
|
|
27
42
|
The package loads JSON in this order:
|
|
@@ -37,3 +52,13 @@ The only supported key is `hideGauntletWidgets`. It defaults to `true`.
|
|
|
37
52
|
```json
|
|
38
53
|
{ "hideGauntletWidgets": true }
|
|
39
54
|
```
|
|
55
|
+
|
|
56
|
+
## Releasing
|
|
57
|
+
|
|
58
|
+
Releases are automatic. To ship a version:
|
|
59
|
+
|
|
60
|
+
1. Add a `## [x.y.z] - YYYY-MM-DD` section to `CHANGELOG.md`.
|
|
61
|
+
2. Bump `version` in `package.json` (and the expectation in `test/package.test.ts`), run `npm run build`, commit `dist/`.
|
|
62
|
+
3. Merge to `main`.
|
|
63
|
+
|
|
64
|
+
The Publish workflow (`.github/workflows/publish.yml`, npm's registered trusted publisher) detects the new version, verifies lint/typecheck/tests/build, tags `vx.y.z`, creates a GitHub Release (CHANGELOG section + auto-generated notes + package tarball), records a deployment to the `npm` environment, and publishes to npm via OIDC trusted publishing (provenance included). Pushes that do not change the version do nothing. `Release (manual fallback)` re-publishes an existing tag with the `NPM_TOKEN` secret if the automated path ever fails.
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Supported Versions
|
|
4
|
+
|
|
5
|
+
Use this section to tell people about which versions of your project are
|
|
6
|
+
currently being supported with security updates.
|
|
7
|
+
|
|
8
|
+
| Version | Supported |
|
|
9
|
+
| ------- | ------------------ |
|
|
10
|
+
| 5.1.x | :white_check_mark: |
|
|
11
|
+
| 5.0.x | :x: |
|
|
12
|
+
| 4.0.x | :white_check_mark: |
|
|
13
|
+
| < 4.0 | :x: |
|
|
14
|
+
|
|
15
|
+
## Reporting a Vulnerability
|
|
16
|
+
|
|
17
|
+
Use this section to tell people how to report a vulnerability.
|
|
18
|
+
|
|
19
|
+
Tell them where to go, how often they can expect to get an update on a
|
|
20
|
+
reported vulnerability, what to expect if the vulnerability is accepted or
|
|
21
|
+
declined, etc.
|
package/dist/activity.js
CHANGED
package/dist/cwd.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { CHROME_MARGIN } from "./chrome.js";
|
|
4
|
-
|
|
4
|
+
import { formatGitState } from "./git-state.js";
|
|
5
|
+
export function formatCwd(cwd) {
|
|
5
6
|
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
6
7
|
if (!home)
|
|
7
8
|
return cwd;
|
|
@@ -12,7 +13,7 @@ function formatCwd(cwd) {
|
|
|
12
13
|
}
|
|
13
14
|
return cwd;
|
|
14
15
|
}
|
|
15
|
-
function formatTokens(n) {
|
|
16
|
+
export function formatTokens(n) {
|
|
16
17
|
if (n < 1000)
|
|
17
18
|
return String(n);
|
|
18
19
|
if (n < 10000)
|
|
@@ -23,7 +24,7 @@ function formatTokens(n) {
|
|
|
23
24
|
return `${(n / 1e6).toFixed(1)}M`;
|
|
24
25
|
return `${Math.round(n / 1e6)}M`;
|
|
25
26
|
}
|
|
26
|
-
function modelIcon(id) {
|
|
27
|
+
export function modelIcon(id) {
|
|
27
28
|
const s = id.toLowerCase();
|
|
28
29
|
if (s.includes("grok"))
|
|
29
30
|
return "✦";
|
|
@@ -38,7 +39,7 @@ function modelIcon(id) {
|
|
|
38
39
|
return "◈";
|
|
39
40
|
return "◆";
|
|
40
41
|
}
|
|
41
|
-
function shortModel(id) {
|
|
42
|
+
export function shortModel(id) {
|
|
42
43
|
return (id.split("/").pop() || id).replace(/^claude-/, "");
|
|
43
44
|
}
|
|
44
45
|
function thinkingColor(level) {
|
|
@@ -48,56 +49,95 @@ function thinkingColor(level) {
|
|
|
48
49
|
return "accent";
|
|
49
50
|
return "dim";
|
|
50
51
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
52
|
+
/** Context pressure band used for the meter and percentage colour. */
|
|
53
|
+
export function contextBand(pct) {
|
|
54
|
+
if (pct >= 90)
|
|
55
|
+
return "error";
|
|
56
|
+
if (pct >= 70)
|
|
57
|
+
return "warning";
|
|
58
|
+
return "accent";
|
|
55
59
|
}
|
|
56
|
-
function
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
60
|
+
export function meter(percent, fill, empty, cells = 8) {
|
|
61
|
+
const filled = Math.max(0, Math.min(cells, Math.round((percent / 100) * cells)));
|
|
62
|
+
return fill.repeat(filled) + empty.repeat(cells - filled);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Fit `left` and `right` into `width`. Right-hand segments are dropped from the end first (they are
|
|
66
|
+
* the least important), then the left is truncated.
|
|
67
|
+
*/
|
|
68
|
+
export function splitRow(left, rightSegments, width, sep = " ") {
|
|
69
|
+
const segments = [...rightSegments.filter((s) => visibleWidth(s) > 0)];
|
|
70
|
+
let right = segments.join(sep);
|
|
71
|
+
while (segments.length > 0 && visibleWidth(left) + 2 + visibleWidth(right) > width) {
|
|
72
|
+
segments.pop();
|
|
73
|
+
right = segments.join(sep);
|
|
61
74
|
}
|
|
62
|
-
|
|
63
|
-
|
|
75
|
+
let L = left;
|
|
76
|
+
if (visibleWidth(L) + (right ? 2 : 0) + visibleWidth(right) > width) {
|
|
77
|
+
L = truncateToWidth(L, Math.max(0, width - (right ? 2 : 0) - visibleWidth(right)), "");
|
|
64
78
|
}
|
|
65
|
-
const gap = Math.max(
|
|
66
|
-
return L + " ".repeat(gap) +
|
|
79
|
+
const gap = right ? Math.max(2, width - visibleWidth(L) - visibleWidth(right)) : 0;
|
|
80
|
+
return L + " ".repeat(gap) + right;
|
|
81
|
+
}
|
|
82
|
+
function siblingColor(status) {
|
|
83
|
+
if (status === "idle" || status === "done")
|
|
84
|
+
return "success";
|
|
85
|
+
if (status === "working")
|
|
86
|
+
return "accent";
|
|
87
|
+
if (status === "blocked")
|
|
88
|
+
return "error";
|
|
89
|
+
return "muted";
|
|
67
90
|
}
|
|
68
|
-
export function installCwd(ctx) {
|
|
69
|
-
ctx.ui.setWidget("tw-cwd", (
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
.
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
91
|
+
export function installCwd(ctx, sources) {
|
|
92
|
+
ctx.ui.setWidget("tw-cwd", (tui, theme) => {
|
|
93
|
+
const unsub = sources.onGitChange(() => tui.requestRender());
|
|
94
|
+
return {
|
|
95
|
+
dispose: unsub,
|
|
96
|
+
invalidate() { },
|
|
97
|
+
render(width) {
|
|
98
|
+
const inner = Math.max(8, width - CHROME_MARGIN * 2);
|
|
99
|
+
const id = ctx.model?.id || "no-model";
|
|
100
|
+
const icon = theme.fg("accent", modelIcon(id));
|
|
101
|
+
const name = theme.fg("text", shortModel(id));
|
|
102
|
+
const thinking = ctx.thinkingLevel || "";
|
|
103
|
+
const think = thinking ? theme.fg(thinkingColor(thinking), thinking) : "";
|
|
104
|
+
const usage = ctx.getContextUsage?.();
|
|
105
|
+
const window = usage?.contextWindow || ctx.model?.contextWindow;
|
|
106
|
+
const used = usage?.tokens;
|
|
107
|
+
let bar = theme.fg("dim", "░░░░░░░░");
|
|
108
|
+
let pctStr = theme.fg("dim", "?%");
|
|
109
|
+
let tokenStr = theme.fg("dim", "?/?");
|
|
110
|
+
if (typeof used === "number" && window) {
|
|
111
|
+
const pct = Math.max(0, Math.min(100, Math.round((used / window) * 100)));
|
|
112
|
+
const band = contextBand(pct);
|
|
113
|
+
bar = meter(pct, theme.fg(band, "▓"), theme.fg("dim", "░"));
|
|
114
|
+
pctStr = theme.fg(band === "accent" ? "muted" : band, `${pct}%`);
|
|
115
|
+
tokenStr = theme.fg("dim", `${formatTokens(used)}/${formatTokens(window)}`);
|
|
116
|
+
}
|
|
117
|
+
else if (used === null && window) {
|
|
118
|
+
tokenStr = theme.fg("dim", `?/${formatTokens(window)}`);
|
|
119
|
+
}
|
|
120
|
+
const midot = theme.fg("dim", " · ");
|
|
121
|
+
const model = [`${icon} ${name}`, think].filter(Boolean).join(midot);
|
|
122
|
+
const context = `${bar} ${pctStr} ${tokenStr}`;
|
|
123
|
+
const left = `${model} ${context}`;
|
|
124
|
+
const right = [];
|
|
125
|
+
if (ctx.cwd)
|
|
126
|
+
right.push(theme.fg("muted", formatCwd(ctx.cwd)));
|
|
127
|
+
const git = formatGitState(sources.gitState());
|
|
128
|
+
if (git.branch) {
|
|
129
|
+
const dirty = git.dirty ? ` ${theme.fg("warning", git.dirty)}` : "";
|
|
130
|
+
right.push(`${theme.fg("dim", "⎇")} ${theme.fg("muted", git.branch)}${dirty}`);
|
|
131
|
+
}
|
|
132
|
+
const siblings = sources.siblings();
|
|
133
|
+
if (siblings && siblings.length > 0) {
|
|
134
|
+
right.push(siblings
|
|
135
|
+
.map((s) => `${theme.fg("text", s.name)} ${theme.fg(siblingColor(s.status), s.status)}`)
|
|
136
|
+
.join(theme.fg("dim", " · ")));
|
|
137
|
+
}
|
|
138
|
+
const row = splitRow(left, right, inner);
|
|
139
|
+
return [`${" ".repeat(CHROME_MARGIN)}${row}`];
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
}, { placement: "aboveEditor" });
|
|
103
143
|
}
|
package/dist/footer.js
CHANGED
|
@@ -1,41 +1,40 @@
|
|
|
1
1
|
import { keyHint } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
import { truncateToWidth
|
|
2
|
+
import { truncateToWidth } from "@earendil-works/pi-tui";
|
|
3
3
|
import { CHROME_MARGIN } from "./chrome.js";
|
|
4
4
|
import { formatTrackerLines } from "./tracker.js";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
if (
|
|
11
|
-
return
|
|
12
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Key hints are shown only when nothing else needs the line: idle agent, empty editor. Otherwise
|
|
7
|
+
* the footer collapses to zero lines (tracker lines excepted).
|
|
8
|
+
*/
|
|
9
|
+
export function footerHints(sources, hints) {
|
|
10
|
+
if (sources.busy())
|
|
11
|
+
return [];
|
|
12
|
+
if (sources.editorText().trim().length > 0)
|
|
13
|
+
return [];
|
|
14
|
+
const pct = sources.contextPct();
|
|
15
|
+
const out = [...hints];
|
|
16
|
+
if (pct !== undefined && pct >= 70)
|
|
17
|
+
out.push("/compact");
|
|
18
|
+
return out;
|
|
13
19
|
}
|
|
14
|
-
export function installFooter(ctx,
|
|
20
|
+
export function installFooter(ctx, sources) {
|
|
15
21
|
ctx.ui.setFooter((tui, theme, footerData) => {
|
|
16
22
|
const unsub = footerData.onBranchChange(() => tui.requestRender());
|
|
17
23
|
return {
|
|
18
24
|
dispose: unsub,
|
|
19
25
|
invalidate() { },
|
|
20
26
|
render(width) {
|
|
21
|
-
const send = keyHint("tui.input.submit", "send");
|
|
22
|
-
const think = keyHint("app.thinking.cycle", "thinking");
|
|
23
|
-
const left = [send, think].filter(Boolean).join(" · ");
|
|
24
|
-
const siblings = getSiblings();
|
|
25
|
-
let right = "";
|
|
26
|
-
if (siblings && siblings.length > 0) {
|
|
27
|
-
right = siblings
|
|
28
|
-
.map((s) => {
|
|
29
|
-
const name = theme.fg("text", s.name);
|
|
30
|
-
const st = theme.fg(statusColor(s.status), s.status);
|
|
31
|
-
return `${name} ${st}`;
|
|
32
|
-
})
|
|
33
|
-
.join(theme.fg("dim", " · "));
|
|
34
|
-
}
|
|
35
27
|
const inner = Math.max(8, width - CHROME_MARGIN * 2);
|
|
36
|
-
const
|
|
37
|
-
const
|
|
38
|
-
|
|
28
|
+
const out = [];
|
|
29
|
+
const hints = footerHints(sources, [
|
|
30
|
+
keyHint("tui.input.submit", "send"),
|
|
31
|
+
keyHint("app.thinking.cycle", "thinking"),
|
|
32
|
+
keyHint("app.tools.expand", "expand tools"),
|
|
33
|
+
].filter(Boolean));
|
|
34
|
+
if (hints.length > 0) {
|
|
35
|
+
const line = theme.fg("dim", hints.join(" · "));
|
|
36
|
+
out.push(`${" ".repeat(CHROME_MARGIN)}${truncateToWidth(line, inner, "")}`);
|
|
37
|
+
}
|
|
39
38
|
for (const extra of formatTrackerLines(ctx, theme)) {
|
|
40
39
|
out.push(`${" ".repeat(CHROME_MARGIN)}${truncateToWidth(extra, inner, "")}`);
|
|
41
40
|
}
|