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