@neuralis/brain-core 0.2.1 → 0.3.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.
Files changed (103) hide show
  1. package/README.md +8 -3
  2. package/app/filesystem/api/README.md +1 -1
  3. package/app/filesystem/api/index.ts +24 -24
  4. package/app/filesystem/store/README.md +1 -1
  5. package/app/filesystem/store/filesystemStore.ts +11 -3
  6. package/app/filesystem/store/listSlice.ts +1 -0
  7. package/app/filesystem/ui/FileEditor.tsx +42 -4
  8. package/app/filesystem/ui/GitControlPanel.tsx +0 -1
  9. package/app/filesystem/ui/GitDiffView.tsx +12 -5
  10. package/app/filesystem/ui/GitFileDiff.tsx +15 -9
  11. package/app/filesystem/ui/PendingChangesPanel.tsx +95 -34
  12. package/app/filesystem/ui/README.md +9 -9
  13. package/app/filesystem/ui/SourcesPanel.tsx +1 -3
  14. package/app/filesystem/ui/Toolbar.tsx +5 -4
  15. package/app/filesystem/ui/VectorHealthPanel.tsx +43 -14
  16. package/dist/app/host.js +40 -40
  17. package/dist/src/config/env.d.ts +4 -0
  18. package/dist/src/config/env.d.ts.map +1 -1
  19. package/dist/src/config/env.js +13 -0
  20. package/dist/src/config/env.js.map +1 -1
  21. package/dist/src/connectors/LocalDiskConnector.d.ts +9 -1
  22. package/dist/src/connectors/LocalDiskConnector.d.ts.map +1 -1
  23. package/dist/src/connectors/LocalDiskConnector.js +51 -2
  24. package/dist/src/connectors/LocalDiskConnector.js.map +1 -1
  25. package/dist/src/domain/healthTypes.d.ts +1 -7
  26. package/dist/src/domain/healthTypes.d.ts.map +1 -1
  27. package/dist/src/domain/pendingChange.d.ts +31 -0
  28. package/dist/src/domain/pendingChange.d.ts.map +1 -1
  29. package/dist/src/domain/pendingChange.js.map +1 -1
  30. package/dist/src/routes/health.js +2 -3
  31. package/dist/src/routes/health.js.map +1 -1
  32. package/dist/src/routes/pending.d.ts +7 -7
  33. package/dist/src/routes/pending.d.ts.map +1 -1
  34. package/dist/src/routes/pending.js +21 -21
  35. package/dist/src/routes/pending.js.map +1 -1
  36. package/dist/src/services/FilesystemService.d.ts +13 -61
  37. package/dist/src/services/FilesystemService.d.ts.map +1 -1
  38. package/dist/src/services/FilesystemService.js +47 -189
  39. package/dist/src/services/FilesystemService.js.map +1 -1
  40. package/dist/src/services/FilesystemUiService.d.ts +33 -1
  41. package/dist/src/services/FilesystemUiService.d.ts.map +1 -1
  42. package/dist/src/services/FilesystemUiService.js +50 -1
  43. package/dist/src/services/FilesystemUiService.js.map +1 -1
  44. package/dist/src/store/PendingChangeStore.d.ts +3 -0
  45. package/dist/src/store/PendingChangeStore.d.ts.map +1 -1
  46. package/dist/src/store/PendingChangeStore.js +84 -25
  47. package/dist/src/store/PendingChangeStore.js.map +1 -1
  48. package/dist/src/usecases/orphanedPending.d.ts +2 -2
  49. package/dist/src/usecases/orphanedPending.d.ts.map +1 -1
  50. package/dist/src/usecases/orphanedPending.js +5 -3
  51. package/dist/src/usecases/orphanedPending.js.map +1 -1
  52. package/dist/src/usecases/pendingListing.d.ts +34 -0
  53. package/dist/src/usecases/pendingListing.d.ts.map +1 -0
  54. package/dist/src/usecases/pendingListing.js +244 -0
  55. package/dist/src/usecases/pendingListing.js.map +1 -0
  56. package/dist/src/usecases/vectorMaintenance.d.ts +6 -9
  57. package/dist/src/usecases/vectorMaintenance.d.ts.map +1 -1
  58. package/dist/src/usecases/vectorMaintenance.js +4 -13
  59. package/dist/src/usecases/vectorMaintenance.js.map +1 -1
  60. package/dist/src/vector/InMemoryVectorIndex.d.ts.map +1 -1
  61. package/dist/src/vector/InMemoryVectorIndex.js +30 -9
  62. package/dist/src/vector/InMemoryVectorIndex.js.map +1 -1
  63. package/package.json +26 -4
  64. package/rules/brain-rules.md +1 -1
  65. package/skills/create-package/SKILL.md +3 -3
  66. package/skills/create-package/references/config-and-credentials.md +1 -1
  67. package/skills/create-package/references/runtime-and-build.md +1 -1
  68. package/skills/create-package/references/skills-and-context.md +25 -12
  69. package/skills/create-package/references/test-and-ship.md +9 -16
  70. package/skills/create-package/scripts/verify-loaded.sh +18 -27
  71. package/skills/git/SKILL.md +27 -29
  72. package/skills/manage-files/SKILL.md +37 -32
  73. package/skills/manage-files/scripts/upload.sh +7 -9
  74. package/skills/manage-files/scripts/write.sh +2 -6
  75. package/skills/manage-sources/SKILL.md +93 -95
  76. package/skills/manage-sources/scripts/reembed.sh +2 -6
  77. package/skills/manage-uri-policy/SKILL.md +30 -58
  78. package/skills/git/scripts/git.sh +0 -48
  79. package/skills/manage-files/scripts/approve.sh +0 -9
  80. package/skills/manage-files/scripts/list.sh +0 -9
  81. package/skills/manage-files/scripts/pending.sh +0 -23
  82. package/skills/manage-files/scripts/raw.sh +0 -11
  83. package/skills/manage-files/scripts/read.sh +0 -9
  84. package/skills/manage-files/scripts/search.sh +0 -15
  85. package/skills/manage-sources/scripts/browse.sh +0 -12
  86. package/skills/manage-sources/scripts/cancel.sh +0 -14
  87. package/skills/manage-sources/scripts/create.sh +0 -13
  88. package/skills/manage-sources/scripts/delete.sh +0 -17
  89. package/skills/manage-sources/scripts/discoverable.sh +0 -6
  90. package/skills/manage-sources/scripts/get.sh +0 -7
  91. package/skills/manage-sources/scripts/kind-schema.sh +0 -7
  92. package/skills/manage-sources/scripts/kinds.sh +0 -6
  93. package/skills/manage-sources/scripts/list.sh +0 -6
  94. package/skills/manage-sources/scripts/purge-index.sh +0 -18
  95. package/skills/manage-sources/scripts/reindex.sh +0 -13
  96. package/skills/manage-sources/scripts/reset.sh +0 -11
  97. package/skills/manage-sources/scripts/set-enabled.sh +0 -17
  98. package/skills/manage-sources/scripts/sync-status.sh +0 -14
  99. package/skills/manage-sources/scripts/sync.sh +0 -13
  100. package/skills/manage-sources/scripts/update.sh +0 -18
  101. package/skills/manage-uri-policy/scripts/manifests.sh +0 -20
  102. package/skills/manage-uri-policy/scripts/set-uri.sh +0 -66
  103. package/skills/manage-uri-policy/scripts/show-uri.sh +0 -23
