@technomoron/agent-run 0.99.27 → 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 +9 -0
- package/README.md +37 -48
- package/dist/cli.js +33 -14
- package/dist/commands.js +73 -7
- package/dist/config-tree.js +5 -2
- package/dist/constants.js +1 -1
- package/dist/project.js +31 -22
- package/dist/renderer.js +10 -3
- package/package.json +1 -1
package/CHANGES
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
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
|
+
|
|
4
13
|
Version 0.99.27 (2026-08-11)
|
|
5
14
|
----------------------------
|
|
6
15
|
* Rename the unattended-run option from `--danger` to `--yolo` for Codex and
|
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,13 +93,15 @@ 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.
|
|
100
|
-
4.
|
|
96
|
+
3. `package.json.repository` (string or object `url`)
|
|
97
|
+
4. `package.json.name`
|
|
98
|
+
5. the project path as `[parent]/[current]`
|
|
101
99
|
|
|
102
100
|
Examples:
|
|
103
101
|
|
|
104
102
|
- `AGENT_RUN_PROFILE=org/my-api` -> `org/my-api`
|
|
105
103
|
- `AGENT_RUN_PROFILE=unrelated/hello` -> `unrelated/hello`
|
|
104
|
+
- `package.json.repository = "github:org/my-api"` -> `org/my-api`
|
|
106
105
|
- `package.json.name = "@org/my-api"` -> `org/my-api`
|
|
107
106
|
- `package.json.name = "my-api"` -> `my-api`
|
|
108
107
|
- `/work/org/my-api` with none of the above -> `org/my-api`
|
|
@@ -124,20 +123,10 @@ AGENT_RUN_PROFILE=org/my-api
|
|
|
124
123
|
|
|
125
124
|
Config root:
|
|
126
125
|
|
|
127
|
-
- default
|
|
128
|
-
- override with `--
|
|
126
|
+
- default: `~/.agent-run`
|
|
127
|
+
- override with `--configdir=/path/to/agent-configs`
|
|
129
128
|
- override with `AGENT_CONFIG_DIR=/path/to/agent-configs`
|
|
130
|
-
-
|
|
131
|
-
- or set `AGENT_CONFIG_DIR=/path/to/agent-configs` or `AGENT_CONFIG_ROOT=/path/to/agent-configs` in `.agent-run.env`
|
|
132
|
-
|
|
133
|
-
For commands without a project root, `agent-run` looks for an existing config
|
|
134
|
-
root in these locations:
|
|
135
|
-
|
|
136
|
-
- Windows: `~/Documents/code/agent-config`, `~/Documents/code/agent-configs`,
|
|
137
|
-
`~/Desktop/code/agent-config`, `~/Desktop/code/agent-configs`,
|
|
138
|
-
`C:\code\agent-config`, `C:\code\agent-configs`
|
|
139
|
-
- Unix: `~/code/agent-config`, `~/code/agent-configs`, `~/agent-config`,
|
|
140
|
-
`~/agent-configs`, `~/.agent-config`, `~/.agent-configs`
|
|
129
|
+
- or set `AGENT_CONFIG_DIR=/path/to/agent-configs` in `.agent-run.env`
|
|
141
130
|
|
|
142
131
|
## Commands
|
|
143
132
|
|
|
@@ -186,12 +175,12 @@ to warn and continue for a specific invocation.
|
|
|
186
175
|
Initialize mapped files for the current repo:
|
|
187
176
|
|
|
188
177
|
```sh
|
|
189
|
-
agent-run
|
|
178
|
+
agent-run generate
|
|
190
179
|
```
|
|
191
180
|
|
|
192
181
|
This creates `<config-root>/agent-run.defaults.jsonc` when needed and a minimal
|
|
193
182
|
`agent-run.jsonc` marker for the mapped profile. It does not create empty local
|
|
194
|
-
instruction or override files.
|
|
183
|
+
instruction or override files. `agent-run init` remains as a deprecated alias.
|
|
195
184
|
|
|
196
185
|
Edit the source file for the current repo:
|
|
197
186
|
|
|
@@ -209,19 +198,18 @@ agent-run update
|
|
|
209
198
|
```
|
|
210
199
|
|
|
211
200
|
`update` requires an existing profile marker or local/legacy instruction file;
|
|
212
|
-
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
|
|
213
202
|
the tool command's `--create` option first.
|
|
214
203
|
|
|
215
204
|
Regenerate every profile under the config root without requiring matching code
|
|
216
205
|
checkouts:
|
|
217
206
|
|
|
218
207
|
```sh
|
|
219
|
-
agent-run update --all ~/.agent-
|
|
208
|
+
agent-run update --all ~/.agent-run
|
|
220
209
|
```
|
|
221
210
|
|
|
222
211
|
If the config root argument is omitted, `update --all` uses the normal configured
|
|
223
|
-
root. Without
|
|
224
|
-
project commands, the default is `[code]/agent-config`.
|
|
212
|
+
root. Without an override, the root is `$HOME/.agent-run`.
|
|
225
213
|
|
|
226
214
|
You can also generate from a tool command and stop before launch:
|
|
227
215
|
|
|
@@ -245,27 +233,28 @@ Per-profile override templates can be placed in:
|
|
|
245
233
|
- `overrides/codex-config.toml.njk`
|
|
246
234
|
- `overrides/claude-settings.json.njk`
|
|
247
235
|
|
|
248
|
-
##
|
|
236
|
+
## Default Config Tree
|
|
249
237
|
|
|
250
|
-
A complete
|
|
238
|
+
A complete default tree lives in `examples/basic-config`. It includes a minimal
|
|
251
239
|
project, `.agent-run.env`, root manifest defaults, global templates and
|
|
252
240
|
snippets, profile-local templates, skill templates, a personal memory skill,
|
|
253
241
|
profile skill overrides, tool config overrides, guardrails, checks, and
|
|
254
242
|
generated runtime paths.
|
|
255
243
|
|
|
256
|
-
To
|
|
244
|
+
To install the default tree and detected profile in your resolved config root:
|
|
257
245
|
|
|
258
246
|
```sh
|
|
259
247
|
agent-run setup
|
|
260
248
|
```
|
|
261
249
|
|
|
262
|
-
Or
|
|
250
|
+
Or select a profile non-interactively:
|
|
263
251
|
|
|
264
252
|
```sh
|
|
265
|
-
agent-run setup
|
|
253
|
+
agent-run setup myorg/myrepo
|
|
266
254
|
```
|
|
267
255
|
|
|
268
|
-
|
|
256
|
+
Use `--configdir=DIR` to choose a different destination. The copy skips files
|
|
257
|
+
that already exist, so local edits are preserved.
|
|
269
258
|
|
|
270
259
|
Try it from a checkout:
|
|
271
260
|
|
|
@@ -298,7 +287,7 @@ agent-run check --all ~/source
|
|
|
298
287
|
Migrate an existing config tree to the manifest/template layout:
|
|
299
288
|
|
|
300
289
|
```sh
|
|
301
|
-
agent-run migrate-config ~/.agent-
|
|
290
|
+
agent-run migrate-config ~/.agent-run
|
|
302
291
|
```
|
|
303
292
|
|
|
304
293
|
This preserves `AGENTS-MODS.md`, creates `local.md.njk` and a sparse
|
package/dist/cli.js
CHANGED
|
@@ -25,7 +25,7 @@ function applyGlobalOptions(options) {
|
|
|
25
25
|
}
|
|
26
26
|
function extractCommand(args) {
|
|
27
27
|
if (args.length === 0) {
|
|
28
|
-
(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)');
|
|
29
29
|
}
|
|
30
30
|
if (isHelpFlag(args[0])) {
|
|
31
31
|
printHelp('general');
|
|
@@ -50,6 +50,8 @@ function parseCommand(command, args) {
|
|
|
50
50
|
return parseCheckCommand(args);
|
|
51
51
|
case 'init':
|
|
52
52
|
return parseInitCommand(args);
|
|
53
|
+
case 'generate':
|
|
54
|
+
return parseGenerateCommand(args);
|
|
53
55
|
case 'setup':
|
|
54
56
|
return parseSetupCommand(args);
|
|
55
57
|
case 'edit':
|
|
@@ -85,18 +87,18 @@ function extractGlobalOptions(argv) {
|
|
|
85
87
|
else if (arg === '-v' || arg === '--verbose') {
|
|
86
88
|
options.verbose = true;
|
|
87
89
|
}
|
|
88
|
-
else if (arg === '--
|
|
90
|
+
else if (arg === '--configdir') {
|
|
89
91
|
const nextArg = argv[index + 1];
|
|
90
92
|
if (nextArg === undefined) {
|
|
91
|
-
(0, utils_1.fail)('missing value for --
|
|
93
|
+
(0, utils_1.fail)('missing value for --configdir');
|
|
92
94
|
}
|
|
93
95
|
options.configRootOverride = path.resolve(nextArg);
|
|
94
96
|
index += 1;
|
|
95
97
|
}
|
|
96
|
-
else if (arg.startsWith('--
|
|
97
|
-
const rootValue = arg.slice('--
|
|
98
|
+
else if (arg.startsWith('--configdir=')) {
|
|
99
|
+
const rootValue = arg.slice('--configdir='.length);
|
|
98
100
|
if (!rootValue) {
|
|
99
|
-
(0, utils_1.fail)('missing value for --
|
|
101
|
+
(0, utils_1.fail)('missing value for --configdir');
|
|
100
102
|
}
|
|
101
103
|
options.configRootOverride = path.resolve(rootValue);
|
|
102
104
|
}
|
|
@@ -196,8 +198,24 @@ function parseCheckCommand(inputArgs) {
|
|
|
196
198
|
function parseInitCommand(inputArgs) {
|
|
197
199
|
return { command: 'init', targetPath: parseSinglePath(inputArgs, 'init') };
|
|
198
200
|
}
|
|
201
|
+
function parseGenerateCommand(inputArgs) {
|
|
202
|
+
return { command: 'generate', targetPath: parseSinglePath(inputArgs, 'generate') };
|
|
203
|
+
}
|
|
199
204
|
function parseSetupCommand(inputArgs) {
|
|
200
|
-
|
|
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 };
|
|
201
219
|
}
|
|
202
220
|
function parseEditCommand(inputArgs) {
|
|
203
221
|
return { command: 'edit', targetPath: parseSinglePath(inputArgs, 'edit') };
|
|
@@ -274,8 +292,9 @@ function renderHelp(topic) {
|
|
|
274
292
|
' --all Check every repo under path',
|
|
275
293
|
''
|
|
276
294
|
],
|
|
277
|
-
init: ['Usage:', ' agent-run init [path]', '', '
|
|
278
|
-
|
|
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.', ''],
|
|
279
298
|
edit: ['Usage:', ' agent-run edit [path]', '', 'Create or open the editable local profile template.', ''],
|
|
280
299
|
update: [
|
|
281
300
|
'Usage:',
|
|
@@ -300,16 +319,16 @@ function renderHelp(topic) {
|
|
|
300
319
|
`agent-run ${agentRunVersion()}`,
|
|
301
320
|
'',
|
|
302
321
|
'Usage:',
|
|
303
|
-
' agent-run <codex|claude|check|setup|
|
|
322
|
+
' agent-run <codex|claude|check|setup|generate|edit|update> [options]',
|
|
304
323
|
'',
|
|
305
324
|
'Commands:',
|
|
306
325
|
' codex [--none] [--create] [--local] [--show] [--generate] [--yolo|--sandboxed] [--network] [args...]',
|
|
307
326
|
' Run codex with generated private config',
|
|
308
327
|
' claude [--none] [--create] [--local] [--show] [--generate] [--yolo] [args...]',
|
|
309
328
|
' Run claude with generated private config',
|
|
310
|
-
' setup [
|
|
329
|
+
' setup [org/repo] Install the default tree and selected profile',
|
|
311
330
|
' check [--all] [path] Validate generated profile output',
|
|
312
|
-
'
|
|
331
|
+
' generate [path] Create a sparse profile marker and render output',
|
|
313
332
|
' edit [path] Create or open local.md.njk',
|
|
314
333
|
' update [--all] [path] Regenerate existing profile output',
|
|
315
334
|
' migrate-config [config-root] Convert existing config tree layout',
|
|
@@ -318,7 +337,7 @@ function renderHelp(topic) {
|
|
|
318
337
|
' -h, --help Show this help text',
|
|
319
338
|
' -V, --version Show the agent-run version',
|
|
320
339
|
' -v, --verbose Print path resolution and wrapper actions',
|
|
321
|
-
' --
|
|
340
|
+
' --configdir=DIR Override the config directory',
|
|
322
341
|
'',
|
|
323
342
|
'Run wrapper options:',
|
|
324
343
|
' --local Warn about local AI files instead of failing',
|
|
@@ -351,7 +370,7 @@ function normalizeCommandName(value) {
|
|
|
351
370
|
if (base === 'agent-run') {
|
|
352
371
|
return null;
|
|
353
372
|
}
|
|
354
|
-
return ['codex', 'claude', 'check', 'setup', 'init', 'edit', 'update', 'migrate-config'].includes(base)
|
|
373
|
+
return ['codex', 'claude', 'check', 'setup', 'generate', 'init', 'edit', 'update', 'migrate-config'].includes(base)
|
|
355
374
|
? base
|
|
356
375
|
: null;
|
|
357
376
|
}
|
package/dist/commands.js
CHANGED
|
@@ -22,7 +22,10 @@ function dispatch(command) {
|
|
|
22
22
|
runCheck(command);
|
|
23
23
|
return;
|
|
24
24
|
case 'init':
|
|
25
|
-
|
|
25
|
+
runGenerate(command);
|
|
26
|
+
return;
|
|
27
|
+
case 'generate':
|
|
28
|
+
runGenerate(command);
|
|
26
29
|
return;
|
|
27
30
|
case 'setup':
|
|
28
31
|
runSetup(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,8 +149,11 @@ 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
|
}
|
|
@@ -155,12 +161,72 @@ function runInit(parsed) {
|
|
|
155
161
|
printUpdateSummary((0, renderer_1.syncAgentProfile)(projectRoot, agentDir));
|
|
156
162
|
}
|
|
157
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`);
|
|
@@ -183,6 +188,28 @@ function profileFromPackage(projectRoot) {
|
|
|
183
188
|
}
|
|
184
189
|
return { profile: null, reason: `cannot resolve agent profile from project path: ${projectRoot}` };
|
|
185
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;
|
|
212
|
+
}
|
|
186
213
|
function resolveGitHubProfile(projectRoot) {
|
|
187
214
|
const result = childProcess.spawnSync('git', ['-C', projectRoot, 'config', '--get', 'remote.origin.url'], {
|
|
188
215
|
encoding: 'utf8',
|
|
@@ -271,17 +298,11 @@ function defaultConfigRoot(projectRoot, options = {}) {
|
|
|
271
298
|
if (explicit !== null) {
|
|
272
299
|
return explicit;
|
|
273
300
|
}
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
if (options.preferProjectRoot || fs.existsSync(projectConfigRoot)) {
|
|
277
|
-
return projectConfigRoot;
|
|
278
|
-
}
|
|
279
|
-
return findExistingConfigRoot(defaultConfigRootSearchCandidates()) ?? projectConfigRoot;
|
|
280
|
-
}
|
|
281
|
-
return findExistingConfigRoot(defaultConfigRootSearchCandidates()) ?? path.join(os.homedir(), '.agent-config');
|
|
301
|
+
void options;
|
|
302
|
+
return path.join(os.homedir(), '.agent-run');
|
|
282
303
|
}
|
|
283
304
|
function explicitConfigRoot(projectRoot) {
|
|
284
|
-
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]]) {
|
|
285
306
|
if (value) {
|
|
286
307
|
return path.resolve(value);
|
|
287
308
|
}
|
|
@@ -290,21 +311,9 @@ function explicitConfigRoot(projectRoot) {
|
|
|
290
311
|
return null;
|
|
291
312
|
}
|
|
292
313
|
const env = readAgentRunEnv(projectRoot);
|
|
293
|
-
const localValue = env[constants_1.CONFIG_DIR_ENV]?.trim()
|
|
314
|
+
const localValue = env[constants_1.CONFIG_DIR_ENV]?.trim();
|
|
294
315
|
return localValue ? path.resolve(projectRoot, localValue) : null;
|
|
295
316
|
}
|
|
296
|
-
function defaultCodeConfigRoot(projectRoot) {
|
|
297
|
-
const resolvedProjectRoot = path.resolve(projectRoot);
|
|
298
|
-
const ownerDir = path.dirname(resolvedProjectRoot);
|
|
299
|
-
const codeRoot = path.dirname(ownerDir);
|
|
300
|
-
if (ownerDir === resolvedProjectRoot || codeRoot === ownerDir) {
|
|
301
|
-
return path.join(os.homedir(), '.agent-config');
|
|
302
|
-
}
|
|
303
|
-
return path.join(codeRoot, 'agent-config');
|
|
304
|
-
}
|
|
305
|
-
function findExistingConfigRoot(candidates) {
|
|
306
|
-
return candidates.find((candidate) => fs.existsSync(candidate)) ?? null;
|
|
307
|
-
}
|
|
308
317
|
function defaultConfigRootSearchCandidates(_projectRoot, options = {}) {
|
|
309
318
|
const platform = options.platform ?? process.platform;
|
|
310
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)));
|