@dyyz1993/pi-coding-agent 0.74.23 → 0.74.25
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/extensions/agent-permissions/index.ts +235 -0
- package/dist/extensions/ask-tools/index.ts +115 -0
- package/dist/extensions/auto-memory/contract.d.ts +51 -0
- package/dist/extensions/auto-memory/contract.d.ts.map +1 -0
- package/dist/extensions/auto-memory/contract.js +2 -0
- package/dist/extensions/auto-memory/contract.js.map +1 -0
- package/dist/extensions/auto-memory/contract.ts +56 -0
- package/dist/extensions/auto-memory/index.ts +969 -0
- package/dist/extensions/auto-memory/prompts.ts +202 -0
- package/dist/extensions/auto-memory/skip-rules.ts +297 -0
- package/dist/extensions/auto-memory/utils.ts +208 -0
- package/dist/extensions/auto-session-title/index.ts +83 -0
- package/dist/extensions/bash-ext/contract.d.ts +79 -0
- package/dist/extensions/bash-ext/contract.d.ts.map +1 -0
- package/dist/extensions/bash-ext/contract.js +2 -0
- package/dist/extensions/bash-ext/contract.js.map +1 -0
- package/dist/extensions/bash-ext/contract.ts +69 -0
- package/dist/extensions/bash-ext/index.ts +858 -0
- package/dist/extensions/claude-hooks-compat/config-loader.ts +49 -0
- package/dist/extensions/claude-hooks-compat/handler-runner.ts +377 -0
- package/dist/extensions/claude-hooks-compat/if-parser.ts +53 -0
- package/dist/extensions/claude-hooks-compat/index.ts +178 -0
- package/dist/extensions/claude-hooks-compat/matcher.ts +17 -0
- package/dist/extensions/claude-hooks-compat/stdin-builder.ts +27 -0
- package/dist/extensions/claude-hooks-compat/types.ts +77 -0
- package/dist/extensions/compaction-manager/config.ts +47 -0
- package/dist/extensions/compaction-manager/context-fold.ts +63 -0
- package/dist/extensions/compaction-manager/index.ts +151 -0
- package/dist/extensions/compaction-manager/microcompact.ts +49 -0
- package/dist/extensions/compaction-manager/reactive.ts +9 -0
- package/dist/extensions/compaction-manager/session-memory.ts +48 -0
- package/dist/extensions/coordinator/INTEGRATION.md +376 -0
- package/dist/extensions/coordinator/handler.test.ts +277 -0
- package/dist/extensions/coordinator/handler.ts +189 -0
- package/dist/extensions/coordinator/index.ts +261 -0
- package/dist/extensions/coordinator/types.d.ts +100 -0
- package/dist/extensions/coordinator/types.d.ts.map +1 -0
- package/dist/extensions/coordinator/types.js +2 -0
- package/dist/extensions/coordinator/types.js.map +1 -0
- package/dist/extensions/coordinator/types.ts +72 -0
- package/dist/extensions/file-snapshot/index.ts +131 -0
- package/dist/extensions/file-time-guard/README.md +133 -0
- package/dist/extensions/file-time-guard/config.ts +13 -0
- package/dist/extensions/file-time-guard/index.ts +171 -0
- package/dist/extensions/hooks-engine/index.ts +117 -0
- package/dist/extensions/lsp/lsp/client/file-tracker.ts +70 -0
- package/dist/extensions/lsp/lsp/client/registry.ts +305 -0
- package/dist/extensions/lsp/lsp/client/runtime.ts +832 -0
- package/dist/extensions/lsp/lsp/config/resolver.ts +573 -0
- package/dist/extensions/lsp/lsp/contract.d.ts +101 -0
- package/dist/extensions/lsp/lsp/contract.d.ts.map +1 -0
- package/dist/extensions/lsp/lsp/contract.js +2 -0
- package/dist/extensions/lsp/lsp/contract.js.map +1 -0
- package/dist/extensions/lsp/lsp/contract.ts +103 -0
- package/dist/extensions/lsp/lsp/hooks/agent-end.ts +169 -0
- package/dist/extensions/lsp/lsp/hooks/diagnostics-mode.d.ts +10 -0
- package/dist/extensions/lsp/lsp/hooks/diagnostics-mode.d.ts.map +1 -0
- package/dist/extensions/lsp/lsp/hooks/diagnostics-mode.js +30 -0
- package/dist/extensions/lsp/lsp/hooks/diagnostics-mode.js.map +1 -0
- package/dist/extensions/lsp/lsp/hooks/diagnostics-mode.ts +41 -0
- package/dist/extensions/lsp/lsp/hooks/writethrough.ts +342 -0
- package/dist/extensions/lsp/lsp/index.ts +307 -0
- package/dist/extensions/lsp/lsp/lsp.test.ts +684 -0
- package/dist/extensions/lsp/lsp/monitoring/server-metrics.ts +176 -0
- package/dist/extensions/lsp/lsp/tools/lsp-tool.ts +402 -0
- package/dist/extensions/lsp/lsp/utils/dependency-resolver.ts +147 -0
- package/dist/extensions/lsp/lsp/utils/diagnostics-wait.ts +41 -0
- package/dist/extensions/lsp/lsp/utils/lsp-helpers.d.ts +20 -0
- package/dist/extensions/lsp/lsp/utils/lsp-helpers.d.ts.map +1 -0
- package/dist/extensions/lsp/lsp/utils/lsp-helpers.js +64 -0
- package/dist/extensions/lsp/lsp/utils/lsp-helpers.js.map +1 -0
- package/dist/extensions/lsp/lsp/utils/lsp-helpers.ts +76 -0
- package/dist/extensions/message-bridge/GUIDE.md +210 -0
- package/dist/extensions/message-bridge/index.ts +222 -0
- package/dist/extensions/output-guard/index.ts +384 -0
- package/dist/extensions/preview/index.ts +278 -0
- package/dist/extensions/rules-engine/MATCH_HISTORY_RECONCILIATION.md +111 -0
- package/dist/extensions/rules-engine/RULES-ENGINE-GUIDE.md +470 -0
- package/dist/extensions/rules-engine/cache.js +232 -0
- package/dist/extensions/rules-engine/cache.ts +38 -0
- package/dist/extensions/rules-engine/config.js +63 -0
- package/dist/extensions/rules-engine/config.ts +70 -0
- package/dist/extensions/rules-engine/index.js +1530 -0
- package/dist/extensions/rules-engine/index.ts +552 -0
- package/dist/extensions/rules-engine/injector.js +68 -0
- package/dist/extensions/rules-engine/injector.ts +74 -0
- package/dist/extensions/rules-engine/loader.js +179 -0
- package/dist/extensions/rules-engine/loader.ts +205 -0
- package/dist/extensions/rules-engine/matcher.js +534 -0
- package/dist/extensions/rules-engine/matcher.ts +52 -0
- package/dist/extensions/rules-engine/types.d.ts +156 -0
- package/dist/extensions/rules-engine/types.d.ts.map +1 -0
- package/dist/extensions/rules-engine/types.js +2 -0
- package/dist/extensions/rules-engine/types.js.map +1 -0
- package/dist/extensions/rules-engine/types.ts +169 -0
- package/dist/extensions/session-supervisor/checker.ts +116 -0
- package/dist/extensions/session-supervisor/config.ts +45 -0
- package/dist/extensions/session-supervisor/index.ts +726 -0
- package/dist/extensions/session-supervisor/prompts.ts +132 -0
- package/dist/extensions/session-supervisor/scheduler.ts +69 -0
- package/dist/extensions/session-supervisor/types.ts +215 -0
- package/dist/extensions/subagent/README.md +172 -0
- package/dist/extensions/subagent/agents/explorer.md +25 -0
- package/dist/extensions/subagent/agents/guide.md +27 -0
- package/dist/extensions/subagent/agents/planner.md +37 -0
- package/dist/extensions/subagent/agents/reviewer.md +35 -0
- package/dist/extensions/subagent/agents/scout.md +50 -0
- package/dist/extensions/subagent/agents/verification.md +35 -0
- package/dist/extensions/subagent/agents/worker.md +24 -0
- package/dist/extensions/subagent/agents.ts +25 -0
- package/dist/extensions/subagent/index.ts +987 -0
- package/dist/extensions/subagent/prompts/implement-and-review.md +10 -0
- package/dist/extensions/subagent/prompts/implement.md +10 -0
- package/dist/extensions/subagent/prompts/scout-and-plan.md +9 -0
- package/dist/extensions/subagent-ext/contract.d.ts +2 -0
- package/dist/extensions/subagent-ext/contract.d.ts.map +1 -0
- package/dist/extensions/subagent-ext/contract.js +2 -0
- package/dist/extensions/subagent-ext/contract.js.map +1 -0
- package/dist/extensions/subagent-ext/contract.ts +1 -0
- package/dist/extensions/subagent-ext/index.ts +347 -0
- package/dist/extensions/subagent-shared/contract.d.ts +25 -0
- package/dist/extensions/subagent-shared/contract.d.ts.map +1 -0
- package/dist/extensions/subagent-shared/contract.js +2 -0
- package/dist/extensions/subagent-shared/contract.js.map +1 -0
- package/dist/extensions/subagent-shared/contract.ts +28 -0
- package/dist/extensions/subagent-shared/index.ts +4 -0
- package/dist/extensions/subagent-shared/render.ts +166 -0
- package/dist/extensions/subagent-shared/types.ts +35 -0
- package/dist/extensions/subagent-shared/utils.ts +112 -0
- package/dist/extensions/subagent-v2/contract.d.ts +2 -0
- package/dist/extensions/subagent-v2/contract.d.ts.map +1 -0
- package/dist/extensions/subagent-v2/contract.js +2 -0
- package/dist/extensions/subagent-v2/contract.js.map +1 -0
- package/dist/extensions/subagent-v2/contract.ts +1 -0
- package/dist/extensions/subagent-v2/index.ts +599 -0
- package/dist/extensions/todo-ext/contract.d.ts +27 -0
- package/dist/extensions/todo-ext/contract.d.ts.map +1 -0
- package/dist/extensions/todo-ext/contract.js +2 -0
- package/dist/extensions/todo-ext/contract.js.map +1 -0
- package/dist/extensions/todo-ext/contract.ts +30 -0
- package/dist/extensions/todo-ext/index.ts +419 -0
- package/package.json +3 -2
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
// packages/coding-agent/extensions/rules-engine/config.ts
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
var DEFAULT_DIRS = {
|
|
5
|
+
managed: ["/etc/claude-code/.claude/rules"],
|
|
6
|
+
user: [path.join(homedir(), ".claude", "rules"), path.join(homedir(), ".config", "opencode", "rules")],
|
|
7
|
+
pi: [".pi/rules"],
|
|
8
|
+
project: [".claude/rules", ".opencode/rules", ".trae/rules"]
|
|
9
|
+
};
|
|
10
|
+
function resolveDirs(projectDir, config) {
|
|
11
|
+
const sources = config.dirs || DEFAULT_DIRS;
|
|
12
|
+
const result = [];
|
|
13
|
+
for (const [scope, paths] of Object.entries(sources)) {
|
|
14
|
+
for (const p of paths) {
|
|
15
|
+
result.push({
|
|
16
|
+
scope,
|
|
17
|
+
dir: p.startsWith("/") || p.startsWith("~") ? p.replace(/^~/, homedir()) : path.resolve(projectDir, p),
|
|
18
|
+
source: p
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return result;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// packages/coding-agent/extensions/rules-engine/loader.ts
|
|
26
|
+
import * as fs from "node:fs";
|
|
27
|
+
import * as path2 from "node:path";
|
|
28
|
+
function splitComma(val) {
|
|
29
|
+
const result = [];
|
|
30
|
+
let depth = 0;
|
|
31
|
+
let current = "";
|
|
32
|
+
for (const ch of val) {
|
|
33
|
+
if (ch === "{" || ch === "(" || ch === "[") depth++;
|
|
34
|
+
else if (ch === "}" || ch === ")" || ch === "]") depth--;
|
|
35
|
+
if (ch === "," && depth === 0) {
|
|
36
|
+
const trimmed2 = current.trim().replace(/^["']|["']$/g, "");
|
|
37
|
+
if (trimmed2) result.push(trimmed2);
|
|
38
|
+
current = "";
|
|
39
|
+
} else {
|
|
40
|
+
current += ch;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const trimmed = current.trim().replace(/^["']|["']$/g, "");
|
|
44
|
+
if (trimmed) result.push(trimmed);
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
47
|
+
function parseFrontmatter(content) {
|
|
48
|
+
const frontmatterRegex = /^---\r?\n([\s\S]*?)\n?---\r?\n([\s\S]*)$/;
|
|
49
|
+
const match = content.match(frontmatterRegex);
|
|
50
|
+
if (!match) {
|
|
51
|
+
return { data: {}, body: content };
|
|
52
|
+
}
|
|
53
|
+
const [, frontmatterStr, body] = match;
|
|
54
|
+
const data = {};
|
|
55
|
+
const lines = frontmatterStr.split("\n");
|
|
56
|
+
let i = 0;
|
|
57
|
+
while (i < lines.length) {
|
|
58
|
+
const line = lines[i];
|
|
59
|
+
const colonIndex = line.indexOf(":");
|
|
60
|
+
if (colonIndex === -1) {
|
|
61
|
+
i++;
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
const rawKey = line.slice(0, colonIndex).trim();
|
|
65
|
+
let value = line.slice(colonIndex + 1).trim();
|
|
66
|
+
if (value === "" || value === "null" || value === "undefined") {
|
|
67
|
+
const listItems = [];
|
|
68
|
+
let j = i + 1;
|
|
69
|
+
while (j < lines.length) {
|
|
70
|
+
const subLine = lines[j];
|
|
71
|
+
if (subLine.match(/^\s*-\s+/)) {
|
|
72
|
+
listItems.push(
|
|
73
|
+
subLine.replace(/^\s*-\s+/, "").trim().replace(/^["']|["']$/g, "")
|
|
74
|
+
);
|
|
75
|
+
j++;
|
|
76
|
+
} else if (subLine.trim() === "" || subLine.match(/^\s+/)) {
|
|
77
|
+
j++;
|
|
78
|
+
} else {
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (listItems.length > 0) {
|
|
83
|
+
const camelKey2 = rawKey.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
84
|
+
data[camelKey2] = listItems;
|
|
85
|
+
i = j;
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
value = null;
|
|
89
|
+
} else if (value.startsWith("[") && value.endsWith("]")) {
|
|
90
|
+
try {
|
|
91
|
+
value = JSON.parse(value.replace(/'/g, '"'));
|
|
92
|
+
} catch (err) {
|
|
93
|
+
console.debug("[rules-engine] JSON array parse failed:", err instanceof Error ? err.message : err);
|
|
94
|
+
value = value.slice(1, -1).split(",").map((v) => v.trim().replace(/^["']|["']$/g, ""));
|
|
95
|
+
}
|
|
96
|
+
} else if (value.startsWith('"') && value.endsWith('"')) {
|
|
97
|
+
value = value.slice(1, -1);
|
|
98
|
+
} else if (value.startsWith("'") && value.endsWith("'")) {
|
|
99
|
+
value = value.slice(1, -1);
|
|
100
|
+
}
|
|
101
|
+
const camelKey = rawKey.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
102
|
+
if ((camelKey === "paths" || camelKey === "globs") && typeof value === "string") {
|
|
103
|
+
data[camelKey] = splitComma(value);
|
|
104
|
+
} else {
|
|
105
|
+
data[camelKey] = value;
|
|
106
|
+
}
|
|
107
|
+
i++;
|
|
108
|
+
}
|
|
109
|
+
return { data, body: body.trim() };
|
|
110
|
+
}
|
|
111
|
+
function extractTitle(body) {
|
|
112
|
+
for (const line of body.split("\n")) {
|
|
113
|
+
const trimmed = line.trim();
|
|
114
|
+
if (trimmed) {
|
|
115
|
+
return trimmed.replace(/^#+\s*/, "").replace(/\*\*/g, "");
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return "Untitled Rule";
|
|
119
|
+
}
|
|
120
|
+
function parsePaths(raw) {
|
|
121
|
+
if (!raw) return [];
|
|
122
|
+
if (typeof raw === "string") {
|
|
123
|
+
return raw.split(",").map((g) => g.trim()).filter(Boolean);
|
|
124
|
+
}
|
|
125
|
+
if (Array.isArray(raw)) return raw;
|
|
126
|
+
return [];
|
|
127
|
+
}
|
|
128
|
+
function parseRuleFile(filePath, content) {
|
|
129
|
+
const { data, body } = parseFrontmatter(content);
|
|
130
|
+
const rawGlobs = data.globs ?? data.paths;
|
|
131
|
+
const globs = parsePaths(rawGlobs);
|
|
132
|
+
const rawPaths = data.paths;
|
|
133
|
+
const paths = parsePaths(rawPaths);
|
|
134
|
+
const isUnconditional = globs.length === 0 || globs.length === 1 && globs[0] === "**";
|
|
135
|
+
const frontmatter = {};
|
|
136
|
+
if (rawGlobs) {
|
|
137
|
+
frontmatter.globs = globs;
|
|
138
|
+
if (rawPaths) {
|
|
139
|
+
frontmatter.paths = paths;
|
|
140
|
+
} else {
|
|
141
|
+
frontmatter.paths = globs;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
if (data.description && typeof data.description === "string") frontmatter.description = data.description;
|
|
145
|
+
if (data.severity && typeof data.severity === "string")
|
|
146
|
+
frontmatter.severity = data.severity;
|
|
147
|
+
if (data.allowedTools)
|
|
148
|
+
frontmatter.allowedTools = typeof data.allowedTools === "string" ? [data.allowedTools] : data.allowedTools;
|
|
149
|
+
if (data.whenToUse && typeof data.whenToUse === "string") frontmatter.whenToUse = data.whenToUse;
|
|
150
|
+
if (data.notifyOnMatch !== void 0)
|
|
151
|
+
frontmatter.notifyOnMatch = data.notifyOnMatch === "true" || data.notifyOnMatch === true;
|
|
152
|
+
if (data.skipInPrompt !== void 0)
|
|
153
|
+
frontmatter.skipInPrompt = data.skipInPrompt === "true" || data.skipInPrompt === true;
|
|
154
|
+
return {
|
|
155
|
+
name: path2.basename(filePath, path2.extname(filePath)),
|
|
156
|
+
filePath,
|
|
157
|
+
title: extractTitle(body),
|
|
158
|
+
content: body.trim(),
|
|
159
|
+
scope: "project",
|
|
160
|
+
source: "",
|
|
161
|
+
frontmatter,
|
|
162
|
+
isUnconditional
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
function scanDir(dir, files = []) {
|
|
166
|
+
if (!fs.existsSync(dir)) return files;
|
|
167
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
168
|
+
for (const entry of entries) {
|
|
169
|
+
const fullPath = path2.join(dir, entry.name);
|
|
170
|
+
if (entry.isDirectory()) {
|
|
171
|
+
scanDir(fullPath, files);
|
|
172
|
+
} else if (entry.isFile() && (entry.name.endsWith(".md") || entry.name.endsWith(".mdc"))) {
|
|
173
|
+
files.push(fullPath);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return files;
|
|
177
|
+
}
|
|
178
|
+
function loadRules(rulesDir) {
|
|
179
|
+
const rules = [];
|
|
180
|
+
const unconditional = [];
|
|
181
|
+
const conditional = [];
|
|
182
|
+
if (!fs.existsSync(rulesDir)) {
|
|
183
|
+
return { rules, unconditional, conditional, loadedAt: Date.now() };
|
|
184
|
+
}
|
|
185
|
+
const files = scanDir(rulesDir);
|
|
186
|
+
for (const filePath of files) {
|
|
187
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
188
|
+
const rule = parseRuleFile(filePath, content);
|
|
189
|
+
rules.push(rule);
|
|
190
|
+
if (rule.isUnconditional) {
|
|
191
|
+
unconditional.push(rule);
|
|
192
|
+
} else {
|
|
193
|
+
conditional.push(rule);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return { rules, unconditional, conditional, loadedAt: Date.now() };
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// packages/coding-agent/extensions/rules-engine/cache.ts
|
|
200
|
+
var cache = null;
|
|
201
|
+
async function getRules(projectDir, config) {
|
|
202
|
+
if (cache && Date.now() - cache.loadedAt < config.cacheTTL) {
|
|
203
|
+
return cache.rules;
|
|
204
|
+
}
|
|
205
|
+
const rules = await loadAllRules(projectDir, config);
|
|
206
|
+
cache = { rules, loadedAt: Date.now() };
|
|
207
|
+
return rules;
|
|
208
|
+
}
|
|
209
|
+
function invalidateCache() {
|
|
210
|
+
cache = null;
|
|
211
|
+
}
|
|
212
|
+
async function loadAllRules(projectDir, config) {
|
|
213
|
+
const sources = resolveDirs(projectDir, config);
|
|
214
|
+
const result = [];
|
|
215
|
+
const seen = /* @__PURE__ */ new Set();
|
|
216
|
+
for (const { scope, dir, source } of sources) {
|
|
217
|
+
const ruleCache = loadRules(dir);
|
|
218
|
+
for (const rule of ruleCache.rules) {
|
|
219
|
+
if (seen.has(rule.filePath)) continue;
|
|
220
|
+
seen.add(rule.filePath);
|
|
221
|
+
rule.scope = scope;
|
|
222
|
+
rule.source = source;
|
|
223
|
+
result.push(rule);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return result;
|
|
227
|
+
}
|
|
228
|
+
export {
|
|
229
|
+
getRules,
|
|
230
|
+
invalidateCache
|
|
231
|
+
};
|
|
232
|
+
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiY29uZmlnLnRzIiwgImxvYWRlci50cyIsICJjYWNoZS50cyJdLAogICJzb3VyY2VzQ29udGVudCI6IFsiaW1wb3J0ICogYXMgZnMgZnJvbSBcIm5vZGU6ZnNcIjtcbmltcG9ydCB7IGhvbWVkaXIgfSBmcm9tIFwibm9kZTpvc1wiO1xuaW1wb3J0ICogYXMgcGF0aCBmcm9tIFwibm9kZTpwYXRoXCI7XG5pbXBvcnQgdHlwZSB7IFJ1bGVzQ29uZmlnIH0gZnJvbSBcIi4vdHlwZXMuanNcIjtcblxuY29uc3QgREVGQVVMVF9DQUNIRV9UVEwgPSAzMF8wMDA7XG5cbmNvbnN0IERFRkFVTFRfRElSUyA9IHtcblx0bWFuYWdlZDogW1wiL2V0Yy9jbGF1ZGUtY29kZS8uY2xhdWRlL3J1bGVzXCJdLFxuXHR1c2VyOiBbcGF0aC5qb2luKGhvbWVkaXIoKSwgXCIuY2xhdWRlXCIsIFwicnVsZXNcIiksIHBhdGguam9pbihob21lZGlyKCksIFwiLmNvbmZpZ1wiLCBcIm9wZW5jb2RlXCIsIFwicnVsZXNcIildLFxuXHRwaTogW1wiLnBpL3J1bGVzXCJdLFxuXHRwcm9qZWN0OiBbXCIuY2xhdWRlL3J1bGVzXCIsIFwiLm9wZW5jb2RlL3J1bGVzXCIsIFwiLnRyYWUvcnVsZXNcIl0sXG59O1xuXG5mdW5jdGlvbiBkZWZhdWx0Q29uZmlnKCk6IFJ1bGVzQ29uZmlnIHtcblx0cmV0dXJuIHtcblx0XHRjYWNoZVRUTDogREVGQVVMVF9DQUNIRV9UVEwsXG5cdFx0bm90aWZ5T25Mb2FkOiB0cnVlLFxuXHRcdG5vdGlmeU9uTWF0Y2g6IHRydWUsXG5cdH07XG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBsb2FkQ29uZmlnKHByb2plY3REaXI6IHN0cmluZyk6IFByb21pc2U8UnVsZXNDb25maWc+IHtcblx0Y29uc3QgY29uZmlnRmlsZXMgPSBbXG5cdFx0XCIucnVsZXMtY29uZmlnLmpzb25cIixcblx0XHRcIi5waS9ydWxlcy1jb25maWcuanNvblwiLFxuXHRcdFwiLmNsYXVkZS9ydWxlcy1jb25maWcuanNvblwiLFxuXHRcdFwiLm9wZW5jb2RlL3J1bGVzLWNvbmZpZy5qc29uXCIsXG5cdF07XG5cblx0Zm9yIChjb25zdCBuYW1lIG9mIGNvbmZpZ0ZpbGVzKSB7XG5cdFx0Y29uc3QgZnAgPSBwYXRoLnJlc29sdmUocHJvamVjdERpciwgbmFtZSk7XG5cdFx0aWYgKCFmcy5leGlzdHNTeW5jKGZwKSkgY29udGludWU7XG5cdFx0dHJ5IHtcblx0XHRcdGNvbnN0IHJhdyA9IGZzLnJlYWRGaWxlU3luYyhmcCwgXCJ1dGYtOFwiKTtcblx0XHRcdGNvbnN0IHBhcnNlZCA9IEpTT04ucGFyc2UocmF3KTtcblx0XHRcdHJldHVybiB7XG5cdFx0XHRcdC4uLmRlZmF1bHRDb25maWcoKSxcblx0XHRcdFx0Li4ucGFyc2VkLFxuXHRcdFx0XHRjYWNoZVRUTDogcGFyc2VkLmNhY2hlVFRMID8/IERFRkFVTFRfQ0FDSEVfVFRMLFxuXHRcdFx0XHRub3RpZnlPbkxvYWQ6IHBhcnNlZC5ub3RpZnlPbkxvYWQgPz8gdHJ1ZSxcblx0XHRcdFx0bm90aWZ5T25NYXRjaDogcGFyc2VkLm5vdGlmeU9uTWF0Y2ggPz8gdHJ1ZSxcblx0XHRcdH07XG5cdFx0fSBjYXRjaCAoZXJyKSB7XG5cdFx0XHRjb25zb2xlLmRlYnVnKFwiW3J1bGVzLWVuZ2luZV0gY29uZmlnIHBhcnNlIGZhaWxlZDpcIiwgZXJyIGluc3RhbmNlb2YgRXJyb3IgPyBlcnIubWVzc2FnZSA6IGVycik7XG5cdFx0fVxuXHR9XG5cblx0cmV0dXJuIGRlZmF1bHRDb25maWcoKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHJlc29sdmVEaXJzKFxuXHRwcm9qZWN0RGlyOiBzdHJpbmcsXG5cdGNvbmZpZzogUnVsZXNDb25maWcsXG4pOiBBcnJheTx7IHNjb3BlOiBzdHJpbmc7IGRpcjogc3RyaW5nOyBzb3VyY2U6IHN0cmluZyB9PiB7XG5cdGNvbnN0IHNvdXJjZXMgPSBjb25maWcuZGlycyB8fCBERUZBVUxUX0RJUlM7XG5cdGNvbnN0IHJlc3VsdDogQXJyYXk8eyBzY29wZTogc3RyaW5nOyBkaXI6IHN0cmluZzsgc291cmNlOiBzdHJpbmcgfT4gPSBbXTtcblxuXHRmb3IgKGNvbnN0IFtzY29wZSwgcGF0aHNdIG9mIE9iamVjdC5lbnRyaWVzKHNvdXJjZXMpKSB7XG5cdFx0Zm9yIChjb25zdCBwIG9mIHBhdGhzKSB7XG5cdFx0XHRyZXN1bHQucHVzaCh7XG5cdFx0XHRcdHNjb3BlLFxuXHRcdFx0XHRkaXI6IHAuc3RhcnRzV2l0aChcIi9cIikgfHwgcC5zdGFydHNXaXRoKFwiflwiKSA/IHAucmVwbGFjZSgvXn4vLCBob21lZGlyKCkpIDogcGF0aC5yZXNvbHZlKHByb2plY3REaXIsIHApLFxuXHRcdFx0XHRzb3VyY2U6IHAsXG5cdFx0XHR9KTtcblx0XHR9XG5cdH1cblxuXHRyZXR1cm4gcmVzdWx0O1xufVxuIiwgImltcG9ydCAqIGFzIGZzIGZyb20gXCJub2RlOmZzXCI7XG5pbXBvcnQgKiBhcyBwYXRoIGZyb20gXCJub2RlOnBhdGhcIjtcbmltcG9ydCB0eXBlIHsgUGFyc2VkUnVsZSwgUnVsZUNhY2hlLCBSdWxlRnJvbnRtYXR0ZXIgfSBmcm9tIFwiLi90eXBlcy5qc1wiO1xuXG5mdW5jdGlvbiBzcGxpdENvbW1hKHZhbDogc3RyaW5nKTogc3RyaW5nW10ge1xuXHRjb25zdCByZXN1bHQ6IHN0cmluZ1tdID0gW107XG5cdGxldCBkZXB0aCA9IDA7XG5cdGxldCBjdXJyZW50ID0gXCJcIjtcblx0Zm9yIChjb25zdCBjaCBvZiB2YWwpIHtcblx0XHRpZiAoY2ggPT09IFwie1wiIHx8IGNoID09PSBcIihcIiB8fCBjaCA9PT0gXCJbXCIpIGRlcHRoKys7XG5cdFx0ZWxzZSBpZiAoY2ggPT09IFwifVwiIHx8IGNoID09PSBcIilcIiB8fCBjaCA9PT0gXCJdXCIpIGRlcHRoLS07XG5cblx0XHRpZiAoY2ggPT09IFwiLFwiICYmIGRlcHRoID09PSAwKSB7XG5cdFx0XHRjb25zdCB0cmltbWVkID0gY3VycmVudC50cmltKCkucmVwbGFjZSgvXltcIiddfFtcIiddJC9nLCBcIlwiKTtcblx0XHRcdGlmICh0cmltbWVkKSByZXN1bHQucHVzaCh0cmltbWVkKTtcblx0XHRcdGN1cnJlbnQgPSBcIlwiO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHRjdXJyZW50ICs9IGNoO1xuXHRcdH1cblx0fVxuXHRjb25zdCB0cmltbWVkID0gY3VycmVudC50cmltKCkucmVwbGFjZSgvXltcIiddfFtcIiddJC9nLCBcIlwiKTtcblx0aWYgKHRyaW1tZWQpIHJlc3VsdC5wdXNoKHRyaW1tZWQpO1xuXHRyZXR1cm4gcmVzdWx0O1xufVxuXG5leHBvcnQgZnVuY3Rpb24gcGFyc2VGcm9udG1hdHRlcihjb250ZW50OiBzdHJpbmcpOiB7IGRhdGE6IFJlY29yZDxzdHJpbmcsIHVua25vd24+OyBib2R5OiBzdHJpbmcgfSB7XG5cdGNvbnN0IGZyb250bWF0dGVyUmVnZXggPSAvXi0tLVxccj9cXG4oW1xcc1xcU10qPylcXG4/LS0tXFxyP1xcbihbXFxzXFxTXSopJC87XG5cdGNvbnN0IG1hdGNoID0gY29udGVudC5tYXRjaChmcm9udG1hdHRlclJlZ2V4KTtcblxuXHRpZiAoIW1hdGNoKSB7XG5cdFx0cmV0dXJuIHsgZGF0YToge30sIGJvZHk6IGNvbnRlbnQgfTtcblx0fVxuXG5cdGNvbnN0IFssIGZyb250bWF0dGVyU3RyLCBib2R5XSA9IG1hdGNoO1xuXHRjb25zdCBkYXRhOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiA9IHt9O1xuXG5cdGNvbnN0IGxpbmVzID0gZnJvbnRtYXR0ZXJTdHIuc3BsaXQoXCJcXG5cIik7XG5cdGxldCBpID0gMDtcblx0d2hpbGUgKGkgPCBsaW5lcy5sZW5ndGgpIHtcblx0XHRjb25zdCBsaW5lID0gbGluZXNbaV07XG5cdFx0Y29uc3QgY29sb25JbmRleCA9IGxpbmUuaW5kZXhPZihcIjpcIik7XG5cdFx0aWYgKGNvbG9uSW5kZXggPT09IC0xKSB7XG5cdFx0XHRpKys7XG5cdFx0XHRjb250aW51ZTtcblx0XHR9XG5cblx0XHRjb25zdCByYXdLZXkgPSBsaW5lLnNsaWNlKDAsIGNvbG9uSW5kZXgpLnRyaW0oKTtcblx0XHRsZXQgdmFsdWU6IHN0cmluZyB8IHN0cmluZ1tdIHwgbnVsbCA9IGxpbmUuc2xpY2UoY29sb25JbmRleCArIDEpLnRyaW0oKTtcblxuXHRcdGlmICh2YWx1ZSA9PT0gXCJcIiB8fCB2YWx1ZSA9PT0gXCJudWxsXCIgfHwgdmFsdWUgPT09IFwidW5kZWZpbmVkXCIpIHtcblx0XHRcdGNvbnN0IGxpc3RJdGVtczogc3RyaW5nW10gPSBbXTtcblx0XHRcdGxldCBqID0gaSArIDE7XG5cdFx0XHR3aGlsZSAoaiA8IGxpbmVzLmxlbmd0aCkge1xuXHRcdFx0XHRjb25zdCBzdWJMaW5lID0gbGluZXNbal07XG5cdFx0XHRcdGlmIChzdWJMaW5lLm1hdGNoKC9eXFxzKi1cXHMrLykpIHtcblx0XHRcdFx0XHRsaXN0SXRlbXMucHVzaChcblx0XHRcdFx0XHRcdHN1YkxpbmVcblx0XHRcdFx0XHRcdFx0LnJlcGxhY2UoL15cXHMqLVxccysvLCBcIlwiKVxuXHRcdFx0XHRcdFx0XHQudHJpbSgpXG5cdFx0XHRcdFx0XHRcdC5yZXBsYWNlKC9eW1wiJ118W1wiJ10kL2csIFwiXCIpLFxuXHRcdFx0XHRcdCk7XG5cdFx0XHRcdFx0aisrO1xuXHRcdFx0XHR9IGVsc2UgaWYgKHN1YkxpbmUudHJpbSgpID09PSBcIlwiIHx8IHN1YkxpbmUubWF0Y2goL15cXHMrLykpIHtcblx0XHRcdFx0XHRqKys7XG5cdFx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHRcdGlmIChsaXN0SXRlbXMubGVuZ3RoID4gMCkge1xuXHRcdFx0XHRjb25zdCBjYW1lbEtleSA9IHJhd0tleS5yZXBsYWNlKC8tKFthLXpdKS9nLCAoXywgbGV0dGVyKSA9PiBsZXR0ZXIudG9VcHBlckNhc2UoKSk7XG5cdFx0XHRcdGRhdGFbY2FtZWxLZXldID0gbGlzdEl0ZW1zO1xuXHRcdFx0XHRpID0gajtcblx0XHRcdFx0Y29udGludWU7XG5cdFx0XHR9XG5cdFx0XHR2YWx1ZSA9IG51bGw7XG5cdFx0fSBlbHNlIGlmICh2YWx1ZS5zdGFydHNXaXRoKFwiW1wiKSAmJiB2YWx1ZS5lbmRzV2l0aChcIl1cIikpIHtcblx0XHRcdHRyeSB7XG5cdFx0XHRcdHZhbHVlID0gSlNPTi5wYXJzZSh2YWx1ZS5yZXBsYWNlKC8nL2csICdcIicpKTtcblx0XHRcdH0gY2F0Y2ggKGVycikge1xuXHRcdFx0XHRjb25zb2xlLmRlYnVnKFwiW3J1bGVzLWVuZ2luZV0gSlNPTiBhcnJheSBwYXJzZSBmYWlsZWQ6XCIsIGVyciBpbnN0YW5jZW9mIEVycm9yID8gZXJyLm1lc3NhZ2UgOiBlcnIpO1xuXHRcdFx0XHR2YWx1ZSA9ICh2YWx1ZSBhcyBzdHJpbmcpXG5cdFx0XHRcdFx0LnNsaWNlKDEsIC0xKVxuXHRcdFx0XHRcdC5zcGxpdChcIixcIilcblx0XHRcdFx0XHQubWFwKCh2OiBzdHJpbmcpID0+IHYudHJpbSgpLnJlcGxhY2UoL15bXCInXXxbXCInXSQvZywgXCJcIikpO1xuXHRcdFx0fVxuXHRcdH0gZWxzZSBpZiAodmFsdWUuc3RhcnRzV2l0aCgnXCInKSAmJiB2YWx1ZS5lbmRzV2l0aCgnXCInKSkge1xuXHRcdFx0dmFsdWUgPSB2YWx1ZS5zbGljZSgxLCAtMSk7XG5cdFx0fSBlbHNlIGlmICh2YWx1ZS5zdGFydHNXaXRoKFwiJ1wiKSAmJiB2YWx1ZS5lbmRzV2l0aChcIidcIikpIHtcblx0XHRcdHZhbHVlID0gdmFsdWUuc2xpY2UoMSwgLTEpO1xuXHRcdH1cblxuXHRcdGNvbnN0IGNhbWVsS2V5ID0gcmF3S2V5LnJlcGxhY2UoLy0oW2Etel0pL2csIChfLCBsZXR0ZXIpID0+IGxldHRlci50b1VwcGVyQ2FzZSgpKTtcblxuXHRcdGlmICgoY2FtZWxLZXkgPT09IFwicGF0aHNcIiB8fCBjYW1lbEtleSA9PT0gXCJnbG9ic1wiKSAmJiB0eXBlb2YgdmFsdWUgPT09IFwic3RyaW5nXCIpIHtcblx0XHRcdGRhdGFbY2FtZWxLZXldID0gc3BsaXRDb21tYSh2YWx1ZSk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdGRhdGFbY2FtZWxLZXldID0gdmFsdWU7XG5cdFx0fVxuXHRcdGkrKztcblx0fVxuXG5cdHJldHVybiB7IGRhdGEsIGJvZHk6IGJvZHkudHJpbSgpIH07XG59XG5cbmZ1bmN0aW9uIGV4dHJhY3RUaXRsZShib2R5OiBzdHJpbmcpOiBzdHJpbmcge1xuXHRmb3IgKGNvbnN0IGxpbmUgb2YgYm9keS5zcGxpdChcIlxcblwiKSkge1xuXHRcdGNvbnN0IHRyaW1tZWQgPSBsaW5lLnRyaW0oKTtcblx0XHRpZiAodHJpbW1lZCkge1xuXHRcdFx0cmV0dXJuIHRyaW1tZWQucmVwbGFjZSgvXiMrXFxzKi8sIFwiXCIpLnJlcGxhY2UoL1xcKlxcKi9nLCBcIlwiKTtcblx0XHR9XG5cdH1cblx0cmV0dXJuIFwiVW50aXRsZWQgUnVsZVwiO1xufVxuXG5mdW5jdGlvbiBwYXJzZVBhdGhzKHJhdzogdW5rbm93bik6IHN0cmluZ1tdIHtcblx0aWYgKCFyYXcpIHJldHVybiBbXTtcblx0aWYgKHR5cGVvZiByYXcgPT09IFwic3RyaW5nXCIpIHtcblx0XHRyZXR1cm4gcmF3XG5cdFx0XHQuc3BsaXQoXCIsXCIpXG5cdFx0XHQubWFwKChnKSA9PiBnLnRyaW0oKSlcblx0XHRcdC5maWx0ZXIoQm9vbGVhbik7XG5cdH1cblx0aWYgKEFycmF5LmlzQXJyYXkocmF3KSkgcmV0dXJuIHJhdyBhcyBzdHJpbmdbXTtcblx0cmV0dXJuIFtdO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gcGFyc2VSdWxlRmlsZShmaWxlUGF0aDogc3RyaW5nLCBjb250ZW50OiBzdHJpbmcpOiBQYXJzZWRSdWxlIHtcblx0Y29uc3QgeyBkYXRhLCBib2R5IH0gPSBwYXJzZUZyb250bWF0dGVyKGNvbnRlbnQpO1xuXHRjb25zdCByYXdHbG9icyA9IGRhdGEuZ2xvYnMgPz8gZGF0YS5wYXRocztcblx0Y29uc3QgZ2xvYnMgPSBwYXJzZVBhdGhzKHJhd0dsb2JzKTtcblx0Y29uc3QgcmF3UGF0aHMgPSBkYXRhLnBhdGhzO1xuXHRjb25zdCBwYXRocyA9IHBhcnNlUGF0aHMocmF3UGF0aHMpO1xuXHRjb25zdCBpc1VuY29uZGl0aW9uYWwgPSBnbG9icy5sZW5ndGggPT09IDAgfHwgKGdsb2JzLmxlbmd0aCA9PT0gMSAmJiBnbG9ic1swXSA9PT0gXCIqKlwiKTtcblxuXHRjb25zdCBmcm9udG1hdHRlcjogUnVsZUZyb250bWF0dGVyID0ge307XG5cdGlmIChyYXdHbG9icykge1xuXHRcdGZyb250bWF0dGVyLmdsb2JzID0gZ2xvYnM7XG5cdFx0aWYgKHJhd1BhdGhzKSB7XG5cdFx0XHRmcm9udG1hdHRlci5wYXRocyA9IHBhdGhzO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHRmcm9udG1hdHRlci5wYXRocyA9IGdsb2JzO1xuXHRcdH1cblx0fVxuXHRpZiAoZGF0YS5kZXNjcmlwdGlvbiAmJiB0eXBlb2YgZGF0YS5kZXNjcmlwdGlvbiA9PT0gXCJzdHJpbmdcIikgZnJvbnRtYXR0ZXIuZGVzY3JpcHRpb24gPSBkYXRhLmRlc2NyaXB0aW9uO1xuXHRpZiAoZGF0YS5zZXZlcml0eSAmJiB0eXBlb2YgZGF0YS5zZXZlcml0eSA9PT0gXCJzdHJpbmdcIilcblx0XHRmcm9udG1hdHRlci5zZXZlcml0eSA9IGRhdGEuc2V2ZXJpdHkgYXMgUGFyc2VkUnVsZVtcImZyb250bWF0dGVyXCJdW1wic2V2ZXJpdHlcIl07XG5cdGlmIChkYXRhLmFsbG93ZWRUb29scylcblx0XHRmcm9udG1hdHRlci5hbGxvd2VkVG9vbHMgPVxuXHRcdFx0dHlwZW9mIGRhdGEuYWxsb3dlZFRvb2xzID09PSBcInN0cmluZ1wiID8gW2RhdGEuYWxsb3dlZFRvb2xzXSA6IChkYXRhLmFsbG93ZWRUb29scyBhcyBzdHJpbmdbXSk7XG5cdGlmIChkYXRhLndoZW5Ub1VzZSAmJiB0eXBlb2YgZGF0YS53aGVuVG9Vc2UgPT09IFwic3RyaW5nXCIpIGZyb250bWF0dGVyLndoZW5Ub1VzZSA9IGRhdGEud2hlblRvVXNlO1xuXHRpZiAoZGF0YS5ub3RpZnlPbk1hdGNoICE9PSB1bmRlZmluZWQpXG5cdFx0ZnJvbnRtYXR0ZXIubm90aWZ5T25NYXRjaCA9IGRhdGEubm90aWZ5T25NYXRjaCA9PT0gXCJ0cnVlXCIgfHwgZGF0YS5ub3RpZnlPbk1hdGNoID09PSB0cnVlO1xuXHRpZiAoZGF0YS5za2lwSW5Qcm9tcHQgIT09IHVuZGVmaW5lZClcblx0XHRmcm9udG1hdHRlci5za2lwSW5Qcm9tcHQgPSBkYXRhLnNraXBJblByb21wdCA9PT0gXCJ0cnVlXCIgfHwgZGF0YS5za2lwSW5Qcm9tcHQgPT09IHRydWU7XG5cblx0cmV0dXJuIHtcblx0XHRuYW1lOiBwYXRoLmJhc2VuYW1lKGZpbGVQYXRoLCBwYXRoLmV4dG5hbWUoZmlsZVBhdGgpKSxcblx0XHRmaWxlUGF0aCxcblx0XHR0aXRsZTogZXh0cmFjdFRpdGxlKGJvZHkpLFxuXHRcdGNvbnRlbnQ6IGJvZHkudHJpbSgpLFxuXHRcdHNjb3BlOiBcInByb2plY3RcIixcblx0XHRzb3VyY2U6IFwiXCIsXG5cdFx0ZnJvbnRtYXR0ZXIsXG5cdFx0aXNVbmNvbmRpdGlvbmFsLFxuXHR9O1xufVxuXG5mdW5jdGlvbiBzY2FuRGlyKGRpcjogc3RyaW5nLCBmaWxlczogc3RyaW5nW10gPSBbXSk6IHN0cmluZ1tdIHtcblx0aWYgKCFmcy5leGlzdHNTeW5jKGRpcikpIHJldHVybiBmaWxlcztcblx0Y29uc3QgZW50cmllcyA9IGZzLnJlYWRkaXJTeW5jKGRpciwgeyB3aXRoRmlsZVR5cGVzOiB0cnVlIH0pO1xuXHRmb3IgKGNvbnN0IGVudHJ5IG9mIGVudHJpZXMpIHtcblx0XHRjb25zdCBmdWxsUGF0aCA9IHBhdGguam9pbihkaXIsIGVudHJ5Lm5hbWUpO1xuXHRcdGlmIChlbnRyeS5pc0RpcmVjdG9yeSgpKSB7XG5cdFx0XHRzY2FuRGlyKGZ1bGxQYXRoLCBmaWxlcyk7XG5cdFx0fSBlbHNlIGlmIChlbnRyeS5pc0ZpbGUoKSAmJiAoZW50cnkubmFtZS5lbmRzV2l0aChcIi5tZFwiKSB8fCBlbnRyeS5uYW1lLmVuZHNXaXRoKFwiLm1kY1wiKSkpIHtcblx0XHRcdGZpbGVzLnB1c2goZnVsbFBhdGgpO1xuXHRcdH1cblx0fVxuXHRyZXR1cm4gZmlsZXM7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBsb2FkUnVsZXMocnVsZXNEaXI6IHN0cmluZyk6IFJ1bGVDYWNoZSB7XG5cdGNvbnN0IHJ1bGVzOiBQYXJzZWRSdWxlW10gPSBbXTtcblx0Y29uc3QgdW5jb25kaXRpb25hbDogUGFyc2VkUnVsZVtdID0gW107XG5cdGNvbnN0IGNvbmRpdGlvbmFsOiBQYXJzZWRSdWxlW10gPSBbXTtcblxuXHRpZiAoIWZzLmV4aXN0c1N5bmMocnVsZXNEaXIpKSB7XG5cdFx0cmV0dXJuIHsgcnVsZXMsIHVuY29uZGl0aW9uYWwsIGNvbmRpdGlvbmFsLCBsb2FkZWRBdDogRGF0ZS5ub3coKSB9O1xuXHR9XG5cblx0Y29uc3QgZmlsZXMgPSBzY2FuRGlyKHJ1bGVzRGlyKTtcblxuXHRmb3IgKGNvbnN0IGZpbGVQYXRoIG9mIGZpbGVzKSB7XG5cdFx0Y29uc3QgY29udGVudCA9IGZzLnJlYWRGaWxlU3luYyhmaWxlUGF0aCwgXCJ1dGYtOFwiKTtcblx0XHRjb25zdCBydWxlID0gcGFyc2VSdWxlRmlsZShmaWxlUGF0aCwgY29udGVudCk7XG5cdFx0cnVsZXMucHVzaChydWxlKTtcblx0XHRpZiAocnVsZS5pc1VuY29uZGl0aW9uYWwpIHtcblx0XHRcdHVuY29uZGl0aW9uYWwucHVzaChydWxlKTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0Y29uZGl0aW9uYWwucHVzaChydWxlKTtcblx0XHR9XG5cdH1cblxuXHRyZXR1cm4geyBydWxlcywgdW5jb25kaXRpb25hbCwgY29uZGl0aW9uYWwsIGxvYWRlZEF0OiBEYXRlLm5vdygpIH07XG59XG4iLCAiaW1wb3J0IHsgcmVzb2x2ZURpcnMgfSBmcm9tIFwiLi9jb25maWcuanNcIjtcbmltcG9ydCB7IGxvYWRSdWxlcyB9IGZyb20gXCIuL2xvYWRlci5qc1wiO1xuaW1wb3J0IHR5cGUgeyBQYXJzZWRSdWxlLCBSdWxlc0NvbmZpZyB9IGZyb20gXCIuL3R5cGVzLmpzXCI7XG5cbmxldCBjYWNoZTogeyBydWxlczogUGFyc2VkUnVsZVtdOyBsb2FkZWRBdDogbnVtYmVyIH0gfCBudWxsID0gbnVsbDtcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGdldFJ1bGVzKHByb2plY3REaXI6IHN0cmluZywgY29uZmlnOiBSdWxlc0NvbmZpZyk6IFByb21pc2U8UGFyc2VkUnVsZVtdPiB7XG5cdGlmIChjYWNoZSAmJiBEYXRlLm5vdygpIC0gY2FjaGUubG9hZGVkQXQgPCBjb25maWcuY2FjaGVUVEwpIHtcblx0XHRyZXR1cm4gY2FjaGUucnVsZXM7XG5cdH1cblxuXHRjb25zdCBydWxlcyA9IGF3YWl0IGxvYWRBbGxSdWxlcyhwcm9qZWN0RGlyLCBjb25maWcpO1xuXHRjYWNoZSA9IHsgcnVsZXMsIGxvYWRlZEF0OiBEYXRlLm5vdygpIH07XG5cdHJldHVybiBydWxlcztcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGludmFsaWRhdGVDYWNoZSgpOiB2b2lkIHtcblx0Y2FjaGUgPSBudWxsO1xufVxuXG5hc3luYyBmdW5jdGlvbiBsb2FkQWxsUnVsZXMocHJvamVjdERpcjogc3RyaW5nLCBjb25maWc6IFJ1bGVzQ29uZmlnKTogUHJvbWlzZTxQYXJzZWRSdWxlW10+IHtcblx0Y29uc3Qgc291cmNlcyA9IHJlc29sdmVEaXJzKHByb2plY3REaXIsIGNvbmZpZyk7XG5cdGNvbnN0IHJlc3VsdDogUGFyc2VkUnVsZVtdID0gW107XG5cdGNvbnN0IHNlZW4gPSBuZXcgU2V0PHN0cmluZz4oKTtcblxuXHRmb3IgKGNvbnN0IHsgc2NvcGUsIGRpciwgc291cmNlIH0gb2Ygc291cmNlcykge1xuXHRcdGNvbnN0IHJ1bGVDYWNoZSA9IGxvYWRSdWxlcyhkaXIpO1xuXHRcdGZvciAoY29uc3QgcnVsZSBvZiBydWxlQ2FjaGUucnVsZXMpIHtcblx0XHRcdGlmIChzZWVuLmhhcyhydWxlLmZpbGVQYXRoKSkgY29udGludWU7XG5cdFx0XHRzZWVuLmFkZChydWxlLmZpbGVQYXRoKTtcblx0XHRcdHJ1bGUuc2NvcGUgPSBzY29wZSBhcyBQYXJzZWRSdWxlW1wic2NvcGVcIl07XG5cdFx0XHRydWxlLnNvdXJjZSA9IHNvdXJjZTtcblx0XHRcdHJlc3VsdC5wdXNoKHJ1bGUpO1xuXHRcdH1cblx0fVxuXG5cdHJldHVybiByZXN1bHQ7XG59XG4iXSwKICAibWFwcGluZ3MiOiAiO0FBQ0EsU0FBUyxlQUFlO0FBQ3hCLFlBQVksVUFBVTtBQUt0QixJQUFNLGVBQWU7QUFBQSxFQUNwQixTQUFTLENBQUMsZ0NBQWdDO0FBQUEsRUFDMUMsTUFBTSxDQUFNLFVBQUssUUFBUSxHQUFHLFdBQVcsT0FBTyxHQUFRLFVBQUssUUFBUSxHQUFHLFdBQVcsWUFBWSxPQUFPLENBQUM7QUFBQSxFQUNyRyxJQUFJLENBQUMsV0FBVztBQUFBLEVBQ2hCLFNBQVMsQ0FBQyxpQkFBaUIsbUJBQW1CLGFBQWE7QUFDNUQ7QUF1Q08sU0FBUyxZQUNmLFlBQ0EsUUFDd0Q7QUFDeEQsUUFBTSxVQUFVLE9BQU8sUUFBUTtBQUMvQixRQUFNLFNBQWdFLENBQUM7QUFFdkUsYUFBVyxDQUFDLE9BQU8sS0FBSyxLQUFLLE9BQU8sUUFBUSxPQUFPLEdBQUc7QUFDckQsZUFBVyxLQUFLLE9BQU87QUFDdEIsYUFBTyxLQUFLO0FBQUEsUUFDWDtBQUFBLFFBQ0EsS0FBSyxFQUFFLFdBQVcsR0FBRyxLQUFLLEVBQUUsV0FBVyxHQUFHLElBQUksRUFBRSxRQUFRLE1BQU0sUUFBUSxDQUFDLElBQVMsYUFBUSxZQUFZLENBQUM7QUFBQSxRQUNyRyxRQUFRO0FBQUEsTUFDVCxDQUFDO0FBQUEsSUFDRjtBQUFBLEVBQ0Q7QUFFQSxTQUFPO0FBQ1I7OztBQ3JFQSxZQUFZLFFBQVE7QUFDcEIsWUFBWUEsV0FBVTtBQUd0QixTQUFTLFdBQVcsS0FBdUI7QUFDMUMsUUFBTSxTQUFtQixDQUFDO0FBQzFCLE1BQUksUUFBUTtBQUNaLE1BQUksVUFBVTtBQUNkLGFBQVcsTUFB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QUFHO0FBQ3hCLGNBQVEsVUFBVSxLQUFLO0FBQUEsSUFDeEIsV0FBVyxNQUFNLE9BQU8sTUFBTSxNQUFNLEtBQUssU0FBUyxLQUFLLEtBQUssTUFBTSxLQUFLLFNBQVMsTUFBTSxJQUFJO0FBQ3pGLFlBQU0sS0FBSyxRQUFRO0FBQUEsSUFDcEI7QUFBQSxFQUNEO0FBQ0EsU0FBTztBQUNSO0FBRU8sU0FBUyxVQUFVLFVBQTZCO0FBQ3RELFFBQU0sUUFBc0IsQ0FBQztBQUM3QixRQUFNLGdCQUE4QixDQUFDO0FBQ3JDLFFBQU0sY0FBNEIsQ0FBQztBQUVuQyxNQUFJLENBQUksY0FBVyxRQUFRLEdBQUc7QUFDN0IsV0FBTyxFQUFFLE9BQU8sZUFBZSxhQUFhLFVBQVUsS0FBSyxJQUFJLEVBQUU7QUFBQSxFQUNsRTtBQUVBLFFBQU0sUUFBUSxRQUFRLFFBQVE7QUFFOUIsYUFBVyxZQUFZLE9BQU87QUFDN0IsVUFBTSxVQUFhLGdCQUFhLFVBQVUsT0FBTztBQUNqRCxVQUFNLE9BQU8sY0FBYyxVQUFVLE9BQU87QUFDNUMsVUFBTSxLQUFLLElBQUk7QUFDZixRQUFJLEtBQUssaUJBQWlCO0FBQ3pCLG9CQUFjLEtBQUssSUFBSTtBQUFBLElBQ3hCLE9BQU87QUFDTixrQkFBWSxLQUFLLElBQUk7QUFBQSxJQUN0QjtBQUFBLEVBQ0Q7QUFFQSxTQUFPLEVBQUUsT0FBTyxlQUFlLGFBQWEsVUFBVSxLQUFLLElBQUksRUFBRTtBQUNsRTs7O0FDeE1BLElBQUksUUFBMEQ7QUFFOUQsZUFBc0IsU0FBUyxZQUFvQixRQUE0QztBQUM5RixNQUFJLFNBQVMsS0FBSyxJQUFJLElBQUksTUFBTSxXQUFXLE9BQU8sVUFBVTtBQUMzRCxXQUFPLE1BQU07QUFBQSxFQUNkO0FBRUEsUUFBTSxRQUFRLE1BQU0sYUFBYSxZQUFZLE1BQU07QUFDbkQsVUFBUSxFQUFFLE9BQU8sVUFBVSxLQUFLLElBQUksRUFBRTtBQUN0QyxTQUFPO0FBQ1I7QUFFTyxTQUFTLGtCQUF3QjtBQUN2QyxVQUFRO0FBQ1Q7QUFFQSxlQUFlLGFBQWEsWUFBb0IsUUFBNEM7QUFDM0YsUUFBTSxVQUFVLFlBQVksWUFBWSxNQUFNO0FBQzlDLFFBQU0sU0FBdUIsQ0FBQztBQUM5QixRQUFNLE9BQU8sb0JBQUksSUFBWTtBQUU3QixhQUFXLEVBQUUsT0FBTyxLQUFLLE9BQU8sS0FBSyxTQUFTO0FBQzdDLFVBQU0sWUFBWSxVQUFVLEdBQUc7QUFDL0IsZUFBVyxRQUFRLFVBQVUsT0FBTztBQUNuQyxVQUFJLEtBQUssSUFBSSxLQUFLLFFBQVEsRUFBRztBQUM3QixXQUFLLElBQUksS0FBSyxRQUFRO0FBQ3RCLFdBQUssUUFBUTtBQUNiLFdBQUssU0FBUztBQUNkLGFBQU8sS0FBSyxJQUFJO0FBQUEsSUFDakI7QUFBQSxFQUNEO0FBRUEsU0FBTztBQUNSOyIsCiAgIm5hbWVzIjogWyJwYXRoIiwgInRyaW1tZWQiLCAiY2FtZWxLZXkiXQp9Cg==
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { resolveDirs } from "./config.js";
|
|
2
|
+
import { loadRules } from "./loader.js";
|
|
3
|
+
import type { ParsedRule, RulesConfig } from "./types.js";
|
|
4
|
+
|
|
5
|
+
let cache: { rules: ParsedRule[]; loadedAt: number } | null = null;
|
|
6
|
+
|
|
7
|
+
export async function getRules(projectDir: string, config: RulesConfig): Promise<ParsedRule[]> {
|
|
8
|
+
if (cache && Date.now() - cache.loadedAt < config.cacheTTL) {
|
|
9
|
+
return cache.rules;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const rules = await loadAllRules(projectDir, config);
|
|
13
|
+
cache = { rules, loadedAt: Date.now() };
|
|
14
|
+
return rules;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function invalidateCache(): void {
|
|
18
|
+
cache = null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function loadAllRules(projectDir: string, config: RulesConfig): Promise<ParsedRule[]> {
|
|
22
|
+
const sources = resolveDirs(projectDir, config);
|
|
23
|
+
const result: ParsedRule[] = [];
|
|
24
|
+
const seen = new Set<string>();
|
|
25
|
+
|
|
26
|
+
for (const { scope, dir, source } of sources) {
|
|
27
|
+
const ruleCache = loadRules(dir);
|
|
28
|
+
for (const rule of ruleCache.rules) {
|
|
29
|
+
if (seen.has(rule.filePath)) continue;
|
|
30
|
+
seen.add(rule.filePath);
|
|
31
|
+
rule.scope = scope as ParsedRule["scope"];
|
|
32
|
+
rule.source = source;
|
|
33
|
+
result.push(rule);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return result;
|
|
38
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// packages/coding-agent/extensions/rules-engine/config.ts
|
|
2
|
+
import * as fs from "node:fs";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import * as path from "node:path";
|
|
5
|
+
var DEFAULT_CACHE_TTL = 3e4;
|
|
6
|
+
var DEFAULT_DIRS = {
|
|
7
|
+
managed: ["/etc/claude-code/.claude/rules"],
|
|
8
|
+
user: [path.join(homedir(), ".claude", "rules"), path.join(homedir(), ".config", "opencode", "rules")],
|
|
9
|
+
pi: [".pi/rules"],
|
|
10
|
+
project: [".claude/rules", ".opencode/rules", ".trae/rules"]
|
|
11
|
+
};
|
|
12
|
+
function defaultConfig() {
|
|
13
|
+
return {
|
|
14
|
+
cacheTTL: DEFAULT_CACHE_TTL,
|
|
15
|
+
notifyOnLoad: true,
|
|
16
|
+
notifyOnMatch: true
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
async function loadConfig(projectDir) {
|
|
20
|
+
const configFiles = [
|
|
21
|
+
".rules-config.json",
|
|
22
|
+
".pi/rules-config.json",
|
|
23
|
+
".claude/rules-config.json",
|
|
24
|
+
".opencode/rules-config.json"
|
|
25
|
+
];
|
|
26
|
+
for (const name of configFiles) {
|
|
27
|
+
const fp = path.resolve(projectDir, name);
|
|
28
|
+
if (!fs.existsSync(fp)) continue;
|
|
29
|
+
try {
|
|
30
|
+
const raw = fs.readFileSync(fp, "utf-8");
|
|
31
|
+
const parsed = JSON.parse(raw);
|
|
32
|
+
return {
|
|
33
|
+
...defaultConfig(),
|
|
34
|
+
...parsed,
|
|
35
|
+
cacheTTL: parsed.cacheTTL ?? DEFAULT_CACHE_TTL,
|
|
36
|
+
notifyOnLoad: parsed.notifyOnLoad ?? true,
|
|
37
|
+
notifyOnMatch: parsed.notifyOnMatch ?? true
|
|
38
|
+
};
|
|
39
|
+
} catch (err) {
|
|
40
|
+
console.debug("[rules-engine] config parse failed:", err instanceof Error ? err.message : err);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return defaultConfig();
|
|
44
|
+
}
|
|
45
|
+
function resolveDirs(projectDir, config) {
|
|
46
|
+
const sources = config.dirs || DEFAULT_DIRS;
|
|
47
|
+
const result = [];
|
|
48
|
+
for (const [scope, paths] of Object.entries(sources)) {
|
|
49
|
+
for (const p of paths) {
|
|
50
|
+
result.push({
|
|
51
|
+
scope,
|
|
52
|
+
dir: p.startsWith("/") || p.startsWith("~") ? p.replace(/^~/, homedir()) : path.resolve(projectDir, p),
|
|
53
|
+
source: p
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return result;
|
|
58
|
+
}
|
|
59
|
+
export {
|
|
60
|
+
loadConfig,
|
|
61
|
+
resolveDirs
|
|
62
|
+
};
|
|
63
|
+
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiY29uZmlnLnRzIl0sCiAgInNvdXJjZXNDb250ZW50IjogWyJpbXBvcnQgKiBhcyBmcyBmcm9tIFwibm9kZTpmc1wiO1xuaW1wb3J0IHsgaG9tZWRpciB9IGZyb20gXCJub2RlOm9zXCI7XG5pbXBvcnQgKiBhcyBwYXRoIGZyb20gXCJub2RlOnBhdGhcIjtcbmltcG9ydCB0eXBlIHsgUnVsZXNDb25maWcgfSBmcm9tIFwiLi90eXBlcy5qc1wiO1xuXG5jb25zdCBERUZBVUxUX0NBQ0hFX1RUTCA9IDMwXzAwMDtcblxuY29uc3QgREVGQVVMVF9ESVJTID0ge1xuXHRtYW5hZ2VkOiBbXCIvZXRjL2NsYXVkZS1jb2RlLy5jbGF1ZGUvcnVsZXNcIl0sXG5cdHVzZXI6IFtwYXRoLmpvaW4oaG9tZWRpcigpLCBcIi5jbGF1ZGVcIiwgXCJydWxlc1wiKSwgcGF0aC5qb2luKGhvbWVkaXIoKSwgXCIuY29uZmlnXCIsIFwib3BlbmNvZGVcIiwgXCJydWxlc1wiKV0sXG5cdHBpOiBbXCIucGkvcnVsZXNcIl0sXG5cdHByb2plY3Q6IFtcIi5jbGF1ZGUvcnVsZXNcIiwgXCIub3BlbmNvZGUvcnVsZXNcIiwgXCIudHJhZS9ydWxlc1wiXSxcbn07XG5cbmZ1bmN0aW9uIGRlZmF1bHRDb25maWcoKTogUnVsZXNDb25maWcge1xuXHRyZXR1cm4ge1xuXHRcdGNhY2hlVFRMOiBERUZBVUxUX0NBQ0hFX1RUTCxcblx0XHRub3RpZnlPbkxvYWQ6IHRydWUsXG5cdFx0bm90aWZ5T25NYXRjaDogdHJ1ZSxcblx0fTtcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGxvYWRDb25maWcocHJvamVjdERpcjogc3RyaW5nKTogUHJvbWlzZTxSdWxlc0NvbmZpZz4ge1xuXHRjb25zdCBjb25maWdGaWxlcyA9IFtcblx0XHRcIi5ydWxlcy1jb25maWcuanNvblwiLFxuXHRcdFwiLnBpL3J1bGVzLWNvbmZpZy5qc29uXCIsXG5cdFx0XCIuY2xhdWRlL3J1bGVzLWNvbmZpZy5qc29uXCIsXG5cdFx0XCIub3BlbmNvZGUvcnVsZXMtY29uZmlnLmpzb25cIixcblx0XTtcblxuXHRmb3IgKGNvbnN0IG5hbWUgb2YgY29uZmlnRmlsZXMpIHtcblx0XHRjb25zdCBmcCA9IHBhdGgucmVzb2x2ZShwcm9qZWN0RGlyLCBuYW1lKTtcblx0XHRpZiAoIWZzLmV4aXN0c1N5bmMoZnApKSBjb250aW51ZTtcblx0XHR0cnkge1xuXHRcdFx0Y29uc3QgcmF3ID0gZnMucmVhZEZpbGVTeW5jKGZwLCBcInV0Zi04XCIpO1xuXHRcdFx0Y29uc3QgcGFyc2VkID0gSlNPTi5wYXJzZShyYXcpO1xuXHRcdFx0cmV0dXJuIHtcblx0XHRcdFx0Li4uZGVmYXVsdENvbmZpZygpLFxuXHRcdFx0XHQuLi5wYXJzZWQsXG5cdFx0XHRcdGNhY2hlVFRMOiBwYXJzZWQuY2FjaGVUVEwgPz8gREVGQVVMVF9DQUNIRV9UVEwsXG5cdFx0XHRcdG5vdGlmeU9uTG9hZDogcGFyc2VkLm5vdGlmeU9uTG9hZCA/PyB0cnVlLFxuXHRcdFx0XHRub3RpZnlPbk1hdGNoOiBwYXJzZWQubm90aWZ5T25NYXRjaCA/PyB0cnVlLFxuXHRcdFx0fTtcblx0XHR9IGNhdGNoIChlcnIpIHtcblx0XHRcdGNvbnNvbGUuZGVidWcoXCJbcnVsZXMtZW5naW5lXSBjb25maWcgcGFyc2UgZmFpbGVkOlwiLCBlcnIgaW5zdGFuY2VvZiBFcnJvciA/IGVyci5tZXNzYWdlIDogZXJyKTtcblx0XHR9XG5cdH1cblxuXHRyZXR1cm4gZGVmYXVsdENvbmZpZygpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gcmVzb2x2ZURpcnMoXG5cdHByb2plY3REaXI6IHN0cmluZyxcblx0Y29uZmlnOiBSdWxlc0NvbmZpZyxcbik6IEFycmF5PHsgc2NvcGU6IHN0cmluZzsgZGlyOiBzdHJpbmc7IHNvdXJjZTogc3RyaW5nIH0+IHtcblx0Y29uc3Qgc291cmNlcyA9IGNvbmZpZy5kaXJzIHx8IERFRkFVTFRfRElSUztcblx0Y29uc3QgcmVzdWx0OiBBcnJheTx7IHNjb3BlOiBzdHJpbmc7IGRpcjogc3RyaW5nOyBzb3VyY2U6IHN0cmluZyB9PiA9IFtdO1xuXG5cdGZvciAoY29uc3QgW3Njb3BlLCBwYXRoc10gb2YgT2JqZWN0LmVudHJpZXMoc291cmNlcykpIHtcblx0XHRmb3IgKGNvbnN0IHAgb2YgcGF0aHMpIHtcblx0XHRcdHJlc3VsdC5wdXNoKHtcblx0XHRcdFx0c2NvcGUsXG5cdFx0XHRcdGRpcjogcC5zdGFydHNXaXRoKFwiL1wiKSB8fCBwLnN0YXJ0c1dpdGgoXCJ+XCIpID8gcC5yZXBsYWNlKC9efi8sIGhvbWVkaXIoKSkgOiBwYXRoLnJlc29sdmUocHJvamVjdERpciwgcCksXG5cdFx0XHRcdHNvdXJjZTogcCxcblx0XHRcdH0pO1xuXHRcdH1cblx0fVxuXG5cdHJldHVybiByZXN1bHQ7XG59XG4iXSwKICAibWFwcGluZ3MiOiAiO0FBQUEsWUFBWSxRQUFRO0FBQ3BCLFNBQVMsZUFBZTtBQUN4QixZQUFZLFVBQVU7QUFHdEIsSUFBTSxvQkFBb0I7QUFFMUIsSUFBTSxlQUFlO0FBQUEsRUFDcEIsU0FBUyxDQUFDLGdDQUFnQztBQUFBLEVBQzFDLE1BQU0sQ0FBTSxVQUFLLFFBQVEsR0FBRyxXQUFXLE9BQU8sR0FBUSxVQUFLLFFBQVEsR0FBRyxXQUFXLFlBQVksT0FBTyxDQUFDO0FBQUEsRUFDckcsSUFBSSxDQUFDLFdBQVc7QUFBQSxFQUNoQixTQUFTLENBQUMsaUJBQWlCLG1CQUFtQixhQUFhO0FBQzVEO0FBRUEsU0FBUyxnQkFBNkI7QUFDckMsU0FBTztBQUFBLElBQ04sVUFBVTtBQUFBLElBQ1YsY0FBYztBQUFBLElBQ2QsZUFBZTtBQUFBLEVBQ2hCO0FBQ0Q7QUFFQSxlQUFzQixXQUFXLFlBQTBDO0FBQzFFLFFBQU0sY0FBYztBQUFBLElBQ25CO0FBQUEsSUFDQTtBQUFBLElBQ0E7QUFBQSxJQUNBO0FBQUEsRUFDRDtBQUVBLGFBQVcsUUFBUSxhQUFhO0FBQy9CLFVBQU0sS0FBVSxhQUFRLFlBQVksSUFBSTtBQUN4QyxRQUFJLENBQUksY0FBVyxFQUFFLEVBQUc7QUFDeEIsUUFBSTtBQUNILFlBQU0sTUFBUyxnQkFBYSxJQUFJLE9BQU87QUFDdkMsWUFBTSxTQUFTLEtBQUssTUFBTSxHQUFHO0FBQzdCLGFBQU87QUFBQSxRQUNOLEdBQUcsY0FBYztBQUFBLFFBQ2pCLEdBQUc7QUFBQSxRQUNILFVBQVUsT0FBTyxZQUFZO0FBQUEsUUFDN0IsY0FBYyxPQUFPLGdCQUFnQjtBQUFBLFFBQ3JDLGVBQWUsT0FBTyxpQkFBaUI7QUFBQSxNQUN4QztBQUFBLElBQ0QsU0FBUyxLQUFLO0FBQ2IsY0FBUSxNQUFNLHVDQUF1QyxlQUFlLFFBQVEsSUFBSSxVQUFVLEdBQUc7QUFBQSxJQUM5RjtBQUFBLEVBQ0Q7QUFFQSxTQUFPLGNBQWM7QUFDdEI7QUFFTyxTQUFTLFlBQ2YsWUFDQSxRQUN3RDtBQUN4RCxRQUFNLFVBQVUsT0FBTyxRQUFRO0FBQy9CLFFBQU0sU0FBZ0UsQ0FBQztBQUV2RSxhQUFXLENBQUMsT0FBTyxLQUFLLEtBQUssT0FBTyxRQUFRLE9BQU8sR0FBRztBQUNyRCxlQUFXLEtBQUssT0FBTztBQUN0QixhQUFPLEtBQUs7QUFBQSxRQUNYO0FBQUEsUUFDQSxLQUFLLEVBQUUsV0FBVyxHQUFHLEtBQUssRUFBRSxXQUFXLEdBQUcsSUFBSSxFQUFFLFFBQVEsTUFBTSxRQUFRLENBQUMsSUFBUyxhQUFRLFlBQVksQ0FBQztBQUFBLFFBQ3JHLFFBQVE7QUFBQSxNQUNULENBQUM7QUFBQSxJQUNGO0FBQUEsRUFDRDtBQUVBLFNBQU87QUFDUjsiLAogICJuYW1lcyI6IFtdCn0K
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import type { RulesConfig } from "./types.js";
|
|
5
|
+
|
|
6
|
+
const DEFAULT_CACHE_TTL = 30_000;
|
|
7
|
+
|
|
8
|
+
const DEFAULT_DIRS = {
|
|
9
|
+
managed: ["/etc/claude-code/.claude/rules"],
|
|
10
|
+
user: [path.join(homedir(), ".claude", "rules"), path.join(homedir(), ".config", "opencode", "rules")],
|
|
11
|
+
pi: [".pi/rules"],
|
|
12
|
+
project: [".claude/rules", ".opencode/rules", ".trae/rules"],
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
function defaultConfig(): RulesConfig {
|
|
16
|
+
return {
|
|
17
|
+
cacheTTL: DEFAULT_CACHE_TTL,
|
|
18
|
+
notifyOnLoad: true,
|
|
19
|
+
notifyOnMatch: true,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function loadConfig(projectDir: string): Promise<RulesConfig> {
|
|
24
|
+
const configFiles = [
|
|
25
|
+
".rules-config.json",
|
|
26
|
+
".pi/rules-config.json",
|
|
27
|
+
".claude/rules-config.json",
|
|
28
|
+
".opencode/rules-config.json",
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
for (const name of configFiles) {
|
|
32
|
+
const fp = path.resolve(projectDir, name);
|
|
33
|
+
if (!fs.existsSync(fp)) continue;
|
|
34
|
+
try {
|
|
35
|
+
const raw = fs.readFileSync(fp, "utf-8");
|
|
36
|
+
const parsed = JSON.parse(raw);
|
|
37
|
+
return {
|
|
38
|
+
...defaultConfig(),
|
|
39
|
+
...parsed,
|
|
40
|
+
cacheTTL: parsed.cacheTTL ?? DEFAULT_CACHE_TTL,
|
|
41
|
+
notifyOnLoad: parsed.notifyOnLoad ?? true,
|
|
42
|
+
notifyOnMatch: parsed.notifyOnMatch ?? true,
|
|
43
|
+
};
|
|
44
|
+
} catch (err) {
|
|
45
|
+
console.debug("[rules-engine] config parse failed:", err instanceof Error ? err.message : err);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return defaultConfig();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function resolveDirs(
|
|
53
|
+
projectDir: string,
|
|
54
|
+
config: RulesConfig,
|
|
55
|
+
): Array<{ scope: string; dir: string; source: string }> {
|
|
56
|
+
const sources = config.dirs || DEFAULT_DIRS;
|
|
57
|
+
const result: Array<{ scope: string; dir: string; source: string }> = [];
|
|
58
|
+
|
|
59
|
+
for (const [scope, paths] of Object.entries(sources)) {
|
|
60
|
+
for (const p of paths) {
|
|
61
|
+
result.push({
|
|
62
|
+
scope,
|
|
63
|
+
dir: p.startsWith("/") || p.startsWith("~") ? p.replace(/^~/, homedir()) : path.resolve(projectDir, p),
|
|
64
|
+
source: p,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return result;
|
|
70
|
+
}
|