@neuralis/brain-core 0.2.1 → 0.3.1

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
@@ -9,8 +9,9 @@
9
9
  # only what the platform said.
10
10
  #
11
11
  # WHAT IT CHECKS
12
- # Reads the agent-core package diagnostics and overview surfaces as YOU, with
13
- # the session ticket already in the script environment, and reports one of:
12
+ # Reads the agent-core package diagnostics and overview surfaces as YOU through
13
+ # `neuralis request` (the session ticket inside an activated skill, your
14
+ # `neuralis login` on your own machine), and reports one of:
14
15
  # loaded — registered and operational
15
16
  # partial — declarative contributions registered, no code runtime; prints
16
17
  # the reason (build-missing | runtime-unavailable |
@@ -52,7 +53,7 @@
52
53
  # 1 = partial (reason printed)
53
54
  # 2 = absent — not listed for this caller
54
55
  # 3 = error / missing (recorded message printed)
55
- # 4 = could not reach the diagnostics surface (no session ticket, no
56
+ # 4 = could not reach the diagnostics surface (no `neuralis` CLI or login, no
56
57
  # network, HTTP failure, or the caller lacks the `core.observe`
57
58
  # feature). NOTHING is claimed about the package in this state.
58
59
  #
@@ -127,8 +128,8 @@ if [ -z "$PKG_ID" ]; then
127
128
  echo "usage: bash verify-loaded.sh <package-id> [--raw]" >&2
128
129
  exit 4
129
130
  fi
130
- if ! command -v curl >/dev/null 2>&1; then
131
- echo "ERROR curl not found on PATH" >&2
131
+ if ! command -v neuralis >/dev/null 2>&1; then
132
+ echo "ERROR neuralis (the CLI) not found on PATH" >&2
132
133
  manual_fallback
133
134
  exit 4
134
135
  fi
@@ -137,30 +138,20 @@ if ! command -v node >/dev/null 2>&1; then
137
138
  manual_fallback
138
139
  exit 4
139
140
  fi
140
- if [ -z "${NEURALIS_API:-}" ]; then
141
- echo "ERROR NEURALIS_API is not set — activate this skill first (execute action=\"skill\"); the API base and the session ticket are injected at activation" >&2
142
- manual_fallback
143
- exit 4
144
- fi
145
- if [ -z "${NEURALIS_SESSION_TOKEN:-}" ]; then
146
- echo "ERROR NEURALIS_SESSION_TOKEN is not set — activate this skill first; every internal call authenticates with the per-stream session ticket" >&2
147
- manual_fallback
148
- exit 4
149
- fi
150
-
151
- API_BASE="${NEURALIS_API%/}"
152
- BASE_ROUTE="${API_BASE}/api/packages/@neuralis/agent-core/packages"
141
+ BASE_ROUTE="@neuralis/agent-core/packages"
153
142
 
154
143
  WORK="$(mktemp -d)"
155
144
  trap 'rm -rf "$WORK"' EXIT
156
145
 
157
- fetch() { # fetch <url> <outfile> -> echoes the HTTP status ("000" = no response)
158
- local code
159
- code="$(curl -sS -o "$2" -w '%{http_code}' \
160
- -H "Authorization: Bearer ${NEURALIS_SESSION_TOKEN}" \
161
- -H "X-Neuralis-Skill: create-package" \
162
- "$1" 2>"$WORK/curl.err")" || true
163
- [ -n "$code" ] || code="000"
146
+ # `neuralis request` prints a 2xx body on stdout (exit 0); a non-2xx body goes to
147
+ # stderr with an `HTTP <code>` line (exit 1). No such line = no answer
148
+ # at all (no CLI login, no network) — "000". Its error text stays in request.err.
149
+ fetch() { # fetch <route> <outfile> -> echoes the HTTP status ("000" = no response)
150
+ local rc=0 code
151
+ neuralis request GET "$1" >"$2" 2>"$WORK/request.err" || rc=$?
152
+ if [ "$rc" -eq 0 ]; then echo 200; return; fi
153
+ code="$(sed -n '/^HTTP [0-9][0-9][0-9]$/{s/^HTTP //p;q;}' "$WORK/request.err")"
154
+ if [ -n "$code" ]; then awk '!d && /^HTTP [0-9][0-9][0-9]$/ {d=1; next} {print}' "$WORK/request.err" > "$2"; else code="000"; fi
164
155
  echo "$code"
165
156
  }
166
157
 
@@ -174,7 +165,7 @@ if [ "$DIAG_CODE" = "403" ]; then
174
165
  fi
175
166
  if [ "$DIAG_CODE" != "200" ]; then
176
167
  echo "ERROR GET ${BASE_ROUTE}/diagnostics returned HTTP ${DIAG_CODE}" >&2
177
- [ -s "$WORK/curl.err" ] && sed 's/^/ /' "$WORK/curl.err" >&2
168
+ [ -s "$WORK/request.err" ] && sed 's/^/ /' "$WORK/request.err" >&2
178
169
  [ -s "$WORK/diag.json" ] && head -c 600 "$WORK/diag.json" >&2 && echo >&2
179
170
  manual_fallback
180
171
  exit 4
