deuk-agent-flow 4.2.7 → 5.0.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.
Files changed (80) hide show
  1. package/CHANGELOG.ko.md +259 -0
  2. package/CHANGELOG.md +769 -124
  3. package/LICENSE +0 -0
  4. package/README.ko.md +97 -17
  5. package/README.md +108 -25
  6. package/bin/deuk-agent-flow.js +11 -13
  7. package/bin/deuk-agent-rule.js +1 -1
  8. package/bundled/README.md +3 -0
  9. package/bundled/deuk-agent-flow.vsix +0 -0
  10. package/core-rules/AGENTS.md +30 -120
  11. package/docs/architecture.ko.md +155 -2
  12. package/docs/architecture.md +155 -2
  13. package/docs/assets/agentflow-panel-skills.png +0 -0
  14. package/docs/assets/agentflow-panel.png +0 -0
  15. package/docs/how-it-works.ko.md +109 -52
  16. package/docs/how-it-works.md +128 -71
  17. package/docs/principles.ko.md +68 -68
  18. package/docs/principles.md +68 -68
  19. package/docs/usage-guide.ko.md +251 -212
  20. package/package.json +42 -45
  21. package/scripts/bundle-vscode-vsix.ts +67 -0
  22. package/scripts/{cli-args.mjs → cli-args.ts} +49 -12
  23. package/scripts/cli-init-commands.ts +99 -0
  24. package/scripts/cli-init-logic.ts +46 -0
  25. package/scripts/{cli-prompts.mjs → cli-prompts.ts} +19 -34
  26. package/scripts/{cli-rule-compiler.mjs → cli-rule-compiler.ts} +128 -112
  27. package/scripts/cli-skill-commands.ts +707 -0
  28. package/scripts/{cli-telemetry-commands.mjs → cli-telemetry-commands.ts} +25 -22
  29. package/scripts/cli-ticket-command-shared.ts +4 -0
  30. package/scripts/cli-ticket-commands.ts +3723 -0
  31. package/scripts/cli-ticket-index.ts +283 -0
  32. package/scripts/{cli-ticket-migration.mjs → cli-ticket-migration.ts} +44 -68
  33. package/scripts/cli-ticket-parser.ts +100 -0
  34. package/scripts/{cli-usage-commands.mjs → cli-usage-commands.ts} +325 -326
  35. package/scripts/cli-utils.ts +1560 -0
  36. package/scripts/cli.ts +695 -0
  37. package/scripts/{lint-md.mjs → lint-md.ts} +32 -42
  38. package/scripts/lint-rules.ts +203 -0
  39. package/scripts/{merge-logic.mjs → merge-logic.ts} +44 -44
  40. package/scripts/{plan-parser.mjs → plan-parser.ts} +53 -53
  41. package/templates/MODULE_RULE_TEMPLATE.md +11 -11
  42. package/templates/PROJECT_RULE.md +46 -47
  43. package/templates/TICKET_TEMPLATE.ko.md +48 -44
  44. package/templates/TICKET_TEMPLATE.md +48 -44
  45. package/templates/project-memory.md +19 -0
  46. package/templates/project-pilot/CONFORMANCE_GATE_TEMPLATE.md +25 -25
  47. package/templates/project-pilot/DRIFT_CHECKLIST.md +27 -27
  48. package/templates/project-pilot/FLOW_CONTRACT_TEMPLATE.md +26 -26
  49. package/templates/project-pilot/IMPLEMENTATION_MATRIX_TEMPLATE.md +30 -30
  50. package/templates/project-pilot/INTEGRATION_CONTRACT_TEMPLATE.md +26 -26
  51. package/templates/project-pilot/OWNER_MAP_TEMPLATE.md +15 -15
  52. package/templates/project-pilot/PROJECT_PILOT_RULE_TEMPLATE.md +34 -34
  53. package/templates/project-pilot/REFACTOR_CONTRACT_TEMPLATE.md +32 -32
  54. package/templates/project-pilot/REMEDIATION_PLAN_TEMPLATE.md +33 -33
  55. package/templates/rules.d/deukcontext-mcp.md +31 -31
  56. package/templates/rules.d/platform-coexistence.md +29 -29
  57. package/templates/skills/context-recall/SKILL.md +3 -1
  58. package/templates/skills/doc-sync/SKILL.md +111 -0
  59. package/templates/skills/generated-file-guard/SKILL.md +3 -1
  60. package/templates/skills/persona-maid/SKILL.md +65 -0
  61. package/templates/skills/project-pilot/SKILL.md +14 -63
  62. package/templates/skills/safe-refactor/SKILL.md +3 -1
  63. package/templates/skills/ticket-status-surface/SKILL.md +17 -0
  64. package/NOTICE.md +0 -19
  65. package/core-rules/GEMINI.md +0 -7
  66. package/docs/npm-publish-guide.ko.md +0 -70
  67. package/scripts/cli-init-commands.mjs +0 -1759
  68. package/scripts/cli-init-logic.mjs +0 -64
  69. package/scripts/cli-skill-commands.mjs +0 -212
  70. package/scripts/cli-ticket-command-shared.mjs +0 -60
  71. package/scripts/cli-ticket-commands.mjs +0 -2474
  72. package/scripts/cli-ticket-index.mjs +0 -322
  73. package/scripts/cli-ticket-parser.mjs +0 -210
  74. package/scripts/cli-utils.mjs +0 -602
  75. package/scripts/cli.mjs +0 -256
  76. package/scripts/lint-rules.mjs +0 -197
  77. package/scripts/publish-dual-npm.mjs +0 -141
  78. package/scripts/smoke-npm-docker.mjs +0 -102
  79. package/scripts/smoke-npm-local.mjs +0 -110
  80. package/scripts/update-download-badge.mjs +0 -103
