@adhdev/daemon-core 0.9.82-rc.9 → 0.9.82-rc.91
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli-adapters/provider-cli-adapter.d.ts +2 -0
- package/dist/cli-adapters/provider-cli-parse.d.ts +1 -0
- package/dist/cli-adapters/provider-cli-shared.d.ts +2 -0
- package/dist/commands/router.d.ts +22 -0
- package/dist/config/mesh-config.d.ts +66 -1
- package/dist/index.d.ts +13 -6
- package/dist/index.js +5417 -1207
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +5381 -1193
- package/dist/index.mjs.map +1 -1
- package/dist/installer.d.ts +1 -4
- package/dist/launch.d.ts +1 -1
- package/dist/logging/async-batch-writer.d.ts +10 -0
- package/dist/mesh/beads-db.d.ts +18 -0
- package/dist/mesh/mesh-active-work.d.ts +60 -0
- package/dist/mesh/mesh-events.d.ts +29 -5
- package/dist/mesh/mesh-fast-forward.d.ts +39 -0
- package/dist/mesh/mesh-host-ownership.d.ts +9 -0
- package/dist/mesh/mesh-ledger.d.ts +38 -1
- package/dist/mesh/mesh-work-queue.d.ts +27 -5
- package/dist/mesh/refine-config.d.ts +176 -0
- package/dist/providers/chat-message-normalization.d.ts +1 -0
- package/dist/providers/cli-provider-instance.d.ts +2 -1
- package/dist/repo-mesh-types.d.ts +46 -0
- package/dist/status/reporter.d.ts +2 -0
- package/package.json +3 -1
- package/src/boot/daemon-lifecycle.ts +1 -0
- package/src/cli-adapters/provider-cli-adapter.ts +91 -3
- package/src/cli-adapters/provider-cli-parse.d.ts +1 -0
- package/src/cli-adapters/provider-cli-parse.ts +4 -0
- package/src/cli-adapters/provider-cli-runtime.ts +3 -1
- package/src/cli-adapters/provider-cli-shared.d.ts +2 -0
- package/src/cli-adapters/provider-cli-shared.ts +20 -10
- package/src/commands/chat-commands.ts +472 -15
- package/src/commands/cli-manager.ts +126 -0
- package/src/commands/handler.ts +8 -1
- package/src/commands/mesh-coordinator.ts +13 -143
- package/src/commands/router.ts +2687 -435
- package/src/config/chat-history.ts +9 -7
- package/src/config/mesh-config.ts +245 -1
- package/src/daemon/dev-cli-debug.ts +10 -1
- package/src/detection/ide-detector.ts +26 -16
- package/src/index.ts +31 -5
- package/src/installer.d.ts +1 -1
- package/src/installer.ts +8 -6
- package/src/launch.d.ts +1 -1
- package/src/launch.ts +37 -28
- package/src/logging/async-batch-writer.ts +55 -0
- package/src/logging/logger.ts +2 -1
- package/src/mesh/beads-db.ts +176 -0
- package/src/mesh/coordinator-prompt.ts +30 -7
- package/src/mesh/mesh-active-work.ts +255 -0
- package/src/mesh/mesh-events.ts +400 -47
- package/src/mesh/mesh-fast-forward.ts +430 -0
- package/src/mesh/mesh-host-ownership.ts +73 -0
- package/src/mesh/mesh-ledger.ts +138 -1
- package/src/mesh/mesh-work-queue.ts +199 -137
- package/src/mesh/refine-config.ts +356 -0
- package/src/providers/chat-message-normalization.ts +7 -12
- package/src/providers/cli-provider-instance.ts +93 -14
- package/src/providers/ide-provider-instance.ts +17 -3
- package/src/providers/provider-loader.ts +10 -4
- package/src/providers/read-chat-contract.ts +1 -1
- package/src/providers/version-archive.ts +38 -20
- package/src/repo-mesh-types.ts +51 -0
- package/src/status/reporter.ts +15 -0
- package/src/system/host-memory.ts +29 -12
|
@@ -19,10 +19,37 @@ export interface RepoMesh {
|
|
|
19
19
|
defaultBranch?: string;
|
|
20
20
|
policy: RepoMeshPolicy;
|
|
21
21
|
coordinator: RepoMeshCoordinatorConfig;
|
|
22
|
+
meshHost?: RepoMeshHostMetadata;
|
|
22
23
|
projectContext: ProjectContextSnapshot;
|
|
23
24
|
nodes: RepoMeshNode[];
|
|
24
25
|
status: 'active' | 'archived' | 'deleted';
|
|
25
26
|
}
|
|
27
|
+
export type RepoMeshDaemonRole = 'host' | 'member';
|
|
28
|
+
export interface RepoMeshHostPairingMetadata {
|
|
29
|
+
status: 'not_configured' | 'pairing' | 'paired' | 'rejected' | 'revoked';
|
|
30
|
+
tokenId?: string;
|
|
31
|
+
joinedAt?: string;
|
|
32
|
+
lastPairedAt?: string;
|
|
33
|
+
lastRejectedAt?: string;
|
|
34
|
+
expiresAt?: string;
|
|
35
|
+
}
|
|
36
|
+
export interface RepoMeshHostMetadata {
|
|
37
|
+
/** Local daemon role for this mesh. Missing metadata defaults to host for standalone compatibility. */
|
|
38
|
+
role: RepoMeshDaemonRole;
|
|
39
|
+
/** Daemon that owns mesh truth/status/git/queue/session/ledger/coordinator ownership. */
|
|
40
|
+
hostDaemonId?: string;
|
|
41
|
+
/** Mesh node that represents the host daemon, when known. */
|
|
42
|
+
hostNodeId?: string;
|
|
43
|
+
/** Future standalone manual pairing endpoint entered by member daemons. */
|
|
44
|
+
hostAddress?: string;
|
|
45
|
+
/** Redacted pairing state only; raw join tokens must not be persisted here. */
|
|
46
|
+
pairing?: RepoMeshHostPairingMetadata;
|
|
47
|
+
}
|
|
48
|
+
export interface RepoMeshHostStatus extends RepoMeshHostMetadata {
|
|
49
|
+
canOwnCoordinator: boolean;
|
|
50
|
+
canOwnQueue: boolean;
|
|
51
|
+
defaulted: boolean;
|
|
52
|
+
}
|
|
26
53
|
export interface RepoMeshNode {
|
|
27
54
|
id: string;
|
|
28
55
|
daemonId: string;
|
|
@@ -37,6 +64,7 @@ export interface RepoMeshNode {
|
|
|
37
64
|
effectiveCapabilities: RepoMeshNodeCapabilities;
|
|
38
65
|
policy: RepoMeshNodePolicy;
|
|
39
66
|
health: RepoMeshNodeHealth;
|
|
67
|
+
role?: RepoMeshDaemonRole;
|
|
40
68
|
status: 'enabled' | 'disabled' | 'removed';
|
|
41
69
|
}
|
|
42
70
|
export type RepoMeshNodeHealth = 'online' | 'offline' | 'degraded' | 'dirty' | 'wrong_branch' | 'unknown';
|
|
@@ -46,6 +74,13 @@ export interface RepoMeshPolicy {
|
|
|
46
74
|
requirePreTaskCheckpoint: boolean;
|
|
47
75
|
requirePostTaskCheckpoint: boolean;
|
|
48
76
|
requireApprovalForPush: boolean;
|
|
77
|
+
/**
|
|
78
|
+
* Narrow Refinery opt-in: when validation and patch-equivalence have passed,
|
|
79
|
+
* allow Refinery to publish submodule gitlink commits to each submodule's
|
|
80
|
+
* configured remote main branch with a non-force push, then verify reachability.
|
|
81
|
+
* Defaults to false; root branch pushes/merges are not affected.
|
|
82
|
+
*/
|
|
83
|
+
allowAutoPublishSubmoduleMainCommits?: boolean;
|
|
49
84
|
requireApprovalForDestructiveGit: boolean;
|
|
50
85
|
dirtyWorkspaceBehavior: 'block' | 'warn' | 'checkpoint_then_continue';
|
|
51
86
|
maxParallelTasks: number;
|
|
@@ -185,6 +220,7 @@ export interface LocalMeshEntry {
|
|
|
185
220
|
defaultBranch?: string;
|
|
186
221
|
policy: RepoMeshPolicy;
|
|
187
222
|
coordinator: RepoMeshCoordinatorConfig;
|
|
223
|
+
meshHost?: RepoMeshHostMetadata;
|
|
188
224
|
nodes: LocalMeshNodeEntry[];
|
|
189
225
|
createdAt: string;
|
|
190
226
|
updatedAt: string;
|
|
@@ -206,12 +242,15 @@ export interface LocalMeshNodeEntry {
|
|
|
206
242
|
clonedFromNodeId?: string;
|
|
207
243
|
/** Optional associated/external repos configured as node metadata. */
|
|
208
244
|
relatedRepos?: RepoMeshRelatedRepo[];
|
|
245
|
+
role?: RepoMeshDaemonRole;
|
|
209
246
|
}
|
|
210
247
|
export interface RepoMeshStatus {
|
|
211
248
|
meshId: string;
|
|
212
249
|
meshName: string;
|
|
213
250
|
repoIdentity: string;
|
|
251
|
+
defaultBranch?: string;
|
|
214
252
|
refreshedAt: string;
|
|
253
|
+
meshHost?: RepoMeshHostStatus;
|
|
215
254
|
nodes: RepoMeshNodeStatus[];
|
|
216
255
|
queue?: RepoMeshQueueStatus;
|
|
217
256
|
ledger?: RepoMeshLedgerStatus;
|
|
@@ -248,11 +287,18 @@ export interface RepoMeshNodeStatus {
|
|
|
248
287
|
repoRoot?: string;
|
|
249
288
|
daemonId?: string;
|
|
250
289
|
machineId?: string;
|
|
290
|
+
role?: RepoMeshDaemonRole;
|
|
251
291
|
machineStatus?: string;
|
|
252
292
|
isLocalWorktree?: boolean;
|
|
253
293
|
worktreeBranch?: string;
|
|
254
294
|
health: RepoMeshNodeHealth;
|
|
255
295
|
git?: GitRepoStatus;
|
|
296
|
+
/**
|
|
297
|
+
* True when the selected coordinator has evidence that a peer git probe is still
|
|
298
|
+
* in flight or just timed out during initial mesh handshake, so callers should
|
|
299
|
+
* treat missing git data as pending instead of authoritative absence.
|
|
300
|
+
*/
|
|
301
|
+
gitProbePending?: boolean;
|
|
256
302
|
providers: string[];
|
|
257
303
|
activeSessions: string[];
|
|
258
304
|
activeSessionDetails?: RepoMeshSessionStatus[];
|
|
@@ -46,6 +46,8 @@ export declare class DaemonStatusReporter {
|
|
|
46
46
|
private lastStatusSentAt;
|
|
47
47
|
private statusPendingThrottle;
|
|
48
48
|
private lastP2PStatusHash;
|
|
49
|
+
private lastP2PStatusSentAt;
|
|
50
|
+
private p2pDebounceTimer;
|
|
49
51
|
private lastServerStatusHash;
|
|
50
52
|
private lastStatusSummary;
|
|
51
53
|
private statusTimer;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adhdev/daemon-core",
|
|
3
|
-
"version": "0.9.82-rc.
|
|
3
|
+
"version": "0.9.82-rc.91",
|
|
4
4
|
"description": "ADHDev daemon core — CDP, IDE detection, providers, command execution",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
"@adhdev/session-host-core": "*",
|
|
50
50
|
"@agentclientprotocol/sdk": "^0.16.1",
|
|
51
51
|
"@xterm/xterm": "^6.0.0",
|
|
52
|
+
"better-sqlite3": "^12.10.0",
|
|
52
53
|
"chalk": "^5.3.0",
|
|
53
54
|
"chokidar": "^4.0.3",
|
|
54
55
|
"conf": "^13.0.0",
|
|
@@ -60,6 +61,7 @@
|
|
|
60
61
|
"@adhdev/ghostty-vt-node": "*"
|
|
61
62
|
},
|
|
62
63
|
"devDependencies": {
|
|
64
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
63
65
|
"@types/js-yaml": "^4.0.9",
|
|
64
66
|
"@types/node": "^22.0.0",
|
|
65
67
|
"@types/ws": "^8.18.1",
|
|
@@ -309,6 +309,7 @@ export async function initDaemonComponents(config: DaemonInitConfig): Promise<Da
|
|
|
309
309
|
statusInstanceId: config.statusInstanceId,
|
|
310
310
|
statusVersion: config.statusVersion,
|
|
311
311
|
getMeshPeerConnectionStatus: config.getMeshPeerConnectionStatus,
|
|
312
|
+
dispatchMeshCommand: config.dispatchMeshCommand,
|
|
312
313
|
getCdpLogFn: config.getCdpLogFn || ((ideType: string) => LOG.forComponent(`CDP:${ideType}`).asLogFn()),
|
|
313
314
|
});
|
|
314
315
|
|
|
@@ -761,6 +761,17 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
761
761
|
if (stableMs < 2000) return;
|
|
762
762
|
|
|
763
763
|
const startupModal = this.runParseApproval(this.recentOutputBuffer);
|
|
764
|
+
const startupStatus = this.runDetectStatus(screenText || this.recentOutputBuffer);
|
|
765
|
+
if (!startupModal && startupStatus !== 'idle') {
|
|
766
|
+
this.recordTrace('startup_settle_deferred', {
|
|
767
|
+
trigger,
|
|
768
|
+
startupStatus,
|
|
769
|
+
stableMs,
|
|
770
|
+
screenText: summarizeCliTraceText(screenText, 500),
|
|
771
|
+
});
|
|
772
|
+
this.scheduleStartupSettleCheck();
|
|
773
|
+
return;
|
|
774
|
+
}
|
|
764
775
|
this.startupParseGate = false;
|
|
765
776
|
if (this.startupSettleTimer) {
|
|
766
777
|
clearTimeout(this.startupSettleTimer);
|
|
@@ -956,6 +967,38 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
956
967
|
return true;
|
|
957
968
|
}
|
|
958
969
|
|
|
970
|
+
private clearParsedIdleResponseGuard(reason: string, parsedStatus: any): boolean {
|
|
971
|
+
const parsedRawStatus = typeof parsedStatus?.status === 'string' ? parsedStatus.status.trim() : '';
|
|
972
|
+
const parsedModal = parsedStatus?.activeModal ?? parsedStatus?.modal ?? null;
|
|
973
|
+
const blockingModal = this.activeModal || this.runParseApproval(this.recentOutputBuffer);
|
|
974
|
+
if (
|
|
975
|
+
!this.isWaitingForResponse
|
|
976
|
+
|| parsedRawStatus !== 'idle'
|
|
977
|
+
|| !!parsedModal
|
|
978
|
+
|| !!blockingModal
|
|
979
|
+
|| !this.parsedStatusHasFinalAssistantMessage(parsedStatus)
|
|
980
|
+
) {
|
|
981
|
+
return false;
|
|
982
|
+
}
|
|
983
|
+
this.clearAllTimers();
|
|
984
|
+
this.clearIdleFinishCandidate(reason);
|
|
985
|
+
this.responseBuffer = '';
|
|
986
|
+
this.isWaitingForResponse = false;
|
|
987
|
+
this.responseSettleIgnoreUntil = 0;
|
|
988
|
+
this.submitRetryUsed = false;
|
|
989
|
+
this.submitRetryPromptSnippet = '';
|
|
990
|
+
this.finishRetryCount = 0;
|
|
991
|
+
this.currentTurnScope = null;
|
|
992
|
+
this.activeModal = null;
|
|
993
|
+
this.setStatus('idle', reason);
|
|
994
|
+
this.recordTrace('parsed_idle_response_cleared', {
|
|
995
|
+
reason,
|
|
996
|
+
parsedStatus: parsedRawStatus,
|
|
997
|
+
parsedMessageCount: Array.isArray(parsedStatus?.messages) ? parsedStatus.messages.length : 0,
|
|
998
|
+
});
|
|
999
|
+
return true;
|
|
1000
|
+
}
|
|
1001
|
+
|
|
959
1002
|
private hasMeaningfulResponseBuffer(promptSnippet: string): boolean {
|
|
960
1003
|
const raw = String(this.responseBuffer || '').trim();
|
|
961
1004
|
if (!raw) return false;
|
|
@@ -1489,6 +1532,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1489
1532
|
accumulatedRawBuffer: this.accumulatedRawBuffer,
|
|
1490
1533
|
recentOutputBuffer: this.recentOutputBuffer,
|
|
1491
1534
|
terminalScreenText: parseScreenText,
|
|
1535
|
+
workingDir: this.workingDir,
|
|
1492
1536
|
baseMessages: [],
|
|
1493
1537
|
partialResponse: this.responseBuffer,
|
|
1494
1538
|
isWaitingForResponse: this.isWaitingForResponse,
|
|
@@ -1552,6 +1596,15 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1552
1596
|
return !!(startupModal || this.activeModal);
|
|
1553
1597
|
}
|
|
1554
1598
|
|
|
1599
|
+
private parsedStatusHasFinalAssistantMessage(parsed: any): boolean {
|
|
1600
|
+
const messages = Array.isArray(parsed?.messages) ? parsed.messages : [];
|
|
1601
|
+
const lastAssistant = [...messages].reverse().find((message: any) => {
|
|
1602
|
+
if (!message || message.role !== 'assistant') return false;
|
|
1603
|
+
return typeof message.content === 'string' && message.content.trim().length > 0;
|
|
1604
|
+
});
|
|
1605
|
+
return !!lastAssistant;
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1555
1608
|
private projectEffectiveStatus(startupModal: { message: string; buttons: string[] } | null = null): CliSessionStatus['status'] {
|
|
1556
1609
|
if (this.parseErrorMessage) return 'error';
|
|
1557
1610
|
if (this.hasActionableApproval(startupModal)) return 'waiting_approval';
|
|
@@ -1564,8 +1617,14 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1564
1617
|
getStatus(options: { allowParse?: boolean } = {}): CliSessionStatus {
|
|
1565
1618
|
const allowParse = options.allowParse !== false;
|
|
1566
1619
|
const startupModal = allowParse && this.startupParseGate ? this.runParseApproval(this.recentOutputBuffer) : null;
|
|
1620
|
+
const startupDetectedStatus = allowParse && this.startupParseGate && !startupModal
|
|
1621
|
+
? this.runDetectStatus(this.recentOutputBuffer || this.terminalScreen.getText())
|
|
1622
|
+
: null;
|
|
1567
1623
|
let effectiveStatus = this.projectEffectiveStatus(startupModal);
|
|
1568
1624
|
let effectiveModal = startupModal || this.activeModal;
|
|
1625
|
+
if (startupDetectedStatus === 'waiting_approval') {
|
|
1626
|
+
effectiveStatus = 'waiting_approval';
|
|
1627
|
+
}
|
|
1569
1628
|
if (allowParse && !startupModal && !effectiveModal) {
|
|
1570
1629
|
const parsed = this.getFreshParsedStatusCache();
|
|
1571
1630
|
const parsedModal = parsed?.activeModal && Array.isArray(parsed.activeModal.buttons)
|
|
@@ -1575,6 +1634,18 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1575
1634
|
if (parsed?.status === 'waiting_approval' && parsedModal) {
|
|
1576
1635
|
effectiveStatus = 'waiting_approval';
|
|
1577
1636
|
effectiveModal = parsedModal;
|
|
1637
|
+
} else if (
|
|
1638
|
+
effectiveStatus === 'idle'
|
|
1639
|
+
&& parsed?.status === 'generating'
|
|
1640
|
+
&& !this.parsedStatusHasFinalAssistantMessage(parsed)
|
|
1641
|
+
) {
|
|
1642
|
+
effectiveStatus = 'generating';
|
|
1643
|
+
} else if (
|
|
1644
|
+
effectiveStatus === 'generating'
|
|
1645
|
+
&& parsed?.status === 'idle'
|
|
1646
|
+
&& this.parsedStatusHasFinalAssistantMessage(parsed)
|
|
1647
|
+
) {
|
|
1648
|
+
effectiveStatus = 'idle';
|
|
1578
1649
|
}
|
|
1579
1650
|
}
|
|
1580
1651
|
const bufferState = this.getBufferState();
|
|
@@ -1665,6 +1736,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1665
1736
|
accumulatedRawBuffer: this.accumulatedRawBuffer,
|
|
1666
1737
|
recentOutputBuffer: this.recentOutputBuffer,
|
|
1667
1738
|
terminalScreenText: this.getParseScreenText(this.terminalScreen.getText()),
|
|
1739
|
+
workingDir: this.workingDir,
|
|
1668
1740
|
baseMessages: [],
|
|
1669
1741
|
partialResponse: this.responseBuffer,
|
|
1670
1742
|
isWaitingForResponse: this.isWaitingForResponse,
|
|
@@ -1979,7 +2051,10 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1979
2051
|
}
|
|
1980
2052
|
}
|
|
1981
2053
|
if (this.isWaitingForResponse && !allowInputDuringGeneration) {
|
|
1982
|
-
if (
|
|
2054
|
+
if (
|
|
2055
|
+
!this.clearStaleIdleResponseGuard('send_message_guard')
|
|
2056
|
+
&& !this.clearParsedIdleResponseGuard('send_message_parsed_idle_guard', parsedStatusBeforeSend)
|
|
2057
|
+
) {
|
|
1983
2058
|
throw new Error(`${this.cliName} is still processing the previous prompt`);
|
|
1984
2059
|
}
|
|
1985
2060
|
}
|
|
@@ -2369,10 +2444,23 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
2369
2444
|
getDebugState(): Record<string, any> {
|
|
2370
2445
|
const screenText = sanitizeTerminalText(this.terminalScreen.getText());
|
|
2371
2446
|
const startupModal = this.startupParseGate ? this.runParseApproval(this.recentOutputBuffer) : null;
|
|
2372
|
-
const
|
|
2373
|
-
|
|
2447
|
+
const startupDetectedStatus = this.startupParseGate && !startupModal
|
|
2448
|
+
? this.runDetectStatus(this.recentOutputBuffer || screenText)
|
|
2449
|
+
: null;
|
|
2450
|
+
const effectiveReady = this.ready || !!startupModal || startupDetectedStatus === 'waiting_approval';
|
|
2374
2451
|
const parsedDebugState = this.getParsedDebugState();
|
|
2375
2452
|
const parsedMessages = Array.isArray(parsedDebugState?.messages) ? parsedDebugState.messages : [];
|
|
2453
|
+
let effectiveStatus = this.projectEffectiveStatus(startupModal);
|
|
2454
|
+
if (startupDetectedStatus === 'waiting_approval') {
|
|
2455
|
+
effectiveStatus = 'waiting_approval';
|
|
2456
|
+
}
|
|
2457
|
+
if (
|
|
2458
|
+
effectiveStatus === 'idle'
|
|
2459
|
+
&& parsedDebugState?.status === 'generating'
|
|
2460
|
+
&& !this.parsedStatusHasFinalAssistantMessage(parsedDebugState)
|
|
2461
|
+
) {
|
|
2462
|
+
effectiveStatus = 'generating';
|
|
2463
|
+
}
|
|
2376
2464
|
return {
|
|
2377
2465
|
type: this.cliType,
|
|
2378
2466
|
name: this.cliName,
|
|
@@ -15,6 +15,7 @@ export declare function buildCliParseInput(options: {
|
|
|
15
15
|
accumulatedRawBuffer: string;
|
|
16
16
|
recentOutputBuffer: string;
|
|
17
17
|
terminalScreenText: string;
|
|
18
|
+
workingDir?: string;
|
|
18
19
|
baseMessages: CliChatMessage[];
|
|
19
20
|
partialResponse: string;
|
|
20
21
|
isWaitingForResponse?: boolean;
|
|
@@ -35,6 +35,7 @@ export function buildCliParseInput(options: {
|
|
|
35
35
|
accumulatedRawBuffer: string;
|
|
36
36
|
recentOutputBuffer: string;
|
|
37
37
|
terminalScreenText: string;
|
|
38
|
+
workingDir?: string;
|
|
38
39
|
baseMessages: CliChatMessage[];
|
|
39
40
|
partialResponse: string;
|
|
40
41
|
isWaitingForResponse?: boolean;
|
|
@@ -46,6 +47,7 @@ export function buildCliParseInput(options: {
|
|
|
46
47
|
accumulatedRawBuffer,
|
|
47
48
|
recentOutputBuffer,
|
|
48
49
|
terminalScreenText,
|
|
50
|
+
workingDir,
|
|
49
51
|
baseMessages,
|
|
50
52
|
partialResponse,
|
|
51
53
|
isWaitingForResponse,
|
|
@@ -66,6 +68,8 @@ export function buildCliParseInput(options: {
|
|
|
66
68
|
rawBuffer,
|
|
67
69
|
recentBuffer,
|
|
68
70
|
screenText,
|
|
71
|
+
workspace: workingDir,
|
|
72
|
+
workingDir,
|
|
69
73
|
screen: buildCliScreenSnapshot(screenText),
|
|
70
74
|
bufferScreen: buildCliScreenSnapshot(buffer),
|
|
71
75
|
recentScreen: buildCliScreenSnapshot(recentBuffer),
|
|
@@ -36,7 +36,9 @@ export function resolveCliSpawnPlan(options: {
|
|
|
36
36
|
: spawnConfig.command;
|
|
37
37
|
const binaryPath = findBinary(configuredCommand);
|
|
38
38
|
const isWin = os.platform() === 'win32';
|
|
39
|
-
const allArgs = [...spawnConfig.args, ...extraArgs]
|
|
39
|
+
const allArgs = [...spawnConfig.args, ...extraArgs].map((arg) =>
|
|
40
|
+
typeof arg === 'string' ? arg.replace(/\{\{workingDir\}\}/g, workingDir) : arg,
|
|
41
|
+
);
|
|
40
42
|
|
|
41
43
|
let shellCmd: string;
|
|
42
44
|
let shellArgs: string[];
|
|
@@ -94,6 +94,8 @@ export interface CliScriptInput {
|
|
|
94
94
|
rawBuffer: string;
|
|
95
95
|
recentBuffer: string;
|
|
96
96
|
screenText: string;
|
|
97
|
+
workspace?: string;
|
|
98
|
+
workingDir?: string;
|
|
97
99
|
screen: CliScreenSnapshot;
|
|
98
100
|
bufferScreen: CliScreenSnapshot;
|
|
99
101
|
recentScreen: CliScreenSnapshot;
|
|
@@ -484,17 +486,25 @@ export function findBinary(name: string): string {
|
|
|
484
486
|
return path.isAbsolute(expanded) ? expanded : path.resolve(expanded);
|
|
485
487
|
}
|
|
486
488
|
const isWin = os.platform() === 'win32';
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
489
|
+
const paths = (process.env.PATH || '').split(path.delimiter);
|
|
490
|
+
const exes = isWin ? ['.exe', '.cmd', '.bat', ''] : [''];
|
|
491
|
+
|
|
492
|
+
for (const p of paths) {
|
|
493
|
+
if (!p) continue;
|
|
494
|
+
for (const ext of exes) {
|
|
495
|
+
const fullPath = path.join(p, trimmed + ext);
|
|
496
|
+
try {
|
|
497
|
+
const fs = require('fs');
|
|
498
|
+
if (fs.existsSync(fullPath)) {
|
|
499
|
+
const stat = fs.statSync(fullPath);
|
|
500
|
+
if (stat.isFile() && (isWin || (stat.mode & 0o111))) {
|
|
501
|
+
return fullPath;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
} catch { }
|
|
505
|
+
}
|
|
497
506
|
}
|
|
507
|
+
return isWin ? `${trimmed}.cmd` : trimmed;
|
|
498
508
|
}
|
|
499
509
|
|
|
500
510
|
export function isScriptBinary(binaryPath: string): boolean {
|