@ulpi/cli 0.1.5 → 0.1.6

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 (109) hide show
  1. package/LICENSE +21 -0
  2. package/dist/{auth-PN7TMQHV-2W4ICG64.js → auth-FWM7MM4Q-VZC3U2XZ.js} +1 -1
  3. package/dist/{auth-BFFBUJUC.js → auth-HDK7ECJL.js} +2 -1
  4. package/dist/{chunk-RJIRWQJD.js → chunk-3BCW6ABU.js} +402 -142
  5. package/dist/{chunk-L3PWNHSA.js → chunk-3WB5CXH4.js} +180 -5
  6. package/dist/{chunk-K4OVPFY2.js → chunk-4UCJIAOU.js} +2 -2
  7. package/dist/chunk-4XTHZVDS.js +109 -0
  8. package/dist/chunk-4ZPOZULQ.js +6522 -0
  9. package/dist/{chunk-SIAQVRKG.js → chunk-5MI5GIXM.js} +48 -2
  10. package/dist/{chunk-KLEASXUR.js → chunk-6ZL6NXMV.js} +1 -1
  11. package/dist/{chunk-AV5RB3N2.js → chunk-76D3BYJD.js} +48 -0
  12. package/dist/{chunk-DOIKS6C5.js → chunk-AWOSRA5F.js} +1 -1
  13. package/dist/{chunk-UCMT5OKP.js → chunk-BFEKZZHM.js} +274 -57
  14. package/dist/chunk-C7CLUQI6.js +1286 -0
  15. package/dist/{chunk-ELTGWMDE.js → chunk-E3B5NROU.js} +7 -7
  16. package/dist/chunk-EJ7TW77N.js +1418 -0
  17. package/dist/{chunk-6OURRFP7.js → chunk-IV6MWETF.js} +383 -168
  18. package/dist/chunk-IZPJHSPX.js +1478 -0
  19. package/dist/chunk-JLHNLM3C.js +228 -0
  20. package/dist/{chunk-P2RESJRN.js → chunk-KYYI23AQ.js} +2 -2
  21. package/dist/chunk-S6ANCSYO.js +1271 -0
  22. package/dist/chunk-SEU7WWNQ.js +1251 -0
  23. package/dist/chunk-SNQ7NAIS.js +453 -0
  24. package/dist/{ulpi-RMMCUAGP-EWYUE7RU.js → chunk-TSLDGT5O.js} +73 -35
  25. package/dist/{chunk-EIWYSP3A.js → chunk-UXHCHOWQ.js} +83 -62
  26. package/dist/chunk-V2H5D6Y3.js +146 -0
  27. package/dist/{chunk-5SCG7UYM.js → chunk-VVEDXI7E.js} +1 -1
  28. package/dist/chunk-VXH5Y4FO.js +6761 -0
  29. package/dist/chunk-WED4LM5N.js +322 -0
  30. package/dist/{chunk-74WVVWJ4.js → chunk-YOKL7RB5.js} +184 -15
  31. package/dist/chunk-Z53CAR7G.js +298 -0
  32. package/dist/{ci-JQ56YIKC.js → ci-X3U2W4HC.js} +124 -26
  33. package/dist/cloud-2F3NLVHN.js +274 -0
  34. package/dist/{codemap-HMYBXJL2.js → codemap-XNGMAF3F.js} +37 -37
  35. package/dist/codex-MB5YTMRT.js +132 -0
  36. package/dist/{config-YYWEN7U2.js → config-OOELBYTH.js} +1 -1
  37. package/dist/dist-2BJYR5EI.js +59 -0
  38. package/dist/dist-3EIQTZHT.js +1380 -0
  39. package/dist/{dist-WAMAQVPK.js → dist-4U5L2X2C.js} +2 -2
  40. package/dist/{dist-4XTJ6HLM.js → dist-54KAMNLO.js} +16 -15
  41. package/dist/dist-6M4MZWZW.js +58 -0
  42. package/dist/dist-6X576SU2.js +27 -0
  43. package/dist/dist-7QOEYLFX.js +103 -0
  44. package/dist/dist-AYBGHEDY.js +2541 -0
  45. package/dist/dist-EK45QNEM.js +45 -0
  46. package/dist/{dist-U7ZIJMZD.js → dist-FKFEJRPX.js} +16 -15
  47. package/dist/dist-GTEJUBBT.js +66 -0
  48. package/dist/dist-HA74OKJZ.js +40 -0
  49. package/dist/{dist-XG2GG5SD.js → dist-HU5RZAON.js} +14 -2
  50. package/dist/dist-IYE3OBRB.js +374 -0
  51. package/dist/{dist-7WLLPWWB.js → dist-JLU26AB6.js} +12 -9
  52. package/dist/{dist-6G7JC2RA.js → dist-KUCI6JFE.js} +49 -9
  53. package/dist/dist-NUEMFZFL.js +33 -0
  54. package/dist/{dist-GWGTAHNM.js → dist-NUXMDXZ3.js} +31 -3
  55. package/dist/{dist-5R4RYNQO.js → dist-YCNWHSLN.js} +15 -5
  56. package/dist/{dist-6MFVWIFF.js → dist-YFFG2ZD6.js} +9 -16
  57. package/dist/dist-ZG4OKCSR.js +15 -0
  58. package/dist/doctor-SI4LLLDZ.js +345 -0
  59. package/dist/{export-import-4A5MWLIA.js → export-import-JFQH4KSJ.js} +1 -1
  60. package/dist/{history-RNUWO4JZ.js → history-5NE46ZAH.js} +7 -7
  61. package/dist/{hooks-installer-K2JXEBNN.js → hooks-installer-UN5JZLDQ.js} +2 -2
  62. package/dist/index.js +394 -618
  63. package/dist/{init-NQWFZPKO.js → init-5FK3VKRT.js} +76 -10
  64. package/dist/job-HIDMAFW2.js +376 -0
  65. package/dist/jobs.memory-PLMMSFHB-VBECCTHN.js +33 -0
  66. package/dist/kiro-VMUHDFGK.js +153 -0
  67. package/dist/{launchd-OYXUAVW6.js → launchd-6AWT54HR.js} +9 -17
  68. package/dist/mcp-PDUD7SGP.js +249 -0
  69. package/dist/mcp-installer-PQU3XOGO.js +259 -0
  70. package/dist/mcp-setup-OA7IB3H3.js +263 -0
  71. package/dist/{memory-D6ZFFCI2.js → memory-ZNAEAK3B.js} +17 -17
  72. package/dist/{ollama-3XCUZMZT-FYKHW4TZ.js → ollama-3XCUZMZT-4JMH6B7P.js} +1 -1
  73. package/dist/{openai-E7G2YAHU-IG33BFYF.js → openai-E7G2YAHU-T3HMBPH7.js} +2 -2
  74. package/dist/portal-JYWVHXDU.js +210 -0
  75. package/dist/prd-Q4J5NVAR.js +408 -0
  76. package/dist/repos-WWZXNN3P.js +271 -0
  77. package/dist/review-integration-5WHEJU2A.js +14 -0
  78. package/dist/{rules-3OFGWHP4.js → rules-Y4VSOY5Y.js} +3 -3
  79. package/dist/run-VPNXEIBY.js +687 -0
  80. package/dist/server-COL4AXKU-P7S7NNF6.js +11 -0
  81. package/dist/server-KKSETHDV-XSSLEENT.js +20 -0
  82. package/dist/{skills-GY2CTPWN.js → skills-QEYU2N27.js} +4 -2
  83. package/dist/start-JYOEL7AJ.js +303 -0
  84. package/dist/{status-SE43TIFJ.js → status-BHQYYGAL.js} +2 -2
  85. package/dist/{templates-O2XDKB5R.js → templates-CBRUJ66V.js} +6 -5
  86. package/dist/tui-DP7736EX.js +61 -0
  87. package/dist/ulpi-5EN6JCAS-LFE3WSL4.js +10 -0
  88. package/dist/{uninstall-KWGSGZTI.js → uninstall-ICUV6DDV.js} +3 -3
  89. package/dist/{update-QYZA4D23.js → update-7ZMAYRBH.js} +3 -3
  90. package/dist/{version-checker-MVB74DEX.js → version-checker-4ZFMZA7Y.js} +2 -2
  91. package/package.json +39 -31
  92. package/dist/chunk-26LLDX2T.js +0 -553
  93. package/dist/chunk-DDRLI6JU.js +0 -331
  94. package/dist/chunk-IFATANHR.js +0 -453
  95. package/dist/chunk-JWUUVXIV.js +0 -13694
  96. package/dist/chunk-LD52XG3X.js +0 -4273
  97. package/dist/chunk-MIAQVCFW.js +0 -39
  98. package/dist/chunk-YYZOFYS6.js +0 -415
  99. package/dist/dist-XD4YI27T.js +0 -26
  100. package/dist/mcp-installer-TOYDP77X.js +0 -124
  101. package/dist/projects-COUJP4ZC.js +0 -271
  102. package/dist/review-KMGP2S25.js +0 -152
  103. package/dist/server-USLHY6GH-F4JSXCWA.js +0 -18
  104. package/dist/server-X5P6WH2M-ULZF5WHZ.js +0 -11
  105. package/dist/skills/ulpi-generate-guardian/SKILL.md +0 -750
  106. package/dist/skills/ulpi-generate-guardian/references/framework-rules.md +0 -849
  107. package/dist/skills/ulpi-generate-guardian/references/language-rules.md +0 -591
  108. package/dist/ui-4SM2SUI6.js +0 -167
  109. package/dist/ui.html +0 -698