@@ -8,10 +8,13 @@ allowed-tools: execute fs_list fs_read fs_search
8
8
  # Sources Management Skill
9
9
 
10
10
  The `connect-business-data` skill owns common source listing, health checks, and
11
- creation through `connect`; this skill remains the full route/script lifecycle,
11
+ creation through `connect`; this skill remains the full route lifecycle,
12
12
  especially policy-preserving updates, sync, enablement, reset and deletion.
13
13
 
14
- Wraps `/api/packages/@neuralis/brain-core/sources/*`. Useful for:
14
+ Covers `@neuralis/brain-core/sources/*`, called with `neuralis request` (the
15
+ session ticket inside an activated skill, `neuralis login` on your own machine;
16
+ a non-2xx prints the route's body and `HTTP <code>` on stderr and exits 1).
17
+ Useful for:
15
18
 
16
19
  - Bootstrapping a project's sources from a script (`for slug in …`).
17
20
  - Bulk-cancel + re-sync after a connector outage.
@@ -21,8 +24,7 @@ Wraps `/api/packages/@neuralis/brain-core/sources/*`. Useful for:
21
24
  ## Read / survey mode (sources + freshness)
22
25
 
23
26
  To just **survey** the configured sources and their freshness (read-only, the
24
- `drive.read` floor — no mutation), use the `fs_*` tools rather than the route
25
- scripts:
27
+ `drive.read` floor — no mutation), use the `fs_*` tools rather than the routes:
26
28
 
27
29
  1. Call `fs_list` with `include_counts: true` (no `uri` — root registry) to
28
30
  enumerate sources with `on_disk`, `indexed_count`, `unindexed`,
@@ -42,7 +44,7 @@ scripts:
42
44
  semantic/filter search needs the source to be indexed. For unsynced content,
43
45
  recommend `fs_search search_mode="grep"` scoped to the source URI.
44
46
 
45
- (`list.sh` below is the route-backed equivalent enriched with
47
+ (`GET sources` below is the route-backed equivalent enriched with
46
48
  `kind_label`/`kind_icon`/`kind_trust`/`orphan`; use it when you want the
47
49
  server-side source rows rather than the index-count survey.)
48
50
 
@@ -52,41 +54,40 @@ server-side source rows rather than the index-count survey.)
52
54
  **in-handler** to a stronger feature and 403 an under-privileged caller (see
53
55
  **Feature gates** below). The `Feature` column is the gate actually enforced.
54
56
 
55
- | Action | Method | Path | Feature | Script |
57
+ | Action | Method | Path (`@neuralis/brain-core/…`) | Fields | Feature |
56
58
  |---|---|---|---|---|
57
- | List configured sources | GET | `/sources` | `drive.read` | `list.sh` |
58
- | Get one source's config | GET | `/sources/:slug` | `drive.read` | `get.sh` |
59
- | List registered kinds | GET | `/sources/kinds` | `drive.read` | `kinds.sh` |
60
- | Get a kind's config schema | GET | `/sources/kinds/:kind/schema` | `drive.read` | `kind-schema.sh` |
61
- | Discoverable connectors (filesystem hints) | GET | `/sources/discoverable` | `drive.mount` | `discoverable.sh` |
62
- | Browse a filesystem path before adding it | GET | `/sources/browse?path=…` | `drive.mount` | `browse.sh` |
63
- | Create a new source | POST | `/sources` | `drive.mount` | `create.sh` |
64
- | Update an existing source | PATCH | `/sources` | `drive.mount` | `update.sh` |
65
- | Delete a source | DELETE | `/sources/:slug` | `drive.mount` | `delete.sh` |
66
- | Enable / disable a source | POST | `/sources/enabled` | `drive.mount` | `set-enabled.sh` |
67
- | Trigger sync (202 + queue job snapshot) | POST | `/sources/sync` | `drive.sync` | `sync.sh` |
68
- | Cancel an in-flight sync (optional SOURCE) | POST | `/sources/cancel` | `drive.sync` | `cancel.sh` |
69
- | Job status (optional SOURCE) | GET | `/sources/sync-status` | `drive.sync` | `sync-status.sh` |
70
- | Forget sync state (next tick re-walks; deletes nothing) | POST | `/sources/reset` | `drive.sync` | `reset.sh` |
71
- | Rescan changes (full re-walk now; unchanged files keep their vectors; 202 + job) | POST | `/sources/reindex` | `drive.sync` | `reindex.sh` |
72
- | Clean a file / folder / source index (estimate first) | POST | `/sources/purge` | `drive.mount` + `drive.sync` | `purge-index.sh` |
73
- | Catch up embedding debt; FORCE=1 re-embeds (estimate first) | POST | `/sources/reindex` + `embeddingOnly` or `force` | `drive.sync` | `reembed.sh` |
74
-
75
- > **`update` takes no `:slug`.** PATCH targets the bare `/sources` path; the
59
+ | List configured sources | GET | `sources` | `scope?`, `observed?` | `drive.read` |
60
+ | Get one source's config | GET | `sources/<slug>` | | `drive.read` |
61
+ | List registered kinds | GET | `sources/kinds` | | `drive.read` |
62
+ | Get a kind's config schema | GET | `sources/kinds/<kind>/schema` | | `drive.read` |
63
+ | Discoverable connectors (filesystem hints) | GET | `sources/discoverable` | | `drive.mount` |
64
+ | Browse a filesystem path before adding it | GET | `sources/browse` | `path` | `drive.mount` |
65
+ | Create a new source | POST | `sources` | body (below) | `drive.mount` |
66
+ | Update an existing source | PATCH | `sources` | body (below) | `drive.mount` |
67
+ | Delete a source | DELETE | `sources/<slug>[?purge=1]` | | `drive.mount` |
68
+ | Enable / disable a source | POST | `sources/enabled` | `source`, `enabled` (`true`\|`false`\|`"auto"`) | `drive.mount` |
69
+ | Trigger sync (202 + queue job snapshot) | POST | `sources/sync` | `source`, `agentId?` | `drive.sync` |
70
+ | Cancel an in-flight sync | POST | `sources/cancel` | `source?` | `drive.sync` |
71
+ | Job status | GET | `sources/sync-status` | `source?` | `drive.sync` |
72
+ | Forget sync state (next tick re-walks; deletes nothing) | POST | `sources/reset` | `source` | `drive.sync` |
73
+ | Rescan changes (full re-walk now; unchanged files keep their vectors; 202 + job) | POST | `sources/reindex` | `source`, `agentId?` | `drive.sync` |
74
+ | Clean a file / folder / source index (estimate first) | POST | `sources/purge` | body (below) | `drive.mount` + `drive.sync` |
75
+ | Catch up embedding debt; FORCE=1 re-embeds (estimate first) | POST | `sources/reindex` + `embeddingOnly` or `force` — run `bash ${SKILL_DIR}/scripts/reembed.sh` | env | `drive.sync` |
76
+
77
+ > **`update` takes no `<slug>`.** PATCH targets the bare `sources` path; the
76
78
  > source to patch is the body `source` field (the handler ignores the path).
