@pugi/cli 0.1.0-beta.87 → 0.1.0-beta.89
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 +36 -0
- package/LICENSE +1 -1
- package/dist/core/agents/registry.js +1 -1
- package/dist/core/auth/env-provider.js +1 -1
- package/dist/core/checkpoints/shadow-git.js +1 -1
- package/dist/core/context/compaction.js +1 -1
- package/dist/core/context/markdown-traverse.js +1 -1
- package/dist/core/credentials.js +1 -1
- package/dist/core/denial-tracking/state.js +1 -1
- package/dist/core/edits/fuzzy-ladder.js +1 -1
- package/dist/core/edits/layer-a-fuzzy-apply.js +1 -1
- package/dist/core/engine/anvil-client.js +76 -2
- package/dist/core/engine/native-pugi.js +1 -1
- package/dist/core/engine/tool-bridge.js +436 -0
- package/dist/core/hooks/events.js +3 -1
- package/dist/core/hooks/registry.js +3 -0
- package/dist/core/hooks/worktree-events.js +158 -0
- package/dist/core/lsp/client.js +453 -0
- package/dist/core/lsp/server-detect.js +173 -0
- package/dist/core/lsp/symbol-cache.js +162 -0
- package/dist/core/lsp/symbol-tools.js +296 -4
- package/dist/core/mcp/server-tools.js +1 -1
- package/dist/core/mcp/server.js +1 -1
- package/dist/core/memory/secret-scanner.js +6 -6
- package/dist/core/onboarding/ensure-initialized.js +1 -1
- package/dist/core/plans/plan-artifact.js +2 -2
- package/dist/core/repl/ask.js +1 -1
- package/dist/core/repl/cap-warning.js +1 -1
- package/dist/core/repl/session.js +3 -3
- package/dist/core/repl/slash-commands.js +1 -1
- package/dist/core/routing/pre-flight-estimator.js +1 -1
- package/dist/core/settings.js +38 -0
- package/dist/core/worktree/include-parser.js +249 -0
- package/dist/index.js +8 -0
- package/dist/runtime/cli.js +176 -28
- package/dist/runtime/commands/agents.js +1 -1
- package/dist/runtime/commands/config.js +41 -7
- package/dist/runtime/commands/hooks.js +3 -0
- package/dist/runtime/commands/review-consensus.js +1 -1
- package/dist/runtime/sigint-guard.js +272 -0
- package/dist/runtime/version.js +1 -1
- package/dist/runtime/worktree-bootstrap.js +579 -0
- package/dist/skills/bundled/batch.js +2 -2
- package/dist/skills/bundled/index.js +3 -3
- package/dist/skills/bundled/loop.js +2 -2
- package/dist/skills/bundled/remember.js +1 -1
- package/dist/skills/bundled/simplify.js +1 -1
- package/dist/skills/bundled/skillify.js +2 -2
- package/dist/skills/bundled/stuck.js +1 -1
- package/dist/skills/bundled/verify.js +2 -2
- package/dist/testing/vcr.js +2 -2
- package/dist/tools/ask-user-question.js +66 -0
- package/dist/tools/bash.js +2 -2
- package/dist/tools/lsp-tools.js +377 -1
- package/dist/tools/powershell.js +1 -1
- package/dist/tools/registry.js +23 -0
- package/dist/tui/ask-user-question-chips.js +257 -0
- package/dist/tui/input-box.js +1 -1
- package/dist/tui/render.js +1 -1
- package/dist/tui/repl.js +1 -1
- package/dist/tui/status-bar.js +1 -1
- package/dist/tui/update-banner.js +1 -1
- package/dist/tui/welcome-data.js +4 -4
- package/package.json +4 -3
- package/test/scenarios/compact-force.scenario.txt +3 -2
- package/test/scenarios/identity.scenario.txt +6 -5
- package/test/scenarios/persona-handoff.scenario.txt +2 -1
- package/test/scenarios/walkback.scenario.txt +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,42 @@ releases listed first. Section format: `## [<version>] - <YYYY-MM-DD>`.
|
|
|
7
7
|
The bundled `pugi release-notes` command parses this file and renders sections
|
|
8
8
|
strictly newer than `~/.pugi/.last-seen-version` after every upgrade.
|
|
9
9
|
|
|
10
|
+
## [0.1.0-beta.88] - 2026-06-02
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- **Pugi identity intro no longer chants on later turns**. The output gate now
|
|
14
|
+
strips the canonical long-form intro ("I'm Pugi - your engineering copilot.
|
|
15
|
+
Tell me what you need...") and its Russian variant on mid-thread replies.
|
|
16
|
+
Operators were seeing the intro re-emit on turn 2+ after a session-resume
|
|
17
|
+
or autonomous tick.
|
|
18
|
+
- **Ctrl+C double-press exit guard**. A single Ctrl+C in the REPL no longer
|
|
19
|
+
kills the CLI. First press prompts "Press Ctrl+C again to exit (within 2s)
|
|
20
|
+
or any key to continue". Second press within 2s exits cleanly; any other
|
|
21
|
+
key cancels. Headless mode emits a `session-end` envelope and exits 0.
|
|
22
|
+
- **Persona no longer over-clarifies trivial creative tasks**. Asking for a
|
|
23
|
+
well-known game / demo / todo app now picks reasonable defaults and starts
|
|
24
|
+
building in one turn. Ambiguous tasks (auth, deploy targets) still ask
|
|
25
|
+
≤ 3 short choices via the `ask_user_question` tool.
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
- **Short-format AskUserQuestion chip renderer**. Up to 3 questions render
|
|
29
|
+
side-by-side as Ink chips with ▸ default highlight, ↑↓ in-question nav,
|
|
30
|
+
←→/Tab between questions, 1-9 jump, [s] skip-with-default, [Esc] cancel,
|
|
31
|
+
[Enter] commit. Labels truncate at 5 words / 22 chars. Caller can opt
|
|
32
|
+
into a non-TTY numbered fallback via `forceFallback`.
|
|
33
|
+
|
|
34
|
+
### Security
|
|
35
|
+
- **npm publish leak-gate hardening**. `prepublishOnly` now runs a
|
|
36
|
+
banned-string scan against the staged tarball (`tools/scrub/scan-tarball.sh`)
|
|
37
|
+
covering personal names, competitor refs, internal codenames, engineering
|
|
38
|
+
provenance, absolute paths, brand legacy, and secret-token shapes.
|
|
39
|
+
Per-line `[pugi-leak-ok]` allowlist marker for legitimate references;
|
|
40
|
+
path allowlist for LICENSE / THIRD_PARTY_NOTICES files where MIT requires
|
|
41
|
+
copyright-holder name. Synthetic injection regression test runs in CI.
|
|
42
|
+
|
|
43
|
+
### Chore
|
|
44
|
+
- MIT LICENSE copyright holder set to `Pugi.io` (was personal name).
|
|
45
|
+
|
|
10
46
|
## [0.1.0-beta.26] - 2026-05-27
|
|
11
47
|
|
|
12
48
|
### Added
|
package/LICENSE
CHANGED
|
@@ -30,7 +30,7 @@ function requirePersona(slug) {
|
|
|
30
30
|
/**
|
|
31
31
|
* CLI-only role-to-persona mapping. Roles are dispatcher-facing strings;
|
|
32
32
|
* personas come from the brand-canonical THE_TEN. Vera (qa) intentionally
|
|
33
|
-
* dual-roles as verifier + reviewer per
|
|
33
|
+
* dual-roles as verifier + reviewer per — the cabinet's review
|
|
34
34
|
* pipeline already merges the two surfaces.
|
|
35
35
|
*/
|
|
36
36
|
export const SUBAGENT_REGISTRY = [
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `pugi login --provider env` — env-var auth path ().
|
|
3
3
|
*
|
|
4
|
-
* the upstream tool,
|
|
4
|
+
* the upstream tool, peer CLI, and gh CLI all ship a way to authenticate via
|
|
5
5
|
* an environment variable so CI / container / scripted contexts can
|
|
6
6
|
* skip the device flow entirely. This module backs that path:
|
|
7
7
|
*
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Per-task shadow git repo — file-state checkpoint surface.
|
|
3
3
|
*
|
|
4
4
|
* Inspired by Cline `CheckpointTracker.ts` (Apache-2.0).
|
|
5
|
-
*
|
|
5
|
+
* independent implementation TypeScript implementation following Pugi conventions.
|
|
6
6
|
*
|
|
7
7
|
* Goal: every Pugi-orchestrated file mutation lands in a per-task
|
|
8
8
|
* shadow git history kept entirely separate from the user's real
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Per-directory PUGI.md / AGENTS.md / CLAUDE.md / GEMINI.md traverse-up
|
|
3
3
|
* loader — β5a R4+P5.
|
|
4
4
|
*
|
|
5
|
-
* the upstream tool,
|
|
5
|
+
* the upstream tool, peer CLI, and Gemini CLI all support a "walk up from
|
|
6
6
|
* cwd to the workspace root, pick up agent-context markdown at every
|
|
7
7
|
* level" pattern. Without this, a `pugi explain` invoked from
|
|
8
8
|
* `apps/admin-api/` cannot see project-local conventions encoded in
|
package/dist/core/credentials.js
CHANGED
|
@@ -6,7 +6,7 @@ import { z } from 'zod';
|
|
|
6
6
|
* Local credentials store for the Pugi CLI.
|
|
7
7
|
*
|
|
8
8
|
* Stored at `~/.pugi/credentials.json` (mode 0o600). Mirrors the convention
|
|
9
|
-
*
|
|
9
|
+
* peer CLI uses (`~/.codex/auth.json`) and matches gh CLI's per-host
|
|
10
10
|
* token model. The store is intentionally file-based, not OS keychain —
|
|
11
11
|
* adding the native `keytar` dep would force per-platform native builds
|
|
12
12
|
* across npm distribution and complicate the install path. The 0600
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* L11 — DenialTrackingState surface (the upstream tool parity).
|
|
3
3
|
*
|
|
4
|
-
* Per the upstream /
|
|
4
|
+
* Per the upstream / the upstream behavior (``
|
|
5
5
|
* §5.2): the upstream tool's `QueryEngine.ts` maintains a per-session
|
|
6
6
|
* `DenialTrackingState` that records every tool-dispatch denial.
|
|
7
7
|
* Subsequent turns receive a compact reminder so the model does not
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
* >= 0.8 to count as a match; below that we fail LOUD rather
|
|
31
31
|
* than write the wrong region.
|
|
32
32
|
*
|
|
33
|
-
* Inspired by Aider editblock_coder.py (Apache-2.0).
|
|
33
|
+
* Inspired by Aider editblock_coder.py (Apache-2.0). independent implementation
|
|
34
34
|
* implementation; no Aider source code copied.
|
|
35
35
|
*
|
|
36
36
|
* Pure functions throughout — every tier returns a structured result
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* - `identical_replacement` — search and replace are identical;
|
|
20
20
|
* would no-op; surfaced LOUD.
|
|
21
21
|
*
|
|
22
|
-
* Inspired by Aider editblock_coder.py (Apache-2.0).
|
|
22
|
+
* Inspired by Aider editblock_coder.py (Apache-2.0). independent implementation
|
|
23
23
|
* implementation; no Aider source code copied.
|
|
24
24
|
*/
|
|
25
25
|
import { existsSync, readFileSync, renameSync, writeFileSync, unlinkSync } from 'node:fs';
|
|
@@ -64,11 +64,22 @@ export class AnvilEngineLoopClient {
|
|
|
64
64
|
// PR-CLI-SERVER-VERSION-HANDSHAKE . Stamp the outbound
|
|
65
65
|
// X-Pugi-Cli-Version header so the admin-api middleware can
|
|
66
66
|
// decide whether to honour, soft-warn, or 426 this request.
|
|
67
|
-
|
|
67
|
+
// PUGI-260: also stamp `X-Pugi-Context-Tier: 1m` when the
|
|
68
|
+
// operator opted into the long-context lane. The server reads
|
|
69
|
+
// either the body's `contextTier` field OR this header (header is
|
|
70
|
+
// a fallback for older runtimes / non-CLI clients), so emitting
|
|
71
|
+
// both is harmless и belt-and-suspenders. Only emitted for the
|
|
72
|
+
// `'1m'` value — the absence of the header is wire-equivalent к
|
|
73
|
+
// `'standard'`, keeping the default-lane path header-free.
|
|
74
|
+
const baseHeaders = {
|
|
68
75
|
'content-type': 'application/json',
|
|
69
76
|
authorization: `Bearer ${this.config.apiKey}`,
|
|
70
77
|
'user-agent': 'pugi-cli/0.0.1',
|
|
71
|
-
}
|
|
78
|
+
};
|
|
79
|
+
if (options.contextTier === '1m') {
|
|
80
|
+
baseHeaders['x-pugi-context-tier'] = '1m';
|
|
81
|
+
}
|
|
82
|
+
const outboundHeaders = injectClientVersionHeader(baseHeaders, PUGI_CLI_VERSION);
|
|
72
83
|
const res = await fetch(url, {
|
|
73
84
|
method: 'POST',
|
|
74
85
|
headers: outboundHeaders,
|
|
@@ -246,6 +257,23 @@ export class AnvilEngineLoopClient {
|
|
|
246
257
|
message: 'runtime rate limit reached for this tenant',
|
|
247
258
|
};
|
|
248
259
|
}
|
|
260
|
+
// PUGI-490 (2026-06-03): structured envelope for upstream-proxy
|
|
261
|
+
// static error pages. When the response body is HTML (the upstream
|
|
262
|
+
// proxy's static 5xx page rather than a NestJS JSON envelope), the
|
|
263
|
+
// raw truncated HTML is useless to operators — it cannot point at
|
|
264
|
+
// what failed. Parse the `cf-ray` request id so the operator can
|
|
265
|
+
// look the request up in the proxy dashboard, and surface a
|
|
266
|
+
// remediation hint pointing at the engine VM logs.
|
|
267
|
+
const cfDetails = detectUpstreamProxyError(res, text);
|
|
268
|
+
if (cfDetails) {
|
|
269
|
+
return {
|
|
270
|
+
stop: 'error',
|
|
271
|
+
code: 'failed',
|
|
272
|
+
message: `runtime error (cf-ray: ${cfDetails.cfRay ?? 'unknown'}) — ` +
|
|
273
|
+
`upstream proxy returned ${res.status} static page. ` +
|
|
274
|
+
`Check engine VM logs for the correlating request id.`,
|
|
275
|
+
};
|
|
276
|
+
}
|
|
249
277
|
return {
|
|
250
278
|
stop: 'error',
|
|
251
279
|
code: 'failed',
|
|
@@ -267,4 +295,50 @@ export class AnvilEngineLoopClient {
|
|
|
267
295
|
}
|
|
268
296
|
}
|
|
269
297
|
}
|
|
298
|
+
/**
|
|
299
|
+
* PUGI-490 (2026-06-03): detect when the response body is a static HTML
|
|
300
|
+
* error page emitted by the upstream proxy (rather than a JSON envelope
|
|
301
|
+
* produced by admin-api). Returns the cf-ray request id when matched, so
|
|
302
|
+
* the CLI can surface a meaningful runtime-error envelope instead of
|
|
303
|
+
* truncating raw HTML in front of the operator.
|
|
304
|
+
*
|
|
305
|
+
* Heuristic: the body starts with `<!DOCTYPE` or `<html`, or carries the
|
|
306
|
+
* `cf-ray` response header. We require BOTH conditions when the body is
|
|
307
|
+
* non-empty (defense against admin-api accidentally emitting an HTML
|
|
308
|
+
* fragment) and accept just the header when the body is empty (some
|
|
309
|
+
* proxy 5xx variants ship empty bodies). The function is intentionally
|
|
310
|
+
* permissive — false positives produce a clearer error than false
|
|
311
|
+
* negatives, and the message still includes "upstream proxy" so the
|
|
312
|
+
* operator knows the call did not reach a Pugi controller.
|
|
313
|
+
*
|
|
314
|
+
* Exported for unit testing; not for runtime callers.
|
|
315
|
+
*/
|
|
316
|
+
export function detectUpstreamProxyError(res, body) {
|
|
317
|
+
if (res.status < 500)
|
|
318
|
+
return null;
|
|
319
|
+
// Pull cf-ray via the same getter shim the version-interceptor uses;
|
|
320
|
+
// it tolerates both real `Response.headers.get` and fixture/stub
|
|
321
|
+
// headers represented as plain objects.
|
|
322
|
+
const h = res.headers;
|
|
323
|
+
const readHeader = (name) => {
|
|
324
|
+
if (h && typeof h.get === 'function') {
|
|
325
|
+
return h.get(name);
|
|
326
|
+
}
|
|
327
|
+
if (h && typeof h === 'object') {
|
|
328
|
+
const lowered = h[name.toLowerCase()];
|
|
329
|
+
return lowered ?? null;
|
|
330
|
+
}
|
|
331
|
+
return null;
|
|
332
|
+
};
|
|
333
|
+
const cfRay = readHeader('cf-ray');
|
|
334
|
+
const bodyTrimmed = (body ?? '').trimStart();
|
|
335
|
+
const looksHtml = bodyTrimmed.startsWith('<!DOCTYPE') ||
|
|
336
|
+
bodyTrimmed.startsWith('<!doctype') ||
|
|
337
|
+
bodyTrimmed.startsWith('<html');
|
|
338
|
+
if (looksHtml)
|
|
339
|
+
return { cfRay };
|
|
340
|
+
if (cfRay && bodyTrimmed.length === 0)
|
|
341
|
+
return { cfRay };
|
|
342
|
+
return null;
|
|
343
|
+
}
|
|
270
344
|
//# sourceMappingURL=anvil-client.js.map
|
|
@@ -1127,7 +1127,7 @@ function extractPathArg(raw) {
|
|
|
1127
1127
|
const parsed = JSON.parse(raw);
|
|
1128
1128
|
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
1129
1129
|
const obj = parsed;
|
|
1130
|
-
// Accept canonical `path` OR the
|
|
1130
|
+
// Accept canonical `path` OR the peer-CLI-trained `filePath`
|
|
1131
1131
|
// alias so the filesChanged summary captures writes regardless of
|
|
1132
1132
|
// which key the model emitted. Without the alias the operator
|
|
1133
1133
|
// sees "Files modified: none" even when a write actually landed,
|