@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.
- package/LICENSE +21 -0
- package/dist/{auth-PN7TMQHV-2W4ICG64.js → auth-FWM7MM4Q-VZC3U2XZ.js} +1 -1
- package/dist/{auth-BFFBUJUC.js → auth-HDK7ECJL.js} +2 -1
- package/dist/{chunk-RJIRWQJD.js → chunk-3BCW6ABU.js} +402 -142
- package/dist/{chunk-L3PWNHSA.js → chunk-3WB5CXH4.js} +180 -5
- package/dist/{chunk-K4OVPFY2.js → chunk-4UCJIAOU.js} +2 -2
- package/dist/chunk-4XTHZVDS.js +109 -0
- package/dist/chunk-4ZPOZULQ.js +6522 -0
- package/dist/{chunk-SIAQVRKG.js → chunk-5MI5GIXM.js} +48 -2
- package/dist/{chunk-KLEASXUR.js → chunk-6ZL6NXMV.js} +1 -1
- package/dist/{chunk-AV5RB3N2.js → chunk-76D3BYJD.js} +48 -0
- package/dist/{chunk-DOIKS6C5.js → chunk-AWOSRA5F.js} +1 -1
- package/dist/{chunk-UCMT5OKP.js → chunk-BFEKZZHM.js} +274 -57
- package/dist/chunk-C7CLUQI6.js +1286 -0
- package/dist/{chunk-ELTGWMDE.js → chunk-E3B5NROU.js} +7 -7
- package/dist/chunk-EJ7TW77N.js +1418 -0
- package/dist/{chunk-6OURRFP7.js → chunk-IV6MWETF.js} +383 -168
- package/dist/chunk-IZPJHSPX.js +1478 -0
- package/dist/chunk-JLHNLM3C.js +228 -0
- package/dist/{chunk-P2RESJRN.js → chunk-KYYI23AQ.js} +2 -2
- package/dist/chunk-S6ANCSYO.js +1271 -0
- package/dist/chunk-SEU7WWNQ.js +1251 -0
- package/dist/chunk-SNQ7NAIS.js +453 -0
- package/dist/{ulpi-RMMCUAGP-EWYUE7RU.js → chunk-TSLDGT5O.js} +73 -35
- package/dist/{chunk-EIWYSP3A.js → chunk-UXHCHOWQ.js} +83 -62
- package/dist/chunk-V2H5D6Y3.js +146 -0
- package/dist/{chunk-5SCG7UYM.js → chunk-VVEDXI7E.js} +1 -1
- package/dist/chunk-VXH5Y4FO.js +6761 -0
- package/dist/chunk-WED4LM5N.js +322 -0
- package/dist/{chunk-74WVVWJ4.js → chunk-YOKL7RB5.js} +184 -15
- package/dist/chunk-Z53CAR7G.js +298 -0
- package/dist/{ci-JQ56YIKC.js → ci-X3U2W4HC.js} +124 -26
- package/dist/cloud-2F3NLVHN.js +274 -0
- package/dist/{codemap-HMYBXJL2.js → codemap-XNGMAF3F.js} +37 -37
- package/dist/codex-MB5YTMRT.js +132 -0
- package/dist/{config-YYWEN7U2.js → config-OOELBYTH.js} +1 -1
- package/dist/dist-2BJYR5EI.js +59 -0
- package/dist/dist-3EIQTZHT.js +1380 -0
- package/dist/{dist-WAMAQVPK.js → dist-4U5L2X2C.js} +2 -2
- package/dist/{dist-4XTJ6HLM.js → dist-54KAMNLO.js} +16 -15
- package/dist/dist-6M4MZWZW.js +58 -0
- package/dist/dist-6X576SU2.js +27 -0
- package/dist/dist-7QOEYLFX.js +103 -0
- package/dist/dist-AYBGHEDY.js +2541 -0
- package/dist/dist-EK45QNEM.js +45 -0
- package/dist/{dist-U7ZIJMZD.js → dist-FKFEJRPX.js} +16 -15
- package/dist/dist-GTEJUBBT.js +66 -0
- package/dist/dist-HA74OKJZ.js +40 -0
- package/dist/{dist-XG2GG5SD.js → dist-HU5RZAON.js} +14 -2
- package/dist/dist-IYE3OBRB.js +374 -0
- package/dist/{dist-7WLLPWWB.js → dist-JLU26AB6.js} +12 -9
- package/dist/{dist-6G7JC2RA.js → dist-KUCI6JFE.js} +49 -9
- package/dist/dist-NUEMFZFL.js +33 -0
- package/dist/{dist-GWGTAHNM.js → dist-NUXMDXZ3.js} +31 -3
- package/dist/{dist-5R4RYNQO.js → dist-YCNWHSLN.js} +15 -5
- package/dist/{dist-6MFVWIFF.js → dist-YFFG2ZD6.js} +9 -16
- package/dist/dist-ZG4OKCSR.js +15 -0
- package/dist/doctor-SI4LLLDZ.js +345 -0
- package/dist/{export-import-4A5MWLIA.js → export-import-JFQH4KSJ.js} +1 -1
- package/dist/{history-RNUWO4JZ.js → history-5NE46ZAH.js} +7 -7
- package/dist/{hooks-installer-K2JXEBNN.js → hooks-installer-UN5JZLDQ.js} +2 -2
- package/dist/index.js +394 -618
- package/dist/{init-NQWFZPKO.js → init-5FK3VKRT.js} +76 -10
- package/dist/job-HIDMAFW2.js +376 -0
- package/dist/jobs.memory-PLMMSFHB-VBECCTHN.js +33 -0
- package/dist/kiro-VMUHDFGK.js +153 -0
- package/dist/{launchd-OYXUAVW6.js → launchd-6AWT54HR.js} +9 -17
- package/dist/mcp-PDUD7SGP.js +249 -0
- package/dist/mcp-installer-PQU3XOGO.js +259 -0
- package/dist/mcp-setup-OA7IB3H3.js +263 -0
- package/dist/{memory-D6ZFFCI2.js → memory-ZNAEAK3B.js} +17 -17
- package/dist/{ollama-3XCUZMZT-FYKHW4TZ.js → ollama-3XCUZMZT-4JMH6B7P.js} +1 -1
- package/dist/{openai-E7G2YAHU-IG33BFYF.js → openai-E7G2YAHU-T3HMBPH7.js} +2 -2
- package/dist/portal-JYWVHXDU.js +210 -0
- package/dist/prd-Q4J5NVAR.js +408 -0
- package/dist/repos-WWZXNN3P.js +271 -0
- package/dist/review-integration-5WHEJU2A.js +14 -0
- package/dist/{rules-3OFGWHP4.js → rules-Y4VSOY5Y.js} +3 -3
- package/dist/run-VPNXEIBY.js +687 -0
- package/dist/server-COL4AXKU-P7S7NNF6.js +11 -0
- package/dist/server-KKSETHDV-XSSLEENT.js +20 -0
- package/dist/{skills-GY2CTPWN.js → skills-QEYU2N27.js} +4 -2
- package/dist/start-JYOEL7AJ.js +303 -0
- package/dist/{status-SE43TIFJ.js → status-BHQYYGAL.js} +2 -2
- package/dist/{templates-O2XDKB5R.js → templates-CBRUJ66V.js} +6 -5
- package/dist/tui-DP7736EX.js +61 -0
- package/dist/ulpi-5EN6JCAS-LFE3WSL4.js +10 -0
- package/dist/{uninstall-KWGSGZTI.js → uninstall-ICUV6DDV.js} +3 -3
- package/dist/{update-QYZA4D23.js → update-7ZMAYRBH.js} +3 -3
- package/dist/{version-checker-MVB74DEX.js → version-checker-4ZFMZA7Y.js} +2 -2
- package/package.json +39 -31
- package/dist/chunk-26LLDX2T.js +0 -553
- package/dist/chunk-DDRLI6JU.js +0 -331
- package/dist/chunk-IFATANHR.js +0 -453
- package/dist/chunk-JWUUVXIV.js +0 -13694
- package/dist/chunk-LD52XG3X.js +0 -4273
- package/dist/chunk-MIAQVCFW.js +0 -39
- package/dist/chunk-YYZOFYS6.js +0 -415
- package/dist/dist-XD4YI27T.js +0 -26
- package/dist/mcp-installer-TOYDP77X.js +0 -124
- package/dist/projects-COUJP4ZC.js +0 -271
- package/dist/review-KMGP2S25.js +0 -152
- package/dist/server-USLHY6GH-F4JSXCWA.js +0 -18
- package/dist/server-X5P6WH2M-ULZF5WHZ.js +0 -11
- package/dist/skills/ulpi-generate-guardian/SKILL.md +0 -750
- package/dist/skills/ulpi-generate-guardian/references/framework-rules.md +0 -849
- package/dist/skills/ulpi-generate-guardian/references/language-rules.md +0 -591
- package/dist/ui-4SM2SUI6.js +0 -167
- 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
|
+
};
|