@@ -322,7 +313,7 @@ const counts = () => {
322
313
  process.stdout.write(' drop — and into NO tenant at all when the package is neither\n');
323
314
  process.stdout.write(' first-party nor owned by a resolvable project, which is silent\n');
324
315
  process.stdout.write(' apart from one server log line. Prove the instance exists with\n');
325
- process.stdout.write(' GET <api>/api/packages/@neuralis/agent-core/connectors; that list is\n');
316
+ process.stdout.write(' neuralis request GET @neuralis/agent-core/connectors; that list is\n');
326
317
  process.stdout.write(' the platform lane plus your own project, and nothing else.\n');
327
318
  }
328
319
  if (overEntry.enabled === false) {
@@ -14,28 +14,32 @@ panel drives**, so an agent gets exactly the rights a human member has: gated by
14
14
  before every destructive op** and **credential-safe remotes** (the push/pull token is
15
15
  resolved server-side and never touches the agent shell).
16
16
 
17
- Call them with the one generic helper — `bash ${SKILL_DIR}/scripts/git.sh <GET|POST> <route> …`
18
- — which authenticates with the per-stream session ticket (`NEURALIS_API` +
19
- `NEURALIS_SESSION_TOKEN`, both injected at activation). This is *not* about committing to
20
- the Neuralis repo itself — it is about an agent operating git on a user's source.
17
+ Call them with `neuralis request <GET|POST> @neuralis/brain-core/git/<route> …` — inside an
18
+ activated skill it authenticates with the per-stream session ticket, on your own machine with
19
+ `neuralis login`; never with a git credential (fetch/pull/push resolve the remote token
20
+ server-side). The method is part of every call line: `commit` and `branch` READ on GET and
21
+ WRITE on POST. This is *not* about committing to the Neuralis repo itself — it is about an
22
+ agent operating git on a user's source.
21
23
 
22
24
  ## Three ways to reach git — pick the plane
23
25
 
24
26
  | Plane | Use it for | How | Guarantees |
25
27
  |---|---|---|---|
26
- | **A. Governed routes** (prefer for local ops + your own remote) | Review, stage, commit, branch/checkout, and fetch/pull/push on a git-backed source in the workspace | `bash ${SKILL_DIR}/scripts/git.sh …` (this skill) | uri-policy gated; auto-snapshot on destructive ops; remote token resolved server-side, never in the shell |
28
+ | **A. Governed routes** (prefer for local ops + your own remote) | Review, stage, commit, branch/checkout, and fetch/pull/push on a git-backed source in the workspace | `neuralis request <GET|POST> @neuralis/brain-core/git/<route>` (this skill) | uri-policy gated; auto-snapshot on destructive ops; remote token resolved server-side, never in the shell |
27
29
  | **B. External GitHub MCP** | Collaboration on the hosting platform — pull requests, issues, reviews, releases | the connected GitHub MCP server's tools (if the user connected one) | authz delegated to the GitHub account + enterprise settings — Neuralis does not re-implement git ACLs |
28
30
  | **C. Raw git CLI** | Quick local inspection, or a user's own imported git skill | `execute action="shell"` running `git …` | uri-policy + Landlock sandboxed; secrets are stripped from the env, so it **cannot push**, and there is **no server snapshot**. It also resolves the repository by walking UP from `cwd`, so it is **repository-scoped, never source-scoped** — see the `repo` parameter note below |
29
31
 
30
32
  Default to **A** for governed local ops and for pushing/pulling your connected remote; **B**
31
33
  for platform collaboration; **C** for a fast read or when a user's own skill already drives git.
32
- Plane A is always "call the routes via `git.sh`" — the first-party `git` connector shown in the
34
+ Plane A is always "call the routes with `neuralis request`" — the first-party `git` connector shown in the
33
35
  `<packages>` overview is metadata; there are no first-party git MCP *tools* to call.
34
36
 
35
37
  ## Routes covered
36
38
 
37
- Base path: `/api/packages/@neuralis/brain-core/git/<route>`. GET carries `source` (+ optional
38
- `repo`) as query params; POST carries them in the JSON body. `repo` is the optional in-source
39
+ Path: `@neuralis/brain-core/git/<route>`. GET carries `source` (+ optional `repo`) as `-f`
40
+ query fields; POST carries them in the JSON body (`--input -`), and every POST body must include
41
+ `"source"` plus that route's own required fields (e.g. `paths[]` for `stage`, `message` for
42
+ `commit`) — a source-less body is a `400`. `repo` is the optional in-source
39
43
  selector for a nested checkout when the source root is not itself the repo (omit for a
40
44
  single-repo source).
41
45
 
@@ -58,8 +62,10 @@ with nothing to report**. The same empty answer covers a
58
62
  source with no `.git` at all. (`commit`, `outgoing` and `show` are honest — they answer a typed
59
63
  `{"ok":false,"code":"unavailable"}` — and `diff` returns an empty diff string, except when it
60
64
  refuses a huge read with its own typed `{"ok":false,"code":"too_large"}`. Both of those typed
61
- refusals arrive on a **200**, so `git.sh` exits 0: a zero exit with `ok:false` is a soft answer,
62
- a non-zero exit is an HTTP refusal.)
65
+ refusals arrive on a **200**, so `neuralis request` exits 0: a zero exit with `ok:false` is a
66
+ soft answer, a non-zero exit is an HTTP refusal whose body and `HTTP <code>` are on stderr —
67
+ EVERY refusal carries `error`; a write-op failure and the three remote 409s also carry
68
+ `ok:false` and `code`; a 403 carries `code` (`feature_denied` / `uri_policy_denied`).)
63
69
 
