@ghyper9023/pi-dev-workflow 0.2.0 → 0.3.1

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.
@@ -35,53 +35,69 @@ const PROGRESS_INTERVAL_MS = 1_500;
35
35
  /** Hard limit on accumulated output per subagent (prevents OOM on runaway). */
36
36
  const MAX_BUFFER_BYTES = 500_000;
37
37
 
38
- /** Locations to auto-load APPEND_SYSTEM.md / append.system.md from (checked in order). */
39
- const APPEND_SYSTEM_PATHS = [
40
- path.join(osHomedir(), ".pi", "agent", "append.system.md"),
41
- ".pi/append.system.md",
42
- "APPEND_SYSTEM.md",
43
- ];
38
+ /**
39
+ * Locations to auto-load APPEND_SYSTEM.md / append.system.md from (checked in order).
40
+ * Defined as a getter so that osHomedir() is called lazily rather than at module init,
41
+ * avoiding a potential crash when the module is loaded in restricted environments.
42
+ */
43
+ function getAppendSystemPaths(): string[] {
44
+ return [
45
+ path.join(osHomedir(), ".pi", "agent", "append.system.md"),
46
+ ".pi/append.system.md",
47
+ "APPEND_SYSTEM.md",
48
+ ];
49
+ }
44
50
 
45
- // Lazily resolved
46
- let _appendSystemContent: string | null | undefined; // undefined = not checked yet
51
+ /** Cache for append.system.md content, keyed by cwd to support multi-project isolation. */
52
+ const _appendSystemCache = new Map<string, string | null>();
47
53
 
