@ikarmic/forge-core 5.0.0 → 5.1.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/README.md +1 -1
- package/dist/agent/agent.d.ts +8 -0
- package/dist/agent/agent.js +148 -24
- package/dist/agent/background.d.ts +58 -0
- package/dist/agent/background.js +139 -0
- package/dist/agent/enhance-prompt.d.ts +3 -1
- package/dist/agent/enhance-prompt.js +6 -2
- package/dist/agent/goal.d.ts +54 -0
- package/dist/agent/goal.js +65 -0
- package/dist/agent/prompts.d.ts +25 -1
- package/dist/agent/prompts.js +61 -17
- package/dist/agents/agent-files.d.ts +43 -0
- package/dist/agents/agent-files.js +109 -0
- package/dist/checkpoints/store.js +5 -1
- package/dist/commands/custom-commands.d.ts +9 -4
- package/dist/commands/custom-commands.js +13 -2
- package/dist/config-file.d.ts +43 -0
- package/dist/config-file.js +249 -5
- package/dist/config-merge.d.ts +13 -0
- package/dist/config-merge.js +67 -0
- package/dist/config-schema.d.ts +340 -0
- package/dist/config-schema.js +126 -0
- package/dist/config.js +2 -1
- package/dist/context/compaction.js +4 -0
- package/dist/context/mask.js +1 -1
- package/dist/custom-providers.d.ts +95 -0
- package/dist/custom-providers.js +120 -0
- package/dist/git/auto-commit.d.ts +2 -2
- package/dist/git/auto-commit.js +5 -4
- package/dist/github/github-tools.d.ts +2 -2
- package/dist/hooks/runner.js +3 -2
- package/dist/import/claude-code.d.ts +39 -0
- package/dist/import/claude-code.js +254 -0
- package/dist/index.d.ts +34 -5
- package/dist/index.js +27 -4
- package/dist/journey/answers-block.d.ts +17 -0
- package/dist/journey/answers-block.js +15 -0
- package/dist/journey/index.d.ts +9 -0
- package/dist/journey/index.js +8 -0
- package/dist/journey/journey.d.ts +13 -0
- package/dist/journey/journey.js +40 -0
- package/dist/journey/offer.d.ts +25 -0
- package/dist/journey/offer.js +48 -0
- package/dist/journey/questions.d.ts +23 -0
- package/dist/journey/questions.js +49 -0
- package/dist/journey/stages.d.ts +13 -0
- package/dist/journey/stages.js +107 -0
- package/dist/journey/survey.d.ts +10 -0
- package/dist/journey/survey.js +203 -0
- package/dist/journey/trigger.d.ts +19 -0
- package/dist/journey/trigger.js +79 -0
- package/dist/journey/types.d.ts +72 -0
- package/dist/journey/types.js +1 -0
- package/dist/lsp/client.d.ts +5 -0
- package/dist/lsp/client.js +39 -3
- package/dist/mcp/index.d.ts +5 -1
- package/dist/mcp/index.js +3 -1
- package/dist/mcp/manager.d.ts +17 -3
- package/dist/mcp/manager.js +27 -2
- package/dist/mcp/marketplace.d.ts +2 -0
- package/dist/mcp/marketplace.js +10 -4
- package/dist/mcp/mcp-client.d.ts +12 -2
- package/dist/mcp/mcp-client.js +74 -17
- package/dist/mcp/mcp-config.d.ts +2 -0
- package/dist/mcp/mcp-config.js +1 -0
- package/dist/mcp/oauth.d.ts +55 -0
- package/dist/mcp/oauth.js +197 -0
- package/dist/mcp/resource-tools.d.ts +8 -0
- package/dist/mcp/resource-tools.js +56 -0
- package/dist/memory/session-digest.d.ts +12 -0
- package/dist/memory/session-digest.js +47 -0
- package/dist/memory/stale.js +2 -1
- package/dist/model-alias.d.ts +18 -2
- package/dist/model-alias.js +27 -3
- package/dist/model-catalog.js +0 -1
- package/dist/model-list.js +2 -1
- package/dist/model-overrides.d.ts +30 -0
- package/dist/model-overrides.js +48 -0
- package/dist/orchestrator/specialists-registry.js +2 -3
- package/dist/orchestrator/specialists.js +4 -4
- package/dist/policy/ca-certs.d.ts +8 -0
- package/dist/policy/ca-certs.js +30 -0
- package/dist/policy/proxy.d.ts +10 -3
- package/dist/policy/proxy.js +33 -6
- package/dist/pricing.js +4 -0
- package/dist/providers.d.ts +7 -0
- package/dist/providers.js +39 -5
- package/dist/semantic/embedder.d.ts +15 -0
- package/dist/semantic/embedder.js +25 -0
- package/dist/semantic/semantic-index.d.ts +10 -2
- package/dist/semantic/semantic-index.js +16 -7
- package/dist/sessions/session-store.js +2 -1
- package/dist/skills/builtin.d.ts +2 -0
- package/dist/skills/builtin.js +68 -0
- package/dist/skills/index.d.ts +2 -0
- package/dist/skills/index.js +2 -0
- package/dist/skills/loader.d.ts +10 -3
- package/dist/skills/loader.js +20 -7
- package/dist/skills/marketplace.js +2 -1
- package/dist/skills/remote.d.ts +14 -0
- package/dist/skills/remote.js +131 -0
- package/dist/skills/types.d.ts +10 -0
- package/dist/specs/generate.d.ts +18 -9
- package/dist/specs/generate.js +174 -14
- package/dist/specs/index.d.ts +2 -2
- package/dist/specs/index.js +1 -1
- package/dist/specs/store.d.ts +14 -2
- package/dist/specs/store.js +26 -1
- package/dist/specs/types.d.ts +11 -0
- package/dist/steering/extra-instructions.d.ts +8 -0
- package/dist/steering/extra-instructions.js +104 -0
- package/dist/steering/folder-instructions.d.ts +8 -0
- package/dist/steering/folder-instructions.js +28 -0
- package/dist/steering/select.js +2 -1
- package/dist/timed-fetch.d.ts +12 -0
- package/dist/timed-fetch.js +83 -0
- package/dist/tools/apply-patch.d.ts +52 -0
- package/dist/tools/apply-patch.js +197 -0
- package/dist/tools/ask-user.d.ts +27 -0
- package/dist/tools/ask-user.js +60 -0
- package/dist/tools/browser-tools.d.ts +6 -6
- package/dist/tools/browser-tools.js +3 -13
- package/dist/tools/codebase-search.d.ts +4 -2
- package/dist/tools/codebase-search.js +2 -2
- package/dist/tools/edit-file.js +5 -3
- package/dist/tools/fetch-url.d.ts +7 -0
- package/dist/tools/fetch-url.js +52 -19
- package/dist/tools/find-symbol.d.ts +2 -2
- package/dist/tools/ignore-rules.js +2 -1
- package/dist/tools/index.d.ts +5 -1
- package/dist/tools/index.js +10 -1
- package/dist/tools/memory.d.ts +6 -6
- package/dist/tools/notebook.d.ts +2 -2
- package/dist/tools/permission-rules.d.ts +32 -0
- package/dist/tools/permission-rules.js +182 -0
- package/dist/tools/read-file.d.ts +5 -0
- package/dist/tools/read-file.js +12 -2
- package/dist/tools/read-only.d.ts +1 -1
- package/dist/tools/read-only.js +2 -0
- package/dist/tools/run-command.d.ts +4 -2
- package/dist/tools/run-command.js +12 -8
- package/dist/tools/sandbox-loader.js +3 -13
- package/dist/tools/search-files.d.ts +8 -2
- package/dist/tools/search-files.js +43 -0
- package/dist/tools/shared.d.ts +5 -0
- package/dist/tools/shared.js +30 -2
- package/dist/tools/shell-env.d.ts +7 -0
- package/dist/tools/shell-env.js +9 -0
- package/dist/tools/sub-agent-wait.d.ts +29 -0
- package/dist/tools/sub-agent-wait.js +60 -0
- package/dist/tools/sub-agent.d.ts +50 -4
- package/dist/tools/sub-agent.js +213 -125
- package/dist/tools/text-encoding.d.ts +10 -0
- package/dist/tools/text-encoding.js +40 -0
- package/dist/tools/todo.d.ts +36 -0
- package/dist/tools/todo.js +42 -0
- package/dist/tools/write-file.js +15 -9
- package/dist/tools/write-tools.d.ts +12 -0
- package/dist/tools/write-tools.js +18 -0
- package/dist/types.d.ts +98 -0
- package/dist/worktree/worktree.d.ts +3 -0
- package/dist/worktree/worktree.js +22 -0
- package/package.json +1 -1
package/dist/types.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { ModelMessage, LanguageModel } from 'ai';
|
|
2
2
|
import type { z } from 'zod';
|
|
3
|
+
import type { BackgroundAgents } from './agent/background.js';
|
|
3
4
|
import type { Attachment } from './attachments.js';
|
|
5
|
+
import type { Answer } from './journey/types.js';
|
|
4
6
|
import type { SkillRegistry } from './skills/types.js';
|
|
5
7
|
/**
|
|
6
8
|
* The summarizer takes a chunk of messages and returns a structured
|
|
@@ -20,6 +22,11 @@ export type ProviderName = 'openai' | 'anthropic' | 'google' | 'ollama' | 'openr
|
|
|
20
22
|
export interface ModelConfig {
|
|
21
23
|
provider: ProviderName;
|
|
22
24
|
model: string;
|
|
25
|
+
/**
|
|
26
|
+
* A named provider (preset or `customProviders`) resolved to where the call goes. Set together with
|
|
27
|
+
* `provider: 'openai-compatible'`; `createLanguageModel` then ignores the OPENAI_COMPATIBLE_* environment.
|
|
28
|
+
*/
|
|
29
|
+
endpoint?: import('./custom-providers.js').ProviderEndpoint;
|
|
23
30
|
/**
|
|
24
31
|
* Reasoning effort for models that support it. Passed straight through
|
|
25
32
|
* to the AI SDK's own unified `reasoning` call option: no per-provider
|
|
@@ -27,6 +34,8 @@ export interface ModelConfig {
|
|
|
27
34
|
* behavior, unchanged from today.
|
|
28
35
|
*/
|
|
29
36
|
reasoningEffort?: 'none' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh';
|
|
37
|
+
/** Sampling temperature (0 to 2); omitted = the provider's default. Set by an agent file. */
|
|
38
|
+
temperature?: number;
|
|
30
39
|
}
|
|
31
40
|
/**
|
|
32
41
|
* A tool definition as the agent sees it: a name, a Zod parameter schema, and
|
|
@@ -34,6 +43,18 @@ export interface ModelConfig {
|
|
|
34
43
|
* so it can decide whether to expose the `execute` function (autonomous mode)
|
|
35
44
|
* or strip it (interactive mode where approval is required).
|
|
36
45
|
*/
|
|
46
|
+
/** A question for the person (`ask_user`). */
|
|
47
|
+
export interface UserQuestion {
|
|
48
|
+
question: string;
|
|
49
|
+
options?: string[];
|
|
50
|
+
allowCustom?: boolean;
|
|
51
|
+
}
|
|
52
|
+
/** One step of the agent's checklist (`todo_write`). */
|
|
53
|
+
export interface TodoItem {
|
|
54
|
+
id: string;
|
|
55
|
+
content: string;
|
|
56
|
+
status: 'pending' | 'in_progress' | 'completed';
|
|
57
|
+
}
|
|
37
58
|
export interface ToolDefinition<TArgs = unknown, TResult = unknown> {
|
|
38
59
|
name: string;
|
|
39
60
|
description: string;
|
|
@@ -44,6 +65,8 @@ export interface ToolDefinition<TArgs = unknown, TResult = unknown> {
|
|
|
44
65
|
* approval even in `--auto` mode unless the user passes `--yes-destructive`.
|
|
45
66
|
*/
|
|
46
67
|
destructive?: boolean;
|
|
68
|
+
/** True for tools that only change the session's own bookkeeping (the todo list): never asked about, whatever the mode. */
|
|
69
|
+
noApproval?: boolean;
|
|
47
70
|
/**
|
|
48
71
|
* Optional text shown in the approval prompt so the user sees what the call
|
|
49
72
|
* will change (for example a diff). Failures are swallowed: no preview, still asked.
|
|
@@ -84,8 +107,37 @@ export interface ToolContext {
|
|
|
84
107
|
* the default `child_process` runner.
|
|
85
108
|
*/
|
|
86
109
|
commandRunner?: CommandRunner;
|
|
110
|
+
/** The model sub-agents use instead of the parent's (`AgentConfig.subAgentModel`). */
|
|
111
|
+
subAgentModel?: ModelConfig;
|
|
112
|
+
/** Folder `AGENTS.md` paths already shown in this run; `read_file` adds each folder's file once. */
|
|
113
|
+
shownFolderInstructions?: Set<string>;
|
|
114
|
+
/** Longest output `run_command` returns per stream before cutting the middle (`AgentConfig.maxToolOutputChars`). */
|
|
115
|
+
maxOutputChars?: number;
|
|
116
|
+
/** Asks the person a question (`ask_user`); absent when no surface can answer. */
|
|
117
|
+
askUser?: (question: UserQuestion, signal?: AbortSignal) => Promise<string>;
|
|
118
|
+
/**
|
|
119
|
+
* How many questions this run may still ask (a guided journey's interview
|
|
120
|
+
* budget). Mutated in place as questions are asked, and handed to a child
|
|
121
|
+
* agent so a child cannot start a fresh four (RB-14). Absent means no limit.
|
|
122
|
+
*/
|
|
123
|
+
askBudget?: {
|
|
124
|
+
max: number;
|
|
125
|
+
used: number;
|
|
126
|
+
};
|
|
127
|
+
/** The run's todo list. `set` also tells the UI (`todo_updated`). Supplied by the Agent. */
|
|
128
|
+
todos?: {
|
|
129
|
+
get(): TodoItem[];
|
|
130
|
+
set(todos: TodoItem[]): void;
|
|
131
|
+
};
|
|
87
132
|
/** The run's abort signal; long-running tools (run_command) stop when it fires. */
|
|
88
133
|
abortSignal?: AbortSignal;
|
|
134
|
+
/** The tools the parent runs without asking; a sub-agent starts from the same list (it has no one to ask). */
|
|
135
|
+
autoApproveTools?: readonly string[];
|
|
136
|
+
/** The parent's permission rules and tool sandbox, handed to a sub-agent so it cannot step around them. */
|
|
137
|
+
permissionRules?: import('./tools/permission-rules.js').PermissionConfig;
|
|
138
|
+
toolSandbox?: ToolSandbox;
|
|
139
|
+
/** Where `sub_agent { background: true }` runs go; absent where no surface can show them (the child then runs to completion as usual). */
|
|
140
|
+
background?: BackgroundAgents;
|
|
89
141
|
/** Longest body `memory_write` accepts, in characters; absent = the default of 1200. */
|
|
90
142
|
memoryMaxBodyChars?: number;
|
|
91
143
|
/** A command that outlives its timeout must be stopped, not left running (verification checks; a dev server must not be). */
|
|
@@ -233,6 +285,9 @@ export type AgentEvent = {
|
|
|
233
285
|
name: string;
|
|
234
286
|
id: string;
|
|
235
287
|
args: unknown;
|
|
288
|
+
} | {
|
|
289
|
+
type: 'todo_updated';
|
|
290
|
+
todos: TodoItem[];
|
|
236
291
|
} | {
|
|
237
292
|
type: 'iteration';
|
|
238
293
|
iteration: number;
|
|
@@ -514,11 +569,24 @@ export interface AgentConfig {
|
|
|
514
569
|
* working directory into the system prompt.
|
|
515
570
|
*/
|
|
516
571
|
disableProjectInstructions?: boolean;
|
|
572
|
+
/** Extra instruction sources (files, globs, URLs) from the user's `instructions` setting. */
|
|
573
|
+
extraInstructions?: string[];
|
|
574
|
+
/** What connected MCP servers say about using them (see `mcpInstructionsBlock`); added to the system prompt as it is. */
|
|
575
|
+
mcpInstructions?: string;
|
|
576
|
+
/** Longest `run_command` output per stream, in characters. Default 200000. */
|
|
577
|
+
maxToolOutputChars?: number;
|
|
517
578
|
/**
|
|
518
579
|
* A ready-made `<project-commands>` block (see `formatProjectCommandsBlock`) telling the model how
|
|
519
580
|
* this project is checked and built. Added to the system prompt after the project instructions.
|
|
520
581
|
*/
|
|
521
582
|
projectCommands?: string;
|
|
583
|
+
/**
|
|
584
|
+
* Answers the person gave in a guided journey's interview, rendered into the
|
|
585
|
+
* system prompt as a `<journey-answers>` block. The interview runs before the
|
|
586
|
+
* first agent run, because the blocks are assembled only on a session's first
|
|
587
|
+
* turn (see `buildInitialMessages`), so answers that arrive later cannot enter.
|
|
588
|
+
*/
|
|
589
|
+
journeyAnswers?: Answer[];
|
|
522
590
|
/**
|
|
523
591
|
* Override the byte budget for memory injected into the system prompt.
|
|
524
592
|
* Default 8 KB. Larger budgets use more context window but surface more
|
|
@@ -609,6 +677,9 @@ export interface AgentConfig {
|
|
|
609
677
|
*/
|
|
610
678
|
semanticSearch?: {
|
|
611
679
|
embedder?: import('./semantic/embedder.js').Embedder;
|
|
680
|
+
extensions?: string[];
|
|
681
|
+
minScore?: number;
|
|
682
|
+
defaultLimit?: number;
|
|
612
683
|
};
|
|
613
684
|
/**
|
|
614
685
|
* After the model finishes a run that edited files, run these commands and
|
|
@@ -625,6 +696,13 @@ export interface AgentConfig {
|
|
|
625
696
|
* "a destructive tool is never auto-approved"; see `isListedCommand`.
|
|
626
697
|
*/
|
|
627
698
|
autoApproveCommands?: string[];
|
|
699
|
+
/**
|
|
700
|
+
* Ordered permission rules (`read`, `edit`, `bash`, `webfetch`, `websearch`, `task`, `external_directory`, `*`):
|
|
701
|
+
* the last matching rule wins; `deny` blocks the call, `ask` always prompts (even in `auto` mode), `allow` runs it
|
|
702
|
+
* without a prompt. `.env` reads and commands that reach outside `cwd` prompt unless a rule says otherwise.
|
|
703
|
+
* See tools/permission-rules.ts.
|
|
704
|
+
*/
|
|
705
|
+
permissionRules?: import('./tools/permission-rules.js').PermissionConfig;
|
|
628
706
|
/**
|
|
629
707
|
* Put a token-budgeted outline of the codebase (files and their top symbol
|
|
630
708
|
* signatures, ranked by how much other code depends on them) in the system prompt.
|
|
@@ -669,6 +747,26 @@ export interface AgentConfig {
|
|
|
669
747
|
modelRouter?: ModelRouter;
|
|
670
748
|
/** Replace how `run_command` executes. See {@link CommandRunner}. */
|
|
671
749
|
commandRunner?: CommandRunner;
|
|
750
|
+
/**
|
|
751
|
+
* Answers `ask_user`. When absent, or when `auto` is on, the model is not offered the tool.
|
|
752
|
+
*/
|
|
753
|
+
askUser?: (question: UserQuestion, signal?: AbortSignal) => Promise<string>;
|
|
754
|
+
/**
|
|
755
|
+
* How many `ask_user` questions this run may ask. Reaches tools as
|
|
756
|
+
* `ToolContext.askBudget` and is handed down to sub-agents. Absent means no limit.
|
|
757
|
+
*/
|
|
758
|
+
askBudget?: {
|
|
759
|
+
max: number;
|
|
760
|
+
used: number;
|
|
761
|
+
};
|
|
762
|
+
/** The model `sub_agent` children run on; the parent's model when unset. */
|
|
763
|
+
subAgentModel?: ModelConfig;
|
|
764
|
+
/** Where background sub-agents are kept between messages. Without it `sub_agent { background: true }` runs the child to completion. */
|
|
765
|
+
backgroundAgents?: BackgroundAgents;
|
|
766
|
+
/** The todo list a run starts with (the chat keeps it between turns). */
|
|
767
|
+
initialTodos?: TodoItem[];
|
|
768
|
+
/** Never offer `ask_user`, even when `askUser` is set (a goal run must not stop to ask). */
|
|
769
|
+
disableAskUser?: boolean;
|
|
672
770
|
/** Custom approval callback. If absent, uses an internal yes/no gate. */
|
|
673
771
|
approve?: (event: ApprovalRequest) => Promise<ApprovalDecision> | ApprovalDecision;
|
|
674
772
|
/** Abort signal. */
|
|
@@ -237,6 +237,7 @@ export declare class WorktreeManager {
|
|
|
237
237
|
* clean up so we don't leak orphan directories.
|
|
238
238
|
*/
|
|
239
239
|
create(opts?: CreateOptions): Promise<WorktreeInfo>;
|
|
240
|
+
private createNow;
|
|
240
241
|
/**
|
|
241
242
|
* Remove a worktree previously created by this manager. By default
|
|
242
243
|
* refuses if the worktree is dirty (uncommitted changes); pass
|
|
@@ -252,6 +253,7 @@ export declare class WorktreeManager {
|
|
|
252
253
|
* outcome (e.g. emit "removed worktree + branch X" on the CLI).
|
|
253
254
|
*/
|
|
254
255
|
remove(worktreePath: string, opts?: RemoveOptions): Promise<RemoveResult>;
|
|
256
|
+
private removeNow;
|
|
255
257
|
/**
|
|
256
258
|
* Best-effort cleanup of every worktree this manager created. Used
|
|
257
259
|
* by the sub_agent tool's finally block so a child crash doesn't
|
|
@@ -270,6 +272,7 @@ export declare class WorktreeManager {
|
|
|
270
272
|
* leak.
|
|
271
273
|
*/
|
|
272
274
|
pruneStale(): Promise<number>;
|
|
275
|
+
private pruneStaleNow;
|
|
273
276
|
/**
|
|
274
277
|
* List worktrees under our `.forge/worktrees/` directory. Returns
|
|
275
278
|
* the empty array when none exist or git isn't available.
|
|
@@ -284,6 +284,9 @@ export class WorktreeManager {
|
|
|
284
284
|
* clean up so we don't leak orphan directories.
|
|
285
285
|
*/
|
|
286
286
|
async create(opts = {}) {
|
|
287
|
+
return onRepoQueue(this.repoRoot, () => this.createNow(opts));
|
|
288
|
+
}
|
|
289
|
+
async createNow(opts) {
|
|
287
290
|
// Generate a label + branch name first so we can pass them to git.
|
|
288
291
|
const label = opts.label ? sanitizeLabel(opts.label) : randomLabel();
|
|
289
292
|
const branch = opts.branchName ?? `forge/${label}-${await shortSha(this.repoRoot)}`;
|
|
@@ -341,6 +344,9 @@ export class WorktreeManager {
|
|
|
341
344
|
* outcome (e.g. emit "removed worktree + branch X" on the CLI).
|
|
342
345
|
*/
|
|
343
346
|
async remove(worktreePath, opts = {}) {
|
|
347
|
+
return onRepoQueue(this.repoRoot, () => this.removeNow(worktreePath, opts));
|
|
348
|
+
}
|
|
349
|
+
async removeNow(worktreePath, opts) {
|
|
344
350
|
const resolved = path.resolve(worktreePath);
|
|
345
351
|
const normalizedRoot = path.resolve(this.worktreesRoot);
|
|
346
352
|
if (!resolved.startsWith(normalizedRoot + path.sep) && resolved !== normalizedRoot) {
|
|
@@ -395,6 +401,9 @@ export class WorktreeManager {
|
|
|
395
401
|
* leak.
|
|
396
402
|
*/
|
|
397
403
|
async pruneStale() {
|
|
404
|
+
return onRepoQueue(this.repoRoot, () => this.pruneStaleNow());
|
|
405
|
+
}
|
|
406
|
+
async pruneStaleNow() {
|
|
398
407
|
let pruned = 0;
|
|
399
408
|
try {
|
|
400
409
|
const { stdout } = await runGit(this.repoRoot, [
|
|
@@ -626,6 +635,19 @@ export function canonicalPath(p) {
|
|
|
626
635
|
* with the stderr message in the Error. We use `execFile` (not `exec`)
|
|
627
636
|
* so user-controlled input can't shell-inject.
|
|
628
637
|
*/
|
|
638
|
+
const repoQueues = new Map();
|
|
639
|
+
/**
|
|
640
|
+
* Run `work` after every earlier queued worktree change of the same repository has finished. Parallel steps create
|
|
641
|
+
* and remove worktrees at the same moment, and two `git worktree add` / `remove` calls on one repository fight over
|
|
642
|
+
* git's lock files; one of them then fails at random, mostly under load. The agents still run in parallel: only these
|
|
643
|
+
* short git changes take turns.
|
|
644
|
+
*/
|
|
645
|
+
function onRepoQueue(repoRoot, work) {
|
|
646
|
+
const key = canonicalPath(repoRoot);
|
|
647
|
+
const next = (repoQueues.get(key) ?? Promise.resolve()).then(work, work);
|
|
648
|
+
repoQueues.set(key, next.catch(() => undefined));
|
|
649
|
+
return next;
|
|
650
|
+
}
|
|
629
651
|
async function runGit(cwd, args) {
|
|
630
652
|
return await execFileAsync('git', args, { cwd, maxBuffer: 16 * 1024 * 1024 });
|
|
631
653
|
}
|
package/package.json
CHANGED