64
70
  **So an empty read is never evidence of a clean tree.** Do not test it with `branch:null`: a real
65
71
  repository on a detached HEAD, or freshly initialised with no commit yet, reports exactly that.
@@ -112,7 +118,7 @@ Notes:
112
118
  `no_credential` / `no_https_remote` otherwise).
113
119
  - Destructive routes (`discard`, `branch` delete) snapshot server-side **before** mutating and
114
120
  return a typed `rescue` carrier. **Undo it through the governed `restore` route** — echo the
115
- carrier straight back: `POST restore '{"source":"…","rescue":{"kind":"stash","ref":"<sha>"}}'`
121
+ carrier straight back: `POST restore` with `{"source":"…","rescue":{"kind":"stash","ref":"<sha>"}}`
116
122
  for discarded content, `{"kind":"branch","ref":"<anchor>","name":"<branch>"}` for a deleted
117
123
  branch. Same gate as any other write (`drive.write` + `write` on the root — no `exec`, no new
118
124
  capability), and the restore itself snapshots first. Do **not** reach for raw-shell
@@ -153,21 +159,13 @@ connect the remote in that card first.
153
159
  general "don't `reset --hard` / `--no-verify` / force-push unrequested" rule still applies to
154
160
  everything not named here.
155
161
 
156
- ## Env vars
157
-
158
- - `NEURALIS_API` + `NEURALIS_SESSION_TOKEN` — injected at activation; every `git.sh` call reads them.
159
- - `git.sh` arguments: `<GET|POST> <route>` then, for **GET**, URL-encoded `key=value` pairs; for
160
- **POST**, a single JSON body string. Every POST body must include `"source"` plus that route's
161
- own required fields (e.g. `paths[]` for `stage`, `message` for `commit`) — a source-less body is
162
- a `400`.
163
-
164
162
  ## Examples
165
163
 
166
164
  ```bash
167
165
  # Review: what changed, then the diff of one file, then recent history
168
- bash ${SKILL_DIR}/scripts/git.sh GET status source=my-repo
169
- bash ${SKILL_DIR}/scripts/git.sh GET diff source=my-repo "path=src/app.ts"
170
- bash ${SKILL_DIR}/scripts/git.sh GET log source=my-repo maxCount=10
166
+ neuralis request GET @neuralis/brain-core/git/status -f source=my-repo
167
+ neuralis request GET @neuralis/brain-core/git/diff -f source=my-repo -f path=src/app.ts
168
+ neuralis request GET @neuralis/brain-core/git/log -f source=my-repo -f maxCount=10
171
169
 
172
170
  # NOT DISPOSABLE — every POST below writes to a REAL repository. These routes only
173
171
  # operate on an already-attached git-backed source, so there is no throwaway repo to
@@ -181,16 +179,16 @@ bash ${SKILL_DIR}/scripts/git.sh GET log source=my-repo maxCount=10
181
179
  # authorship, and is unsafe once it has been pushed (the remote keeps the original
182
180
  # and the two histories diverge). Stage NAMED paths only — `paths:[…]` is what keeps
183
181
  # a stray secret from being swept in; there is no route that un-commits.
184
- bash ${SKILL_DIR}/scripts/git.sh POST stage '{"source":"my-repo","paths":["src/app.ts","README.md"]}'
185
- bash ${SKILL_DIR}/scripts/git.sh POST commit '{"source":"my-repo","message":"Fix null guard in loader"}'
182
+ neuralis request POST @neuralis/brain-core/git/stage --input - <<<'{"source":"my-repo","paths":["src/app.ts","README.md"]}'
183
+ neuralis request POST @neuralis/brain-core/git/commit --input - <<<'{"source":"my-repo","message":"Fix null guard in loader"}'
186
184
 
187
185
  # Create a branch, then switch to it.
188
186
  # checkout MOVES HEAD and swaps the working tree to that branch's content — the files
189
187
  # the user is looking at change under them. It is plain (never `-f`): if the switch
190
188
  # would overwrite uncommitted local changes, git refuses and you get a 409. Report
191
189
  # that refusal; there is no force option here and you must not build one in Plane C.
192
- bash ${SKILL_DIR}/scripts/git.sh POST branch '{"source":"my-repo","action":"create","name":"fix/loader"}'
193
- bash ${SKILL_DIR}/scripts/git.sh POST checkout '{"source":"my-repo","ref":"fix/loader"}'
190
+ neuralis request POST @neuralis/brain-core/git/branch --input - <<<'{"source":"my-repo","action":"create","name":"fix/loader"}'
191
+ neuralis request POST @neuralis/brain-core/git/checkout -f source=my-repo -f ref=fix/loader
194
192
 
195
193
  # Sync a connected remote (needs uri-policy exec + a connected remote).
196
194
  # pull REWRITES the working tree to the remote tip — fast-forward only, so it refuses
@@ -200,9 +198,9 @@ bash ${SKILL_DIR}/scripts/git.sh POST checkout '{"source":"my-repo","ref":"fix/l
200
198
  # OUTWARD-FACING: teammates, CI, and anything deploying from that branch see it the
201
199
  # moment it lands, and nothing in Neuralis can take it back. ASK THE USER FIRST, every
202
200
  # time — this is the one line here you never run on your own initiative.
203
- bash ${SKILL_DIR}/scripts/git.sh POST pull '{"source":"my-repo"}'
204
- bash ${SKILL_DIR}/scripts/git.sh POST push '{"source":"my-repo"}'
201
+ neuralis request POST @neuralis/brain-core/git/pull -f source=my-repo
202
+ neuralis request POST @neuralis/brain-core/git/push -f source=my-repo
205
203
 
206
204
  # Nested repo inside a multi-repo source: add the repo selector
207
- bash ${SKILL_DIR}/scripts/git.sh GET status source=mono repo=packages/api
205
+ neuralis request GET @neuralis/brain-core/git/status -f source=mono -f repo=packages/api
208
206
  ```