48
54
  function loadAppendSystem(cwd: string): string | null {
49
- if (_appendSystemContent !== undefined) return _appendSystemContent;
50
- for (const loc of APPEND_SYSTEM_PATHS) {
55
+ const cached = _appendSystemCache.get(cwd);
56
+ if (cached !== undefined) return cached;
57
+ for (const loc of getAppendSystemPaths()) {
51
58
  const abs = path.isAbsolute(loc) ? loc : path.join(cwd, loc);
52
59
  try {
53
60
  const content = fs.readFileSync(abs, "utf-8").trim();
54
61
  if (content) {
55
- _appendSystemContent = content;
62
+ _appendSystemCache.set(cwd, content);
56
63
  return content;
57
64
  }
58
65
  } catch {
59
66
  // file not found or unreadable, try next
60
67
  }
61
68
  }
62
- _appendSystemContent = null;
69
+ _appendSystemCache.set(cwd, null);
63
70
  return null;
64
71
  }
65
72
 
66
- /** Find the newest HTML review file in pi-review/ directory. */
73
+ /** Find the newest HTML review file in pi-review/ or pi-dev-output/pi-review/ directory. */
67
74
  function findNewestReviewHtml(cwd: string): string {
68
- try {
69
- const reviewDir = path.join(cwd, "pi-review");
70
- if (fs.existsSync(reviewDir)) {
71
- const files = fs.readdirSync(reviewDir)
72
- .filter(f => f.endsWith(".html"))
73
- .map(f => ({
74
- name: f,
75
- mtime: fs.statSync(path.join(reviewDir, f)).mtimeMs,
76
- }))
77
- .sort((a, b) => b.mtime - a.mtime);
78
- if (files.length > 0) {
79
- return "pi-review/" + files[0].name;
75
+ const candidates = [
76
+ path.join(cwd, "pi-review"),
77
+ path.join(cwd, "pi-dev-output", "pi-review"),
78
+ ];
79
+
80
+ for (const reviewDir of candidates) {
81
+ try {
82
+ if (fs.existsSync(reviewDir)) {
83
+ const files = fs.readdirSync(reviewDir)
84
+ .filter(f => f.endsWith(".html"))
85
+ .map(f => ({
86
+ name: f,
87
+ mtime: fs.statSync(path.join(reviewDir, f)).mtimeMs,
88
+ }))
89
+ .sort((a, b) => b.mtime - a.mtime);
90
+ if (files.length > 0) {
91
+ // Return relative path from cwd
92
+ const rel = path.relative(cwd, reviewDir);
93
+ return rel + "/" + files[0].name;
94
+ }
80
95
  }
96
+ } catch {
97
+ // ignore fs errors
81
98
  }
82
- } catch {
83
- // ignore fs errors
84
99
  }
100
+
85
101
  return "";
86
102
  }
87
103
 
@@ -108,7 +124,10 @@ function osHomedir(): string {
108
124
  const activeChildren = new Set<import("node:child_process").ChildProcess>();
109
125
 
110
126
  function killAllChildren(): void {
111
- for (const proc of activeChildren) {
127
+ // Snapshot the Set to avoid potential iteration issues if exit callbacks
128
+ // modify activeChildren during the loop.
129
+ const children = [...activeChildren];
130
+ for (const proc of children) {
112
131
  try {
113
132
  if (proc.pid !== undefined && !proc.killed) {
114
133
  proc.kill("SIGTERM");
@@ -337,9 +356,7 @@ export async function spawnSubagent(
337
356
  onProgress(`[${agent.name}] (${elapsed}s) ⏳ 处理中...`);
338
357
  }
339
358
  }, PROGRESS_INTERVAL_MS);
340
- if (typeof progressTimer === "object" && "unref" in progressTimer) {
341
- progressTimer.unref();
342
- }
359
+ progressTimer.unref();
343
360
 
344
361
  const settle = (result: SubagentResult) => {
345
362
  if (settled) return;
@@ -402,7 +419,7 @@ export async function spawnSubagent(
402
419
  durationMs: 0,
403
420
  });
404
421
  }, effectiveTimeout);
405
- if (typeof timer === "object" && "unref" in timer) timer.unref();
422
+ timer.unref();
406
423
 
407
424
  // ── Abort signal wiring ─────────────────────────────
408
425
  if (signal) {
@@ -422,6 +439,7 @@ export function extractFinalOutput(jsonOutput: string): string {
422
439
  // Parse JSON lines from --mode json output
423
440
  // Try multiple event formats to find the final assistant response
424
441
  let result = "";
442
+ let textEndSeen = false;
425
443
 
426
444
  for (const line of jsonOutput.split("\n")) {
427
445
  if (!line.trim()) continue;
@@ -430,7 +448,9 @@ export function extractFinalOutput(jsonOutput: string): string {
430
448
 
431
449
  // Format 1: pi's --mode json message_update with text_delta (streaming)
432
450
  // {"type":"message_update","assistantMessageEvent":{"type":"text_delta","delta":"..."}}
433
- if (event.type === "message_update" &&
451
+ // Once text_end has been seen, skip subsequent text_delta events to
452
+ // avoid appending stale deltas that arrive out of order.
453
+ if (!textEndSeen && event.type === "message_update" &&
434
454
  event.assistantMessageEvent?.type === "text_delta") {
435
455
  result += event.assistantMessageEvent.delta || "";
436
456
  }
@@ -441,6 +461,7 @@ export function extractFinalOutput(jsonOutput: string): string {
441
461
  event.assistantMessageEvent?.type === "text_end" &&
442
462
  event.assistantMessageEvent.content) {
443
463
  result = event.assistantMessageEvent.content;
464
+ textEndSeen = true;
444
465
  }
445
466
 
446
467
  // Format 2: Anthropic-style message events
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ghyper9023/pi-dev-workflow",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "keywords": [
5
5
  "pi-package"
6
6
  ],
@@ -0,0 +1,47 @@
1
+ # ADR Format
2
+
3
+ ADRs live in `docs/adr/` and use sequential numbering: `0001-slug.md`, `0002-slug.md`, etc.
4
+
5
+ Create the `docs/adr/` directory lazily — only when the first ADR is needed.
6
+
7
+ ## Template
8
+
9
+ ```md
10
+ # {Short title of the decision}
11
+
12
+ {1-3 sentences: what's the context, what did we decide, and why.}
13
+ ```
14
+
15
+ That's it. An ADR can be a single paragraph. The value is in recording *that* a decision was made and *why* — not in filling out sections.
16
+
17
+ ## Optional sections
18
+
19
+ Only include these when they add genuine value. Most ADRs won't need them.
20
+
21
+ - **Status** frontmatter (`proposed | accepted | deprecated | superseded by ADR-NNNN`) — useful when decisions are revisited
22
+ - **Considered Options** — only when the rejected alternatives are worth remembering
23
+ - **Consequences** — only when non-obvious downstream effects need to be called out
24
+
25
+ ## Numbering
26
+
27
+ Scan `docs/adr/` for the highest existing number and increment by one.
28
+
29
+ ## When to offer an ADR
30
+
31
+ All three of these must be true:
32
+
33
+ 1. **Hard to reverse** — the cost of changing your mind later is meaningful
34
+ 2. **Surprising without context** — a future reader will look at the code and wonder "why on earth did they do it this way?"
35
+ 3. **The result of a real trade-off** — there were genuine alternatives and you picked one for specific reasons
36
+
37
+ If a decision is easy to reverse, skip it — you'll just reverse it. If it's not surprising, nobody will wonder why. If there was no real alternative, there's nothing to record beyond "we did the obvious thing."
38
+
39
+ ### What qualifies
40
+
41
+ - **Architectural shape.** "We're using a monorepo." "The write model is event-sourced, the read model is projected into Postgres."
42
+ - **Integration patterns between contexts.** "Ordering and Billing communicate via domain events, not synchronous HTTP."
43
+ - **Technology choices that carry lock-in.** Database, message bus, auth provider, deployment target. Not every library — just the ones that would take a quarter to swap out.
44
+ - **Boundary and scope decisions.** "Customer data is owned by the Customer context; other contexts reference it by ID only." The explicit no-s are as valuable as the yes-s.
45
+ - **Deliberate deviations from the obvious path.** "We're using manual SQL instead of an ORM because X." Anything where a reasonable reader would assume the opposite. These stop the next engineer from "fixing" something that was deliberate.
46
+ - **Constraints not visible in the code.** "We can't use AWS because of compliance requirements." "Response times must be under 200ms because of the partner API contract."
47
+ - **Rejected alternatives when the rejection is non-obvious.** If you considered GraphQL and picked REST for subtle reasons, record it — otherwise someone will suggest GraphQL again in six months.
@@ -0,0 +1,77 @@
1
+ # CONTEXT.md Format
2
+
3
+ ## Structure
4
+
5
+ ```md
6
+ # {Context Name}
7
+
8
+ {One or two sentence description of what this context is and why it exists.}
9
+
10
+ ## Language
11
+
12
+ **Order**:
13
+ {A concise description of the term}
14
+ _Avoid_: Purchase, transaction
15
+
16
+ **Invoice**:
17
+ A request for payment sent to a customer after delivery.
18
+ _Avoid_: Bill, payment request
19
+
20
+ **Customer**:
21
+ A person or organization that places orders.
22
+ _Avoid_: Client, buyer, account
23
+
24
+ ## Relationships
25
+
26
+ - An **Order** produces one or more **Invoices**
27
+ - An **Invoice** belongs to exactly one **Customer**
28
+
29
+ ## Example dialogue
30
+
31
+ > **Dev:** "When a **Customer** places an **Order**, do we create the **Invoice** immediately?"
32
+ > **Domain expert:** "No — an **Invoice** is only generated once a **Fulfillment** is confirmed."
33
+
34
+ ## Flagged ambiguities
35
+
36
+ - "account" was used to mean both **Customer** and **User** — resolved: these are distinct concepts.
37
+ ```
38
+
39
+ ## Rules
40
+
41
+ - **Be opinionated.** When multiple words exist for the same concept, pick the best one and list the others as aliases to avoid.
42
+ - **Flag conflicts explicitly.** If a term is used ambiguously, call it out in "Flagged ambiguities" with a clear resolution.
43
+ - **Keep definitions tight.** One sentence max. Define what it IS, not what it does.
44
+ - **Show relationships.** Use bold term names and express cardinality where obvious.
45
+ - **Only include terms specific to this project's context.** General programming concepts (timeouts, error types, utility patterns) don't belong even if the project uses them extensively. Before adding a term, ask: is this a concept unique to this context, or a general programming concept? Only the former belongs.
46
+ - **Group terms under subheadings** when natural clusters emerge. If all terms belong to a single cohesive area, a flat list is fine.
47
+ - **Write an example dialogue.** A conversation between a dev and a domain expert that demonstrates how the terms interact naturally and clarifies boundaries between related concepts.
48
+
49
+ ## Single vs multi-context repos
50
+
51
+ **Single context (most repos):** One `CONTEXT.md` at the repo root.
52
+
53
+ **Multiple contexts:** A `CONTEXT-MAP.md` at the repo root lists the contexts, where they live, and how they relate to each other:
54
+
55
+ ```md
56
+ # Context Map
57
+
58
+ ## Contexts
59
+
60
+ - [Ordering](./src/ordering/CONTEXT.md) — receives and tracks customer orders
61
+ - [Billing](./src/billing/CONTEXT.md) — generates invoices and processes payments
62
+ - [Fulfillment](./src/fulfillment/CONTEXT.md) — manages warehouse picking and shipping
63
+
64
+ ## Relationships
65
+
66
+ - **Ordering → Fulfillment**: Ordering emits `OrderPlaced` events; Fulfillment consumes them to start picking
67
+ - **Fulfillment → Billing**: Fulfillment emits `ShipmentDispatched` events; Billing consumes them to generate invoices
68
+ - **Ordering ↔ Billing**: Shared types for `CustomerId` and `Money`
69
+ ```
70
+
71
+ The skill infers which structure applies:
72
+
73
+ - If `CONTEXT-MAP.md` exists, read it to find contexts
74
+ - If only a root `CONTEXT.md` exists, single context
75
+ - If neither exists, create a root `CONTEXT.md` lazily when the first term is resolved
76
+
77
+ When multiple contexts exist, infer which one the current topic relates to. If unclear, ask.
@@ -0,0 +1,88 @@
1
+ ---
2
+ name: grill-with-docs
3
+ description: Grilling session that challenges your plan against the existing domain model, sharpens terminology, and updates documentation (CONTEXT.md, ADRs) inline as decisions crystallise. Use when user wants to stress-test a plan against their project's language and documented decisions.
4
+ ---
5
+
6
+ <what-to-do>
7
+
8
+ Interview me relentlessly about every aspect of this plan until we reach a shared understanding. Walk down each branch of the design tree, resolving dependencies between decisions one-by-one. For each question, provide your recommended answer.
9
+
10
+ Ask the questions one at a time, waiting for feedback on each question before continuing.
11
+
12
+ If a question can be answered by exploring the codebase, explore the codebase instead.
13
+
14
+ </what-to-do>
15
+
16
+ <supporting-info>
17
+
18
+ ## Domain awareness
19
+
20
+ During codebase exploration, also look for existing documentation:
21
+
22
+ ### File structure
23
+
24
+ Most repos have a single context:
25
+
26
+ ```
27
+ /
28
+ ├── CONTEXT.md
29
+ ├── docs/
30
+ │ └── adr/
31
+ │ ├── 0001-event-sourced-orders.md
32
+ │ └── 0002-postgres-for-write-model.md
33
+ └── src/
34
+ ```
35
+
36
+ If a `CONTEXT-MAP.md` exists at the root, the repo has multiple contexts. The map points to where each one lives:
37
+
38
+ ```
39
+ /
40
+ ├── CONTEXT-MAP.md
41
+ ├── docs/
42
+ │ └── adr/ ← system-wide decisions
43
+ ├── src/
44
+ │ ├── ordering/
45
+ │ │ ├── CONTEXT.md
46
+ │ │ └── docs/adr/ ← context-specific decisions
47
+ │ └── billing/
48
+ │ ├── CONTEXT.md
49
+ │ └── docs/adr/
50
+ ```
51
+
52
+ Create files lazily — only when you have something to write. If no `CONTEXT.md` exists, create one when the first term is resolved. If no `docs/adr/` exists, create it when the first ADR is needed.
53
+
54
+ ## During the session
55
+
56
+ ### Challenge against the glossary
57
+
58
+ When the user uses a term that conflicts with the existing language in `CONTEXT.md`, call it out immediately. "Your glossary defines 'cancellation' as X, but you seem to mean Y — which is it?"
59
+
60
+ ### Sharpen fuzzy language
61
+
62
+ When the user uses vague or overloaded terms, propose a precise canonical term. "You're saying 'account' — do you mean the Customer or the User? Those are different things."
63
+
64
+ ### Discuss concrete scenarios
65
+
66
+ When domain relationships are being discussed, stress-test them with specific scenarios. Invent scenarios that probe edge cases and force the user to be precise about the boundaries between concepts.
67
+
68
+ ### Cross-reference with code
69
+
70
+ When the user states how something works, check whether the code agrees. If you find a contradiction, surface it: "Your code cancels entire Orders, but you just said partial cancellation is possible — which is right?"
71
+
72
+ ### Update CONTEXT.md inline
73
+
74
+ When a term is resolved, update `CONTEXT.md` right there. Don't batch these up — capture them as they happen. Use the format in [CONTEXT-FORMAT.md](./CONTEXT-FORMAT.md).
75
+
76
+ `CONTEXT.md` should be totally devoid of implementation details. Do not treat `CONTEXT.md` as a spec, a scratch pad, or a repository for implementation decisions. It is a glossary and nothing else.
77
+
78
+ ### Offer ADRs sparingly
79
+
80
+ Only offer to create an ADR when all three are true:
81
+
82
+ 1. **Hard to reverse** — the cost of changing your mind later is meaningful
83
+ 2. **Surprising without context** — a future reader will wonder "why did they do it this way?"
84
+ 3. **The result of a real trade-off** — there were genuine alternatives and you picked one for specific reasons
85
+
86
+ If any of the three is missing, skip the ADR. Use the format in [ADR-FORMAT.md](./ADR-FORMAT.md).
87
+
88
+ </supporting-info>
@@ -4,9 +4,9 @@ description: review 代码并输出交互式 HTML 报告。适用于代码审查
4
4
  ---
5
5
 
6
6
  你是一个代码审查助手。用户要求你审查**当前代码库**的改动,具体包括:
7
- - `git diff` 相对于 HEAD 的所有未提交改动(未指明范围时默认行为,如diff无变动则默认最近1次commit,默认不允许探索整个代码库除非用户明确要求)
8
- - 最近 **2(或几个) commit** 的完整改动(使用 `git log -p -n 2`)
9
- - 完整的整个代码库
7
+ - `git diff HEAD` 相对于 HEAD 的所有未提交改动(如果无输出,则默认使用最近 1 commit 的改动 `git log -p -n 1`)。默认不允许探索整个代码库除非用户明确要求。
8
+ - 最近 **2(或指定个数)个 commit** 的完整改动(使用 `git log -p -n <N>`)
9
+ - 完整的整个代码库(需用户明确说明)
10
10
 
11
11
  ## 硬性输出要求
12
12
 
@@ -14,32 +14,43 @@ description: review 代码并输出交互式 HTML 报告。适用于代码审查
14
14
  **直接从 `<!DOCTYPE html>` 开始输出一个完整的、自包含的 HTML 文档。**
15
15
  **使用中文+英文专业名词**
16
16
 
17
- ### review 的约束
17
+ ### 审查问题分类与重要程度
18
18
 
19
- 1. **BUG**:发现潜在bug,重要程度:最高。
20
- 2. **敏感信息**:敏感信息泄露,敏感信息文件在gitignore忘记添加,重要程度:高。
21
- 3. **可维护性**:代码解耦,结构清晰,冗余代码优化,重要程度:中。
22
- 4. **规范**:代码规范,代码风格,重要程度:低。
23
- 6. **语法糖**:适合当前语言的高阶语法帮助优化代码,维持原逻辑和无bug>性能提升大>可读性>性能提升可忽略>缩短代码行数,重要程度:低。
24
- 7. **其他**:其他问题。
19
+ 1. **BUG**:潜在 bug,最高优先级。
20
+ 2. **敏感信息**:敏感信息泄露、敏感文件未加入 `.gitignore`,高优先级。
21
+ 3. **可维护性**:代码解耦、结构清晰、冗余代码优化,中优先级。
22
+ 4. **规范**:代码规范、代码风格,低优先级。
23
+ 5. **语法糖**:适合当前语言的高阶语法优化,维持原逻辑和无 bug > 性能提升大 > 可读性 > 性能提升可忽略 > 缩短代码行数,低优先级。
24
+ 6. **其他**:其他问题。
25
25
 
26
- ### HTML 内容的约束
26
+ ### HTML 内容约束
27
27
 
28
- 1. **外观**:使用简单的现代 CSS(白/灰背景,清晰字体)。
29
- 2. **交互**:如需:提供折叠/展开功能,便于快速浏览多个文件的审查意见。
30
- - 默认只展示文件名称和问题数量,点击文件名可展开详细审查意见。
31
- 3. **内容**:对于每个改动文件,至少包含:
32
- - 文件路径
33
- - 改动摘要(+/- 行数)
34
- - 具体审查意见(潜在 bug、代码风格、可维护性等)
35
- 4. **轻量**:不引用外部 CDN,全部内联(CSS,JS少量)。总 HTML 大小控制在 300 行以内(最大700行),避免浪费 token。
28
+ 1. **外观**:简单的现代 CSS(白/灰背景,清晰字体)。全部内联,不引用外部 CDN。
29
+ 2. **交互**:提供折叠/展开功能,默认只展示文件名称和问题数量,点击文件名展开详细审查意见。
30
+ 3. **内容**:每个改动文件至少包含:文件路径、改动摘要(+/- 行数)、具体审查意见(问题分类及描述)。
31
+ 4. **轻量**:总 HTML 大小控制如下——审查整个代码库时上限为 2000 行(若代码库内容较少则仍遵循 700 行限制以保持简洁);其他情况(diff / commit)严格控制在 700 行以内。CSS 和 JS 均为少量内联。
32
+
33
+ ### 特殊场景:简洁总结与评分
34
+
35
+ 当审查结果**没有任何重要程度为“高”或“最高”的问题**(即仅包含中、低或无问题)时,使用简洁 HTML 输出,不需要交互式折叠,仅展示:
36
+ - 改动摘要
37
+ - 总体评分(满分 100)及简短理由
38
+ - 如果审查对象是**未提交的改动**,额外提供一条符合 Conventional Commits 规范的 commit message 建议(例如 `fix: 修复xxx`、`feat: 新增xxx`),从改动内容智能生成;若审查对象是已提交的 commit 或整个代码库,则只展示评分与总结,不提供 commit message。
39
+
40
+ 评分参考标准(严格、公正):
41
+ - **60 分(及格)**:存在少量中等问题,代码可运行但需改进。
42
+ - **80 分(良好)**:仅有轻微(低)问题,整体质量较好。
43
+ - **95 分(优秀)**:几乎无问题,设计清晰。
44
+ - **99 分(完美)**:无可挑剔,代码典范。
45
+ (实际得分根据问题数量与严重性严格判定)
36
46
 
37
47
  ## 执行步骤
38
48
 
39
- 1. 运行 `git diff` 获取未暂存/未提交的改动或运行 `git log -p -n 2` 获取最近 2 个 commit 的详细改动或查看完整项目。
40
- 2. 合并上述改动,按文件分组。
41
- 3. 对每个改动文件进行审查,并生成上述 HTML。
49
+ 1. 根据用户指令运行 `git diff HEAD` `git log -p -n <N>` 获取改动,或遍历代码库。
50
+ 2. 合并改动,按文件分组。
51
+ 3. 对每个文件进行审查,并生成符合上述要求的 HTML。
52
+ 4. 自动确保 `pi-dev-output/pi-review/` 文件夹存在于项目根目录,若不存在则创建;并确保 `pi-dev-output/` 下的 `.gitignore` 包含 `*`(忽略所有内容但保留目录)。
53
+ 5. HTML 文件命名格式:`年月日时分-任务简述(极简10词以内)-index.html`,若同名文件已存在则编号递增(如 `index1.html`)。
54
+ 6. 直接输出 HTML 文件到 `pi-dev-output/pi-review/` 目录,**不要添加任何解释性文字**,仅简短说明工作完成和输出文件路径即可。
42
55
 
43
- 请立即输出 HTML 文件到项目根目录的pi-review文件夹(没有则创建,同时添加文件夹到gitignore),不要加任何解释性文字,不需要输出大量md说明,简短说明工作完成和输出文件即可。
44
- HTML文件名称格式:年月日时分-任务简述(极简10词以内)-index.html,有同名则index+1。
45
- 如果review未发现必须需要修改(重要程度>=中)的问题,如果改动未提交,则html简约输出只说明改动总结即可,无需交互,并在开头附带规范的git-commit信息便于用户提交。
56
+ 请严格遵循以上规则。
@@ -0,0 +1,74 @@
1
+ ---
2
+ name: to-prd
3
+ description: Turn the current conversation context into a PRD document and save it locally. Use when user wants to create a PRD from the current context.
4
+ ---
5
+
6
+ This skill takes the current conversation context and codebase understanding and produces a PRD. Do NOT interview the user — just synthesize what you already know.
7
+
8
+ ## Process
9
+
10
+ 1. Explore the repo to understand the current state of the codebase, if you haven't already. Use the project's domain glossary vocabulary throughout the PRD, and respect any ADRs in the area you're touching.
11
+
12
+ 2. Sketch out the major modules you will need to build or modify to complete the implementation. Actively look for opportunities to extract deep modules that can be tested in isolation.
13
+
14
+ A deep module (as opposed to a shallow module) is one which encapsulates a lot of functionality in a simple, testable interface which rarely changes.
15
+
16
+ Check with the user that these modules match their expectations. Check with the user which modules they want tests written for.
17
+
18
+ 3. Write the PRD using the template below, then save it to `pi-dev-output/pi-prd/` directory.
19
+
20
+ <prd-template>
21
+
22
+ ## Problem Statement
23
+
24
+ The problem that the user is facing, from the user's perspective.
25
+
26
+ ## Solution
27
+
28
+ The solution to the problem, from the user's perspective.
29
+
30
+ ## User Stories
31
+
32
+ A LONG, numbered list of user stories. Each user story should be in the format of:
33
+
34
+ 1. As an <actor>, I want a <feature>, so that <benefit>
35
+
36
+ <user-story-example>
37
+ 1. As a mobile bank customer, I want to see balance on my accounts, so that I can make better informed decisions about my spending
38
+ </user-story-example>
39
+
40
+ This list of user stories should be extremely extensive and cover all aspects of the feature.
41
+
42
+ ## Implementation Decisions
43
+
44
+ A list of implementation decisions that were made. This can include:
45
+
46
+ - The modules that will be built/modified
47
+ - The interfaces of those modules that will be modified
48
+ - Technical clarifications from the developer
49
+ - Architectural decisions
50
+ - Schema changes
51
+ - API contracts
52
+ - Specific interactions
53
+
54
+ Do NOT include specific file paths or code snippets. They may end up being outdated very quickly.
55
+
56
+ Exception: if a prototype produced a snippet that encodes a decision more precisely than prose can (state machine, reducer, schema, type shape), inline it within the relevant decision and note briefly that it came from a prototype. Trim to the decision-rich parts — not a working demo, just the important bits.
57
+
58
+ ## Testing Decisions
59
+
60
+ A list of testing decisions that were made. Include:
61
+
62
+ - A description of what makes a good test (only test external behavior, not implementation details)
63
+ - Which modules will be tested
64
+ - Prior art for the tests (i.e. similar types of tests in the codebase)
65
+
66
+ ## Out of Scope
67
+
68
+ A description of the things that are out of scope for this PRD.
69
+
70
+ ## Further Notes
71
+
72
+ Any further notes about the feature.
73
+
74
+ </prd-template>