@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,287 @@
|
|
|
1
|
+
"""Token list -> command AST; raises `UnsupportedConstruct` for banned grammar.
|
|
2
|
+
|
|
3
|
+
`parse(tokens) -> CommandList` per windows-shell-workpackages-2026-07-19.md §1.4. Never
|
|
4
|
+
returns a partial/guessed tree: any construct outside the frozen grammar raises
|
|
5
|
+
`UnsupportedConstruct` with the matching catalog id and an actionable message naming the
|
|
6
|
+
construct.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import re
|
|
12
|
+
|
|
13
|
+
from errors import UnsupportedConstruct
|
|
14
|
+
from nodes import (
|
|
15
|
+
AndOr,
|
|
16
|
+
BraceGroup,
|
|
17
|
+
CommandList,
|
|
18
|
+
DQ,
|
|
19
|
+
Lit,
|
|
20
|
+
Pipeline,
|
|
21
|
+
PipelineElement,
|
|
22
|
+
Raw,
|
|
23
|
+
Redirect,
|
|
24
|
+
SimpleCommand,
|
|
25
|
+
Subshell,
|
|
26
|
+
Word,
|
|
27
|
+
)
|
|
28
|
+
from tokens import Token
|
|
29
|
+
|
|
30
|
+
_ASSIGNMENT_RE = re.compile(r"^([A-Za-z_][A-Za-z0-9_]*)=(.*)$", re.DOTALL)
|
|
31
|
+
_EXTGLOB_RE = re.compile(r"[@!?*+]\(")
|
|
32
|
+
_BRACE_EXPANSION_RE = re.compile(r"\{[^{}]*,[^{}]*\}")
|
|
33
|
+
|
|
34
|
+
_JOB_CONTROL_WORDS = {"fg", "bg", "jobs", "wait", "disown"}
|
|
35
|
+
_CONTROL_FLOW_WORDS = {"if", "for", "while", "until", "case", "select"}
|
|
36
|
+
_ARITHMETIC_WORDS = {"let"}
|
|
37
|
+
_UNSUPPORTED_BUILTIN_WORDS = {"eval", "source", ".", "alias", "trap", "set", "shopt", "read", "declare", "local"}
|
|
38
|
+
_NESTED_SHELL_WORDS = {"bash", "sh", "zsh", "fish", "cmd", "powershell", "pwsh", "wsl"}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _literal_text_from_segments(segments: list) -> str | None:
|
|
42
|
+
parts: list[str] = []
|
|
43
|
+
for seg in segments:
|
|
44
|
+
if isinstance(seg, Lit):
|
|
45
|
+
parts.append(seg.text)
|
|
46
|
+
elif isinstance(seg, Raw):
|
|
47
|
+
parts.append(seg.text)
|
|
48
|
+
elif isinstance(seg, DQ):
|
|
49
|
+
inner = _literal_text_from_segments(seg.segments)
|
|
50
|
+
if inner is None:
|
|
51
|
+
return None
|
|
52
|
+
parts.append(inner)
|
|
53
|
+
else:
|
|
54
|
+
return None
|
|
55
|
+
return "".join(parts)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _literal_text(word: Word) -> str | None:
|
|
59
|
+
return _literal_text_from_segments(word.segments)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _check_word_banned_patterns(word: Word) -> None:
|
|
63
|
+
for seg in word.segments:
|
|
64
|
+
if isinstance(seg, Raw):
|
|
65
|
+
if _EXTGLOB_RE.search(seg.text):
|
|
66
|
+
raise UnsupportedConstruct(
|
|
67
|
+
"extended-glob", "Extended glob patterns ('@(...)','!(...)', etc.) are not supported."
|
|
68
|
+
)
|
|
69
|
+
if _BRACE_EXPANSION_RE.search(seg.text):
|
|
70
|
+
raise UnsupportedConstruct(
|
|
71
|
+
"brace-expansion", "Brace expansion ('{a,b,c}') is not supported; list the values explicitly."
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _check_command_word_banned(text: str) -> None:
|
|
76
|
+
if text in _JOB_CONTROL_WORDS:
|
|
77
|
+
raise UnsupportedConstruct("job-control", f"Job control ('{text}') is not supported.")
|
|
78
|
+
if text in _CONTROL_FLOW_WORDS:
|
|
79
|
+
raise UnsupportedConstruct(
|
|
80
|
+
"control-flow", f"Compound control-flow commands ('{text}') are not supported."
|
|
81
|
+
)
|
|
82
|
+
if text in _ARITHMETIC_WORDS:
|
|
83
|
+
raise UnsupportedConstruct("arithmetic-expansion", f"Arithmetic command '{text}' is not supported.")
|
|
84
|
+
if text == "function":
|
|
85
|
+
raise UnsupportedConstruct("function-definition", "Function definitions are not supported.")
|
|
86
|
+
if text == "exec":
|
|
87
|
+
raise UnsupportedConstruct("exec-builtin", "The 'exec' builtin is not supported.")
|
|
88
|
+
if text in _UNSUPPORTED_BUILTIN_WORDS:
|
|
89
|
+
raise UnsupportedConstruct("unsupported-builtin", f"The '{text}' builtin is not supported.")
|
|
90
|
+
if text in _NESTED_SHELL_WORDS:
|
|
91
|
+
raise UnsupportedConstruct(
|
|
92
|
+
"nested-shell", f"Nesting another shell ('{text}') is not supported; run the command directly."
|
|
93
|
+
)
|
|
94
|
+
if text.endswith(".sh") or text.startswith("/bin/"):
|
|
95
|
+
raise UnsupportedConstruct("posix-script", f"Invoking a script/path directly ('{text}') is not supported.")
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
_REDIRECT_OP_RE = re.compile(r"^(\d*)(.*)$", re.DOTALL)
|
|
99
|
+
_DUP_SUFFIX_RE = re.compile(r"&(\d+)$")
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _is_redirect_op_text(text: str) -> bool:
|
|
103
|
+
return "<" in text or ">" in text
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class _Parser:
|
|
107
|
+
def __init__(self, toks: list[Token]) -> None:
|
|
108
|
+
self.toks = toks
|
|
109
|
+
self.i = 0
|
|
110
|
+
|
|
111
|
+
def peek(self) -> Token | None:
|
|
112
|
+
return self.toks[self.i] if self.i < len(self.toks) else None
|
|
113
|
+
|
|
114
|
+
def peek_at(self, offset: int) -> Token | None:
|
|
115
|
+
j = self.i + offset
|
|
116
|
+
return self.toks[j] if j < len(self.toks) else None
|
|
117
|
+
|
|
118
|
+
def advance(self) -> Token:
|
|
119
|
+
tok = self.toks[self.i]
|
|
120
|
+
self.i += 1
|
|
121
|
+
return tok
|
|
122
|
+
|
|
123
|
+
def at_op(self, text: str) -> bool:
|
|
124
|
+
tok = self.peek()
|
|
125
|
+
return tok is not None and tok.kind == "OP" and tok.text == text
|
|
126
|
+
|
|
127
|
+
def _skip_seps(self) -> None:
|
|
128
|
+
while self.at_op(";") or self.at_op("\n"):
|
|
129
|
+
self.advance()
|
|
130
|
+
|
|
131
|
+
def parse_command_list(self, stop_texts: frozenset[str]) -> CommandList:
|
|
132
|
+
entries = []
|
|
133
|
+
separators: list[str] = []
|
|
134
|
+
self._skip_seps()
|
|
135
|
+
while True:
|
|
136
|
+
tok = self.peek()
|
|
137
|
+
if tok is None or (tok.kind == "OP" and tok.text in stop_texts):
|
|
138
|
+
break
|
|
139
|
+
entries.append(self.parse_and_or())
|
|
140
|
+
if self.at_op("&"):
|
|
141
|
+
raise UnsupportedConstruct(
|
|
142
|
+
"job-control", "Background execution ('&') is not supported; run commands synchronously."
|
|
143
|
+
)
|
|
144
|
+
tok = self.peek()
|
|
145
|
+
if tok is None or (tok.kind == "OP" and tok.text in stop_texts):
|
|
146
|
+
break
|
|
147
|
+
if tok.kind == "OP" and tok.text in (";", "\n"):
|
|
148
|
+
separators.append(self.advance().text) # type: ignore[arg-type]
|
|
149
|
+
self._skip_seps()
|
|
150
|
+
continue
|
|
151
|
+
raise UnsupportedConstruct(
|
|
152
|
+
"malformed-syntax", f"Unexpected token in command list: {tok!r}."
|
|
153
|
+
)
|
|
154
|
+
return CommandList(entries=entries, separators=separators)
|
|
155
|
+
|
|
156
|
+
def parse_and_or(self) -> AndOr:
|
|
157
|
+
pipelines = [self.parse_pipeline()]
|
|
158
|
+
operators: list[str] = []
|
|
159
|
+
while self.at_op("&&") or self.at_op("||"):
|
|
160
|
+
operators.append(self.advance().text) # type: ignore[arg-type]
|
|
161
|
+
while self.at_op("\n"):
|
|
162
|
+
self.advance()
|
|
163
|
+
pipelines.append(self.parse_pipeline())
|
|
164
|
+
return AndOr(pipelines=pipelines, operators=operators)
|
|
165
|
+
|
|
166
|
+
def parse_pipeline(self) -> Pipeline:
|
|
167
|
+
negated = False
|
|
168
|
+
tok = self.peek()
|
|
169
|
+
if tok is not None and tok.kind == "WORD" and _literal_text_from_segments(tok.segments or []) == "!":
|
|
170
|
+
negated = True
|
|
171
|
+
self.advance()
|
|
172
|
+
elements: list[PipelineElement] = [self.parse_pipeline_element()]
|
|
173
|
+
while self.at_op("|"):
|
|
174
|
+
self.advance()
|
|
175
|
+
while self.at_op("\n"):
|
|
176
|
+
self.advance()
|
|
177
|
+
elements.append(self.parse_pipeline_element())
|
|
178
|
+
return Pipeline(elements=elements, negated=negated)
|
|
179
|
+
|
|
180
|
+
def parse_pipeline_element(self) -> PipelineElement:
|
|
181
|
+
if self.at_op("("):
|
|
182
|
+
self.advance()
|
|
183
|
+
body = self.parse_command_list(frozenset({")"}))
|
|
184
|
+
if not self.at_op(")"):
|
|
185
|
+
raise UnsupportedConstruct("malformed-syntax", "Unterminated subshell: missing closing ')'.")
|
|
186
|
+
self.advance()
|
|
187
|
+
redirects = self._parse_redirects()
|
|
188
|
+
return Subshell(body=body, redirects=redirects)
|
|
189
|
+
if self.at_op("{"):
|
|
190
|
+
self.advance()
|
|
191
|
+
body = self.parse_command_list(frozenset({"}"}))
|
|
192
|
+
if not self.at_op("}"):
|
|
193
|
+
raise UnsupportedConstruct("malformed-syntax", "Unterminated brace group: missing closing '}'.")
|
|
194
|
+
self.advance()
|
|
195
|
+
redirects = self._parse_redirects()
|
|
196
|
+
return BraceGroup(body=body, redirects=redirects)
|
|
197
|
+
command = self.parse_simple_command()
|
|
198
|
+
if not command.assignments and not command.words and not command.redirects:
|
|
199
|
+
# A fully empty SimpleCommand reached as a pipeline element means a missing
|
|
200
|
+
# command word (e.g. "| foo", "a && ", "a | | b"). Assignment-only (`FOO=1`) and
|
|
201
|
+
# redirect-only (`> file`) commands are valid and never reach here empty.
|
|
202
|
+
raise UnsupportedConstruct(
|
|
203
|
+
"malformed-syntax", "Missing command: a pipeline/list element has no command word."
|
|
204
|
+
)
|
|
205
|
+
return command
|
|
206
|
+
|
|
207
|
+
def _parse_redirects(self) -> list[Redirect]:
|
|
208
|
+
redirects: list[Redirect] = []
|
|
209
|
+
while True:
|
|
210
|
+
tok = self.peek()
|
|
211
|
+
if tok is not None and tok.kind == "OP" and tok.text is not None and _is_redirect_op_text(tok.text):
|
|
212
|
+
redirects.append(self._parse_one_redirect())
|
|
213
|
+
continue
|
|
214
|
+
break
|
|
215
|
+
return redirects
|
|
216
|
+
|
|
217
|
+
def _parse_one_redirect(self) -> Redirect:
|
|
218
|
+
tok = self.advance()
|
|
219
|
+
text = tok.text or ""
|
|
220
|
+
m = _REDIRECT_OP_RE.match(text)
|
|
221
|
+
assert m is not None
|
|
222
|
+
fd_text, _core = m.group(1), m.group(2)
|
|
223
|
+
fd = int(fd_text) if fd_text else None
|
|
224
|
+
dup_match = _DUP_SUFFIX_RE.search(text)
|
|
225
|
+
dup_fd = int(dup_match.group(1)) if dup_match else None
|
|
226
|
+
target: Word | None = None
|
|
227
|
+
if dup_fd is None:
|
|
228
|
+
target_tok = self.peek()
|
|
229
|
+
if target_tok is None or target_tok.kind != "WORD":
|
|
230
|
+
raise UnsupportedConstruct("malformed-syntax", f"Redirect '{text}' is missing its target.")
|
|
231
|
+
self.advance()
|
|
232
|
+
target = Word(segments=target_tok.segments or [])
|
|
233
|
+
return Redirect(op=text, fd=fd, target=target, dup_fd=dup_fd)
|
|
234
|
+
|
|
235
|
+
def parse_simple_command(self) -> SimpleCommand:
|
|
236
|
+
assignments: list[tuple[str, Word]] = []
|
|
237
|
+
words: list[Word] = []
|
|
238
|
+
redirects: list[Redirect] = []
|
|
239
|
+
while True:
|
|
240
|
+
tok = self.peek()
|
|
241
|
+
if tok is None:
|
|
242
|
+
break
|
|
243
|
+
if tok.kind == "OP":
|
|
244
|
+
if tok.text is not None and _is_redirect_op_text(tok.text):
|
|
245
|
+
redirects.append(self._parse_one_redirect())
|
|
246
|
+
continue
|
|
247
|
+
break
|
|
248
|
+
word = Word(segments=tok.segments or [])
|
|
249
|
+
if not words:
|
|
250
|
+
lead = word.segments[0] if word.segments else None
|
|
251
|
+
if isinstance(lead, Raw):
|
|
252
|
+
m = _ASSIGNMENT_RE.match(lead.text)
|
|
253
|
+
if m:
|
|
254
|
+
self.advance()
|
|
255
|
+
name, rest = m.group(1), m.group(2)
|
|
256
|
+
value_segments = ([Raw(text=rest)] if rest else []) + list(word.segments[1:])
|
|
257
|
+
assignments.append((name, Word(segments=value_segments)))
|
|
258
|
+
continue
|
|
259
|
+
_check_word_banned_patterns(word)
|
|
260
|
+
if not words:
|
|
261
|
+
nxt1 = self.peek_at(1)
|
|
262
|
+
nxt2 = self.peek_at(2)
|
|
263
|
+
if (
|
|
264
|
+
nxt1 is not None
|
|
265
|
+
and nxt1.kind == "OP"
|
|
266
|
+
and nxt1.text == "("
|
|
267
|
+
and nxt2 is not None
|
|
268
|
+
and nxt2.kind == "OP"
|
|
269
|
+
and nxt2.text == ")"
|
|
270
|
+
):
|
|
271
|
+
raise UnsupportedConstruct("function-definition", "Function definitions are not supported.")
|
|
272
|
+
literal = _literal_text(word)
|
|
273
|
+
if literal is not None:
|
|
274
|
+
_check_command_word_banned(literal)
|
|
275
|
+
words.append(word)
|
|
276
|
+
self.advance()
|
|
277
|
+
return SimpleCommand(assignments=assignments, words=words, redirects=redirects)
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def parse(tokens: list[Token]) -> CommandList:
|
|
281
|
+
parser = _Parser(tokens)
|
|
282
|
+
result = parser.parse_command_list(frozenset())
|
|
283
|
+
if parser.peek() is not None:
|
|
284
|
+
raise UnsupportedConstruct(
|
|
285
|
+
"malformed-syntax", f"Unexpected trailing token: {parser.peek()!r}."
|
|
286
|
+
)
|
|
287
|
+
return result
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"""External-command spawn: PATHEXT-aware resolution, pipe wiring, deadline-safe kill.
|
|
2
|
+
|
|
3
|
+
Owned by WP-C. Pure stdlib. See windows-shell-workpackages-2026-07-19.md §3 (WP-C spec).
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import os
|
|
9
|
+
import signal
|
|
10
|
+
import subprocess
|
|
11
|
+
import time
|
|
12
|
+
from typing import BinaryIO
|
|
13
|
+
|
|
14
|
+
from state import ShellState
|
|
15
|
+
|
|
16
|
+
TIMEOUT_EXIT_CODE = 124
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def resolve_external(name: str, env: dict[str, str], cwd: str | None = None) -> str | None:
|
|
20
|
+
"""Resolve `name` to an absolute path over `env["PATH"]`, honoring PATHEXT on win32.
|
|
21
|
+
|
|
22
|
+
Direct `.exe` (or extension-less on POSIX) targets resolve as-is; `.bat`/`.cmd`
|
|
23
|
+
targets are still resolved to a path (the caller wraps them via `cmd /c`).
|
|
24
|
+
|
|
25
|
+
Thread-safe: pipeline stages may resolve concurrently on threads, so this never
|
|
26
|
+
reads or mutates `os.environ` (which `shutil.which` does internally for PATH).
|
|
27
|
+
Manual resolution only, entirely off the request env passed in.
|
|
28
|
+
"""
|
|
29
|
+
path_value = env.get("PATH", "")
|
|
30
|
+
if os.path.isabs(name) or os.sep in name or (os.altsep and os.altsep in name):
|
|
31
|
+
candidate = name if os.path.isabs(name) else os.path.join(cwd or os.getcwd(), name)
|
|
32
|
+
if os.path.isfile(candidate):
|
|
33
|
+
return os.path.abspath(candidate)
|
|
34
|
+
return None
|
|
35
|
+
|
|
36
|
+
path_dirs = [d for d in path_value.split(os.pathsep) if d]
|
|
37
|
+
|
|
38
|
+
if os.name == "nt":
|
|
39
|
+
pathext = env.get("PATHEXT") or ".COM;.EXE;.BAT;.CMD"
|
|
40
|
+
exts = [e for e in pathext.split(os.pathsep) if e]
|
|
41
|
+
has_ext = any(name.lower().endswith(ext.lower()) for ext in exts)
|
|
42
|
+
candidates = [name] if has_ext else [name + ext for ext in exts] + [name]
|
|
43
|
+
for directory in path_dirs:
|
|
44
|
+
for candidate in candidates:
|
|
45
|
+
full = os.path.join(directory, candidate)
|
|
46
|
+
if os.path.isfile(full):
|
|
47
|
+
return full
|
|
48
|
+
return None
|
|
49
|
+
|
|
50
|
+
for directory in path_dirs:
|
|
51
|
+
base_dir = directory if os.path.isabs(directory) else os.path.join(cwd or os.getcwd(), directory)
|
|
52
|
+
full = os.path.join(base_dir, name)
|
|
53
|
+
if os.path.isfile(full) and os.access(full, os.X_OK):
|
|
54
|
+
return full
|
|
55
|
+
return None
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def build_argv(resolved_path: str, argv: list[str]) -> list[str]:
|
|
59
|
+
"""Wrap `.bat`/`.cmd` targets via `cmd /c`; direct-exec everything else."""
|
|
60
|
+
lower = resolved_path.lower()
|
|
61
|
+
if lower.endswith(".bat") or lower.endswith(".cmd"):
|
|
62
|
+
return ["cmd", "/c", resolved_path, *argv[1:]]
|
|
63
|
+
return [resolved_path, *argv[1:]]
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def spawn_external(
|
|
67
|
+
argv: list[str],
|
|
68
|
+
state: ShellState,
|
|
69
|
+
stdin: BinaryIO | int,
|
|
70
|
+
stdout: BinaryIO | int,
|
|
71
|
+
stderr: BinaryIO | int,
|
|
72
|
+
deadline: float | None,
|
|
73
|
+
) -> "subprocess.Popen[bytes]":
|
|
74
|
+
"""Resolve argv[0] and spawn it (no shell) with the given cwd/env/streams.
|
|
75
|
+
|
|
76
|
+
Raises FileNotFoundError if argv[0] does not resolve.
|
|
77
|
+
"""
|
|
78
|
+
resolved = resolve_external(argv[0], state.env, state.cwd)
|
|
79
|
+
if resolved is None:
|
|
80
|
+
raise FileNotFoundError(argv[0])
|
|
81
|
+
full_argv = build_argv(resolved, argv)
|
|
82
|
+
kwargs: dict = {}
|
|
83
|
+
if os.name != "nt":
|
|
84
|
+
kwargs["start_new_session"] = True
|
|
85
|
+
else:
|
|
86
|
+
kwargs["creationflags"] = getattr(subprocess, "CREATE_NEW_PROCESS_GROUP", 0)
|
|
87
|
+
return subprocess.Popen(
|
|
88
|
+
full_argv,
|
|
89
|
+
cwd=state.cwd,
|
|
90
|
+
env=dict(state.env),
|
|
91
|
+
stdin=stdin,
|
|
92
|
+
stdout=stdout,
|
|
93
|
+
stderr=stderr,
|
|
94
|
+
**kwargs,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def kill_process_tree(proc: "subprocess.Popen[bytes]") -> None:
|
|
99
|
+
"""Best-effort kill of `proc` and its children."""
|
|
100
|
+
if os.name == "nt":
|
|
101
|
+
subprocess.run(
|
|
102
|
+
["taskkill", "/F", "/T", "/PID", str(proc.pid)],
|
|
103
|
+
stdout=subprocess.DEVNULL,
|
|
104
|
+
stderr=subprocess.DEVNULL,
|
|
105
|
+
check=False,
|
|
106
|
+
)
|
|
107
|
+
else:
|
|
108
|
+
try:
|
|
109
|
+
pgid = os.getpgid(proc.pid)
|
|
110
|
+
os.killpg(pgid, signal.SIGKILL)
|
|
111
|
+
except (ProcessLookupError, PermissionError, OSError):
|
|
112
|
+
try:
|
|
113
|
+
proc.kill()
|
|
114
|
+
except (ProcessLookupError, OSError):
|
|
115
|
+
pass
|
|
116
|
+
try:
|
|
117
|
+
proc.wait(timeout=5)
|
|
118
|
+
except Exception:
|
|
119
|
+
pass
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def wait_with_deadline(proc: "subprocess.Popen[bytes]", deadline: float | None) -> int:
|
|
123
|
+
"""Wait for `proc`; if `deadline` (a `time.monotonic()` budget) elapses first, kill it.
|
|
124
|
+
|
|
125
|
+
Returns the process exit code, or 124 on deadline breach.
|
|
126
|
+
"""
|
|
127
|
+
if deadline is None:
|
|
128
|
+
return proc.wait()
|
|
129
|
+
remaining = deadline - time.monotonic()
|
|
130
|
+
if remaining <= 0:
|
|
131
|
+
kill_process_tree(proc)
|
|
132
|
+
return TIMEOUT_EXIT_CODE
|
|
133
|
+
try:
|
|
134
|
+
return proc.wait(timeout=remaining)
|
|
135
|
+
except subprocess.TimeoutExpired:
|
|
136
|
+
kill_process_tree(proc)
|
|
137
|
+
return TIMEOUT_EXIT_CODE
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"""Shell state: current working directory + environment, and delta computation.
|
|
2
|
+
|
|
3
|
+
Owned by WP-C. Pure stdlib. See windows-shell-workpackages-2026-07-19.md §3 (WP-C spec).
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import os
|
|
9
|
+
from dataclasses import dataclass, field
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ShellEnvironment(dict[str, str]):
|
|
13
|
+
"""Windows-compatible environment mapping while preserving the first key spelling."""
|
|
14
|
+
|
|
15
|
+
def _existing_key(self, key: object) -> str | None:
|
|
16
|
+
if not isinstance(key, str) or os.name != "nt":
|
|
17
|
+
return key if isinstance(key, str) and dict.__contains__(self, key) else None
|
|
18
|
+
folded = key.casefold()
|
|
19
|
+
return next((candidate for candidate in dict.keys(self) if candidate.casefold() == folded), None)
|
|
20
|
+
|
|
21
|
+
def __contains__(self, key: object) -> bool:
|
|
22
|
+
return self._existing_key(key) is not None
|
|
23
|
+
|
|
24
|
+
def __getitem__(self, key: str) -> str:
|
|
25
|
+
existing = self._existing_key(key)
|
|
26
|
+
if existing is None:
|
|
27
|
+
raise KeyError(key)
|
|
28
|
+
return dict.__getitem__(self, existing)
|
|
29
|
+
|
|
30
|
+
def get(self, key: str, default: str | None = None) -> str | None:
|
|
31
|
+
existing = self._existing_key(key)
|
|
32
|
+
return dict.get(self, existing, default) if existing is not None else default
|
|
33
|
+
|
|
34
|
+
def __setitem__(self, key: str, value: str) -> None:
|
|
35
|
+
existing = self._existing_key(key)
|
|
36
|
+
dict.__setitem__(self, existing or key, value)
|
|
37
|
+
|
|
38
|
+
def __delitem__(self, key: str) -> None:
|
|
39
|
+
existing = self._existing_key(key)
|
|
40
|
+
if existing is None:
|
|
41
|
+
raise KeyError(key)
|
|
42
|
+
dict.__delitem__(self, existing)
|
|
43
|
+
|
|
44
|
+
def pop(self, key: str, default=None):
|
|
45
|
+
existing = self._existing_key(key)
|
|
46
|
+
if existing is None:
|
|
47
|
+
return default
|
|
48
|
+
return dict.pop(self, existing)
|
|
49
|
+
|
|
50
|
+
def setdefault(self, key: str, default: str = "") -> str:
|
|
51
|
+
existing = self._existing_key(key)
|
|
52
|
+
if existing is not None:
|
|
53
|
+
return dict.__getitem__(self, existing)
|
|
54
|
+
dict.__setitem__(self, key, default)
|
|
55
|
+
return default
|
|
56
|
+
|
|
57
|
+
def copy(self) -> "ShellEnvironment":
|
|
58
|
+
return ShellEnvironment(self)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@dataclass
|
|
62
|
+
class ShellState:
|
|
63
|
+
"""Mutable cwd + env carried through a single engine run."""
|
|
64
|
+
|
|
65
|
+
cwd: str
|
|
66
|
+
env: ShellEnvironment = field(default_factory=ShellEnvironment)
|
|
67
|
+
|
|
68
|
+
def __post_init__(self) -> None:
|
|
69
|
+
if not isinstance(self.env, ShellEnvironment):
|
|
70
|
+
self.env = ShellEnvironment(self.env)
|
|
71
|
+
|
|
72
|
+
def copy(self) -> "ShellState":
|
|
73
|
+
"""Return an independent copy (used for subshell isolation)."""
|
|
74
|
+
return ShellState(cwd=self.cwd, env=self.env.copy())
|
|
75
|
+
|
|
76
|
+
def chdir(self, path: str) -> None:
|
|
77
|
+
"""Validate `path` exists and is a directory, then update cwd + OLDPWD."""
|
|
78
|
+
target = path if os.path.isabs(path) else os.path.join(self.cwd, path)
|
|
79
|
+
target = os.path.normpath(target)
|
|
80
|
+
if not os.path.isdir(target):
|
|
81
|
+
raise FileNotFoundError(target)
|
|
82
|
+
self.env["OLDPWD"] = self.cwd
|
|
83
|
+
self.cwd = target
|
|
84
|
+
|
|
85
|
+
def setenv(self, name: str, value: str) -> None:
|
|
86
|
+
self.env[name] = value
|
|
87
|
+
|
|
88
|
+
def unsetenv(self, name: str) -> None:
|
|
89
|
+
self.env.pop(name, None)
|
|
90
|
+
|
|
91
|
+
def delta(self, original_env: dict[str, str]) -> dict[str, str | None]:
|
|
92
|
+
"""Compute envDelta vs `original_env`: changed/added -> value, removed -> None."""
|
|
93
|
+
result: dict[str, str | None] = {}
|
|
94
|
+
for key, value in self.env.items():
|
|
95
|
+
if key not in original_env or original_env[key] != value:
|
|
96
|
+
result[key] = value
|
|
97
|
+
for key in original_env:
|
|
98
|
+
if key not in self.env:
|
|
99
|
+
result[key] = None
|
|
100
|
+
return result
|