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
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# 08 — CLI Interface
|
|
2
|
+
|
|
3
|
+
> Unified `coaia` command with subcommands for all modules.
|
|
4
|
+
|
|
5
|
+
## Desired Outcome
|
|
6
|
+
|
|
7
|
+
A single `coaia` CLI binary that provides subcommands for every CoAiA module — replacing the separate CLIs in coaiapy (argparse-based) and coaia-narrative (minimist-based) with a unified commander-based interface.
|
|
8
|
+
|
|
9
|
+
## Structural Tension
|
|
10
|
+
|
|
11
|
+
**Current Reality:**
|
|
12
|
+
- CLI entry point defined in `package.json` as `"coaia": "./dist/src/cli.js"` but no implementation exists
|
|
13
|
+
- commander dependency is installed
|
|
14
|
+
- coaiapy has an argparse-based CLI covering: tash, fetch, env, pipeline, transcribe, synthesize, fuse, gh
|
|
15
|
+
- coaia-narrative has a minimist-based CLI covering: chart visualization, markdown export, progress display
|
|
16
|
+
- coaia-pde has no CLI
|
|
17
|
+
- coaia-planning has no CLI
|
|
18
|
+
|
|
19
|
+
**Desired Outcome:**
|
|
20
|
+
Unified CLI at `src/cli.ts` using commander, with subcommands:
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
coaia tash <key> <value> [--ttl N]
|
|
24
|
+
coaia fetch <key>
|
|
25
|
+
coaia env [list|get|set|unset|init]
|
|
26
|
+
coaia fuse [traces|trace|prompts|scores|datasets]
|
|
27
|
+
coaia pipeline [list|render|execute] <template>
|
|
28
|
+
coaia transcribe <audio-file>
|
|
29
|
+
coaia synthesize <text> --output <file>
|
|
30
|
+
coaia gh [issues|issue|comments]
|
|
31
|
+
coaia narrative [charts|chart|progress|export]
|
|
32
|
+
coaia pde [decompose|list|get|export|to-stc]
|
|
33
|
+
coaia plan [parse|to-stc|from-stc|sync|diff]
|
|
34
|
+
coaia mcp [start|tools]
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Core Structure
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
import { Command } from 'commander';
|
|
41
|
+
|
|
42
|
+
const program = new Command()
|
|
43
|
+
.name('coaia')
|
|
44
|
+
.version(pkg.version)
|
|
45
|
+
.description('CoAiA unified CLI');
|
|
46
|
+
|
|
47
|
+
// Register subcommands
|
|
48
|
+
program.addCommand(redisCommands());
|
|
49
|
+
program.addCommand(envCommands());
|
|
50
|
+
program.addCommand(fuseCommands());
|
|
51
|
+
program.addCommand(pipelineCommands());
|
|
52
|
+
program.addCommand(audioCommands());
|
|
53
|
+
program.addCommand(ghCommands());
|
|
54
|
+
program.addCommand(narrativeCommands());
|
|
55
|
+
program.addCommand(pdeCommands());
|
|
56
|
+
program.addCommand(planCommands());
|
|
57
|
+
program.addCommand(mcpCommands());
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Command Details
|
|
61
|
+
|
|
62
|
+
### Redis Commands
|
|
63
|
+
```
|
|
64
|
+
coaia tash <key> <value> [--ttl N] # Set key with optional TTL
|
|
65
|
+
coaia fetch <key> # Get value by key
|
|
66
|
+
coaia del <key> # Delete key
|
|
67
|
+
coaia keys [pattern] # List keys matching pattern
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Environment Commands
|
|
71
|
+
```
|
|
72
|
+
coaia env init # Create .coaia-env file
|
|
73
|
+
coaia env list # Show all env vars
|
|
74
|
+
coaia env get <key> # Get specific var
|
|
75
|
+
coaia env set <key> <value> # Set env var
|
|
76
|
+
coaia env unset <key> # Remove env var
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Langfuse Commands
|
|
80
|
+
```
|
|
81
|
+
coaia fuse traces [--limit N] # List traces
|
|
82
|
+
coaia fuse trace <id> # Get trace details
|
|
83
|
+
coaia fuse prompts [--name X] # List prompts
|
|
84
|
+
coaia fuse scores [--trace-id X] # List scores
|
|
85
|
+
coaia fuse datasets # List datasets
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Narrative Commands
|
|
89
|
+
```
|
|
90
|
+
coaia narrative charts # List all active charts
|
|
91
|
+
coaia narrative chart <id> # Show chart details
|
|
92
|
+
coaia narrative progress <id> # Show chart progress
|
|
93
|
+
coaia narrative export <id> [--format md|json] # Export chart
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### PDE Commands
|
|
97
|
+
```
|
|
98
|
+
coaia pde decompose <prompt> # Decompose a prompt
|
|
99
|
+
coaia pde list # List decompositions
|
|
100
|
+
coaia pde get <id> # Get decomposition
|
|
101
|
+
coaia pde export <id> # Export as markdown
|
|
102
|
+
coaia pde to-stc <id> # Transform to STC
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Plan Commands
|
|
106
|
+
```
|
|
107
|
+
coaia plan parse <file> # Parse plan markdown
|
|
108
|
+
coaia plan to-stc <file> # Convert plan to STC
|
|
109
|
+
coaia plan from-stc <chart-id> # Generate plan from STC
|
|
110
|
+
coaia plan sync <file> <chart-id> # Bidirectional sync
|
|
111
|
+
coaia plan diff <file> <chart-id> # Show differences
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### MCP Commands
|
|
115
|
+
```
|
|
116
|
+
coaia mcp start [--mode MINIMAL|STANDARD|FULL] # Start MCP server
|
|
117
|
+
coaia mcp tools [--mode X] # List available tools
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Output Formatting
|
|
121
|
+
|
|
122
|
+
- Use `chalk` for colored terminal output
|
|
123
|
+
- JSON output available via `--json` global flag
|
|
124
|
+
- Quiet mode via `--quiet` global flag (exit code only)
|
|
125
|
+
- Error output to stderr, data to stdout
|
|
126
|
+
|
|
127
|
+
## Quality Criteria
|
|
128
|
+
|
|
129
|
+
- ✅ Every coaiapy CLI command has an equivalent subcommand
|
|
130
|
+
- ✅ `coaia --help` shows all subcommands with descriptions
|
|
131
|
+
- ✅ `coaia <subcommand> --help` shows subcommand-specific help
|
|
132
|
+
- ✅ Exit code 0 on success, 1 on error
|
|
133
|
+
- ✅ `--json` flag produces machine-parseable output for every command
|
|
134
|
+
- ✅ Works in pipe context (no TTY-dependent formatting when piped)
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# 09 — MCP Server
|
|
2
|
+
|
|
3
|
+
> Unified MCP server exposing 64+ tools from all modules with feature gating.
|
|
4
|
+
|
|
5
|
+
## Desired Outcome
|
|
6
|
+
|
|
7
|
+
A single MCP server (`coaiajs-mcp`) that consolidates the 44 tools from coaia-narrative (27), coaia-pde (12), and coaia-planning (5) into one server, adds 20 new tools for Redis, Langfuse, pipeline, and audio, and supports feature gating so resource-constrained environments can load only what they need.
|
|
8
|
+
|
|
9
|
+
## Structural Tension
|
|
10
|
+
|
|
11
|
+
**Current Reality:**
|
|
12
|
+
- MCP server entry defined in `package.json` as `"coaiajs-mcp": "./dist/mcp/server.js"` but `mcp/` directory is empty (only a `tools/` subdirectory stub)
|
|
13
|
+
- `@modelcontextprotocol/sdk` v1.25.0 is installed as a dependency
|
|
14
|
+
- Four separate MCP servers exist in parent projects:
|
|
15
|
+
- coaia-narrative: 27 tools (STC lifecycle, knowledge graph, narrative beats, MMOT)
|
|
16
|
+
- coaia-pde: 12 tools (decomposition, session management, STC transformation)
|
|
17
|
+
- coaia-planning: 5 tools (plan parsing, plan↔STC sync, PDE bridge)
|
|
18
|
+
- coaiapy: 0 MCP tools (Python library only, no MCP)
|
|
19
|
+
- Each parent server has its own startup, tool registration, and transport handling
|
|
20
|
+
- Users currently need 3 MCP server entries in their config to access all tools
|
|
21
|
+
|
|
22
|
+
**Desired Outcome:**
|
|
23
|
+
Single MCP server at `mcp/server.ts` providing:
|
|
24
|
+
- All 44 parent tools with identical schemas and behavior
|
|
25
|
+
- 20 new tools for previously-CLI-only functionality
|
|
26
|
+
- Feature gating via `COAIAJS_MCP_MODE` environment variable
|
|
27
|
+
- Single server entry in MCP client config
|
|
28
|
+
|
|
29
|
+
## Feature Gating
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
type McpMode = 'MINIMAL' | 'STANDARD' | 'FULL';
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
| Mode | Tools loaded | Use case |
|
|
36
|
+
|------|-------------|----------|
|
|
37
|
+
| **MINIMAL** | STC tools (11), KG tools (9), MMOT (2), narrative beats (3) = **25** | Memory-constrained agents, basic STC workflow |
|
|
38
|
+
| **STANDARD** | MINIMAL + PDE (12) + planning (6) + Redis (5) = **48** | Standard development sessions |
|
|
39
|
+
| **FULL** | STANDARD + Langfuse (8) + pipeline (3) + audio (2) + guidance (3) = **64** | Full-featured agent sessions |
|
|
40
|
+
|
|
41
|
+
Default mode: `STANDARD`.
|
|
42
|
+
|
|
43
|
+
## Server Architecture
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
// mcp/server.ts
|
|
47
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
48
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
49
|
+
|
|
50
|
+
const server = new McpServer({
|
|
51
|
+
name: 'coaiajs-mcp',
|
|
52
|
+
version: pkg.version
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Register tools based on mode
|
|
56
|
+
const mode = (process.env.COAIAJS_MCP_MODE ?? 'STANDARD') as McpMode;
|
|
57
|
+
registerNarrativeTools(server); // always
|
|
58
|
+
if (mode !== 'MINIMAL') {
|
|
59
|
+
registerPdeTools(server);
|
|
60
|
+
registerPlanningTools(server);
|
|
61
|
+
registerRedisTools(server);
|
|
62
|
+
}
|
|
63
|
+
if (mode === 'FULL') {
|
|
64
|
+
registerLangfuseTools(server);
|
|
65
|
+
registerPipelineTools(server);
|
|
66
|
+
registerAudioTools(server);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const transport = new StdioServerTransport();
|
|
70
|
+
await server.connect(transport);
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Tool Registry
|
|
74
|
+
|
|
75
|
+
### Narrative Engine (28 tools — always loaded)
|
|
76
|
+
|
|
77
|
+
STC: `create_structural_tension_chart`, `list_active_charts`, `get_chart`, `get_chart_progress`, `manage_action_step`, `mark_action_complete`, `update_action_progress`, `update_current_reality`, `update_desired_outcome`, `get_action_step`, `remove_action_step`
|
|
78
|
+
|
|
79
|
+
Knowledge Graph: `create_entities`, `create_relations`, `add_observations`, `search_nodes`, `open_nodes`, `read_graph`, `delete_entities`, `delete_relations`, `delete_observations`
|
|
80
|
+
|
|
81
|
+
Narrative Beats: `create_narrative_beat`, `telescope_narrative_beat`, `list_narrative_beats`
|
|
82
|
+
|
|
83
|
+
MMOT: `perform_mmot_evaluation`, `init_llm_guidance`
|
|
84
|
+
|
|
85
|
+
### PDE Engine (12 tools — STANDARD+)
|
|
86
|
+
|
|
87
|
+
`pde_decompose`, `pde_parse_response`, `pde_get`, `pde_list`, `pde_export_markdown`, `pde_to_stc`, `pde_preview_stc`, `pde_create_session`, `pde_get_session`, `pde_list_sessions`, `pde_session_status`, `pde_bridge_plan`
|
|
88
|
+
|
|
89
|
+
### Planning Engine (6 tools — STANDARD+)
|
|
90
|
+
|
|
91
|
+
`plan_parse`, `plan_to_stc`, `plan_from_stc`, `plan_sync`, `plan_diff`, `plan_bridge_pde`
|
|
92
|
+
|
|
93
|
+
### Redis (5 tools — STANDARD+)
|
|
94
|
+
|
|
95
|
+
`redis_tash`, `redis_fetch`, `redis_del`, `redis_keys`, `redis_exists`
|
|
96
|
+
|
|
97
|
+
### Langfuse (8 tools — FULL only)
|
|
98
|
+
|
|
99
|
+
`langfuse_list_traces`, `langfuse_get_trace`, `langfuse_list_prompts`, `langfuse_get_prompt`, `langfuse_list_datasets`, `langfuse_list_scores`, `langfuse_create_score`, `langfuse_list_score_configs`
|
|
100
|
+
|
|
101
|
+
### Pipeline (3 tools — FULL only)
|
|
102
|
+
|
|
103
|
+
`pipeline_list`, `pipeline_render`, `pipeline_execute`
|
|
104
|
+
|
|
105
|
+
### Audio (2 tools — FULL only)
|
|
106
|
+
|
|
107
|
+
`audio_transcribe`, `audio_synthesize`
|
|
108
|
+
|
|
109
|
+
## Tool Registration Pattern
|
|
110
|
+
|
|
111
|
+
Each module provides a `registerXxxTools(server: McpServer)` function:
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
// mcp/tools/redis.ts
|
|
115
|
+
export function registerRedisTools(server: McpServer) {
|
|
116
|
+
server.tool('redis_tash', 'Store a value in Redis with optional TTL', {
|
|
117
|
+
key: z.string().describe('Redis key'),
|
|
118
|
+
value: z.string().describe('Value to store'),
|
|
119
|
+
ttl: z.number().optional().describe('TTL in seconds'),
|
|
120
|
+
}, async ({ key, value, ttl }) => {
|
|
121
|
+
await tash(key, value, ttl);
|
|
122
|
+
return { content: [{ type: 'text', text: `Stored ${key}` }] };
|
|
123
|
+
});
|
|
124
|
+
// ... more tools
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Transport
|
|
129
|
+
|
|
130
|
+
stdio transport only (standard MCP pattern). Server reads JSON-RPC from stdin, writes to stdout. This is compatible with all MCP clients (Claude Code, Cursor, VS Code Copilot, etc.).
|
|
131
|
+
|
|
132
|
+
## Quality Criteria
|
|
133
|
+
|
|
134
|
+
- ✅ Single MCP config entry replaces three separate servers
|
|
135
|
+
- ✅ All 44 parent tools produce identical results to their parent implementations
|
|
136
|
+
- ✅ Feature gating reduces tool count without breaking functionality
|
|
137
|
+
- ✅ `COAIAJS_MCP_MODE=MINIMAL` loads ≤25 tools
|
|
138
|
+
- ✅ Server starts in <500ms for MINIMAL mode
|
|
139
|
+
- ✅ Tool descriptions are self-documenting (LLMs understand usage without external docs)
|
|
140
|
+
- ✅ Error responses include actionable information
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# 10 — Audio Module
|
|
2
|
+
|
|
3
|
+
> Audio transcription (OpenAI Whisper) and synthesis (AWS Polly).
|
|
4
|
+
|
|
5
|
+
## Desired Outcome
|
|
6
|
+
|
|
7
|
+
An audio module providing speech-to-text transcription via OpenAI Whisper and text-to-speech synthesis via AWS Polly — enabling voice-driven agent interactions and audio content generation.
|
|
8
|
+
|
|
9
|
+
## Structural Tension
|
|
10
|
+
|
|
11
|
+
**Current Reality:**
|
|
12
|
+
- `src/audio.ts` is fully implemented (76 lines) with `synthesize()` function using AWS Polly
|
|
13
|
+
- `src/llm.ts` includes `transcribeAudio()` using OpenAI Whisper
|
|
14
|
+
- Both use lazy client initialization
|
|
15
|
+
- coaiapy's `syntation.py` provides the same functionality via boto3 and openai Python packages
|
|
16
|
+
- No MCP tools exist for audio in any parent project
|
|
17
|
+
|
|
18
|
+
**Desired Outcome:**
|
|
19
|
+
Audio module consolidating transcription and synthesis in one place:
|
|
20
|
+
- `transcribe(audioPath)` — OpenAI Whisper speech-to-text
|
|
21
|
+
- `synthesize(text, outputPath, options?)` — AWS Polly text-to-speech
|
|
22
|
+
- 2 MCP tools for interactive audio operations
|
|
23
|
+
- Support for multiple voices, engines, and output formats
|
|
24
|
+
|
|
25
|
+
## Core API
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
// Transcription (via OpenAI Whisper)
|
|
29
|
+
transcribe(audioPath: string, options?: {
|
|
30
|
+
model?: string; // default: 'whisper-1'
|
|
31
|
+
language?: string; // ISO 639-1 code
|
|
32
|
+
prompt?: string; // context hint
|
|
33
|
+
}): Promise<string> // returns transcribed text
|
|
34
|
+
|
|
35
|
+
// Synthesis (via AWS Polly)
|
|
36
|
+
synthesize(text: string, outputPath: string, options?: {
|
|
37
|
+
voice?: string; // default: 'Joanna' (from config)
|
|
38
|
+
engine?: 'neural' | 'standard'; // default: 'neural'
|
|
39
|
+
format?: 'mp3' | 'ogg_vorbis' | 'pcm'; // default: 'mp3'
|
|
40
|
+
}): Promise<string> // returns output file path
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## MCP Tools (2)
|
|
44
|
+
|
|
45
|
+
| Tool | Purpose |
|
|
46
|
+
|------|---------|
|
|
47
|
+
| `audio_transcribe` | Transcribe audio file to text |
|
|
48
|
+
| `audio_synthesize` | Synthesize text to audio file |
|
|
49
|
+
|
|
50
|
+
## Configuration
|
|
51
|
+
|
|
52
|
+
Uses config keys from `src/config.ts`:
|
|
53
|
+
- `openai_api_key` — for Whisper transcription
|
|
54
|
+
- `aws_access_key_id`, `aws_secret_access_key`, `aws_region` — for Polly synthesis
|
|
55
|
+
- `aws_polly_voice` — default voice (fallback: `Joanna`)
|
|
56
|
+
|
|
57
|
+
## Quality Criteria
|
|
58
|
+
|
|
59
|
+
- ✅ `transcribe()` accepts wav, mp3, m4a, webm, mp4 audio files
|
|
60
|
+
- ✅ `synthesize()` produces valid MP3 output playable in standard players
|
|
61
|
+
- ✅ Missing credentials produce clear error at call time, not import time
|
|
62
|
+
- ✅ Parity with coaiapy's transcription and synthesis behavior
|
|
63
|
+
- ✅ `resetClient()` clears cached clients for test isolation
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# KINSHIP.md — coaiajs/rispecs
|
|
2
|
+
|
|
3
|
+
## Identity
|
|
4
|
+
|
|
5
|
+
**Name:** coaiajs rispecs
|
|
6
|
+
**Role:** RISE-based structural specifications for the coaiajs platform
|
|
7
|
+
**Status:** Genesis (2026-03-11)
|
|
8
|
+
|
|
9
|
+
## Lineage
|
|
10
|
+
|
|
11
|
+
These specifications synthesize the creative intent, structural patterns, and behavioral requirements from four parent projects into a unified specification set.
|
|
12
|
+
|
|
13
|
+
### Parent Rispecs
|
|
14
|
+
|
|
15
|
+
| Source | Location | What was inherited |
|
|
16
|
+
|--------|----------|-------------------|
|
|
17
|
+
| **coaia-narrative rispecs** | `/src/coaia-narrative/rispecs/` | 15 specs — STC creation, telescoping, advancing patterns, JSONL storage, MCP tools, MMOT, narrative beats, input validation, educational guidance, CLI visualization |
|
|
18
|
+
| **coaia-pde rispecs** | `/src/coaia-pde/rispecs/` | 1 spec — PDE→STC transformation mapping (Four Directions → STC components) |
|
|
19
|
+
| **RISE Framework** | `/src/llms/llms-rise-framework.txt` | Methodology: creative orientation, structural tension dynamics, variable detail levels, spec-as-source-of-truth |
|
|
20
|
+
|
|
21
|
+
### No Prior Rispecs Existed For
|
|
22
|
+
|
|
23
|
+
| Module | First specified here |
|
|
24
|
+
|--------|---------------------|
|
|
25
|
+
| Core config | `01-core-config.spec.md` |
|
|
26
|
+
| Redis | `02-redis-module.spec.md` |
|
|
27
|
+
| Langfuse | `03-langfuse-module.spec.md` |
|
|
28
|
+
| Planning | `06-planning-engine.spec.md` |
|
|
29
|
+
| Pipeline | `07-pipeline-templates.spec.md` |
|
|
30
|
+
| CLI | `08-cli-interface.spec.md` |
|
|
31
|
+
| MCP server (unified) | `09-mcp-server.spec.md` |
|
|
32
|
+
| Audio | `10-audio-module.spec.md` |
|
|
33
|
+
|
|
34
|
+
## Accountabilities
|
|
35
|
+
|
|
36
|
+
1. **Completeness** — These specs are sufficient for another LLM to re-implement the entire coaiajs platform from scratch
|
|
37
|
+
2. **Accuracy** — Current reality assessments are honest and factual, reflecting actual implementation state as of 2026-03-11
|
|
38
|
+
3. **Creative orientation** — All desired outcomes use creation language, not problem-solving language
|
|
39
|
+
4. **Backward compatibility** — Specs preserve behavioral compatibility with parent projects
|
|
40
|
+
5. **Independence** — Specs are codebase-agnostic; they describe behavior, not implementation
|
|
41
|
+
|
|
42
|
+
## Consumers
|
|
43
|
+
|
|
44
|
+
| Consumer | How they use these specs |
|
|
45
|
+
|----------|------------------------|
|
|
46
|
+
| **coaiajs implementors** | Build each module from its spec |
|
|
47
|
+
| **coaia-narrative maintainers** | Verify their rispecs are preserved in consolidation |
|
|
48
|
+
| **coaia-pde maintainers** | Verify PDE→STC mapping is preserved |
|
|
49
|
+
| **MMOT evaluations** | Elements of Performance derived from quality criteria sections |
|
|
50
|
+
| **mia-code / miaco** | Reference for PDE and narrative integration patterns |
|
|
51
|
+
|
|
52
|
+
## Structural Tension
|
|
53
|
+
|
|
54
|
+
**Desired Outcome:** These rispecs serve as the single source of truth for what coaiajs should become — complete enough for autonomous re-implementation, accurate enough for MMOT self-evaluation.
|
|
55
|
+
|
|
56
|
+
**Current Reality:** 13 spec files covering the full platform. Parent project rispecs remain authoritative for deep behavioral details (especially coaia-narrative's 15 specs). These specs provide the consolidation vision and module boundaries.
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# CoAiA.js — RISE Specifications
|
|
2
|
+
|
|
3
|
+
> Complete RISE-based specifications for rebuilding CoAiA.js from scratch.
|
|
4
|
+
> Another LLM or developer reading these specs alone should be able to re-implement the entire system.
|
|
5
|
+
|
|
6
|
+
## Purpose
|
|
7
|
+
|
|
8
|
+
These rispecs define the structural specifications for **coaiajs** — the unified TypeScript platform that consolidates `coaiapy`, `coaia-narrative`, `coaia-pde`, and `coaia-planning` into a single modern Node.js package.
|
|
9
|
+
|
|
10
|
+
## Specification Index
|
|
11
|
+
|
|
12
|
+
### Platform
|
|
13
|
+
|
|
14
|
+
| # | Spec | Scope |
|
|
15
|
+
|---|------|-------|
|
|
16
|
+
| 00 | [coaiajs-platform](./00-coaiajs-platform.spec.md) | Master platform spec — vision, lineage, component map |
|
|
17
|
+
|
|
18
|
+
### Core Modules
|
|
19
|
+
|
|
20
|
+
| # | Spec | Scope | Lineage |
|
|
21
|
+
|---|------|-------|---------|
|
|
22
|
+
| 01 | [core-config](./01-core-config.spec.md) | Config loading, env vars, deep merge | coaiapy `read_config()` |
|
|
23
|
+
| 02 | [redis-module](./02-redis-module.spec.md) | Redis tash/fetch, lazy connection, Upstash | coaiapy `tash/fetch` |
|
|
24
|
+
| 03 | [langfuse-module](./03-langfuse-module.spec.md) | Langfuse observability REST client | coaiapy `cofuse.py` |
|
|
25
|
+
|
|
26
|
+
### Engine Modules
|
|
27
|
+
|
|
28
|
+
| # | Spec | Scope | Lineage |
|
|
29
|
+
|---|------|-------|---------|
|
|
30
|
+
| 04 | [narrative-engine](./04-narrative-engine.spec.md) | JSONL knowledge graph, STC, beats, MMOT | coaia-narrative `graph-manager.ts` |
|
|
31
|
+
| 05 | [pde-engine](./05-pde-engine.spec.md) | PDE→STC transformation, session management | coaia-pde `stc-mapper.ts` |
|
|
32
|
+
| 06 | [planning-engine](./06-planning-engine.spec.md) | Plan parsing, plan↔STC sync | coaia-planning `plan-parser.ts` |
|
|
33
|
+
|
|
34
|
+
### Support Modules
|
|
35
|
+
|
|
36
|
+
| # | Spec | Scope | Lineage |
|
|
37
|
+
|---|------|-------|---------|
|
|
38
|
+
| 07 | [pipeline-templates](./07-pipeline-templates.spec.md) | Template rendering, variable substitution | coaiapy `pipeline.py` |
|
|
39
|
+
| 10 | [audio-module](./10-audio-module.spec.md) | Whisper transcription, Polly synthesis | coaiapy `syntation.py` |
|
|
40
|
+
|
|
41
|
+
### Interface Layers
|
|
42
|
+
|
|
43
|
+
| # | Spec | Scope | Lineage |
|
|
44
|
+
|---|------|-------|---------|
|
|
45
|
+
| 08 | [cli-interface](./08-cli-interface.spec.md) | Unified `coaia` CLI with all subcommands | coaiapy argparse + coaia-narrative minimist |
|
|
46
|
+
| 09 | [mcp-server](./09-mcp-server.spec.md) | Unified MCP server with 64+ tools | 4 separate MCP servers |
|
|
47
|
+
|
|
48
|
+
## How Specs Relate
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
┌─────────────────────────────────────────────────────┐
|
|
52
|
+
│ 00-coaiajs-platform (master) │
|
|
53
|
+
│ │
|
|
54
|
+
│ ┌─────────┐ ┌──────────┐ ┌───────────────────┐ │
|
|
55
|
+
│ │01 config │→ │02 redis │ │03 langfuse │ │
|
|
56
|
+
│ └────┬─────┘ └─────┬────┘ └────────┬──────────┘ │
|
|
57
|
+
│ │ │ │ │
|
|
58
|
+
│ ┌────▼──────────────▼───────────────▼──────────┐ │
|
|
59
|
+
│ │ 04 narrative-engine (JSONL, STC, MMOT) │ │
|
|
60
|
+
│ └────────┬──────────────────────┬──────────────┘ │
|
|
61
|
+
│ │ │ │
|
|
62
|
+
│ ┌────────▼──────┐ ┌───────────▼──────────────┐ │
|
|
63
|
+
│ │05 pde-engine │ │06 planning-engine │ │
|
|
64
|
+
│ └───────────────┘ └──────────────────────────┘ │
|
|
65
|
+
│ │
|
|
66
|
+
│ ┌───────────────┐ ┌──────────────────────────┐ │
|
|
67
|
+
│ │07 pipeline │ │10 audio │ │
|
|
68
|
+
│ └───────────────┘ └──────────────────────────┘ │
|
|
69
|
+
│ │
|
|
70
|
+
│ ┌──────────────────────────────────────────────┐ │
|
|
71
|
+
│ │ 08 cli-interface 09 mcp-server │ │
|
|
72
|
+
│ └──────────────────────────────────────────────┘ │
|
|
73
|
+
└─────────────────────────────────────────────────────┘
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Conventions
|
|
77
|
+
|
|
78
|
+
- **Creative orientation**: Desired Outcome describes what IS CREATED, not what is fixed
|
|
79
|
+
- **Honest current reality**: Factual assessment of where things stand today
|
|
80
|
+
- **Structural tension**: The gap between current and desired that drives advancement
|
|
81
|
+
- **Variable detail**: Broad for obvious patterns, precise for critical behavior
|
|
82
|
+
- **Naming**: `NN-kebab-case.spec.md`
|
|
83
|
+
- **Codebase-agnostic**: Specs describe behavior, not implementation details
|
|
84
|
+
|
|
85
|
+
## Relationship to Parent Rispecs
|
|
86
|
+
|
|
87
|
+
| Project | Rispecs location | Status |
|
|
88
|
+
|---------|-----------------|--------|
|
|
89
|
+
| coaia-narrative | `/src/coaia-narrative/rispecs/` | 15 specs — most mature, patterns inherited here |
|
|
90
|
+
| coaia-pde | `/src/coaia-pde/rispecs/` | 1 spec — PDE→STC transformation |
|
|
91
|
+
| coaia-planning | — | No rispecs yet — spec 06 here is the first |
|
|
92
|
+
| coaiapy | — | No rispecs — specs 01-03, 07, 10 here are the first |
|
|
93
|
+
|
|
94
|
+
## RISE Framework Reference
|
|
95
|
+
|
|
96
|
+
These specs follow the RISE Framework (`/src/llms/llms-rise-framework.txt`):
|
|
97
|
+
- **R**everse-engineer: Extract creative intent from parent projects
|
|
98
|
+
- **I**ntent-extract: Identify what each module enables users to create
|
|
99
|
+
- **S**pecify: Define structural tension between current and desired
|
|
100
|
+
- **E**xport: Produce specs sufficient for autonomous re-implementation
|
package/src/audio.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// coaiajs/src/audio.ts — Audio synthesis module
|
|
2
|
+
// Parity with coaiapy's syntation.py using AWS Polly
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
PollyClient,
|
|
6
|
+
SynthesizeSpeechCommand,
|
|
7
|
+
type SynthesizeSpeechCommandInput,
|
|
8
|
+
} from '@aws-sdk/client-polly';
|
|
9
|
+
import { writeFileSync } from 'node:fs';
|
|
10
|
+
import { resolve } from 'node:path';
|
|
11
|
+
import { getConfig } from './config.js';
|
|
12
|
+
|
|
13
|
+
let _client: PollyClient | null = null;
|
|
14
|
+
|
|
15
|
+
function getPollyClient(): PollyClient {
|
|
16
|
+
if (!_client) {
|
|
17
|
+
const cfg = getConfig();
|
|
18
|
+
_client = new PollyClient({
|
|
19
|
+
region: cfg.aws?.region ?? process.env['AWS_REGION'] ?? 'us-east-1',
|
|
20
|
+
credentials:
|
|
21
|
+
cfg.aws?.accessKeyId
|
|
22
|
+
? {
|
|
23
|
+
accessKeyId: cfg.aws.accessKeyId,
|
|
24
|
+
secretAccessKey: cfg.aws.secretAccessKey ?? '',
|
|
25
|
+
}
|
|
26
|
+
: undefined,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
return _client;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Reset client (testing). */
|
|
33
|
+
export function resetClient(): void {
|
|
34
|
+
_client = null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Synthesize text to speech using AWS Polly.
|
|
39
|
+
* Returns the path to the output audio file.
|
|
40
|
+
*
|
|
41
|
+
* @param text - Text to synthesize
|
|
42
|
+
* @param voiceId - Polly voice ID (default: "Joanna")
|
|
43
|
+
* @param outFile - Output file path (default: "./output.mp3")
|
|
44
|
+
*/
|
|
45
|
+
export async function synthesize(
|
|
46
|
+
text: string,
|
|
47
|
+
voiceId?: string,
|
|
48
|
+
outFile?: string,
|
|
49
|
+
): Promise<string> {
|
|
50
|
+
const client = getPollyClient();
|
|
51
|
+
const outputPath = resolve(outFile ?? './output.mp3');
|
|
52
|
+
|
|
53
|
+
const params: SynthesizeSpeechCommandInput = {
|
|
54
|
+
Text: text,
|
|
55
|
+
OutputFormat: 'mp3',
|
|
56
|
+
VoiceId: (voiceId ?? 'Joanna') as SynthesizeSpeechCommandInput['VoiceId'],
|
|
57
|
+
Engine: 'neural',
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const result = await client.send(new SynthesizeSpeechCommand(params));
|
|
61
|
+
|
|
62
|
+
if (result.AudioStream) {
|
|
63
|
+
// AudioStream is a Readable or Uint8Array depending on environment
|
|
64
|
+
const chunks: Uint8Array[] = [];
|
|
65
|
+
const stream = result.AudioStream as AsyncIterable<Uint8Array>;
|
|
66
|
+
|
|
67
|
+
for await (const chunk of stream) {
|
|
68
|
+
chunks.push(chunk);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const buffer = Buffer.concat(chunks);
|
|
72
|
+
writeFileSync(outputPath, buffer);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return outputPath;
|
|
76
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
|
|
3
|
+
let colorEnabled = true;
|
|
4
|
+
|
|
5
|
+
export function setColorEnabled(enabled: boolean): void {
|
|
6
|
+
colorEnabled = enabled;
|
|
7
|
+
if (!enabled) {
|
|
8
|
+
chalk.level = 0;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function formatTable(headers: string[], rows: string[][]): string {
|
|
13
|
+
if (rows.length === 0) return '(no data)';
|
|
14
|
+
|
|
15
|
+
const widths = headers.map((h, i) =>
|
|
16
|
+
Math.max(h.length, ...rows.map(r => (r[i] ?? '').length))
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
const headerLine = headers
|
|
20
|
+
.map((h, i) => h.padEnd(widths[i]!))
|
|
21
|
+
.join(' ');
|
|
22
|
+
const separator = widths.map(w => '─'.repeat(w)).join('──');
|
|
23
|
+
const dataLines = rows.map(row =>
|
|
24
|
+
row.map((cell, i) => (cell ?? '').padEnd(widths[i]!)).join(' ')
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
return [
|
|
28
|
+
colorEnabled ? chalk.bold(headerLine) : headerLine,
|
|
29
|
+
separator,
|
|
30
|
+
...dataLines,
|
|
31
|
+
].join('\n');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function formatJson(data: unknown): string {
|
|
35
|
+
return JSON.stringify(data, null, 2);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function formatError(message: string): string {
|
|
39
|
+
const sym = colorEnabled ? chalk.red('✗') : '✗';
|
|
40
|
+
const txt = colorEnabled ? chalk.red(message) : message;
|
|
41
|
+
return `${sym} ${txt}`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function formatSuccess(message: string): string {
|
|
45
|
+
const sym = colorEnabled ? chalk.green('✓') : '✓';
|
|
46
|
+
const txt = colorEnabled ? chalk.green(message) : message;
|
|
47
|
+
return `${sym} ${txt}`;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function truncate(text: string, maxLength: number): string {
|
|
51
|
+
if (text.length <= maxLength) return text;
|
|
52
|
+
return text.slice(0, maxLength - 1) + '…';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function formatDate(iso: string): string {
|
|
56
|
+
const date = new Date(iso);
|
|
57
|
+
if (isNaN(date.getTime())) return iso;
|
|
58
|
+
const now = new Date();
|
|
59
|
+
const diffMs = now.getTime() - date.getTime();
|
|
60
|
+
const diffMins = Math.floor(diffMs / 60_000);
|
|
61
|
+
const diffHours = Math.floor(diffMins / 60);
|
|
62
|
+
const diffDays = Math.floor(diffHours / 24);
|
|
63
|
+
|
|
64
|
+
if (diffMins < 1) return 'just now';
|
|
65
|
+
if (diffMins < 60) return `${diffMins}m ago`;
|
|
66
|
+
if (diffHours < 24) return `${diffHours}h ago`;
|
|
67
|
+
if (diffDays < 7) return `${diffDays}d ago`;
|
|
68
|
+
return date.toISOString().slice(0, 10);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function formatProgress(completed: number, total: number): string {
|
|
72
|
+
if (total === 0) return '[░░░░░░░░░░] 0% (0/0)';
|
|
73
|
+
const ratio = Math.min(completed / total, 1);
|
|
74
|
+
const pct = Math.round(ratio * 100);
|
|
75
|
+
const filled = Math.round(ratio * 10);
|
|
76
|
+
const empty = 10 - filled;
|
|
77
|
+
const bar = '█'.repeat(filled) + '░'.repeat(empty);
|
|
78
|
+
const colored = colorEnabled
|
|
79
|
+
? ratio >= 1
|
|
80
|
+
? chalk.green(bar)
|
|
81
|
+
: ratio >= 0.5
|
|
82
|
+
? chalk.yellow(bar)
|
|
83
|
+
: chalk.red(bar)
|
|
84
|
+
: bar;
|
|
85
|
+
return `[${colored}] ${pct}% (${completed}/${total})`;
|
|
86
|
+
}
|