@technomoron/agent-run 0.99.26 → 0.99.28
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 +18 -0
- package/README.md +42 -50
- package/dist/cli.js +47 -42
- package/dist/commands.js +76 -10
- package/dist/config-tree.js +5 -2
- package/dist/constants.js +1 -1
- package/dist/project.js +40 -26
- package/dist/renderer.js +10 -3
- package/package.json +1 -1
package/CHANGES
CHANGED
|
@@ -1,6 +1,24 @@
|
|
|
1
1
|
CHANGES
|
|
2
2
|
=======
|
|
3
3
|
|
|
4
|
+
Version 0.99.28 (2026-08-30)
|
|
5
|
+
----------------------------
|
|
6
|
+
* Add interactive `agent-run setup [org/repo]`, make `agent-run generate` the
|
|
7
|
+
profile-initialization command, and retain `init` as a deprecated alias.
|
|
8
|
+
* Use `~/.agent-run` as the single default config directory, overridable with
|
|
9
|
+
`--configdir`, `AGENT_CONFIG_DIR`, or a project-local `.agent-run.env`.
|
|
10
|
+
* Resolve profiles from `package.json.repository` and avoid rewriting unchanged
|
|
11
|
+
generated files.
|
|
12
|
+
|
|
13
|
+
Version 0.99.27 (2026-08-11)
|
|
14
|
+
----------------------------
|
|
15
|
+
* Rename the unattended-run option from `--danger` to `--yolo` for Codex and
|
|
16
|
+
Claude.
|
|
17
|
+
* Add `agent-run setup [config-root]` to create the packaged starter agent
|
|
18
|
+
configuration files without overwriting local edits.
|
|
19
|
+
* Resolve plain directories without Git metadata or a package name as
|
|
20
|
+
`[parent]/[current]` profiles.
|
|
21
|
+
|
|
4
22
|
Version 0.99.26 (2026-07-31)
|
|
5
23
|
----------------------------
|
|
6
24
|
* Add `--danger` support for Claude so unattended runs pass
|
package/README.md
CHANGED
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
Small wrapper for AI coding CLIs like Codex and Claude.
|
|
4
4
|
|
|
5
5
|
It keeps agent files out of normal repos and stores them in a separate
|
|
6
|
-
agent config tree. By default, that tree lives
|
|
7
|
-
under the same code root.
|
|
6
|
+
agent config tree. By default, that tree lives at `~/.agent-run`.
|
|
8
7
|
|
|
9
8
|
## Model
|
|
10
9
|
|
|
@@ -17,31 +16,29 @@ There are three things:
|
|
|
17
16
|
Example:
|
|
18
17
|
|
|
19
18
|
```text
|
|
20
|
-
|
|
21
|
-
agent-
|
|
22
|
-
agent-run.defaults.jsonc
|
|
23
|
-
org/
|
|
24
|
-
my-api/
|
|
25
|
-
agent-run.jsonc
|
|
26
|
-
local.md.njk # optional
|
|
27
|
-
overrides/ # optional
|
|
28
|
-
live/
|
|
29
|
-
CLAUDE.md
|
|
30
|
-
.claude/
|
|
31
|
-
bin/
|
|
32
|
-
memories/
|
|
33
|
-
codex-home/
|
|
34
|
-
AGENTS.md
|
|
35
|
-
config.toml
|
|
36
|
-
skills/
|
|
19
|
+
~/.agent-run/
|
|
20
|
+
agent-run.defaults.jsonc
|
|
37
21
|
org/
|
|
38
22
|
my-api/
|
|
23
|
+
agent-run.jsonc
|
|
24
|
+
local.md.njk # optional
|
|
25
|
+
overrides/ # optional
|
|
26
|
+
live/
|
|
27
|
+
CLAUDE.md
|
|
28
|
+
.claude/
|
|
29
|
+
bin/
|
|
30
|
+
memories/
|
|
31
|
+
codex-home/
|
|
32
|
+
AGENTS.md
|
|
33
|
+
config.toml
|
|
34
|
+
skills/
|
|
35
|
+
~/source/org/my-api/
|
|
39
36
|
```
|
|
40
37
|
|
|
41
38
|
If your source repo is `~/source/org/my-api`, `agent-run` maps it to:
|
|
42
39
|
|
|
43
40
|
```text
|
|
44
|
-
|
|
41
|
+
~/.agent-run/org/my-api
|
|
45
42
|
```
|
|
46
43
|
|
|
47
44
|
The source repo stays clean. The agent files live in the matching path under
|
|
@@ -96,19 +93,24 @@ The mapped path is:
|
|
|
96
93
|
|
|
97
94
|
1. `AGENT_RUN_PROFILE` in `.agent-run.env`
|
|
98
95
|
2. GitHub `origin` remote
|
|
99
|
-
3. `package.json.
|
|
96
|
+
3. `package.json.repository` (string or object `url`)
|
|
97
|
+
4. `package.json.name`
|
|
98
|
+
5. the project path as `[parent]/[current]`
|
|
100
99
|
|
|
101
100
|
Examples:
|
|
102
101
|
|
|
103
102
|
- `AGENT_RUN_PROFILE=org/my-api` -> `org/my-api`
|
|
104
103
|
- `AGENT_RUN_PROFILE=unrelated/hello` -> `unrelated/hello`
|
|
104
|
+
- `package.json.repository = "github:org/my-api"` -> `org/my-api`
|
|
105
105
|
- `package.json.name = "@org/my-api"` -> `org/my-api`
|
|
106
106
|
- `package.json.name = "my-api"` -> `my-api`
|
|
107
|
+
- `/work/org/my-api` with none of the above -> `org/my-api`
|
|
107
108
|
|
|
108
109
|
`AGENT_RUN_PROFILE` is relative to the config root. It is not a filesystem
|
|
109
110
|
path, so values like `/tmp/foo`, `C:/tmp/foo`, or `../foo` are rejected.
|
|
110
111
|
|
|
111
|
-
|
|
112
|
+
The path fallback lets plain directories work without a Git repository or a
|
|
113
|
+
`package.json`. The explicit profile sources above always take precedence.
|
|
112
114
|
|
|
113
115
|
## Overrides
|
|
114
116
|
|
|
@@ -121,20 +123,10 @@ AGENT_RUN_PROFILE=org/my-api
|
|
|
121
123
|
|
|
122
124
|
Config root:
|
|
123
125
|
|
|
124
|
-
- default
|
|
125
|
-
- override with `--
|
|
126
|
+
- default: `~/.agent-run`
|
|
127
|
+
- override with `--configdir=/path/to/agent-configs`
|
|
126
128
|
- override with `AGENT_CONFIG_DIR=/path/to/agent-configs`
|
|
127
|
-
-
|
|
128
|
-
- or set `AGENT_CONFIG_DIR=/path/to/agent-configs` or `AGENT_CONFIG_ROOT=/path/to/agent-configs` in `.agent-run.env`
|
|
129
|
-
|
|
130
|
-
For commands without a project root, `agent-run` looks for an existing config
|
|
131
|
-
root in these locations:
|
|
132
|
-
|
|
133
|
-
- Windows: `~/Documents/code/agent-config`, `~/Documents/code/agent-configs`,
|
|
134
|
-
`~/Desktop/code/agent-config`, `~/Desktop/code/agent-configs`,
|
|
135
|
-
`C:\code\agent-config`, `C:\code\agent-configs`
|
|
136
|
-
- Unix: `~/code/agent-config`, `~/code/agent-configs`, `~/agent-config`,
|
|
137
|
-
`~/agent-configs`, `~/.agent-config`, `~/.agent-configs`
|
|
129
|
+
- or set `AGENT_CONFIG_DIR=/path/to/agent-configs` in `.agent-run.env`
|
|
138
130
|
|
|
139
131
|
## Commands
|
|
140
132
|
|
|
@@ -168,7 +160,7 @@ replace the user's normal `CLAUDE_CONFIG_DIR`. If Claude writes remembered
|
|
|
168
160
|
permissions to `.claude/settings.local.json`, `agent-run` removes that file
|
|
169
161
|
after the session unless `--local` was used.
|
|
170
162
|
|
|
171
|
-
`--
|
|
163
|
+
`--yolo` runs either tool unattended, with no approval prompts: Codex with
|
|
172
164
|
`-a never -s danger-full-access`, Claude with `--dangerously-skip-permissions`.
|
|
173
165
|
Only use it when the agent may change or delete anything it can reach. Claude
|
|
174
166
|
refuses `--dangerously-skip-permissions` when it runs as root, so run it as a
|
|
@@ -183,12 +175,12 @@ to warn and continue for a specific invocation.
|
|
|
183
175
|
Initialize mapped files for the current repo:
|
|
184
176
|
|
|
185
177
|
```sh
|
|
186
|
-
agent-run
|
|
178
|
+
agent-run generate
|
|
187
179
|
```
|
|
188
180
|
|
|
189
181
|
This creates `<config-root>/agent-run.defaults.jsonc` when needed and a minimal
|
|
190
182
|
`agent-run.jsonc` marker for the mapped profile. It does not create empty local
|
|
191
|
-
instruction or override files.
|
|
183
|
+
instruction or override files. `agent-run init` remains as a deprecated alias.
|
|
192
184
|
|
|
193
185
|
Edit the source file for the current repo:
|
|
194
186
|
|
|
@@ -206,19 +198,18 @@ agent-run update
|
|
|
206
198
|
```
|
|
207
199
|
|
|
208
200
|
`update` requires an existing profile marker or local/legacy instruction file;
|
|
209
|
-
it does not silently scaffold an unconfigured project. Use `agent-run
|
|
201
|
+
it does not silently scaffold an unconfigured project. Use `agent-run generate` or
|
|
210
202
|
the tool command's `--create` option first.
|
|
211
203
|
|
|
212
204
|
Regenerate every profile under the config root without requiring matching code
|
|
213
205
|
checkouts:
|
|
214
206
|
|
|
215
207
|
```sh
|
|
216
|
-
agent-run update --all ~/.agent-
|
|
208
|
+
agent-run update --all ~/.agent-run
|
|
217
209
|
```
|
|
218
210
|
|
|
219
211
|
If the config root argument is omitted, `update --all` uses the normal configured
|
|
220
|
-
root. Without
|
|
221
|
-
project commands, the default is `[code]/agent-config`.
|
|
212
|
+
root. Without an override, the root is `$HOME/.agent-run`.
|
|
222
213
|
|
|
223
214
|
You can also generate from a tool command and stop before launch:
|
|
224
215
|
|
|
@@ -242,27 +233,28 @@ Per-profile override templates can be placed in:
|
|
|
242
233
|
- `overrides/codex-config.toml.njk`
|
|
243
234
|
- `overrides/claude-settings.json.njk`
|
|
244
235
|
|
|
245
|
-
##
|
|
236
|
+
## Default Config Tree
|
|
246
237
|
|
|
247
|
-
A complete
|
|
238
|
+
A complete default tree lives in `examples/basic-config`. It includes a minimal
|
|
248
239
|
project, `.agent-run.env`, root manifest defaults, global templates and
|
|
249
240
|
snippets, profile-local templates, skill templates, a personal memory skill,
|
|
250
241
|
profile skill overrides, tool config overrides, guardrails, checks, and
|
|
251
242
|
generated runtime paths.
|
|
252
243
|
|
|
253
|
-
To
|
|
244
|
+
To install the default tree and detected profile in your resolved config root:
|
|
254
245
|
|
|
255
246
|
```sh
|
|
256
|
-
agent-run
|
|
247
|
+
agent-run setup
|
|
257
248
|
```
|
|
258
249
|
|
|
259
|
-
Or
|
|
250
|
+
Or select a profile non-interactively:
|
|
260
251
|
|
|
261
252
|
```sh
|
|
262
|
-
agent-run
|
|
253
|
+
agent-run setup myorg/myrepo
|
|
263
254
|
```
|
|
264
255
|
|
|
265
|
-
|
|
256
|
+
Use `--configdir=DIR` to choose a different destination. The copy skips files
|
|
257
|
+
that already exist, so local edits are preserved.
|
|
266
258
|
|
|
267
259
|
Try it from a checkout:
|
|
268
260
|
|
|
@@ -295,7 +287,7 @@ agent-run check --all ~/source
|
|
|
295
287
|
Migrate an existing config tree to the manifest/template layout:
|
|
296
288
|
|
|
297
289
|
```sh
|
|
298
|
-
agent-run migrate-config ~/.agent-
|
|
290
|
+
agent-run migrate-config ~/.agent-run
|
|
299
291
|
```
|
|
300
292
|
|
|
301
293
|
This preserves `AGENTS-MODS.md`, creates `local.md.njk` and a sparse
|
package/dist/cli.js
CHANGED
|
@@ -10,15 +10,6 @@ function parseInvocation(invokedTool, argv) {
|
|
|
10
10
|
let command = normalizeCommandName(invokedTool);
|
|
11
11
|
const options = extractGlobalOptions(argv);
|
|
12
12
|
applyGlobalOptions(options);
|
|
13
|
-
if (options.initConfig) {
|
|
14
|
-
if (options.args.length > 0) {
|
|
15
|
-
(0, utils_1.fail)('--init cannot be combined with a command');
|
|
16
|
-
}
|
|
17
|
-
return {
|
|
18
|
-
command: 'init-config',
|
|
19
|
-
targetPath: path.resolve(options.initConfigTargetPath ?? (0, project_1.defaultConfigRoot)())
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
13
|
if (command === null) {
|
|
23
14
|
command = extractCommand(options.args);
|
|
24
15
|
}
|
|
@@ -34,7 +25,7 @@ function applyGlobalOptions(options) {
|
|
|
34
25
|
}
|
|
35
26
|
function extractCommand(args) {
|
|
36
27
|
if (args.length === 0) {
|
|
37
|
-
(0, utils_1.fail)('usage: agent-run <codex|claude|check|
|
|
28
|
+
(0, utils_1.fail)('usage: agent-run <codex|claude|check|setup|generate|edit|update> [options] (run with --help for details)');
|
|
38
29
|
}
|
|
39
30
|
if (isHelpFlag(args[0])) {
|
|
40
31
|
printHelp('general');
|
|
@@ -59,6 +50,10 @@ function parseCommand(command, args) {
|
|
|
59
50
|
return parseCheckCommand(args);
|
|
60
51
|
case 'init':
|
|
61
52
|
return parseInitCommand(args);
|
|
53
|
+
case 'generate':
|
|
54
|
+
return parseGenerateCommand(args);
|
|
55
|
+
case 'setup':
|
|
56
|
+
return parseSetupCommand(args);
|
|
62
57
|
case 'edit':
|
|
63
58
|
return parseEditCommand(args);
|
|
64
59
|
case 'update':
|
|
@@ -73,8 +68,6 @@ function extractGlobalOptions(argv) {
|
|
|
73
68
|
const options = {
|
|
74
69
|
args: [],
|
|
75
70
|
configRootOverride: null,
|
|
76
|
-
initConfig: false,
|
|
77
|
-
initConfigTargetPath: null,
|
|
78
71
|
verbose: false
|
|
79
72
|
};
|
|
80
73
|
let passthrough = false;
|
|
@@ -94,26 +87,18 @@ function extractGlobalOptions(argv) {
|
|
|
94
87
|
else if (arg === '-v' || arg === '--verbose') {
|
|
95
88
|
options.verbose = true;
|
|
96
89
|
}
|
|
97
|
-
else if (arg === '--
|
|
98
|
-
options.initConfig = true;
|
|
99
|
-
const nextArg = argv[index + 1];
|
|
100
|
-
if (nextArg !== undefined && !nextArg.startsWith('-')) {
|
|
101
|
-
options.initConfigTargetPath = nextArg;
|
|
102
|
-
index += 1;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
else if (arg === '--config-root') {
|
|
90
|
+
else if (arg === '--configdir') {
|
|
106
91
|
const nextArg = argv[index + 1];
|
|
107
92
|
if (nextArg === undefined) {
|
|
108
|
-
(0, utils_1.fail)('missing value for --
|
|
93
|
+
(0, utils_1.fail)('missing value for --configdir');
|
|
109
94
|
}
|
|
110
95
|
options.configRootOverride = path.resolve(nextArg);
|
|
111
96
|
index += 1;
|
|
112
97
|
}
|
|
113
|
-
else if (arg.startsWith('--
|
|
114
|
-
const rootValue = arg.slice('--
|
|
98
|
+
else if (arg.startsWith('--configdir=')) {
|
|
99
|
+
const rootValue = arg.slice('--configdir='.length);
|
|
115
100
|
if (!rootValue) {
|
|
116
|
-
(0, utils_1.fail)('missing value for --
|
|
101
|
+
(0, utils_1.fail)('missing value for --configdir');
|
|
117
102
|
}
|
|
118
103
|
options.configRootOverride = path.resolve(rootValue);
|
|
119
104
|
}
|
|
@@ -154,7 +139,7 @@ function parseRunCommand(command, inputArgs) {
|
|
|
154
139
|
args.push(arg);
|
|
155
140
|
}
|
|
156
141
|
if (wrapperArgs.codexNetwork && wrapperArgs.sandboxMode === 'danger') {
|
|
157
|
-
(0, utils_1.fail)('--network cannot be combined with agent-run codex --
|
|
142
|
+
(0, utils_1.fail)('--network cannot be combined with agent-run codex --yolo');
|
|
158
143
|
}
|
|
159
144
|
return { args, command, wrapperArgs };
|
|
160
145
|
}
|
|
@@ -174,19 +159,19 @@ function applyCommonRunOption(arg, options) {
|
|
|
174
159
|
return true;
|
|
175
160
|
}
|
|
176
161
|
function applySandboxRunOption(command, arg, options) {
|
|
177
|
-
if (!['--
|
|
162
|
+
if (!['--yolo', '--sandboxed', '--network'].includes(arg)) {
|
|
178
163
|
return false;
|
|
179
164
|
}
|
|
180
|
-
if (arg !== '--
|
|
165
|
+
if (arg !== '--yolo' && command !== 'codex') {
|
|
181
166
|
(0, utils_1.fail)(`${arg} is only supported for agent-run codex`);
|
|
182
167
|
}
|
|
183
168
|
if (arg === '--network') {
|
|
184
169
|
options.codexNetwork = true;
|
|
185
170
|
return true;
|
|
186
171
|
}
|
|
187
|
-
const mode = arg === '--
|
|
172
|
+
const mode = arg === '--yolo' ? 'danger' : 'sandboxed';
|
|
188
173
|
if (options.sandboxMode !== null && options.sandboxMode !== mode) {
|
|
189
|
-
(0, utils_1.fail)('cannot combine --
|
|
174
|
+
(0, utils_1.fail)('cannot combine --yolo and --sandboxed');
|
|
190
175
|
}
|
|
191
176
|
options.sandboxMode = mode;
|
|
192
177
|
return true;
|
|
@@ -213,11 +198,30 @@ function parseCheckCommand(inputArgs) {
|
|
|
213
198
|
function parseInitCommand(inputArgs) {
|
|
214
199
|
return { command: 'init', targetPath: parseSinglePath(inputArgs, 'init') };
|
|
215
200
|
}
|
|
201
|
+
function parseGenerateCommand(inputArgs) {
|
|
202
|
+
return { command: 'generate', targetPath: parseSinglePath(inputArgs, 'generate') };
|
|
203
|
+
}
|
|
204
|
+
function parseSetupCommand(inputArgs) {
|
|
205
|
+
let profile = null;
|
|
206
|
+
for (const arg of inputArgs) {
|
|
207
|
+
if (isHelpFlag(arg)) {
|
|
208
|
+
printHelp('setup');
|
|
209
|
+
}
|
|
210
|
+
if (arg.startsWith('--')) {
|
|
211
|
+
(0, utils_1.fail)(`unknown setup option: ${arg}`);
|
|
212
|
+
}
|
|
213
|
+
if (profile !== null) {
|
|
214
|
+
(0, utils_1.fail)('setup accepts at most one profile argument');
|
|
215
|
+
}
|
|
216
|
+
profile = arg;
|
|
217
|
+
}
|
|
218
|
+
return { command: 'setup', profile };
|
|
219
|
+
}
|
|
216
220
|
function parseEditCommand(inputArgs) {
|
|
217
221
|
return { command: 'edit', targetPath: parseSinglePath(inputArgs, 'edit') };
|
|
218
222
|
}
|
|
219
|
-
function parseSinglePath(inputArgs, command) {
|
|
220
|
-
let targetPath =
|
|
223
|
+
function parseSinglePath(inputArgs, command, defaultPath = process.cwd()) {
|
|
224
|
+
let targetPath = defaultPath;
|
|
221
225
|
for (const arg of inputArgs) {
|
|
222
226
|
if (isHelpFlag(arg)) {
|
|
223
227
|
printHelp(command);
|
|
@@ -288,7 +292,9 @@ function renderHelp(topic) {
|
|
|
288
292
|
' --all Check every repo under path',
|
|
289
293
|
''
|
|
290
294
|
],
|
|
291
|
-
init: ['Usage:', ' agent-run init [path]', '', '
|
|
295
|
+
init: ['Usage:', ' agent-run init [path]', '', 'Deprecated alias for `agent-run generate [path]`.', ''],
|
|
296
|
+
generate: ['Usage:', ' agent-run generate [path]', '', 'Create a minimal mapped profile marker and render generated files.', ''],
|
|
297
|
+
setup: ['Usage:', ' agent-run setup [org/repo]', '', 'Install the default config tree and selected profile.', ''],
|
|
292
298
|
edit: ['Usage:', ' agent-run edit [path]', '', 'Create or open the editable local profile template.', ''],
|
|
293
299
|
update: [
|
|
294
300
|
'Usage:',
|
|
@@ -313,16 +319,16 @@ function renderHelp(topic) {
|
|
|
313
319
|
`agent-run ${agentRunVersion()}`,
|
|
314
320
|
'',
|
|
315
321
|
'Usage:',
|
|
316
|
-
' agent-run <codex|claude|check|
|
|
317
|
-
' agent-run --init [config-root]',
|
|
322
|
+
' agent-run <codex|claude|check|setup|generate|edit|update> [options]',
|
|
318
323
|
'',
|
|
319
324
|
'Commands:',
|
|
320
|
-
' codex [--none] [--create] [--local] [--show] [--generate] [--
|
|
325
|
+
' codex [--none] [--create] [--local] [--show] [--generate] [--yolo|--sandboxed] [--network] [args...]',
|
|
321
326
|
' Run codex with generated private config',
|
|
322
|
-
' claude [--none] [--create] [--local] [--show] [--generate] [--
|
|
327
|
+
' claude [--none] [--create] [--local] [--show] [--generate] [--yolo] [args...]',
|
|
323
328
|
' Run claude with generated private config',
|
|
329
|
+
' setup [org/repo] Install the default tree and selected profile',
|
|
324
330
|
' check [--all] [path] Validate generated profile output',
|
|
325
|
-
'
|
|
331
|
+
' generate [path] Create a sparse profile marker and render output',
|
|
326
332
|
' edit [path] Create or open local.md.njk',
|
|
327
333
|
' update [--all] [path] Regenerate existing profile output',
|
|
328
334
|
' migrate-config [config-root] Convert existing config tree layout',
|
|
@@ -331,14 +337,13 @@ function renderHelp(topic) {
|
|
|
331
337
|
' -h, --help Show this help text',
|
|
332
338
|
' -V, --version Show the agent-run version',
|
|
333
339
|
' -v, --verbose Print path resolution and wrapper actions',
|
|
334
|
-
' --
|
|
335
|
-
' --init [DIR] Copy the packaged starter config root to DIR (default ~/.agent-config)',
|
|
340
|
+
' --configdir=DIR Override the config directory',
|
|
336
341
|
'',
|
|
337
342
|
'Run wrapper options:',
|
|
338
343
|
' --local Warn about local AI files instead of failing',
|
|
339
344
|
' --show Show read/include and generated files without running the tool',
|
|
340
345
|
' --generate Generate files without running the tool',
|
|
341
|
-
' --
|
|
346
|
+
' --yolo Run unattended without approval prompts:',
|
|
342
347
|
' Codex with -a never -s danger-full-access,',
|
|
343
348
|
' Claude with --dangerously-skip-permissions',
|
|
344
349
|
'',
|
|
@@ -365,7 +370,7 @@ function normalizeCommandName(value) {
|
|
|
365
370
|
if (base === 'agent-run') {
|
|
366
371
|
return null;
|
|
367
372
|
}
|
|
368
|
-
return ['codex', 'claude', 'check', 'init', 'edit', 'update', 'migrate-config'].includes(base)
|
|
373
|
+
return ['codex', 'claude', 'check', 'setup', 'generate', 'init', 'edit', 'update', 'migrate-config'].includes(base)
|
|
369
374
|
? base
|
|
370
375
|
: null;
|
|
371
376
|
}
|
package/dist/commands.js
CHANGED
|
@@ -22,10 +22,13 @@ function dispatch(command) {
|
|
|
22
22
|
runCheck(command);
|
|
23
23
|
return;
|
|
24
24
|
case 'init':
|
|
25
|
-
|
|
25
|
+
runGenerate(command);
|
|
26
26
|
return;
|
|
27
|
-
case '
|
|
28
|
-
|
|
27
|
+
case 'generate':
|
|
28
|
+
runGenerate(command);
|
|
29
|
+
return;
|
|
30
|
+
case 'setup':
|
|
31
|
+
runSetup(command);
|
|
29
32
|
return;
|
|
30
33
|
case 'edit':
|
|
31
34
|
runEdit(command);
|
|
@@ -65,7 +68,7 @@ function runTool(parsed) {
|
|
|
65
68
|
return;
|
|
66
69
|
}
|
|
67
70
|
if (wrapperArgs.create) {
|
|
68
|
-
|
|
71
|
+
runGenerate({ command: 'generate', targetPath: projectRoot });
|
|
69
72
|
}
|
|
70
73
|
const configRoot = (0, project_1.defaultConfigRoot)(projectRoot);
|
|
71
74
|
const agentDir = path.join(configRoot, profile);
|
|
@@ -110,7 +113,7 @@ function requireProfile(tool, agentDir) {
|
|
|
110
113
|
}
|
|
111
114
|
const label = tool ?? 'agent';
|
|
112
115
|
process.stderr.write(`agent-run: no ${label} profile found for this project: ${agentDir}\n`);
|
|
113
|
-
process.stderr.write('agent-run: run `agent-run
|
|
116
|
+
process.stderr.write('agent-run: run `agent-run generate` or use the tool with `--create`.\n');
|
|
114
117
|
process.exit(1);
|
|
115
118
|
}
|
|
116
119
|
function checkLocalAiFiles(command, projectRoot, agentDir, context, allowLocal) {
|
|
@@ -146,21 +149,84 @@ function ensureToolEnabled(context, command) {
|
|
|
146
149
|
(0, utils_1.fail)(`${command} is disabled for agent-run profile ${context.profile}`);
|
|
147
150
|
}
|
|
148
151
|
}
|
|
149
|
-
function
|
|
150
|
-
|
|
152
|
+
function runGenerate(parsed) {
|
|
153
|
+
if (parsed.command === 'init') {
|
|
154
|
+
process.stderr.write('agent-run: `init` is deprecated; use `agent-run generate [path]` instead.\n');
|
|
155
|
+
}
|
|
156
|
+
const projectRoot = activeProject(parsed.targetPath, 'generate');
|
|
151
157
|
if (projectRoot === null) {
|
|
152
158
|
return;
|
|
153
159
|
}
|
|
154
160
|
const agentDir = initializeProfileSource(projectRoot, true);
|
|
155
161
|
printUpdateSummary((0, renderer_1.syncAgentProfile)(projectRoot, agentDir));
|
|
156
162
|
}
|
|
157
|
-
function
|
|
163
|
+
function runSetup(parsed) {
|
|
164
|
+
const projectRoot = (0, project_1.findProjectRoot)(process.cwd());
|
|
165
|
+
const detectedProfile = (0, project_1.resolveProfile)(projectRoot);
|
|
166
|
+
const profile = parsed.profile === null ? confirmProfile(detectedProfile) : requireValidProfile(parsed.profile);
|
|
167
|
+
const configRoot = (0, project_1.defaultConfigRoot)(projectRoot);
|
|
158
168
|
const sourcePath = (0, config_tree_1.starterConfigRootPath)();
|
|
159
169
|
if (!fs.existsSync(sourcePath)) {
|
|
160
170
|
(0, utils_1.fail)(`starter config skeleton not found: ${sourcePath}`);
|
|
161
171
|
}
|
|
162
|
-
(0, config_tree_1.copySkeletonTree)(sourcePath,
|
|
163
|
-
|
|
172
|
+
(0, config_tree_1.copySkeletonTree)(sourcePath, configRoot, new Set(['starter']));
|
|
173
|
+
const agentDir = path.join(configRoot, profile);
|
|
174
|
+
(0, config_tree_1.copySkeletonTree)(path.join(sourcePath, 'starter', 'basic-project'), agentDir);
|
|
175
|
+
if (profile !== detectedProfile) {
|
|
176
|
+
writeProjectProfileMapping(projectRoot, profile);
|
|
177
|
+
}
|
|
178
|
+
printUpdateSummary((0, renderer_1.syncAgentProfile)(projectRoot, agentDir, { configRoot, profile }));
|
|
179
|
+
process.stdout.write(`OK installed default config tree at ${configRoot}\n`);
|
|
180
|
+
}
|
|
181
|
+
function requireValidProfile(value) {
|
|
182
|
+
const parsed = (0, project_1.parseProfile)(value, 'setup profile');
|
|
183
|
+
if (parsed.profile === null) {
|
|
184
|
+
(0, utils_1.fail)(parsed.reason);
|
|
185
|
+
}
|
|
186
|
+
return parsed.profile;
|
|
187
|
+
}
|
|
188
|
+
function confirmProfile(detectedProfile) {
|
|
189
|
+
process.stdout.write(`Detected profile: ${detectedProfile}\n`);
|
|
190
|
+
const confirmation = promptLine('Is this correct? [Y/n] ').trim().toLowerCase();
|
|
191
|
+
if (confirmation === '' || confirmation === 'y' || confirmation === 'yes') {
|
|
192
|
+
return detectedProfile;
|
|
193
|
+
}
|
|
194
|
+
return requireValidProfile(promptLine('Profile (org/repo): '));
|
|
195
|
+
}
|
|
196
|
+
function promptLine(prompt) {
|
|
197
|
+
if (!process.stdin.isTTY) {
|
|
198
|
+
(0, utils_1.fail)('setup requires a profile argument when input is not interactive');
|
|
199
|
+
}
|
|
200
|
+
process.stdout.write(prompt);
|
|
201
|
+
const bytes = [];
|
|
202
|
+
const buffer = Buffer.alloc(1);
|
|
203
|
+
while (fs.readSync(process.stdin.fd, buffer, 0, 1, null) === 1) {
|
|
204
|
+
if (buffer[0] === 10) {
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
if (buffer[0] !== 13) {
|
|
208
|
+
bytes.push(buffer[0] ?? 0);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return Buffer.from(bytes).toString('utf8');
|
|
212
|
+
}
|
|
213
|
+
function writeProjectProfileMapping(projectRoot, profile) {
|
|
214
|
+
const envPath = path.join(projectRoot, constants_1.ENV_FILE_NAME);
|
|
215
|
+
const existing = fs.existsSync(envPath) ? fs.readFileSync(envPath, 'utf8').replace(/\r\n/g, '\n') : '';
|
|
216
|
+
const lines = existing.length > 0 ? existing.replace(/\n+$/, '').split('\n') : [];
|
|
217
|
+
const profileLine = `AGENT_RUN_PROFILE=${profile}`;
|
|
218
|
+
const index = lines.findIndex((line) => /^\s*AGENT_RUN_PROFILE\s*=/.test(line));
|
|
219
|
+
if (index >= 0) {
|
|
220
|
+
lines[index] = profileLine;
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
lines.push(profileLine);
|
|
224
|
+
}
|
|
225
|
+
const content = `${lines.join('\n')}\n`;
|
|
226
|
+
if (content !== existing) {
|
|
227
|
+
fs.writeFileSync(envPath, content, 'utf8');
|
|
228
|
+
(0, utils_1.verbose)(`write ${envPath}`);
|
|
229
|
+
}
|
|
164
230
|
}
|
|
165
231
|
function runEdit(parsed) {
|
|
166
232
|
const projectRoot = activeProject(parsed.targetPath, 'edit');
|
package/dist/config-tree.js
CHANGED
|
@@ -153,14 +153,17 @@ function migrateLooseFiles(agentDir, pattern, targetDirName) {
|
|
|
153
153
|
function starterConfigRootPath() {
|
|
154
154
|
return path.resolve(__dirname, '..', 'examples', 'basic-config', 'agent-config');
|
|
155
155
|
}
|
|
156
|
-
function copySkeletonTree(sourceDir, targetDir) {
|
|
156
|
+
function copySkeletonTree(sourceDir, targetDir, excludedNames = new Set()) {
|
|
157
157
|
fs.mkdirSync(targetDir, { recursive: true });
|
|
158
158
|
const entries = fs.readdirSync(sourceDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
|
|
159
159
|
for (const entry of entries) {
|
|
160
|
+
if (excludedNames.has(entry.name)) {
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
160
163
|
const sourcePath = path.join(sourceDir, entry.name);
|
|
161
164
|
const targetPath = path.join(targetDir, entry.name === 'gitignore' ? '.gitignore' : entry.name);
|
|
162
165
|
if (entry.isDirectory()) {
|
|
163
|
-
copySkeletonTree(sourcePath, targetPath);
|
|
166
|
+
copySkeletonTree(sourcePath, targetPath, excludedNames);
|
|
164
167
|
}
|
|
165
168
|
else if (entry.isFile() && !fs.existsSync(targetPath)) {
|
|
166
169
|
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
package/dist/constants.js
CHANGED
|
@@ -11,7 +11,7 @@ exports.LIVE_DIR_NAME = 'live';
|
|
|
11
11
|
exports.CONFIG_ROOT_OVERRIDE_ENV = 'AGENT_RUN_CONFIG_ROOT_OVERRIDE';
|
|
12
12
|
exports.CONFIG_DIR_ENV = 'AGENT_CONFIG_DIR';
|
|
13
13
|
exports.VERBOSE_ENV = 'AGENT_RUN_VERBOSE';
|
|
14
|
-
exports.PACKAGE_VERSION = '0.99.
|
|
14
|
+
exports.PACKAGE_VERSION = '0.99.28';
|
|
15
15
|
exports.UNEXPANDED_TEMPLATE_RE = /\{\{[^}]+\}\}|\{%[^%]+%\}/;
|
|
16
16
|
exports.LOCAL_AI_FILE_NAMES = new Set([
|
|
17
17
|
'AGENTS.md',
|
package/dist/project.js
CHANGED
|
@@ -164,6 +164,11 @@ function profileFromPackage(projectRoot) {
|
|
|
164
164
|
if (fs.existsSync(packagePath)) {
|
|
165
165
|
try {
|
|
166
166
|
const pkg = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
|
|
167
|
+
const repositoryProfile = parsePackageRepositoryProfile(pkg.repository);
|
|
168
|
+
if (repositoryProfile !== null) {
|
|
169
|
+
(0, utils_1.verbose)(`using package.json repository profile=${repositoryProfile}`);
|
|
170
|
+
return parseProfile(repositoryProfile, `${packagePath} repository`);
|
|
171
|
+
}
|
|
167
172
|
if (typeof pkg.name === 'string' && pkg.name.length > 0) {
|
|
168
173
|
(0, utils_1.verbose)(`using package.json name=${pkg.name}`);
|
|
169
174
|
return parseProfile(pkg.name.startsWith('@') ? pkg.name.slice(1) : pkg.name, `${packagePath} name`);
|
|
@@ -173,10 +178,37 @@ function profileFromPackage(projectRoot) {
|
|
|
173
178
|
return { profile: null, reason: `cannot parse package.json: ${packagePath}` };
|
|
174
179
|
}
|
|
175
180
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
181
|
+
const current = path.basename(projectRoot);
|
|
182
|
+
const parentDir = path.dirname(projectRoot);
|
|
183
|
+
const parent = path.basename(parentDir);
|
|
184
|
+
if (current && parent && parentDir !== projectRoot) {
|
|
185
|
+
const inferredProfile = `${parent}/${current}`;
|
|
186
|
+
(0, utils_1.verbose)(`using project path profile=${inferredProfile}`);
|
|
187
|
+
return parseProfile(inferredProfile, 'project parent and directory name');
|
|
188
|
+
}
|
|
189
|
+
return { profile: null, reason: `cannot resolve agent profile from project path: ${projectRoot}` };
|
|
190
|
+
}
|
|
191
|
+
function parsePackageRepositoryProfile(repository) {
|
|
192
|
+
let repositoryPath = null;
|
|
193
|
+
if (typeof repository === 'string') {
|
|
194
|
+
repositoryPath = repository;
|
|
195
|
+
}
|
|
196
|
+
else if (repository !== null && typeof repository === 'object' && 'url' in repository) {
|
|
197
|
+
const url = repository.url;
|
|
198
|
+
if (typeof url === 'string') {
|
|
199
|
+
repositoryPath = url;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
if (repositoryPath === null) {
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
const value = repositoryPath.trim();
|
|
206
|
+
const githubProfile = parseGitHubRemoteProfile(value.replace(/^git\+/, ''));
|
|
207
|
+
if (githubProfile !== null) {
|
|
208
|
+
return githubProfile;
|
|
209
|
+
}
|
|
210
|
+
const shorthand = /^(?:github:)?([^/:\s]+)\/([^/\s]+?)(?:\.git)?$/.exec(value);
|
|
211
|
+
return shorthand?.[1] && shorthand[2] ? `${shorthand[1]}/${shorthand[2]}` : null;
|
|
180
212
|
}
|
|
181
213
|
function resolveGitHubProfile(projectRoot) {
|
|
182
214
|
const result = childProcess.spawnSync('git', ['-C', projectRoot, 'config', '--get', 'remote.origin.url'], {
|
|
@@ -266,17 +298,11 @@ function defaultConfigRoot(projectRoot, options = {}) {
|
|
|
266
298
|
if (explicit !== null) {
|
|
267
299
|
return explicit;
|
|
268
300
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
if (options.preferProjectRoot || fs.existsSync(projectConfigRoot)) {
|
|
272
|
-
return projectConfigRoot;
|
|
273
|
-
}
|
|
274
|
-
return findExistingConfigRoot(defaultConfigRootSearchCandidates()) ?? projectConfigRoot;
|
|
275
|
-
}
|
|
276
|
-
return findExistingConfigRoot(defaultConfigRootSearchCandidates()) ?? path.join(os.homedir(), '.agent-config');
|
|
301
|
+
void options;
|
|
302
|
+
return path.join(os.homedir(), '.agent-run');
|
|
277
303
|
}
|
|
278
304
|
function explicitConfigRoot(projectRoot) {
|
|
279
|
-
for (const value of [process.env[constants_1.CONFIG_ROOT_OVERRIDE_ENV], process.env[constants_1.CONFIG_DIR_ENV]
|
|
305
|
+
for (const value of [process.env[constants_1.CONFIG_ROOT_OVERRIDE_ENV], process.env[constants_1.CONFIG_DIR_ENV]]) {
|
|
280
306
|
if (value) {
|
|
281
307
|
return path.resolve(value);
|
|
282
308
|
}
|
|
@@ -285,21 +311,9 @@ function explicitConfigRoot(projectRoot) {
|
|
|
285
311
|
return null;
|
|
286
312
|
}
|
|
287
313
|
const env = readAgentRunEnv(projectRoot);
|
|
288
|
-
const localValue = env[constants_1.CONFIG_DIR_ENV]?.trim()
|
|
314
|
+
const localValue = env[constants_1.CONFIG_DIR_ENV]?.trim();
|
|
289
315
|
return localValue ? path.resolve(projectRoot, localValue) : null;
|
|
290
316
|
}
|
|
291
|
-
function defaultCodeConfigRoot(projectRoot) {
|
|
292
|
-
const resolvedProjectRoot = path.resolve(projectRoot);
|
|
293
|
-
const ownerDir = path.dirname(resolvedProjectRoot);
|
|
294
|
-
const codeRoot = path.dirname(ownerDir);
|
|
295
|
-
if (ownerDir === resolvedProjectRoot || codeRoot === ownerDir) {
|
|
296
|
-
return path.join(os.homedir(), '.agent-config');
|
|
297
|
-
}
|
|
298
|
-
return path.join(codeRoot, 'agent-config');
|
|
299
|
-
}
|
|
300
|
-
function findExistingConfigRoot(candidates) {
|
|
301
|
-
return candidates.find((candidate) => fs.existsSync(candidate)) ?? null;
|
|
302
|
-
}
|
|
303
317
|
function defaultConfigRootSearchCandidates(_projectRoot, options = {}) {
|
|
304
318
|
const platform = options.platform ?? process.platform;
|
|
305
319
|
const pathApi = platform === 'win32' ? path.win32 : path.posix;
|
package/dist/renderer.js
CHANGED
|
@@ -101,11 +101,18 @@ function syncAgentProfile(projectRoot, agentDir, options) {
|
|
|
101
101
|
}
|
|
102
102
|
function writeGeneratedFile(filePath, content, executable) {
|
|
103
103
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
104
|
-
fs.
|
|
104
|
+
const unchanged = fs.existsSync(filePath) && fs.readFileSync(filePath, 'utf8') === content;
|
|
105
|
+
if (!unchanged) {
|
|
106
|
+
fs.writeFileSync(filePath, content, 'utf8');
|
|
107
|
+
(0, utils_1.verbose)(`write ${filePath}`);
|
|
108
|
+
}
|
|
105
109
|
if (executable && !constants_1.IS_WINDOWS) {
|
|
106
|
-
fs.
|
|
110
|
+
const currentMode = fs.statSync(filePath).mode & 0o777;
|
|
111
|
+
if (currentMode !== 0o755) {
|
|
112
|
+
fs.chmodSync(filePath, 0o755);
|
|
113
|
+
(0, utils_1.verbose)(`chmod 755 ${filePath}`);
|
|
114
|
+
}
|
|
107
115
|
}
|
|
108
|
-
(0, utils_1.verbose)(`write ${filePath}`);
|
|
109
116
|
}
|
|
110
117
|
function removeStaleGeneratedEntries(rendered) {
|
|
111
118
|
const expectedFiles = new Set(rendered.files.map((file) => path.resolve(file.path)));
|