@@ -0,0 +1,408 @@
1
+ import "./chunk-4VNS5WPM.js";
2
+
3
+ // src/commands/prd.ts
4
+ import * as fs from "fs";
5
+ import * as path from "path";
6
+ import chalk from "chalk";
7
+ async function runPrd(args, projectDir) {
8
+ const subcommand = args[0];
9
+ switch (subcommand) {
10
+ case "create":
11
+ return await createSubcommand(args.slice(1), projectDir);
12
+ case "convert":
13
+ return await convertSubcommand(args.slice(1), projectDir);
14
+ case "show":
15
+ return await showSubcommand(args.slice(1), projectDir);
16
+ default:
17
+ console.log(`
18
+ Usage: ulpi prd <subcommand> [options]
19
+
20
+ Subcommands:
21
+ create [file] [--chat] Create or parse a PRD
22
+ convert <file> [--output] Convert markdown PRD to JSON tasks
23
+ show <file> Show parsed PRD summary
24
+
25
+ Options:
26
+ --chat Interactive AI mode for PRD creation
27
+ --agent <name> AI agent to use (default: claude)
28
+ --model <id> Model override for the agent
29
+ --output, -o Output file path for conversion
30
+ --prefix Task ID prefix (default: "TASK-")
31
+ `.trim());
32
+ }
33
+ }
34
+ async function createSubcommand(args, projectDir) {
35
+ const isChat = args.includes("--chat");
36
+ const fileArg = args.find((a) => !a.startsWith("--"));
37
+ if (isChat) {
38
+ return await runChatMode(args, projectDir);
39
+ }
40
+ if (!fileArg) {
41
+ console.log(chalk.yellow("Provide a markdown file to parse, or use --chat for interactive mode."));
42
+ console.log(chalk.dim(" ulpi prd create <file.md>"));
43
+ console.log(chalk.dim(" ulpi prd create --chat"));
44
+ return;
45
+ }
46
+ const filePath = path.isAbsolute(fileArg) ? fileArg : path.resolve(projectDir, fileArg);
47
+ if (!fs.existsSync(filePath)) {
48
+ console.error(chalk.red(`File not found: ${filePath}`));
49
+ process.exit(1);
50
+ }
51
+ const markdown = fs.readFileSync(filePath, "utf-8");
52
+ const { parsePrdMarkdown } = await import("./dist-3EIQTZHT.js");
53
+ const result = parsePrdMarkdown(markdown);
54
+ console.log(chalk.bold(`
55
+ PRD: ${result.document.title}
56
+ `));
57
+ console.log(chalk.dim(`Description: ${result.document.description}
58
+ `));
59
+ if (result.document.tasks.length > 0) {
60
+ console.log(chalk.bold(`Tasks (${result.document.tasks.length}):`));
61
+ for (const task of result.document.tasks) {
62
+ const priorityColor = task.priority <= 1 ? "red" : task.priority <= 2 ? "yellow" : "green";
63
+ const priorityStr = chalk[priorityColor](`P${task.priority}`);
64
+ console.log(` ${chalk.cyan(task.id)} ${task.title} ${priorityStr}`);
65
+ if (task.acceptanceCriteria.length > 0) {
66
+ console.log(chalk.dim(` ${task.acceptanceCriteria.length} acceptance criteria`));
67
+ }
68
+ if (task.dependsOn.length > 0) {
69
+ console.log(chalk.dim(` depends on: ${task.dependsOn.join(", ")}`));
70
+ }
71
+ }
72
+ } else {
73
+ console.log(chalk.yellow("No tasks found in PRD."));
74
+ }
75
+ if (result.warnings.length > 0) {
76
+ console.log(chalk.yellow(`
77
+ Warnings (${result.warnings.length}):`));
78
+ for (const warning of result.warnings) {
79
+ console.log(chalk.yellow(` - ${warning}`));
80
+ }
81
+ }
82
+ console.log("");
83
+ }
84
+ async function convertSubcommand(args, projectDir) {
85
+ const fileArg = args.find((a) => !a.startsWith("--") && !a.startsWith("-"));
86
+ const outputIdx = args.findIndex((a) => a === "--output" || a === "-o");
87
+ const outputArg = outputIdx !== -1 ? args[outputIdx + 1] : void 0;
88
+ const prefixIdx = args.findIndex((a) => a === "--prefix");
89
+ const prefix = prefixIdx !== -1 ? args[prefixIdx + 1] : void 0;
90
+ if (!fileArg) {
91
+ console.error(chalk.red("Provide a markdown PRD file to convert."));
92
+ console.log(chalk.dim(" ulpi prd convert <file.md> [--output tasks.json]"));
93
+ process.exit(1);
94
+ }
95
+ const filePath = path.isAbsolute(fileArg) ? fileArg : path.resolve(projectDir, fileArg);
96
+ if (!fs.existsSync(filePath)) {
97
+ console.error(chalk.red(`File not found: ${filePath}`));
98
+ process.exit(1);
99
+ }
100
+ const markdown = fs.readFileSync(filePath, "utf-8");
101
+ const { parsePrdMarkdown, convertPrdToTasks, convertPrdToJson } = await import("./dist-3EIQTZHT.js");
102
+ const result = parsePrdMarkdown(markdown, { taskPrefix: prefix });
103
+ if (result.document.tasks.length === 0) {
104
+ console.error(chalk.red("No tasks found in PRD. Cannot convert."));
105
+ if (result.warnings.length > 0) {
106
+ for (const warning of result.warnings) {
107
+ console.error(chalk.yellow(` - ${warning}`));
108
+ }
109
+ }
110
+ process.exit(1);
111
+ }
112
+ const tasks = convertPrdToTasks(result.document, true);
113
+ const jsonOutput = convertPrdToJson(result.document);
114
+ const outputPath = outputArg ? path.isAbsolute(outputArg) ? outputArg : path.resolve(projectDir, outputArg) : path.resolve(projectDir, ".ulpi", "prds", `${path.basename(filePath, path.extname(filePath))}.json`);
115
+ const outputDir = path.dirname(outputPath);
116
+ if (!fs.existsSync(outputDir)) {
117
+ fs.mkdirSync(outputDir, { recursive: true });
118
+ }
119
+ fs.writeFileSync(outputPath, JSON.stringify(jsonOutput, null, 2) + "\n", "utf-8");
120
+ console.log(chalk.green(`Converted ${tasks.length} tasks to: ${outputPath}`));
121
+ for (const task of tasks) {
122
+ console.log(chalk.dim(` ${task.id}: ${task.title} (P${task.priority})`));
123
+ }
124
+ }
125
+ async function showSubcommand(args, projectDir) {
126
+ const fileArg = args.find((a) => !a.startsWith("--"));
127
+ if (!fileArg) {
128
+ console.error(chalk.red("Provide a markdown PRD file to show."));
129
+ console.log(chalk.dim(" ulpi prd show <file.md>"));
130
+ process.exit(1);
131
+ }
132
+ const filePath = path.isAbsolute(fileArg) ? fileArg : path.resolve(projectDir, fileArg);
133
+ if (!fs.existsSync(filePath)) {
134
+ console.error(chalk.red(`File not found: ${filePath}`));
135
+ process.exit(1);
136
+ }
137
+ const markdown = fs.readFileSync(filePath, "utf-8");
138
+ const { parsePrdMarkdown } = await import("./dist-3EIQTZHT.js");
139
+ const result = parsePrdMarkdown(markdown);
140
+ const doc = result.document;
141
+ console.log(chalk.bold(`
142
+ ${doc.title}`));
143
+ console.log(chalk.dim("=".repeat(doc.title.length)));
144
+ console.log("");
145
+ if (doc.metadata.branchName) {
146
+ console.log(`Branch: ${chalk.cyan(String(doc.metadata.branchName))}`);
147
+ }
148
+ if (doc.metadata.createdAt) {
149
+ console.log(`Created: ${chalk.dim(String(doc.metadata.createdAt))}`);
150
+ }
151
+ console.log("");
152
+ console.log(doc.description);
153
+ console.log("");
154
+ if (doc.sections.length > 0) {
155
+ console.log(chalk.bold("Sections:"));
156
+ for (const section of doc.sections) {
157
+ const indent = " ".repeat(Math.max(0, section.level - 1));
158
+ console.log(`${indent}${"#".repeat(section.level)} ${section.heading}`);
159
+ }
160
+ console.log("");
161
+ }
162
+ if (doc.tasks.length > 0) {
163
+ console.log(chalk.bold(`Tasks (${doc.tasks.length}):`));
164
+ console.log("");
165
+ for (const task of doc.tasks) {
166
+ const priorityColor = task.priority <= 1 ? "red" : task.priority <= 2 ? "yellow" : "green";
167
+ console.log(` ${chalk.bold.cyan(task.id)}: ${chalk.bold(task.title)} ${chalk[priorityColor](`P${task.priority}`)}`);
168
+ if (task.description !== task.title) {
169
+ console.log(chalk.dim(` ${task.description.slice(0, 120)}${task.description.length > 120 ? "..." : ""}`));
170
+ }
171
+ if (task.acceptanceCriteria.length > 0) {
172
+ console.log(` ${chalk.dim("Criteria:")} ${task.acceptanceCriteria.length} items`);
173
+ for (const ac of task.acceptanceCriteria.slice(0, 3)) {
174
+ console.log(chalk.dim(` - ${ac}`));
175
+ }
176
+ if (task.acceptanceCriteria.length > 3) {
177
+ console.log(chalk.dim(` ... and ${task.acceptanceCriteria.length - 3} more`));
178
+ }
179
+ }
180
+ if (task.dependsOn.length > 0) {
181
+ console.log(` ${chalk.dim("Depends on:")} ${task.dependsOn.join(", ")}`);
182
+ }
183
+ if (task.labels.length > 0) {
184
+ console.log(` ${chalk.dim("Labels:")} ${task.labels.map((l) => `[${l}]`).join(" ")}`);
185
+ }
186
+ console.log("");
187
+ }
188
+ }
189
+ if (result.warnings.length > 0) {
190
+ console.log(chalk.yellow(`Warnings (${result.warnings.length}):`));
191
+ for (const warning of result.warnings) {
192
+ console.log(chalk.yellow(` - ${warning}`));
193
+ }
194
+ console.log("");
195
+ }
196
+ }
197
+ async function runChatMode(args, projectDir) {
198
+ console.log(chalk.bold("\n PRD Generator -- AI-Powered\n"));
199
+ console.log(chalk.dim(" Describe a feature and the AI will help you build a structured PRD."));
200
+ console.log(chalk.dim(" Type 'quit' or press Ctrl+C to exit.\n"));
201
+ const agentIdx = args.findIndex((a) => a === "--agent");
202
+ const agentName = agentIdx !== -1 && args[agentIdx + 1] ? args[agentIdx + 1] : "claude";
203
+ const modelIdx = args.findIndex((a) => a === "--model");
204
+ const model = modelIdx !== -1 && args[modelIdx + 1] ? args[modelIdx + 1] : "";
205
+ const {
206
+ getPrdSystemPrompt,
207
+ buildConversationPrompt,
208
+ detectPrd,
209
+ parsePrdMarkdown,
210
+ slugify
211
+ } = await import("./dist-3EIQTZHT.js");
212
+ const { getAgentRegistry } = await import("./dist-7QOEYLFX.js");
213
+ const readline = await import("readline/promises");
214
+ const registry = getAgentRegistry();
215
+ await registry.initialize();
216
+ if (!registry.hasPlugin(agentName)) {
217
+ console.error(chalk.red(`Unknown agent: ${agentName}`));
218
+ console.log(chalk.dim(`Available agents: ${registry.getRegisteredPlugins().map((p) => p.name).join(", ")}`));
219
+ return;
220
+ }
221
+ const plugin = await registry.getInstance({
222
+ name: `prd-chat-${agentName}`,
223
+ plugin: agentName,
224
+ options: model ? { model } : {}
225
+ });
226
+ const detection = await plugin.detect();
227
+ if (!detection.available) {
228
+ console.error(chalk.red(`Agent ${agentName} is not available: ${detection.error ?? "not found in PATH"}`));
229
+ return;
230
+ }
231
+ console.log(chalk.dim(` Agent: ${agentName}${model ? ` (model: ${model})` : ""}${detection.executablePath ? ` (${detection.executablePath})` : ""}`));
232
+ console.log("");
233
+ const history = [];
234
+ const rl = readline.createInterface({
235
+ input: process.stdin,
236
+ output: process.stdout
237
+ });
238
+ rl.on("close", () => {
239
+ console.log(chalk.yellow("\n PRD creation cancelled."));
240
+ });
241
+ try {
242
+ while (true) {
243
+ let userInput;
244
+ try {
245
+ userInput = await rl.question(chalk.cyan(" You: "));
246
+ } catch {
247
+ break;
248
+ }
249
+ const trimmed = userInput.trim();
250
+ if (!trimmed) continue;
251
+ if (trimmed.toLowerCase() === "quit" || trimmed.toLowerCase() === "exit") {
252
+ console.log(chalk.dim("\n Goodbye!"));
253
+ break;
254
+ }
255
+ history.push({
256
+ role: "user",
257
+ content: trimmed,
258
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
259
+ });
260
+ const prompt = buildConversationPrompt(history, trimmed);
261
+ process.stdout.write(chalk.dim(" AI: thinking..."));
262
+ try {
263
+ let responseText = "";
264
+ const handle = plugin.execute(prompt, {
265
+ cwd: projectDir,
266
+ timeout: 12e4,
267
+ onStdout: (data) => {
268
+ responseText += data;
269
+ }
270
+ });
271
+ const result = await handle.promise;
272
+ process.stdout.write("\r" + " ".repeat(40) + "\r");
273
+ if (result.status !== "completed") {
274
+ const errorDetail = result.error ?? result.stderr?.slice(0, 200) ?? "unknown error";
275
+ console.log(chalk.red(` AI: Error - ${errorDetail}`));
276
+ history.pop();
277
+ continue;
278
+ }
279
+ const aiResponse = extractTextFromAgentOutput(responseText);
280
+ if (!aiResponse.trim()) {
281
+ console.log(chalk.red(" AI: (empty response)"));
282
+ history.pop();
283
+ continue;
284
+ }
285
+ history.push({
286
+ role: "assistant",
287
+ content: aiResponse,
288
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
289
+ });
290
+ const lines = aiResponse.split("\n");
291
+ console.log(chalk.white(` AI: ${lines[0]}`));
292
+ for (const line of lines.slice(1)) {
293
+ console.log(chalk.white(` ${line}`));
294
+ }
295
+ console.log("");
296
+ const prdDetection = detectPrd(aiResponse);
297
+ if (prdDetection.found && prdDetection.content) {
298
+ const parsed = parsePrdMarkdown(prdDetection.content);
299
+ const doc = parsed.document;
300
+ console.log(chalk.green.bold(" PRD detected!"));
301
+ console.log(chalk.green(` Title: ${doc.title}`));
302
+ console.log(chalk.green(` Tasks: ${doc.tasks.length}`));
303
+ if (doc.tasks.length > 0) {
304
+ for (const task of doc.tasks) {
305
+ const priorityColor = task.priority <= 1 ? "red" : task.priority <= 2 ? "yellow" : "green";
306
+ console.log(` ${chalk.cyan(task.id)} ${task.title} ${chalk[priorityColor](`P${task.priority}`)}`);
307
+ }
308
+ }
309
+ console.log("");
310
+ let saveAnswer;
311
+ try {
312
+ saveAnswer = await rl.question(chalk.cyan(" Save this PRD? [Y/n] "));
313
+ } catch {
314
+ break;
315
+ }
316
+ if (saveAnswer.trim().toLowerCase() !== "n") {
317
+ const slug = slugify(doc.title || "untitled");
318
+ const filename = `prd-${slug}.md`;
319
+ const prdsDir = path.join(projectDir, ".ulpi", "prds");
320
+ if (!fs.existsSync(prdsDir)) {
321
+ fs.mkdirSync(prdsDir, { recursive: true });
322
+ }
323
+ const outputPath = path.join(prdsDir, filename);
324
+ fs.writeFileSync(outputPath, prdDetection.content, "utf-8");
325
+ console.log(chalk.green(` Saved to: ${outputPath}`));
326
+ console.log("");
327
+ let runAnswer;
328
+ try {
329
+ runAnswer = await rl.question(chalk.cyan(" Run this PRD? [y/N] "));
330
+ } catch {
331
+ break;
332
+ }
333
+ if (runAnswer.trim().toLowerCase() === "y") {
334
+ const modelArgs = model ? ` --model ${model}` : "";
335
+ console.log(chalk.dim(`
336
+ Starting: ulpi run --prd ${outputPath} --agent ${agentName}${modelArgs}
337
+ `));
338
+ rl.close();
339
+ try {
340
+ const { runRunCommand } = await import("./run-VPNXEIBY.js");
341
+ const runArgs = ["--prd", outputPath, "--agent", agentName];
342
+ if (model) runArgs.push("--model", model);
343
+ await runRunCommand(runArgs, projectDir);
344
+ } catch (runErr) {
345
+ const msg = runErr instanceof Error ? runErr.message : String(runErr);
346
+ console.error(chalk.red(` Failed to start run: ${msg}`));
347
+ }
348
+ return;
349
+ }
350
+ }
351
+ break;
352
+ }
353
+ } catch (err) {
354
+ process.stdout.write("\r" + " ".repeat(40) + "\r");
355
+ const message = err instanceof Error ? err.message : String(err);
356
+ console.log(chalk.red(` Error: ${message}`));
357
+ history.pop();
358
+ }
359
+ }
360
+ } finally {
361
+ try {
362
+ rl.close();
363
+ } catch {
364
+ }
365
+ try {
366
+ await registry.disposeInstance(`prd-chat-${agentName}`);
367
+ } catch {
368
+ }
369
+ }
370
+ }
371
+ function extractTextFromAgentOutput(raw) {
372
+ const lines = raw.split("\n").filter((l) => l.trim());
373
+ const textParts = [];
374
+ let isStreamJson = false;
375
+ for (const line of lines) {
376
+ try {
377
+ const parsed = JSON.parse(line);
378
+ isStreamJson = true;
379
+ if (parsed.type === "assistant" && parsed.message) {
380
+ const msg = parsed.message;
381
+ if (Array.isArray(msg.content)) {
382
+ for (const block of msg.content) {
383
+ if (block.type === "text") {
384
+ textParts.push(block.text);
385
+ }
386
+ }
387
+ }
388
+ }
389
+ if (parsed.type === "content_block_delta") {
390
+ const delta = parsed.delta;
391
+ if (delta && delta.type === "text_delta" && typeof delta.text === "string") {
392
+ textParts.push(delta.text);
393
+ }
394
+ }
395
+ if (parsed.type === "result" && typeof parsed.result === "string") {
396
+ textParts.push(parsed.result);
397
+ }
398
+ } catch {
399
+ if (!isStreamJson) {
400
+ textParts.push(line);
401
+ }
402
+ }
403
+ }
404
+ return textParts.join("").trim() || raw.trim();
405
+ }
406
+ export {
407
+ runPrd
408
+ };
@@ -0,0 +1,271 @@
1
+ import {
2
+ getDefaultRepo,
3
+ getRepo,
4
+ listRepos,
5
+ loadRepoRegistry,
6
+ registerRepo,
7
+ scanForRepos,
8
+ setDefaultRepo,
9
+ unregisterRepo
10
+ } from "./chunk-UXHCHOWQ.js";
11
+ import {
12
+ JsonSessionStore
13
+ } from "./chunk-SEU7WWNQ.js";
14
+ import "./chunk-C7CLUQI6.js";
15
+ import "./chunk-4VNS5WPM.js";
16
+
17
+ // src/commands/repos.ts
18
+ import * as fs from "fs";
19
+ import * as path from "path";
20
+ import chalk from "chalk";
21
+ function runRepos(args) {
22
+ const subcommand = args[0];
23
+ switch (subcommand) {
24
+ case "list":
25
+ case "ls":
26
+ listReposCmd();
27
+ break;
28
+ case "add":
29
+ addRepoCmd(args[1]);
30
+ break;
31
+ case "remove":
32
+ case "rm":
33
+ removeRepoCmd(args[1]);
34
+ break;
35
+ case "default":
36
+ defaultRepoCmd(args[1]);
37
+ break;
38
+ case "scan":
39
+ scanReposCmd(args[1]);
40
+ break;
41
+ case "status":
42
+ statusCmd();
43
+ break;
44
+ default:
45
+ printUsage();
46
+ }
47
+ }
48
+ function printUsage() {
49
+ console.log(`
50
+ ${chalk.bold("ULPI \u2014 Repo Management")}
51
+
52
+ Usage: ulpi repos <command>
53
+
54
+ Commands:
55
+ list, ls List all registered repos
56
+ add <path> Register a repo
57
+ remove, rm <id|path> Unregister a repo
58
+ default [id|path] Get/set default repo
59
+ scan [root-dir] Discover repos with .ulpi/
60
+ status Global health overview
61
+
62
+ Examples:
63
+ ulpi repos list
64
+ ulpi repos add /path/to/my-repo
65
+ ulpi repos default my-webapp-id
66
+ ulpi repos scan ~/projects
67
+ `.trim());
68
+ }
69
+ function listReposCmd() {
70
+ const repos = listRepos();
71
+ const defaultRepo = getDefaultRepo();
72
+ if (repos.length === 0) {
73
+ console.log(chalk.yellow("\nNo repos registered."));
74
+ console.log("Run 'ulpi repos add <path>' to register a repo.");
75
+ console.log("Or run 'ulpi init' in a repo directory.\n");
76
+ return;
77
+ }
78
+ console.log(chalk.bold(`
79
+ Repos (${repos.length} registered)
80
+ `));
81
+ for (const repo of repos) {
82
+ const isDefault = defaultRepo?.id === repo.id;
83
+ const prefix = isDefault ? chalk.yellow("\u2605") : " ";
84
+ const name = chalk.bold(repo.name);
85
+ const defaultLabel = isDefault ? chalk.dim(" (default)") : "";
86
+ console.log(` ${prefix} ${name}${defaultLabel}`);
87
+ console.log(chalk.dim(` ${repo.path}`));
88
+ if (repo.stack) {
89
+ const stackParts = [];
90
+ if (repo.stack.runtime) stackParts.push(repo.stack.runtime);
91
+ if (repo.stack.framework) stackParts.push(repo.stack.framework);
92
+ if (repo.stack.packageManager) stackParts.push(repo.stack.packageManager);
93
+ if (stackParts.length > 0) {
94
+ console.log(chalk.dim(` Stack: ${stackParts.join(" + ")}`));
95
+ }
96
+ }
97
+ const statusIcon = getStatusIcon(repo);
98
+ const statusLabel = getStatusLabel(repo);
99
+ console.log(` Status: ${statusIcon} ${statusLabel}`);
100
+ const lastAccessed = formatRelativeTime(repo.lastAccessed);
101
+ console.log(chalk.dim(` Last accessed: ${lastAccessed}`));
102
+ console.log();
103
+ }
104
+ }
105
+ function addRepoCmd(repoPath) {
106
+ if (!repoPath) {
107
+ console.log(chalk.red("Error: Repo path required."));
108
+ console.log("Usage: ulpi repos add <path>");
109
+ return;
110
+ }
111
+ const absPath = path.resolve(repoPath);
112
+ if (!fs.existsSync(absPath)) {
113
+ console.log(chalk.red(`Error: Path does not exist: ${absPath}`));
114
+ return;
115
+ }
116
+ if (!fs.statSync(absPath).isDirectory()) {
117
+ console.log(chalk.red(`Error: Path is not a directory: ${absPath}`));
118
+ return;
119
+ }
120
+ const entry = registerRepo(absPath);
121
+ console.log(chalk.green(`Registered: ${entry.name}`));
122
+ console.log(chalk.dim(` ID: ${entry.id}`));
123
+ console.log(chalk.dim(` Path: ${entry.path}`));
124
+ console.log(chalk.dim(` Status: ${getStatusLabel(entry)}`));
125
+ }
126
+ function removeRepoCmd(idOrPath) {
127
+ if (!idOrPath) {
128
+ console.log(chalk.red("Error: Repo ID or path required."));
129
+ console.log("Usage: ulpi repos remove <id|path>");
130
+ return;
131
+ }
132
+ const repo = getRepo(idOrPath);
133
+ if (!repo) {
134
+ console.log(chalk.red(`Error: Repo not found: ${idOrPath}`));
135
+ return;
136
+ }
137
+ const removed = unregisterRepo(idOrPath);
138
+ if (removed) {
139
+ console.log(chalk.green(`Unregistered: ${repo.name}`));
140
+ } else {
141
+ console.log(chalk.red(`Error: Failed to unregister repo.`));
142
+ }
143
+ }
144
+ function defaultRepoCmd(idOrPath) {
145
+ if (!idOrPath) {
146
+ const defaultRepo = getDefaultRepo();
147
+ if (defaultRepo) {
148
+ console.log(`Default repo: ${chalk.bold(defaultRepo.name)}`);
149
+ console.log(chalk.dim(` ID: ${defaultRepo.id}`));
150
+ console.log(chalk.dim(` Path: ${defaultRepo.path}`));
151
+ } else {
152
+ console.log(chalk.yellow("No default repo set."));
153
+ console.log("Use 'ulpi repos default <id|path>' to set one.");
154
+ }
155
+ return;
156
+ }
157
+ if (idOrPath === "none" || idOrPath === "clear") {
158
+ setDefaultRepo(void 0);
159
+ console.log(chalk.green("Default repo cleared."));
160
+ return;
161
+ }
162
+ const repo = getRepo(idOrPath);
163
+ if (!repo) {
164
+ console.log(chalk.red(`Error: Repo not found: ${idOrPath}`));
165
+ return;
166
+ }
167
+ const success = setDefaultRepo(repo.id);
168
+ if (success) {
169
+ console.log(chalk.green(`Default repo set to: ${repo.name}`));
170
+ } else {
171
+ console.log(chalk.red("Error: Failed to set default repo."));
172
+ }
173
+ }
174
+ function scanReposCmd(rootDir) {
175
+ const scanRoot = rootDir ? path.resolve(rootDir) : process.cwd();
176
+ if (!fs.existsSync(scanRoot)) {
177
+ console.log(chalk.red(`Error: Path does not exist: ${scanRoot}`));
178
+ return;
179
+ }
180
+ console.log(chalk.cyan(`Scanning for repos in: ${scanRoot}
181
+ `));
182
+ const found = scanForRepos(scanRoot);
183
+ if (found.length === 0) {
184
+ console.log(chalk.yellow("No repos with .ulpi/ found."));
185
+ return;
186
+ }
187
+ console.log(`Found ${found.length} repo(s):
188
+ `);
189
+ for (const repoPath of found) {
190
+ const existing = getRepo(repoPath);
191
+ const status = existing ? chalk.dim("(already registered)") : "";
192
+ console.log(` ${chalk.green("\u2713")} ${repoPath} ${status}`);
193
+ if (!existing) {
194
+ registerRepo(repoPath);
195
+ }
196
+ }
197
+ console.log(chalk.green(`
198
+ All repos registered.`));
199
+ }
200
+ function statusCmd() {
201
+ const repos = listRepos();
202
+ const registry = loadRepoRegistry();
203
+ const store = new JsonSessionStore();
204
+ console.log(chalk.bold("\nULPI \u2014 Global Status\n"));
205
+ console.log(chalk.cyan("Summary"));
206
+ console.log(` Total repos: ${repos.length}`);
207
+ const configured = repos.filter((r) => r.configStatus === "configured").length;
208
+ const withHooks = repos.filter((r) => r.hooksInstalled).length;
209
+ console.log(` Configured: ${configured}`);
210
+ console.log(` Hooks installed: ${withHooks}`);
211
+ if (registry.defaultRepo) {
212
+ const defaultRepo = getRepo(registry.defaultRepo);
213
+ if (defaultRepo) {
214
+ console.log(` Default: ${defaultRepo.name}`);
215
+ }
216
+ }
217
+ const allSessions = store.list();
218
+ console.log(` Total sessions: ${allSessions.length}`);
219
+ console.log();
220
+ if (repos.length > 0) {
221
+ console.log(chalk.cyan("Repos"));
222
+ for (const repo of repos) {
223
+ const statusIcon = getStatusIcon(repo);
224
+ const isDefault = registry.defaultRepo === repo.id;
225
+ const defaultLabel = isDefault ? chalk.yellow(" \u2605") : "";
226
+ console.log(` ${statusIcon} ${repo.name}${defaultLabel}`);
227
+ const repoSessions = store.listByProject(repo.path);
228
+ if (repoSessions.length > 0) {
229
+ console.log(chalk.dim(` Sessions: ${repoSessions.length}`));
230
+ }
231
+ }
232
+ }
233
+ console.log();
234
+ }
235
+ function getStatusIcon(repo) {
236
+ if (repo.configStatus === "configured" && repo.hooksInstalled) {
237
+ return chalk.green("\u2713");
238
+ }
239
+ if (repo.configStatus === "configured" || repo.hooksInstalled) {
240
+ return chalk.yellow("\u26A0");
241
+ }
242
+ return chalk.red("\u2717");
243
+ }
244
+ function getStatusLabel(repo) {
245
+ if (repo.configStatus === "configured" && repo.hooksInstalled) {
246
+ return chalk.green("Configured");
247
+ }
248
+ if (repo.configStatus === "configured" && !repo.hooksInstalled) {
249
+ return chalk.yellow("Rules configured, hooks not installed");
250
+ }
251
+ if (repo.configStatus !== "configured" && repo.hooksInstalled) {
252
+ return chalk.yellow("Hooks installed, no rules");
253
+ }
254
+ return chalk.red("Not configured");
255
+ }
256
+ function formatRelativeTime(isoString) {
257
+ const date = new Date(isoString);
258
+ const now = /* @__PURE__ */ new Date();
259
+ const diffMs = now.getTime() - date.getTime();
260
+ const diffMins = Math.floor(diffMs / 6e4);
261
+ const diffHours = Math.floor(diffMins / 60);
262
+ const diffDays = Math.floor(diffHours / 24);
263
+ if (diffMins < 1) return "just now";
264
+ if (diffMins < 60) return `${diffMins} minute${diffMins === 1 ? "" : "s"} ago`;
265
+ if (diffHours < 24) return `${diffHours} hour${diffHours === 1 ? "" : "s"} ago`;
266
+ if (diffDays < 7) return `${diffDays} day${diffDays === 1 ? "" : "s"} ago`;
267
+ return date.toLocaleDateString();
268
+ }
269
+ export {
270
+ runRepos
271
+ };
@@ -0,0 +1,14 @@
1
+ import {
2
+ collectPostReviewContexts,
3
+ extractPlanForReview,
4
+ isReviewEnabled,
5
+ runPlanReviewSession
6
+ } from "./chunk-V2H5D6Y3.js";
7
+ import "./chunk-C7CLUQI6.js";
8
+ import "./chunk-4VNS5WPM.js";
9
+ export {
10
+ collectPostReviewContexts,
11
+ extractPlanForReview,
12
+ isReviewEnabled,
13
+ runPlanReviewSession
14
+ };