deuk-agent-rule 2.2.1 ā 2.2.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.md +144 -132
- package/LICENSE +0 -0
- package/README.ko.md +86 -86
- package/README.md +86 -84
- package/bundle/.cursorrules +7 -7
- package/bundle/AGENTS.md +23 -74
- package/bundle/rules/delivery-and-parallel-work.mdc +26 -26
- package/bundle/rules/git-commit.mdc +24 -18
- package/bundle/rules/multi-ai-workflow.mdc +0 -0
- package/bundle/templates/MODULE_RULE_TEMPLATE.md +24 -0
- package/bundle/templates/TICKET_TEMPLATE.md +40 -0
- package/package.json +2 -3
- package/scripts/changelog-polish.mjs +0 -0
- package/scripts/cli-args.mjs +2 -0
- package/scripts/cli-ticket-commands.mjs +115 -5
- package/scripts/cli.mjs +4 -2
- package/scripts/merge-logic.mjs +0 -0
- package/scripts/sync-bundle.mjs +0 -0
- package/scripts/sync-oss.mjs +2 -9
- package/README.ko.pdf +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from "fs";
|
|
2
|
-
import { basename, join } from "path";
|
|
3
|
-
import { toSlug, toRepoRelativePath, inferRefTitleAndTopic, resolveReferencedTicketPath } from "./cli-utils.mjs";
|
|
4
|
-
import { TICKET_DIR_NAME, appendTicketEntry, rebuildTicketIndexFromTopicFilesIfNeeded, detectConsumerTicketDir } from "./cli-ticket-logic.mjs";
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync, copyFileSync, readdirSync, rmSync, statSync } from "fs";
|
|
2
|
+
import { basename, join, dirname, relative, resolve } from "path";
|
|
3
|
+
import { toSlug, toRepoRelativePath, inferRefTitleAndTopic, resolveReferencedTicketPath, toPosixPath } from "./cli-utils.mjs";
|
|
4
|
+
import { TICKET_DIR_NAME, appendTicketEntry, rebuildTicketIndexFromTopicFilesIfNeeded, detectConsumerTicketDir, readTicketIndexJson, writeTicketIndexJson, writeTicketListFile } from "./cli-ticket-logic.mjs";
|
|
5
5
|
|
|
6
6
|
import { createInterface } from "readline";
|
|
7
7
|
import { selectOne } from "./cli-prompts.mjs";
|
|
@@ -118,7 +118,6 @@ export async function runTicketUse(opts) {
|
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
import { getLegacyMigrationCandidate, parseLegacyTicketMeta } from "./cli-ticket-logic.mjs";
|
|
121
|
-
import { dirname } from "path";
|
|
122
121
|
|
|
123
122
|
export async function runTicketMigrate(opts) {
|
|
124
123
|
const candidate = getLegacyMigrationCandidate(opts.cwd);
|
|
@@ -157,3 +156,114 @@ export async function runTicketMigrate(opts) {
|
|
|
157
156
|
}
|
|
158
157
|
}
|
|
159
158
|
}
|
|
159
|
+
|
|
160
|
+
export function pickTicketEntry(opts, indexJson) {
|
|
161
|
+
const rows = [...indexJson.entries].sort((a, b) => String(b.createdAt || "").localeCompare(String(a.createdAt || "")));
|
|
162
|
+
if (rows.length === 0) return null;
|
|
163
|
+
if (opts.topic) {
|
|
164
|
+
const key = String(opts.topic).toLowerCase();
|
|
165
|
+
return rows.find(e => String(e.topic || "").toLowerCase().includes(key)) || null;
|
|
166
|
+
}
|
|
167
|
+
return rows[0];
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export async function runTicketArchive(opts) {
|
|
171
|
+
if (!opts.latest && !opts.topic) {
|
|
172
|
+
if (process.stdout.isTTY) {
|
|
173
|
+
const index = rebuildTicketIndexFromTopicFilesIfNeeded(opts.cwd, opts);
|
|
174
|
+
const choices = index.entries
|
|
175
|
+
.filter(e => e.status !== "archived")
|
|
176
|
+
.map(e => ({ label: `[${e.group}] ${e.title}`, value: e.topic }));
|
|
177
|
+
if (choices.length > 0) {
|
|
178
|
+
const { createInterface } = await import("readline");
|
|
179
|
+
const { selectOne } = await import("./cli-prompts.mjs");
|
|
180
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
181
|
+
try {
|
|
182
|
+
opts.topic = await selectOne(rl, "Choose a ticket to archive (this will move the file to archive/):", choices);
|
|
183
|
+
} finally {
|
|
184
|
+
rl.close();
|
|
185
|
+
}
|
|
186
|
+
} else {
|
|
187
|
+
throw new Error("No active tickets found to archive.");
|
|
188
|
+
}
|
|
189
|
+
} else {
|
|
190
|
+
throw new Error("ticket archive requires --latest or --topic <prefix>");
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const indexJson = rebuildTicketIndexFromTopicFilesIfNeeded(opts.cwd, opts);
|
|
195
|
+
const found = pickTicketEntry(opts, indexJson);
|
|
196
|
+
if (!found) throw new Error("ticket archive: no matching entry");
|
|
197
|
+
|
|
198
|
+
const absPath = join(opts.cwd, found.path);
|
|
199
|
+
if (!existsSync(absPath)) {
|
|
200
|
+
throw new Error("ticket archive: file not found " + found.path);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const archiveDir = join(opts.cwd, TICKET_DIR_NAME, "archive", found.group || "sub");
|
|
204
|
+
if (!opts.dryRun) mkdirSync(archiveDir, { recursive: true });
|
|
205
|
+
|
|
206
|
+
const fileName = found.path.split(/[/\\]/).pop();
|
|
207
|
+
const newAbsPath = join(archiveDir, fileName);
|
|
208
|
+
const bodyLines = readFileSync(absPath, "utf8").trimEnd().split(/\r?\n/);
|
|
209
|
+
|
|
210
|
+
if (opts.report) {
|
|
211
|
+
const reportSrc = resolve(opts.cwd, opts.report);
|
|
212
|
+
if (!existsSync(reportSrc)) {
|
|
213
|
+
throw new Error("ticket archive: report file not found " + opts.report);
|
|
214
|
+
}
|
|
215
|
+
const reportDir = join(opts.cwd, TICKET_DIR_NAME, "reports");
|
|
216
|
+
if (!opts.dryRun) mkdirSync(reportDir, { recursive: true });
|
|
217
|
+
|
|
218
|
+
const reportDest = join(reportDir, `REPORT-${fileName}`);
|
|
219
|
+
if (!opts.dryRun) copyFileSync(reportSrc, reportDest);
|
|
220
|
+
console.log("ticket archive: copied report to " + toRepoRelativePath(opts.cwd, reportDest));
|
|
221
|
+
|
|
222
|
+
bodyLines.push("");
|
|
223
|
+
bodyLines.push("## š Attached Report");
|
|
224
|
+
const relativeLink = toPosixPath(relative(dirname(newAbsPath), reportDest));
|
|
225
|
+
bodyLines.push(`- [View Report](${relativeLink})`);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (opts.dryRun) {
|
|
229
|
+
console.log("ticket archive: would move " + toRepoRelativePath(opts.cwd, absPath) + " to " + toRepoRelativePath(opts.cwd, newAbsPath));
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
writeFileSync(newAbsPath, bodyLines.join("\n") + "\n", "utf8");
|
|
234
|
+
rmSync(absPath);
|
|
235
|
+
console.log("ticket archive: moved ticket to " + toRepoRelativePath(opts.cwd, newAbsPath));
|
|
236
|
+
|
|
237
|
+
const entryIdx = indexJson.entries.findIndex(e => e.id === found.id);
|
|
238
|
+
if (entryIdx >= 0) {
|
|
239
|
+
indexJson.entries[entryIdx].status = "archived";
|
|
240
|
+
indexJson.entries[entryIdx].path = toRepoRelativePath(opts.cwd, newAbsPath);
|
|
241
|
+
indexJson.entries[entryIdx].updatedAt = new Date().toISOString();
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
writeTicketIndexJson(opts.cwd, indexJson, opts);
|
|
245
|
+
writeTicketListFile(opts.cwd, indexJson.entries, opts);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export async function runTicketReports(opts) {
|
|
249
|
+
const reportDir = join(opts.cwd, TICKET_DIR_NAME, "reports");
|
|
250
|
+
console.log("\nš Agent Reports:");
|
|
251
|
+
if (!existsSync(reportDir)) {
|
|
252
|
+
console.log(" No reports found.");
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
const files = readdirSync(reportDir).filter(f => f.startsWith("REPORT-") && f.endsWith(".md"));
|
|
256
|
+
if (files.length === 0) {
|
|
257
|
+
console.log(" No reports found.");
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const sorted = files.sort((a, b) => {
|
|
262
|
+
return statSync(join(reportDir, b)).mtime.getTime() - statSync(join(reportDir, a)).mtime.getTime();
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
sorted.slice(0, opts.limit || 30).forEach(f => {
|
|
266
|
+
console.log(` - [${f}]`);
|
|
267
|
+
});
|
|
268
|
+
console.log("");
|
|
269
|
+
}
|
package/scripts/cli.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import { dirname, join } from "path";
|
|
|
4
4
|
import { fileURLToPath } from "url";
|
|
5
5
|
import { parseArgs, parseTicketArgs } from "./cli-args.mjs";
|
|
6
6
|
import { runInit, runMerge } from "./cli-init-commands.mjs";
|
|
7
|
-
import { runTicketCreate, runTicketList, runTicketUse, runTicketClose } from "./cli-ticket-commands.mjs";
|
|
7
|
+
import { runTicketCreate, runTicketList, runTicketUse, runTicketClose, runTicketArchive, runTicketReports } from "./cli-ticket-commands.mjs";
|
|
8
8
|
import { loadInitConfig, writeInitConfig } from "./cli-prompts.mjs";
|
|
9
9
|
import { runInteractive } from "./cli-prompts.mjs";
|
|
10
10
|
|
|
@@ -28,6 +28,8 @@ async function main() {
|
|
|
28
28
|
else if (action === "list") await runTicketList(opts);
|
|
29
29
|
else if (action === "use") await runTicketUse(opts);
|
|
30
30
|
else if (action === "close") await runTicketClose(opts);
|
|
31
|
+
else if (action === "archive") await runTicketArchive(opts);
|
|
32
|
+
else if (action === "reports") await runTicketReports(opts);
|
|
31
33
|
else if (action === "migrate") await runTicketMigrate(opts);
|
|
32
34
|
else {
|
|
33
35
|
console.error("Unknown ticket action: " + action);
|
|
@@ -84,7 +86,7 @@ function printHelp() {
|
|
|
84
86
|
Usage:
|
|
85
87
|
npx deuk-agent-rule init [options]
|
|
86
88
|
npx deuk-agent-rule merge [options]
|
|
87
|
-
npx deuk-agent-rule ticket <create|list|use|close|migrate> [options]
|
|
89
|
+
npx deuk-agent-rule ticket <create|list|use|close|archive|reports|migrate> [options]
|
|
88
90
|
|
|
89
91
|
Options:
|
|
90
92
|
--cwd <path> Target repo root
|
package/scripts/merge-logic.mjs
CHANGED
|
File without changes
|
package/scripts/sync-bundle.mjs
CHANGED
|
File without changes
|
package/scripts/sync-oss.mjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Populates ../DeukAgentRulesOSS for the public GitHub repo.
|
|
3
3
|
* Run: cd deuk-agent-rule && npm run sync:oss
|
|
4
4
|
*/
|
|
5
|
-
import { cpSync, existsSync, mkdirSync, readFileSync,
|
|
5
|
+
import { cpSync, existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "fs";
|
|
6
6
|
import { dirname, join } from "path";
|
|
7
7
|
import { fileURLToPath } from "url";
|
|
8
8
|
|
|
@@ -39,14 +39,7 @@ cpSync(join(pkgRoot, "publish"), join(ossRoot, "publish"), { recursive: true, fo
|
|
|
39
39
|
if (existsSync(join(pkgRoot, ".github"))) {
|
|
40
40
|
cpSync(join(pkgRoot, ".github"), join(ossRoot, ".github"), { recursive: true, force: true });
|
|
41
41
|
}
|
|
42
|
-
|
|
43
|
-
// Modular CLI: Copy all runtime scripts, exclude internal sync tools
|
|
44
|
-
const scriptsDir = join(pkgRoot, "scripts");
|
|
45
|
-
for (const name of readdirSync(scriptsDir)) {
|
|
46
|
-
if (!name.endsWith(".mjs")) continue;
|
|
47
|
-
if (name === "sync-oss.mjs" || name === "sync-bundle.mjs") continue;
|
|
48
|
-
cpSync(join(scriptsDir, name), join(ossRoot, "scripts", name), { force: true });
|
|
49
|
-
}
|
|
42
|
+
cpSync(join(pkgRoot, "scripts"), join(ossRoot, "scripts"), { recursive: true, force: true });
|
|
50
43
|
|
|
51
44
|
if (!existsSync(ossPublic)) {
|
|
52
45
|
throw new Error("Missing oss-public/: " + ossPublic);
|
package/README.ko.pdf
DELETED
|
Binary file
|