77
79
 
78
- `list.sh` returns each row enriched with `kind_label` / `kind_icon` /
80
+ `GET sources` returns each row enriched with `kind_label` / `kind_icon` /
79
81
  `kind_trust` / `orphan` (an orphan row's contributing kind package is missing).
80
- The list route also accepts optional `?scope=project|user|agent` and
81
- `?observed=1` query filters — `list.sh` sends neither, so it returns the
82
- default project-visible set.
82
+ Without `-f scope=project|user|agent` or `-f observed=1` it returns the default
83
+ project-visible set.
83
84
 
84
85
  **Output shape (jq) — read before piping.** `GET /sources` returns a **top-level
85
86
  array** of source rows (NOT `{ sources: [...] }`). Pipe `jq '.[]'`, never
86
87
  `jq '.sources[]'` (that errors `Cannot index array with string "sources"`). Each
87
88
  element carries `slug`, `enabled`, `connection.kind`, `scope`, plus the enrichment
88
89
  fields above. `connection.config` is `{}` unless you could attach the source or
89
- read its root, and `sync.lastSyncResult.error` is a fixed text without `drive.sync`. `get.sh` (`/sources/:slug`) returns a single object, not an array.
90
+ read its root, and `sync.lastSyncResult.error` is a fixed text without `drive.sync`. `GET sources/<slug>` returns a single object, not an array.
90
91
 
91
92
  ## Feature gates
92
93
 
@@ -95,17 +96,17 @@ Everything that mutates, syncs, or ENUMERATES THE HOST enforces a **stronger**
95
96
  feature server-side, beyond the `drive.read` floor — a caller holding only
96
97
  `drive.read` gets a `403`:
97
98
 
98
- - **`drive.mount`** — `create.sh`, `update.sh`, `delete.sh`, `set-enabled.sh`,
99
- `browse.sh` and `discoverable.sh`. Browsing and the
99
+ - **`drive.mount`** — create, update, delete, enable/disable, browse and the
100
+ discoverable list. Browsing and the
100
101
  discoverable list are the steps that PRECEDE attaching, and they enumerate the
101
102
  host: you may browse exactly what you would be allowed to attach.
102
- - **`drive.mount.privileged`** — `browse.sh` on the filesystem root or any
103
+ - **`drive.mount.privileged`** — browsing the filesystem root or any
103
104
  protected host zone (the App Zone, the Neuralis tree), the same zones whose
104
105
  ATTACH needs it. A protected directory is also omitted from the listing of a
105
106
  parent you may browse, so an absent entry is not proof it does not exist.
106
- - **`drive.sync`** — `sync.sh`, `cancel.sh`, `reset.sh`, `reindex.sh`, `reembed.sh`, `sync-status.sh`.
107
+ - **`drive.sync`** — sync, cancel, reset, reindex, `reembed.sh`, sync-status.
107
108
 
108
- `purge-index.sh` requires both `drive.mount` and `drive.sync`, source-scope management authority and current READ visibility.
109
+ The index purge requires both `drive.mount` and `drive.sync`, source-scope management authority and current READ visibility.
109
110
 
110
111
  Create/update/delete/enable additionally enforce `canAccessScope(.., 'write')`
111
112
  on the target source's scope, and create validates the kind's scope rules.
@@ -143,42 +144,37 @@ role or agent asking for a shell needs a policy row added with
143
144
 
144
145
  ## Async sync semantics
145
146
 
146
- `sync.sh` and `reindex.sh` return **202 + a queue job snapshot**
147
+ `sources/sync` and `sources/reindex` return **202 + a queue job snapshot**
147
148
  (`{jobId, source, mode, status}`) immediately — the run executes on the
148
- server-side sync queue. Poll `sync-status.sh` (`SOURCE=<slug>`) until
149
+ server-side sync queue. Poll `sources/sync-status` (`-f source=<slug>`) until
149
150
  `status` leaves `pending`/`running`; a `failed` job carries `error`, a
150
- `cancelled` one is not an error (partial progress stays valid). `cancel.sh`
151
- with `SOURCE` stops one source's job mid-run; without it, every job on
151
+ `cancelled` one is not an error (partial progress stays valid). `sources/cancel`
152
+ with `source` stops one source's job mid-run; without it, EVERY job on
152
153
  sources you can see.
153
154
 
154
155
  An embedding job can end `paused`: report `result.embedding.pausedReason` and partial counts. Resume is a new explicitly authorized `reembed.sh` request, using durable point debt; no saved job ID or automatic paid restart.
155
156
 
156
- `sync.sh` refuses in two ways worth telling apart. **404** — the slug does not
157
+ `sources/sync` refuses in two ways worth telling apart. **404** — the slug does not
157
158
  resolve for you (it does not exist, or it is outside your scope; the two answer
158
159
  alike on purpose). **400** — the source exists but carries no `sync` block, so
159
- there is nothing to trigger: add one with `update.sh` first. Neither is a server
160
+ there is nothing to trigger: add one with an update first. Neither is a server
160
161
  fault, so neither should be retried unchanged.
161
162
 
162
- ## Env vars
163
-
164
- Per-action:
165
- - `SLUG` — source slug path segment for `get.sh` and `delete.sh` ONLY (it is a
166
- real path segment there). `update.sh` does NOT take a slug — it keys off the
167
- `source` field inside `BODY_JSON`.
168
- - `KIND` — for `kind-schema.sh`: a registry kind as listed by `kinds.sh`, NOT a connector
169
- id or a UI label. An unregistered value is `404 Unknown kind "<x>"` for every caller.
170
- - `PATH_ARG` — for `browse.sh` (we don't reuse `PATH`).
171
- - `PURGE` — optional for `delete.sh`; `PURGE=1` (or `true`) enqueues an async
172
- vector purge. Delete then returns **202 + `{purgeJob, …}`** (vs 200 normally). Any delete
173
- resolves the source's open notifications (its unread failure warnings) for you and its creator.
174
- - `SOURCE` — source name for `sync.sh`, `reindex.sh`, `reset.sh`, `reembed.sh` (required) and
175
- `cancel.sh`, `sync-status.sh` (optional).
176
- - `FOLDER` / `URI` — `reembed.sh` scope (one of them, inside `SOURCE`; neither = the whole
163
+ ## Fields, bodies and script env
164
+
165
+ - `<slug>` is a real path segment for get and delete ONLY. The update does NOT
166
+ take a slug — it keys off the `source` field inside the body.
167
+ - `<kind>` in `sources/kinds/<kind>/schema` is a registry kind as listed by
168
+ `sources/kinds`, NOT a connector id or a UI label. An unregistered value is
169
+ `404 Unknown kind "<x>"` for every caller.
170
+ - `?purge=1` on delete enqueues an async vector purge. Delete then returns
171
+ **202 + `{purgeJob, …}`** (vs 200 normally). Any delete resolves the source's
172
+ open notifications (its unread failure warnings) for you and its creator.
173
+ - `reembed.sh` takes `SOURCE` (required) and `FOLDER` / `URI` — its scope (one of them, inside `SOURCE`; neither = the whole
177
174
  source). `CONFIRM=1` starts debt catchup; without it the script only ESTIMATES. `FORCE=1` selects a forced connector re-embed instead.
178
175
  `SCOPE=platform` (catch-up only, whole source, needs `platform.vector.maintain`) catches up EVERY
179
176
  writer's points of the source, not only yours — the way back after an operator dense clear.
180
- - `ENABLED` — `true | false | auto` for `set-enabled.sh`.
181
- - `BODY_JSON` — POST/PATCH bodies (`create.sh`, `update.sh`). Both MUST include
177
+ - Create and update bodies (`--input -`) MUST include
182
178
  `source` + `permissions`; create additionally requires `connection.kind`. May also
183
179
  carry `recognizesPackages` (boolean; absent or `null` = unchanged, so a listing
184
180
  round-tripped through jq never 400s on it) — see **Package sources** below. A `sync`
@@ -192,14 +188,14 @@ Per-action:
192
188
  `permissions.paths[]` — so narrowing a wide source with `include` alone leaves every
193
189
  file under that root readable. Re-root it, or add a path deny, or both.
194
190
 
195
- **`update.sh` REPLACES `permissions` wholesale — it does not merge.** A PATCH body
191
+ **The update REPLACES `permissions` wholesale — it does not merge.** A PATCH body
196
192
  carrying only `permissions.default` answers `200` and **silently drops every
197
193
  `paths[]` rule the source was carrying** (`data` typically holds dozens of
198
194
  per-package rules). Immutable `floor:true` rules are the one protected class — omitting
199
195
  one returns `409` instead of destroying it — so a green `200` is NOT evidence that the
200
196
  rest survived. Never hand-write a `permissions` object for an existing source: read the
201
- current one with `list.sh` and send it back unchanged, editing only the field you meant
202
- to change. Every `update.sh` example below does exactly that.
197
+ current one with `GET sources` and send it back unchanged, editing only the field you meant
198
+ to change. Every update example below does exactly that.
203
199
 
204
200
  **`syncExcludes` is a PREFERENCE; the uri policy is the security half. Do not
205
201
  confuse them.** A source's `sync.exclude` list is yours to edit — it is noise
@@ -218,28 +214,28 @@ theirs.
218
214
 
219
215
  ## Clean index without detaching the source
220
216
 
221
- `purge-index.sh` uses `{source,selector:{kind:"source"|"folder"|"file",uri?},mode:"estimate"|"apply",obsoleteOnly:true|false}`. `SOURCE` is required; `FOLDER` or `URI` selects a canonical URI inside it; neither selects the whole source. It returns only authorized counts: `removedParents`, `removedChunks`, `retained`, `unresolved`, `canMaintain`. Unreadable children are withheld. Missing configuration, invalid provenance or read failures are not deletion evidence; unresolved points stay untouched. No source config, host bytes, journal or baseline is deleted and no embedding provider is called.
217
+ `POST sources/purge` takes `{source,selector:{kind:"source"|"folder"|"file",uri?},mode:"estimate"|"apply",obsoleteOnly:true|false}`. `source` and `mode` are required (no `mode` is a 400, never an implicit apply); a `folder` or `file` selector names a canonical URI inside the source; `kind:"source"` selects the whole source. It returns only authorized counts: `removedParents`, `removedChunks`, `retained`, `unresolved`, `canMaintain`. Unreadable children are withheld. Missing configuration, invalid provenance or read failures are not deletion evidence; unresolved points stay untouched. No source config, host bytes, journal or baseline is deleted and no embedding provider is called.
222
218
 
223
- The default `OBSOLETE_ONLY=true` cleans only current include/exclude or disabled-media exclusions. `false` clears all selected searchable cache, preserving recovery payloads; later sync can rebuild it. Existing scoped recovery-only payloads can remain locally retained if source config is physically absent; that marker grants no READ or indexing permission, and unmarked missing input or invalid provenance still blocks generation switching. Already recovery-only eligible records with unavailable input stay fully preserved without vectors in the target, including when source I/O returns no input; verified input restores searchable embedding. Estimate first, show counts and the selection, then use `CONFIRM=1` for the explicit apply. Apply rereads authority and eligibility; an estimate is not an authorization token. In the Sources panel, **Clean index…** exposes the same selection, counts and confirmation. An idle manual source otherwise waits until the next requested FULL sync; targeted sync and Enabled/auto changes do not clean it.
219
+ `obsoleteOnly:true` cleans only current include/exclude or disabled-media exclusions. `false` clears all selected searchable cache, preserving recovery payloads; later sync can rebuild it. Existing scoped recovery-only payloads can remain locally retained if source config is physically absent; that marker grants no READ or indexing permission, and unmarked missing input or invalid provenance still blocks generation switching. Already recovery-only eligible records with unavailable input stay fully preserved without vectors in the target, including when source I/O returns no input; verified input restores searchable embedding. Estimate first, show counts and the selection, then resend the same body with `mode:"apply"`. Apply rereads authority and eligibility; an estimate is not an authorization token. In the Sources panel, **Clean index…** exposes the same selection, counts and confirmation. An idle manual source otherwise waits until the next requested FULL sync; targeted sync and Enabled/auto changes do not clean it.
224
220
 
225
221
  ```bash
226
- # Disposable source from the lifecycle walkthrough only; default = estimate.
227
- SOURCE=scratch-demo OBSOLETE_ONLY=true bash ${SKILL_DIR}/scripts/purge-index.sh
222
+ # Disposable source from the lifecycle walkthrough only; estimate first.
223
+ neuralis request POST @neuralis/brain-core/sources/purge --input - <<<'{"source":"scratch-demo","mode":"estimate","obsoleteOnly":true,"selector":{"kind":"source"}}'
228
224
  # Apply only after reviewing the same selection’s counts.
229
- SOURCE=scratch-demo OBSOLETE_ONLY=true CONFIRM=1 bash ${SKILL_DIR}/scripts/purge-index.sh
225
+ neuralis request POST @neuralis/brain-core/sources/purge --input - <<<'{"source":"scratch-demo","mode":"apply","obsoleteOnly":true,"selector":{"kind":"source"}}'
230
226
  # A canonical folder selection, still estimate-only:
231
- SOURCE=scratch-demo FOLDER=scratch-demo://docs OBSOLETE_ONLY=false bash ${SKILL_DIR}/scripts/purge-index.sh
227
+ neuralis request POST @neuralis/brain-core/sources/purge --input - <<<'{"source":"scratch-demo","mode":"estimate","obsoleteOnly":false,"selector":{"kind":"folder","uri":"scratch-demo://docs"}}'
232
228
  ```
233
229
 
234
230
  ## Re-embed (a file, a folder, a source)
235
231
 
236
- "Rescan changes" reuses unchanged indexed files, but rebuilds re-included recovery-only content. `reembed.sh` catches up missing/stale indexed embeddings, including brain-only and unchanged legacy records. Its read-only estimate returns `estimate_kind:scope_upper_bound`: authorized indexed scope counts are an upper bound, not a provider quote; no provider call or job starts. Show the estimate before `CONFIRM=1` (202 + embedding job; poll `sync-status.sh`). Disabled embedding refuses: each `list.sh` row's `embedding_enabled` is the live platform switch, and while it is `false` `reembed.sh` answers 409 `embedding_disabled`; without an active dense generation the estimate refuses and a submitted job pauses as `metadata_only`, never activating a model. `FORCE=1` instead rereads and re-embeds the connector scope; its estimate includes `{documents,estimate:{tokens,relStdErr,usd,seconds}}` (`usd:null` for an unpriced model). A single file you may not read answers
232
+ "Rescan changes" reuses unchanged indexed files, but rebuilds re-included recovery-only content. `reembed.sh` catches up missing/stale indexed embeddings, including brain-only and unchanged legacy records. Its read-only estimate returns `estimate_kind:scope_upper_bound`: authorized indexed scope counts are an upper bound, not a provider quote; no provider call or job starts. Show the estimate before `CONFIRM=1` (202 + embedding job; poll `sources/sync-status`). Disabled embedding refuses: each `GET sources` row's `embedding_enabled` is the live platform switch, and while it is `false` `reembed.sh` answers 409 `embedding_disabled`; without an active dense generation the estimate refuses and a submitted job pauses as `metadata_only`, never activating a model. `FORCE=1` instead rereads and re-embeds the connector scope; its estimate includes `{documents,estimate:{tokens,relStdErr,usd,seconds}}` (`usd:null` for an unpriced model). A single file you may not read answers
237
233
  `404`, exactly like a missing one. Switching the platform to a DIFFERENT model is not this:
238
234
  that is the owner's rebuild in the admin Vector section.
239
235
 
240
236
  ## Package sources (`recognizesPackages`)
241
237
 
242
- `create.sh` / `update.sh` accept `recognizesPackages` (boolean) in `BODY_JSON`. It does
238
+ Create and update accept `recognizesPackages` (boolean) in the body. It does
243
239
  NOT switch markdown discovery on — a synced source's markdown becomes source-package
244
240
  contributions (skill / instruction / rule / agent / docs) either way; the flag lets the host
245
241
  load the source as a runtime package source (create-package
@@ -267,25 +263,25 @@ scope (a different run identity sees nothing — correct-by-design, not a load f
267
263
  ```bash
268
264
  # NOT DISPOSABLE — promoting is only meaningful on a source that already exists
269
265
  # and already holds markdown, so this edits a REAL source. `repo` is a stand-in:
270
- # substitute a slug from list.sh. The effect is live — every matching .md under
266
+ # substitute a slug from the sources list. The effect is live — every matching .md under
271
267
  # it starts being advertised as a package contribution on the next sync — and it
272
268
  # is reversible only by resending the same body with `recognizesPackages:false`.
273
269
  # Needs drive.mount, then priority <= 2 (the admin anchor) or a '*' grant, and a
274
270
  # local-kind source. The body carries the source's CURRENT permissions back —
275
271
  # PATCH replaces them:
276
- BODY_JSON=$(bash ${SKILL_DIR}/scripts/list.sh | jq -c '.[] | select(.source=="repo") | {source, permissions, recognizesPackages:true}') bash ${SKILL_DIR}/scripts/update.sh
272
+ neuralis request GET @neuralis/brain-core/sources --jq '.[] | select(.source=="repo") | {source, permissions, recognizesPackages:true}' | neuralis request PATCH @neuralis/brain-core/sources --input -
277
273
  ```
278
274
 
279
275
  ## `description` field
280
276
 
281
- Both create and update accept an optional `description` (string) in `BODY_JSON`.
277
+ Both create and update accept an optional `description` (string) in the body.
282
278
  It is a one-line summary of what the source holds; it is persisted on the source
283
279
  config and loaded into the agent system prompt's `<runtime_stack>` source table,
284
280
  so keep it short and purpose-focused (e.g. `"Project data zone — working files"`).
285
281
 
286
282
  **Leaving it unset is fine.** The description you see is RESOLVED, not
287
283
  necessarily stored: the authored text, else what the owning package declared for
288
- that source, else what the connector kind says it is. `list.sh` and `get.sh`
284
+ that source, else what the connector kind says it is. The list and the get
289
285
  return both — `description` is what is on disk, `description_effective` is what
290
286
  the agent actually reads. So a blank `description` with a non-null
291
287
  `description_effective` is a source described by its package, not an undescribed
@@ -298,18 +294,22 @@ each sync — you do not set them.
298
294
  ## Examples
299
295
 
300
296
  ```bash
301
- bash ${SKILL_DIR}/scripts/list.sh
302
- # list.sh = TOP-LEVEL ARRAY → filter with .[] (NOT .sources[]):
297
+ neuralis request GET @neuralis/brain-core/sources
298
+ # a TOP-LEVEL ARRAY → filter with .[] (NOT .sources[]):
303
299
  # Each row IS the source config: the slug lives in `.source` (there is no `.slug` key).
304
- bash ${SKILL_DIR}/scripts/list.sh | jq '.[] | {source, enabled, kind: .connection.kind, orphan}'
305
- bash ${SKILL_DIR}/scripts/list.sh | jq '.[] | select(.connection.kind=="local")'
306
- # `KIND` is the registry KIND, not a connector id or a UI label. Enumerate them with
307
- # kinds.sh (today: brain, host, local, webtop); an unregistered value is 404
300
+ neuralis request GET @neuralis/brain-core/sources --jq '.[] | {source, enabled, kind: .connection.kind, orphan}'
301
+ neuralis request GET @neuralis/brain-core/sources --jq '.[] | select(.connection.kind=="local")'
302
+ # The kind is the registry KIND, not a connector id or a UI label. Enumerate them
303
+ # (today: brain, host, local, webtop); an unregistered value is 404
308
304
  # `Unknown kind "<x>"` for EVERY caller, so it is never a permission problem.
309
305
  # The on-disk connector's id is `local-disk` but its kind is `local` — send `local`.
310
- bash ${SKILL_DIR}/scripts/kinds.sh | jq -r '.kinds[].kind'
311
- KIND=local bash ${SKILL_DIR}/scripts/kind-schema.sh
312
- SOURCE=data bash ${SKILL_DIR}/scripts/sync.sh # needs drive.sync
306
+ neuralis request GET @neuralis/brain-core/sources/kinds --jq '.kinds[].kind'
307
+ neuralis request GET @neuralis/brain-core/sources/kinds/local/schema
308
+ neuralis request POST @neuralis/brain-core/sources/sync -f source=data # needs drive.sync
309
+ neuralis request GET @neuralis/brain-core/sources/sync-status -f source=data
310
+ # `"auto"` = live only while the source's runtime runs; a boolean is typed with -F
311
+ neuralis request POST @neuralis/brain-core/sources/enabled -f source=data -F enabled=true
312
+ neuralis request GET @neuralis/brain-core/sources/browse -f path=/tmp # needs drive.mount
313
313
  ```
314
314
 
315
315
  ### Lifecycle walkthrough — create → update → delete
@@ -327,26 +327,26 @@ the throwaway.
327
327
  ```bash
328
328
  # 1. Create (needs drive.mount). source + connection.kind + permissions.default.* required.
329
329
  # The root does not have to exist yet — create records the coordinate, sync walks it.
330
- BODY_JSON='{"source":"scratch-demo","connection":{"kind":"local","config":{"root":"/tmp/scratch-demo"}},"permissions":{"default":{"read":true,"write":true,"exec":false}}}' bash ${SKILL_DIR}/scripts/create.sh
330
+ neuralis request POST @neuralis/brain-core/sources --input - <<<'{"source":"scratch-demo","connection":{"kind":"local","config":{"root":"/tmp/scratch-demo"}},"permissions":{"default":{"read":true,"write":true,"exec":false}}}'
331
331
  # 2. Update — NO SLUG; the body `source` field is the key (needs drive.mount).
332
332
  # Sets the description shown in the agent <runtime_stack>. `permissions` is
333
333
  # REPLACED, so read the live object back and resend it unchanged, editing only
334
334
  # the field you meant to change — a hand-written {"default":…} body here would
335
335
  # delete every paths[] rule the source carried:
336
- BODY_JSON=$(bash ${SKILL_DIR}/scripts/list.sh | jq -c '.[] | select(.source=="scratch-demo") | {source, permissions, description:"Throwaway source — lifecycle demo"}') bash ${SKILL_DIR}/scripts/update.sh
337
- # 3. DESTRUCTIVE — deletes the source config, and with PURGE=1 enqueues an async purge
336
+ neuralis request GET @neuralis/brain-core/sources --jq '.[] | select(.source=="scratch-demo") | {source, permissions, description:"Throwaway source — lifecycle demo"}' | neuralis request PATCH @neuralis/brain-core/sources --input -
337
+ # 3. DESTRUCTIVE — deletes the source config, and with ?purge=1 enqueues an async purge
338
338
  # of every vector indexed under it. Returns 202 + {purgeJob} when purging, 200
339
339
  # otherwise. There is no undo and no backup. Only ever aim this at a source you
340
340
  # created yourself; never at `data`, `packages` or `brain`.
341
- SLUG=scratch-demo PURGE=1 bash ${SKILL_DIR}/scripts/delete.sh
341
+ neuralis request DELETE '@neuralis/brain-core/sources/scratch-demo?purge=1'
342
342
  ```
343
343
 
344
344
  ## Vector health (index diagnostics)
345
345
 
346
346
  The health route is SPLIT into a count-cheap path and a scan path (pattern `health/*`, feature
347
- `drive.read`) — call it with the implicit session ticket via the `execute` shell action. **The two
347
+ `drive.read`) — call it with `neuralis request`. **The two
348
348
  read paths run on `drive.read`; the `gc` path does not** — it additionally requires `drive.write`
349
- and is POST-only (see below). That covers all three of its actions: `gc` and `repair-orphans`
349
+ and is POST-only (see below). That covers its actions: `gc`, `repair-orphans` and `repair-pending`
350
350
  mutate, and `deduplicate` is read-only but rides the same gate — its rows enumerate other agents'
351
351
  `data://` paths.
352
352
 
@@ -359,14 +359,14 @@ mutate, and `deduplicate` is read-only but rides the same gate — its rows enum
359
359
  `duplicateGroups` are COUNTS (numbers) — read them directly, never `|length`. The full duplicate
360
360
  ROWS arrive only from `POST …/health/gc {action:deduplicate}` → `{ duplicates: DuplicateGroup[] }`
361
361
  (each `{contentHash, source, paths, ids}`).
362
- - **`POST …/brain-core/health/gc`** with `{action}` (`gc` / `repair-orphans` / `deduplicate`) —
363
- `gc` and `repair-orphans` MUTATE — run those only after a scan confirms a finding; `deduplicate`
362
+ - **`POST …/brain-core/health/gc`** with `{action}` (`gc` / `repair-orphans` / `repair-pending` / `deduplicate`) —
363
+ `gc`, `repair-orphans` and `repair-pending` MUTATE — run those only after a scan confirms a finding; `deduplicate`
364
364
  removes nothing (it reports groups) but is gated identically. **POST only** — a GET answers
365
365
  `405 Method GET not allowed`. It needs **`drive.write`** on top of the route's `drive.read`; a
366
366
  caller without it gets `403 {"error":"Missing feature: drive.write (vector health repair)"}`.
367
367
  That is a capability the caller lacks, not a broken call: report the finding and say that the
368
368
  sweep needs `drive.write` (manager tier and up), rather than retrying.
369
- `dryRun` DEFAULTS TO FALSE — pass `{"dryRun": true}` first when you want a preview.
369
+ `dryRun` DEFAULTS TO FALSE — pass `{"dryRun": true}` first when you want a preview. `repair-pending` processes one visible batch (default 50) and returns `scannedVisible`, `orphaned`, `attempted`, `discarded`, `refused`, `failed`, safe reason counts, `nextCursor`, `complete` and `partial`. Pass the returned `nextCursor` as `cursor` in the next POST only after checking the result; pause on partial/failure, retry explicitly, and restart on `restartRequired`. Each page reconstructs the complete projected inventory; the batch bounds mutations, not scan work. A terminal acknowledgement is not a newly discarded record.
370
370
  - **`GET …/brain-core/health/hot-uris`** → the files the index rewrote most in the last hour:
371
371
  `{windowMinutes, threshold, uris: [{uri, writes, points, lastWriteAt, hot}]}` (`writes` = file
372
372
  writes, `points` = index points incl. chunks). Same `drive.write` gate; already filtered to the
@@ -377,11 +377,9 @@ mutate, and `deduplicate` is read-only but rides the same gate — its rows enum
377
377
 
378
378
  ```bash
379
379
  # counts (cheap):
380
- curl --fail-with-body -sS -H "Authorization: Bearer ${NEURALIS_SESSION_TOKEN}" -H "X-Neuralis-Skill: manage-sources" \
381
- "${NEURALIS_API}/api/packages/@neuralis/brain-core/health"
380
+ neuralis request GET @neuralis/brain-core/health
382
381
  # orphans / duplicate groups (scrolls — slower):
383
- curl --fail-with-body -sS -H "Authorization: Bearer ${NEURALIS_SESSION_TOKEN}" -H "X-Neuralis-Skill: manage-sources" \
384
- "${NEURALIS_API}/api/packages/@neuralis/brain-core/health/scan"
382
+ neuralis request GET @neuralis/brain-core/health/scan
385
383
  ```
386
384
 
387
385
  ### Health steps
@@ -1,7 +1,5 @@
1
1
  #!/usr/bin/env bash
2
2
  set -euo pipefail
3
- API_BASE="${NEURALIS_API:?NEURALIS_API not set; activate skill first}"
4
- TOKEN="${NEURALIS_SESSION_TOKEN:?missing session ticket; activate the skill via execute action=\"skill\"}"
5
3
  # Requires drive.sync (403 otherwise). FORCE=1 re-embeds with the ACTIVE model; otherwise catches up durable embedding debt — a
6
4
  # file (URI), a folder's indexed files (FOLDER) or, with neither, the whole
7
5
  # source — even when nothing changed. Default = the ESTIMATE only (changes
@@ -21,7 +19,5 @@ BODY=$(jq -n --arg source "${SOURCE}" --arg folder "${FOLDER}" --arg uri "${URI}
21
19
  + (if $force == "1" then {force: ($confirm == "1")} else {embeddingOnly: true} end)
22
20
  + (if $scope == "" then {} else {scope: $scope} end)
23
21
  + (if $confirm == "1" then {} else {estimate: true} end)')
24
- curl --fail-with-body -sS -X POST \
25
- -H "Authorization: Bearer ${TOKEN}" -H "X-Neuralis-Skill: manage-sources" \
26
- -H "Content-Type: application/json" -d "${BODY}" \
27
- "${API_BASE}/api/packages/@neuralis/brain-core/sources/reindex"
22
+ # A non-2xx answer prints the route's body and `HTTP <code>` on stderr and exits 1.
23
+ neuralis request POST @neuralis/brain-core/sources/reindex --input - <<<"${BODY}"
@@ -12,10 +12,11 @@ Patch the `paths[]` rules on a brain-core source-config so subsequent
12
12
  it edits the URI policy on any source (`data://`, `packages://`,
13
13
  `brain://`, future remote connectors), not vector or embedding state.
14
14
 
15
- Wraps the brain-core `policies/uri` routes, authenticated with the
16
- per-stream session ticket (`NEURALIS_SESSION_TOKEN`) auto-injected into the
17
- script env at skill activation. It carries your real identity; the routes
18
- gate on your real granted features server-side.
15
+ Covers the brain-core `policies/uri` routes, called with `neuralis request` —
16
+ inside an activated skill it authenticates with the per-stream session ticket
17
+ (your real identity), on your own machine with `neuralis login`. The routes
18
+ gate on your real granted features server-side; a non-2xx prints the route's
19
+ body and `HTTP <code>` on stderr and exits 1.
19
20
 
20
21
  ## When to use
21
22
 
@@ -27,69 +28,56 @@ gate on your real granted features server-side.
27
28
 
28
29
  ## Routes
29
30
 
30
- | Method | Path | Wrapper |
31
- |---|---|---|
32
- | GET | `/policies/uri/manifests` | `scripts/manifests.sh` |
33
- | GET | `/policies/uri/:source` | `scripts/show-uri.sh` |
34
- | POST | `/policies/uri` | `scripts/set-uri.sh` |
31
+ | Method | Path (`@neuralis/brain-core/…`) |
32
+ |---|---|
33
+ | GET | `policies/uri/manifests` |
34
+ | GET | `policies/uri/<source>` |
35
+ | POST | `policies/uri` |
35
36
 
36
- ## Scripts
37
+ ## Calls
37
38
 
38
39
  ### Show effective URI policy for a source
39
40
 
40
41
  ```bash
41
- SOURCE=data bash ${SKILL_DIR}/scripts/show-uri.sh
42
+ neuralis request GET @neuralis/brain-core/policies/uri/data
42
43
  ```
43
44
 
44
- Required env vars (pass via `execute action="shell" env={...}`):
45
- - `SOURCE` — source slug (e.g. `data`, `packages`).
46
-
47
45
  ### Add or remove a path rule
48
46
 
49
47
  **`addRule` takes effect the moment it returns — on a REAL source.** There is no
50
48
  staging, no preview and no dry run: the next `fs_read`, route call, UI listing or
51
49
  shell command evaluated against that source already sees the new rule, for every
52
- caller it names. `removeRule` is the only undo, and it matches on the `PATTERN`
50
+ caller it names. `removePatterns` is the only undo, and it matches on the `pattern`
53
51
  string, so a rule you cannot spell exactly is a rule you cannot take back. Read
54
- the current policy with `show-uri.sh` before and after — the pair below is
52
+ the current policy before and after — the pair below is
55
53
  written to be run top-to-bottom and leaves the policy exactly as it found it.
56
- One caveat the rules do not show: a `removeRule` is not always the last word
54
+ One caveat the rules do not show: a removal is not always the last word
57
55
  across a restart. The old cause is FIXED — the route no longer un-stamps
58
56
  `baselineRepairVersion`, so a patch (a no-op included) does not re-arm the
59
57
  baseline repair and cannot resurrect a rule you removed. What remains is the
60
58
  legitimate case: a package that SHIPS a new manifest baseline, or a connector
61
59
  that adds a `floor:true` row, still reaches an existing source on the next boot
62
60
  by design — floors are reconciled on every boot, past the version stamp. So
63
- re-check with `show-uri.sh` after a restart when a package version changed, and
61
+ re-check the policy after a restart when a package version changed, and
64
62
  read a reappeared rule as a NEW declaration rather than as your removal being
65
63
  undone.
66
64
 
67
65
  ```bash
68
66
  # 0. Read the policy you are about to change, so you can prove you restored it.
69
- SOURCE=data bash ${SKILL_DIR}/scripts/show-uri.sh
67
+ neuralis request GET @neuralis/brain-core/policies/uri/data
70
68
  # 1. Add the rule (LIVE — members lose read on this subtree immediately).
71
- SOURCE=data \
72
- PATTERN='data://agent-core/admin/**' \
73
- DENY=read \
74
- ROLES=member \
75
- bash ${SKILL_DIR}/scripts/set-uri.sh
76
- # 2. Take it back — same SOURCE, same PATTERN, byte for byte.
77
- SOURCE=data \
78
- PATTERN='data://agent-core/admin/**' \
79
- OPERATION=removeRule \
80
- bash ${SKILL_DIR}/scripts/set-uri.sh
69
+ neuralis request POST @neuralis/brain-core/policies/uri --input - <<<'{"source":"data","addPaths":[{"pattern":"data://agent-core/admin/**","permissions":{"read":false},"roles":["member"]}]}'
70
+ # 2. Take it back — same source, same pattern, byte for byte.
71
+ neuralis request POST @neuralis/brain-core/policies/uri --input - <<<'{"source":"data","removePatterns":["data://agent-core/admin/**"]}'
81
72
  ```
82
73
 
83
74
  To keep a rule, run step 1 and stop; step 2 exists so the example is reversible
84
75
  rather than a permanent edit to a live source's access policy.
85
76
 
86
- Env vars:
87
- - `SOURCE` — source slug (required).
88
- - `OPERATION` — `addRule` (default) or `removeRule`.
89
- - `PATTERN` — URI pattern with `**` wildcards (required for both).
90
- - `DENY` — comma-list of capabilities to deny (`read,write,exec`).
91
- - `ALLOW` — comma-list of capabilities to allow.
92
- - `ROLES` — comma-list of roles to scope the rule to.
77
+ Body: `source` (slug, required) plus either `addPaths` — rules of `{pattern,
78
+ permissions, roles?}`, where `pattern` takes `**` wildcards, `permissions` maps
79
+ `read`/`write`/`exec` to `false` (deny) or `true` (allow) and `roles` scopes the
80
+ rule — or `removePatterns` (the patterns to take back).
93
81
 
94
82
  `exec` decides whether a shell may START in that directory — on an exec-capable
95
83
  source (a virtual desktop or any other one whose connector runs commands) as
@@ -101,34 +89,19 @@ refused" usually needs an `exec` ALLOW row for their role or agent here.
101
89
  ### List baseline URI policies from every loaded package's manifest
102
90
 
103
91
  ```bash
104
- bash ${SKILL_DIR}/scripts/manifests.sh
92
+ neuralis request GET @neuralis/brain-core/policies/uri/manifests
105
93
  ```
106
94
 
107
- No env vars — returns every package's resolved `uriPolicies` baseline
95
+ Returns every package's resolved `uriPolicies` baseline
108
96
  (read-only). Useful for diffing the persisted source-config against the
109
97
  package's seed.
110
98
 
111
- ## Substitution placeholders
112
-
113
- When this skill is activated the host substitutes:
114
-
115
- - `${SKILL_DIR}` → container path of this skill bundle
116
- (e.g. `/neuralis/packages/brain-core/skills/manage-uri-policy`).
117
- - `${NEURALIS_API}` → the host's internal API base URL.
118
- - `${SKILL_URI}` → `neuralis://`-addressable URI if the `neuralis://`
119
- source is enabled, otherwise a sentinel.
120
-
121
- If `${SKILL_DIR}` resolves to a sentinel ("(no local containerDir)"),
122
- there is no local script directory — unless the envelope printed a source directory URI to pass as `cwd`, read
123
- `fs_read uri="${SKILL_URI}/SKILL.md"` for the procedure and call the
124
- routes manually with `execute action="shell" command="curl ..."`.
125
-
126
99
  ## Notes
127
100
 
128
101
  - **Two arms, both keyed on FEATURES — there is no role-name check on this
129
- route.** Reading (`GET /policies/uri/...`, `manifests.sh` / `show-uri.sh`) is
102
+ route.** Reading (`GET policies/uri/...`) is
130
103
  the module feature `drive.read`, which every built-in role holds down to
131
- `viewer`. Patching (`POST /policies/uri`, `set-uri.sh`) escalates in-handler to
104
+ `viewer`. Patching (`POST policies/uri`) escalates in-handler to
132
105
  `drive.policy` and otherwise returns
133
106
  `403 Missing feature: drive.policy (URI policy editing)`. `drive.policy` has
134
107
  no grant below the `admin` tier (enumerated for `admin`, `'*'` for `owner`)
@@ -155,9 +128,8 @@ routes manually with `execute action="shell" command="curl ..."`.
155
128
  conversation's creator). It is accepted only on an allow rule with
156
129
  `override: true` — the server refuses it on a deny. That is how agent-core
157
130
  ships conversations: `*/conversations/**` denied, re-granted to the creator
158
- and to owner/admin, so a member sees only their own. This script does not
159
- write `users`; widening who sees a conversation is done in the source's
160
- policy editor.
131
+ and to owner/admin, so a member sees only their own. Widening who sees a
132
+ conversation is done in the source's policy editor.
161
133
  - **Connector-agnostic.** A rule like `roles:["member"] deny:["write"]`
162
134
  on `webtop://**` applies to a future WebtopConnector source just as
163
135
  it does to `data://agent-core/**` today — the URI is the unit of
@@ -1,48 +0,0 @@
1
- #!/usr/bin/env bash
2
- # Generic caller for the brain-core git routes — ONE helper for all 19 endpoints
3
- # (9 GET + 10 POST, spread over 17 route names: `commit` and `branch` carry both verbs).
4
- # NOTE the shape: the HTTP VERB is a positional ARGUMENT, not a property of this file.
5
- # Anything auditing "which examples mutate" must read the verb off the INVOCATION line
6
- # (`git.sh POST commit …`), never off this script — 10 of the 19 endpoints are POST writes,
7
- # and `commit`/`branch` READ on GET while WRITING on POST.
8
- # GET: git.sh GET <route> [key=value ...] # each pair is URL-encoded
9
- # POST: git.sh POST <route> '<json-body>' # body carries "source" + params
10
- # Auth is the per-stream session ticket (NEURALIS_SESSION_TOKEN), injected by the
11
- # skill activation — never a platform/admin token, never a git credential (the
12
- # route resolves the remote token server-side for fetch/pull/push).
13
- # `--fail-with-body` (NOT `-f`): the routes' error bodies still reach you on
14
- # 400/403/404/409 — `-f` would leave a bare `curl: (22)` — and a refused call
15
- # now exits NON-ZERO as well, so a rejected commit/push can no longer read as a
16
- # success. Failure shapes, measured 2026-09-01 against `src/routes/git.ts` —
17
- # EVERY refusal carries `error`, and:
18
- # · a write-op failure (400/409/500) and the three remote 409s
19
- # (`no_https_remote` / `not_connected` / `no_credential`) also carry
20
- # `ok:false` AND `code`;
21
- # · a 403 carries `code` (`feature_denied` / `uri_policy_denied`), no `ok`;
22
- # · a plain argument or lookup 400/404 carries `error` only.
23
- # Separately, a **200** may still carry `ok:false`: `unavailable` from
24
- # `commit`/`outgoing`/`show`, `too_large` from `diff`. So use THIS script's exit
25
- # code as the discriminator — non-zero = an HTTP refusal (read `.error`, then
26
- # `.code` when present); zero + `ok:false` = a soft 200.
27
- set -euo pipefail
28
- API_BASE="${NEURALIS_API:?NEURALIS_API not set; activate skill first}"
29
- TOKEN="${NEURALIS_SESSION_TOKEN:?missing session ticket; activate the skill via execute action=\"skill\"}"
30
- METHOD="${1:?usage: git.sh <GET|POST> <route> [key=value ... | json-body]}"
31
- ROUTE="${2:?usage: git.sh <GET|POST> <route> [key=value ... | json-body]}"
32
- shift 2
33
- URL="${API_BASE}/api/packages/@neuralis/brain-core/git/${ROUTE}"
34
- AUTH=(-H "Authorization: Bearer ${TOKEN}" -H "X-Neuralis-Skill: git")
35
- if [ "${METHOD}" = "GET" ]; then
36
- ENC=()
37
- for kv in "$@"; do ENC+=(--data-urlencode "${kv}"); done
38
- curl --fail-with-body -sS -G "${AUTH[@]}" "${ENC[@]}" "${URL}"
39
- else
40
- # `${1:-{}}` does NOT work here: bash closes the expansion at the FIRST `}`, so
41
- # the default becomes `{` and the trailing `}` is appended as a LITERAL — every
42
- # POST body went on the wire as `{…}}`, invalid JSON, for all 10 POST routes.
43
- # `--data-raw` (not `-d`): `-d` treats a leading `@` as a FILE reference.
44
- BODY="${1:-}"
45
- [ -z "${BODY}" ] && BODY='{}'
46
- curl --fail-with-body -sS -X "${METHOD}" "${AUTH[@]}" \
47
- -H "Content-Type: application/json" --data-raw "${BODY}" "${URL}"
48
- fi