@wrongstack/core 0.313.0 → 0.316.1
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/dist/chronicle/index.js +84 -6391
- package/dist/chronicle/legacy-journal-import.d.ts +0 -6
- package/dist/chronicle/metrics-ingest.d.ts +14 -0
- package/dist/chronicle/metrics-schema.d.ts +10 -1
- package/dist/chronicle/metrics-store.d.ts +20 -2
- package/dist/chronicle/project-server.js +98 -4874
- package/dist/chronicle/sink.d.ts +13 -0
- package/dist/chronicle/sqlite-journal.d.ts +20 -2
- package/dist/chunk-25LSLC3G.js +334 -0
- package/dist/chunk-27QIURLN.js +1073 -0
- package/dist/chunk-2FK4WTFF.js +207 -0
- package/dist/chunk-2YU4WVBW.js +134 -0
- package/dist/chunk-3AGWVSS7.js +4608 -0
- package/dist/chunk-3BDV7RL3.js +211 -0
- package/dist/chunk-3ECENVHL.js +325 -0
- package/dist/chunk-3XPVB7BA.js +135 -0
- package/dist/chunk-45WI72ZB.js +16884 -0
- package/dist/chunk-4BMVPOHO.js +3748 -0
- package/dist/chunk-4I7HGVVA.js +363 -0
- package/dist/chunk-4LCZVJVF.js +317 -0
- package/dist/chunk-4SJNS7OB.js +217 -0
- package/dist/chunk-4VWO3ZXL.js +342 -0
- package/dist/chunk-57GEWK5X.js +26 -0
- package/dist/chunk-5BMIE7QG.js +46 -0
- package/dist/chunk-5S437PRC.js +50 -0
- package/dist/chunk-5XDUGTKN.js +511 -0
- package/dist/chunk-5YPSXQXM.js +1443 -0
- package/dist/chunk-6FHWXLPT.js +10 -0
- package/dist/chunk-6H5GEOET.js +2406 -0
- package/dist/chunk-6NWEVFEL.js +151 -0
- package/dist/chunk-AYW5YINX.js +58 -0
- package/dist/chunk-B4AMLZPB.js +9 -0
- package/dist/chunk-B7F4WVP4.js +65 -0
- package/dist/chunk-BJHVULHO.js +55 -0
- package/dist/chunk-BPBKKBUK.js +2085 -0
- package/dist/chunk-BYD345OX.js +589 -0
- package/dist/chunk-C27KDLOX.js +279 -0
- package/dist/chunk-CU546ESW.js +597 -0
- package/dist/chunk-DLHHXUAM.js +172 -0
- package/dist/chunk-DOTHQSJ2.js +429 -0
- package/dist/chunk-DYLPMV4Q.js +711 -0
- package/dist/chunk-EODDVJBM.js +82 -0
- package/dist/chunk-F3JWNST7.js +361 -0
- package/dist/chunk-FC2ZGRF3.js +140 -0
- package/dist/chunk-FCBTO76Z.js +5786 -0
- package/dist/chunk-FHZVFYSL.js +396 -0
- package/dist/chunk-FNXVT7HF.js +52 -0
- package/dist/chunk-FWNLEZYG.js +259 -0
- package/dist/chunk-FXQLIX3K.js +211 -0
- package/dist/chunk-G5S3S5ZN.js +38 -0
- package/dist/chunk-GDQ7CZRQ.js +116 -0
- package/dist/chunk-GJJGJB2R.js +1374 -0
- package/dist/chunk-GTCU6DZ3.js +1 -0
- package/dist/chunk-HA4ZYA6T.js +178 -0
- package/dist/chunk-HI3JGZR2.js +188 -0
- package/dist/chunk-HMLIGAU7.js +395 -0
- package/dist/chunk-HXKZ7OBQ.js +303 -0
- package/dist/chunk-I7DALG7J.js +292 -0
- package/dist/chunk-IRUTEHCE.js +960 -0
- package/dist/chunk-IYFBFX34.js +86 -0
- package/dist/chunk-J2WRWR37.js +1104 -0
- package/dist/chunk-JFPJ6LLQ.js +129 -0
- package/dist/chunk-JMULFFLN.js +9 -0
- package/dist/chunk-JO4MGB6Z.js +1264 -0
- package/dist/chunk-JVKB2TBL.js +48 -0
- package/dist/chunk-JX7VBZDK.js +31 -0
- package/dist/chunk-K6BXGITA.js +931 -0
- package/dist/chunk-KDQJCT2J.js +45 -0
- package/dist/chunk-KJ2ZTQSL.js +47 -0
- package/dist/chunk-KZMSZV32.js +187 -0
- package/dist/chunk-LA7CLDRW.js +129 -0
- package/dist/chunk-LHOA5DWA.js +28 -0
- package/dist/chunk-LSMMOS2Z.js +119 -0
- package/dist/chunk-LX5QWLBL.js +27 -0
- package/dist/chunk-LXWCMQNJ.js +789 -0
- package/dist/chunk-M27LIEKV.js +2829 -0
- package/dist/chunk-MJPHJRAL.js +663 -0
- package/dist/chunk-MTL4O5YR.js +228 -0
- package/dist/chunk-NDBIPFNY.js +102 -0
- package/dist/chunk-NEMIWEXC.js +1199 -0
- package/dist/chunk-NHJ75HXY.js +1 -0
- package/dist/chunk-NIOAGFPB.js +1510 -0
- package/dist/chunk-NS52ISWS.js +355 -0
- package/dist/chunk-NSOFPKGT.js +5352 -0
- package/dist/chunk-NVIZG63R.js +757 -0
- package/dist/chunk-O37BQ65S.js +21 -0
- package/dist/chunk-OOYZPOXM.js +308 -0
- package/dist/chunk-OSGLYW5J.js +28 -0
- package/dist/chunk-PFCMSSUF.js +1640 -0
- package/dist/chunk-PFZO3C6H.js +14 -0
- package/dist/chunk-PGRVXIMY.js +158 -0
- package/dist/chunk-PHH7LD45.js +113 -0
- package/dist/chunk-PISBFBJN.js +109 -0
- package/dist/chunk-POOOIWE5.js +9 -0
- package/dist/chunk-PQNS3U7Z.js +1240 -0
- package/dist/chunk-QSTSJOCK.js +402 -0
- package/dist/chunk-RFQMOKWK.js +282 -0
- package/dist/chunk-RJIZEJ42.js +30 -0
- package/dist/chunk-RTOTNK3P.js +484 -0
- package/dist/chunk-RXKSDUAU.js +1172 -0
- package/dist/chunk-S45JPKQN.js +3317 -0
- package/dist/chunk-SAFHACFA.js +152 -0
- package/dist/chunk-SQJVFEBV.js +41 -0
- package/dist/chunk-SVT4RNTR.js +49 -0
- package/dist/chunk-SWUFN7XM.js +43 -0
- package/dist/chunk-SXMVYC5H.js +174 -0
- package/dist/chunk-SZ6AYUIZ.js +23 -0
- package/dist/chunk-TBRL2RCC.js +9 -0
- package/dist/chunk-TCCHINEF.js +655 -0
- package/dist/chunk-THEGTRAG.js +316 -0
- package/dist/chunk-TR4LQE7L.js +2256 -0
- package/dist/chunk-TRT2HRWZ.js +18 -0
- package/dist/chunk-TWJMGXCI.js +900 -0
- package/dist/chunk-U45AXT5D.js +1023 -0
- package/dist/chunk-U4FWBSO3.js +509 -0
- package/dist/chunk-UBGDX6FI.js +147 -0
- package/dist/chunk-UJILKYES.js +75 -0
- package/dist/chunk-ULMAQV3T.js +234 -0
- package/dist/chunk-VVBP6KGT.js +9 -0
- package/dist/chunk-W5VITDM2.js +168 -0
- package/dist/chunk-WAHQVNWT.js +194 -0
- package/dist/chunk-WBPVKEEE.js +227 -0
- package/dist/chunk-WGGYRBXH.js +5434 -0
- package/dist/chunk-WLYKU45P.js +5770 -0
- package/dist/chunk-WP7BVANJ.js +53 -0
- package/dist/chunk-WQQON3SV.js +473 -0
- package/dist/chunk-WQTOVMGV.js +119 -0
- package/dist/chunk-XBDP4QPW.js +7 -0
- package/dist/chunk-XEBHSJLT.js +103 -0
- package/dist/chunk-XF4VUMKW.js +231 -0
- package/dist/chunk-XQ2ECNK3.js +1 -0
- package/dist/chunk-Y5YXDM7D.js +74 -0
- package/dist/chunk-YOPAUVU4.js +2588 -0
- package/dist/chunk-YPT4A46F.js +362 -0
- package/dist/chunk-YWXT3JNM.js +425 -0
- package/dist/chunk-YY6APYUM.js +49 -0
- package/dist/chunk-Z2IJUO45.js +691 -0
- package/dist/chunk-ZHZ2Y2DH.js +1643 -0
- package/dist/chunk-ZT7JNIGX.js +152 -0
- package/dist/coordination/agent-monitor.d.ts +9 -1
- package/dist/coordination/agent-subagent-runner.d.ts +1 -1
- package/dist/coordination/agents/agent-prompts.d.ts +8 -0
- package/dist/coordination/agents/index.d.ts +33 -1
- package/dist/coordination/agents/index.js +184 -6269
- package/dist/coordination/agents/phase3-wave1-platform.d.ts +1 -3
- package/dist/coordination/agents/phase3-wave2-meta.d.ts +1 -3
- package/dist/coordination/agents/phase8-wave3-products.d.ts +1 -3
- package/dist/coordination/agents/phase9-wave4-platform-meta.d.ts +1 -3
- package/dist/coordination/agents/project-agent-identity.d.ts +11 -11
- package/dist/coordination/agents/types.d.ts +13 -2
- package/dist/coordination/brain-monitor.d.ts +30 -7
- package/dist/coordination/collab-debug.d.ts +1 -1
- package/dist/coordination/collab-pause.d.ts +1 -1
- package/dist/coordination/delegate-tool.d.ts +7 -5
- package/dist/coordination/director-kanban-queue-helpers.d.ts +1 -0
- package/dist/coordination/director-options.d.ts +7 -0
- package/dist/coordination/director-spawn-model.d.ts +17 -0
- package/dist/coordination/director-tools.d.ts +2 -2
- package/dist/coordination/director.d.ts +12 -0
- package/dist/coordination/dispatcher.d.ts +8 -0
- package/dist/coordination/fleet-bus.d.ts +35 -4
- package/dist/coordination/fleet-supervisor.d.ts +10 -2
- package/dist/coordination/fleet.d.ts +16 -7
- package/dist/coordination/index.d.ts +4 -0
- package/dist/coordination/index.js +402 -34324
- package/dist/coordination/kanban-dispatch-port.d.ts +2 -2
- package/dist/coordination/mailbox-http-router.d.ts +4 -3
- package/dist/coordination/mailbox-http-validation.d.ts +1 -1
- package/dist/coordination/mailbox-project-server.js +48 -3276
- package/dist/coordination/mailbox-types.d.ts +24 -6
- package/dist/coordination/model-tier-leader.d.ts +122 -0
- package/dist/coordination/model-tier.d.ts +122 -0
- package/dist/coordination/multi-agent-coordinator.d.ts +24 -0
- package/dist/coordination/origin-session.d.ts +16 -0
- package/dist/coordination/provider-status-tracker.d.ts +41 -5
- package/dist/coordination/session-note-hub.d.ts +44 -0
- package/dist/coordination/session-note-tool.d.ts +10 -0
- package/dist/core/agent.d.ts +15 -2
- package/dist/core/context.d.ts +22 -3
- package/dist/core/index.d.ts +2 -0
- package/dist/core/index.js +239 -12360
- package/dist/core/model-ref.js +5 -23
- package/dist/core/provider-runner.d.ts +36 -1
- package/dist/core/request-conversation-binding.d.ts +37 -0
- package/dist/core/session-notes.d.ts +25 -0
- package/dist/core/system-prompt-builder.d.ts +11 -2
- package/dist/core/system-prompt-variants.d.ts +78 -0
- package/dist/design/index.js +50 -1087
- package/dist/execution/auto-compaction-middleware.d.ts +11 -0
- package/dist/execution/autonomy-brain.d.ts +2 -1
- package/dist/execution/autonomy-prompt-contributor.d.ts +7 -2
- package/dist/execution/compaction-core.d.ts +4 -4
- package/dist/execution/eternal-autonomy.d.ts +1 -1
- package/dist/execution/index.d.ts +1 -1
- package/dist/execution/index.js +1615 -21895
- package/dist/execution/model-runtime.d.ts +0 -7
- package/dist/execution/parallel-eternal-engine.d.ts +7 -7
- package/dist/execution/prompt-enhancer.js +16 -535
- package/dist/execution/retry-policy.d.ts +31 -1
- package/dist/extension/index.js +7 -311
- package/dist/goal/index.js +33 -3435
- package/dist/hooks/index.js +14 -1138
- package/dist/hq/index.js +114 -5139
- package/dist/hq/persistence/event-log.d.ts +0 -1
- package/dist/hq/persistence.d.ts +4 -4
- package/dist/hq/protocol.js +21 -668
- package/dist/hq/publisher.d.ts +2 -0
- package/dist/index.d.ts +14 -11
- package/dist/index.js +1502 -92655
- package/dist/infrastructure/index.d.ts +4 -4
- package/dist/infrastructure/index.js +504 -5230
- package/dist/infrastructure/token-counter.d.ts +9 -0
- package/dist/kernel/events/agent-events.d.ts +77 -3
- package/dist/kernel/events/brain-events.d.ts +12 -0
- package/dist/kernel/events/provider-events.d.ts +22 -1
- package/dist/kernel/events/session-events.d.ts +14 -0
- package/dist/kernel/events/tool-events.d.ts +13 -0
- package/dist/kernel/index.js +14 -992
- package/dist/mailbox-attach.d.ts +1 -1
- package/dist/models/index.js +41 -1117
- package/dist/notifications/index.js +3 -108
- package/dist/observability/event-bridge.d.ts +25 -5
- package/dist/observability/index.d.ts +9 -8
- package/dist/observability/index.js +477 -631
- package/dist/observability/tool-usage-source.d.ts +65 -0
- package/dist/plugin/index.d.ts +5 -0
- package/dist/plugin/index.js +600 -14835
- package/dist/plugins/auto-review-plugin.d.ts +3 -2
- package/dist/plugins/review-finding-commands.d.ts +0 -8
- package/dist/plugins/review-finding-store.js +22 -0
- package/dist/plugins/review-finding-types.js +19 -0
- package/dist/plugins/review-report-integration.js +21 -0
- package/dist/plugins/review-report-store.js +20 -0
- package/dist/plugins/review-report-types.js +13 -0
- package/dist/prompts/index.js +14 -948
- package/dist/registry/index.js +9 -822
- package/dist/registry/tool-registry.d.ts +59 -9
- package/dist/registry-7N4O3E7W.js +14 -0
- package/dist/replay/index.js +6 -84
- package/dist/security/capabilities.d.ts +6 -0
- package/dist/security/file-permissions.d.ts +1 -1
- package/dist/security/index.js +161 -2235
- package/dist/security/permission-policy.d.ts +12 -1
- package/dist/security/secret-scrubber.d.ts +15 -0
- package/dist/session-catalog/index.d.ts +1 -0
- package/dist/session-catalog/index.js +41 -2103
- package/dist/session-catalog/project-server.js +59 -1435
- package/dist/session-catalog/protocol.d.ts +32 -1
- package/dist/session-catalog/registry.d.ts +17 -1
- package/dist/session-catalog/session-agents.d.ts +56 -0
- package/dist/session-catalog/store-rebuild.d.ts +14 -0
- package/dist/session-catalog/store-schema.d.ts +26 -0
- package/dist/session-catalog/store.d.ts +27 -1
- package/dist/session-note-attach.d.ts +9 -0
- package/dist/skills/index.js +35 -1578
- package/dist/statusline/index.d.ts +43 -0
- package/dist/statusline/index.js +102 -0
- package/dist/storage/board-store-port.d.ts +2 -2
- package/dist/storage/config-loader/in-project-policy.d.ts +29 -0
- package/dist/storage/config-loader.d.ts +3 -2
- package/dist/storage/file-session-writer.d.ts +4 -2
- package/dist/storage/goal-coordination.d.ts +6 -1
- package/dist/storage/goal-kanban.d.ts +4 -1
- package/dist/storage/index.d.ts +12 -4
- package/dist/storage/index.js +198 -15601
- package/dist/storage/input-history-store.d.ts +2 -4
- package/dist/storage/memory-curator.d.ts +133 -0
- package/dist/storage/session-agent-attribution.d.ts +39 -0
- package/dist/storage/session-doctor.d.ts +109 -0
- package/dist/storage/session-event-bridge.d.ts +5 -7
- package/dist/storage/session-outcome.d.ts +42 -0
- package/dist/storage/session-recovery.d.ts +55 -0
- package/dist/storage/session-store/load-session-data.d.ts +11 -0
- package/dist/storage/session-store/prune-helpers.d.ts +1 -1
- package/dist/storage/session-store/resume-session.d.ts +0 -2
- package/dist/storage/session-store/types.d.ts +8 -0
- package/dist/storage/session-store.d.ts +3 -3
- package/dist/storage/session-summary-tracker.d.ts +8 -1
- package/dist/storage/session-write-buffer.d.ts +18 -0
- package/dist/storage/session-writer-truncate.d.ts +2 -0
- package/dist/tasking/index.js +10 -676
- package/dist/tools/fallback-manage-tools.d.ts +6 -4
- package/dist/tools/index.js +52 -9590
- package/dist/tools/model-tier-set-tool.d.ts +66 -0
- package/dist/types/agent-bridge.d.ts +2 -1
- package/dist/types/config/autonomy.d.ts +5 -0
- package/dist/types/config/mcp-features.d.ts +7 -0
- package/dist/types/config/model-tiers.d.ts +148 -0
- package/dist/types/config/root.d.ts +9 -0
- package/dist/types/config/tools.d.ts +73 -0
- package/dist/types/config/ui.d.ts +1 -1
- package/dist/types/config.d.ts +1 -0
- package/dist/types/context.d.ts +2 -0
- package/dist/types/default-config.d.ts +6 -0
- package/dist/types/errors.d.ts +20 -1
- package/dist/types/index.d.ts +11 -9
- package/dist/types/index.js +131 -1342
- package/dist/types/multi-agent.d.ts +43 -0
- package/dist/types/plugin.d.ts +1 -1
- package/dist/types/provider-runner.d.ts +8 -0
- package/dist/types/provider.d.ts +15 -0
- package/dist/types/runtime-capability-manifest.d.ts +1 -1
- package/dist/types/secret-scrubber.d.ts +15 -0
- package/dist/types/session-markers.d.ts +37 -10
- package/dist/types/session-markers.js +8 -144
- package/dist/types/session-timeline.d.ts +197 -0
- package/dist/types/session-timeline.js +12 -0
- package/dist/types/session.d.ts +172 -11
- package/dist/types/system-prompt.d.ts +27 -0
- package/dist/utils/config-backup.d.ts +9 -1
- package/dist/utils/error.js +3 -4
- package/dist/utils/expect-defined.js +3 -9
- package/dist/utils/heap-watchdog.js +6 -706
- package/dist/utils/index.js +271 -5685
- package/dist/utils/message-invariants.d.ts +4 -1
- package/dist/utils/regex-guard.d.ts +1 -1
- package/dist/utils/sage-output-block.js +5 -42
- package/dist/utils/socket-path.d.ts +1 -1
- package/dist/utils/token-estimate.d.ts +6 -2
- package/dist/utils/wstack-paths.d.ts +2 -0
- package/dist/wiring/proxy-rewrite.d.ts +46 -0
- package/dist/wiring/proxy-rewrite.js +24 -121
- package/dist/worktree/index.js +6 -887
- package/instructions/agents/explore-companion.md +45 -18
- package/instructions/agents/memory-curator.md +34 -22
- package/instructions/coordination/director-preamble.md +12 -7
- package/instructions/coordination/subagent-baseline.md +26 -5
- package/instructions/leader-after-task.md +4 -1
- package/instructions/llm/chimera-review.md +30 -9
- package/instructions/llm/memory-curator.md +31 -0
- package/instructions/sections/tool/delegation-compact.md +2 -0
- package/instructions/sections/tool/delegation-full.md +5 -1
- package/instructions/sections/tool/mailbox-compact.md +5 -1
- package/instructions/sections/tool/mailbox-full.md +3 -0
- package/instructions/sections/tool/session-note-compact.md +3 -0
- package/instructions/sections/tool/session-note-full.md +15 -0
- package/instructions/system-lite.md +9 -4
- package/instructions/system-pro.md +66 -143
- package/instructions/system.md +73 -175
- package/package.json +32 -4
- package/dist/coordination/agents.d.ts +0 -8
- package/dist/coordination/transport.d.ts +0 -2
- package/dist/storage/session-writer/session-writer-summary-tracker.d.ts +0 -28
package/dist/worktree/index.js
CHANGED
|
@@ -1,890 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
return err instanceof Error ? err.message : String(err);
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
// src/worktree/worktree-git.ts
|
|
11
|
-
import { spawn } from "node:child_process";
|
|
12
|
-
import { resolve, sep } from "node:path";
|
|
13
|
-
|
|
14
|
-
// src/utils/child-env.ts
|
|
15
|
-
var ALLOWED_KEYS = /* @__PURE__ */ new Set([
|
|
16
|
-
"PATH",
|
|
17
|
-
"HOME",
|
|
18
|
-
"USER",
|
|
19
|
-
"USERNAME",
|
|
20
|
-
"LOGNAME",
|
|
21
|
-
"SHELL",
|
|
22
|
-
"LANG",
|
|
23
|
-
"LC_ALL",
|
|
24
|
-
"LC_CTYPE",
|
|
25
|
-
"TERM",
|
|
26
|
-
"TZ",
|
|
27
|
-
"TMPDIR",
|
|
28
|
-
"TEMP",
|
|
29
|
-
"TMP",
|
|
30
|
-
"PWD",
|
|
31
|
-
"OLDPWD",
|
|
32
|
-
"COMSPEC",
|
|
33
|
-
"SYSTEMROOT",
|
|
34
|
-
"SYSTEMDRIVE",
|
|
35
|
-
"WINDIR",
|
|
36
|
-
"PROGRAMFILES",
|
|
37
|
-
"PROGRAMFILES(X86)",
|
|
38
|
-
"PROGRAMDATA",
|
|
39
|
-
"APPDATA",
|
|
40
|
-
"LOCALAPPDATA",
|
|
41
|
-
"USERPROFILE",
|
|
42
|
-
"PUBLIC",
|
|
43
|
-
"PATHEXT"
|
|
44
|
-
]);
|
|
45
|
-
var SECRET_NAME_PARTS = [
|
|
46
|
-
"TOKEN",
|
|
47
|
-
"SECRET",
|
|
48
|
-
"PASSWORD",
|
|
49
|
-
"PASSWD",
|
|
50
|
-
// WRONGSTACK_VAULT_PASSPHRASE is the vault KEK. Without this entry it matched
|
|
51
|
-
// no rule here and fell through to the `WRONGSTACK_` prefix allow-rule, so the
|
|
52
|
-
// key that protects the credential vault was forwarded to every child process
|
|
53
|
-
// — including the agent's own exec/bash tools and MCP stdio servers (WS-033).
|
|
54
|
-
"PASSPHRASE",
|
|
55
|
-
"AUTH",
|
|
56
|
-
"CRED",
|
|
57
|
-
"BEARER",
|
|
58
|
-
"COOKIE",
|
|
59
|
-
"PRIVATE"
|
|
60
|
-
];
|
|
61
|
-
function looksSecret(name) {
|
|
62
|
-
const upper = name.toUpperCase();
|
|
63
|
-
for (const p of SECRET_NAME_PARTS) {
|
|
64
|
-
if (upper.includes(p)) return true;
|
|
65
|
-
}
|
|
66
|
-
if (/(?:^|_)KEY(?:$|_|S$)/i.test(upper)) return true;
|
|
67
|
-
if (/API[_-]?KEY/i.test(upper)) return true;
|
|
68
|
-
if (/ACCESS[_-]?KEY/i.test(upper)) return true;
|
|
69
|
-
if (/SESSION[_-]?ID/i.test(upper) === false && /SESSION/i.test(upper)) {
|
|
70
|
-
return true;
|
|
71
|
-
}
|
|
72
|
-
return false;
|
|
73
|
-
}
|
|
74
|
-
function valueHasEmbeddedCredential(value) {
|
|
75
|
-
return /\b[a-z][a-z0-9+.-]*:\/\/[^/\s:@]*:[^/\s@]+@/i.test(value);
|
|
76
|
-
}
|
|
77
|
-
var NODE_OPTIONS_INJECTION_FLAG = /^(?:--require|-r|--import|--loader|--experimental-loader)$/;
|
|
78
|
-
var NODE_OPTIONS_INJECTION_FLAG_EQ = /^(?:--require|-r|--import|--loader|--experimental-loader)=/;
|
|
79
|
-
function sanitizeNodeOptions(value) {
|
|
80
|
-
const tokens = value.split(/\s+/).filter(Boolean);
|
|
81
|
-
const kept = [];
|
|
82
|
-
for (let i = 0; i < tokens.length; i++) {
|
|
83
|
-
const tok = tokens[i];
|
|
84
|
-
if (NODE_OPTIONS_INJECTION_FLAG_EQ.test(tok)) continue;
|
|
85
|
-
if (NODE_OPTIONS_INJECTION_FLAG.test(tok)) {
|
|
86
|
-
i++;
|
|
87
|
-
continue;
|
|
88
|
-
}
|
|
89
|
-
kept.push(tok);
|
|
90
|
-
}
|
|
91
|
-
return kept.join(" ");
|
|
92
|
-
}
|
|
93
|
-
var gitIdentity = null;
|
|
94
|
-
function buildChildEnv(optsOrSessionId) {
|
|
95
|
-
const opts = typeof optsOrSessionId === "string" ? { sessionId: optsOrSessionId } : optsOrSessionId ?? {};
|
|
96
|
-
const hasOwn = Object.hasOwn(process.env, "WRONGSTACK_CHILD_ENV_PASSTHROUGH");
|
|
97
|
-
const legacyHasOwn = Object.hasOwn(process.env, "WRONGSTACK_BASH_ENV_PASSTHROUGH");
|
|
98
|
-
const passthrough = hasOwn && process.env["WRONGSTACK_CHILD_ENV_PASSTHROUGH"] === "1" || legacyHasOwn && process.env["WRONGSTACK_BASH_ENV_PASSTHROUGH"] === "1";
|
|
99
|
-
if (passthrough && !process.env["CI"]) {
|
|
100
|
-
console.warn(
|
|
101
|
-
"[agent] WARNING: WRONGSTACK_*_ENV_PASSTHROUGH=1 is active \u2014\n all parent env vars (including API keys) forwarded to child processes.\n Do not use on shared or multi-tenant systems."
|
|
102
|
-
);
|
|
103
|
-
}
|
|
104
|
-
const out = {};
|
|
105
|
-
const nodeEnvDefaulted = process.env["WRONGSTACK_NODE_ENV_DEFAULTED"] === "1";
|
|
106
|
-
for (const [k, v] of Object.entries(process.env)) {
|
|
107
|
-
if (v === void 0) continue;
|
|
108
|
-
if (nodeEnvDefaulted && (k === "NODE_ENV" || k === "WRONGSTACK_NODE_ENV_DEFAULTED")) continue;
|
|
109
|
-
if (passthrough) {
|
|
110
|
-
out[k] = v;
|
|
111
|
-
continue;
|
|
112
|
-
}
|
|
113
|
-
const upper = k.toUpperCase();
|
|
114
|
-
if (valueHasEmbeddedCredential(v)) continue;
|
|
115
|
-
if (ALLOWED_KEYS.has(upper)) {
|
|
116
|
-
out[k] = v;
|
|
117
|
-
continue;
|
|
118
|
-
}
|
|
119
|
-
if (looksSecret(upper)) continue;
|
|
120
|
-
if (upper === "NODE_OPTIONS") {
|
|
121
|
-
const sanitized = sanitizeNodeOptions(v);
|
|
122
|
-
if (sanitized) out[k] = sanitized;
|
|
123
|
-
continue;
|
|
124
|
-
}
|
|
125
|
-
if (upper.startsWith("NODE_") || upper.startsWith("NPM_") || upper.startsWith("PNPM_") || upper.startsWith("YARN_") || upper.startsWith("GIT_") || upper.startsWith("CI") || upper.startsWith("XDG_") || // Our own non-secret knobs (WRONGSTACK_HOME, WRONGSTACK_SESSION_ID, …).
|
|
126
|
-
// Secrets never live in WRONGSTACK_* env vars (they're in the encrypted
|
|
127
|
-
// vault). Forwarding keeps child wstack processes — e.g. ones spawned
|
|
128
|
-
// by the test suite — inside the same redirected global root.
|
|
129
|
-
upper.startsWith("WRONGSTACK_") || upper === "EDITOR" || upper === "VISUAL" || upper === "PAGER") {
|
|
130
|
-
out[k] = v;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
if (gitIdentity) {
|
|
134
|
-
if (gitIdentity.name) {
|
|
135
|
-
out["GIT_AUTHOR_NAME"] = gitIdentity.name;
|
|
136
|
-
out["GIT_COMMITTER_NAME"] = gitIdentity.name;
|
|
137
|
-
}
|
|
138
|
-
if (gitIdentity.email) {
|
|
139
|
-
out["GIT_AUTHOR_EMAIL"] = gitIdentity.email;
|
|
140
|
-
out["GIT_COMMITTER_EMAIL"] = gitIdentity.email;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
if (opts.extra) {
|
|
144
|
-
Object.assign(out, opts.extra);
|
|
145
|
-
}
|
|
146
|
-
if (opts.sessionId) out["WRONGSTACK_SESSION_ID"] = opts.sessionId;
|
|
147
|
-
return out;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// src/worktree/worktree-git.ts
|
|
151
|
-
var MAX_SLUG = 40;
|
|
152
|
-
function makeSlug(hint, usedSlugs) {
|
|
153
|
-
let base = hint.toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/-+/g, "-").replace(/^[-.]+/, "").replace(/[-.]+$/, "").slice(0, MAX_SLUG).replace(/[-.]+$/, "");
|
|
154
|
-
if (!base) base = "wt";
|
|
155
|
-
let slug = `${base}-${crypto.randomUUID().slice(0, 6)}`;
|
|
156
|
-
while (usedSlugs.has(slug)) slug = `${base}-${crypto.randomUUID().slice(0, 6)}`;
|
|
157
|
-
usedSlugs.add(slug);
|
|
158
|
-
return slug;
|
|
159
|
-
}
|
|
160
|
-
async function collectStats(runGit, dir) {
|
|
161
|
-
const sha = (await runGit(["rev-parse", "HEAD"], dir)).stdout.trim();
|
|
162
|
-
const numstat = await runGit(["show", "--numstat", "--format=", "HEAD"], dir);
|
|
163
|
-
let insertions = 0;
|
|
164
|
-
let deletions = 0;
|
|
165
|
-
let files = 0;
|
|
166
|
-
for (const line of numstat.stdout.split("\n")) {
|
|
167
|
-
const m = line.trim().match(/^(\d+|-)\t(\d+|-)\t(.+)$/);
|
|
168
|
-
if (!m) continue;
|
|
169
|
-
files++;
|
|
170
|
-
if (m[1] !== "-") insertions += Number(m[1]);
|
|
171
|
-
if (m[2] !== "-") deletions += Number(m[2]);
|
|
172
|
-
}
|
|
173
|
-
return { insertions, deletions, files, sha };
|
|
174
|
-
}
|
|
175
|
-
async function identityArgs(runGit, cwd) {
|
|
176
|
-
const name = (await runGit(["config", "user.name"], cwd)).stdout.trim();
|
|
177
|
-
const email = (await runGit(["config", "user.email"], cwd)).stdout.trim();
|
|
178
|
-
if (name && email) return [];
|
|
179
|
-
return ["-c", `user.name=${name || "Goal"}`, "-c", `user.email=${email || "goal@agent.local"}`];
|
|
180
|
-
}
|
|
181
|
-
function defaultRun(gitBin, args, cwd) {
|
|
182
|
-
return new Promise((res) => {
|
|
183
|
-
let stdout = "";
|
|
184
|
-
let stderr = "";
|
|
185
|
-
const MAX_GIT_OUTPUT = 1e6;
|
|
186
|
-
const child = spawn(gitBin, args, {
|
|
187
|
-
cwd,
|
|
188
|
-
env: buildChildEnv(),
|
|
189
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
190
|
-
signal: AbortSignal.timeout(3e4),
|
|
191
|
-
windowsHide: true
|
|
192
|
-
});
|
|
193
|
-
child.stdout?.on("data", (c) => {
|
|
194
|
-
if (stdout.length < MAX_GIT_OUTPUT) stdout += c.toString();
|
|
195
|
-
});
|
|
196
|
-
child.stderr?.on("data", (c) => {
|
|
197
|
-
if (stderr.length < MAX_GIT_OUTPUT) stderr += c.toString();
|
|
198
|
-
});
|
|
199
|
-
child.on("error", (err) => res({ code: 1, stdout, stderr: err.message }));
|
|
200
|
-
child.on("close", (code) => res({ code: code ?? 1, stdout, stderr }));
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
function parseConflictPaths(output) {
|
|
204
|
-
const paths = /* @__PURE__ */ new Set();
|
|
205
|
-
for (const line of output.split("\n")) {
|
|
206
|
-
const m = line.match(/^CONFLICT \([^)]*\): Merge conflict in (.+?)\s*$/);
|
|
207
|
-
if (m?.[1]) paths.add(m[1]);
|
|
208
|
-
}
|
|
209
|
-
return [...paths];
|
|
210
|
-
}
|
|
211
|
-
function assertSafePath(dir, projectRoot) {
|
|
212
|
-
const root = resolve(projectRoot);
|
|
213
|
-
const abs = resolve(dir);
|
|
214
|
-
if (abs !== root && !abs.startsWith(root + sep)) {
|
|
215
|
-
throw new Error(`worktree path escapes project root: ${dir}`);
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// src/worktree/worktree-managed-ops.ts
|
|
220
|
-
import { resolve as resolve2, sep as sep2 } from "node:path";
|
|
221
|
-
async function cleanupAllManaged(ctx) {
|
|
222
|
-
const root = resolve2(ctx.worktreesRoot);
|
|
223
|
-
let removed = 0;
|
|
224
|
-
try {
|
|
225
|
-
const listed = await ctx.runGit(["worktree", "list", "--porcelain"], ctx.projectRoot);
|
|
226
|
-
for (const line of listed.stdout.split("\n")) {
|
|
227
|
-
const m = line.match(/^worktree\s+(.+?)\s*$/);
|
|
228
|
-
if (!m?.[1]) continue;
|
|
229
|
-
const dir = resolve2(m[1]);
|
|
230
|
-
if (dir !== root && (dir === root || dir.startsWith(root + sep2))) {
|
|
231
|
-
const rm = await ctx.runGit(["worktree", "remove", "--force", dir], ctx.projectRoot);
|
|
232
|
-
if (rm.code === 0) removed++;
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
} catch {
|
|
236
|
-
}
|
|
237
|
-
try {
|
|
238
|
-
const branches = await ctx.runGit(
|
|
239
|
-
["branch", "--list", "--format=%(refname:short)", "wstack/ap/*"],
|
|
240
|
-
ctx.projectRoot
|
|
241
|
-
);
|
|
242
|
-
for (const b of branches.stdout.split("\n").map((s) => s.trim()).filter(Boolean)) {
|
|
243
|
-
await ctx.runGit(["branch", "-D", b], ctx.projectRoot);
|
|
244
|
-
}
|
|
245
|
-
} catch {
|
|
246
|
-
}
|
|
247
|
-
await ctx.runGit(["worktree", "prune"], ctx.projectRoot).catch(() => void 0);
|
|
248
|
-
ctx.handles.clear();
|
|
249
|
-
ctx.usedSlugs.clear();
|
|
250
|
-
ctx.emit("worktree.released", {
|
|
251
|
-
handleId: "cleanup-all",
|
|
252
|
-
ownerId: "cleanup-all",
|
|
253
|
-
branch: "wstack/ap/*",
|
|
254
|
-
kept: false
|
|
255
|
-
});
|
|
256
|
-
return { removed };
|
|
257
|
-
}
|
|
258
|
-
async function listManaged(ctx) {
|
|
259
|
-
const root = resolve2(ctx.worktreesRoot);
|
|
260
|
-
const worktrees = [];
|
|
261
|
-
try {
|
|
262
|
-
const listed = await ctx.runGit(["worktree", "list", "--porcelain"], ctx.projectRoot);
|
|
263
|
-
let curDir = null;
|
|
264
|
-
let curBranch;
|
|
265
|
-
const flush = () => {
|
|
266
|
-
if (curDir) {
|
|
267
|
-
const d = resolve2(curDir);
|
|
268
|
-
if (d !== root && d.startsWith(root + sep2)) {
|
|
269
|
-
worktrees.push({ dir: curDir, branch: curBranch });
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
curDir = null;
|
|
273
|
-
curBranch = void 0;
|
|
274
|
-
};
|
|
275
|
-
for (const line of listed.stdout.split("\n")) {
|
|
276
|
-
if (line.startsWith("worktree ")) {
|
|
277
|
-
flush();
|
|
278
|
-
curDir = line.slice("worktree ".length).trim();
|
|
279
|
-
} else if (line.startsWith("branch ")) {
|
|
280
|
-
curBranch = line.slice("branch ".length).trim().replace(/^refs\/heads\//, "");
|
|
281
|
-
} else if (line.trim() === "") {
|
|
282
|
-
flush();
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
flush();
|
|
286
|
-
} catch {
|
|
287
|
-
}
|
|
288
|
-
let branches = [];
|
|
289
|
-
try {
|
|
290
|
-
const b = await ctx.runGit(
|
|
291
|
-
["branch", "--list", "--format=%(refname:short)", "wstack/ap/*"],
|
|
292
|
-
ctx.projectRoot
|
|
293
|
-
);
|
|
294
|
-
branches = b.stdout.split("\n").map((s) => s.trim()).filter(Boolean);
|
|
295
|
-
} catch {
|
|
296
|
-
}
|
|
297
|
-
return { worktrees, branches };
|
|
298
|
-
}
|
|
299
|
-
async function removeOne(ctx, dir, branch) {
|
|
300
|
-
const root = resolve2(ctx.worktreesRoot);
|
|
301
|
-
const abs = resolve2(dir);
|
|
302
|
-
if (abs === root || !abs.startsWith(root + sep2)) {
|
|
303
|
-
return { removed: false };
|
|
304
|
-
}
|
|
305
|
-
let removed = false;
|
|
306
|
-
try {
|
|
307
|
-
const rm = await ctx.runGit(["worktree", "remove", "--force", abs], ctx.projectRoot);
|
|
308
|
-
removed = rm.code === 0;
|
|
309
|
-
} catch {
|
|
310
|
-
}
|
|
311
|
-
if (branch && !branch.startsWith("-")) {
|
|
312
|
-
await ctx.runGit(["branch", "-D", "--", branch], ctx.projectRoot).catch(() => void 0);
|
|
313
|
-
}
|
|
314
|
-
await ctx.runGit(["worktree", "prune"], ctx.projectRoot).catch(() => void 0);
|
|
315
|
-
for (const [ownerId, h] of [...ctx.handles]) {
|
|
316
|
-
if (resolve2(h.dir) === abs) ctx.handles.delete(ownerId);
|
|
317
|
-
}
|
|
318
|
-
return { removed };
|
|
319
|
-
}
|
|
320
|
-
async function cleanupStale(ctx) {
|
|
321
|
-
const root = resolve2(ctx.worktreesRoot);
|
|
322
|
-
let detected = 0;
|
|
323
|
-
try {
|
|
324
|
-
const listed = await ctx.runGit(["worktree", "list", "--porcelain"], ctx.projectRoot);
|
|
325
|
-
for (const line of listed.stdout.split("\n")) {
|
|
326
|
-
const m = line.match(/^worktree\s+(.+?)\s*$/);
|
|
327
|
-
if (!m?.[1]) continue;
|
|
328
|
-
const dir = resolve2(m[1]);
|
|
329
|
-
if (dir !== root && (dir === root || dir.startsWith(root + sep2))) {
|
|
330
|
-
detected++;
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
} catch {
|
|
334
|
-
return { removed: 0, detected: 0 };
|
|
335
|
-
}
|
|
336
|
-
if (detected === 0) {
|
|
337
|
-
try {
|
|
338
|
-
const branches = await ctx.runGit(
|
|
339
|
-
["branch", "--list", "--format=%(refname:short)", "wstack/ap/*"],
|
|
340
|
-
ctx.projectRoot
|
|
341
|
-
);
|
|
342
|
-
detected += branches.stdout.split("\n").map((s) => s.trim()).filter(Boolean).length;
|
|
343
|
-
} catch {
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
if (detected === 0) {
|
|
347
|
-
return { removed: 0, detected: 0 };
|
|
348
|
-
}
|
|
349
|
-
const { removed } = await cleanupAllManaged(ctx);
|
|
350
|
-
return { removed, detected };
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
// src/worktree/worktree-manager.ts
|
|
354
|
-
var WorktreeManager = class {
|
|
355
|
-
projectRoot;
|
|
356
|
-
events;
|
|
357
|
-
sessionIdSource;
|
|
358
|
-
gitBin;
|
|
359
|
-
runGit;
|
|
360
|
-
/** Keyed by ownerId. */
|
|
361
|
-
handles = /* @__PURE__ */ new Map();
|
|
362
|
-
usedSlugs = /* @__PURE__ */ new Set();
|
|
363
|
-
constructor(opts) {
|
|
364
|
-
this.projectRoot = resolve3(opts.projectRoot);
|
|
365
|
-
this.events = opts.events;
|
|
366
|
-
this.sessionIdSource = opts.sessionId;
|
|
367
|
-
this.gitBin = opts.gitBin ?? "git";
|
|
368
|
-
this.runGit = opts.run ?? ((args, cwd) => this.defaultRun(args, cwd));
|
|
369
|
-
}
|
|
370
|
-
/** Create a fresh worktree + branch forked from the current base branch. */
|
|
371
|
-
async allocate(ownerId, opts = {}) {
|
|
372
|
-
const existing = this.handles.get(ownerId);
|
|
373
|
-
if (existing && (existing.status === "allocating" || existing.status === "active")) {
|
|
374
|
-
return existing;
|
|
375
|
-
}
|
|
376
|
-
const slug = this.makeSlug(opts.slugHint ?? ownerId);
|
|
377
|
-
const branch = `wstack/ap/${slug}`;
|
|
378
|
-
const dir = join(this.worktreesRoot(), slug);
|
|
379
|
-
const absDir = resolve3(dir);
|
|
380
|
-
const absRoot = resolve3(this.projectRoot);
|
|
381
|
-
if (!absDir.startsWith(absRoot + sep3)) {
|
|
382
|
-
throw new Error(`Worktree dir "${absDir}" resolves outside project root`);
|
|
383
|
-
}
|
|
384
|
-
const baseBranch = opts.baseBranch ?? await this.detectBaseBranch();
|
|
385
|
-
const handle = {
|
|
386
|
-
id: slug,
|
|
387
|
-
ownerId,
|
|
388
|
-
ownerLabel: opts.ownerLabel ?? opts.slugHint ?? ownerId,
|
|
389
|
-
slug,
|
|
390
|
-
dir,
|
|
391
|
-
branch,
|
|
392
|
-
baseBranch,
|
|
393
|
-
status: "allocating",
|
|
394
|
-
createdAt: Date.now(),
|
|
395
|
-
updatedAt: Date.now(),
|
|
396
|
-
insertions: 0,
|
|
397
|
-
deletions: 0,
|
|
398
|
-
files: 0
|
|
399
|
-
};
|
|
400
|
-
this.handles.set(ownerId, handle);
|
|
401
|
-
try {
|
|
402
|
-
await mkdir(this.worktreesRoot(), { recursive: true });
|
|
403
|
-
const res = await this.runGit(
|
|
404
|
-
["worktree", "add", "-b", branch, dir, baseBranch],
|
|
405
|
-
this.projectRoot
|
|
406
|
-
);
|
|
407
|
-
if (res.code !== 0) {
|
|
408
|
-
return this.fail(handle, res.stderr || "git worktree add failed");
|
|
409
|
-
}
|
|
410
|
-
} catch (err) {
|
|
411
|
-
return this.fail(handle, toErrorMessage(err));
|
|
412
|
-
}
|
|
413
|
-
this.setStatus(handle, "active");
|
|
414
|
-
this.emit("worktree.allocated", {
|
|
415
|
-
handleId: handle.id,
|
|
416
|
-
ownerId: handle.ownerId,
|
|
417
|
-
ownerLabel: handle.ownerLabel,
|
|
418
|
-
slug: handle.slug,
|
|
419
|
-
dir: handle.dir,
|
|
420
|
-
branch: handle.branch,
|
|
421
|
-
baseBranch: handle.baseBranch
|
|
422
|
-
});
|
|
423
|
-
return handle;
|
|
424
|
-
}
|
|
425
|
-
/** Stage everything and commit inside the worktree. */
|
|
426
|
-
async commitAll(handle, message) {
|
|
427
|
-
this.setStatus(handle, "committing");
|
|
428
|
-
await this.runGit(["add", "-A"], handle.dir);
|
|
429
|
-
const staged = await this.runGit(["diff", "--cached", "--quiet"], handle.dir);
|
|
430
|
-
if (staged.code === 0) {
|
|
431
|
-
this.emitCommitted(handle, false);
|
|
432
|
-
return { committed: false };
|
|
433
|
-
}
|
|
434
|
-
const idArgs = await this.identityArgs(handle.dir);
|
|
435
|
-
const committed = await this.runGit([...idArgs, "commit", "-m", message], handle.dir);
|
|
436
|
-
if (committed.code !== 0) {
|
|
437
|
-
this.fail(handle, committed.stderr || "git commit failed");
|
|
438
|
-
return { committed: false };
|
|
439
|
-
}
|
|
440
|
-
const stats = await this.collectStats(handle.dir);
|
|
441
|
-
handle.insertions = stats.insertions;
|
|
442
|
-
handle.deletions = stats.deletions;
|
|
443
|
-
handle.files = stats.files;
|
|
444
|
-
handle.sha = stats.sha;
|
|
445
|
-
handle.updatedAt = Date.now();
|
|
446
|
-
this.emitCommitted(handle, true);
|
|
447
|
-
return { committed: true };
|
|
448
|
-
}
|
|
449
|
-
/** Merge the worktree branch back into the base branch (squash by default). */
|
|
450
|
-
async merge(handle, opts = {}) {
|
|
451
|
-
const squash = opts.squash ?? true;
|
|
452
|
-
this.setStatus(handle, "merging");
|
|
453
|
-
const status = await this.runGit(
|
|
454
|
-
["status", "--porcelain", "--untracked-files=no"],
|
|
455
|
-
this.projectRoot
|
|
456
|
-
);
|
|
457
|
-
if (status.stdout.trim().length > 0) {
|
|
458
|
-
const reason = "working tree has uncommitted changes \u2014 commit or stash first";
|
|
459
|
-
this.fail(handle, reason);
|
|
460
|
-
return { ok: false, stderr: reason };
|
|
461
|
-
}
|
|
462
|
-
const checkout = await this.runGit(["checkout", handle.baseBranch], this.projectRoot);
|
|
463
|
-
if (checkout.code !== 0) {
|
|
464
|
-
this.fail(handle, checkout.stderr || `checkout ${handle.baseBranch} failed`);
|
|
465
|
-
return { ok: false, stderr: checkout.stderr };
|
|
466
|
-
}
|
|
467
|
-
const mergeArgs = squash ? ["merge", "--squash", handle.branch] : ["merge", "--no-ff", handle.branch];
|
|
468
|
-
const merged = await this.runGit(mergeArgs, this.projectRoot);
|
|
469
|
-
const fromOutput = parseConflictPaths(`${merged.stdout}
|
|
470
|
-
${merged.stderr}`);
|
|
471
|
-
const fromIndex = await this.unmergedFiles();
|
|
472
|
-
const conflictFiles = [.../* @__PURE__ */ new Set([...fromOutput, ...fromIndex])];
|
|
473
|
-
if (merged.code !== 0 || conflictFiles.length > 0) {
|
|
474
|
-
if (opts.resolve) {
|
|
475
|
-
const finalized = await this.tryResolveConflict(handle, conflictFiles, opts);
|
|
476
|
-
if (finalized) return finalized;
|
|
477
|
-
}
|
|
478
|
-
await this.runGit(["reset", "--hard", "HEAD"], this.projectRoot);
|
|
479
|
-
handle.conflictFiles = conflictFiles;
|
|
480
|
-
this.setStatus(handle, "needs-review", { lastError: merged.stderr });
|
|
481
|
-
this.emit("worktree.conflict", {
|
|
482
|
-
handleId: handle.id,
|
|
483
|
-
ownerId: handle.ownerId,
|
|
484
|
-
branch: handle.branch,
|
|
485
|
-
conflictFiles
|
|
486
|
-
});
|
|
487
|
-
return { ok: false, conflict: true, conflictFiles, stderr: merged.stderr };
|
|
488
|
-
}
|
|
489
|
-
if (squash) {
|
|
490
|
-
const msg = opts.message ?? `merge ${handle.branch} (squash)`;
|
|
491
|
-
const idArgs = await this.identityArgs(this.projectRoot);
|
|
492
|
-
const commit = await this.runGit([...idArgs, "commit", "-m", msg], this.projectRoot);
|
|
493
|
-
if (commit.code !== 0 && !/nothing to commit/i.test(commit.stdout + commit.stderr)) {
|
|
494
|
-
this.fail(handle, commit.stderr || "squash commit failed");
|
|
495
|
-
return { ok: false, stderr: commit.stderr };
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
this.setStatus(handle, "merged");
|
|
499
|
-
this.emit("worktree.merged", {
|
|
500
|
-
handleId: handle.id,
|
|
501
|
-
ownerId: handle.ownerId,
|
|
502
|
-
branch: handle.branch,
|
|
503
|
-
baseBranch: handle.baseBranch,
|
|
504
|
-
squash
|
|
505
|
-
});
|
|
506
|
-
return { ok: true };
|
|
507
|
-
}
|
|
508
|
-
/**
|
|
509
|
-
* Current tip SHA of a handle's base branch (without checking it out). Capture
|
|
510
|
-
* this before a merge so a regressed merge can be reverted to exactly this
|
|
511
|
-
* commit — unambiguous even when a squash produced no diff. Returns null on
|
|
512
|
-
* failure (caller then skips the revert).
|
|
513
|
-
*/
|
|
514
|
-
async baseHead(handle) {
|
|
515
|
-
const res = await this.runGit(["rev-parse", handle.baseBranch], this.projectRoot);
|
|
516
|
-
const sha = res.stdout.trim();
|
|
517
|
-
return res.code === 0 && sha ? sha : null;
|
|
518
|
-
}
|
|
519
|
-
/**
|
|
520
|
-
* Hard-reset the base branch back to `sha` (a value previously returned by
|
|
521
|
-
* {@link baseHead}). Used to undo a squash-merge whose integrated result failed
|
|
522
|
-
* re-verification, so an auto-resolved-but-broken merge never sticks on base.
|
|
523
|
-
* Safe because SDD merges are serialized — no other commit lands in between.
|
|
524
|
-
*/
|
|
525
|
-
async revertBaseTo(handle, sha) {
|
|
526
|
-
const status = await this.runGit(["status", "--porcelain"], this.projectRoot);
|
|
527
|
-
if (status.code !== 0) return false;
|
|
528
|
-
const hasTrackedDirt = status.stdout.split("\n").some((line) => line.trim().length > 0 && !line.startsWith("??"));
|
|
529
|
-
if (hasTrackedDirt) return false;
|
|
530
|
-
const co = await this.runGit(["checkout", handle.baseBranch], this.projectRoot);
|
|
531
|
-
if (co.code !== 0) return false;
|
|
532
|
-
const reset = await this.runGit(["reset", "--hard", sha], this.projectRoot);
|
|
533
|
-
return reset.code === 0;
|
|
534
|
-
}
|
|
535
|
-
/**
|
|
536
|
-
* Current base branch + tip SHA, captured WITHOUT a handle. The SDD run calls
|
|
537
|
-
* this once at start so a later rollback knows which branch the run's squash
|
|
538
|
-
* commits landed on. Returns null when not in a usable git state.
|
|
539
|
-
*/
|
|
540
|
-
async currentBase() {
|
|
541
|
-
const branch = await this.detectBaseBranch();
|
|
542
|
-
const head = await this.runGit(["rev-parse", "HEAD"], this.projectRoot);
|
|
543
|
-
const sha = head.stdout.trim();
|
|
544
|
-
return head.code === 0 && sha ? { branch, sha } : null;
|
|
545
|
-
}
|
|
546
|
-
/**
|
|
547
|
-
* Force-remove EVERY managed worktree + branch this project owns, without
|
|
548
|
-
* relying on the in-memory `handles` map — so it works post-run (a fresh
|
|
549
|
-
* manager can clean up a previous run's leftovers). Enumerates
|
|
550
|
-
* `git worktree list --porcelain`, removes every checkout living under the
|
|
551
|
-
* `.wrongstack/worktrees` root, deletes every `wstack/ap/*` branch, then prunes.
|
|
552
|
-
* Returns the number of worktrees removed. Never throws — best-effort cleanup.
|
|
553
|
-
*/
|
|
554
|
-
async cleanupAllManaged() {
|
|
555
|
-
return cleanupAllManaged(this.managedOpsContext());
|
|
556
|
-
}
|
|
557
|
-
/**
|
|
558
|
-
* Read-only inventory of this project's managed git artifacts: every worktree
|
|
559
|
-
* checkout under the `.wrongstack/worktrees` root (with its branch, when the
|
|
560
|
-
* checkout has one) plus every `wstack/ap/*` branch. Unlike `cleanupStale` /
|
|
561
|
-
* `cleanupAllManaged` this removes nothing — it powers the WebUI worktree panel
|
|
562
|
-
* so the user can SEE orphans before deciding to clean them. Never throws.
|
|
563
|
-
*/
|
|
564
|
-
async listManaged() {
|
|
565
|
-
return listManaged(this.managedOpsContext());
|
|
566
|
-
}
|
|
567
|
-
/**
|
|
568
|
-
* Force-remove ONE managed worktree + (optionally) its branch — the targeted
|
|
569
|
-
* counterpart to `cleanupAllManaged`. Used by the WebUI worktree panel's
|
|
570
|
-
* per-row Remove/Discard. Path-guarded so only checkouts under the project's
|
|
571
|
-
* worktrees root can be removed. Best-effort; never throws.
|
|
572
|
-
*/
|
|
573
|
-
async removeOne(dir, branch) {
|
|
574
|
-
return removeOne(this.managedOpsContext(), dir, branch);
|
|
575
|
-
}
|
|
576
|
-
/**
|
|
577
|
-
* Squash-merge an arbitrary `wstack/ap/*` branch into the base branch from the
|
|
578
|
-
* main checkout — the handle-free counterpart to {@link merge}, used by the
|
|
579
|
-
* WebUI worktree panel's per-row "Merge to base". On conflict it hard-resets
|
|
580
|
-
* the base tree (never leaves it dirty) and reports the conflicted paths.
|
|
581
|
-
* Refuses on a dirty base tree so uncommitted work is never clobbered.
|
|
582
|
-
*/
|
|
583
|
-
async mergeBranch(branch, baseBranch) {
|
|
584
|
-
const base = baseBranch ?? await this.detectBaseBranch();
|
|
585
|
-
if (branch.startsWith("-") || base.startsWith("-")) {
|
|
586
|
-
return { ok: false, reason: "invalid ref" };
|
|
587
|
-
}
|
|
588
|
-
const status = await this.runGit(["status", "--porcelain"], this.projectRoot);
|
|
589
|
-
if (status.stdout.trim().length > 0) {
|
|
590
|
-
return { ok: false, reason: "working tree has uncommitted changes \u2014 commit or stash first" };
|
|
591
|
-
}
|
|
592
|
-
const co = await this.runGit(["checkout", base], this.projectRoot);
|
|
593
|
-
if (co.code !== 0) {
|
|
594
|
-
return { ok: false, reason: co.stderr || `checkout ${base} failed` };
|
|
595
|
-
}
|
|
596
|
-
const merged = await this.runGit(["merge", "--squash", branch], this.projectRoot);
|
|
597
|
-
if (merged.code !== 0) {
|
|
598
|
-
const fromOutput = parseConflictPaths(`${merged.stdout}
|
|
599
|
-
${merged.stderr}`);
|
|
600
|
-
const fromIndex = await this.unmergedFiles();
|
|
601
|
-
const conflictFiles = [.../* @__PURE__ */ new Set([...fromOutput, ...fromIndex])];
|
|
602
|
-
await this.runGit(["reset", "--hard", "HEAD"], this.projectRoot).catch(() => void 0);
|
|
603
|
-
return {
|
|
604
|
-
ok: false,
|
|
605
|
-
conflict: true,
|
|
606
|
-
conflictFiles,
|
|
607
|
-
reason: merged.stderr || "merge conflict"
|
|
608
|
-
};
|
|
609
|
-
}
|
|
610
|
-
const idArgs = await this.identityArgs(this.projectRoot);
|
|
611
|
-
const commit = await this.runGit(
|
|
612
|
-
[...idArgs, "commit", "-m", `merge ${branch} (squash)`],
|
|
613
|
-
this.projectRoot
|
|
614
|
-
);
|
|
615
|
-
if (commit.code !== 0 && !/nothing to commit/i.test(commit.stdout + commit.stderr)) {
|
|
616
|
-
return { ok: false, reason: commit.stderr || "squash commit failed" };
|
|
617
|
-
}
|
|
618
|
-
return { ok: true };
|
|
619
|
-
}
|
|
620
|
-
/**
|
|
621
|
-
* Compact change summary for a worktree checkout: working-tree edits
|
|
622
|
-
* (numstat vs HEAD) + commit count ahead of `baseBranch`. Powers the panel's
|
|
623
|
-
* "View changes" without streaming a full diff. Never throws.
|
|
624
|
-
*/
|
|
625
|
-
async diffSummary(dir, baseBranch) {
|
|
626
|
-
const files = [];
|
|
627
|
-
let insertions = 0;
|
|
628
|
-
let deletions = 0;
|
|
629
|
-
let commits = 0;
|
|
630
|
-
try {
|
|
631
|
-
const numstat = await this.runGit(["diff", "--numstat", "HEAD"], dir);
|
|
632
|
-
for (const line of numstat.stdout.split("\n")) {
|
|
633
|
-
const m = line.trim().match(/^(\d+|-)\t(\d+|-)\t(.+)$/);
|
|
634
|
-
if (!m) continue;
|
|
635
|
-
const ins = m[1] === "-" ? 0 : Number(m[1]);
|
|
636
|
-
const del = m[2] === "-" ? 0 : Number(m[2]);
|
|
637
|
-
files.push({ path: m[3], insertions: ins, deletions: del });
|
|
638
|
-
insertions += ins;
|
|
639
|
-
deletions += del;
|
|
640
|
-
}
|
|
641
|
-
} catch {
|
|
642
|
-
}
|
|
643
|
-
try {
|
|
644
|
-
const base = baseBranch ?? await this.detectBaseBranch();
|
|
645
|
-
const count = await this.runGit(["rev-list", "--count", `${base}..HEAD`], dir);
|
|
646
|
-
commits = Number(count.stdout.trim()) || 0;
|
|
647
|
-
} catch {
|
|
648
|
-
}
|
|
649
|
-
return { files, insertions, deletions, commits };
|
|
650
|
-
}
|
|
651
|
-
/**
|
|
652
|
-
* Detect and clean up stale worktrees left behind by crashed subagents.
|
|
653
|
-
*
|
|
654
|
-
* P2 #B6 (sprint2 audit): when a subagent crashes (OOM, SIGKILL), its
|
|
655
|
-
* worktree checkout and branch remain on disk. `cleanupAllManaged()`
|
|
656
|
-
* sweeps everything, but is never called automatically — only via
|
|
657
|
-
* `/worktree clean`. This method runs on Director boot (or any other
|
|
658
|
-
* entry point) to clean up leftovers from a previous crashed run.
|
|
659
|
-
*
|
|
660
|
-
* Unlike `cleanupAllManaged()`, this method is a no-op when no stale
|
|
661
|
-
* worktrees are detected — it doesn't clear in-memory handles or emit
|
|
662
|
-
* events unnecessarily. When stale worktrees ARE found, it delegates
|
|
663
|
-
* to `cleanupAllManaged()` for the actual removal.
|
|
664
|
-
*
|
|
665
|
-
* Returns the number of stale worktrees removed (0 = clean).
|
|
666
|
-
*/
|
|
667
|
-
async cleanupStale() {
|
|
668
|
-
return cleanupStale(this.managedOpsContext());
|
|
669
|
-
}
|
|
670
|
-
/**
|
|
671
|
-
* Undo a run's squash commits by reverting each (newest → oldest) on the base
|
|
672
|
-
* branch — history-preserving, never a destructive reset. Refuses on a dirty
|
|
673
|
-
* working tree (so uncommitted work is never clobbered) and aborts cleanly if a
|
|
674
|
-
* revert conflicts, reporting which SHA. `shas` are the run commit SHAs in the
|
|
675
|
-
* order they landed; this reverses them. Returns the count reverted.
|
|
676
|
-
*/
|
|
677
|
-
async revertCommits(baseBranch, shas) {
|
|
678
|
-
if (shas.length === 0) return { ok: true, reverted: 0, reason: "nothing to revert" };
|
|
679
|
-
const status = await this.runGit(["status", "--porcelain"], this.projectRoot);
|
|
680
|
-
if (status.stdout.trim().length > 0) {
|
|
681
|
-
return {
|
|
682
|
-
ok: false,
|
|
683
|
-
reverted: 0,
|
|
684
|
-
reason: "working tree has uncommitted changes \u2014 commit or stash first"
|
|
685
|
-
};
|
|
686
|
-
}
|
|
687
|
-
const co = await this.runGit(["checkout", baseBranch], this.projectRoot);
|
|
688
|
-
if (co.code !== 0) {
|
|
689
|
-
return { ok: false, reverted: 0, reason: co.stderr || `checkout ${baseBranch} failed` };
|
|
690
|
-
}
|
|
691
|
-
const idArgs = await this.identityArgs(this.projectRoot);
|
|
692
|
-
let reverted = 0;
|
|
693
|
-
for (const sha of [...shas].reverse()) {
|
|
694
|
-
const res = await this.runGit([...idArgs, "revert", "--no-edit", sha], this.projectRoot);
|
|
695
|
-
if (res.code !== 0) {
|
|
696
|
-
await this.runGit(["revert", "--abort"], this.projectRoot).catch(() => void 0);
|
|
697
|
-
return {
|
|
698
|
-
ok: false,
|
|
699
|
-
reverted,
|
|
700
|
-
reason: `revert of ${sha.slice(0, 8)} failed: ${(res.stderr || res.stdout).trim().split("\n")[0] ?? "conflict"}`
|
|
701
|
-
};
|
|
702
|
-
}
|
|
703
|
-
reverted++;
|
|
704
|
-
}
|
|
705
|
-
return { ok: true, reverted };
|
|
706
|
-
}
|
|
707
|
-
/**
|
|
708
|
-
* Run the caller-supplied resolver against a conflicted squash-merge, then
|
|
709
|
-
* commit if it cleared every marker. Returns a successful `MergeResult` on a
|
|
710
|
-
* clean resolution, or `null` to signal the caller should fall back to the
|
|
711
|
-
* abort path. Never leaves the base tree committed-but-dirty: a partial or
|
|
712
|
-
* failed resolution returns `null` and the caller hard-resets.
|
|
713
|
-
*/
|
|
714
|
-
async tryResolveConflict(handle, conflictFiles, opts) {
|
|
715
|
-
let resolved = false;
|
|
716
|
-
try {
|
|
717
|
-
resolved = opts.resolve ? await opts.resolve({ conflictFiles, cwd: this.projectRoot }) : false;
|
|
718
|
-
} catch {
|
|
719
|
-
resolved = false;
|
|
720
|
-
}
|
|
721
|
-
if (!resolved) return null;
|
|
722
|
-
await this.runGit(["add", "-A"], this.projectRoot);
|
|
723
|
-
if (await this.hasConflictMarkers(conflictFiles)) return null;
|
|
724
|
-
const idArgs = await this.identityArgs(this.projectRoot);
|
|
725
|
-
const msg = opts.message ?? `merge ${handle.branch} (squash, conflict resolved)`;
|
|
726
|
-
const commit = await this.runGit([...idArgs, "commit", "-m", msg], this.projectRoot);
|
|
727
|
-
if (commit.code !== 0 && !/nothing to commit/i.test(commit.stdout + commit.stderr)) {
|
|
728
|
-
return null;
|
|
729
|
-
}
|
|
730
|
-
handle.conflictFiles = conflictFiles;
|
|
731
|
-
this.setStatus(handle, "merged");
|
|
732
|
-
this.emit("worktree.merged", {
|
|
733
|
-
handleId: handle.id,
|
|
734
|
-
ownerId: handle.ownerId,
|
|
735
|
-
branch: handle.branch,
|
|
736
|
-
baseBranch: handle.baseBranch,
|
|
737
|
-
squash: true
|
|
738
|
-
});
|
|
739
|
-
return { ok: true, resolved: true, conflictFiles };
|
|
740
|
-
}
|
|
741
|
-
/**
|
|
742
|
-
* True when staged content still carries conflict markers.
|
|
743
|
-
*
|
|
744
|
-
* Reads the working-tree files directly and matches a full marker line
|
|
745
|
-
* (`<<<<<<< `, `=======`, `>>>>>>> `, `||||||| `) with a lenient regex
|
|
746
|
-
* (`{7,}`) that stays platform- and config-independent — unlike the old
|
|
747
|
-
* `git diff --check` (only fires when `core.whitespace` opts in) and
|
|
748
|
-
* `git grep` (assumes the default 7-char markers).
|
|
749
|
-
*
|
|
750
|
-
* Scan set: when the caller supplies the conflicted paths (the normal
|
|
751
|
-
* path from {@link tryResolveConflict}), ONLY those files are scanned so
|
|
752
|
-
* an unrelated `=======` setext-heading underline in a staged document
|
|
753
|
-
* can't false-positive and discard valid resolution work. The staged-file
|
|
754
|
-
* listing (`git diff --cached --name-only`) is used only as a fallback
|
|
755
|
-
* when no paths are given.
|
|
756
|
-
*
|
|
757
|
-
* CRLF caveat: conflicted files checked out with CRLF (`core.autocrlf=true`,
|
|
758
|
-
* `.gitattributes eol=crlf`) carry bare markers as `=======\r\n`. Because
|
|
759
|
-
* `$` in multiline mode matches only before `\n`, carriage returns are
|
|
760
|
-
* stripped before matching so `\r` never defeats the end-of-line anchor.
|
|
761
|
-
*/
|
|
762
|
-
async hasConflictMarkers(files) {
|
|
763
|
-
const listed = files ?? [];
|
|
764
|
-
const paths = listed.length > 0 ? listed : (await this.runGit(["diff", "--cached", "--name-only", "-z"], this.projectRoot)).stdout.split("\0").filter(Boolean);
|
|
765
|
-
const marker = /^(?:<{7,}(?: |$)|={7,}$|>{7,}(?: |$)|\|{7,}(?: |$))/m;
|
|
766
|
-
const startMarker = /^(?:<{7,}(?: |$)|\|{7,}(?: |$))/m;
|
|
767
|
-
for (const rel of paths) {
|
|
768
|
-
try {
|
|
769
|
-
const content = (await readFile(join(this.projectRoot, rel), "utf8")).replace(/\r/g, "");
|
|
770
|
-
const lines = content.split("\n");
|
|
771
|
-
let seenStart = false;
|
|
772
|
-
for (const line of lines) {
|
|
773
|
-
if (!marker.test(line)) continue;
|
|
774
|
-
if (line.startsWith("=====")) {
|
|
775
|
-
if (!seenStart) continue;
|
|
776
|
-
} else if (startMarker.test(line)) {
|
|
777
|
-
seenStart = true;
|
|
778
|
-
}
|
|
779
|
-
return true;
|
|
780
|
-
}
|
|
781
|
-
} catch {
|
|
782
|
-
}
|
|
783
|
-
}
|
|
784
|
-
return false;
|
|
785
|
-
}
|
|
786
|
-
/**
|
|
787
|
-
* Remove the worktree + branch. Conflicted/failed handles (or `keep:true`)
|
|
788
|
-
* are left on disk for inspection.
|
|
789
|
-
*/
|
|
790
|
-
async release(handle, opts = {}) {
|
|
791
|
-
const keep = opts.keep || handle.status === "needs-review" || handle.status === "failed";
|
|
792
|
-
if (!keep) {
|
|
793
|
-
await this.runGit(["worktree", "remove", "--force", handle.dir], this.projectRoot);
|
|
794
|
-
await this.runGit(["branch", "-D", handle.branch], this.projectRoot);
|
|
795
|
-
await this.runGit(["worktree", "prune"], this.projectRoot);
|
|
796
|
-
this.handles.delete(handle.ownerId);
|
|
797
|
-
}
|
|
798
|
-
this.emit("worktree.released", {
|
|
799
|
-
handleId: handle.id,
|
|
800
|
-
ownerId: handle.ownerId,
|
|
801
|
-
branch: handle.branch,
|
|
802
|
-
kept: keep
|
|
803
|
-
});
|
|
804
|
-
}
|
|
805
|
-
get(ownerId) {
|
|
806
|
-
return this.handles.get(ownerId);
|
|
807
|
-
}
|
|
808
|
-
list() {
|
|
809
|
-
return [...this.handles.values()];
|
|
810
|
-
}
|
|
811
|
-
// ── internals ────────────────────────────────────────────────────────────
|
|
812
|
-
worktreesRoot() {
|
|
813
|
-
return join(this.projectRoot, ".wrongstack", "worktrees");
|
|
814
|
-
}
|
|
815
|
-
async detectBaseBranch() {
|
|
816
|
-
const head = await this.runGit(["rev-parse", "--abbrev-ref", "HEAD"], this.projectRoot);
|
|
817
|
-
const name = head.stdout.trim();
|
|
818
|
-
if (name && name !== "HEAD") return name;
|
|
819
|
-
const sha = await this.runGit(["rev-parse", "HEAD"], this.projectRoot);
|
|
820
|
-
return sha.stdout.trim() || "HEAD";
|
|
821
|
-
}
|
|
822
|
-
makeSlug(hint) {
|
|
823
|
-
return makeSlug(hint, this.usedSlugs);
|
|
824
|
-
}
|
|
825
|
-
async collectStats(dir) {
|
|
826
|
-
return collectStats(this.runGit, dir);
|
|
827
|
-
}
|
|
828
|
-
async identityArgs(cwd) {
|
|
829
|
-
return identityArgs(this.runGit, cwd);
|
|
830
|
-
}
|
|
831
|
-
async unmergedFiles() {
|
|
832
|
-
const res = await this.runGit(["diff", "--name-only", "--diff-filter=U"], this.projectRoot);
|
|
833
|
-
return res.stdout.split("\n").map((s) => s.trim()).filter(Boolean);
|
|
834
|
-
}
|
|
835
|
-
emitCommitted(handle, committed) {
|
|
836
|
-
this.emit("worktree.committed", {
|
|
837
|
-
handleId: handle.id,
|
|
838
|
-
ownerId: handle.ownerId,
|
|
839
|
-
branch: handle.branch,
|
|
840
|
-
committed,
|
|
841
|
-
insertions: handle.insertions,
|
|
842
|
-
deletions: handle.deletions,
|
|
843
|
-
files: handle.files,
|
|
844
|
-
sha: handle.sha
|
|
845
|
-
});
|
|
846
|
-
}
|
|
847
|
-
fail(handle, error) {
|
|
848
|
-
this.setStatus(handle, "failed", { lastError: error });
|
|
849
|
-
this.emit("worktree.failed", {
|
|
850
|
-
handleId: handle.id,
|
|
851
|
-
ownerId: handle.ownerId,
|
|
852
|
-
branch: handle.branch,
|
|
853
|
-
error
|
|
854
|
-
});
|
|
855
|
-
return handle;
|
|
856
|
-
}
|
|
857
|
-
setStatus(handle, status, patch) {
|
|
858
|
-
handle.status = status;
|
|
859
|
-
handle.updatedAt = Date.now();
|
|
860
|
-
if (patch) Object.assign(handle, patch);
|
|
861
|
-
}
|
|
862
|
-
emit(event, payload) {
|
|
863
|
-
const sessionId = this.currentSessionId();
|
|
864
|
-
this.events?.emit(
|
|
865
|
-
event,
|
|
866
|
-
sessionId && payload && typeof payload === "object" ? { ...payload, sessionId } : payload
|
|
867
|
-
);
|
|
868
|
-
}
|
|
869
|
-
currentSessionId() {
|
|
870
|
-
const value = typeof this.sessionIdSource === "function" ? this.sessionIdSource() : this.sessionIdSource;
|
|
871
|
-
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
872
|
-
}
|
|
873
|
-
defaultRun(args, cwd) {
|
|
874
|
-
return defaultRun(this.gitBin, args, cwd);
|
|
875
|
-
}
|
|
876
|
-
/** Bundle of manager state the handle-free managed-artifact ops operate on. */
|
|
877
|
-
managedOpsContext() {
|
|
878
|
-
return {
|
|
879
|
-
runGit: this.runGit,
|
|
880
|
-
projectRoot: this.projectRoot,
|
|
881
|
-
worktreesRoot: this.worktreesRoot(),
|
|
882
|
-
handles: this.handles,
|
|
883
|
-
usedSlugs: this.usedSlugs,
|
|
884
|
-
emit: (event, payload) => this.emit(event, payload)
|
|
885
|
-
};
|
|
886
|
-
}
|
|
887
|
-
};
|
|
1
|
+
import {
|
|
2
|
+
WorktreeManager,
|
|
3
|
+
assertSafePath
|
|
4
|
+
} from "../chunk-NVIZG63R.js";
|
|
5
|
+
import "../chunk-6NWEVFEL.js";
|
|
6
|
+
import "../chunk-TBRL2RCC.js";
|
|
888
7
|
export {
|
|
889
8
|
WorktreeManager,
|
|
890
9
|
assertSafePath
|