overlord-cli 0.2606021930.0 → 0.2606041130.1
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/bin/_cli/launcher.mjs +155 -21
- package/bin/_cli/setup.mjs +3 -1
- package/package.json +1 -1
package/bin/_cli/launcher.mjs
CHANGED
|
@@ -17,6 +17,21 @@ import { runAttachCommand } from './attach.mjs';
|
|
|
17
17
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
18
18
|
const PACKAGE_CLAUDE_PLUGIN_DIR = path.resolve(__dirname, '..', '..', 'plugins', 'claude');
|
|
19
19
|
const REPO_CLAUDE_PLUGIN_DIR = path.resolve(__dirname, '..', '..', '..', '..', 'plugins', 'claude');
|
|
20
|
+
const CLAUDE_PLUGIN_CACHE_DIR = path.join(
|
|
21
|
+
os.homedir(),
|
|
22
|
+
'.claude',
|
|
23
|
+
'plugins',
|
|
24
|
+
'cache',
|
|
25
|
+
'overlord-local',
|
|
26
|
+
'overlord'
|
|
27
|
+
);
|
|
28
|
+
const CLAUDE_INSTALLED_PLUGINS_FILE = path.join(
|
|
29
|
+
os.homedir(),
|
|
30
|
+
'.claude',
|
|
31
|
+
'plugins',
|
|
32
|
+
'installed_plugins.json'
|
|
33
|
+
);
|
|
34
|
+
const CLAUDE_INSTALLED_PLUGIN_KEY = 'overlord@overlord-local';
|
|
20
35
|
|
|
21
36
|
function resolveLaunchScratchDir(workingDirectory, { explicit = false } = {}) {
|
|
22
37
|
const trimmed = typeof workingDirectory === 'string' ? workingDirectory.trim() : '';
|
|
@@ -41,12 +56,84 @@ function withLaunchTempEnv(baseEnv, scratchDir) {
|
|
|
41
56
|
};
|
|
42
57
|
}
|
|
43
58
|
|
|
59
|
+
function contextFileTag(ticketId) {
|
|
60
|
+
const safeTicket = String(ticketId ?? 'ticket').replace(/[^a-zA-Z0-9._-]+/g, '-');
|
|
61
|
+
return `overlord-${safeTicket}-${Date.now()}-${crypto.randomUUID().slice(0, 8)}`;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function writeLaunchContextFile(scratchDir, ticketId, context) {
|
|
65
|
+
const contextFile = path.join(scratchDir, `${contextFileTag(ticketId)}-context.md`);
|
|
66
|
+
fs.mkdirSync(path.dirname(contextFile), { recursive: true });
|
|
67
|
+
fs.writeFileSync(contextFile, context, 'utf-8');
|
|
68
|
+
setTimeout(() => {
|
|
69
|
+
try {
|
|
70
|
+
fs.unlinkSync(contextFile);
|
|
71
|
+
} catch {
|
|
72
|
+
// Best-effort cleanup only.
|
|
73
|
+
}
|
|
74
|
+
}, 30 * 60_000).unref();
|
|
75
|
+
return contextFile;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function buildContextFilePrompt(contextFile, startPrompt) {
|
|
79
|
+
return `${startPrompt}\n\nRead the Overlord launch context from ${contextFile} before taking action. Follow the ticket workflow and objective described in that file.`;
|
|
80
|
+
}
|
|
81
|
+
|
|
44
82
|
function claudeSourcePluginDir() {
|
|
45
83
|
if (fs.existsSync(PACKAGE_CLAUDE_PLUGIN_DIR)) return PACKAGE_CLAUDE_PLUGIN_DIR;
|
|
46
84
|
if (fs.existsSync(REPO_CLAUDE_PLUGIN_DIR)) return REPO_CLAUDE_PLUGIN_DIR;
|
|
47
85
|
return null;
|
|
48
86
|
}
|
|
49
87
|
|
|
88
|
+
function readJsonFile(filePath) {
|
|
89
|
+
try {
|
|
90
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
91
|
+
} catch {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function claudeInstalledPluginDir() {
|
|
97
|
+
const installed = readJsonFile(CLAUDE_INSTALLED_PLUGINS_FILE);
|
|
98
|
+
const pluginEntries = installed?.plugins?.[CLAUDE_INSTALLED_PLUGIN_KEY];
|
|
99
|
+
|
|
100
|
+
if (Array.isArray(pluginEntries)) {
|
|
101
|
+
for (const entry of pluginEntries) {
|
|
102
|
+
const installPath = typeof entry?.installPath === 'string' ? entry.installPath.trim() : '';
|
|
103
|
+
if (installPath && fs.existsSync(path.join(installPath, '.claude-plugin', 'plugin.json'))) {
|
|
104
|
+
return installPath;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (!fs.existsSync(CLAUDE_PLUGIN_CACHE_DIR)) return null;
|
|
110
|
+
|
|
111
|
+
const versionDirs = fs
|
|
112
|
+
.readdirSync(CLAUDE_PLUGIN_CACHE_DIR, { withFileTypes: true })
|
|
113
|
+
.filter(entry => entry.isDirectory())
|
|
114
|
+
.map(entry => path.join(CLAUDE_PLUGIN_CACHE_DIR, entry.name))
|
|
115
|
+
.filter(candidate => fs.existsSync(path.join(candidate, '.claude-plugin', 'plugin.json')))
|
|
116
|
+
.sort()
|
|
117
|
+
.reverse();
|
|
118
|
+
|
|
119
|
+
return versionDirs[0] ?? null;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function envTruthy(value) {
|
|
123
|
+
return /^(1|true|yes|on)$/i.test(String(value ?? '').trim());
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function claudeLaunchPluginDir() {
|
|
127
|
+
const explicitPluginDir = process.env.OVERLORD_CLAUDE_PLUGIN_DIR?.trim();
|
|
128
|
+
if (explicitPluginDir) return explicitPluginDir;
|
|
129
|
+
|
|
130
|
+
if (envTruthy(process.env.OVERLORD_USE_CLAUDE_SOURCE_PLUGIN)) {
|
|
131
|
+
return claudeSourcePluginDir();
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
|
|
50
137
|
function antigravityBundleInstalled() {
|
|
51
138
|
return fs.existsSync(
|
|
52
139
|
path.join(os.homedir(), '.gemini', 'antigravity-cli', 'plugins', 'plugin.json')
|
|
@@ -55,7 +142,7 @@ function antigravityBundleInstalled() {
|
|
|
55
142
|
|
|
56
143
|
function getInstructionMode(agent) {
|
|
57
144
|
if (agent === 'claude') {
|
|
58
|
-
return
|
|
145
|
+
return claudeInstalledPluginDir() || claudeLaunchPluginDir() ? 'bundle' : 'legacy';
|
|
59
146
|
}
|
|
60
147
|
|
|
61
148
|
if (agent === 'codex') {
|
|
@@ -397,8 +484,9 @@ async function runAgent(agent, mode = 'run', options = {}) {
|
|
|
397
484
|
process.chdir(options.workingDirectory);
|
|
398
485
|
}
|
|
399
486
|
|
|
487
|
+
const useMountedProjectScratch = Boolean(options.workingDirectory || options.preCommand?.trim());
|
|
400
488
|
const scratchDir = resolveLaunchScratchDir(options.workingDirectory, {
|
|
401
|
-
explicit:
|
|
489
|
+
explicit: useMountedProjectScratch
|
|
402
490
|
});
|
|
403
491
|
|
|
404
492
|
const launchOrganizationId = resolveLaunchOrganizationId(
|
|
@@ -471,24 +559,38 @@ async function runAgent(agent, mode = 'run', options = {}) {
|
|
|
471
559
|
: {})
|
|
472
560
|
}, scratchDir);
|
|
473
561
|
const extraArgs = buildExtraArgs(agent, options);
|
|
562
|
+
const useContextFilePrompt = Boolean(options.preCommand?.trim());
|
|
563
|
+
const launchContextFile = useContextFilePrompt
|
|
564
|
+
? writeLaunchContextFile(scratchDir, ticketId, context)
|
|
565
|
+
: null;
|
|
474
566
|
|
|
475
567
|
try {
|
|
476
568
|
if (agent === 'claude') {
|
|
477
|
-
const pluginDir =
|
|
569
|
+
const pluginDir = claudeLaunchPluginDir();
|
|
478
570
|
if (mode === 'resume') {
|
|
479
571
|
const claudeSessionId = process.env.CLAUDE_SESSION_ID?.trim();
|
|
572
|
+
const resumePrompt = launchContextFile
|
|
573
|
+
? buildContextFilePrompt(launchContextFile, 'Resume working on this Overlord ticket.')
|
|
574
|
+
: context;
|
|
480
575
|
const args = claudeSessionId
|
|
481
|
-
? ['--resume', claudeSessionId,
|
|
482
|
-
: ['--continue',
|
|
576
|
+
? ['--resume', claudeSessionId, resumePrompt]
|
|
577
|
+
: ['--continue', resumePrompt];
|
|
578
|
+
if (launchContextFile) args.unshift('--append-system-prompt-file', launchContextFile);
|
|
483
579
|
args.unshift(...extraArgs);
|
|
484
580
|
if (pluginDir) args.unshift('--plugin-dir', pluginDir);
|
|
485
581
|
execAgentBinary('claude', args, { stdio: 'inherit', env: childEnv }, options.preCommand);
|
|
486
582
|
} else {
|
|
583
|
+
const startPrompt = launchContextFile
|
|
584
|
+
? buildContextFilePrompt(
|
|
585
|
+
launchContextFile,
|
|
586
|
+
'Begin working on this ticket.'
|
|
587
|
+
)
|
|
588
|
+
: 'Begin working on this ticket. Start by calling the attach endpoint, then proceed with the objective described in your system prompt.';
|
|
487
589
|
const args = [
|
|
488
|
-
'--append-system-prompt',
|
|
489
|
-
context,
|
|
590
|
+
launchContextFile ? '--append-system-prompt-file' : '--append-system-prompt',
|
|
591
|
+
launchContextFile ?? context,
|
|
490
592
|
...extraArgs,
|
|
491
|
-
|
|
593
|
+
startPrompt
|
|
492
594
|
];
|
|
493
595
|
if (pluginDir) args.unshift('--plugin-dir', pluginDir);
|
|
494
596
|
execAgentBinary('claude', args, { stdio: 'inherit', env: childEnv }, options.preCommand);
|
|
@@ -496,15 +598,23 @@ async function runAgent(agent, mode = 'run', options = {}) {
|
|
|
496
598
|
} else if (agent === 'codex') {
|
|
497
599
|
if (mode === 'resume') {
|
|
498
600
|
const codexSessionId = process.env.CODEX_SESSION_ID?.trim();
|
|
601
|
+
const resumePrompt = launchContextFile
|
|
602
|
+
? buildContextFilePrompt(launchContextFile, 'Resume working on this Overlord ticket.')
|
|
603
|
+
: context;
|
|
499
604
|
const args = codexSessionId
|
|
500
|
-
? ['resume', codexSessionId,
|
|
501
|
-
: ['resume', '--last',
|
|
605
|
+
? ['resume', codexSessionId, resumePrompt]
|
|
606
|
+
: ['resume', '--last', resumePrompt];
|
|
502
607
|
args.splice(1, 0, ...extraArgs);
|
|
503
608
|
execAgentBinary('codex', args, { stdio: 'inherit', env: childEnv }, options.preCommand);
|
|
504
609
|
} else {
|
|
505
610
|
execAgentBinary(
|
|
506
611
|
'codex',
|
|
507
|
-
[
|
|
612
|
+
[
|
|
613
|
+
...extraArgs,
|
|
614
|
+
launchContextFile
|
|
615
|
+
? buildContextFilePrompt(launchContextFile, 'Begin working on this Overlord ticket.')
|
|
616
|
+
: context
|
|
617
|
+
],
|
|
508
618
|
{ stdio: 'inherit', env: childEnv },
|
|
509
619
|
options.preCommand
|
|
510
620
|
);
|
|
@@ -512,22 +622,36 @@ async function runAgent(agent, mode = 'run', options = {}) {
|
|
|
512
622
|
} else if (agent === 'cursor') {
|
|
513
623
|
execAgentBinary(
|
|
514
624
|
'agent',
|
|
515
|
-
[
|
|
625
|
+
[
|
|
626
|
+
...extraArgs,
|
|
627
|
+
launchContextFile
|
|
628
|
+
? buildContextFilePrompt(launchContextFile, 'Begin working on this Overlord ticket.')
|
|
629
|
+
: context
|
|
630
|
+
],
|
|
516
631
|
{ stdio: 'inherit', env: childEnv },
|
|
517
632
|
options.preCommand
|
|
518
633
|
);
|
|
519
634
|
} else if (agent === 'opencode') {
|
|
520
635
|
if (mode === 'resume') {
|
|
521
636
|
const openCodeSessionId = process.env.OPENCODE_SESSION_ID?.trim();
|
|
637
|
+
const resumePrompt = launchContextFile
|
|
638
|
+
? buildContextFilePrompt(launchContextFile, 'Resume working on this Overlord ticket.')
|
|
639
|
+
: context;
|
|
522
640
|
const args = openCodeSessionId
|
|
523
|
-
? ['--continue', '--session', openCodeSessionId, '--prompt',
|
|
524
|
-
: ['--continue', '--prompt',
|
|
641
|
+
? ['--continue', '--session', openCodeSessionId, '--prompt', resumePrompt]
|
|
642
|
+
: ['--continue', '--prompt', resumePrompt];
|
|
525
643
|
args.unshift(...extraArgs);
|
|
526
644
|
execAgentBinary('opencode', args, { stdio: 'inherit', env: childEnv }, options.preCommand);
|
|
527
645
|
} else {
|
|
528
646
|
execAgentBinary(
|
|
529
647
|
'opencode',
|
|
530
|
-
[
|
|
648
|
+
[
|
|
649
|
+
...extraArgs,
|
|
650
|
+
'--prompt',
|
|
651
|
+
launchContextFile
|
|
652
|
+
? buildContextFilePrompt(launchContextFile, 'Begin working on this Overlord ticket.')
|
|
653
|
+
: context
|
|
654
|
+
],
|
|
531
655
|
{ stdio: 'inherit', env: childEnv },
|
|
532
656
|
options.preCommand
|
|
533
657
|
);
|
|
@@ -535,24 +659,34 @@ async function runAgent(agent, mode = 'run', options = {}) {
|
|
|
535
659
|
} else if (agent === 'pi') {
|
|
536
660
|
if (mode === 'resume') {
|
|
537
661
|
const piSessionId = process.env.PI_SESSION_ID?.trim();
|
|
662
|
+
const resumePrompt = launchContextFile
|
|
663
|
+
? buildContextFilePrompt(launchContextFile, 'Resume working on this Overlord ticket.')
|
|
664
|
+
: context;
|
|
538
665
|
const args = piSessionId
|
|
539
|
-
? ['--session', piSessionId,
|
|
540
|
-
: ['--continue',
|
|
666
|
+
? ['--session', piSessionId, resumePrompt]
|
|
667
|
+
: ['--continue', resumePrompt];
|
|
541
668
|
args.unshift(...extraArgs);
|
|
542
669
|
execAgentBinary('pi', args, { stdio: 'inherit', env: childEnv }, options.preCommand);
|
|
543
670
|
} else {
|
|
544
671
|
execAgentBinary(
|
|
545
672
|
'pi',
|
|
546
|
-
[
|
|
673
|
+
[
|
|
674
|
+
...extraArgs,
|
|
675
|
+
launchContextFile
|
|
676
|
+
? buildContextFilePrompt(launchContextFile, 'Begin working on this Overlord ticket.')
|
|
677
|
+
: context
|
|
678
|
+
],
|
|
547
679
|
{ stdio: 'inherit', env: childEnv },
|
|
548
680
|
options.preCommand
|
|
549
681
|
);
|
|
550
682
|
}
|
|
551
683
|
} else if (agent === 'antigravity') {
|
|
552
684
|
const tag = `overlord-${ticketId.slice(-8)}-${Date.now()}`;
|
|
553
|
-
const contextFile = path.join(scratchDir, `${tag}-ctx.md`);
|
|
554
|
-
|
|
555
|
-
|
|
685
|
+
const contextFile = launchContextFile ?? path.join(scratchDir, `${tag}-ctx.md`);
|
|
686
|
+
if (!launchContextFile) {
|
|
687
|
+
fs.writeFileSync(contextFile, context, 'utf-8');
|
|
688
|
+
setTimeout(() => { try { fs.unlinkSync(contextFile); } catch { /* already gone */ } }, 30 * 60_000).unref();
|
|
689
|
+
}
|
|
556
690
|
|
|
557
691
|
const agySessionId =
|
|
558
692
|
process.env.AGY_SESSION_ID?.trim() ?? process.env.GEMINI_SESSION_ID?.trim();
|
package/bin/_cli/setup.mjs
CHANGED
|
@@ -1138,7 +1138,9 @@ function installClaude() {
|
|
|
1138
1138
|
} else {
|
|
1139
1139
|
console.log(' ✓ No v3.25 Claude connector files needed migration.');
|
|
1140
1140
|
}
|
|
1141
|
-
console.log(
|
|
1141
|
+
console.log(
|
|
1142
|
+
' ✓ `ovld launch claude` now uses the installed Claude local marketplace plugin by default.'
|
|
1143
|
+
);
|
|
1142
1144
|
|
|
1143
1145
|
const manifest = readManifest();
|
|
1144
1146
|
manifest.claude = {
|