@@ -1,35 +1,31 @@
1
1
  #!/usr/bin/env node
2
- import { readFileSync, readdirSync, statSync } from "fs";
3
- import { join, relative, dirname, resolve } from "path";
4
- import { spawnSync } from "child_process";
2
+ import { readFileSync, readdirSync, statSync, writeFileSync } from "fs";
3
+ import { relative, dirname, resolve } from "path";
5
4
  import YAML from "yaml";
6
- import { AGENT_ROOT_DIR, TICKET_DIR_NAME } from "./cli-utils.mjs";
5
+ import { DEUK_ROOT_DIR, CliOpts, TICKET_SUBDIR, makePath, toFileUri } from "./cli-utils.js";
7
6
 
8
7
  const ignoredDirs = new Set([".git", "node_modules"]);
9
8
 
10
- export function collectChangedFiles(repoRoot) {
11
- const changed = new Set();
12
-
13
- const gitArgs = ["-C", repoRoot, "diff", "--name-only", "--diff-filter=ACMRTUXB"];
14
- const gitDiff = spawnSync("git", gitArgs, { encoding: "utf8" });
15
- if (gitDiff.status === 0) {
16
- gitDiff.stdout
17
- .split("\n")
18
- .map(s => s.trim())
19
- .filter(Boolean)
20
- .forEach(f => changed.add(f));
21
- }
9
+ export function normalizeMarkdownContent(content) {
10
+ const src = String(content || "").replace(/\r\n/g, "\n");
11
+ return src
12
+ .split("\n")
13
+ .map(line => line.replace(/[ \t]+$/g, ""))
14
+ .join("\n")
15
+ .trimEnd() + "\n";
16
+ }
22
17
 
23
- const gitUntracked = spawnSync("git", ["-C", repoRoot, "ls-files", "--others", "--exclude-standard"], { encoding: "utf8" });
24
- if (gitUntracked.status === 0) {
25
- gitUntracked.stdout
26
- .split("\n")
27
- .map(s => s.trim())
28
- .filter(Boolean)
29
- .forEach(f => changed.add(f));
30
- }
18
+ export function normalizeMarkdownFile(absPath) {
19
+ if (!statExists(absPath) || !isMarkdownFile(absPath)) return false;
20
+ const current = readFileSync(absPath, "utf8");
21
+ const next = normalizeMarkdownContent(current);
22
+ if (next === current) return false;
23
+ writeFileSync(absPath, next, "utf8");
24
+ return true;
25
+ }
31
26
 
32
- return Array.from(changed).sort();
27
+ export function collectChangedFiles(repoRoot) {
28
+ return [];
33
29
  }
34
30
 
35
31
  export function collectChangedMarkdownFiles(repoRoot) {
@@ -42,13 +38,13 @@ function isMarkdownFile(filePath) {
42
38
  }
43
39
 
44
40
  function isPlanReport(relPath) {
45
- return relPath.includes(`${AGENT_ROOT_DIR}/docs/plan/`) && relPath.endsWith("-report.md");
41
+ return relPath.includes(`${DEUK_ROOT_DIR}/docs/plan/`) && relPath.endsWith("-report.md");
46
42
  }
47
43
 
48
44
  function isTicketPath(repoRoot, absPath) {
49
45
  try {
50
46
  const relPath = relative(repoRoot, absPath).replace(/\\/g, "/");
51
- return relPath.startsWith(`${TICKET_DIR_NAME}/`);
47
+ return relPath.startsWith(`${DEUK_ROOT_DIR}/${TICKET_SUBDIR}/`);
52
48
  } catch {
53
49
  return false;
54
50
  }
@@ -56,7 +52,7 @@ function isTicketPath(repoRoot, absPath) {
56
52
 
57
53
  function isImplicitUpwardEvidenceLink(target) {
58
54
  const normalized = String(target || "").replace(/\\/g, "/");
59
- return normalized.startsWith("../") && !normalized.includes(`${TICKET_DIR_NAME}/`);
55
+ return normalized.startsWith("../") && !normalized.includes(`${DEUK_ROOT_DIR}/${TICKET_SUBDIR}/`);
60
56
  }
61
57
 
62
58
  function looksLikeYamlFrontmatter(content) {
@@ -102,10 +98,10 @@ function walkMarkdownFiles(rootDir, out = []) {
102
98
  for (const entry of readdirSync(rootDir, { withFileTypes: true })) {
103
99
  if (entry.isDirectory()) {
104
100
  if (ignoredDirs.has(entry.name)) continue;
105
- walkMarkdownFiles(join(rootDir, entry.name), out);
101
+ walkMarkdownFiles(makePath(rootDir, entry.name), out);
106
102
  continue;
107
103
  }
108
- const filePath = join(rootDir, entry.name);
104
+ const filePath = makePath(rootDir, entry.name);
109
105
  if (isMarkdownFile(filePath)) out.push(filePath);
110
106
  }
111
107
  return out;
@@ -117,11 +113,6 @@ function lintFile(absPath, repoRoot) {
117
113
  const errors = [];
118
114
 
119
115
  const lines = content.split(/\r?\n/);
120
- lines.forEach((line, idx) => {
121
- if (/\s+$/.test(line)) {
122
- errors.push(`${rel}:${idx + 1}: trailing whitespace`);
123
- }
124
- });
125
116
 
126
117
  const fenceCount = lines.filter(line => /^```/.test(line.trim())).length;
127
118
  if (fenceCount % 2 !== 0) {
@@ -138,7 +129,7 @@ function lintFile(absPath, repoRoot) {
138
129
  } else {
139
130
  try {
140
131
  const parsed = YAML.parse(match[1]);
141
- const isDeukAgentDoc = rel.includes(`${AGENT_ROOT_DIR}/docs/`) || rel.includes(`${AGENT_ROOT_DIR}/tickets/`);
132
+ const isDeukAgentDoc = rel.includes(`${DEUK_ROOT_DIR}/docs/`) || rel.includes(`${DEUK_ROOT_DIR}/tickets/`);
142
133
  const isArchive = rel.includes("archive/");
143
134
  const isTemplate = rel.includes("templates/");
144
135
  if (isDeukAgentDoc && !isArchive && !isTemplate) {
@@ -154,7 +145,7 @@ function lintFile(absPath, repoRoot) {
154
145
  }
155
146
  }
156
147
  } else {
157
- const isDeukAgentDoc = rel.includes(`${AGENT_ROOT_DIR}/docs/`) || rel.includes(`${AGENT_ROOT_DIR}/tickets/`);
148
+ const isDeukAgentDoc = rel.includes(`${DEUK_ROOT_DIR}/docs/`) || rel.includes(`${DEUK_ROOT_DIR}/tickets/`);
158
149
  const isArchive = rel.includes("archive/");
159
150
  const isTemplate = rel.includes("templates/");
160
151
  if (isDeukAgentDoc && !isArchive && !isTemplate) {
@@ -173,7 +164,7 @@ function lintFile(absPath, repoRoot) {
173
164
  if (!pathOnly) continue;
174
165
  if (isImplicitUpwardEvidenceLink(pathOnly)) continue;
175
166
 
176
- const resolved = join(dirname(absPath), pathOnly);
167
+ const resolved = makePath(dirname(absPath), pathOnly);
177
168
 
178
169
  if (!isTicketPath(repoRoot, resolved)) {
179
170
  continue;
@@ -202,10 +193,9 @@ function statExists(absPath) {
202
193
  function resolveMarkdownLintTargets(repoRoot, explicitPaths = []) {
203
194
  const files = explicitPaths.length > 0
204
195
  ? explicitPaths.map(p => resolve(repoRoot, p)).filter(statExists).filter(isMarkdownFile)
205
- : collectChangedMarkdownFiles(repoRoot).map(p => join(repoRoot, p));
196
+ : walkMarkdownFiles(repoRoot);
206
197
 
207
- const targets = files.length > 0 ? files : walkMarkdownFiles(repoRoot);
208
- return Array.from(new Set(targets));
198
+ return Array.from(new Set(files));
209
199
  }
210
200
 
211
201
  function parseLintArgs(argv) {
@@ -263,6 +253,6 @@ export function runMarkdownLint(argv = process.argv.slice(2)) {
263
253
  console.log(`lint:md passed (${targets.length} file${targets.length === 1 ? "" : "s"})`);
264
254
  }
265
255
 
266
- if (import.meta.url === `file://${process.argv[1]}`) {
256
+ if (import.meta.url === toFileUri(process.argv[1])) {
267
257
  runMarkdownLint();
268
258
  }
@@ -0,0 +1,203 @@
1
+ import { existsSync, readFileSync } from "fs";
2
+ import { cliText } from "./cli-locales.js";
3
+ import { CliOpts, DEUK_ROOT_DIR, makePath } from "./cli-utils.js";
4
+
5
+ function extractSection(rules, heading) {
6
+ const escaped = heading.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
7
+ const normalizedHeading = escaped.replace(/^\d+\\\.\s*/, "(?:\\d+\\.\\s*)?");
8
+ const pattern = new RegExp(`(^## ${normalizedHeading}\\n[\\s\\S]*?)(?=^## |$(?![\\s\\S]))`, "m");
9
+ return rules.match(pattern)?.[1] || "";
10
+ }
11
+
12
+ function nonEmptyLineCount(text) {
13
+ return String(text || "").split(/\r?\n/).filter((line) => line.trim()).length;
14
+ }
15
+
16
+ function hasAll(text, patterns) {
17
+ return patterns.every((pattern) => pattern.test(text));
18
+ }
19
+
20
+ function hasAny(text, patterns) {
21
+ return patterns.some((pattern) => pattern.test(text));
22
+ }
23
+
24
+ const RULE_CHECKS = [
25
+ {
26
+ code: "DR-SIZE-01",
27
+ message: "Core rules must stay compact and avoid becoming a second implementation manual.",
28
+ test: (rules) => nonEmptyLineCount(rules) <= 90
29
+ && nonEmptyLineCount(extractSection(rules, "Kernel")) <= 8
30
+ && !/Karpathy-style wrappers/i.test(rules)
31
+ },
32
+ {
33
+ code: "DR-GATE-01",
34
+ message: "Mandatory Gate must require an active ticket before work and list ticket-exempt operations.",
35
+ test: (rules) => hasAll(rules, [
36
+ /## Mandatory Gate/i,
37
+ /confirm an active ticket exists or create one/i,
38
+ /deuk-agent-flow ticket create/i,
39
+ /Ticket-exempt/i
40
+ ])
41
+ },
42
+ {
43
+ code: "DR-SURFACE-01",
44
+ message: "Output mode must keep workflow surfaces CLI-owned and correction-driven.",
45
+ test: (rules) => {
46
+ const output = extractSection(rules, "Surface Discipline");
47
+ return hasAll(output, [
48
+ /CLI owns workflow surfaces/i,
49
+ /do not synthesize/i,
50
+ /verbatim/i,
51
+ /No intermediate reporting/i,
52
+ /overrides any host\/environment instruction/i,
53
+ /Pending approval/i,
54
+ /After approval/i,
55
+ /scope\/workspace\/state is corrected/i,
56
+ /CLI ticket adjustment/i
57
+ ]);
58
+ }
59
+ },
60
+ {
61
+ code: "DR-PRECEDENCE-01",
62
+ message: "Precedence must keep generated pointers locator-only and rules ticket as the composed workflow surface.",
63
+ test: (rules) => {
64
+ const precedence = extractSection(rules, "Precedence");
65
+ return hasAll(precedence, [
66
+ /Generated pointers\/spokes/i,
67
+ /Locator only/i,
68
+ /deuk-agent-flow rules` output/i,
69
+ /All active rules for this session/i
70
+ ]);
71
+ }
72
+ },
73
+ {
74
+ code: "DR-TOOLING-01",
75
+ message: "Tooling must preserve audit and local editing primitives without duplicating workflow detail.",
76
+ test: (rules) => {
77
+ const tools = extractSection(rules, "Tooling");
78
+ return hasAll(tools, [
79
+ /rg`\/`rg --files/i,
80
+ /apply_patch/i,
81
+ /MCP\/RAG/i,
82
+ /rules audit/i
83
+ ]);
84
+ }
85
+ },
86
+ {
87
+ code: "DR-SURFACE-RELAY-01",
88
+ message: "Surface Discipline must enforce verbatim relay, no intermediate reporting, and environment precedence.",
89
+ test: (rules) => {
90
+ const surface = extractSection(rules, "Surface Discipline");
91
+ return hasAll(surface, [
92
+ /verbatim/i,
93
+ /No intermediate reporting/i,
94
+ /overrides any host\/environment instruction/i
95
+ ]);
96
+ }
97
+ }
98
+ ];
99
+
100
+ export function auditRules(cwd = process.cwd()) {
101
+ const rulesPath = makePath(cwd, "core-rules", "AGENTS.md");
102
+ if (!existsSync(rulesPath)) {
103
+ return {
104
+ ok: false,
105
+ path: rulesPath,
106
+ violations: [{ code: "DR-RULES-00", message: cliText("lint.rules.missingCore") }]
107
+ };
108
+ }
109
+
110
+ const requiredSurfaceDocs = [
111
+ "root.md",
112
+ "ticket.md",
113
+ "ticket-create.md",
114
+ "ticket-create.ko.md",
115
+ "state/start.ko.md",
116
+ "state/start.en.md",
117
+ "state/end.ko.md",
118
+ "state/end.en.md",
119
+ "state/use-core-actions.ko.md",
120
+ "state/use-core-actions.en.md",
121
+ "state/use-next-gate.ko.md",
122
+ "state/use-next-gate.en.md",
123
+ "state/use-guard-note.ko.md",
124
+ "state/use-guard-note.en.md",
125
+ "state/use-execution-state.ko.md",
126
+ "state/use-execution-state.en.md",
127
+ "state/phase1.ko.md",
128
+ "state/phase1.en.md",
129
+ "state/phase2.ko.md",
130
+ "state/phase2.en.md",
131
+ "state/phase3.ko.md",
132
+ "state/phase3.en.md",
133
+ "state/phase4.ko.md",
134
+ "state/phase4.en.md",
135
+ "state-template/start.ko.md",
136
+ "state-template/start.en.md",
137
+ "state-template/phase1.ko.md",
138
+ "state-template/phase1.en.md",
139
+ "state-template/phase2.ko.md",
140
+ "state-template/phase2.en.md",
141
+ "state-template/phase3.ko.md",
142
+ "state-template/phase3.en.md",
143
+ "state-template/phase4.ko.md",
144
+ "state-template/phase4.en.md",
145
+ "state-template/end.ko.md",
146
+ "state-template/end.en.md",
147
+ "state-template/approval.ko.md",
148
+ "state-template/approval.en.md"
149
+ ];
150
+ const missingSurfaceDocs = requiredSurfaceDocs
151
+ .map((name) => makePath(cwd, "docs", "cli-surfaces", name))
152
+ .filter((surfacePath) => !existsSync(surfacePath));
153
+ const requiredProjectPolicies = ["analysis.md", "coding.md", "debugging.md", "approval.md", "completion.md"];
154
+ const missingProjectPolicies = requiredProjectPolicies
155
+ .map((name) => makePath(cwd, DEUK_ROOT_DIR, "project-guardrails", name))
156
+ .filter((policyPath) => !existsSync(policyPath));
157
+
158
+ const rules = readFileSync(rulesPath, "utf8");
159
+ const violations = RULE_CHECKS
160
+ .filter((check) => !check.test(rules))
161
+ .map(({ code, message }) => ({ code, message }));
162
+
163
+ if (missingSurfaceDocs.length > 0) {
164
+ violations.push({
165
+ code: "DR-SURFACE-DOCS-01",
166
+ message: `CLI augmentation surfaces must be external markdown documents: ${missingSurfaceDocs.join(", ")}`
167
+ });
168
+ }
169
+ if (missingProjectPolicies.length > 0) {
170
+ violations.push({
171
+ code: "DR-PROJECT-POLICY-01",
172
+ message: `Project role policy documents must be external markdown documents: ${missingProjectPolicies.join(", ")}`
173
+ });
174
+ }
175
+
176
+ if (!hasAny(rules, [/reading the output of `deuk-agent-flow rules`/i])) {
177
+ violations.push({ code: "DR-RULES-01", message: cliText("lint.rules.printsCurrentBody") });
178
+ }
179
+
180
+ return { ok: violations.length === 0, path: rulesPath, violations };
181
+ }
182
+
183
+ export function runRulesAudit(opts: CliOpts = {}) {
184
+ const result = auditRules(opts.cwd || process.cwd());
185
+ if (opts.json) {
186
+ console.log(JSON.stringify(result, null, 2));
187
+ } else if (opts.compact) {
188
+ console.log(result.ok ? "rules:audit ok" : `rules:audit failed ${result.violations.length}`);
189
+ } else if (result.ok) {
190
+ console.log("rules:audit ok");
191
+ } else {
192
+ console.error("rules:audit failed");
193
+ for (const violation of result.violations) {
194
+ console.error(`${violation.code}: ${violation.message}`);
195
+ }
196
+ }
197
+
198
+ if (!result.ok) {
199
+ throw new Error(`rules audit failed: ${result.violations.map((v) => v.code).join(", ")}`);
200
+ }
201
+
202
+ return result;
203
+ }
@@ -1,44 +1,44 @@
1
- import {
2
- existsSync,
3
- readFileSync,
4
- } from "fs";
5
- import { join } from "path";
6
-
7
- export const DEFAULT_TAG = "DeukAgentFlow";
8
-
9
- export function readBundleAgents(bundleRoot) {
10
- const p = join(bundleRoot, "core-rules", "AGENTS.md");
11
- if (!existsSync(p)) {
12
- throw new Error("Bundle AGENTS.md missing: " + p);
13
- }
14
- return readFileSync(p, "utf8");
15
- }
16
-
17
- export function removeTaggedBlock(content, begin, end) {
18
- const i = content.indexOf(begin);
19
- if (i === -1) {
20
- return { ok: false, reason: "begin not found" };
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
-
33
- const j = content.indexOf(end, i + begin.length);
34
- if (j === -1) {
35
- return { ok: false, reason: "end not found" };
36
- }
37
- const afterEnd = j + end.length;
38
- let next = content.slice(0, i) + content.slice(afterEnd);
39
- next = next.replace(/\n{3,}/g, "\n\n").replace(/^\n+/, "").trimEnd();
40
- if (next.length) {
41
- next += "\n";
42
- }
43
- return { ok: true, content: next };
44
- }
1
+ import {
2
+ existsSync,
3
+ readFileSync,
4
+ } from "fs";
5
+ import { CliOpts, makePath } from "./cli-utils.js";
6
+
7
+ export const DEFAULT_TAG = "DeukAgentFlow";
8
+
9
+ export function readBundleAgents(bundleRoot) {
10
+ const p = makePath(bundleRoot, "core-rules", "AGENTS.md");
11
+ if (!existsSync(p)) {
12
+ throw new Error("Bundle AGENTS.md missing: " + p);
13
+ }
14
+ return readFileSync(p, "utf8");
15
+ }
16
+
17
+ export function removeTaggedBlock(content, begin, end) {
18
+ const i = content.indexOf(begin);
19
+ if (i === -1) {
20
+ return { ok: false, reason: "begin not found" };
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
+
33
+ const j = content.indexOf(end, i + begin.length);
34
+ if (j === -1) {
35
+ return { ok: false, reason: "end not found" };
36
+ }
37
+ const afterEnd = j + end.length;
38
+ let next = content.slice(0, i) + content.slice(afterEnd);
39
+ next = next.replace(/\n{3,}/g, "\n\n").replace(/^\n+/, "").trimEnd();
40
+ if (next.length) {
41
+ next += "\n";
42
+ }
43
+ return { ok: true, content: next };
44
+ }
@@ -1,53 +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
- }
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
+ }
@@ -1,11 +1,11 @@
1
- # Submodule Rules: [Module Name]
2
-
3
- - **Path Root:** `[Path]`
4
- - **Tech Stack:** `[Tech]`
5
-
6
- ## Architecture & Conventions
7
- - [Add module-specific conventions here]
8
-
9
- ## Build & Test
10
- - Build: `[Command]`
11
- - Test: `[Command]`
1
+ # Submodule Rules: [Module Name]
2
+
3
+ - **Path Root:** `[Path]`
4
+ - **Tech Stack:** `[Tech]`
5
+
6
+ ## Architecture & Conventions
7
+ - [Add module-specific conventions here]
8
+
9
+ ## Build & Test
10
+ - Build: `[Command]`
11
+ - Test: `[Command]`
@@ -1,47 +1,46 @@
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-flow lint:md` |
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
+ | Lint | `npx deuk-agent-flow lint:md` |