@caupulican/pi-adaptative 0.81.37 → 0.81.39
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 +91 -0
- package/README.md +1 -1
- package/dist/bundled-resources/extensions/tmux-agent-manager/README.md +18 -1
- package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.d.ts +131 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.d.ts.map +1 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.js +196 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.js.map +1 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.ts +308 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/index.d.ts +22 -2
- package/dist/bundled-resources/extensions/tmux-agent-manager/index.d.ts.map +1 -1
- package/dist/bundled-resources/extensions/tmux-agent-manager/index.js +585 -24
- package/dist/bundled-resources/extensions/tmux-agent-manager/index.js.map +1 -1
- package/dist/bundled-resources/extensions/tmux-agent-manager/index.ts +749 -27
- package/dist/bundled-resources/runtimes/pi-shell-engine/commands/__init__.py +43 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/commands/fs.py +270 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/commands/search.py +252 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/commands/strings.py +399 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/commands/text.py +575 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/context.py +52 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/errors.py +49 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/exec.py +734 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/expand.py +238 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/main.py +132 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/nodes.py +116 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/parser.py +287 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/proc.py +137 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/state.py +100 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/tokens.py +579 -0
- package/dist/bundled-resources/skills/tool-call-repair/SKILL.md +19 -9
- package/dist/bundled-resources/skills/tool-call-repair/references/failure-grammar.md +22 -5
- package/dist/bundled-resources/skills/tool-call-repair/references/repair-catalogue.md +5 -5
- package/dist/bundled-resources/skills/tool-call-repair/references/text-protocol-grammar.md +28 -7
- package/dist/cli/args.d.ts +3 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +15 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/core/agent-paths.d.ts +49 -0
- package/dist/core/agent-paths.d.ts.map +1 -0
- package/dist/core/agent-paths.js +107 -0
- package/dist/core/agent-paths.js.map +1 -0
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +3 -3
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session.d.ts +234 -17
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +484 -62
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/autonomy/contracts.d.ts +9 -0
- package/dist/core/autonomy/contracts.d.ts.map +1 -1
- package/dist/core/autonomy/contracts.js.map +1 -1
- package/dist/core/autonomy/lane-tracker.d.ts +10 -1
- package/dist/core/autonomy/lane-tracker.d.ts.map +1 -1
- package/dist/core/autonomy/lane-tracker.js +5 -1
- package/dist/core/autonomy/lane-tracker.js.map +1 -1
- package/dist/core/background-lane-controller.d.ts +122 -6
- package/dist/core/background-lane-controller.d.ts.map +1 -1
- package/dist/core/background-lane-controller.js +447 -90
- package/dist/core/background-lane-controller.js.map +1 -1
- package/dist/core/bash-execution-controller.d.ts +4 -0
- package/dist/core/bash-execution-controller.d.ts.map +1 -1
- package/dist/core/bash-execution-controller.js +7 -1
- package/dist/core/bash-execution-controller.js.map +1 -1
- package/dist/core/compaction-support.d.ts +13 -3
- package/dist/core/compaction-support.d.ts.map +1 -1
- package/dist/core/compaction-support.js +43 -7
- package/dist/core/compaction-support.js.map +1 -1
- package/dist/core/context/context-audit.d.ts +33 -1
- package/dist/core/context/context-audit.d.ts.map +1 -1
- package/dist/core/context/context-audit.js +31 -5
- package/dist/core/context/context-audit.js.map +1 -1
- package/dist/core/context/sqlite-runtime-index.d.ts.map +1 -1
- package/dist/core/context/sqlite-runtime-index.js +13 -7
- package/dist/core/context/sqlite-runtime-index.js.map +1 -1
- package/dist/core/context-gc.d.ts.map +1 -1
- package/dist/core/context-gc.js +16 -1
- package/dist/core/context-gc.js.map +1 -1
- package/dist/core/context-pipeline.d.ts +26 -4
- package/dist/core/context-pipeline.d.ts.map +1 -1
- package/dist/core/context-pipeline.js +137 -14
- package/dist/core/context-pipeline.js.map +1 -1
- package/dist/core/cost-guard.d.ts +19 -3
- package/dist/core/cost-guard.d.ts.map +1 -1
- package/dist/core/cost-guard.js +18 -3
- package/dist/core/cost-guard.js.map +1 -1
- package/dist/core/delegation/session-worker-result.d.ts +34 -4
- package/dist/core/delegation/session-worker-result.d.ts.map +1 -1
- package/dist/core/delegation/session-worker-result.js +64 -4
- package/dist/core/delegation/session-worker-result.js.map +1 -1
- package/dist/core/delegation/worker-actions.d.ts.map +1 -1
- package/dist/core/delegation/worker-actions.js +1 -1
- package/dist/core/delegation/worker-actions.js.map +1 -1
- package/dist/core/delegation/worker-result.d.ts +42 -1
- package/dist/core/delegation/worker-result.d.ts.map +1 -1
- package/dist/core/delegation/worker-result.js +68 -0
- package/dist/core/delegation/worker-result.js.map +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +23 -7
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +1 -0
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +51 -0
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/goal-loop-controller.d.ts +17 -1
- package/dist/core/goal-loop-controller.d.ts.map +1 -1
- package/dist/core/goal-loop-controller.js +79 -11
- package/dist/core/goal-loop-controller.js.map +1 -1
- package/dist/core/goals/goal-continuation-controller.d.ts +48 -2
- package/dist/core/goals/goal-continuation-controller.d.ts.map +1 -1
- package/dist/core/goals/goal-continuation-controller.js +77 -0
- package/dist/core/goals/goal-continuation-controller.js.map +1 -1
- package/dist/core/goals/goal-continuation-defaults.d.ts +39 -0
- package/dist/core/goals/goal-continuation-defaults.d.ts.map +1 -1
- package/dist/core/goals/goal-continuation-defaults.js +42 -0
- package/dist/core/goals/goal-continuation-defaults.js.map +1 -1
- package/dist/core/goals/goal-continuation-prompt.d.ts +4 -0
- package/dist/core/goals/goal-continuation-prompt.d.ts.map +1 -1
- package/dist/core/goals/goal-continuation-prompt.js +51 -10
- package/dist/core/goals/goal-continuation-prompt.js.map +1 -1
- package/dist/core/goals/goal-runtime-snapshot.d.ts +101 -2
- package/dist/core/goals/goal-runtime-snapshot.d.ts.map +1 -1
- package/dist/core/goals/goal-runtime-snapshot.js +87 -4
- package/dist/core/goals/goal-runtime-snapshot.js.map +1 -1
- package/dist/core/goals/goal-state.d.ts +89 -1
- package/dist/core/goals/goal-state.d.ts.map +1 -1
- package/dist/core/goals/goal-state.js +71 -5
- package/dist/core/goals/goal-state.js.map +1 -1
- package/dist/core/goals/goal-tool-core.d.ts +56 -2
- package/dist/core/goals/goal-tool-core.d.ts.map +1 -1
- package/dist/core/goals/goal-tool-core.js +111 -5
- package/dist/core/goals/goal-tool-core.js.map +1 -1
- package/dist/core/goals/session-goal-state.d.ts +11 -2
- package/dist/core/goals/session-goal-state.d.ts.map +1 -1
- package/dist/core/goals/session-goal-state.js +32 -17
- package/dist/core/goals/session-goal-state.js.map +1 -1
- package/dist/core/keybindings.d.ts +10 -0
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +10 -2
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/learning/observation-store.d.ts +12 -3
- package/dist/core/learning/observation-store.d.ts.map +1 -1
- package/dist/core/learning/observation-store.js +30 -15
- package/dist/core/learning/observation-store.js.map +1 -1
- package/dist/core/learning/skill-curator.d.ts +5 -1
- package/dist/core/learning/skill-curator.d.ts.map +1 -1
- package/dist/core/learning/skill-curator.js +21 -19
- package/dist/core/learning/skill-curator.js.map +1 -1
- package/dist/core/local-runtime-controller.d.ts +65 -3
- package/dist/core/local-runtime-controller.d.ts.map +1 -1
- package/dist/core/local-runtime-controller.js +186 -26
- package/dist/core/local-runtime-controller.js.map +1 -1
- package/dist/core/memory/providers/file-store.d.ts +1 -1
- package/dist/core/memory/providers/file-store.d.ts.map +1 -1
- package/dist/core/memory/providers/file-store.js +6 -6
- package/dist/core/memory/providers/file-store.js.map +1 -1
- package/dist/core/model-capability.d.ts +34 -0
- package/dist/core/model-capability.d.ts.map +1 -1
- package/dist/core/model-capability.js +42 -1
- package/dist/core/model-capability.js.map +1 -1
- package/dist/core/model-router/tool-escalation.d.ts +15 -0
- package/dist/core/model-router/tool-escalation.d.ts.map +1 -1
- package/dist/core/model-router/tool-escalation.js +23 -1
- package/dist/core/model-router/tool-escalation.js.map +1 -1
- package/dist/core/model-router-controller.d.ts +34 -7
- package/dist/core/model-router-controller.d.ts.map +1 -1
- package/dist/core/model-router-controller.js +95 -16
- package/dist/core/model-router-controller.js.map +1 -1
- package/dist/core/models/adaptation-store.d.ts +7 -0
- package/dist/core/models/adaptation-store.d.ts.map +1 -1
- package/dist/core/models/adaptation-store.js +47 -11
- package/dist/core/models/adaptation-store.js.map +1 -1
- package/dist/core/models/default-model-suggestions.d.ts.map +1 -1
- package/dist/core/models/default-model-suggestions.js +17 -0
- package/dist/core/models/default-model-suggestions.js.map +1 -1
- package/dist/core/models/fitness-store.d.ts +3 -0
- package/dist/core/models/fitness-store.d.ts.map +1 -1
- package/dist/core/models/fitness-store.js +11 -2
- package/dist/core/models/fitness-store.js.map +1 -1
- package/dist/core/models/llamacpp-runtime.d.ts +180 -0
- package/dist/core/models/llamacpp-runtime.d.ts.map +1 -0
- package/dist/core/models/llamacpp-runtime.js +475 -0
- package/dist/core/models/llamacpp-runtime.js.map +1 -0
- package/dist/core/models/local-registration.d.ts +40 -0
- package/dist/core/models/local-registration.d.ts.map +1 -1
- package/dist/core/models/local-registration.js +94 -5
- package/dist/core/models/local-registration.js.map +1 -1
- package/dist/core/models/local-runtime.d.ts.map +1 -1
- package/dist/core/models/local-runtime.js +9 -7
- package/dist/core/models/local-runtime.js.map +1 -1
- package/dist/core/models/model-ref.d.ts +7 -0
- package/dist/core/models/model-ref.d.ts.map +1 -1
- package/dist/core/models/model-ref.js +26 -0
- package/dist/core/models/model-ref.js.map +1 -1
- package/dist/core/models/needle-runtime.d.ts +257 -0
- package/dist/core/models/needle-runtime.d.ts.map +1 -0
- package/dist/core/models/needle-runtime.js +519 -0
- package/dist/core/models/needle-runtime.js.map +1 -0
- package/dist/core/models/prism-llamacpp-lifecycle.d.ts +89 -0
- package/dist/core/models/prism-llamacpp-lifecycle.d.ts.map +1 -0
- package/dist/core/models/prism-llamacpp-lifecycle.js +121 -0
- package/dist/core/models/prism-llamacpp-lifecycle.js.map +1 -0
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +11 -10
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/process-matrix/codes.d.ts +72 -0
- package/dist/core/process-matrix/codes.d.ts.map +1 -0
- package/dist/core/process-matrix/codes.js +15 -0
- package/dist/core/process-matrix/codes.js.map +1 -0
- package/dist/core/process-matrix/runtime.d.ts +63 -0
- package/dist/core/process-matrix/runtime.d.ts.map +1 -0
- package/dist/core/process-matrix/runtime.js +310 -0
- package/dist/core/process-matrix/runtime.js.map +1 -0
- package/dist/core/process-matrix/store.d.ts +22 -0
- package/dist/core/process-matrix/store.d.ts.map +1 -0
- package/dist/core/process-matrix/store.js +80 -0
- package/dist/core/process-matrix/store.js.map +1 -0
- package/dist/core/process-matrix/supervisor.d.ts +72 -0
- package/dist/core/process-matrix/supervisor.d.ts.map +1 -0
- package/dist/core/process-matrix/supervisor.js +130 -0
- package/dist/core/process-matrix/supervisor.js.map +1 -0
- package/dist/core/profile-registry.d.ts +7 -0
- package/dist/core/profile-registry.d.ts.map +1 -1
- package/dist/core/profile-registry.js +20 -0
- package/dist/core/profile-registry.js.map +1 -1
- package/dist/core/python-runtime.d.ts.map +1 -1
- package/dist/core/python-runtime.js +3 -3
- package/dist/core/python-runtime.js.map +1 -1
- package/dist/core/reflection-controller.d.ts +29 -5
- package/dist/core/reflection-controller.d.ts.map +1 -1
- package/dist/core/reflection-controller.js +215 -126
- package/dist/core/reflection-controller.js.map +1 -1
- package/dist/core/reload-blockers.d.ts +36 -0
- package/dist/core/reload-blockers.d.ts.map +1 -1
- package/dist/core/reload-blockers.js +44 -0
- package/dist/core/reload-blockers.js.map +1 -1
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +8 -7
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/runtime-builder.d.ts +58 -5
- package/dist/core/runtime-builder.d.ts.map +1 -1
- package/dist/core/runtime-builder.js +209 -25
- package/dist/core/runtime-builder.js.map +1 -1
- package/dist/core/scout-controller.d.ts +6 -0
- package/dist/core/scout-controller.d.ts.map +1 -1
- package/dist/core/scout-controller.js +66 -52
- package/dist/core/scout-controller.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +3 -3
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-role.d.ts +31 -0
- package/dist/core/session-role.d.ts.map +1 -0
- package/dist/core/session-role.js +52 -0
- package/dist/core/session-role.js.map +1 -0
- package/dist/core/settings-manager.d.ts +42 -3
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +94 -60
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/system-prompt-builder.d.ts +12 -0
- package/dist/core/system-prompt-builder.d.ts.map +1 -1
- package/dist/core/system-prompt-builder.js +6 -0
- package/dist/core/system-prompt-builder.js.map +1 -1
- package/dist/core/system-prompt.d.ts +14 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +20 -3
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tasks/session-task-state.d.ts +11 -2
- package/dist/core/tasks/session-task-state.d.ts.map +1 -1
- package/dist/core/tasks/session-task-state.js +27 -11
- package/dist/core/tasks/session-task-state.js.map +1 -1
- package/dist/core/tasks/task-contract-monitor.d.ts +45 -0
- package/dist/core/tasks/task-contract-monitor.d.ts.map +1 -0
- package/dist/core/tasks/task-contract-monitor.js +56 -0
- package/dist/core/tasks/task-contract-monitor.js.map +1 -0
- package/dist/core/tasks/task-state.d.ts +8 -0
- package/dist/core/tasks/task-state.d.ts.map +1 -1
- package/dist/core/tasks/task-state.js +28 -1
- package/dist/core/tasks/task-state.js.map +1 -1
- package/dist/core/tool-gate-controller.d.ts.map +1 -1
- package/dist/core/tool-gate-controller.js +5 -0
- package/dist/core/tool-gate-controller.js.map +1 -1
- package/dist/core/tool-recovery-log-records.d.ts +7 -0
- package/dist/core/tool-recovery-log-records.d.ts.map +1 -1
- package/dist/core/tool-recovery-log-records.js +14 -6
- package/dist/core/tool-recovery-log-records.js.map +1 -1
- package/dist/core/tool-selection/promotion.d.ts +54 -0
- package/dist/core/tool-selection/promotion.d.ts.map +1 -0
- package/dist/core/tool-selection/promotion.js +81 -0
- package/dist/core/tool-selection/promotion.js.map +1 -0
- package/dist/core/tool-selection/tool-performance-store.d.ts +37 -0
- package/dist/core/tool-selection/tool-performance-store.d.ts.map +1 -1
- package/dist/core/tool-selection/tool-performance-store.js +87 -3
- package/dist/core/tool-selection/tool-performance-store.js.map +1 -1
- package/dist/core/tool-selection/tool-selection-controller.d.ts +45 -0
- package/dist/core/tool-selection/tool-selection-controller.d.ts.map +1 -1
- package/dist/core/tool-selection/tool-selection-controller.js +96 -0
- package/dist/core/tool-selection/tool-selection-controller.js.map +1 -1
- package/dist/core/tools/bash.d.ts +18 -0
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +97 -18
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/delegate-status.d.ts +14 -0
- package/dist/core/tools/delegate-status.d.ts.map +1 -1
- package/dist/core/tools/delegate-status.js +82 -7
- package/dist/core/tools/delegate-status.js.map +1 -1
- package/dist/core/tools/delegate.d.ts.map +1 -1
- package/dist/core/tools/delegate.js +25 -8
- package/dist/core/tools/delegate.js.map +1 -1
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js +52 -44
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/goal.d.ts +94 -3
- package/dist/core/tools/goal.d.ts.map +1 -1
- package/dist/core/tools/goal.js +165 -15
- package/dist/core/tools/goal.js.map +1 -1
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js +5 -4
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/model-fitness.d.ts +7 -0
- package/dist/core/tools/model-fitness.d.ts.map +1 -1
- package/dist/core/tools/model-fitness.js +2 -2
- package/dist/core/tools/model-fitness.js.map +1 -1
- package/dist/core/tools/render-utils.d.ts.map +1 -1
- package/dist/core/tools/render-utils.js +1 -1
- package/dist/core/tools/render-utils.js.map +1 -1
- package/dist/core/tools/shell-contract-router.d.ts +6 -1
- package/dist/core/tools/shell-contract-router.d.ts.map +1 -1
- package/dist/core/tools/shell-contract-router.js +69 -13
- package/dist/core/tools/shell-contract-router.js.map +1 -1
- package/dist/core/tools/shell-session.d.ts +89 -0
- package/dist/core/tools/shell-session.d.ts.map +1 -0
- package/dist/core/tools/shell-session.js +432 -0
- package/dist/core/tools/shell-session.js.map +1 -0
- package/dist/core/tools/task-steps.d.ts +4 -0
- package/dist/core/tools/task-steps.d.ts.map +1 -1
- package/dist/core/tools/task-steps.js +63 -8
- package/dist/core/tools/task-steps.js.map +1 -1
- package/dist/core/tools/tmux-dispatch.d.ts +86 -0
- package/dist/core/tools/tmux-dispatch.d.ts.map +1 -0
- package/dist/core/tools/tmux-dispatch.js +91 -0
- package/dist/core/tools/tmux-dispatch.js.map +1 -0
- package/dist/core/tools/windows-shell-engine.d.ts +42 -0
- package/dist/core/tools/windows-shell-engine.d.ts.map +1 -0
- package/dist/core/tools/windows-shell-engine.js +153 -0
- package/dist/core/tools/windows-shell-engine.js.map +1 -0
- package/dist/core/tools/windows-shell-state.d.ts +40 -0
- package/dist/core/tools/windows-shell-state.d.ts.map +1 -0
- package/dist/core/tools/windows-shell-state.js +59 -0
- package/dist/core/tools/windows-shell-state.js.map +1 -0
- package/dist/core/tools/worktree-sync.d.ts +24 -0
- package/dist/core/tools/worktree-sync.d.ts.map +1 -0
- package/dist/core/tools/worktree-sync.js +338 -0
- package/dist/core/tools/worktree-sync.js.map +1 -0
- package/dist/core/trust-manager.d.ts +4 -1
- package/dist/core/trust-manager.d.ts.map +1 -1
- package/dist/core/trust-manager.js +20 -2
- package/dist/core/trust-manager.js.map +1 -1
- package/dist/core/util/atomic-file.d.ts +55 -0
- package/dist/core/util/atomic-file.d.ts.map +1 -0
- package/dist/core/util/atomic-file.js +255 -0
- package/dist/core/util/atomic-file.js.map +1 -0
- package/dist/core/util/minimatch-cache.d.ts +33 -0
- package/dist/core/util/minimatch-cache.d.ts.map +1 -0
- package/dist/core/util/minimatch-cache.js +0 -0
- package/dist/core/util/minimatch-cache.js.map +1 -0
- package/dist/core/worktree-sync/codes.d.ts +227 -0
- package/dist/core/worktree-sync/codes.d.ts.map +1 -0
- package/dist/core/worktree-sync/codes.js +14 -0
- package/dist/core/worktree-sync/codes.js.map +1 -0
- package/dist/core/worktree-sync/git-engine.d.ts +156 -0
- package/dist/core/worktree-sync/git-engine.d.ts.map +1 -0
- package/dist/core/worktree-sync/git-engine.js +1191 -0
- package/dist/core/worktree-sync/git-engine.js.map +1 -0
- package/dist/core/worktree-sync/lane-gate.d.ts +75 -0
- package/dist/core/worktree-sync/lane-gate.d.ts.map +1 -0
- package/dist/core/worktree-sync/lane-gate.js +360 -0
- package/dist/core/worktree-sync/lane-gate.js.map +1 -0
- package/dist/core/worktree-sync/runtime.d.ts +47 -0
- package/dist/core/worktree-sync/runtime.d.ts.map +1 -0
- package/dist/core/worktree-sync/runtime.js +96 -0
- package/dist/core/worktree-sync/runtime.js.map +1 -0
- package/dist/core/worktree-sync/store.d.ts +69 -0
- package/dist/core/worktree-sync/store.d.ts.map +1 -0
- package/dist/core/worktree-sync/store.js +247 -0
- package/dist/core/worktree-sync/store.js.map +1 -0
- package/dist/core/worktree-sync/watcher.d.ts +29 -0
- package/dist/core/worktree-sync/watcher.d.ts.map +1 -0
- package/dist/core/worktree-sync/watcher.js +93 -0
- package/dist/core/worktree-sync/watcher.js.map +1 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +94 -0
- package/dist/main.js.map +1 -1
- package/dist/migrations.d.ts +9 -0
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +38 -0
- package/dist/migrations.js.map +1 -1
- package/dist/modes/interactive/auto-learn-controller.d.ts +16 -1
- package/dist/modes/interactive/auto-learn-controller.d.ts.map +1 -1
- package/dist/modes/interactive/auto-learn-controller.js +50 -8
- package/dist/modes/interactive/auto-learn-controller.js.map +1 -1
- package/dist/modes/interactive/components/profile-resource-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/profile-resource-editor.js +23 -1
- package/dist/modes/interactive/components/profile-resource-editor.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +4 -0
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/local-model-commands.d.ts +43 -0
- package/dist/modes/interactive/local-model-commands.d.ts.map +1 -1
- package/dist/modes/interactive/local-model-commands.js +290 -3
- package/dist/modes/interactive/local-model-commands.js.map +1 -1
- package/dist/modes/interactive/session-flow-commands.d.ts.map +1 -1
- package/dist/modes/interactive/session-flow-commands.js +24 -4
- package/dist/modes/interactive/session-flow-commands.js.map +1 -1
- package/dist/utils/fs-watch.d.ts +11 -0
- package/dist/utils/fs-watch.d.ts.map +1 -1
- package/dist/utils/fs-watch.js +20 -2
- package/dist/utils/fs-watch.js.map +1 -1
- package/dist/utils/highlight-js-languages.d.ts +4 -0
- package/dist/utils/highlight-js-languages.d.ts.map +1 -0
- package/dist/utils/highlight-js-languages.js +573 -0
- package/dist/utils/highlight-js-languages.js.map +1 -0
- package/dist/utils/shell.d.ts +7 -1
- package/dist/utils/shell.d.ts.map +1 -1
- package/dist/utils/shell.js +39 -9
- package/dist/utils/shell.js.map +1 -1
- package/dist/utils/syntax-highlight.d.ts.map +1 -1
- package/dist/utils/syntax-highlight.js +53 -5
- package/dist/utils/syntax-highlight.js.map +1 -1
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +112 -1
- package/dist/utils/tools-manager.js.map +1 -1
- package/docs/development.md +2 -0
- package/docs/packages.md +1 -1
- package/docs/process-matrix.md +120 -0
- package/docs/settings.md +5 -2
- package/docs/tmux-agent-manager.md +85 -2
- package/docs/windows.md +52 -3
- package/docs/work-directory.md +29 -0
- package/docs/worktree-sync.md +250 -0
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/npm-shrinkwrap.json +12 -12
- package/package.json +10 -4
- package/docs/integration-sweep-builder-blueprint-2026-07-09.md +0 -365
- package/docs/integration-sweep-resume-2026-07-09.md +0 -407
|
@@ -0,0 +1,734 @@
|
|
|
1
|
+
"""Executes the frozen AST: lists, pipelines, redirections, subshells, dispatch.
|
|
2
|
+
|
|
3
|
+
Owned by WP-C. Reaches expansion/builtins ONLY through `ExecContext` (never imports
|
|
4
|
+
`expand` or `commands`). See windows-shell-workpackages-2026-07-19.md §3.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import io
|
|
10
|
+
import os
|
|
11
|
+
import subprocess
|
|
12
|
+
import threading
|
|
13
|
+
from typing import BinaryIO
|
|
14
|
+
|
|
15
|
+
import nodes
|
|
16
|
+
import parser as parser_module
|
|
17
|
+
import proc
|
|
18
|
+
import tokens as tokens_module
|
|
19
|
+
from context import RUNNER_BUILTINS, STATE_BUILTINS, BuiltinContext, ExecContext
|
|
20
|
+
from errors import UnsupportedConstruct
|
|
21
|
+
from state import ShellState
|
|
22
|
+
|
|
23
|
+
DEVNULL = os.devnull
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class _Redirected:
|
|
27
|
+
"""Tracks open file handles opened for a command's redirects, closed after run."""
|
|
28
|
+
|
|
29
|
+
def __init__(self) -> None:
|
|
30
|
+
self.handles: list[BinaryIO] = []
|
|
31
|
+
|
|
32
|
+
def open(self, path: str, mode: str) -> BinaryIO:
|
|
33
|
+
handle = open(path, mode) # noqa: SIM115 - closed explicitly in close()
|
|
34
|
+
self.handles.append(handle)
|
|
35
|
+
return handle
|
|
36
|
+
|
|
37
|
+
def close(self) -> None:
|
|
38
|
+
for handle in self.handles:
|
|
39
|
+
try:
|
|
40
|
+
handle.close()
|
|
41
|
+
except OSError:
|
|
42
|
+
pass
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _redirect_target_path(redirect: nodes.Redirect, ctx: ExecContext) -> str:
|
|
46
|
+
if redirect.target is None:
|
|
47
|
+
raise UnsupportedConstruct("malformed-syntax", "redirect missing a target")
|
|
48
|
+
parts = ctx.expand_word(redirect.target, ctx)
|
|
49
|
+
if not parts:
|
|
50
|
+
raise UnsupportedConstruct("malformed-syntax", "redirect target expanded to nothing")
|
|
51
|
+
if len(parts) != 1:
|
|
52
|
+
raise UnsupportedConstruct("malformed-syntax", "redirect target expanded to multiple words (ambiguous redirect)")
|
|
53
|
+
path = parts[0]
|
|
54
|
+
if path == "/dev/null":
|
|
55
|
+
path = DEVNULL
|
|
56
|
+
if not os.path.isabs(path):
|
|
57
|
+
path = os.path.join(ctx.state.cwd, path)
|
|
58
|
+
return path
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _apply_redirects(
|
|
62
|
+
redirects: list[nodes.Redirect],
|
|
63
|
+
ctx: ExecContext,
|
|
64
|
+
stdin_stream: BinaryIO | int,
|
|
65
|
+
stdout_stream: BinaryIO | int,
|
|
66
|
+
stderr_stream: BinaryIO | int,
|
|
67
|
+
tracker: _Redirected,
|
|
68
|
+
) -> tuple[BinaryIO | int, BinaryIO | int, BinaryIO | int]:
|
|
69
|
+
"""Return the (stdin, stdout, stderr) streams after applying `redirects` in order."""
|
|
70
|
+
cur_in, cur_out, cur_err = stdin_stream, stdout_stream, stderr_stream
|
|
71
|
+
for redirect in redirects:
|
|
72
|
+
op = redirect.op
|
|
73
|
+
if op in (">", "1>"):
|
|
74
|
+
cur_out = tracker.open(_redirect_target_path(redirect, ctx), "wb")
|
|
75
|
+
elif op in (">>", "1>>"):
|
|
76
|
+
cur_out = tracker.open(_redirect_target_path(redirect, ctx), "ab")
|
|
77
|
+
elif op == "<":
|
|
78
|
+
cur_in = tracker.open(_redirect_target_path(redirect, ctx), "rb")
|
|
79
|
+
elif op == "2>":
|
|
80
|
+
cur_err = tracker.open(_redirect_target_path(redirect, ctx), "wb")
|
|
81
|
+
elif op == "2>>":
|
|
82
|
+
cur_err = tracker.open(_redirect_target_path(redirect, ctx), "ab")
|
|
83
|
+
elif op in ("2>&1",):
|
|
84
|
+
cur_err = cur_out
|
|
85
|
+
elif op in ("&>", ">&"):
|
|
86
|
+
target = tracker.open(_redirect_target_path(redirect, ctx), "wb")
|
|
87
|
+
cur_out = target
|
|
88
|
+
cur_err = target
|
|
89
|
+
elif op == "1>&2":
|
|
90
|
+
cur_out = cur_err
|
|
91
|
+
else:
|
|
92
|
+
raise UnsupportedConstruct("malformed-syntax", f"unsupported redirect operator {op!r}")
|
|
93
|
+
return cur_in, cur_out, cur_err
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class _ChildStream:
|
|
97
|
+
"""An OS-level fd usable by a spawned child, plus optional pipe-bridge cleanup.
|
|
98
|
+
|
|
99
|
+
`stream_or_fd` handed to us may already be a real fd (a redirected file, or the
|
|
100
|
+
process's own stdin/stdout) — in that case we hand it straight through. It may also
|
|
101
|
+
be a plain in-memory `BinaryIO` (e.g. the merged-sink `io.BytesIO` used by `main.py`
|
|
102
|
+
and command substitution) which has no fd a child process can write/read directly;
|
|
103
|
+
for that case we bridge through a real `os.pipe()` with a background thread copying
|
|
104
|
+
bytes to/from the memory stream, so external children work identically either way.
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
def __init__(self, fd: int, owns_fd: bool, join: object | None = None) -> None:
|
|
108
|
+
self.fd = fd
|
|
109
|
+
self.owns_fd = owns_fd
|
|
110
|
+
self.join = join
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _real_fd(stream: BinaryIO | int) -> int | None:
|
|
114
|
+
if isinstance(stream, int):
|
|
115
|
+
return stream
|
|
116
|
+
try:
|
|
117
|
+
return stream.fileno()
|
|
118
|
+
except (io.UnsupportedOperation, AttributeError, ValueError, OSError):
|
|
119
|
+
return None
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def _prepare_child_output(stream: BinaryIO | int) -> _ChildStream:
|
|
123
|
+
fd = _real_fd(stream)
|
|
124
|
+
if fd is not None:
|
|
125
|
+
return _ChildStream(fd, owns_fd=False)
|
|
126
|
+
assert not isinstance(stream, int)
|
|
127
|
+
read_fd, write_fd = os.pipe()
|
|
128
|
+
|
|
129
|
+
def pump() -> None:
|
|
130
|
+
with os.fdopen(read_fd, "rb", closefd=True) as reader:
|
|
131
|
+
while True:
|
|
132
|
+
chunk = reader.read(65536)
|
|
133
|
+
if not chunk:
|
|
134
|
+
break
|
|
135
|
+
stream.write(chunk)
|
|
136
|
+
|
|
137
|
+
thread = threading.Thread(target=pump, daemon=True)
|
|
138
|
+
thread.start()
|
|
139
|
+
return _ChildStream(write_fd, owns_fd=True, join=thread.join)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _prepare_child_input(stream: BinaryIO | int) -> _ChildStream:
|
|
143
|
+
fd = _real_fd(stream)
|
|
144
|
+
if fd is not None:
|
|
145
|
+
return _ChildStream(fd, owns_fd=False)
|
|
146
|
+
assert not isinstance(stream, int)
|
|
147
|
+
data = stream.read()
|
|
148
|
+
read_fd, write_fd = os.pipe()
|
|
149
|
+
|
|
150
|
+
def feed() -> None:
|
|
151
|
+
with os.fdopen(write_fd, "wb", closefd=True) as writer:
|
|
152
|
+
writer.write(data)
|
|
153
|
+
|
|
154
|
+
thread = threading.Thread(target=feed, daemon=True)
|
|
155
|
+
thread.start()
|
|
156
|
+
return _ChildStream(read_fd, owns_fd=True, join=thread.join)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def _spawn_with_bridges(
|
|
160
|
+
argv: list[str],
|
|
161
|
+
state: ShellState,
|
|
162
|
+
stdin_stream: BinaryIO | int,
|
|
163
|
+
stdout_stream: BinaryIO | int,
|
|
164
|
+
stderr_stream: BinaryIO | int,
|
|
165
|
+
deadline: float | None,
|
|
166
|
+
) -> tuple["subprocess.Popen[bytes]", _ChildStream, _ChildStream, _ChildStream]:
|
|
167
|
+
"""Spawn `argv`, bridging any non-fd streams through a pipe.
|
|
168
|
+
|
|
169
|
+
Returns `(child, in_prep, out_prep, err_prep)`; the caller must wait on `child` and
|
|
170
|
+
then call `_finish_bridges(in_prep, out_prep, err_prep)` so any bridge thread has
|
|
171
|
+
fully drained/fed the memory stream before the caller reads its contents.
|
|
172
|
+
"""
|
|
173
|
+
in_prep = _prepare_child_input(stdin_stream)
|
|
174
|
+
out_prep = _prepare_child_output(stdout_stream)
|
|
175
|
+
err_prep = out_prep if stderr_stream is stdout_stream else _prepare_child_output(stderr_stream)
|
|
176
|
+
try:
|
|
177
|
+
child = proc.spawn_external(argv, state, in_prep.fd, out_prep.fd, err_prep.fd, deadline)
|
|
178
|
+
finally:
|
|
179
|
+
if in_prep.owns_fd:
|
|
180
|
+
_safe_close(in_prep.fd)
|
|
181
|
+
if out_prep.owns_fd:
|
|
182
|
+
_safe_close(out_prep.fd)
|
|
183
|
+
if err_prep is not out_prep and err_prep.owns_fd:
|
|
184
|
+
_safe_close(err_prep.fd)
|
|
185
|
+
return child, in_prep, out_prep, err_prep
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def _finish_bridges(*preps: _ChildStream) -> None:
|
|
189
|
+
for prep in preps:
|
|
190
|
+
if prep.join is not None:
|
|
191
|
+
prep.join()
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def _merged_sink(ctx: ExecContext) -> BinaryIO | int:
|
|
195
|
+
"""The session merged sink for un-redirected stderr: `ctx.stderr` if set, else `ctx.stdout`."""
|
|
196
|
+
return ctx.stderr if ctx.stderr is not None else ctx.stdout
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def _write_merged(stream: BinaryIO | int, data: bytes, ctx: ExecContext) -> None:
|
|
200
|
+
if isinstance(stream, int):
|
|
201
|
+
os.write(stream, data)
|
|
202
|
+
else:
|
|
203
|
+
stream.write(data)
|
|
204
|
+
stream.flush()
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def execute(list_node: nodes.CommandList, ctx: ExecContext) -> int:
|
|
208
|
+
"""Run a `CommandList`; returns the exit code of the last executed entry."""
|
|
209
|
+
exit_code = 0
|
|
210
|
+
for entry in list_node.entries:
|
|
211
|
+
exit_code = _execute_andor(entry, ctx)
|
|
212
|
+
return exit_code
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def _execute_andor(andor: nodes.AndOr, ctx: ExecContext) -> int:
|
|
216
|
+
exit_code = 0
|
|
217
|
+
for i, pipeline in enumerate(andor.pipelines):
|
|
218
|
+
if i > 0:
|
|
219
|
+
operator = andor.operators[i - 1]
|
|
220
|
+
if operator == "&&" and exit_code != 0:
|
|
221
|
+
continue
|
|
222
|
+
if operator == "||" and exit_code == 0:
|
|
223
|
+
continue
|
|
224
|
+
exit_code = _execute_pipeline(pipeline, ctx)
|
|
225
|
+
return exit_code
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def _execute_pipeline(pipeline: nodes.Pipeline, ctx: ExecContext) -> int:
|
|
229
|
+
elements = pipeline.elements
|
|
230
|
+
exit_code = _run_pipeline_elements(elements, ctx)
|
|
231
|
+
if pipeline.negated:
|
|
232
|
+
exit_code = 0 if exit_code != 0 else 1
|
|
233
|
+
return exit_code
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def _run_pipeline_elements(elements: list, ctx: ExecContext) -> int:
|
|
237
|
+
stderr_base = _merged_sink(ctx)
|
|
238
|
+
if len(elements) == 1:
|
|
239
|
+
return _dispatch_element(elements[0], ctx, ctx.stdin, ctx.stdout, stderr_base)
|
|
240
|
+
|
|
241
|
+
n = len(elements)
|
|
242
|
+
read_ends: list[int] = []
|
|
243
|
+
write_ends: list[int] = []
|
|
244
|
+
for _ in range(n - 1):
|
|
245
|
+
r, w = os.pipe()
|
|
246
|
+
read_ends.append(r)
|
|
247
|
+
write_ends.append(w)
|
|
248
|
+
|
|
249
|
+
# Every fd number actually closed so far. `os.pipe()` recycles fd numbers, so a
|
|
250
|
+
# bridge pipe created later in this same loop (for a non-fd stdout/stdin stream)
|
|
251
|
+
# may reuse a number we already closed; guard every close through this set so a
|
|
252
|
+
# live, recycled fd is never closed a second time.
|
|
253
|
+
closed_fds: set[int] = set()
|
|
254
|
+
|
|
255
|
+
def close_once(fd: int) -> None:
|
|
256
|
+
if fd not in closed_fds:
|
|
257
|
+
closed_fds.add(fd)
|
|
258
|
+
_safe_close(fd)
|
|
259
|
+
|
|
260
|
+
results: list[int] = [0] * n
|
|
261
|
+
refusals: list[UnsupportedConstruct | None] = [None] * n
|
|
262
|
+
|
|
263
|
+
# External-process stages spawn as real OS processes (concurrent by construction).
|
|
264
|
+
# Builtin/state stages run in-process; each runs on its own thread so a stage that
|
|
265
|
+
# writes more than a pipe's kernel buffer cannot deadlock waiting for a downstream
|
|
266
|
+
# reader that hasn't started yet (real bash pipes are all concurrent).
|
|
267
|
+
procs: list[subprocess.Popen | None] = [None] * n
|
|
268
|
+
proc_preps: list[tuple[_ChildStream, _ChildStream, _ChildStream] | None] = [None] * n
|
|
269
|
+
threads: list[threading.Thread | None] = [None] * n
|
|
270
|
+
|
|
271
|
+
for index in range(n):
|
|
272
|
+
stdin_stream = ctx.stdin if index == 0 else read_ends[index - 1]
|
|
273
|
+
stdout_stream = ctx.stdout if index == n - 1 else write_ends[index]
|
|
274
|
+
element = elements[index]
|
|
275
|
+
is_write_end_owned_here = index < n - 1
|
|
276
|
+
|
|
277
|
+
if isinstance(element, nodes.SimpleCommand) and _is_external_dispatch(element, ctx):
|
|
278
|
+
spawned = _spawn_external_stage(element, ctx, stdin_stream, stdout_stream, stderr_base)
|
|
279
|
+
if spawned is None:
|
|
280
|
+
# Command-not-found in a pipeline stage: report it, record 127 for this
|
|
281
|
+
# stage, and close the stage's owned write end so downstream sees EOF —
|
|
282
|
+
# never let this crash the engine or stall the rest of the pipeline.
|
|
283
|
+
results[index] = 127
|
|
284
|
+
else:
|
|
285
|
+
child, preps = spawned
|
|
286
|
+
procs[index] = child
|
|
287
|
+
proc_preps[index] = preps
|
|
288
|
+
# Parent's real-pipe copies were dup'd into the child by fork/exec (or were
|
|
289
|
+
# never used, on the not-found path); close ours so downstream sees EOF
|
|
290
|
+
# once the (now independent) child process exits or the stage is skipped.
|
|
291
|
+
if is_write_end_owned_here:
|
|
292
|
+
close_once(write_ends[index])
|
|
293
|
+
if index > 0:
|
|
294
|
+
close_once(read_ends[index - 1])
|
|
295
|
+
else:
|
|
296
|
+
# Inline (in-process) stage: the thread reads/writes these SAME fds directly
|
|
297
|
+
# (no fork/dup), so the main loop must not close them out from under it.
|
|
298
|
+
|
|
299
|
+
def run_inline(idx=index, sin=stdin_stream, sout=stdout_stream, owns_write=is_write_end_owned_here) -> None:
|
|
300
|
+
try:
|
|
301
|
+
# Pipeline stages are subshell-like: state mutations belong to the stage,
|
|
302
|
+
# never to the parent command context or a sibling stage.
|
|
303
|
+
stage_ctx = _sub_ctx(ctx, ctx.state.copy(), sin, sout, stderr_base)
|
|
304
|
+
results[idx] = _dispatch_element(elements[idx], stage_ctx, sin, sout, stderr_base, is_pipeline=True)
|
|
305
|
+
except UnsupportedConstruct as exc:
|
|
306
|
+
# A structured refusal from an inline stage means the WHOLE command
|
|
307
|
+
# is outside the frozen grammar/behavior contract — captured here and
|
|
308
|
+
# re-raised on the main thread after all stages join (first one wins),
|
|
309
|
+
# so the caller sees the normal structured-refusal frame, never a
|
|
310
|
+
# fabricated exit-0/empty-output success.
|
|
311
|
+
refusals[idx] = exc
|
|
312
|
+
results[idx] = 1
|
|
313
|
+
except BaseException as exc: # noqa: BLE001 - any other stage failure must not crash the engine
|
|
314
|
+
# Any OTHER exception is a real per-stage failure, not a refusal: it
|
|
315
|
+
# gets a named, actionable one-line message on the merged sink (never
|
|
316
|
+
# a Python traceback) and this stage records exit 1.
|
|
317
|
+
message = f"{_stage_label(elements[idx])}: {exc}\n"
|
|
318
|
+
_write_merged(stderr_base, message.encode("utf-8", errors="replace"), ctx)
|
|
319
|
+
results[idx] = 1
|
|
320
|
+
finally:
|
|
321
|
+
if owns_write:
|
|
322
|
+
close_once(sout)
|
|
323
|
+
|
|
324
|
+
thread = threading.Thread(target=run_inline, daemon=True)
|
|
325
|
+
threads[index] = thread
|
|
326
|
+
thread.start()
|
|
327
|
+
|
|
328
|
+
for thread in threads:
|
|
329
|
+
if thread is not None:
|
|
330
|
+
thread.join()
|
|
331
|
+
for index, proc_obj in enumerate(procs):
|
|
332
|
+
if proc_obj is not None:
|
|
333
|
+
results[index] = proc.wait_with_deadline(proc_obj, ctx.deadline)
|
|
334
|
+
preps = proc_preps[index]
|
|
335
|
+
if preps is not None:
|
|
336
|
+
_finish_bridges(*preps)
|
|
337
|
+
|
|
338
|
+
# Defensive cleanup: any pipe fd an inline stage held onto (rather than an external
|
|
339
|
+
# stage's already-closed dup) is now unreachable and can be closed — but never a
|
|
340
|
+
# number already closed and potentially recycled by a bridge pipe above.
|
|
341
|
+
for fd in (*read_ends, *write_ends):
|
|
342
|
+
close_once(fd)
|
|
343
|
+
|
|
344
|
+
for refusal in refusals:
|
|
345
|
+
if refusal is not None:
|
|
346
|
+
# First refusal wins; re-raise on the main thread (after every stage has
|
|
347
|
+
# joined/waited and every fd is cleaned up) so `execute()`'s caller sees the
|
|
348
|
+
# normal structured-refusal frame for the whole command — never a fabricated
|
|
349
|
+
# exit-0/empty-output success.
|
|
350
|
+
raise refusal
|
|
351
|
+
|
|
352
|
+
return results[-1]
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
def _stage_label(element) -> str:
|
|
356
|
+
"""A short, human-actionable name for a pipeline stage used in error messages.
|
|
357
|
+
|
|
358
|
+
Uses the unexpanded command word text when it is a plain `Lit`/`Raw` segment (no
|
|
359
|
+
expansion needed to read it); falls back to the node's type name otherwise.
|
|
360
|
+
"""
|
|
361
|
+
if isinstance(element, nodes.SimpleCommand) and element.words:
|
|
362
|
+
segments = element.words[0].segments
|
|
363
|
+
if len(segments) == 1 and isinstance(segments[0], (nodes.Lit, nodes.Raw)):
|
|
364
|
+
return segments[0].text
|
|
365
|
+
return type(element).__name__
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
def _safe_close(fd_or_stream) -> None:
|
|
369
|
+
if isinstance(fd_or_stream, int):
|
|
370
|
+
try:
|
|
371
|
+
os.close(fd_or_stream)
|
|
372
|
+
except OSError:
|
|
373
|
+
pass
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
def _is_external_dispatch(command: nodes.SimpleCommand, ctx: ExecContext) -> bool:
|
|
377
|
+
if not command.words:
|
|
378
|
+
return False
|
|
379
|
+
expanded = ctx.expand_word(command.words[0], ctx)
|
|
380
|
+
if not expanded:
|
|
381
|
+
return False
|
|
382
|
+
name = expanded[0]
|
|
383
|
+
if name in STATE_BUILTINS or name in RUNNER_BUILTINS or name in ctx.builtins:
|
|
384
|
+
return False
|
|
385
|
+
return True
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
def _spawn_external_stage(
|
|
389
|
+
command: nodes.SimpleCommand,
|
|
390
|
+
ctx: ExecContext,
|
|
391
|
+
stdin_stream: BinaryIO | int,
|
|
392
|
+
stdout_stream: BinaryIO | int,
|
|
393
|
+
stderr_base: BinaryIO | int,
|
|
394
|
+
) -> tuple[subprocess.Popen, tuple[_ChildStream, _ChildStream, _ChildStream]] | None:
|
|
395
|
+
"""Spawn one external pipeline stage, applying ITS OWN redirects first.
|
|
396
|
+
|
|
397
|
+
Un-redirected stdout stays wired to the pipe; un-redirected stderr routes to
|
|
398
|
+
`stderr_base` (the session merged sink), never into the pipe. Returns `None` (after
|
|
399
|
+
reporting "command not found" to `stderr_base`) instead of raising, so a bad stage
|
|
400
|
+
name cannot crash the whole pipeline — the caller records exit 127 for this stage.
|
|
401
|
+
"""
|
|
402
|
+
tracker = _Redirected()
|
|
403
|
+
try:
|
|
404
|
+
r_in, r_out, r_err = _apply_redirects(command.redirects, ctx, stdin_stream, stdout_stream, stderr_base, tracker)
|
|
405
|
+
argv = _expand_argv(command, ctx)
|
|
406
|
+
env = _apply_transient_assignments(command, ctx)
|
|
407
|
+
scratch_state = ShellState(cwd=ctx.state.cwd, env=env)
|
|
408
|
+
try:
|
|
409
|
+
child, in_prep, out_prep, err_prep = _spawn_with_bridges(
|
|
410
|
+
argv, scratch_state, r_in, r_out, r_err, ctx.deadline
|
|
411
|
+
)
|
|
412
|
+
except FileNotFoundError:
|
|
413
|
+
message = f"{argv[0]}: command not found\n"
|
|
414
|
+
_write_merged(stderr_base, message.encode("utf-8", errors="replace"), ctx)
|
|
415
|
+
return None
|
|
416
|
+
return child, (in_prep, out_prep, err_prep)
|
|
417
|
+
finally:
|
|
418
|
+
tracker.close()
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
def _expand_argv(command: nodes.SimpleCommand, ctx: ExecContext) -> list[str]:
|
|
422
|
+
argv: list[str] = []
|
|
423
|
+
for word in command.words:
|
|
424
|
+
argv.extend(ctx.expand_word(word, ctx))
|
|
425
|
+
return argv
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
def _apply_transient_assignments(command: nodes.SimpleCommand, ctx: ExecContext) -> dict[str, str]:
|
|
429
|
+
env = ctx.state.env.copy()
|
|
430
|
+
for name, word in command.assignments:
|
|
431
|
+
values = ctx.expand_word(word, ctx)
|
|
432
|
+
env[name] = "".join(values) if values else ""
|
|
433
|
+
return env
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
def _dispatch_element(
|
|
437
|
+
element,
|
|
438
|
+
ctx: ExecContext,
|
|
439
|
+
stdin_stream: BinaryIO | int,
|
|
440
|
+
stdout_stream: BinaryIO | int,
|
|
441
|
+
stderr_stream: BinaryIO | int,
|
|
442
|
+
is_pipeline: bool = False,
|
|
443
|
+
) -> int:
|
|
444
|
+
if isinstance(element, nodes.Subshell):
|
|
445
|
+
return _execute_subshell(element, ctx, stdin_stream, stdout_stream, stderr_stream)
|
|
446
|
+
if isinstance(element, nodes.BraceGroup):
|
|
447
|
+
return _execute_brace_group(element, ctx, stdin_stream, stdout_stream, stderr_stream)
|
|
448
|
+
if isinstance(element, nodes.SimpleCommand):
|
|
449
|
+
return _dispatch_simple_command(element, ctx, stdin_stream, stdout_stream, stderr_stream)
|
|
450
|
+
raise UnsupportedConstruct("malformed-syntax", f"unrecognized pipeline element {type(element)!r}")
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
def _sub_ctx(
|
|
454
|
+
ctx: ExecContext,
|
|
455
|
+
state: ShellState,
|
|
456
|
+
stdin_stream: BinaryIO | int,
|
|
457
|
+
stdout_stream: BinaryIO | int,
|
|
458
|
+
stderr_stream: BinaryIO | int,
|
|
459
|
+
) -> ExecContext:
|
|
460
|
+
return ExecContext(
|
|
461
|
+
state=state,
|
|
462
|
+
stdin=stdin_stream,
|
|
463
|
+
stdout=stdout_stream,
|
|
464
|
+
expand_word=ctx.expand_word,
|
|
465
|
+
run_command_substitution=ctx.run_command_substitution,
|
|
466
|
+
builtins=ctx.builtins,
|
|
467
|
+
deadline=ctx.deadline,
|
|
468
|
+
stderr=stderr_stream,
|
|
469
|
+
)
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
def _execute_subshell(node: nodes.Subshell, ctx: ExecContext, stdin_stream, stdout_stream, stderr_stream) -> int:
|
|
473
|
+
tracker = _Redirected()
|
|
474
|
+
try:
|
|
475
|
+
r_in, r_out, r_err = _apply_redirects(
|
|
476
|
+
node.redirects, ctx, stdin_stream, stdout_stream, stderr_stream, tracker
|
|
477
|
+
)
|
|
478
|
+
isolated_state = ctx.state.copy()
|
|
479
|
+
inner_ctx = _sub_ctx(ctx, isolated_state, r_in, r_out, r_err)
|
|
480
|
+
return execute(node.body, inner_ctx)
|
|
481
|
+
finally:
|
|
482
|
+
tracker.close()
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
def _execute_brace_group(node: nodes.BraceGroup, ctx: ExecContext, stdin_stream, stdout_stream, stderr_stream) -> int:
|
|
486
|
+
tracker = _Redirected()
|
|
487
|
+
try:
|
|
488
|
+
r_in, r_out, r_err = _apply_redirects(
|
|
489
|
+
node.redirects, ctx, stdin_stream, stdout_stream, stderr_stream, tracker
|
|
490
|
+
)
|
|
491
|
+
inner_ctx = _sub_ctx(ctx, ctx.state, r_in, r_out, r_err)
|
|
492
|
+
return execute(node.body, inner_ctx)
|
|
493
|
+
finally:
|
|
494
|
+
tracker.close()
|
|
495
|
+
|
|
496
|
+
|
|
497
|
+
def _dispatch_simple_command(
|
|
498
|
+
command: nodes.SimpleCommand,
|
|
499
|
+
ctx: ExecContext,
|
|
500
|
+
stdin_stream: BinaryIO | int,
|
|
501
|
+
stdout_stream: BinaryIO | int,
|
|
502
|
+
stderr_stream: BinaryIO | int,
|
|
503
|
+
) -> int:
|
|
504
|
+
tracker = _Redirected()
|
|
505
|
+
try:
|
|
506
|
+
r_in, r_out, r_err = _apply_redirects(
|
|
507
|
+
command.redirects, ctx, stdin_stream, stdout_stream, stderr_stream, tracker
|
|
508
|
+
)
|
|
509
|
+
|
|
510
|
+
if not command.words:
|
|
511
|
+
# Pure assignment(s), no command word: mutate ctx.state.
|
|
512
|
+
for name, word in command.assignments:
|
|
513
|
+
values = ctx.expand_word(word, ctx)
|
|
514
|
+
ctx.state.setenv(name, "".join(values) if values else "")
|
|
515
|
+
return 0
|
|
516
|
+
|
|
517
|
+
argv = _expand_argv(command, ctx)
|
|
518
|
+
if not argv:
|
|
519
|
+
# All words expanded to zero argv strings (e.g. an unset/empty `$VAR` alone):
|
|
520
|
+
# bash runs no command here. Redirects/assignments above still applied; exit 0.
|
|
521
|
+
return 0
|
|
522
|
+
name = argv[0]
|
|
523
|
+
|
|
524
|
+
if name in STATE_BUILTINS:
|
|
525
|
+
return _run_state_builtin(name, argv, ctx, r_out)
|
|
526
|
+
|
|
527
|
+
if name in RUNNER_BUILTINS:
|
|
528
|
+
return _run_xargs(argv, ctx, r_in, r_out)
|
|
529
|
+
|
|
530
|
+
if name in ctx.builtins:
|
|
531
|
+
env = _apply_transient_assignments(command, ctx)
|
|
532
|
+
builtin_ctx = BuiltinContext(
|
|
533
|
+
argv=argv,
|
|
534
|
+
cwd=ctx.state.cwd,
|
|
535
|
+
env=env,
|
|
536
|
+
stdin=_as_stream(r_in, "rb"),
|
|
537
|
+
stdout=_as_stream(r_out, "wb"),
|
|
538
|
+
stderr=_as_stream(r_err, "wb"),
|
|
539
|
+
)
|
|
540
|
+
return ctx.builtins[name](builtin_ctx)
|
|
541
|
+
|
|
542
|
+
# External command.
|
|
543
|
+
env = _apply_transient_assignments(command, ctx)
|
|
544
|
+
scratch_state = ShellState(cwd=ctx.state.cwd, env=env)
|
|
545
|
+
try:
|
|
546
|
+
child, in_prep, out_prep, err_prep = _spawn_with_bridges(argv, scratch_state, r_in, r_out, r_err, ctx.deadline)
|
|
547
|
+
except FileNotFoundError:
|
|
548
|
+
message = f"{name}: command not found\n"
|
|
549
|
+
_write_merged(r_err, message.encode("utf-8", errors="replace"), ctx)
|
|
550
|
+
return 127
|
|
551
|
+
exit_code = proc.wait_with_deadline(child, ctx.deadline)
|
|
552
|
+
_finish_bridges(in_prep, out_prep, err_prep)
|
|
553
|
+
return exit_code
|
|
554
|
+
finally:
|
|
555
|
+
tracker.close()
|
|
556
|
+
|
|
557
|
+
|
|
558
|
+
def _as_stream(fd_or_stream: BinaryIO | int, mode: str) -> BinaryIO:
|
|
559
|
+
"""Wrap `fd_or_stream` as a `BinaryIO` opened in the CALLER-KNOWN `mode`.
|
|
560
|
+
|
|
561
|
+
A pipeline stage's stdin/stdout fd is an arbitrary kernel fd number picked by
|
|
562
|
+
`os.pipe()` — it is never reliably 0 or 1 — so the read/write intent must come from
|
|
563
|
+
the call site (which knows whether this is stdin or stdout), never be guessed from
|
|
564
|
+
the fd's numeric value.
|
|
565
|
+
"""
|
|
566
|
+
if isinstance(fd_or_stream, int):
|
|
567
|
+
return os.fdopen(fd_or_stream, mode, closefd=False)
|
|
568
|
+
return fd_or_stream
|
|
569
|
+
|
|
570
|
+
|
|
571
|
+
def _run_state_builtin(name: str, argv: list[str], ctx: ExecContext, out_stream: BinaryIO | int) -> int:
|
|
572
|
+
if name == "cd":
|
|
573
|
+
target = argv[1] if len(argv) > 1 else ctx.state.env.get("HOME")
|
|
574
|
+
print_new_cwd = argv[1:2] == ["-"]
|
|
575
|
+
if print_new_cwd:
|
|
576
|
+
oldpwd = ctx.state.env.get("OLDPWD")
|
|
577
|
+
if not oldpwd:
|
|
578
|
+
_write_merged(out_stream, b"cd: OLDPWD not set\n", ctx)
|
|
579
|
+
return 1
|
|
580
|
+
target = oldpwd
|
|
581
|
+
if target is None:
|
|
582
|
+
_write_merged(out_stream, b"cd: HOME not set\n", ctx)
|
|
583
|
+
return 1
|
|
584
|
+
try:
|
|
585
|
+
ctx.state.chdir(target)
|
|
586
|
+
except FileNotFoundError:
|
|
587
|
+
_write_merged(out_stream, f"cd: {target}: No such file or directory\n".encode("utf-8"), ctx)
|
|
588
|
+
return 1
|
|
589
|
+
if print_new_cwd:
|
|
590
|
+
_write_merged(out_stream, f"{ctx.state.cwd}\n".encode("utf-8"), ctx)
|
|
591
|
+
return 0
|
|
592
|
+
|
|
593
|
+
if name == "export":
|
|
594
|
+
for item in argv[1:]:
|
|
595
|
+
if "=" in item:
|
|
596
|
+
key, _, value = item.partition("=")
|
|
597
|
+
ctx.state.setenv(key, value)
|
|
598
|
+
else:
|
|
599
|
+
ctx.state.env.setdefault(item, "")
|
|
600
|
+
return 0
|
|
601
|
+
|
|
602
|
+
if name == "unset":
|
|
603
|
+
for item in argv[1:]:
|
|
604
|
+
ctx.state.unsetenv(item)
|
|
605
|
+
return 0
|
|
606
|
+
|
|
607
|
+
raise UnsupportedConstruct("unsupported-builtin", f"unsupported state builtin {name!r}")
|
|
608
|
+
|
|
609
|
+
|
|
610
|
+
def _run_xargs(argv: list[str], ctx: ExecContext, in_stream: BinaryIO | int, out_stream: BinaryIO | int) -> int:
|
|
611
|
+
args = argv[1:]
|
|
612
|
+
null_sep = False
|
|
613
|
+
n_count: int | None = None
|
|
614
|
+
replace_marker: str | None = None
|
|
615
|
+
i = 0
|
|
616
|
+
while i < len(args):
|
|
617
|
+
a = args[i]
|
|
618
|
+
if a == "-0":
|
|
619
|
+
null_sep = True
|
|
620
|
+
i += 1
|
|
621
|
+
elif a == "-n":
|
|
622
|
+
i += 1
|
|
623
|
+
if i >= len(args):
|
|
624
|
+
raise UnsupportedConstruct("unsupported-flag", "xargs -n requires an argument")
|
|
625
|
+
n_count = int(args[i])
|
|
626
|
+
i += 1
|
|
627
|
+
elif a.startswith("-n") and len(a) > 2 and a[2:].isdigit():
|
|
628
|
+
n_count = int(a[2:])
|
|
629
|
+
i += 1
|
|
630
|
+
elif a == "-I":
|
|
631
|
+
i += 1
|
|
632
|
+
if i >= len(args):
|
|
633
|
+
raise UnsupportedConstruct("unsupported-flag", "xargs -I requires an argument")
|
|
634
|
+
replace_marker = args[i]
|
|
635
|
+
i += 1
|
|
636
|
+
elif a.startswith("-I") and len(a) > 2:
|
|
637
|
+
replace_marker = a[2:]
|
|
638
|
+
i += 1
|
|
639
|
+
elif a.startswith("-") and a not in ("-",):
|
|
640
|
+
raise UnsupportedConstruct("unsupported-flag", f"xargs: unsupported flag {a!r}")
|
|
641
|
+
else:
|
|
642
|
+
break
|
|
643
|
+
cmd_argv = args[i:]
|
|
644
|
+
if not cmd_argv:
|
|
645
|
+
raise UnsupportedConstruct("malformed-syntax", "xargs requires a command")
|
|
646
|
+
|
|
647
|
+
data = _read_all(in_stream)
|
|
648
|
+
if null_sep:
|
|
649
|
+
raw_tokens = [t for t in data.split(b"\x00") if t != b""]
|
|
650
|
+
items = [t.decode("utf-8", errors="replace") for t in raw_tokens]
|
|
651
|
+
else:
|
|
652
|
+
items = data.decode("utf-8", errors="replace").split()
|
|
653
|
+
|
|
654
|
+
exit_code = 0
|
|
655
|
+
if replace_marker is not None:
|
|
656
|
+
for item in items:
|
|
657
|
+
# GNU xargs -I replaces every OCCURRENCE of the marker inside each argument
|
|
658
|
+
# (a substring replace), not only args that are exactly the marker token.
|
|
659
|
+
batch_argv = [piece.replace(replace_marker, item) for piece in cmd_argv]
|
|
660
|
+
exit_code = _run_xargs_batch(batch_argv, ctx, out_stream)
|
|
661
|
+
return exit_code
|
|
662
|
+
|
|
663
|
+
batch_size = n_count if n_count is not None else len(items) if items else 1
|
|
664
|
+
if batch_size <= 0:
|
|
665
|
+
batch_size = 1
|
|
666
|
+
if not items:
|
|
667
|
+
return 0
|
|
668
|
+
for start in range(0, len(items), batch_size):
|
|
669
|
+
batch = items[start : start + batch_size]
|
|
670
|
+
exit_code = _run_xargs_batch([*cmd_argv, *batch], ctx, out_stream)
|
|
671
|
+
return exit_code
|
|
672
|
+
|
|
673
|
+
|
|
674
|
+
def _run_xargs_batch(argv: list[str], ctx: ExecContext, out_stream: BinaryIO | int) -> int:
|
|
675
|
+
name = argv[0]
|
|
676
|
+
if name in ctx.builtins:
|
|
677
|
+
builtin_ctx = BuiltinContext(
|
|
678
|
+
argv=argv,
|
|
679
|
+
cwd=ctx.state.cwd,
|
|
680
|
+
env=ctx.state.env.copy(),
|
|
681
|
+
stdin=_empty_stream(),
|
|
682
|
+
stdout=_as_stream(out_stream, "wb"),
|
|
683
|
+
stderr=_as_stream(out_stream, "wb"),
|
|
684
|
+
)
|
|
685
|
+
return ctx.builtins[name](builtin_ctx)
|
|
686
|
+
if name in STATE_BUILTINS:
|
|
687
|
+
return _run_state_builtin(name, argv, ctx, out_stream)
|
|
688
|
+
scratch_state = ShellState(cwd=ctx.state.cwd, env=ctx.state.env.copy())
|
|
689
|
+
try:
|
|
690
|
+
child, in_prep, out_prep, err_prep = _spawn_with_bridges(
|
|
691
|
+
argv, scratch_state, subprocess.DEVNULL, out_stream, out_stream, ctx.deadline
|
|
692
|
+
)
|
|
693
|
+
except FileNotFoundError:
|
|
694
|
+
_write_merged(out_stream, f"{name}: command not found\n".encode("utf-8"), ctx)
|
|
695
|
+
return 127
|
|
696
|
+
exit_code = proc.wait_with_deadline(child, ctx.deadline)
|
|
697
|
+
_finish_bridges(in_prep, out_prep, err_prep)
|
|
698
|
+
return exit_code
|
|
699
|
+
|
|
700
|
+
|
|
701
|
+
def _empty_stream() -> BinaryIO:
|
|
702
|
+
return io.BytesIO(b"")
|
|
703
|
+
|
|
704
|
+
|
|
705
|
+
def _read_all(stream: BinaryIO | int) -> bytes:
|
|
706
|
+
if isinstance(stream, int):
|
|
707
|
+
chunks = []
|
|
708
|
+
while True:
|
|
709
|
+
chunk = os.read(stream, 65536)
|
|
710
|
+
if not chunk:
|
|
711
|
+
break
|
|
712
|
+
chunks.append(chunk)
|
|
713
|
+
return b"".join(chunks)
|
|
714
|
+
return stream.read()
|
|
715
|
+
|
|
716
|
+
|
|
717
|
+
def run_command_substitution(src: str, ctx: ExecContext) -> tuple[str, int]:
|
|
718
|
+
"""Tokenize+parse+execute `src` with stdout captured; returns (stripped_stdout, exit_code)."""
|
|
719
|
+
tokens = tokens_module.tokenize(src)
|
|
720
|
+
ast = parser_module.parse(tokens)
|
|
721
|
+
buffer = io.BytesIO()
|
|
722
|
+
sub_ctx = ExecContext(
|
|
723
|
+
state=ctx.state.copy(),
|
|
724
|
+
stdin=ctx.stdin,
|
|
725
|
+
stdout=buffer,
|
|
726
|
+
expand_word=ctx.expand_word,
|
|
727
|
+
run_command_substitution=ctx.run_command_substitution,
|
|
728
|
+
builtins=ctx.builtins,
|
|
729
|
+
deadline=ctx.deadline,
|
|
730
|
+
stderr=ctx.stderr,
|
|
731
|
+
)
|
|
732
|
+
exit_code = execute(ast, sub_ctx)
|
|
733
|
+
text = buffer.getvalue().decode("utf-8", errors="replace")
|
|
734
|
+
return text.rstrip("\n"), exit_code
|