@technomoron/agent-run 0.99.37 → 0.99.39

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 CHANGED
@@ -1,6 +1,21 @@
1
1
  CHANGES
2
2
  =======
3
3
 
4
+ Version 0.99.39 (2026-09-11)
5
+ ----------------------------
6
+ * Fix the AI tools updater failing under sudo or a root shell with inherited
7
+ SUDO_USER when configuring root-owned pnpm paths.
8
+ * Allow Claude, Grok, and esbuild install hooks during global npm updates,
9
+ and remove the duplicate manual Claude install step.
10
+
11
+ Version 0.99.38 (2026-09-11)
12
+ ----------------------------
13
+ * Add agent-brain pull, push, and save commands without --confirmed. Save
14
+ generates a commit message and pushes the saved changes.
15
+ * Make agent-brain sync commit, pull, and push; move the preview to sync status.
16
+ * Report completed sync steps and preserve useful progress details on failure.
17
+ * Allow prompt-driven MCP sync without a supplied message or confirmation field.
18
+
4
19
  Version 0.99.37 (2026-09-11)
5
20
  ----------------------------
6
21
  * Skip brain lookups for ordinary questions unless saved knowledge is needed,
package/README.md CHANGED
@@ -939,15 +939,25 @@ a local task does not close a GitHub issue or modify a Trello card.
939
939
  ### Git portability
940
940
 
941
941
  ```sh
942
- agent-brain sync
943
- agent-brain sync init --confirmed
944
- agent-brain sync save --confirmed --message "Your approved commit message"
945
- agent-brain sync pull --confirmed
946
- agent-brain sync push --confirmed
942
+ agent-brain sync status # Preview Git status and eligible files
943
+ agent-brain sync init # Initialize the configuration Git repository
944
+ agent-brain save # Commit local brain changes and push
945
+ agent-brain pull # Pull remote changes with rebase
946
+ agent-brain push # Push existing commits
947
+ agent-brain sync # Commit local changes, pull, then push
947
948
  ```
948
949
 
949
- The command without an action previews Git status and eligible files. Saving
950
- includes Markdown knowledge, skills, tasks, project metadata, and `.gitignore`.
950
+ Commands need no `--confirmed` flag. Saving generates a commit message from the
951
+ number of changed files; use `--message "Your message"` to override it. Output
952
+ reports completed steps, including a local commit if a later pull or push fails.
953
+ Existing `agent-brain sync save|pull|push` forms remain supported. Bare
954
+ `agent-brain sync` now performs synchronization; use `sync status` for a preview.
955
+
956
+ From an agent prompt, ask to "save brain changes" or "sync brain". The MCP `sync`
957
+ tool accepts the same actions and an optional message, without a confirmation
958
+ parameter. Agents still need a user request or standing authorization to sync.
959
+
960
+ Saving includes Markdown knowledge, skills, tasks, project metadata, and `.gitignore`.
951
961
  It also includes all regular files under each scope's `templates/` directory,
952
962
  including nested Nunjucks templates and supporting files. Template additions,
953
963
  edits, and deletions are synced; symlinks are rejected.
@@ -961,8 +971,10 @@ profile markers and shared templates, then update source roots in `config.yaml`
961
971
  to match that machine.
962
972
 
963
973
  The configuration directory must itself be the repository root. Pull uses rebase
964
- and reports conflicted files; resolve conflicts before syncing again. Each action
965
- requires explicit authorization. There are no automatic commits, pulls, or pushes.
974
+ and reports conflicted files; resolve the rebase before syncing again. Sync stops
975
+ on failure without pushing. Commands apply across all scopes in the configuration
976
+ repository, not just the current code project. Background commits and pushes are
977
+ not performed; the service can optionally pull at startup as described below.
966
978
 
967
979
  ### Persistent service under apicore-server
968
980
 
@@ -1058,6 +1070,12 @@ npm, pnpm, corepack, fallow, ripgrep, pm2, tsx, typescript,
1058
1070
  @xai-official/grok, and @technomoron/agent-run
