@duckmind/dm-darwin-x64 0.43.2 → 0.43.8
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/dm +0 -0
- package/extensions/.dm-extensions.json +242 -49
- package/extensions/dm-9router-ext/package.json +2 -19
- package/extensions/dm-9router-ext/src/index.js +5 -0
- package/extensions/dm-ask-user/index.js +10 -0
- package/extensions/dm-ask-user/package.json +1 -21
- package/extensions/dm-cli-anything/package.json +0 -6
- package/extensions/dm-context/package.json +2 -12
- package/extensions/dm-context/src/context.js +1 -0
- package/extensions/dm-context/src/index.js +7 -0
- package/extensions/dm-context/src/utils.js +1 -0
- package/extensions/dm-cua/package.json +0 -7
- package/extensions/dm-fff/package.json +1 -28
- package/extensions/dm-fff/src/index.js +15 -0
- package/extensions/dm-fff/src/query.js +1 -0
- package/extensions/dm-goal/package.json +2 -31
- package/extensions/dm-goal/src/goal.js +43 -0
- package/extensions/dm-grill-me/index.js +174 -0
- package/extensions/dm-grill-me/package.json +1 -26
- package/extensions/dm-image2/package.json +0 -7
- package/extensions/dm-subagents/package.json +1 -32
- package/extensions/dm-subagents/src/agents/agent-management.js +37 -0
- package/extensions/dm-subagents/src/agents/agent-memory.js +6 -0
- package/extensions/dm-subagents/src/agents/agent-scope.js +1 -0
- package/extensions/dm-subagents/src/agents/agent-selection.js +1 -0
- package/extensions/dm-subagents/src/agents/agent-serializer.js +7 -0
- package/extensions/dm-subagents/src/agents/agents.js +11 -0
- package/extensions/dm-subagents/src/agents/chain-serializer.js +12 -0
- package/extensions/dm-subagents/src/agents/frontmatter.js +6 -0
- package/extensions/dm-subagents/src/agents/identity.js +1 -0
- package/extensions/dm-subagents/src/agents/proactive-skills.js +1 -0
- package/extensions/dm-subagents/src/agents/skills.js +6 -0
- package/extensions/dm-subagents/src/extension/config.js +2 -0
- package/extensions/dm-subagents/src/extension/control-notices.js +4 -0
- package/extensions/dm-subagents/src/extension/doctor.js +16 -0
- package/extensions/dm-subagents/src/extension/fanout-child.js +231 -0
- package/extensions/dm-subagents/src/extension/index.js +367 -0
- package/extensions/dm-subagents/src/extension/rpc.js +8 -0
- package/extensions/dm-subagents/src/extension/schemas.js +1 -0
- package/extensions/dm-subagents/src/extension/{tool-description.ts → tool-description.js} +6 -126
- package/extensions/dm-subagents/src/intercom/intercom-bridge.js +13 -0
- package/extensions/dm-subagents/src/intercom/native-supervisor-channel.js +5 -0
- package/extensions/dm-subagents/src/intercom/result-intercom.js +3 -0
- package/extensions/dm-subagents/src/profiles/profiles.js +3 -0
- package/extensions/dm-subagents/src/runs/background/async-execution.js +46 -0
- package/extensions/dm-subagents/src/runs/background/async-job-tracker.js +14 -0
- package/extensions/dm-subagents/src/runs/background/async-resume.js +8 -0
- package/extensions/dm-subagents/src/runs/background/async-status.js +12 -0
- package/extensions/dm-subagents/src/runs/background/chain-append.js +2 -0
- package/extensions/dm-subagents/src/runs/background/chain-root-attachment.js +1 -0
- package/extensions/dm-subagents/src/runs/background/completion-batcher.js +1 -0
- package/extensions/dm-subagents/src/runs/background/completion-dedupe.js +1 -0
- package/extensions/dm-subagents/src/runs/background/control-channel.js +1 -0
- package/extensions/dm-subagents/src/runs/background/fleet-view.js +17 -0
- package/extensions/dm-subagents/src/runs/background/notify.js +3 -0
- package/extensions/dm-subagents/src/runs/background/parallel-groups.js +1 -0
- package/extensions/dm-subagents/src/runs/background/result-watcher.js +8 -0
- package/extensions/dm-subagents/src/runs/background/run-id-resolver.js +4 -0
- package/extensions/dm-subagents/src/runs/background/run-status.js +23 -0
- package/extensions/dm-subagents/src/runs/background/scheduled-runs.js +4 -0
- package/extensions/dm-subagents/src/runs/background/stale-run-reconciler.js +11 -0
- package/extensions/dm-subagents/src/runs/background/subagent-runner.js +78 -0
- package/extensions/dm-subagents/src/runs/background/top-level-async.js +1 -0
- package/extensions/dm-subagents/src/runs/background/wait.js +11 -0
- package/extensions/dm-subagents/src/runs/foreground/chain-clarify.js +12 -0
- package/extensions/dm-subagents/src/runs/foreground/chain-execution.js +102 -0
- package/extensions/dm-subagents/src/runs/foreground/execution.js +51 -0
- package/extensions/dm-subagents/src/runs/foreground/subagent-executor.js +228 -0
- package/extensions/dm-subagents/src/runs/shared/acceptance.js +3 -0
- package/extensions/dm-subagents/src/runs/shared/chain-outputs.js +1 -0
- package/extensions/dm-subagents/src/runs/shared/completion-guard.js +3 -0
- package/extensions/dm-subagents/src/runs/shared/dm-args.js +1 -0
- package/extensions/dm-subagents/src/runs/shared/dm-spawn.js +1 -0
- package/extensions/dm-subagents/src/runs/shared/dynamic-fanout.js +1 -0
- package/extensions/dm-subagents/src/runs/shared/long-running-guard.js +1 -0
- package/extensions/dm-subagents/src/runs/shared/mcp-direct-tool-allowlist.js +1 -0
- package/extensions/dm-subagents/src/runs/shared/model-fallback.js +1 -0
- package/extensions/dm-subagents/src/runs/shared/model-scope.js +1 -0
- package/extensions/dm-subagents/src/runs/shared/nested-events.js +8 -0
- package/extensions/dm-subagents/src/runs/shared/nested-path.js +1 -0
- package/extensions/dm-subagents/src/runs/shared/nested-render.js +1 -0
- package/extensions/dm-subagents/src/runs/shared/parallel-utils.js +5 -0
- package/extensions/dm-subagents/src/runs/shared/run-history.js +5 -0
- package/extensions/dm-subagents/src/runs/shared/single-output.js +14 -0
- package/extensions/dm-subagents/src/runs/shared/structured-output.js +1 -0
- package/extensions/dm-subagents/src/runs/shared/subagent-control.js +5 -0
- package/extensions/dm-subagents/src/runs/shared/subagent-prompt-runtime.js +22 -0
- package/extensions/dm-subagents/src/runs/shared/tool-budget.js +1 -0
- package/extensions/dm-subagents/src/runs/shared/turn-budget.js +7 -0
- package/extensions/dm-subagents/src/runs/shared/workflow-graph.js +1 -0
- package/extensions/dm-subagents/src/runs/shared/worktree.js +3 -0
- package/extensions/dm-subagents/src/shared/artifacts.js +2 -0
- package/extensions/dm-subagents/src/shared/atomic-json.js +1 -0
- package/extensions/dm-subagents/src/shared/child-transcript.js +5 -0
- package/extensions/dm-subagents/src/shared/file-coalescer.js +1 -0
- package/extensions/dm-subagents/src/shared/fork-context.js +6 -0
- package/extensions/dm-subagents/src/shared/formatters.js +9 -0
- package/extensions/dm-subagents/src/shared/jsonl-writer.js +2 -0
- package/extensions/dm-subagents/src/shared/model-info.js +1 -0
- package/extensions/dm-subagents/src/shared/post-exit-stdio-guard.js +1 -0
- package/extensions/dm-subagents/src/shared/session-identity.js +1 -0
- package/extensions/dm-subagents/src/shared/session-tokens.js +2 -0
- package/extensions/dm-subagents/src/shared/settings.js +23 -0
- package/extensions/dm-subagents/src/shared/status-format.js +1 -0
- package/extensions/dm-subagents/src/shared/types.js +8 -0
- package/extensions/dm-subagents/src/shared/utils.js +2 -0
- package/extensions/dm-subagents/src/slash/prompt-template-bridge.js +1 -0
- package/extensions/dm-subagents/src/slash/prompt-workflows.js +7 -0
- package/extensions/dm-subagents/src/slash/slash-bridge.js +1 -0
- package/extensions/dm-subagents/src/slash/slash-commands.js +38 -0
- package/extensions/dm-subagents/src/slash/slash-live-state.js +6 -0
- package/extensions/dm-subagents/src/tui/render-helpers.js +1 -0
- package/extensions/dm-subagents/src/tui/render.js +4 -0
- package/extensions/dm-tasks/package.json +1 -30
- package/extensions/dm-tasks/src/auto-clear.js +1 -0
- package/extensions/dm-tasks/src/index.js +212 -0
- package/extensions/dm-tasks/src/process-tracker.js +1 -0
- package/extensions/dm-tasks/src/task-store.js +1 -0
- package/extensions/dm-tasks/src/tasks-config.js +1 -0
- package/extensions/dm-tasks/src/types.js +0 -0
- package/extensions/dm-tasks/src/ui/settings-menu.js +1 -0
- package/extensions/dm-tasks/src/ui/task-widget.js +1 -0
- package/extensions/dm-usage/index.js +9 -0
- package/extensions/dm-usage/package.json +1 -18
- package/extensions/greedysearch-dm/bin/search.mjs +135 -135
- package/extensions/greedysearch-dm/index.js +20 -0
- package/extensions/greedysearch-dm/package.json +1 -22
- package/extensions/greedysearch-dm/src/formatters/results.js +8 -0
- package/extensions/greedysearch-dm/src/formatters/sources.js +1 -0
- package/extensions/greedysearch-dm/src/formatters/synthesis.js +1 -0
- package/extensions/greedysearch-dm/src/search/engines.mjs +9 -9
- package/extensions/greedysearch-dm/src/search/paths.mjs +1 -0
- package/extensions/greedysearch-dm/src/search/research.mjs +100 -100
- package/extensions/greedysearch-dm/src/search/synthesis-runner.mjs +10 -10
- package/extensions/greedysearch-dm/src/tools/greedy-search-handler.js +20 -0
- package/extensions/greedysearch-dm/src/tools/shared.js +9 -0
- package/extensions/greedysearch-dm/src/types.js +0 -0
- package/extensions/greedysearch-dm/src/utils/helpers.js +1 -0
- package/package.json +1 -1
- package/extensions/dm-9router-ext/src/index.ts +0 -541
- package/extensions/dm-ask-user/index.ts +0 -857
- package/extensions/dm-context/src/context.ts +0 -158
- package/extensions/dm-context/src/index.ts +0 -439
- package/extensions/dm-context/src/utils.ts +0 -6
- package/extensions/dm-fff/src/index.ts +0 -1023
- package/extensions/dm-fff/src/query.ts +0 -87
- package/extensions/dm-goal/src/goal.ts +0 -1073
- package/extensions/dm-grill-me/index.ts +0 -1085
- package/extensions/dm-subagents/src/agents/agent-management.ts +0 -1052
- package/extensions/dm-subagents/src/agents/agent-memory.ts +0 -254
- package/extensions/dm-subagents/src/agents/agent-scope.ts +0 -6
- package/extensions/dm-subagents/src/agents/agent-selection.ts +0 -25
- package/extensions/dm-subagents/src/agents/agent-serializer.ts +0 -121
- package/extensions/dm-subagents/src/agents/agents.ts +0 -1553
- package/extensions/dm-subagents/src/agents/chain-serializer.ts +0 -277
- package/extensions/dm-subagents/src/agents/frontmatter.ts +0 -93
- package/extensions/dm-subagents/src/agents/identity.ts +0 -30
- package/extensions/dm-subagents/src/agents/proactive-skills.ts +0 -191
- package/extensions/dm-subagents/src/agents/skills.ts +0 -729
- package/extensions/dm-subagents/src/extension/config.ts +0 -39
- package/extensions/dm-subagents/src/extension/control-notices.ts +0 -92
- package/extensions/dm-subagents/src/extension/doctor.ts +0 -214
- package/extensions/dm-subagents/src/extension/fanout-child.ts +0 -172
- package/extensions/dm-subagents/src/extension/index.ts +0 -656
- package/extensions/dm-subagents/src/extension/rpc.ts +0 -369
- package/extensions/dm-subagents/src/extension/schemas.ts +0 -309
- package/extensions/dm-subagents/src/intercom/intercom-bridge.ts +0 -180
- package/extensions/dm-subagents/src/intercom/native-supervisor-channel.ts +0 -519
- package/extensions/dm-subagents/src/intercom/result-intercom.ts +0 -377
- package/extensions/dm-subagents/src/profiles/profiles.ts +0 -637
- package/extensions/dm-subagents/src/runs/background/async-execution.ts +0 -1065
- package/extensions/dm-subagents/src/runs/background/async-job-tracker.ts +0 -441
- package/extensions/dm-subagents/src/runs/background/async-resume.ts +0 -391
- package/extensions/dm-subagents/src/runs/background/async-status.ts +0 -395
- package/extensions/dm-subagents/src/runs/background/chain-append.ts +0 -282
- package/extensions/dm-subagents/src/runs/background/chain-root-attachment.ts +0 -191
- package/extensions/dm-subagents/src/runs/background/completion-batcher.ts +0 -166
- package/extensions/dm-subagents/src/runs/background/completion-dedupe.ts +0 -63
- package/extensions/dm-subagents/src/runs/background/control-channel.ts +0 -332
- package/extensions/dm-subagents/src/runs/background/fleet-view.ts +0 -515
- package/extensions/dm-subagents/src/runs/background/notify.ts +0 -225
- package/extensions/dm-subagents/src/runs/background/parallel-groups.ts +0 -45
- package/extensions/dm-subagents/src/runs/background/result-watcher.ts +0 -315
- package/extensions/dm-subagents/src/runs/background/run-id-resolver.ts +0 -84
- package/extensions/dm-subagents/src/runs/background/run-status.ts +0 -434
- package/extensions/dm-subagents/src/runs/background/scheduled-runs.ts +0 -514
- package/extensions/dm-subagents/src/runs/background/stale-run-reconciler.ts +0 -368
- package/extensions/dm-subagents/src/runs/background/subagent-runner.ts +0 -3171
- package/extensions/dm-subagents/src/runs/background/top-level-async.ts +0 -13
- package/extensions/dm-subagents/src/runs/background/wait.ts +0 -353
- package/extensions/dm-subagents/src/runs/foreground/chain-clarify.ts +0 -1333
- package/extensions/dm-subagents/src/runs/foreground/chain-execution.ts +0 -1313
- package/extensions/dm-subagents/src/runs/foreground/execution.ts +0 -1239
- package/extensions/dm-subagents/src/runs/foreground/subagent-executor.ts +0 -3613
- package/extensions/dm-subagents/src/runs/shared/acceptance.ts +0 -879
- package/extensions/dm-subagents/src/runs/shared/chain-outputs.ts +0 -116
- package/extensions/dm-subagents/src/runs/shared/completion-guard.ts +0 -147
- package/extensions/dm-subagents/src/runs/shared/dm-args.ts +0 -271
- package/extensions/dm-subagents/src/runs/shared/dm-spawn.ts +0 -147
- package/extensions/dm-subagents/src/runs/shared/dynamic-fanout.ts +0 -295
- package/extensions/dm-subagents/src/runs/shared/long-running-guard.ts +0 -175
- package/extensions/dm-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +0 -365
- package/extensions/dm-subagents/src/runs/shared/model-fallback.ts +0 -292
- package/extensions/dm-subagents/src/runs/shared/model-scope.ts +0 -128
- package/extensions/dm-subagents/src/runs/shared/nested-events.ts +0 -908
- package/extensions/dm-subagents/src/runs/shared/nested-path.ts +0 -52
- package/extensions/dm-subagents/src/runs/shared/nested-render.ts +0 -115
- package/extensions/dm-subagents/src/runs/shared/parallel-utils.ts +0 -198
- package/extensions/dm-subagents/src/runs/shared/run-history.ts +0 -60
- package/extensions/dm-subagents/src/runs/shared/single-output.ts +0 -180
- package/extensions/dm-subagents/src/runs/shared/structured-output.ts +0 -77
- package/extensions/dm-subagents/src/runs/shared/subagent-control.ts +0 -223
- package/extensions/dm-subagents/src/runs/shared/subagent-prompt-runtime.ts +0 -342
- package/extensions/dm-subagents/src/runs/shared/tool-budget.ts +0 -74
- package/extensions/dm-subagents/src/runs/shared/turn-budget.ts +0 -52
- package/extensions/dm-subagents/src/runs/shared/workflow-graph.ts +0 -206
- package/extensions/dm-subagents/src/runs/shared/worktree.ts +0 -600
- package/extensions/dm-subagents/src/shared/artifacts.ts +0 -113
- package/extensions/dm-subagents/src/shared/atomic-json.ts +0 -86
- package/extensions/dm-subagents/src/shared/child-transcript.ts +0 -212
- package/extensions/dm-subagents/src/shared/file-coalescer.ts +0 -40
- package/extensions/dm-subagents/src/shared/fork-context.ts +0 -194
- package/extensions/dm-subagents/src/shared/formatters.ts +0 -133
- package/extensions/dm-subagents/src/shared/jsonl-writer.ts +0 -81
- package/extensions/dm-subagents/src/shared/model-info.ts +0 -78
- package/extensions/dm-subagents/src/shared/post-exit-stdio-guard.ts +0 -85
- package/extensions/dm-subagents/src/shared/session-identity.ts +0 -10
- package/extensions/dm-subagents/src/shared/session-tokens.ts +0 -44
- package/extensions/dm-subagents/src/shared/settings.ts +0 -450
- package/extensions/dm-subagents/src/shared/status-format.ts +0 -49
- package/extensions/dm-subagents/src/shared/types.ts +0 -1257
- package/extensions/dm-subagents/src/shared/utils.ts +0 -554
- package/extensions/dm-subagents/src/slash/prompt-template-bridge.ts +0 -420
- package/extensions/dm-subagents/src/slash/prompt-workflows.ts +0 -330
- package/extensions/dm-subagents/src/slash/slash-bridge.ts +0 -176
- package/extensions/dm-subagents/src/slash/slash-commands.ts +0 -1296
- package/extensions/dm-subagents/src/slash/slash-live-state.ts +0 -292
- package/extensions/dm-subagents/src/tui/render-helpers.ts +0 -80
- package/extensions/dm-subagents/src/tui/render.ts +0 -1748
- package/extensions/dm-tasks/src/auto-clear.ts +0 -91
- package/extensions/dm-tasks/src/index.ts +0 -1145
- package/extensions/dm-tasks/src/process-tracker.ts +0 -140
- package/extensions/dm-tasks/src/task-store.ts +0 -305
- package/extensions/dm-tasks/src/tasks-config.ts +0 -23
- package/extensions/dm-tasks/src/types.ts +0 -40
- package/extensions/dm-tasks/src/ui/settings-menu.ts +0 -100
- package/extensions/dm-tasks/src/ui/task-widget.ts +0 -265
- package/extensions/dm-usage/index.ts +0 -1718
- package/extensions/greedysearch-dm/index.ts +0 -177
- package/extensions/greedysearch-dm/src/formatters/results.ts +0 -163
- package/extensions/greedysearch-dm/src/formatters/sources.ts +0 -116
- package/extensions/greedysearch-dm/src/formatters/synthesis.ts +0 -87
- package/extensions/greedysearch-dm/src/tools/greedy-search-handler.ts +0 -370
- package/extensions/greedysearch-dm/src/tools/shared.ts +0 -187
- package/extensions/greedysearch-dm/src/types.ts +0 -110
- package/extensions/greedysearch-dm/src/utils/helpers.ts +0 -40
|
@@ -1,1553 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Agent discovery and configuration
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { execSync } from "node:child_process";
|
|
6
|
-
import * as fs from "node:fs";
|
|
7
|
-
import * as os from "node:os";
|
|
8
|
-
import * as path from "node:path";
|
|
9
|
-
import { fileURLToPath } from "node:url";
|
|
10
|
-
import type { AcceptanceInput, OutputMode, ToolBudgetConfig } from "../shared/types.ts";
|
|
11
|
-
import { getAgentDir, getProjectConfigDir } from "../shared/utils.ts";
|
|
12
|
-
import { KNOWN_FIELDS } from "./agent-serializer.ts";
|
|
13
|
-
import { parseChain, parseJsonChain } from "./chain-serializer.ts";
|
|
14
|
-
import { mergeAgentsForScope } from "./agent-selection.ts";
|
|
15
|
-
import { parseFrontmatter } from "./frontmatter.ts";
|
|
16
|
-
import { buildRuntimeName, parsePackageName } from "./identity.ts";
|
|
17
|
-
import { parseModelScopeConfig, type ModelScopeConfig } from "../runs/shared/model-scope.ts";
|
|
18
|
-
export { buildRuntimeName, frontmatterNameForConfig, parsePackageName } from "./identity.ts";
|
|
19
|
-
import { parseMemoryFrontmatter } from "./agent-memory.ts";
|
|
20
|
-
|
|
21
|
-
export type AgentScope = "user" | "project" | "both";
|
|
22
|
-
|
|
23
|
-
export type AgentSource = "builtin" | "package" | "user" | "project";
|
|
24
|
-
type SystemPromptMode = "append" | "replace";
|
|
25
|
-
export type AgentDefaultContext = "fresh" | "fork";
|
|
26
|
-
|
|
27
|
-
export type AgentMemoryScope = "project" | "user";
|
|
28
|
-
|
|
29
|
-
export interface AgentMemoryConfig {
|
|
30
|
-
scope: AgentMemoryScope;
|
|
31
|
-
path: string;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export const BUILTIN_AGENT_NAMES = [
|
|
35
|
-
"context-builder",
|
|
36
|
-
"delegate",
|
|
37
|
-
"oracle",
|
|
38
|
-
"planner",
|
|
39
|
-
"researcher",
|
|
40
|
-
"reviewer",
|
|
41
|
-
"scout",
|
|
42
|
-
"worker",
|
|
43
|
-
] as const;
|
|
44
|
-
|
|
45
|
-
export function defaultSystemPromptMode(name: string): SystemPromptMode {
|
|
46
|
-
return name === "delegate" ? "append" : "replace";
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export function defaultInheritProjectContext(name: string): boolean {
|
|
50
|
-
return name === "delegate";
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export function defaultInheritSkills(): boolean {
|
|
54
|
-
return false;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export interface BuiltinAgentOverrideBase {
|
|
58
|
-
model?: string;
|
|
59
|
-
fallbackModels?: string[];
|
|
60
|
-
thinking?: string;
|
|
61
|
-
systemPromptMode: SystemPromptMode;
|
|
62
|
-
inheritProjectContext: boolean;
|
|
63
|
-
inheritSkills: boolean;
|
|
64
|
-
defaultContext?: AgentDefaultContext;
|
|
65
|
-
disabled?: boolean;
|
|
66
|
-
systemPrompt: string;
|
|
67
|
-
skills?: string[];
|
|
68
|
-
tools?: string[];
|
|
69
|
-
mcpDirectTools?: string[];
|
|
70
|
-
subagentOnlyExtensions?: string[];
|
|
71
|
-
completionGuard?: boolean;
|
|
72
|
-
toolBudget?: ToolBudgetConfig;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
interface BuiltinAgentOverrideConfig {
|
|
76
|
-
model?: string | false;
|
|
77
|
-
fallbackModels?: string[] | false;
|
|
78
|
-
thinking?: string | false;
|
|
79
|
-
systemPromptMode?: SystemPromptMode;
|
|
80
|
-
inheritProjectContext?: boolean;
|
|
81
|
-
inheritSkills?: boolean;
|
|
82
|
-
defaultContext?: AgentDefaultContext | false;
|
|
83
|
-
disabled?: boolean;
|
|
84
|
-
systemPrompt?: string;
|
|
85
|
-
skills?: string[] | false;
|
|
86
|
-
tools?: string[] | false;
|
|
87
|
-
subagentOnlyExtensions?: string[] | false;
|
|
88
|
-
completionGuard?: boolean;
|
|
89
|
-
toolBudget?: ToolBudgetConfig | false;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
interface BuiltinAgentOverrideInfo {
|
|
93
|
-
scope: "user" | "project";
|
|
94
|
-
path: string;
|
|
95
|
-
base: BuiltinAgentOverrideBase;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
export interface AgentModelSourceInfo {
|
|
99
|
-
type: "subagents.defaultModel";
|
|
100
|
-
scope: "user" | "project";
|
|
101
|
-
path: string;
|
|
102
|
-
model: string;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
export interface AgentConfig {
|
|
106
|
-
name: string;
|
|
107
|
-
localName?: string;
|
|
108
|
-
packageName?: string;
|
|
109
|
-
description: string;
|
|
110
|
-
tools?: string[];
|
|
111
|
-
mcpDirectTools?: string[];
|
|
112
|
-
model?: string;
|
|
113
|
-
fallbackModels?: string[];
|
|
114
|
-
thinking?: string;
|
|
115
|
-
systemPromptMode: SystemPromptMode;
|
|
116
|
-
inheritProjectContext: boolean;
|
|
117
|
-
inheritSkills: boolean;
|
|
118
|
-
defaultContext?: AgentDefaultContext;
|
|
119
|
-
systemPrompt: string;
|
|
120
|
-
source: AgentSource;
|
|
121
|
-
filePath: string;
|
|
122
|
-
skills?: string[];
|
|
123
|
-
extensions?: string[];
|
|
124
|
-
subagentOnlyExtensions?: string[];
|
|
125
|
-
output?: string;
|
|
126
|
-
defaultReads?: string[];
|
|
127
|
-
defaultProgress?: boolean;
|
|
128
|
-
interactive?: boolean;
|
|
129
|
-
maxSubagentDepth?: number;
|
|
130
|
-
completionGuard?: boolean;
|
|
131
|
-
toolBudget?: ToolBudgetConfig;
|
|
132
|
-
memory?: AgentMemoryConfig;
|
|
133
|
-
disabled?: boolean;
|
|
134
|
-
extraFields?: Record<string, string>;
|
|
135
|
-
override?: BuiltinAgentOverrideInfo;
|
|
136
|
-
modelSource?: AgentModelSourceInfo;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
interface SubagentSettings {
|
|
140
|
-
overrides: Record<string, BuiltinAgentOverrideConfig>;
|
|
141
|
-
defaultModel?: string;
|
|
142
|
-
disableBuiltins?: boolean;
|
|
143
|
-
disableThinking?: boolean;
|
|
144
|
-
modelScope?: ModelScopeConfig;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
const EMPTY_SUBAGENT_SETTINGS: SubagentSettings = { overrides: {} };
|
|
148
|
-
const agentFrontmatterFields = new WeakMap<AgentConfig, Set<string>>();
|
|
149
|
-
|
|
150
|
-
export interface ChainStepConfig {
|
|
151
|
-
agent?: string;
|
|
152
|
-
task?: string;
|
|
153
|
-
phase?: string;
|
|
154
|
-
label?: string;
|
|
155
|
-
as?: string;
|
|
156
|
-
outputSchema?: string | Record<string, unknown>;
|
|
157
|
-
output?: string | false;
|
|
158
|
-
outputMode?: OutputMode;
|
|
159
|
-
reads?: string[] | false;
|
|
160
|
-
model?: string;
|
|
161
|
-
skills?: string[] | false;
|
|
162
|
-
progress?: boolean;
|
|
163
|
-
parallel?: unknown;
|
|
164
|
-
expand?: unknown;
|
|
165
|
-
collect?: unknown;
|
|
166
|
-
concurrency?: number;
|
|
167
|
-
failFast?: boolean;
|
|
168
|
-
worktree?: boolean;
|
|
169
|
-
acceptance?: AcceptanceInput;
|
|
170
|
-
toolBudget?: ToolBudgetConfig;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
export interface ChainConfig {
|
|
174
|
-
name: string;
|
|
175
|
-
localName?: string;
|
|
176
|
-
packageName?: string;
|
|
177
|
-
description: string;
|
|
178
|
-
source: AgentSource;
|
|
179
|
-
filePath: string;
|
|
180
|
-
steps: ChainStepConfig[];
|
|
181
|
-
extraFields?: Record<string, string>;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
export interface ChainDiscoveryDiagnostic {
|
|
185
|
-
source: AgentSource;
|
|
186
|
-
filePath: string;
|
|
187
|
-
error: string;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
interface AgentDiscoveryResult {
|
|
191
|
-
agents: AgentConfig[];
|
|
192
|
-
projectAgentsDir: string | null;
|
|
193
|
-
modelScope?: ModelScopeConfig;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
function getUserChainDir(): string {
|
|
197
|
-
return path.join(getAgentDir(), "chains");
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
interface PackageSubagentPaths {
|
|
201
|
-
agents: string[];
|
|
202
|
-
chains: string[];
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
let cachedGlobalNpmRoot: string | null = null;
|
|
206
|
-
|
|
207
|
-
function readJsonFileBestEffort(filePath: string): unknown {
|
|
208
|
-
try {
|
|
209
|
-
return JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
210
|
-
} catch {
|
|
211
|
-
// Installed package scans are opportunistic; bad third-party manifests
|
|
212
|
-
// should not break local agent discovery.
|
|
213
|
-
return null;
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
function readOptionalJsonFile(filePath: string): unknown {
|
|
218
|
-
try {
|
|
219
|
-
return JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
220
|
-
} catch (error) {
|
|
221
|
-
const code = typeof error === "object" && error !== null && "code" in error
|
|
222
|
-
? (error as { code?: unknown }).code
|
|
223
|
-
: undefined;
|
|
224
|
-
if (code === "ENOENT") return null;
|
|
225
|
-
throw error;
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
function isSafePackagePath(value: string): boolean {
|
|
230
|
-
return value.length > 0
|
|
231
|
-
&& !path.isAbsolute(value)
|
|
232
|
-
&& value.split(/[\\/]/).every((part) => part.length > 0 && part !== "." && part !== "..");
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
function parseNpmPackageName(source: string): string | undefined {
|
|
236
|
-
const spec = source.slice(4).trim();
|
|
237
|
-
if (!spec) return undefined;
|
|
238
|
-
const match = spec.match(/^(@?[^@]+(?:\/[^@]+)?)(?:@(.+))?$/);
|
|
239
|
-
const packageName = match?.[1] ?? spec;
|
|
240
|
-
return isSafePackagePath(packageName) ? packageName : undefined;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
function stripGitRef(repoPath: string): string {
|
|
244
|
-
const atIndex = repoPath.indexOf("@");
|
|
245
|
-
const hashIndex = repoPath.indexOf("#");
|
|
246
|
-
const refIndex = [atIndex, hashIndex].filter((index) => index >= 0).sort((a, b) => a - b)[0];
|
|
247
|
-
return refIndex === undefined ? repoPath : repoPath.slice(0, refIndex);
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
function parseGitPackagePath(source: string): { host: string; repoPath: string } | undefined {
|
|
251
|
-
const spec = source.slice(4).trim();
|
|
252
|
-
if (!spec) return undefined;
|
|
253
|
-
|
|
254
|
-
let host = "";
|
|
255
|
-
let repoPath = "";
|
|
256
|
-
const scpLike = spec.match(/^git@([^:]+):(.+)$/);
|
|
257
|
-
if (scpLike) {
|
|
258
|
-
host = scpLike[1] ?? "";
|
|
259
|
-
repoPath = scpLike[2] ?? "";
|
|
260
|
-
} else if (/^[a-z][a-z0-9+.-]*:\/\//i.test(spec)) {
|
|
261
|
-
try {
|
|
262
|
-
const url = new URL(spec);
|
|
263
|
-
host = url.hostname;
|
|
264
|
-
repoPath = url.pathname.replace(/^\/+/, "");
|
|
265
|
-
} catch {
|
|
266
|
-
return undefined;
|
|
267
|
-
}
|
|
268
|
-
} else {
|
|
269
|
-
const slashIndex = spec.indexOf("/");
|
|
270
|
-
if (slashIndex < 0) return undefined;
|
|
271
|
-
host = spec.slice(0, slashIndex);
|
|
272
|
-
repoPath = spec.slice(slashIndex + 1);
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
const normalizedPath = stripGitRef(repoPath).replace(/\.git$/, "").replace(/^\/+/, "");
|
|
276
|
-
if (!host || !isSafePackagePath(host) || !isSafePackagePath(normalizedPath) || normalizedPath.split(/[\\/]/).length < 2) {
|
|
277
|
-
return undefined;
|
|
278
|
-
}
|
|
279
|
-
return { host, repoPath: normalizedPath };
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
function resolveSettingsPackageRoot(source: string, baseDir: string): string | undefined {
|
|
283
|
-
const trimmed = source.trim();
|
|
284
|
-
if (!trimmed) return undefined;
|
|
285
|
-
if (trimmed.startsWith("git:")) {
|
|
286
|
-
const parsed = parseGitPackagePath(trimmed);
|
|
287
|
-
return parsed ? path.join(baseDir, "git", parsed.host, parsed.repoPath) : undefined;
|
|
288
|
-
}
|
|
289
|
-
if (trimmed.startsWith("npm:")) {
|
|
290
|
-
const packageName = parseNpmPackageName(trimmed);
|
|
291
|
-
return packageName ? path.join(baseDir, "npm", "node_modules", packageName) : undefined;
|
|
292
|
-
}
|
|
293
|
-
const normalized = trimmed.startsWith("file:") ? trimmed.slice(5) : trimmed;
|
|
294
|
-
if (normalized === "~") return os.homedir();
|
|
295
|
-
if (normalized.startsWith("~/")) return path.join(os.homedir(), normalized.slice(2));
|
|
296
|
-
if (path.isAbsolute(normalized)) return normalized;
|
|
297
|
-
if (normalized === "." || normalized === ".." || normalized.startsWith("./") || normalized.startsWith("../")) {
|
|
298
|
-
return path.resolve(baseDir, normalized);
|
|
299
|
-
}
|
|
300
|
-
return undefined;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
function getGlobalNpmRoot(): string | null {
|
|
304
|
-
if (cachedGlobalNpmRoot !== null) return cachedGlobalNpmRoot;
|
|
305
|
-
try {
|
|
306
|
-
cachedGlobalNpmRoot = fs.realpathSync(execSync("npm root -g", { encoding: "utf-8", timeout: 5000 }).trim());
|
|
307
|
-
return cachedGlobalNpmRoot;
|
|
308
|
-
} catch {
|
|
309
|
-
cachedGlobalNpmRoot = "";
|
|
310
|
-
return null;
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
function stringArray(value: unknown): string[] {
|
|
315
|
-
if (!Array.isArray(value)) return [];
|
|
316
|
-
return value.filter((entry): entry is string => typeof entry === "string" && entry.trim().length > 0);
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
function extractSubagentPathsFromPackageRoot(packageRoot: string): PackageSubagentPaths {
|
|
320
|
-
const packageJsonPath = path.join(packageRoot, "package.json");
|
|
321
|
-
const pkg = readJsonFileBestEffort(packageJsonPath);
|
|
322
|
-
if (!pkg || typeof pkg !== "object" || Array.isArray(pkg)) return { agents: [], chains: [] };
|
|
323
|
-
|
|
324
|
-
const roots: Record<string, unknown>[] = [];
|
|
325
|
-
const piSubagents = (pkg as { "dm-subagents"?: unknown })["dm-subagents"];
|
|
326
|
-
if (piSubagents && typeof piSubagents === "object" && !Array.isArray(piSubagents)) {
|
|
327
|
-
roots.push(piSubagents as Record<string, unknown>);
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
const pi = (pkg as { pi?: unknown }).pi;
|
|
331
|
-
if (pi && typeof pi === "object" && !Array.isArray(pi)) {
|
|
332
|
-
const subagents = (pi as { subagents?: unknown }).subagents;
|
|
333
|
-
if (subagents && typeof subagents === "object" && !Array.isArray(subagents)) {
|
|
334
|
-
roots.push(subagents as Record<string, unknown>);
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
const agents: string[] = [];
|
|
339
|
-
const chains: string[] = [];
|
|
340
|
-
for (const root of roots) {
|
|
341
|
-
for (const entry of stringArray(root.agents)) agents.push(path.resolve(packageRoot, entry));
|
|
342
|
-
for (const entry of stringArray(root.chains)) chains.push(path.resolve(packageRoot, entry));
|
|
343
|
-
}
|
|
344
|
-
return { agents, chains };
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
function collectPackageRootsFromNodeModules(nodeModulesDir: string): string[] {
|
|
348
|
-
const roots: string[] = [];
|
|
349
|
-
if (!fs.existsSync(nodeModulesDir)) return roots;
|
|
350
|
-
|
|
351
|
-
let entries: fs.Dirent[];
|
|
352
|
-
try {
|
|
353
|
-
entries = fs.readdirSync(nodeModulesDir, { withFileTypes: true });
|
|
354
|
-
} catch {
|
|
355
|
-
return roots;
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
for (const entry of entries) {
|
|
359
|
-
if (entry.name.startsWith(".")) continue;
|
|
360
|
-
if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
|
|
361
|
-
|
|
362
|
-
if (entry.name.startsWith("@")) {
|
|
363
|
-
const scopeDir = path.join(nodeModulesDir, entry.name);
|
|
364
|
-
let scopeEntries: fs.Dirent[];
|
|
365
|
-
try {
|
|
366
|
-
scopeEntries = fs.readdirSync(scopeDir, { withFileTypes: true });
|
|
367
|
-
} catch {
|
|
368
|
-
continue;
|
|
369
|
-
}
|
|
370
|
-
for (const scopeEntry of scopeEntries) {
|
|
371
|
-
if (scopeEntry.name.startsWith(".")) continue;
|
|
372
|
-
if (!scopeEntry.isDirectory() && !scopeEntry.isSymbolicLink()) continue;
|
|
373
|
-
roots.push(path.join(scopeDir, scopeEntry.name));
|
|
374
|
-
}
|
|
375
|
-
continue;
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
roots.push(path.join(nodeModulesDir, entry.name));
|
|
379
|
-
}
|
|
380
|
-
return roots;
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
function collectSettingsPackageRoots(settingsFile: string, baseDir: string): string[] {
|
|
384
|
-
const settings = readOptionalJsonFile(settingsFile);
|
|
385
|
-
if (!settings || typeof settings !== "object" || Array.isArray(settings)) return [];
|
|
386
|
-
const packages = (settings as { packages?: unknown }).packages;
|
|
387
|
-
if (!Array.isArray(packages)) return [];
|
|
388
|
-
|
|
389
|
-
const roots: string[] = [];
|
|
390
|
-
for (const entry of packages) {
|
|
391
|
-
const packageSource = typeof entry === "string"
|
|
392
|
-
? entry
|
|
393
|
-
: typeof entry === "object" && entry !== null && typeof (entry as { source?: unknown }).source === "string"
|
|
394
|
-
? (entry as { source: string }).source
|
|
395
|
-
: undefined;
|
|
396
|
-
if (!packageSource) continue;
|
|
397
|
-
const packageRoot = resolveSettingsPackageRoot(packageSource, baseDir);
|
|
398
|
-
if (packageRoot) roots.push(packageRoot);
|
|
399
|
-
}
|
|
400
|
-
return roots;
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
function collectPackageSubagentPaths(cwd: string, options: { includeUser: boolean; includeProject: boolean } = { includeUser: true, includeProject: true }): PackageSubagentPaths {
|
|
404
|
-
const agentDir = getAgentDir();
|
|
405
|
-
const projectRoot = findNearestProjectRoot(cwd) ?? cwd;
|
|
406
|
-
const packageRoots = [
|
|
407
|
-
projectRoot,
|
|
408
|
-
];
|
|
409
|
-
|
|
410
|
-
if (options.includeProject) {
|
|
411
|
-
const projectConfigDir = getProjectConfigDir(projectRoot);
|
|
412
|
-
packageRoots.push(
|
|
413
|
-
...collectPackageRootsFromNodeModules(path.join(projectConfigDir, "npm", "node_modules")),
|
|
414
|
-
...collectSettingsPackageRoots(path.join(projectConfigDir, "settings.json"), projectConfigDir),
|
|
415
|
-
);
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
if (options.includeUser) {
|
|
419
|
-
packageRoots.push(
|
|
420
|
-
...collectPackageRootsFromNodeModules(path.join(agentDir, "npm", "node_modules")),
|
|
421
|
-
...collectSettingsPackageRoots(path.join(agentDir, "settings.json"), agentDir),
|
|
422
|
-
);
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
if (options.includeUser) {
|
|
426
|
-
const globalRoot = getGlobalNpmRoot();
|
|
427
|
-
if (globalRoot) packageRoots.push(...collectPackageRootsFromNodeModules(globalRoot));
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
const seenRoots = new Set<string>();
|
|
431
|
-
const seenAgents = new Set<string>();
|
|
432
|
-
const seenChains = new Set<string>();
|
|
433
|
-
const agents: string[] = [];
|
|
434
|
-
const chains: string[] = [];
|
|
435
|
-
for (const packageRoot of packageRoots) {
|
|
436
|
-
const resolvedRoot = path.resolve(packageRoot);
|
|
437
|
-
if (seenRoots.has(resolvedRoot)) continue;
|
|
438
|
-
seenRoots.add(resolvedRoot);
|
|
439
|
-
const paths = extractSubagentPathsFromPackageRoot(resolvedRoot);
|
|
440
|
-
for (const agentDir of paths.agents) {
|
|
441
|
-
if (seenAgents.has(agentDir)) continue;
|
|
442
|
-
seenAgents.add(agentDir);
|
|
443
|
-
agents.push(agentDir);
|
|
444
|
-
}
|
|
445
|
-
for (const chainDir of paths.chains) {
|
|
446
|
-
if (seenChains.has(chainDir)) continue;
|
|
447
|
-
seenChains.add(chainDir);
|
|
448
|
-
chains.push(chainDir);
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
return { agents, chains };
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
function splitToolList(rawTools: string[] | undefined): { tools?: string[]; mcpDirectTools?: string[] } {
|
|
455
|
-
const mcpDirectTools: string[] = [];
|
|
456
|
-
const tools: string[] = [];
|
|
457
|
-
for (const tool of rawTools ?? []) {
|
|
458
|
-
if (tool.startsWith("mcp:")) {
|
|
459
|
-
mcpDirectTools.push(tool.slice(4));
|
|
460
|
-
} else {
|
|
461
|
-
tools.push(tool);
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
return {
|
|
465
|
-
...(tools.length > 0 ? { tools } : {}),
|
|
466
|
-
...(mcpDirectTools.length > 0 ? { mcpDirectTools } : {}),
|
|
467
|
-
};
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
function joinToolList(config: Pick<AgentConfig, "tools" | "mcpDirectTools">): string[] | undefined {
|
|
471
|
-
const joined = [
|
|
472
|
-
...(config.tools ?? []),
|
|
473
|
-
...(config.mcpDirectTools ?? []).map((tool) => `mcp:${tool}`),
|
|
474
|
-
];
|
|
475
|
-
return joined.length > 0 ? joined : undefined;
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
function arraysEqual(a: string[] | undefined, b: string[] | undefined): boolean {
|
|
479
|
-
if (!a && !b) return true;
|
|
480
|
-
if (!a || !b) return false;
|
|
481
|
-
if (a.length !== b.length) return false;
|
|
482
|
-
for (let i = 0; i < a.length; i++) {
|
|
483
|
-
if (a[i] !== b[i]) return false;
|
|
484
|
-
}
|
|
485
|
-
return true;
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
function cloneOverrideBase(agent: AgentConfig): BuiltinAgentOverrideBase {
|
|
489
|
-
return {
|
|
490
|
-
model: agent.model,
|
|
491
|
-
fallbackModels: agent.fallbackModels ? [...agent.fallbackModels] : undefined,
|
|
492
|
-
thinking: agent.thinking,
|
|
493
|
-
systemPromptMode: agent.systemPromptMode,
|
|
494
|
-
inheritProjectContext: agent.inheritProjectContext,
|
|
495
|
-
inheritSkills: agent.inheritSkills,
|
|
496
|
-
defaultContext: agent.defaultContext,
|
|
497
|
-
disabled: agent.disabled,
|
|
498
|
-
systemPrompt: agent.systemPrompt,
|
|
499
|
-
skills: agent.skills ? [...agent.skills] : undefined,
|
|
500
|
-
tools: agent.tools ? [...agent.tools] : undefined,
|
|
501
|
-
mcpDirectTools: agent.mcpDirectTools ? [...agent.mcpDirectTools] : undefined,
|
|
502
|
-
subagentOnlyExtensions: agent.subagentOnlyExtensions ? [...agent.subagentOnlyExtensions] : undefined,
|
|
503
|
-
completionGuard: agent.completionGuard,
|
|
504
|
-
toolBudget: agent.toolBudget,
|
|
505
|
-
};
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
function cloneOverrideValue(override: BuiltinAgentOverrideConfig): BuiltinAgentOverrideConfig {
|
|
509
|
-
return {
|
|
510
|
-
...(override.model !== undefined ? { model: override.model } : {}),
|
|
511
|
-
...(override.fallbackModels !== undefined
|
|
512
|
-
? { fallbackModels: override.fallbackModels === false ? false : [...override.fallbackModels] }
|
|
513
|
-
: {}),
|
|
514
|
-
...(override.thinking !== undefined ? { thinking: override.thinking } : {}),
|
|
515
|
-
...(override.systemPromptMode !== undefined ? { systemPromptMode: override.systemPromptMode } : {}),
|
|
516
|
-
...(override.inheritProjectContext !== undefined ? { inheritProjectContext: override.inheritProjectContext } : {}),
|
|
517
|
-
...(override.inheritSkills !== undefined ? { inheritSkills: override.inheritSkills } : {}),
|
|
518
|
-
...(override.defaultContext !== undefined ? { defaultContext: override.defaultContext } : {}),
|
|
519
|
-
...(override.disabled !== undefined ? { disabled: override.disabled } : {}),
|
|
520
|
-
...(override.systemPrompt !== undefined ? { systemPrompt: override.systemPrompt } : {}),
|
|
521
|
-
...(override.skills !== undefined ? { skills: override.skills === false ? false : [...override.skills] } : {}),
|
|
522
|
-
...(override.tools !== undefined ? { tools: override.tools === false ? false : [...override.tools] } : {}),
|
|
523
|
-
...(override.subagentOnlyExtensions !== undefined ? { subagentOnlyExtensions: override.subagentOnlyExtensions === false ? false : [...override.subagentOnlyExtensions] } : {}),
|
|
524
|
-
...(override.completionGuard !== undefined ? { completionGuard: override.completionGuard } : {}),
|
|
525
|
-
...(override.toolBudget !== undefined ? { toolBudget: override.toolBudget === false ? false : { ...override.toolBudget, ...(Array.isArray(override.toolBudget.block) ? { block: [...override.toolBudget.block] } : {}) } } : {}),
|
|
526
|
-
};
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
export function findNearestProjectRoot(cwd: string): string | null {
|
|
530
|
-
let currentDir = cwd;
|
|
531
|
-
while (true) {
|
|
532
|
-
if (isDirectory(getProjectConfigDir(currentDir)) || isDirectory(path.join(currentDir, ".agents"))) {
|
|
533
|
-
return currentDir;
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
const parentDir = path.dirname(currentDir);
|
|
537
|
-
if (parentDir === currentDir) return null;
|
|
538
|
-
currentDir = parentDir;
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
function getUserAgentSettingsPath(): string {
|
|
543
|
-
return path.join(getAgentDir(), "settings.json");
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
function getProjectAgentSettingsPath(cwd: string): string | null {
|
|
547
|
-
const projectRoot = findNearestProjectRoot(cwd);
|
|
548
|
-
return projectRoot ? path.join(getProjectConfigDir(projectRoot), "settings.json") : null;
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
function readSettingsFileStrict(filePath: string): Record<string, unknown> {
|
|
552
|
-
if (!fs.existsSync(filePath)) return {};
|
|
553
|
-
let raw: string;
|
|
554
|
-
try {
|
|
555
|
-
raw = fs.readFileSync(filePath, "utf-8");
|
|
556
|
-
} catch (error) {
|
|
557
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
558
|
-
throw new Error(`Failed to read settings file '${filePath}': ${message}`, { cause: error });
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
let parsed: unknown;
|
|
562
|
-
try {
|
|
563
|
-
parsed = JSON.parse(raw);
|
|
564
|
-
} catch (error) {
|
|
565
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
566
|
-
throw new Error(`Failed to parse settings file '${filePath}': ${message}`, { cause: error });
|
|
567
|
-
}
|
|
568
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
569
|
-
throw new Error(`Settings file '${filePath}' must contain a JSON object.`);
|
|
570
|
-
}
|
|
571
|
-
return parsed as Record<string, unknown>;
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
function writeSettingsFile(filePath: string, settings: Record<string, unknown>): void {
|
|
575
|
-
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
576
|
-
fs.writeFileSync(filePath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
function parseOverrideStringArrayOrFalse(
|
|
580
|
-
value: unknown,
|
|
581
|
-
meta: { filePath: string; name: string; field: string },
|
|
582
|
-
): string[] | false | undefined {
|
|
583
|
-
if (value === undefined) return undefined;
|
|
584
|
-
if (value === false) return false;
|
|
585
|
-
if (!Array.isArray(value)) {
|
|
586
|
-
throw new Error(`Builtin override '${meta.name}' in '${meta.filePath}' has invalid '${meta.field}'; expected an array of strings or false.`);
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
const items: string[] = [];
|
|
590
|
-
for (const item of value) {
|
|
591
|
-
if (typeof item !== "string") {
|
|
592
|
-
throw new Error(`Builtin override '${meta.name}' in '${meta.filePath}' has invalid '${meta.field}'; expected an array of strings or false.`);
|
|
593
|
-
}
|
|
594
|
-
const trimmed = item.trim();
|
|
595
|
-
if (trimmed) items.push(trimmed);
|
|
596
|
-
}
|
|
597
|
-
return items;
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
function parseBuiltinOverrideEntry(
|
|
601
|
-
name: string,
|
|
602
|
-
value: unknown,
|
|
603
|
-
filePath: string,
|
|
604
|
-
): BuiltinAgentOverrideConfig | undefined {
|
|
605
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
606
|
-
throw new Error(`Builtin override '${name}' in '${filePath}' must be an object.`);
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
const input = value as Record<string, unknown>;
|
|
610
|
-
const override: BuiltinAgentOverrideConfig = {};
|
|
611
|
-
|
|
612
|
-
if ("model" in input) {
|
|
613
|
-
if (typeof input.model === "string" || input.model === false) override.model = input.model;
|
|
614
|
-
else throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'model'; expected a string or false.`);
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
if ("thinking" in input) {
|
|
618
|
-
if (typeof input.thinking === "string" || input.thinking === false) override.thinking = input.thinking;
|
|
619
|
-
else throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'thinking'; expected a string or false.`);
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
if ("systemPromptMode" in input) {
|
|
623
|
-
if (input.systemPromptMode === "append" || input.systemPromptMode === "replace") {
|
|
624
|
-
override.systemPromptMode = input.systemPromptMode;
|
|
625
|
-
} else {
|
|
626
|
-
throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'systemPromptMode'; expected 'append' or 'replace'.`);
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
if ("inheritProjectContext" in input) {
|
|
631
|
-
if (typeof input.inheritProjectContext === "boolean") {
|
|
632
|
-
override.inheritProjectContext = input.inheritProjectContext;
|
|
633
|
-
} else {
|
|
634
|
-
throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'inheritProjectContext'; expected a boolean.`);
|
|
635
|
-
}
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
if ("inheritSkills" in input) {
|
|
639
|
-
if (typeof input.inheritSkills === "boolean") {
|
|
640
|
-
override.inheritSkills = input.inheritSkills;
|
|
641
|
-
} else {
|
|
642
|
-
throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'inheritSkills'; expected a boolean.`);
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
if ("defaultContext" in input) {
|
|
647
|
-
if (input.defaultContext === "fresh" || input.defaultContext === "fork" || input.defaultContext === false) {
|
|
648
|
-
override.defaultContext = input.defaultContext;
|
|
649
|
-
} else {
|
|
650
|
-
throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'defaultContext'; expected 'fresh', 'fork', or false.`);
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
if ("disabled" in input) {
|
|
655
|
-
if (typeof input.disabled === "boolean") {
|
|
656
|
-
override.disabled = input.disabled;
|
|
657
|
-
} else {
|
|
658
|
-
throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'disabled'; expected a boolean.`);
|
|
659
|
-
}
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
if ("completionGuard" in input) {
|
|
663
|
-
if (typeof input.completionGuard === "boolean") {
|
|
664
|
-
override.completionGuard = input.completionGuard;
|
|
665
|
-
} else {
|
|
666
|
-
throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'completionGuard'; expected a boolean.`);
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
if ("toolBudget" in input) {
|
|
671
|
-
if (input.toolBudget === false) {
|
|
672
|
-
override.toolBudget = false;
|
|
673
|
-
} else if (input.toolBudget && typeof input.toolBudget === "object" && !Array.isArray(input.toolBudget)) {
|
|
674
|
-
override.toolBudget = input.toolBudget as ToolBudgetConfig;
|
|
675
|
-
} else {
|
|
676
|
-
throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'toolBudget'; expected an object or false.`);
|
|
677
|
-
}
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
if ("systemPrompt" in input) {
|
|
681
|
-
if (typeof input.systemPrompt === "string") override.systemPrompt = input.systemPrompt;
|
|
682
|
-
else throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'systemPrompt'; expected a string.`);
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
const fallbackModels = parseOverrideStringArrayOrFalse(input.fallbackModels, { filePath, name, field: "fallbackModels" });
|
|
686
|
-
if (fallbackModels !== undefined) override.fallbackModels = fallbackModels;
|
|
687
|
-
|
|
688
|
-
const skills = parseOverrideStringArrayOrFalse(input.skills, { filePath, name, field: "skills" });
|
|
689
|
-
if (skills !== undefined) override.skills = skills;
|
|
690
|
-
|
|
691
|
-
const tools = parseOverrideStringArrayOrFalse(input.tools, { filePath, name, field: "tools" });
|
|
692
|
-
if (tools !== undefined) override.tools = tools;
|
|
693
|
-
|
|
694
|
-
const subagentOnlyExtensions = parseOverrideStringArrayOrFalse(input.subagentOnlyExtensions, { filePath, name, field: "subagentOnlyExtensions" });
|
|
695
|
-
if (subagentOnlyExtensions !== undefined) override.subagentOnlyExtensions = subagentOnlyExtensions;
|
|
696
|
-
|
|
697
|
-
return Object.keys(override).length > 0 ? override : undefined;
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
function readSubagentSettings(filePath: string | null): SubagentSettings {
|
|
701
|
-
if (!filePath) return EMPTY_SUBAGENT_SETTINGS;
|
|
702
|
-
const settings = readSettingsFileStrict(filePath);
|
|
703
|
-
const subagents = settings.subagents;
|
|
704
|
-
if (!subagents || typeof subagents !== "object" || Array.isArray(subagents)) return EMPTY_SUBAGENT_SETTINGS;
|
|
705
|
-
|
|
706
|
-
const subagentsObject = subagents as Record<string, unknown>;
|
|
707
|
-
let disableBuiltins: boolean | undefined;
|
|
708
|
-
if ("disableBuiltins" in subagentsObject) {
|
|
709
|
-
if (typeof subagentsObject.disableBuiltins === "boolean") {
|
|
710
|
-
disableBuiltins = subagentsObject.disableBuiltins;
|
|
711
|
-
} else {
|
|
712
|
-
throw new Error(`Subagent settings in '${filePath}' have invalid 'disableBuiltins'; expected a boolean.`);
|
|
713
|
-
}
|
|
714
|
-
}
|
|
715
|
-
let disableThinking: boolean | undefined;
|
|
716
|
-
if ("disableThinking" in subagentsObject) {
|
|
717
|
-
if (typeof subagentsObject.disableThinking === "boolean") {
|
|
718
|
-
disableThinking = subagentsObject.disableThinking;
|
|
719
|
-
} else {
|
|
720
|
-
throw new Error(`Subagent settings in '${filePath}' have invalid 'disableThinking'; expected a boolean.`);
|
|
721
|
-
}
|
|
722
|
-
}
|
|
723
|
-
let defaultModel: string | undefined;
|
|
724
|
-
if ("defaultModel" in subagentsObject) {
|
|
725
|
-
if (typeof subagentsObject.defaultModel === "string" && subagentsObject.defaultModel.trim()) {
|
|
726
|
-
defaultModel = subagentsObject.defaultModel.trim();
|
|
727
|
-
} else {
|
|
728
|
-
throw new Error(`Subagent settings in '${filePath}' have invalid 'defaultModel'; expected a non-empty string.`);
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
const modelScope = parseModelScopeConfig(subagentsObject.modelScope, { filePath });
|
|
732
|
-
|
|
733
|
-
const parsed: Record<string, BuiltinAgentOverrideConfig> = {};
|
|
734
|
-
const agentOverrides = subagentsObject.agentOverrides;
|
|
735
|
-
if (!agentOverrides || typeof agentOverrides !== "object" || Array.isArray(agentOverrides)) {
|
|
736
|
-
return { overrides: parsed, defaultModel, disableBuiltins, disableThinking, modelScope };
|
|
737
|
-
}
|
|
738
|
-
for (const [name, value] of Object.entries(agentOverrides)) {
|
|
739
|
-
const override = parseBuiltinOverrideEntry(name, value, filePath);
|
|
740
|
-
if (override) parsed[name] = override;
|
|
741
|
-
}
|
|
742
|
-
return { overrides: parsed, defaultModel, disableBuiltins, disableThinking, modelScope };
|
|
743
|
-
}
|
|
744
|
-
|
|
745
|
-
function resolveSubagentDefaultModel(
|
|
746
|
-
userSettings: SubagentSettings,
|
|
747
|
-
projectSettings: SubagentSettings,
|
|
748
|
-
userSettingsPath: string,
|
|
749
|
-
projectSettingsPath: string | null,
|
|
750
|
-
): AgentModelSourceInfo | undefined {
|
|
751
|
-
if (projectSettingsPath && projectSettings.defaultModel !== undefined) {
|
|
752
|
-
return { type: "subagents.defaultModel", scope: "project", path: projectSettingsPath, model: projectSettings.defaultModel };
|
|
753
|
-
}
|
|
754
|
-
return userSettings.defaultModel !== undefined
|
|
755
|
-
? { type: "subagents.defaultModel", scope: "user", path: userSettingsPath, model: userSettings.defaultModel }
|
|
756
|
-
: undefined;
|
|
757
|
-
}
|
|
758
|
-
|
|
759
|
-
function applySubagentDefaultModel(agents: AgentConfig[], defaultModel: AgentModelSourceInfo | undefined): AgentConfig[] {
|
|
760
|
-
if (!defaultModel) return agents;
|
|
761
|
-
return agents.map((agent) => {
|
|
762
|
-
if (agent.model !== undefined) return agent;
|
|
763
|
-
const next = { ...agent, model: defaultModel.model, modelSource: defaultModel };
|
|
764
|
-
const frontmatterFields = agentFrontmatterFields.get(agent);
|
|
765
|
-
if (frontmatterFields) agentFrontmatterFields.set(next, frontmatterFields);
|
|
766
|
-
return next;
|
|
767
|
-
});
|
|
768
|
-
}
|
|
769
|
-
|
|
770
|
-
function applyBuiltinOverride(
|
|
771
|
-
agent: AgentConfig,
|
|
772
|
-
override: BuiltinAgentOverrideConfig,
|
|
773
|
-
meta: { scope: "user" | "project"; path: string },
|
|
774
|
-
): AgentConfig {
|
|
775
|
-
const next: AgentConfig = {
|
|
776
|
-
...agent,
|
|
777
|
-
override: { ...meta, base: cloneOverrideBase(agent) },
|
|
778
|
-
};
|
|
779
|
-
|
|
780
|
-
if (override.model !== undefined) next.model = override.model === false ? undefined : override.model;
|
|
781
|
-
if (override.fallbackModels !== undefined) {
|
|
782
|
-
next.fallbackModels = override.fallbackModels === false ? undefined : [...override.fallbackModels];
|
|
783
|
-
}
|
|
784
|
-
if (override.thinking !== undefined) next.thinking = override.thinking === false ? undefined : override.thinking;
|
|
785
|
-
if (override.systemPromptMode !== undefined) next.systemPromptMode = override.systemPromptMode;
|
|
786
|
-
if (override.inheritProjectContext !== undefined) next.inheritProjectContext = override.inheritProjectContext;
|
|
787
|
-
if (override.inheritSkills !== undefined) next.inheritSkills = override.inheritSkills;
|
|
788
|
-
if (override.defaultContext !== undefined) next.defaultContext = override.defaultContext === false ? undefined : override.defaultContext;
|
|
789
|
-
if (override.disabled !== undefined) next.disabled = override.disabled;
|
|
790
|
-
if (override.systemPrompt !== undefined) next.systemPrompt = override.systemPrompt;
|
|
791
|
-
if (override.skills !== undefined) next.skills = override.skills === false ? undefined : [...override.skills];
|
|
792
|
-
if (override.tools !== undefined) {
|
|
793
|
-
const { tools, mcpDirectTools } = splitToolList(override.tools === false ? [] : override.tools);
|
|
794
|
-
next.tools = tools;
|
|
795
|
-
next.mcpDirectTools = mcpDirectTools;
|
|
796
|
-
}
|
|
797
|
-
if (override.subagentOnlyExtensions !== undefined) {
|
|
798
|
-
next.subagentOnlyExtensions = override.subagentOnlyExtensions === false ? undefined : [...override.subagentOnlyExtensions];
|
|
799
|
-
}
|
|
800
|
-
if (override.completionGuard !== undefined) next.completionGuard = override.completionGuard;
|
|
801
|
-
if (override.toolBudget !== undefined) next.toolBudget = override.toolBudget === false ? undefined : override.toolBudget;
|
|
802
|
-
|
|
803
|
-
return next;
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
function clearBuiltinThinking(agent: AgentConfig, meta: { scope: "user" | "project"; path: string }): AgentConfig {
|
|
807
|
-
if (agent.thinking === undefined) return agent;
|
|
808
|
-
return {
|
|
809
|
-
...agent,
|
|
810
|
-
thinking: undefined,
|
|
811
|
-
override: agent.override ?? { ...meta, base: cloneOverrideBase(agent) },
|
|
812
|
-
};
|
|
813
|
-
}
|
|
814
|
-
|
|
815
|
-
function applyBuiltinOverrides(
|
|
816
|
-
builtinAgents: AgentConfig[],
|
|
817
|
-
userSettings: SubagentSettings,
|
|
818
|
-
projectSettings: SubagentSettings,
|
|
819
|
-
userSettingsPath: string,
|
|
820
|
-
projectSettingsPath: string | null,
|
|
821
|
-
): AgentConfig[] {
|
|
822
|
-
const projectBulkDisabled = projectSettings.disableBuiltins === true && projectSettingsPath !== null;
|
|
823
|
-
const userBulkDisabled = projectSettings.disableBuiltins === undefined && userSettings.disableBuiltins === true;
|
|
824
|
-
const projectThinkingConfigured = projectSettings.disableThinking !== undefined && projectSettingsPath !== null;
|
|
825
|
-
const disableThinking = projectThinkingConfigured ? projectSettings.disableThinking === true : userSettings.disableThinking === true;
|
|
826
|
-
const disableThinkingMeta = projectThinkingConfigured
|
|
827
|
-
? { scope: "project" as const, path: projectSettingsPath! }
|
|
828
|
-
: { scope: "user" as const, path: userSettingsPath };
|
|
829
|
-
|
|
830
|
-
const applyGlobalThinking = (agent: AgentConfig, hasExplicitThinkingOverride: boolean): AgentConfig => {
|
|
831
|
-
if (!disableThinking || hasExplicitThinkingOverride) return agent;
|
|
832
|
-
return clearBuiltinThinking(agent, disableThinkingMeta);
|
|
833
|
-
};
|
|
834
|
-
|
|
835
|
-
return builtinAgents.map((agent) => {
|
|
836
|
-
const projectOverride = projectSettings.overrides[agent.name];
|
|
837
|
-
if (projectOverride && projectSettingsPath) {
|
|
838
|
-
return applyGlobalThinking(
|
|
839
|
-
applyBuiltinOverride(agent, projectOverride, { scope: "project", path: projectSettingsPath }),
|
|
840
|
-
projectOverride.thinking !== undefined,
|
|
841
|
-
);
|
|
842
|
-
}
|
|
843
|
-
|
|
844
|
-
if (projectBulkDisabled && projectSettingsPath) {
|
|
845
|
-
return applyGlobalThinking(
|
|
846
|
-
applyBuiltinOverride(agent, { disabled: true }, { scope: "project", path: projectSettingsPath }),
|
|
847
|
-
false,
|
|
848
|
-
);
|
|
849
|
-
}
|
|
850
|
-
|
|
851
|
-
const userOverride = userSettings.overrides[agent.name];
|
|
852
|
-
if (userOverride) {
|
|
853
|
-
return applyGlobalThinking(
|
|
854
|
-
applyBuiltinOverride(agent, userOverride, { scope: "user", path: userSettingsPath }),
|
|
855
|
-
!projectThinkingConfigured && userOverride.thinking !== undefined,
|
|
856
|
-
);
|
|
857
|
-
}
|
|
858
|
-
|
|
859
|
-
if (userBulkDisabled) {
|
|
860
|
-
return applyGlobalThinking(
|
|
861
|
-
applyBuiltinOverride(agent, { disabled: true }, { scope: "user", path: userSettingsPath }),
|
|
862
|
-
false,
|
|
863
|
-
);
|
|
864
|
-
}
|
|
865
|
-
|
|
866
|
-
return applyGlobalThinking(agent, false);
|
|
867
|
-
});
|
|
868
|
-
}
|
|
869
|
-
|
|
870
|
-
function customAgentHasFrontmatterField(agent: AgentConfig, ...fields: string[]): boolean {
|
|
871
|
-
const frontmatterFields = agentFrontmatterFields.get(agent);
|
|
872
|
-
return frontmatterFields ? fields.some((field) => frontmatterFields.has(field)) : false;
|
|
873
|
-
}
|
|
874
|
-
|
|
875
|
-
function applyCustomAgentOverride(
|
|
876
|
-
agent: AgentConfig,
|
|
877
|
-
override: BuiltinAgentOverrideConfig,
|
|
878
|
-
meta: { scope: "user" | "project"; path: string },
|
|
879
|
-
): AgentConfig {
|
|
880
|
-
let next: AgentConfig | undefined;
|
|
881
|
-
let anyFilled = false;
|
|
882
|
-
|
|
883
|
-
const mutable = (): AgentConfig => {
|
|
884
|
-
next ??= { ...agent };
|
|
885
|
-
return next;
|
|
886
|
-
};
|
|
887
|
-
|
|
888
|
-
const fill = <K extends keyof AgentConfig>(
|
|
889
|
-
field: K,
|
|
890
|
-
frontmatterFields: string[],
|
|
891
|
-
value: AgentConfig[K],
|
|
892
|
-
): void => {
|
|
893
|
-
if (customAgentHasFrontmatterField(agent, ...frontmatterFields)) return;
|
|
894
|
-
mutable()[field] = value;
|
|
895
|
-
anyFilled = true;
|
|
896
|
-
};
|
|
897
|
-
|
|
898
|
-
if (override.model !== undefined) {
|
|
899
|
-
fill("model", ["model"], override.model === false ? undefined : override.model);
|
|
900
|
-
}
|
|
901
|
-
if (override.fallbackModels !== undefined) {
|
|
902
|
-
fill(
|
|
903
|
-
"fallbackModels",
|
|
904
|
-
["fallbackModels"],
|
|
905
|
-
override.fallbackModels === false ? undefined : [...override.fallbackModels],
|
|
906
|
-
);
|
|
907
|
-
}
|
|
908
|
-
if (override.thinking !== undefined) {
|
|
909
|
-
fill("thinking", ["thinking"], override.thinking === false ? undefined : override.thinking);
|
|
910
|
-
}
|
|
911
|
-
if (override.systemPromptMode !== undefined) {
|
|
912
|
-
fill("systemPromptMode", ["systemPromptMode"], override.systemPromptMode);
|
|
913
|
-
}
|
|
914
|
-
if (override.inheritProjectContext !== undefined) {
|
|
915
|
-
fill("inheritProjectContext", ["inheritProjectContext"], override.inheritProjectContext);
|
|
916
|
-
}
|
|
917
|
-
if (override.inheritSkills !== undefined) {
|
|
918
|
-
fill("inheritSkills", ["inheritSkills"], override.inheritSkills);
|
|
919
|
-
}
|
|
920
|
-
if (override.defaultContext !== undefined) {
|
|
921
|
-
fill("defaultContext", ["defaultContext"], override.defaultContext === false ? undefined : override.defaultContext);
|
|
922
|
-
}
|
|
923
|
-
if (override.disabled !== undefined && agent.disabled === undefined) {
|
|
924
|
-
mutable().disabled = override.disabled;
|
|
925
|
-
anyFilled = true;
|
|
926
|
-
}
|
|
927
|
-
if (override.skills !== undefined) {
|
|
928
|
-
fill("skills", ["skill", "skills"], override.skills === false ? undefined : [...override.skills]);
|
|
929
|
-
}
|
|
930
|
-
if (override.tools !== undefined && !customAgentHasFrontmatterField(agent, "tools")) {
|
|
931
|
-
const { tools, mcpDirectTools } = splitToolList(override.tools === false ? [] : override.tools);
|
|
932
|
-
const target = mutable();
|
|
933
|
-
target.tools = tools;
|
|
934
|
-
target.mcpDirectTools = mcpDirectTools;
|
|
935
|
-
anyFilled = true;
|
|
936
|
-
}
|
|
937
|
-
if (override.subagentOnlyExtensions !== undefined) {
|
|
938
|
-
fill(
|
|
939
|
-
"subagentOnlyExtensions",
|
|
940
|
-
["subagentOnlyExtensions"],
|
|
941
|
-
override.subagentOnlyExtensions === false ? undefined : [...override.subagentOnlyExtensions],
|
|
942
|
-
);
|
|
943
|
-
}
|
|
944
|
-
if (override.completionGuard !== undefined) {
|
|
945
|
-
fill("completionGuard", ["completionGuard"], override.completionGuard);
|
|
946
|
-
}
|
|
947
|
-
if (override.toolBudget !== undefined) {
|
|
948
|
-
fill("toolBudget", ["toolBudget"], override.toolBudget === false ? undefined : override.toolBudget);
|
|
949
|
-
}
|
|
950
|
-
|
|
951
|
-
if (!anyFilled || !next) return agent;
|
|
952
|
-
next.override = { ...meta, base: cloneOverrideBase(agent) };
|
|
953
|
-
return next;
|
|
954
|
-
}
|
|
955
|
-
|
|
956
|
-
function applyCustomAgentOverrides(
|
|
957
|
-
agents: AgentConfig[],
|
|
958
|
-
userSettings: SubagentSettings,
|
|
959
|
-
projectSettings: SubagentSettings,
|
|
960
|
-
userSettingsPath: string,
|
|
961
|
-
projectSettingsPath: string | null,
|
|
962
|
-
): AgentConfig[] {
|
|
963
|
-
return agents.map((agent) => {
|
|
964
|
-
const projectOverride = projectSettings.overrides[agent.name];
|
|
965
|
-
if (projectOverride && projectSettingsPath) {
|
|
966
|
-
return applyCustomAgentOverride(agent, projectOverride, { scope: "project", path: projectSettingsPath });
|
|
967
|
-
}
|
|
968
|
-
|
|
969
|
-
const userOverride = userSettings.overrides[agent.name];
|
|
970
|
-
if (userOverride) {
|
|
971
|
-
return applyCustomAgentOverride(agent, userOverride, { scope: "user", path: userSettingsPath });
|
|
972
|
-
}
|
|
973
|
-
|
|
974
|
-
return agent;
|
|
975
|
-
});
|
|
976
|
-
}
|
|
977
|
-
|
|
978
|
-
export function buildBuiltinOverrideConfig(
|
|
979
|
-
base: BuiltinAgentOverrideBase,
|
|
980
|
-
draft: Pick<AgentConfig, "model" | "fallbackModels" | "thinking" | "systemPromptMode" | "inheritProjectContext" | "inheritSkills" | "defaultContext" | "disabled" | "systemPrompt" | "skills" | "tools" | "mcpDirectTools" | "subagentOnlyExtensions" | "completionGuard" | "toolBudget">,
|
|
981
|
-
): BuiltinAgentOverrideConfig | undefined {
|
|
982
|
-
const override: BuiltinAgentOverrideConfig = {};
|
|
983
|
-
|
|
984
|
-
if (draft.model !== base.model) override.model = draft.model ?? false;
|
|
985
|
-
if (!arraysEqual(draft.fallbackModels, base.fallbackModels)) override.fallbackModels = draft.fallbackModels ? [...draft.fallbackModels] : false;
|
|
986
|
-
if (draft.thinking !== base.thinking) override.thinking = draft.thinking ?? false;
|
|
987
|
-
if (draft.systemPromptMode !== base.systemPromptMode) override.systemPromptMode = draft.systemPromptMode;
|
|
988
|
-
if (draft.inheritProjectContext !== base.inheritProjectContext) override.inheritProjectContext = draft.inheritProjectContext;
|
|
989
|
-
if (draft.inheritSkills !== base.inheritSkills) override.inheritSkills = draft.inheritSkills;
|
|
990
|
-
if (draft.defaultContext !== base.defaultContext) override.defaultContext = draft.defaultContext ?? false;
|
|
991
|
-
if (draft.disabled !== base.disabled) override.disabled = draft.disabled ?? false;
|
|
992
|
-
if (draft.systemPrompt !== base.systemPrompt) override.systemPrompt = draft.systemPrompt;
|
|
993
|
-
if (!arraysEqual(draft.skills, base.skills)) override.skills = draft.skills ? [...draft.skills] : false;
|
|
994
|
-
|
|
995
|
-
const baseTools = joinToolList(base);
|
|
996
|
-
const draftTools = joinToolList(draft);
|
|
997
|
-
if (!arraysEqual(draftTools, baseTools)) override.tools = draftTools ? [...draftTools] : false;
|
|
998
|
-
if (!arraysEqual(draft.subagentOnlyExtensions, base.subagentOnlyExtensions)) {
|
|
999
|
-
override.subagentOnlyExtensions = draft.subagentOnlyExtensions ? [...draft.subagentOnlyExtensions] : false;
|
|
1000
|
-
}
|
|
1001
|
-
if ((draft.completionGuard !== false) !== (base.completionGuard !== false)) {
|
|
1002
|
-
override.completionGuard = draft.completionGuard !== false;
|
|
1003
|
-
}
|
|
1004
|
-
if (JSON.stringify(draft.toolBudget) !== JSON.stringify(base.toolBudget)) override.toolBudget = draft.toolBudget ?? false;
|
|
1005
|
-
|
|
1006
|
-
return Object.keys(override).length > 0 ? override : undefined;
|
|
1007
|
-
}
|
|
1008
|
-
|
|
1009
|
-
export function saveBuiltinAgentOverride(
|
|
1010
|
-
cwd: string,
|
|
1011
|
-
name: string,
|
|
1012
|
-
scope: "user" | "project",
|
|
1013
|
-
override: BuiltinAgentOverrideConfig,
|
|
1014
|
-
): string {
|
|
1015
|
-
const filePath = scope === "project" ? getProjectAgentSettingsPath(cwd) : getUserAgentSettingsPath();
|
|
1016
|
-
if (!filePath) throw new Error("Project override is not available here. No project config root was found.");
|
|
1017
|
-
|
|
1018
|
-
const settings = readSettingsFileStrict(filePath);
|
|
1019
|
-
const subagents = settings.subagents && typeof settings.subagents === "object" && !Array.isArray(settings.subagents)
|
|
1020
|
-
? { ...(settings.subagents as Record<string, unknown>) }
|
|
1021
|
-
: {};
|
|
1022
|
-
const agentOverrides = subagents.agentOverrides && typeof subagents.agentOverrides === "object" && !Array.isArray(subagents.agentOverrides)
|
|
1023
|
-
? { ...(subagents.agentOverrides as Record<string, unknown>) }
|
|
1024
|
-
: {};
|
|
1025
|
-
|
|
1026
|
-
agentOverrides[name] = cloneOverrideValue(override);
|
|
1027
|
-
subagents.agentOverrides = agentOverrides;
|
|
1028
|
-
settings.subagents = subagents;
|
|
1029
|
-
writeSettingsFile(filePath, settings);
|
|
1030
|
-
return filePath;
|
|
1031
|
-
}
|
|
1032
|
-
|
|
1033
|
-
export function removeBuiltinAgentOverride(cwd: string, name: string, scope: "user" | "project"): { path: string; removed: boolean } {
|
|
1034
|
-
const filePath = scope === "project" ? getProjectAgentSettingsPath(cwd) : getUserAgentSettingsPath();
|
|
1035
|
-
if (!filePath) throw new Error("Project override is not available here. No project config root was found.");
|
|
1036
|
-
if (!fs.existsSync(filePath)) return { path: filePath, removed: false };
|
|
1037
|
-
|
|
1038
|
-
const settings = readSettingsFileStrict(filePath);
|
|
1039
|
-
const subagents = settings.subagents;
|
|
1040
|
-
if (!subagents || typeof subagents !== "object" || Array.isArray(subagents)) return { path: filePath, removed: false };
|
|
1041
|
-
const nextSubagents = { ...(subagents as Record<string, unknown>) };
|
|
1042
|
-
const agentOverrides = nextSubagents.agentOverrides;
|
|
1043
|
-
if (!agentOverrides || typeof agentOverrides !== "object" || Array.isArray(agentOverrides)) return { path: filePath, removed: false };
|
|
1044
|
-
|
|
1045
|
-
const nextOverrides = { ...(agentOverrides as Record<string, unknown>) };
|
|
1046
|
-
if (!Object.prototype.hasOwnProperty.call(nextOverrides, name)) return { path: filePath, removed: false };
|
|
1047
|
-
delete nextOverrides[name];
|
|
1048
|
-
if (Object.keys(nextOverrides).length > 0) nextSubagents.agentOverrides = nextOverrides;
|
|
1049
|
-
else delete nextSubagents.agentOverrides;
|
|
1050
|
-
|
|
1051
|
-
if (Object.keys(nextSubagents).length > 0) settings.subagents = nextSubagents;
|
|
1052
|
-
else delete settings.subagents;
|
|
1053
|
-
|
|
1054
|
-
writeSettingsFile(filePath, settings);
|
|
1055
|
-
return { path: filePath, removed: true };
|
|
1056
|
-
}
|
|
1057
|
-
|
|
1058
|
-
export function mergeBuiltinAgentOverride(
|
|
1059
|
-
cwd: string,
|
|
1060
|
-
name: string,
|
|
1061
|
-
scope: "user" | "project",
|
|
1062
|
-
fields: BuiltinAgentOverrideConfig,
|
|
1063
|
-
): string {
|
|
1064
|
-
const filePath = scope === "project" ? getProjectAgentSettingsPath(cwd) : getUserAgentSettingsPath();
|
|
1065
|
-
if (!filePath) throw new Error("Project override is not available here. No project config root was found.");
|
|
1066
|
-
|
|
1067
|
-
const settings = readSettingsFileStrict(filePath);
|
|
1068
|
-
const subagents = settings.subagents && typeof settings.subagents === "object" && !Array.isArray(settings.subagents)
|
|
1069
|
-
? { ...(settings.subagents as Record<string, unknown>) }
|
|
1070
|
-
: {};
|
|
1071
|
-
const agentOverrides = subagents.agentOverrides && typeof subagents.agentOverrides === "object" && !Array.isArray(subagents.agentOverrides)
|
|
1072
|
-
? { ...(subagents.agentOverrides as Record<string, unknown>) }
|
|
1073
|
-
: {};
|
|
1074
|
-
|
|
1075
|
-
const existing = agentOverrides[name];
|
|
1076
|
-
const base = existing && typeof existing === "object" && !Array.isArray(existing)
|
|
1077
|
-
? existing as Record<string, unknown>
|
|
1078
|
-
: {};
|
|
1079
|
-
agentOverrides[name] = { ...base, ...cloneOverrideValue(fields) };
|
|
1080
|
-
subagents.agentOverrides = agentOverrides;
|
|
1081
|
-
settings.subagents = subagents;
|
|
1082
|
-
writeSettingsFile(filePath, settings);
|
|
1083
|
-
return filePath;
|
|
1084
|
-
}
|
|
1085
|
-
|
|
1086
|
-
export function removeBuiltinAgentOverrideFields(
|
|
1087
|
-
cwd: string,
|
|
1088
|
-
name: string,
|
|
1089
|
-
scope: "user" | "project",
|
|
1090
|
-
fields: string[],
|
|
1091
|
-
): { path: string; removed: boolean } {
|
|
1092
|
-
const filePath = scope === "project" ? getProjectAgentSettingsPath(cwd) : getUserAgentSettingsPath();
|
|
1093
|
-
if (!filePath) throw new Error("Project override is not available here. No project config root was found.");
|
|
1094
|
-
if (!fs.existsSync(filePath)) return { path: filePath, removed: false };
|
|
1095
|
-
|
|
1096
|
-
const settings = readSettingsFileStrict(filePath);
|
|
1097
|
-
const subagents = settings.subagents;
|
|
1098
|
-
if (!subagents || typeof subagents !== "object" || Array.isArray(subagents)) return { path: filePath, removed: false };
|
|
1099
|
-
const agentOverrides = (subagents as Record<string, unknown>).agentOverrides;
|
|
1100
|
-
if (!agentOverrides || typeof agentOverrides !== "object" || Array.isArray(agentOverrides)) return { path: filePath, removed: false };
|
|
1101
|
-
|
|
1102
|
-
const entry = (agentOverrides as Record<string, unknown>)[name];
|
|
1103
|
-
if (!entry || typeof entry !== "object" || Array.isArray(entry)) return { path: filePath, removed: false };
|
|
1104
|
-
|
|
1105
|
-
const nextEntry: Record<string, unknown> = { ...(entry as Record<string, unknown>) };
|
|
1106
|
-
let removed = false;
|
|
1107
|
-
for (const field of fields) {
|
|
1108
|
-
if (Object.prototype.hasOwnProperty.call(nextEntry, field)) {
|
|
1109
|
-
delete nextEntry[field];
|
|
1110
|
-
removed = true;
|
|
1111
|
-
}
|
|
1112
|
-
}
|
|
1113
|
-
if (!removed) return { path: filePath, removed: false };
|
|
1114
|
-
|
|
1115
|
-
const nextSubagents = { ...(subagents as Record<string, unknown>) };
|
|
1116
|
-
if (Object.keys(nextEntry).length > 0) {
|
|
1117
|
-
(nextSubagents.agentOverrides as Record<string, unknown>)[name] = nextEntry;
|
|
1118
|
-
} else {
|
|
1119
|
-
const nextOverrides = { ...(agentOverrides as Record<string, unknown>) };
|
|
1120
|
-
delete nextOverrides[name];
|
|
1121
|
-
if (Object.keys(nextOverrides).length > 0) nextSubagents.agentOverrides = nextOverrides;
|
|
1122
|
-
else delete nextSubagents.agentOverrides;
|
|
1123
|
-
}
|
|
1124
|
-
if (Object.keys(nextSubagents).length > 0) settings.subagents = nextSubagents;
|
|
1125
|
-
else delete settings.subagents;
|
|
1126
|
-
writeSettingsFile(filePath, settings);
|
|
1127
|
-
return { path: filePath, removed: true };
|
|
1128
|
-
}
|
|
1129
|
-
|
|
1130
|
-
function listFilesRecursive(dir: string, predicate: (fileName: string) => boolean): string[] {
|
|
1131
|
-
const files: string[] = [];
|
|
1132
|
-
if (!fs.existsSync(dir)) return files;
|
|
1133
|
-
|
|
1134
|
-
let entries: fs.Dirent[];
|
|
1135
|
-
try {
|
|
1136
|
-
entries = fs.readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
|
|
1137
|
-
} catch {
|
|
1138
|
-
return files;
|
|
1139
|
-
}
|
|
1140
|
-
|
|
1141
|
-
for (const entry of entries) {
|
|
1142
|
-
const filePath = path.join(dir, entry.name);
|
|
1143
|
-
if (entry.isDirectory()) {
|
|
1144
|
-
files.push(...listFilesRecursive(filePath, predicate));
|
|
1145
|
-
continue;
|
|
1146
|
-
}
|
|
1147
|
-
if (!entry.isFile() && !entry.isSymbolicLink()) continue;
|
|
1148
|
-
if (!predicate(entry.name)) continue;
|
|
1149
|
-
files.push(filePath);
|
|
1150
|
-
}
|
|
1151
|
-
return files;
|
|
1152
|
-
}
|
|
1153
|
-
|
|
1154
|
-
function isLegacyAgentSkillPath(rootDir: string, filePath: string): boolean {
|
|
1155
|
-
const relative = path.relative(rootDir, filePath);
|
|
1156
|
-
const parts = relative.split(path.sep).map((part) => part.toLowerCase());
|
|
1157
|
-
if (path.basename(rootDir).toLowerCase() === ".agents") {
|
|
1158
|
-
parts.unshift(".agents");
|
|
1159
|
-
}
|
|
1160
|
-
return parts.some((part, index) => part === ".agents" && parts[index + 1] === "skills");
|
|
1161
|
-
}
|
|
1162
|
-
|
|
1163
|
-
function loadAgentsFromDir(dir: string, source: AgentSource): AgentConfig[] {
|
|
1164
|
-
const agents: AgentConfig[] = [];
|
|
1165
|
-
|
|
1166
|
-
for (const filePath of listFilesRecursive(dir, (fileName) => fileName.endsWith(".md") && !fileName.endsWith(".chain.md"))) {
|
|
1167
|
-
if (isLegacyAgentSkillPath(dir, filePath)) {
|
|
1168
|
-
continue;
|
|
1169
|
-
}
|
|
1170
|
-
|
|
1171
|
-
let content: string;
|
|
1172
|
-
try {
|
|
1173
|
-
content = fs.readFileSync(filePath, "utf-8");
|
|
1174
|
-
} catch {
|
|
1175
|
-
continue;
|
|
1176
|
-
}
|
|
1177
|
-
|
|
1178
|
-
const { frontmatter, body } = parseFrontmatter(content);
|
|
1179
|
-
|
|
1180
|
-
if (!frontmatter.name || !frontmatter.description) {
|
|
1181
|
-
continue;
|
|
1182
|
-
}
|
|
1183
|
-
|
|
1184
|
-
const localName = frontmatter.name;
|
|
1185
|
-
const parsedPackage = parsePackageName(frontmatter.package, `Agent '${localName}' package`);
|
|
1186
|
-
if (parsedPackage.error) continue;
|
|
1187
|
-
const packageName = parsedPackage.packageName;
|
|
1188
|
-
const runtimeName = buildRuntimeName(localName, packageName);
|
|
1189
|
-
|
|
1190
|
-
const rawTools = frontmatter.tools
|
|
1191
|
-
?.split(",")
|
|
1192
|
-
.map((t) => t.trim())
|
|
1193
|
-
.filter(Boolean);
|
|
1194
|
-
|
|
1195
|
-
const mcpDirectTools: string[] = [];
|
|
1196
|
-
const tools: string[] = [];
|
|
1197
|
-
if (rawTools) {
|
|
1198
|
-
for (const tool of rawTools) {
|
|
1199
|
-
if (tool.startsWith("mcp:")) {
|
|
1200
|
-
mcpDirectTools.push(tool.slice(4));
|
|
1201
|
-
} else {
|
|
1202
|
-
tools.push(tool);
|
|
1203
|
-
}
|
|
1204
|
-
}
|
|
1205
|
-
}
|
|
1206
|
-
|
|
1207
|
-
const defaultReads = frontmatter.defaultReads
|
|
1208
|
-
?.split(",")
|
|
1209
|
-
.map((f) => f.trim())
|
|
1210
|
-
.filter(Boolean);
|
|
1211
|
-
|
|
1212
|
-
const skillStr = frontmatter.skill || frontmatter.skills;
|
|
1213
|
-
const skills = skillStr
|
|
1214
|
-
?.split(",")
|
|
1215
|
-
.map((s) => s.trim())
|
|
1216
|
-
.filter(Boolean);
|
|
1217
|
-
const fallbackModels = frontmatter.fallbackModels
|
|
1218
|
-
?.split(",")
|
|
1219
|
-
.map((model) => model.trim())
|
|
1220
|
-
.filter(Boolean);
|
|
1221
|
-
const systemPromptMode = frontmatter.systemPromptMode === "replace"
|
|
1222
|
-
? "replace"
|
|
1223
|
-
: frontmatter.systemPromptMode === "append"
|
|
1224
|
-
? "append"
|
|
1225
|
-
: defaultSystemPromptMode(localName);
|
|
1226
|
-
const inheritProjectContext = frontmatter.inheritProjectContext === "true"
|
|
1227
|
-
? true
|
|
1228
|
-
: frontmatter.inheritProjectContext === "false"
|
|
1229
|
-
? false
|
|
1230
|
-
: defaultInheritProjectContext(localName);
|
|
1231
|
-
const inheritSkills = frontmatter.inheritSkills === "true"
|
|
1232
|
-
? true
|
|
1233
|
-
: frontmatter.inheritSkills === "false"
|
|
1234
|
-
? false
|
|
1235
|
-
: defaultInheritSkills();
|
|
1236
|
-
const defaultContext = frontmatter.defaultContext === "fork"
|
|
1237
|
-
? "fork" as const
|
|
1238
|
-
: frontmatter.defaultContext === "fresh"
|
|
1239
|
-
? "fresh" as const
|
|
1240
|
-
: undefined;
|
|
1241
|
-
|
|
1242
|
-
let extensions: string[] | undefined;
|
|
1243
|
-
if (frontmatter.extensions !== undefined) {
|
|
1244
|
-
extensions = frontmatter.extensions
|
|
1245
|
-
.split(",")
|
|
1246
|
-
.map((e) => e.trim())
|
|
1247
|
-
.filter(Boolean);
|
|
1248
|
-
}
|
|
1249
|
-
let subagentOnlyExtensions: string[] | undefined;
|
|
1250
|
-
if (frontmatter.subagentOnlyExtensions !== undefined) {
|
|
1251
|
-
subagentOnlyExtensions = frontmatter.subagentOnlyExtensions
|
|
1252
|
-
.split(",")
|
|
1253
|
-
.map((e) => e.trim())
|
|
1254
|
-
.filter(Boolean);
|
|
1255
|
-
}
|
|
1256
|
-
|
|
1257
|
-
const extraFields: Record<string, string> = {};
|
|
1258
|
-
for (const [key, value] of Object.entries(frontmatter)) {
|
|
1259
|
-
if (!KNOWN_FIELDS.has(key)) extraFields[key] = value;
|
|
1260
|
-
}
|
|
1261
|
-
|
|
1262
|
-
const parsedMaxSubagentDepth = Number(frontmatter.maxSubagentDepth);
|
|
1263
|
-
let toolBudget: ToolBudgetConfig | undefined;
|
|
1264
|
-
if (frontmatter.toolBudget !== undefined && frontmatter.toolBudget.trim()) {
|
|
1265
|
-
const parsed = JSON.parse(frontmatter.toolBudget) as unknown;
|
|
1266
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
1267
|
-
throw new Error(`Agent '${localName}' has invalid toolBudget frontmatter; expected a JSON object.`);
|
|
1268
|
-
}
|
|
1269
|
-
toolBudget = parsed as ToolBudgetConfig;
|
|
1270
|
-
}
|
|
1271
|
-
const completionGuard = frontmatter.completionGuard === "false"
|
|
1272
|
-
? false
|
|
1273
|
-
: frontmatter.completionGuard === "true"
|
|
1274
|
-
? true
|
|
1275
|
-
: undefined;
|
|
1276
|
-
|
|
1277
|
-
const agent: AgentConfig = {
|
|
1278
|
-
name: runtimeName,
|
|
1279
|
-
localName,
|
|
1280
|
-
packageName,
|
|
1281
|
-
description: frontmatter.description,
|
|
1282
|
-
tools: tools.length > 0 ? tools : undefined,
|
|
1283
|
-
mcpDirectTools: mcpDirectTools.length > 0 ? mcpDirectTools : undefined,
|
|
1284
|
-
model: frontmatter.model,
|
|
1285
|
-
fallbackModels: fallbackModels && fallbackModels.length > 0 ? fallbackModels : undefined,
|
|
1286
|
-
thinking: frontmatter.thinking,
|
|
1287
|
-
systemPromptMode,
|
|
1288
|
-
inheritProjectContext,
|
|
1289
|
-
inheritSkills,
|
|
1290
|
-
defaultContext,
|
|
1291
|
-
systemPrompt: body,
|
|
1292
|
-
source,
|
|
1293
|
-
filePath,
|
|
1294
|
-
skills: skills && skills.length > 0 ? skills : undefined,
|
|
1295
|
-
extensions,
|
|
1296
|
-
subagentOnlyExtensions,
|
|
1297
|
-
output: frontmatter.output,
|
|
1298
|
-
defaultReads: defaultReads && defaultReads.length > 0 ? defaultReads : undefined,
|
|
1299
|
-
defaultProgress: frontmatter.defaultProgress === "true",
|
|
1300
|
-
interactive: frontmatter.interactive === "true",
|
|
1301
|
-
maxSubagentDepth:
|
|
1302
|
-
Number.isInteger(parsedMaxSubagentDepth) && parsedMaxSubagentDepth >= 0
|
|
1303
|
-
? parsedMaxSubagentDepth
|
|
1304
|
-
: undefined,
|
|
1305
|
-
completionGuard,
|
|
1306
|
-
toolBudget,
|
|
1307
|
-
memory: parseMemoryFrontmatter(frontmatter.memory),
|
|
1308
|
-
extraFields: Object.keys(extraFields).length > 0 ? extraFields : undefined,
|
|
1309
|
-
};
|
|
1310
|
-
agentFrontmatterFields.set(agent, new Set(Object.keys(frontmatter)));
|
|
1311
|
-
agents.push(agent);
|
|
1312
|
-
}
|
|
1313
|
-
|
|
1314
|
-
return agents;
|
|
1315
|
-
}
|
|
1316
|
-
|
|
1317
|
-
function loadChainsFromDir(dir: string, source: AgentSource): { chains: ChainConfig[]; diagnostics: ChainDiscoveryDiagnostic[] } {
|
|
1318
|
-
const chains = new Map<string, ChainConfig>();
|
|
1319
|
-
const diagnostics: ChainDiscoveryDiagnostic[] = [];
|
|
1320
|
-
|
|
1321
|
-
for (const filePath of listFilesRecursive(dir, (fileName) => fileName.endsWith(".chain.md") || fileName.endsWith(".chain.json"))) {
|
|
1322
|
-
let content: string;
|
|
1323
|
-
try {
|
|
1324
|
-
content = fs.readFileSync(filePath, "utf-8");
|
|
1325
|
-
} catch {
|
|
1326
|
-
continue;
|
|
1327
|
-
}
|
|
1328
|
-
|
|
1329
|
-
try {
|
|
1330
|
-
const chain = filePath.endsWith(".chain.json") ? parseJsonChain(content, source, filePath) : parseChain(content, source, filePath);
|
|
1331
|
-
const existing = chains.get(chain.name);
|
|
1332
|
-
if (existing && existing.filePath.endsWith(".chain.json") && filePath.endsWith(".chain.md")) continue;
|
|
1333
|
-
chains.set(chain.name, chain);
|
|
1334
|
-
} catch (error) {
|
|
1335
|
-
diagnostics.push({ source, filePath, error: error instanceof Error ? error.message : String(error) });
|
|
1336
|
-
continue;
|
|
1337
|
-
}
|
|
1338
|
-
}
|
|
1339
|
-
|
|
1340
|
-
return { chains: Array.from(chains.values()), diagnostics };
|
|
1341
|
-
}
|
|
1342
|
-
|
|
1343
|
-
function isDirectory(p: string): boolean {
|
|
1344
|
-
try {
|
|
1345
|
-
return fs.statSync(p).isDirectory();
|
|
1346
|
-
} catch {
|
|
1347
|
-
return false;
|
|
1348
|
-
}
|
|
1349
|
-
}
|
|
1350
|
-
|
|
1351
|
-
function resolveNearestProjectAgentDirs(cwd: string): { readDirs: string[]; preferredDir: string | null } {
|
|
1352
|
-
const projectRoot = findNearestProjectRoot(cwd);
|
|
1353
|
-
if (!projectRoot) return { readDirs: [], preferredDir: null };
|
|
1354
|
-
|
|
1355
|
-
const legacyDir = path.join(projectRoot, ".agents");
|
|
1356
|
-
const preferredDir = path.join(getProjectConfigDir(projectRoot), "agents");
|
|
1357
|
-
const readDirs: string[] = [];
|
|
1358
|
-
if (isDirectory(legacyDir)) readDirs.push(legacyDir);
|
|
1359
|
-
if (isDirectory(preferredDir)) readDirs.push(preferredDir);
|
|
1360
|
-
|
|
1361
|
-
return {
|
|
1362
|
-
readDirs,
|
|
1363
|
-
preferredDir,
|
|
1364
|
-
};
|
|
1365
|
-
}
|
|
1366
|
-
|
|
1367
|
-
function resolveNearestProjectChainDirs(cwd: string): { readDirs: string[]; preferredDir: string | null } {
|
|
1368
|
-
const projectRoot = findNearestProjectRoot(cwd);
|
|
1369
|
-
if (!projectRoot) return { readDirs: [], preferredDir: null };
|
|
1370
|
-
|
|
1371
|
-
const preferredDir = path.join(getProjectConfigDir(projectRoot), "chains");
|
|
1372
|
-
return {
|
|
1373
|
-
readDirs: isDirectory(preferredDir) ? [preferredDir] : [],
|
|
1374
|
-
preferredDir,
|
|
1375
|
-
};
|
|
1376
|
-
}
|
|
1377
|
-
const BUILTIN_AGENTS_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..", "agents");
|
|
1378
|
-
|
|
1379
|
-
export const EXTRA_AGENT_DIRS_ENV = "DM_SUBAGENT_EXTRA_AGENT_DIRS";
|
|
1380
|
-
|
|
1381
|
-
// Additional read-only directories to scan for agent definitions, supplied by the
|
|
1382
|
-
// launcher via DM_SUBAGENT_EXTRA_AGENT_DIRS (PATH-style, split on os/path delimiter).
|
|
1383
|
-
// Lets a hermetic wrapper (e.g. a Nix-store install) expose bundled agents without
|
|
1384
|
-
// copying or symlinking them into the writable agent dir. Loaded as "user" source,
|
|
1385
|
-
// at lower precedence than agents the user placed in their own agent dir.
|
|
1386
|
-
function extraUserAgentDirs(): string[] {
|
|
1387
|
-
const raw = process.env[EXTRA_AGENT_DIRS_ENV];
|
|
1388
|
-
if (!raw) return [];
|
|
1389
|
-
return raw
|
|
1390
|
-
.split(path.delimiter)
|
|
1391
|
-
.map((dir) => dir.trim())
|
|
1392
|
-
.filter((dir) => dir.length > 0);
|
|
1393
|
-
}
|
|
1394
|
-
|
|
1395
|
-
export function discoverAgents(cwd: string, scope: AgentScope): AgentDiscoveryResult {
|
|
1396
|
-
const userDirOld = path.join(getAgentDir(), "agents");
|
|
1397
|
-
const userDirNew = path.join(os.homedir(), ".agents");
|
|
1398
|
-
const { readDirs: projectAgentDirs, preferredDir: projectAgentsDir } = resolveNearestProjectAgentDirs(cwd);
|
|
1399
|
-
const userSettingsPath = getUserAgentSettingsPath();
|
|
1400
|
-
const projectSettingsPath = getProjectAgentSettingsPath(cwd);
|
|
1401
|
-
const userSettings = scope === "project" ? EMPTY_SUBAGENT_SETTINGS : readSubagentSettings(userSettingsPath);
|
|
1402
|
-
const projectSettings = scope === "user" ? EMPTY_SUBAGENT_SETTINGS : readSubagentSettings(projectSettingsPath);
|
|
1403
|
-
const defaultModel = resolveSubagentDefaultModel(userSettings, projectSettings, userSettingsPath, projectSettingsPath);
|
|
1404
|
-
const modelScope = projectSettings.modelScope ?? userSettings.modelScope;
|
|
1405
|
-
const packageSubagentPaths = collectPackageSubagentPaths(cwd, {
|
|
1406
|
-
includeUser: scope !== "project",
|
|
1407
|
-
includeProject: scope !== "user",
|
|
1408
|
-
});
|
|
1409
|
-
|
|
1410
|
-
const builtinAgents = applyBuiltinOverrides(
|
|
1411
|
-
applySubagentDefaultModel(loadAgentsFromDir(BUILTIN_AGENTS_DIR, "builtin"), defaultModel),
|
|
1412
|
-
userSettings,
|
|
1413
|
-
projectSettings,
|
|
1414
|
-
userSettingsPath,
|
|
1415
|
-
projectSettingsPath,
|
|
1416
|
-
);
|
|
1417
|
-
|
|
1418
|
-
const userAgentsExtra = scope === "project" ? [] : extraUserAgentDirs().flatMap((dir) => loadAgentsFromDir(dir, "user"));
|
|
1419
|
-
const userAgentsOld = scope === "project" ? [] : loadAgentsFromDir(userDirOld, "user");
|
|
1420
|
-
const userAgentsNew = scope === "project" ? [] : loadAgentsFromDir(userDirNew, "user");
|
|
1421
|
-
const userAgents = applyCustomAgentOverrides(
|
|
1422
|
-
applySubagentDefaultModel([...userAgentsExtra, ...userAgentsOld, ...userAgentsNew], defaultModel),
|
|
1423
|
-
userSettings,
|
|
1424
|
-
projectSettings,
|
|
1425
|
-
userSettingsPath,
|
|
1426
|
-
projectSettingsPath,
|
|
1427
|
-
);
|
|
1428
|
-
|
|
1429
|
-
const projectAgents = applyCustomAgentOverrides(
|
|
1430
|
-
applySubagentDefaultModel(scope === "user" ? [] : projectAgentDirs.flatMap((dir) => loadAgentsFromDir(dir, "project")), defaultModel),
|
|
1431
|
-
userSettings,
|
|
1432
|
-
projectSettings,
|
|
1433
|
-
userSettingsPath,
|
|
1434
|
-
projectSettingsPath,
|
|
1435
|
-
);
|
|
1436
|
-
const packageAgents = applyCustomAgentOverrides(
|
|
1437
|
-
applySubagentDefaultModel(packageSubagentPaths.agents.flatMap((dir) => loadAgentsFromDir(dir, "package")), defaultModel),
|
|
1438
|
-
userSettings,
|
|
1439
|
-
projectSettings,
|
|
1440
|
-
userSettingsPath,
|
|
1441
|
-
projectSettingsPath,
|
|
1442
|
-
);
|
|
1443
|
-
const agents = mergeAgentsForScope(scope, userAgents, projectAgents, builtinAgents, packageAgents)
|
|
1444
|
-
.filter((agent) => agent.disabled !== true);
|
|
1445
|
-
|
|
1446
|
-
return { agents, projectAgentsDir, modelScope };
|
|
1447
|
-
}
|
|
1448
|
-
|
|
1449
|
-
export function discoverAgentsAll(cwd: string): {
|
|
1450
|
-
builtin: AgentConfig[];
|
|
1451
|
-
package: AgentConfig[];
|
|
1452
|
-
user: AgentConfig[];
|
|
1453
|
-
project: AgentConfig[];
|
|
1454
|
-
chains: ChainConfig[];
|
|
1455
|
-
chainDiagnostics: ChainDiscoveryDiagnostic[];
|
|
1456
|
-
userDir: string;
|
|
1457
|
-
projectDir: string | null;
|
|
1458
|
-
userChainDir: string;
|
|
1459
|
-
projectChainDir: string | null;
|
|
1460
|
-
userSettingsPath: string;
|
|
1461
|
-
projectSettingsPath: string | null;
|
|
1462
|
-
} {
|
|
1463
|
-
const userDirOld = path.join(getAgentDir(), "agents");
|
|
1464
|
-
const userDirNew = path.join(os.homedir(), ".agents");
|
|
1465
|
-
const userChainDir = getUserChainDir();
|
|
1466
|
-
const { readDirs: projectDirs, preferredDir: projectDir } = resolveNearestProjectAgentDirs(cwd);
|
|
1467
|
-
const { readDirs: projectChainDirs, preferredDir: projectChainDir } = resolveNearestProjectChainDirs(cwd);
|
|
1468
|
-
const userSettingsPath = getUserAgentSettingsPath();
|
|
1469
|
-
const projectSettingsPath = getProjectAgentSettingsPath(cwd);
|
|
1470
|
-
const userSettings = readSubagentSettings(userSettingsPath);
|
|
1471
|
-
const projectSettings = readSubagentSettings(projectSettingsPath);
|
|
1472
|
-
const defaultModel = resolveSubagentDefaultModel(userSettings, projectSettings, userSettingsPath, projectSettingsPath);
|
|
1473
|
-
const packageSubagentPaths = collectPackageSubagentPaths(cwd);
|
|
1474
|
-
|
|
1475
|
-
const builtin = applyBuiltinOverrides(
|
|
1476
|
-
applySubagentDefaultModel(loadAgentsFromDir(BUILTIN_AGENTS_DIR, "builtin"), defaultModel),
|
|
1477
|
-
userSettings,
|
|
1478
|
-
projectSettings,
|
|
1479
|
-
userSettingsPath,
|
|
1480
|
-
projectSettingsPath,
|
|
1481
|
-
);
|
|
1482
|
-
const user = applyCustomAgentOverrides(
|
|
1483
|
-
applySubagentDefaultModel([
|
|
1484
|
-
...extraUserAgentDirs().flatMap((dir) => loadAgentsFromDir(dir, "user")),
|
|
1485
|
-
...loadAgentsFromDir(userDirOld, "user"),
|
|
1486
|
-
...loadAgentsFromDir(userDirNew, "user"),
|
|
1487
|
-
], defaultModel),
|
|
1488
|
-
userSettings,
|
|
1489
|
-
projectSettings,
|
|
1490
|
-
userSettingsPath,
|
|
1491
|
-
projectSettingsPath,
|
|
1492
|
-
);
|
|
1493
|
-
const packageMap = new Map<string, AgentConfig>();
|
|
1494
|
-
for (const dir of packageSubagentPaths.agents) {
|
|
1495
|
-
for (const agent of loadAgentsFromDir(dir, "package")) {
|
|
1496
|
-
if (!packageMap.has(agent.name)) packageMap.set(agent.name, agent);
|
|
1497
|
-
}
|
|
1498
|
-
}
|
|
1499
|
-
const packageAgents = applyCustomAgentOverrides(
|
|
1500
|
-
applySubagentDefaultModel(Array.from(packageMap.values()), defaultModel),
|
|
1501
|
-
userSettings,
|
|
1502
|
-
projectSettings,
|
|
1503
|
-
userSettingsPath,
|
|
1504
|
-
projectSettingsPath,
|
|
1505
|
-
);
|
|
1506
|
-
const projectMap = new Map<string, AgentConfig>();
|
|
1507
|
-
for (const dir of projectDirs) {
|
|
1508
|
-
for (const agent of loadAgentsFromDir(dir, "project")) {
|
|
1509
|
-
projectMap.set(agent.name, agent);
|
|
1510
|
-
}
|
|
1511
|
-
}
|
|
1512
|
-
const project = applyCustomAgentOverrides(
|
|
1513
|
-
applySubagentDefaultModel(Array.from(projectMap.values()), defaultModel),
|
|
1514
|
-
userSettings,
|
|
1515
|
-
projectSettings,
|
|
1516
|
-
userSettingsPath,
|
|
1517
|
-
projectSettingsPath,
|
|
1518
|
-
);
|
|
1519
|
-
|
|
1520
|
-
const chainMap = new Map<string, ChainConfig>();
|
|
1521
|
-
const packageChainDiagnostics: ChainDiscoveryDiagnostic[] = [];
|
|
1522
|
-
const packageChainMap = new Map<string, ChainConfig>();
|
|
1523
|
-
for (const dir of packageSubagentPaths.chains) {
|
|
1524
|
-
const loaded = loadChainsFromDir(dir, "package");
|
|
1525
|
-
packageChainDiagnostics.push(...loaded.diagnostics);
|
|
1526
|
-
for (const chain of loaded.chains) {
|
|
1527
|
-
if (!packageChainMap.has(chain.name)) packageChainMap.set(chain.name, chain);
|
|
1528
|
-
}
|
|
1529
|
-
}
|
|
1530
|
-
const projectChainDiagnostics: ChainDiscoveryDiagnostic[] = [];
|
|
1531
|
-
for (const dir of projectChainDirs) {
|
|
1532
|
-
const loaded = loadChainsFromDir(dir, "project");
|
|
1533
|
-
projectChainDiagnostics.push(...loaded.diagnostics);
|
|
1534
|
-
for (const chain of loaded.chains) {
|
|
1535
|
-
chainMap.set(chain.name, chain);
|
|
1536
|
-
}
|
|
1537
|
-
}
|
|
1538
|
-
const userChains = loadChainsFromDir(userChainDir, "user");
|
|
1539
|
-
const chains = [
|
|
1540
|
-
...Array.from(packageChainMap.values()),
|
|
1541
|
-
...userChains.chains,
|
|
1542
|
-
...Array.from(chainMap.values()),
|
|
1543
|
-
];
|
|
1544
|
-
const chainDiagnostics = [
|
|
1545
|
-
...packageChainDiagnostics,
|
|
1546
|
-
...userChains.diagnostics,
|
|
1547
|
-
...projectChainDiagnostics,
|
|
1548
|
-
];
|
|
1549
|
-
|
|
1550
|
-
const userDir = process.env.DM_CODING_AGENT_DIR ? userDirOld : fs.existsSync(userDirNew) ? userDirNew : userDirOld;
|
|
1551
|
-
|
|
1552
|
-
return { builtin, package: packageAgents, user, project, chains, chainDiagnostics, userDir, projectDir, userChainDir, projectChainDir, userSettingsPath, projectSettingsPath };
|
|
1553
|
-
}
|