@bodhi-ventures/aiocs 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,21 @@
1
+ name = "aiocs_docs_specialist"
2
+ description = "Development example specialist for local aiocs documentation search, drift checks, diffs, and health verification through a checkout-local MCP server."
3
+ model = "gpt-5.4-mini"
4
+ model_reasoning_effort = "high"
5
+ sandbox_mode = "read-only"
6
+ nickname_candidates = ["Index", "Ledger", "Compass"]
7
+ developer_instructions = """
8
+ Use aiocs as the first stop for local documentation work.
9
+ Prefer aiocs before live browsing when the requested docs may already exist in the local catalog.
10
+ Check source presence and freshness with source_list before assuming docs are missing or stale.
11
+ Default to search mode auto, switch to lexical for exact identifiers, prefer refresh_due for targeted freshness checks, and use batch when multiple aiocs tool calls are needed in one task.
12
+ If the parent agent explicitly asks for aiocs write operations and a source is missing but likely to be reused, add a spec under ~/.aiocs/sources, upsert it, then refresh only that source.
13
+ Avoid fetch all unless the parent agent explicitly asks for broad maintenance.
14
+ When returning results, include sourceId, snapshotId, and pageUrl when they materially improve traceability.
15
+ Do not edit aiocs source specs, catalog contents, or daemon configuration unless the parent agent explicitly asks for it.
16
+ If aiocs health is in doubt, run doctor before assuming the catalog is broken.
17
+ """
18
+
19
+ [mcp_servers.aiocs]
20
+ command = "pnpm"
21
+ args = ["--dir", "/absolute/path/to/aiocs", "dev:mcp"]
@@ -0,0 +1,524 @@
1
+ # CLI JSON Contract
2
+
3
+ `aiocs` exposes a single-document JSON envelope for every one-shot CLI command when the root `--json` flag is present.
4
+
5
+ ## One-shot command envelope
6
+
7
+ Successful commands write exactly one JSON object to stdout:
8
+
9
+ ```json
10
+ {
11
+ "ok": true,
12
+ "command": "search",
13
+ "data": {
14
+ "results": []
15
+ }
16
+ }
17
+ ```
18
+
19
+ Failed commands still write exactly one JSON object to stdout and exit with status `1`:
20
+
21
+ ```json
22
+ {
23
+ "ok": false,
24
+ "command": "show",
25
+ "error": {
26
+ "code": "CHUNK_NOT_FOUND",
27
+ "message": "Chunk 42 not found"
28
+ }
29
+ }
30
+ ```
31
+
32
+ Envelope fields:
33
+
34
+ - `ok`: `true` when the command executed and returned data, `false` when command execution failed
35
+ - `command`: stable command identifier such as `source.list`, `refresh.due`, `doctor`, or `init`
36
+ - `data`: command-specific payload on success
37
+ - `error.code`: stable machine-readable failure code
38
+ - `error.message`: stable human-readable error summary on failure
39
+ - `error.details`: optional extra machine-readable error context
40
+
41
+ ## Supported one-shot commands
42
+
43
+ All of these support the root-level `--json` flag:
44
+
45
+ - `version`
46
+ - `init`
47
+ - `doctor`
48
+ - `source upsert`
49
+ - `source list`
50
+ - `fetch`
51
+ - `canary`
52
+ - `refresh due`
53
+ - `snapshot list`
54
+ - `diff`
55
+ - `project link`
56
+ - `project unlink`
57
+ - `backup export`
58
+ - `backup import`
59
+ - `embeddings status`
60
+ - `embeddings backfill`
61
+ - `embeddings clear`
62
+ - `embeddings run`
63
+ - `search`
64
+ - `verify coverage`
65
+ - `show`
66
+
67
+ ## Command payloads
68
+
69
+ This section documents the stable top-level `data` payload per command.
70
+
71
+ ### `version`
72
+
73
+ ```json
74
+ {
75
+ "name": "@bodhi-ventures/aiocs",
76
+ "version": "0.1.0"
77
+ }
78
+ ```
79
+
80
+ ### `init`
81
+
82
+ ```json
83
+ {
84
+ "sourceSpecDir": "/absolute/path/to/aiocs/sources",
85
+ "fetched": false,
86
+ "initializedSources": [
87
+ {
88
+ "sourceId": "hyperliquid",
89
+ "specPath": "/absolute/path/to/aiocs/sources/hyperliquid.yaml",
90
+ "configHash": "sha256...",
91
+ "configChanged": false
92
+ }
93
+ ],
94
+ "removedSourceIds": [],
95
+ "fetchResults": []
96
+ }
97
+ ```
98
+
99
+ ### `doctor`
100
+
101
+ ```json
102
+ {
103
+ "summary": {
104
+ "status": "healthy",
105
+ "checkCount": 10,
106
+ "passCount": 10,
107
+ "warnCount": 0,
108
+ "failCount": 0
109
+ },
110
+ "checks": [
111
+ {
112
+ "id": "catalog",
113
+ "status": "pass",
114
+ "summary": "Catalog opened successfully at ~/.aiocs/data",
115
+ "details": {}
116
+ }
117
+ ]
118
+ }
119
+ ```
120
+
121
+ Check ids are currently:
122
+
123
+ - `catalog`
124
+ - `playwright`
125
+ - `daemon-config`
126
+ - `source-spec-dirs`
127
+ - `freshness`
128
+ - `daemon-heartbeat`
129
+ - `embedding-provider`
130
+ - `vector-store`
131
+ - `embeddings`
132
+ - `docker`
133
+
134
+ Summary status values:
135
+
136
+ - `healthy`: no warnings or failures
137
+ - `degraded`: warnings but no failures
138
+ - `unhealthy`: at least one failed check
139
+
140
+ ### `source.upsert`
141
+
142
+ ```json
143
+ {
144
+ "sourceId": "hyperliquid",
145
+ "configHash": "sha256...",
146
+ "specPath": "/absolute/path/to/spec.yaml"
147
+ }
148
+ ```
149
+
150
+ ### `source.list`
151
+
152
+ ```json
153
+ {
154
+ "sources": [
155
+ {
156
+ "id": "hyperliquid",
157
+ "label": "Hyperliquid",
158
+ "nextDueAt": "2026-03-26T12:00:00.000Z",
159
+ "nextCanaryDueAt": "2026-03-26T06:00:00.000Z",
160
+ "lastCheckedAt": "2026-03-26T10:00:00.000Z",
161
+ "lastSuccessfulSnapshotAt": "2026-03-26T10:00:00.000Z",
162
+ "lastSuccessfulSnapshotId": "snp_...",
163
+ "lastCanaryCheckedAt": "2026-03-26T08:00:00.000Z",
164
+ "lastSuccessfulCanaryAt": "2026-03-26T08:00:00.000Z",
165
+ "lastCanaryStatus": "pass"
166
+ }
167
+ ]
168
+ }
169
+ ```
170
+
171
+ ### `fetch` and `refresh.due`
172
+
173
+ ```json
174
+ {
175
+ "results": [
176
+ {
177
+ "sourceId": "hyperliquid",
178
+ "snapshotId": "snp_...",
179
+ "pageCount": 139,
180
+ "reused": false
181
+ }
182
+ ]
183
+ }
184
+ ```
185
+
186
+ ### `canary`
187
+
188
+ ```json
189
+ {
190
+ "results": [
191
+ {
192
+ "sourceId": "hyperliquid",
193
+ "status": "pass",
194
+ "checkedAt": "2026-03-26T10:00:00.000Z",
195
+ "summary": {
196
+ "checkCount": 1,
197
+ "passCount": 1,
198
+ "failCount": 0
199
+ },
200
+ "checks": [
201
+ {
202
+ "url": "https://example.dev/docs/start",
203
+ "status": "pass",
204
+ "title": "Docs Start",
205
+ "markdownLength": 120
206
+ }
207
+ ]
208
+ }
209
+ ]
210
+ }
211
+ ```
212
+
213
+ ### `snapshot.list`
214
+
215
+ ```json
216
+ {
217
+ "sourceId": "hyperliquid",
218
+ "snapshots": [
219
+ {
220
+ "snapshotId": "snp_...",
221
+ "sourceId": "hyperliquid",
222
+ "detectedVersion": null,
223
+ "createdAt": "2026-03-26T10:00:00.000Z",
224
+ "pageCount": 139
225
+ }
226
+ ]
227
+ }
228
+ ```
229
+
230
+ ### `project.link` and `project.unlink`
231
+
232
+ ```json
233
+ {
234
+ "projectPath": "/absolute/path/to/project",
235
+ "sourceIds": ["hyperliquid", "lighter"]
236
+ }
237
+ ```
238
+
239
+ ### `diff`
240
+
241
+ ```json
242
+ {
243
+ "sourceId": "hyperliquid",
244
+ "fromSnapshotId": "snp_old",
245
+ "toSnapshotId": "snp_new",
246
+ "summary": {
247
+ "addedPageCount": 1,
248
+ "removedPageCount": 1,
249
+ "changedPageCount": 2,
250
+ "unchangedPageCount": 98
251
+ },
252
+ "addedPages": [
253
+ {
254
+ "url": "https://example.dev/docs/new-page",
255
+ "title": "New page"
256
+ }
257
+ ],
258
+ "removedPages": [],
259
+ "changedPages": [
260
+ {
261
+ "url": "https://example.dev/docs/start",
262
+ "beforeTitle": "Start",
263
+ "afterTitle": "Start",
264
+ "lineSummary": {
265
+ "addedLineCount": 3,
266
+ "removedLineCount": 2
267
+ }
268
+ }
269
+ ]
270
+ }
271
+ ```
272
+
273
+ ### `search`
274
+
275
+ ```json
276
+ {
277
+ "query": "maker flow",
278
+ "total": 42,
279
+ "limit": 20,
280
+ "offset": 0,
281
+ "hasMore": true,
282
+ "modeRequested": "auto",
283
+ "modeUsed": "hybrid",
284
+ "results": [
285
+ {
286
+ "chunkId": 42,
287
+ "sourceId": "hyperliquid",
288
+ "snapshotId": "snp_...",
289
+ "pageUrl": "https://example.dev/docs/maker-flow",
290
+ "pageTitle": "Maker flow",
291
+ "sectionTitle": "Order lifecycle",
292
+ "markdown": "# Order lifecycle\n...",
293
+ "score": 0.036,
294
+ "signals": ["lexical", "vector"]
295
+ }
296
+ ]
297
+ }
298
+ ```
299
+
300
+ `limit` defaults to `20`. `offset` defaults to `0`.
301
+
302
+ `modeRequested` is the requested search mode (`auto`, `lexical`, `hybrid`, `semantic`).
303
+ `modeUsed` is the actual executed mode after fallbacks. In `auto`, `aiocs` can degrade back to lexical if the vector layer is unavailable or incomplete for the requested scope.
304
+
305
+ ### `embeddings.status`
306
+
307
+ ```json
308
+ {
309
+ "queue": {
310
+ "pendingJobs": 0,
311
+ "runningJobs": 0,
312
+ "failedJobs": 0
313
+ },
314
+ "sources": [
315
+ {
316
+ "sourceId": "hyperliquid",
317
+ "snapshotId": "snp_...",
318
+ "totalChunks": 420,
319
+ "indexedChunks": 420,
320
+ "pendingChunks": 0,
321
+ "failedChunks": 0,
322
+ "staleChunks": 0,
323
+ "coverageRatio": 1
324
+ }
325
+ ]
326
+ }
327
+ ```
328
+
329
+ ### `embeddings.backfill`
330
+
331
+ ```json
332
+ {
333
+ "queuedJobs": 5
334
+ }
335
+ ```
336
+
337
+ ### `embeddings.clear`
338
+
339
+ ```json
340
+ {
341
+ "clearedSources": ["hyperliquid", "lighter"]
342
+ }
343
+ ```
344
+
345
+ ### `embeddings.run`
346
+
347
+ ```json
348
+ {
349
+ "processedJobs": 2,
350
+ "succeededJobs": [
351
+ {
352
+ "sourceId": "hyperliquid",
353
+ "snapshotId": "snp_...",
354
+ "chunkCount": 420
355
+ }
356
+ ],
357
+ "failedJobs": []
358
+ }
359
+ ```
360
+
361
+ ### `verify.coverage`
362
+
363
+ ```json
364
+ {
365
+ "sourceId": "hyperliquid",
366
+ "snapshotId": "snp_...",
367
+ "complete": false,
368
+ "summary": {
369
+ "fileCount": 1,
370
+ "headingCount": 100,
371
+ "matchedHeadingCount": 99,
372
+ "missingHeadingCount": 1,
373
+ "matchCounts": {
374
+ "pageTitle": 80,
375
+ "sectionTitle": 15,
376
+ "body": 4
377
+ }
378
+ },
379
+ "files": [
380
+ {
381
+ "referenceFile": "/absolute/path/to/reference.md",
382
+ "headingCount": 100,
383
+ "matchedHeadingCount": 99,
384
+ "missingHeadingCount": 1,
385
+ "missingHeadings": ["Missing Heading"],
386
+ "matchCounts": {
387
+ "pageTitle": 80,
388
+ "sectionTitle": 15,
389
+ "body": 4
390
+ }
391
+ }
392
+ ]
393
+ }
394
+ ```
395
+
396
+ ### `backup.export`
397
+
398
+ ```json
399
+ {
400
+ "outputDir": "/absolute/path/to/backup",
401
+ "manifestPath": "/absolute/path/to/backup/manifest.json",
402
+ "manifest": {
403
+ "formatVersion": 1,
404
+ "createdAt": "2026-03-26T10:00:00.000Z",
405
+ "packageVersion": "0.1.0",
406
+ "entries": [
407
+ {
408
+ "relativePath": "data/catalog.sqlite",
409
+ "type": "file",
410
+ "size": 32768
411
+ }
412
+ ]
413
+ }
414
+ }
415
+ ```
416
+
417
+ ### `backup.import`
418
+
419
+ ```json
420
+ {
421
+ "inputDir": "/absolute/path/to/backup",
422
+ "dataDir": "/Users/example/.aiocs/data",
423
+ "configDir": "/Users/example/.aiocs/config",
424
+ "manifest": {
425
+ "formatVersion": 1,
426
+ "createdAt": "2026-03-26T10:00:00.000Z",
427
+ "packageVersion": "0.1.0",
428
+ "entries": []
429
+ }
430
+ }
431
+ ```
432
+
433
+ ### `show`
434
+
435
+ ```json
436
+ {
437
+ "chunk": {
438
+ "chunkId": 42,
439
+ "sourceId": "hyperliquid",
440
+ "snapshotId": "snp_...",
441
+ "pageUrl": "https://example.dev/docs/maker-flow",
442
+ "pageTitle": "Maker flow",
443
+ "sectionTitle": "Order lifecycle",
444
+ "markdown": "# Order lifecycle\n..."
445
+ }
446
+ }
447
+ ```
448
+
449
+ ## Daemon event stream
450
+
451
+ `docs daemon --json` is intentionally different because the process is long-running. It emits newline-delimited JSON events to stdout rather than a single envelope.
452
+
453
+ Current event types:
454
+
455
+ - `daemon.started`
456
+ - `daemon.cycle.started`
457
+ - `daemon.cycle.completed`
458
+ - `daemon.stopped`
459
+
460
+ Example:
461
+
462
+ ```json
463
+ {"type":"daemon.started","intervalMinutes":60,"fetchOnStart":true,"sourceSpecDirs":["/app/sources"]}
464
+ {"type":"daemon.cycle.started","reason":"startup","startedAt":"2026-03-26T00:00:00.000Z"}
465
+ {"type":"daemon.cycle.completed","reason":"startup","result":{"startedAt":"2026-03-26T00:00:00.000Z","finishedAt":"2026-03-26T00:00:10.000Z","dueSourceIds":[],"bootstrapped":{"processedSpecCount":5,"removedSourceIds":[],"sources":[]},"refreshed":[],"failed":[],"embedded":[],"embeddingFailed":[]}}
466
+ {"type":"daemon.stopped"}
467
+ ```
468
+
469
+ ## Error codes
470
+
471
+ Current stable CLI error codes include:
472
+
473
+ - `INVALID_ARGUMENT`
474
+ - `SOURCE_NOT_FOUND`
475
+ - `SNAPSHOT_NOT_FOUND`
476
+ - `NO_PAGES_FETCHED`
477
+ - `NO_PROJECT_SCOPE`
478
+ - `CHUNK_NOT_FOUND`
479
+ - `REFERENCE_FILE_NOT_FOUND`
480
+ - `INVALID_REFERENCE_FILE`
481
+ - `EMBEDDING_CONFIG_INVALID`
482
+ - `EMBEDDING_PROVIDER_UNAVAILABLE`
483
+ - `VECTOR_STORE_UNAVAILABLE`
484
+ - `EMBEDDING_JOB_NOT_FOUND`
485
+ - `INTERNAL_ERROR`
486
+
487
+ ## MCP relationship
488
+
489
+ The `aiocs-mcp` server uses the same underlying payloads, but wraps them in a structured MCP envelope:
490
+
491
+ Successful MCP tool results:
492
+
493
+ ```json
494
+ {
495
+ "ok": true,
496
+ "data": {
497
+ "name": "@bodhi-ventures/aiocs",
498
+ "version": "0.1.0"
499
+ }
500
+ }
501
+ ```
502
+
503
+ Failed MCP tool results:
504
+
505
+ ```json
506
+ {
507
+ "ok": false,
508
+ "error": {
509
+ "code": "CHUNK_NOT_FOUND",
510
+ "message": "Chunk 42 not found"
511
+ }
512
+ }
513
+ ```
514
+
515
+ The MCP `search` tool supports the same `limit` and `offset` fields as the CLI. The MCP server also exposes:
516
+
517
+ - `embeddings_status`
518
+ - `embeddings_backfill`
519
+ - `embeddings_clear`
520
+ - `embeddings_run`
521
+ - `verify_coverage`
522
+ - `batch`
523
+
524
+ `batch` returns one result object per requested operation, each with its own `ok`, `data`, or `error` fields.
@@ -0,0 +1,135 @@
1
+ # Tag-Driven Release Pipeline Design
2
+
3
+ ## Summary
4
+
5
+ `aiocs` should release as the public scoped npm package `@bodhi-ventures/aiocs` through a stable, tag-driven GitHub Actions workflow. The repository remains the source of truth for versioning. Releases are created only from pushed stable tags of the form `vX.Y.Z`.
6
+
7
+ The workflow must never mutate git state. It should validate the tag against `package.json`, run the full release verification stack, publish publicly to npm with provenance, and create a GitHub release from the same tag.
8
+
9
+ ## Goals
10
+
11
+ - publish `aiocs` publicly under `@bodhi-ventures/aiocs`
12
+ - eliminate workflow-managed version bumps, commits, and tag creation
13
+ - remove improvised git author configuration from the release workflow
14
+ - make the release contract deterministic and easy to reason about
15
+ - keep CI and release validation aligned with the actual shipped package surface
16
+
17
+ ## Non-Goals
18
+
19
+ - prerelease publishing
20
+ - automatic releases from `main`
21
+ - workspace/multi-package publishing
22
+ - alternative binary distribution outside npm
23
+
24
+ ## Current State
25
+
26
+ - `package.json` still uses the unscoped package name `aiocs`
27
+ - the current release workflow is `workflow_dispatch`-driven
28
+ - the workflow edits `package.json`, creates commits and tags, and configures a bot git identity
29
+ - release automation is more complex than necessary and mixes version mutation with publishing
30
+
31
+ ## Chosen Design
32
+
33
+ ### Package Identity
34
+
35
+ - rename the npm package to `@bodhi-ventures/aiocs`
36
+ - keep the package public
37
+ - keep CLI command names unchanged:
38
+ - `docs`
39
+ - `aiocs-mcp`
40
+ - add explicit `publishConfig`:
41
+ - `access: public`
42
+ - `provenance: true`
43
+
44
+ ### Release Trigger
45
+
46
+ - release workflow triggers only on pushed stable semver tags matching `v*.*.*`
47
+ - the tag version must match `package.json.version` exactly
48
+ - only stable releases are supported; prerelease tags are rejected
49
+
50
+ ### Release Workflow Behavior
51
+
52
+ - check out the tagged revision
53
+ - install dependencies and release prerequisites
54
+ - fail fast unless all of these are true:
55
+ - tag matches `vX.Y.Z`
56
+ - `package.json.version === X.Y.Z`
57
+ - `package.json.name === @bodhi-ventures/aiocs`
58
+ - run full release validation:
59
+ - `pnpm lint`
60
+ - `pnpm test`
61
+ - `pnpm build`
62
+ - `npm pack --dry-run`
63
+ - built CLI smoke, at minimum `node dist/cli.js --version`
64
+ - publish to npm with the existing `NPM_TOKEN` org secret
65
+ - create a GitHub release for the pushed tag
66
+ - if a GitHub release already exists for that tag, do not recreate it
67
+
68
+ ### Rerun and Partial-Failure Policy
69
+
70
+ Tag releases must be safely rerunnable.
71
+
72
+ - `workflow_dispatch` is removed entirely; tag pushes are the only release trigger
73
+ - if a rerun sees that `@bodhi-ventures/aiocs@X.Y.Z` is already published on npm, it must skip `npm publish` instead of failing
74
+ - if a rerun sees that the GitHub release for `vX.Y.Z` already exists, it must skip release creation instead of failing
75
+ - validation steps always run on every attempt, even when publish/release steps are skipped
76
+ - if npm already contains `@bodhi-ventures/aiocs@X.Y.Z` but the checked-out tag does not match `package.json.version === X.Y.Z` or `package.json.name === @bodhi-ventures/aiocs`, the workflow must fail fast
77
+ - the workflow should treat npm and GitHub release publication as independently idempotent so a partial success can be completed by rerunning the same tag job
78
+
79
+ ### Git Behavior
80
+
81
+ - the workflow never edits files
82
+ - the workflow never commits
83
+ - the workflow never creates tags
84
+ - the workflow never configures a synthetic git author
85
+
86
+ Version bumps happen in normal development flow:
87
+
88
+ 1. update `package.json.version`
89
+ 2. commit the version bump
90
+ 3. create tag `vX.Y.Z`
91
+ 4. push commit and tag
92
+
93
+ ## CI Alignment
94
+
95
+ CI remains the pre-release gate and should stay close to the release workflow:
96
+
97
+ - install dependencies
98
+ - install Playwright Chromium
99
+ - run lint, tests, build, and `npm pack --dry-run`
100
+ - validate Docker image and compose config
101
+ - smoke test the packaged CLI surface
102
+
103
+ CI should also assert package metadata consistency where useful, especially the scoped package name.
104
+
105
+ ## Documentation Changes
106
+
107
+ Update repository docs to reflect the scoped public package and release process:
108
+
109
+ - install command becomes `npm install -g @bodhi-ventures/aiocs`
110
+ - add a short release section to the README describing the tag-based flow
111
+ - keep the CLI-facing command examples unchanged
112
+
113
+ ## Risks
114
+
115
+ - npm org publishing can still fail if org/package permissions are not configured correctly on npmjs
116
+ - the first release is the highest-risk run because the scoped package has not been proven yet
117
+ - GitHub Actions can only validate the workflow structure locally; final proof requires one live tag release
118
+
119
+ ## Acceptance Criteria
120
+
121
+ - `package.json` is updated to `@bodhi-ventures/aiocs`
122
+ - release workflow triggers on stable tags only
123
+ - release workflow does not mutate git state
124
+ - release workflow validates tag/version/name consistency before publishing
125
+ - README/install docs reference the scoped package name
126
+ - local verification passes on the updated tree:
127
+ - lint
128
+ - tests
129
+ - build
130
+ - `npm pack --dry-run`
131
+ - the first real release can be executed by bumping version, pushing a `vX.Y.Z` tag, and observing npm + GitHub release creation
132
+
133
+ ## Follow-Up
134
+
135
+ After implementation, do one real tagged stable release to prove the pipeline end to end.