@swifty.js/swifty 0.0.28 → 0.0.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -38
- package/dist/{agent-3NON2EXE.js → agent-TCNIRVHD.js} +1 -1
- package/dist/anthropic-7RIVSEDF.js +4 -0
- package/dist/{checker-N5TIJEN5.js → checker-3LHDOATB.js} +1 -1
- package/dist/chunk-2IBMB3ZM.js +150 -0
- package/dist/chunk-AQ3A5CF4.js +5 -0
- package/dist/chunk-FA26MQ7K.js +257 -0
- package/dist/{chunk-JGSIQXEN.js → chunk-QFSCPD63.js} +1 -1
- package/dist/chunk-TKADLB2Q.js +4 -0
- package/dist/chunk-VA64DJNN.js +4 -0
- package/dist/lib/{agent-XYO2MXXK.js → agent-IRY7KR5D.js} +3 -3
- package/dist/lib/{anthropic-TGJGAP5R.js → anthropic-CL5IK7KN.js} +3 -3
- package/dist/lib/{checker-AXHPKVBY.js → checker-BS4MK3O6.js} +1 -1
- package/dist/lib/{chunk-2IVEVG5N.js → chunk-7QPDFSQE.js} +10 -17
- package/dist/lib/{chunk-P4F46PEF.js → chunk-I7OPU4K2.js} +48 -8
- package/dist/lib/{chunk-ALJAVRRX.js → chunk-IYPTOY3Y.js} +59 -19
- package/dist/lib/{chunk-DV6RV5WU.js → chunk-OOSNCCI7.js} +134 -258
- package/dist/lib/{chunk-FBSPZQGC.js → chunk-OQIQOU5S.js} +132 -73
- package/dist/lib/{chunk-IB5IQK2S.js → chunk-PIL7M52F.js} +2 -11
- package/dist/lib/{chunk-DJ6AILPN.js → chunk-RMB3J46W.js} +13 -16
- package/dist/lib/index.d.ts +387 -112
- package/dist/lib/index.js +1000 -1157
- package/dist/lib/{openai-5UCIIDPO.js → openai-LH4E7ZQS.js} +2 -2
- package/dist/lib/{tool-filter-R6HDDJHE.js → tool-filter-CS6W2GMU.js} +1 -1
- package/dist/main.js +186 -178
- package/dist/{openai-YLS2LUAI.js → openai-RZLCY55V.js} +15 -15
- package/dist/{server-GX72MJQF.js → server-IVIRQTO7.js} +17 -17
- package/dist/{tool-filter-VBP6WOGO.js → tool-filter-CG3KGQQW.js} +1 -1
- package/package.json +5 -4
- package/dist/anthropic-BJ5GN2VT.js +0 -4
- package/dist/chunk-D34FUVGU.js +0 -4
- package/dist/chunk-LZBOXJX2.js +0 -301
- package/dist/chunk-R63ASIIW.js +0 -407
- package/dist/chunk-WX3B64R4.js +0 -4
- package/dist/chunk-Y6SQB5FG.js +0 -4
- package/dist/glob.wasm +0 -0
- package/dist/lib/glob.wasm +0 -0
|
@@ -40,7 +40,7 @@ var ContextTooLongError = class extends LLMError {
|
|
|
40
40
|
import { existsSync, readFileSync } from "fs";
|
|
41
41
|
import { homedir } from "os";
|
|
42
42
|
import { join } from "path";
|
|
43
|
-
import { safeParse } from "@modelcontextprotocol/sdk/server/zod-compat.js";
|
|
43
|
+
import { getParseErrorMessage, safeParse } from "@modelcontextprotocol/sdk/server/zod-compat.js";
|
|
44
44
|
import yaml from "js-yaml";
|
|
45
45
|
import { z } from "zod";
|
|
46
46
|
var log = createChildLogger({ module: "config" });
|
|
@@ -59,7 +59,22 @@ var ConfigError = class extends Error {
|
|
|
59
59
|
this.name = "ConfigError";
|
|
60
60
|
}
|
|
61
61
|
};
|
|
62
|
-
|
|
62
|
+
function globalConfigPath() {
|
|
63
|
+
return join(homedir(), ".swifty", "config.yaml");
|
|
64
|
+
}
|
|
65
|
+
var THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
66
|
+
var ReasoningEffortSchema = z.enum(["none", "minimal", "low", "medium", "high", "xhigh", "max"]);
|
|
67
|
+
var AnthropicEffortSchema = z.enum(["low", "medium", "high", "xhigh", "max"]);
|
|
68
|
+
var ThinkingLevelMapSchema = z.strictObject({
|
|
69
|
+
off: z.literal("none").nullable().optional(),
|
|
70
|
+
minimal: ReasoningEffortSchema.nullable().optional(),
|
|
71
|
+
low: ReasoningEffortSchema.nullable().optional(),
|
|
72
|
+
medium: ReasoningEffortSchema.nullable().optional(),
|
|
73
|
+
high: ReasoningEffortSchema.nullable().optional(),
|
|
74
|
+
xhigh: ReasoningEffortSchema.nullable().optional(),
|
|
75
|
+
max: ReasoningEffortSchema.nullable().optional()
|
|
76
|
+
});
|
|
77
|
+
var ProviderConfigSchema = z.looseObject({
|
|
63
78
|
name: z.string(),
|
|
64
79
|
/**
|
|
65
80
|
* enum: ["anthropic", "openai", "openai-compat"]
|
|
@@ -68,17 +83,98 @@ var ProviderConfigSchema = z.object({
|
|
|
68
83
|
base_url: z.string(),
|
|
69
84
|
model: z.string(),
|
|
70
85
|
api_key: z.string().optional(),
|
|
71
|
-
thinking: z.
|
|
86
|
+
thinking: z.enum(THINKING_LEVELS).optional(),
|
|
87
|
+
/** Explicit capability metadata, never inferred from model names. */
|
|
88
|
+
reasoning: z.boolean().optional(),
|
|
89
|
+
thinking_level_map: ThinkingLevelMapSchema.optional(),
|
|
90
|
+
/** Only Anthropic uses this mode; existing configurations use budgets. */
|
|
91
|
+
thinking_mode: z.enum(["budget", "adaptive"]).optional(),
|
|
72
92
|
context_window: z.coerce.number().optional(),
|
|
93
|
+
/**
|
|
94
|
+
* The model's output ceiling (PI's `model.maxTokens`). Clamped to the
|
|
95
|
+
* context window; reasoning shares this ceiling instead of raising it.
|
|
96
|
+
*/
|
|
73
97
|
max_output_tokens: z.coerce.number().optional()
|
|
74
98
|
});
|
|
75
|
-
var
|
|
99
|
+
var DEFAULT_THINKING_LEVEL = "high";
|
|
76
100
|
var DEFAULT_CONTEXT_WINDOW = 1e6;
|
|
77
101
|
var DEFAULT_MAX_OUTPUT_TOKENS = 128e3;
|
|
102
|
+
var THINKING_BUDGETS = {
|
|
103
|
+
minimal: 1024,
|
|
104
|
+
low: 2048,
|
|
105
|
+
medium: 8192,
|
|
106
|
+
high: 16384,
|
|
107
|
+
xhigh: 32768,
|
|
108
|
+
max: 65536
|
|
109
|
+
};
|
|
110
|
+
var MIN_THINKING_BUDGET_TOKENS = 1024;
|
|
111
|
+
var MIN_THINKING_ANSWER_TOKENS = 1024;
|
|
112
|
+
function isValidThinkingLevel(value) {
|
|
113
|
+
return THINKING_LEVELS.some((level) => level === value);
|
|
114
|
+
}
|
|
115
|
+
function getThinkingLevel(provider) {
|
|
116
|
+
return clampThinkingLevel(provider, provider.thinking ?? DEFAULT_THINKING_LEVEL);
|
|
117
|
+
}
|
|
118
|
+
function thinkingBudgetForLevel(level) {
|
|
119
|
+
return level === "off" ? 0 : THINKING_BUDGETS[level];
|
|
120
|
+
}
|
|
121
|
+
function toReasoningEffort(level, provider) {
|
|
122
|
+
if (provider?.reasoning === false) {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
if (level === "off") {
|
|
126
|
+
return "none";
|
|
127
|
+
}
|
|
128
|
+
const mapped = provider?.thinking_level_map?.[level];
|
|
129
|
+
if (mapped !== void 0) {
|
|
130
|
+
return mapped;
|
|
131
|
+
}
|
|
132
|
+
if (provider?.protocol === "anthropic" && provider.thinking_mode === "adaptive") {
|
|
133
|
+
if (level === "minimal") {
|
|
134
|
+
return "low";
|
|
135
|
+
}
|
|
136
|
+
if (level === "xhigh") {
|
|
137
|
+
return "high";
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return level;
|
|
141
|
+
}
|
|
142
|
+
function toAnthropicThinkingEffort(level, provider) {
|
|
143
|
+
const parsed = AnthropicEffortSchema.safeParse(toReasoningEffort(level, provider));
|
|
144
|
+
return parsed.success ? parsed.data : null;
|
|
145
|
+
}
|
|
146
|
+
function getSupportedThinkingLevels(provider) {
|
|
147
|
+
if (provider.reasoning === false || provider.protocol === "anthropic" && provider.thinking_mode !== "adaptive" && getMaxOutputTokens(provider) < MIN_THINKING_BUDGET_TOKENS + MIN_THINKING_ANSWER_TOKENS) {
|
|
148
|
+
return ["off"];
|
|
149
|
+
}
|
|
150
|
+
return THINKING_LEVELS.filter((level) => {
|
|
151
|
+
if (level === "off") {
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
if (provider.protocol === "anthropic" && provider.thinking_mode === "adaptive") {
|
|
155
|
+
return toAnthropicThinkingEffort(level, provider) !== null;
|
|
156
|
+
}
|
|
157
|
+
const effort = toReasoningEffort(level, provider);
|
|
158
|
+
return effort !== null && effort !== "none";
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
function clampThinkingLevel(provider, level) {
|
|
162
|
+
const supported = getSupportedThinkingLevels(provider);
|
|
163
|
+
let effective = "off";
|
|
164
|
+
for (const candidate of THINKING_LEVELS) {
|
|
165
|
+
if (supported.includes(candidate)) {
|
|
166
|
+
effective = candidate;
|
|
167
|
+
}
|
|
168
|
+
if (candidate === level) {
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return effective;
|
|
173
|
+
}
|
|
78
174
|
function withProviderDefaults(provider) {
|
|
79
175
|
return {
|
|
80
176
|
...provider,
|
|
81
|
-
thinking: provider
|
|
177
|
+
thinking: getThinkingLevel(provider),
|
|
82
178
|
context_window: getContextWindow(provider),
|
|
83
179
|
max_output_tokens: getMaxOutputTokens(provider)
|
|
84
180
|
};
|
|
@@ -87,7 +183,9 @@ function getContextWindow(provider) {
|
|
|
87
183
|
return Number.isSafeInteger(provider.context_window) && (provider.context_window ?? 0) > 0 ? provider.context_window ?? DEFAULT_CONTEXT_WINDOW : DEFAULT_CONTEXT_WINDOW;
|
|
88
184
|
}
|
|
89
185
|
function getMaxOutputTokens(provider) {
|
|
90
|
-
|
|
186
|
+
const configured = provider.max_output_tokens;
|
|
187
|
+
const maxOutput = Number.isSafeInteger(configured) && (configured ?? 0) > 0 ? configured ?? DEFAULT_MAX_OUTPUT_TOKENS : DEFAULT_MAX_OUTPUT_TOKENS;
|
|
188
|
+
return Math.min(maxOutput, getContextWindow(provider));
|
|
91
189
|
}
|
|
92
190
|
function resolveAPIKey(p) {
|
|
93
191
|
if (p.api_key) {
|
|
@@ -177,6 +275,10 @@ function loadSingleFile(path) {
|
|
|
177
275
|
const parsed2 = safeParse(z.array(ProviderConfigSchema), raw.providers);
|
|
178
276
|
if (parsed2.success) {
|
|
179
277
|
providers = parsed2.data.map(withProviderDefaults);
|
|
278
|
+
} else {
|
|
279
|
+
throw new ConfigError(
|
|
280
|
+
`Invalid provider configuration in ${path}: ${getParseErrorMessage(parsed2.error)}`
|
|
281
|
+
);
|
|
180
282
|
}
|
|
181
283
|
}
|
|
182
284
|
if ("permission_mode" in raw && typeof raw.permission_mode === "string") {
|
|
@@ -216,41 +318,6 @@ function loadSingleFile(path) {
|
|
|
216
318
|
enable_fork: enableFork
|
|
217
319
|
};
|
|
218
320
|
}
|
|
219
|
-
function mergeConfig(base, override) {
|
|
220
|
-
if (override.providers.length > 0) {
|
|
221
|
-
base.providers = override.providers;
|
|
222
|
-
}
|
|
223
|
-
if (override.permission_mode) {
|
|
224
|
-
base.permission_mode = override.permission_mode;
|
|
225
|
-
}
|
|
226
|
-
if (override.mcp_servers.length > 0) {
|
|
227
|
-
const mcpToIdx = /* @__PURE__ */ new Map();
|
|
228
|
-
for (let i = 0; i < base.mcp_servers.length; i++) {
|
|
229
|
-
const mcp = base.mcp_servers[i];
|
|
230
|
-
mcpToIdx.set(mcp.name, i);
|
|
231
|
-
}
|
|
232
|
-
for (const s of override.mcp_servers) {
|
|
233
|
-
const idx = mcpToIdx.get(s.name);
|
|
234
|
-
if (idx !== void 0) {
|
|
235
|
-
base.mcp_servers[idx] = s;
|
|
236
|
-
} else {
|
|
237
|
-
base.mcp_servers.push(s);
|
|
238
|
-
mcpToIdx.set(s.name, base.mcp_servers.length - 1);
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
base.hooks = [...base.hooks, ...override.hooks];
|
|
243
|
-
if (override.sandbox) {
|
|
244
|
-
base.sandbox = { ...base.sandbox, ...override.sandbox };
|
|
245
|
-
}
|
|
246
|
-
if (override.enable_coordinator_mode) {
|
|
247
|
-
base.enable_coordinator_mode = true;
|
|
248
|
-
}
|
|
249
|
-
if (override.enable_fork !== void 0) {
|
|
250
|
-
base.enable_fork = override.enable_fork;
|
|
251
|
-
}
|
|
252
|
-
return base;
|
|
253
|
-
}
|
|
254
321
|
function validateProviders(config) {
|
|
255
322
|
if (config.providers.length === 0) {
|
|
256
323
|
throw new ConfigError("At least one provider MUST be configured.");
|
|
@@ -277,43 +344,24 @@ function validateProviders(config) {
|
|
|
277
344
|
}
|
|
278
345
|
function loadConfig(path, options = {}) {
|
|
279
346
|
if (path) {
|
|
280
|
-
const
|
|
281
|
-
if (!options.allowEmptyProviders ||
|
|
282
|
-
validateProviders(
|
|
283
|
-
}
|
|
284
|
-
return config;
|
|
285
|
-
}
|
|
286
|
-
const wd = process.cwd();
|
|
287
|
-
const home = homedir();
|
|
288
|
-
const candidates = [
|
|
289
|
-
join(home, ".swifty", "config.yaml"),
|
|
290
|
-
join(wd, ".swifty", "config.yaml"),
|
|
291
|
-
join(wd, ".swifty", "config.local.yaml")
|
|
292
|
-
];
|
|
293
|
-
let merged = null;
|
|
294
|
-
for (const candidate of candidates) {
|
|
295
|
-
if (!existsSync(candidate)) {
|
|
296
|
-
continue;
|
|
297
|
-
}
|
|
298
|
-
const layer = loadSingleFile(candidate);
|
|
299
|
-
if (!merged) {
|
|
300
|
-
merged = layer;
|
|
301
|
-
} else {
|
|
302
|
-
merged = mergeConfig(merged, layer);
|
|
347
|
+
const config2 = loadSingleFile(path);
|
|
348
|
+
if (!options.allowEmptyProviders || config2.providers.length > 0) {
|
|
349
|
+
validateProviders(config2);
|
|
303
350
|
}
|
|
351
|
+
return config2;
|
|
304
352
|
}
|
|
305
|
-
|
|
353
|
+
const candidate = globalConfigPath();
|
|
354
|
+
if (!existsSync(candidate)) {
|
|
306
355
|
if (options.allowEmptyProviders) {
|
|
307
356
|
return { providers: [], mcp_servers: [], hooks: [] };
|
|
308
357
|
}
|
|
309
|
-
throw new ConfigError(
|
|
310
|
-
"No config file found, expected .swifty/config.y(a)ml under project or $HOME/.swifty/config.y(a)ml."
|
|
311
|
-
);
|
|
358
|
+
throw new ConfigError(`No config file found, expected ${candidate}.`);
|
|
312
359
|
}
|
|
313
|
-
|
|
314
|
-
|
|
360
|
+
const config = loadSingleFile(candidate);
|
|
361
|
+
if (!options.allowEmptyProviders || config.providers.length > 0) {
|
|
362
|
+
validateProviders(config);
|
|
315
363
|
}
|
|
316
|
-
return
|
|
364
|
+
return config;
|
|
317
365
|
}
|
|
318
366
|
|
|
319
367
|
// src/conversation/pairing.ts
|
|
@@ -374,17 +422,28 @@ export {
|
|
|
374
422
|
NetworkError,
|
|
375
423
|
ContextTooLongError,
|
|
376
424
|
ConfigError,
|
|
425
|
+
globalConfigPath,
|
|
426
|
+
THINKING_LEVELS,
|
|
377
427
|
ProviderConfigSchema,
|
|
378
|
-
|
|
428
|
+
DEFAULT_THINKING_LEVEL,
|
|
379
429
|
DEFAULT_CONTEXT_WINDOW,
|
|
380
430
|
DEFAULT_MAX_OUTPUT_TOKENS,
|
|
431
|
+
THINKING_BUDGETS,
|
|
432
|
+
MIN_THINKING_BUDGET_TOKENS,
|
|
433
|
+
MIN_THINKING_ANSWER_TOKENS,
|
|
434
|
+
isValidThinkingLevel,
|
|
435
|
+
getThinkingLevel,
|
|
436
|
+
thinkingBudgetForLevel,
|
|
437
|
+
toReasoningEffort,
|
|
438
|
+
toAnthropicThinkingEffort,
|
|
439
|
+
getSupportedThinkingLevels,
|
|
440
|
+
clampThinkingLevel,
|
|
381
441
|
withProviderDefaults,
|
|
382
442
|
getContextWindow,
|
|
383
443
|
getMaxOutputTokens,
|
|
384
444
|
resolveAPIKey,
|
|
385
445
|
HookConfigSchema,
|
|
386
446
|
forkEnabled,
|
|
387
|
-
mergeConfig,
|
|
388
447
|
loadConfig,
|
|
389
448
|
INTERRUPTED_TOOL_RESULT,
|
|
390
449
|
REJECTED_TOOL_RESULT,
|
|
@@ -101,8 +101,7 @@ function isToolResultContentBlock(value) {
|
|
|
101
101
|
return normalizeToolResultContentBlock(value) !== null;
|
|
102
102
|
}
|
|
103
103
|
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
104
|
-
".
|
|
105
|
-
// Claude Code
|
|
104
|
+
".agents",
|
|
106
105
|
".git",
|
|
107
106
|
// Git
|
|
108
107
|
".swifty",
|
|
@@ -113,20 +112,12 @@ var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
|
113
112
|
// Python venv
|
|
114
113
|
".mypy_cache",
|
|
115
114
|
// Python mypy
|
|
116
|
-
".tox",
|
|
117
|
-
// Python tox
|
|
118
115
|
"__pycache__",
|
|
119
116
|
// Python
|
|
120
|
-
"build",
|
|
121
|
-
// C++
|
|
122
117
|
"dist",
|
|
123
118
|
// Webpack, Vite
|
|
124
|
-
"node_modules"
|
|
119
|
+
"node_modules"
|
|
125
120
|
// Node.js
|
|
126
|
-
"vendor",
|
|
127
|
-
// Go
|
|
128
|
-
"venv"
|
|
129
|
-
// Python venv
|
|
130
121
|
]);
|
|
131
122
|
|
|
132
123
|
export {
|
|
@@ -88,11 +88,7 @@ var CONTENT_FIELDS = {
|
|
|
88
88
|
Grep: "pattern",
|
|
89
89
|
InstallSkill: "source"
|
|
90
90
|
};
|
|
91
|
-
var DEFAULT_DENY_WRITE = [
|
|
92
|
-
".swifty/config.yaml",
|
|
93
|
-
".swifty/permissions.local.yaml",
|
|
94
|
-
".swifty/skills/"
|
|
95
|
-
];
|
|
91
|
+
var DEFAULT_DENY_WRITE = [".swifty/permissions.yaml", ".agents/skills/"];
|
|
96
92
|
function extractContent(toolName, args) {
|
|
97
93
|
if (toolName === MCP_CALL_TOOL_NAME) {
|
|
98
94
|
return mcpCallPermissionContent(strArg(args, "server", ""), strArg(args, "tool", ""));
|
|
@@ -218,12 +214,10 @@ function evaluateRules(rules, toolName, content) {
|
|
|
218
214
|
var RuleEngine = class {
|
|
219
215
|
userPath;
|
|
220
216
|
projectPath;
|
|
221
|
-
localPath;
|
|
222
217
|
cache = /* @__PURE__ */ new Map();
|
|
223
218
|
constructor(workDir) {
|
|
224
219
|
this.userPath = join2(homedir(), ".swifty", "permissions.yaml");
|
|
225
220
|
this.projectPath = join2(workDir, ".swifty", "permissions.yaml");
|
|
226
|
-
this.localPath = join2(workDir, ".swifty", "permissions.local.yaml");
|
|
227
221
|
}
|
|
228
222
|
// Read a single rules file; skips disk I/O and parsing on cache hit.
|
|
229
223
|
rulesFor(path) {
|
|
@@ -242,12 +236,12 @@ var RuleEngine = class {
|
|
|
242
236
|
this.cache.set(path, { mtimeNs: st.mtimeNs, size: st.size, rules });
|
|
243
237
|
return rules;
|
|
244
238
|
}
|
|
245
|
-
// Return the merged snapshot of
|
|
239
|
+
// Return the merged snapshot of both rules files. Reuses the previous
|
|
246
240
|
// parse result when files are unchanged; re-reads only on change, so edits
|
|
247
241
|
// take effect on the next evaluation without redundant parsing. One snapshot
|
|
248
242
|
// is taken per tool call and shared across sub-command checks.
|
|
249
243
|
snapshot() {
|
|
250
|
-
return [this.userPath, this.projectPath
|
|
244
|
+
return [this.userPath, this.projectPath].flatMap((p) => this.rulesFor(p));
|
|
251
245
|
}
|
|
252
246
|
// Take a snapshot then adjudicate: reuses the previous parse result when
|
|
253
247
|
// files are unchanged; a freshly written "allow always" rule takes effect
|
|
@@ -257,11 +251,11 @@ var RuleEngine = class {
|
|
|
257
251
|
evaluate(toolName, content) {
|
|
258
252
|
return evaluateRules(this.snapshot(), toolName, content);
|
|
259
253
|
}
|
|
260
|
-
// Persists a rule to the project-
|
|
254
|
+
// Persists a rule to the project-level YAML file in the `Tool(pattern)`
|
|
261
255
|
// format so "allow always" survives a restart.
|
|
262
|
-
|
|
263
|
-
mkdirSync(dirname2(this.
|
|
264
|
-
const rules = loadRulesFile(this.
|
|
256
|
+
appendProjectRule(rule) {
|
|
257
|
+
mkdirSync(dirname2(this.projectPath), { recursive: true });
|
|
258
|
+
const rules = loadRulesFile(this.projectPath);
|
|
265
259
|
const exists = rules.some(
|
|
266
260
|
(r) => r.tool === rule.tool && r.pattern === rule.pattern && r.effect === rule.effect
|
|
267
261
|
);
|
|
@@ -273,7 +267,7 @@ var RuleEngine = class {
|
|
|
273
267
|
rule: `${r.tool}(${r.pattern})`,
|
|
274
268
|
effect: r.effect
|
|
275
269
|
}));
|
|
276
|
-
writeFileSync(this.
|
|
270
|
+
writeFileSync(this.projectPath, yaml.dump(entries), "utf-8");
|
|
277
271
|
}
|
|
278
272
|
};
|
|
279
273
|
function detectDangerous(command) {
|
|
@@ -334,7 +328,10 @@ var PermissionChecker = class _PermissionChecker {
|
|
|
334
328
|
const rules = () => snapshot ??= this.ruleEngine.snapshot();
|
|
335
329
|
const explicitEffect = evaluateRules(rules(), toolName, content);
|
|
336
330
|
if (explicitEffect === "deny" || explicitEffect === "ask") {
|
|
337
|
-
return {
|
|
331
|
+
return {
|
|
332
|
+
effect: explicitEffect,
|
|
333
|
+
reason: `Permission rule: ${explicitEffect}`
|
|
334
|
+
};
|
|
338
335
|
}
|
|
339
336
|
if (this.mode === "plan" && (toolName === "WriteFile" || toolName === "EditFile")) {
|
|
340
337
|
const path = strArg(args, "file_path", "");
|
|
@@ -432,7 +429,7 @@ var PermissionChecker = class _PermissionChecker {
|
|
|
432
429
|
const words = content.trim().split(/\s+/).slice(0, 2);
|
|
433
430
|
pattern = words.join(" ") + "*";
|
|
434
431
|
}
|
|
435
|
-
this.ruleEngine.
|
|
432
|
+
this.ruleEngine.appendProjectRule({
|
|
436
433
|
tool: toolName,
|
|
437
434
|
pattern,
|
|
438
435
|
effect: "allow"
|