1059
1071
  ```
1060
1072
 
1073
+ The updater clears inherited `SUDO_USER` for its root-owned pnpm configuration
1074
+ writes, so it works both under sudo and from a root shell. It explicitly allows
1075
+ the Claude, Grok, esbuild, and pnpm install hooks. Optional native build hooks
1076
+ such as keytar and node-pty remain subject to npm's script policy; the updater
1077
+ does not enable native compilation or require a working node-gyp installation.
1078
+
1061
1079
  The optional apt package list still defaults to `gh` and can be changed with
1062
1080
  `AI_TOOLS_APT_PACKAGES`.
1063
1081
 
package/dist/brain/cli.js CHANGED
@@ -50,7 +50,8 @@ async function brainMain(argv, wrapperCommand) {
50
50
  'agent-brain todo update <id> --revision <hash> --json <changes>',
51
51
  'agent-brain todo complete <id> --revision <hash>',
52
52
  'agent-brain todo import|sync <connector> --scope <scope>',
53
- 'agent-brain sync [init|save|pull|push --confirmed] [--message <commit-message>]',
53
+ 'agent-brain pull | push | save | sync [--message <commit-message>]',
54
+ 'agent-brain sync status | init',
54
55
  'agent-brain serve [--pull] [--socket <path>] | mcp [--socket <path>]',
55
56
  'agent-run create <name> [--quick | --guided]',
56
57
  'agent-run project list | info | bootstrap',
@@ -169,17 +170,23 @@ async function brainMain(argv, wrapperCommand) {
169
170
  throw new Error('Usage: agent-brain review list | resolve <id> --revision <hash> --state fixed|wontfix');
170
171
  break;
171
172
  }
173
+ case 'pull':
174
+ case 'push':
175
+ case 'save':
172
176
  case 'sync': {
173
- const action = positionals[0];
174
- if (!action) {
177
+ const action = command === 'sync' ? positionals.shift() ?? 'sync' : command;
178
+ if (positionals.length)
179
+ throw new Error('Unexpected arguments; use agent-brain --help');
180
+ if (action === 'status') {
175
181
  report((0, git_1.gitPreview)(store));
176
182
  break;
177
183
  }
178
- if (!['init', 'save', 'pull', 'push'].includes(action))
184
+ if (!['init', 'save', 'pull', 'push', 'sync'].includes(action))
179
185
  throw new Error('Unknown sync action');
180
- if (!values.confirmed)
181
- throw new Error('Review agent-brain sync output and obtain authorization before using --confirmed');
182
- report((0, git_1.syncGit)(store, action, values.message));
186
+ const result = (0, git_1.syncGit)(store, action, values.message);
187
+ process.stdout.write(`${result.steps.join('\n')}\n`);
188
+ if (result.files.length)
189
+ process.stdout.write(`Other local changes remain. Use agent-brain sync status to inspect them.\n`);
183
190
  break;
184
191
  }
185
192
  case 'todo': {
package/dist/brain/git.js CHANGED
@@ -59,44 +59,57 @@ function syncableFiles(store) {
59
59
  }
60
60
  function syncGit(store, action, message) {
61
61
  return store.writeLocked(() => {
62
- if (action === 'init') {
63
- if (!fs.existsSync(path.join(store.configRoot, '.git')))
64
- git(store.configRoot, ['init', '--initial-branch=main']);
65
- return gitStatus(store);
66
- }
67
- requireRepository(store);
68
- const status = gitStatus(store);
69
- if (status.state === 'conflicted')
70
- throw new Error(`Resolve Git conflicts before syncing: ${status.conflicts.join(', ')}`);
71
- if (action === 'save') {
72
- if (!message?.trim())
73
- throw new Error('Supply a commit message after reviewing the files to save.');
74
- if (git(store.configRoot, ['diff', '--cached', '--name-only']))
75
- throw new Error('The Git index already has staged changes. Commit or unstage them before saving brain knowledge.');
76
- const files = syncableFiles(store);
77
- if (!files.length)
78
- return status;
79
- git(store.configRoot, ['add', '--', ...files]);
80
- if (git(store.configRoot, ['diff', '--cached', '--name-only']))
81
- git(store.configRoot, ['commit', '-m', message]);
82
- }
83
- else if (action === 'pull') {
84
- if (git(store.configRoot, ['diff', '--name-only']) || git(store.configRoot, ['diff', '--cached', '--name-only']))
85
- throw new Error('Save or resolve local tracked configuration changes before pulling.');
86
- try {
87
- git(store.configRoot, ['pull', '--rebase']);
62
+ const steps = [];
63
+ try {
64
+ if (action === 'init') {
65
+ if (!fs.existsSync(path.join(store.configRoot, '.git')))
66
+ git(store.configRoot, ['init', '--initial-branch=main']);
67
+ return { ...gitStatus(store), steps: ['Configuration Git repository initialized.'] };
68
+ }
69
+ requireRepository(store);
70
+ const status = gitStatus(store);
71
+ if (status.state === 'conflicted')
72
+ throw new Error(`Resolve Git conflicts before syncing: ${status.conflicts.join(', ')}`);
73
+ if (action === 'save' || action === 'sync') {
74
+ if (message !== undefined && !message.trim())
75
+ throw new Error('The commit message must not be empty.');
76
+ if (git(store.configRoot, ['diff', '--cached', '--name-only']))
77
+ throw new Error('The Git index already has staged changes. Commit or unstage them before saving brain knowledge.');
78
+ const files = syncableFiles(store);
79
+ if (files.length)
80
+ git(store.configRoot, ['add', '--', ...files]);
81
+ const changed = git(store.configRoot, ['diff', '--cached', '--name-only', '-z']).split('\0').filter(Boolean);
82
+ if (changed.length) {
83
+ const commitMessage = message ?? `Update brain knowledge (${changed.length} ${changed.length === 1 ? 'file' : 'files'})`;
84
+ git(store.configRoot, ['commit', '-m', commitMessage]);
85
+ steps.push(`Saved ${changed.length} ${changed.length === 1 ? 'file' : 'files'} in commit ${git(store.configRoot, ['rev-parse', '--short', 'HEAD'])}: ${commitMessage}`);
86
+ }
87
+ else
88
+ steps.push('No local brain changes to save.');
89
+ }
90
+ if (action === 'pull' || action === 'sync') {
91
+ if (git(store.configRoot, ['diff', '--name-only']) || git(store.configRoot, ['diff', '--cached', '--name-only']))
92
+ throw new Error('Save or resolve local tracked configuration changes before pulling.');
93
+ try {
94
+ git(store.configRoot, ['-c', 'merge.autoStash=false', '-c', 'rebase.autoStash=false', 'pull', '--rebase']);
95
+ }
96
+ catch (error) {
97
+ const after = gitStatus(store);
98
+ if (after.state === 'conflicted')
99
+ throw new Error(`Pull stopped with conflicts: ${after.conflicts.join(', ')}. Resolve the rebase before syncing again.`);
100
+ throw new Error(`Pull failed: ${error instanceof Error ? error.message : String(error)}`);
101
+ }
102
+ steps.push('Pulled remote changes.');
88
103
  }
89
- catch (error) {
90
- const after = gitStatus(store);
91
- if (after.state === 'conflicted')
92
- return after;
93
- throw error;
104
+ if (action === 'push' || action === 'save' || action === 'sync') {
105
+ git(store.configRoot, ['push']);
106
+ steps.push('Push completed; remote is up to date.');
94
107
  }
108
+ return { ...gitStatus(store), steps };
95
109
  }
96
- else {
97
- git(store.configRoot, ['push']);
110
+ catch (error) {
111
+ throw new Error([...steps, error instanceof Error ? error.message : String(error)].join('\n'));
98
112
  }
99
- return gitStatus(store);
100
113
  });
101
114
  }
102
115
  function gitPreview(store) {
@@ -42,8 +42,8 @@ function createBrainServer(store) {
42
42
  const revision = zod_1.z.string().regex(/^[a-f0-9]{64}$/);
43
43
  tool('brain_status', 'Show the active project and available scopes.', {}, true, () => ({ profile: store.profile, scopes: store.scopes.map((entry) => entry.scope) }));
44
44
  tool('sync_status', 'Preview Git state and the canonical files eligible for saving. Runtime files, indexes, credentials, and native agent state are excluded.', {}, true, () => (0, git_1.gitPreview)(store));
45
- server.registerTool('sync', { description: 'Initialize, save, pull, or push the configuration Git repository. Review sync_status first. Requires explicit user authorization; saving also requires the approved commit message.',
46
- inputSchema: { action: zod_1.z.enum(['init', 'save', 'pull', 'push']), confirmed: zod_1.z.literal(true), message: zod_1.z.string().min(1).max(1000).optional() },
45
+ server.registerTool('sync', { description: 'Synchronize the configuration Git repository when the user requests it. save commits and pushes; sync commits, pulls with rebase, then pushes; pull and push only transfer existing commits. Commit messages are generated unless supplied. Use sync_status to inspect eligible files.',
46
+ inputSchema: { action: zod_1.z.enum(['init', 'save', 'pull', 'push', 'sync']), confirmed: zod_1.z.literal(true).optional(), message: zod_1.z.string().min(1).max(1000).optional() },
47
47
  annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true }
48
48
  }, async ({ action, message }) => {
49
49
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@technomoron/agent-run",
3
- "version": "0.99.37",
3
+ "version": "0.99.39",
4
4
  "packageManager": "pnpm@10.34.5",
5
5
  "bin": {
6
6
  "agent-run": "dist/agent-run.js",
@@ -51,22 +51,19 @@ if [ "${1:-}" = --agent-brain ]; then
51
51
  exit 0
52
52
  fi
53
53
 
54
- "$NPM_BIN" install -g --force "${packages[@]}"
54
+ "$NPM_BIN" install -g --force "${packages[@]}" --allow-scripts=@anthropic-ai/claude-code,@xai-official/grok,esbuild
55
55
  ensure_brain
56
56
 
57
57
  # corepack and pnpm both own the pnpm/pnpx shims; install pnpm last so npm's
58
58
  # current pnpm package remains the command users execute.
59
59
  "$NPM_BIN" install -g --force pnpm@latest --allow-scripts=pnpm
60
60
 
61
+ # This updater deliberately manages root-owned tooling. pnpm rejects inherited
62
+ # sudo identity even in a root shell; clear it only for these configuration writes.
61
63
  install -d -o root -g root -m 0755 /usr/local/share/pnpm/global /usr/local/share/pnpm/store /usr/local/bin
62
- "$PNPM_BIN" config set --global global-dir /usr/local/share/pnpm/global
63
- "$PNPM_BIN" config set --global global-bin-dir /usr/local/bin
64
- "$PNPM_BIN" config set --global store-dir /usr/local/share/pnpm/store
65
-
66
- CLAUDE_INSTALL="$("$NPM_BIN" root -g)/@anthropic-ai/claude-code/install.cjs"
67
- if [ -f "$CLAUDE_INSTALL" ]; then
68
- "$NODE_BIN" "$CLAUDE_INSTALL"
69
- fi
64
+ env -u SUDO_USER "$PNPM_BIN" config set --global global-dir /usr/local/share/pnpm/global
65
+ env -u SUDO_USER "$PNPM_BIN" config set --global global-bin-dir /usr/local/bin
66
+ env -u SUDO_USER "$PNPM_BIN" config set --global store-dir /usr/local/share/pnpm/store
70
67
 
71
68
  if [ -n "$AI_TOOLS_APT_PACKAGES" ] && command -v "$APT_GET_BIN" >/dev/null 2>&1; then
72
69
  read -r -a apt_packages <<<"$AI_TOOLS_APT_PACKAGES"