coaiajs 0.3.0 → 0.3.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.
Files changed (179) hide show
  1. package/KINSHIP.md +1 -1
  2. package/README.md +191 -75
  3. package/dist/mcp/config.d.ts.map +1 -0
  4. package/dist/mcp/config.js +0 -2
  5. package/dist/mcp/config.js.map +1 -0
  6. package/dist/mcp/prompts.d.ts.map +1 -0
  7. package/dist/mcp/prompts.js.map +1 -0
  8. package/dist/mcp/resources.d.ts.map +1 -0
  9. package/dist/mcp/resources.js.map +1 -0
  10. package/dist/mcp/server.d.ts.map +1 -0
  11. package/dist/mcp/server.js +1 -1
  12. package/dist/mcp/server.js.map +1 -0
  13. package/dist/mcp/tools/coaiapy-tools.d.ts.map +1 -0
  14. package/dist/mcp/tools/coaiapy-tools.js.map +1 -0
  15. package/dist/mcp/tools/index.d.ts.map +1 -0
  16. package/dist/mcp/tools/index.js.map +1 -0
  17. package/dist/src/audio.d.ts.map +1 -0
  18. package/dist/src/audio.js.map +1 -0
  19. package/dist/src/cli-helpers.d.ts.map +1 -0
  20. package/dist/src/cli-helpers.js.map +1 -0
  21. package/dist/src/cli.d.ts.map +1 -0
  22. package/dist/src/cli.js +1 -0
  23. package/dist/src/cli.js.map +1 -0
  24. package/dist/src/config.d.ts.map +1 -0
  25. package/dist/src/config.js.map +1 -0
  26. package/dist/src/environment.d.ts.map +1 -0
  27. package/dist/src/environment.js.map +1 -0
  28. package/dist/src/github.d.ts.map +1 -0
  29. package/dist/src/github.js.map +1 -0
  30. package/dist/src/index.d.ts +1 -0
  31. package/dist/src/index.d.ts.map +1 -0
  32. package/dist/src/index.js +1 -0
  33. package/dist/src/index.js.map +1 -0
  34. package/dist/src/langfuse/client.d.ts.map +1 -0
  35. package/dist/src/langfuse/client.js.map +1 -0
  36. package/dist/src/langfuse/comments.d.ts.map +1 -0
  37. package/dist/src/langfuse/comments.js.map +1 -0
  38. package/dist/src/langfuse/datasets.d.ts.map +1 -0
  39. package/dist/src/langfuse/datasets.js.map +1 -0
  40. package/dist/src/langfuse/index.d.ts +1 -0
  41. package/dist/src/langfuse/index.d.ts.map +1 -0
  42. package/dist/src/langfuse/index.js.map +1 -0
  43. package/dist/src/langfuse/media.d.ts.map +1 -0
  44. package/dist/src/langfuse/media.js.map +1 -0
  45. package/dist/src/langfuse/observations.d.ts.map +1 -0
  46. package/dist/src/langfuse/observations.js.map +1 -0
  47. package/dist/src/langfuse/prompts.d.ts +5 -1
  48. package/dist/src/langfuse/prompts.d.ts.map +1 -0
  49. package/dist/src/langfuse/prompts.js +16 -3
  50. package/dist/src/langfuse/prompts.js.map +1 -0
  51. package/dist/src/langfuse/scores.d.ts.map +1 -0
  52. package/dist/src/langfuse/scores.js.map +1 -0
  53. package/dist/src/langfuse/traces.d.ts.map +1 -0
  54. package/dist/src/langfuse/traces.js.map +1 -0
  55. package/dist/src/llm.d.ts.map +1 -0
  56. package/dist/src/llm.js.map +1 -0
  57. package/dist/src/narrative/graph-manager.d.ts.map +1 -0
  58. package/dist/src/narrative/graph-manager.js.map +1 -0
  59. package/dist/src/narrative/index.d.ts.map +1 -0
  60. package/dist/src/narrative/index.js.map +1 -0
  61. package/dist/src/narrative/markdown-export.d.ts.map +1 -0
  62. package/dist/src/narrative/markdown-export.js.map +1 -0
  63. package/dist/src/narrative/tool-definitions.d.ts.map +1 -0
  64. package/dist/src/narrative/tool-definitions.js.map +1 -0
  65. package/dist/src/narrative/tool-handlers.d.ts.map +1 -0
  66. package/dist/src/narrative/tool-handlers.js.map +1 -0
  67. package/dist/src/narrative/types.d.ts.map +1 -0
  68. package/dist/src/narrative/types.js.map +1 -0
  69. package/dist/src/narrative/validation.d.ts.map +1 -0
  70. package/dist/src/narrative/validation.js.map +1 -0
  71. package/dist/src/pde/index.d.ts.map +1 -0
  72. package/dist/src/pde/index.js.map +1 -0
  73. package/dist/src/pde/mcp-handlers.d.ts.map +1 -0
  74. package/dist/src/pde/mcp-handlers.js.map +1 -0
  75. package/dist/src/pde/mcp-tools.d.ts.map +1 -0
  76. package/dist/src/pde/mcp-tools.js.map +1 -0
  77. package/dist/src/pde/session-manager.d.ts.map +1 -0
  78. package/dist/src/pde/session-manager.js.map +1 -0
  79. package/dist/src/pde/stc-mapper.d.ts.map +1 -0
  80. package/dist/src/pde/stc-mapper.js.map +1 -0
  81. package/dist/src/pipeline/index.d.ts.map +1 -0
  82. package/dist/src/pipeline/index.js.map +1 -0
  83. package/dist/src/pipeline/template-engine.d.ts.map +1 -0
  84. package/dist/src/pipeline/template-engine.js.map +1 -0
  85. package/dist/src/planning/index.d.ts.map +1 -0
  86. package/dist/src/planning/index.js.map +1 -0
  87. package/dist/src/planning/mcp-handlers.d.ts.map +1 -0
  88. package/dist/src/planning/mcp-handlers.js.map +1 -0
  89. package/dist/src/planning/mcp-tools.d.ts.map +1 -0
  90. package/dist/src/planning/mcp-tools.js.map +1 -0
  91. package/dist/src/planning/plan-parser.d.ts.map +1 -0
  92. package/dist/src/planning/plan-parser.js.map +1 -0
  93. package/dist/src/redis.d.ts.map +1 -0
  94. package/dist/src/redis.js.map +1 -0
  95. package/dist/src/types.d.ts.map +1 -0
  96. package/dist/src/types.js.map +1 -0
  97. package/dist/src/version.d.ts.map +1 -0
  98. package/dist/src/version.js.map +1 -0
  99. package/llms-full.txt +361 -0
  100. package/llms.txt +43 -0
  101. package/package.json +79 -12
  102. package/.claude/settings.local.json +0 -17
  103. package/.claude-plugin/marketplace.json +0 -26
  104. package/articles/academic/NOTES.md +0 -1
  105. package/articles/academic/README.md +0 -1
  106. package/articles/academic/creative-orientation-vs-problem-solving.md +0 -177
  107. package/articles/academic/jsonl-knowledge-graphs-agent-memory.md +0 -142
  108. package/articles/academic/langfuse-observability-llm-pipelines.md +0 -144
  109. package/articles/academic/medicine-wheel-software-architecture.md +0 -163
  110. package/articles/academic/mmot-autonomous-agents.md +0 -156
  111. package/articles/academic/model-context-protocol-interagent.md +0 -161
  112. package/articles/academic/pde-prompt-decomposition.md +0 -186
  113. package/articles/academic/structural-tension-in-ai-agents.md +0 -134
  114. package/articles/reviews/mcp-protocol-design-review.md +0 -170
  115. package/articles/reviews/observability-ai-systems-review.md +0 -176
  116. package/articles/reviews/prompt-engineering-decomposition-review.md +0 -184
  117. package/articles/surveys/agent-orchestration-survey.md +0 -186
  118. package/articles/surveys/knowledge-graph-storage-survey.md +0 -204
  119. package/articles/surveys/structural-tension-methodology-survey.md +0 -154
  120. package/articles/technical/aws-sdk-v3-polly.md +0 -270
  121. package/articles/technical/commander-cli-framework.md +0 -262
  122. package/articles/technical/dotenv-config-patterns.md +0 -360
  123. package/articles/technical/ioredis-vs-redis.md +0 -142
  124. package/articles/technical/langfuse-js-sdk-vs-rest.md +0 -191
  125. package/articles/technical/mcp-sdk-typescript.md +0 -291
  126. package/articles/technical/octokit-github-api.md +0 -293
  127. package/articles/technical/openai-sdk-modern.md +0 -231
  128. package/articles/technical/yaml-parsing-node.md +0 -266
  129. package/articles/technical/zod-runtime-validation.md +0 -212
  130. package/coaiajs-langfuse-claude-plugin/.claude-plugin/marketplace.json +0 -16
  131. package/coaiajs-langfuse-claude-plugin/.claude-plugin/plugin.json +0 -10
  132. package/coaiajs-langfuse-claude-plugin/.mcp.json +0 -8
  133. package/coaiajs-langfuse-claude-plugin/README.md +0 -60
  134. package/mcp/config.ts +0 -225
  135. package/mcp/prompts.ts +0 -131
  136. package/mcp/resources.ts +0 -84
  137. package/mcp/server.ts +0 -519
  138. package/mcp/tools/coaiapy-tools.ts +0 -366
  139. package/mcp/tools/index.ts +0 -4
  140. package/src/audio.ts +0 -76
  141. package/src/cli-helpers.ts +0 -86
  142. package/src/cli.ts +0 -1261
  143. package/src/config.ts +0 -207
  144. package/src/environment.ts +0 -171
  145. package/src/github.ts +0 -143
  146. package/src/index.ts +0 -82
  147. package/src/langfuse/client.ts +0 -105
  148. package/src/langfuse/comments.ts +0 -52
  149. package/src/langfuse/datasets.ts +0 -178
  150. package/src/langfuse/index.ts +0 -37
  151. package/src/langfuse/media.ts +0 -193
  152. package/src/langfuse/observations.ts +0 -155
  153. package/src/langfuse/prompts.ts +0 -157
  154. package/src/langfuse/scores.ts +0 -456
  155. package/src/langfuse/traces.ts +0 -353
  156. package/src/llm.ts +0 -106
  157. package/src/narrative/graph-manager.ts +0 -1358
  158. package/src/narrative/index.ts +0 -188
  159. package/src/narrative/markdown-export.ts +0 -535
  160. package/src/narrative/tool-definitions.ts +0 -635
  161. package/src/narrative/tool-handlers.ts +0 -528
  162. package/src/narrative/types.ts +0 -9
  163. package/src/narrative/validation.ts +0 -179
  164. package/src/pde/index.ts +0 -34
  165. package/src/pde/mcp-handlers.ts +0 -359
  166. package/src/pde/mcp-tools.ts +0 -201
  167. package/src/pde/session-manager.ts +0 -248
  168. package/src/pde/stc-mapper.ts +0 -298
  169. package/src/pipeline/index.ts +0 -7
  170. package/src/pipeline/template-engine.ts +0 -398
  171. package/src/planning/index.ts +0 -32
  172. package/src/planning/mcp-handlers.ts +0 -369
  173. package/src/planning/mcp-tools.ts +0 -155
  174. package/src/planning/plan-parser.ts +0 -587
  175. package/src/redis.ts +0 -111
  176. package/src/types.ts +0 -281
  177. package/src/version.ts +0 -39
  178. package/test/config.test.mjs +0 -93
  179. package/tsconfig.json +0 -26