@@ -7,24 +7,25 @@ allowed-tools: execute
7
7
 
8
8
  # Files Management Skill
9
9
 
10
- Wraps `/api/packages/@neuralis/brain-core/{list,search,read,raw,write,upload,approve,pending}`
11
- behind one shell-callable bundle. The `fs_*` tools (`fs_read`, `fs_write`,
12
- etc.) remain the primary path for one-off file operations — this bundle is
13
- for *bulk*: iterating with a shell loop, posting a batch, or scripting an
14
- `upload + approve` sequence end-to-end.
10
+ Covers `@neuralis/brain-core/{list,search,read,raw,write,upload,approve,pending}`.
11
+ The `fs_*` tools (`fs_read`, `fs_write`, etc.) remain the primary path for one-off
12
+ file operations — this skill is for *bulk*: iterating with a shell loop, posting a
13
+ batch, or scripting an `upload + approve` sequence end-to-end. Call the routes with
14
+ `neuralis request` (the session ticket inside an activated skill, `neuralis login`
15
+ on your own machine); `write` and `upload` go through their scripts.
15
16
 
16
17
  ## Routes covered
17
18
 
18
- | Action | Method | Path | Script | Feature gate |
19
- |---|---|---|---|---|
20
- | List files / folders at a URI | POST | `/list` | `list.sh` | `drive.read` |
21
- | Search vector index | POST | `/search` | `search.sh` | `drive.search` |
22
- | Read multiple files | POST | `/read` | `read.sh` | `drive.read` |
23
- | Raw (un-summarized) read | GET | `/raw` | `raw.sh` | `drive.read` |
24
- | Write — every verb (create / overwrite / edit / insert / lines / fields / move / copy / mkdir / revert / delete) | POST | `/write` | `write.sh` | `drive.write` |
25
- | Upload a payload (multipart/form-data) | POST | `/upload` | `upload.sh` | `drive.write` |
26
- | Approve a pending change | POST | `/approve` | `approve.sh` | `drive.write` |
27
- | List pending changes (project-wide) | GET | `/pending` | `pending.sh` | `drive.read` |
19
+ | Action | Method | Path (`@neuralis/brain-core/…`) | Feature gate |
20
+ |---|---|---|---|
21
+ | List files / folders at a URI | POST | `list` | `drive.read` |
22
+ | Search vector index | POST | `search` | `drive.search` |
23
+ | Read multiple files | POST | `read` | `drive.read` |
24
+ | Raw (un-summarized) read | GET | `raw` | `drive.read` |
25
+ | Write — every verb (create / overwrite / edit / insert / lines / fields / move / copy / mkdir / revert / delete) | POST | `write` — `scripts/write.sh` | `drive.write` |
26
+ | Upload a payload (multipart/form-data) | POST | `upload` — `scripts/upload.sh` | `drive.write` |
27
+ | Approve a pending change | POST | `approve` | `drive.write` |
28
+ | List pending changes (project-wide) | GET | `pending` | `drive.read` |
28
29
 
29
30
  Per-route feature gates are enforced server-side by the RouteDispatcher against the
30
31
  caller's real `grantedFeatures`, independently of this skill's `requiredFeatures`
