@technomoron/agent-run 0.99.32 → 0.99.33
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.
- package/CHANGES +34 -0
- package/README.md +75 -7
- package/dist/agent-run.js +1 -2
- package/dist/agents/claude.js +1 -5
- package/dist/brain/cli.js +31 -22
- package/dist/brain/git.js +8 -2
- package/dist/brain/projects.js +1 -1
- package/dist/brain/server.js +15 -6
- package/dist/brain/skills.js +2 -1
- package/dist/brain/store.js +197 -48
- package/dist/brain/todos.js +36 -7
- package/dist/commands.js +7 -9
- package/dist/config/render-instructions.js +1 -1
- package/dist/config/render-mcp.js +2 -3
- package/dist/config/render-skills.js +3 -2
- package/dist/config-tree.js +0 -22
- package/dist/guards.js +114 -8
- package/dist/process.js +0 -5
- package/dist/project.js +2 -23
- package/dist/renderer.js +7 -2
- package/dist/templates.js +35 -7
- package/examples/basic-config/agent-config/starter/basic-project/agent-run.jsonc +1 -1
- package/package.json +2 -3
- package/scripts/install-systemd-jobs.sh +1 -8
- /package/examples/basic-config/agent-config/starter/basic-project/{overrides/triage.SKILL.md.njk → skills/triage/SKILL.md.njk} +0 -0
package/CHANGES
CHANGED
|
@@ -1,6 +1,40 @@
|
|
|
1
1
|
CHANGES
|
|
2
2
|
=======
|
|
3
3
|
|
|
4
|
+
Version 0.99.33 (2026-09-06)
|
|
5
|
+
----------------------------
|
|
6
|
+
* Run the agent-brain MCP server as `agent-brain` from `PATH` and choose its
|
|
7
|
+
socket when the bridge starts, so generated agent configuration no longer
|
|
8
|
+
contains the path of whichever install rendered it. Re-render existing
|
|
9
|
+
profiles with `agent-run update`.
|
|
10
|
+
* Let the git guard through for throwaway test repositories: `commit`, `tag`
|
|
11
|
+
and `push` run normally below a directory holding an `.agent-run-test-repo`
|
|
12
|
+
marker, so a project's own test suite works inside an agent session.
|
|
13
|
+
* Match the git guard on the subcommand instead of any argument, so
|
|
14
|
+
`git log --grep push` or a branch named `commit` is no longer blocked.
|
|
15
|
+
* Fix the Windows guard shims: strip a leading PATH entry only when it is the
|
|
16
|
+
guard directory, resolve the real command explicitly, and refuse recursive
|
|
17
|
+
resolution.
|
|
18
|
+
* Archive closed review findings to a compact history file and add
|
|
19
|
+
`review_history` for recalling them without them appearing in normal
|
|
20
|
+
knowledge retrieval.
|
|
21
|
+
* Keep brain retrieval working when a single knowledge or task file is
|
|
22
|
+
malformed, reporting the broken file instead of failing every read.
|
|
23
|
+
* Rebuild the search index only when the underlying files change, so read-only
|
|
24
|
+
retrieval no longer writes on every call.
|
|
25
|
+
* Run brain Git operations against the real git binary rather than the guard
|
|
26
|
+
shim, and stage `templates/` with `sync save`.
|
|
27
|
+
* Render skills through a loader restricted to the skills directories, so a
|
|
28
|
+
skill template cannot include files elsewhere in the configuration root.
|
|
29
|
+
* Omit disabled MCP servers from Codex and Grok configuration, as Claude and
|
|
30
|
+
Gemini already do.
|
|
31
|
+
* Remove `AGENT_WRAPPER_FORCE_PERMISSIVE`, the undocumented environment
|
|
32
|
+
variable that disabled Claude's permission prompts.
|
|
33
|
+
* Stop overwriting systemd units and helper scripts in the configuration root
|
|
34
|
+
on every run.
|
|
35
|
+
* Remove `scripts/release-check.sh`. `pnpm release` now only creates and pushes
|
|
36
|
+
the tag, and `pnpm run release:preflight` is a clean build plus the tests.
|
|
37
|
+
|
|
4
38
|
Version 0.99.32 (2026-09-06)
|
|
5
39
|
----------------------------
|
|
6
40
|
* Add severity, an assigned C1/H1/M1/L1 label, and an open/fixed/wontfix state
|
package/README.md
CHANGED
|
@@ -206,6 +206,23 @@ Codex and Grok and are omitted from Claude and Gemini.
|
|
|
206
206
|
`local.md.njk` and `overrides/` are not created unless they contain actual
|
|
207
207
|
profile-specific configuration.
|
|
208
208
|
|
|
209
|
+
## Guard Shims
|
|
210
|
+
|
|
211
|
+
`blockGitWrite`, `blockPublish`, and `blockGithubRelease` put small `git`, `npm`,
|
|
212
|
+
`pnpm`, and `gh` shims on the launched agent's `PATH`. They refuse
|
|
213
|
+
`git commit`, `git tag`, `git push`, `npm publish`, `pnpm publish`, and
|
|
214
|
+
`gh release create` with exit code 42 and pass everything else through to the
|
|
215
|
+
real command. Set `AGENT_RUN_ALLOW_GIT_WRITE=1`, `AGENT_RUN_ALLOW_PUBLISH=1`, or
|
|
216
|
+
`AGENT_RUN_ALLOW_GITHUB_RELEASE=1` for a single invocation you have reviewed.
|
|
217
|
+
|
|
218
|
+
Throwaway repositories that a test suite creates and deletes are exempt from the
|
|
219
|
+
`git` shim. Create an empty `.agent-run-test-repo` file in the repository, or in
|
|
220
|
+
any directory above it, and `git commit`, `git tag`, and `git push` run normally
|
|
221
|
+
for anything below that marker. The shim reads the marker from the directory
|
|
222
|
+
`git` would work in, following any leading `-C` options. This is what lets
|
|
223
|
+
`pnpm test` run inside an agent session; nothing outside a marked directory is
|
|
224
|
+
affected.
|
|
225
|
+
|
|
209
226
|
## Profile Resolution
|
|
210
227
|
|
|
211
228
|
The mapped path is:
|
|
@@ -597,11 +614,25 @@ and restore the previous unconfigured-project behavior.
|
|
|
597
614
|
reports omitted items. Use `get_knowledge` to read complete items when necessary.
|
|
598
615
|
Always-recalled knowledge is considered first, then matching file patterns,
|
|
599
616
|
then lexical results. Search uses FTS ranking with extra weight for titles and tags.
|
|
617
|
+
Queries compare the current files with the index and write only added, changed,
|
|
618
|
+
or removed entries. Unchanged queries do not take a database write lock. Context
|
|
619
|
+
retrieval scans the files once. Search and context MCP tools declare that they
|
|
620
|
+
may write because they can refresh this derived index.
|
|
600
621
|
Symbols contribute search terms. Manual Markdown changes are read on every query;
|
|
601
622
|
the index can be deleted while the service is stopped and rebuilt on the next query.
|
|
602
623
|
|
|
603
624
|
### Knowledge and skills
|
|
604
625
|
|
|
626
|
+
Malformed knowledge and task files do not block other entries. When a scan finds
|
|
627
|
+
problems, MCP and CLI list/search responses contain `items` and `brokenFiles`;
|
|
628
|
+
object responses, including context, gain a `brokenFiles` field. Each error lists
|
|
629
|
+
the source path, `status: "broken"`, a reason, and an ID when it can be parsed.
|
|
630
|
+
Responses without errors keep their existing format. Broken entries are excluded
|
|
631
|
+
from search and context, and reads or updates of those entries are refused.
|
|
632
|
+
Correct the file on disk; the next scan picks up the repair automatically.
|
|
633
|
+
Duplicate IDs flag all matching files. External task imports and new review
|
|
634
|
+
numbering stop when broken files would make deduplication or numbering unreliable.
|
|
635
|
+
|
|
605
636
|
Knowledge is one Markdown file per item, with YAML metadata:
|
|
606
637
|
|
|
607
638
|
```markdown
|
|
@@ -642,7 +673,7 @@ project. The brain-memory skill explains these choices to native agents.
|
|
|
642
673
|
|
|
643
674
|
Available MCP tools include `get_context`, `search_knowledge`, `get_knowledge`,
|
|
644
675
|
`remember`, `amend_knowledge`, `promote`, `deprecate_knowledge`, `review_context`,
|
|
645
|
-
`review_list`, and `
|
|
676
|
+
`review_list`, `resolve_review`, `review_history`, and `review_archive`.
|
|
646
677
|
Writes use atomic file replacement and a shared lock; concurrent writes either
|
|
647
678
|
complete or report that the caller should retry. After a crashed writer, stop
|
|
648
679
|
the service, verify the PID in `runtime/brain-write.lock` is no longer running,
|
|
@@ -699,8 +730,19 @@ agent-brain review resolve ITEM_ID --revision REVISION --state fixed "Fixed by u
|
|
|
699
730
|
`review_list` returns findings in severity then number order, open ones only
|
|
700
731
|
unless states are given, each with the revision needed to resolve it.
|
|
701
732
|
`resolve_review` records the state and reason and takes the finding out of active
|
|
702
|
-
recall
|
|
703
|
-
|
|
733
|
+
recall. It writes a short record (label, title, outcome, date, reason, and original
|
|
734
|
+
file/revision) to the scope's `review-history.jsonl`, then deletes the full finding
|
|
735
|
+
file. `review-counters.json` preserves numbering. These files participate in brain
|
|
736
|
+
Git sync but are excluded from normal knowledge retrieval.
|
|
737
|
+
|
|
738
|
+
Use MCP `review_history` to recall old fixes, with optional `query`, `scope`, `state`,
|
|
739
|
+
`offset`, and `limit` (default 100). The CLI equivalent is
|
|
740
|
+
`agent-brain review history [query]`. Git retains full details only when the original
|
|
741
|
+
files were committed before cleanup; the MCP history is sufficient for the short fix list.
|
|
742
|
+
Use `review_archive` with a scope, or `agent-brain review archive --scope project`,
|
|
743
|
+
to compact existing files already marked `fixed` or `wontfix`. Deferred findings
|
|
744
|
+
remain open. Mark a finding `wontfix` only when the user explicitly drops it or
|
|
745
|
+
confirms it is intentional. A request to postpone an issue does not close it.
|
|
704
746
|
|
|
705
747
|
Finding labels are indexed, so `search_knowledge` for `H2` finds that finding.
|
|
706
748
|
Review files written before these fields existed keep parsing; they simply carry
|
|
@@ -719,6 +761,13 @@ A project/default skill overrides a global skill with the same name. Add
|
|
|
719
761
|
`extends: global:NAME` to include the global skill's instructions before the local
|
|
720
762
|
instructions. The existing native skill-template system continues to work.
|
|
721
763
|
|
|
764
|
+
Skill templates and their includes must stay under `global/skills/` or the active
|
|
765
|
+
project/default profile's `skills/` directory. Use config-root-relative paths such
|
|
766
|
+
as `{% include "global/skills/shared/checks.md" %}`, or `./` paths relative to the
|
|
767
|
+
including template. Nunjucks variables remain available. Symlinks are rejected,
|
|
768
|
+
and each file is limited to 1 MiB. Move snippets and skill overrides stored
|
|
769
|
+
elsewhere into these directories before rendering or loading those skills.
|
|
770
|
+
|
|
722
771
|
The installed `brain-memory`, `brain-review`, and `todo-manager` skills describe
|
|
723
772
|
retrieval, persistence, review learning, and task workflows. Review records use
|
|
724
773
|
the knowledge tools and remain inferred unless confirmed by the user.
|
|
@@ -790,9 +839,14 @@ agent-brain sync push --confirmed
|
|
|
790
839
|
|
|
791
840
|
The command without an action previews Git status and eligible files. Saving
|
|
792
841
|
includes Markdown knowledge, skills, tasks, project metadata, and `.gitignore`.
|
|
842
|
+
It also includes all regular files under each scope's `templates/` directory,
|
|
843
|
+
including nested Nunjucks templates and supporting files. Template additions,
|
|
844
|
+
edits, and deletions are synced; symlinks are rejected.
|
|
793
845
|
It excludes indexes, runtime state, secrets, `brain.jsonc`, and native agent
|
|
794
846
|
configuration. Existing staged changes block a save. Configure the remote and
|
|
795
847
|
upstream with normal Git commands using your existing credentials.
|
|
848
|
+
Brain Git operations use `AGENT_RUN_REAL_PATH` when available, so agent-session
|
|
849
|
+
Git guards do not intercept configuration sync.
|
|
796
850
|
After cloning onto another machine, run `agent-brain init` to restore generated
|
|
797
851
|
profile markers and shared templates, then update source roots in `config.yaml`
|
|
798
852
|
to match that machine.
|
|
@@ -816,7 +870,13 @@ It listens on `$XDG_RUNTIME_DIR/agent-brain.sock`, or
|
|
|
816
870
|
directory must belong to the current user and have mode `0700`; the socket has
|
|
817
871
|
mode `0600`. No TCP listener or application user database is created.
|
|
818
872
|
|
|
819
|
-
|
|
873
|
+
Generated native-agent configurations run `agent-brain mcp` through `PATH`, so
|
|
874
|
+
`agent-brain` must be installed and available there. The bridge selects the socket
|
|
875
|
+
when it starts; generated files do not depend on the installation or runtime
|
|
876
|
+
directory used to render them. Re-render existing profiles with `agent-run update`
|
|
877
|
+
to replace older commands that contain absolute installation paths.
|
|
878
|
+
|
|
879
|
+
`agent-run mcp` also bridges native-agent stdio to this socket. Use `--socket PATH`
|
|
820
880
|
for a particular service. The bridge verifies the socket's ownership and private
|
|
821
881
|
permissions. A missing or unreachable socket is an error; start the service
|
|
822
882
|
before connecting. There is no standalone stdio server or TCP fallback. Apicore
|
|
@@ -863,6 +923,9 @@ Transport and connector implementations follow the
|
|
|
863
923
|
## Systemd Jobs
|
|
864
924
|
|
|
865
925
|
The repo includes optional systemd units for keeping global AI tooling current.
|
|
926
|
+
Run the installer from the agent-run checkout. Setup, generation, and updates do
|
|
927
|
+
not copy systemd units or helper scripts into `.agent-run`. Remove legacy copies
|
|
928
|
+
there once any custom changes have been moved to your maintained scripts.
|
|
866
929
|
|
|
867
930
|
Install the global AI tools updater:
|
|
868
931
|
|
|
@@ -933,6 +996,11 @@ After updating `package.json` and `CHANGES`, run:
|
|
|
933
996
|
pnpm release
|
|
934
997
|
```
|
|
935
998
|
|
|
936
|
-
This
|
|
937
|
-
|
|
938
|
-
|
|
999
|
+
This creates the annotated tag matching the package version and pushes that tag
|
|
1000
|
+
to `origin`. It stops if the tag already exists, and removes the local tag again
|
|
1001
|
+
if the push fails. The tag push triggers the release workflow, which runs the
|
|
1002
|
+
tests and build, packs, publishes to npm, and creates the GitHub release.
|
|
1003
|
+
|
|
1004
|
+
`pnpm run release:preflight` is the local gate to run first: a clean build and
|
|
1005
|
+
the full test suite. Update `package.json` and `CHANGES` yourself; nothing
|
|
1006
|
+
checks them for you.
|
package/dist/agent-run.js
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.parseEditorCommand = exports.resolveProfile = exports.resolveAgentDir = exports.findProjectRoot = exports.
|
|
4
|
+
exports.parseEditorCommand = exports.resolveProfile = exports.resolveAgentDir = exports.findProjectRoot = exports.defaultConfigRoot = exports.parseInvocation = void 0;
|
|
5
5
|
const path = require("path");
|
|
6
6
|
const commands_1 = require("./commands");
|
|
7
7
|
var cli_1 = require("./cli");
|
|
8
8
|
Object.defineProperty(exports, "parseInvocation", { enumerable: true, get: function () { return cli_1.parseInvocation; } });
|
|
9
9
|
var project_1 = require("./project");
|
|
10
10
|
Object.defineProperty(exports, "defaultConfigRoot", { enumerable: true, get: function () { return project_1.defaultConfigRoot; } });
|
|
11
|
-
Object.defineProperty(exports, "defaultConfigRootSearchCandidates", { enumerable: true, get: function () { return project_1.defaultConfigRootSearchCandidates; } });
|
|
12
11
|
Object.defineProperty(exports, "findProjectRoot", { enumerable: true, get: function () { return project_1.findProjectRoot; } });
|
|
13
12
|
Object.defineProperty(exports, "resolveAgentDir", { enumerable: true, get: function () { return project_1.resolveAgentDir; } });
|
|
14
13
|
Object.defineProperty(exports, "resolveProfile", { enumerable: true, get: function () { return project_1.resolveProfile; } });
|
package/dist/agents/claude.js
CHANGED
|
@@ -51,13 +51,9 @@ exports.claudeAdapter = {
|
|
|
51
51
|
generate,
|
|
52
52
|
spawn(runtime, options) {
|
|
53
53
|
(0, shared_1.assertRuntimeFile)('claude', runtime.instructionFile, runtime.context.profileDir);
|
|
54
|
-
const forcedPermissionArgs = process.env.AGENT_WRAPPER_FORCE_PERMISSIVE === '1' &&
|
|
55
|
-
typeof process.getuid === 'function' && process.getuid() !== 0
|
|
56
|
-
? ['--permission-mode', 'bypassPermissions']
|
|
57
|
-
: [];
|
|
58
54
|
const runtimeArgs = options.wrapperArgs.sandboxMode === 'danger'
|
|
59
55
|
? ['--dangerously-skip-permissions']
|
|
60
|
-
:
|
|
56
|
+
: [];
|
|
61
57
|
return {
|
|
62
58
|
command: options.binary,
|
|
63
59
|
args: [
|
package/dist/brain/cli.js
CHANGED
|
@@ -39,6 +39,8 @@ async function brainMain(argv, wrapperCommand) {
|
|
|
39
39
|
'agent-brain deprecate <id> --revision <hash> [reason]',
|
|
40
40
|
'agent-brain skills [name]',
|
|
41
41
|
'agent-brain review list [--severity <level>] [--state <state>]',
|
|
42
|
+
'agent-brain review history [query] [--scope <scope>] [--state fixed|wontfix]',
|
|
43
|
+
'agent-brain review archive --scope <scope>',
|
|
42
44
|
'agent-brain review resolve <id> --revision <hash> --state fixed|wontfix [reason]',
|
|
43
45
|
'agent-brain todo list | get <id> | add --json <task-object>',
|
|
44
46
|
'agent-brain todo update <id> --revision <hash> --json <changes>',
|
|
@@ -100,48 +102,55 @@ async function brainMain(argv, wrapperCommand) {
|
|
|
100
102
|
throw new Error('--revision is required; read the item first'); return values.revision; };
|
|
101
103
|
const json = () => { if (!values.json)
|
|
102
104
|
throw new Error('--json is required'); return JSON.parse(values.json); };
|
|
105
|
+
const report = (value) => print(store.withDiagnostics(value));
|
|
103
106
|
try {
|
|
104
107
|
switch (command) {
|
|
105
108
|
case 'status':
|
|
106
|
-
|
|
109
|
+
report({ configRoot: root, enabled: (0, config_1.readBrainConfig)(root)?.enabled ?? false, profile: store.profile, scopes: store.scopes, knowledge: store.allKnowledge().length, todos: (0, todos_1.listTodos)(store).length });
|
|
107
110
|
break;
|
|
108
111
|
case 'search':
|
|
109
|
-
|
|
112
|
+
report(store.search(positionals.join(' ')));
|
|
110
113
|
break;
|
|
111
114
|
case 'context':
|
|
112
|
-
|
|
115
|
+
report(store.context(positionals.join(' ')));
|
|
113
116
|
break;
|
|
114
117
|
case 'get':
|
|
115
|
-
|
|
118
|
+
report(store.get(requireId()));
|
|
116
119
|
break;
|
|
117
120
|
case 'remember':
|
|
118
|
-
|
|
121
|
+
report(store.remember(store_1.knowledgeInput.parse(json())));
|
|
119
122
|
break;
|
|
120
123
|
case 'promote':
|
|
121
124
|
if (!values.confirmed)
|
|
122
125
|
throw new Error('Promotion requires --confirmed after explicit user approval');
|
|
123
|
-
|
|
126
|
+
report(store.promote(requireId(), store_1.scopeSchema.parse(values.scope), requireRevision()));
|
|
124
127
|
break;
|
|
125
128
|
case 'amend':
|
|
126
|
-
|
|
129
|
+
report(store.amend(requireId(), requireRevision(), store_1.knowledgeChanges.parse(json())));
|
|
127
130
|
break;
|
|
128
131
|
case 'deprecate':
|
|
129
|
-
|
|
132
|
+
report(store.deprecate(requireId(), requireRevision(), positionals.slice(1).join(' ')));
|
|
130
133
|
break;
|
|
131
134
|
case 'skills':
|
|
132
|
-
|
|
135
|
+
report(positionals[0] ? (0, skills_1.getSkill)(store, positionals[0]) : (0, skills_1.listSkills)(store));
|
|
133
136
|
break;
|
|
134
137
|
case 'review': {
|
|
135
138
|
const operation = positionals.shift() ?? 'list';
|
|
136
139
|
if (operation === 'list') {
|
|
137
|
-
|
|
140
|
+
report(store.reviews({
|
|
138
141
|
...(values.severity ? { severity: [store_1.severitySchema.parse(values.severity)] } : {}),
|
|
139
142
|
...(values.state ? { state: [store_1.reviewStateSchema.parse(values.state)] } : {})
|
|
140
143
|
}).map((item) => ({ finding: item.finding, severity: item.severity, state: item.state ?? 'open', title: item.title, id: item.id, revision: item.revision })));
|
|
141
144
|
}
|
|
145
|
+
else if (operation === 'history') {
|
|
146
|
+
report(store.reviewHistory({ query: positionals.join(' '), ...(values.scope ? { scope: store_1.scopeSchema.parse(values.scope) } : {}), ...(values.state ? { state: zod_1.z.enum(['fixed', 'wontfix']).parse(values.state) } : {}), limit: Number.MAX_SAFE_INTEGER }));
|
|
147
|
+
}
|
|
148
|
+
else if (operation === 'archive') {
|
|
149
|
+
report(store.archiveReviews(store_1.scopeSchema.parse(values.scope)));
|
|
150
|
+
}
|
|
142
151
|
else if (operation === 'resolve') {
|
|
143
152
|
const state = zod_1.z.enum(['fixed', 'wontfix']).parse(values.state);
|
|
144
|
-
|
|
153
|
+
report(store.resolveReview(requireId(), requireRevision(), state, positionals.slice(1).join(' ') || undefined));
|
|
145
154
|
}
|
|
146
155
|
else
|
|
147
156
|
throw new Error('Usage: agent-brain review list | resolve <id> --revision <hash> --state fixed|wontfix');
|
|
@@ -150,30 +159,30 @@ async function brainMain(argv, wrapperCommand) {
|
|
|
150
159
|
case 'sync': {
|
|
151
160
|
const action = positionals[0];
|
|
152
161
|
if (!action) {
|
|
153
|
-
|
|
162
|
+
report((0, git_1.gitPreview)(store));
|
|
154
163
|
break;
|
|
155
164
|
}
|
|
156
165
|
if (!['init', 'save', 'pull', 'push'].includes(action))
|
|
157
166
|
throw new Error('Unknown sync action');
|
|
158
167
|
if (!values.confirmed)
|
|
159
168
|
throw new Error('Review agent-brain sync output and obtain authorization before using --confirmed');
|
|
160
|
-
|
|
169
|
+
report((0, git_1.syncGit)(store, action, values.message));
|
|
161
170
|
break;
|
|
162
171
|
}
|
|
163
172
|
case 'todo': {
|
|
164
173
|
const operation = positionals.shift();
|
|
165
174
|
if (operation === 'list')
|
|
166
|
-
|
|
175
|
+
report((0, todos_1.listTodos)(store));
|
|
167
176
|
else if (operation === 'get')
|
|
168
|
-
|
|
177
|
+
report((0, todos_1.getTodo)(store, requireId()));
|
|
169
178
|
else if (operation === 'add')
|
|
170
|
-
|
|
179
|
+
report((0, todos_1.addTodo)(store, todos_1.todoInput.parse(json())));
|
|
171
180
|
else if (operation === 'update')
|
|
172
|
-
|
|
181
|
+
report((0, todos_1.updateTodo)(store, requireId(), requireRevision(), todos_1.todoChanges.parse(json())));
|
|
173
182
|
else if (operation === 'complete')
|
|
174
|
-
|
|
183
|
+
report((0, todos_1.updateTodo)(store, requireId(), requireRevision(), { status: 'done' }));
|
|
175
184
|
else if (operation === 'import' || operation === 'sync')
|
|
176
|
-
|
|
185
|
+
report(await (0, connectors_1.syncConnector)(store, requireId(), store_1.scopeSchema.parse(values.scope)));
|
|
177
186
|
else
|
|
178
187
|
throw new Error('Unknown todo command; use agent-brain --help');
|
|
179
188
|
break;
|
|
@@ -181,11 +190,11 @@ async function brainMain(argv, wrapperCommand) {
|
|
|
181
190
|
case 'project': {
|
|
182
191
|
const operation = positionals[0];
|
|
183
192
|
if (operation === 'list')
|
|
184
|
-
|
|
193
|
+
report((0, config_1.listBrainProjects)(root));
|
|
185
194
|
else if (operation === 'info')
|
|
186
|
-
|
|
195
|
+
report({ profile: store.profile, ...(0, projects_1.inspectProject)(cwd) });
|
|
187
196
|
else if (operation === 'bootstrap')
|
|
188
|
-
|
|
197
|
+
report({ status: 'proposal', message: 'Inspect the listed documents to propose architecture, constraints, conventions, and specs. Confirm proposals before saving authoritative knowledge.', ...(0, projects_1.inspectProject)(cwd) });
|
|
189
198
|
else
|
|
190
199
|
throw new Error('Usage: agent-run project list | info | bootstrap');
|
|
191
200
|
break;
|
package/dist/brain/git.js
CHANGED
|
@@ -11,7 +11,7 @@ const store_1 = require("./store");
|
|
|
11
11
|
const config_1 = require("./config");
|
|
12
12
|
function git(root, args) {
|
|
13
13
|
const result = (0, node_child_process_1.spawnSync)('git', ['-C', root, ...args], { encoding: 'utf8', shell: false, timeout: 60000,
|
|
14
|
-
env: { ...process.env, GIT_TERMINAL_PROMPT: '0' }, maxBuffer: 16 * 1024 * 1024 });
|
|
14
|
+
env: { ...process.env, PATH: process.env.AGENT_RUN_REAL_PATH ?? process.env.PATH, GIT_TERMINAL_PROMPT: '0' }, maxBuffer: 16 * 1024 * 1024 });
|
|
15
15
|
if (result.error || result.status !== 0)
|
|
16
16
|
throw new Error(`git ${args[0]} failed: ${result.error?.message ?? result.stderr.trim()}`);
|
|
17
17
|
return result.stdout.trimEnd();
|
|
@@ -40,12 +40,18 @@ function syncableFiles(store) {
|
|
|
40
40
|
for (const root of roots) {
|
|
41
41
|
for (const folder of folders)
|
|
42
42
|
files.push(...store.files(path.join(root, folder)));
|
|
43
|
-
|
|
43
|
+
files.push(...store.files(path.join(root, 'templates'), { includeAllFiles: true }));
|
|
44
|
+
for (const name of ['config.yaml', 'project.md', 'review-history.jsonl', 'review-counters.json']) {
|
|
44
45
|
const file = store.safePath(root, name);
|
|
45
46
|
if (fs.existsSync(file))
|
|
46
47
|
files.push(file);
|
|
47
48
|
}
|
|
48
49
|
}
|
|
50
|
+
if (fs.existsSync(path.join(store.configRoot, '.git'))) {
|
|
51
|
+
const removable = roots.flatMap((root) => ['templates', 'reviews'].map((folder) => path.relative(store.configRoot, path.join(root, folder))));
|
|
52
|
+
const deleted = git(store.configRoot, ['ls-files', '--deleted', '-z', '--', ...removable]).split('\0').filter(Boolean);
|
|
53
|
+
files.push(...deleted.map((file) => store.safePath(file)));
|
|
54
|
+
}
|
|
49
55
|
const ignore = store.safePath('.gitignore');
|
|
50
56
|
if (fs.existsSync(ignore))
|
|
51
57
|
files.push(ignore);
|
package/dist/brain/projects.js
CHANGED
|
@@ -21,7 +21,7 @@ const builtInSkills = {
|
|
|
21
21
|
},
|
|
22
22
|
'brain-review': {
|
|
23
23
|
description: 'Review changes against project constraints and previous findings, and save confirmed review knowledge.',
|
|
24
|
-
content: 'Call review_context with the task and changed paths. Check correctness, security, architecture, and meaningful test coverage against relevant constraints. Report concrete, actionable findings with severity and file references. If authorized to save the review, call remember with type=review. Keep suspected hazards marked inferred. Never convert a finding into an authoritative constraint without user confirmation.'
|
|
24
|
+
content: 'Call review_context with the task and changed paths. Check correctness, security, architecture, and meaningful test coverage against relevant constraints. Report concrete, actionable findings with severity and file references. If authorized to save the review, call remember with type=review. Keep suspected hazards marked inferred. Never convert a finding into an authoritative constraint without user confirmation. Use review_list for open findings. After completing an authorized fix and relevant checks, resolve_review as fixed with a short explanation and verification result; use wontfix only for an explicit user decision to drop the finding. Postponed findings stay open. Closing saves compact history and removes the full finding file. Recall old fixes through review_history, not normal knowledge search. Use review_archive to compact already closed files when cleanup is requested. Do not create separate done lists or full archived review copies.'
|
|
25
25
|
},
|
|
26
26
|
'todo-manager': {
|
|
27
27
|
description: 'Manage local tasks and import externally tracked tasks without duplicates.',
|
package/dist/brain/server.js
CHANGED
|
@@ -26,9 +26,11 @@ function createBrainServer(store) {
|
|
|
26
26
|
function tool(name, description, shape, readOnly, handler) {
|
|
27
27
|
const schema = zod_1.z.object(shape).strict();
|
|
28
28
|
server.registerTool(name, { description, inputSchema: schema, annotations: { readOnlyHint: readOnly, destructiveHint: false, openWorldHint: false } }, async (input) => {
|
|
29
|
+
store.knowledgeErrors.length = 0;
|
|
30
|
+
store.todoErrors.length = 0;
|
|
29
31
|
try {
|
|
30
32
|
const result = await handler(zod_1.z.object(shape).strict().parse(input));
|
|
31
|
-
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
|
|
33
|
+
return { content: [{ type: 'text', text: JSON.stringify(store.withDiagnostics(result)) }] };
|
|
32
34
|
}
|
|
33
35
|
catch (error) {
|
|
34
36
|
return { isError: true, content: [{ type: 'text', text: error instanceof Error ? error.message : String(error) }] };
|
|
@@ -52,11 +54,11 @@ function createBrainServer(store) {
|
|
|
52
54
|
});
|
|
53
55
|
tool('get_context', 'Retrieve relevant constraints and knowledge before substantial work. Check omitted; explicitly fetch required knowledge if the budget excluded it.', {
|
|
54
56
|
task: zod_1.z.string().max(10000), files: zod_1.z.array(zod_1.z.string()).max(100).optional(), symbols: zod_1.z.array(zod_1.z.string()).max(100).optional()
|
|
55
|
-
},
|
|
57
|
+
}, false, ({ task, files, symbols }) => store.context(task, files, symbols));
|
|
56
58
|
tool('search_knowledge', 'Search Markdown knowledge in the active scopes using SQLite FTS.', {
|
|
57
59
|
query: zod_1.z.string().max(10000), scopes: zod_1.z.array(store_1.scopeSchema).optional(), types: zod_1.z.array(store_1.typeSchema).optional(),
|
|
58
60
|
includeDeprecated: zod_1.z.boolean().optional(), limit: zod_1.z.number().int().min(1).max(100).optional()
|
|
59
|
-
},
|
|
61
|
+
}, false, ({ query, ...options }) => store.search(query, options));
|
|
60
62
|
tool('get_knowledge', 'Read a complete knowledge item and its current revision.', { id }, true, ({ id }) => store.get(id));
|
|
61
63
|
tool('remember', 'Persist durable knowledge when authorized by the user. Choose scope and a singular type; the server stores it in the matching directory (for example constraint in constraints/, preference in preferences/). Explicit user instructions have authority=user; deductions remain inferred observations. Global writes require explicit global intent. Use todo tools for tasks; skills and templates are separate files.', store_1.knowledgeInput.shape, false, (input) => store.remember(input));
|
|
62
64
|
tool('amend_knowledge', 'Revise an active item in place using its last-read revision, keeping its id, file name, scope, type, authority, and history. Use this to correct or extend existing knowledge instead of writing a near-duplicate. Scope, type, authority, and review severity cannot be changed this way.', {
|
|
@@ -70,14 +72,21 @@ function createBrainServer(store) {
|
|
|
70
72
|
tool('get_skill', 'Load a full skill only when it applies to the current task.', { name: zod_1.z.string().max(100) }, true, ({ name }) => (0, skills_1.getSkill)(store, name));
|
|
71
73
|
tool('review_context', 'Retrieve constraints, decisions, prior review findings, and path-specific knowledge for a code review.', {
|
|
72
74
|
task: zod_1.z.string().max(10000), files: zod_1.z.array(zod_1.z.string()).max(100).optional()
|
|
73
|
-
},
|
|
74
|
-
tool('review_list', 'List review findings in severity then number order. Open findings only unless states are given. Each finding carries its label, severity, state, and the revision needed to resolve it.', {
|
|
75
|
+
}, false, ({ task, files }) => store.context(`review constraint decision ${task}`, files));
|
|
76
|
+
tool('review_list', 'List review findings in severity then number order. Open findings only unless states are given; archived findings are in review_history. Each finding carries its label, severity, state, and the revision needed to resolve it.', {
|
|
75
77
|
severity: zod_1.z.array(store_1.severitySchema).optional(), state: zod_1.z.array(store_1.reviewStateSchema).optional()
|
|
76
78
|
}, true, ({ severity, state }) => store.reviews({ severity, state }).map((item) => ({
|
|
77
79
|
finding: item.finding, severity: item.severity, state: item.state ?? 'open', title: item.title,
|
|
78
80
|
applies_to: item.applies_to, id: item.id, revision: item.revision
|
|
79
81
|
})));
|
|
80
|
-
tool('
|
|
82
|
+
tool('review_history', 'Recall compact closed review records by label, ID, title, or reason. Excluded from normal knowledge retrieval; newest first, with pagination.', {
|
|
83
|
+
scope: store_1.scopeSchema.optional(), query: zod_1.z.string().max(1000).optional(), state: zod_1.z.enum(['fixed', 'wontfix']).optional(),
|
|
84
|
+
offset: zod_1.z.number().int().min(0).optional(), limit: zod_1.z.number().int().min(1).max(1000).optional()
|
|
85
|
+
}, true, (options) => store.reviewHistory(options));
|
|
86
|
+
tool('review_archive', 'Compact already finished review files in one scope into closure history and remove their full files. Leaves unresolved and deferred findings alone.', {
|
|
87
|
+
scope: store_1.scopeSchema
|
|
88
|
+
}, false, ({ scope }) => store.archiveReviews(scope));
|
|
89
|
+
tool('resolve_review', 'Close a review finding as fixed or wontfix, preserving a compact closure record and deleting the full finding file. Supply the revision you last read. Use wontfix only when the user has said the finding is intentional.', {
|
|
81
90
|
id, revision, state: zod_1.z.enum(['fixed', 'wontfix']), reason: zod_1.z.string().max(2000).optional()
|
|
82
91
|
}, false, ({ id, revision, state, reason }) => store.resolveReview(id, revision, state, reason));
|
|
83
92
|
tool('todo_list', 'List structured tasks in the active scopes.', { status: todos_1.todoInput.shape.status.optional() }, true, ({ status }) => (0, todos_1.listTodos)(store).filter((item) => !status || item.status === status));
|
package/dist/brain/skills.js
CHANGED
|
@@ -19,11 +19,12 @@ function getSkills(store) {
|
|
|
19
19
|
const directory = store.scopeDirectory(store.profile ? 'project' : 'default');
|
|
20
20
|
const env = (0, templates_1.createNunjucksEnv)(store.configRoot);
|
|
21
21
|
const context = (0, templates_1.buildRenderContext)(store.projectRoot, directory, store.configRoot, (0, manifest_1.normalizeManifest)((0, manifest_1.loadManifest)(store.configRoot, directory, profile), profile), env);
|
|
22
|
+
const skillEnv = (0, templates_1.createSkillNunjucksEnv)(store.configRoot, directory);
|
|
22
23
|
const skills = new Map();
|
|
23
24
|
const globalSkills = new Map();
|
|
24
25
|
for (const { scope, directory } of store.scopes) {
|
|
25
26
|
for (const file of store.files(path.join(directory, 'skills')).filter((file) => path.basename(file) === 'SKILL.md')) {
|
|
26
|
-
const text =
|
|
27
|
+
const text = skillEnv.render(path.relative(store.configRoot, file), context);
|
|
27
28
|
const document = (0, store_1.readMarkdown)(text);
|
|
28
29
|
const parsed = metadata.parse(document.metadata);
|
|
29
30
|
if (path.basename(path.dirname(file)) !== parsed.name)
|