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.
- package/KINSHIP.md +1 -1
- package/README.md +191 -75
- package/dist/mcp/config.d.ts.map +1 -0
- package/dist/mcp/config.js +0 -2
- package/dist/mcp/config.js.map +1 -0
- package/dist/mcp/prompts.d.ts.map +1 -0
- package/dist/mcp/prompts.js.map +1 -0
- package/dist/mcp/resources.d.ts.map +1 -0
- package/dist/mcp/resources.js.map +1 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +1 -1
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tools/coaiapy-tools.d.ts.map +1 -0
- package/dist/mcp/tools/coaiapy-tools.js.map +1 -0
- package/dist/mcp/tools/index.d.ts.map +1 -0
- package/dist/mcp/tools/index.js.map +1 -0
- package/dist/src/audio.d.ts.map +1 -0
- package/dist/src/audio.js.map +1 -0
- package/dist/src/cli-helpers.d.ts.map +1 -0
- package/dist/src/cli-helpers.js.map +1 -0
- package/dist/src/cli.d.ts.map +1 -0
- package/dist/src/cli.js +1 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/config.d.ts.map +1 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/environment.d.ts.map +1 -0
- package/dist/src/environment.js.map +1 -0
- package/dist/src/github.d.ts.map +1 -0
- package/dist/src/github.js.map +1 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/langfuse/client.d.ts.map +1 -0
- package/dist/src/langfuse/client.js.map +1 -0
- package/dist/src/langfuse/comments.d.ts.map +1 -0
- package/dist/src/langfuse/comments.js.map +1 -0
- package/dist/src/langfuse/datasets.d.ts.map +1 -0
- package/dist/src/langfuse/datasets.js.map +1 -0
- package/dist/src/langfuse/index.d.ts +1 -0
- package/dist/src/langfuse/index.d.ts.map +1 -0
- package/dist/src/langfuse/index.js.map +1 -0
- package/dist/src/langfuse/media.d.ts.map +1 -0
- package/dist/src/langfuse/media.js.map +1 -0
- package/dist/src/langfuse/observations.d.ts.map +1 -0
- package/dist/src/langfuse/observations.js.map +1 -0
- package/dist/src/langfuse/prompts.d.ts +5 -1
- package/dist/src/langfuse/prompts.d.ts.map +1 -0
- package/dist/src/langfuse/prompts.js +16 -3
- package/dist/src/langfuse/prompts.js.map +1 -0
- package/dist/src/langfuse/scores.d.ts.map +1 -0
- package/dist/src/langfuse/scores.js.map +1 -0
- package/dist/src/langfuse/traces.d.ts.map +1 -0
- package/dist/src/langfuse/traces.js.map +1 -0
- package/dist/src/llm.d.ts.map +1 -0
- package/dist/src/llm.js.map +1 -0
- package/dist/src/narrative/graph-manager.d.ts.map +1 -0
- package/dist/src/narrative/graph-manager.js.map +1 -0
- package/dist/src/narrative/index.d.ts.map +1 -0
- package/dist/src/narrative/index.js.map +1 -0
- package/dist/src/narrative/markdown-export.d.ts.map +1 -0
- package/dist/src/narrative/markdown-export.js.map +1 -0
- package/dist/src/narrative/tool-definitions.d.ts.map +1 -0
- package/dist/src/narrative/tool-definitions.js.map +1 -0
- package/dist/src/narrative/tool-handlers.d.ts.map +1 -0
- package/dist/src/narrative/tool-handlers.js.map +1 -0
- package/dist/src/narrative/types.d.ts.map +1 -0
- package/dist/src/narrative/types.js.map +1 -0
- package/dist/src/narrative/validation.d.ts.map +1 -0
- package/dist/src/narrative/validation.js.map +1 -0
- package/dist/src/pde/index.d.ts.map +1 -0
- package/dist/src/pde/index.js.map +1 -0
- package/dist/src/pde/mcp-handlers.d.ts.map +1 -0
- package/dist/src/pde/mcp-handlers.js.map +1 -0
- package/dist/src/pde/mcp-tools.d.ts.map +1 -0
- package/dist/src/pde/mcp-tools.js.map +1 -0
- package/dist/src/pde/session-manager.d.ts.map +1 -0
- package/dist/src/pde/session-manager.js.map +1 -0
- package/dist/src/pde/stc-mapper.d.ts.map +1 -0
- package/dist/src/pde/stc-mapper.js.map +1 -0
- package/dist/src/pipeline/index.d.ts.map +1 -0
- package/dist/src/pipeline/index.js.map +1 -0
- package/dist/src/pipeline/template-engine.d.ts.map +1 -0
- package/dist/src/pipeline/template-engine.js.map +1 -0
- package/dist/src/planning/index.d.ts.map +1 -0
- package/dist/src/planning/index.js.map +1 -0
- package/dist/src/planning/mcp-handlers.d.ts.map +1 -0
- package/dist/src/planning/mcp-handlers.js.map +1 -0
- package/dist/src/planning/mcp-tools.d.ts.map +1 -0
- package/dist/src/planning/mcp-tools.js.map +1 -0
- package/dist/src/planning/plan-parser.d.ts.map +1 -0
- package/dist/src/planning/plan-parser.js.map +1 -0
- package/dist/src/redis.d.ts.map +1 -0
- package/dist/src/redis.js.map +1 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/version.d.ts.map +1 -0
- package/dist/src/version.js.map +1 -0
- package/llms-full.txt +361 -0
- package/llms.txt +43 -0
- package/package.json +79 -12
- package/.claude/settings.local.json +0 -17
- package/.claude-plugin/marketplace.json +0 -26
- package/articles/academic/NOTES.md +0 -1
- package/articles/academic/README.md +0 -1
- package/articles/academic/creative-orientation-vs-problem-solving.md +0 -177
- package/articles/academic/jsonl-knowledge-graphs-agent-memory.md +0 -142
- package/articles/academic/langfuse-observability-llm-pipelines.md +0 -144
- package/articles/academic/medicine-wheel-software-architecture.md +0 -163
- package/articles/academic/mmot-autonomous-agents.md +0 -156
- package/articles/academic/model-context-protocol-interagent.md +0 -161
- package/articles/academic/pde-prompt-decomposition.md +0 -186
- package/articles/academic/structural-tension-in-ai-agents.md +0 -134
- package/articles/reviews/mcp-protocol-design-review.md +0 -170
- package/articles/reviews/observability-ai-systems-review.md +0 -176
- package/articles/reviews/prompt-engineering-decomposition-review.md +0 -184
- package/articles/surveys/agent-orchestration-survey.md +0 -186
- package/articles/surveys/knowledge-graph-storage-survey.md +0 -204
- package/articles/surveys/structural-tension-methodology-survey.md +0 -154
- package/articles/technical/aws-sdk-v3-polly.md +0 -270
- package/articles/technical/commander-cli-framework.md +0 -262
- package/articles/technical/dotenv-config-patterns.md +0 -360
- package/articles/technical/ioredis-vs-redis.md +0 -142
- package/articles/technical/langfuse-js-sdk-vs-rest.md +0 -191
- package/articles/technical/mcp-sdk-typescript.md +0 -291
- package/articles/technical/octokit-github-api.md +0 -293
- package/articles/technical/openai-sdk-modern.md +0 -231
- package/articles/technical/yaml-parsing-node.md +0 -266
- package/articles/technical/zod-runtime-validation.md +0 -212
- package/coaiajs-langfuse-claude-plugin/.claude-plugin/marketplace.json +0 -16
- package/coaiajs-langfuse-claude-plugin/.claude-plugin/plugin.json +0 -10
- package/coaiajs-langfuse-claude-plugin/.mcp.json +0 -8
- package/coaiajs-langfuse-claude-plugin/README.md +0 -60
- package/mcp/config.ts +0 -225
- package/mcp/prompts.ts +0 -131
- package/mcp/resources.ts +0 -84
- package/mcp/server.ts +0 -519
- package/mcp/tools/coaiapy-tools.ts +0 -366
- package/mcp/tools/index.ts +0 -4
- package/src/audio.ts +0 -76
- package/src/cli-helpers.ts +0 -86
- package/src/cli.ts +0 -1261
- package/src/config.ts +0 -207
- package/src/environment.ts +0 -171
- package/src/github.ts +0 -143
- package/src/index.ts +0 -82
- package/src/langfuse/client.ts +0 -105
- package/src/langfuse/comments.ts +0 -52
- package/src/langfuse/datasets.ts +0 -178
- package/src/langfuse/index.ts +0 -37
- package/src/langfuse/media.ts +0 -193
- package/src/langfuse/observations.ts +0 -155
- package/src/langfuse/prompts.ts +0 -157
- package/src/langfuse/scores.ts +0 -456
- package/src/langfuse/traces.ts +0 -353
- package/src/llm.ts +0 -106
- package/src/narrative/graph-manager.ts +0 -1358
- package/src/narrative/index.ts +0 -188
- package/src/narrative/markdown-export.ts +0 -535
- package/src/narrative/tool-definitions.ts +0 -635
- package/src/narrative/tool-handlers.ts +0 -528
- package/src/narrative/types.ts +0 -9
- package/src/narrative/validation.ts +0 -179
- package/src/pde/index.ts +0 -34
- package/src/pde/mcp-handlers.ts +0 -359
- package/src/pde/mcp-tools.ts +0 -201
- package/src/pde/session-manager.ts +0 -248
- package/src/pde/stc-mapper.ts +0 -298
- package/src/pipeline/index.ts +0 -7
- package/src/pipeline/template-engine.ts +0 -398
- package/src/planning/index.ts +0 -32
- package/src/planning/mcp-handlers.ts +0 -369
- package/src/planning/mcp-tools.ts +0 -155
- package/src/planning/plan-parser.ts +0 -587
- package/src/redis.ts +0 -111
- package/src/types.ts +0 -281
- package/src/version.ts +0 -39
- package/test/config.test.mjs +0 -93
- package/tsconfig.json +0 -26
|
@@ -1,262 +0,0 @@
|
|
|
1
|
-
# Commander.js CLI Framework: Technical Assessment for CoAiA.js
|
|
2
|
-
|
|
3
|
-
> Package selection brief — CLI framework for coaiajs's multi-subcommand interface (tash, fetch, fuse, pipeline, env, gh, narrative, pde, planning)
|
|
4
|
-
|
|
5
|
-
## Summary & Recommendation
|
|
6
|
-
|
|
7
|
-
**Use `commander` v12.x** for the coaiajs CLI. Commander's Git-style subcommand model maps directly to our 9+ subcommand structure. It's the most widely adopted Node.js CLI framework (weekly downloads dwarf alternatives), has zero dependencies, generates help automatically, and every team member already knows it. Clipanion's class-based approach is elegant but niche; yargs is powerful but over-engineered for our tree.
|
|
8
|
-
|
|
9
|
-
**Pin:** `"commander": "^12.1.0"`
|
|
10
|
-
|
|
11
|
-
## What We're Replacing
|
|
12
|
-
|
|
13
|
-
**Coaiapy** uses Python's `argparse` with subparsers:
|
|
14
|
-
|
|
15
|
-
```python
|
|
16
|
-
# coaiapy/coaiacli.py — argparse subcommand pattern
|
|
17
|
-
import argparse
|
|
18
|
-
parser = argparse.ArgumentParser(description='CoAiA CLI')
|
|
19
|
-
subparsers = parser.add_subparsers(dest='command')
|
|
20
|
-
parser_fuse = subparsers.add_parser('fuse', help='Manage Langfuse integrations.')
|
|
21
|
-
parser_gh = subparsers.add_parser('gh', help='GitHub operations.')
|
|
22
|
-
# ... 9+ subcommands
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
**coaia-narrative** uses `minimist` — bare-bones argv parsing with no help generation:
|
|
26
|
-
|
|
27
|
-
```typescript
|
|
28
|
-
// coaia-narrative/src/cli.ts — minimist pattern
|
|
29
|
-
import minimist from 'minimist';
|
|
30
|
-
const args = minimist(process.argv.slice(2));
|
|
31
|
-
// Manual flag handling: --memory-path, -M, --json, --no-color, --interactive
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
**coaia-planning** also uses `minimist` for `--plans-dir` and `--output-dir`.
|
|
35
|
-
|
|
36
|
-
## Options Compared
|
|
37
|
-
|
|
38
|
-
| Feature | Commander v12 | Yargs v17 | Clipanion v4 | minimist v1.2 |
|
|
39
|
-
|---------|--------------|-----------|-------------|--------------|
|
|
40
|
-
| Weekly downloads | ~125M | ~80M | ~4M | ~50M |
|
|
41
|
-
| Dependencies | 0 | 5+ | 0 | 0 |
|
|
42
|
-
| Subcommand model | Git-style `.command()` | Builder callbacks | Class-based `paths` | None (manual) |
|
|
43
|
-
| Auto-help | ✅ Excellent | ✅ Excellent | ✅ Good | ❌ None |
|
|
44
|
-
| TypeScript | Good (DefinitelyTyped) | Good | Excellent (first-class) | Minimal |
|
|
45
|
-
| Learning curve | Low | Medium | Medium-high | Very low |
|
|
46
|
-
| Argument parsing | Options, args, variadic | All types + coercion | All types + validation | Raw key-value |
|
|
47
|
-
| Completion (bash/zsh) | Plugin available | Built-in | Not built-in | ❌ |
|
|
48
|
-
| Version handling | Built-in `.version()` | Built-in | Manual | ❌ |
|
|
49
|
-
| Nested subcommands | `.command()` nesting | Recursive builders | Arbitrary `paths` depth | ❌ |
|
|
50
|
-
| Exit override | `.exitOverride()` for testing | `fail()` handler | Exception-based | N/A |
|
|
51
|
-
|
|
52
|
-
## API Overview
|
|
53
|
-
|
|
54
|
-
### Main CLI Entry Point
|
|
55
|
-
|
|
56
|
-
```typescript
|
|
57
|
-
// src/cli.ts
|
|
58
|
-
import { Command } from 'commander';
|
|
59
|
-
import { version } from '../package.json';
|
|
60
|
-
|
|
61
|
-
const program = new Command()
|
|
62
|
-
.name('coaia')
|
|
63
|
-
.version(version)
|
|
64
|
-
.description('CoAiA.js — Structural Tension Agent Framework');
|
|
65
|
-
|
|
66
|
-
// Register subcommands
|
|
67
|
-
program.addCommand(createTashCommand());
|
|
68
|
-
program.addCommand(createFetchCommand());
|
|
69
|
-
program.addCommand(createFuseCommand());
|
|
70
|
-
program.addCommand(createPipelineCommand());
|
|
71
|
-
program.addCommand(createEnvCommand());
|
|
72
|
-
program.addCommand(createGhCommand());
|
|
73
|
-
program.addCommand(createNarrativeCommand());
|
|
74
|
-
program.addCommand(createPdeCommand());
|
|
75
|
-
program.addCommand(createPlanningCommand());
|
|
76
|
-
|
|
77
|
-
program.parse();
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
### Subcommand Definition Pattern
|
|
81
|
-
|
|
82
|
-
```typescript
|
|
83
|
-
// src/commands/fuse.ts
|
|
84
|
-
import { Command } from 'commander';
|
|
85
|
-
|
|
86
|
-
export function createFuseCommand(): Command {
|
|
87
|
-
const fuse = new Command('fuse')
|
|
88
|
-
.description('Manage Langfuse integrations');
|
|
89
|
-
|
|
90
|
-
fuse
|
|
91
|
-
.command('trace')
|
|
92
|
-
.description('Create or list traces')
|
|
93
|
-
.option('-s, --session <id>', 'Session ID')
|
|
94
|
-
.option('--list', 'List recent traces')
|
|
95
|
-
.option('--json', 'Output as JSON')
|
|
96
|
-
.action(async (opts) => {
|
|
97
|
-
if (opts.list) {
|
|
98
|
-
const traces = await listTraces(opts);
|
|
99
|
-
console.log(opts.json ? JSON.stringify(traces) : formatTable(traces));
|
|
100
|
-
} else {
|
|
101
|
-
await createTrace(opts);
|
|
102
|
-
}
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
fuse
|
|
106
|
-
.command('score')
|
|
107
|
-
.description('Score a trace')
|
|
108
|
-
.requiredOption('-t, --trace-id <id>', 'Trace ID to score')
|
|
109
|
-
.requiredOption('-n, --name <name>', 'Score name')
|
|
110
|
-
.requiredOption('-v, --value <number>', 'Score value', parseFloat)
|
|
111
|
-
.action(async (opts) => {
|
|
112
|
-
await scoreTrace(opts.traceId, opts.name, opts.value);
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
return fuse;
|
|
116
|
-
}
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
### GitHub Subcommand (replacing cogh.py)
|
|
120
|
-
|
|
121
|
-
```typescript
|
|
122
|
-
// src/commands/gh.ts
|
|
123
|
-
import { Command } from 'commander';
|
|
124
|
-
|
|
125
|
-
export function createGhCommand(): Command {
|
|
126
|
-
const gh = new Command('gh')
|
|
127
|
-
.description('GitHub operations');
|
|
128
|
-
|
|
129
|
-
gh.command('issues')
|
|
130
|
-
.description('List repository issues')
|
|
131
|
-
.argument('<owner>', 'Repository owner')
|
|
132
|
-
.argument('<repo>', 'Repository name')
|
|
133
|
-
.option('--state <state>', 'Filter by state', 'open')
|
|
134
|
-
.option('--labels <labels...>', 'Filter by labels')
|
|
135
|
-
.option('--json', 'JSON output')
|
|
136
|
-
.action(async (owner, repo, opts) => {
|
|
137
|
-
const issues = await listIssues(owner, repo, opts);
|
|
138
|
-
console.log(opts.json ? JSON.stringify(issues) : formatIssuesTable(issues));
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
gh.command('issue <owner> <repo> <number>')
|
|
142
|
-
.description('Get a specific issue')
|
|
143
|
-
.action(async (owner, repo, number) => {
|
|
144
|
-
const issue = await getIssue(owner, repo, parseInt(number));
|
|
145
|
-
console.log(JSON.stringify(issue, null, 2));
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
return gh;
|
|
149
|
-
}
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
### Pipeline Subcommand (with YAML template loading)
|
|
153
|
-
|
|
154
|
-
```typescript
|
|
155
|
-
// src/commands/pipeline.ts
|
|
156
|
-
import { Command } from 'commander';
|
|
157
|
-
|
|
158
|
-
export function createPipelineCommand(): Command {
|
|
159
|
-
const pipeline = new Command('pipeline')
|
|
160
|
-
.description('Execute pipeline templates');
|
|
161
|
-
|
|
162
|
-
pipeline
|
|
163
|
-
.command('run <template>')
|
|
164
|
-
.description('Run a pipeline template')
|
|
165
|
-
.option('-v, --var <key=value...>', 'Template variables')
|
|
166
|
-
.option('--dry-run', 'Show execution plan without running')
|
|
167
|
-
.option('--template-dir <dir>', 'Template search directory')
|
|
168
|
-
.action(async (template, opts) => {
|
|
169
|
-
const vars = parseVars(opts.var ?? []);
|
|
170
|
-
await runPipeline(template, vars, opts);
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
pipeline
|
|
174
|
-
.command('list')
|
|
175
|
-
.description('List available templates')
|
|
176
|
-
.action(async () => {
|
|
177
|
-
const templates = await listTemplates();
|
|
178
|
-
templates.forEach(t => console.log(` ${t.name} (v${t.version})`));
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
return pipeline;
|
|
182
|
-
}
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
### Global Options Pattern
|
|
186
|
-
|
|
187
|
-
```typescript
|
|
188
|
-
// Shared options across all subcommands
|
|
189
|
-
program
|
|
190
|
-
.option('--json', 'Output as JSON')
|
|
191
|
-
.option('--no-color', 'Disable colored output')
|
|
192
|
-
.option('--verbose', 'Verbose logging')
|
|
193
|
-
.option('-M, --memory-path <path>', 'Memory file path')
|
|
194
|
-
.option('--env <file>', 'Custom env file');
|
|
195
|
-
|
|
196
|
-
// Access in any subcommand action
|
|
197
|
-
function getGlobalOpts(cmd: Command) {
|
|
198
|
-
const root = cmd.parent ?? cmd;
|
|
199
|
-
return root.opts() as { json?: boolean; color?: boolean; verbose?: boolean; memoryPath?: string };
|
|
200
|
-
}
|
|
201
|
-
```
|
|
202
|
-
|
|
203
|
-
## Integration Plan
|
|
204
|
-
|
|
205
|
-
1. **Entry point:** `src/cli.ts` — Commander program with `.parse()`
|
|
206
|
-
2. **Command directory:** `src/commands/` — one file per subcommand
|
|
207
|
-
- `tash.ts` — Task management (structural tension)
|
|
208
|
-
- `fetch.ts` — Data fetching operations
|
|
209
|
-
- `fuse.ts` — Langfuse integration
|
|
210
|
-
- `pipeline.ts` — Pipeline template execution
|
|
211
|
-
- `env.ts` — Environment management
|
|
212
|
-
- `gh.ts` — GitHub operations
|
|
213
|
-
- `narrative.ts` — Narrative/chart operations
|
|
214
|
-
- `pde.ts` — Prompt decomposition
|
|
215
|
-
- `planning.ts` — Plan-to-STC operations
|
|
216
|
-
3. **Bin entry:** `package.json` → `"bin": { "coaia": "./dist/src/cli.js" }`
|
|
217
|
-
4. **Testing:** Commander's `.exitOverride()` + `.configureOutput()` for unit tests
|
|
218
|
-
|
|
219
|
-
### Migration Path from minimist
|
|
220
|
-
|
|
221
|
-
```typescript
|
|
222
|
-
// BEFORE (coaia-narrative minimist pattern)
|
|
223
|
-
const args = minimist(process.argv.slice(2));
|
|
224
|
-
const memoryPath = args['memory-path'] || args.M || process.env.COAIA_MEMORY_PATH;
|
|
225
|
-
|
|
226
|
-
// AFTER (Commander)
|
|
227
|
-
program.option('-M, --memory-path <path>', 'Memory file path', process.env.COAIA_MEMORY_PATH);
|
|
228
|
-
// memoryPath is now typed and auto-documented in --help
|
|
229
|
-
```
|
|
230
|
-
|
|
231
|
-
## Version & Ecosystem
|
|
232
|
-
|
|
233
|
-
| Metric | Value |
|
|
234
|
-
|--------|-------|
|
|
235
|
-
| Current version | 12.1.0 |
|
|
236
|
-
| Weekly downloads | ~125M |
|
|
237
|
-
| Dependencies | 0 |
|
|
238
|
-
| TypeScript | `@types/commander` (DefinitelyTyped) |
|
|
239
|
-
| GitHub stars | ~27,000 |
|
|
240
|
-
| Used by | npm CLI, Vue CLI, create-react-app |
|
|
241
|
-
| Node.js compat | ≥16 (we target ≥20) |
|
|
242
|
-
| License | MIT |
|
|
243
|
-
|
|
244
|
-
## Why Not Yargs?
|
|
245
|
-
|
|
246
|
-
Yargs is powerful but overkill. Our subcommand tree is wide (9 commands) but not deeply nested. Commander's chainable API makes each command file self-contained and readable. Yargs' middleware system and callback-based builders add complexity we don't need.
|
|
247
|
-
|
|
248
|
-
## Why Not Clipanion?
|
|
249
|
-
|
|
250
|
-
Clipanion's TypeScript-first class-based approach is architecturally beautiful, but:
|
|
251
|
-
1. ~4M weekly downloads vs Commander's ~125M = smaller help ecosystem
|
|
252
|
-
2. Class-based commands are less familiar to contributors
|
|
253
|
-
3. Yarn is its primary consumer; documentation coverage is thin for edge cases
|
|
254
|
-
4. Commander is already in our `package.json`
|
|
255
|
-
|
|
256
|
-
## References
|
|
257
|
-
|
|
258
|
-
- npm: https://www.npmjs.com/package/commander
|
|
259
|
-
- GitHub: https://github.com/tj/commander.js
|
|
260
|
-
- Guide: https://betterstack.com/community/guides/scaling-nodejs/commander-explained/
|
|
261
|
-
- Comparison: https://npm-compare.com/commander,yargs
|
|
262
|
-
- Clipanion: https://github.com/arcanis/clipanion
|
|
@@ -1,360 +0,0 @@
|
|
|
1
|
-
# Config Management in Node.js: Technical Assessment for CoAiA.js
|
|
2
|
-
|
|
3
|
-
> Package selection brief — Configuration cascade replacing coaiapy's multi-file config (env vars > .env > coaia.json > defaults)
|
|
4
|
-
|
|
5
|
-
## Summary & Recommendation
|
|
6
|
-
|
|
7
|
-
**Use `dotenv` v16.x + `cosmiconfig` v9.x + `zod` for validation.** This triple replaces coaiapy's hand-rolled config cascade with a standard, validated pattern:
|
|
8
|
-
- `dotenv` loads `.env` files (matching coaiapy's `.coaia-env`)
|
|
9
|
-
- `cosmiconfig` discovers `coaia.json`, `coaia.yaml`, `.coaiarc`, or `coaia.config.js` (matching coaiapy's multi-format loading)
|
|
10
|
-
- `zod` validates the merged config at startup (what coaiapy lacks entirely)
|
|
11
|
-
|
|
12
|
-
**Pin:**
|
|
13
|
-
```json
|
|
14
|
-
"dotenv": "^16.4.0",
|
|
15
|
-
"cosmiconfig": "^9.0.0",
|
|
16
|
-
"zod": "^4.0.0"
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
## What We're Replacing
|
|
20
|
-
|
|
21
|
-
Coaiapy has a complex config cascade across multiple modules:
|
|
22
|
-
|
|
23
|
-
### environment.py (EnvironmentManager)
|
|
24
|
-
```python
|
|
25
|
-
@dataclass
|
|
26
|
-
class EnvironmentConfig:
|
|
27
|
-
project_env_file: str = ".coaia-env"
|
|
28
|
-
global_env_file: str = "~/.coaia/global.env"
|
|
29
|
-
supported_formats: List[str] = ["json", "env"]
|
|
30
|
-
|
|
31
|
-
class EnvironmentManager:
|
|
32
|
-
def _read_env_file(self, file_path: Path) -> Dict[str, Any]:
|
|
33
|
-
if content.startswith('{'):
|
|
34
|
-
return json.loads(content)
|
|
35
|
-
# Parse KEY=value format
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
### coaiamodule.py (read_config)
|
|
39
|
-
```python
|
|
40
|
-
def read_config():
|
|
41
|
-
# Priority: env vars > .env > coaia.json > defaults
|
|
42
|
-
# Searches: cwd, parent dirs, ~/.coaia/
|
|
43
|
-
# Supports: JSON, .env, shell env vars
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
### coaia-narrative CLI (minimist + dotenv)
|
|
47
|
-
```typescript
|
|
48
|
-
// Priority order:
|
|
49
|
-
// 1. Command-line flags (--memory-path, -M)
|
|
50
|
-
// 2. Custom env file (--env)
|
|
51
|
-
// 3. .env in cwd
|
|
52
|
-
// 4. _env.sh (fallback)
|
|
53
|
-
// 5. Environment variables
|
|
54
|
-
// 6. Defaults
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
The Python approach has no validation — missing keys fail at runtime deep in execution. The TypeScript approach in coaia-narrative works but is ad-hoc per module.
|
|
58
|
-
|
|
59
|
-
## Options Compared
|
|
60
|
-
|
|
61
|
-
| Feature | dotenv + cosmiconfig + zod | dotenv only | convict | nconf | node-config |
|
|
62
|
-
|---------|--------------------------|-------------|---------|-------|-------------|
|
|
63
|
-
| .env loading | ✅ dotenv | ✅ | ⚠️ Manual | ✅ | ❌ |
|
|
64
|
-
| Multi-format (JSON/YAML/JS) | ✅ cosmiconfig | ❌ | ✅ JSON | ✅ | ✅ |
|
|
65
|
-
| Parent dir search | ✅ cosmiconfig | ❌ | ❌ | ❌ | ❌ |
|
|
66
|
-
| Schema validation | ✅ zod | ❌ | ✅ convict | ❌ | ❌ |
|
|
67
|
-
| Type inference | ✅ zod infer | ❌ | ⚠️ Manual | ❌ | ❌ |
|
|
68
|
-
| Variable expansion | ✅ dotenv-expand | ✅ dotenv-expand | ❌ | ❌ | ❌ |
|
|
69
|
-
| Env override | ✅ process.env priority | ✅ | ✅ | ✅ | ✅ |
|
|
70
|
-
| Weekly downloads | ~93M + ~30M + ~100M | ~93M | ~900K | ~900K | ~1.5M |
|
|
71
|
-
| Bundle overhead | Minimal | Tiny | Medium | Medium | Medium |
|
|
72
|
-
| TypeScript | All have types | ✅ | ⚠️ | ⚠️ | ⚠️ |
|
|
73
|
-
|
|
74
|
-
## API Overview
|
|
75
|
-
|
|
76
|
-
### Config Schema (Zod)
|
|
77
|
-
|
|
78
|
-
```typescript
|
|
79
|
-
// src/config/schema.ts
|
|
80
|
-
import { z } from 'zod';
|
|
81
|
-
|
|
82
|
-
export const CoaiaConfigSchema = z.object({
|
|
83
|
-
// Redis
|
|
84
|
-
redisUrl: z.string().url().default('redis://localhost:6379'),
|
|
85
|
-
|
|
86
|
-
// Langfuse
|
|
87
|
-
langfusePublicKey: z.string().optional(),
|
|
88
|
-
langfuseSecretKey: z.string().optional(),
|
|
89
|
-
langfuseBaseUrl: z.string().url().default('https://cloud.langfuse.com'),
|
|
90
|
-
|
|
91
|
-
// OpenAI
|
|
92
|
-
openaiApiKey: z.string().optional(),
|
|
93
|
-
openaiBaseUrl: z.string().url().optional(),
|
|
94
|
-
openaiModel: z.string().default('gpt-5.4'),
|
|
95
|
-
|
|
96
|
-
// AWS Polly
|
|
97
|
-
awsRegion: z.string().default('us-east-1'),
|
|
98
|
-
pollyKey: z.string().optional(),
|
|
99
|
-
pollySecret: z.string().optional(),
|
|
100
|
-
|
|
101
|
-
// GitHub
|
|
102
|
-
githubToken: z.string().optional(),
|
|
103
|
-
|
|
104
|
-
// Paths
|
|
105
|
-
memoryPath: z.string().default('.coaia/memory.jsonl'),
|
|
106
|
-
plansDir: z.string().default('.coaia/plans'),
|
|
107
|
-
outputDir: z.string().default('.coaia/output'),
|
|
108
|
-
templateDir: z.string().default('.coaia/templates'),
|
|
109
|
-
|
|
110
|
-
// Behavior
|
|
111
|
-
jsonOutput: z.boolean().default(false),
|
|
112
|
-
noColor: z.boolean().default(false),
|
|
113
|
-
verbose: z.boolean().default(false),
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
export type CoaiaConfig = z.infer<typeof CoaiaConfigSchema>;
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
### Config Loader (cosmiconfig + dotenv)
|
|
120
|
-
|
|
121
|
-
```typescript
|
|
122
|
-
// src/config/loader.ts
|
|
123
|
-
import { cosmiconfig } from 'cosmiconfig';
|
|
124
|
-
import dotenv from 'dotenv';
|
|
125
|
-
import { CoaiaConfigSchema, type CoaiaConfig } from './schema.js';
|
|
126
|
-
import { existsSync } from 'fs';
|
|
127
|
-
import { resolve } from 'path';
|
|
128
|
-
|
|
129
|
-
// cosmiconfig explorer — searches for config files
|
|
130
|
-
const explorer = cosmiconfig('coaia', {
|
|
131
|
-
searchPlaces: [
|
|
132
|
-
'coaia.config.js',
|
|
133
|
-
'coaia.config.ts',
|
|
134
|
-
'coaia.json',
|
|
135
|
-
'coaia.yaml',
|
|
136
|
-
'coaia.yml',
|
|
137
|
-
'.coaiarc',
|
|
138
|
-
'.coaiarc.json',
|
|
139
|
-
'.coaiarc.yaml',
|
|
140
|
-
'package.json', // "coaia" key in package.json
|
|
141
|
-
],
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
let _config: CoaiaConfig | null = null;
|
|
145
|
-
|
|
146
|
-
export async function loadConfig(overrides?: Partial<CoaiaConfig>): Promise<CoaiaConfig> {
|
|
147
|
-
if (_config && !overrides) return _config;
|
|
148
|
-
|
|
149
|
-
// Step 1: Load .env files (matches coaiapy's .coaia-env pattern)
|
|
150
|
-
const envFiles = [
|
|
151
|
-
'.env',
|
|
152
|
-
'.coaia-env',
|
|
153
|
-
resolve(process.env.HOME ?? '', '.coaia', 'global.env'),
|
|
154
|
-
];
|
|
155
|
-
|
|
156
|
-
for (const envFile of envFiles) {
|
|
157
|
-
if (existsSync(envFile)) {
|
|
158
|
-
dotenv.config({ path: envFile });
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// Step 2: Load config file via cosmiconfig (searches parent dirs)
|
|
163
|
-
const result = await explorer.search();
|
|
164
|
-
const fileConfig = result?.config ?? {};
|
|
165
|
-
|
|
166
|
-
// Step 3: Merge sources (priority: CLI overrides > env vars > config file > defaults)
|
|
167
|
-
const merged = {
|
|
168
|
-
...fileConfig,
|
|
169
|
-
// Env var overrides (matching coaiapy's env var names)
|
|
170
|
-
...(process.env.REDIS_URL && { redisUrl: process.env.REDIS_URL }),
|
|
171
|
-
...(process.env.UPSTASH_REDIS_URL && { redisUrl: process.env.UPSTASH_REDIS_URL }),
|
|
172
|
-
...(process.env.LANGFUSE_PUBLIC_KEY && { langfusePublicKey: process.env.LANGFUSE_PUBLIC_KEY }),
|
|
173
|
-
...(process.env.LANGFUSE_SECRET_KEY && { langfuseSecretKey: process.env.LANGFUSE_SECRET_KEY }),
|
|
174
|
-
...(process.env.LANGFUSE_BASE_URL && { langfuseBaseUrl: process.env.LANGFUSE_BASE_URL }),
|
|
175
|
-
...(process.env.OPENAI_API_KEY && { openaiApiKey: process.env.OPENAI_API_KEY }),
|
|
176
|
-
...(process.env.OPENAI_BASE_URL && { openaiBaseUrl: process.env.OPENAI_BASE_URL }),
|
|
177
|
-
...(process.env.AWS_REGION && { awsRegion: process.env.AWS_REGION }),
|
|
178
|
-
...(process.env.GH_TOKEN && { githubToken: process.env.GH_TOKEN }),
|
|
179
|
-
...(process.env.GITHUB_TOKEN && { githubToken: process.env.GITHUB_TOKEN }),
|
|
180
|
-
...(process.env.COAIA_MEMORY_PATH && { memoryPath: process.env.COAIA_MEMORY_PATH }),
|
|
181
|
-
...(process.env.COAIA_PLANS_DIR && { plansDir: process.env.COAIA_PLANS_DIR }),
|
|
182
|
-
...(process.env.COAIA_OUTPUT_DIR && { outputDir: process.env.COAIA_OUTPUT_DIR }),
|
|
183
|
-
// CLI overrides (highest priority)
|
|
184
|
-
...overrides,
|
|
185
|
-
};
|
|
186
|
-
|
|
187
|
-
// Step 4: Validate with Zod
|
|
188
|
-
_config = CoaiaConfigSchema.parse(merged);
|
|
189
|
-
return _config;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// Synchronous getter after initial load
|
|
193
|
-
export function getConfig(): CoaiaConfig {
|
|
194
|
-
if (!_config) throw new Error('Config not loaded. Call loadConfig() first.');
|
|
195
|
-
return _config;
|
|
196
|
-
}
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
### CLI Integration with Commander
|
|
200
|
-
|
|
201
|
-
```typescript
|
|
202
|
-
// src/cli.ts — CLI flags feed into config overrides
|
|
203
|
-
import { Command } from 'commander';
|
|
204
|
-
import { loadConfig } from './config/loader.js';
|
|
205
|
-
|
|
206
|
-
const program = new Command()
|
|
207
|
-
.option('-M, --memory-path <path>', 'Memory file path')
|
|
208
|
-
.option('--env <file>', 'Custom env file path')
|
|
209
|
-
.option('--json', 'Output as JSON')
|
|
210
|
-
.option('--no-color', 'Disable colored output')
|
|
211
|
-
.option('--verbose', 'Verbose logging');
|
|
212
|
-
|
|
213
|
-
program.hook('preAction', async (thisCommand) => {
|
|
214
|
-
const opts = thisCommand.opts();
|
|
215
|
-
|
|
216
|
-
// Load custom env file if specified
|
|
217
|
-
if (opts.env) {
|
|
218
|
-
dotenv.config({ path: opts.env });
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
// Initialize config with CLI overrides
|
|
222
|
-
await loadConfig({
|
|
223
|
-
...(opts.memoryPath && { memoryPath: opts.memoryPath }),
|
|
224
|
-
...(opts.json && { jsonOutput: true }),
|
|
225
|
-
...(opts.color === false && { noColor: true }),
|
|
226
|
-
...(opts.verbose && { verbose: true }),
|
|
227
|
-
});
|
|
228
|
-
});
|
|
229
|
-
```
|
|
230
|
-
|
|
231
|
-
### Variable Expansion (dotenv-expand)
|
|
232
|
-
|
|
233
|
-
```typescript
|
|
234
|
-
// For .env files that reference other variables
|
|
235
|
-
// .env:
|
|
236
|
-
// BASE_DIR=/opt/coaia
|
|
237
|
-
// MEMORY_PATH=${BASE_DIR}/memory.jsonl
|
|
238
|
-
// PLANS_DIR=${BASE_DIR}/plans
|
|
239
|
-
|
|
240
|
-
import dotenv from 'dotenv';
|
|
241
|
-
import { expand } from 'dotenv-expand';
|
|
242
|
-
|
|
243
|
-
const env = dotenv.config({ path: '.env' });
|
|
244
|
-
expand(env); // Resolves ${BASE_DIR} references
|
|
245
|
-
```
|
|
246
|
-
|
|
247
|
-
### Config File Examples
|
|
248
|
-
|
|
249
|
-
```yaml
|
|
250
|
-
# coaia.yaml — project-level config
|
|
251
|
-
redis:
|
|
252
|
-
url: redis://localhost:6379
|
|
253
|
-
|
|
254
|
-
langfuse:
|
|
255
|
-
publicKey: pk-lf-xxx
|
|
256
|
-
secretKey: sk-lf-xxx
|
|
257
|
-
baseUrl: https://cloud.langfuse.com
|
|
258
|
-
|
|
259
|
-
openai:
|
|
260
|
-
apiKey: sk-xxx
|
|
261
|
-
model: gpt-5.4
|
|
262
|
-
|
|
263
|
-
paths:
|
|
264
|
-
memory: .coaia/memory.jsonl
|
|
265
|
-
plans: .coaia/plans
|
|
266
|
-
templates: .coaia/templates
|
|
267
|
-
```
|
|
268
|
-
|
|
269
|
-
```json
|
|
270
|
-
// coaia.json — alternative JSON format
|
|
271
|
-
{
|
|
272
|
-
"redisUrl": "redis://localhost:6379",
|
|
273
|
-
"langfusePublicKey": "pk-lf-xxx",
|
|
274
|
-
"openaiModel": "gpt-5.4",
|
|
275
|
-
"memoryPath": ".coaia/memory.jsonl"
|
|
276
|
-
}
|
|
277
|
-
```
|
|
278
|
-
|
|
279
|
-
```ini
|
|
280
|
-
# .coaia-env — environment variable format (coaiapy compatible)
|
|
281
|
-
REDIS_URL=redis://localhost:6379
|
|
282
|
-
LANGFUSE_PUBLIC_KEY=pk-lf-xxx
|
|
283
|
-
LANGFUSE_SECRET_KEY=sk-lf-xxx
|
|
284
|
-
OPENAI_API_KEY=sk-xxx
|
|
285
|
-
GH_TOKEN=ghp_xxx
|
|
286
|
-
AWS_REGION=us-east-1
|
|
287
|
-
COAIA_MEMORY_PATH=.coaia/memory.jsonl
|
|
288
|
-
```
|
|
289
|
-
|
|
290
|
-
### Validation Error Messages
|
|
291
|
-
|
|
292
|
-
```typescript
|
|
293
|
-
// On startup with invalid config:
|
|
294
|
-
try {
|
|
295
|
-
await loadConfig();
|
|
296
|
-
} catch (error) {
|
|
297
|
-
if (error instanceof z.ZodError) {
|
|
298
|
-
console.error('Configuration errors:');
|
|
299
|
-
error.issues.forEach(issue => {
|
|
300
|
-
console.error(` ${issue.path.join('.')}: ${issue.message}`);
|
|
301
|
-
});
|
|
302
|
-
process.exit(1);
|
|
303
|
-
}
|
|
304
|
-
throw error;
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
// Output:
|
|
308
|
-
// Configuration errors:
|
|
309
|
-
// redisUrl: Invalid url
|
|
310
|
-
// langfuseBaseUrl: Invalid url
|
|
311
|
-
```
|
|
312
|
-
|
|
313
|
-
## Integration Plan
|
|
314
|
-
|
|
315
|
-
1. **Schema:** `src/config/schema.ts` — Zod schema for all config values
|
|
316
|
-
2. **Loader:** `src/config/loader.ts` — dotenv + cosmiconfig + Zod validation
|
|
317
|
-
3. **Export:** `src/config/index.ts` — `loadConfig()` and `getConfig()`
|
|
318
|
-
4. **CLI hook:** Commander `preAction` hook loads config with CLI overrides
|
|
319
|
-
5. **Testing:** Config loader is mockable via overrides parameter
|
|
320
|
-
6. **Migration:** Support both `coaia.json` and `.coaia-env` formats (backward compat)
|
|
321
|
-
|
|
322
|
-
### Config Priority (matches coaiapy's cascade)
|
|
323
|
-
|
|
324
|
-
```
|
|
325
|
-
1. CLI flags (--memory-path, --json) ← Highest
|
|
326
|
-
2. Environment variables (REDIS_URL, GH_TOKEN)
|
|
327
|
-
3. .env / .coaia-env (project root)
|
|
328
|
-
4. ~/.coaia/global.env (global user config)
|
|
329
|
-
5. coaia.json / coaia.yaml (cosmiconfig search)
|
|
330
|
-
6. Zod defaults (schema defaults) ← Lowest
|
|
331
|
-
```
|
|
332
|
-
|
|
333
|
-
## Version & Ecosystem
|
|
334
|
-
|
|
335
|
-
| Package | Version | Weekly Downloads | Purpose |
|
|
336
|
-
|---------|---------|-----------------|---------|
|
|
337
|
-
| dotenv | 16.4.x | ~93M | .env file loading |
|
|
338
|
-
| dotenv-expand | 12.0.x | ~30M | Variable expansion in .env |
|
|
339
|
-
| cosmiconfig | 9.0.x | ~30M | Multi-format config discovery |
|
|
340
|
-
| zod | 4.3.x | ~100M+ | Schema validation + type inference |
|
|
341
|
-
|
|
342
|
-
All packages: MIT licensed, zero or minimal dependencies, TypeScript supported.
|
|
343
|
-
|
|
344
|
-
## Why Not Convict?
|
|
345
|
-
|
|
346
|
-
Convict (from Mozilla) offers built-in schema validation, but:
|
|
347
|
-
1. ~900K downloads vs dotenv's ~93M — smaller ecosystem
|
|
348
|
-
2. Doesn't support YAML config files natively
|
|
349
|
-
3. Doesn't search parent directories (cosmiconfig does)
|
|
350
|
-
4. We already have Zod for validation (no need for convict's schema DSL)
|
|
351
|
-
5. Convict's API is less TypeScript-friendly than Zod's type inference
|
|
352
|
-
|
|
353
|
-
## References
|
|
354
|
-
|
|
355
|
-
- dotenv npm: https://www.npmjs.com/package/dotenv
|
|
356
|
-
- cosmiconfig npm: https://www.npmjs.com/package/cosmiconfig
|
|
357
|
-
- cosmiconfig GitHub: https://github.com/cosmiconfig/cosmiconfig
|
|
358
|
-
- dotenv security: https://www.johal.in/secrets-dotenv-secure-config-management-in-production-2026/
|
|
359
|
-
- Config comparison: https://npm-compare.com/config,convict,dotenv,nconf
|
|
360
|
-
- dotenv usage: https://oneuptime.com/blog/post/2026-01-25-dotenv-configuration-nodejs/view
|