@pugi/cli 0.1.0-beta.9 → 0.1.0-beta.91
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 +132 -0
- package/LICENSE +1 -1
- 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 +3 -3
- 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 +13 -13
- 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 +333 -7
- package/dist/core/edits/format-detector.js +260 -0
- package/dist/core/edits/format-matrix.js +26 -0
- package/dist/core/edits/fuzzy-ladder.js +650 -0
- package/dist/core/edits/index.js +5 -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 +29 -29
- package/dist/core/engine/anvil-client.js +214 -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 +129 -19
- package/dist/core/engine/strip-internal-fields.js +124 -0
- package/dist/core/engine/tool-bridge.js +1792 -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 +46 -0
- package/dist/core/hooks/index.js +15 -0
- package/dist/core/hooks/registry.js +216 -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/hooks/worktree-events.js +158 -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 +551 -41
- package/dist/core/lsp/language-detect.js +66 -0
- package/dist/core/lsp/post-edit-diagnostics.js +171 -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 +664 -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 +2148 -217
- package/dist/core/repl/slash-commands.js +501 -41
- 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 +324 -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 +30 -30
- 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/include-parser.js +249 -0
- package/dist/core/worktree-manager/cleanup.js +123 -0
- package/dist/core/worktree-manager/manager.js +303 -0
- package/dist/index.js +36 -0
- package/dist/runtime/bootstrap.js +190 -0
- package/dist/runtime/cli.js +4185 -549
- package/dist/runtime/commands/agents.js +31 -31
- 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 +73 -39
- package/dist/runtime/commands/cost.js +199 -0
- package/dist/runtime/commands/delegate.js +27 -4
- 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 +187 -0
- package/dist/runtime/commands/init.js +254 -0
- package/dist/runtime/commands/lsp.js +200 -38
- 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 +12 -12
- 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 +8 -8
- 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 +22 -22
- package/dist/runtime/sigint-guard.js +272 -0
- package/dist/runtime/update-check.js +28 -28
- package/dist/runtime/version.js +65 -0
- package/dist/runtime/worktree-bootstrap.js +579 -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 +89 -28
- package/dist/tools/ask-user-question.js +337 -0
- package/dist/tools/ask-user.js +115 -0
- package/dist/tools/bash.js +624 -46
- package/dist/tools/brief.js +224 -0
- package/dist/tools/cron.js +433 -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 +377 -1
- 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 +99 -4
- 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-chips.js +315 -0
- 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/multi-file-diff-approval.js +375 -0
- package/dist/tui/onboarding-wizard.js +240 -0
- package/dist/tui/permissions-picker.js +86 -0
- package/dist/tui/render.js +36 -1
- package/dist/tui/repl-render.js +176 -25
- 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 +125 -45
- 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/package.json +31 -16
- package/test/scenarios/codegen-create-file.scenario.txt +13 -0
- package/test/scenarios/compact-force.scenario.txt +12 -0
- package/test/scenarios/identity.scenario.txt +12 -0
- package/test/scenarios/persona-handoff.scenario.txt +12 -0
- package/test/scenarios/walkback.scenario.txt +12 -0
- package/dist/core/engine/compaction-hook.js +0 -154
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `pugi skillify` — codify a freeform operator description into a
|
|
3
|
+
* reusable skill scaffold.
|
|
4
|
+
*
|
|
5
|
+
* Bundled skill, batch 2 (backlog). The operator types a short
|
|
6
|
+
* description ("Write a Pugi skill that scans for `// TODO ASAP` and
|
|
7
|
+
* fails CI"); this skill asks the engine to produce a canonical
|
|
8
|
+
* skill scaffold (name + description + mode + allowedTools + prompt
|
|
9
|
+
* template + example invocation), validates the engine output, and
|
|
10
|
+
* writes the result к `.pugi/skills/<kebab-name>.md` in the same
|
|
11
|
+
* frontmatter format the existing bundled skills use.
|
|
12
|
+
*
|
|
13
|
+
* # Output shape
|
|
14
|
+
*
|
|
15
|
+
* The on-disk file mirrors the stuck / simplify / remember frontmatter
|
|
16
|
+
* shape — `---name / description / mode---` block followed by markdown
|
|
17
|
+
* body. The body holds:
|
|
18
|
+
*
|
|
19
|
+
* - Operator-facing usage block.
|
|
20
|
+
* - Prompt template that the slash dispatcher injects when the skill
|
|
21
|
+
* fires.
|
|
22
|
+
* - Example invocation.
|
|
23
|
+
*
|
|
24
|
+
* # Safety
|
|
25
|
+
*
|
|
26
|
+
* The engine's JSON envelope is validated field-by-field before any
|
|
27
|
+
* file is touched. A malformed envelope produces an explicit error
|
|
28
|
+
* with the failing field — we never write a half-baked skill to disk.
|
|
29
|
+
*
|
|
30
|
+
* # Provenance
|
|
31
|
+
*
|
|
32
|
+
* Inspired by the external bundled-skills pattern (intel from
|
|
33
|
+
* leak-research memos, independent implementation TS). No upstream code reused.
|
|
34
|
+
*/
|
|
35
|
+
import { existsSync, mkdirSync, renameSync, rmSync, writeFileSync, } from 'node:fs';
|
|
36
|
+
import { dirname, join } from 'node:path';
|
|
37
|
+
function parseFlags(args) {
|
|
38
|
+
const flags = {
|
|
39
|
+
json: false,
|
|
40
|
+
description: null,
|
|
41
|
+
outputDir: null,
|
|
42
|
+
force: false,
|
|
43
|
+
dryRun: false,
|
|
44
|
+
};
|
|
45
|
+
const positional = [];
|
|
46
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
47
|
+
const arg = args[i];
|
|
48
|
+
if (arg === undefined)
|
|
49
|
+
continue;
|
|
50
|
+
if (arg === '--json') {
|
|
51
|
+
flags.json = true;
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (arg === '--force') {
|
|
55
|
+
flags.force = true;
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (arg === '--dry-run') {
|
|
59
|
+
flags.dryRun = true;
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (arg === '--output-dir') {
|
|
63
|
+
const next = args[i + 1];
|
|
64
|
+
if (next === undefined) {
|
|
65
|
+
return { flags, error: '--output-dir requires a value' };
|
|
66
|
+
}
|
|
67
|
+
flags.outputDir = next;
|
|
68
|
+
i += 1;
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (arg === '--help' || arg === '-h') {
|
|
72
|
+
return { flags, error: 'help' };
|
|
73
|
+
}
|
|
74
|
+
if (arg.startsWith('--')) {
|
|
75
|
+
return { flags, error: `unknown flag: ${arg}` };
|
|
76
|
+
}
|
|
77
|
+
positional.push(arg);
|
|
78
|
+
}
|
|
79
|
+
if (positional.length > 0) {
|
|
80
|
+
flags.description = positional.join(' ').trim();
|
|
81
|
+
if (flags.description === '')
|
|
82
|
+
flags.description = null;
|
|
83
|
+
}
|
|
84
|
+
return { flags, error: null };
|
|
85
|
+
}
|
|
86
|
+
const ALLOWED_TOOL_PATTERN = /^[a-zA-Z][a-zA-Z0-9_-]*$/;
|
|
87
|
+
const NAME_PATTERN = /^[a-z][a-z0-9-]*$/;
|
|
88
|
+
/**
|
|
89
|
+
* Validate + normalise the engine's JSON envelope. Public for direct
|
|
90
|
+
* test coverage of the boundary contract.
|
|
91
|
+
*/
|
|
92
|
+
export function parseScaffoldEnvelope(raw) {
|
|
93
|
+
const json = extractJsonBlock(raw);
|
|
94
|
+
if (json === null) {
|
|
95
|
+
return { ok: false, error: 'engine output did not contain a JSON object' };
|
|
96
|
+
}
|
|
97
|
+
let value;
|
|
98
|
+
try {
|
|
99
|
+
value = JSON.parse(json);
|
|
100
|
+
}
|
|
101
|
+
catch (err) {
|
|
102
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
103
|
+
return { ok: false, error: `engine JSON parse failed: ${message}` };
|
|
104
|
+
}
|
|
105
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
106
|
+
return { ok: false, error: 'engine envelope must be a JSON object' };
|
|
107
|
+
}
|
|
108
|
+
const obj = value;
|
|
109
|
+
const name = obj.name;
|
|
110
|
+
if (typeof name !== 'string' || !NAME_PATTERN.test(name)) {
|
|
111
|
+
return {
|
|
112
|
+
ok: false,
|
|
113
|
+
error: 'envelope.name must be kebab-case [a-z][a-z0-9-]*',
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
const description = obj.description;
|
|
117
|
+
if (typeof description !== 'string' ||
|
|
118
|
+
description.length < 8 ||
|
|
119
|
+
description.length > 500) {
|
|
120
|
+
return {
|
|
121
|
+
ok: false,
|
|
122
|
+
error: 'envelope.description must be a string 8..500 chars',
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
const modeRaw = obj.mode;
|
|
126
|
+
if (modeRaw !== 'read-only' &&
|
|
127
|
+
modeRaw !== 'read-write' &&
|
|
128
|
+
modeRaw !== 'agent') {
|
|
129
|
+
return {
|
|
130
|
+
ok: false,
|
|
131
|
+
error: 'envelope.mode must be read-only | read-write | agent',
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
const allowedToolsRaw = obj.allowedTools;
|
|
135
|
+
if (!Array.isArray(allowedToolsRaw)) {
|
|
136
|
+
return { ok: false, error: 'envelope.allowedTools must be an array' };
|
|
137
|
+
}
|
|
138
|
+
const allowedTools = [];
|
|
139
|
+
for (let i = 0; i < allowedToolsRaw.length; i += 1) {
|
|
140
|
+
const item = allowedToolsRaw[i];
|
|
141
|
+
if (typeof item !== 'string' || !ALLOWED_TOOL_PATTERN.test(item)) {
|
|
142
|
+
return {
|
|
143
|
+
ok: false,
|
|
144
|
+
error: `envelope.allowedTools[${i}] must match ${String(ALLOWED_TOOL_PATTERN)}`,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
allowedTools.push(item);
|
|
148
|
+
}
|
|
149
|
+
const promptTemplate = obj.promptTemplate;
|
|
150
|
+
if (typeof promptTemplate !== 'string' ||
|
|
151
|
+
promptTemplate.length < 16 ||
|
|
152
|
+
promptTemplate.length > 8000) {
|
|
153
|
+
return {
|
|
154
|
+
ok: false,
|
|
155
|
+
error: 'envelope.promptTemplate must be a string 16..8000 chars',
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
const exampleInvocation = obj.exampleInvocation;
|
|
159
|
+
if (typeof exampleInvocation !== 'string' ||
|
|
160
|
+
exampleInvocation.length < 4 ||
|
|
161
|
+
exampleInvocation.length > 500) {
|
|
162
|
+
return {
|
|
163
|
+
ok: false,
|
|
164
|
+
error: 'envelope.exampleInvocation must be a string 4..500 chars',
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
return {
|
|
168
|
+
ok: true,
|
|
169
|
+
scaffold: {
|
|
170
|
+
name,
|
|
171
|
+
description,
|
|
172
|
+
mode: modeRaw,
|
|
173
|
+
allowedTools,
|
|
174
|
+
promptTemplate,
|
|
175
|
+
exampleInvocation,
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Extract a top-level JSON object from a freeform engine response. We
|
|
181
|
+
* walk the string and balance braces, ignoring brace characters inside
|
|
182
|
+
* strings. Returns the first balanced `{...}` span or null if none.
|
|
183
|
+
* Public for direct test coverage; engines sometimes prepend prose to
|
|
184
|
+
* the envelope and we have to be resilient.
|
|
185
|
+
*/
|
|
186
|
+
export function extractJsonBlock(raw) {
|
|
187
|
+
const start = raw.indexOf('{');
|
|
188
|
+
if (start === -1)
|
|
189
|
+
return null;
|
|
190
|
+
let depth = 0;
|
|
191
|
+
let inString = null;
|
|
192
|
+
let escape = false;
|
|
193
|
+
for (let i = start; i < raw.length; i += 1) {
|
|
194
|
+
const ch = raw.charAt(i);
|
|
195
|
+
if (inString !== null) {
|
|
196
|
+
if (escape) {
|
|
197
|
+
escape = false;
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
if (ch === '\\') {
|
|
201
|
+
escape = true;
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
if (ch === inString) {
|
|
205
|
+
inString = null;
|
|
206
|
+
}
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
if (ch === '"' || ch === "'") {
|
|
210
|
+
inString = ch;
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
if (ch === '{')
|
|
214
|
+
depth += 1;
|
|
215
|
+
if (ch === '}') {
|
|
216
|
+
depth -= 1;
|
|
217
|
+
if (depth === 0) {
|
|
218
|
+
return raw.slice(start, i + 1);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
function atomicWriteFile(targetPath, body) {
|
|
225
|
+
const parent = dirname(targetPath);
|
|
226
|
+
if (!existsSync(parent)) {
|
|
227
|
+
mkdirSync(parent, { recursive: true });
|
|
228
|
+
}
|
|
229
|
+
const tmpPath = `${targetPath}.tmp-${process.pid}-${Date.now()}-${Math.floor(Math.random() * 1_000_000)}`;
|
|
230
|
+
try {
|
|
231
|
+
writeFileSync(tmpPath, body, 'utf8');
|
|
232
|
+
renameSync(tmpPath, targetPath);
|
|
233
|
+
}
|
|
234
|
+
catch (err) {
|
|
235
|
+
try {
|
|
236
|
+
rmSync(tmpPath, { force: true });
|
|
237
|
+
}
|
|
238
|
+
catch {
|
|
239
|
+
// ignore cleanup
|
|
240
|
+
}
|
|
241
|
+
throw err;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Render the on-disk skill markdown using the canonical frontmatter
|
|
246
|
+
* format the existing batch-1 bundled skills follow. Public so the
|
|
247
|
+
* test suite can assert the rendered text directly.
|
|
248
|
+
*/
|
|
249
|
+
export function renderSkillMarkdown(scaffold) {
|
|
250
|
+
// The frontmatter description is quoted to keep multi-line summaries
|
|
251
|
+
// legal YAML without dragging in a real YAML emitter.
|
|
252
|
+
const allowedToolsLine = scaffold.allowedTools.length === 0
|
|
253
|
+
? '[]'
|
|
254
|
+
: `[${scaffold.allowedTools.map((t) => `"${t}"`).join(', ')}]`;
|
|
255
|
+
const lines = [];
|
|
256
|
+
lines.push('---');
|
|
257
|
+
lines.push(`name: ${scaffold.name}`);
|
|
258
|
+
lines.push(`description: ${JSON.stringify(scaffold.description)}`);
|
|
259
|
+
lines.push(`mode: ${scaffold.mode}`);
|
|
260
|
+
lines.push(`allowedTools: ${allowedToolsLine}`);
|
|
261
|
+
lines.push('---');
|
|
262
|
+
lines.push('');
|
|
263
|
+
lines.push(`# /${scaffold.name}`);
|
|
264
|
+
lines.push('');
|
|
265
|
+
lines.push(scaffold.description);
|
|
266
|
+
lines.push('');
|
|
267
|
+
lines.push('## Prompt template');
|
|
268
|
+
lines.push('');
|
|
269
|
+
lines.push(scaffold.promptTemplate.trim());
|
|
270
|
+
lines.push('');
|
|
271
|
+
lines.push('## Example');
|
|
272
|
+
lines.push('');
|
|
273
|
+
lines.push('```');
|
|
274
|
+
lines.push(scaffold.exampleInvocation.trim());
|
|
275
|
+
lines.push('```');
|
|
276
|
+
lines.push('');
|
|
277
|
+
return `${lines.join('\n')}\n`;
|
|
278
|
+
}
|
|
279
|
+
const SCHEMA_HINT = JSON.stringify({
|
|
280
|
+
name: '<kebab-case>',
|
|
281
|
+
description: '<8..500 chars>',
|
|
282
|
+
mode: 'read-only | read-write | agent',
|
|
283
|
+
allowedTools: ['<tool-name>', '...'],
|
|
284
|
+
promptTemplate: '<16..8000 chars>',
|
|
285
|
+
exampleInvocation: '<short usage example>',
|
|
286
|
+
});
|
|
287
|
+
export async function runSkillifyCommand(args, ctx) {
|
|
288
|
+
const { flags, error } = parseFlags(args);
|
|
289
|
+
if (error === 'help') {
|
|
290
|
+
ctx.writeOutput({ ok: true, command: 'skillify', usage: SKILLIFY_USAGE }, SKILLIFY_USAGE);
|
|
291
|
+
return { scaffold: null, writtenPath: null, exitCode: 0 };
|
|
292
|
+
}
|
|
293
|
+
if (error !== null) {
|
|
294
|
+
ctx.writeOutput({ ok: false, command: 'skillify', error }, `pugi skillify: ${error}`);
|
|
295
|
+
return { scaffold: null, writtenPath: null, exitCode: 2 };
|
|
296
|
+
}
|
|
297
|
+
if (flags.description === null) {
|
|
298
|
+
const msg = 'description is required';
|
|
299
|
+
ctx.writeOutput({ ok: false, command: 'skillify', error: msg }, `pugi skillify: ${msg}\n\n${SKILLIFY_USAGE}`);
|
|
300
|
+
return { scaffold: null, writtenPath: null, exitCode: 2 };
|
|
301
|
+
}
|
|
302
|
+
let engineRaw;
|
|
303
|
+
try {
|
|
304
|
+
engineRaw = await ctx.engineInvoker({
|
|
305
|
+
description: flags.description,
|
|
306
|
+
schemaHint: SCHEMA_HINT,
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
catch (err) {
|
|
310
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
311
|
+
ctx.writeOutput({ ok: false, command: 'skillify', error: `engine failed: ${message}` }, `pugi skillify: engine failed — ${message}`);
|
|
312
|
+
return { scaffold: null, writtenPath: null, exitCode: 1 };
|
|
313
|
+
}
|
|
314
|
+
const parsed = parseScaffoldEnvelope(engineRaw);
|
|
315
|
+
if (!parsed.ok) {
|
|
316
|
+
ctx.writeOutput({ ok: false, command: 'skillify', error: parsed.error, raw: engineRaw }, `pugi skillify: engine envelope invalid — ${parsed.error}`);
|
|
317
|
+
return { scaffold: null, writtenPath: null, exitCode: 1 };
|
|
318
|
+
}
|
|
319
|
+
const scaffold = parsed.scaffold;
|
|
320
|
+
const outputDir = flags.outputDir ?? join(ctx.cwd, '.pugi', 'skills');
|
|
321
|
+
const writtenPath = join(outputDir, `${scaffold.name}.md`);
|
|
322
|
+
if (flags.dryRun) {
|
|
323
|
+
ctx.writeOutput({
|
|
324
|
+
ok: true,
|
|
325
|
+
command: 'skillify',
|
|
326
|
+
scaffold,
|
|
327
|
+
wouldWrite: writtenPath,
|
|
328
|
+
body: renderSkillMarkdown(scaffold),
|
|
329
|
+
dryRun: true,
|
|
330
|
+
}, `pugi skillify: dry-run — would write ${writtenPath}`);
|
|
331
|
+
return { scaffold, writtenPath: null, exitCode: 0 };
|
|
332
|
+
}
|
|
333
|
+
if (existsSync(writtenPath) && !flags.force) {
|
|
334
|
+
const msg = `target exists: ${writtenPath} (pass --force to overwrite)`;
|
|
335
|
+
ctx.writeOutput({ ok: false, command: 'skillify', error: msg, scaffold }, `pugi skillify: ${msg}`);
|
|
336
|
+
return { scaffold, writtenPath: null, exitCode: 1 };
|
|
337
|
+
}
|
|
338
|
+
const body = renderSkillMarkdown(scaffold);
|
|
339
|
+
try {
|
|
340
|
+
atomicWriteFile(writtenPath, body);
|
|
341
|
+
}
|
|
342
|
+
catch (err) {
|
|
343
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
344
|
+
ctx.writeOutput({
|
|
345
|
+
ok: false,
|
|
346
|
+
command: 'skillify',
|
|
347
|
+
error: `write failed: ${message}`,
|
|
348
|
+
scaffold,
|
|
349
|
+
}, `pugi skillify: write failed — ${message}`);
|
|
350
|
+
return { scaffold, writtenPath: null, exitCode: 1 };
|
|
351
|
+
}
|
|
352
|
+
ctx.writeOutput({ ok: true, command: 'skillify', scaffold, writtenPath }, `Wrote ${writtenPath} — invoke via \`/skill-${scaffold.name}\`.`);
|
|
353
|
+
return { scaffold, writtenPath, exitCode: 0 };
|
|
354
|
+
}
|
|
355
|
+
const SKILLIFY_USAGE = [
|
|
356
|
+
'pugi skillify — generate a reusable skill scaffold from a freeform description.',
|
|
357
|
+
'',
|
|
358
|
+
'Usage:',
|
|
359
|
+
' pugi skillify "<description>" [--output-dir <dir>] [--force] [--dry-run] [--json]',
|
|
360
|
+
'',
|
|
361
|
+
'Flags:',
|
|
362
|
+
' --output-dir <dir> Destination directory (default: .pugi/skills/).',
|
|
363
|
+
' --force Overwrite an existing file with the same name.',
|
|
364
|
+
' --dry-run Print the rendered markdown without writing it.',
|
|
365
|
+
' --json Emit a JSON envelope instead of human text.',
|
|
366
|
+
'',
|
|
367
|
+
'Engine envelope schema:',
|
|
368
|
+
' { "name": "kebab", "description": "...", "mode": "read-only|read-write|agent",',
|
|
369
|
+
' "allowedTools": ["..."], "promptTemplate": "...", "exampleInvocation": "..." }',
|
|
370
|
+
'',
|
|
371
|
+
'Output lands at .pugi/skills/<name>.md in the canonical frontmatter format.',
|
|
372
|
+
].join('\n');
|
|
373
|
+
//# sourceMappingURL=skillify.js.map
|