@pugi/cli 0.1.0-beta.8 → 0.1.0-beta.87
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 +96 -0
- package/THIRD_PARTY_NOTICES.md +40 -0
- package/assets/pugi-prozr2-mascot.ansi +9 -0
- package/bin/run.js +33 -1
- package/dist/commands/deploy.js +40 -40
- package/dist/commands/flatten.js +191 -0
- package/dist/commands/jobs-watch.js +201 -0
- package/dist/commands/jobs.js +42 -27
- package/dist/commands/smoke.js +133 -0
- package/dist/core/agent-progress/cleanup.js +134 -0
- package/dist/core/agent-progress/schema.js +144 -0
- package/dist/core/agent-progress/writer.js +101 -0
- package/dist/core/agents/adaptive-router.js +330 -0
- package/dist/core/agents/query-decomposer.js +297 -0
- package/dist/core/agents/registry.js +2 -2
- package/dist/core/approvals/shortcut-resolver.js +98 -0
- package/dist/core/artifact-chain/dispatcher.js +148 -0
- package/dist/core/artifact-chain/exporter.js +164 -0
- package/dist/core/artifact-chain/state.js +243 -0
- package/dist/core/artifact-chain/steps.js +169 -0
- package/dist/core/ask-user/question.js +92 -0
- package/dist/core/audit/audit-trail.js +275 -0
- package/dist/core/auth/ensure-authenticated.js +129 -0
- package/dist/core/auth/env-provider.js +238 -0
- package/dist/core/auto-open-browser.js +4 -4
- package/dist/core/auto-update/channels.js +122 -0
- package/dist/core/auto-update/checker.js +241 -0
- package/dist/core/auto-update/state.js +235 -0
- package/dist/core/bare-mode/index.js +107 -0
- package/dist/core/bash/redirect.js +281 -0
- package/dist/core/bash-classifier.js +436 -40
- package/dist/core/checkpoint/resumer.js +149 -0
- package/dist/core/checkpoint/rewinder.js +291 -0
- package/dist/core/checkpoints/shadow-git.js +670 -0
- package/dist/core/citations/parser.js +109 -0
- package/dist/core/classifier/yolo-classifier.js +88 -0
- package/dist/core/codegraph/decision-store.js +248 -0
- package/dist/core/codegraph/detect-repo.js +459 -0
- package/dist/core/codegraph/install.js +134 -0
- package/dist/core/codegraph/offer-hook.js +220 -0
- package/dist/core/compact/auto-trigger.js +96 -0
- package/dist/core/compact/buffer-rewriter.js +115 -0
- package/dist/core/compact/summarizer.js +208 -0
- package/dist/core/compact/token-counter.js +108 -0
- package/dist/core/consensus/anvil-fanout.js +25 -25
- package/dist/core/consensus/diff-capture.js +121 -12
- package/dist/core/consensus/rubric.js +21 -21
- package/dist/core/context/builder.js +6 -6
- package/dist/core/context/compaction-events.js +8 -8
- package/dist/core/context/compaction.js +31 -31
- package/dist/core/context/index.js +15 -8
- package/dist/core/context/invariants.js +51 -51
- package/dist/core/context/markdown-loader.js +28 -10
- package/dist/core/context/markdown-traverse.js +255 -0
- package/dist/core/context/pugiignore.js +41 -41
- package/dist/core/context/repo-skeleton.js +37 -37
- package/dist/core/context/tool-eviction.js +55 -0
- package/dist/core/context/watcher.js +32 -32
- package/dist/core/context/working-set.js +23 -23
- package/dist/core/coordinator/agent-tools.js +77 -0
- package/dist/core/coordinator/agent-toolset.js +65 -0
- package/dist/core/coordinator/fsm.js +73 -0
- package/dist/core/coordinator/mode-fsm.js +70 -0
- package/dist/core/cost/rate-card.js +129 -0
- package/dist/core/cost/tracker.js +221 -0
- package/dist/core/credentials.js +12 -12
- package/dist/core/cron/scheduler.js +138 -0
- package/dist/core/denial-tracking/index.js +8 -0
- package/dist/core/denial-tracking/state.js +264 -0
- package/dist/core/diagnostics/probe-runner.js +93 -0
- package/dist/core/diagnostics/probes/api.js +46 -0
- package/dist/core/diagnostics/probes/auth.js +93 -0
- package/dist/core/diagnostics/probes/bare-mode.js +42 -0
- package/dist/core/diagnostics/probes/cli-version.js +127 -0
- package/dist/core/diagnostics/probes/config.js +72 -0
- package/dist/core/diagnostics/probes/denial-tracking.js +57 -0
- package/dist/core/diagnostics/probes/disk.js +81 -0
- package/dist/core/diagnostics/probes/engine-live.js +46 -0
- package/dist/core/diagnostics/probes/git.js +65 -0
- package/dist/core/diagnostics/probes/hooks.js +118 -0
- package/dist/core/diagnostics/probes/mcp.js +75 -0
- package/dist/core/diagnostics/probes/node.js +59 -0
- package/dist/core/diagnostics/probes/pnpm.js +36 -0
- package/dist/core/diagnostics/probes/pugi-md.js +89 -0
- package/dist/core/diagnostics/probes/sandbox.js +40 -0
- package/dist/core/diagnostics/probes/session.js +74 -0
- package/dist/core/diagnostics/probes/status-snapshot.js +488 -0
- package/dist/core/diagnostics/probes/workspace.js +63 -0
- package/dist/core/diagnostics/types.js +70 -0
- package/dist/core/dispatch/cache-cleanup.js +197 -0
- package/dist/core/dispatch/cache-handoff.js +295 -0
- package/dist/core/edits/apply-patch-layer-e.js +189 -0
- package/dist/core/edits/dispatch.js +293 -7
- package/dist/core/edits/format-matrix.js +26 -0
- package/dist/core/edits/fuzzy-ladder.js +650 -0
- package/dist/core/edits/index.js +3 -1
- package/dist/core/edits/journal.js +199 -0
- package/dist/core/edits/layer-a-apply.js +15 -15
- package/dist/core/edits/layer-a-fuzzy-apply.js +198 -0
- package/dist/core/edits/layer-b-apply.js +9 -9
- package/dist/core/edits/layer-c-apply.js +6 -6
- package/dist/core/edits/layer-d-ast.js +557 -14
- package/dist/core/edits/marker-parser.js +12 -12
- package/dist/core/edits/security-gate.js +27 -27
- package/dist/core/edits/verify-hook.js +273 -0
- package/dist/core/edits/worktree.js +322 -0
- package/dist/core/engine/anvil-client.js +140 -26
- package/dist/core/engine/auto-compact.js +179 -0
- package/dist/core/engine/budgets.js +186 -0
- package/dist/core/engine/context-prefix.js +155 -0
- package/dist/core/engine/index.js +1 -1
- package/dist/core/engine/intensity.js +158 -0
- package/dist/core/engine/intent.js +260 -0
- package/dist/core/engine/native-pugi.js +1295 -227
- package/dist/core/engine/prompts.js +134 -16
- package/dist/core/engine/strip-internal-fields.js +124 -0
- package/dist/core/engine/tool-bridge.js +1295 -59
- package/dist/core/evaluation/golden-dataset.js +293 -0
- package/dist/core/feedback/queue.js +177 -0
- package/dist/core/feedback/submitter.js +145 -0
- package/dist/core/file-cache.js +113 -1
- package/dist/core/flatten/flatten-repo.js +439 -0
- package/dist/core/format/osc8-link.js +28 -0
- package/dist/core/hook-chains.js +392 -0
- package/dist/core/hooks/citation-verify-hook.js +138 -0
- package/dist/core/hooks/citation-verify.js +112 -0
- package/dist/core/hooks/events.js +44 -0
- package/dist/core/hooks/index.js +15 -0
- package/dist/core/hooks/registry.js +213 -0
- package/dist/core/hooks/runner.js +236 -0
- package/dist/core/hooks/v2/event-emitter.js +115 -0
- package/dist/core/hooks/v2/executor.js +282 -0
- package/dist/core/hooks/v2/index.js +25 -0
- package/dist/core/hooks/v2/lifecycle.js +104 -0
- package/dist/core/hooks/v2/loader.js +216 -0
- package/dist/core/hooks/v2/matcher.js +125 -0
- package/dist/core/hooks/v2/trust.js +143 -0
- package/dist/core/hooks/v2/types.js +86 -0
- package/dist/core/image/renderer.js +71 -0
- package/dist/core/init/detector.js +582 -0
- package/dist/core/init/template-renderer.js +242 -0
- package/dist/core/jobs/registry.js +18 -18
- package/dist/core/ledger/results-tsv.js +142 -0
- package/dist/core/log-discipline/stdout-redirect.js +51 -0
- package/dist/core/lsp/cache.js +105 -0
- package/dist/core/lsp/client.js +776 -0
- package/dist/core/lsp/language-detect.js +66 -0
- package/dist/core/lsp/post-edit-diagnostics.js +171 -0
- package/dist/core/lsp/symbol-tools.js +372 -0
- package/dist/core/mcp/client.js +97 -28
- package/dist/core/mcp/http-server.js +553 -0
- package/dist/core/mcp/orchestrator-tools.js +662 -0
- package/dist/core/mcp/permission.js +190 -0
- package/dist/core/mcp/registry.js +39 -17
- package/dist/core/mcp/server-tools.js +219 -0
- package/dist/core/mcp/server.js +397 -0
- package/dist/core/mcp/trust.js +10 -10
- package/dist/core/memory/dual-write.js +416 -0
- package/dist/core/memory/passive-extract.js +130 -0
- package/dist/core/memory/phase1-kinds.js +20 -0
- package/dist/core/memory/secret-scanner.js +304 -0
- package/dist/core/memory-sync/queue.js +170 -0
- package/dist/core/metrics/extract.js +113 -0
- package/dist/core/modes/roo-modes.js +68 -0
- package/dist/core/onboarding/ensure-initialized.js +133 -0
- package/dist/core/onboarding/marker.js +111 -0
- package/dist/core/onboarding/telemetry-state.js +108 -0
- package/dist/core/output-style/presets.js +176 -0
- package/dist/core/output-style/state.js +185 -0
- package/dist/core/path-security.js +287 -5
- package/dist/core/permission.js +82 -22
- package/dist/core/permissions/auto-classifier.js +124 -0
- package/dist/core/permissions/bash-parser.js +371 -0
- package/dist/core/permissions/circuit-breaker.js +83 -0
- package/dist/core/permissions/constrained-edit.js +91 -0
- package/dist/core/permissions/gate.js +278 -0
- package/dist/core/permissions/index.js +20 -0
- package/dist/core/permissions/mode.js +174 -0
- package/dist/core/permissions/network-egress.js +137 -0
- package/dist/core/permissions/state.js +241 -0
- package/dist/core/permissions/tool-class.js +93 -0
- package/dist/core/plan-mode/ui-state.js +51 -0
- package/dist/core/plans/plan-artifact.js +721 -0
- package/dist/core/policy-limits/etag-store.js +122 -0
- package/dist/core/prd-check/parser.js +215 -0
- package/dist/core/prd-check/reporter.js +127 -0
- package/dist/core/prd-check/session-review.js +557 -0
- package/dist/core/prd-check/verifiers.js +223 -0
- package/dist/core/prompt-cache/client-cache.js +99 -0
- package/dist/core/prompts/assembly.js +29 -0
- package/dist/core/prompts/registry.js +364 -0
- package/dist/core/pugi-md/cc-compat-rules.js +735 -0
- package/dist/core/pugi-md/context-injector.js +76 -0
- package/dist/core/pugi-md/walk-up.js +207 -0
- package/dist/core/python/uv-installer.js +270 -0
- package/dist/core/python/uv-resolver.js +83 -0
- package/dist/core/rate-limit/narrator.js +146 -0
- package/dist/core/recipes/cli-types.js +20 -0
- package/dist/core/recipes/loader.js +103 -0
- package/dist/core/recipes/runner.js +345 -0
- package/dist/core/recipes/schema.js +587 -0
- package/dist/core/release-notes/parser.js +241 -0
- package/dist/core/release-notes/state.js +116 -0
- package/dist/core/repl/ask.js +37 -37
- package/dist/core/repl/cancellation.js +26 -26
- package/dist/core/repl/cap-warning.js +4 -4
- package/dist/core/repl/clipboard-read.js +11 -11
- package/dist/core/repl/dispatch-fsm.js +12 -12
- package/dist/core/repl/history-search.js +15 -15
- package/dist/core/repl/history.js +28 -18
- package/dist/core/repl/kill-ring.js +5 -5
- package/dist/core/repl/model-pricing.js +135 -0
- package/dist/core/repl/privacy-banner.js +22 -22
- package/dist/core/repl/session.js +2157 -214
- package/dist/core/repl/slash-commands.js +533 -40
- package/dist/core/repl/store/index.js +1 -1
- package/dist/core/repl/store/jsonl-log.js +22 -22
- package/dist/core/repl/store/lockfile.js +10 -10
- package/dist/core/repl/store/session-store.js +136 -107
- package/dist/core/repl/store/types.js +15 -15
- package/dist/core/repl/store/uuid-v7.js +12 -12
- package/dist/core/repl/workspace-context.js +43 -21
- package/dist/core/repo-map/build.js +125 -0
- package/dist/core/repo-map/cache.js +185 -0
- package/dist/core/repo-map/extractor.js +254 -0
- package/dist/core/repo-map/formatter.js +145 -0
- package/dist/core/repo-map/page-rank.js +105 -0
- package/dist/core/repo-map/scanner.js +211 -0
- package/dist/core/retry-budget/budget.js +284 -0
- package/dist/core/retry-budget/index.js +5 -0
- package/dist/core/retry-budget/retry-cap.js +74 -0
- package/dist/core/routing/lead-worker.js +43 -0
- package/dist/core/routing/pre-flight-estimator.js +108 -0
- package/dist/core/runs/run-tree.js +103 -0
- package/dist/core/security/injection-scanner.js +367 -0
- package/dist/core/security/output-filter.js +418 -0
- package/dist/core/session/env-file.js +105 -0
- package/dist/core/session/section-budgets.js +140 -0
- package/dist/core/session.js +92 -0
- package/dist/core/settings.js +286 -5
- package/dist/core/share/formatter.js +271 -0
- package/dist/core/share/redactor.js +221 -0
- package/dist/core/share/uploader.js +267 -0
- package/dist/core/skills/defaults.js +457 -0
- package/dist/core/skills/loader.js +22 -22
- package/dist/core/skills/sources.js +27 -27
- package/dist/core/smoke/headless-driver.js +174 -0
- package/dist/core/smoke/orchestrator.js +194 -0
- package/dist/core/smoke/runner.js +238 -0
- package/dist/core/smoke/scenario-parser.js +316 -0
- package/dist/core/statusline.js +99 -0
- package/dist/core/subagents/dispatcher-real.js +600 -0
- package/dist/core/subagents/dispatcher.js +132 -43
- package/dist/core/subagents/index.js +19 -6
- package/dist/core/subagents/isolation-matrix.js +213 -0
- package/dist/core/subagents/spawn.js +19 -4
- package/dist/core/telemetry/emitter.js +229 -0
- package/dist/core/telemetry/queue.js +251 -0
- package/dist/core/theme/context.js +91 -0
- package/dist/core/theme/presets.js +228 -0
- package/dist/core/theme/state.js +181 -0
- package/dist/core/todos/invariant.js +10 -0
- package/dist/core/todos/state.js +177 -0
- package/dist/core/tool-schema/compressor.js +89 -0
- package/dist/core/transport/version-interceptor.js +166 -0
- package/dist/core/trust.js +2 -2
- package/dist/core/tui/thinking-block.js +64 -0
- package/dist/core/vim/keymap.js +288 -0
- package/dist/core/vim/state.js +92 -0
- package/dist/core/watch-markers/marker-watcher.js +133 -0
- package/dist/core/worktree-manager/cleanup.js +123 -0
- package/dist/core/worktree-manager/manager.js +303 -0
- package/dist/index.js +28 -0
- package/dist/runtime/bootstrap.js +190 -0
- package/dist/runtime/cli.js +4151 -489
- package/dist/runtime/commands/agents.js +30 -30
- package/dist/runtime/commands/budget.js +5 -5
- package/dist/runtime/commands/cancel.js +231 -0
- package/dist/runtime/commands/chain.js +489 -0
- package/dist/runtime/commands/codegraph-status.js +227 -0
- package/dist/runtime/commands/compact.js +297 -0
- package/dist/runtime/commands/config.js +32 -32
- package/dist/runtime/commands/cost.js +199 -0
- package/dist/runtime/commands/delegate.js +244 -13
- package/dist/runtime/commands/dispatch.js +126 -0
- package/dist/runtime/commands/doctor.js +579 -0
- package/dist/runtime/commands/feedback.js +184 -0
- package/dist/runtime/commands/hooks.js +184 -0
- package/dist/runtime/commands/init.js +254 -0
- package/dist/runtime/commands/lsp.js +368 -0
- package/dist/runtime/commands/mcp.js +879 -0
- package/dist/runtime/commands/memory.js +582 -0
- package/dist/runtime/commands/model.js +237 -0
- package/dist/runtime/commands/onboarding.js +275 -0
- package/dist/runtime/commands/patch.js +128 -0
- package/dist/runtime/commands/permissions.js +112 -0
- package/dist/runtime/commands/plan.js +143 -0
- package/dist/runtime/commands/prd-check.js +285 -0
- package/dist/runtime/commands/privacy.js +17 -17
- package/dist/runtime/commands/recipe.js +325 -0
- package/dist/runtime/commands/redo-blob-store.js +92 -0
- package/dist/runtime/commands/redo.js +361 -0
- package/dist/runtime/commands/release-notes.js +229 -0
- package/dist/runtime/commands/repo-map.js +95 -0
- package/dist/runtime/commands/report.js +299 -0
- package/dist/runtime/commands/resume.js +118 -0
- package/dist/runtime/commands/review-consensus.js +68 -53
- package/dist/runtime/commands/rewind.js +333 -0
- package/dist/runtime/commands/roster.js +14 -14
- package/dist/runtime/commands/sessions.js +163 -0
- package/dist/runtime/commands/share.js +316 -0
- package/dist/runtime/commands/skills.js +31 -31
- package/dist/runtime/commands/status.js +186 -0
- package/dist/runtime/commands/stickers.js +82 -0
- package/dist/runtime/commands/style.js +194 -0
- package/dist/runtime/commands/theme.js +196 -0
- package/dist/runtime/commands/undo.js +54 -22
- package/dist/runtime/commands/update.js +289 -0
- package/dist/runtime/commands/vim.js +140 -0
- package/dist/runtime/commands/worktree.js +177 -0
- package/dist/runtime/commands/worktrees.js +155 -0
- package/dist/runtime/headless-repl.js +195 -0
- package/dist/runtime/headless.js +543 -0
- package/dist/runtime/load-hooks-or-exit.js +71 -0
- package/dist/runtime/plan-decompose.js +531 -0
- package/dist/runtime/update-check.js +28 -28
- package/dist/runtime/version.js +65 -0
- package/dist/skills/bundled/batch.js +617 -0
- package/dist/skills/bundled/index.js +45 -0
- package/dist/skills/bundled/loop.js +358 -0
- package/dist/skills/bundled/remember.js +383 -0
- package/dist/skills/bundled/simplify.js +289 -0
- package/dist/skills/bundled/skillify.js +373 -0
- package/dist/skills/bundled/stuck.js +558 -0
- package/dist/skills/bundled/verify.js +439 -0
- package/dist/testing/vcr.js +486 -0
- package/dist/tools/agent-tool.js +229 -0
- package/dist/tools/apply-patch.js +556 -0
- package/dist/tools/ask-user-question.js +222 -0
- package/dist/tools/ask-user.js +115 -0
- package/dist/tools/bash.js +623 -45
- package/dist/tools/brief.js +224 -0
- package/dist/tools/enter-worktree.js +250 -0
- package/dist/tools/exit-worktree.js +147 -0
- package/dist/tools/file-tools.js +161 -44
- package/dist/tools/lsp-tools.js +189 -0
- package/dist/tools/mcp-tool.js +260 -0
- package/dist/tools/multi-edit.js +361 -0
- package/dist/tools/powershell.js +268 -0
- package/dist/tools/registry.js +85 -0
- package/dist/tools/skill-tool.js +96 -0
- package/dist/tools/sleep.js +99 -0
- package/dist/tools/synthetic-output.js +133 -0
- package/dist/tools/tasks.js +208 -0
- package/dist/tools/todo-write.js +184 -0
- package/dist/tools/verify-plan-execution.js +295 -0
- package/dist/tools/web-fetch-injection-scanner.js +207 -0
- package/dist/tools/web-fetch.js +195 -10
- package/dist/tools/web-search.js +458 -0
- package/dist/tui/agent-progress-card.js +111 -0
- package/dist/tui/agent-tree.js +11 -1
- package/dist/tui/ask-modal.js +14 -14
- package/dist/tui/ask-user-question-prompt.js +203 -0
- package/dist/tui/compact-banner.js +81 -0
- package/dist/tui/conversation-pane.js +85 -11
- package/dist/tui/cost-table.js +111 -0
- package/dist/tui/device-flow.js +2 -2
- package/dist/tui/doctor-table.js +46 -0
- package/dist/tui/feedback-prompt.js +156 -0
- package/dist/tui/input-box.js +247 -32
- package/dist/tui/login-picker.js +3 -3
- package/dist/tui/markdown-render.js +6 -6
- package/dist/tui/onboarding-wizard.js +240 -0
- package/dist/tui/permissions-picker.js +86 -0
- package/dist/tui/render.js +35 -0
- package/dist/tui/repl-render.js +332 -54
- package/dist/tui/repl-splash-art.js +16 -16
- package/dist/tui/repl-splash-mascot.js +48 -24
- package/dist/tui/repl-splash.js +22 -22
- package/dist/tui/repl.js +124 -44
- package/dist/tui/slash-palette.js +6 -6
- package/dist/tui/splash.js +2 -2
- package/dist/tui/status-bar.js +109 -31
- package/dist/tui/status-table.js +7 -0
- package/dist/tui/stickers-art.js +136 -0
- package/dist/tui/style-table.js +28 -0
- package/dist/tui/theme-table.js +29 -0
- package/dist/tui/thinking-spinner.js +123 -0
- package/dist/tui/tool-stream-pane.js +53 -4
- package/dist/tui/update-banner.js +27 -2
- package/dist/tui/vim-input.js +267 -0
- package/dist/tui/welcome-banner.js +107 -0
- package/dist/tui/welcome-data.js +293 -0
- package/dist/tui/workspace-context.js +2 -2
- package/docs/examples/codegraph.mcp.json +10 -0
- package/package.json +23 -6
- package/test/scenarios/codegen-create-file.scenario.txt +13 -0
- package/test/scenarios/compact-force.scenario.txt +11 -0
- package/test/scenarios/identity.scenario.txt +11 -0
- package/test/scenarios/persona-handoff.scenario.txt +11 -0
- package/test/scenarios/walkback.scenario.txt +12 -0
- package/dist/core/engine/compaction-hook.js +0 -154
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keep-a-Changelog parser — .
|
|
3
|
+
*
|
|
4
|
+
* Parses a `CHANGELOG.md` written в the Keep-a-Changelog v1.1 layout
|
|
5
|
+
* (https://keepachangelog.com) into an ordered list of version
|
|
6
|
+
* sections. The parser is intentionally minimal — it understands the
|
|
7
|
+
* `## [<version>] - <date>` header marker and the section body up к
|
|
8
|
+
* the next header, and ignores every other Markdown construct (links,
|
|
9
|
+
* footnotes, sub-sub-headers). That is enough к drive the
|
|
10
|
+
* `pugi release-notes` diff between last-seen and current.
|
|
11
|
+
*
|
|
12
|
+
* # Module contract
|
|
13
|
+
*
|
|
14
|
+
* - Pure function. Takes the raw CHANGELOG text + returns parsed
|
|
15
|
+
* sections. Zero IO; the file read happens at the call site so the
|
|
16
|
+
* spec can pin fixtures without touching disk.
|
|
17
|
+
*
|
|
18
|
+
* - Header grammar: `## [<version>] - <YYYY-MM-DD>`. The leading
|
|
19
|
+
* `## ` is required (h2). The version is everything between the
|
|
20
|
+
* square brackets — semver-shaped strings are not validated
|
|
21
|
+
* beyond non-emptiness so pre-release tags like `0.1.0-beta.21`
|
|
22
|
+
* parse correctly. The date is captured verbatim; the comparator
|
|
23
|
+
* does not parse it — version ordering is enough.
|
|
24
|
+
*
|
|
25
|
+
* - Section body: every line from the header (exclusive) up к the
|
|
26
|
+
* next `## [...] - ...` header (exclusive). Lines are joined
|
|
27
|
+
* verbatim with `\n`; leading + trailing blank lines are trimmed
|
|
28
|
+
* so the renderer can paste sections back-to-back without double
|
|
29
|
+
* blank lines piling up.
|
|
30
|
+
*
|
|
31
|
+
* - Pre-header content (the leading `# Changelog` + introduction
|
|
32
|
+
* blurb) is discarded. The parser only surfaces version sections;
|
|
33
|
+
* callers that want к render the introduction read the source
|
|
34
|
+
* file directly.
|
|
35
|
+
*
|
|
36
|
+
* - Sections appear в the order they are written in the file. The
|
|
37
|
+
* Keep-a-Changelog convention is newest-first; the parser does
|
|
38
|
+
* NOT re-sort. The slicing helpers (`sliceVersionsBetween`) trust
|
|
39
|
+
* the input order so a malformed CHANGELOG with shuffled
|
|
40
|
+
* versions produces a deterministic — if surprising — diff
|
|
41
|
+
* instead of silently swallowing entries.
|
|
42
|
+
*
|
|
43
|
+
* - Semver comparison (`compareSemver`) handles the canonical
|
|
44
|
+
* `MAJOR.MINOR.PATCH[-PRERELEASE]` shape. The pre-release tail
|
|
45
|
+
* is compared lexicographically by dot-separated identifier per
|
|
46
|
+
* semver §11. Unknown / malformed input compares lower than any
|
|
47
|
+
* valid semver so an accidental `unknown` last-seen marker
|
|
48
|
+
* never blocks the operator from seeing new notes.
|
|
49
|
+
*/
|
|
50
|
+
const SECTION_HEADER_RE = /^##\s+\[([^\]]+)\](?:\s*-\s*(.+))?\s*$/u;
|
|
51
|
+
/**
|
|
52
|
+
* Parse the raw `CHANGELOG.md` text into ordered version sections.
|
|
53
|
+
*
|
|
54
|
+
* Returns sections in the same order they appear in the source. The
|
|
55
|
+
* Keep-a-Changelog convention is newest-first; the parser does not
|
|
56
|
+
* re-sort.
|
|
57
|
+
*/
|
|
58
|
+
export function parseChangelog(raw) {
|
|
59
|
+
const lines = raw.split(/\r?\n/u);
|
|
60
|
+
const sections = [];
|
|
61
|
+
let current = null;
|
|
62
|
+
const flush = () => {
|
|
63
|
+
if (!current)
|
|
64
|
+
return;
|
|
65
|
+
sections.push({
|
|
66
|
+
version: current.version,
|
|
67
|
+
date: current.date,
|
|
68
|
+
body: trimBlankEdges(current.lines).join('\n'),
|
|
69
|
+
});
|
|
70
|
+
current = null;
|
|
71
|
+
};
|
|
72
|
+
for (const line of lines) {
|
|
73
|
+
const match = SECTION_HEADER_RE.exec(line);
|
|
74
|
+
if (match) {
|
|
75
|
+
flush();
|
|
76
|
+
const version = (match[1] ?? '').trim();
|
|
77
|
+
const date = (match[2] ?? '').trim();
|
|
78
|
+
if (version.length === 0) {
|
|
79
|
+
// Malformed `## []` header — skip entirely instead of
|
|
80
|
+
// emitting a zero-version row that would corrupt the diff.
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
current = { version, date, lines: [] };
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (current) {
|
|
87
|
+
current.lines.push(line);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
flush();
|
|
91
|
+
return sections;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Slice the section list к those strictly newer than `lastSeen` and
|
|
95
|
+
* up к (and including) `current`. Both bounds are matched on the
|
|
96
|
+
* verbatim version string — the comparator runs on every section к
|
|
97
|
+
* decide membership.
|
|
98
|
+
*
|
|
99
|
+
* Semantics:
|
|
100
|
+
*
|
|
101
|
+
* - If `lastSeen` is null OR empty OR matches no section, every
|
|
102
|
+
* section ≤ current is returned. This is the first-run path —
|
|
103
|
+
* the operator has never run the command before, so the entire
|
|
104
|
+
* bundled changelog is fair game.
|
|
105
|
+
*
|
|
106
|
+
* - If `lastSeen` equals `current`, the empty array is returned.
|
|
107
|
+
* The caller renders the "no new release notes" copy.
|
|
108
|
+
*
|
|
109
|
+
* - If `lastSeen` is newer than `current`, the empty array is
|
|
110
|
+
* returned. This is the dev-build path — operators running a
|
|
111
|
+
* local build of `0.1.0-beta.30` against a registry that
|
|
112
|
+
* publishes `0.1.0-beta.22` would otherwise see the stale
|
|
113
|
+
* bundled notes; the caller still surfaces the same no-op copy.
|
|
114
|
+
*
|
|
115
|
+
* - Otherwise: sections strictly newer than `lastSeen` and ≤
|
|
116
|
+
* `current`, in the source order (newest-first by convention).
|
|
117
|
+
*
|
|
118
|
+
* The function is pure — no IO, no clock — so the spec can pin every
|
|
119
|
+
* branch with hand-rolled fixtures.
|
|
120
|
+
*/
|
|
121
|
+
export function sliceVersionsBetween(sections, lastSeen, current) {
|
|
122
|
+
if (sections.length === 0)
|
|
123
|
+
return [];
|
|
124
|
+
// Treat а blank / sentinel last-seen as "never seen".
|
|
125
|
+
const lastSeenValue = typeof lastSeen === 'string' && lastSeen.trim().length > 0 && lastSeen !== 'none'
|
|
126
|
+
? lastSeen.trim()
|
|
127
|
+
: null;
|
|
128
|
+
// Dev-build path: operator's last-seen marker is strictly newer than
|
|
129
|
+
// the installed CLI version. This happens when running а local build
|
|
130
|
+
// older than the registry, or when the operator manually edited the
|
|
131
|
+
// marker. Either way, return the empty array — re-rendering the
|
|
132
|
+
// bundled notes would be misleading. The renderer surfaces the same
|
|
133
|
+
// "no new release notes" copy as the up-to-date branch.
|
|
134
|
+
if (lastSeenValue !== null && compareSemver(lastSeenValue, current) > 0) {
|
|
135
|
+
return [];
|
|
136
|
+
}
|
|
137
|
+
// Diff path: surface sections strictly newer than the marker and ≤
|
|
138
|
+
// current. The comparator drives every section — а marker that does
|
|
139
|
+
// not appear in the bundled changelog (operator on а stale build,
|
|
140
|
+
// hand-edited marker, version no longer published) still bisects
|
|
141
|
+
// correctly because every comparison runs against the marker value
|
|
142
|
+
// directly, not the matching-section guard.
|
|
143
|
+
const out = [];
|
|
144
|
+
for (const section of sections) {
|
|
145
|
+
// Anything newer than current is а future entry that should not
|
|
146
|
+
// surface until the operator actually upgrades — guards against а
|
|
147
|
+
// dev build of CHANGELOG.md leaking unreleased notes к а customer
|
|
148
|
+
// install.
|
|
149
|
+
if (compareSemver(section.version, current) > 0)
|
|
150
|
+
continue;
|
|
151
|
+
if (lastSeenValue !== null && compareSemver(section.version, lastSeenValue) <= 0) {
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
out.push(section);
|
|
155
|
+
}
|
|
156
|
+
return out;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Semver comparator. Returns a negative number when `a < b`, zero
|
|
160
|
+
* when equal, and a positive number when `a > b`. Pre-release tags
|
|
161
|
+
* compare lexicographically by dot-separated identifier per semver §11.
|
|
162
|
+
*
|
|
163
|
+
* Unknown / malformed input compares lower than any valid semver so
|
|
164
|
+
* an accidental sentinel like `unknown` or `none` never accidentally
|
|
165
|
+
* blocks the diff from surfacing newer entries.
|
|
166
|
+
*/
|
|
167
|
+
export function compareSemver(a, b) {
|
|
168
|
+
const left = parseSemver(a);
|
|
169
|
+
const right = parseSemver(b);
|
|
170
|
+
if (!left && !right)
|
|
171
|
+
return 0;
|
|
172
|
+
if (!left)
|
|
173
|
+
return -1;
|
|
174
|
+
if (!right)
|
|
175
|
+
return 1;
|
|
176
|
+
for (let i = 0; i < 3; i += 1) {
|
|
177
|
+
const cmp = (left.core[i] ?? 0) - (right.core[i] ?? 0);
|
|
178
|
+
if (cmp !== 0)
|
|
179
|
+
return cmp;
|
|
180
|
+
}
|
|
181
|
+
// A version without a pre-release tag is newer than the same core
|
|
182
|
+
// with a pre-release tag (per semver §11: 1.0.0 > 1.0.0-rc).
|
|
183
|
+
if (left.pre.length === 0 && right.pre.length === 0)
|
|
184
|
+
return 0;
|
|
185
|
+
if (left.pre.length === 0)
|
|
186
|
+
return 1;
|
|
187
|
+
if (right.pre.length === 0)
|
|
188
|
+
return -1;
|
|
189
|
+
const len = Math.max(left.pre.length, right.pre.length);
|
|
190
|
+
for (let i = 0; i < len; i += 1) {
|
|
191
|
+
const li = left.pre[i];
|
|
192
|
+
const ri = right.pre[i];
|
|
193
|
+
if (li === undefined)
|
|
194
|
+
return -1;
|
|
195
|
+
if (ri === undefined)
|
|
196
|
+
return 1;
|
|
197
|
+
const ln = Number.parseInt(li, 10);
|
|
198
|
+
const rn = Number.parseInt(ri, 10);
|
|
199
|
+
const liNumeric = Number.isFinite(ln) && String(ln) === li;
|
|
200
|
+
const riNumeric = Number.isFinite(rn) && String(rn) === ri;
|
|
201
|
+
if (liNumeric && riNumeric) {
|
|
202
|
+
if (ln !== rn)
|
|
203
|
+
return ln - rn;
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
if (liNumeric)
|
|
207
|
+
return -1; // numeric < alphanumeric per §11
|
|
208
|
+
if (riNumeric)
|
|
209
|
+
return 1;
|
|
210
|
+
if (li < ri)
|
|
211
|
+
return -1;
|
|
212
|
+
if (li > ri)
|
|
213
|
+
return 1;
|
|
214
|
+
}
|
|
215
|
+
return 0;
|
|
216
|
+
}
|
|
217
|
+
function parseSemver(raw) {
|
|
218
|
+
if (typeof raw !== 'string')
|
|
219
|
+
return null;
|
|
220
|
+
const trimmed = raw.trim();
|
|
221
|
+
if (trimmed.length === 0)
|
|
222
|
+
return null;
|
|
223
|
+
const match = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?$/u.exec(trimmed);
|
|
224
|
+
if (!match)
|
|
225
|
+
return null;
|
|
226
|
+
const major = Number.parseInt(match[1] ?? '0', 10);
|
|
227
|
+
const minor = Number.parseInt(match[2] ?? '0', 10);
|
|
228
|
+
const patch = Number.parseInt(match[3] ?? '0', 10);
|
|
229
|
+
const pre = match[4] ? match[4].split('.') : [];
|
|
230
|
+
return { core: [major, minor, patch], pre };
|
|
231
|
+
}
|
|
232
|
+
function trimBlankEdges(lines) {
|
|
233
|
+
let start = 0;
|
|
234
|
+
let end = lines.length;
|
|
235
|
+
while (start < end && (lines[start] ?? '').trim().length === 0)
|
|
236
|
+
start += 1;
|
|
237
|
+
while (end > start && (lines[end - 1] ?? '').trim().length === 0)
|
|
238
|
+
end -= 1;
|
|
239
|
+
return lines.slice(start, end);
|
|
240
|
+
}
|
|
241
|
+
//# sourceMappingURL=parser.js.map
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `~/.pugi/.last-seen-version` state I/O — .
|
|
3
|
+
*
|
|
4
|
+
* The `pugi release-notes` command renders the diff between the
|
|
5
|
+
* version the operator last saw notes for and the currently
|
|
6
|
+
* installed CLI version. This module owns the on-disk marker that
|
|
7
|
+
* tracks the last-seen value.
|
|
8
|
+
*
|
|
9
|
+
* # Module contract
|
|
10
|
+
*
|
|
11
|
+
* - File path: `<home>/.pugi/.last-seen-version`. The leading dot
|
|
12
|
+
* keeps it out of casual `ls` output; the file is plain ASCII
|
|
13
|
+
* (one line, the version string) so operators can edit it by
|
|
14
|
+
* hand when reproducing scenarios. Missing parent dir is
|
|
15
|
+
* created on write.
|
|
16
|
+
*
|
|
17
|
+
* - Reads: missing file → null. Unreadable / blank file → null.
|
|
18
|
+
* The caller treats null as "operator has never run the command"
|
|
19
|
+
* and surfaces every bundled section. Read failures NEVER throw —
|
|
20
|
+
* the command surface is informational and must keep working on
|
|
21
|
+
* a read-only mount or a misconfigured permission bit.
|
|
22
|
+
*
|
|
23
|
+
* - Writes: best-effort. EACCES / EROFS / ENOSPC log а warning к
|
|
24
|
+
* the caller-supplied logger and return the failure code so the
|
|
25
|
+
* command renderer can footer the output with "could not persist
|
|
26
|
+
* last-seen — re-run will show the same notes". The command
|
|
27
|
+
* itself stays exit 0 because the value of the render did not
|
|
28
|
+
* depend on the write succeeding.
|
|
29
|
+
*
|
|
30
|
+
* - The helpers are pure I/O wrappers — no clock, no random, no
|
|
31
|
+
* env reads. Callers pass `home` explicitly so the spec can
|
|
32
|
+
* pin a tmp dir without monkey-patching `os.homedir`.
|
|
33
|
+
*/
|
|
34
|
+
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
35
|
+
import { resolve } from 'node:path';
|
|
36
|
+
/** Filename inside `<home>/.pugi/` that holds the last-seen marker. */
|
|
37
|
+
export const LAST_SEEN_VERSION_FILE = '.last-seen-version';
|
|
38
|
+
/**
|
|
39
|
+
* Resolve the absolute path of the last-seen marker file inside the
|
|
40
|
+
* passed home directory. Pure helper — no IO.
|
|
41
|
+
*/
|
|
42
|
+
export function lastSeenVersionPath(home) {
|
|
43
|
+
return resolve(home, '.pugi', LAST_SEEN_VERSION_FILE);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Read the last-seen marker. Returns null when the file is missing,
|
|
47
|
+
* unreadable, or empty. Never throws.
|
|
48
|
+
*/
|
|
49
|
+
export function readLastSeenVersion(home) {
|
|
50
|
+
const path = lastSeenVersionPath(home);
|
|
51
|
+
try {
|
|
52
|
+
if (!existsSync(path))
|
|
53
|
+
return null;
|
|
54
|
+
const raw = readFileSync(path, 'utf8').trim();
|
|
55
|
+
if (raw.length === 0)
|
|
56
|
+
return null;
|
|
57
|
+
return raw;
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// Read-only mount, permission denied, race with another process
|
|
61
|
+
// unlinking the file — every read failure degrades к null so the
|
|
62
|
+
// command treats the operator as a first-time viewer instead of
|
|
63
|
+
// dropping out of the slash dispatcher with an unhandled error.
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Persist the last-seen marker. Creates `<home>/.pugi/` if it does
|
|
69
|
+
* not exist. Returns a structured envelope describing success or the
|
|
70
|
+
* failure reason so the renderer can footer а warning when the write
|
|
71
|
+
* could not be made durable.
|
|
72
|
+
*/
|
|
73
|
+
export function writeLastSeenVersion(home, version) {
|
|
74
|
+
const path = lastSeenVersionPath(home);
|
|
75
|
+
try {
|
|
76
|
+
const dir = resolve(home, '.pugi');
|
|
77
|
+
if (!existsSync(dir)) {
|
|
78
|
+
mkdirSync(dir, { recursive: true });
|
|
79
|
+
}
|
|
80
|
+
// Trailing newline so `cat ~/.pugi/.last-seen-version` reads
|
|
81
|
+
// nicely in shells that do not auto-append one for the prompt.
|
|
82
|
+
writeFileSync(path, `${version}\n`, { encoding: 'utf8', mode: 0o600 });
|
|
83
|
+
return { status: 'ok', path };
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
return {
|
|
87
|
+
status: 'failed',
|
|
88
|
+
path,
|
|
89
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Clear the last-seen marker — used by `pugi release-notes --reset`
|
|
95
|
+
* so the operator can force the full bundled changelog к re-render.
|
|
96
|
+
* Returns `absent` when the marker did not exist, `cleared` on
|
|
97
|
+
* success, `failed` on permission errors.
|
|
98
|
+
*/
|
|
99
|
+
export function clearLastSeenVersion(home) {
|
|
100
|
+
const path = lastSeenVersionPath(home);
|
|
101
|
+
try {
|
|
102
|
+
if (!existsSync(path)) {
|
|
103
|
+
return { status: 'absent', path };
|
|
104
|
+
}
|
|
105
|
+
unlinkSync(path);
|
|
106
|
+
return { status: 'cleared', path };
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
return {
|
|
110
|
+
status: 'failed',
|
|
111
|
+
path,
|
|
112
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=state.js.map
|
package/dist/core/repl/ask.js
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Office-hours forcing questions + plan-review tag parser - Sprint
|
|
2
|
+
* Office-hours forcing questions + plan-review tag parser - Sprint .
|
|
3
3
|
*
|
|
4
|
-
* Pugi's persona prompt teaches
|
|
4
|
+
* Pugi's persona prompt teaches Pugi to emit two structured XML envelopes
|
|
5
5
|
* when she would otherwise have to guess. Operator chat then pauses on a
|
|
6
6
|
* modal until the operator answers, eliminating the "fabricate a default
|
|
7
|
-
* silently" failure mode that Codex CLI,
|
|
7
|
+
* silently" failure mode that Codex CLI, the upstream tool, and Gemini CLI all
|
|
8
8
|
* trip on with low-confidence intents.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
10
|
+
* <pugi-ask>
|
|
11
|
+
* <question>Which deployment target?</question>
|
|
12
|
+
* <option value="vercel" label="Vercel" desc="Static + edge"/>
|
|
13
|
+
* <option value="cloudflare" label="Cloudflare Pages" desc="Edge runtime"/>
|
|
14
|
+
* </pugi-ask>
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
16
|
+
* <pugi-plan-review>
|
|
17
|
+
* <step>1. Edit src/foo.ts: add new export</step>
|
|
18
|
+
* <step>2. Write tests/foo.spec.ts: 8 test cases</step>
|
|
19
|
+
* <risk>Touches public API surface.</risk>
|
|
20
|
+
* </pugi-plan-review>
|
|
21
21
|
*
|
|
22
22
|
* This module is a pure, framework-free parser. The session module imports
|
|
23
23
|
* `extractAskTags()` / `extractPlanReviewTags()` and routes the typed
|
|
@@ -32,16 +32,16 @@
|
|
|
32
32
|
* with a closed attribute set, so a bounded tokenizer is both safer and
|
|
33
33
|
* smaller. Defence-in-depth choices:
|
|
34
34
|
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
35
|
+
* - Reject raw `&` outside `&` / `<` / `>` / `"` /
|
|
36
|
+
* `'` (entities are decoded; everything else is malformed).
|
|
37
|
+
* - Forbid nested ask-within-ask (would crash the modal stack).
|
|
38
|
+
* - Cap option count at 4 (the upstream tool AskUserQuestion baseline).
|
|
39
|
+
* - Cap label / desc / question / step / risk at 80 chars (terminal
|
|
40
|
+
* rendering budget, also discourages prompt injection via huge
|
|
41
|
+
* payloads).
|
|
42
|
+
* - Reject CDATA, comments, processing instructions, DOCTYPE — none
|
|
43
|
+
* of those appear in the legal grammar.
|
|
44
|
+
* - Reject any attribute that is not in the per-tag allowlist.
|
|
45
45
|
*
|
|
46
46
|
* # Buffering across streaming chunks
|
|
47
47
|
*
|
|
@@ -53,9 +53,9 @@
|
|
|
53
53
|
* post-tag remainder. The same shape applies to `<pugi-plan-review>`.
|
|
54
54
|
*/
|
|
55
55
|
/* ------------------------------------------------------------------ */
|
|
56
|
-
/* Bounded constants
|
|
56
|
+
/* Bounded constants */
|
|
57
57
|
/* ------------------------------------------------------------------ */
|
|
58
|
-
/** Hard cap on options per `<pugi-ask>`.
|
|
58
|
+
/** Hard cap on options per `<pugi-ask>`. the upstream tool AskUserQuestion uses 4. */
|
|
59
59
|
export const ASK_MAX_OPTIONS = 4;
|
|
60
60
|
/** Hard cap on question / label / desc length. Terminal-row budget. */
|
|
61
61
|
export const ASK_MAX_TEXT_LEN = 80;
|
|
@@ -68,7 +68,7 @@ export const PLAN_REVIEW_MAX_RISK_LEN = 240;
|
|
|
68
68
|
/** Hard cap on the entire tag span. Long enough for 4 options + risk; defends against runaway payloads. */
|
|
69
69
|
const TAG_MAX_SPAN_BYTES = 8 * 1024;
|
|
70
70
|
/* ------------------------------------------------------------------ */
|
|
71
|
-
/* Public extraction API
|
|
71
|
+
/* Public extraction API */
|
|
72
72
|
/* ------------------------------------------------------------------ */
|
|
73
73
|
/**
|
|
74
74
|
* Find every well-formed `<pugi-ask>` in `body`. Malformed tags are
|
|
@@ -131,7 +131,7 @@ function extractTags(body, config) {
|
|
|
131
131
|
// or completes mid-tag. The caller keeps the original buffer for
|
|
132
132
|
// the next chunk merge via pendingOpenTag; if the turn ends with
|
|
133
133
|
// the tag still open, the caller emits a system warning instead
|
|
134
|
-
// of surfacing the raw XML. Codex triple-review P2 (PR
|
|
134
|
+
// of surfacing the raw XML. Codex triple-review P2 (PR).
|
|
135
135
|
pendingOpenTag = true;
|
|
136
136
|
cursor = body.length;
|
|
137
137
|
break;
|
|
@@ -170,7 +170,7 @@ function extractTags(body, config) {
|
|
|
170
170
|
};
|
|
171
171
|
}
|
|
172
172
|
/* ------------------------------------------------------------------ */
|
|
173
|
-
/* `<pugi-ask>` inner parser
|
|
173
|
+
/* `<pugi-ask>` inner parser */
|
|
174
174
|
/* ------------------------------------------------------------------ */
|
|
175
175
|
function parseAskInner(inner, span) {
|
|
176
176
|
// Reject CDATA, comments, processing instructions, DOCTYPE.
|
|
@@ -339,7 +339,7 @@ function parseAttrBlob(blob) {
|
|
|
339
339
|
return result;
|
|
340
340
|
}
|
|
341
341
|
/* ------------------------------------------------------------------ */
|
|
342
|
-
/* `<pugi-plan-review>` inner parser
|
|
342
|
+
/* `<pugi-plan-review>` inner parser */
|
|
343
343
|
/* ------------------------------------------------------------------ */
|
|
344
344
|
function parsePlanReviewInner(inner, span) {
|
|
345
345
|
if (/<!--|<!\[|<\?|<!DOCTYPE/i.test(inner))
|
|
@@ -389,9 +389,9 @@ function extractStepTags(inner) {
|
|
|
389
389
|
}
|
|
390
390
|
/**
|
|
391
391
|
* Optional single-child text. Returns:
|
|
392
|
-
*
|
|
393
|
-
*
|
|
394
|
-
*
|
|
392
|
+
* - `null` when the tag is absent (legal absence)
|
|
393
|
+
* - the trimmed body when exactly one occurrence is present
|
|
394
|
+
* - `undefined` to signal a malformed (rejected) state to the caller
|
|
395
395
|
*/
|
|
396
396
|
function extractOptionalChildText(inner, tagName, maxLen) {
|
|
397
397
|
const open = `<${tagName}>`;
|
|
@@ -413,7 +413,7 @@ function extractOptionalChildText(inner, tagName, maxLen) {
|
|
|
413
413
|
return decoded;
|
|
414
414
|
}
|
|
415
415
|
/* ------------------------------------------------------------------ */
|
|
416
|
-
/* Entity decoding + amp safety
|
|
416
|
+
/* Entity decoding + amp safety */
|
|
417
417
|
/* ------------------------------------------------------------------ */
|
|
418
418
|
const ENTITY_MAP = Object.freeze({
|
|
419
419
|
amp: '&',
|
|
@@ -437,7 +437,7 @@ function decodeEntities(input) {
|
|
|
437
437
|
* Applied AFTER decodeEntities so an `&` escape cannot smuggle an ESC
|
|
438
438
|
* byte through the entity layer.
|
|
439
439
|
*
|
|
440
|
-
* Claude triple-review P2 (PR
|
|
440
|
+
* Claude triple-review P2 (PR).
|
|
441
441
|
*/
|
|
442
442
|
function stripControlChars(input) {
|
|
443
443
|
// eslint-disable-next-line no-control-regex -- deliberately matching control range
|
|
@@ -467,7 +467,7 @@ function containsRawAmpersand(input) {
|
|
|
467
467
|
return false;
|
|
468
468
|
}
|
|
469
469
|
/* ------------------------------------------------------------------ */
|
|
470
|
-
/* Signatures
|
|
470
|
+
/* Signatures */
|
|
471
471
|
/* ------------------------------------------------------------------ */
|
|
472
472
|
/**
|
|
473
473
|
* Stable dedupe signature for an ask tag. The session keeps the most
|
|
@@ -488,7 +488,7 @@ function containsRawAmpersand(input) {
|
|
|
488
488
|
* could share a signature with a local synthesiser even though the
|
|
489
489
|
* computation diverged - dedupe would suppress the persona modal.
|
|
490
490
|
*
|
|
491
|
-
* Claude triple-review P1 (PR
|
|
491
|
+
* Claude triple-review P1 (PR).
|
|
492
492
|
*/
|
|
493
493
|
export function signatureForAsk(question, options) {
|
|
494
494
|
const values = options.map((o) => o.value).sort().join('|');
|
|
@@ -501,7 +501,7 @@ export function signatureForAsk(question, options) {
|
|
|
501
501
|
* `synthesiseLocalPlanReview` in cli.ts must produce identical
|
|
502
502
|
* signatures to parser-extracted tags for the same logical input.
|
|
503
503
|
*
|
|
504
|
-
* Codex triple-review P2 (PR
|
|
504
|
+
* Codex triple-review P2 (PR).
|
|
505
505
|
*/
|
|
506
506
|
export function signatureForPlanReview(steps, risk) {
|
|
507
507
|
const stepsJoined = steps.map((s) => s.text.trim()).join('|');
|
|
@@ -1,38 +1,38 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Cancellation token — Sprint
|
|
2
|
+
* Cancellation token — Sprint Phase 1 (agent loop FSM + cancellation).
|
|
3
3
|
*
|
|
4
4
|
* A pure-JS one-shot signal that fans out to N listeners. One token is
|
|
5
5
|
* minted per dispatch turn (fresh on each operator brief); when the
|
|
6
6
|
* operator hits Ctrl+C, the REPL calls `abort()` which:
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
8
|
+
* 1. Latches `aborted = true` so any future `isAborted` check returns
|
|
9
|
+
* true (a tool that observes the flag mid-execution can short-circuit
|
|
10
|
+
* its loop without waiting for an explicit signal-handler callback).
|
|
11
|
+
* 2. Drains the listener set, firing each callback exactly once. The
|
|
12
|
+
* set is cleared after the drain so a late `onAbort` listener
|
|
13
|
+
* attached AFTER abort does NOT fire — that is the documented
|
|
14
|
+
* contract; late listeners are expected to check `isAborted`
|
|
15
|
+
* explicitly at registration time if they need to know whether the
|
|
16
|
+
* token already tripped.
|
|
17
17
|
*
|
|
18
18
|
* Design choices:
|
|
19
19
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
20
|
+
* - No coupling to `AbortController` / `AbortSignal`. The session +
|
|
21
|
+
* tool path are wrapped around this token; where a Web platform
|
|
22
|
+
* primitive is needed (fetch signal, MCP call), the wrapper bridges
|
|
23
|
+
* `onAbort` → `controller.abort()` at the seam.
|
|
24
|
+
* - Idempotent `abort()`. Calling twice is safe and the second call is
|
|
25
|
+
* a no-op (the listener set is already empty). This matters because
|
|
26
|
+
* two code paths can race to cancel — the Ctrl+C handler and a
|
|
27
|
+
* downstream tool that observed `isAborted` and threw — and both
|
|
28
|
+
* end up calling `dispatch.cancel()` which transitively calls
|
|
29
|
+
* `token.abort()`.
|
|
30
|
+
* - Listener errors do NOT block the drain. A throwing listener
|
|
31
|
+
* stops itself but the next listener still fires. The error is
|
|
32
|
+
* swallowed because the cancellation path is best-effort and
|
|
33
|
+
* surfacing the error mid-drain would leak through the abort
|
|
34
|
+
* pathway into the REPL state (a UI rerender on a half-aborted
|
|
35
|
+
* session is worse than a silent listener crash).
|
|
36
36
|
*
|
|
37
37
|
* Brand voice: no forbidden words. ASCII only. No emoji.
|
|
38
38
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Client-side concurrent-subagent cap - Sprint
|
|
2
|
+
* Client-side concurrent-subagent cap - Sprint (ADR-0056
|
|
3
3
|
* acceptance #6, Mac safety memo
|
|
4
4
|
* `feedback_max_3_parallel_agents_mac_safety.md`).
|
|
5
5
|
*
|
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
* per workstation. The REPL has its own client-side gate that mirrors
|
|
8
8
|
* the global rule for one workstation worth of dispatches:
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* - active count >= 3 → soft warning (operator may still dispatch).
|
|
11
|
+
* - active count >= 5 → hard block unless the operator opted in via
|
|
12
|
+
* PUGI_FORCE_PARALLEL=1.
|
|
13
13
|
*
|
|
14
14
|
* The gate is pure: callers pass the current active count and we return
|
|
15
15
|
* a verdict. The REPL session module reads the dispatcher event stream,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Best-effort clipboard READ helper - Sprint
|
|
2
|
+
* Best-effort clipboard READ helper - Sprint .
|
|
3
3
|
*
|
|
4
4
|
* Mirror of the clipboard WRITE helper but for the opposite
|
|
5
5
|
* direction. Powers Ctrl+V paste in the REPL input box: when the
|
|
@@ -8,15 +8,15 @@
|
|
|
8
8
|
* insert the result at the cursor.
|
|
9
9
|
*
|
|
10
10
|
* Contract:
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
11
|
+
* - Returns `{ text }` when the helper exited 0 with non-empty
|
|
12
|
+
* stdout. Trailing single newline is stripped (clipboard
|
|
13
|
+
* contents authored by the operator rarely include the trailing
|
|
14
|
+
* LF the paste helpers append).
|
|
15
|
+
* - Returns `{ text: null }` on any failure: missing binary,
|
|
16
|
+
* non-zero exit, no $DISPLAY on headless Linux, EAGAIN.
|
|
17
|
+
* - Never throws. Caller falls back to "Ctrl+V not available -
|
|
18
|
+
* try right-click paste" hint.
|
|
19
|
+
* - Targets node >= 20.
|
|
20
20
|
*
|
|
21
21
|
* Implementation note: we do NOT optimistically run all three Linux
|
|
22
22
|
* helpers in parallel - the cost of spawning xclip when wl-copy
|
|
@@ -126,7 +126,7 @@ function defaultSpawnRead(cmd, args) {
|
|
|
126
126
|
});
|
|
127
127
|
}
|
|
128
128
|
/* ------------------------------------------------------------------ */
|
|
129
|
-
/* Bracketed-paste mode helpers
|
|
129
|
+
/* Bracketed-paste mode helpers */
|
|
130
130
|
/* ------------------------------------------------------------------ */
|
|
131
131
|
/**
|
|
132
132
|
* Modern terminals (iTerm2, Alacritty, GNOME Terminal, kitty,
|