@triflux/core 10.50.2 → 10.52.0

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.
Files changed (84) hide show
  1. package/hooks/agy-session-hook.mjs +11 -56
  2. package/hooks/codex-session-hook.mjs +11 -101
  3. package/hub/bridge.mjs +65 -1250
  4. package/hub/cli-adapter-base.mjs +12 -90
  5. package/hub/codex-adapter.mjs +0 -1
  6. package/hub/codex-preflight.mjs +5 -28
  7. package/hub/index.mjs +3 -19
  8. package/hub/lib/process-utils.mjs +1 -156
  9. package/hub/lib/spawn-trace.mjs +3 -2
  10. package/hub/lib/timeout-defaults.mjs +0 -1
  11. package/hub/team/claude-daemon-control.mjs +1 -2
  12. package/hub/team/claude-session-projection.mjs +5 -73
  13. package/hub/team/nativeProxy.mjs +759 -0
  14. package/hub/team/session-context.mjs +1 -1
  15. package/hud/cli-policy.mjs +0 -22
  16. package/hud/constants.mjs +13 -26
  17. package/hud/hud-qos-status.mjs +42 -54
  18. package/hud/providers/gemini.mjs +125 -20
  19. package/hud/renderers.mjs +101 -122
  20. package/hud/utils.mjs +10 -6
  21. package/package.json +2 -6
  22. package/scripts/lib/agent-route-policy.mjs +143 -62
  23. package/scripts/lib/cli-claude.mjs +0 -14
  24. package/scripts/lib/env-probe.mjs +2 -131
  25. package/scripts/lib/gemini-profiles.mjs +2 -69
  26. package/scripts/lib/legacy-mcp-cleanup.mjs +283 -0
  27. package/scripts/lib/machine-profile.mjs +3 -17
  28. package/scripts/lib/mcp-filter.mjs +109 -53
  29. package/scripts/lib/mcp-guard-engine.mjs +40 -98
  30. package/scripts/lib/mcp-health.mjs +2 -2
  31. package/scripts/lib/mcp-server-catalog.mjs +2 -2
  32. package/scripts/lib/psmux-info.mjs +11 -59
  33. package/scripts/lib/session-presence.mjs +2 -100
  34. package/scripts/lib/skill-template.mjs +0 -245
  35. package/hub/adaptive-diagnostic.mjs +0 -350
  36. package/hub/adaptive-inject.mjs +0 -224
  37. package/hub/adaptive-memory.mjs +0 -277
  38. package/hub/adaptive.mjs +0 -172
  39. package/hub/assign-callbacks.mjs +0 -170
  40. package/hub/delegator/contracts.mjs +0 -38
  41. package/hub/delegator/index.mjs +0 -14
  42. package/hub/delegator/schema/delegator-tools.schema.json +0 -250
  43. package/hub/delegator/service.mjs +0 -336
  44. package/hub/delegator/tool-definitions.mjs +0 -35
  45. package/hub/dynamic-routing-engine.mjs +0 -511
  46. package/hub/hitl.mjs +0 -202
  47. package/hub/lib/known-errors.json +0 -72
  48. package/hub/lib/memory-store.mjs +0 -1001
  49. package/hub/lib/phase-manager.mjs +0 -493
  50. package/hub/lib/risk-tier.mjs +0 -53
  51. package/hub/lib/ssh-command.mjs +0 -231
  52. package/hub/lib/staged-file-detect.mjs +0 -43
  53. package/hub/lib/state-snapshot.mjs +0 -293
  54. package/hub/lib/tfx-route-args.mjs +0 -353
  55. package/hub/lib/trace-recorder.mjs +0 -153
  56. package/hub/lib/uuidv7.mjs +0 -45
  57. package/hub/memory-doctor.mjs +0 -640
  58. package/hub/middleware/quota-middleware.mjs +0 -70
  59. package/hub/middleware/request-logger.mjs +0 -191
  60. package/hub/paths.mjs +0 -24
  61. package/hub/pipeline/index.mjs +0 -163
  62. package/hub/pipeline/state.mjs +0 -191
  63. package/hub/pipeline/transitions.mjs +0 -131
  64. package/hub/reflexion.mjs +0 -290
  65. package/hub/role-contract.mjs +0 -314
  66. package/hub/router.mjs +0 -2026
  67. package/hub/routing-snapshot.mjs +0 -261
  68. package/hub/session-fingerprint.mjs +0 -418
  69. package/hub/state.mjs +0 -300
  70. package/hub/team/synapse-http.mjs +0 -311
  71. package/hub/team-bridge.mjs +0 -27
  72. package/mesh/index.mjs +0 -69
  73. package/mesh/mesh-budget.mjs +0 -137
  74. package/mesh/mesh-heartbeat.mjs +0 -100
  75. package/mesh/mesh-protocol.mjs +0 -96
  76. package/mesh/mesh-queue.mjs +0 -163
  77. package/mesh/mesh-registry.mjs +0 -78
  78. package/mesh/mesh-router.mjs +0 -77
  79. package/scripts/lib/claudemd-scanner.mjs +0 -223
  80. package/scripts/lib/context.mjs +0 -67
  81. package/scripts/lib/dynamic-route-cli.mjs +0 -107
  82. package/scripts/lib/handoff.mjs +0 -187
  83. package/scripts/lib/logger.mjs +0 -122
  84. package/scripts/lib/stealth-fetch.mjs +0 -176
