@imdeadpool/guardex 7.0.24 → 7.0.25
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/README.md +25 -3
- package/package.json +1 -1
- package/src/cli/args.js +9 -0
- package/src/context.js +22 -0
- package/src/doctor/index.js +158 -5
- package/src/finish/index.js +1 -0
- package/templates/AGENTS.multiagent-safety.md +3 -0
- package/templates/codex/skills/guardex-merge-skills-to-dev/SKILL.md +3 -2
- package/templates/scripts/agent-branch-finish.sh +106 -5
- package/templates/scripts/agent-worktree-prune.sh +22 -1
- package/templates/vscode/guardex-active-agents/README.md +6 -3
- package/templates/vscode/guardex-active-agents/extension.js +1706 -247
- package/templates/vscode/guardex-active-agents/fileicons/gitguardex-fileicons.json +54 -0
- package/templates/vscode/guardex-active-agents/fileicons/icons/agent.svg +4 -0
- package/templates/vscode/guardex-active-agents/fileicons/icons/branch.svg +4 -0
- package/templates/vscode/guardex-active-agents/fileicons/icons/config.svg +4 -0
- package/templates/vscode/guardex-active-agents/fileicons/icons/hook.svg +3 -0
- package/templates/vscode/guardex-active-agents/fileicons/icons/openspec.svg +5 -0
- package/templates/vscode/guardex-active-agents/fileicons/icons/plan.svg +4 -0
- package/templates/vscode/guardex-active-agents/fileicons/icons/spec.svg +4 -0
- package/templates/vscode/guardex-active-agents/media/active-agents-hivemind.svg +14 -0
- package/templates/vscode/guardex-active-agents/package.json +39 -11
- package/templates/vscode/guardex-active-agents/session-schema.js +226 -8
package/README.md
CHANGED
|
@@ -140,7 +140,7 @@ That's it. Install and update via `@imdeadpool/guardex`. Setup installs the mini
|
|
|
140
140
|
</div>
|
|
141
141
|
|
|
142
142
|
> [!NOTE]
|
|
143
|
-
> In this repo, `CLAUDE.md` is a symlink to `AGENTS.md`, so Claude reads the same contract. Optional Codex/Claude companion files
|
|
143
|
+
> In this repo, `CLAUDE.md` is a symlink to `AGENTS.md`, so Claude reads the same contract. Optional Codex/Claude companion files still install at the user level with `gx install-agent-skills`, while the generic repo skill catalog is available through `npx skills add recodeee/` or directly via `npx skills add recodeee/gitguardex`.
|
|
144
144
|
|
|
145
145
|
### Decision flow
|
|
146
146
|
|
|
@@ -266,7 +266,7 @@ To install the real companion into local VS Code from a GitGuardex-wired repo:
|
|
|
266
266
|
node scripts/install-vscode-active-agents-extension.js
|
|
267
267
|
```
|
|
268
268
|
|
|
269
|
-
It adds
|
|
269
|
+
It adds a dedicated `Active Agents` Activity Bar container with a hive icon, shows the live active-agent count as a badge on that icon, groups each live repo into `ACTIVE AGENTS` and `CHANGES` sections, splits `ACTIVE AGENTS` into `BLOCKED`, `WORKING NOW`, `IDLE`, `STALLED`, and `DEAD` when those states are present, mirrors the selected session or active-agent count in the VS Code status bar, reads `.omx/state/active-sessions/*.json`, derives session state from git conflict markers, dirty worktree status, PID liveness, and recent file mtimes, and surfaces working/dead counts in the repo/header affordances. Reload the VS Code window after install.
|
|
270
270
|
|
|
271
271
|
---
|
|
272
272
|
|
|
@@ -459,6 +459,23 @@ npm i -g oh-my-claude-sisyphus@latest
|
|
|
459
459
|
Repo: <https://github.com/Yeachan-Heo/oh-my-claudecode>
|
|
460
460
|
[](https://github.com/Yeachan-Heo/oh-my-claudecode)
|
|
461
461
|
|
|
462
|
+
### GitGuardex skills - install the repo skill catalog through `npx skills`
|
|
463
|
+
|
|
464
|
+
For agents that already support the generic `skills` installer flow, GitGuardex now exposes its repo skill catalog directly. You can start from the broader `recodeee` source or jump straight into this repo's catalog.
|
|
465
|
+
|
|
466
|
+
```sh
|
|
467
|
+
npx skills add recodeee/
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
```sh
|
|
471
|
+
npx skills add recodeee/gitguardex
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
This repo currently exposes `gitguardex` and `guardex-merge-skills-to-dev` through that flow. If the picker does not show a separate `guardex` skill, that is expected: `guardex` remains the legacy CLI alias, while the repo skill itself is named `gitguardex`. Use `gx install-agent-skills` when you want the Codex/Claude user-home startup files instead of the generic `skills` catalog.
|
|
475
|
+
|
|
476
|
+
Repo: <https://github.com/recodeee/gitguardex>
|
|
477
|
+
[](https://github.com/recodeee/gitguardex)
|
|
478
|
+
|
|
462
479
|
### Caveman — output compression for long agent runs
|
|
463
480
|
|
|
464
481
|
Ultra-compressed response mode for Claude/Codex-style agents. Useful when you want less output-token churn during long reviews, debug loops, or multi-agent sessions.
|
|
@@ -609,7 +626,7 @@ vscode/guardex-active-agents/README.md
|
|
|
609
626
|
|
|
610
627
|
Legacy compatibility note: older repos may still contain repo-local workflow scripts under `scripts/`. Direct `gx branch ...`, `gx locks ...`, `gx finish`, `gx cleanup`, `gx merge`, and `gx migrate` do not require them. `gx migrate` removes those leftover workflow shims by default. The CLI still honors repo-local `scripts/review-bot-watch.sh` and `scripts/codex-agent.sh` when they are already present so older repos can keep working during migration.
|
|
611
628
|
|
|
612
|
-
Optional Codex/Claude user-level companions still install with `gx install-agent-skills`;
|
|
629
|
+
Optional Codex/Claude user-level companions still install with `gx install-agent-skills`; the generic repo skill catalog is available with `npx skills add recodeee/` or directly via `npx skills add recodeee/gitguardex`. Neither path copies those user-home files into each repo.
|
|
613
630
|
|
|
614
631
|
---
|
|
615
632
|
|
|
@@ -672,6 +689,11 @@ npm pack --dry-run
|
|
|
672
689
|
<details>
|
|
673
690
|
<summary><strong>v7.x</strong></summary>
|
|
674
691
|
|
|
692
|
+
### v7.0.25
|
|
693
|
+
- Bumped `@imdeadpool/guardex` from `7.0.24` to `7.0.25` so npm and GitHub Releases can ship the current `main` payload.
|
|
694
|
+
- The bundled `GitGuardex Active Agents` VS Code companion now self-heals stale repo-scan ignore settings in older repos, keeps plain managed sandboxes visible in Source Control, and preserves cleanup truth so the tree matches actual sandbox state.
|
|
695
|
+
- Bumped the shipped Active Agents companion manifests from `0.0.8` to `0.0.9` so local VS Code installs can pick up the newer workspace build and show the refreshed extension version from this release.
|
|
696
|
+
|
|
675
697
|
### v7.0.24
|
|
676
698
|
- Bumped `@imdeadpool/guardex` from `7.0.23` to `7.0.24` so GitHub Releases and the npm publish retry can advance together after `v7.0.23` landed on GitHub but not on npm.
|
|
677
699
|
- Release verification no longer loses its base ref on tag-triggered runs, so the publish workflow keeps the history it needs before packing and publish checks.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@imdeadpool/guardex",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.25",
|
|
4
4
|
"description": "Guardian T-Rex for your multi-agent repo. Isolated worktrees, file locks, and PR-only merges stop parallel Codex & Claude agents from overwriting each other's work. Auto-wires Oh My Codex, Oh My Claude, OpenSpec, and Caveman.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"preferGlobal": true,
|
package/src/cli/args.js
CHANGED
|
@@ -770,6 +770,7 @@ function parseFinishArgs(rawArgs, defaults = {}) {
|
|
|
770
770
|
cleanup: defaults.cleanup ?? true,
|
|
771
771
|
keepRemote: false,
|
|
772
772
|
noAutoCommit: false,
|
|
773
|
+
parentGitlinkCommit: defaults.parentGitlinkCommit ?? true,
|
|
773
774
|
failFast: false,
|
|
774
775
|
commitMessage: '',
|
|
775
776
|
mergeMode: defaults.mergeMode || 'pr',
|
|
@@ -865,6 +866,14 @@ function parseFinishArgs(rawArgs, defaults = {}) {
|
|
|
865
866
|
options.noAutoCommit = true;
|
|
866
867
|
continue;
|
|
867
868
|
}
|
|
869
|
+
if (arg === '--parent-gitlink-commit') {
|
|
870
|
+
options.parentGitlinkCommit = true;
|
|
871
|
+
continue;
|
|
872
|
+
}
|
|
873
|
+
if (arg === '--no-parent-gitlink-commit') {
|
|
874
|
+
options.parentGitlinkCommit = false;
|
|
875
|
+
continue;
|
|
876
|
+
}
|
|
868
877
|
if (arg === '--fail-fast') {
|
|
869
878
|
options.failFast = true;
|
|
870
879
|
continue;
|
package/src/context.js
CHANGED
|
@@ -129,6 +129,14 @@ const TEMPLATE_FILES = [
|
|
|
129
129
|
'vscode/guardex-active-agents/session-schema.js',
|
|
130
130
|
'vscode/guardex-active-agents/README.md',
|
|
131
131
|
'vscode/guardex-active-agents/icon.png',
|
|
132
|
+
'vscode/guardex-active-agents/fileicons/gitguardex-fileicons.json',
|
|
133
|
+
'vscode/guardex-active-agents/fileicons/icons/agent.svg',
|
|
134
|
+
'vscode/guardex-active-agents/fileicons/icons/branch.svg',
|
|
135
|
+
'vscode/guardex-active-agents/fileicons/icons/config.svg',
|
|
136
|
+
'vscode/guardex-active-agents/fileicons/icons/hook.svg',
|
|
137
|
+
'vscode/guardex-active-agents/fileicons/icons/openspec.svg',
|
|
138
|
+
'vscode/guardex-active-agents/fileicons/icons/plan.svg',
|
|
139
|
+
'vscode/guardex-active-agents/fileicons/icons/spec.svg',
|
|
132
140
|
];
|
|
133
141
|
|
|
134
142
|
const PACKAGE_ROOT_SOURCE_OVERRIDES = new Set([
|
|
@@ -139,6 +147,14 @@ const PACKAGE_ROOT_SOURCE_OVERRIDES = new Set([
|
|
|
139
147
|
'vscode/guardex-active-agents/session-schema.js',
|
|
140
148
|
'vscode/guardex-active-agents/README.md',
|
|
141
149
|
'vscode/guardex-active-agents/icon.png',
|
|
150
|
+
'vscode/guardex-active-agents/fileicons/gitguardex-fileicons.json',
|
|
151
|
+
'vscode/guardex-active-agents/fileicons/icons/agent.svg',
|
|
152
|
+
'vscode/guardex-active-agents/fileicons/icons/branch.svg',
|
|
153
|
+
'vscode/guardex-active-agents/fileicons/icons/config.svg',
|
|
154
|
+
'vscode/guardex-active-agents/fileicons/icons/hook.svg',
|
|
155
|
+
'vscode/guardex-active-agents/fileicons/icons/openspec.svg',
|
|
156
|
+
'vscode/guardex-active-agents/fileicons/icons/plan.svg',
|
|
157
|
+
'vscode/guardex-active-agents/fileicons/icons/spec.svg',
|
|
142
158
|
]);
|
|
143
159
|
|
|
144
160
|
const LEGACY_WORKFLOW_SHIM_SPECS = [
|
|
@@ -263,8 +279,12 @@ const AGENT_WORKTREE_RELATIVE_DIRS = [
|
|
|
263
279
|
const MANAGED_REPO_SCAN_IGNORED_FOLDERS = [
|
|
264
280
|
'.omx/agent-worktrees',
|
|
265
281
|
'**/.omx/agent-worktrees',
|
|
282
|
+
'.omx/.tmp-worktrees',
|
|
283
|
+
'**/.omx/.tmp-worktrees',
|
|
266
284
|
'.omc/agent-worktrees',
|
|
267
285
|
'**/.omc/agent-worktrees',
|
|
286
|
+
'.omc/.tmp-worktrees',
|
|
287
|
+
'**/.omc/.tmp-worktrees',
|
|
268
288
|
];
|
|
269
289
|
const MANAGED_GITIGNORE_PATHS = [
|
|
270
290
|
'.omx/',
|
|
@@ -427,6 +447,8 @@ const AI_SETUP_PARTS = [
|
|
|
427
447
|
'gx branch start "<task>" "<agent>"',
|
|
428
448
|
'then gx locks claim --branch "<agent-branch>" <file...> -> inspect once -> patch once -> verify once -> gx branch finish',
|
|
429
449
|
'batch discovery, git/PR, and CI by phase; avoid repeated peeks or stdin loops',
|
|
450
|
+
'checkpoint after each milestone: Task -> Done -> Current status -> Next; keep only the latest checkpoint(s) in active context',
|
|
451
|
+
'summarize tool results, keep stdin/process chatter ephemeral, and keep execution log separate from reasoning context',
|
|
430
452
|
],
|
|
431
453
|
execLines: [
|
|
432
454
|
'gx branch start "<task>" "<agent>"',
|
package/src/doctor/index.js
CHANGED
|
@@ -314,6 +314,118 @@ function doctorFinishFlowIsPending(output) {
|
|
|
314
314
|
);
|
|
315
315
|
}
|
|
316
316
|
|
|
317
|
+
function verifyDoctorSandboxCleanup(repoRoot, metadata) {
|
|
318
|
+
const branchExists = Boolean(metadata.branch) && gitRefExists(repoRoot, `refs/heads/${metadata.branch}`);
|
|
319
|
+
const worktreeExists = Boolean(metadata.worktreePath) && fs.existsSync(metadata.worktreePath);
|
|
320
|
+
|
|
321
|
+
if (!branchExists && !worktreeExists) {
|
|
322
|
+
return {
|
|
323
|
+
status: 'verified',
|
|
324
|
+
note: 'doctor sandbox cleanup verified',
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const cleanup = cleanupProtectedBaseSandbox(repoRoot, metadata);
|
|
329
|
+
const branchStillExists = Boolean(metadata.branch) && gitRefExists(repoRoot, `refs/heads/${metadata.branch}`);
|
|
330
|
+
const worktreeStillExists = Boolean(metadata.worktreePath) && fs.existsSync(metadata.worktreePath);
|
|
331
|
+
if (branchStillExists || worktreeStillExists) {
|
|
332
|
+
return {
|
|
333
|
+
status: 'failed',
|
|
334
|
+
note:
|
|
335
|
+
'doctor sandbox cleanup incomplete ' +
|
|
336
|
+
`(branch=${branchStillExists ? 'present' : 'missing'}, worktree=${worktreeStillExists ? 'present' : 'missing'})`,
|
|
337
|
+
cleanup,
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
return {
|
|
342
|
+
status: 'verified',
|
|
343
|
+
note: 'doctor sandbox cleanup verified',
|
|
344
|
+
cleanup,
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function verifyDoctorSandboxRemoteCleanup(repoRoot, metadata) {
|
|
349
|
+
if (!metadata.branch || !hasOriginRemote(repoRoot)) {
|
|
350
|
+
return {
|
|
351
|
+
status: 'skipped',
|
|
352
|
+
note: 'doctor sandbox remote cleanup skipped',
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const remoteBefore = run(
|
|
357
|
+
'git',
|
|
358
|
+
['-C', repoRoot, 'ls-remote', '--heads', 'origin', metadata.branch],
|
|
359
|
+
{ timeout: 20_000 },
|
|
360
|
+
);
|
|
361
|
+
if (isSpawnFailure(remoteBefore)) {
|
|
362
|
+
throw remoteBefore.error;
|
|
363
|
+
}
|
|
364
|
+
if (remoteBefore.status !== 0) {
|
|
365
|
+
return {
|
|
366
|
+
status: 'failed',
|
|
367
|
+
note: 'doctor sandbox remote branch inspection failed',
|
|
368
|
+
stdout: remoteBefore.stdout || '',
|
|
369
|
+
stderr: remoteBefore.stderr || '',
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
if (!String(remoteBefore.stdout || '').trim()) {
|
|
373
|
+
return {
|
|
374
|
+
status: 'verified',
|
|
375
|
+
note: 'doctor sandbox remote cleanup verified',
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const deleteResult = run(
|
|
380
|
+
'git',
|
|
381
|
+
['-C', repoRoot, 'push', 'origin', '--delete', metadata.branch],
|
|
382
|
+
{ timeout: 30_000 },
|
|
383
|
+
);
|
|
384
|
+
if (isSpawnFailure(deleteResult)) {
|
|
385
|
+
throw deleteResult.error;
|
|
386
|
+
}
|
|
387
|
+
if (deleteResult.status !== 0) {
|
|
388
|
+
return {
|
|
389
|
+
status: 'failed',
|
|
390
|
+
note: 'doctor sandbox remote branch cleanup failed',
|
|
391
|
+
stdout: deleteResult.stdout || '',
|
|
392
|
+
stderr: deleteResult.stderr || '',
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
const remoteAfter = run(
|
|
397
|
+
'git',
|
|
398
|
+
['-C', repoRoot, 'ls-remote', '--heads', 'origin', metadata.branch],
|
|
399
|
+
{ timeout: 20_000 },
|
|
400
|
+
);
|
|
401
|
+
if (isSpawnFailure(remoteAfter)) {
|
|
402
|
+
throw remoteAfter.error;
|
|
403
|
+
}
|
|
404
|
+
if (remoteAfter.status !== 0) {
|
|
405
|
+
return {
|
|
406
|
+
status: 'failed',
|
|
407
|
+
note: 'doctor sandbox remote cleanup recheck failed',
|
|
408
|
+
stdout: remoteAfter.stdout || '',
|
|
409
|
+
stderr: remoteAfter.stderr || '',
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
if (String(remoteAfter.stdout || '').trim()) {
|
|
413
|
+
return {
|
|
414
|
+
status: 'failed',
|
|
415
|
+
note: 'doctor sandbox remote branch still present after cleanup',
|
|
416
|
+
stdout: remoteAfter.stdout || '',
|
|
417
|
+
stderr: remoteAfter.stderr || '',
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
return {
|
|
422
|
+
status: 'verified',
|
|
423
|
+
note: 'doctor sandbox remote cleanup verified',
|
|
424
|
+
stdout: deleteResult.stdout || '',
|
|
425
|
+
stderr: deleteResult.stderr || '',
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
|
|
317
429
|
function finishDoctorSandboxBranch(blocked, metadata, options = {}) {
|
|
318
430
|
if (!hasOriginRemote(blocked.repoRoot)) {
|
|
319
431
|
return {
|
|
@@ -353,8 +465,8 @@ function finishDoctorSandboxBranch(blocked, metadata, options = {}) {
|
|
|
353
465
|
|
|
354
466
|
const finishResult = runPackageAsset(
|
|
355
467
|
'branchFinish',
|
|
356
|
-
['--branch', metadata.branch, '--base', blocked.branch, '--via-pr', waitForMergeArg, '--cleanup'],
|
|
357
|
-
{ cwd:
|
|
468
|
+
['--branch', metadata.branch, '--base', blocked.branch, '--via-pr', waitForMergeArg, '--no-cleanup'],
|
|
469
|
+
{ cwd: blocked.repoRoot, timeout: finishTimeoutMs },
|
|
358
470
|
);
|
|
359
471
|
if (isSpawnFailure(finishResult)) {
|
|
360
472
|
return {
|
|
@@ -384,11 +496,52 @@ function finishDoctorSandboxBranch(blocked, metadata, options = {}) {
|
|
|
384
496
|
};
|
|
385
497
|
}
|
|
386
498
|
|
|
499
|
+
let cleanupVerification;
|
|
500
|
+
try {
|
|
501
|
+
cleanupVerification = verifyDoctorSandboxCleanup(blocked.repoRoot, metadata);
|
|
502
|
+
} catch (error) {
|
|
503
|
+
return {
|
|
504
|
+
status: 'failed',
|
|
505
|
+
note: `doctor sandbox cleanup verification failed: ${error.message}`,
|
|
506
|
+
stdout: finishResult.stdout || '',
|
|
507
|
+
stderr: finishResult.stderr || '',
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
if (cleanupVerification.status === 'failed') {
|
|
511
|
+
return {
|
|
512
|
+
status: 'failed',
|
|
513
|
+
note: cleanupVerification.note,
|
|
514
|
+
stdout: finishResult.stdout || '',
|
|
515
|
+
stderr: finishResult.stderr || '',
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
let remoteCleanupVerification;
|
|
520
|
+
try {
|
|
521
|
+
remoteCleanupVerification = verifyDoctorSandboxRemoteCleanup(blocked.repoRoot, metadata);
|
|
522
|
+
} catch (error) {
|
|
523
|
+
return {
|
|
524
|
+
status: 'failed',
|
|
525
|
+
note: `doctor sandbox remote cleanup verification failed: ${error.message}`,
|
|
526
|
+
stdout: finishResult.stdout || '',
|
|
527
|
+
stderr: finishResult.stderr || '',
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
if (remoteCleanupVerification.status === 'failed') {
|
|
531
|
+
return {
|
|
532
|
+
status: 'failed',
|
|
533
|
+
note: remoteCleanupVerification.note,
|
|
534
|
+
stdout: [finishResult.stdout || '', remoteCleanupVerification.stdout || ''].filter(Boolean).join('\n'),
|
|
535
|
+
stderr: [finishResult.stderr || '', remoteCleanupVerification.stderr || ''].filter(Boolean).join('\n'),
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
|
|
387
539
|
return {
|
|
388
540
|
status: 'completed',
|
|
389
|
-
note: 'doctor sandbox finish flow completed',
|
|
390
|
-
stdout: finishResult.stdout || '',
|
|
391
|
-
stderr: finishResult.stderr || '',
|
|
541
|
+
note: 'doctor sandbox finish flow completed and cleanup verified',
|
|
542
|
+
stdout: [finishResult.stdout || '', remoteCleanupVerification.stdout || ''].filter(Boolean).join('\n'),
|
|
543
|
+
stderr: [finishResult.stderr || '', remoteCleanupVerification.stderr || ''].filter(Boolean).join('\n'),
|
|
544
|
+
cleanup: cleanupVerification.cleanup,
|
|
392
545
|
};
|
|
393
546
|
}
|
|
394
547
|
|
package/src/finish/index.js
CHANGED
|
@@ -317,6 +317,7 @@ function finish(rawArgs, defaults = {}) {
|
|
|
317
317
|
if (options.keepRemote) {
|
|
318
318
|
finishArgs.push('--keep-remote-branch');
|
|
319
319
|
}
|
|
320
|
+
finishArgs.push(options.parentGitlinkCommit ? '--parent-gitlink-commit' : '--no-parent-gitlink-commit');
|
|
320
321
|
|
|
321
322
|
if (options.dryRun) {
|
|
322
323
|
console.log(`[${TOOL_NAME}] [dry-run] Would run: gx branch finish ${finishArgs.join(' ')}`);
|
|
@@ -21,8 +21,11 @@ Default: less word, same proof.
|
|
|
21
21
|
- Front-load scaffold/path discovery into one grouped inspection pass. Avoid serial `ls` / `find` / `rg` / `cat` retries that only rediscover the same path state.
|
|
22
22
|
- Treat repeated `write_stdin`, repeated `sed` / `cat` peeks, and tiny diagnostic follow-up checks as strong negative signals. If they appear alongside climbing input cost, stop the probe loop and batch the next phase.
|
|
23
23
|
- Tool / hook summaries stay tiny: command, status, last meaningful lines only. Drop routine hook boilerplate.
|
|
24
|
+
- Keep raw terminal interaction out of long-lived context. For `write_stdin` or interactive babysitting, retain only process, action sent, current result, and next action.
|
|
25
|
+
- Keep execution log separate from reasoning context: full commands/stdout belong in logs, while prompt context keeps only the latest 1-2 checkpoints plus the newest tool-result summary.
|
|
24
26
|
- Treat local edit/commit, remote publish/PR, CI diagnosis, and cleanup as bounded phases. Do not spend fresh narration or approval turns on obvious safe follow-ons inside an already authorized phase unless the risk changes.
|
|
25
27
|
- When a session turns fragmented, collapse back to inspect once, patch once, verify once, and summarize once.
|
|
28
|
+
- Use a fixed checkpoint shape when compacting: `Task`, `Done`, `Current status`, and `Next`.
|
|
26
29
|
- Keep `.omx/notepad.md` lean: live handoffs only. Use exactly `branch`, `task`, `blocker`, `next step`, and `evidence`; move narrative proof into OpenSpec artifacts, PRs, or command output.
|
|
27
30
|
|
|
28
31
|
## OMX Caveman Style
|
|
@@ -9,6 +9,7 @@ Use this skill when you only want to promote Codex skill file updates into the b
|
|
|
9
9
|
|
|
10
10
|
## What this merges
|
|
11
11
|
|
|
12
|
+
- `skills/**/SKILL.md`
|
|
12
13
|
- `.codex/skills/**/SKILL.md`
|
|
13
14
|
- `templates/codex/skills/**/SKILL.md`
|
|
14
15
|
|
|
@@ -33,7 +34,7 @@ gx branch start "merge-skill-files-to-${BASE_BRANCH}" "skill-merge" "$BASE_BRANC
|
|
|
33
34
|
|
|
34
35
|
```sh
|
|
35
36
|
SOURCE_BRANCH="<agent-branch>"
|
|
36
|
-
git checkout "$SOURCE_BRANCH" -- ':(glob).codex/skills/**/SKILL.md' ':(glob)templates/codex/skills/**/SKILL.md'
|
|
37
|
+
git checkout "$SOURCE_BRANCH" -- ':(glob)skills/**/SKILL.md' ':(glob).codex/skills/**/SKILL.md' ':(glob)templates/codex/skills/**/SKILL.md'
|
|
37
38
|
```
|
|
38
39
|
|
|
39
40
|
5. Verify scope before commit:
|
|
@@ -46,7 +47,7 @@ git diff --name-only
|
|
|
46
47
|
6. Commit and merge back to base using guardex finish flow:
|
|
47
48
|
|
|
48
49
|
```sh
|
|
49
|
-
git add .codex/skills templates/codex/skills
|
|
50
|
+
git add skills .codex/skills templates/codex/skills
|
|
50
51
|
git commit -m "Merge skill file updates into ${BASE_BRANCH}"
|
|
51
52
|
gx branch finish --branch "$(git rev-parse --abbrev-ref HEAD)" --base "$BASE_BRANCH" --via-pr --wait-for-merge --cleanup
|
|
52
53
|
```
|
|
@@ -15,6 +15,7 @@ CLEANUP_AFTER_MERGE_RAW="${GUARDEX_FINISH_CLEANUP:-false}"
|
|
|
15
15
|
WAIT_FOR_MERGE_RAW="${GUARDEX_FINISH_WAIT_FOR_MERGE:-false}"
|
|
16
16
|
WAIT_TIMEOUT_SECONDS_RAW="${GUARDEX_FINISH_WAIT_TIMEOUT_SECONDS:-1800}"
|
|
17
17
|
WAIT_POLL_SECONDS_RAW="${GUARDEX_FINISH_WAIT_POLL_SECONDS:-10}"
|
|
18
|
+
PARENT_GITLINK_AUTO_COMMIT_RAW="${GUARDEX_FINISH_PARENT_GITLINK_AUTO_COMMIT:-true}"
|
|
18
19
|
|
|
19
20
|
run_guardex_cli() {
|
|
20
21
|
if [[ -n "$CLI_ENTRY" ]]; then
|
|
@@ -67,6 +68,7 @@ CLEANUP_AFTER_MERGE="$(normalize_bool "$CLEANUP_AFTER_MERGE_RAW" "0")"
|
|
|
67
68
|
WAIT_FOR_MERGE="$(normalize_bool "$WAIT_FOR_MERGE_RAW" "0")"
|
|
68
69
|
WAIT_TIMEOUT_SECONDS="$(normalize_int "$WAIT_TIMEOUT_SECONDS_RAW" "1800" "30")"
|
|
69
70
|
WAIT_POLL_SECONDS="$(normalize_int "$WAIT_POLL_SECONDS_RAW" "10" "0")"
|
|
71
|
+
PARENT_GITLINK_AUTO_COMMIT="$(normalize_bool "$PARENT_GITLINK_AUTO_COMMIT_RAW" "1")"
|
|
70
72
|
|
|
71
73
|
while [[ $# -gt 0 ]]; do
|
|
72
74
|
case "$1" in
|
|
@@ -117,6 +119,14 @@ while [[ $# -gt 0 ]]; do
|
|
|
117
119
|
WAIT_POLL_SECONDS="$(normalize_int "${2:-}" "10" "0")"
|
|
118
120
|
shift 2
|
|
119
121
|
;;
|
|
122
|
+
--parent-gitlink-commit)
|
|
123
|
+
PARENT_GITLINK_AUTO_COMMIT=1
|
|
124
|
+
shift
|
|
125
|
+
;;
|
|
126
|
+
--no-parent-gitlink-commit)
|
|
127
|
+
PARENT_GITLINK_AUTO_COMMIT=0
|
|
128
|
+
shift
|
|
129
|
+
;;
|
|
120
130
|
--mode)
|
|
121
131
|
MERGE_MODE="${2:-auto}"
|
|
122
132
|
shift 2
|
|
@@ -131,7 +141,7 @@ while [[ $# -gt 0 ]]; do
|
|
|
131
141
|
;;
|
|
132
142
|
*)
|
|
133
143
|
echo "[agent-branch-finish] Unknown argument: $1" >&2
|
|
134
|
-
echo "Usage: $0 [--base <branch>] [--branch <branch>] [--no-push] [--cleanup|--no-cleanup] [--wait-for-merge|--no-wait-for-merge] [--wait-timeout-seconds <n>] [--wait-poll-seconds <n>] [--keep-remote-branch|--delete-remote-branch] [--mode auto|direct|pr|--via-pr|--direct-only]" >&2
|
|
144
|
+
echo "Usage: $0 [--base <branch>] [--branch <branch>] [--no-push] [--cleanup|--no-cleanup] [--wait-for-merge|--no-wait-for-merge] [--wait-timeout-seconds <n>] [--wait-poll-seconds <n>] [--parent-gitlink-commit|--no-parent-gitlink-commit] [--keep-remote-branch|--delete-remote-branch] [--mode auto|direct|pr|--via-pr|--direct-only]" >&2
|
|
135
145
|
exit 1
|
|
136
146
|
;;
|
|
137
147
|
esac
|
|
@@ -173,6 +183,8 @@ if [[ -z "$stored_worktree_root_rel" ]]; then
|
|
|
173
183
|
stored_worktree_root_rel=".omx/agent-worktrees"
|
|
174
184
|
fi
|
|
175
185
|
agent_worktree_root="${repo_common_root}/${stored_worktree_root_rel}"
|
|
186
|
+
runtime_state_root_rel="$(dirname "$stored_worktree_root_rel")"
|
|
187
|
+
temp_worktree_root="${repo_common_root}/${runtime_state_root_rel}/.tmp-worktrees"
|
|
176
188
|
|
|
177
189
|
if [[ "$BASE_BRANCH_EXPLICIT" -eq 1 && -z "$BASE_BRANCH" ]]; then
|
|
178
190
|
echo "[agent-branch-finish] --base requires a non-empty branch name." >&2
|
|
@@ -218,7 +230,7 @@ fi
|
|
|
218
230
|
|
|
219
231
|
get_worktree_for_branch() {
|
|
220
232
|
local branch="$1"
|
|
221
|
-
git -C "$repo_root" worktree list --porcelain | awk -v target="refs/heads/${branch}" -v probe_prefix="${
|
|
233
|
+
git -C "$repo_root" worktree list --porcelain | awk -v target="refs/heads/${branch}" -v probe_prefix="${temp_worktree_root}/__source-probe-" '
|
|
222
234
|
$1 == "worktree" { wt = $2 }
|
|
223
235
|
$1 == "branch" && $2 == target {
|
|
224
236
|
if (index(wt, probe_prefix) != 1) {
|
|
@@ -242,7 +254,7 @@ remove_stale_source_probe_worktrees() {
|
|
|
242
254
|
git -C "$stale_probe" merge --abort >/dev/null 2>&1 || true
|
|
243
255
|
git -C "$repo_root" worktree remove "$stale_probe" --force >/dev/null 2>&1 || true
|
|
244
256
|
done < <(
|
|
245
|
-
git -C "$repo_root" worktree list --porcelain | awk -v target="refs/heads/${branch}" -v probe_prefix="${
|
|
257
|
+
git -C "$repo_root" worktree list --porcelain | awk -v target="refs/heads/${branch}" -v probe_prefix="${temp_worktree_root}/__source-probe-" '
|
|
246
258
|
$1 == "worktree" { wt = $2 }
|
|
247
259
|
$1 == "branch" && $2 == target {
|
|
248
260
|
if (index(wt, probe_prefix) == 1) {
|
|
@@ -264,11 +276,15 @@ source_worktree="$(get_worktree_for_branch "$SOURCE_BRANCH")"
|
|
|
264
276
|
created_source_probe=0
|
|
265
277
|
source_probe_path=""
|
|
266
278
|
integration_worktree=""
|
|
279
|
+
integration_branch=""
|
|
267
280
|
|
|
268
281
|
cleanup() {
|
|
269
282
|
if [[ -n "$integration_worktree" && -d "$integration_worktree" ]]; then
|
|
270
283
|
git -C "$repo_root" worktree remove "$integration_worktree" --force >/dev/null 2>&1 || true
|
|
271
284
|
fi
|
|
285
|
+
if [[ -n "${integration_branch:-}" ]]; then
|
|
286
|
+
git -C "$repo_root" branch -D "$integration_branch" >/dev/null 2>&1 || true
|
|
287
|
+
fi
|
|
272
288
|
if [[ "$created_source_probe" -eq 1 && -n "$source_probe_path" && -d "$source_probe_path" ]]; then
|
|
273
289
|
# Abort any in-progress git op so `worktree remove --force` succeeds on conflict-stuck probes.
|
|
274
290
|
git -C "$source_probe_path" rebase --abort >/dev/null 2>&1 || true
|
|
@@ -279,7 +295,7 @@ cleanup() {
|
|
|
279
295
|
trap cleanup EXIT
|
|
280
296
|
|
|
281
297
|
if [[ -z "$source_worktree" ]]; then
|
|
282
|
-
source_probe_path="${
|
|
298
|
+
source_probe_path="${temp_worktree_root}/__source-probe-${SOURCE_BRANCH//\//__}-$(date +%Y%m%d-%H%M%S)"
|
|
283
299
|
mkdir -p "$(dirname "$source_probe_path")"
|
|
284
300
|
git -C "$repo_root" worktree add "$source_probe_path" "$SOURCE_BRANCH" >/dev/null
|
|
285
301
|
source_worktree="$source_probe_path"
|
|
@@ -343,7 +359,7 @@ if [[ "$should_require_sync" -eq 1 ]] && git -C "$repo_root" show-ref --verify -
|
|
|
343
359
|
fi
|
|
344
360
|
|
|
345
361
|
integration_stamp="$(date +%Y%m%d-%H%M%S)"
|
|
346
|
-
integration_worktree_base="${
|
|
362
|
+
integration_worktree_base="${temp_worktree_root}/__integrate-${BASE_BRANCH//\//__}-${integration_stamp}"
|
|
347
363
|
integration_branch_base="__agent_integrate_${BASE_BRANCH//\//_}_$(date +%Y%m%d_%H%M%S)"
|
|
348
364
|
integration_worktree="$integration_worktree_base"
|
|
349
365
|
integration_branch="$integration_branch_base"
|
|
@@ -493,6 +509,90 @@ read_merged_pr_for_head() {
|
|
|
493
509
|
return 0
|
|
494
510
|
}
|
|
495
511
|
|
|
512
|
+
maybe_auto_commit_parent_gitlink() {
|
|
513
|
+
local base_wt="${1:-}"
|
|
514
|
+
local base_wt_real=""
|
|
515
|
+
local super_root_raw=""
|
|
516
|
+
local super_root=""
|
|
517
|
+
local subrepo_rel=""
|
|
518
|
+
local gitlink_mode=""
|
|
519
|
+
local gitlink_index_sha=""
|
|
520
|
+
local gitlink_parent_head_sha=""
|
|
521
|
+
local subrepo_head_sha=""
|
|
522
|
+
local update_index_output=""
|
|
523
|
+
local commit_output=""
|
|
524
|
+
local commit_message=""
|
|
525
|
+
|
|
526
|
+
if [[ "$PARENT_GITLINK_AUTO_COMMIT" -ne 1 || "$PUSH_ENABLED" -ne 1 ]]; then
|
|
527
|
+
return 0
|
|
528
|
+
fi
|
|
529
|
+
if [[ -z "$base_wt" ]]; then
|
|
530
|
+
return 0
|
|
531
|
+
fi
|
|
532
|
+
if ! base_wt_real="$(cd "$base_wt" && pwd -P 2>/dev/null)"; then
|
|
533
|
+
return 0
|
|
534
|
+
fi
|
|
535
|
+
if [[ "$base_wt_real" != "$repo_common_root" ]]; then
|
|
536
|
+
return 0
|
|
537
|
+
fi
|
|
538
|
+
if ! is_clean_worktree "$repo_common_root"; then
|
|
539
|
+
echo "[agent-branch-finish] Parent gitlink auto-commit skipped; nested base worktree is dirty: ${repo_common_root}" >&2
|
|
540
|
+
return 0
|
|
541
|
+
fi
|
|
542
|
+
|
|
543
|
+
super_root_raw="$(git -C "$repo_common_root" rev-parse --show-superproject-working-tree 2>/dev/null || true)"
|
|
544
|
+
if [[ -z "$super_root_raw" ]]; then
|
|
545
|
+
return 0
|
|
546
|
+
fi
|
|
547
|
+
if ! super_root="$(cd "$super_root_raw" && pwd -P 2>/dev/null)"; then
|
|
548
|
+
return 0
|
|
549
|
+
fi
|
|
550
|
+
|
|
551
|
+
case "$repo_common_root" in
|
|
552
|
+
"$super_root"/*) subrepo_rel="${repo_common_root#"$super_root"/}" ;;
|
|
553
|
+
*) return 0 ;;
|
|
554
|
+
esac
|
|
555
|
+
if [[ -z "$subrepo_rel" || "$subrepo_rel" == "$repo_common_root" ]]; then
|
|
556
|
+
return 0
|
|
557
|
+
fi
|
|
558
|
+
|
|
559
|
+
gitlink_mode="$(git -C "$super_root" ls-files -s -- "$subrepo_rel" | awk 'NR == 1 { print $1 }')"
|
|
560
|
+
if [[ "$gitlink_mode" != "160000" ]]; then
|
|
561
|
+
return 0
|
|
562
|
+
fi
|
|
563
|
+
gitlink_index_sha="$(git -C "$super_root" ls-files -s -- "$subrepo_rel" | awk 'NR == 1 { print $2 }')"
|
|
564
|
+
gitlink_parent_head_sha="$(git -C "$super_root" ls-tree HEAD -- "$subrepo_rel" | awk 'NR == 1 { print $3 }')"
|
|
565
|
+
subrepo_head_sha="$(git -C "$repo_common_root" rev-parse HEAD 2>/dev/null || true)"
|
|
566
|
+
if [[ -z "$subrepo_head_sha" ]]; then
|
|
567
|
+
return 0
|
|
568
|
+
fi
|
|
569
|
+
if [[ -n "$gitlink_index_sha" && "$gitlink_index_sha" == "$gitlink_parent_head_sha" && "$gitlink_index_sha" == "$subrepo_head_sha" ]]; then
|
|
570
|
+
return 0
|
|
571
|
+
fi
|
|
572
|
+
|
|
573
|
+
if [[ "$gitlink_index_sha" != "$subrepo_head_sha" ]]; then
|
|
574
|
+
if ! update_index_output="$(git -C "$super_root" update-index --cacheinfo 160000 "$subrepo_head_sha" "$subrepo_rel" 2>&1)"; then
|
|
575
|
+
echo "[agent-branch-finish] Warning: parent gitlink staging failed for ${subrepo_rel} in ${super_root}." >&2
|
|
576
|
+
[[ -n "$update_index_output" ]] && echo "$update_index_output" >&2
|
|
577
|
+
return 0
|
|
578
|
+
fi
|
|
579
|
+
gitlink_index_sha="$(git -C "$super_root" ls-files -s -- "$subrepo_rel" | awk 'NR == 1 { print $2 }')"
|
|
580
|
+
fi
|
|
581
|
+
gitlink_parent_head_sha="$(git -C "$super_root" ls-tree HEAD -- "$subrepo_rel" | awk 'NR == 1 { print $3 }')"
|
|
582
|
+
if [[ "$gitlink_index_sha" == "$gitlink_parent_head_sha" ]]; then
|
|
583
|
+
return 0
|
|
584
|
+
fi
|
|
585
|
+
|
|
586
|
+
commit_message="Update ${subrepo_rel} subrepo pointer"
|
|
587
|
+
if ! commit_output="$(git -C "$super_root" commit -m "$commit_message" -- "$subrepo_rel" 2>&1)"; then
|
|
588
|
+
echo "[agent-branch-finish] Warning: parent gitlink auto-commit failed in ${super_root}." >&2
|
|
589
|
+
[[ -n "$commit_output" ]] && echo "$commit_output" >&2
|
|
590
|
+
return 0
|
|
591
|
+
fi
|
|
592
|
+
|
|
593
|
+
echo "[agent-branch-finish] Parent gitlink auto-committed '${subrepo_rel}' in ${super_root}."
|
|
594
|
+
}
|
|
595
|
+
|
|
496
596
|
wait_for_pr_merge() {
|
|
497
597
|
local deadline
|
|
498
598
|
deadline=$(( $(date +%s) + WAIT_TIMEOUT_SECONDS ))
|
|
@@ -661,6 +761,7 @@ base_worktree="$(get_worktree_for_branch "$BASE_BRANCH")"
|
|
|
661
761
|
if [[ -n "$base_worktree" ]] && is_clean_worktree "$base_worktree" && [[ "$PUSH_ENABLED" -eq 1 ]]; then
|
|
662
762
|
git -C "$base_worktree" pull --ff-only origin "$BASE_BRANCH" >/dev/null 2>&1 || true
|
|
663
763
|
fi
|
|
764
|
+
maybe_auto_commit_parent_gitlink "$base_worktree"
|
|
664
765
|
|
|
665
766
|
if [[ "$CLEANUP_AFTER_MERGE" -eq 1 ]]; then
|
|
666
767
|
if [[ "$source_worktree" == "$repo_root" ]]; then
|
|
@@ -20,7 +20,9 @@ PR_MERGED_LOOKUP_LOADED=0
|
|
|
20
20
|
declare -A MERGED_PR_BRANCHES=()
|
|
21
21
|
WORKTREE_ROOT_RELS=(
|
|
22
22
|
".omx/agent-worktrees"
|
|
23
|
+
".omx/.tmp-worktrees"
|
|
23
24
|
".omc/agent-worktrees"
|
|
25
|
+
".omc/.tmp-worktrees"
|
|
24
26
|
)
|
|
25
27
|
|
|
26
28
|
if [[ -n "$BASE_BRANCH" ]]; then
|
|
@@ -90,9 +92,15 @@ repo_common_dir="$(cd "$repo_common_dir" && pwd -P)"
|
|
|
90
92
|
resolve_worktree_root_rel_for_entry() {
|
|
91
93
|
local entry="$1"
|
|
92
94
|
case "$entry" in
|
|
95
|
+
*/.omc/.tmp-worktrees/*)
|
|
96
|
+
printf '%s' '.omc/.tmp-worktrees'
|
|
97
|
+
;;
|
|
93
98
|
*/.omc/agent-worktrees/*)
|
|
94
99
|
printf '%s' '.omc/agent-worktrees'
|
|
95
100
|
;;
|
|
101
|
+
*/.omx/.tmp-worktrees/*)
|
|
102
|
+
printf '%s' '.omx/.tmp-worktrees'
|
|
103
|
+
;;
|
|
96
104
|
*)
|
|
97
105
|
printf '%s' '.omx/agent-worktrees'
|
|
98
106
|
;;
|
|
@@ -538,6 +546,19 @@ if [[ "$DELETE_BRANCHES" -eq 1 ]]; then
|
|
|
538
546
|
if branch_has_worktree "$branch"; then
|
|
539
547
|
continue
|
|
540
548
|
fi
|
|
549
|
+
if [[ "$branch" == __agent_integrate_* || "$branch" == __source-probe-* ]]; then
|
|
550
|
+
if ! branch_idle_gate "$branch" "" "temporary-worktree"; then
|
|
551
|
+
continue
|
|
552
|
+
fi
|
|
553
|
+
if run_cmd git -C "$repo_root" branch -D "$branch" >/dev/null 2>&1; then
|
|
554
|
+
removed_branches=$((removed_branches + 1))
|
|
555
|
+
echo "[agent-worktree-prune] Deleted stale temporary branch: ${branch}"
|
|
556
|
+
fi
|
|
557
|
+
continue
|
|
558
|
+
fi
|
|
559
|
+
if [[ "$branch" != agent/* ]]; then
|
|
560
|
+
continue
|
|
561
|
+
fi
|
|
541
562
|
if ! branch_idle_gate "$branch" "" "stale-merged-branch"; then
|
|
542
563
|
continue
|
|
543
564
|
fi
|
|
@@ -566,7 +587,7 @@ if [[ "$DELETE_BRANCHES" -eq 1 ]]; then
|
|
|
566
587
|
fi
|
|
567
588
|
fi
|
|
568
589
|
fi
|
|
569
|
-
done < <(git -C "$repo_root" for-each-ref --format='%(refname:short)' refs/heads
|
|
590
|
+
done < <(git -C "$repo_root" for-each-ref --format='%(refname:short)' refs/heads)
|
|
570
591
|
fi
|
|
571
592
|
|
|
572
593
|
run_cmd git -C "$repo_root" worktree prune
|