@groeponline/pi-wishcraft 1.23.0 → 1.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/RELEASE.md +37 -8
- package/bash-mode/completion.ts +2 -0
- package/build-stamp.json +1 -1
- package/package.json +1 -1
- package/src/config/tokens.ts +21 -3
- package/src/config/types.ts +4 -1
- package/src/extension/ui/deck/component.ts +11 -0
- package/src/git/status.ts +4 -0
- package/src/segments/system.ts +7 -1
- package/src/theme/theme.ts +6 -0
- package/src/usage/ledger.ts +5 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [1.24.0] - 2026-11-12
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
- **Git probes no longer race interactive git.** Every read-only `git` spawn (`src/git/status.ts`, `bash-mode/completion.ts`) now runs with `GIT_OPTIONAL_LOCKS=0` and `windowsHide: true`, so status polling can't refresh `.git/index` under a real checkout and orphaned `index.lock` files stop bricking the repo.
|
|
9
|
+
- **Think levels high/xhigh/max got their rainbow back.** The thinking segment collapsed all three into the plain `thinking` color; it now uses dedicated `thinkingHigh`/`thinkingXhigh`/`thinkingMax` semantic colors (default `rainbow`, overridable per preset — the `rainbow` ColorValue itself is new).
|
|
10
|
+
- **Token totals prefer the provider-reported `totalTokens`.** The four-field sum undercounted providers that report reasoning/other tokens only in the total.
|
|
11
|
+
|
|
12
|
+
|
|
5
13
|
## [1.23.0] - 2026-11-08
|
|
6
14
|
|
|
7
15
|
### Fixed
|
package/RELEASE.md
CHANGED
|
@@ -113,11 +113,34 @@ Measured from this repository on 2026-11-08:
|
|
|
113
113
|
| `npm whoami` with `secrets.NPM_TOKEN` | `chefgroeponline` — valid |
|
|
114
114
|
| `ACTIONS_ID_TOKEN_REQUEST_URL` in a job with `id-token: write` | present |
|
|
115
115
|
| `POST /-/npm/v1/oidc/token/exchange/package/@groeponline%2fpi-wishcraft` | **401** |
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
116
|
+
| `npm publish` with that token (v1.23.0, run 42464125440) | **EOTP** — one-time password required |
|
|
117
|
+
|
|
118
|
+
**The token authenticates but cannot write.** It is a *Publish* token on an
|
|
119
|
+
account with 2FA enabled, so npm demands an OTP for `npm publish` and a CI
|
|
120
|
+
runner can never answer one.
|
|
121
|
+
|
|
122
|
+
**Resolved 2026-11-09 03:58 UTC.** The org `NPM_TOKEN` secret was replaced with a
|
|
123
|
+
granular token that has *Bypass 2FA*; run 42559311821 (dispatch on `v1.23.0`)
|
|
124
|
+
then published `1.23.0` and created its GitHub Release. No workflow change was
|
|
125
|
+
needed — `NODE_AUTH_TOKEN` still publishes. `v1.18.0`–`v1.22.0` stay unpublished:
|
|
126
|
+
their tag refs carry unpinned `uses: actions/...@v7` and the org policy rejects
|
|
127
|
+
them, so they cannot be replayed without rewriting tags. `1.23.0` supersedes them.
|
|
128
|
+
|
|
129
|
+
The two routes that were available, for the record:
|
|
130
|
+
|
|
131
|
+
- **Token route (unblocks v1.23.0 as tagged):** Access Tokens → Generate New
|
|
132
|
+
Token → **Granular**, read+write on `@groeponline` (or a classic
|
|
133
|
+
**Automation** token), with **Bypass 2FA** enabled; store it as the org
|
|
134
|
+
`NPM_TOKEN` secret. Then replay the tag:
|
|
135
|
+
`gh workflow run release.yml --ref v1.23.0` — that same job also creates the
|
|
136
|
+
missing GitHub Release.
|
|
137
|
+
- **OIDC route (no credential at all):** configure the trusted publisher below
|
|
138
|
+
and drop the `NODE_AUTH_TOKEN` env from the `publish` job. A tag cut *before*
|
|
139
|
+
that change still carries the token env and cannot be replayed, so this route
|
|
140
|
+
publishes the next version and leaves the hole.
|
|
141
|
+
|
|
142
|
+
Switching to trusted publishing is a one-time npmjs.com change, after which the
|
|
143
|
+
`NODE_AUTH_TOKEN` env comes out of the job:
|
|
121
144
|
|
|
122
145
|
1. Sign in as the maintainer of `@groeponline/pi-wishcraft` (`chefgroeponline
|
|
123
146
|
<joep@chefgroep.online>` — `npm view @groeponline/pi-wishcraft maintainers`).
|
|
@@ -149,9 +172,15 @@ Rules that follow from the current (token) configuration:
|
|
|
149
172
|
|
|
150
173
|
For a scoped package npm answers a PUT with **404, not 403**, when the caller
|
|
151
174
|
may not publish. That means the credential is missing, revoked, read-only or
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
175
|
+
does not own the scope. It is not "package not found", and it is not a
|
|
176
|
+
retryable transient. Two neighbours are distinct and have their own fix:
|
|
177
|
+
|
|
178
|
+
- `ENEEDAUTH` — npm had no credential at all: `NODE_AUTH_TOKEN` was empty and
|
|
179
|
+
the OIDC exchange refused.
|
|
180
|
+
- `EOTP` — the credential authenticates but the account requires a one-time
|
|
181
|
+
password for writes, which CI cannot supply. This is what v1.23.0 hit. The
|
|
182
|
+
token needs 2FA bypass (granular token with *Bypass 2FA*, or a classic
|
|
183
|
+
Automation token); retrying with the same token only burns another run.
|
|
155
184
|
|
|
156
185
|
`scripts/npm-publish.sh` prints that diagnosis instead of the raw 404. Recovery:
|
|
157
186
|
|
package/bash-mode/completion.ts
CHANGED
|
@@ -246,6 +246,8 @@ function runGit(args: string[], cwd: string): string[] {
|
|
|
246
246
|
cwd,
|
|
247
247
|
encoding: "utf8",
|
|
248
248
|
stdio: ["ignore", "pipe", "ignore"],
|
|
249
|
+
env: { ...process.env, GIT_OPTIONAL_LOCKS: "0" },
|
|
250
|
+
windowsHide: true,
|
|
249
251
|
});
|
|
250
252
|
if (result.status !== 0 || !result.stdout) return [];
|
|
251
253
|
return result.stdout.split("\n").map((line) => line.trim()).filter(Boolean);
|
package/build-stamp.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"source_sha":"
|
|
1
|
+
{"source_sha":"1e85fa41738909a1dd3d098ad8ef0986ae76a298"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@groeponline/pi-wishcraft",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.24.0",
|
|
4
4
|
"description": "Pi coding agent extension: operator cockpit with a live powerline status bar, idea queue, skill search, sticky Bash, hooks, policy guardrails, and TUI session UX.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "bun@1.4.2",
|
package/src/config/tokens.ts
CHANGED
|
@@ -29,11 +29,20 @@ export type { TokenRole, WishcraftTokens };
|
|
|
29
29
|
/**
|
|
30
30
|
* Which token each legacy semantic color reads from.
|
|
31
31
|
*
|
|
32
|
-
* `thinking*` is deliberately absent: those
|
|
32
|
+
* `thinking*` is deliberately absent: those seven are Pi's own thinking-level
|
|
33
33
|
* colors, not part of the Wishcraft palette, so they pass through untouched.
|
|
34
34
|
*/
|
|
35
35
|
export const SEMANTIC_TOKEN_ROLE: Record<
|
|
36
|
-
Exclude<
|
|
36
|
+
Exclude<
|
|
37
|
+
SemanticColor,
|
|
38
|
+
| "thinking"
|
|
39
|
+
| "thinkingMinimal"
|
|
40
|
+
| "thinkingLow"
|
|
41
|
+
| "thinkingMedium"
|
|
42
|
+
| "thinkingHigh"
|
|
43
|
+
| "thinkingXhigh"
|
|
44
|
+
| "thinkingMax"
|
|
45
|
+
>,
|
|
37
46
|
TokenRole
|
|
38
47
|
> = {
|
|
39
48
|
model: "primary",
|
|
@@ -54,12 +63,21 @@ export const SEMANTIC_TOKEN_ROLE: Record<
|
|
|
54
63
|
/** Pi thinking-level colors, passed through rather than tokenised. */
|
|
55
64
|
export const THINKING_COLORS: Pick<
|
|
56
65
|
Required<ColorScheme>,
|
|
57
|
-
|
|
66
|
+
| "thinking"
|
|
67
|
+
| "thinkingMinimal"
|
|
68
|
+
| "thinkingLow"
|
|
69
|
+
| "thinkingMedium"
|
|
70
|
+
| "thinkingHigh"
|
|
71
|
+
| "thinkingXhigh"
|
|
72
|
+
| "thinkingMax"
|
|
58
73
|
> = {
|
|
59
74
|
thinking: "thinkingOff",
|
|
60
75
|
thinkingMinimal: "thinkingMinimal",
|
|
61
76
|
thinkingLow: "thinkingLow",
|
|
62
77
|
thinkingMedium: "thinkingMedium",
|
|
78
|
+
thinkingHigh: "rainbow",
|
|
79
|
+
thinkingXhigh: "rainbow",
|
|
80
|
+
thinkingMax: "rainbow",
|
|
63
81
|
};
|
|
64
82
|
|
|
65
83
|
/**
|
package/src/config/types.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { MotionEvent } from "../motion/types.ts";
|
|
|
3
3
|
import type { CostCurrencyCode } from "../usage/rates.ts";
|
|
4
4
|
|
|
5
5
|
// Theme color - either a pi theme color name or a custom hex color
|
|
6
|
-
export type ColorValue = ThemeColor | `#${string}
|
|
6
|
+
export type ColorValue = ThemeColor | `#${string}` | "rainbow";
|
|
7
7
|
export type ThemeLike = Pick<Theme, "fg">;
|
|
8
8
|
|
|
9
9
|
// Semantic color names for segments
|
|
@@ -17,6 +17,9 @@ export type SemanticColor =
|
|
|
17
17
|
| "thinkingMinimal"
|
|
18
18
|
| "thinkingLow"
|
|
19
19
|
| "thinkingMedium"
|
|
20
|
+
| "thinkingHigh"
|
|
21
|
+
| "thinkingXhigh"
|
|
22
|
+
| "thinkingMax"
|
|
20
23
|
| "context"
|
|
21
24
|
| "contextWarn"
|
|
22
25
|
| "contextError"
|
|
@@ -370,6 +370,16 @@ export function createDeckComponent(
|
|
|
370
370
|
if (state.route === "ideas") {
|
|
371
371
|
const ideas = filteredIdeas(liveSnapshot(), state);
|
|
372
372
|
if (!state.navMode && handleList(data, "selectedIdea", ideas.length)) return;
|
|
373
|
+
// Nav ↑↓ must reach the route-column handlers below, like every other
|
|
374
|
+
// list route; the body below used to swallow them (trailing return),
|
|
375
|
+
// stranding the cursor on Ideas forever.
|
|
376
|
+
if (matchesKey(data, "up") || matchesKey(data, "down")) {
|
|
377
|
+
if (state.navMode || ideas.length === 0) {
|
|
378
|
+
// fall through to the nav handlers at the bottom
|
|
379
|
+
} else {
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
} else {
|
|
373
383
|
const cursor = Math.min(state.selectedIdea, Math.max(0, ideas.length - 1));
|
|
374
384
|
const idea = ideas[cursor];
|
|
375
385
|
if (!idea?.id) return;
|
|
@@ -404,6 +414,7 @@ export function createDeckComponent(
|
|
|
404
414
|
return;
|
|
405
415
|
}
|
|
406
416
|
return;
|
|
417
|
+
}
|
|
407
418
|
}
|
|
408
419
|
|
|
409
420
|
if (state.route === "ports") {
|
package/src/git/status.ts
CHANGED
|
@@ -94,6 +94,10 @@ function runGit(args: string[], timeoutMs = 200): Promise<string | null> {
|
|
|
94
94
|
return new Promise((resolve) => {
|
|
95
95
|
const proc = spawn("git", args, {
|
|
96
96
|
stdio: ["ignore", "pipe", "pipe"],
|
|
97
|
+
// Read-only probes must not refresh .git/index — a stray index.lock
|
|
98
|
+
// there races interactive git and bricks the repo (upstream #156/#180).
|
|
99
|
+
env: { ...process.env, GIT_OPTIONAL_LOCKS: "0" },
|
|
100
|
+
windowsHide: true,
|
|
97
101
|
});
|
|
98
102
|
|
|
99
103
|
let stdout = "";
|
package/src/segments/system.ts
CHANGED
|
@@ -32,7 +32,13 @@ export const thinkingSegment: StatusLineSegment = {
|
|
|
32
32
|
const content = `think:${label}`;
|
|
33
33
|
|
|
34
34
|
if (level === "high" || level === "xhigh" || level === "max") {
|
|
35
|
-
|
|
35
|
+
const highKey =
|
|
36
|
+
level === "high"
|
|
37
|
+
? "thinkingHigh"
|
|
38
|
+
: level === "xhigh"
|
|
39
|
+
? "thinkingXhigh"
|
|
40
|
+
: "thinkingMax";
|
|
41
|
+
return { content: color(ctx, highKey, content), visible: true };
|
|
36
42
|
}
|
|
37
43
|
|
|
38
44
|
if (level === "minimal") {
|
package/src/theme/theme.ts
CHANGED
|
@@ -25,6 +25,9 @@ const DEFAULT_COLORS: Required<ColorScheme> = {
|
|
|
25
25
|
thinkingMinimal: "thinkingMinimal",
|
|
26
26
|
thinkingLow: "thinkingLow",
|
|
27
27
|
thinkingMedium: "thinkingMedium",
|
|
28
|
+
thinkingHigh: "rainbow",
|
|
29
|
+
thinkingXhigh: "rainbow",
|
|
30
|
+
thinkingMax: "rainbow",
|
|
28
31
|
context: "dim",
|
|
29
32
|
contextWarn: "warning",
|
|
30
33
|
contextError: "error",
|
|
@@ -189,6 +192,9 @@ export function applyColor(
|
|
|
189
192
|
color: ColorValue,
|
|
190
193
|
text: string,
|
|
191
194
|
): string {
|
|
195
|
+
if (color === "rainbow") {
|
|
196
|
+
return rainbow(text);
|
|
197
|
+
}
|
|
192
198
|
if (typeof color === "string" && /^#[0-9a-fA-F]{6}$/.test(color)) {
|
|
193
199
|
return `${hexToRgbAnsi(color)}${text}\x1b[0m`;
|
|
194
200
|
}
|
package/src/usage/ledger.ts
CHANGED
|
@@ -36,6 +36,11 @@ export function isSessionAssistantMessage(value: unknown): value is AssistantMes
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
export function getUsageTokenTotal(usage: SessionAssistantUsage): number {
|
|
39
|
+
// Prefer the provider-reported total: it can cover token kinds the four
|
|
40
|
+
// fields miss (e.g. reasoning on providers that don't itemize).
|
|
41
|
+
if (typeof usage.totalTokens === "number" && usage.totalTokens > 0) {
|
|
42
|
+
return usage.totalTokens;
|
|
43
|
+
}
|
|
39
44
|
return usage.input + usage.output + usage.cacheRead + usage.cacheWrite;
|
|
40
45
|
}
|
|
41
46
|
|