capman 0.4.1 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/CHANGELOG.md +127 -0
  2. package/CODEBASE.md +391 -0
  3. package/README.md +76 -97
  4. package/bin/capman.js +21 -405
  5. package/bin/lib/cmd-demo.js +180 -0
  6. package/bin/lib/cmd-explain.js +72 -0
  7. package/bin/lib/cmd-generate.js +280 -0
  8. package/bin/lib/cmd-help.js +26 -0
  9. package/bin/lib/cmd-init.js +19 -0
  10. package/bin/lib/cmd-inspect.js +33 -0
  11. package/bin/lib/cmd-run.js +71 -0
  12. package/bin/lib/cmd-validate.js +32 -0
  13. package/bin/lib/shared.js +70 -0
  14. package/dist/cjs/engine.d.ts +58 -1
  15. package/dist/cjs/engine.d.ts.map +1 -1
  16. package/dist/cjs/engine.js +307 -12
  17. package/dist/cjs/engine.js.map +1 -1
  18. package/dist/cjs/generator.d.ts.map +1 -1
  19. package/dist/cjs/generator.js +4 -0
  20. package/dist/cjs/generator.js.map +1 -1
  21. package/dist/cjs/index.d.ts +13 -17
  22. package/dist/cjs/index.d.ts.map +1 -1
  23. package/dist/cjs/index.js +12 -7
  24. package/dist/cjs/index.js.map +1 -1
  25. package/dist/cjs/matcher.d.ts.map +1 -1
  26. package/dist/cjs/matcher.js +19 -25
  27. package/dist/cjs/matcher.js.map +1 -1
  28. package/dist/cjs/parser.d.ts +11 -0
  29. package/dist/cjs/parser.d.ts.map +1 -0
  30. package/dist/cjs/parser.js +304 -0
  31. package/dist/cjs/parser.js.map +1 -0
  32. package/dist/cjs/types.d.ts +27 -0
  33. package/dist/cjs/types.d.ts.map +1 -1
  34. package/dist/cjs/version.d.ts +1 -1
  35. package/dist/cjs/version.js +1 -1
  36. package/dist/esm/cache.d.ts +49 -0
  37. package/dist/esm/engine.d.ts +138 -0
  38. package/dist/esm/engine.js +307 -12
  39. package/dist/esm/generator.d.ts +7 -0
  40. package/dist/esm/generator.js +4 -0
  41. package/dist/esm/index.d.ts +47 -0
  42. package/dist/esm/index.js +6 -4
  43. package/dist/esm/learning.d.ts +55 -0
  44. package/dist/esm/logger.d.ts +21 -0
  45. package/dist/esm/matcher.d.ts +6 -0
  46. package/dist/esm/matcher.js +19 -25
  47. package/dist/esm/parser.d.ts +10 -0
  48. package/dist/esm/parser.js +267 -0
  49. package/dist/esm/resolver.d.ts +21 -0
  50. package/dist/esm/schema.d.ts +740 -0
  51. package/dist/esm/types.d.ts +136 -0
  52. package/dist/esm/version.d.ts +1 -0
  53. package/dist/esm/version.js +1 -1
  54. package/package.json +5 -3
package/README.md CHANGED
@@ -32,21 +32,21 @@ User query → match capability → resolve via API or nav → structured result
32
32
 
33
33
  ## Quick Start
34
34
 
35
- **1. Create your manifest config**
35
+ **1. Generate your manifest — three ways:**
36
36
 
37
37
  ```bash
38
- npx capman init
39
- ```
38
+ # From an OpenAPI/Swagger spec (fastest, no API key needed)
39
+ npx capman generate --from openapi.json
40
+ npx capman generate --from https://api.your-app.com/openapi.json
40
41
 
41
- Edit `capman.config.js` to define your app's capabilities.
42
+ # AI-assisted describe your app in plain English
43
+ npx capman generate --ai
42
44
 
