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
package/src/cli.ts DELETED
@@ -1,1261 +0,0 @@
1
- #!/usr/bin/env node
2
- import { Command } from 'commander';
3
- import chalk from 'chalk';
4
- import { readFileSync, writeFileSync, existsSync } from 'node:fs';
5
- import { resolve } from 'node:path';
6
-
7
- import { tash, fetch as redisFetch, keys as redisKeys, disconnect as redisDisconnect } from './redis.js';
8
- import { readConfig, getConfig } from './config.js';
9
- import { llm as llmCall, transcribeAudio, abstractProcess } from './llm.js';
10
- import { listIssues, getIssue } from './github.js';
11
- import { createEnvironment, findEnvironment } from './environment.js';
12
- import { getPackageVersion } from './version.js';
13
- import {
14
- formatTable, formatJson, formatError, formatSuccess,
15
- truncate, formatDate, formatProgress, setColorEnabled,
16
- } from './cli-helpers.js';
17
-
18
- // ---------------------------------------------------------------------------
19
- // Types
20
- // ---------------------------------------------------------------------------
21
-
22
- interface GlobalOpts {
23
- env?: string;
24
- memoryPath?: string;
25
- color: boolean;
26
- json: boolean;
27
- }
28
-
29
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
30
- type AnyModule = Record<string, (...args: any[]) => any>;
31
-
32
- // ---------------------------------------------------------------------------
33
- // Helpers
34
- // ---------------------------------------------------------------------------
35
-
36
- async function loadModule(name: string): Promise<AnyModule> {
37
- const paths: Record<string, string> = {
38
- langfuse: './langfuse/index.js',
39
- narrative: './narrative/index.js',
40
- pde: './pde/index.js',
41
- planning: './planning/index.js',
42
- pipeline: './pipeline/index.js',
43
- };
44
- const p = paths[name];
45
- if (!p) throw new Error(`Unknown module: ${name}`);
46
- try {
47
- return (await import(p)) as AnyModule;
48
- } catch {
49
- throw new Error(`Module '${name}' is not yet implemented. Expected at ${p}`);
50
- }
51
- }
52
-
53
- async function callModule(moduleName: string, funcName: string, ...args: unknown[]): Promise<unknown> {
54
- const mod = await loadModule(moduleName);
55
- const fn = mod[funcName];
56
- if (typeof fn !== 'function') {
57
- throw new Error(`${moduleName}.${funcName} is not available`);
58
- }
59
- return fn(...args);
60
- }
61
-
62
- function globals(cmd: Command): GlobalOpts {
63
- return cmd.optsWithGlobals() as GlobalOpts;
64
- }
65
-
66
- function output(data: unknown, cmd: Command): void {
67
- if (globals(cmd).json) {
68
- console.log(formatJson(data));
69
- } else if (typeof data === 'string') {
70
- console.log(data);
71
- } else {
72
- console.log(formatJson(data));
73
- }
74
- }
75
-
76
- async function readStdin(): Promise<string> {
77
- if (process.stdin.isTTY) return '';
78
- const chunks: string[] = [];
79
- for await (const chunk of process.stdin) {
80
- chunks.push(String(chunk));
81
- }
82
- return chunks.join('');
83
- }
84
-
85
- function parseInteger(value: string): number {
86
- return parseInt(value, 10);
87
- }
88
-
89
- function resolveText(
90
- positional: string | undefined,
91
- opts: { file?: string },
92
- ): string | undefined {
93
- if (opts.file) return readFileSync(resolve(opts.file), 'utf-8');
94
- return positional;
95
- }
96
-
97
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
98
- function actionHandler(
99
- fn: (...args: any[]) => Promise<void>,
100
- ): (...args: any[]) => Promise<void> {
101
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
102
- return async (...args: any[]) => {
103
- try {
104
- await fn(...args);
105
- } catch (err) {
106
- console.error(formatError(err instanceof Error ? err.message : String(err)));
107
- process.exit(1);
108
- }
109
- };
110
- }
111
-
112
- // ---------------------------------------------------------------------------
113
- // Root commands
114
- // ---------------------------------------------------------------------------
115
-
116
- function registerRootCommands(program: Command): void {
117
- // ── tash ─────────────────────────────────────────────────────────────
118
- program
119
- .command('tash')
120
- .alias('m')
121
- .description('Store a key-value pair in Redis')
122
- .argument('<key>', 'Redis key')
123
- .argument('[value]', 'Value to store')
124
- .option('-F, --file <path>', 'Read value from file')
125
- .option('-T, --ttl <minutes>', 'Time to live in minutes', parseInteger, 5555)
126
- .option('-v, --verbose', 'Verbose output')
127
- .action(
128
- actionHandler(async (key: string, value: string | undefined, opts: { file?: string; ttl?: number; verbose?: boolean }, cmd: Command) => {
129
- const val = resolveText(value, opts);
130
- if (!val) {
131
- console.error(formatError('Value required — provide as argument or with --file'));
132
- process.exit(1);
133
- }
134
- await tash(key, val, opts.ttl);
135
- if (opts.verbose) {
136
- console.log(formatSuccess(`Stored ${key} (${val.length} bytes${opts.ttl ? `, ttl=${opts.ttl}m` : ''})`));
137
- } else {
138
- console.log(formatSuccess(`Stored ${key}`));
139
- }
140
- }),
141
- );
142
-
143
- // ── fetch ────────────────────────────────────────────────────────────
144
- program
145
- .command('fetch')
146
- .description('Get a value from Redis')
147
- .argument('<key>', 'Redis key')
148
- .option('-O, --output <path>', 'Write result to file')
149
- .option('-v, --verbose', 'Verbose output')
150
- .action(
151
- actionHandler(async (key: string, opts: { output?: string; verbose?: boolean }, cmd: Command) => {
152
- const result = await redisFetch(key);
153
- if (result === null) {
154
- console.error(formatError(`Key not found: ${key}`));
155
- process.exit(1);
156
- }
157
- if (opts.output) {
158
- writeFileSync(resolve(opts.output), result);
159
- console.log(formatSuccess(`Written to ${opts.output}`));
160
- } else {
161
- output(result, cmd);
162
- }
163
- }),
164
- );
165
-
166
- // ── transcribe ───────────────────────────────────────────────────────
167
- program
168
- .command('transcribe')
169
- .alias('t')
170
- .description('Transcribe audio via Whisper')
171
- .argument('<file>', 'Audio file path')
172
- .option('--output <path>', 'Write transcript to file')
173
- .action(
174
- actionHandler(async (file: string, opts: { output?: string }, cmd: Command) => {
175
- const result = await transcribeAudio(resolve(file));
176
- if (opts.output) {
177
- writeFileSync(resolve(opts.output), result);
178
- console.log(formatSuccess(`Transcript written to ${opts.output}`));
179
- } else {
180
- output(result, cmd);
181
- }
182
- }),
183
- );
184
-
185
- // ── summarize ────────────────────────────────────────────────────────
186
- program
187
- .command('summarize')
188
- .alias('s')
189
- .description('Summarize text')
190
- .argument('[text]', 'Text to summarize')
191
- .option('--output <path>', 'Write summary to file')
192
- .option('--file <path>', 'Read text from file')
193
- .action(
194
- actionHandler(async (text: string | undefined, opts: { output?: string; file?: string }, cmd: Command) => {
195
- let input = resolveText(text, opts);
196
- if (!input) input = await readStdin();
197
- if (!input) {
198
- console.error(formatError('Text required — provide as argument, --file, or pipe via stdin'));
199
- process.exit(1);
200
- }
201
- const result = await abstractProcess('summarize', input);
202
- if (opts.output) {
203
- writeFileSync(resolve(opts.output), result);
204
- console.log(formatSuccess(`Summary written to ${opts.output}`));
205
- } else {
206
- output(result, cmd);
207
- }
208
- }),
209
- );
210
-
211
- // ── p (process) ──────────────────────────────────────────────────────
212
- program
213
- .command('p')
214
- .description('Process text with a custom tag')
215
- .argument('<tag>', 'Processing tag')
216
- .argument('[text]', 'Text to process')
217
- .option('--output <path>', 'Write result to file')
218
- .option('--file <path>', 'Read text from file')
219
- .action(
220
- actionHandler(async (tag: string, text: string | undefined, opts: { output?: string; file?: string }, cmd: Command) => {
221
- let input = resolveText(text, opts);
222
- if (!input) input = await readStdin();
223
- if (!input) {
224
- console.error(formatError('Text required — provide as argument, --file, or pipe via stdin'));
225
- process.exit(1);
226
- }
227
- const result = await abstractProcess(tag, input);
228
- if (opts.output) {
229
- writeFileSync(resolve(opts.output), result);
230
- console.log(formatSuccess(`Result written to ${opts.output}`));
231
- } else {
232
- output(result, cmd);
233
- }
234
- }),
235
- );
236
-
237
- // ── init ─────────────────────────────────────────────────────────────
238
- program
239
- .command('init')
240
- .description('Create a sample coaia.json config')
241
- .action(
242
- actionHandler(async () => {
243
- const target = resolve('coaia.json');
244
- if (existsSync(target)) {
245
- console.error(formatError('coaia.json already exists'));
246
- process.exit(1);
247
- }
248
- const sample = {
249
- redis: { url: 'redis://localhost:6379' },
250
- langfuse: { publicKey: '', secretKey: '', baseUrl: 'https://cloud.langfuse.com' },
251
- openai: { apiKey: '', model: 'gpt-4o-mini' },
252
- github: { token: '' },
253
- };
254
- writeFileSync(target, JSON.stringify(sample, null, 2) + '\n');
255
- console.log(formatSuccess('Created coaia.json'));
256
- }),
257
- );
258
-
259
- // ── llm ──────────────────────────────────────────────────────────────
260
- program
261
- .command('llm')
262
- .description('Make a raw LLM call')
263
- .argument('<user>', 'User message')
264
- .argument('[system]', 'System message')
265
- .option('--temperature <n>', 'Temperature (0-2)', parseFloat)
266
- .action(
267
- actionHandler(async (user: string, system: string | undefined, opts: { temperature?: number }, cmd: Command) => {
268
- const result = await llmCall(user, system, opts.temperature);
269
- output(result, cmd);
270
- }),
271
- );
272
- }
273
-
274
- // ---------------------------------------------------------------------------
275
- // Fuse (Langfuse) commands
276
- // ---------------------------------------------------------------------------
277
-
278
- function registerFuseCommands(program: Command): void {
279
- const fuse = program
280
- .command('fuse')
281
- .description('Langfuse operations');
282
-
283
- // ── comments ─────────────────────────────────────────────────────────
284
- const comments = fuse.command('comments').description('Comment operations');
285
-
286
- comments
287
- .command('list')
288
- .description('List comments')
289
- .option('--trace-id <id>', 'Filter by trace ID')
290
- .option('--page <n>', 'Page number', parseInt)
291
- .option('--limit <n>', 'Results per page', parseInt)
292
- .action(actionHandler(async (opts: Record<string, unknown>, cmd: Command) => {
293
- const result = await callModule('langfuse', 'listComments', opts);
294
- output(result, cmd);
295
- }));
296
-
297
- comments
298
- .command('post')
299
- .description('Post a comment')
300
- .option('--trace-id <id>', 'Trace ID')
301
- .option('--content <text>', 'Comment content')
302
- .option('--author <name>', 'Author name')
303
- .action(actionHandler(async (opts: Record<string, unknown>, cmd: Command) => {
304
- const result = await callModule('langfuse', 'postComment', opts);
305
- output(result, cmd);
306
- }));
307
-
308
- // ── prompts ──────────────────────────────────────────────────────────
309
- const prompts = fuse.command('prompts').description('Prompt management');
310
-
311
- prompts
312
- .command('list')
313
- .description('List prompts')
314
- .option('--page <n>', 'Page number', parseInt)
315
- .option('--limit <n>', 'Results per page', parseInt)
316
- .action(actionHandler(async (opts: Record<string, unknown>, cmd: Command) => {
317
- const result = await callModule('langfuse', 'listPrompts', opts);
318
- output(result, cmd);
319
- }));
320
-
321
- prompts
322
- .command('get')
323
- .description('Get a prompt')
324
- .argument('<name>', 'Prompt name')
325
- .option('--version <n>', 'Prompt version', parseInt)
326
- .action(actionHandler(async (name: string, opts: Record<string, unknown>, cmd: Command) => {
327
- const result = await callModule('langfuse', 'getPrompt', name, opts);
328
- output(result, cmd);
329
- }));
330
-
331
- prompts
332
- .command('create')
333
- .description('Create a prompt')
334
- .option('--name <name>', 'Prompt name')
335
- .option('--prompt <text>', 'Prompt text')
336
- .option('--model <model>', 'Model name')
337
- .option('--config <json>', 'Config JSON')
338
- .action(actionHandler(async (opts: Record<string, unknown>, cmd: Command) => {
339
- const result = await callModule('langfuse', 'createPrompt', opts);
340
- output(result, cmd);
341
- }));
342
-
343
- // ── datasets ─────────────────────────────────────────────────────────
344
- const datasets = fuse.command('datasets').description('Dataset management');
345
-
346
- datasets
347
- .command('list')
348
- .description('List datasets')
349
- .option('--page <n>', 'Page number', parseInt)
350
- .option('--limit <n>', 'Results per page', parseInt)
351
- .action(actionHandler(async (opts: Record<string, unknown>, cmd: Command) => {
352
- const result = await callModule('langfuse', 'listDatasets', opts);
353
- output(result, cmd);
354
- }));
355
-
356
- datasets
357
- .command('get')
358
- .description('Get a dataset')
359
- .argument('<name>', 'Dataset name')
360
- .action(actionHandler(async (name: string, opts: Record<string, unknown>, cmd: Command) => {
361
- const result = await callModule('langfuse', 'getDataset', name);
362
- output(result, cmd);
363
- }));
364
-
365
- datasets
366
- .command('create')
367
- .description('Create a dataset')
368
- .option('--name <name>', 'Dataset name')
369
- .option('--description <text>', 'Description')
370
- .action(actionHandler(async (opts: Record<string, unknown>, cmd: Command) => {
371
- const result = await callModule('langfuse', 'createDataset', opts);
372
- output(result, cmd);
373
- }));
374
-
375
- // ── sessions ─────────────────────────────────────────────────────────
376
- const sessions = fuse.command('sessions').description('Session management');
377
-
378
- sessions
379
- .command('create')
380
- .description('Create a session')
381
- .option('--id <id>', 'Session ID')
382
- .option('--project-id <id>', 'Project ID')
383
- .action(actionHandler(async (opts: Record<string, unknown>, cmd: Command) => {
384
- const result = await callModule('langfuse', 'createSession', opts);
385
- output(result, cmd);
386
- }));
387
-
388
- sessions
389
- .command('addnode')
390
- .description('Add a node to a session')
391
- .option('--session-id <id>', 'Session ID')
392
- .option('--trace-id <id>', 'Trace ID')
393
- .action(actionHandler(async (opts: Record<string, unknown>, cmd: Command) => {
394
- const result = await callModule('langfuse', 'addSessionNode', opts);
395
- output(result, cmd);
396
- }));
397
-
398
- sessions
399
- .command('view')
400
- .description('View a session')
401
- .argument('<sessionId>', 'Session ID')
402
- .action(actionHandler(async (sessionId: string, opts: Record<string, unknown>, cmd: Command) => {
403
- const result = await callModule('langfuse', 'viewSession', sessionId);
404
- output(result, cmd);
405
- }));
406
-
407
- // ── scores ───────────────────────────────────────────────────────────
408
- const scores = fuse.command('scores').alias('sc').description('Score management');
409
-
410
- scores
411
- .command('create')
412
- .description('Create a score')
413
- .option('--name <name>', 'Score name')
414
- .option('--trace-id <id>', 'Trace ID')
415
- .option('--observation-id <id>', 'Observation ID')
416
- .option('--value <n>', 'Score value', parseFloat)
417
- .option('--data-type <type>', 'Data type (NUMERIC|CATEGORICAL|BOOLEAN)')
418
- .option('--comment <text>', 'Comment')
419
- .action(actionHandler(async (opts: Record<string, unknown>, cmd: Command) => {
420
- const result = await callModule('langfuse', 'createScore', opts);
421
- output(result, cmd);
422
- }));
423
-
424
- scores
425
- .command('apply')
426
- .description('Apply a score config')
427
- .option('--config <name>', 'Score config name')
428
- .option('--trace-id <id>', 'Trace ID')
429
- .option('--value <n>', 'Score value', parseFloat)
430
- .action(actionHandler(async (opts: Record<string, unknown>, cmd: Command) => {
431
- const result = await callModule('langfuse', 'applyScore', opts);
432
- output(result, cmd);
433
- }));
434
-
435
- scores
436
- .command('list')
437
- .description('List scores')
438
- .option('--trace-id <id>', 'Filter by trace ID')
439
- .option('--name <name>', 'Filter by name')
440
- .option('--page <n>', 'Page number', parseInt)
441
- .option('--limit <n>', 'Results per page', parseInt)
442
- .action(actionHandler(async (opts: Record<string, unknown>, cmd: Command) => {
443
- const result = await callModule('langfuse', 'listScores', opts);
444
- output(result, cmd);
445
- }));
446
-
447
- // ── score-configs ────────────────────────────────────────────────────
448
- const scc = fuse.command('score-configs').alias('scc').description('Score config management');
449
-
450
- scc
451
- .command('list')
452
- .description('List score configs')
453
- .option('--page <n>', 'Page number', parseInt)
454
- .option('--limit <n>', 'Results per page', parseInt)
455
- .action(actionHandler(async (opts: Record<string, unknown>, cmd: Command) => {
456
- const result = await callModule('langfuse', 'listScoreConfigs', opts);
457
- output(result, cmd);
458
- }));
459
-
460
- scc
461
- .command('get')
462
- .description('Get a score config')
463
- .argument('<name>', 'Config name')
464
- .action(actionHandler(async (name: string, opts: Record<string, unknown>, cmd: Command) => {
465
- const result = await callModule('langfuse', 'getScoreConfig', name);
466
- output(result, cmd);
467
- }));
468
-
469
- scc
470
- .command('create')
471
- .description('Create a score config')
472
- .option('--name <name>', 'Config name')
473
- .option('--data-type <type>', 'NUMERIC | CATEGORICAL | BOOLEAN')
474
- .option('--description <text>', 'Description')
475
- .option('--min <n>', 'Min value (NUMERIC)', parseFloat)
476
- .option('--max <n>', 'Max value (NUMERIC)', parseFloat)
477
- .option('--categories <json>', 'Categories JSON (CATEGORICAL)')
478
- .action(actionHandler(async (opts: Record<string, unknown>, cmd: Command) => {
479
- const result = await callModule('langfuse', 'createScoreConfig', opts);
480
- output(result, cmd);
481
- }));
482
-
483
- scc
484
- .command('export')
485
- .description('Export score configs')
486
- .option('--output <path>', 'Output file path')
487
- .action(actionHandler(async (opts: Record<string, unknown>, cmd: Command) => {
488
- const result = await callModule('langfuse', 'exportScoreConfigs', opts);
489
- output(result, cmd);
490
- }));
491
-
492
- scc
493
- .command('import')
494
- .description('Import score configs')
495
- .argument('<path>', 'Config file path')
496
- .action(actionHandler(async (path: string, opts: Record<string, unknown>, cmd: Command) => {
497
- const result = await callModule('langfuse', 'importScoreConfigs', resolve(path));
498
- output(result, cmd);
499
- }));
500
-
501
- scc
502
- .command('presets')
503
- .description('List available presets')
504
- .action(actionHandler(async (opts: Record<string, unknown>, cmd: Command) => {
505
- const result = await callModule('langfuse', 'listScoreConfigPresets');
506
- output(result, cmd);
507
- }));
508
-
509
- scc
510
- .command('apply')
511
- .description('Apply a preset')
512
- .argument('<preset>', 'Preset name')
513
- .action(actionHandler(async (preset: string, opts: Record<string, unknown>, cmd: Command) => {
514
- const result = await callModule('langfuse', 'applyScoreConfigPreset', preset);
515
- output(result, cmd);
516
- }));
517
-
518
- scc
519
- .command('available')
520
- .description('Show available score types')
521
- .action(actionHandler(async (opts: Record<string, unknown>, cmd: Command) => {
522
- const result = await callModule('langfuse', 'availableScoreTypes');
523
- output(result, cmd);
524
- }));
525
-
526
- scc
527
- .command('show')
528
- .description('Show a score config in detail')
529
- .argument('<name>', 'Config name')
530
- .action(actionHandler(async (name: string, opts: Record<string, unknown>, cmd: Command) => {
531
- const result = await callModule('langfuse', 'showScoreConfig', name);
532
- output(result, cmd);
533
- }));
534
-
535
- // ── traces ───────────────────────────────────────────────────────────
536
- const traces = fuse.command('traces').description('Trace operations');
537
-
538
- traces
539
- .command('list')
540
- .description('List traces with optional filters')
541
- .option('--session-id <id>', 'Filter by session ID')
542
- .option('--user-id <id>', 'Filter by user ID')
543
- .option('--name <name>', 'Filter by trace name')
544
- .option('--tags <tags>', 'Comma-separated tags to filter by')
545
- .option('--from <timestamp>', 'From timestamp (ISO 8601)')
546
- .option('--to <timestamp>', 'To timestamp (ISO 8601)')
547
- .option('--order-by <field>', 'Order by field')
548
- .option('--page <n>', 'Page number', parseInt)
549
- .option('--limit <n>', 'Items per page (default 50)', parseInt)
550
- .action(actionHandler(async (opts: Record<string, unknown>, cmd: Command) => {
551
- const filters = {
552
- sessionId: opts.sessionId as string | undefined,
553
- userId: opts.userId as string | undefined,
554
- name: opts.name as string | undefined,
555
- tags: opts.tags ? (opts.tags as string).split(',').map((t: string) => t.trim()) : undefined,
556
- fromTimestamp: opts.from as string | undefined,
557
- toTimestamp: opts.to as string | undefined,
558
- orderBy: opts.orderBy as string | undefined,
559
- page: opts.page as number | undefined,
560
- limit: opts.limit as number | undefined,
561
- };
562
- const raw = await callModule('langfuse', 'listTraces', filters) as string;
563
- if (globals(cmd).json) {
564
- console.log(raw);
565
- } else {
566
- const formatted = await callModule('langfuse', 'formatTracesMarkdown', raw);
567
- console.log(formatted);
568
- }
569
- }));
570
-
571
- traces
572
- .command('create')
573
- .description('Create a trace')
574
- .option('--name <name>', 'Trace name')
575
- .option('--session-id <id>', 'Session ID')
576
- .option('--input <json>', 'Input JSON')
577
- .option('--metadata <json>', 'Metadata JSON')
578
- .action(actionHandler(async (opts: Record<string, unknown>, cmd: Command) => {
579
- const result = await callModule('langfuse', 'createTrace', opts);
580
- output(result, cmd);
581
- }));
582
-
583
- traces
584
- .command('add-observation')
585
- .description('Add an observation to a trace')
586
- .option('--trace-id <id>', 'Trace ID')
587
- .option('--name <name>', 'Observation name')
588
- .option('--type <type>', 'Type: generation | span | event')
589
- .option('--input <json>', 'Input JSON')
590
- .option('--output <json>', 'Output JSON')
591
- .action(actionHandler(async (opts: Record<string, unknown>, cmd: Command) => {
592
- const result = await callModule('langfuse', 'addObservation', opts);
593
- output(result, cmd);
594
- }));
595
-
596
- traces
597
- .command('add-observations')
598
- .description('Add multiple observations from a file')
599
- .option('--trace-id <id>', 'Trace ID')
600
- .option('--file <path>', 'JSON file with observations')
601
- .action(actionHandler(async (opts: { traceId?: string; file?: string }, cmd: Command) => {
602
- let data: unknown = opts;
603
- if (opts.file) {
604
- const raw = readFileSync(resolve(opts.file), 'utf-8');
605
- data = { traceId: opts.traceId, observations: JSON.parse(raw) };
606
- }
607
- const result = await callModule('langfuse', 'addObservations', data);
608
- output(result, cmd);
609
- }));
610
-
611
- traces
612
- .command('patch-output')
613
- .description('Patch trace output')
614
- .option('--trace-id <id>', 'Trace ID')
615
- .option('--output <json>', 'Output JSON')
616
- .action(actionHandler(async (opts: Record<string, unknown>, cmd: Command) => {
617
- const result = await callModule('langfuse', 'patchTraceOutput', opts);
618
- output(result, cmd);
619
- }));
620
-
621
- traces
622
- .command('session-view')
623
- .description('View traces by session')
624
- .argument('<sessionId>', 'Session ID')
625
- .action(actionHandler(async (sessionId: string, opts: Record<string, unknown>, cmd: Command) => {
626
- const result = await callModule('langfuse', 'sessionView', sessionId);
627
- output(result, cmd);
628
- }));
629
-
630
- traces
631
- .command('trace-view')
632
- .description('View a specific trace')
633
- .argument('<traceId>', 'Trace ID')
634
- .action(actionHandler(async (traceId: string, opts: Record<string, unknown>, cmd: Command) => {
635
- const result = await callModule('langfuse', 'traceView', traceId);
636
- output(result, cmd);
637
- }));
638
-
639
- traces
640
- .command('get-observation')
641
- .description('Get an observation')
642
- .argument('<observationId>', 'Observation ID')
643
- .action(actionHandler(async (observationId: string, opts: Record<string, unknown>, cmd: Command) => {
644
- const result = await callModule('langfuse', 'getObservation', observationId);
645
- output(result, cmd);
646
- }));
647
-
648
- // ── projects ─────────────────────────────────────────────────────────
649
- fuse
650
- .command('projects')
651
- .description('List projects')
652
- .action(actionHandler(async (opts: Record<string, unknown>, cmd: Command) => {
653
- const result = await callModule('langfuse', 'listProjects');
654
- output(result, cmd);
655
- }));
656
-
657
- // ── media ────────────────────────────────────────────────────────────
658
- const media = fuse.command('media').description('Media operations');
659
-
660
- media
661
- .command('upload')
662
- .description('Upload a media file')
663
- .argument('<file>', 'File path')
664
- .option('--trace-id <id>', 'Associate with trace')
665
- .action(actionHandler(async (file: string, opts: Record<string, unknown>, cmd: Command) => {
666
- const result = await callModule('langfuse', 'uploadMedia', resolve(file), opts);
667
- output(result, cmd);
668
- }));
669
-
670
- media
671
- .command('get')
672
- .description('Get media details')
673
- .argument('<mediaId>', 'Media ID')
674
- .action(actionHandler(async (mediaId: string, opts: Record<string, unknown>, cmd: Command) => {
675
- const result = await callModule('langfuse', 'getMedia', mediaId);
676
- output(result, cmd);
677
- }));
678
-
679
- // ── dataset-items ────────────────────────────────────────────────────
680
- const datasetItems = fuse.command('dataset-items').description('Dataset item operations');
681
-
682
- datasetItems
683
- .command('create')
684
- .description('Create a dataset item')
685
- .option('--dataset <name>', 'Dataset name')
686
- .option('--input <json>', 'Input JSON')
687
- .option('--expected <json>', 'Expected output JSON')
688
- .option('--metadata <json>', 'Metadata JSON')
689
- .action(actionHandler(async (opts: Record<string, unknown>, cmd: Command) => {
690
- const result = await callModule('langfuse', 'createDatasetItem', opts);
691
- output(result, cmd);
692
- }));
693
- }
694
-
695
- // ---------------------------------------------------------------------------
696
- // Pipeline commands
697
- // ---------------------------------------------------------------------------
698
-
699
- function registerPipelineCommands(program: Command): void {
700
- const pipeline = program
701
- .command('pipeline')
702
- .description('Pipeline template operations');
703
-
704
- pipeline
705
- .command('list')
706
- .description('List pipeline templates')
707
- .option('--path <dir>', 'Templates directory')
708
- .option('--json', 'JSON output')
709
- .action(actionHandler(async (opts: { path?: string; json?: boolean }, cmd: Command) => {
710
- const result = await callModule('pipeline', 'listTemplates', opts);
711
- output(result, cmd);
712
- }));
713
-
714
- pipeline
715
- .command('show')
716
- .description('Show a pipeline template')
717
- .argument('<name>', 'Template name')
718
- .option('--preview', 'Preview rendered template')
719
- .action(actionHandler(async (name: string, opts: { preview?: boolean }, cmd: Command) => {
720
- const result = await callModule('pipeline', 'showTemplate', name, opts);
721
- output(result, cmd);
722
- }));
723
-
724
- pipeline
725
- .command('create')
726
- .description('Create a run from template')
727
- .argument('<name>', 'Template name')
728
- .option(
729
- '--var <pair>',
730
- 'Template variable (KEY=value), repeatable',
731
- (val: string, list: string[]) => { list.push(val); return list; },
732
- [] as string[],
733
- )
734
- .option('--trace-id <id>', 'Trace ID')
735
- .option('--session-id <id>', 'Session ID')
736
- .option('--dry-run', 'Preview without executing')
737
- .action(actionHandler(async (name: string, opts: { var: string[]; traceId?: string; sessionId?: string; dryRun?: boolean }, cmd: Command) => {
738
- const vars: Record<string, string> = {};
739
- for (const pair of opts.var) {
740
- const eq = pair.indexOf('=');
741
- if (eq === -1) { console.error(formatError(`Invalid --var format: ${pair} (expected KEY=value)`)); process.exit(1); }
742
- vars[pair.slice(0, eq)] = pair.slice(eq + 1);
743
- }
744
- const result = await callModule('pipeline', 'createFromTemplate', name, {
745
- variables: vars,
746
- traceId: opts.traceId,
747
- sessionId: opts.sessionId,
748
- dryRun: opts.dryRun,
749
- });
750
- output(result, cmd);
751
- }));
752
-
753
- pipeline
754
- .command('init')
755
- .description('Create a new pipeline template')
756
- .argument('<name>', 'Template name')
757
- .action(actionHandler(async (name: string, opts: Record<string, unknown>, cmd: Command) => {
758
- const result = await callModule('pipeline', 'initTemplate', name);
759
- output(result, cmd);
760
- }));
761
- }
762
-
763
- // ---------------------------------------------------------------------------
764
- // Environment commands
765
- // ---------------------------------------------------------------------------
766
-
767
- function registerEnvCommands(program: Command): void {
768
- const env = program
769
- .command('env')
770
- .description('Environment variable management');
771
-
772
- env
773
- .command('init')
774
- .description('Initialize environment file')
775
- .action(actionHandler(async () => {
776
- const mgr = createEnvironment();
777
- mgr.init();
778
- console.log(formatSuccess(`Initialized ${mgr.getFilePath()}`));
779
- }));
780
-
781
- env
782
- .command('list')
783
- .description('List environment variables')
784
- .action(actionHandler(async (opts: Record<string, unknown>, cmd: Command) => {
785
- const mgr = findEnvironment();
786
- const vars = mgr.list();
787
- if (globals(cmd).json) {
788
- console.log(formatJson(vars));
789
- } else {
790
- const entries = Object.entries(vars);
791
- if (entries.length === 0) {
792
- console.log('(no variables set)');
793
- } else {
794
- console.log(formatTable(
795
- ['Key', 'Value'],
796
- entries.map(([k, v]) => [k, truncate(v, 60)]),
797
- ));
798
- }
799
- }
800
- }));
801
-
802
- env
803
- .command('source')
804
- .description('Load env vars into process')
805
- .action(actionHandler(async () => {
806
- const mgr = findEnvironment();
807
- mgr.source();
808
- console.log(formatSuccess('Environment loaded'));
809
- }));
810
-
811
- env
812
- .command('set')
813
- .description('Set an environment variable')
814
- .argument('<key>', 'Variable name')
815
- .argument('<value>', 'Variable value')
816
- .action(actionHandler(async (key: string, value: string) => {
817
- const mgr = findEnvironment();
818
- mgr.set(key, value);
819
- console.log(formatSuccess(`Set ${key}`));
820
- }));
821
-
822
- env
823
- .command('get')
824
- .description('Get an environment variable')
825
- .argument('<key>', 'Variable name')
826
- .action(actionHandler(async (key: string, opts: Record<string, unknown>, cmd: Command) => {
827
- const mgr = findEnvironment();
828
- const value = mgr.get(key);
829
- if (value === undefined) {
830
- console.error(formatError(`Variable not set: ${key}`));
831
- process.exit(1);
832
- }
833
- output(value, cmd);
834
- }));
835
-
836
- env
837
- .command('unset')
838
- .description('Remove an environment variable')
839
- .argument('<key>', 'Variable name')
840
- .action(actionHandler(async (key: string) => {
841
- const mgr = findEnvironment();
842
- mgr.unset(key);
843
- console.log(formatSuccess(`Unset ${key}`));
844
- }));
845
-
846
- env
847
- .command('clear')
848
- .description('Remove environment file')
849
- .action(actionHandler(async () => {
850
- const mgr = findEnvironment();
851
- mgr.clear();
852
- console.log(formatSuccess('Environment cleared'));
853
- }));
854
-
855
- env
856
- .command('save')
857
- .description('Save current context')
858
- .action(actionHandler(async () => {
859
- const mgr = findEnvironment();
860
- mgr.save();
861
- console.log(formatSuccess(`Saved to ${mgr.getFilePath()}`));
862
- }));
863
- }
864
-
865
- // ---------------------------------------------------------------------------
866
- // GitHub commands
867
- // ---------------------------------------------------------------------------
868
-
869
- function registerGhCommands(program: Command): void {
870
- const gh = program
871
- .command('gh')
872
- .description('GitHub operations');
873
-
874
- const issues = gh.command('issues').description('Issue operations');
875
-
876
- issues
877
- .command('list')
878
- .description('List issues')
879
- .requiredOption('--owner <owner>', 'Repository owner')
880
- .requiredOption('--repo <repo>', 'Repository name')
881
- .option('--state <state>', 'Filter: open | closed | all', 'open')
882
- .option('--labels <labels>', 'Comma-separated labels')
883
- .option('--per-page <n>', 'Results per page', parseInt)
884
- .action(actionHandler(async (opts: { owner: string; repo: string; state?: string; labels?: string; perPage?: number }, cmd: Command) => {
885
- const result = await listIssues(opts.owner, opts.repo, {
886
- state: opts.state as 'open' | 'closed' | 'all' | undefined,
887
- labels: opts.labels,
888
- per_page: opts.perPage,
889
- });
890
- if (globals(cmd).json) {
891
- console.log(formatJson(result));
892
- } else {
893
- console.log(formatTable(
894
- ['#', 'State', 'Title', 'Labels', 'Updated'],
895
- result.map(i => [
896
- String(i.number),
897
- i.state,
898
- truncate(i.title, 50),
899
- i.labels.map(l => l.name).join(', '),
900
- formatDate(i.updated_at),
901
- ]),
902
- ));
903
- }
904
- }));
905
-
906
- issues
907
- .command('get')
908
- .description('Get an issue (owner/repo#123 or number with --owner/--repo)')
909
- .argument('<spec>', 'Issue spec: owner/repo#123 or issue number')
910
- .option('--owner <owner>', 'Repository owner')
911
- .option('--repo <repo>', 'Repository name')
912
- .action(actionHandler(async (spec: string, opts: { owner?: string; repo?: string }, cmd: Command) => {
913
- let owner = opts.owner;
914
- let repo = opts.repo;
915
- let num: number;
916
-
917
- const match = spec.match(/^([^/]+)\/([^#]+)#(\d+)$/);
918
- if (match) {
919
- owner = match[1];
920
- repo = match[2];
921
- num = parseInt(match[3]!, 10);
922
- } else {
923
- num = parseInt(spec, 10);
924
- if (isNaN(num) || !owner || !repo) {
925
- console.error(formatError('Provide owner/repo#number or a number with --owner and --repo'));
926
- process.exit(1);
927
- }
928
- }
929
-
930
- const issue = await getIssue(owner!, repo!, num);
931
- if (globals(cmd).json) {
932
- console.log(formatJson(issue));
933
- } else {
934
- console.log([
935
- `${chalk.bold(`#${issue.number}`)} ${issue.title}`,
936
- `State: ${issue.state} Author: ${issue.user?.login ?? '?'}`,
937
- `Labels: ${issue.labels.map(l => l.name).join(', ') || '(none)'}`,
938
- `URL: ${issue.html_url}`,
939
- '',
940
- issue.body ?? '(no body)',
941
- ].join('\n'));
942
- }
943
- }));
944
- }
945
-
946
- // ---------------------------------------------------------------------------
947
- // Narrative commands
948
- // ---------------------------------------------------------------------------
949
-
950
- function registerNarrativeCommands(program: Command): void {
951
- const narr = program
952
- .command('narrative')
953
- .alias('n')
954
- .description('Structural tension narrative operations');
955
-
956
- narr
957
- .command('list')
958
- .alias('ls')
959
- .description('List active charts')
960
- .action(actionHandler(async (opts: Record<string, unknown>, cmd: Command) => {
961
- const result = await callModule('narrative', 'listCharts', globals(cmd));
962
- output(result, cmd);
963
- }));
964
-
965
- narr
966
- .command('view')
967
- .alias('v')
968
- .description('View chart details')
969
- .argument('<chartId>', 'Chart ID')
970
- .action(actionHandler(async (chartId: string, opts: Record<string, unknown>, cmd: Command) => {
971
- const result = await callModule('narrative', 'viewChart', chartId, globals(cmd));
972
- output(result, cmd);
973
- }));
974
-
975
- narr
976
- .command('current')
977
- .alias('cur')
978
- .description('Show or set the current chart')
979
- .argument('[chartId]', 'Chart ID to set as current')
980
- .action(actionHandler(async (chartId: string | undefined, opts: Record<string, unknown>, cmd: Command) => {
981
- if (chartId) {
982
- await callModule('narrative', 'setCurrentChart', chartId);
983
- console.log(formatSuccess(`Current chart set to ${chartId}`));
984
- } else {
985
- const result = await callModule('narrative', 'getCurrentChart');
986
- output(result, cmd);
987
- }
988
- }));
989
-
990
- narr
991
- .command('update')
992
- .alias('up')
993
- .description('Update a chart')
994
- .argument('<chartId>', 'Chart ID')
995
- .option('--desired-outcome <text>', 'New desired outcome')
996
- .option('--current-reality <text>', 'New current reality observation')
997
- .action(actionHandler(async (chartId: string, opts: { desiredOutcome?: string; currentReality?: string }, cmd: Command) => {
998
- const result = await callModule('narrative', 'updateChart', chartId, opts);
999
- output(result, cmd);
1000
- }));
1001
-
1002
- narr
1003
- .command('add-action')
1004
- .alias('aa')
1005
- .description('Add an action step to a chart')
1006
- .argument('<chartId>', 'Chart ID')
1007
- .option('--title <text>', 'Action step title')
1008
- .option('--current-reality <text>', 'Current reality for this step')
1009
- .option('--due <date>', 'Due date (ISO)')
1010
- .action(actionHandler(async (chartId: string, opts: { title?: string; currentReality?: string; due?: string }, cmd: Command) => {
1011
- const result = await callModule('narrative', 'addAction', chartId, opts);
1012
- output(result, cmd);
1013
- }));
1014
-
1015
- narr
1016
- .command('add-obs')
1017
- .alias('ao')
1018
- .description('Add an observation to a chart')
1019
- .argument('<chartId>', 'Chart ID')
1020
- .argument('[text...]', 'Observation text')
1021
- .action(actionHandler(async (chartId: string, text: string[], opts: Record<string, unknown>, cmd: Command) => {
1022
- const observation = text.join(' ');
1023
- if (!observation) {
1024
- console.error(formatError('Observation text required'));
1025
- process.exit(1);
1026
- }
1027
- const result = await callModule('narrative', 'addObservation', chartId, observation);
1028
- output(result, cmd);
1029
- }));
1030
-
1031
- narr
1032
- .command('complete')
1033
- .alias('done')
1034
- .description('Mark an action step complete')
1035
- .argument('<actionName>', 'Action step name')
1036
- .action(actionHandler(async (actionName: string, opts: Record<string, unknown>, cmd: Command) => {
1037
- const result = await callModule('narrative', 'completeAction', actionName);
1038
- if (typeof result === 'string') console.log(formatSuccess(result));
1039
- else output(result, cmd);
1040
- }));
1041
-
1042
- narr
1043
- .command('export')
1044
- .alias('exp')
1045
- .description('Export a chart to markdown')
1046
- .argument('<chartId>', 'Chart ID')
1047
- .option('--output <path>', 'Output file path')
1048
- .action(actionHandler(async (chartId: string, opts: { output?: string }, cmd: Command) => {
1049
- const result = await callModule('narrative', 'exportChart', chartId) as string;
1050
- if (opts.output) {
1051
- writeFileSync(resolve(opts.output), result);
1052
- console.log(formatSuccess(`Exported to ${opts.output}`));
1053
- } else {
1054
- console.log(result);
1055
- }
1056
- }));
1057
-
1058
- narr
1059
- .command('export-all')
1060
- .description('Export all charts to markdown')
1061
- .option('--output <dir>', 'Output directory')
1062
- .action(actionHandler(async (opts: { output?: string }, cmd: Command) => {
1063
- const result = await callModule('narrative', 'exportAllCharts', opts);
1064
- output(result, cmd);
1065
- }));
1066
-
1067
- narr
1068
- .command('stats')
1069
- .alias('st')
1070
- .description('Show chart statistics')
1071
- .action(actionHandler(async (opts: Record<string, unknown>, cmd: Command) => {
1072
- const result = await callModule('narrative', 'getStats', globals(cmd));
1073
- output(result, cmd);
1074
- }));
1075
-
1076
- narr
1077
- .command('progress')
1078
- .alias('pg')
1079
- .description('Progress report for a chart')
1080
- .argument('<chartId>', 'Chart ID')
1081
- .action(actionHandler(async (chartId: string, opts: Record<string, unknown>, cmd: Command) => {
1082
- const result = await callModule('narrative', 'getProgress', chartId) as {
1083
- completed: number; total: number; chartId: string;
1084
- } | unknown;
1085
- if (globals(cmd).json) {
1086
- console.log(formatJson(result));
1087
- } else if (
1088
- result &&
1089
- typeof result === 'object' &&
1090
- 'completed' in result &&
1091
- 'total' in result
1092
- ) {
1093
- const r = result as { completed: number; total: number; chartId: string };
1094
- console.log(`${chalk.bold(r.chartId)} ${formatProgress(r.completed, r.total)}`);
1095
- } else {
1096
- output(result, cmd);
1097
- }
1098
- }));
1099
-
1100
- narr
1101
- .command('mmot')
1102
- .description('Run MMOT evaluation on a chart')
1103
- .argument('<chartId>', 'Chart ID')
1104
- .option('--assessment <text>', 'Assessment text')
1105
- .option('--direction <dir>', 'Direction: South | East | West | North')
1106
- .action(actionHandler(async (chartId: string, opts: { assessment?: string; direction?: string }, cmd: Command) => {
1107
- const result = await callModule('narrative', 'performMmot', chartId, opts);
1108
- output(result, cmd);
1109
- }));
1110
-
1111
- narr
1112
- .command('set-date')
1113
- .alias('sd')
1114
- .description('Set due date on a chart')
1115
- .argument('<chartId>', 'Chart ID')
1116
- .argument('<date>', 'Due date (ISO)')
1117
- .action(actionHandler(async (chartId: string, date: string, opts: Record<string, unknown>, cmd: Command) => {
1118
- const result = await callModule('narrative', 'setDueDate', chartId, date);
1119
- if (typeof result === 'string') console.log(formatSuccess(result));
1120
- else output(result, cmd);
1121
- }));
1122
- }
1123
-
1124
- // ---------------------------------------------------------------------------
1125
- // PDE commands
1126
- // ---------------------------------------------------------------------------
1127
-
1128
- function registerPdeCommands(program: Command): void {
1129
- const pde = program
1130
- .command('pde')
1131
- .description('Prompt Decomposition Engine');
1132
-
1133
- pde
1134
- .command('import')
1135
- .description('Import a PDE decomposition into narrative')
1136
- .argument('<pdeId>', 'Decomposition ID')
1137
- .action(actionHandler(async (pdeId: string, opts: Record<string, unknown>, cmd: Command) => {
1138
- const result = await callModule('pde', 'importDecomposition', pdeId);
1139
- output(result, cmd);
1140
- }));
1141
-
1142
- pde
1143
- .command('list')
1144
- .description('List decompositions')
1145
- .action(actionHandler(async (opts: Record<string, unknown>, cmd: Command) => {
1146
- const result = await callModule('pde', 'listDecompositions');
1147
- output(result, cmd);
1148
- }));
1149
-
1150
- pde
1151
- .command('sessions')
1152
- .description('List PDE sessions')
1153
- .action(actionHandler(async (opts: Record<string, unknown>, cmd: Command) => {
1154
- const result = await callModule('pde', 'listSessions');
1155
- output(result, cmd);
1156
- }));
1157
-
1158
- pde
1159
- .command('show')
1160
- .description('Show session details')
1161
- .argument('<sessionId>', 'Session ID')
1162
- .action(actionHandler(async (sessionId: string, opts: Record<string, unknown>, cmd: Command) => {
1163
- const result = await callModule('pde', 'showSession', sessionId);
1164
- output(result, cmd);
1165
- }));
1166
- }
1167
-
1168
- // ---------------------------------------------------------------------------
1169
- // Planning commands
1170
- // ---------------------------------------------------------------------------
1171
-
1172
- function registerPlanCommands(program: Command): void {
1173
- const plan = program
1174
- .command('plan')
1175
- .description('Structural tension plan operations');
1176
-
1177
- plan
1178
- .command('parse')
1179
- .description('Parse a plan file structurally')
1180
- .argument('<planPath>', 'Path to plan file')
1181
- .action(actionHandler(async (planPath: string, opts: Record<string, unknown>, cmd: Command) => {
1182
- const result = await callModule('planning', 'parsePlan', resolve(planPath));
1183
- output(result, cmd);
1184
- }));
1185
-
1186
- plan
1187
- .command('convert')
1188
- .description('Convert a plan to a structural tension chart')
1189
- .argument('<planPath>', 'Path to plan file')
1190
- .action(actionHandler(async (planPath: string, opts: Record<string, unknown>, cmd: Command) => {
1191
- const result = await callModule('planning', 'convertToChart', resolve(planPath));
1192
- output(result, cmd);
1193
- }));
1194
-
1195
- plan
1196
- .command('sync-to-chart')
1197
- .description('Sync a plan file into charts')
1198
- .argument('<planPath>', 'Path to plan file')
1199
- .argument('<chartsPath>', 'Path to charts JSONL')
1200
- .action(actionHandler(async (planPath: string, chartsPath: string, opts: Record<string, unknown>, cmd: Command) => {
1201
- const result = await callModule('planning', 'syncToChart', resolve(planPath), resolve(chartsPath));
1202
- output(result, cmd);
1203
- }));
1204
-
1205
- plan
1206
- .command('sync-to-plan')
1207
- .description('Sync chart data back to a plan file')
1208
- .argument('<chartsPath>', 'Path to charts JSONL')
1209
- .argument('<planPath>', 'Path to plan file')
1210
- .action(actionHandler(async (chartsPath: string, planPath: string, opts: Record<string, unknown>, cmd: Command) => {
1211
- const result = await callModule('planning', 'syncToPlan', resolve(chartsPath), resolve(planPath));
1212
- output(result, cmd);
1213
- }));
1214
- }
1215
-
1216
- // ---------------------------------------------------------------------------
1217
- // Main
1218
- // ---------------------------------------------------------------------------
1219
-
1220
- async function main(): Promise<void> {
1221
- const program = new Command();
1222
-
1223
- program
1224
- .name('coaia')
1225
- .version(getPackageVersion(), '-V, --version')
1226
- .description('CoAIA unified CLI — structural tension, narrative, and DevOps tooling')
1227
- .option('--env <path>', 'Load environment file')
1228
- .option('-M, --memory-path <path>', 'JSONL memory file path')
1229
- .option('--no-color', 'Disable colors')
1230
- .option('--json', 'JSON output', false);
1231
-
1232
- program.hook('preAction', (_thisCmd, actionCmd) => {
1233
- const opts = actionCmd.optsWithGlobals();
1234
- if (opts.color === false) {
1235
- setColorEnabled(false);
1236
- }
1237
- if (opts.env) {
1238
- readConfig(opts.env as string);
1239
- }
1240
- });
1241
-
1242
- registerRootCommands(program);
1243
- registerFuseCommands(program);
1244
- registerPipelineCommands(program);
1245
- registerEnvCommands(program);
1246
- registerGhCommands(program);
1247
- registerNarrativeCommands(program);
1248
- registerPdeCommands(program);
1249
- registerPlanCommands(program);
1250
-
1251
- program.hook('postAction', async () => {
1252
- try { await redisDisconnect(); } catch { /* client may not have been initialized */ }
1253
- });
1254
-
1255
- await program.parseAsync(process.argv);
1256
- }
1257
-
1258
- main().catch((err: unknown) => {
1259
- console.error(formatError(err instanceof Error ? err.message : String(err)));
1260
- process.exit(1);
1261
- });