capman 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.
@@ -0,0 +1,108 @@
1
+ # Contributing to capman
2
+
3
+ First off — thank you for taking the time to contribute.
4
+ capman is an open source project and welcomes contributions of all kinds.
5
+
6
+ ---
7
+
8
+ ## What we're building
9
+
10
+ capman is a developer tool that lets AI agents interact with applications
11
+ efficiently — without navigating the UI. If you're here, you probably
12
+ have an idea to make that better. Let's hear it.
13
+
14
+ ---
15
+
16
+ ## Ways to contribute
17
+
18
+ - **Bug reports** — something broke? Open an issue with steps to reproduce
19
+ - **Feature suggestions** — have an idea? Open an issue and describe it
20
+ - **Code contributions** — fix a bug or build a feature via pull request
21
+ - **New test configs** — tested capman against a real app? Add it to test/
22
+ - **Documentation** — spotted something unclear in the README? Fix it
23
+
24
+ ---
25
+
26
+ ## Getting started locally
27
+
28
+ ```bash
29
+ # Clone the repo
30
+ git clone https://github.com/your-username/capman.git
31
+ cd capman
32
+
33
+ # Install dependencies
34
+ npm install
35
+
36
+ # Build
37
+ npm run build
38
+
39
+ # Run the example
40
+ npm run example
41
+ ```
42
+
43
+ ---
44
+
45
+ ## Project structure
46
+
47
+ ```
48
+ src/
49
+ types.ts — all TypeScript types (the contract)
50
+ generator.ts — generate(), validate(), loadConfig()
51
+ matcher.ts — match() and matchWithLLM()
52
+ resolver.ts — resolve() for API, nav, and hybrid
53
+ index.ts — public SDK entry point + ask()
54
+ bin/
55
+ capman.js — CLI (init, generate, validate, inspect)
56
+ examples/
57
+ basic.ts — simple runnable example
58
+ test/
59
+ conduit.config.js — real world test config (Conduit app)
60
+ test-conduit.ts — keyword matcher tests
61
+ test-conduit-llm.ts — LLM matcher tests
62
+ ```
63
+
64
+ ---
65
+
66
+ ## Before submitting a pull request
67
+
68
+ 1. Run `npm run build` — make sure it compiles clean
69
+ 2. Run `npx ts-node examples/basic.ts` — make sure the example works
70
+ 3. If you changed matching logic, run `npx ts-node test/test-conduit.ts`
71
+ 4. Keep pull requests focused — one thing per PR
72
+ 5. Write clear commit messages — say what changed and why
73
+
74
+ ---
75
+
76
+ ## Adding a new test config
77
+
78
+ If you've tested capman against a real app, we'd love to include it.
79
+
80
+ 1. Create `test/your-app.config.js`
81
+ 2. Create `test/test-your-app.ts`
82
+ 3. Open a pull request with both files
83
+ 4. Include your test results in the PR description
84
+
85
+ ---
86
+
87
+ ## Reporting a bug
88
+
89
+ Open an issue and include:
90
+ - What you ran
91
+ - What you expected
92
+ - What actually happened
93
+ - Your Node.js version (`node --version`)
94
+
95
+ ---
96
+
97
+ ## Code style
98
+
99
+ - TypeScript everywhere in `src/`
100
+ - No external runtime dependencies in core src/
101
+ - Keep functions small and focused
102
+ - Add a comment if something isn't obvious
103
+
104
+ ---
105
+
106
+ ## Questions?
107
+
108
+ Open an issue — no question is too small.
package/README.md ADDED
@@ -0,0 +1,208 @@
1
+ # Capman — Capability Manifest Engine
2
+
3
+ Let AI agents interact with your app **without navigating the UI**.
4
+
5
+ Instead of an AI blindly clicking through screens to find information,
6
+ capman lets your app declare what it can do — and the AI uses that map
7
+ to get answers directly.
8
+
9
+ ---
10
+
11
+ ## The Problem
12
+
13
+ When an AI agent needs to answer "are there available seats for Friday?",
14
+ today it navigates your entire app like a tourist with no map:
15
+
16
+ ```
17
+ AI clicks → Home → Explore → Events → Category → Detail → Availability
18
+ ```
19
+
20
+ That's slow, wasteful, and exposes parts of your app the AI shouldn't see.
21
+
22
+ ## The Solution
23
+
24
+ Your app publishes a **capability manifest** — a machine-readable list of
25
+ everything it can do, what API to call, and what data scope is allowed.
26
+
27
+ The AI reads the manifest and goes directly to the answer.
28
+
29
+ ```
30
+ User query → match capability → resolve via API or nav → done
31
+ ```
32
+
33
+ ---
34
+
35
+ ## Install
36
+
37
+ ```bash
38
+ npm install capman
39
+ ```
40
+
41
+ ---
42
+
43
+ ## Quick Start
44
+
45
+ **1. Create a config file**
46
+
47
+ ```bash
48
+ npx capman init
49
+ ```
50
+
51
+ This creates a `capman.config.js` in your project. Edit it to define
52
+ your app's capabilities.
53
+
54
+ **2. Generate the manifest**
55
+
56
+ ```bash
57
+ npx capman generate
58
+ ```
59
+
60
+ This reads your config and outputs a `manifest.json`.
61
+
62
+ **3. Use the SDK in your AI agent**
63
+
64
+ ```typescript
65
+ import { match, resolve, readManifest } from 'capman'
66
+
67
+ const manifest = readManifest()
68
+
69
+ // Match a user query to a capability
70
+ const matchResult = match("show me my account details", manifest)
71
+
72
+ // Resolve it
73
+ const result = await resolve(matchResult, {}, {
74
+ baseUrl: 'https://api.your-app.com'
75
+ })
76
+
77
+ console.log(result.apiCalls) // [{ method: 'GET', url: '...' }]
78
+ console.log(result.navTarget) // '/dashboard/profile'
79
+ ```
80
+
81
+ ---
82
+
83
+ ## CLI Commands
84
+
85
+ | Command | What it does |
86
+ |---|---|
87
+ | `capman init` | Create a starter `capman.config.js` |
88
+ | `capman generate` | Generate `manifest.json` from config |
89
+ | `capman validate` | Validate your manifest for errors |
90
+ | `capman inspect` | Print all capabilities in the manifest |
91
+
92
+ ---
93
+
94
+ ## SDK Reference
95
+
96
+ ### `match(query, manifest)`
97
+ Matches a user query to the best capability using keyword scoring.
98
+ Returns a `MatchResult` with the capability, confidence score, and intent.
99
+
100
+ ### `matchWithLLM(query, manifest, { llm })`
101
+ Same as `match()` but uses an LLM for higher accuracy on ambiguous queries.
102
+ Pass in any LLM function — works with Anthropic, OpenAI, or any local model.
103
+
104
+ ```typescript
105
+ const result = await matchWithLLM("find me something", manifest, {
106
+ llm: async (prompt) => {
107
+ const res = await anthropic.messages.create({
108
+ model: 'claude-sonnet-4-20250514',
109
+ max_tokens: 500,
110
+ messages: [{ role: 'user', content: prompt }]
111
+ })
112
+ return res.content[0].text
113
+ }
114
+ })
115
+ ```
116
+
117
+ ### `resolve(matchResult, params, options)`
118
+ Executes a matched capability via API call, navigation, or both.
119
+
120
+ ### `ask(query, manifest, options)`
121
+ Convenience function — match + resolve in one call.
122
+
123
+ ```typescript
124
+ const { match, resolution } = await ask("go to settings", manifest)
125
+ ```
126
+
127
+ ---
128
+
129
+ ## Capability Config
130
+
131
+ Each capability in your `capman.config.js` looks like this:
132
+
133
+ ```javascript
134
+ {
135
+ id: 'get_resource',
136
+ name: 'Get a resource',
137
+ description: 'Fetch a resource by ID or name.', // used for matching
138
+ examples: [ // improves accuracy
139
+ 'Show me resource details',
140
+ 'Find resource by ID',
141
+ ],
142
+ params: [
143
+ {
144
+ name: 'resource_id',
145
+ description: 'The resource ID',
146
+ required: true,
147
+ source: 'user_query', // or 'session', 'context', 'static'
148
+ }
149
+ ],
150
+ returns: ['resource', 'metadata'],
151
+ resolver: {
152
+ type: 'api', // 'api', 'nav', or 'hybrid'
153
+ endpoints: [
154
+ { method: 'GET', path: '/resources/{resource_id}' }
155
+ ],
156
+ },
157
+ privacy: {
158
+ level: 'public', // 'public', 'user_owned', or 'admin'
159
+ note: 'No auth required'
160
+ }
161
+ }
162
+ ```
163
+
164
+ ---
165
+
166
+ ## Privacy Scopes
167
+
168
+ | Level | Meaning |
169
+ |---|---|
170
+ | `public` | No auth required |
171
+ | `user_owned` | Requires auth, scoped to current user only |
172
+ | `admin` | Restricted to admin roles |
173
+
174
+ Privacy scope is declared **per capability** — the AI is scoped to only
175
+ what each capability allows, before resolution happens.
176
+
177
+ ---
178
+
179
+ ## Resolver Types
180
+
181
+ | Type | When to use |
182
+ |---|---|
183
+ | `api` | Answer lives in a backend API call |
184
+ | `nav` | User needs to be routed to a screen |
185
+ | `hybrid` | Both — fetch data AND navigate |
186
+
187
+ ---
188
+
189
+ ## Honest Limits
190
+
191
+ **Works well:**
192
+ - Structured data retrieval via APIs
193
+ - Navigating to known app screens
194
+ - Multi-endpoint aggregation
195
+ - Privacy scoping per capability
196
+ - Auto-updating on deploy
197
+
198
+ **Current limits:**
199
+ - Real-time infra status (is the server down?)
200
+ - UI-only state with no API backing
201
+ - Cross-app orchestration
202
+ - Very ambiguous queries without LLM matcher
203
+
204
+ ---
205
+
206
+ ## License
207
+
208
+ MIT
package/bin/capman.js ADDED
@@ -0,0 +1,192 @@
1
+ #!/usr/bin/env node
2
+ 'use strict'
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
+ console.log()
36
+ console.log(`${c.bold}${c.teal} capman${c.reset} ${c.gray}v0.1.0 — Capability Manifest Engine${c.reset}`)
37
+ console.log(`${c.gray} ─────────────────────────────────────────${c.reset}`)
38
+ console.log()
39
+ }
40
+
41
+ function requireSrc() {
42
+ const distPath = path.join(__dirname, '..', 'dist', 'index.js')
43
+ if (fs.existsSync(distPath)) return require(distPath)
44
+
45
+ try {
46
+ require('ts-node/register')
47
+ return require(path.join(__dirname, '..', 'src', 'index.ts'))
48
+ } catch {
49
+ log.error('Cannot find dist/. Run: npx tsc')
50
+ process.exit(1)
51
+ }
52
+ }
53
+
54
+ function cmdHelp() {
55
+ header()
56
+ console.log(`${c.bold} Usage:${c.reset} node bin/capman.js <command>`)
57
+ console.log()
58
+ console.log(`${c.bold} Commands:${c.reset}`)
59
+ console.log(` ${c.teal}init${c.reset} Create a starter capman.config.js`)
60
+ console.log(` ${c.teal}generate${c.reset} Generate manifest.json from config`)
61
+ console.log(` ${c.teal}validate${c.reset} Validate an existing manifest.json`)
62
+ console.log(` ${c.teal}inspect${c.reset} Print all capabilities in manifest`)
63
+ console.log()
64
+ console.log(`${c.bold} Options:${c.reset}`)
65
+ console.log(` ${c.gray}--config Path to config file (default: capman.config.js)${c.reset}`)
66
+ console.log(` ${c.gray}--out Output path (default: manifest.json)${c.reset}`)
67
+ console.log(` ${c.gray}--manifest Manifest to read (default: manifest.json)${c.reset}`)
68
+ console.log()
69
+ }
70
+
71
+ function cmdInit() {
72
+ header()
73
+ const outPath = path.resolve(process.cwd(), 'capman.config.js')
74
+ if (fs.existsSync(outPath)) {
75
+ log.warn('capman.config.js already exists — not overwriting.')
76
+ process.exit(0)
77
+ }
78
+ const { generateStarterConfig } = requireSrc()
79
+ fs.writeFileSync(outPath, generateStarterConfig())
80
+ log.success(`Created ${c.bold}capman.config.js${c.reset}`)
81
+ log.info(`Edit it with your app's capabilities, then run:`)
82
+ console.log(`\n node bin/capman.js generate\n`)
83
+ }
84
+
85
+ function cmdGenerate() {
86
+ header()
87
+ const { loadConfig, generate, writeManifest, validate } = requireSrc()
88
+
89
+ const configPath = getFlag('--config')
90
+ const outPath = getFlag('--out') ?? 'manifest.json'
91
+
92
+ log.info('Loading config...')
93
+ let config
94
+ try {
95
+ config = loadConfig(configPath)
96
+ } catch (e) {
97
+ log.error(e.message)
98
+ process.exit(1)
99
+ }
100
+
101
+ log.info(`Generating manifest for ${c.bold}${config.app}${c.reset}...`)
102
+ const manifest = generate(config)
103
+ const result = validate(manifest)
104
+
105
+ for (const w of result.warnings) log.warn(w)
106
+ for (const e of result.errors) log.error(e)
107
+
108
+ if (!result.valid) {
109
+ log.error('Manifest has errors — fix them before writing.')
110
+ process.exit(1)
111
+ }
112
+
113
+ const written = writeManifest(manifest, outPath)
114
+ log.blank()
115
+ log.success(`Manifest written to ${c.bold}${written}${c.reset}`)
116
+ log.info(`${manifest.capabilities.length} capabilities registered`)
117
+ console.log()
118
+ }
119
+
120
+ function cmdValidate() {
121
+ header()
122
+ const { readManifest, validate } = requireSrc()
123
+
124
+ const manifestPath = getFlag('--manifest') ?? 'manifest.json'
125
+ let manifest
126
+ try {
127
+ manifest = readManifest(manifestPath)
128
+ } catch (e) {
129
+ log.error(e.message)
130
+ process.exit(1)
131
+ }
132
+
133
+ log.info(`Validating ${c.bold}${manifestPath}${c.reset}...`)
134
+ const result = validate(manifest)
135
+ log.blank()
136
+
137
+ for (const w of result.warnings) log.warn(w)
138
+ for (const e of result.errors) log.error(e)
139
+
140
+ if (result.valid) {
141
+ log.success(`${manifest.capabilities.length} capabilities — all valid`)
142
+ } else {
143
+ log.error(`${result.errors.length} error(s) found.`)
144
+ process.exit(1)
145
+ }
146
+ console.log()
147
+ }
148
+
149
+ function cmdInspect() {
150
+ header()
151
+ const { readManifest } = requireSrc()
152
+
153
+ const manifestPath = getFlag('--manifest') ?? 'manifest.json'
154
+ let manifest
155
+ try {
156
+ manifest = readManifest(manifestPath)
157
+ } catch (e) {
158
+ log.error(e.message)
159
+ process.exit(1)
160
+ }
161
+
162
+ console.log(`${c.bold} App:${c.reset} ${manifest.app}`)
163
+ console.log(`${c.bold} Generated:${c.reset} ${manifest.generatedAt}`)
164
+ console.log(`${c.bold} Capabilities:${c.reset} ${manifest.capabilities.length}`)
165
+ console.log()
166
+
167
+ for (const cap of manifest.capabilities) {
168
+ const col = cap.resolver.type === 'api' ? c.teal : cap.resolver.type === 'nav' ? c.teal : c.yellow
169
+ console.log(` ${c.bold}${cap.name}${c.reset} ${col}[${cap.resolver.type}]${c.reset} ${c.gray}${cap.privacy.level}${c.reset}`)
170
+ console.log(` ${c.gray}id: ${cap.id}${c.reset}`)
171
+ console.log(` ${cap.description}`)
172
+ if (cap.examples?.length) {
173
+ console.log(` ${c.gray}e.g. "${cap.examples[0]}"${c.reset}`)
174
+ }
175
+ console.log()
176
+ }
177
+ }
178
+
179
+ switch (command) {
180
+ case 'init': cmdInit(); break
181
+ case 'generate': cmdGenerate(); break
182
+ case 'validate': cmdValidate(); break
183
+ case 'inspect': cmdInspect(); break
184
+ case undefined:
185
+ case '--help':
186
+ case '-h': cmdHelp(); break
187
+ default:
188
+ header()
189
+ log.error(`Unknown command: ${command}`)
190
+ console.log(` Run: node bin/capman.js --help\n`)
191
+ process.exit(1)
192
+ }
@@ -0,0 +1,8 @@
1
+ import type { CapmanConfig, Manifest, ValidationResult } from './types';
2
+ export declare function generate(config: CapmanConfig): Manifest;
3
+ export declare function loadConfig(configPath?: string): CapmanConfig;
4
+ export declare function writeManifest(manifest: Manifest, outputPath?: string): string;
5
+ export declare function readManifest(manifestPath?: string): Manifest;
6
+ export declare function validate(manifest: Manifest): ValidationResult;
7
+ export declare function generateStarterConfig(): string;
8
+ //# sourceMappingURL=generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../src/generator.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAIvE,wBAAgB,QAAQ,CAAC,MAAM,EAAE,YAAY,GAAG,QAAQ,CAOvD;AAED,wBAAgB,UAAU,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,YAAY,CAsD5D;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,QAAQ,EAAE,UAAU,SAAkB,GAAG,MAAM,CAItF;AAED,wBAAgB,YAAY,CAAC,YAAY,SAAkB,GAAG,QAAQ,CAgBrE;AAED,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,QAAQ,GAAG,gBAAgB,CA4B7D;AACD,wBAAgB,qBAAqB,IAAI,MAAM,CAoF9C"}