43
- **2. Generate the manifest**
44
-
45
- ```bash
46
- npx capman generate
45
+ # Manual edit capman.config.js yourself
46
+ npx capman init
47
47
  ```
48
48
 
49
- **3. Use the engine in your AI agent**
49
+ **2. Use the engine in your AI agent**
50
50
 
51
51
  ```typescript
52
52
  import { CapmanEngine, readManifest } from 'capman'
@@ -66,7 +66,7 @@ console.log(result.resolvedVia) // 'keyword' | 'llm' | 'cache'
66
66
  console.log(result.trace.reasoning) // ['Matched "check_product_availability" with 100% confidence', ...]
67
67
  ```
68
68
 
69
- **4. See it live**
69
+ **3. See it live**
70
70
 
71
71
  ```bash
72
72
  npx capman demo
@@ -74,6 +74,61 @@ npx capman demo
74
74
 
75
75
  ---
76
76
 
77
+ ## Manifest Generation
78
+
79
+ capman gives you three ways to create your manifest — pick based on what you have:
80
+
81
+ ### From OpenAPI / Swagger spec
82
+
83
+ If your backend has an OpenAPI spec (most do), capman reads it and generates a complete manifest automatically. No LLM needed, no API key, works offline.
84
+
85
+ ```bash
86
+ npx capman generate --from openapi.json
87
+ npx capman generate --from https://api.your-app.com/openapi.json
88
+ ```
89
+
90
+ What it does automatically:
91
+ - Converts every endpoint into a capability with correct ID, name, and description
92
+ - Extracts path params, query params, and request body fields
93
+ - Infers privacy scope from security schemes — bearer token → `user_owned`, admin tags → `admin`, no auth → `public`
94
+ - Generates natural language examples from the operation summary
95
+ - Writes a ready `capman.config.js` for you to review and adjust
96
+
97
+ ```
98
+ ✓ Parsed 19 capabilities from spec
99
+ ✓ Config written to capman.config.js
100
+ ✓ Manifest written to manifest.json
101
+ ```
102
+
103
+ ### AI-assisted generation
104
+
105
+ No OpenAPI spec? Describe your app in plain English and capman uses an LLM to generate a full manifest.
106
+
107
+ ```bash
108
+ npx capman generate --ai
109
+ ```
110
+
111
+ ```
112
+ Describe your app and its main capabilities:
113
+ > A SaaS CRM. Users can create contacts, log calls, view pipeline
114
+ stages. Admins can manage teams and billing.
115
+
116
+ Using anthropic to generate manifest...
117
+ ✓ 6 capabilities generated
118
+ ```
119
+
120
+ Requires one of: `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, or `OPENROUTER_API_KEY` in your environment.
121
+
122
+ ### Manual
123
+
124
+ For full control, start from a starter config and define capabilities yourself:
125
+
126
+ ```bash
127
+ npx capman init
128
+ ```
129
+
130
+ ---
131
+
77
132
  ## Execution Trace
78
133
 
79
134
  Every `engine.ask()` call returns a full execution trace — so you always know why the AI did what it did.
@@ -112,19 +167,6 @@ Debug any query from the CLI:
112
167
  npx capman run "check availability for blue jacket" --debug
