pi-gauntlet 5.5.0 → 5.5.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/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## v5.5.1 - 2026-09-10
4
+
5
+ - `linear`: copyable, version-scoped recovery for attachment-download 401s resolves the decrypted credential through linearis instead of reading encrypted token storage. Restricts credential delivery to HTTPS Linear uploads, rejects redirects, and checks downloaded bytes; an offline regression executes the documented example.
6
+
3
7
  ## v5.5.0 - 2026-09-10
4
8
 
5
9
  - `writing-plans`: every task carries a `**Tests:**` block - scoped commands anchored to the task's `Test:` paths, optional `via:` entry point, or `none: <category>`; the plan grammar `plan_check` enforces moves to `skills/writing-plans/reference/plan-contract.md`. (#28)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-gauntlet",
3
- "version": "5.5.0",
3
+ "version": "5.5.1",
4
4
  "description": "Opinionated, gated workflow skills, subagent personas, and runtime extensions for the pi coding agent.",
5
5
  "author": "Jacek Juraszek",
6
6
  "type": "module",
@@ -24,7 +24,9 @@ this skill alters re-gates.
24
24
  Preferred: `linearis` on PATH and authenticated (`linearis auth status`). Token
25
25
  resolution order: `--api-token`, `LINEAR_API_TOKEN`, `~/.linearis/token`. This is a
26
26
  preference, not a precondition - a missing or unauthenticated CLI degrades Linear
27
- functionality and is reported, never blocks the run.
27
+ functionality and is reported, never blocks the run. The token file is encrypted
28
+ storage: never use its contents as an HTTP credential or infer the credential type
29
+ from its `v1:` storage-format prefix. Resolve it through linearis instead.
28
30
 
29
31
  > **No `linearis` installed?** If `command -v linearis` fails, fall back to a
30
32
  > **Linear MCP server** when the harness has one configured - its tools cover the
@@ -192,7 +194,7 @@ Safety rules, in addition to the write gate above:
192
194
  | Symptom | Cause | Fix |
193
195
  |---|---|---|
194
196
  | 401 | Not authenticated / expired token | `linearis auth status`; re-auth - unless the download row below applies. |
195
- | 401 on `files download` while `issues read` works | linearis 2026.7.0 and 2026.8.0 prepend `Bearer ` to personal API keys on file downloads ([linearis-oss/linearis#300](https://github.com/linearis-oss/linearis/issues/300)) | Not an auth problem - do not re-auth. Fetch the URL with the bare key, or use a version without the bug once one ships. |
197
+ | 401 on `files download` while `issues read` works | linearis 2026.7.0 and 2026.8.0 prepend `Bearer ` to personal API keys on file downloads ([linearis-oss/linearis#300](https://github.com/linearis-oss/linearis/issues/300)) | Not an auth problem - do not re-auth. Use the recovery below, or use a version without the bug once one ships. |
196
198
  | Issue not found | Wrong workspace, or issue archived | Confirm workspace; check archived state. |
197
199
  | Status not found | Status name doesn't match the team's workflow states | List the team's states before setting one. |
198
200
  | Missing `--team` error on create | `--team` is required | Supply `--team <default team>`. |
@@ -203,6 +205,45 @@ Safety rules, in addition to the write gate above:
203
205
  | Read is slow | Big ticket with many comments/attachments | Drop `--with-*` flags not needed. |
204
206
  | Parser-shape failure on a documented invocation: unknown command/option, unexpected argument | Section 3's snapshot may have drifted from the installed CLI | Re-read that subcommand's `--help`; report the row stale **only if** help actually contradicts it, then follow help |
205
207
 
208
+ For that download-only 401, this 2026.7.0/2026.8.0 workaround calls linearis's
209
+ version-specific internal `getApiToken` API. Supply the fresh `uploads.linear.app` URL
210
+ from `issues read --with-attachments` and an output path. It rejects other hosts and
211
+ redirects, sends the resolved key without `Bearer`, writes only a non-empty response,
212
+ and never prints or stores the key separately:
213
+
214
+ <!-- linear-download-recovery:start -->
215
+ ```bash
216
+ download_linear_asset() {
217
+ LINEARIS_BIN="${LINEARIS_BIN:-$(command -v linearis)}" node --input-type=module - "$1" "$2" <<'NODE'
218
+ import { realpathSync, writeFileSync } from "node:fs";
219
+ import { dirname, join } from "node:path";
220
+ import { pathToFileURL } from "node:url";
221
+
222
+ const [urlText, output] = process.argv.slice(2);
223
+ const url = new URL(urlText);
224
+ if (url.protocol !== "https:" || url.hostname !== "uploads.linear.app")
225
+ throw new Error("refusing to send a credential outside https://uploads.linear.app");
226
+ const packageRoot = dirname(dirname(realpathSync(process.env.LINEARIS_BIN)));
227
+ const { getApiToken } = await import(pathToFileURL(join(packageRoot, "dist/common/auth.js")));
228
+ const response = await fetch(url, {
229
+ headers: { Authorization: getApiToken({}) },
230
+ redirect: "error",
231
+ });
232
+ if (!response.ok) throw new Error(`download failed: HTTP ${response.status}`);
233
+ const bytes = new Uint8Array(await response.arrayBuffer());
234
+ if (bytes.byteLength === 0) throw new Error("download failed: empty response");
235
+ writeFileSync(output, bytes);
236
+ console.log(`downloaded ${bytes.byteLength} bytes to ${output}`);
237
+ NODE
238
+ }
239
+ download_linear_asset 'https://uploads.linear.app/...' '/tmp/attachment'
240
+ ```
241
+ <!-- linear-download-recovery:end -->
242
+
243
+ Do not declare the attachment inaccessible until this recovery used the resolved
244
+ credential; reading `~/.linearis/token` directly does not count. Afterward, confirm the
245
+ reported byte count and inspect the file type before consuming or extracting it.
246
+
206
247
  The last row's trigger is deliberately narrow. Data, auth, status-name, and root-thread
207
248
  validation errors have their own rows above and are **not** drift - routing them to "the
208
249
  skill is stale" would misdiagnose ordinary failures. This row is the reactive path for