iaip-mcp-pde 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +187 -0
- package/dist/cli.d.ts +18 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +385 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +34 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp-server.d.ts +59 -0
- package/dist/mcp-server.d.ts.map +1 -0
- package/dist/mcp-server.js +293 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/parser.d.ts +19 -0
- package/dist/parser.d.ts.map +1 -0
- package/dist/parser.js +89 -0
- package/dist/parser.js.map +1 -0
- package/dist/pde-engine.d.ts +54 -0
- package/dist/pde-engine.d.ts.map +1 -0
- package/dist/pde-engine.js +85 -0
- package/dist/pde-engine.js.map +1 -0
- package/dist/prompts.d.ts +10 -0
- package/dist/prompts.d.ts.map +1 -0
- package/dist/prompts.js +93 -0
- package/dist/prompts.js.map +1 -0
- package/dist/storage.d.ts +44 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +369 -0
- package/dist/storage.js.map +1 -0
- package/dist/types.d.ts +108 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +24 -0
- package/dist/types.js.map +1 -0
- package/package.json +63 -0
- package/rispecs/pde-data-models.rispec.md +182 -0
- package/rispecs/pde-overview.rispec.md +157 -0
- package/rispecs/pde-parent-child-schema.rispec.md +525 -0
- package/rispecs/pde-prompts.rispec.md +144 -0
- package/rispecs/pde-resources.rispec.md +101 -0
- package/rispecs/pde-tools.rispec.md +179 -0
- package/rispecs/relation-to-mcp-structural-thinking.kin.md +66 -0
- package/scenarios/01-simple-decomposition.md +88 -0
- package/scenarios/02-multi-intent-workflow.md +112 -0
- package/scenarios/03-ceremonial-alignment.md +155 -0
- package/scenarios/04-dependency-resolution.md +163 -0
- package/scenarios/05-checkpoint-recovery.md +171 -0
|
@@ -0,0 +1,525 @@
|
|
|
1
|
+
# PDE Parent-Child Schema & Folder-Based Output
|
|
2
|
+
> EAST Phase Design — parent_pde_uuid feature for mcp-pde v2.1
|
|
3
|
+
> Issue: jgwill/src#421 | Related: #418, #419
|
|
4
|
+
|
|
5
|
+
**Version**: 2.1.0-design
|
|
6
|
+
**Document ID**: pde-parent-child-schema-v2.1
|
|
7
|
+
|
|
8
|
+
## Creative Intent
|
|
9
|
+
|
|
10
|
+
### Desired Outcome
|
|
11
|
+
PDEs form **parent-child hierarchies** stored in timestamped folders, enabling multi-level decomposition (mission → sub-missions), traceable lineage, and human-navigable `.pde/` layouts.
|
|
12
|
+
|
|
13
|
+
### Current Reality
|
|
14
|
+
- Storage: flat `.pde/<uuid>.json` + `.pde/<uuid>.md`
|
|
15
|
+
- No parent-child relationship
|
|
16
|
+
- No timestamp in filenames
|
|
17
|
+
- Manually created prototype exists: `.pde/2603261642--a448b195-c4ba-4d92-8220-a9ac92bc6d60/`
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 1. Schema Changes
|
|
22
|
+
|
|
23
|
+
### 1a. `StoredDecomposition` — Add `parent_pde_id`
|
|
24
|
+
|
|
25
|
+
**File**: `src/types.ts` (lines 110-118)
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
export interface StoredDecomposition {
|
|
29
|
+
id: string;
|
|
30
|
+
timestamp: string;
|
|
31
|
+
prompt: string;
|
|
32
|
+
result: DecompositionResult;
|
|
33
|
+
options: DecompositionOptions;
|
|
34
|
+
markdownPath?: string;
|
|
35
|
+
// --- NEW (v2.1) ---
|
|
36
|
+
/** UUID of parent PDE. When set, this PDE is stored inside the parent's folder. */
|
|
37
|
+
parent_pde_id?: string;
|
|
38
|
+
/** Folder name: <yyMMddHHmm>--<id>. Computed at save time. */
|
|
39
|
+
folder_name?: string;
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Non-breaking**: Both new fields are optional. Old JSON files without them load normally.
|
|
44
|
+
|
|
45
|
+
### 1b. MCP Tool Input Types — Add `parent_pde_id`
|
|
46
|
+
|
|
47
|
+
**File**: `src/types.ts` (lines 124-145)
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
export interface DecomposeInput {
|
|
51
|
+
prompt: string;
|
|
52
|
+
options?: Partial<DecompositionOptions>;
|
|
53
|
+
workdir?: string;
|
|
54
|
+
// --- NEW (v2.1) ---
|
|
55
|
+
/** Parent PDE UUID. If set, child will be stored inside parent's folder. */
|
|
56
|
+
parent_pde_id?: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface ParseResponseInput {
|
|
60
|
+
llm_response: string;
|
|
61
|
+
original_prompt: string;
|
|
62
|
+
workdir?: string;
|
|
63
|
+
options?: Partial<DecompositionOptions>;
|
|
64
|
+
// --- NEW (v2.1) ---
|
|
65
|
+
parent_pde_id?: string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface ListDecompositionsInput {
|
|
69
|
+
workdir?: string;
|
|
70
|
+
limit?: number;
|
|
71
|
+
// --- NEW (v2.1) ---
|
|
72
|
+
/** When set, only list children of this parent PDE. */
|
|
73
|
+
parent_pde_id?: string;
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
`GetDecompositionInput` and `ExportMarkdownInput` need **no changes** — `pde_get` resolves paths by scanning both old flat format and new folder format.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## 2. New Folder-Based Output Format
|
|
82
|
+
|
|
83
|
+
### 2a. Layout
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
.pde/
|
|
87
|
+
├── <uuid>.json # Old format (still supported for reads)
|
|
88
|
+
├── <uuid>.md # Old format (still supported for reads)
|
|
89
|
+
│
|
|
90
|
+
├── <yyMMddHHmm>--<parent-uuid>/ # NEW: folder-based parent
|
|
91
|
+
│ ├── pde-<parent-uuid>.json # Parent decomposition
|
|
92
|
+
│ ├── pde-<parent-uuid>.md # Parent markdown
|
|
93
|
+
│ ├── AGENTS.md # Agent instructions (optional)
|
|
94
|
+
│ │
|
|
95
|
+
│ ├── <yyMMddHHmm>--<child-uuid>/ # Child PDE folder
|
|
96
|
+
│ │ ├── pde-<child-uuid>.json
|
|
97
|
+
│ │ └── pde-<child-uuid>.md
|
|
98
|
+
│ │
|
|
99
|
+
│ └── <yyMMddHHmm>--<child2-uuid>/ # Another child
|
|
100
|
+
│ ├── pde-<child2-uuid>.json
|
|
101
|
+
│ └── pde-<child2-uuid>.md
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### 2b. Timestamp Format
|
|
105
|
+
|
|
106
|
+
`yyMMddHHmm` — e.g., `2604030845` for 2026-04-03 08:45.
|
|
107
|
+
|
|
108
|
+
**Helper function** (new in `storage.ts`):
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
function formatTimestamp(date: Date = new Date()): string {
|
|
112
|
+
const yy = String(date.getFullYear()).slice(2);
|
|
113
|
+
const MM = String(date.getMonth() + 1).padStart(2, '0');
|
|
114
|
+
const dd = String(date.getDate()).padStart(2, '0');
|
|
115
|
+
const HH = String(date.getHours()).padStart(2, '0');
|
|
116
|
+
const mm = String(date.getMinutes()).padStart(2, '0');
|
|
117
|
+
return `${yy}${MM}${dd}${HH}${mm}`;
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### 2c. Folder Name Construction
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
function buildFolderName(id: string, date?: Date): string {
|
|
125
|
+
return `${formatTimestamp(date)}--${id}`;
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### 2d. When Folder Format Is Used
|
|
130
|
+
|
|
131
|
+
| Condition | Storage Format |
|
|
132
|
+
|-----------|---------------|
|
|
133
|
+
| `parent_pde_id` is set | Child stored in parent's folder: `.pde/<parent-folder>/<yyMMddHHmm>--<child-uuid>/` |
|
|
134
|
+
| No `parent_pde_id` | **New default**: `.pde/<yyMMddHHmm>--<uuid>/pde-<uuid>.json|md` |
|
|
135
|
+
| Reading old PDE | Falls back to `.pde/<uuid>.json` if folder not found |
|
|
136
|
+
|
|
137
|
+
**All new writes use folder format.** Old flat files are read-only legacy.
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## 3. File Modifications List
|
|
142
|
+
|
|
143
|
+
### `src/types.ts`
|
|
144
|
+
| Line(s) | Change |
|
|
145
|
+
|---------|--------|
|
|
146
|
+
| 110-118 | Add `parent_pde_id?: string` and `folder_name?: string` to `StoredDecomposition` |
|
|
147
|
+
| 124-129 | Add `parent_pde_id?: string` to `DecomposeInput` |
|
|
148
|
+
| 136-139 | Add `parent_pde_id?: string` to `ListDecompositionsInput` |
|
|
149
|
+
| (new) | Add `ParseResponseInput` interface (currently inline-typed in mcp-server.ts) |
|
|
150
|
+
|
|
151
|
+
### `src/storage.ts`
|
|
152
|
+
| Function | Change |
|
|
153
|
+
|----------|--------|
|
|
154
|
+
| `ensureDir()` | Support subfolder creation for parent/child paths |
|
|
155
|
+
| `saveDecomposition()` | **Major**: Accept `parent_pde_id`, compute folder path, write to `<folder>/pde-<id>.json|md` |
|
|
156
|
+
| `loadDecomposition()` | **Major**: Scan both `<id>.json` (legacy) and `*--<id>/pde-<id>.json` (new) |
|
|
157
|
+
| `listDecompositions()` | **Major**: Scan folders recursively; support `parent_pde_id` filter |
|
|
158
|
+
| (new) | `formatTimestamp()`, `buildFolderName()`, `resolveDecompositionPath()` helpers |
|
|
159
|
+
|
|
160
|
+
### `src/pde-engine.ts`
|
|
161
|
+
| Method | Change |
|
|
162
|
+
|--------|--------|
|
|
163
|
+
| `parseAndStore()` | Accept `parent_pde_id` param, pass through to `saveDecomposition()` |
|
|
164
|
+
| `get()` | Use new `resolveDecompositionPath()` for path resolution |
|
|
165
|
+
| `list()` | Pass `parent_pde_id` filter to `listDecompositions()` |
|
|
166
|
+
|
|
167
|
+
### `src/mcp-server.ts`
|
|
168
|
+
| Tool | Change |
|
|
169
|
+
|------|--------|
|
|
170
|
+
| `pde_decompose` | Add `parent_pde_id` to inputSchema and response echo |
|
|
171
|
+
| `pde_parse_response` | Add `parent_pde_id` to inputSchema; pass to `engine.parseAndStore()` |
|
|
172
|
+
| `pde_list` | Add `parent_pde_id` to inputSchema; pass to `engine.list()` |
|
|
173
|
+
| `pde_get` | No schema change (resolution is internal) |
|
|
174
|
+
|
|
175
|
+
### `src/cli.ts`
|
|
176
|
+
| Command | Change |
|
|
177
|
+
|---------|--------|
|
|
178
|
+
| `decompose` | Add `--parent` / `-p` flag |
|
|
179
|
+
| `parse` | Add `--parent` / `-p` flag |
|
|
180
|
+
| `list` | Add `--parent` / `-p` flag for filtering children |
|
|
181
|
+
| Console output | Update path display to show folder structure |
|
|
182
|
+
|
|
183
|
+
### `tests/pde-engine.test.ts`
|
|
184
|
+
| Test | Change |
|
|
185
|
+
|------|--------|
|
|
186
|
+
| `parseAndStore` | Add tests for folder-based storage |
|
|
187
|
+
| `parseAndStore` | Add tests for parent_pde_id child storage |
|
|
188
|
+
| `get` | Add tests for resolving both formats |
|
|
189
|
+
| `list` | Add tests for parent_pde_id filter |
|
|
190
|
+
|
|
191
|
+
### `rispecs/pde-data-models.rispec.md`
|
|
192
|
+
| Section | Change |
|
|
193
|
+
|---------|--------|
|
|
194
|
+
| Storage Types | Document `parent_pde_id`, `folder_name` fields |
|
|
195
|
+
|
|
196
|
+
### `rispecs/pde-tools.rispec.md`
|
|
197
|
+
| Section | Change |
|
|
198
|
+
|---------|--------|
|
|
199
|
+
| Tool 1-4 schemas | Document `parent_pde_id` parameter |
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## 4. API Surface Changes
|
|
204
|
+
|
|
205
|
+
### Tool: `pde_decompose`
|
|
206
|
+
|
|
207
|
+
```diff
|
|
208
|
+
inputSchema: {
|
|
209
|
+
properties: {
|
|
210
|
+
prompt: { type: 'string' },
|
|
211
|
+
options: { ... },
|
|
212
|
+
+ parent_pde_id: {
|
|
213
|
+
+ type: 'string',
|
|
214
|
+
+ description: 'UUID of parent PDE. Echoed in response for pass-through to pde_parse_response.'
|
|
215
|
+
+ },
|
|
216
|
+
},
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Response adds `parent_pde_id` echo for pipeline continuity.
|
|
221
|
+
|
|
222
|
+
### Tool: `pde_parse_response`
|
|
223
|
+
|
|
224
|
+
```diff
|
|
225
|
+
inputSchema: {
|
|
226
|
+
properties: {
|
|
227
|
+
llm_response: { type: 'string' },
|
|
228
|
+
original_prompt: { type: 'string' },
|
|
229
|
+
workdir: { type: 'string' },
|
|
230
|
+
options: { ... },
|
|
231
|
+
+ parent_pde_id: {
|
|
232
|
+
+ type: 'string',
|
|
233
|
+
+ description: 'UUID of parent PDE. When set, stores this decomposition inside the parent folder.'
|
|
234
|
+
+ },
|
|
235
|
+
},
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### Tool: `pde_list`
|
|
240
|
+
|
|
241
|
+
```diff
|
|
242
|
+
inputSchema: {
|
|
243
|
+
properties: {
|
|
244
|
+
workdir: { type: 'string' },
|
|
245
|
+
limit: { type: 'number' },
|
|
246
|
+
+ parent_pde_id: {
|
|
247
|
+
+ type: 'string',
|
|
248
|
+
+ description: 'When set, only list children of this parent PDE.'
|
|
249
|
+
+ },
|
|
250
|
+
},
|
|
251
|
+
}
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### Tool: `pde_get`
|
|
255
|
+
|
|
256
|
+
No input schema changes. Resolution logic updated internally.
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
## 5. Key Implementation: `storage.ts` Changes
|
|
261
|
+
|
|
262
|
+
### `resolveDecompositionPath()` — NEW
|
|
263
|
+
|
|
264
|
+
```typescript
|
|
265
|
+
/**
|
|
266
|
+
* Resolve the JSON file path for a decomposition ID.
|
|
267
|
+
* Tries new folder format first, falls back to legacy flat format.
|
|
268
|
+
*/
|
|
269
|
+
function resolveDecompositionPath(
|
|
270
|
+
workdir: string,
|
|
271
|
+
id: string,
|
|
272
|
+
parentId?: string
|
|
273
|
+
): string | null {
|
|
274
|
+
const pdeDir = join(workdir, PDE_DIR);
|
|
275
|
+
|
|
276
|
+
// If parentId given, look inside parent's folder
|
|
277
|
+
if (parentId) {
|
|
278
|
+
const parentFolder = findFolderById(pdeDir, parentId);
|
|
279
|
+
if (parentFolder) {
|
|
280
|
+
const childFolder = findFolderById(join(pdeDir, parentFolder), id);
|
|
281
|
+
if (childFolder) {
|
|
282
|
+
const path = join(pdeDir, parentFolder, childFolder, `pde-${id}.json`);
|
|
283
|
+
if (existsSync(path)) return path;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Try new folder format: .pde/*--<id>/pde-<id>.json
|
|
289
|
+
const folder = findFolderById(pdeDir, id);
|
|
290
|
+
if (folder) {
|
|
291
|
+
const path = join(pdeDir, folder, `pde-${id}.json`);
|
|
292
|
+
if (existsSync(path)) return path;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Recurse into all folders to find nested children
|
|
296
|
+
if (existsSync(pdeDir)) {
|
|
297
|
+
for (const entry of readdirSync(pdeDir)) {
|
|
298
|
+
const subdir = join(pdeDir, entry);
|
|
299
|
+
if (statSync(subdir).isDirectory()) {
|
|
300
|
+
const childFolder = findFolderById(subdir, id);
|
|
301
|
+
if (childFolder) {
|
|
302
|
+
const path = join(subdir, childFolder, `pde-${id}.json`);
|
|
303
|
+
if (existsSync(path)) return path;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Legacy flat format: .pde/<id>.json
|
|
310
|
+
const legacyPath = join(pdeDir, `${id}.json`);
|
|
311
|
+
if (existsSync(legacyPath)) return legacyPath;
|
|
312
|
+
|
|
313
|
+
return null;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Find a folder matching *--<id> pattern in a directory.
|
|
318
|
+
*/
|
|
319
|
+
function findFolderById(dir: string, id: string): string | null {
|
|
320
|
+
if (!existsSync(dir)) return null;
|
|
321
|
+
const suffix = `--${id}`;
|
|
322
|
+
const match = readdirSync(dir).find(
|
|
323
|
+
(f) => f.endsWith(suffix) && statSync(join(dir, f)).isDirectory()
|
|
324
|
+
);
|
|
325
|
+
return match || null;
|
|
326
|
+
}
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
### `saveDecomposition()` — UPDATED
|
|
330
|
+
|
|
331
|
+
```typescript
|
|
332
|
+
export function saveDecomposition(
|
|
333
|
+
workdir: string,
|
|
334
|
+
id: string,
|
|
335
|
+
prompt: string,
|
|
336
|
+
result: DecompositionResult,
|
|
337
|
+
options: DecompositionOptions,
|
|
338
|
+
parentPdeId?: string // NEW parameter
|
|
339
|
+
): StoredDecomposition {
|
|
340
|
+
const pdeDir = join(workdir, PDE_DIR);
|
|
341
|
+
const now = new Date();
|
|
342
|
+
const folderName = buildFolderName(id, now);
|
|
343
|
+
|
|
344
|
+
let targetDir: string;
|
|
345
|
+
|
|
346
|
+
if (parentPdeId) {
|
|
347
|
+
// Find parent's folder
|
|
348
|
+
const parentFolder = findFolderById(pdeDir, parentPdeId);
|
|
349
|
+
if (!parentFolder) {
|
|
350
|
+
throw new Error(
|
|
351
|
+
`Parent PDE ${parentPdeId} not found in ${pdeDir}. ` +
|
|
352
|
+
`Parent must be saved first.`
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
targetDir = join(pdeDir, parentFolder, folderName);
|
|
356
|
+
} else {
|
|
357
|
+
// Top-level: new folder format
|
|
358
|
+
targetDir = join(pdeDir, folderName);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
mkdirSync(targetDir, { recursive: true });
|
|
362
|
+
|
|
363
|
+
const stored: StoredDecomposition = {
|
|
364
|
+
id,
|
|
365
|
+
timestamp: now.toISOString(),
|
|
366
|
+
prompt,
|
|
367
|
+
result,
|
|
368
|
+
options,
|
|
369
|
+
parent_pde_id: parentPdeId,
|
|
370
|
+
folder_name: folderName,
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
// Save JSON
|
|
374
|
+
const jsonPath = join(targetDir, `pde-${id}.json`);
|
|
375
|
+
writeFileSync(jsonPath, JSON.stringify(stored, null, 2), "utf-8");
|
|
376
|
+
|
|
377
|
+
// Save Markdown
|
|
378
|
+
const mdPath = join(targetDir, `pde-${id}.md`);
|
|
379
|
+
const md = decompositionToMarkdown(result, prompt);
|
|
380
|
+
writeFileSync(mdPath, md, "utf-8");
|
|
381
|
+
stored.markdownPath = mdPath;
|
|
382
|
+
|
|
383
|
+
return stored;
|
|
384
|
+
}
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
### `loadDecomposition()` — UPDATED
|
|
388
|
+
|
|
389
|
+
```typescript
|
|
390
|
+
export function loadDecomposition(
|
|
391
|
+
workdir: string,
|
|
392
|
+
id: string
|
|
393
|
+
): StoredDecomposition | null {
|
|
394
|
+
const resolved = resolveDecompositionPath(workdir, id);
|
|
395
|
+
if (!resolved) return null;
|
|
396
|
+
const raw = readFileSync(resolved, "utf-8");
|
|
397
|
+
return JSON.parse(raw) as StoredDecomposition;
|
|
398
|
+
}
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
### `listDecompositions()` — UPDATED
|
|
402
|
+
|
|
403
|
+
```typescript
|
|
404
|
+
export function listDecompositions(
|
|
405
|
+
workdir: string,
|
|
406
|
+
limit?: number,
|
|
407
|
+
parentPdeId?: string // NEW parameter
|
|
408
|
+
): StoredDecomposition[] {
|
|
409
|
+
const pdeDir = join(workdir, PDE_DIR);
|
|
410
|
+
if (!existsSync(pdeDir)) return [];
|
|
411
|
+
|
|
412
|
+
const results: StoredDecomposition[] = [];
|
|
413
|
+
|
|
414
|
+
if (parentPdeId) {
|
|
415
|
+
// List children of a specific parent
|
|
416
|
+
const parentFolder = findFolderById(pdeDir, parentPdeId);
|
|
417
|
+
if (!parentFolder) return [];
|
|
418
|
+
const parentDir = join(pdeDir, parentFolder);
|
|
419
|
+
for (const entry of readdirSync(parentDir)) {
|
|
420
|
+
if (entry.includes('--') && statSync(join(parentDir, entry)).isDirectory()) {
|
|
421
|
+
const childId = entry.split('--').pop()!;
|
|
422
|
+
const jsonPath = join(parentDir, entry, `pde-${childId}.json`);
|
|
423
|
+
if (existsSync(jsonPath)) {
|
|
424
|
+
results.push(JSON.parse(readFileSync(jsonPath, 'utf-8')));
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
} else {
|
|
429
|
+
// List all top-level decompositions
|
|
430
|
+
|
|
431
|
+
// New format: folders matching *--<uuid>/
|
|
432
|
+
for (const entry of readdirSync(pdeDir)) {
|
|
433
|
+
const fullPath = join(pdeDir, entry);
|
|
434
|
+
if (entry.includes('--') && statSync(fullPath).isDirectory()) {
|
|
435
|
+
const id = entry.split('--').pop()!;
|
|
436
|
+
const jsonPath = join(fullPath, `pde-${id}.json`);
|
|
437
|
+
if (existsSync(jsonPath)) {
|
|
438
|
+
results.push(JSON.parse(readFileSync(jsonPath, 'utf-8')));
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// Legacy format: .pde/<uuid>.json
|
|
444
|
+
for (const entry of readdirSync(pdeDir)) {
|
|
445
|
+
if (entry.endsWith('.json') && !entry.startsWith('pde-')) {
|
|
446
|
+
const fullPath = join(pdeDir, entry);
|
|
447
|
+
if (statSync(fullPath).isFile()) {
|
|
448
|
+
results.push(JSON.parse(readFileSync(fullPath, 'utf-8')));
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// Sort by timestamp descending
|
|
455
|
+
results.sort((a, b) => b.timestamp.localeCompare(a.timestamp));
|
|
456
|
+
return limit ? results.slice(0, limit) : results;
|
|
457
|
+
}
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
---
|
|
461
|
+
|
|
462
|
+
## 6. Migration Strategy
|
|
463
|
+
|
|
464
|
+
### Backward Compatibility Rules
|
|
465
|
+
|
|
466
|
+
1. **Old `.pde/<uuid>.json` files are never moved or modified** — they remain readable
|
|
467
|
+
2. **All new writes use folder format** — even top-level PDEs without a parent
|
|
468
|
+
3. **`pde_get` resolves transparently** — tries folder format first, falls back to flat
|
|
469
|
+
4. **`pde_list` merges both formats** — deduplicates by ID if a PDE exists in both
|
|
470
|
+
5. **No migration script needed** — coexistence is the strategy
|
|
471
|
+
|
|
472
|
+
### Version Bump
|
|
473
|
+
|
|
474
|
+
- `package.json`: `2.0.1` → `2.1.0` (minor: new non-breaking features)
|
|
475
|
+
- `mcp-server.ts`: server version `2.0.0` → `2.1.0`
|
|
476
|
+
|
|
477
|
+
### Risk Assessment
|
|
478
|
+
|
|
479
|
+
| Risk | Mitigation |
|
|
480
|
+
|------|-----------|
|
|
481
|
+
| Parent folder not found when saving child | Throw clear error: "Parent must be saved first" |
|
|
482
|
+
| UUID collision in folder scan | UUID v4 collision probability is negligible |
|
|
483
|
+
| Performance with many folders | `readdirSync` is fast; add caching later if needed |
|
|
484
|
+
| Breaking existing .pde/ consumers | Old format still works; new fields are optional |
|
|
485
|
+
|
|
486
|
+
---
|
|
487
|
+
|
|
488
|
+
## 7. Implementation Estimate
|
|
489
|
+
|
|
490
|
+
### This Session (Phase 1 — EAST Design) ✅
|
|
491
|
+
- [x] Analyze codebase completely
|
|
492
|
+
- [x] Design schema changes
|
|
493
|
+
- [x] Identify all insertion points with line numbers
|
|
494
|
+
- [x] Write design document (this file)
|
|
495
|
+
|
|
496
|
+
### Next Session (Phase 2 — SOUTH/WEST Implementation)
|
|
497
|
+
**Estimated: 1 focused session**
|
|
498
|
+
|
|
499
|
+
1. **`src/types.ts`** — Add fields (~5 min)
|
|
500
|
+
2. **`src/storage.ts`** — Implement folder logic (~45 min, most complex)
|
|
501
|
+
3. **`src/pde-engine.ts`** — Pass-through params (~10 min)
|
|
502
|
+
4. **`src/mcp-server.ts`** — Tool schema additions (~15 min)
|
|
503
|
+
5. **`src/cli.ts`** — `--parent` flag (~10 min)
|
|
504
|
+
6. **Tests** — New test cases for folder format + parent-child (~30 min)
|
|
505
|
+
7. **rispecs update** — Document new schemas (~10 min)
|
|
506
|
+
8. **Build + verify** — `npm run build && npm test` (~5 min)
|
|
507
|
+
|
|
508
|
+
### Deferred (Phase 3 — NORTH)
|
|
509
|
+
- `AGENTS.md` auto-generation inside PDE folders
|
|
510
|
+
- Recursive child listing (grandchildren)
|
|
511
|
+
- `pde_tree` tool for visualizing hierarchy
|
|
512
|
+
- coaia-pde adoption of the new format
|
|
513
|
+
- mia-code/miaco widget upgrades (separate repos, separate issues)
|
|
514
|
+
|
|
515
|
+
---
|
|
516
|
+
|
|
517
|
+
## 8. Structural Tension Summary
|
|
518
|
+
|
|
519
|
+
| Aspect | Current Reality | Desired Outcome |
|
|
520
|
+
|--------|----------------|-----------------|
|
|
521
|
+
| **Storage** | Flat `.pde/<uuid>.json` | Timestamped folders with parent-child nesting |
|
|
522
|
+
| **Lineage** | None — each PDE is an island | `parent_pde_id` traces decomposition → sub-decomposition |
|
|
523
|
+
| **Navigability** | UUIDs are opaque | `<yyMMddHHmm>--<uuid>/` folders show time + structure |
|
|
524
|
+
| **Agent support** | No `AGENTS.md` convention | Folder format naturally hosts `AGENTS.md` alongside PDE |
|
|
525
|
+
| **Backward compat** | N/A | Old flat files remain readable forever |
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# PDE-MCP Prompts Specification
|
|
2
|
+
> MCP Prompt Template Definitions for Prompt Decomposition Engine v2
|
|
3
|
+
|
|
4
|
+
**Version**: 2.0.0
|
|
5
|
+
**Document ID**: pde-mcp-prompts-v2
|
|
6
|
+
|
|
7
|
+
## Creative Intent
|
|
8
|
+
|
|
9
|
+
### Desired Outcome
|
|
10
|
+
LLM agents **access** a single reusable prompt template (`pde-decompose`) that produces a complete `DecompositionResult` JSON in one LLM call, replacing the v1 five-layer chained approach.
|
|
11
|
+
|
|
12
|
+
## Prompt Definitions
|
|
13
|
+
|
|
14
|
+
### Prompt 1: `pde-decompose`
|
|
15
|
+
|
|
16
|
+
**Purpose**: System prompt for LLM-driven prompt decomposition — instructs the LLM to output a `DecompositionResult` JSON object in a single call.
|
|
17
|
+
|
|
18
|
+
**What This Enables**: Any LLM (Claude, Gemini, GPT) produces a structured, consistent decomposition regardless of provider.
|
|
19
|
+
|
|
20
|
+
**Arguments**:
|
|
21
|
+
```typescript
|
|
22
|
+
{
|
|
23
|
+
userPrompt: string; // Required: The prompt to decompose
|
|
24
|
+
extractImplicit?: string; // "true" (default) | "false"
|
|
25
|
+
mapDependencies?: string; // "true" (default) | "false"
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**System Prompt Structure**:
|
|
30
|
+
```
|
|
31
|
+
You are a Prompt Decomposition Engine (PDE).
|
|
32
|
+
|
|
33
|
+
CRITICAL: Your response must be ONLY a valid JSON object. Do not include:
|
|
34
|
+
- Markdown code fences (no ```json)
|
|
35
|
+
- Explanatory text before or after the JSON
|
|
36
|
+
- Any commentary or notes
|
|
37
|
+
|
|
38
|
+
Just output the raw JSON object starting with { and ending with }.
|
|
39
|
+
|
|
40
|
+
Analyze the user's prompt and output with this exact structure:
|
|
41
|
+
<JSON_SCHEMA_EXAMPLE>
|
|
42
|
+
|
|
43
|
+
Directions mapping (Medicine Wheel / Four Directions):
|
|
44
|
+
- EAST (🌅 Vision): Understanding what is being asked, clarifying requirements, envisioning desired outcomes
|
|
45
|
+
- SOUTH (🔥 Analysis): Research, learning, investigation, growth tasks
|
|
46
|
+
- WEST (🌊 Validation): Testing, reflection, review, accountability tasks
|
|
47
|
+
- NORTH (❄️ Action): Implementation, execution, delivery, wisdom tasks
|
|
48
|
+
|
|
49
|
+
[extractImplicit rule: extract implicit intents from hedging language OR only explicit intents]
|
|
50
|
+
[mapDependencies rule: map dependency fields OR set all to null]
|
|
51
|
+
|
|
52
|
+
Rules:
|
|
53
|
+
- Assign confidence scores (0.0-1.0) based on how clearly the intent is stated.
|
|
54
|
+
- Flag ambiguities where the prompt uses "somehow", "probably", "maybe", or leaves method unspecified.
|
|
55
|
+
- Generate actionStack as an ordered list respecting dependencies, each item mapped to a direction.
|
|
56
|
+
- For secondary intents, distinguish explicit from implicit.
|
|
57
|
+
- The primary intent is the single most important action.
|
|
58
|
+
- context.assumptions captures statements assumed true but not verified.
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**User Message Format**:
|
|
62
|
+
```
|
|
63
|
+
Prompt to decompose:
|
|
64
|
+
"<userPrompt>"
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**Expected JSON Output** (DecompositionResult schema):
|
|
68
|
+
```json
|
|
69
|
+
{
|
|
70
|
+
"primary": {
|
|
71
|
+
"action": "main action verb",
|
|
72
|
+
"target": "what the action applies to",
|
|
73
|
+
"urgency": "immediate|session|persistent",
|
|
74
|
+
"confidence": 0.95
|
|
75
|
+
},
|
|
76
|
+
"secondary": [
|
|
77
|
+
{
|
|
78
|
+
"action": "action verb",
|
|
79
|
+
"target": "target",
|
|
80
|
+
"implicit": false,
|
|
81
|
+
"dependency": "what this depends on or null",
|
|
82
|
+
"confidence": 0.8
|
|
83
|
+
}
|
|
84
|
+
],
|
|
85
|
+
"context": {
|
|
86
|
+
"files_needed": ["list of files"],
|
|
87
|
+
"tools_required": ["list of tools"],
|
|
88
|
+
"assumptions": ["statements assumed true"]
|
|
89
|
+
},
|
|
90
|
+
"outputs": {
|
|
91
|
+
"artifacts": ["new files to create"],
|
|
92
|
+
"updates": ["existing files to update"],
|
|
93
|
+
"communications": ["PRs, issues, docs"]
|
|
94
|
+
},
|
|
95
|
+
"directions": {
|
|
96
|
+
"east": [{"text": "vision items", "confidence": 0.9, "implicit": false}],
|
|
97
|
+
"south": [{"text": "analysis items", "confidence": 0.8, "implicit": false}],
|
|
98
|
+
"west": [{"text": "validation items", "confidence": 0.7, "implicit": true}],
|
|
99
|
+
"north": [{"text": "action items", "confidence": 0.9, "implicit": false}]
|
|
100
|
+
},
|
|
101
|
+
"actionStack": [
|
|
102
|
+
{"text": "task description", "direction": "east", "dependency": null, "completed": false},
|
|
103
|
+
{"text": "next task", "direction": "south", "dependency": "prior task", "completed": false}
|
|
104
|
+
],
|
|
105
|
+
"ambiguities": [
|
|
106
|
+
{"text": "ambiguous part", "suggestion": "how to clarify"}
|
|
107
|
+
]
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Prompt Access via MCP
|
|
112
|
+
|
|
113
|
+
### Via `GetPrompt` (MCP prompt primitive)
|
|
114
|
+
```
|
|
115
|
+
name: pde-decompose
|
|
116
|
+
arguments:
|
|
117
|
+
userPrompt: "Create user auth with JWT, PostgreSQL, tests, deploy to staging"
|
|
118
|
+
extractImplicit: "true"
|
|
119
|
+
mapDependencies: "true"
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Returns a `messages` array with a single user message combining system prompt + user message.
|
|
123
|
+
|
|
124
|
+
### Via `pde_decompose` Tool (preferred for agents)
|
|
125
|
+
The `pde_decompose` tool is the preferred access pattern — it returns `systemPrompt` and `userMessage` separately, so the calling agent can send them as proper system/user roles to their LLM provider.
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
pde_decompose({ prompt: "...", options: { extractImplicit: true, mapDependencies: true } })
|
|
129
|
+
→ {
|
|
130
|
+
instructions: "Send systemPrompt as system message...",
|
|
131
|
+
systemPrompt: "You are a Prompt Decomposition Engine...",
|
|
132
|
+
userMessage: 'Prompt to decompose:\n"..."',
|
|
133
|
+
original_prompt: "..."
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Options Behavior
|
|
138
|
+
|
|
139
|
+
| Option | Default | Effect |
|
|
140
|
+
|--------|---------|--------|
|
|
141
|
+
| `extractImplicit: true` | ✅ | Extracts implicit intents from hedging language ("which I assume", "you will need", "somehow", "I expect", "probably", "should"). Marks them `implicit: true`. |
|
|
142
|
+
| `extractImplicit: false` | — | Only explicit intents. All `implicit: false`. |
|
|
143
|
+
| `mapDependencies: true` | ✅ | Maps `dependency` fields in secondary intents and actionStack to show ordering requirements. |
|
|
144
|
+
| `mapDependencies: false` | — | All `dependency` fields set to `null`. |
|