113
168
  ```
114
169
 
115
- ```
116
- ✓ Matched: check_product_availability
117
- Intent: retrieval
118
- Confidence: 100%
119
- Resolver: api
120
- Params: product=blue-jacket
121
-
122
- ── All candidates:
123
- ✓ check_product_availability: 100%
124
- ○ get_order_status: 12%
125
- ○ navigate_to_screen: 0%
126
- ```
127
-
128
170
  ---
129
171
 
130
172
  ## Matching Modes
@@ -171,29 +213,15 @@ import { CapmanEngine, FileCache, FileLearningStore } from 'capman'
171
213
 
172
214
  const engine = new CapmanEngine({
173
215
  manifest,
174
- // Default: MemoryCache (fast, resets on restart)
175
- // For persistence across restarts:
176
216
  cache: new FileCache('.capman/cache.json'),
177
217
  learning: new FileLearningStore('.capman/learning.json'),
178
218
  })
179
219
 
180
- // After real usage, see what's happening
181
220
  const stats = await engine.getStats()
182
- console.log(stats)
183
- // {
184
- // totalQueries: 142,
185
- // llmQueries: 18,
186
- // cacheHits: 67,
187
- // outOfScope: 3,
188
- // index: { 'availability': { 'check_product_availability': 34 }, ... }
189
- // }
221
+ // { totalQueries: 142, llmQueries: 18, cacheHits: 67, outOfScope: 3 }
190
222
 
191
223
  const top = await engine.getTopCapabilities(3)
192
- // [
193
- // { id: 'check_product_availability', hits: 58 },
194
- // { id: 'navigate_to_screen', hits: 41 },
195
- // { id: 'get_order_status', hits: 28 },
196
- // ]
224
+ // [{ id: 'check_product_availability', hits: 58 }, ...]
197
225
  ```
198
226
 
199
227
  ---
@@ -212,77 +240,29 @@ const engine = new CapmanEngine({
212
240
  userId: 'user-123', // auto-injected into session params
213
241
  },
214
242
  })
215
-
216
- // user_owned capabilities require auth — blocked without it
217
- // admin capabilities require role: 'admin' — blocked for regular users
218
- // session params like {user_id} are auto-replaced from auth.userId
219
243
  ```
220
244
 
221
245
  ---
222
246
 
223
247
  ## Resolver Hardening
224
248
 
225
- Configure retries and timeouts per call:
226
-
227
249
  ```typescript
228
250
  const result = await engine.ask('show my orders', {
229
- retries: 2, // retry failed requests (default: 0)
230
- timeoutMs: 3000, // abort after 3 seconds (default: 5000)
251
+ retries: 2,
252
+ timeoutMs: 3000,
231
253
  })