@@ -32,41 +33,45 @@ frontmatter — `/list`, `/read`, `/raw` and `GET /pending` need `drive.read`; `
32
33
  `drive.search`; `/write`, `/upload`, `/approve` and the mutating `/pending`
33
34
  verbs (`revert`, `discard`, `dismiss`, `discard-orphans`) need `drive.write`. A caller who holds
34
35
  `drive.write` (enough to see and activate this skill) but NOT `drive.search` gets a
35
- server-side **403** from `search.sh` even though the other scripts work.
36
+ server-side **403** from `search` even though the other routes work.
36
37
 
37
- ## Env vars
38
+ ## Bodies
38
39
 
39
- - All scripts read `NEURALIS_API` + `NEURALIS_SESSION_TOKEN` (both injected by the skill activation; the token is a per-stream session ticket).
40
- - `BODY_JSON` — the JSON request body for `list` / `search` / `read` / `write` / `approve`; on `pending.sh` it selects POST instead of GET. `create.sh`, `modify.sh`, `copy.sh` and `mkdir.sh` are kept only so an imported skill's existing path still writes; they exec `write.sh`, so they take the same verb-keyed body.
41
- - `/write` body = `{"agentId":"…","uri":"data://notes/plan.md","<verb>":…}` — the VERB is a KEY and its presence is the intent. Content verbs COMPOSE in one call, in a fixed order, as ONE write with ONE baseline: `create` (`{content, client_request_id?}` — never overwrites; a taken name lands BESIDE as `name-2.ext` and the response `uri` is where it went, so read it back), `overwrite` (`{content}` — refuses on a missing file and names `create`), `edit` (`[{old_string, new_string?, replace_all?, expected_replacements?}]`, applied in order, one failure discards all of them), `insert` (`{insert_text, insert_line?}` — inserts AFTER that line, `0` = start of file, omitted = append), `lines` (`{line_start, line_end, content}`), `fields` (`{category, status, pinned, tags{add,remove,set}, metadata}`). Structural verbs stand ALONE: `move`/`copy` (a destination uri or, for a file move, a bare new filename; `copy` may cross sources), `mkdir:true` (a real empty directory; not on `brain://`, where folders are implicit), `revert:true` (one-level undo), `delete:true` (a file, or a whole folder recursively). **A 2xx does not mean success** — a refusal is 200 with `ok:false`, so branch on `ok`. A `move` (or a `revert` that relocates or re-creates) onto a uri that already holds something answers `ok:false` + `error:"DESTINATION_EXISTS"` — nothing was overwritten; delete or rename the occupant first. A delete has ONE spelling, the `delete` key: `fields.status:"deleted"` is refused. `review` is never read from the body — a content edit (`overwrite`/`edit`/`insert`/`lines`) lands `pending_review` on every lane (visible and revertible in Changes); `create`/`move`/`copy`/`mkdir` land committed for a human interactive caller and `pending_review` from this ticket lane; `/upload` also journals agent-origin uploads as pending review; interactive human uploads land committed. Use `/write` for JSON text and `/upload` for multipart file bytes. A missing `agentId`/`uri` is a **400** naming the field, a body the server could not parse a **400** `Request body must be a JSON object`. A uri-policy denial is a **403 with no `ok` field**, a disabled source a **409** with `code:"source_disabled"`. A source slug this project does not have answers one of two ways, and BOTH mean "stop guessing this name": with a scheme (`zzz://a.md`) the uri gate answers **403 `uri_policy_denied`** first; without one (`notes.md`) you get **404 `code:"source_not_configured"`** carrying `availableSources` — the sources you CAN address. `/list` gives the 404 in both forms. Switch to a source from that list (or from `<runtime_stack>`); retrying the same name never works. `/read` is different again: it reports PER FILE inside a 200, so a denied or disabled source arrives as `{uri, error, code:"forbidden"}` — never a 409 — and a missing file as `code:"not_found"`. No code means the read failed for some other reason; do not conclude the file is gone. A folder delete or copy that partly succeeded reports what really happened: `affected` (files touched) and `failed` (the NAMES that could not be), so "failed" does not mean "nothing happened". `warning:"moved_with_source_retained"` on a move means the copy landed but the original could not be removed — BOTH copies exist on purpose; do not re-run the move. `indexed:false` means the bytes DID land and are readable, but search indexing was skipped or failed; report it, do not re-write. Sync retries failed indexing only for eligible files; excluded files remain unindexed. Every script here runs curl with `--fail-with-body`, so a non-2xx prints that body too — in the merged output the body comes FIRST and curl's own `curl: (22)` line after it, under a `--- stderr ---` separator, so read the whole output.
40
+ - `list` / `search` / `read` / `approve` take a JSON body (`--input - <<<'<json>'`);
41
+ `write.sh` takes it as `BODY_JSON`. `create.sh`, `modify.sh`, `copy.sh` and `mkdir.sh` are kept only so an imported skill's existing path still writes; they exec `write.sh`, so they take the same verb-keyed body.
42
+ - `/write` body = `{"agentId":"…","uri":"data://notes/plan.md","<verb>":…}` — the VERB is a KEY and its presence is the intent. Content verbs COMPOSE in one call, in a fixed order, as ONE write with ONE baseline: `create` (`{content, client_request_id?}` — never overwrites; a taken name lands BESIDE as `name-2.ext` and the response `uri` is where it went, so read it back), `overwrite` (`{content}` — refuses on a missing file and names `create`), `edit` (`[{old_string, new_string?, replace_all?, expected_replacements?}]`, applied in order, one failure discards all of them), `insert` (`{insert_text, insert_line?}` — inserts AFTER that line, `0` = start of file, omitted = append), `lines` (`{line_start, line_end, content}`), `fields` (`{category, status, pinned, tags{add,remove,set}, metadata}`). Structural verbs stand ALONE: `move`/`copy` (a destination uri or, for a file move, a bare new filename; `copy` may cross sources), `mkdir:true` (a real empty directory; not on `brain://`, where folders are implicit), `revert:true` (one-level undo), `delete:true` (a file, or a whole folder recursively). **A 2xx does not mean success** — a refusal is 200 with `ok:false`, so branch on `ok`. A `move` (or a `revert` that relocates or re-creates) onto a uri that already holds something answers `ok:false` + `error:"DESTINATION_EXISTS"` — nothing was overwritten; delete or rename the occupant first. A delete has ONE spelling, the `delete` key: `fields.status:"deleted"` is refused. `review` is never read from the body — a content edit (`overwrite`/`edit`/`insert`/`lines`) lands `pending_review` on every lane (visible and revertible in Changes); `create`/`move`/`copy`/`mkdir` land committed for a human interactive caller and `pending_review` from this ticket lane; `/upload` also journals agent-origin uploads as pending review; interactive human uploads land committed. Use `/write` for JSON text and `/upload` for multipart file bytes. A missing `agentId`/`uri` is a **400** naming the field, a body the server could not parse a **400** `Request body must be a JSON object`. A uri-policy denial is a **403 with no `ok` field**, a disabled source a **409** with `code:"source_disabled"`. A source slug this project does not have answers one of two ways, and BOTH mean "stop guessing this name": with a scheme (`zzz://a.md`) the uri gate answers **403 `uri_policy_denied`** first; without one (`notes.md`) you get **404 `code:"source_not_configured"`** carrying `availableSources` — the sources you CAN address. `/list` gives the 404 in both forms. Switch to a source from that list (or from `<runtime_stack>`); retrying the same name never works. `/read` is different again: it reports PER FILE inside a 200, so a denied or disabled source arrives as `{uri, error, code:"forbidden"}` — never a 409 — and a missing file as `code:"not_found"`. No code means the read failed for some other reason; do not conclude the file is gone. A folder delete or copy that partly succeeded reports what really happened: `affected` (files touched) and `failed` (the NAMES that could not be), so "failed" does not mean "nothing happened". `warning:"moved_with_source_retained"` on a move means the copy landed but the original could not be removed — BOTH copies exist on purpose; do not re-run the move. `indexed:false` means the bytes DID land and are readable, but search indexing was skipped or failed; report it, do not re-write. Sync retries failed indexing only for eligible files; excluded files remain unindexed. A non-2xx prints the route's body too — on stderr, followed by an `HTTP <code>` line, under a `--- stderr ---` separator, so read the whole output.
42
43
  - `/search` body key is `search_mode` (semantic|filter|grep) — the SAME vocabulary as the `fs_search` tool; the legacy `mode` key is rejected with 400. The route forwards `search_mode`, `query`, `folder_uri`, `glob`, `category`, `tags`, `status`, `created_after`/`created_before`, `limit`, and the grep-only `path_glob`, `context` and `case_sensitive`. In `grep` mode the search is uri-policy `read`-gated (only paths the caller holds `read` policy on are grepped), the match is a FIXED STRING, and the answer carries `truncated` + `total_seen` — a cut set says so, so never read a full-looking list as proof that the rest is absent.
