deuk-agent-rule 2.5.13 → 3.3.2
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/CHANGELOG.ko.md +74 -0
- package/CHANGELOG.md +138 -316
- package/README.ko.md +134 -154
- package/README.md +121 -153
- package/package.json +29 -7
- package/scripts/cli-args.mjs +87 -3
- package/scripts/cli-init-commands.mjs +1382 -223
- package/scripts/cli-init-logic.mjs +28 -16
- package/scripts/cli-prompts.mjs +13 -4
- package/scripts/cli-rule-compiler.mjs +44 -34
- package/scripts/cli-skill-commands.mjs +172 -0
- package/scripts/cli-telemetry-commands.mjs +429 -0
- package/scripts/cli-ticket-commands.mjs +1934 -161
- package/scripts/cli-ticket-index.mjs +298 -0
- package/scripts/cli-ticket-migration.mjs +320 -0
- package/scripts/cli-ticket-parser.mjs +207 -0
- package/scripts/cli-utils.mjs +381 -59
- package/scripts/cli.mjs +99 -19
- package/scripts/lint-md.mjs +247 -0
- package/scripts/lint-rules.mjs +143 -0
- package/scripts/merge-logic.mjs +13 -306
- package/scripts/plan-parser.mjs +53 -0
- package/templates/MODULE_RULE_TEMPLATE.md +11 -0
- package/templates/PROJECT_RULE.md +47 -0
- package/templates/TICKET_TEMPLATE.ko.md +21 -0
- package/templates/TICKET_TEMPLATE.md +21 -0
- package/templates/rules.d/deukcontext-mcp.md +31 -0
- package/templates/rules.d/platform-coexistence.md +29 -0
- package/templates/skills/context-recall/SKILL.md +25 -0
- package/templates/skills/generated-file-guard/SKILL.md +25 -0
- package/templates/skills/safe-refactor/SKILL.md +25 -0
- package/bundle/.cursorrules +0 -11
- package/bundle/AGENTS.md +0 -146
- package/bundle/gemini.md +0 -26
- package/bundle/rules/delivery-and-parallel-work.mdc +0 -26
- package/bundle/rules/git-commit.mdc +0 -24
- package/bundle/rules/multi-ai-workflow.mdc +0 -104
- package/bundle/rules.d/core-workflow.md +0 -48
- package/bundle/rules.d/deukrag-mcp.md +0 -37
- package/bundle/templates/MODULE_RULE_TEMPLATE.md +0 -24
- package/bundle/templates/TICKET_TEMPLATE.md +0 -58
- package/scripts/cli-ticket-logic.mjs +0 -568
- package/scripts/sync-bundle.mjs +0 -77
- package/scripts/sync-oss.mjs +0 -126
package/scripts/merge-logic.mjs
CHANGED
|
@@ -1,258 +1,35 @@
|
|
|
1
1
|
import {
|
|
2
|
-
copyFileSync,
|
|
3
2
|
existsSync,
|
|
4
|
-
mkdirSync,
|
|
5
3
|
readFileSync,
|
|
6
|
-
readdirSync,
|
|
7
|
-
unlinkSync,
|
|
8
|
-
writeFileSync,
|
|
9
4
|
} from "fs";
|
|
10
5
|
import { join } from "path";
|
|
11
6
|
|
|
12
|
-
export const DEFAULT_TAG = "
|
|
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
|
-
}
|
|
7
|
+
export const DEFAULT_TAG = "DeukAgentRules";
|
|
226
8
|
|
|
227
9
|
export function readBundleAgents(bundleRoot) {
|
|
228
|
-
const p = join(bundleRoot, "AGENTS.md");
|
|
10
|
+
const p = join(bundleRoot, "core-rules", "AGENTS.md");
|
|
229
11
|
if (!existsSync(p)) {
|
|
230
12
|
throw new Error("Bundle AGENTS.md missing: " + p);
|
|
231
13
|
}
|
|
232
14
|
return readFileSync(p, "utf8");
|
|
233
15
|
}
|
|
234
16
|
|
|
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
17
|
export function removeTaggedBlock(content, begin, end) {
|
|
252
18
|
const i = content.indexOf(begin);
|
|
253
19
|
if (i === -1) {
|
|
254
20
|
return { ok: false, reason: "begin not found" };
|
|
255
21
|
}
|
|
22
|
+
if (end === null) {
|
|
23
|
+
let blockStart = i;
|
|
24
|
+
const prevText = content.slice(0, i);
|
|
25
|
+
const hrIndex = prevText.lastIndexOf("---");
|
|
26
|
+
if (hrIndex !== -1 && prevText.slice(hrIndex).trim() === "") {
|
|
27
|
+
blockStart = hrIndex;
|
|
28
|
+
}
|
|
29
|
+
let next = content.slice(0, blockStart).trimEnd() + "\n";
|
|
30
|
+
return { ok: true, content: next };
|
|
31
|
+
}
|
|
32
|
+
|
|
256
33
|
const j = content.indexOf(end, i + begin.length);
|
|
257
34
|
if (j === -1) {
|
|
258
35
|
return { ok: false, reason: "end not found" };
|
|
@@ -265,73 +42,3 @@ export function removeTaggedBlock(content, begin, end) {
|
|
|
265
42
|
}
|
|
266
43
|
return { ok: true, content: next };
|
|
267
44
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
/**
|
|
271
|
-
* @param {{
|
|
272
|
-
* bundleRoot: string,
|
|
273
|
-
* cwd: string,
|
|
274
|
-
* markers: { begin: string, end: string },
|
|
275
|
-
* cursorrulesMode: "skip" | "inject" | "overwrite",
|
|
276
|
-
* dryRun?: boolean,
|
|
277
|
-
* backup?: boolean,
|
|
278
|
-
* }} opts
|
|
279
|
-
*/
|
|
280
|
-
export function applyCursorrules(opts) {
|
|
281
|
-
const { bundleRoot, cwd, markers, cursorrulesMode, dryRun, backup } = opts;
|
|
282
|
-
const raw = readBundleCursorrules(bundleRoot);
|
|
283
|
-
if (!raw) {
|
|
284
|
-
return { action: "skip", reason: "bundle .cursorrules missing" };
|
|
285
|
-
}
|
|
286
|
-
const inner = raw.trimEnd();
|
|
287
|
-
const targetPath = join(cwd, ".cursorrules");
|
|
288
|
-
|
|
289
|
-
if (cursorrulesMode === "skip") {
|
|
290
|
-
return { action: "skip", reason: "cursorrules mode skip" };
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
const writeTaggedOnly = (bodyInner) =>
|
|
294
|
-
markers.begin + "\n\n" + bodyInner + "\n\n" + markers.end + "\n";
|
|
295
|
-
|
|
296
|
-
if (cursorrulesMode === "overwrite") {
|
|
297
|
-
const next = writeTaggedOnly(inner);
|
|
298
|
-
if (dryRun) {
|
|
299
|
-
return { action: "would-write", path: targetPath, mode: "overwrite" };
|
|
300
|
-
}
|
|
301
|
-
if (backup && existsSync(targetPath)) {
|
|
302
|
-
copyFileSync(targetPath, targetPath + ".bak");
|
|
303
|
-
}
|
|
304
|
-
writeFileSync(targetPath, next, "utf8");
|
|
305
|
-
return { action: "write", path: targetPath, mode: "overwrite" };
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
// inject: update tagged region, or prepend tagged block above existing content
|
|
309
|
-
const existing = existsSync(targetPath) ? readFileSync(targetPath, "utf8") : "";
|
|
310
|
-
const region = findMarkerRegion(existing, markers.begin, markers.end);
|
|
311
|
-
|
|
312
|
-
if (region) {
|
|
313
|
-
const next =
|
|
314
|
-
existing.slice(0, region.innerStart) + "\n" + inner + "\n" + existing.slice(region.innerEnd);
|
|
315
|
-
if (dryRun) {
|
|
316
|
-
return { action: "would-write", path: targetPath, mode: "inject-region" };
|
|
317
|
-
}
|
|
318
|
-
if (backup && existsSync(targetPath)) {
|
|
319
|
-
copyFileSync(targetPath, targetPath + ".bak");
|
|
320
|
-
}
|
|
321
|
-
writeFileSync(targetPath, next, "utf8");
|
|
322
|
-
return { action: "write", path: targetPath, mode: "inject-region" };
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
const block = writeTaggedOnly(inner) + "\n";
|
|
326
|
-
const rest = existing.replace(/^\uFEFF/, "").trimStart();
|
|
327
|
-
const next = rest.length ? block + rest : block.trimEnd() + "\n";
|
|
328
|
-
|
|
329
|
-
if (dryRun) {
|
|
330
|
-
return { action: "would-write", path: targetPath, mode: "prepend-tagged" };
|
|
331
|
-
}
|
|
332
|
-
if (backup && existsSync(targetPath)) {
|
|
333
|
-
copyFileSync(targetPath, targetPath + ".bak");
|
|
334
|
-
}
|
|
335
|
-
writeFileSync(targetPath, next, "utf8");
|
|
336
|
-
return { action: "write", path: targetPath, mode: "prepend-tagged" };
|
|
337
|
-
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic Markdown Plan Parser
|
|
3
|
+
*
|
|
4
|
+
* Supports various AI Agent Plan formats (Antigravity, Copilot, etc.)
|
|
5
|
+
* Extracts basic ticket fields (title, summary, tasks) and body.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export function parseGenericMarkdownPlan(content) {
|
|
9
|
+
const lines = content.split('\n');
|
|
10
|
+
|
|
11
|
+
// 1. Title: First H1
|
|
12
|
+
const h1Line = lines.find(l => /^# /.test(l));
|
|
13
|
+
const title = h1Line ? h1Line.replace(/^# /, '').trim() : '';
|
|
14
|
+
|
|
15
|
+
// 2. Summary: Text between H1 and the first --- or ##
|
|
16
|
+
const h1Idx = h1Line ? lines.indexOf(h1Line) : -1;
|
|
17
|
+
let summaryLines = [];
|
|
18
|
+
for (let i = h1Idx + 1; i < lines.length; i++) {
|
|
19
|
+
if (/^---$/.test(lines[i].trim()) || /^## /.test(lines[i])) break;
|
|
20
|
+
if (lines[i].trim()) summaryLines.push(lines[i].trim());
|
|
21
|
+
}
|
|
22
|
+
const summary = summaryLines.join(' ').slice(0, 200);
|
|
23
|
+
|
|
24
|
+
// 3. Tasks: Collect all checklist items
|
|
25
|
+
const tasks = lines
|
|
26
|
+
.filter(l => /^\s*- \[[ x/]\]/.test(l))
|
|
27
|
+
.map(l => l.replace(/^\s*- \[[ x/]\]\s*/, '').trim());
|
|
28
|
+
|
|
29
|
+
return { title, summary, tasks, body: content };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Detect agent type from plan path
|
|
34
|
+
*/
|
|
35
|
+
export function detectAgentFromPath(planPath) {
|
|
36
|
+
if (planPath.includes('.gemini')) return 'antigravity';
|
|
37
|
+
if (planPath.includes('copilot') || planPath.includes('.codex')) return 'copilot';
|
|
38
|
+
if (planPath.includes('.cursor')) return 'cursor';
|
|
39
|
+
if (planPath.includes('.claude')) return 'claude';
|
|
40
|
+
return 'generic';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const PLAN_PARSERS = {
|
|
44
|
+
'antigravity': parseGenericMarkdownPlan,
|
|
45
|
+
'copilot': parseGenericMarkdownPlan,
|
|
46
|
+
'generic': parseGenericMarkdownPlan
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export function parsePlan(planPath, content) {
|
|
50
|
+
const agent = detectAgentFromPath(planPath);
|
|
51
|
+
const parser = PLAN_PARSERS[agent] || parseGenericMarkdownPlan;
|
|
52
|
+
return parser(content);
|
|
53
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
architecture_docs: ""
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Project Rules
|
|
6
|
+
|
|
7
|
+
## Architecture Boundaries
|
|
8
|
+
|
|
9
|
+
> [!NOTE] **To AI Agents**
|
|
10
|
+
> If `architecture_docs` is empty and no rules are defined below,
|
|
11
|
+
> this project has no architecture rules yet.
|
|
12
|
+
> → Ask user to define them, or analyze codebase and propose a draft.
|
|
13
|
+
> → Do NOT make assumptions about project architecture.
|
|
14
|
+
|
|
15
|
+
### Module Ownership
|
|
16
|
+
<!-- Define which modules exist and who owns them -->
|
|
17
|
+
| Module | Owner | Editable |
|
|
18
|
+
|--------|-------|----------|
|
|
19
|
+
| `src/` | Core source | Yes |
|
|
20
|
+
|
|
21
|
+
### Dependency Direction
|
|
22
|
+
<!-- Define allowed dependency directions between modules -->
|
|
23
|
+
|
|
24
|
+
## DC-CODEGEN: Generated File Mapping
|
|
25
|
+
|
|
26
|
+
> [!IMPORTANT] **REQUIRED**
|
|
27
|
+
> Agents use this table to decide which files are safe to edit.
|
|
28
|
+
> Files not listed here → agent MUST ask user before editing.
|
|
29
|
+
|
|
30
|
+
| Generated (DO NOT EDIT) | Source (edit here) | Build command |
|
|
31
|
+
|-------------------------|-------------------|---------------|
|
|
32
|
+
| `dist/` | `src/` | `npm run build` |
|
|
33
|
+
|
|
34
|
+
## DC-* Guards (project-specific)
|
|
35
|
+
|
|
36
|
+
| Guard | Condition → Action |
|
|
37
|
+
|-------|-------------------|
|
|
38
|
+
| DC-HALT | Infrastructure error → stop, no bypass, report to user. |
|
|
39
|
+
| DC-INFRA | Bootstrap/transport/DB/routing code → separate ticket + approval. |
|
|
40
|
+
|
|
41
|
+
## Build & Test
|
|
42
|
+
|
|
43
|
+
| Action | Command |
|
|
44
|
+
|--------|---------|
|
|
45
|
+
| Build | `npm run build` |
|
|
46
|
+
| Test | `npm test` |
|
|
47
|
+
| Lint | `npx deuk-agent-rule lint:md` |
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
<%- frontmatter %>
|
|
3
|
+
---
|
|
4
|
+
# <%= meta.title %>
|
|
5
|
+
|
|
6
|
+
## Compact Plan
|
|
7
|
+
|
|
8
|
+
- **찾은 점:** 이 티켓이 다루는 문제를 한두 줄로 적습니다.
|
|
9
|
+
- **방향:** 가장 작은 수정으로 해결하고, 큰 범위는 다른 티켓으로 분리합니다.
|
|
10
|
+
- **검증:** 실패 가능성을 잘 드러내는 최소 검증만 적습니다.
|
|
11
|
+
- **메모:** 상세 근거는 꼭 필요한 곳에만 둡니다.
|
|
12
|
+
|
|
13
|
+
## Tasks
|
|
14
|
+
|
|
15
|
+
- [ ] 계획 정리
|
|
16
|
+
- [ ] 변경 적용
|
|
17
|
+
- [ ] 검증 기록
|
|
18
|
+
|
|
19
|
+
## Done When
|
|
20
|
+
|
|
21
|
+
- 티켓이 짧고, 바뀐 점과 실패 가능성이 분명하다.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
<%- frontmatter %>
|
|
3
|
+
---
|
|
4
|
+
# <%= meta.title %>
|
|
5
|
+
|
|
6
|
+
## Compact Plan
|
|
7
|
+
|
|
8
|
+
- **Finding:** Keep the ticket focused on the concrete problem and the minimum change needed.
|
|
9
|
+
- **Approach:** Use the narrowest safe fix; split new scope into another ticket.
|
|
10
|
+
- **Verification:** Record the smallest check that exposes the main risk, not a success parade.
|
|
11
|
+
- **Notes:** Put detailed evidence only where the problem actually needs it.
|
|
12
|
+
|
|
13
|
+
## Tasks
|
|
14
|
+
|
|
15
|
+
- [ ] Complete compact plan.
|
|
16
|
+
- [ ] Execute changes.
|
|
17
|
+
- [ ] Record verification outcome.
|
|
18
|
+
|
|
19
|
+
## Done When
|
|
20
|
+
|
|
21
|
+
- Ticket is scoped, implemented, and verified.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: deukcontext-mcp
|
|
3
|
+
condition:
|
|
4
|
+
mcp: deuk-agent-context
|
|
5
|
+
inject_target: ["AGENTS.md", "GEMINI.md"]
|
|
6
|
+
---
|
|
7
|
+
## DeukContext RAG Protocol
|
|
8
|
+
|
|
9
|
+
### RAG Usage
|
|
10
|
+
- Start with local source-of-truth files: current source code, tests, project rules, and CLI ticket state.
|
|
11
|
+
- Use `mcp_deukcontext_search_*` when local evidence is insufficient, prior decisions may matter, the task crosses old tickets, or the user asks for historical/deep analysis.
|
|
12
|
+
- Use RAG as advisory memory. Current source code, tests, and ticket state remain the source of truth.
|
|
13
|
+
- Treat DeukAgentContext as an online-only memory layer. Do not rely on offline snapshots or local mirrors as the primary context source.
|
|
14
|
+
- Choose the narrowest MCP tool: `search_code` for symbols, `search_rules` for policy, `search_tickets` for prior outcomes, and `synthesize_knowledge` only for cross-collection questions.
|
|
15
|
+
- Search narrowly: include the concrete project plus symbol, file, command, rule id, or failure mode.
|
|
16
|
+
- Stop after 2 MCP calls for the same question. Do not broaden repeatedly.
|
|
17
|
+
- **Do NOT use RAG for Ticket Navigation.** Ticket lookup is a direct CLI operation, not a search task.
|
|
18
|
+
|
|
19
|
+
### RAG Quality Gate
|
|
20
|
+
- Treat placeholder summaries, duplicate ticket/report chunks, stale archive-only hits, unrelated projects, or summaries with no usable fact as a miss.
|
|
21
|
+
- When a result only says to read the file, read the current file locally before deciding.
|
|
22
|
+
- Prefer `search_code` for implementation questions and request evidence only when the extra context is likely to be useful.
|
|
23
|
+
- Record hit/weak-hit/miss/stale evidence in the ticket when it changes confidence, plan, or follow-up direction.
|
|
24
|
+
- For investigation, regression, quality, or root-cause tasks, write confirmed facts, hypotheses, improvement direction, and open questions into the active ticket before asking the user for clarification. Then point the user to the ticket instead of keeping the analysis only in chat.
|
|
25
|
+
|
|
26
|
+
### RAG Failure Handling
|
|
27
|
+
- **Error** (2+ failures): Switch to local search (`grep_search`/`view_file`). Do not retry in a loop.
|
|
28
|
+
- **Miss or weak hit** (`[RAG-MISS]`, placeholder, duplicate, stale): Fall back to local search. If local analysis finds reusable current-code knowledge, inject it once via `mcp_deukcontext_add_knowledge` with project, source path, code status, and applicability.
|
|
29
|
+
- **Stale indexed doc**: If an indexed document is wrong or incomplete, use `mcp_deukcontext_refresh_document` instead of adding a competing fragment.
|
|
30
|
+
- **Archive boundary**: Completed work should be preserved through ticket archive and knowledge distillation so the active context does not accumulate stale live-state references.
|
|
31
|
+
- **Docs vs knowledge**: `.deuk-agent/docs/` holds human-readable source artifacts (all plans and reports under `docs/plan`, archived originals in `docs/archive`). `.deuk-agent/knowledge/` holds only distilled machine-readable retrieval JSON generated from archived tickets/plans.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: platform-coexistence
|
|
3
|
+
inject_target: ["AGENTS.md"]
|
|
4
|
+
---
|
|
5
|
+
## Platform MCP Bridge
|
|
6
|
+
|
|
7
|
+
### MCP Configuration per Platform
|
|
8
|
+
|
|
9
|
+
Each platform uses a different MCP config file. Ensure `deuk-agent-context` is registered in the appropriate location:
|
|
10
|
+
|
|
11
|
+
| Platform | MCP Config Path | Registration |
|
|
12
|
+
|----------|----------------|--------------|
|
|
13
|
+
| Antigravity | `.mcp.json` (project root) | Auto-detected by IDE |
|
|
14
|
+
| Claude Code | `.mcp.json` (project root) | `claude mcp add --transport sse deuk-agent-context <url>` |
|
|
15
|
+
| Copilot | `.vscode/mcp.json` | VS Code settings or manual JSON edit |
|
|
16
|
+
| Codex | `.mcp.json` (project root) | Manual JSON edit |
|
|
17
|
+
| Cursor | `.cursor/mcp.json` | Cursor Settings > MCP |
|
|
18
|
+
|
|
19
|
+
### Artifact Path Self-Check
|
|
20
|
+
|
|
21
|
+
Before saving any artifact, verify the platform's native path is NOT the only copy:
|
|
22
|
+
|
|
23
|
+
| Platform Native Path | RAG-Indexed? | Action Required |
|
|
24
|
+
|---------------------|:------------:|-----------------|
|
|
25
|
+
| `brain/<conv>/` (Antigravity) | NO | Copy to `.deuk-agent/docs/` |
|
|
26
|
+
| `.cursor/plans/` (Cursor) | NO | Copy to `.deuk-agent/docs/plan/` |
|
|
27
|
+
| Terminal output (Claude/Codex) | NO | Save directly to `.deuk-agent/docs/` |
|
|
28
|
+
| VS Code diff (Copilot) | NO | Save plan text to `.deuk-agent/docs/plan/` |
|
|
29
|
+
| `.deuk-agent/docs/` | **YES** | ✓ Correct path |
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: context-recall
|
|
3
|
+
summary: Reuse prior ticket and rule memory without turning RAG into the source of truth.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Context Recall
|
|
7
|
+
|
|
8
|
+
Authority: follow `core-rules/AGENTS.md`, the active ticket APC, Phase Gate, and `PROJECT_RULE.md`.
|
|
9
|
+
|
|
10
|
+
Use this skill when a task repeats a failure family, references prior decisions, asks why something happened, or crosses old tickets/rules.
|
|
11
|
+
|
|
12
|
+
## Micro-Protocol
|
|
13
|
+
|
|
14
|
+
1. Read current local source, project rules, and active ticket first.
|
|
15
|
+
2. Ask DeukAgentContext one narrow query naming the project, symbol, file, command, or failure mode.
|
|
16
|
+
3. Treat stale, duplicate, placeholder, or unrelated hits as weak evidence.
|
|
17
|
+
4. Put useful recall into the ticket as evidence, not chat narration.
|
|
18
|
+
5. If local analysis produces reusable current knowledge after a miss, add one knowledge record.
|
|
19
|
+
|
|
20
|
+
## Stop Conditions
|
|
21
|
+
|
|
22
|
+
- Two weak or stale recall attempts for the same question.
|
|
23
|
+
- RAG conflicts with current local source.
|
|
24
|
+
- Recall would expand scope beyond the active ticket.
|
|
25
|
+
- The remembered fix path bypasses current generated/source, parity, or verification rules.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: generated-file-guard
|
|
3
|
+
summary: Prevent direct generated artifact edits and route changes to their source.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Generated File Guard
|
|
7
|
+
|
|
8
|
+
Authority: follow `core-rules/AGENTS.md`, the active ticket APC, Phase Gate, and `PROJECT_RULE.md`.
|
|
9
|
+
|
|
10
|
+
Use this skill when a task mentions generated files, `dist`, `gen`, codegen, reports, benchmark outputs, or synchronized spokes.
|
|
11
|
+
|
|
12
|
+
## Micro-Protocol
|
|
13
|
+
|
|
14
|
+
1. Check `PROJECT_RULE.md` generated/source mapping.
|
|
15
|
+
2. Check target files for `@generated`, `DO NOT EDIT`, generated directories, or report artifact paths.
|
|
16
|
+
3. If the target is generated, identify the source file or generator command.
|
|
17
|
+
4. Prefer dry-run or `/tmp` output when probing generators.
|
|
18
|
+
5. Record the source-of-truth owner and verification command in the ticket before edits.
|
|
19
|
+
|
|
20
|
+
## Stop Conditions
|
|
21
|
+
|
|
22
|
+
- The source-of-truth file cannot be identified after bounded lookup.
|
|
23
|
+
- A broad regeneration would modify outputs outside ticket scope.
|
|
24
|
+
- The proposed fix hides, relabels, or narrows failing generated/report rows.
|
|
25
|
+
- The change would edit both generated output and source in the same patch.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: safe-refactor
|
|
3
|
+
summary: Keep refactors small, scoped, and test-backed inside DeukAgentRules TDW.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Safe Refactor
|
|
7
|
+
|
|
8
|
+
Authority: follow `core-rules/AGENTS.md`, the active ticket APC, Phase Gate, and `PROJECT_RULE.md`.
|
|
9
|
+
|
|
10
|
+
Use this skill when the user asks for refactor, cleanup, simplification, or restructuring.
|
|
11
|
+
|
|
12
|
+
## Micro-Protocol
|
|
13
|
+
|
|
14
|
+
1. Confirm the active ticket allows refactoring of the target module.
|
|
15
|
+
2. Name the smallest behavior-preserving change that satisfies the request.
|
|
16
|
+
3. Inspect current tests or the smallest verification command before editing.
|
|
17
|
+
4. Avoid unrelated renames, formatting sweeps, dependency changes, and style churn.
|
|
18
|
+
5. After edits, run the narrowest relevant test or lint gate and record the result in the ticket.
|
|
19
|
+
|
|
20
|
+
## Stop Conditions
|
|
21
|
+
|
|
22
|
+
- No active ticket or APC boundary for the refactor.
|
|
23
|
+
- Target crosses unrelated modules.
|
|
24
|
+
- The feature has no meaningful verification path.
|
|
25
|
+
- The change would alter generated output or shared interfaces without explicit approval.
|
package/bundle/.cursorrules
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
<!-- deuk-agent-rule-cursorrules:begin -->
|
|
2
|
-
# 💎 RAG-FIRST HARD LOCK IS AUTHORITATIVE
|
|
3
|
-
|
|
4
|
-
For Antigravity (Gemini), the **RAG-FIRST ACTION [ACTION 0]** and **CONTINUOUS RAG POLICY** defined in **AGENTS.md** and **gemini.md** are the absolute Single Source of Truth.
|
|
5
|
-
|
|
6
|
-
1. **[ABSOLUTE PRIORITY]**: Every request MUST begin with `mcp_deukrag_search_*`.
|
|
7
|
-
2. **[gemini.md]**: You MUST read and follow the specific hard locks in `gemini.md` at the root.
|
|
8
|
-
3. **[TICKET SYSTEM]**: Follow the TDD workflow in `AGENTS.md` strictly.
|
|
9
|
-
|
|
10
|
-
Do not skip RAG research by relying on local file reads (`view_file`, `grep_search`). Local files are for details; RAG is for architectural truth and zero-regression.
|
|
11
|
-
<!-- deuk-agent-rule-cursorrules:end -->
|