232
254
  ```
233
255
 
234
256
  ---
235
257
 
236
- ## Capability Config
237
-
238
- Each capability in `capman.config.js`:
239
-
240
- ```javascript
241
- module.exports = {
242
- app: 'your-app',
243
- baseUrl: 'https://api.your-app.com',
244
- capabilities: [
245
- {
246
- id: 'check_product_availability',
247
- name: 'Check product availability',
248
- description: 'Check stock and pricing for a product by name or ID.',
249
- examples: [
250
- 'Is the blue jacket available?',
251
- 'Check availability for product 42',
252
- 'Do you have size M in stock?',
253
- ],
254
- params: [
255
- {
256
- name: 'product',
257
- description: 'Product name or ID',
258
- required: true,
259
- source: 'user_query', // extracted from the query
260
- },
261
- ],
262
- returns: ['stock', 'price', 'variants'],
263
- resolver: {
264
- type: 'api', // 'api' | 'nav' | 'hybrid'
265
- endpoints: [
266
- { method: 'GET', path: '/products/{product}/availability' },
267
- ],
268
- },
269
- privacy: {
270
- level: 'public', // 'public' | 'user_owned' | 'admin'
271
- note: 'No auth required',
272
- },
273
- },
274
- ],
275
- }
276
- ```
277
-
278
- ---
279
-
280
258
  ## CLI Commands
281
259
 
282
260
  | Command | What it does |
283
261
  |---|---|
284
262
  | `capman init` | Create a starter `capman.config.js` |
285
- | `capman generate` | Generate `manifest.json` from config |
263
+ | `capman generate` | Generate manifest from `capman.config.js` |
264
+ | `capman generate --from <path\|url>` | Generate from OpenAPI/Swagger spec |
265
+ | `capman generate --ai` | Generate manifest using AI |
286
266
  | `capman validate` | Validate your manifest for errors |
287
267
  | `capman inspect` | Print all capabilities in the manifest |
288
268
  | `capman run "query"` | Run a query against your manifest |
@@ -326,20 +306,19 @@ module.exports = {
326
306
 
327
307
  **Works well:**
328
308
  - Structured data retrieval via APIs
329
- - Navigating to known app screens
330
- - Multi-endpoint aggregation
309
+ - Auto-generating manifests from OpenAPI specs
331
310
  - Privacy enforcement per capability
332
- - Caching repeated queries
333
311
  - Full execution tracing and debugging
312
+ - Caching repeated queries
334
313
 
335
314
  **Current limits:**
336
315
  - Real-time infra status (is the server down?)
337
316
  - UI-only state with no API backing
338
317
  - Very ambiguous queries — use `mode: 'accurate'` with an LLM
339
- - Cross-app orchestration (planned)
318
+ - Multi-instance deployments need Redis adapter (planned for v0.5)
340
319
 
341
320
  ---
342
321
 
343
322
  ## License
344
323
 
345
- MIT — [github.com/Hobbydefiningdoctory/capman](https://github.com/Hobbydefiningdoctory/capman)
324
+ MIT — [(github.com/Hobbydefiningdoctory/capman)](https://github.com/Hobbydefiningdoctory/capman.git)
package/bin/capman.js CHANGED
@@ -1,408 +1,24 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict'
3
3
 
4
- const path = require('path')
5
- const fs = require('fs')
6
-
7
- const args = process.argv.slice(2)
8
- const command = args[0]
9
- const flags = args.slice(1)
10
-
11
- const getFlag = (name) => {
12
- const i = flags.indexOf(name)
13
- return i !== -1 ? flags[i + 1] : undefined
14
- }
15
-
16
- const c = {
17
- reset: '\x1b[0m',
18
- bold: '\x1b[1m',
19
- teal: '\x1b[36m',
20
- yellow: '\x1b[33m',
21
- red: '\x1b[31m',
22
- green: '\x1b[32m',
23
- gray: '\x1b[90m',
24
- }
25
-
26
- const log = {
27
- info: (...a) => console.log(`${c.teal}i${c.reset}`, ...a),
28
- success: (...a) => console.log(`${c.green}✓${c.reset}`, ...a),
29
- warn: (...a) => console.log(`${c.yellow}⚠${c.reset}`, ...a),
30
- error: (...a) => console.error(`${c.red}✗${c.reset}`, ...a),
31
- blank: () => console.log(),
32
- }
33
-
34
- function header() {
35
- const pkg = require(path.join(__dirname, '..', 'package.json'))
36
- console.log()
37
- console.log(`${c.bold}${c.teal} capman${c.reset} ${c.gray}v${pkg.version} — Capability Manifest Engine${c.reset}`)
38
- console.log(`${c.gray} ─────────────────────────────────────────${c.reset}`)
39
- console.log()
40
- }
41
-
42
- function requireSrc() {
43
- const distPath = path.join(__dirname, '..', 'dist', 'cjs', 'index.js')
44
- if (fs.existsSync(distPath)) return require(distPath)
45
-
46
- // dist not built — try to build automatically
47
- log.info('dist/cjs not found — running build...')
48
- try {
49
- require('child_process').execSync('npm run build', {
50
- cwd: path.join(__dirname, '..'),
51
- stdio: 'inherit'
52
- })
53
- if (fs.existsSync(distPath)) return require(distPath)
54
- } catch {
55
- // build failed
56
- }
57
-
58
- log.error('Cannot find dist/cjs/. Run: pnpm run build')
59
- process.exit(1)
60
- }
61
-
62
- function cmdHelp() {
63
- header()
64
- console.log(`${c.bold} Usage:${c.reset} node bin/capman.js <command>`)
65
- console.log()
66
- console.log(`${c.bold} Commands:${c.reset}`)
67
- console.log(` ${c.teal}init${c.reset} Create a starter capman.config.js`)
68
- console.log(` ${c.teal}generate${c.reset} Generate manifest.json from config`)
69
- console.log(` ${c.teal}validate${c.reset} Validate an existing manifest.json`)
70
- console.log(` ${c.teal}inspect${c.reset} Print all capabilities in manifest`)
71
- console.log(` ${c.teal}demo${c.reset} Run a live demo with sample queries`)
72
- console.log(` ${c.teal}run${c.reset} Run a query against your manifest`)
73
- console.log()
74
- console.log(`${c.bold} Options:${c.reset}`)
75
- console.log(` ${c.gray}--config Path to config file (default: capman.config.js)${c.reset}`)
76
- console.log(` ${c.gray}--out Output path (default: manifest.json)${c.reset}`)
77
- console.log(` ${c.gray}--manifest Manifest to read (default: manifest.json)${c.reset}`)
78
- console.log(` ${c.gray}Options: --debug (show all candidates)${c.reset}`)
79
- console.log()
80
- }
81
-
82
- function cmdInit() {
83
- header()
84
- const outPath = path.resolve(process.cwd(), 'capman.config.js')
85
- if (fs.existsSync(outPath)) {
86
- log.warn('capman.config.js already exists — not overwriting.')
87
- process.exit(0)
88
- }
89
- const { generateStarterConfig } = requireSrc()
90
- fs.writeFileSync(outPath, generateStarterConfig())
91
- log.success(`Created ${c.bold}capman.config.js${c.reset}`)
92
- log.info(`Edit it with your app's capabilities, then run:`)
93
- console.log(`\n node bin/capman.js generate\n`)
94
- }
95
-
96
- function cmdGenerate() {
97
- header()
98
- const { loadConfig, generate, writeManifest, validate } = requireSrc()
99
-
100
- const configPath = getFlag('--config')
101
- const outPath = getFlag('--out') ?? 'manifest.json'
102
-
103
- log.info('Loading config...')
104
- let config
105
- try {
106
- config = loadConfig(configPath)
107
- } catch (e) {
108
- log.error(e.message)
109
- process.exit(1)
110
- }
111
-
112
- log.info(`Generating manifest for ${c.bold}${config.app}${c.reset}...`)
113
- const manifest = generate(config)
114
- const result = validate(manifest)
115
-
116
- for (const w of result.warnings) log.warn(w)
117
- for (const e of result.errors) log.error(e)
118
-
119
- if (!result.valid) {
120
- log.error('Manifest has errors — fix them before writing.')
121
- process.exit(1)
122
- }
123
-
124
- const written = writeManifest(manifest, outPath)
125
- log.blank()
126
- log.success(`Manifest written to ${c.bold}${written}${c.reset}`)
127
- log.info(`${manifest.capabilities.length} capabilities registered`)
128
- console.log()
129
- }
130
-
131
- function cmdValidate() {
132
- header()
133
- const { readManifest, validate } = requireSrc()
134
-
135
- const manifestPath = getFlag('--manifest') ?? 'manifest.json'
136
- let manifest
137
- try {
138
- manifest = readManifest(manifestPath)
139
- } catch (e) {
140
- log.error(e.message)
141
- process.exit(1)
142
- }
143
-
144
- log.info(`Validating ${c.bold}${manifestPath}${c.reset}...`)
145
- const result = validate(manifest)
146
- log.blank()
147
-
148
- for (const w of result.warnings) log.warn(w)
149
- for (const e of result.errors) log.error(e)
150
-
151
- if (result.valid) {
152
- log.success(`${manifest.capabilities.length} capabilities — all valid`)
153
- } else {
154
- log.error(`${result.errors.length} error(s) found.`)
155
- process.exit(1)
156
- }
157
- console.log()
158
- }
159
-
160
- function cmdInspect() {
161
- header()
162
- const { readManifest } = requireSrc()
163
-
164
- const manifestPath = getFlag('--manifest') ?? 'manifest.json'
165
- let manifest
166
- try {
167
- manifest = readManifest(manifestPath)
168
- } catch (e) {
169
- log.error(e.message)
170
- process.exit(1)
171
- }
172
-
173
- console.log(`${c.bold} App:${c.reset} ${manifest.app}`)
174
- console.log(`${c.bold} Generated:${c.reset} ${manifest.generatedAt}`)
175
- console.log(`${c.bold} Capabilities:${c.reset} ${manifest.capabilities.length}`)
176
- console.log()
177
-
178
- for (const cap of manifest.capabilities) {
179
- const col = cap.resolver.type === 'api' ? c.teal : cap.resolver.type === 'nav' ? c.teal : c.yellow
180
- console.log(` ${c.bold}${cap.name}${c.reset} ${col}[${cap.resolver.type}]${c.reset} ${c.gray}${cap.privacy.level}${c.reset}`)
181
- console.log(` ${c.gray}id: ${cap.id}${c.reset}`)
182
- console.log(` ${cap.description}`)
183
- if (cap.examples?.length) {
184
- console.log(` ${c.gray}e.g. "${cap.examples[0]}"${c.reset}`)
185
- }
186
- console.log()
187
- }
188
- }
189
-
190
- function cmdDemo() {
191
- header()
192
- const { generate, match, resolve } = requireSrc()
193
-
194
- console.log(`${c.bold} Live demo — see capman in action${c.reset}`)
195
- console.log(`${c.gray} ─────────────────────────────────────────${c.reset}\n`)
196
-
197
- // Demo manifest — generic e-commerce app
198
- const config = {
199
- app: 'demo-store',
200
- baseUrl: 'https://api.demo-store.com',
201
- capabilities: [
202
- {
203
- id: 'check_product_availability',
204
- name: 'Check product availability',
205
- description: 'Check stock and pricing for a product by name or ID.',
206
- examples: [
207
- 'Is the blue jacket in stock?',
208
- 'Check stock for blue jacket',
209
- 'Do you have size M available?',
210
- 'Product availability for jacket',
211
- 'Is jacket available?',
212
- ],
213
- params: [
214
- { name: 'product', description: 'Product name or ID', required: true, source: 'user_query' }
215
- ],
216
- returns: ['stock', 'price', 'variants'],
217
- resolver: { type: 'api', endpoints: [{ method: 'GET', path: '/products/{product}/availability' }] },
218
- privacy: { level: 'public' },
219
- },
220
- {
221
- id: 'get_order_status',
222
- name: 'Get order status',
223
- description: 'Retrieve the current status and tracking info for an order.',
224
- examples: [
225
- 'Where is my order?',
226
- 'Track order 1234',
227
- 'What is the status of my recent purchase?',
228
- ],
229
- params: [
230
- { name: 'order_id', description: 'Order ID', required: true, source: 'user_query' }
231
- ],
232
- returns: ['status', 'tracking', 'estimated_delivery'],
233
- resolver: { type: 'api', endpoints: [{ method: 'GET', path: '/orders/{order_id}' }] },
234
- privacy: { level: 'user_owned' },
235
- },
236
- {
237
- id: 'navigate_to_screen',
238
- name: 'Navigate to screen',
239
- description: 'Route the user to a specific page in the store.',
240
- examples: [
241
- 'Take me to cart',
242
- 'Open cart',
243
- 'Go to checkout',
244
- 'Navigate to account',
245
- 'Show homepage',
246
- ],
247
- params: [
248
- { name: 'destination', description: 'Target screen', required: true, source: 'user_query' }
249
- ],
250
- returns: ['deep_link'],
251
- resolver: { type: 'nav', destination: '/{destination}' },
252
- privacy: { level: 'public' },
253
- },
254
- ],
255
- }
256
-
257
- const manifest = generate(config)
258
-
259
- const queries = [
260
- 'Check availability for blue jacket',
261
- 'Track order 1234',
262
- 'Go to cart',
263
- 'Is the website down?',
264
- ]
265
-
266
- console.log(`${c.gray} App: ${c.reset}${c.bold}${config.app}${c.reset}`)
267
- console.log(`${c.gray} Capabilities: ${c.reset}${manifest.capabilities.length}`)
268
- console.log(`${c.gray} Mode: keyword matcher (no LLM required)\n${c.reset}`)
269
-
270
- let passed = 0
271
- let outOfScope = 0
272
-
273
- for (const query of queries) {
274
- const start = Date.now()
275
- const result = match(query, manifest)
276
- const duration = Date.now() - start
277
-
278
- if (result.capability) {
279
- passed++
280
- const resolverColor = result.capability.resolver.type === 'api' ? c.teal :
281
- result.capability.resolver.type === 'nav' ? c.teal : c.yellow
282
-
283
- console.log(` ${c.green}✓${c.reset} ${c.bold}"${query}"${c.reset}`)
284
- console.log(` ${c.gray}→ matched:${c.reset} ${resolverColor}${result.capability.id}${c.reset}`)
285
- console.log(` ${c.gray}→ intent:${c.reset} ${result.intent}`)
286
- console.log(` ${c.gray}→ confidence:${c.reset} ${result.confidence}%`)
287
-
288
- if (Object.keys(result.extractedParams).length > 0) {
289
- const params = Object.entries(result.extractedParams)
290
- .map(([k, v]) => `${k}=${v}`)
291
- .join(', ')
292
- console.log(` ${c.gray}→ params:${c.reset} ${params}`)
293
- }
294
-
295
- // Show what API call would be made
296
- if (result.capability.resolver.type === 'api') {
297
- const endpoint = result.capability.resolver.endpoints[0]
298
- let path = endpoint.path
299
- for (const [k, v] of Object.entries(result.extractedParams)) {
300
- if (v) path = path.replace(`{${k}}`, v)
301
- }
302
- console.log(` ${c.gray}→ api call:${c.reset} ${endpoint.method} ${config.baseUrl}${path}`)
303
- } else if (result.capability.resolver.type === 'nav') {
304
- let dest = result.capability.resolver.destination
305
- for (const [k, v] of Object.entries(result.extractedParams)) {
306
- if (v) dest = dest.replace(`{${k}}`, v)
307
- }
308
- console.log(` ${c.gray}→ nav target:${c.reset} ${dest}`)
309
- }
310
-
311
- console.log(` ${c.gray}→ time:${c.reset} ${duration}ms\n`)
312
- } else {
313
- outOfScope++
314
- console.log(` ${c.yellow}○${c.reset} ${c.bold}"${query}"${c.reset}`)
315
- console.log(` ${c.gray}→ out of scope — no capability handles this\n${c.reset}`)
316
- }
317
- }
318
-
319
- console.log(`${c.gray} ─────────────────────────────────────────${c.reset}`)
320
- console.log(` ${c.green}${passed} matched${c.reset} ${c.gray}·${c.reset} ${c.yellow}${outOfScope} out of scope${c.reset} ${c.gray}·${c.reset} ${manifest.capabilities.length} capabilities\n`)
321
- console.log(` ${c.gray}Try it on your own app:${c.reset}`)
322
- console.log(` ${c.teal}npx capman init${c.reset} ${c.gray}→ create your manifest${c.reset}`)
323
- console.log(` ${c.teal}npx capman generate${c.reset} ${c.gray}→ generate manifest.json${c.reset}\n`)
324
- }
325
-
326
- function cmdRun() {
327
- header()
328
- const query = args[1]
329
- const debug = flags.includes('--debug')
330
- const manifestPath = getFlag('--manifest') ?? 'manifest.json'
331
-
332
- if (!query) {
333
- log.error('Please provide a query. Example: node bin/capman.js run "show me articles"')
334
- process.exit(1)
335
- }
336
-
337
- const { readManifest, match } = requireSrc()
338
-
339
- let manifest
340
- try {
341
- manifest = readManifest(manifestPath)
342
- } catch (e) {
343
- log.error(e.message)
344
- process.exit(1)
345
- }
346
-
347
- log.info(`Query: "${query}"`)
348
- log.blank()
349
-
350
- const result = match(query, manifest)
351
-
352
- if (result.capability) {
353
- console.log(` ${c.green}✓${c.reset} Matched: ${c.bold}${result.capability.id}${c.reset}`)
354
- console.log(` Intent: ${result.intent}`)
355
- console.log(` Confidence: ${result.confidence}%`)
356
- console.log(` Resolver: ${result.capability.resolver.type}`)
357
-
358
- if (Object.keys(result.extractedParams).length > 0) {
359
- const params = Object.entries(result.extractedParams)
360
- .map(([k, v]) => `${k}=${v}`)
361
- .join(', ')
362
- console.log(` Params: ${params}`)
363
- }
364
-
365
- if (debug && result.candidates?.length) {
366
- log.blank()
367
- console.log(` ${c.gray}── All candidates:${c.reset}`)
368
- result.candidates
369
- .sort((a, b) => b.score - a.score)
370
- .forEach(c2 => {
371
- const marker = c2.matched ? c.green + '✓' : c.gray + '○'
372
- console.log(` ${marker}${c.reset} ${c2.capabilityId}: ${c2.score}%`)
373
- })
374
- }
375
- } else {
376
- console.log(` ${c.yellow}○${c.reset} OUT_OF_SCOPE — no capability matched`)
377
- console.log(` ${c.gray}${result.reasoning}${c.reset}`)
378
-
379
- if (debug && result.candidates?.length) {
380
- log.blank()
381
- console.log(` ${c.gray}── All candidates:${c.reset}`)
382
- result.candidates
383
- .sort((a, b) => b.score - a.score)
384
- .slice(0, 5)
385
- .forEach(c2 => {
386
- console.log(` ${c.gray}○ ${c2.capabilityId}: ${c2.score}%${c.reset}`)
387
- })
388
- }
389
- }
390
- console.log()
391
- }
392
-
393
- switch (command) {
394
- case 'init': cmdInit(); break
395
- case 'generate': cmdGenerate(); break
396
- case 'validate': cmdValidate(); break
397
- case 'inspect': cmdInspect(); break
398
- case 'demo': cmdDemo(); break
399
- case 'run': cmdRun(); break
400
- case undefined:
401
- case '--help':
402
- case '-h': cmdHelp(); break
403
- default:
404
- header()
405
- log.error(`Unknown command: ${command}`)
406
- console.log(` Run: node bin/capman.js --help\n`)
407
- process.exit(1)
408
- }
4
+ const { command, header, log, c } = require('./lib/shared')
5
+
6
+ ;(async () => {
7
+ switch (command) {
8
+ case 'init': require('./lib/cmd-init')(); break
9
+ case 'generate': await require('./lib/cmd-generate')(); break
10
+ case 'validate': require('./lib/cmd-validate')(); break
11
+ case 'inspect': require('./lib/cmd-inspect')(); break
12
+ case 'demo': require('./lib/cmd-demo')(); break
13
+ case 'run': require('./lib/cmd-run')(); break
14
+ case 'explain': await require('./lib/cmd-explain')(); break
15
+ case undefined:
16
+ case '--help':
17
+ case '-h': require('./lib/cmd-help')(); break
18
+ default:
19
+ header()
20
+ log.error(`Unknown command: ${command}`)
21
+ console.log(` Run: ${c.teal}capman --help${c.reset}\n`)
22
+ process.exit(1)
23
+ }
24
+ })()