@posthog/agent 2.3.349 → 2.3.353
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/claude/permissions/permission-options.d.ts +1 -1
- package/dist/adapters/claude/permissions/permission-options.js +3 -3
- package/dist/adapters/claude/permissions/permission-options.js.map +1 -1
- package/dist/agent.js +5961 -128
- package/dist/agent.js.map +1 -1
- package/dist/posthog-api.js +1 -1
- package/dist/posthog-api.js.map +1 -1
- package/dist/server/agent-server.js +227 -53
- package/dist/server/agent-server.js.map +1 -1
- package/dist/server/bin.cjs +225 -51
- package/dist/server/bin.cjs.map +1 -1
- package/package.json +3 -3
- package/src/adapters/claude/hooks.ts +9 -2
- package/src/adapters/claude/permissions/permission-handlers.ts +53 -10
- package/src/adapters/claude/permissions/permission-options.ts +3 -3
- package/src/adapters/claude/session/options.test.ts +72 -0
- package/src/adapters/claude/session/options.ts +56 -1
- package/src/adapters/claude/session/repo-path.ts +22 -0
- package/src/adapters/claude/session/settings.test.ts +159 -0
- package/src/adapters/claude/session/settings.ts +92 -6
package/dist/server/bin.cjs
CHANGED
|
@@ -8688,6 +8688,32 @@ async function getCurrentBranch(baseDir, options) {
|
|
|
8688
8688
|
return branch === "HEAD" ? null : branch;
|
|
8689
8689
|
}, { signal: options?.abortSignal });
|
|
8690
8690
|
}
|
|
8691
|
+
async function listWorktrees(baseDir, options) {
|
|
8692
|
+
const manager = getGitOperationManager();
|
|
8693
|
+
return manager.executeRead(baseDir, async (git) => {
|
|
8694
|
+
const output = await git.raw(["worktree", "list", "--porcelain"]);
|
|
8695
|
+
const worktrees = [];
|
|
8696
|
+
let current2 = {};
|
|
8697
|
+
for (const line of output.split("\n")) {
|
|
8698
|
+
if (line.startsWith("worktree ")) {
|
|
8699
|
+
if (current2.path) {
|
|
8700
|
+
worktrees.push(current2);
|
|
8701
|
+
}
|
|
8702
|
+
current2 = { path: line.slice(9), branch: null };
|
|
8703
|
+
} else if (line.startsWith("HEAD ")) {
|
|
8704
|
+
current2.head = line.slice(5);
|
|
8705
|
+
} else if (line.startsWith("branch ")) {
|
|
8706
|
+
current2.branch = line.slice(7).replace("refs/heads/", "");
|
|
8707
|
+
} else if (line === "detached") {
|
|
8708
|
+
current2.branch = null;
|
|
8709
|
+
}
|
|
8710
|
+
}
|
|
8711
|
+
if (current2.path) {
|
|
8712
|
+
worktrees.push(current2);
|
|
8713
|
+
}
|
|
8714
|
+
return worktrees;
|
|
8715
|
+
}, { signal: options?.abortSignal });
|
|
8716
|
+
}
|
|
8691
8717
|
async function getHeadSha(baseDir, options) {
|
|
8692
8718
|
const manager = getGitOperationManager();
|
|
8693
8719
|
return manager.executeRead(baseDir, (git) => git.revparse(["HEAD"]), {
|
|
@@ -8701,7 +8727,7 @@ var import_hono = require("hono");
|
|
|
8701
8727
|
// package.json
|
|
8702
8728
|
var package_default = {
|
|
8703
8729
|
name: "@posthog/agent",
|
|
8704
|
-
version: "2.3.
|
|
8730
|
+
version: "2.3.353",
|
|
8705
8731
|
repository: "https://github.com/PostHog/code",
|
|
8706
8732
|
description: "TypeScript agent framework wrapping Claude Agent SDK with Git-based task execution for PostHog",
|
|
8707
8733
|
exports: {
|
|
@@ -12740,7 +12766,7 @@ var createPostToolUseHook = ({ onModeChange, logger }) => async (input, toolUseI
|
|
|
12740
12766
|
var SUBAGENT_REWRITES = {
|
|
12741
12767
|
Explore: "ph-explore"
|
|
12742
12768
|
};
|
|
12743
|
-
var createSubagentRewriteHook = (logger) => async (input, _toolUseID) => {
|
|
12769
|
+
var createSubagentRewriteHook = (logger, registeredAgents) => async (input, _toolUseID) => {
|
|
12744
12770
|
if (input.hook_event_name !== "PreToolUse") {
|
|
12745
12771
|
return { continue: true };
|
|
12746
12772
|
}
|
|
@@ -12753,6 +12779,12 @@ var createSubagentRewriteHook = (logger) => async (input, _toolUseID) => {
|
|
|
12753
12779
|
return { continue: true };
|
|
12754
12780
|
}
|
|
12755
12781
|
const target = SUBAGENT_REWRITES[subagentType];
|
|
12782
|
+
if (!registeredAgents.has(target)) {
|
|
12783
|
+
logger.warn(
|
|
12784
|
+
`[SubagentRewriteHook] Skipping rewrite ${subagentType} \u2192 ${target}: target agent not registered for this session. Falling back to built-in ${subagentType}.`
|
|
12785
|
+
);
|
|
12786
|
+
return { continue: true };
|
|
12787
|
+
}
|
|
12756
12788
|
logger.info(
|
|
12757
12789
|
`[SubagentRewriteHook] Rewriting subagent_type: ${subagentType} \u2192 ${target}`
|
|
12758
12790
|
);
|
|
@@ -14225,16 +14257,16 @@ function permissionOptions(allowAlwaysLabel) {
|
|
|
14225
14257
|
}
|
|
14226
14258
|
];
|
|
14227
14259
|
}
|
|
14228
|
-
function buildPermissionOptions(toolName, toolInput,
|
|
14260
|
+
function buildPermissionOptions(toolName, toolInput, repoRoot, suggestions) {
|
|
14229
14261
|
if (BASH_TOOLS.has(toolName)) {
|
|
14230
14262
|
const rawRuleContent = suggestions?.flatMap((s) => "rules" in s ? s.rules : []).find((r) => r.toolName === "Bash" && r.ruleContent)?.ruleContent;
|
|
14231
14263
|
const ruleContent = rawRuleContent?.replace(/:?\*$/, "");
|
|
14232
14264
|
const command = toolInput?.command;
|
|
14233
14265
|
const cmdName = command?.split(/\s+/)[0] ?? "this command";
|
|
14234
|
-
const
|
|
14266
|
+
const scopeLabel = repoRoot ? ` in ${repoRoot}` : "";
|
|
14235
14267
|
const label = ruleContent ?? `\`${cmdName}\` commands`;
|
|
14236
14268
|
return permissionOptions(
|
|
14237
|
-
`Yes, and don't ask again for ${label}${
|
|
14269
|
+
`Yes, and don't ask again for ${label}${scopeLabel}`
|
|
14238
14270
|
);
|
|
14239
14271
|
}
|
|
14240
14272
|
if (toolName === "BashOutput") {
|
|
@@ -14520,7 +14552,7 @@ async function handleDefaultPermissionFlow(context) {
|
|
|
14520
14552
|
const options = buildPermissionOptions(
|
|
14521
14553
|
toolName,
|
|
14522
14554
|
toolInput,
|
|
14523
|
-
session
|
|
14555
|
+
session.settingsManager.getRepoRoot(),
|
|
14524
14556
|
suggestions
|
|
14525
14557
|
);
|
|
14526
14558
|
const response = await client.requestPermission({
|
|
@@ -14540,17 +14572,19 @@ async function handleDefaultPermissionFlow(context) {
|
|
|
14540
14572
|
}
|
|
14541
14573
|
if (response.outcome?.outcome === "selected" && (response.outcome.optionId === "allow" || response.outcome.optionId === "allow_always")) {
|
|
14542
14574
|
if (response.outcome.optionId === "allow_always") {
|
|
14575
|
+
const rules = extractAllowRules(suggestions, toolName);
|
|
14576
|
+
try {
|
|
14577
|
+
await session.settingsManager.addAllowRules(rules);
|
|
14578
|
+
} catch (error) {
|
|
14579
|
+
context.logger.warn(
|
|
14580
|
+
"[canUseTool] Failed to persist allow rules to repository settings",
|
|
14581
|
+
{ error: error instanceof Error ? error.message : String(error) }
|
|
14582
|
+
);
|
|
14583
|
+
}
|
|
14543
14584
|
return {
|
|
14544
14585
|
behavior: "allow",
|
|
14545
14586
|
updatedInput: toolInput,
|
|
14546
|
-
updatedPermissions: suggestions
|
|
14547
|
-
{
|
|
14548
|
-
type: "addRules",
|
|
14549
|
-
rules: [{ toolName }],
|
|
14550
|
-
behavior: "allow",
|
|
14551
|
-
destination: "localSettings"
|
|
14552
|
-
}
|
|
14553
|
-
]
|
|
14587
|
+
updatedPermissions: buildSessionPermissions(suggestions, rules)
|
|
14554
14588
|
};
|
|
14555
14589
|
}
|
|
14556
14590
|
return {
|
|
@@ -14583,6 +14617,26 @@ function handlePlanFileException(context) {
|
|
|
14583
14617
|
updatedInput: toolInput
|
|
14584
14618
|
};
|
|
14585
14619
|
}
|
|
14620
|
+
function extractAllowRules(suggestions, toolName) {
|
|
14621
|
+
if (!suggestions || suggestions.length === 0) {
|
|
14622
|
+
return [{ toolName }];
|
|
14623
|
+
}
|
|
14624
|
+
return suggestions.filter(
|
|
14625
|
+
(update) => update.type === "addRules" && update.behavior === "allow"
|
|
14626
|
+
).flatMap((update) => "rules" in update ? update.rules : []);
|
|
14627
|
+
}
|
|
14628
|
+
function buildSessionPermissions(suggestions, rules) {
|
|
14629
|
+
const passthrough = (suggestions ?? []).filter(
|
|
14630
|
+
(update) => !(update.type === "addRules" && update.behavior === "allow")
|
|
14631
|
+
).map((update) => ({ ...update, destination: "session" }));
|
|
14632
|
+
if (rules.length === 0) {
|
|
14633
|
+
return passthrough;
|
|
14634
|
+
}
|
|
14635
|
+
return [
|
|
14636
|
+
{ type: "addRules", rules, behavior: "allow", destination: "session" },
|
|
14637
|
+
...passthrough
|
|
14638
|
+
];
|
|
14639
|
+
}
|
|
14586
14640
|
function extractDomainFromUrl(url) {
|
|
14587
14641
|
try {
|
|
14588
14642
|
return new URL(url).hostname;
|
|
@@ -14759,7 +14813,7 @@ function buildEnvironment() {
|
|
|
14759
14813
|
CLAUDE_CODE_EMIT_SESSION_STATE_EVENTS: "1"
|
|
14760
14814
|
};
|
|
14761
14815
|
}
|
|
14762
|
-
function buildHooks(userHooks, onModeChange, settingsManager, logger, enrichmentDeps, enrichedReadCache) {
|
|
14816
|
+
function buildHooks(userHooks, onModeChange, settingsManager, logger, enrichmentDeps, enrichedReadCache, registeredAgents) {
|
|
14763
14817
|
const postToolUseHooks = [createPostToolUseHook({ onModeChange, logger })];
|
|
14764
14818
|
if (enrichmentDeps && enrichedReadCache) {
|
|
14765
14819
|
postToolUseHooks.push(
|
|
@@ -14777,12 +14831,49 @@ function buildHooks(userHooks, onModeChange, settingsManager, logger, enrichment
|
|
|
14777
14831
|
{
|
|
14778
14832
|
hooks: [
|
|
14779
14833
|
createPreToolUseHook(settingsManager, logger),
|
|
14780
|
-
createSubagentRewriteHook(logger)
|
|
14834
|
+
createSubagentRewriteHook(logger, registeredAgents)
|
|
14781
14835
|
]
|
|
14782
14836
|
}
|
|
14783
14837
|
]
|
|
14784
14838
|
};
|
|
14785
14839
|
}
|
|
14840
|
+
var PH_EXPLORE_AGENT = {
|
|
14841
|
+
description: 'Fast agent for exploring and understanding codebases. Use this when you need to find files by pattern (eg. "src/components/**/*.tsx"), search for code or keywords (eg. "where is the auth middleware?"), or answer questions about how the codebase works (eg. "how does the session service handle reconnects?"). When calling this agent, specify a thoroughness level: "quick" for targeted lookups, "medium" for broader exploration, or "very thorough" for comprehensive analysis across multiple locations.',
|
|
14842
|
+
model: "haiku",
|
|
14843
|
+
prompt: `You are a fast, read-only codebase exploration agent.
|
|
14844
|
+
|
|
14845
|
+
Your job is to find files, search code, read the most relevant sources, and report findings clearly.
|
|
14846
|
+
|
|
14847
|
+
Rules:
|
|
14848
|
+
- Never create, modify, delete, move, or copy files.
|
|
14849
|
+
- Never use shell redirection or any command that changes system state.
|
|
14850
|
+
- Use Glob for broad file pattern matching.
|
|
14851
|
+
- Use Grep for searching file contents.
|
|
14852
|
+
- Use Read when you know the exact file path to inspect.
|
|
14853
|
+
- Use Bash only for safe read-only commands like ls, git status, git log, git diff, find, cat, head, and tail.
|
|
14854
|
+
- Adapt your search approach based on the thoroughness level specified by the caller.
|
|
14855
|
+
- Return file paths as absolute paths in your final response.
|
|
14856
|
+
- Avoid using emojis.
|
|
14857
|
+
- Wherever possible, spawn multiple parallel tool calls for grepping and reading files.
|
|
14858
|
+
- Search efficiently, then read only the most relevant files.
|
|
14859
|
+
- Return findings directly in your final response \u2014 do not create files.`,
|
|
14860
|
+
tools: [
|
|
14861
|
+
"Bash",
|
|
14862
|
+
"Glob",
|
|
14863
|
+
"Grep",
|
|
14864
|
+
"Read",
|
|
14865
|
+
"WebFetch",
|
|
14866
|
+
"WebSearch",
|
|
14867
|
+
"NotebookRead",
|
|
14868
|
+
"TodoWrite"
|
|
14869
|
+
]
|
|
14870
|
+
};
|
|
14871
|
+
function buildAgents(userAgents) {
|
|
14872
|
+
return {
|
|
14873
|
+
"ph-explore": PH_EXPLORE_AGENT,
|
|
14874
|
+
...userAgents || {}
|
|
14875
|
+
};
|
|
14876
|
+
}
|
|
14786
14877
|
function getAbortController(userProvidedController) {
|
|
14787
14878
|
const controller = userProvidedController ?? new AbortController();
|
|
14788
14879
|
if (controller.signal.aborted) {
|
|
@@ -14868,6 +14959,8 @@ function ensureLocalSettings(cwd) {
|
|
|
14868
14959
|
function buildSessionOptions(params) {
|
|
14869
14960
|
ensureLocalSettings(params.cwd);
|
|
14870
14961
|
const tools = params.userProvidedOptions?.tools ?? (params.disableBuiltInTools ? [] : { type: "preset", preset: "claude_code" });
|
|
14962
|
+
const agents = buildAgents(params.userProvidedOptions?.agents);
|
|
14963
|
+
const registeredAgentNames = new Set(Object.keys(agents));
|
|
14871
14964
|
const options = {
|
|
14872
14965
|
...params.userProvidedOptions,
|
|
14873
14966
|
betas: ["context-1m-2025-08-07"],
|
|
@@ -14881,6 +14974,7 @@ function buildSessionOptions(params) {
|
|
|
14881
14974
|
canUseTool: params.canUseTool,
|
|
14882
14975
|
executable: "node",
|
|
14883
14976
|
tools,
|
|
14977
|
+
agents,
|
|
14884
14978
|
extraArgs: {
|
|
14885
14979
|
...params.userProvidedOptions?.extraArgs,
|
|
14886
14980
|
"replay-user-messages": ""
|
|
@@ -14896,7 +14990,8 @@ function buildSessionOptions(params) {
|
|
|
14896
14990
|
params.settingsManager,
|
|
14897
14991
|
params.logger,
|
|
14898
14992
|
params.enrichmentDeps,
|
|
14899
|
-
params.enrichedReadCache
|
|
14993
|
+
params.enrichedReadCache,
|
|
14994
|
+
registeredAgentNames
|
|
14900
14995
|
),
|
|
14901
14996
|
outputFormat: params.outputFormat,
|
|
14902
14997
|
abortController: getAbortController(
|
|
@@ -14944,6 +15039,47 @@ var fs7 = __toESM(require("fs"), 1);
|
|
|
14944
15039
|
var os3 = __toESM(require("os"), 1);
|
|
14945
15040
|
var path9 = __toESM(require("path"), 1);
|
|
14946
15041
|
var import_minimatch = require("minimatch");
|
|
15042
|
+
|
|
15043
|
+
// src/utils/async-mutex.ts
|
|
15044
|
+
var AsyncMutex = class {
|
|
15045
|
+
locked = false;
|
|
15046
|
+
queue = [];
|
|
15047
|
+
async acquire() {
|
|
15048
|
+
if (!this.locked) {
|
|
15049
|
+
this.locked = true;
|
|
15050
|
+
return;
|
|
15051
|
+
}
|
|
15052
|
+
return new Promise((resolve4) => {
|
|
15053
|
+
this.queue.push(resolve4);
|
|
15054
|
+
});
|
|
15055
|
+
}
|
|
15056
|
+
release() {
|
|
15057
|
+
const next = this.queue.shift();
|
|
15058
|
+
if (next) {
|
|
15059
|
+
next();
|
|
15060
|
+
} else {
|
|
15061
|
+
this.locked = false;
|
|
15062
|
+
}
|
|
15063
|
+
}
|
|
15064
|
+
isLocked() {
|
|
15065
|
+
return this.locked;
|
|
15066
|
+
}
|
|
15067
|
+
get queueLength() {
|
|
15068
|
+
return this.queue.length;
|
|
15069
|
+
}
|
|
15070
|
+
};
|
|
15071
|
+
|
|
15072
|
+
// src/adapters/claude/session/repo-path.ts
|
|
15073
|
+
async function resolveMainRepoPath(cwd) {
|
|
15074
|
+
try {
|
|
15075
|
+
const worktrees = await listWorktrees(cwd);
|
|
15076
|
+
return worktrees[0]?.path ?? cwd;
|
|
15077
|
+
} catch {
|
|
15078
|
+
return cwd;
|
|
15079
|
+
}
|
|
15080
|
+
}
|
|
15081
|
+
|
|
15082
|
+
// src/adapters/claude/session/settings.ts
|
|
14947
15083
|
var ACP_TOOL_NAME_PREFIX = "mcp__acp__";
|
|
14948
15084
|
var acpToolNames = {
|
|
14949
15085
|
read: `${ACP_TOOL_NAME_PREFIX}Read`,
|
|
@@ -15000,7 +15136,7 @@ function matchesGlob(pattern, filePath, cwd) {
|
|
|
15000
15136
|
});
|
|
15001
15137
|
}
|
|
15002
15138
|
function matchesRule(rule, toolName, toolInput, cwd) {
|
|
15003
|
-
const ruleAppliesToTool = rule.toolName === "Bash" && toolName === acpToolNames.bash || rule.toolName === "Edit" && FILE_EDITING_TOOLS.includes(toolName) || rule.toolName === "Read" && FILE_READING_TOOLS.includes(toolName);
|
|
15139
|
+
const ruleAppliesToTool = rule.toolName === "Bash" && toolName === acpToolNames.bash || rule.toolName === "Edit" && FILE_EDITING_TOOLS.includes(toolName) || rule.toolName === "Read" && FILE_READING_TOOLS.includes(toolName) || rule.toolName === toolName && !rule.argument;
|
|
15004
15140
|
if (!ruleAppliesToTool) {
|
|
15005
15141
|
return false;
|
|
15006
15142
|
}
|
|
@@ -15030,6 +15166,19 @@ function matchesRule(rule, toolName, toolInput, cwd) {
|
|
|
15030
15166
|
}
|
|
15031
15167
|
return matchesGlob(rule.argument, actualArg, cwd);
|
|
15032
15168
|
}
|
|
15169
|
+
function formatRule(rule) {
|
|
15170
|
+
return rule.ruleContent ? `${rule.toolName}(${rule.ruleContent})` : rule.toolName;
|
|
15171
|
+
}
|
|
15172
|
+
async function writeFileAtomic(filePath, data) {
|
|
15173
|
+
const tmpPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
15174
|
+
await fs7.promises.writeFile(tmpPath, data);
|
|
15175
|
+
try {
|
|
15176
|
+
await fs7.promises.rename(tmpPath, filePath);
|
|
15177
|
+
} catch (error) {
|
|
15178
|
+
await fs7.promises.rm(tmpPath, { force: true });
|
|
15179
|
+
throw error;
|
|
15180
|
+
}
|
|
15181
|
+
}
|
|
15033
15182
|
async function loadSettingsFile(filePath) {
|
|
15034
15183
|
if (!filePath) {
|
|
15035
15184
|
return {};
|
|
@@ -15048,6 +15197,17 @@ async function loadSettingsFile(filePath) {
|
|
|
15048
15197
|
return {};
|
|
15049
15198
|
}
|
|
15050
15199
|
}
|
|
15200
|
+
async function readSettingsFileForUpdate(filePath) {
|
|
15201
|
+
try {
|
|
15202
|
+
const content = await fs7.promises.readFile(filePath, "utf-8");
|
|
15203
|
+
return JSON.parse(content);
|
|
15204
|
+
} catch (error) {
|
|
15205
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
15206
|
+
return {};
|
|
15207
|
+
}
|
|
15208
|
+
throw error;
|
|
15209
|
+
}
|
|
15210
|
+
}
|
|
15051
15211
|
function getManagedSettingsPath() {
|
|
15052
15212
|
switch (process.platform) {
|
|
15053
15213
|
case "darwin":
|
|
@@ -15062,6 +15222,7 @@ function getManagedSettingsPath() {
|
|
|
15062
15222
|
}
|
|
15063
15223
|
var SettingsManager = class {
|
|
15064
15224
|
cwd;
|
|
15225
|
+
repoRoot;
|
|
15065
15226
|
userSettings = {};
|
|
15066
15227
|
projectSettings = {};
|
|
15067
15228
|
localSettings = {};
|
|
@@ -15069,8 +15230,10 @@ var SettingsManager = class {
|
|
|
15069
15230
|
mergedSettings = {};
|
|
15070
15231
|
initialized = false;
|
|
15071
15232
|
initPromise = null;
|
|
15233
|
+
writeMutex = new AsyncMutex();
|
|
15072
15234
|
constructor(cwd) {
|
|
15073
15235
|
this.cwd = cwd;
|
|
15236
|
+
this.repoRoot = cwd;
|
|
15074
15237
|
}
|
|
15075
15238
|
async initialize() {
|
|
15076
15239
|
if (this.initialized) return;
|
|
@@ -15088,10 +15251,16 @@ var SettingsManager = class {
|
|
|
15088
15251
|
getProjectSettingsPath() {
|
|
15089
15252
|
return path9.join(this.cwd, ".claude", "settings.json");
|
|
15090
15253
|
}
|
|
15254
|
+
/**
|
|
15255
|
+
* Local settings are anchored to the primary worktree so every worktree of
|
|
15256
|
+
* the same repository shares a single `.claude/settings.local.json`. This
|
|
15257
|
+
* avoids re-prompting for the same permission in every worktree.
|
|
15258
|
+
*/
|
|
15091
15259
|
getLocalSettingsPath() {
|
|
15092
|
-
return path9.join(this.
|
|
15260
|
+
return path9.join(this.repoRoot, ".claude", "settings.local.json");
|
|
15093
15261
|
}
|
|
15094
15262
|
async loadAllSettings() {
|
|
15263
|
+
this.repoRoot = await resolveMainRepoPath(this.cwd);
|
|
15095
15264
|
const [userSettings, projectSettings, localSettings, enterpriseSettings] = await Promise.all([
|
|
15096
15265
|
loadSettingsFile(this.getUserSettingsPath()),
|
|
15097
15266
|
loadSettingsFile(this.getProjectSettingsPath()),
|
|
@@ -15148,9 +15317,6 @@ var SettingsManager = class {
|
|
|
15148
15317
|
this.mergedSettings = merged;
|
|
15149
15318
|
}
|
|
15150
15319
|
checkPermission(toolName, toolInput) {
|
|
15151
|
-
if (!toolName.startsWith(ACP_TOOL_NAME_PREFIX)) {
|
|
15152
|
-
return { decision: "ask" };
|
|
15153
|
-
}
|
|
15154
15320
|
const permissions = this.mergedSettings.permissions;
|
|
15155
15321
|
if (!permissions) {
|
|
15156
15322
|
return { decision: "ask" };
|
|
@@ -15181,6 +15347,43 @@ var SettingsManager = class {
|
|
|
15181
15347
|
getCwd() {
|
|
15182
15348
|
return this.cwd;
|
|
15183
15349
|
}
|
|
15350
|
+
getRepoRoot() {
|
|
15351
|
+
return this.repoRoot;
|
|
15352
|
+
}
|
|
15353
|
+
/**
|
|
15354
|
+
* Persists allow rules to `<primary-worktree>/.claude/settings.local.json`.
|
|
15355
|
+
* Because local settings are resolved against the primary worktree, every
|
|
15356
|
+
* worktree of the same repository picks up the new rule on next load.
|
|
15357
|
+
*
|
|
15358
|
+
* Writes are serialised via `writeMutex` to prevent concurrent callers from
|
|
15359
|
+
* clobbering each other, and use a temp-file + rename to keep the file
|
|
15360
|
+
* consistent if the process dies mid-write.
|
|
15361
|
+
*/
|
|
15362
|
+
async addAllowRules(rules) {
|
|
15363
|
+
if (rules.length === 0) return;
|
|
15364
|
+
if (!this.initialized) await this.initialize();
|
|
15365
|
+
await this.writeMutex.acquire();
|
|
15366
|
+
try {
|
|
15367
|
+
const filePath = this.getLocalSettingsPath();
|
|
15368
|
+
const existing = await readSettingsFileForUpdate(filePath);
|
|
15369
|
+
const permissions = {
|
|
15370
|
+
...existing.permissions ?? {}
|
|
15371
|
+
};
|
|
15372
|
+
const current2 = new Set(permissions.allow ?? []);
|
|
15373
|
+
for (const rule of rules) {
|
|
15374
|
+
current2.add(formatRule(rule));
|
|
15375
|
+
}
|
|
15376
|
+
permissions.allow = Array.from(current2);
|
|
15377
|
+
const next = { ...existing, permissions };
|
|
15378
|
+
await fs7.promises.mkdir(path9.dirname(filePath), { recursive: true });
|
|
15379
|
+
await writeFileAtomic(filePath, `${JSON.stringify(next, null, 2)}
|
|
15380
|
+
`);
|
|
15381
|
+
this.localSettings = next;
|
|
15382
|
+
this.mergeAllSettings();
|
|
15383
|
+
} finally {
|
|
15384
|
+
this.writeMutex.release();
|
|
15385
|
+
}
|
|
15386
|
+
}
|
|
15184
15387
|
async setCwd(cwd) {
|
|
15185
15388
|
if (this.cwd === cwd) return;
|
|
15186
15389
|
if (this.initPromise) await this.initPromise;
|
|
@@ -18766,35 +18969,6 @@ var SessionLogWriter = class _SessionLogWriter {
|
|
|
18766
18969
|
}
|
|
18767
18970
|
};
|
|
18768
18971
|
|
|
18769
|
-
// src/utils/async-mutex.ts
|
|
18770
|
-
var AsyncMutex = class {
|
|
18771
|
-
locked = false;
|
|
18772
|
-
queue = [];
|
|
18773
|
-
async acquire() {
|
|
18774
|
-
if (!this.locked) {
|
|
18775
|
-
this.locked = true;
|
|
18776
|
-
return;
|
|
18777
|
-
}
|
|
18778
|
-
return new Promise((resolve4) => {
|
|
18779
|
-
this.queue.push(resolve4);
|
|
18780
|
-
});
|
|
18781
|
-
}
|
|
18782
|
-
release() {
|
|
18783
|
-
const next = this.queue.shift();
|
|
18784
|
-
if (next) {
|
|
18785
|
-
next();
|
|
18786
|
-
} else {
|
|
18787
|
-
this.locked = false;
|
|
18788
|
-
}
|
|
18789
|
-
}
|
|
18790
|
-
isLocked() {
|
|
18791
|
-
return this.locked;
|
|
18792
|
-
}
|
|
18793
|
-
get queueLength() {
|
|
18794
|
-
return this.queue.length;
|
|
18795
|
-
}
|
|
18796
|
-
};
|
|
18797
|
-
|
|
18798
18972
|
// src/server/cloud-prompt.ts
|
|
18799
18973
|
function normalizeCloudPromptContent(content) {
|
|
18800
18974
|
if (typeof content === "string") {
|