@@ -1,201 +0,0 @@
1
- /**
2
- * MCP tool definitions for PDE module (10 tools)
3
- * Ported from coaia-pde/src/mcp-server.ts tool schemas
4
- */
5
-
6
- export interface McpToolDef {
7
- name: string;
8
- description: string;
9
- inputSchema: {
10
- type: 'object';
11
- properties: Record<string, unknown>;
12
- required?: string[];
13
- };
14
- }
15
-
16
- export const PDE_MCP_TOOLS: McpToolDef[] = [
17
- {
18
- name: 'import_pde_decomposition',
19
- description: 'Import a .pde/*.json StoredDecomposition and create an STC session from it. Reads the decomposition result, maps it to Structural Tension Chart entities, and persists as .coaia/pde/<UUID>.jsonl.',
20
- inputSchema: {
21
- type: 'object',
22
- properties: {
23
- pde_id: {
24
- type: 'string',
25
- description: 'ID of the PDE decomposition in .pde/ folder'
26
- },
27
- workdir: {
28
- type: 'string',
29
- description: 'Working directory containing .pde/ folder (default: cwd)'
30
- }
31
- },
32
- required: ['pde_id']
33
- }
34
- },
35
- {
36
- name: 'create_stc_from_pde',
37
- description: 'One-shot: provide a DecompositionResult JSON directly and create a Structural Tension Chart session. Use when you already have the decomposition in memory.',
38
- inputSchema: {
39
- type: 'object',
40
- properties: {
41
- decomposition_result: {
42
- type: 'object',
43
- description: 'DecompositionResult JSON object from mcp-pde'
44
- },
45
- original_prompt: {
46
- type: 'string',
47
- description: 'The original prompt that was decomposed'
48
- }
49
- },
50
- required: ['decomposition_result', 'original_prompt']
51
- }
52
- },
53
- {
54
- name: 'list_pde_decompositions',
55
- description: 'List available .pde/*.json decomposition files that can be imported into STC sessions.',
56
- inputSchema: {
57
- type: 'object',
58
- properties: {
59
- workdir: {
60
- type: 'string',
61
- description: 'Working directory containing .pde/ folder (default: cwd)'
62
- }
63
- }
64
- }
65
- },
66
- {
67
- name: 'get_session',
68
- description: 'Get the current state of a PDE session including the Structural Tension Chart, action steps, and progress.',
69
- inputSchema: {
70
- type: 'object',
71
- properties: {
72
- session_id: {
73
- type: 'string',
74
- description: 'The session UUID'
75
- }
76
- },
77
- required: ['session_id']
78
- }
79
- },
80
- {
81
- name: 'list_sessions',
82
- description: 'List all PDE sessions in the current directory.',
83
- inputSchema: {
84
- type: 'object',
85
- properties: {
86
- status: {
87
- type: 'string',
88
- enum: ['active', 'completed', 'abandoned', 'all'],
89
- description: 'Filter by session status'
90
- }
91
- }
92
- }
93
- },
94
- {
95
- name: 'update_action_progress',
96
- description: 'Update progress on an action step with a factual observation.',
97
- inputSchema: {
98
- type: 'object',
99
- properties: {
100
- session_id: {
101
- type: 'string',
102
- description: 'The session UUID'
103
- },
104
- action_name: {
105
- type: 'string',
106
- description: 'The action step entity name (e.g., chart_abc123_action_1)'
107
- },
108
- observation: {
109
- type: 'string',
110
- description: 'Factual progress observation (what was done, not what will be done)'
111
- },
112
- update_current_reality: {
113
- type: 'boolean',
114
- description: 'Whether to also add this observation to the chart current reality'
115
- }
116
- },
117
- required: ['session_id', 'action_name', 'observation']
118
- }
119
- },
120
- {
121
- name: 'mark_action_complete',
122
- description: 'Mark an action step as complete. The completion flows into current reality.',
123
- inputSchema: {
124
- type: 'object',
125
- properties: {
126
- session_id: {
127
- type: 'string',
128
- description: 'The session UUID'
129
- },
130
- action_name: {
131
- type: 'string',
132
- description: 'The action step entity name'
133
- },
134
- completion_note: {
135
- type: 'string',
136
- description: 'Optional note about what was accomplished'
137
- }
138
- },
139
- required: ['session_id', 'action_name']
140
- }
141
- },
142
- {
143
- name: 'add_action_step',
144
- description: 'Add a new strategic action step to the chart. Each action step is a secondary choice supporting the primary goal.',
145
- inputSchema: {
146
- type: 'object',
147
- properties: {
148
- session_id: {
149
- type: 'string',
150
- description: 'The session UUID'
151
- },
152
- action_description: {
153
- type: 'string',
154
- description: 'Description of the strategic action'
155
- },
156
- current_reality: {
157
- type: 'string',
158
- description: 'Current reality for this specific action step'
159
- }
160
- },
161
- required: ['session_id', 'action_description']
162
- }
163
- },
164
- {
165
- name: 'update_current_reality',
166
- description: 'Add observations to the current reality of the chart.',
167
- inputSchema: {
168
- type: 'object',
169
- properties: {
170
- session_id: {
171
- type: 'string',
172
- description: 'The session UUID'
173
- },
174
- observations: {
175
- type: 'array',
176
- items: { type: 'string' },
177
- description: 'Factual observations to add to current reality'
178
- }
179
- },
180
- required: ['session_id', 'observations']
181
- }
182
- },
183
- {
184
- name: 'complete_session',
185
- description: 'Mark the session as completed when the desired outcome is achieved.',
186
- inputSchema: {
187
- type: 'object',
188
- properties: {
189
- session_id: {
190
- type: 'string',
191
- description: 'The session UUID'
192
- },
193
- final_note: {
194
- type: 'string',
195
- description: 'Final observation about the completed outcome'
196
- }
197
- },
198
- required: ['session_id']
199
- }
200
- },
201
- ];
@@ -1,248 +0,0 @@
1
- /**
2
- * Session Manager — JSONL persistence for PDE sessions in .coaia/pde/<UUID>.jsonl
3
- * Ported from coaia-pde/src/session-manager.ts
4
- */
5
-
6
- import { promises as fs } from 'fs';
7
- import path from 'path';
8
- import { v4 as uuidv4 } from 'uuid';
9
- import type { Entity, Relation, PdeSession, StoredDecomposition } from '../types.js';
10
-
11
- function serializeEntity(entity: Entity): string {
12
- return JSON.stringify({ type: 'entity', ...entity });
13
- }
14
-
15
- function serializeRelation(relation: Relation): string {
16
- return JSON.stringify({ type: 'relation', ...relation });
17
- }
18
-
19
- export class SessionManager {
20
- private baseDir: string;
21
-
22
- constructor(baseDir?: string) {
23
- this.baseDir = baseDir || path.join(process.cwd(), '.coaia', 'pde');
24
- }
25
-
26
- private getSessionPath(sessionId: string): string {
27
- return path.join(this.baseDir, `${sessionId}.jsonl`);
28
- }
29
-
30
- async ensureBaseDir(): Promise<void> {
31
- await fs.mkdir(this.baseDir, { recursive: true });
32
- }
33
-
34
- async initSession(
35
- originalPrompt: string,
36
- masterChartId: string,
37
- pdeDecompositionId?: string
38
- ): Promise<PdeSession> {
39
- await this.ensureBaseDir();
40
-
41
- const sessionId = uuidv4();
42
- const now = new Date().toISOString();
43
-
44
- const session: PdeSession = {
45
- type: 'pde_session',
46
- sessionId,
47
- originalPrompt,
48
- masterChartId,
49
- pdeDecompositionId,
50
- createdAt: now,
51
- updatedAt: now,
52
- status: 'active'
53
- };
54
-
55
- const sessionPath = this.getSessionPath(sessionId);
56
- await fs.writeFile(sessionPath, JSON.stringify(session) + '\n');
57
-
58
- return session;
59
- }
60
-
61
- async loadSession(sessionId: string): Promise<{
62
- session: PdeSession;
63
- entities: Entity[];
64
- relations: Relation[];
65
- }> {
66
- const sessionPath = this.getSessionPath(sessionId);
67
- const data = await fs.readFile(sessionPath, 'utf-8');
68
- const lines = data.split('\n').filter(line => line.trim());
69
-
70
- let session: PdeSession | null = null;
71
- const entities: Entity[] = [];
72
- const relations: Relation[] = [];
73
-
74
- for (const line of lines) {
75
- const item = JSON.parse(line);
76
- if (item.type === 'pde_session') {
77
- session = item as PdeSession;
78
- } else if (item.type === 'entity') {
79
- const { type: _discriminator, ...entity } = item;
80
- entities.push(entity as Entity);
81
- } else if (item.type === 'relation') {
82
- const { type: _discriminator, ...relation } = item;
83
- relations.push(relation as Relation);
84
- }
85
- }
86
-
87
- if (!session) {
88
- throw new Error(`Session ${sessionId} not found or corrupted`);
89
- }
90
-
91
- return { session, entities, relations };
92
- }
93
-
94
- async appendEntity(sessionId: string, entity: Entity): Promise<void> {
95
- const sessionPath = this.getSessionPath(sessionId);
96
- await fs.appendFile(sessionPath, serializeEntity(entity) + '\n');
97
- }
98
-
99
- async appendRelation(sessionId: string, relation: Relation): Promise<void> {
100
- const sessionPath = this.getSessionPath(sessionId);
101
- await fs.appendFile(sessionPath, serializeRelation(relation) + '\n');
102
- }
103
-
104
- async updateSession(sessionId: string, updates: Partial<PdeSession>): Promise<void> {
105
- const { session, entities, relations } = await this.loadSession(sessionId);
106
- const updatedSession = { ...session, ...updates, updatedAt: new Date().toISOString() };
107
-
108
- const sessionPath = this.getSessionPath(sessionId);
109
- const lines = [
110
- JSON.stringify(updatedSession),
111
- ...entities.map(e => serializeEntity(e)),
112
- ...relations.map(r => serializeRelation(r))
113
- ];
114
- await fs.writeFile(sessionPath, lines.join('\n') + '\n');
115
- }
116
-
117
- async updateEntity(sessionId: string, entityName: string, updates: Partial<Entity>): Promise<void> {
118
- const { session, entities, relations } = await this.loadSession(sessionId);
119
-
120
- const entityIndex = entities.findIndex(e => e.name === entityName);
121
- if (entityIndex === -1) {
122
- throw new Error(`Entity ${entityName} not found in session ${sessionId}`);
123
- }
124
-
125
- entities[entityIndex] = { ...entities[entityIndex], ...updates };
126
- if (entities[entityIndex].metadata) {
127
- entities[entityIndex].metadata!.updatedAt = new Date().toISOString();
128
- }
129
-
130
- const sessionPath = this.getSessionPath(sessionId);
131
- const lines = [
132
- JSON.stringify(session),
133
- ...entities.map(e => serializeEntity(e)),
134
- ...relations.map(r => serializeRelation(r))
135
- ];
136
- await fs.writeFile(sessionPath, lines.join('\n') + '\n');
137
- }
138
-
139
- async listSessions(): Promise<PdeSession[]> {
140
- try {
141
- await this.ensureBaseDir();
142
- const files = await fs.readdir(this.baseDir);
143
- const sessions: PdeSession[] = [];
144
-
145
- for (const file of files) {
146
- if (file.endsWith('.jsonl')) {
147
- try {
148
- const sessionId = file.replace('.jsonl', '');
149
- const { session } = await this.loadSession(sessionId);
150
- sessions.push(session);
151
- } catch {
152
- // Skip corrupted files
153
- }
154
- }
155
- }
156
-
157
- return sessions.sort((a, b) =>
158
- new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
159
- );
160
- } catch {
161
- return [];
162
- }
163
- }
164
-
165
- async deleteSession(sessionId: string): Promise<void> {
166
- const sessionPath = this.getSessionPath(sessionId);
167
- await fs.unlink(sessionPath);
168
- }
169
-
170
- async getSessionFilePath(sessionId: string): Promise<string> {
171
- return this.getSessionPath(sessionId);
172
- }
173
-
174
- // =========================================================================
175
- // PDE Decomposition File Access (.pde/ directory)
176
- // =========================================================================
177
-
178
- async listPdeDecompositions(workdir?: string): Promise<Array<{
179
- id: string;
180
- timestamp: string;
181
- prompt: string;
182
- }>> {
183
- const pdeDir = path.join(workdir || process.cwd(), '.pde');
184
- try {
185
- const entries = await fs.readdir(pdeDir);
186
- const results: Array<{ id: string; timestamp: string; prompt: string }> = [];
187
-
188
- for (const entry of entries) {
189
- if (!entry.endsWith('.json')) continue;
190
- try {
191
- const filePath = path.join(pdeDir, entry);
192
- const data = await fs.readFile(filePath, 'utf-8');
193
- const stored = JSON.parse(data) as StoredDecomposition;
194
- if (stored.id && stored.result) {
195
- results.push({
196
- id: stored.id,
197
- timestamp: stored.timestamp,
198
- prompt: stored.prompt,
199
- });
200
- }
201
- } catch {
202
- // Skip invalid files
203
- }
204
- }
205
-
206
- return results.sort((a, b) =>
207
- new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()
208
- );
209
- } catch {
210
- return [];
211
- }
212
- }
213
-
214
- async loadPdeDecomposition(id: string, workdir?: string): Promise<StoredDecomposition | null> {
215
- const pdeDir = path.join(workdir || process.cwd(), '.pde');
216
-
217
- // Try direct {id}.json file first
218
- const directPath = path.join(pdeDir, `${id}.json`);
219
- try {
220
- const data = await fs.readFile(directPath, 'utf-8');
221
- return JSON.parse(data) as StoredDecomposition;
222
- } catch {
223
- // Not found as direct file — scan for matching id
224
- }
225
-
226
- // Scan directory for a .json file whose parsed id matches
227
- try {
228
- const entries = await fs.readdir(pdeDir);
229
- for (const entry of entries) {
230
- if (!entry.endsWith('.json')) continue;
231
- try {
232
- const filePath = path.join(pdeDir, entry);
233
- const data = await fs.readFile(filePath, 'utf-8');
234
- const parsed = JSON.parse(data) as StoredDecomposition;
235
- if (parsed.id === id) return parsed;
236
- } catch {
237
- // Skip invalid
238
- }
239
- }
240
- } catch {
241
- // Directory not found
242
- }
243
-
244
- return null;
245
- }
246
- }
247
-
248
- export const sessionManager = new SessionManager();