@adhdev/daemon-core 0.9.82-rc.2 → 0.9.82-rc.200
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/boot/daemon-lifecycle.d.ts +3 -0
- package/dist/boot/process-hardening.d.ts +50 -0
- package/dist/chat/source-machine.d.ts +166 -0
- package/dist/chat/source-resolver.d.ts +104 -0
- package/dist/chat/subscription-updates.d.ts +1 -0
- package/dist/cli-adapter-types.d.ts +23 -2
- package/dist/cli-adapters/cli-script-runner.d.ts +117 -0
- package/dist/cli-adapters/cli-state-engine.d.ts +178 -0
- package/dist/cli-adapters/provider-cli-adapter.d.ts +106 -63
- package/dist/cli-adapters/provider-cli-parse.d.ts +4 -0
- package/dist/cli-adapters/provider-cli-shared.d.ts +29 -0
- package/dist/cli-adapters/raw-terminal-io.d.ts +37 -0
- package/dist/commands/cli-manager.d.ts +2 -1
- package/dist/commands/handler.d.ts +110 -0
- package/dist/commands/mesh-coordinator.d.ts +85 -1
- package/dist/commands/router.d.ts +29 -1
- package/dist/config/chat-history.d.ts +9 -0
- package/dist/config/config.d.ts +5 -0
- package/dist/config/mesh-config.d.ts +72 -1
- package/dist/git/git-commands.d.ts +8 -1
- package/dist/git/git-status.d.ts +5 -0
- package/dist/git/git-types.d.ts +12 -0
- package/dist/index.d.ts +39 -10
- package/dist/index.js +29425 -12026
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +25700 -8378
- package/dist/index.mjs.map +1 -1
- package/dist/installer.d.ts +1 -4
- package/dist/ipc/local-ipc-server.d.ts +91 -0
- package/dist/launch.d.ts +1 -1
- package/dist/logging/async-batch-writer.d.ts +10 -0
- package/dist/mesh/contracts.d.ts +164 -0
- package/dist/mesh/coordinator-prompt.d.ts +30 -0
- package/dist/mesh/coordinator-registry.d.ts +59 -0
- package/dist/mesh/mesh-active-work.d.ts +90 -0
- package/dist/mesh/mesh-delivery-policy.d.ts +126 -0
- package/dist/mesh/mesh-events.d.ts +167 -6
- package/dist/mesh/mesh-fast-forward.d.ts +41 -0
- package/dist/mesh/mesh-host-ownership.d.ts +9 -0
- package/dist/mesh/mesh-ledger.d.ts +58 -1
- package/dist/mesh/mesh-refine-status.d.ts +26 -0
- package/dist/mesh/mesh-runtime-store.d.ts +149 -0
- package/dist/mesh/mesh-work-queue.d.ts +55 -6
- package/dist/mesh/preview-freshness.d.ts +18 -0
- package/dist/mesh/refine-config.d.ts +193 -0
- package/dist/mesh/worktree-bootstrap-config.d.ts +113 -0
- package/dist/providers/approval-utils.d.ts +13 -0
- package/dist/providers/chat-message-normalization.d.ts +1 -0
- package/dist/providers/cli-provider-instance.d.ts +39 -3
- package/dist/providers/contracts.d.ts +130 -6
- package/dist/providers/external-sources.d.ts +71 -0
- package/dist/providers/native-history/antigravity-cli-transcript.d.ts +100 -0
- package/dist/providers/native-history/claude-cli-transcript.d.ts +70 -0
- package/dist/providers/native-history/codex-cli-transcript.d.ts +73 -0
- package/dist/providers/native-history/dispatcher.d.ts +27 -0
- package/dist/providers/native-history/hermes-cli-transcript.d.ts +30 -0
- package/dist/providers/native-history/index.d.ts +13 -0
- package/dist/providers/provider-instance-manager.d.ts +12 -0
- package/dist/providers/provider-instance.d.ts +13 -1
- package/dist/providers/provider-loader.d.ts +26 -4
- package/dist/providers/provider-trust.d.ts +31 -0
- package/dist/providers/read-chat-contract.d.ts +29 -0
- package/dist/providers/sdk/v1/builders/acp/detect-status.d.ts +68 -0
- package/dist/providers/sdk/v1/builders/cli/detect-status.d.ts +85 -0
- package/dist/providers/sdk/v1/builders/cli/parse-approval-squash.d.ts +59 -0
- package/dist/providers/sdk/v1/builders/cli/parse-approval.d.ts +65 -0
- package/dist/providers/sdk/v1/builders/cli/parse-session.d.ts +91 -0
- package/dist/providers/sdk/v1/builders/cli/visible-region.d.ts +42 -0
- package/dist/providers/sdk/v1/fixture-tooling/format.d.ts +126 -0
- package/dist/providers/sdk/v1/fixture-tooling/index.d.ts +8 -0
- package/dist/providers/sdk/v1/fixture-tooling/replay.d.ts +38 -0
- package/dist/providers/sdk/v1/index.d.ts +30 -0
- package/dist/providers/sdk/v1/sandbox/README-design.d.ts +193 -0
- package/dist/providers/sdk/v1/sandbox/require-whitelist.d.ts +74 -0
- package/dist/providers/sdk/v1/sandbox/script-runner.d.ts +98 -0
- package/dist/providers/sdk/v1/types/cli/index.d.ts +277 -0
- package/dist/providers/sdk/v1/types/common/index.d.ts +203 -0
- package/dist/providers/sdk/v1/validators/index.d.ts +5 -0
- package/dist/providers/sdk/v1/validators/manifest.d.ts +40 -0
- package/dist/providers/sdk/v1/validators/taint.d.ts +52 -0
- package/dist/providers/spec/adapter.d.ts +60 -0
- package/dist/providers/spec/cli-adapter.d.ts +98 -0
- package/dist/providers/spec/driver.d.ts +190 -0
- package/dist/providers/spec/evaluator.d.ts +55 -0
- package/dist/providers/spec/loader.d.ts +14 -0
- package/dist/providers/spec/native-history-executor.d.ts +41 -0
- package/dist/providers/spec/route.d.ts +4 -0
- package/dist/providers/spec/schema.gen.d.ts +599 -0
- package/dist/providers/spec/types.d.ts +282 -0
- package/dist/providers/transcript-v2.d.ts +176 -0
- package/dist/providers/types/interactive-prompt.d.ts +48 -0
- package/dist/repo-mesh-types.d.ts +230 -1
- package/dist/sessions/registry.d.ts +3 -0
- package/dist/shared-types-extra.d.ts +1 -1
- package/dist/shared-types.d.ts +42 -1
- package/dist/status/normalize.d.ts +1 -1
- package/dist/status/normalize.js +1 -0
- package/dist/status/normalize.js.map +1 -1
- package/dist/status/normalize.mjs +1 -0
- package/dist/status/normalize.mjs.map +1 -1
- package/dist/status/reporter.d.ts +2 -0
- package/dist/status/snapshot.d.ts +1 -0
- package/dist/types.d.ts +5 -0
- package/package.json +7 -2
- package/src/agent-stream/poller.ts +2 -3
- package/src/boot/daemon-lifecycle.ts +22 -10
- package/src/boot/process-hardening.ts +89 -0
- package/src/chat/source-machine.ts +534 -0
- package/src/chat/source-resolver.ts +0 -0
- package/src/chat/subscription-updates.ts +20 -1
- package/src/cli-adapter-types.d.ts +1 -0
- package/src/cli-adapter-types.ts +22 -2
- package/src/cli-adapters/cli-script-runner.ts +421 -0
- package/src/cli-adapters/cli-state-engine.ts +1138 -0
- package/src/cli-adapters/provider-cli-adapter.d.ts +1 -1
- package/src/cli-adapters/provider-cli-adapter.ts +670 -1137
- package/src/cli-adapters/provider-cli-parse.d.ts +1 -0
- package/src/cli-adapters/provider-cli-parse.ts +13 -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 +59 -11
- package/src/cli-adapters/raw-terminal-io.ts +252 -0
- package/src/cli-adapters/terminal-backends/ghostty-vt-backend.ts +17 -1
- package/src/cli-adapters/terminal-backends/xterm-backend.ts +8 -1
- package/src/commands/chat-commands.ts +1787 -60
- package/src/commands/cli-manager.ts +283 -14
- package/src/commands/handler.ts +809 -2
- package/src/commands/mesh-coordinator.ts +331 -122
- package/src/commands/router.ts +3915 -546
- package/src/config/chat-history.ts +95 -24
- package/src/config/config.ts +12 -0
- package/src/config/mesh-config.ts +280 -2
- package/src/config/recent-activity.ts +8 -2
- package/src/daemon/dev-cli-debug.ts +10 -1
- package/src/detection/ide-detector.ts +26 -16
- package/src/git/git-commands.ts +38 -8
- package/src/git/git-status.ts +132 -12
- package/src/git/git-summary.ts +3 -0
- package/src/git/git-types.ts +13 -0
- package/src/git/git-worktree.ts +8 -1
- package/src/index.ts +127 -9
- package/src/installer.d.ts +1 -1
- package/src/installer.ts +8 -6
- package/src/ipc/local-ipc-server.ts +278 -0
- 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/contracts.ts +329 -0
- package/src/mesh/coordinator-prompt.ts +204 -30
- package/src/mesh/coordinator-registry.ts +121 -0
- package/src/mesh/mesh-active-work.ts +437 -0
- package/src/mesh/mesh-delivery-policy.ts +298 -0
- package/src/mesh/mesh-events.ts +1352 -109
- package/src/mesh/mesh-fast-forward.ts +438 -0
- package/src/mesh/mesh-host-ownership.ts +73 -0
- package/src/mesh/mesh-ledger.ts +457 -104
- package/src/mesh/mesh-refine-status.ts +144 -0
- package/src/mesh/mesh-runtime-store.ts +769 -0
- package/src/mesh/mesh-work-queue.ts +267 -159
- package/src/mesh/preview-freshness.ts +118 -0
- package/src/mesh/refine-config.ts +366 -0
- package/src/mesh/worktree-bootstrap-config.ts +247 -0
- package/src/providers/approval-utils.d.ts +4 -0
- package/src/providers/approval-utils.ts +47 -5
- package/src/providers/chat-message-normalization.ts +7 -12
- package/src/providers/cli-provider-instance.ts +806 -64
- package/src/providers/contracts.d.ts +55 -0
- package/src/providers/contracts.ts +141 -6
- package/src/providers/external-sources.ts +218 -0
- package/src/providers/ide-provider-instance.ts +19 -5
- package/src/providers/native-history/antigravity-cli-transcript.ts +643 -0
- package/src/providers/native-history/claude-cli-transcript.ts +396 -0
- package/src/providers/native-history/codex-cli-transcript.ts +419 -0
- package/src/providers/native-history/dispatcher.ts +340 -0
- package/src/providers/native-history/hermes-cli-transcript.ts +230 -0
- package/src/providers/native-history/index.ts +30 -0
- package/src/providers/provider-instance-manager.ts +30 -0
- package/src/providers/provider-instance.ts +10 -1
- package/src/providers/provider-loader.ts +582 -50
- package/src/providers/provider-schema.ts +87 -14
- package/src/providers/provider-trust.ts +114 -0
- package/src/providers/read-chat-contract.ts +76 -16
- package/src/providers/sdk/README.md +49 -0
- package/src/providers/sdk/v1/builders/acp/detect-status.ts +144 -0
- package/src/providers/sdk/v1/builders/cli/detect-status.ts +262 -0
- package/src/providers/sdk/v1/builders/cli/parse-approval-squash.ts +158 -0
- package/src/providers/sdk/v1/builders/cli/parse-approval.ts +250 -0
- package/src/providers/sdk/v1/builders/cli/parse-session.ts +276 -0
- package/src/providers/sdk/v1/builders/cli/visible-region.ts +143 -0
- package/src/providers/sdk/v1/fixture-tooling/format.ts +130 -0
- package/src/providers/sdk/v1/fixture-tooling/index.ts +22 -0
- package/src/providers/sdk/v1/fixture-tooling/replay.ts +352 -0
- package/src/providers/sdk/v1/index.ts +152 -0
- package/src/providers/sdk/v1/sandbox/README-design.ts +195 -0
- package/src/providers/sdk/v1/sandbox/require-whitelist.ts +472 -0
- package/src/providers/sdk/v1/sandbox/script-runner.ts +150 -0
- package/src/providers/sdk/v1/schemas/cli/provider.schema.json +504 -0
- package/src/providers/sdk/v1/schemas/primitives/acp-session-protocol-v1.json +131 -0
- package/src/providers/sdk/v1/schemas/primitives/native-history-codex-rollout-v1.json +66 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-approval-squash-v1.json +91 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-assistant-block-v1.json +91 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-cue-ordering-v1.json +47 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-dispatch-order-v1.json +32 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-footer-chrome-v1.json +42 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-index-finder-v1.json +27 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-modal-v1.json +125 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-prompt-marker-v1.json +45 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-session-id-extraction-v1.json +46 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-settled-prompt-v1.json +71 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-spinner-v1.json +83 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-transcript-pty-v1.json +83 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-visible-region-v1.json +57 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-welcome-screen-v1.json +35 -0
- package/src/providers/sdk/v1/types/cli/index.ts +365 -0
- package/src/providers/sdk/v1/types/common/index.ts +229 -0
- package/src/providers/sdk/v1/validators/index.ts +19 -0
- package/src/providers/sdk/v1/validators/manifest.ts +110 -0
- package/src/providers/sdk/v1/validators/taint.ts +309 -0
- package/src/providers/spec/adapter.ts +176 -0
- package/src/providers/spec/cli-adapter.ts +538 -0
- package/src/providers/spec/driver.ts +725 -0
- package/src/providers/spec/evaluator.ts +373 -0
- package/src/providers/spec/loader.ts +130 -0
- package/src/providers/spec/native-history-executor.ts +939 -0
- package/src/providers/spec/route.ts +51 -0
- package/src/providers/spec/schema.gen.ts +559 -0
- package/src/providers/spec/schema.json +237 -0
- package/src/providers/spec/types.ts +301 -0
- package/src/providers/transcript-v2.ts +567 -0
- package/src/providers/types/interactive-prompt.ts +425 -0
- package/src/providers/version-archive.ts +38 -20
- package/src/repo-mesh-types.ts +250 -1
- package/src/sessions/registry.ts +3 -0
- package/src/shared-types-extra.ts +1 -1
- package/src/shared-types.ts +45 -1
- package/src/status/builders.ts +24 -6
- package/src/status/normalize.ts +2 -0
- package/src/status/reporter.ts +15 -0
- package/src/status/snapshot.ts +84 -25
- package/src/system/host-memory.ts +29 -12
- package/src/types.ts +5 -0
- package/dist/mesh/mesh-sync.d.ts +0 -53
- package/src/mesh/mesh-sync.ts +0 -111
|
@@ -7,8 +7,10 @@
|
|
|
7
7
|
* Migrated from @adhdev/core — this is now the single source of truth.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
10
|
+
import { exec } from 'child_process';
|
|
11
|
+
import { promisify } from 'util';
|
|
12
|
+
const execAsync = promisify(exec);
|
|
13
|
+
import { existsSync, statSync } from 'fs';
|
|
12
14
|
import { platform, homedir } from 'os';
|
|
13
15
|
import * as path from 'path';
|
|
14
16
|
import type { ProviderLoader } from '../providers/provider-loader.js';
|
|
@@ -73,25 +75,33 @@ function findCliCommand(command: string): string | null {
|
|
|
73
75
|
const resolved = path.isAbsolute(candidate) ? candidate : path.resolve(candidate);
|
|
74
76
|
return existsSync(resolved) ? resolved : null;
|
|
75
77
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
78
|
+
const isWin = platform() === 'win32';
|
|
79
|
+
const paths = (process.env.PATH || '').split(isWin ? ';' : ':');
|
|
80
|
+
const exes = isWin ? ['.exe', '.cmd', '.bat', ''] : [''];
|
|
81
|
+
for (const p of paths) {
|
|
82
|
+
if (!p) continue;
|
|
83
|
+
for (const ext of exes) {
|
|
84
|
+
const fullPath = path.join(p, trimmed + ext);
|
|
85
|
+
try {
|
|
86
|
+
if (existsSync(fullPath)) {
|
|
87
|
+
const stat = statSync(fullPath);
|
|
88
|
+
if (stat.isFile() && (isWin || (stat.mode & 0o111))) {
|
|
89
|
+
return fullPath;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
} catch { }
|
|
93
|
+
}
|
|
84
94
|
}
|
|
95
|
+
return null;
|
|
85
96
|
}
|
|
86
97
|
|
|
87
|
-
function getIdeVersion(cliCommand: string): string | null {
|
|
98
|
+
async function getIdeVersion(cliCommand: string): Promise<string | null> {
|
|
88
99
|
try {
|
|
89
|
-
const
|
|
100
|
+
const { stdout } = await execAsync(`"${cliCommand}" --version`, {
|
|
90
101
|
encoding: 'utf-8',
|
|
91
102
|
timeout: 10000,
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
return result.split('\n')[0] || null;
|
|
103
|
+
});
|
|
104
|
+
return stdout.trim().split('\n')[0] || null;
|
|
95
105
|
} catch {
|
|
96
106
|
return null;
|
|
97
107
|
}
|
|
@@ -152,7 +162,7 @@ export async function detectIDEs(providerLoader?: ProviderLoader): Promise<IDEIn
|
|
|
152
162
|
const installed = os === 'darwin'
|
|
153
163
|
? !!(resolvedCli || appPath)
|
|
154
164
|
: !!resolvedCli;
|
|
155
|
-
const version = resolvedCli ? getIdeVersion(resolvedCli) : null;
|
|
165
|
+
const version = resolvedCli ? await getIdeVersion(resolvedCli) : null;
|
|
156
166
|
|
|
157
167
|
results.push({
|
|
158
168
|
id: def.id,
|
package/src/git/git-commands.ts
CHANGED
|
@@ -42,8 +42,12 @@ export interface GitLogResult extends GitRepoIdentity {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
export interface GitCheckpointResult extends GitRepoIdentity {
|
|
45
|
-
commit
|
|
45
|
+
commit?: string;
|
|
46
46
|
message: string;
|
|
47
|
+
status?: 'created' | 'skipped';
|
|
48
|
+
skipped?: boolean;
|
|
49
|
+
noop?: boolean;
|
|
50
|
+
reason?: 'nothing_to_commit';
|
|
47
51
|
lastCheckedAt: number;
|
|
48
52
|
}
|
|
49
53
|
|
|
@@ -62,7 +66,7 @@ export interface GitPushResult extends GitRepoIdentity {
|
|
|
62
66
|
}
|
|
63
67
|
|
|
64
68
|
export interface GitCommandServices {
|
|
65
|
-
getStatus?: (params: { workspace: string }) => Promise<GitRepoStatus> | GitRepoStatus;
|
|
69
|
+
getStatus?: (params: { workspace: string; refreshUpstream?: boolean; includeSubmodules?: boolean; submoduleIgnorePaths?: string[] }) => Promise<GitRepoStatus> | GitRepoStatus;
|
|
66
70
|
getDiffSummary?: (params: { workspace: string; staged?: boolean }) => Promise<GitDiffSummary> | GitDiffSummary;
|
|
67
71
|
getDiffFile?: (params: { workspace: string; path: string; staged?: boolean }) => Promise<GitFileDiff> | GitFileDiff;
|
|
68
72
|
createSnapshot?: (params: {
|
|
@@ -171,7 +175,7 @@ const defaultSnapshotStore = createGitSnapshotStore({
|
|
|
171
175
|
|
|
172
176
|
export function createDefaultGitCommandServices(): GitCommandServices {
|
|
173
177
|
return {
|
|
174
|
-
getStatus: ({ workspace }) => getGitRepoStatus(workspace),
|
|
178
|
+
getStatus: ({ workspace, refreshUpstream }) => getGitRepoStatus(workspace, { refreshUpstream }),
|
|
175
179
|
getDiffSummary: ({ workspace }) => getGitDiffSummary(workspace),
|
|
176
180
|
getDiffFile: ({ workspace, path: filePath }) => getGitFileDiff(workspace, filePath),
|
|
177
181
|
createSnapshot: ({ workspace, reason, sessionId, turnId }) => defaultSnapshotStore.create({
|
|
@@ -290,7 +294,16 @@ export async function handleGitCommand(
|
|
|
290
294
|
switch (command) {
|
|
291
295
|
case 'git_status': {
|
|
292
296
|
if (!services.getStatus) return serviceNotImplemented(command);
|
|
293
|
-
const
|
|
297
|
+
const submoduleIgnorePaths = Array.isArray(args?.submoduleIgnorePaths)
|
|
298
|
+
? args.submoduleIgnorePaths.filter((value: unknown): value is string => typeof value === 'string' && value.trim().length > 0)
|
|
299
|
+
: undefined;
|
|
300
|
+
const statusParams: { workspace: string; refreshUpstream?: boolean; includeSubmodules?: boolean; submoduleIgnorePaths?: string[] } = { workspace };
|
|
301
|
+
const refreshUpstream = optionalBoolean(args?.refreshUpstream);
|
|
302
|
+
const includeSubmodules = optionalBoolean(args?.includeSubmodules);
|
|
303
|
+
if (refreshUpstream !== undefined) statusParams.refreshUpstream = refreshUpstream;
|
|
304
|
+
if (includeSubmodules !== undefined) statusParams.includeSubmodules = includeSubmodules;
|
|
305
|
+
if (submoduleIgnorePaths && submoduleIgnorePaths.length > 0) statusParams.submoduleIgnorePaths = submoduleIgnorePaths;
|
|
306
|
+
const status = await runService(() => services.getStatus!(statusParams));
|
|
294
307
|
return 'success' in status ? status : { success: true, status };
|
|
295
308
|
}
|
|
296
309
|
|
|
@@ -442,6 +455,15 @@ async function gitCheckpoint(
|
|
|
442
455
|
if (statusResult.hasConflicts) {
|
|
443
456
|
throw new GitCommandError('conflict', 'Repository has conflicts — resolve before checkpointing');
|
|
444
457
|
}
|
|
458
|
+
const dirtySubmodules = (statusResult.submodules || []).filter(submodule => submodule.dirty);
|
|
459
|
+
if (dirtySubmodules.length > 0) {
|
|
460
|
+
const paths = dirtySubmodules.map(submodule => submodule.path).join(', ');
|
|
461
|
+
throw new GitCommandError(
|
|
462
|
+
'dirty_index_required',
|
|
463
|
+
`Repository has dirty submodules that must be checkpointed first: ${paths}. ` +
|
|
464
|
+
'Checkpoint or commit each dirty submodule, then checkpoint this repository to record gitlink changes.',
|
|
465
|
+
);
|
|
466
|
+
}
|
|
445
467
|
|
|
446
468
|
const addArgs = includeUntracked ? ['-A'] : ['-u'];
|
|
447
469
|
await runGit(repo, ['add', ...addArgs], { cwd: repoRoot });
|
|
@@ -455,10 +477,17 @@ async function gitCheckpoint(
|
|
|
455
477
|
} catch (err: any) {
|
|
456
478
|
const output = (err?.stdout || '') + (err?.stderr || '');
|
|
457
479
|
if (/nothing to commit/i.test(output)) {
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
480
|
+
return {
|
|
481
|
+
workspace: repo.workspace,
|
|
482
|
+
repoRoot,
|
|
483
|
+
isGitRepo: true,
|
|
484
|
+
message: fullMsg,
|
|
485
|
+
status: 'skipped',
|
|
486
|
+
skipped: true,
|
|
487
|
+
noop: true,
|
|
488
|
+
reason: 'nothing_to_commit',
|
|
489
|
+
lastCheckedAt: Date.now(),
|
|
490
|
+
};
|
|
462
491
|
}
|
|
463
492
|
throw err;
|
|
464
493
|
}
|
|
@@ -469,6 +498,7 @@ async function gitCheckpoint(
|
|
|
469
498
|
isGitRepo: true,
|
|
470
499
|
commit: commitSha,
|
|
471
500
|
message: fullMsg,
|
|
501
|
+
status: 'created',
|
|
472
502
|
lastCheckedAt: Date.now(),
|
|
473
503
|
};
|
|
474
504
|
}
|
package/src/git/git-status.ts
CHANGED
|
@@ -1,12 +1,25 @@
|
|
|
1
|
-
import type { GitRepoStatus, GitSubmoduleStatus } from './git-types.js';
|
|
1
|
+
import type { GitRepoStatus, GitSubmoduleStatus, GitUpstreamFreshness } from './git-types.js';
|
|
2
2
|
import { GitCommandError, resolveGitRepository, runGit } from './git-executor.js';
|
|
3
3
|
|
|
4
|
+
type ResolvedGitRepo = { workspace: string; repoRoot: string | null; isGitRepo: boolean };
|
|
5
|
+
|
|
4
6
|
export interface GitStatusOptions {
|
|
5
7
|
timeoutMs?: number;
|
|
6
8
|
/** When true, include submodule status in the result. Defaults to true. */
|
|
7
9
|
includeSubmodules?: boolean;
|
|
8
10
|
/** Optional filter to exclude specific submodule paths from status */
|
|
9
11
|
submoduleIgnorePaths?: string[];
|
|
12
|
+
/**
|
|
13
|
+
* When true, refresh the tracked remote before trusting ahead/behind.
|
|
14
|
+
* Callers should opt into this only for convergence-critical surfaces.
|
|
15
|
+
*/
|
|
16
|
+
refreshUpstream?: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface GitUpstreamProbe {
|
|
20
|
+
upstreamStatus: GitUpstreamFreshness;
|
|
21
|
+
upstreamFetchedAt?: number;
|
|
22
|
+
upstreamFetchError?: string;
|
|
10
23
|
}
|
|
11
24
|
|
|
12
25
|
export async function getGitRepoStatus(
|
|
@@ -18,8 +31,16 @@ export async function getGitRepoStatus(
|
|
|
18
31
|
|
|
19
32
|
try {
|
|
20
33
|
const repo = await resolveGitRepository(workspace, options);
|
|
21
|
-
|
|
22
|
-
|
|
34
|
+
let parsed = await readPorcelainStatus(repo, options);
|
|
35
|
+
let upstreamProbe: GitUpstreamProbe = getInitialUpstreamProbe(parsed);
|
|
36
|
+
|
|
37
|
+
if (options.refreshUpstream) {
|
|
38
|
+
upstreamProbe = await refreshTrackedUpstream(repo, parsed, options);
|
|
39
|
+
if (upstreamProbe.upstreamStatus === 'fresh') {
|
|
40
|
+
parsed = await readPorcelainStatus(repo, options);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
23
44
|
const head = await readHead(repo, options);
|
|
24
45
|
const stashCount = await readStashCount(repo, options);
|
|
25
46
|
|
|
@@ -27,6 +48,11 @@ export async function getGitRepoStatus(
|
|
|
27
48
|
if (includeSubmodules) {
|
|
28
49
|
submodules = await getSubmoduleStatuses(repo, options);
|
|
29
50
|
}
|
|
51
|
+
const submoduleDirty = (submodules || []).some(submodule => submodule.dirty || submodule.outOfSync || !!submodule.error);
|
|
52
|
+
const dirty = parsed.staged + parsed.modified + parsed.untracked + parsed.deleted + parsed.renamed > 0
|
|
53
|
+
|| parsed.conflictFiles.length > 0
|
|
54
|
+
|| stashCount > 0
|
|
55
|
+
|| submoduleDirty;
|
|
30
56
|
|
|
31
57
|
return {
|
|
32
58
|
workspace: repo.workspace,
|
|
@@ -36,6 +62,9 @@ export async function getGitRepoStatus(
|
|
|
36
62
|
headCommit: head.commit,
|
|
37
63
|
headMessage: head.message,
|
|
38
64
|
upstream: parsed.upstream,
|
|
65
|
+
upstreamStatus: parsed.upstream ? upstreamProbe.upstreamStatus : 'no_upstream',
|
|
66
|
+
upstreamFetchedAt: upstreamProbe.upstreamFetchedAt,
|
|
67
|
+
upstreamFetchError: upstreamProbe.upstreamFetchError,
|
|
39
68
|
ahead: parsed.ahead,
|
|
40
69
|
behind: parsed.behind,
|
|
41
70
|
staged: parsed.staged,
|
|
@@ -43,6 +72,7 @@ export async function getGitRepoStatus(
|
|
|
43
72
|
untracked: parsed.untracked,
|
|
44
73
|
deleted: parsed.deleted,
|
|
45
74
|
renamed: parsed.renamed,
|
|
75
|
+
dirty,
|
|
46
76
|
hasConflicts: parsed.conflictFiles.length > 0,
|
|
47
77
|
conflictFiles: parsed.conflictFiles,
|
|
48
78
|
stashCount,
|
|
@@ -74,6 +104,72 @@ interface ParsedPorcelainStatus {
|
|
|
74
104
|
conflictFiles: string[];
|
|
75
105
|
}
|
|
76
106
|
|
|
107
|
+
async function readPorcelainStatus(repo: ResolvedGitRepo, options: GitStatusOptions): Promise<ParsedPorcelainStatus> {
|
|
108
|
+
const statusOutput = await runGit(repo, ['status', '--porcelain=v2', '--branch'], options);
|
|
109
|
+
return parsePorcelainV2Status(statusOutput.stdout);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function getInitialUpstreamProbe(parsed: ParsedPorcelainStatus): GitUpstreamProbe {
|
|
113
|
+
return {
|
|
114
|
+
upstreamStatus: parsed.upstream ? 'unchecked' : 'no_upstream',
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async function refreshTrackedUpstream(
|
|
119
|
+
repo: ResolvedGitRepo,
|
|
120
|
+
parsed: ParsedPorcelainStatus,
|
|
121
|
+
options: GitStatusOptions,
|
|
122
|
+
): Promise<GitUpstreamProbe> {
|
|
123
|
+
if (!parsed.upstream || !parsed.branch) {
|
|
124
|
+
return { upstreamStatus: 'no_upstream' };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const remoteName = (await readBranchRemote(repo, parsed.branch, options)) ?? inferRemoteName(parsed.upstream);
|
|
128
|
+
if (!remoteName) {
|
|
129
|
+
return {
|
|
130
|
+
upstreamStatus: 'stale',
|
|
131
|
+
upstreamFetchError: `Unable to resolve remote for upstream '${parsed.upstream}'`,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
await runGit(repo, ['fetch', '--quiet', '--prune', '--no-tags', remoteName], options);
|
|
137
|
+
return {
|
|
138
|
+
upstreamStatus: 'fresh',
|
|
139
|
+
upstreamFetchedAt: Date.now(),
|
|
140
|
+
};
|
|
141
|
+
} catch (error) {
|
|
142
|
+
return {
|
|
143
|
+
upstreamStatus: 'stale',
|
|
144
|
+
upstreamFetchError: formatGitError(error),
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async function readBranchRemote(repo: ResolvedGitRepo, branch: string, options: GitStatusOptions): Promise<string | null> {
|
|
150
|
+
try {
|
|
151
|
+
const result = await runGit(repo, ['config', '--get', `branch.${branch}.remote`], options);
|
|
152
|
+
return result.stdout.trim() || null;
|
|
153
|
+
} catch {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function inferRemoteName(upstream: string): string | null {
|
|
159
|
+
const [remoteName] = upstream.split('/');
|
|
160
|
+
return remoteName?.trim() || null;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function formatGitError(error: unknown): string {
|
|
164
|
+
if (error instanceof GitCommandError) {
|
|
165
|
+
return error.stderr || error.message;
|
|
166
|
+
}
|
|
167
|
+
if (error instanceof Error) {
|
|
168
|
+
return error.message;
|
|
169
|
+
}
|
|
170
|
+
return String(error);
|
|
171
|
+
}
|
|
172
|
+
|
|
77
173
|
export function parsePorcelainV2Status(output: string): ParsedPorcelainStatus {
|
|
78
174
|
const parsed: ParsedPorcelainStatus = {
|
|
79
175
|
branch: null,
|
|
@@ -145,7 +241,7 @@ export function parsePorcelainV2Status(output: string): ParsedPorcelainStatus {
|
|
|
145
241
|
}
|
|
146
242
|
|
|
147
243
|
async function readHead(
|
|
148
|
-
repo:
|
|
244
|
+
repo: ResolvedGitRepo,
|
|
149
245
|
options: GitStatusOptions,
|
|
150
246
|
): Promise<{ commit: string | null; message: string | null }> {
|
|
151
247
|
try {
|
|
@@ -163,7 +259,7 @@ async function readHead(
|
|
|
163
259
|
}
|
|
164
260
|
|
|
165
261
|
async function readStashCount(
|
|
166
|
-
repo:
|
|
262
|
+
repo: ResolvedGitRepo,
|
|
167
263
|
options: GitStatusOptions,
|
|
168
264
|
): Promise<number> {
|
|
169
265
|
try {
|
|
@@ -187,6 +283,7 @@ function emptyStatus(workspace: string, lastCheckedAt: number, error: GitCommand
|
|
|
187
283
|
headCommit: null,
|
|
188
284
|
headMessage: null,
|
|
189
285
|
upstream: null,
|
|
286
|
+
upstreamStatus: 'unavailable',
|
|
190
287
|
ahead: 0,
|
|
191
288
|
behind: 0,
|
|
192
289
|
staged: 0,
|
|
@@ -194,6 +291,7 @@ function emptyStatus(workspace: string, lastCheckedAt: number, error: GitCommand
|
|
|
194
291
|
untracked: 0,
|
|
195
292
|
deleted: 0,
|
|
196
293
|
renamed: 0,
|
|
294
|
+
dirty: false,
|
|
197
295
|
hasConflicts: false,
|
|
198
296
|
conflictFiles: [],
|
|
199
297
|
stashCount: 0,
|
|
@@ -206,19 +304,41 @@ function emptyStatus(workspace: string, lastCheckedAt: number, error: GitCommand
|
|
|
206
304
|
// ─── Submodule Status ───────────────────────────
|
|
207
305
|
|
|
208
306
|
async function getSubmoduleStatuses(
|
|
209
|
-
repo:
|
|
307
|
+
repo: ResolvedGitRepo,
|
|
210
308
|
options: GitStatusOptions,
|
|
211
309
|
): Promise<GitSubmoduleStatus[]> {
|
|
212
310
|
if (!repo.repoRoot) return [];
|
|
213
311
|
|
|
214
312
|
try {
|
|
215
313
|
const result = await runGit(repo, ['submodule', 'status', '--recursive'], options);
|
|
216
|
-
|
|
314
|
+
const submodules = parseSubmoduleStatusOutput(result.stdout, repo.repoRoot, options.submoduleIgnorePaths);
|
|
315
|
+
await Promise.all(submodules.map(submodule => enrichSubmoduleWorktreeStatus(repo, submodule, options)));
|
|
316
|
+
return submodules;
|
|
217
317
|
} catch {
|
|
218
318
|
return [];
|
|
219
319
|
}
|
|
220
320
|
}
|
|
221
321
|
|
|
322
|
+
async function enrichSubmoduleWorktreeStatus(
|
|
323
|
+
repo: ResolvedGitRepo,
|
|
324
|
+
submodule: GitSubmoduleStatus,
|
|
325
|
+
options: GitStatusOptions,
|
|
326
|
+
): Promise<void> {
|
|
327
|
+
try {
|
|
328
|
+
const result = await runGit(repo, ['status', '--porcelain=v2', '--branch'], {
|
|
329
|
+
...options,
|
|
330
|
+
cwd: submodule.repoPath,
|
|
331
|
+
});
|
|
332
|
+
const parsed = parsePorcelainV2Status(result.stdout);
|
|
333
|
+
const dirty = parsed.staged + parsed.modified + parsed.untracked + parsed.deleted + parsed.renamed > 0
|
|
334
|
+
|| parsed.conflictFiles.length > 0;
|
|
335
|
+
submodule.dirty = submodule.dirty || dirty;
|
|
336
|
+
} catch (error) {
|
|
337
|
+
submodule.dirty = true;
|
|
338
|
+
submodule.error = formatGitError(error);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
222
342
|
function parseSubmoduleStatusOutput(
|
|
223
343
|
output: string,
|
|
224
344
|
repoRoot: string,
|
|
@@ -230,9 +350,9 @@ function parseSubmoduleStatusOutput(
|
|
|
230
350
|
for (const line of output.split('\n')) {
|
|
231
351
|
if (!line.trim()) continue;
|
|
232
352
|
|
|
233
|
-
// Format: [+- ]<commit> <path> (<branch>)
|
|
234
|
-
// - = out of sync,
|
|
235
|
-
const match = line.match(/^([
|
|
353
|
+
// Format: [+-U ]<commit> <path> (<branch>)
|
|
354
|
+
// - = not initialized, + = gitlink out of sync, U = conflict, ' ' = aligned.
|
|
355
|
+
const match = line.match(/^([\-+U\s])([0-9a-f]{40})\s+(\S+)(?:\s+\(([^)]+)\))?/);
|
|
236
356
|
if (!match) continue;
|
|
237
357
|
|
|
238
358
|
const prefix = match[1];
|
|
@@ -245,8 +365,8 @@ function parseSubmoduleStatusOutput(
|
|
|
245
365
|
path,
|
|
246
366
|
commit,
|
|
247
367
|
repoPath: repoRoot + '/' + path,
|
|
248
|
-
dirty: prefix === '
|
|
249
|
-
outOfSync: prefix === '-',
|
|
368
|
+
dirty: prefix === 'U',
|
|
369
|
+
outOfSync: prefix === '-' || prefix === '+',
|
|
250
370
|
lastCheckedAt: Date.now(),
|
|
251
371
|
});
|
|
252
372
|
}
|
package/src/git/git-summary.ts
CHANGED
|
@@ -22,6 +22,9 @@ export function createGitCompactSummary(status: GitRepoStatus, diffSummary?: Git
|
|
|
22
22
|
isGitRepo: status.isGitRepo,
|
|
23
23
|
repoRoot: status.repoRoot,
|
|
24
24
|
branch: status.branch,
|
|
25
|
+
upstreamStatus: status.upstreamStatus,
|
|
26
|
+
upstreamFetchedAt: status.upstreamFetchedAt,
|
|
27
|
+
upstreamFetchError: status.upstreamFetchError,
|
|
25
28
|
dirty:
|
|
26
29
|
status.staged > 0 ||
|
|
27
30
|
status.modified > 0 ||
|
package/src/git/git-types.ts
CHANGED
|
@@ -40,11 +40,19 @@ export interface GitSubmoduleStatus {
|
|
|
40
40
|
error?: string;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
export type GitUpstreamFreshness = 'fresh' | 'unchecked' | 'stale' | 'no_upstream' | 'unavailable';
|
|
44
|
+
|
|
43
45
|
export interface GitRepoStatus extends GitRepoIdentity {
|
|
44
46
|
branch: string | null;
|
|
45
47
|
headCommit: string | null;
|
|
46
48
|
headMessage: string | null;
|
|
47
49
|
upstream: string | null;
|
|
50
|
+
/** Whether ahead/behind was verified against a freshly fetched upstream ref. */
|
|
51
|
+
upstreamStatus: GitUpstreamFreshness;
|
|
52
|
+
/** Timestamp for the fetch that refreshed upstream refs when upstreamStatus === 'fresh'. */
|
|
53
|
+
upstreamFetchedAt?: number;
|
|
54
|
+
/** Error from the last refresh attempt when upstreamStatus === 'stale'. */
|
|
55
|
+
upstreamFetchError?: string;
|
|
48
56
|
ahead: number;
|
|
49
57
|
behind: number;
|
|
50
58
|
staged: number;
|
|
@@ -52,6 +60,8 @@ export interface GitRepoStatus extends GitRepoIdentity {
|
|
|
52
60
|
untracked: number;
|
|
53
61
|
deleted: number;
|
|
54
62
|
renamed: number;
|
|
63
|
+
/** Aggregate dirty flag including root worktree changes, conflicts, stash, and submodule drift. */
|
|
64
|
+
dirty: boolean;
|
|
55
65
|
hasConflicts: boolean;
|
|
56
66
|
conflictFiles: string[];
|
|
57
67
|
stashCount: number;
|
|
@@ -134,6 +144,9 @@ export interface GitCompactSummary {
|
|
|
134
144
|
isGitRepo: boolean;
|
|
135
145
|
repoRoot: string | null;
|
|
136
146
|
branch: string | null;
|
|
147
|
+
upstreamStatus: GitUpstreamFreshness;
|
|
148
|
+
upstreamFetchedAt?: number;
|
|
149
|
+
upstreamFetchError?: string;
|
|
137
150
|
dirty: boolean;
|
|
138
151
|
changedFiles: number;
|
|
139
152
|
ahead: number;
|
package/src/git/git-worktree.ts
CHANGED
|
@@ -116,8 +116,11 @@ export async function createWorktree(opts: WorktreeCreateOptions): Promise<Workt
|
|
|
116
116
|
});
|
|
117
117
|
} catch (error: any) {
|
|
118
118
|
const stderr = typeof error.stderr === 'string' ? error.stderr : '';
|
|
119
|
-
// Clean error messages for common failures
|
|
120
119
|
if (/already exists/i.test(stderr)) {
|
|
120
|
+
// Distinguish directory-collision (TOCTOU race) from branch-already-exists
|
|
121
|
+
if (existsSync(targetDir)) {
|
|
122
|
+
throw new Error(`Worktree target directory was created concurrently: ${targetDir}`);
|
|
123
|
+
}
|
|
121
124
|
throw new Error(`Branch '${branch}' already exists or is checked out in another worktree`);
|
|
122
125
|
}
|
|
123
126
|
throw new Error(`git worktree add failed: ${stderr.trim() || error.message}`);
|
|
@@ -170,6 +173,10 @@ export async function removeWorktree(repoRoot: string, worktreePath: string, opt
|
|
|
170
173
|
const stdout = typeof error.stdout === 'string' ? error.stdout : '';
|
|
171
174
|
const detail = `${stderr}\n${stdout}\n${error.message || ''}`;
|
|
172
175
|
if (opts.allowSubmoduleForceFallback && SUBMODULE_WORKTREE_REMOVE_RE.test(detail)) {
|
|
176
|
+
process.stderr.write(
|
|
177
|
+
`[adhdev-mesh] WARNING: git worktree remove --force fallback for submodule worktree '${worktreePath}'. `
|
|
178
|
+
+ `Any uncommitted changes inside submodules will be lost.\n`,
|
|
179
|
+
);
|
|
173
180
|
try {
|
|
174
181
|
await execFileAsync('git', ['worktree', 'remove', '--force', worktreePath], {
|
|
175
182
|
cwd: repoRoot,
|