43
- - `/raw` is a GET — pass the file URI via `URI=…` (no `BODY_JSON`). It always returns the full bytes, so `raw.sh` is unaffected by the response headers; what those headers say is a BROWSER instruction. Safe types (images except SVG, video, audio, PDF, text, JSON) are marked for inline display; SVG, HTML and XML are marked `attachment` so a person clicking the link downloads them instead of executing them on the app origin. Do not describe a `.svg` or `.html` you wrote as viewable in the browser — offer it as a download, or convert it to a raster image if a human needs to look at it.
44
+ - `/raw` is a GET — pass the file URI as `-f uri=<uri>` and redirect stdout to a file: it always returns the full bytes, unaffected by the response headers; what those headers say is a BROWSER instruction. Safe types (images except SVG, video, audio, PDF, text, JSON) are marked for inline display; SVG, HTML and XML are marked `attachment` so a person clicking the link downloads them instead of executing them on the app origin. Do not describe a `.svg` or `.html` you wrote as viewable in the browser — offer it as a download, or convert it to a raster image if a human needs to look at it.
44
45
  - `/read` is at parity with the `fs_read` tool: the body takes `files[]` — each entry a URI string or `{"uri":"…","line_start":N,"line_end":M}` with that file's own window — plus `read_version`, batch-level `line_start`/`line_end` and `include_ids`. An entry that is neither shape is a **400**. Text is a bounded preview; native media may accompany it, while unsupported/invalid binary data has a URI and marker. For a byte-exact binary copy use the authorized `/raw` byte lane and multipart `/upload`, never re-encode a text preview. If you only need the pre-edit content, a plain `/read` response already carries it: each file's `version.baseline.content`, with `available_versions` listing `baseline` exactly when one exists.
