deuk-agent-flow 4.0.37 → 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.
- package/CHANGELOG.ko.md +282 -0
- package/CHANGELOG.md +788 -120
- package/LICENSE +0 -0
- package/README.ko.md +97 -17
- package/README.md +108 -25
- package/bin/deuk-agent-flow.js +11 -13
- package/bin/deuk-agent-rule.js +1 -1
- package/bundled/README.md +3 -0
- package/bundled/deuk-agent-flow.vsix +0 -0
- package/core-rules/AGENTS.md +30 -118
- package/docs/architecture.ko.md +155 -2
- package/docs/architecture.md +155 -2
- package/docs/assets/agentflow-panel-skills.png +0 -0
- package/docs/assets/agentflow-panel.png +0 -0
- package/docs/how-it-works.ko.md +109 -52
- package/docs/how-it-works.md +128 -71
- package/docs/principles.ko.md +68 -68
- package/docs/principles.md +68 -68
- package/docs/usage-guide.ko.md +251 -212
- package/package.json +41 -34
- package/scripts/bundle-vscode-vsix.ts +67 -0
- package/scripts/{cli-args.mjs → cli-args.ts} +49 -12
- package/scripts/cli-init-commands.ts +99 -0
- package/scripts/cli-init-logic.ts +46 -0
- package/scripts/{cli-prompts.mjs → cli-prompts.ts} +19 -34
- package/scripts/{cli-rule-compiler.mjs → cli-rule-compiler.ts} +128 -112
- package/scripts/cli-skill-commands.ts +707 -0
- package/scripts/{cli-telemetry-commands.mjs → cli-telemetry-commands.ts} +25 -22
- package/scripts/cli-ticket-command-shared.ts +4 -0
- package/scripts/cli-ticket-commands.ts +3723 -0
- package/scripts/cli-ticket-index.ts +283 -0
- package/scripts/{cli-ticket-migration.mjs → cli-ticket-migration.ts} +44 -68
- package/scripts/cli-ticket-parser.ts +100 -0
- package/scripts/{cli-usage-commands.mjs → cli-usage-commands.ts} +325 -326
- package/scripts/cli-utils.ts +1560 -0
- package/scripts/cli.ts +695 -0
- package/scripts/{lint-md.mjs → lint-md.ts} +32 -42
- package/scripts/lint-rules.ts +203 -0
- package/scripts/{merge-logic.mjs → merge-logic.ts} +44 -44
- package/scripts/{plan-parser.mjs → plan-parser.ts} +53 -53
- package/templates/MODULE_RULE_TEMPLATE.md +11 -11
- package/templates/PROJECT_RULE.md +46 -47
- package/templates/TICKET_TEMPLATE.ko.md +48 -44
- package/templates/TICKET_TEMPLATE.md +48 -44
- package/templates/project-memory.md +19 -0
- package/templates/project-pilot/CONFORMANCE_GATE_TEMPLATE.md +25 -23
- package/templates/project-pilot/DRIFT_CHECKLIST.md +27 -19
- package/templates/project-pilot/FLOW_CONTRACT_TEMPLATE.md +26 -26
- package/templates/project-pilot/IMPLEMENTATION_MATRIX_TEMPLATE.md +30 -30
- package/templates/project-pilot/INTEGRATION_CONTRACT_TEMPLATE.md +26 -26
- package/templates/project-pilot/OWNER_MAP_TEMPLATE.md +15 -15
- package/templates/project-pilot/PROJECT_PILOT_RULE_TEMPLATE.md +34 -34
- package/templates/project-pilot/REFACTOR_CONTRACT_TEMPLATE.md +32 -32
- package/templates/project-pilot/REMEDIATION_PLAN_TEMPLATE.md +33 -33
- package/templates/rules.d/deukcontext-mcp.md +31 -31
- package/templates/rules.d/platform-coexistence.md +29 -29
- package/templates/skills/context-recall/SKILL.md +3 -1
- package/templates/skills/doc-sync/SKILL.md +111 -0
- package/templates/skills/generated-file-guard/SKILL.md +3 -1
- package/templates/skills/persona-maid/SKILL.md +65 -0
- package/templates/skills/project-pilot/SKILL.md +13 -52
- package/templates/skills/safe-refactor/SKILL.md +3 -1
- package/templates/skills/ticket-status-surface/SKILL.md +17 -0
- package/core-rules/GEMINI.md +0 -7
- package/docs/badges/npm-downloads.json +0 -8
- package/scripts/cli-init-commands.mjs +0 -1750
- package/scripts/cli-init-logic.mjs +0 -64
- package/scripts/cli-skill-commands.mjs +0 -201
- package/scripts/cli-ticket-commands.mjs +0 -2427
- package/scripts/cli-ticket-index.mjs +0 -298
- package/scripts/cli-ticket-parser.mjs +0 -209
- package/scripts/cli-utils.mjs +0 -602
- package/scripts/cli.mjs +0 -256
- package/scripts/lint-rules.mjs +0 -196
- package/scripts/publish-dual-npm.mjs +0 -141
- package/scripts/smoke-npm-docker.mjs +0 -102
- package/scripts/smoke-npm-local.mjs +0 -109
- 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 {
|
|
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 {
|
|
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
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
24
|
-
if (
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
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(`${
|
|
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(`${
|
|
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(`${
|
|
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(
|
|
101
|
+
walkMarkdownFiles(makePath(rootDir, entry.name), out);
|
|
106
102
|
continue;
|
|
107
103
|
}
|
|
108
|
-
const filePath =
|
|
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(`${
|
|
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(`${
|
|
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 =
|
|
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
|
-
:
|
|
196
|
+
: walkMarkdownFiles(repoRoot);
|
|
206
197
|
|
|
207
|
-
|
|
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 ===
|
|
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 {
|
|
6
|
-
|
|
7
|
-
export const DEFAULT_TAG = "DeukAgentFlow";
|
|
8
|
-
|
|
9
|
-
export function readBundleAgents(bundleRoot) {
|
|
10
|
-
const p =
|
|
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
|
-
|
|
|
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` |
|