deuk-agent-rule 1.0.13 → 2.2.1

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.
@@ -1,195 +1,365 @@
1
- import {
2
- copyFileSync,
3
- existsSync,
4
- mkdirSync,
5
- readFileSync,
6
- readdirSync,
7
- writeFileSync,
8
- } from "fs";
9
- import { join } from "path";
10
-
11
- export const DEFAULT_TAG = "deuk-agent-rule";
12
-
13
- export function resolveMarkers(o) {
14
- const hasBegin = o.markerBegin != null && o.markerBegin !== "";
15
- const hasEnd = o.markerEnd != null && o.markerEnd !== "";
16
- if (hasBegin !== hasEnd) {
17
- throw new Error("Use both --marker-begin and --marker-end, or neither.");
18
- }
19
- if (hasBegin && hasEnd) {
20
- if (o.markerBegin === o.markerEnd) {
21
- throw new Error("--marker-begin and --marker-end must differ.");
22
- }
23
- return { begin: o.markerBegin, end: o.markerEnd };
24
- }
25
- const id = o.tag && o.tag.trim() ? o.tag.trim() : DEFAULT_TAG;
26
- return {
27
- begin: "<!-- " + id + ":begin -->",
28
- end: "<!-- " + id + ":end -->",
29
- };
30
- }
31
-
32
- export function findMarkerRegion(content, begin, end) {
33
- const i = content.indexOf(begin);
34
- if (i === -1) return null;
35
- const j = content.indexOf(end, i + begin.length);
36
- if (j === -1) {
37
- throw new Error(
38
- "Found begin marker but no matching end marker after it.\n begin: " + begin + "\n end: " + end,
39
- );
40
- }
41
- const innerStart = i + begin.length;
42
- const innerEnd = j;
43
- return { innerStart, innerEnd };
44
- }
45
-
46
- export function applyAgents(opts) {
47
- const {
48
- targetPath,
49
- bundleContent,
50
- markers,
51
- flavor,
52
- appendIfNoMarkers,
53
- dryRun,
54
- backup,
55
- agentsMode,
56
- } = opts;
57
-
58
- if (agentsMode === "skip") {
59
- return {
60
- action: "skip",
61
- reason: existsSync(targetPath)
62
- ? "agents mode skip (file exists)"
63
- : "agents mode skip",
64
- };
65
- }
66
-
67
- if (agentsMode === "overwrite") {
68
- const prev = existsSync(targetPath) ? readFileSync(targetPath, "utf8") : "";
69
- if (dryRun) {
70
- return { action: "would-write", path: targetPath, mode: "overwrite" };
71
- }
72
- if (backup && existsSync(targetPath)) {
73
- copyFileSync(targetPath, targetPath + ".bak");
74
- }
75
- writeFileSync(targetPath, bundleContent, "utf8");
76
- return { action: "write", path: targetPath, mode: "overwrite", hadPrevious: !!prev };
77
- }
78
-
79
- const existing = existsSync(targetPath) ? readFileSync(targetPath, "utf8") : "";
80
- const region = findMarkerRegion(existing, markers.begin, markers.end);
81
-
82
- if (region) {
83
- const inner = bundleContent.trimEnd() + "\n";
84
- const next =
85
- existing.slice(0, region.innerStart) +
86
- "\n" +
87
- inner +
88
- existing.slice(region.innerEnd);
89
- if (dryRun) {
90
- return { action: "would-write", path: targetPath, mode: "inject-region" };
91
- }
92
- if (backup && existsSync(targetPath)) {
93
- copyFileSync(targetPath, targetPath + ".bak");
94
- }
95
- writeFileSync(targetPath, next, "utf8");
96
- return { action: "write", path: targetPath, mode: "inject-region" };
97
- }
98
-
99
- const allowAppend =
100
- appendIfNoMarkers || flavor === "init";
101
-
102
- if (!allowAppend) {
103
- const hint = [
104
- "",
105
- "No marker region found. Add a pair like:",
106
- "",
107
- markers.begin,
108
- "",
109
- markers.end,
110
- "",
111
- "Or run: npx deuk-agent-rule init (appends markers once)",
112
- "Or pass: --append-if-no-markers",
113
- "",
114
- ].join("\n");
115
- throw new Error(
116
- "Inject mode requires markers in " + targetPath + " or use --append-if-no-markers." + hint,
117
- );
118
- }
119
-
120
- const inner = bundleContent.trimEnd() + "\n";
121
- const block = "\n" + markers.begin + "\n\n" + inner + "\n" + markers.end + "\n";
122
- const next = existing ? existing.replace(/\s*$/, "") + block : markers.begin + "\n\n" + inner + "\n" + markers.end + "\n";
123
-
124
- if (dryRun) {
125
- return {
126
- action: "would-write",
127
- path: targetPath,
128
- mode: flavor === "init" ? "append-markers-init" : "append-markers",
129
- };
130
- }
131
- if (backup && existsSync(targetPath)) {
132
- copyFileSync(targetPath, targetPath + ".bak");
133
- }
134
- writeFileSync(targetPath, next, "utf8");
135
- return {
136
- action: "write",
137
- path: targetPath,
138
- mode: flavor === "init" ? "append-markers-init" : "append-markers",
139
- };
140
- }
141
-
142
- export function applyRules(opts) {
143
- const {
144
- bundleRulesDir,
145
- targetRulesDir,
146
- rulesMode,
147
- filePrefix = "deuk-agent-rule-",
148
- dryRun,
149
- backup,
150
- } = opts;
151
-
152
- if (!existsSync(bundleRulesDir)) {
153
- throw new Error("Bundle rules directory missing: " + bundleRulesDir);
154
- }
155
-
156
- const actions = [];
157
- mkdirSync(targetRulesDir, { recursive: true });
158
-
159
- for (const name of readdirSync(bundleRulesDir)) {
160
- if (!name.endsWith(".mdc")) continue;
161
- const src = join(bundleRulesDir, name);
162
- let destPath = join(targetRulesDir, name);
163
-
164
- if (rulesMode === "skip" && existsSync(destPath)) {
165
- actions.push({ action: "skip", src, dest: destPath, reason: "exists" });
166
- continue;
167
- }
168
-
169
- if (rulesMode === "prefix" && existsSync(destPath)) {
170
- destPath = join(targetRulesDir, filePrefix + name);
171
- // If prefixed file exists (repeat init after npm update), overwrite from bundle — do not skip.
172
- }
173
-
174
- if (dryRun) {
175
- actions.push({ action: "would-copy", src, dest: destPath });
176
- continue;
177
- }
178
-
179
- if (backup && existsSync(destPath)) {
180
- copyFileSync(destPath, destPath + ".bak");
181
- }
182
- copyFileSync(src, destPath);
183
- actions.push({ action: "copy", src, dest: destPath });
184
- }
185
-
186
- return actions;
187
- }
188
-
189
- export function readBundleAgents(bundleRoot) {
190
- const p = join(bundleRoot, "AGENTS.md");
191
- if (!existsSync(p)) {
192
- throw new Error("Bundle AGENTS.md missing: " + p);
193
- }
194
- return readFileSync(p, "utf8");
195
- }
1
+ import {
2
+ copyFileSync,
3
+ existsSync,
4
+ mkdirSync,
5
+ readFileSync,
6
+ readdirSync,
7
+ unlinkSync,
8
+ writeFileSync,
9
+ } from "fs";
10
+ import { join } from "path";
11
+
12
+ export const DEFAULT_TAG = "deuk-agent-rule";
13
+
14
+ /** HTML-style markers in `.cursorrules` (separate from AGENTS.md markers). */
15
+ export const CURSORRULES_TAG = "deuk-agent-rule-cursorrules";
16
+
17
+ export function resolveCursorrulesMarkers(o = {}) {
18
+ return resolveMarkers({
19
+ tag: o.tag && String(o.tag).trim() ? String(o.tag).trim() : CURSORRULES_TAG,
20
+ markerBegin: o.markerBegin,
21
+ markerEnd: o.markerEnd,
22
+ });
23
+ }
24
+
25
+ export function resolveMarkers(o) {
26
+ const hasBegin = o.markerBegin != null && o.markerBegin !== "";
27
+ const hasEnd = o.markerEnd != null && o.markerEnd !== "";
28
+ if (hasBegin !== hasEnd) {
29
+ throw new Error("Use both --marker-begin and --marker-end, or neither.");
30
+ }
31
+ if (hasBegin && hasEnd) {
32
+ if (o.markerBegin === o.markerEnd) {
33
+ throw new Error("--marker-begin and --marker-end must differ.");
34
+ }
35
+ return { begin: o.markerBegin, end: o.markerEnd };
36
+ }
37
+ const id = o.tag && o.tag.trim() ? o.tag.trim() : DEFAULT_TAG;
38
+ return {
39
+ begin: "<!-- " + id + ":begin -->",
40
+ end: "<!-- " + id + ":end -->",
41
+ };
42
+ }
43
+
44
+ export function findMarkerRegion(content, begin, end) {
45
+ const i = content.indexOf(begin);
46
+ if (i === -1) return null;
47
+ const j = content.indexOf(end, i + begin.length);
48
+ if (j === -1) {
49
+ throw new Error(
50
+ `[MARKER ERROR] Found begin marker "${begin}" but no matching end marker "${end}" after it.\n` +
51
+ ` This usually happens if one marker was deleted or renamed manually. Please verify the target file.`
52
+ );
53
+ }
54
+ const innerStart = i + begin.length;
55
+ const innerEnd = j;
56
+ return { innerStart, innerEnd };
57
+ }
58
+
59
+ export function applyAgents(opts) {
60
+ const { agentsMode, targetPath } = opts;
61
+
62
+ if (agentsMode === "skip") {
63
+ return {
64
+ action: "skip",
65
+ reason: existsSync(targetPath) ? "agents mode skip (file exists)" : "agents mode skip",
66
+ };
67
+ }
68
+
69
+ if (agentsMode === "overwrite") {
70
+ return handleAgentOverwrite(opts);
71
+ }
72
+
73
+ const existing = existsSync(targetPath) ? readFileSync(targetPath, "utf8") : "";
74
+
75
+ // Workflow Safety Check: warn about different tags and error on case-mismatch
76
+ const markerRegex = /<!--\s*([a-zA-Z0-9_-]+):begin\s*-->/gi;
77
+ const currentTagMatch = opts.markers.begin.match(/<!--\s*([a-zA-Z0-9_-]+):begin\s*-->/i);
78
+ const currentTag = currentTagMatch ? currentTagMatch[1] : null;
79
+
80
+ const foundTags = [];
81
+ let m;
82
+ while ((m = markerRegex.exec(existing)) !== null) {
83
+ if (currentTag && m[1].toLowerCase() === currentTag.toLowerCase()) {
84
+ if (m[1] !== currentTag) {
85
+ throw new Error(
86
+ `[CRITICAL ERROR] Case mismatch for tag "${currentTag}". Found "${m[1]}" in ${targetPath}.\n` +
87
+ ` Please unify casing to avoid duplicate managed blocks.`
88
+ );
89
+ }
90
+ } else {
91
+ foundTags.push(m[1]);
92
+ }
93
+ }
94
+
95
+ if (foundTags.length > 0) {
96
+ console.warn(`[WARNING] Foreign markers in ${targetPath}: ${[...new Set(foundTags)].join(", ")}`);
97
+ console.warn(` Current tag is "${currentTag}". This might lead to duplicate managed blocks.`);
98
+ }
99
+
100
+ const region = findMarkerRegion(existing, opts.markers.begin, opts.markers.end);
101
+
102
+ if (region) {
103
+ return handleAgentInject(opts, existing, region);
104
+ }
105
+
106
+ return handleAgentAppend(opts, existing);
107
+ }
108
+
109
+ function handleAgentOverwrite(opts) {
110
+ const { targetPath, bundleContent, dryRun, backup } = opts;
111
+ const prev = existsSync(targetPath) ? readFileSync(targetPath, "utf8") : "";
112
+ if (dryRun) {
113
+ return { action: "would-write", path: targetPath, mode: "overwrite" };
114
+ }
115
+ if (backup && existsSync(targetPath)) {
116
+ copyFileSync(targetPath, targetPath + ".bak");
117
+ }
118
+ writeFileSync(targetPath, bundleContent, "utf8");
119
+ return { action: "write", path: targetPath, mode: "overwrite", hadPrevious: !!prev };
120
+ }
121
+
122
+ function handleAgentInject(opts, existing, region) {
123
+ const { targetPath, bundleContent, dryRun, backup } = opts;
124
+ const inner = bundleContent.trimEnd() + "\n";
125
+ const next = existing.slice(0, region.innerStart) + "\n" + inner + existing.slice(region.innerEnd);
126
+ if (dryRun) {
127
+ return { action: "would-write", path: targetPath, mode: "inject-region" };
128
+ }
129
+ if (backup && existsSync(targetPath)) {
130
+ copyFileSync(targetPath, targetPath + ".bak");
131
+ }
132
+ writeFileSync(targetPath, next, "utf8");
133
+ return { action: "write", path: targetPath, mode: "inject-region" };
134
+ }
135
+
136
+ function handleAgentAppend(opts, existing) {
137
+ const { targetPath, bundleContent, markers, flavor, appendIfNoMarkers, dryRun, backup } = opts;
138
+ const allowAppend = appendIfNoMarkers || flavor === "init";
139
+
140
+ if (!allowAppend) {
141
+ const hint = [
142
+ "",
143
+ "No marker region found. Add a pair like:",
144
+ "",
145
+ markers.begin,
146
+ "",
147
+ markers.end,
148
+ "",
149
+ "Or run: npx deuk-agent-rule init (appends markers once)",
150
+ "Or pass: --append-if-no-markers",
151
+ "",
152
+ ].join("\n");
153
+ throw new Error(
154
+ "Inject mode requires markers in " + targetPath + " or use --append-if-no-markers." + hint,
155
+ );
156
+ }
157
+
158
+ const inner = bundleContent.trimEnd() + "\n";
159
+ const block = "\n" + markers.begin + "\n\n" + inner + "\n" + markers.end + "\n";
160
+ const next = existing ? existing.replace(/\s*$/, "") + block : markers.begin + "\n\n" + inner + "\n" + markers.end + "\n";
161
+
162
+ if (dryRun) {
163
+ return {
164
+ action: "would-write",
165
+ path: targetPath,
166
+ mode: flavor === "init" ? "append-markers-init" : "append-markers",
167
+ };
168
+ }
169
+ if (backup && existsSync(targetPath)) {
170
+ copyFileSync(targetPath, targetPath + ".bak");
171
+ }
172
+ writeFileSync(targetPath, next, "utf8");
173
+ return {
174
+ action: "write",
175
+ path: targetPath,
176
+ mode: flavor === "init" ? "append-markers-init" : "append-markers",
177
+ };
178
+ }
179
+
180
+ export function applyRules(opts) {
181
+ const {
182
+ bundleRulesDir,
183
+ targetRulesDir,
184
+ rulesMode,
185
+ filePrefix = "deuk-agent-rule-",
186
+ dryRun,
187
+ backup,
188
+ } = opts;
189
+
190
+ if (!existsSync(bundleRulesDir)) {
191
+ throw new Error("Bundle rules directory missing: " + bundleRulesDir);
192
+ }
193
+
194
+ const actions = [];
195
+ mkdirSync(targetRulesDir, { recursive: true });
196
+
197
+ for (const name of readdirSync(bundleRulesDir)) {
198
+ if (!name.endsWith(".mdc")) continue;
199
+ const src = join(bundleRulesDir, name);
200
+ let destPath = join(targetRulesDir, name);
201
+
202
+ if (rulesMode === "skip" && existsSync(destPath)) {
203
+ actions.push({ action: "skip", src, dest: destPath, reason: "exists" });
204
+ continue;
205
+ }
206
+
207
+ if (rulesMode === "prefix" && existsSync(destPath)) {
208
+ destPath = join(targetRulesDir, filePrefix + name);
209
+ // If prefixed file exists (repeat init after npm update), overwrite from bundle — do not skip.
210
+ }
211
+
212
+ if (dryRun) {
213
+ actions.push({ action: "would-copy", src, dest: destPath });
214
+ continue;
215
+ }
216
+
217
+ if (backup && existsSync(destPath)) {
218
+ copyFileSync(destPath, destPath + ".bak");
219
+ }
220
+ copyFileSync(src, destPath);
221
+ actions.push({ action: "copy", src, dest: destPath });
222
+ }
223
+
224
+ return actions;
225
+ }
226
+
227
+ export function readBundleAgents(bundleRoot) {
228
+ const p = join(bundleRoot, "AGENTS.md");
229
+ if (!existsSync(p)) {
230
+ throw new Error("Bundle AGENTS.md missing: " + p);
231
+ }
232
+ return readFileSync(p, "utf8");
233
+ }
234
+
235
+ /**
236
+ * Optional bundle file: Cursor root rules pointer to AGENTS.md.
237
+ * @returns {string|null} file contents, or null if missing
238
+ */
239
+ export function readBundleCursorrules(bundleRoot) {
240
+ const p = join(bundleRoot, ".cursorrules");
241
+ if (!existsSync(p)) {
242
+ return null;
243
+ }
244
+ return readFileSync(p, "utf8");
245
+ }
246
+
247
+ /**
248
+ * Remove one tagged block (markers inclusive). Returns ok:false if begin marker not found.
249
+ * @returns {{ ok: true, content: string } | { ok: false, reason: string }}
250
+ */
251
+ export function removeTaggedBlock(content, begin, end) {
252
+ const i = content.indexOf(begin);
253
+ if (i === -1) {
254
+ return { ok: false, reason: "begin not found" };
255
+ }
256
+ const j = content.indexOf(end, i + begin.length);
257
+ if (j === -1) {
258
+ return { ok: false, reason: "end not found" };
259
+ }
260
+ const afterEnd = j + end.length;
261
+ let next = content.slice(0, i) + content.slice(afterEnd);
262
+ next = next.replace(/\n{3,}/g, "\n\n").replace(/^\n+/, "").trimEnd();
263
+ if (next.length) {
264
+ next += "\n";
265
+ }
266
+ return { ok: true, content: next };
267
+ }
268
+
269
+ /**
270
+ * Strip `deuk-agent-rule-cursorrules` (or custom markers) region from `.cursorrules`.
271
+ * @param {{ cwd: string, markers: { begin: string, end: string }, dryRun?: boolean, backup?: boolean }} opts
272
+ */
273
+ export function removeCursorrules(opts) {
274
+ const { cwd, markers, dryRun, backup } = opts;
275
+ const targetPath = join(cwd, ".cursorrules");
276
+ if (!existsSync(targetPath)) {
277
+ return { action: "skip", reason: "no file" };
278
+ }
279
+ const existing = readFileSync(targetPath, "utf8");
280
+ const result = removeTaggedBlock(existing, markers.begin, markers.end);
281
+ if (!result.ok) {
282
+ return { action: "skip", path: targetPath, reason: result.reason };
283
+ }
284
+ if (dryRun) {
285
+ return { action: "would-write", path: targetPath, mode: "remove-tagged" };
286
+ }
287
+ if (backup && existsSync(targetPath)) {
288
+ copyFileSync(targetPath, targetPath + ".bak");
289
+ }
290
+ if (result.content === "") {
291
+ unlinkSync(targetPath);
292
+ return { action: "delete", path: targetPath, mode: "remove-tagged" };
293
+ }
294
+ writeFileSync(targetPath, result.content, "utf8");
295
+ return { action: "write", path: targetPath, mode: "remove-tagged" };
296
+ }
297
+
298
+ /**
299
+ * @param {{
300
+ * bundleRoot: string,
301
+ * cwd: string,
302
+ * markers: { begin: string, end: string },
303
+ * cursorrulesMode: "skip" | "inject" | "overwrite",
304
+ * dryRun?: boolean,
305
+ * backup?: boolean,
306
+ * }} opts
307
+ */
308
+ export function applyCursorrules(opts) {
309
+ const { bundleRoot, cwd, markers, cursorrulesMode, dryRun, backup } = opts;
310
+ const raw = readBundleCursorrules(bundleRoot);
311
+ if (!raw) {
312
+ return { action: "skip", reason: "bundle .cursorrules missing" };
313
+ }
314
+ const inner = raw.trimEnd();
315
+ const targetPath = join(cwd, ".cursorrules");
316
+
317
+ if (cursorrulesMode === "skip") {
318
+ return { action: "skip", reason: "cursorrules mode skip" };
319
+ }
320
+
321
+ const writeTaggedOnly = (bodyInner) =>
322
+ markers.begin + "\n\n" + bodyInner + "\n\n" + markers.end + "\n";
323
+
324
+ if (cursorrulesMode === "overwrite") {
325
+ const next = writeTaggedOnly(inner);
326
+ if (dryRun) {
327
+ return { action: "would-write", path: targetPath, mode: "overwrite" };
328
+ }
329
+ if (backup && existsSync(targetPath)) {
330
+ copyFileSync(targetPath, targetPath + ".bak");
331
+ }
332
+ writeFileSync(targetPath, next, "utf8");
333
+ return { action: "write", path: targetPath, mode: "overwrite" };
334
+ }
335
+
336
+ // inject: update tagged region, or prepend tagged block above existing content
337
+ const existing = existsSync(targetPath) ? readFileSync(targetPath, "utf8") : "";
338
+ const region = findMarkerRegion(existing, markers.begin, markers.end);
339
+
340
+ if (region) {
341
+ const next =
342
+ existing.slice(0, region.innerStart) + "\n" + inner + "\n" + existing.slice(region.innerEnd);
343
+ if (dryRun) {
344
+ return { action: "would-write", path: targetPath, mode: "inject-region" };
345
+ }
346
+ if (backup && existsSync(targetPath)) {
347
+ copyFileSync(targetPath, targetPath + ".bak");
348
+ }
349
+ writeFileSync(targetPath, next, "utf8");
350
+ return { action: "write", path: targetPath, mode: "inject-region" };
351
+ }
352
+
353
+ const block = writeTaggedOnly(inner) + "\n";
354
+ const rest = existing.replace(/^\uFEFF/, "").trimStart();
355
+ const next = rest.length ? block + rest : block.trimEnd() + "\n";
356
+
357
+ if (dryRun) {
358
+ return { action: "would-write", path: targetPath, mode: "prepend-tagged" };
359
+ }
360
+ if (backup && existsSync(targetPath)) {
361
+ copyFileSync(targetPath, targetPath + ".bak");
362
+ }
363
+ writeFileSync(targetPath, next, "utf8");
364
+ return { action: "write", path: targetPath, mode: "prepend-tagged" };
365
+ }
@@ -1,44 +1,50 @@
1
- import {
2
- copyFileSync,
3
- existsSync,
4
- mkdirSync,
5
- readFileSync,
6
- readdirSync,
7
- rmSync,
8
- writeFileSync,
9
- } from "fs";
10
- import { join, dirname } from "path";
11
- import { fileURLToPath } from "url";
12
-
13
- const __dirname = dirname(fileURLToPath(import.meta.url));
14
- const pkgRoot = join(__dirname, "..");
15
-
16
- /** Public npm bundle: generic template only (no monorepo paths or internal handoffs). */
17
- const publishDir = join(pkgRoot, "publish");
18
- const publishRulesDir = join(publishDir, "rules");
19
- const rulesDest = join(pkgRoot, "bundle", "rules");
20
- const agentsSrc = join(publishDir, "AGENTS.md");
21
- const agentsDest = join(pkgRoot, "bundle", "AGENTS.md");
22
-
23
- if (!existsSync(publishDir)) {
24
- throw new Error("Missing publish template dir: " + publishDir);
25
- }
26
- if (!existsSync(publishRulesDir)) {
27
- throw new Error("Missing publish/rules: " + publishRulesDir);
28
- }
29
- if (!existsSync(agentsSrc)) {
30
- throw new Error("Missing publish/AGENTS.md: " + agentsSrc);
31
- }
32
-
33
- if (existsSync(rulesDest)) {
34
- rmSync(rulesDest, { recursive: true });
35
- }
36
- mkdirSync(rulesDest, { recursive: true });
37
- for (const name of readdirSync(publishRulesDir)) {
38
- if (!name.endsWith(".mdc")) continue;
39
- copyFileSync(join(publishRulesDir, name), join(rulesDest, name));
40
- }
41
-
42
- const agentsBody = readFileSync(agentsSrc, "utf8");
43
- writeFileSync(agentsDest, agentsBody, "utf8");
44
- console.log("deuk-agent-rule: synced bundle from publish/ template.");
1
+ import {
2
+ copyFileSync,
3
+ existsSync,
4
+ mkdirSync,
5
+ readFileSync,
6
+ readdirSync,
7
+ rmSync,
8
+ writeFileSync,
9
+ } from "fs";
10
+ import { join, dirname } from "path";
11
+ import { fileURLToPath } from "url";
12
+
13
+ const __dirname = dirname(fileURLToPath(import.meta.url));
14
+ const pkgRoot = join(__dirname, "..");
15
+
16
+ /** Copy publish/ templates into bundle/ for npm packaging. */
17
+ const publishDir = join(pkgRoot, "publish");
18
+ const publishRulesDir = join(publishDir, "rules");
19
+ const rulesDest = join(pkgRoot, "bundle", "rules");
20
+ const agentsSrc = join(publishDir, "AGENTS.md");
21
+ const agentsDest = join(pkgRoot, "bundle", "AGENTS.md");
22
+ const cursorrulesSrc = join(publishDir, ".cursorrules");
23
+ const cursorrulesDest = join(pkgRoot, "bundle", ".cursorrules");
24
+
25
+ if (!existsSync(publishDir)) {
26
+ throw new Error("Missing publish template dir: " + publishDir);
27
+ }
28
+ if (!existsSync(publishRulesDir)) {
29
+ throw new Error("Missing publish/rules: " + publishRulesDir);
30
+ }
31
+ if (!existsSync(agentsSrc)) {
32
+ throw new Error("Missing publish/AGENTS.md: " + agentsSrc);
33
+ }
34
+ if (!existsSync(cursorrulesSrc)) {
35
+ throw new Error("Missing publish/.cursorrules: " + cursorrulesSrc);
36
+ }
37
+
38
+ if (existsSync(rulesDest)) {
39
+ rmSync(rulesDest, { recursive: true });
40
+ }
41
+ mkdirSync(rulesDest, { recursive: true });
42
+ for (const name of readdirSync(publishRulesDir)) {
43
+ if (!name.endsWith(".mdc")) continue;
44
+ copyFileSync(join(publishRulesDir, name), join(rulesDest, name));
45
+ }
46
+
47
+ const agentsBody = readFileSync(agentsSrc, "utf8");
48
+ writeFileSync(agentsDest, agentsBody, "utf8");
49
+ copyFileSync(cursorrulesSrc, cursorrulesDest);
50
+ console.log("deuk-agent-rule: synced bundle from publish/ template.");