45
- - `/upload` is **multipart/form-data**, NOT JSON — pass `FILE_PATH=…` `AGENT_ID=…` (required) plus optional `URI=…` / `TAGS=a,b`; a JSON body is rejected with 400. The 200 carries `indexed`: `false` means the bytes DID land but search indexing was skipped by the source selection or failed (provider outage/quota) — report it, do not re-upload. An eligible sync retries failed indexing; excluded uploads remain unindexed until the selection changes. The 200 also carries `mimeType`, and it may differ from what the upload declared: the stored type is resolved from the filename extension first, with the declared value kept only for an extension the platform does not know, and a common-image claim the leading bytes contradict falls back to `application/octet-stream`. Give the file a truthful extension and the stored type follows.
46
- - `GET /pending` lists durable pending records inside the Changes workspace. `STATUS`, `SOURCE`, `OP`, and `LIMIT` filter the list; owner/scope and URI READ gates apply per record. Review begins with `POST /pending` `{action:"preview",id}`: it returns the current connector content separately from the recorded snapshot and saved baseline, typed missing/unavailable/binary/too-large states, `indexState`, `requiresAcknowledgement`, and `expectedFingerprint`. The preview is bounded by `pendingPreviewMaxBytes`; it never fabricates missing content or vectors. Resolve through `POST /approve` `{id,expectedFingerprint}` or `POST /pending` `{action:"revert"|"dismiss",id,expectedFingerprint}`. If the preview requires acknowledgement, inspect those states and include `acknowledgeCurrentState:true`; never auto-acknowledge an unseen preview. The server rechecks freshness and WRITE authority at apply: a changed fingerprint requires a new preview, and acknowledgement grants no missing permission. Bare legacy `/write revert:true` refuses unsafe pending recovery; ordinary non-pending one-level undo remains available. Approval/dismissal preserve the stored snapshot/index state (including recovery-only or absent index), rather than certifying current connector bytes as indexed. For a file the preview shows `missing` with a saved `journal-latest-write` baseline, an acknowledged approve/dismiss closes the review and the file stays gone; revert is the restore. Terminal retries remain owner-gated and idempotent; `uri` is omitted without current READ and WRITE access. Revert restores the saved baseline and refuses missing baselines or occupied move destinations. Discard/Repair remain orphan-only, owner-gated operations; no transient outage is proof of orphanhood.
46
+ - `/upload` is **multipart/form-data**, NOT JSON — `upload.sh` takes `FILE_PATH=…` `AGENT_ID=…` (required) plus optional `URI=…` / `TAGS=a,b`; a JSON body is rejected with 400. The 200 carries `indexed`: `false` means the bytes DID land but search indexing was skipped by the source selection or failed (provider outage/quota) — report it, do not re-upload. An eligible sync retries failed indexing; excluded uploads remain unindexed until the selection changes. The 200 also carries `mimeType`, and it may differ from what the upload declared: the stored type is resolved from the filename extension first, with the declared value kept only for an extension the platform does not know, and a common-image claim the leading bytes contradict falls back to `application/octet-stream`. Give the file a truthful extension and the stored type follows.
47
+ - `GET /pending` lists durable pending records inside the Changes workspace. `status` (`pending_review`\|`approved`\|`reverted`), `source`, `op` (`create`\|`modify`\|`delete`\|`move`) and `limit` filter the list; owner/scope and URI READ gates apply per record. Review begins with `POST /pending` `{action:"preview",id}`: it returns the current connector content separately from the recorded snapshot and saved baseline, typed missing/unavailable/binary/too-large states, `indexState`, `requiresAcknowledgement`, and `expectedFingerprint`. The preview is bounded by `pendingPreviewMaxBytes`; it never fabricates missing content or vectors. Resolve through `POST /approve` `{id,expectedFingerprint}` or `POST /pending` `{action:"revert"|"dismiss",id,expectedFingerprint}`. If the preview requires acknowledgement, inspect those states and include `acknowledgeCurrentState:true`; never auto-acknowledge an unseen preview. The server rechecks freshness and WRITE authority at apply: a changed fingerprint requires a new preview, and acknowledgement grants no missing permission. Bare legacy `/write revert:true` refuses unsafe pending recovery; ordinary non-pending one-level undo remains available. Approval/dismissal preserve the stored snapshot/index state (including recovery-only or absent index), rather than certifying current connector bytes as indexed. For a file the preview shows `missing` with a saved `journal-latest-write` baseline, an acknowledged approve/dismiss closes the review and the file stays gone; revert is the restore. Terminal retries remain owner-gated and idempotent; `uri` is omitted without current READ and WRITE access. Revert restores the saved baseline and refuses missing baselines or occupied move destinations. Discard/Repair remain orphan-only, owner-gated operations; no transient outage is proof of orphanhood.
47
48
 
48
49
 
49
- The body shape is route-specific — refer to brain-core's
50
- `/api/packages/@neuralis/brain-core/list,search,read,write,…` route source
51
- for the exact schemas. For one-off calls, prefer the `fs_*` tools.
50
+ For one-off calls, prefer the `fs_*` tools.
52
51
 
53
52
  ## Examples
54
53
 
55
54
  For a disposable pending record you created in your project, substitute its real id below. Inspect the preview before resolving; copy its exact fingerprint, and add acknowledgement only after reviewing the states the preview names. A 409 requires a new preview, never a fabricated fingerprint.
56
55
 
57
56
  ```bash
58
- BODY_JSON='{"action":"preview","id":"<scratch-record-id>"}' bash ${SKILL_DIR}/scripts/pending.sh
59
- BODY_JSON='{"action":"revert","id":"<scratch-record-id>","expectedFingerprint":"<64-hex-preview-fingerprint>","acknowledgeCurrentState":true}' bash ${SKILL_DIR}/scripts/pending.sh
57
+ neuralis request POST @neuralis/brain-core/pending -f action=preview -f id=<scratch-record-id>
58
+ neuralis request POST @neuralis/brain-core/pending --input - <<<'{"action":"revert","id":"<scratch-record-id>","expectedFingerprint":"<64-hex-preview-fingerprint>","acknowledgeCurrentState":true}'
60
59
  ```