@@ -1,350 +0,0 @@
1
- import { readFileSync } from "node:fs";
2
- import { dirname, resolve } from "node:path";
3
- import { fileURLToPath } from "node:url";
4
-
5
- import { normalizeError } from "./reflexion.mjs";
6
-
7
- const DEFAULT_KNOWN_ERRORS_PATH = resolve(
8
- dirname(fileURLToPath(import.meta.url)),
9
- "lib/known-errors.json",
10
- );
11
- const DEFAULT_CONFIDENCE = 0.5;
12
- const ADAPTIVE_CONFIDENCE_STEP = 0.1;
13
- const MAX_ADAPTIVE_CONFIDENCE = 0.95;
14
-
15
- function clone(value) {
16
- return value == null ? value : JSON.parse(JSON.stringify(value));
17
- }
18
-
19
- function pickString(...values) {
20
- for (const value of values) {
21
- if (typeof value === "string" && value.trim()) return value.trim();
22
- }
23
- return "";
24
- }
25
-
26
- function pickObject(...values) {
27
- return (
28
- values.find(
29
- (value) => value && typeof value === "object" && !Array.isArray(value),
30
- ) || {}
31
- );
32
- }
33
-
34
- function clampConfidence(value, fallback = DEFAULT_CONFIDENCE) {
35
- const numeric = Number(value);
36
- if (!Number.isFinite(numeric)) return fallback;
37
- return Math.max(0, Math.min(1, numeric));
38
- }
39
-
40
- function normalizeLookup(text) {
41
- return String(text || "")
42
- .trim()
43
- .toLowerCase();
44
- }
45
-
46
- function readPathValue(source, path) {
47
- if (!source || !path) return undefined;
48
- return String(path)
49
- .split(".")
50
- .filter(Boolean)
51
- .reduce((current, key) => current?.[key], source);
52
- }
53
-
54
- function renderTemplate(template, observation, signature) {
55
- if (!template) return "";
56
- const context = pickObject(observation.context);
57
- const dna = pickObject(observation.dna);
58
- const dnaValue = signature.dna_factor
59
- ? (readPathValue(dna, signature.dna_factor) ??
60
- readPathValue(context, signature.dna_factor) ??
61
- readPathValue(observation, signature.dna_factor))
62
- : undefined;
63
- const variables = {
64
- ...context,
65
- ...pickObject(observation.variables),
66
- host: pickString(observation.host, context.host),
67
- value: dnaValue,
68
- };
69
- return String(template).replace(/\{([^}]+)\}/gu, (match, key) => {
70
- const value = variables[key];
71
- return value == null || value === "" ? match : String(value);
72
- });
73
- }
74
-
75
- function buildKnownContextText(observation) {
76
- return normalizeLookup(
77
- [
78
- pickString(observation.contextLabel, observation.context),
79
- pickString(observation.phase),
80
- pickString(observation.step),
81
- ]
82
- .filter(Boolean)
83
- .join(" "),
84
- );
85
- }
86
-
87
- function buildErrorText(observation = {}) {
88
- return [
89
- pickString(observation.error),
90
- pickString(observation.stderr),
91
- pickString(observation.tool_output, observation.output),
92
- pickString(observation.message),
93
- ]
94
- .filter(Boolean)
95
- .join("\n")
96
- .trim();
97
- }
98
-
99
- function normalizeObservation(observation = {}) {
100
- const errorText = buildErrorText(observation);
101
- const projectSlug = pickString(
102
- observation.project_slug,
103
- observation.projectSlug,
104
- observation.context?.project_slug,
105
- observation.context?.projectSlug,
106
- );
107
- return {
108
- ...clone(observation),
109
- errorText,
110
- errorPattern: normalizeError(errorText),
111
- projectSlug,
112
- tool: pickString(observation.tool, observation.tool_name),
113
- contextText: buildKnownContextText(observation),
114
- };
115
- }
116
-
117
- function compileSignatures(raw = {}) {
118
- return Object.entries(raw.signatures || {}).map(([id, signature]) => ({
119
- id,
120
- ...clone(signature),
121
- matcher: new RegExp(String(signature.pattern || ""), "iu"),
122
- }));
123
- }
124
-
125
- export function loadKnownErrors(filePath = DEFAULT_KNOWN_ERRORS_PATH) {
126
- try {
127
- const parsed = JSON.parse(readFileSync(filePath, "utf8"));
128
- return {
129
- path: filePath,
130
- version: parsed.version ?? 1,
131
- signatures: compileSignatures(parsed),
132
- };
133
- } catch {
134
- return { path: filePath, version: 0, signatures: [] };
135
- }
136
- }
137
-
138
- function scoreKnownMatch(signature, observation) {
139
- if (
140
- !observation.errorText ||
141
- !signature.matcher.test(observation.errorText)
142
- ) {
143
- return null;
144
- }
145
- if (
146
- signature.tool &&
147
- observation.tool &&
148
- normalizeLookup(signature.tool) !== normalizeLookup(observation.tool)
149
- ) {
150
- return null;
151
- }
152
- if (
153
- signature.context &&
154
- observation.contextText &&
155
- !observation.contextText.includes(normalizeLookup(signature.context))
156
- ) {
157
- return null;
158
- }
159
- return clampConfidence(
160
- Number(signature.confidence_base ?? DEFAULT_CONFIDENCE) +
161
- (signature.tool && observation.tool ? 0.02 : 0) +
162
- (signature.context && observation.contextText ? 0.02 : 0) +
163
- (signature.dna_factor ? 0.01 : 0),
164
- );
165
- }
166
-
167
- function severityFromConfidence(confidence, fallback = "medium") {
168
- if (confidence >= 0.9) return "critical";
169
- if (confidence >= 0.75) return "high";
170
- if (confidence >= 0.55) return "medium";
171
- return fallback;
172
- }
173
-
174
- export function matchKnownError(catalog, observationInput = {}) {
175
- const observation = normalizeObservation(observationInput);
176
- const matched = (catalog?.signatures || [])
177
- .map((signature) => {
178
- const confidence = scoreKnownMatch(signature, observation);
179
- return confidence == null ? null : { signature, confidence };
180
- })
181
- .filter(Boolean)
182
- .sort((left, right) => right.confidence - left.confidence)[0];
183
-
184
- if (!matched) return null;
185
- const { signature, confidence } = matched;
186
- return {
187
- matched: true,
188
- source: "known",
189
- signature_id: signature.id,
190
- project_slug: observation.projectSlug,
191
- error_pattern: observation.errorPattern,
192
- error_message: observation.errorText,
193
- confidence,
194
- severity: signature.severity || severityFromConfidence(confidence),
195
- tool: signature.tool || observation.tool,
196
- context: signature.context || observation.contextText || null,
197
- root_cause: signature.root_cause || "known failure pattern",
198
- rule: renderTemplate(signature.rule_template, observation, signature),
199
- fix: signature.fix || null,
200
- dna_factor: signature.dna_factor || null,
201
- };
202
- }
203
-
204
- function resolveRuleStore(options = {}) {
205
- return options.store || options.adaptiveMemory || null;
206
- }
207
-
208
- function ensureAdaptiveRule(store, observation) {
209
- if (
210
- !store?.findAdaptiveRule ||
211
- !store?.addAdaptiveRule ||
212
- !observation.projectSlug
213
- ) {
214
- return null;
215
- }
216
- const identity = {
217
- project_slug: observation.projectSlug,
218
- pattern: observation.errorPattern,
219
- };
220
- const current = store.findAdaptiveRule(
221
- identity.project_slug,
222
- identity.pattern,
223
- );
224
- if (!current) {
225
- return store.addAdaptiveRule(identity);
226
- }
227
- if (!store.updateRuleConfidence) return current;
228
- return store.updateRuleConfidence(
229
- identity.project_slug,
230
- identity.pattern,
231
- Math.min(
232
- MAX_ADAPTIVE_CONFIDENCE,
233
- current.confidence + ADAPTIVE_CONFIDENCE_STEP,
234
- ),
235
- { hit_count_increment: 1 },
236
- );
237
- }
238
-
239
- function buildAdaptiveDiagnosis(rule, observation) {
240
- if (!rule) {
241
- return {
242
- matched: false,
243
- source: "novel",
244
- project_slug: observation.projectSlug,
245
- error_pattern: observation.errorPattern,
246
- error_message: observation.errorText,
247
- confidence: DEFAULT_CONFIDENCE,
248
- severity: "low",
249
- root_cause: "새로운 실패 패턴으로 분류됨",
250
- rule: "",
251
- fix: null,
252
- };
253
- }
254
- const matched =
255
- Number(rule.hit_count || 0) > 1 ||
256
- Number(rule.confidence || 0) > DEFAULT_CONFIDENCE;
257
- const confidence = clampConfidence(rule.confidence, DEFAULT_CONFIDENCE);
258
- return {
259
- matched,
260
- source: matched ? "adaptive" : "novel",
261
- project_slug: rule.project_slug,
262
- error_pattern: rule.pattern,
263
- error_message: observation.errorText,
264
- confidence,
265
- severity: severityFromConfidence(confidence, matched ? "medium" : "low"),
266
- root_cause: matched
267
- ? "반복 관측된 adaptive rule과 일치"
268
- : "adaptive memory에 첫 관측으로 저장됨",
269
- rule: matched
270
- ? `프로젝트 ${rule.project_slug}에서 동일 패턴이 ${rule.hit_count}회 관측되었습니다.`
271
- : `프로젝트 ${rule.project_slug}의 adaptive memory에 패턴을 기록했습니다.`,
272
- fix: matched
273
- ? "최근 성공한 수정/회피 전략을 재적용하세요."
274
- : "추가 관측 후 adaptive rule을 승격하세요.",
275
- adaptive_rule: clone(rule),
276
- };
277
- }
278
-
279
- function createHealthyState(catalog) {
280
- return {
281
- state: "healthy",
282
- known_errors_count: catalog.signatures.length,
283
- last_error: null,
284
- };
285
- }
286
-
287
- function createDegradedState(error) {
288
- return {
289
- state: "degraded",
290
- known_errors_count: 0,
291
- last_error: {
292
- name: error?.name || "Error",
293
- message: error?.message || "unknown adaptive diagnostic error",
294
- },
295
- };
296
- }
297
-
298
- export function createDiagnosticPipeline(options = {}) {
299
- const store = resolveRuleStore(options);
300
- let catalog = options.knownErrors
301
- ? { signatures: compileSignatures({ signatures: options.knownErrors }) }
302
- : null;
303
- let health = catalog ? createHealthyState(catalog) : null;
304
-
305
- function ensureCatalog() {
306
- if (catalog) return catalog;
307
- try {
308
- catalog = loadKnownErrors(
309
- options.knownErrorsPath || DEFAULT_KNOWN_ERRORS_PATH,
310
- );
311
- health = createHealthyState(catalog);
312
- } catch (error) {
313
- catalog = { signatures: [] };
314
- health = createDegradedState(error);
315
- }
316
- return catalog;
317
- }
318
-
319
- function diagnoseFailure(observationInput = {}) {
320
- const observation = normalizeObservation(observationInput);
321
- const nextCatalog = ensureCatalog();
322
- const known = matchKnownError(nextCatalog, observation);
323
- if (known) return known;
324
- const adaptiveRule = observation.errorPattern
325
- ? ensureAdaptiveRule(store, observation)
326
- : null;
327
- return buildAdaptiveDiagnosis(adaptiveRule, observation);
328
- }
329
-
330
- function getHealth() {
331
- return clone(health || (ensureCatalog() && health));
332
- }
333
-
334
- function listKnownErrors() {
335
- return (ensureCatalog().signatures || []).map(({ matcher, ...signature }) =>
336
- clone(signature),
337
- );
338
- }
339
-
340
- return Object.freeze({
341
- diagnose: diagnoseFailure,
342
- diagnoseFailure,
343
- run: diagnoseFailure,
344
- getHealth,
345
- listKnownErrors,
346
- });
347
- }
348
-
349
- export { DEFAULT_KNOWN_ERRORS_PATH };
350
- export default createDiagnosticPipeline;
@@ -1,224 +0,0 @@
1
- import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
- import { join, resolve } from "node:path";
3
-
4
- const SECTION_HEADING = "## Adaptive Rules (triflux auto-generated)";
5
- const DEFAULT_MAX_RULES = 10;
6
- const SECTION_RE = /^## Adaptive Rules \(triflux auto-generated\)$/mu;
7
- const BLOCK_RE =
8
- /<!-- tfx-adaptive:start rule_id="([^"]+)" confidence=([0-9.]+) occurrences=(\d+) first_seen=([0-9-]+) last_seen=([0-9-]+) -->\r?\n([^\r\n]*)\r?\n<!-- tfx-adaptive:end -->/gu;
9
-
10
- function cloneRule(rule) {
11
- return Object.freeze({ ...rule });
12
- }
13
-
14
- function clampConfidence(value) {
15
- const numeric = Number(value);
16
- if (!Number.isFinite(numeric)) return 0;
17
- return Math.min(1, Math.max(0, numeric));
18
- }
19
-
20
- function normalizeOccurrences(value) {
21
- const numeric = Number(value);
22
- if (!Number.isFinite(numeric)) return 1;
23
- return Math.max(1, Math.trunc(numeric));
24
- }
25
-
26
- function formatConfidence(value) {
27
- return Number(clampConfidence(value).toFixed(6)).toString();
28
- }
29
-
30
- function normalizeDate(value, fallback) {
31
- const text = String(value ?? fallback ?? "").trim();
32
- return text || fallback;
33
- }
34
-
35
- function normalizeRuleInput(rule, fallback = {}) {
36
- if (!rule || typeof rule !== "object") return null;
37
-
38
- const id = String(rule.id ?? rule.rule_id ?? fallback.id ?? "").trim();
39
- const text = String(rule.rule ?? rule.text ?? fallback.rule ?? "").trim();
40
- const firstSeen = normalizeDate(
41
- rule.firstSeen ?? rule.first_seen,
42
- fallback.firstSeen ?? fallback.lastSeen ?? "1970-01-01",
43
- );
44
- const lastSeen = normalizeDate(
45
- rule.lastSeen ?? rule.last_seen,
46
- fallback.lastSeen ?? firstSeen,
47
- );
48
- if (!id || !text || /["\r\n]/u.test(id) || /[\r\n]/u.test(text)) {
49
- return null;
50
- }
51
-
52
- return cloneRule({
53
- id,
54
- rule: text,
55
- confidence: clampConfidence(rule.confidence ?? fallback.confidence),
56
- occurrences: normalizeOccurrences(rule.occurrences ?? fallback.occurrences),
57
- firstSeen,
58
- lastSeen,
59
- });
60
- }
61
-
62
- function trimLeadingBlankLines(text) {
63
- return String(text ?? "").replace(/^(?:[ \t]*\r?\n)+/u, "");
64
- }
65
-
66
- function trimTrailingBlankLines(text) {
67
- return String(text ?? "").replace(/(?:\r?\n[ \t]*)+$/u, "");
68
- }
69
-
70
- function parseInjectedRules(sectionBody = "") {
71
- const matches = Array.from(String(sectionBody).matchAll(BLOCK_RE));
72
- return matches.map(
73
- ([, id, confidence, occurrences, firstSeen, lastSeen, text]) =>
74
- cloneRule({
75
- id,
76
- rule: text,
77
- confidence: clampConfidence(confidence),
78
- occurrences: normalizeOccurrences(occurrences),
79
- firstSeen,
80
- lastSeen,
81
- }),
82
- );
83
- }
84
-
85
- function readDocument(claudeMdPath) {
86
- const raw = existsSync(claudeMdPath)
87
- ? readFileSync(claudeMdPath, "utf8")
88
- : "";
89
- const sectionStart = raw.search(SECTION_RE);
90
- if (sectionStart === -1) {
91
- return { before: raw, after: "", rules: [] };
92
- }
93
-
94
- const headingEnd = raw.indexOf("\n", sectionStart);
95
- const bodyStart = headingEnd === -1 ? raw.length : headingEnd + 1;
96
- const rest = raw.slice(bodyStart);
97
- const nextHeadingOffset = rest.search(/^#{1,6}\s/mu);
98
- const sectionEnd =
99
- nextHeadingOffset === -1 ? raw.length : bodyStart + nextHeadingOffset;
100
- const body = raw.slice(bodyStart, sectionEnd);
101
-
102
- return {
103
- before: raw.slice(0, sectionStart),
104
- after: raw.slice(sectionEnd),
105
- rules: parseInjectedRules(body),
106
- };
107
- }
108
-
109
- function serializeRule(rule) {
110
- return [
111
- `<!-- tfx-adaptive:start rule_id="${rule.id}" confidence=${formatConfidence(rule.confidence)} occurrences=${rule.occurrences} first_seen=${rule.firstSeen} last_seen=${rule.lastSeen} -->`,
112
- rule.rule,
113
- "<!-- tfx-adaptive:end -->",
114
- ].join("\n");
115
- }
116
-
117
- function serializeDocument(before, rules, after) {
118
- const section =
119
- rules.length > 0
120
- ? `${SECTION_HEADING}\n\n${rules.map(serializeRule).join("\n\n")}`
121
- : "";
122
- const parts = [
123
- trimTrailingBlankLines(before),
124
- section,
125
- trimLeadingBlankLines(after),
126
- ].filter(Boolean);
127
- return parts.length > 0 ? `${parts.join("\n\n")}\n` : "";
128
- }
129
-
130
- function enforceMaxRules(rules, maxRules) {
131
- if (rules.length <= maxRules) return rules.map(cloneRule);
132
- const ranked = rules
133
- .map((rule, index) => ({ ...rule, index }))
134
- .sort(
135
- (left, right) =>
136
- left.confidence - right.confidence ||
137
- left.occurrences - right.occurrences ||
138
- left.lastSeen.localeCompare(right.lastSeen) ||
139
- left.index - right.index ||
140
- left.id.localeCompare(right.id),
141
- );
142
- const removedIds = new Set(
143
- ranked.slice(0, rules.length - maxRules).map((rule) => rule.id),
144
- );
145
- return rules.filter((rule) => !removedIds.has(rule.id)).map(cloneRule);
146
- }
147
-
148
- export function createAdaptiveInjector(opts = {}) {
149
- const claudeMdPath = resolve(
150
- opts.claudeMdPath ?? join(process.cwd(), "CLAUDE.md"),
151
- );
152
- const maxRules =
153
- Number.isInteger(opts.maxRules) && opts.maxRules > 0
154
- ? opts.maxRules
155
- : DEFAULT_MAX_RULES;
156
-
157
- function listInjected() {
158
- return readDocument(claudeMdPath).rules.map(cloneRule);
159
- }
160
-
161
- function inject(rule) {
162
- const document = readDocument(claudeMdPath);
163
- const targetId = String(rule?.id ?? rule?.rule_id ?? "").trim();
164
- const existing = document.rules.find((item) => item.id === targetId);
165
- const normalized = normalizeRuleInput(rule, existing ?? {});
166
- if (!normalized) return false;
167
-
168
- const nextRules = existing
169
- ? document.rules.map((item) =>
170
- item.id === normalized.id
171
- ? cloneRule({
172
- ...item,
173
- confidence: normalized.confidence,
174
- occurrences: normalized.occurrences,
175
- lastSeen: normalized.lastSeen,
176
- })
177
- : cloneRule(item),
178
- )
179
- : [...document.rules.map(cloneRule), normalized];
180
- const limitedRules = enforceMaxRules(nextRules, maxRules);
181
- writeFileSync(
182
- claudeMdPath,
183
- serializeDocument(document.before, limitedRules, document.after),
184
- "utf8",
185
- );
186
- return limitedRules.some((item) => item.id === normalized.id);
187
- }
188
-
189
- function remove(ruleId) {
190
- const targetId = String(ruleId ?? "").trim();
191
- if (!targetId || !existsSync(claudeMdPath)) return false;
192
- const document = readDocument(claudeMdPath);
193
- if (!document.rules.some((rule) => rule.id === targetId)) return false;
194
- const nextRules = document.rules
195
- .filter((rule) => rule.id !== targetId)
196
- .map(cloneRule);
197
- writeFileSync(
198
- claudeMdPath,
199
- serializeDocument(document.before, nextRules, document.after),
200
- "utf8",
201
- );
202
- return true;
203
- }
204
-
205
- function cleanup(activeRuleIds = []) {
206
- const activeIds = new Set(
207
- Array.isArray(activeRuleIds) ? activeRuleIds : Array.from(activeRuleIds),
208
- );
209
- return listInjected().reduce(
210
- (count, rule) =>
211
- count + (activeIds.has(rule.id) ? 0 : Number(remove(rule.id))),
212
- 0,
213
- );
214
- }
215
-
216
- return Object.freeze({
217
- inject,
218
- remove,
219
- listInjected,
220
- cleanup,
221
- });
222
- }
223
-
224
- export default createAdaptiveInjector;