61
60
 
61
+ Pending calls return one page; scan-orphans preserves that page in `changes` and puts its orphan subset in `orphans`/`orphanIds`: pass `cursor` from `nextCursor` unchanged with the same filters, or `uri` for an exact URI. Check `complete`, `partial` and `hasMoreAvailable`; a partial exhausted branch remains incomplete. On `restartRequired`, restart the list instead of retrying the old cursor. Repair returns scannedVisible/orphaned/attempted/discarded/refused/failed/reasons for one batch; continue only via nextCursor; pause on partial and use an explicit Continue available/Retry.
62
+
62
63
  ```bash
63
64
  # Recursive list of /data
64
- BODY_JSON='{"uri":"data://","recursive":true,"limit":100}' \
65
- bash ${SKILL_DIR}/scripts/list.sh
65
+ neuralis request POST @neuralis/brain-core/list --input - <<<'{"uri":"data://","recursive":true,"limit":100}'
66
66
 
67
67
  # Vector search across all sources (body key is `search_mode`, same as the fs_search tool)
68
- BODY_JSON='{"search_mode":"semantic","query":"telemetry pipeline","limit":10}' \
69
- bash ${SKILL_DIR}/scripts/search.sh
68
+ neuralis request POST @neuralis/brain-core/search -f search_mode=semantic -f query='telemetry pipeline' -F limit=10
69
+
70
+ # Pending review records still waiting, one page
71
+ neuralis request GET @neuralis/brain-core/pending -f status=pending_review -f limit=20
72
+
73
+ # The exact bytes of one file
74
+ neuralis request GET @neuralis/brain-core/raw -f uri=data://report.pdf > /tmp/report.pdf
70
75
 
71
76
  # Create a text file — the journaled write lane. The verb is the KEY.
72
77
  # `<agent-id>` is a placeholder; a real id makes this line write for real.
@@ -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
  # POST /upload REQUIRES multipart/form-data (NOT a JSON body): the handler reads the
6
4
  # form fields file (the binary/text payload), agentId, uri (optional), tags (optional,
7
5
  # comma-split). projectId is taken from the session ticket — passing a different one
@@ -9,10 +7,10 @@ TOKEN="${NEURALIS_SESSION_TOKEN:?missing session ticket; activate the skill via
9
7
  # from the form field.
10
8
  FILE_PATH="${FILE_PATH:?FILE_PATH is required (local path of the payload to upload)}"
11
9
  AGENT_ID="${AGENT_ID:?AGENT_ID is required (form field; the destination agent)}"
12
- curl --fail-with-body -sS -X POST \
13
- -H "Authorization: Bearer ${TOKEN}" -H "X-Neuralis-Skill: manage-files" \
14
- -F "file=@${FILE_PATH}" \
15
- -F "agentId=${AGENT_ID}" \
16
- ${URI:+-F "uri=${URI}"} \
17
- ${TAGS:+-F "tags=${TAGS}"} \
18
- "${API_BASE}/api/packages/@neuralis/brain-core/upload"
10
+ # `--form` sends multipart/form-data (`file=@path` is the file part). A non-2xx
11
+ # answer prints the route's body and `HTTP <code>` on stderr and exits 1.
12
+ neuralis request POST @neuralis/brain-core/upload \
13
+ --form "file=@${FILE_PATH}" \
14
+ --form "agentId=${AGENT_ID}" \
15
+ ${URI:+--form "uri=${URI}"} \
16
+ ${TAGS:+--form "tags=${TAGS}"}
@@ -16,8 +16,6 @@ set -euo pipefail
16
16
  # `edit`, `insert`, `lines`) lands `pending_review` on EVERY lane; `create`, `move`,
17
17
  # `copy` and `mkdir` land committed for a human interactive caller and
18
18
  # `pending_review` from this ticket lane; `delete` tombstones on both.
19
- API_BASE="${NEURALIS_API:?NEURALIS_API not set; activate skill first}"
20
- TOKEN="${NEURALIS_SESSION_TOKEN:?missing session ticket; activate the skill via execute action=\"skill\"}"
21
19
  BODY_JSON="${BODY_JSON:?BODY_JSON is required — a JSON object with agentId, uri and ONE verb key; see the header comment for the shape}"
22
20
  # The retired body shape names no verb; the route would only say "Missing uri".
23
21
  # Refuse it HERE, naming what the caller has to send instead.
@@ -27,7 +25,5 @@ case "${BODY_JSON}" in
27
25
  exit 2
28
26
  ;;
29
27
  esac
30
- curl --fail-with-body -sS -X POST \
31
- -H "Authorization: Bearer ${TOKEN}" -H "X-Neuralis-Skill: manage-files" \
32
- -H "Content-Type: application/json" -d "${BODY_JSON}" \
33
- "${API_BASE}/api/packages/@neuralis/brain-core/write"
28
+ # A non-2xx answer prints the route's body and `HTTP <code>` on stderr and exits 1.
29
+ neuralis request POST @neuralis/brain-core/write --input - <<<"${BODY_JSON}"