overlord-cli 0.2605111338.0 → 0.2605120852.0
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/protocol.mjs +221 -127
- package/package.json +1 -1
- package/plugins/claude/skills/overlord-ticket/SKILL.md +2 -1
- package/plugins/cursor/scripts/overlord-mcp.mjs +14 -9
- package/plugins/cursor/skills/overlord-ticket/SKILL.md +2 -1
- package/plugins/overlord/README.md +3 -3
- package/plugins/overlord/scripts/overlord-mcp.mjs +40 -30
- package/plugins/overlord/skills/overlord-ticket/SKILL.md +3 -2
package/bin/_cli/protocol.mjs
CHANGED
|
@@ -7,7 +7,6 @@ import path from 'node:path';
|
|
|
7
7
|
|
|
8
8
|
import { buildAuthHeaders, getAuthStatus, resolveAuth } from './credentials.mjs';
|
|
9
9
|
|
|
10
|
-
|
|
11
10
|
/**
|
|
12
11
|
* Parse simple CLI flags: --key value or --key=value
|
|
13
12
|
* @param {string[]} args
|
|
@@ -361,8 +360,8 @@ async function resolveSnapshotContext(flags) {
|
|
|
361
360
|
|
|
362
361
|
/**
|
|
363
362
|
* Shallow-merge snapshot metadata: launch/env context first, then deliver payload
|
|
364
|
-
* (payload wins on duplicate keys) so agents can add
|
|
365
|
-
*
|
|
363
|
+
* (payload wins on duplicate keys) so agents can add provenance details without
|
|
364
|
+
* losing launch context from OVERLORD_SNAPSHOT_JSON.
|
|
366
365
|
* @param {Record<string, unknown> | null | undefined} fromEnvOrFlags
|
|
367
366
|
* @param {Record<string, unknown> | null | undefined} fromPayload
|
|
368
367
|
* @returns {Record<string, unknown> | null}
|
|
@@ -390,133 +389,207 @@ function runLocalCommand(command, args, options = {}) {
|
|
|
390
389
|
});
|
|
391
390
|
}
|
|
392
391
|
|
|
393
|
-
function
|
|
392
|
+
function resolveGitRepoRoot(workspacePath) {
|
|
394
393
|
try {
|
|
395
|
-
runLocalCommand(
|
|
396
|
-
|
|
394
|
+
return runLocalCommand('git', ['-C', workspacePath, 'rev-parse', '--show-toplevel'], {
|
|
395
|
+
cwd: workspacePath,
|
|
396
|
+
timeoutMs: 15000
|
|
397
|
+
}).trim();
|
|
397
398
|
} catch {
|
|
398
|
-
return
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
function detectLocalCheckpointBackend(workspacePath, preference) {
|
|
403
|
-
if (!workspacePath || !fs.existsSync(workspacePath)) return null;
|
|
404
|
-
if (preference === 'jj') return 'jj';
|
|
405
|
-
if (preference === 'git') return 'git';
|
|
406
|
-
if (
|
|
407
|
-
fs.existsSync(path.join(workspacePath, '.jj')) ||
|
|
408
|
-
commandSucceeds('jj', ['--repository', workspacePath, 'root'], workspacePath)
|
|
409
|
-
) {
|
|
410
|
-
return 'jj';
|
|
411
|
-
}
|
|
412
|
-
if (commandSucceeds('git', ['-C', workspacePath, 'rev-parse', '--show-toplevel'], workspacePath)) {
|
|
413
|
-
return 'git';
|
|
399
|
+
return null;
|
|
414
400
|
}
|
|
415
|
-
return null;
|
|
416
401
|
}
|
|
417
402
|
|
|
418
|
-
|
|
403
|
+
/**
|
|
404
|
+
* Snapshot the working tree to a hidden ref refs/overlord/checkpoints/<objectiveId>.
|
|
405
|
+
* Captures tracked + untracked + staged + unstaged in one commit-tree object.
|
|
406
|
+
* Idempotent: if the ref already exists, returns the existing sha.
|
|
407
|
+
* Returns null when not in a git repo or when no objectiveId is available.
|
|
408
|
+
*/
|
|
409
|
+
function createLocalCheckpoint({ flags, kind = 'objective', objectiveId, snapshot = null }) {
|
|
419
410
|
if (flags['skip-checkpoint']) return null;
|
|
420
411
|
|
|
421
|
-
const preference = String(flags['checkpoint-backend'] ?? 'auto').trim();
|
|
422
|
-
if (!['auto', 'jj', 'git'].includes(preference)) {
|
|
423
|
-
throw new Error('--checkpoint-backend must be one of: auto, jj, git');
|
|
424
|
-
}
|
|
425
|
-
|
|
426
412
|
const workspacePath = path.resolve(
|
|
427
413
|
String(snapshot?.workspacePath ?? process.env.OVERLORD_WORKSPACE_PATH ?? process.cwd())
|
|
428
414
|
);
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
if (
|
|
415
|
+
const repoRoot = fs.existsSync(workspacePath) ? resolveGitRepoRoot(workspacePath) : null;
|
|
416
|
+
if (!repoRoot) return null;
|
|
417
|
+
|
|
418
|
+
const resolvedObjectiveId =
|
|
419
|
+
objectiveId ?? snapshot?.objectiveId ?? process.env.OVERLORD_OBJECTIVE_ID ?? null;
|
|
420
|
+
if (!resolvedObjectiveId || !/^[A-Za-z0-9_-]+$/.test(String(resolvedObjectiveId))) {
|
|
421
|
+
// Without an objective id we cannot name the ref deterministically.
|
|
422
|
+
// Fall back to recording HEAD only so the protocol still has provenance.
|
|
435
423
|
try {
|
|
436
|
-
runLocalCommand('
|
|
437
|
-
cwd:
|
|
438
|
-
});
|
|
439
|
-
const
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
{ cwd: workspacePath }
|
|
443
|
-
);
|
|
444
|
-
const operation = runLocalCommand(
|
|
445
|
-
'jj',
|
|
446
|
-
[
|
|
447
|
-
'--repository',
|
|
448
|
-
workspacePath,
|
|
449
|
-
'op',
|
|
450
|
-
'log',
|
|
451
|
-
'--at-op=@',
|
|
452
|
-
'--ignore-working-copy',
|
|
453
|
-
'-n',
|
|
454
|
-
'1',
|
|
455
|
-
'-T',
|
|
456
|
-
'id'
|
|
457
|
-
],
|
|
458
|
-
{ cwd: workspacePath }
|
|
459
|
-
);
|
|
460
|
-
const diffStat = runLocalCommand('jj', ['--repository', workspacePath, 'diff', '--stat'], {
|
|
461
|
-
cwd: workspacePath
|
|
462
|
-
});
|
|
463
|
-
const [jjChangeId = null, jjCommitId = null] = commit.trim().split(/\s+/, 2);
|
|
424
|
+
const gitCommitId = runLocalCommand('git', ['-C', repoRoot, 'rev-parse', 'HEAD'], {
|
|
425
|
+
cwd: repoRoot
|
|
426
|
+
}).trim();
|
|
427
|
+
const diffStat = runLocalCommand('git', ['-C', repoRoot, 'diff', '--stat', 'HEAD'], {
|
|
428
|
+
cwd: repoRoot
|
|
429
|
+
}).trim();
|
|
464
430
|
return {
|
|
465
|
-
checkpoint: {
|
|
466
|
-
diffStat: diffStat.trim() || null,
|
|
467
|
-
kind: 'delivery'
|
|
468
|
-
},
|
|
431
|
+
checkpoint: { diffStat: diffStat || null, kind },
|
|
469
432
|
snapshot: {
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
jjChangeId,
|
|
474
|
-
jjCommitId,
|
|
475
|
-
jjOperationId: operation.trim() || null,
|
|
476
|
-
diffStat: diffStat.trim() || null
|
|
433
|
+
gitCommitId: gitCommitId || null,
|
|
434
|
+
headSha: gitCommitId || null,
|
|
435
|
+
diffStat: diffStat || null
|
|
477
436
|
}
|
|
478
437
|
};
|
|
479
|
-
} catch
|
|
480
|
-
|
|
481
|
-
`Failed to create JJ delivery checkpoint in ${workspacePath}: ${
|
|
482
|
-
error instanceof Error ? error.message : String(error)
|
|
483
|
-
}\nRe-run with --skip-checkpoint only if you intentionally want to deliver without local JJ provenance.`
|
|
484
|
-
);
|
|
438
|
+
} catch {
|
|
439
|
+
return null;
|
|
485
440
|
}
|
|
486
441
|
}
|
|
487
442
|
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
443
|
+
const ref = `refs/overlord/checkpoints/${resolvedObjectiveId}`;
|
|
444
|
+
const headSha = runLocalCommand('git', ['-C', repoRoot, 'rev-parse', 'HEAD'], {
|
|
445
|
+
cwd: repoRoot
|
|
446
|
+
}).trim();
|
|
491
447
|
|
|
448
|
+
// If the ref already exists, return the existing snapshot.
|
|
449
|
+
let existingSha = '';
|
|
492
450
|
try {
|
|
493
|
-
|
|
494
|
-
cwd:
|
|
451
|
+
existingSha = runLocalCommand('git', ['-C', repoRoot, 'rev-parse', '--verify', ref], {
|
|
452
|
+
cwd: repoRoot
|
|
495
453
|
}).trim();
|
|
496
|
-
|
|
497
|
-
|
|
454
|
+
} catch {
|
|
455
|
+
/* not present */
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
if (existingSha) {
|
|
459
|
+
let diffStat = '';
|
|
460
|
+
try {
|
|
461
|
+
diffStat = runLocalCommand('git', ['-C', repoRoot, 'diff', '--stat', `${existingSha}^!`], {
|
|
462
|
+
cwd: repoRoot
|
|
463
|
+
}).trim();
|
|
464
|
+
} catch {
|
|
465
|
+
/* ignore */
|
|
466
|
+
}
|
|
467
|
+
return {
|
|
468
|
+
checkpoint: { diffStat: diffStat || null, kind },
|
|
469
|
+
snapshot: {
|
|
470
|
+
gitCommitId: existingSha,
|
|
471
|
+
gitRefName: ref,
|
|
472
|
+
headSha,
|
|
473
|
+
objectiveId: String(resolvedObjectiveId),
|
|
474
|
+
diffStat: diffStat || null
|
|
475
|
+
}
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// Build a tree from the current working copy without polluting the user's
|
|
480
|
+
// index by writing to a temporary index file.
|
|
481
|
+
const tempIndex = path.join(repoRoot, '.git', `overlord-snap-index-${Date.now()}`);
|
|
482
|
+
const env = { ...process.env, NO_COLOR: '1', GIT_INDEX_FILE: tempIndex };
|
|
483
|
+
try {
|
|
484
|
+
execFileSync('git', ['-C', repoRoot, 'read-tree', 'HEAD'], { cwd: repoRoot, env });
|
|
485
|
+
execFileSync('git', ['-C', repoRoot, 'add', '-A'], { cwd: repoRoot, env });
|
|
486
|
+
const tree = execFileSync('git', ['-C', repoRoot, 'write-tree'], {
|
|
487
|
+
cwd: repoRoot,
|
|
488
|
+
env,
|
|
489
|
+
encoding: 'utf8'
|
|
498
490
|
}).trim();
|
|
491
|
+
if (!tree) throw new Error('git write-tree produced no output.');
|
|
492
|
+
const commit = execFileSync(
|
|
493
|
+
'git',
|
|
494
|
+
[
|
|
495
|
+
'-C',
|
|
496
|
+
repoRoot,
|
|
497
|
+
'commit-tree',
|
|
498
|
+
tree,
|
|
499
|
+
'-p',
|
|
500
|
+
headSha,
|
|
501
|
+
'-m',
|
|
502
|
+
`overlord checkpoint ${resolvedObjectiveId}`
|
|
503
|
+
],
|
|
504
|
+
{ cwd: repoRoot, env: { ...process.env, NO_COLOR: '1' }, encoding: 'utf8' }
|
|
505
|
+
).trim();
|
|
506
|
+
if (!commit) throw new Error('git commit-tree produced no output.');
|
|
507
|
+
execFileSync('git', ['-C', repoRoot, 'update-ref', ref, commit], {
|
|
508
|
+
cwd: repoRoot
|
|
509
|
+
});
|
|
510
|
+
let diffStat = '';
|
|
511
|
+
try {
|
|
512
|
+
diffStat = runLocalCommand('git', ['-C', repoRoot, 'diff', '--stat', `${commit}^!`], {
|
|
513
|
+
cwd: repoRoot
|
|
514
|
+
}).trim();
|
|
515
|
+
} catch {
|
|
516
|
+
/* ignore */
|
|
517
|
+
}
|
|
499
518
|
return {
|
|
500
|
-
checkpoint: {
|
|
501
|
-
diffStat: diffStat || null,
|
|
502
|
-
kind: 'delivery'
|
|
503
|
-
},
|
|
519
|
+
checkpoint: { diffStat: diffStat || null, kind },
|
|
504
520
|
snapshot: {
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
521
|
+
gitCommitId: commit,
|
|
522
|
+
gitRefName: ref,
|
|
523
|
+
headSha,
|
|
524
|
+
objectiveId: String(resolvedObjectiveId),
|
|
509
525
|
diffStat: diffStat || null
|
|
510
526
|
}
|
|
511
527
|
};
|
|
512
|
-
} catch {
|
|
513
|
-
|
|
514
|
-
|
|
528
|
+
} catch (error) {
|
|
529
|
+
throw new Error(
|
|
530
|
+
`Failed to create git checkpoint in ${repoRoot}: ${
|
|
531
|
+
error instanceof Error ? error.message : String(error)
|
|
532
|
+
}\nRe-run attach with --skip-checkpoint to proceed without local provenance.`
|
|
533
|
+
);
|
|
534
|
+
} finally {
|
|
535
|
+
try {
|
|
536
|
+
fs.unlinkSync(tempIndex);
|
|
537
|
+
} catch {
|
|
538
|
+
/* ignore */
|
|
515
539
|
}
|
|
516
|
-
return null;
|
|
517
540
|
}
|
|
518
541
|
}
|
|
519
542
|
|
|
543
|
+
async function createAndRecordPendingCheckpoints({
|
|
544
|
+
attachData,
|
|
545
|
+
bearerToken,
|
|
546
|
+
flags,
|
|
547
|
+
localSecret,
|
|
548
|
+
organizationId,
|
|
549
|
+
platformUrl,
|
|
550
|
+
ticketId,
|
|
551
|
+
timeoutMs
|
|
552
|
+
}) {
|
|
553
|
+
const pendingObjectiveIds = Array.isArray(attachData.pendingCheckpointObjectiveIds)
|
|
554
|
+
? attachData.pendingCheckpointObjectiveIds.filter(id => typeof id === 'string' && id.trim())
|
|
555
|
+
: [];
|
|
556
|
+
const sessionKey = attachData.session?.sessionKey;
|
|
557
|
+
if (!sessionKey || pendingObjectiveIds.length === 0) return [];
|
|
558
|
+
|
|
559
|
+
const recorded = [];
|
|
560
|
+
for (const objectiveId of pendingObjectiveIds) {
|
|
561
|
+
const checkpointResult = createLocalCheckpoint({
|
|
562
|
+
flags,
|
|
563
|
+
kind: 'objective',
|
|
564
|
+
objectiveId: objectiveId.trim()
|
|
565
|
+
});
|
|
566
|
+
if (!checkpointResult?.snapshot) {
|
|
567
|
+
process.stderr.write(
|
|
568
|
+
`[protocol] Skipped checkpoint for objective ${objectiveId}: no git repository found at ${process.cwd()}.\n`
|
|
569
|
+
);
|
|
570
|
+
continue;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
await apiPost(
|
|
574
|
+
platformUrl,
|
|
575
|
+
bearerToken,
|
|
576
|
+
localSecret,
|
|
577
|
+
organizationId,
|
|
578
|
+
'/api/protocol/update',
|
|
579
|
+
{
|
|
580
|
+
sessionKey,
|
|
581
|
+
ticketId,
|
|
582
|
+
summary: `Created local git checkpoint for objective ${objectiveId}.`,
|
|
583
|
+
phase: 'execute',
|
|
584
|
+
snapshot: checkpointResult.snapshot
|
|
585
|
+
},
|
|
586
|
+
timeoutMs
|
|
587
|
+
);
|
|
588
|
+
recorded.push(objectiveId);
|
|
589
|
+
}
|
|
590
|
+
return recorded;
|
|
591
|
+
}
|
|
592
|
+
|
|
520
593
|
function normalizeRepoRelativeFilePath(filePath, repoRoot) {
|
|
521
594
|
if (typeof filePath !== 'string') return null;
|
|
522
595
|
|
|
@@ -692,7 +765,8 @@ function resolveExternalSessionId(flags) {
|
|
|
692
765
|
async function protocolAttach(args) {
|
|
693
766
|
const flags = parseFlags(args);
|
|
694
767
|
const ticketId = requireFlag(flags, 'ticket-id', 'TICKET_ID');
|
|
695
|
-
const { platformUrl, bearerToken, localSecret, organizationId } =
|
|
768
|
+
const { platformUrl, bearerToken, localSecret, organizationId } =
|
|
769
|
+
await resolveProtocolAuthForFlags(flags, ticketId);
|
|
696
770
|
const timeoutMs = resolveTimeout(flags);
|
|
697
771
|
|
|
698
772
|
const externalSessionId = resolveExternalSessionId(flags);
|
|
@@ -715,6 +789,20 @@ async function protocolAttach(args) {
|
|
|
715
789
|
timeoutMs
|
|
716
790
|
);
|
|
717
791
|
|
|
792
|
+
const recordedCheckpointObjectiveIds = await createAndRecordPendingCheckpoints({
|
|
793
|
+
attachData: data,
|
|
794
|
+
bearerToken,
|
|
795
|
+
flags,
|
|
796
|
+
localSecret,
|
|
797
|
+
organizationId,
|
|
798
|
+
platformUrl,
|
|
799
|
+
ticketId,
|
|
800
|
+
timeoutMs
|
|
801
|
+
});
|
|
802
|
+
if (recordedCheckpointObjectiveIds.length > 0) {
|
|
803
|
+
data.recordedCheckpointObjectiveIds = recordedCheckpointObjectiveIds;
|
|
804
|
+
}
|
|
805
|
+
|
|
718
806
|
const sessionKey = data.session?.sessionKey;
|
|
719
807
|
console.log(JSON.stringify(data, null, 2));
|
|
720
808
|
|
|
@@ -738,7 +826,8 @@ async function protocolUpdate(args) {
|
|
|
738
826
|
? readTextFile(String(flags['summary-file']), '--summary-file')
|
|
739
827
|
: requireFlag(flags, 'summary', undefined);
|
|
740
828
|
|
|
741
|
-
const { platformUrl, bearerToken, localSecret, organizationId } =
|
|
829
|
+
const { platformUrl, bearerToken, localSecret, organizationId } =
|
|
830
|
+
await resolveProtocolAuthForFlags(flags, ticketId);
|
|
742
831
|
const timeoutMs = resolveTimeout(flags);
|
|
743
832
|
const changeRationales = await resolveChangeRationales(flags);
|
|
744
833
|
const externalSessionId = resolveExternalSessionId(flags);
|
|
@@ -795,7 +884,8 @@ async function protocolRecordChangeRationales(args) {
|
|
|
795
884
|
);
|
|
796
885
|
}
|
|
797
886
|
|
|
798
|
-
const { platformUrl, bearerToken, localSecret, organizationId } =
|
|
887
|
+
const { platformUrl, bearerToken, localSecret, organizationId } =
|
|
888
|
+
await resolveProtocolAuthForFlags(flags, ticketId);
|
|
799
889
|
const timeoutMs = resolveTimeout(flags);
|
|
800
890
|
const snapshot = await resolveSnapshotContext(flags);
|
|
801
891
|
|
|
@@ -837,7 +927,8 @@ async function protocolAsk(args) {
|
|
|
837
927
|
? readTextFile(String(flags['question-file']), '--question-file')
|
|
838
928
|
: requireFlag(flags, 'question', undefined);
|
|
839
929
|
|
|
840
|
-
const { platformUrl, bearerToken, localSecret, organizationId } =
|
|
930
|
+
const { platformUrl, bearerToken, localSecret, organizationId } =
|
|
931
|
+
await resolveProtocolAuthForFlags(flags, ticketId);
|
|
841
932
|
const timeoutMs = resolveTimeout(flags);
|
|
842
933
|
|
|
843
934
|
const body = {
|
|
@@ -873,7 +964,8 @@ async function protocolPermissionRequest(args) {
|
|
|
873
964
|
? await readJsonFileOrStdin(String(flags['payload-file']), '--payload-file')
|
|
874
965
|
: {};
|
|
875
966
|
|
|
876
|
-
const { platformUrl, bearerToken, localSecret, organizationId } =
|
|
967
|
+
const { platformUrl, bearerToken, localSecret, organizationId } =
|
|
968
|
+
await resolveProtocolAuthForFlags(flags, ticketId);
|
|
877
969
|
const timeoutMs = resolveTimeout(flags);
|
|
878
970
|
|
|
879
971
|
const data = await apiPost(
|
|
@@ -898,7 +990,8 @@ async function protocolReadContext(args) {
|
|
|
898
990
|
if (!sessionKey) throw new Error('--session-key is required (or set SESSION_KEY)');
|
|
899
991
|
if (!ticketId) throw new Error('--ticket-id is required (or set TICKET_ID)');
|
|
900
992
|
|
|
901
|
-
const { platformUrl, bearerToken, localSecret, organizationId } =
|
|
993
|
+
const { platformUrl, bearerToken, localSecret, organizationId } =
|
|
994
|
+
await resolveProtocolAuthForFlags(flags, ticketId);
|
|
902
995
|
const timeoutMs = resolveTimeout(flags);
|
|
903
996
|
|
|
904
997
|
const body = {
|
|
@@ -942,7 +1035,8 @@ async function protocolWriteContext(args) {
|
|
|
942
1035
|
value = String(flags.value);
|
|
943
1036
|
}
|
|
944
1037
|
|
|
945
|
-
const { platformUrl, bearerToken, localSecret, organizationId } =
|
|
1038
|
+
const { platformUrl, bearerToken, localSecret, organizationId } =
|
|
1039
|
+
await resolveProtocolAuthForFlags(flags, ticketId);
|
|
946
1040
|
const timeoutMs = resolveTimeout(flags);
|
|
947
1041
|
|
|
948
1042
|
const body = {
|
|
@@ -989,7 +1083,8 @@ async function protocolDeliver(args) {
|
|
|
989
1083
|
? readTextFile(String(flags['summary-file']), '--summary-file')
|
|
990
1084
|
: requireFlag(flags, 'summary', undefined));
|
|
991
1085
|
|
|
992
|
-
const { platformUrl, bearerToken, localSecret, organizationId } =
|
|
1086
|
+
const { platformUrl, bearerToken, localSecret, organizationId } =
|
|
1087
|
+
await resolveProtocolAuthForFlags(flags, ticketId);
|
|
993
1088
|
const timeoutMs = resolveTimeout(flags);
|
|
994
1089
|
|
|
995
1090
|
let artifacts = deliverPayload?.artifacts ?? [];
|
|
@@ -1011,14 +1106,10 @@ async function protocolDeliver(args) {
|
|
|
1011
1106
|
|
|
1012
1107
|
const changeRationales =
|
|
1013
1108
|
deliverPayload?.changeRationales ?? (await resolveChangeRationales(flags));
|
|
1014
|
-
|
|
1109
|
+
const snapshot = mergeDeliverSnapshot(
|
|
1015
1110
|
await resolveSnapshotContext(flags),
|
|
1016
1111
|
deliverPayload?.snapshot ?? null
|
|
1017
1112
|
);
|
|
1018
|
-
const checkpointResult = createLocalDeliveryCheckpoint(flags, snapshot);
|
|
1019
|
-
if (checkpointResult) {
|
|
1020
|
-
snapshot = mergeDeliverSnapshot(snapshot, checkpointResult.snapshot);
|
|
1021
|
-
}
|
|
1022
1113
|
validateDeliverFileChanges(flags, changeRationales);
|
|
1023
1114
|
|
|
1024
1115
|
const body = {
|
|
@@ -1026,9 +1117,6 @@ async function protocolDeliver(args) {
|
|
|
1026
1117
|
ticketId,
|
|
1027
1118
|
summary,
|
|
1028
1119
|
artifacts,
|
|
1029
|
-
...(checkpointResult?.checkpoint
|
|
1030
|
-
? { checkpoint: { ...checkpointResult.checkpoint, summary } }
|
|
1031
|
-
: {}),
|
|
1032
1120
|
...(snapshot ? { snapshot } : {}),
|
|
1033
1121
|
...(changeRationales.length > 0 ? { changeRationales } : {})
|
|
1034
1122
|
};
|
|
@@ -1055,7 +1143,8 @@ async function protocolAttachmentList(args) {
|
|
|
1055
1143
|
if (!sessionKey) throw new Error('--session-key is required (or set SESSION_KEY)');
|
|
1056
1144
|
if (!ticketId) throw new Error('--ticket-id is required (or set TICKET_ID)');
|
|
1057
1145
|
|
|
1058
|
-
const { platformUrl, bearerToken, localSecret, organizationId } =
|
|
1146
|
+
const { platformUrl, bearerToken, localSecret, organizationId } =
|
|
1147
|
+
await resolveProtocolAuthForFlags(flags, ticketId);
|
|
1059
1148
|
const timeoutMs = resolveTimeout(flags);
|
|
1060
1149
|
|
|
1061
1150
|
const body = {
|
|
@@ -1084,7 +1173,8 @@ async function protocolAttachmentPrepareUpload(args) {
|
|
|
1084
1173
|
const objectiveId = requireFlag(flags, 'objective-id', undefined);
|
|
1085
1174
|
const fileName = requireFlag(flags, 'file-name', undefined);
|
|
1086
1175
|
|
|
1087
|
-
const { platformUrl, bearerToken, localSecret, organizationId } =
|
|
1176
|
+
const { platformUrl, bearerToken, localSecret, organizationId } =
|
|
1177
|
+
await resolveProtocolAuthForFlags(flags, ticketId);
|
|
1088
1178
|
const timeoutMs = resolveTimeout(flags);
|
|
1089
1179
|
|
|
1090
1180
|
const body = {
|
|
@@ -1121,7 +1211,8 @@ async function protocolAttachmentFinalizeUpload(args) {
|
|
|
1121
1211
|
const storagePath = requireFlag(flags, 'storage-path', undefined);
|
|
1122
1212
|
const label = requireFlag(flags, 'label', undefined);
|
|
1123
1213
|
|
|
1124
|
-
const { platformUrl, bearerToken, localSecret, organizationId } =
|
|
1214
|
+
const { platformUrl, bearerToken, localSecret, organizationId } =
|
|
1215
|
+
await resolveProtocolAuthForFlags(flags, ticketId);
|
|
1125
1216
|
const timeoutMs = resolveTimeout(flags);
|
|
1126
1217
|
|
|
1127
1218
|
const body = {
|
|
@@ -1161,7 +1252,8 @@ async function protocolAttachmentGetDownloadUrl(args) {
|
|
|
1161
1252
|
throw new Error('--objective-id is required when using --storage-path');
|
|
1162
1253
|
}
|
|
1163
1254
|
|
|
1164
|
-
const { platformUrl, bearerToken, localSecret, organizationId } =
|
|
1255
|
+
const { platformUrl, bearerToken, localSecret, organizationId } =
|
|
1256
|
+
await resolveProtocolAuthForFlags(flags, ticketId);
|
|
1165
1257
|
const timeoutMs = resolveTimeout(flags);
|
|
1166
1258
|
|
|
1167
1259
|
const body = {
|
|
@@ -1202,7 +1294,8 @@ async function protocolAttachmentUploadFile(args) {
|
|
|
1202
1294
|
const fileStats = await stat(filePath);
|
|
1203
1295
|
const fileBytes = await readFile(filePath);
|
|
1204
1296
|
|
|
1205
|
-
const { platformUrl, bearerToken, localSecret, organizationId } =
|
|
1297
|
+
const { platformUrl, bearerToken, localSecret, organizationId } =
|
|
1298
|
+
await resolveProtocolAuthForFlags(flags, ticketId);
|
|
1206
1299
|
const timeoutMs = resolveTimeout(flags);
|
|
1207
1300
|
|
|
1208
1301
|
const metadata = flags['metadata-json']
|
|
@@ -1293,7 +1386,8 @@ async function protocolDiscoverProject(args) {
|
|
|
1293
1386
|
async function protocolConnect(args) {
|
|
1294
1387
|
const flags = parseFlags(args);
|
|
1295
1388
|
const ticketId = requireFlag(flags, 'ticket-id', 'TICKET_ID');
|
|
1296
|
-
const { platformUrl, bearerToken, localSecret, organizationId } =
|
|
1389
|
+
const { platformUrl, bearerToken, localSecret, organizationId } =
|
|
1390
|
+
await resolveProtocolAuthForFlags(flags, ticketId);
|
|
1297
1391
|
const timeoutMs = resolveTimeout(flags);
|
|
1298
1392
|
|
|
1299
1393
|
const body = {
|
|
@@ -1328,7 +1422,8 @@ async function protocolConnect(args) {
|
|
|
1328
1422
|
async function protocolLoadContext(args) {
|
|
1329
1423
|
const flags = parseFlags(args);
|
|
1330
1424
|
const ticketId = requireFlag(flags, 'ticket-id', 'TICKET_ID');
|
|
1331
|
-
const { platformUrl, bearerToken, localSecret, organizationId } =
|
|
1425
|
+
const { platformUrl, bearerToken, localSecret, organizationId } =
|
|
1426
|
+
await resolveProtocolAuthForFlags(flags, ticketId);
|
|
1332
1427
|
const timeoutMs = resolveTimeout(flags);
|
|
1333
1428
|
|
|
1334
1429
|
const body = { ticketId };
|
|
@@ -1421,7 +1516,8 @@ async function protocolCreateTicket(args) {
|
|
|
1421
1516
|
const flags = parseFlags(args);
|
|
1422
1517
|
const { sessionKey, ticketId } = resolveSessionFlags(flags);
|
|
1423
1518
|
const objective = requireFlag(flags, 'objective', undefined);
|
|
1424
|
-
const { platformUrl, bearerToken, localSecret, organizationId } =
|
|
1519
|
+
const { platformUrl, bearerToken, localSecret, organizationId } =
|
|
1520
|
+
await resolveProtocolAuthForFlags(flags, ticketId);
|
|
1425
1521
|
const timeoutMs = resolveTimeout(flags);
|
|
1426
1522
|
const agentIdentifier = resolveProtocolAgentIdentifier(flags);
|
|
1427
1523
|
const modelIdentifier = resolveProtocolModelIdentifier(flags);
|
|
@@ -1664,10 +1760,12 @@ attach:
|
|
|
1664
1760
|
--method <connectionMethod>
|
|
1665
1761
|
--external-session-id <id|null> Store the native agent thread/session id, or clear it with null
|
|
1666
1762
|
--metadata-json <json> Extra session metadata object
|
|
1763
|
+
--skip-checkpoint Bypass automatic objective-start git checkpoint creation
|
|
1667
1764
|
Returns:
|
|
1668
1765
|
Full JSON including session.sessionKey, ticket, history, artifacts, sharedState, and promptContext
|
|
1669
1766
|
Notes:
|
|
1670
1767
|
If --external-session-id is omitted, the CLI may auto-detect Codex or Claude session ids
|
|
1768
|
+
Attach creates and records missing local git checkpoints for executing objectives before work starts.
|
|
1671
1769
|
|
|
1672
1770
|
connect:
|
|
1673
1771
|
Purpose:
|
|
@@ -1789,13 +1887,10 @@ deliver:
|
|
|
1789
1887
|
--artifacts-file <path|->
|
|
1790
1888
|
--change-rationales-json <json>
|
|
1791
1889
|
--change-rationales-file <path|->
|
|
1792
|
-
--checkpoint-backend <auto|jj|git> Backend for the local delivery checkpoint (default: auto)
|
|
1793
|
-
--skip-checkpoint Bypass automatic local checkpoint creation
|
|
1794
1890
|
--skip-file-change-check Bypass local git vs changeRationales validation
|
|
1795
1891
|
Notes:
|
|
1796
1892
|
Use --payload-file - to read the full delivery JSON from stdin without creating a scratch file.
|
|
1797
1893
|
Do not combine --payload-file with --artifacts-json/--artifacts-file or change-rationale flags.
|
|
1798
|
-
Deliver creates a local checkpoint before the API request when the current workspace is JJ- or Git-managed.
|
|
1799
1894
|
In a git workspace, deliver validates that changed files are represented by changeRationales unless skipped.
|
|
1800
1895
|
|
|
1801
1896
|
prompt:
|
|
@@ -1932,7 +2027,6 @@ Examples:
|
|
|
1932
2027
|
ovld protocol deliver --session-key <key> --ticket-id <ticket_id> --summary "Done" --artifacts-file ./artifacts.json
|
|
1933
2028
|
ovld protocol deliver --session-key <key> --ticket-id <ticket_id> --payload-file ./deliver.json
|
|
1934
2029
|
ovld protocol deliver --session-key <key> --ticket-id <ticket_id> --payload-file -
|
|
1935
|
-
ovld protocol deliver --session-key <key> --ticket-id <ticket_id> --summary "Done" --checkpoint-backend jj
|
|
1936
2030
|
ovld protocol deliver --session-key <key> --ticket-id <ticket_id> --summary "Done" --skip-file-change-check
|
|
1937
2031
|
ovld protocol deliver --session-key <key> --ticket-id <ticket_id> --summary "Done" --timeout 60000
|
|
1938
2032
|
`);
|
package/package.json
CHANGED
|
@@ -152,6 +152,7 @@ The `attach` and `load-context` responses already include `attachments` and `obj
|
|
|
152
152
|
## Defaults And Notes
|
|
153
153
|
|
|
154
154
|
- API requires `agentIdentifier` and `connectionMethod` on attach/connect/prompt. The CLI defaults them to `claude-code`/`cli`; the MCP tool defaults to `mcp`. Override with `--agent` / `--method` when calling from a different runtime.
|
|
155
|
+
- Hosted Overlord MCP (`/functions/v1/mcp`) uses the same canonical tool names as any local MCP shim that shells into `ovld protocol` (`attach`, `update`, `deliver`, …). Hosted calls use camelCase JSON keys (`ticketId`, `sessionKey`) matching `POST /api/protocol/*` bodies; the local shim uses snake_case keys mapped to CLI flags (`ticket_id`, `session_key`).
|
|
155
156
|
- `permission-request` is invoked by the Claude Code permission hook installed by the bundle. Agents do not normally call it directly.
|
|
156
157
|
- `record_change_rationales` (MCP) and `ovld protocol record-change-rationales` (CLI) both write to the same `file_changes` table. The dedicated CLI route is `POST /api/protocol/record-change-rationales`.
|
|
157
158
|
- Objective attachment tools follow the `<verb>_<noun>` MCP naming: `list_attachments`, `prepare_attachment_upload`, `finalize_attachment_upload`, `get_attachment_download_url`, `upload_attachment_file`. CLI commands use `attachment-*` and require `--objective-id` for upload/finalize.
|
|
@@ -169,4 +170,4 @@ The `attach` and `load-context` responses already include `attachments` and `obj
|
|
|
169
170
|
- Do not add or commit changes unless the user explicitly asks you to commit.
|
|
170
171
|
- Delivery is the concluding step. After delivering, stop unless the user follows up or the ticket is reopened.
|
|
171
172
|
|
|
172
|
-
<!-- version: 0.4.
|
|
173
|
+
<!-- version: 0.4.7 -->
|
|
@@ -69,7 +69,7 @@ process.stdin.on('data', async chunk => {
|
|
|
69
69
|
result: {
|
|
70
70
|
protocolVersion: PROTOCOL_VERSION,
|
|
71
71
|
capabilities: { tools: { listChanged: false } },
|
|
72
|
-
serverInfo: { name: 'overlord-cursor', version: '0.1.
|
|
72
|
+
serverInfo: { name: 'overlord-cursor', version: '0.1.2' }
|
|
73
73
|
}
|
|
74
74
|
});
|
|
75
75
|
continue;
|
|
@@ -81,21 +81,26 @@ process.stdin.on('data', async chunk => {
|
|
|
81
81
|
result: {
|
|
82
82
|
tools: [
|
|
83
83
|
{
|
|
84
|
-
name: '
|
|
84
|
+
name: 'attach',
|
|
85
85
|
description: 'Attach to an Overlord ticket.',
|
|
86
86
|
inputSchema: { type: 'object', properties: { ticket_id: { type: 'string' } }, required: ['ticket_id'] }
|
|
87
87
|
},
|
|
88
88
|
{
|
|
89
|
-
name: '
|
|
89
|
+
name: 'update',
|
|
90
90
|
description: 'Post a progress update.',
|
|
91
91
|
inputSchema: {
|
|
92
92
|
type: 'object',
|
|
93
|
-
properties: {
|
|
93
|
+
properties: {
|
|
94
|
+
session_key: { type: 'string' },
|
|
95
|
+
ticket_id: { type: 'string' },
|
|
96
|
+
summary: { type: 'string' },
|
|
97
|
+
phase: { type: 'string' }
|
|
98
|
+
},
|
|
94
99
|
required: ['session_key', 'ticket_id', 'summary']
|
|
95
100
|
}
|
|
96
101
|
},
|
|
97
102
|
{
|
|
98
|
-
name: '
|
|
103
|
+
name: 'deliver',
|
|
99
104
|
description: 'Deliver completed work.',
|
|
100
105
|
inputSchema: {
|
|
101
106
|
type: 'object',
|
|
@@ -111,9 +116,9 @@ process.stdin.on('data', async chunk => {
|
|
|
111
116
|
if (message.method === 'tools/call') {
|
|
112
117
|
const toolName = message.params?.name;
|
|
113
118
|
const args = message.params?.arguments ?? {};
|
|
114
|
-
if (toolName === '
|
|
119
|
+
if (toolName === 'attach') {
|
|
115
120
|
send({ jsonrpc: '2.0', id: message.id, result: await runProtocol('attach', { 'ticket-id': args.ticket_id }) });
|
|
116
|
-
} else if (toolName === '
|
|
121
|
+
} else if (toolName === 'update') {
|
|
117
122
|
send({
|
|
118
123
|
jsonrpc: '2.0',
|
|
119
124
|
id: message.id,
|
|
@@ -121,10 +126,10 @@ process.stdin.on('data', async chunk => {
|
|
|
121
126
|
'session-key': args.session_key,
|
|
122
127
|
'ticket-id': args.ticket_id,
|
|
123
128
|
summary: args.summary,
|
|
124
|
-
phase: 'execute'
|
|
129
|
+
phase: args.phase && String(args.phase).trim() ? String(args.phase).trim() : 'execute'
|
|
125
130
|
})
|
|
126
131
|
});
|
|
127
|
-
} else if (toolName === '
|
|
132
|
+
} else if (toolName === 'deliver') {
|
|
128
133
|
send({
|
|
129
134
|
jsonrpc: '2.0',
|
|
130
135
|
id: message.id,
|
|
@@ -137,6 +137,7 @@ Objective attachment uploads also expose two-step variants — `attachment-prepa
|
|
|
137
137
|
## Defaults And Notes
|
|
138
138
|
|
|
139
139
|
- The Overlord API requires `agentIdentifier` and `connectionMethod` on attach/connect/prompt, but the CLI defaults them to `cursor`/`cli` (override with `--agent` / `--method`). The MCP tools default to `mcp`.
|
|
140
|
+
- The Cursor local MCP stub exposes `attach`, `update`, and `deliver` only (same names as the hosted Overlord MCP core set).
|
|
140
141
|
- `permission-request` is invoked by the installed permission hook; agents normally do not call it directly.
|
|
141
142
|
- The `record_change_rationales` MCP tool and `ovld protocol record-change-rationales` CLI both write to the same `file_changes` table; pick whichever fits your runtime.
|
|
142
143
|
|
|
@@ -151,4 +152,4 @@ Objective attachment uploads also expose two-step variants — `attachment-prepa
|
|
|
151
152
|
- If you must run `ovld auth login`, always include `--organization-id <id>` — use the organization ID from the ticket prompt context to select the organization non-interactively and avoid a blocking TTY prompt.
|
|
152
153
|
- Delivery is the concluding step. After delivering, stop unless the user follows up or the ticket is reopened.
|
|
153
154
|
|
|
154
|
-
<!-- version: 0.4.
|
|
155
|
+
<!-- version: 0.4.7 -->
|
|
@@ -22,9 +22,9 @@ personal marketplace entry at `~/.agents/plugins/marketplace.json`.
|
|
|
22
22
|
|
|
23
23
|
- Project resolution: `discover_project`
|
|
24
24
|
- Ticket search: `search_tickets` — keyword search + status/filter (direct API call)
|
|
25
|
-
- Ticket session flow: `
|
|
26
|
-
- Progress and review flow: `
|
|
27
|
-
- Shared context: `
|
|
25
|
+
- Ticket session flow: `attach`, `connect`, `load_ticket_context`, `prompt`
|
|
26
|
+
- Progress and review flow: `update`, `record_change_rationales`, `ask`, `deliver`
|
|
27
|
+
- Shared context: `read_context`, `write_context`
|
|
28
28
|
- Objective attachments: `list_attachments`, `prepare_attachment_upload`, `finalize_attachment_upload`, `get_attachment_download_url`, `upload_attachment_file`
|
|
29
29
|
|
|
30
30
|
The MCP server shells into the installed `ovld` binary so the plugin stays aligned with the shipped CLI behavior instead of depending on this repository checkout.
|
|
@@ -44,7 +44,7 @@ const tools = [
|
|
|
44
44
|
subcommand: 'discover-project'
|
|
45
45
|
},
|
|
46
46
|
{
|
|
47
|
-
name: '
|
|
47
|
+
name: 'attach',
|
|
48
48
|
description: 'Attach an agent session to an existing Overlord ticket and return the working context.',
|
|
49
49
|
inputSchema: {
|
|
50
50
|
type: 'object',
|
|
@@ -52,7 +52,11 @@ const tools = [
|
|
|
52
52
|
ticket_id: { type: 'string', description: 'Ticket identifier (e.g. 1:899). Also accepts UUID.' },
|
|
53
53
|
agent: { type: 'string' },
|
|
54
54
|
method: { type: 'string' },
|
|
55
|
-
external_session_id: { type: ['string', 'null'] }
|
|
55
|
+
external_session_id: { type: ['string', 'null'] },
|
|
56
|
+
metadata: {
|
|
57
|
+
type: 'object',
|
|
58
|
+
description: 'Optional extra metadata merged into the attach request (same as --metadata-json on the CLI).'
|
|
59
|
+
}
|
|
56
60
|
},
|
|
57
61
|
required: ['ticket_id']
|
|
58
62
|
},
|
|
@@ -60,36 +64,47 @@ const tools = [
|
|
|
60
64
|
'ticket-id': args.ticket_id,
|
|
61
65
|
agent: args.agent,
|
|
62
66
|
method: args.method,
|
|
63
|
-
'external-session-id': args.external_session_id
|
|
67
|
+
'external-session-id': args.external_session_id,
|
|
68
|
+
...(args.metadata && typeof args.metadata === 'object' && !Array.isArray(args.metadata)
|
|
69
|
+
? { 'metadata-json': args.metadata }
|
|
70
|
+
: {})
|
|
64
71
|
}),
|
|
65
72
|
subcommand: 'attach'
|
|
66
73
|
},
|
|
67
74
|
{
|
|
68
|
-
name: '
|
|
75
|
+
name: 'connect',
|
|
69
76
|
description: 'Create a lightweight Overlord session without loading the full ticket context.',
|
|
70
77
|
inputSchema: {
|
|
71
78
|
type: 'object',
|
|
72
79
|
properties: {
|
|
73
80
|
ticket_id: { type: 'string', description: 'Ticket identifier (e.g. 1:899). Also accepts UUID.' },
|
|
74
81
|
agent: { type: 'string' },
|
|
75
|
-
method: { type: 'string' }
|
|
82
|
+
method: { type: 'string' },
|
|
83
|
+
metadata: {
|
|
84
|
+
type: 'object',
|
|
85
|
+
description: 'Optional extra metadata merged into the connect request (same as --metadata-json on the CLI).'
|
|
86
|
+
}
|
|
76
87
|
},
|
|
77
88
|
required: ['ticket_id']
|
|
78
89
|
},
|
|
79
90
|
toCliFlags: args => ({
|
|
80
91
|
'ticket-id': args.ticket_id,
|
|
81
92
|
agent: args.agent,
|
|
82
|
-
method: args.method
|
|
93
|
+
method: args.method,
|
|
94
|
+
...(args.metadata && typeof args.metadata === 'object' && !Array.isArray(args.metadata)
|
|
95
|
+
? { 'metadata-json': args.metadata }
|
|
96
|
+
: {})
|
|
83
97
|
}),
|
|
84
98
|
subcommand: 'connect'
|
|
85
99
|
},
|
|
86
100
|
{
|
|
87
101
|
name: 'load_ticket_context',
|
|
88
|
-
description:
|
|
102
|
+
description:
|
|
103
|
+
'Fetch Overlord ticket context without creating a session (maps to ovld protocol load-context; not the same as read_context).',
|
|
89
104
|
inputSchema: {
|
|
90
105
|
type: 'object',
|
|
91
106
|
properties: {
|
|
92
|
-
ticket_id: { type: 'string' }
|
|
107
|
+
ticket_id: { type: 'string', description: 'Ticket identifier (e.g. 1:899). Also accepts UUID.' }
|
|
93
108
|
},
|
|
94
109
|
required: ['ticket_id']
|
|
95
110
|
},
|
|
@@ -99,8 +114,8 @@ const tools = [
|
|
|
99
114
|
subcommand: 'load-context'
|
|
100
115
|
},
|
|
101
116
|
{
|
|
102
|
-
name: '
|
|
103
|
-
description: 'Create a ticket and attach to it immediately.',
|
|
117
|
+
name: 'prompt',
|
|
118
|
+
description: 'Create a ticket and attach to it immediately (ovld protocol prompt).',
|
|
104
119
|
inputSchema: {
|
|
105
120
|
type: 'object',
|
|
106
121
|
properties: {
|
|
@@ -109,6 +124,7 @@ const tools = [
|
|
|
109
124
|
priority: { type: 'string', enum: ['low', 'medium', 'high', 'urgent'] },
|
|
110
125
|
project_id: { type: 'string' },
|
|
111
126
|
working_directory: { type: 'string' },
|
|
127
|
+
personal: { type: 'boolean', description: 'Create without assigning a project (private ticket).' },
|
|
112
128
|
acceptance_criteria: { type: 'string' },
|
|
113
129
|
available_tools: { type: 'string' },
|
|
114
130
|
execution_target: { type: 'string', enum: ['agent', 'human'] },
|
|
@@ -126,6 +142,7 @@ const tools = [
|
|
|
126
142
|
priority: args.priority,
|
|
127
143
|
'project-id': args.project_id,
|
|
128
144
|
'working-directory': args.working_directory,
|
|
145
|
+
personal: args.personal === true ? true : undefined,
|
|
129
146
|
'acceptance-criteria': args.acceptance_criteria,
|
|
130
147
|
'available-tools': args.available_tools,
|
|
131
148
|
'execution-target': args.execution_target,
|
|
@@ -138,7 +155,7 @@ const tools = [
|
|
|
138
155
|
subcommand: 'prompt'
|
|
139
156
|
},
|
|
140
157
|
{
|
|
141
|
-
name: '
|
|
158
|
+
name: 'update',
|
|
142
159
|
description: 'Post an Overlord progress update or activity event.',
|
|
143
160
|
inputSchema: {
|
|
144
161
|
type: 'object',
|
|
@@ -192,7 +209,7 @@ const tools = [
|
|
|
192
209
|
subcommand: 'record-change-rationales'
|
|
193
210
|
},
|
|
194
211
|
{
|
|
195
|
-
name: '
|
|
212
|
+
name: 'ask',
|
|
196
213
|
description: 'Send a blocking question to the human reviewer or PM.',
|
|
197
214
|
inputSchema: {
|
|
198
215
|
type: 'object',
|
|
@@ -215,7 +232,7 @@ const tools = [
|
|
|
215
232
|
subcommand: 'ask'
|
|
216
233
|
},
|
|
217
234
|
{
|
|
218
|
-
name: '
|
|
235
|
+
name: 'read_context',
|
|
219
236
|
description: 'Read persistent shared context entries for a ticket.',
|
|
220
237
|
inputSchema: {
|
|
221
238
|
type: 'object',
|
|
@@ -236,7 +253,7 @@ const tools = [
|
|
|
236
253
|
subcommand: 'read-context'
|
|
237
254
|
},
|
|
238
255
|
{
|
|
239
|
-
name: '
|
|
256
|
+
name: 'write_context',
|
|
240
257
|
description: 'Write a persistent shared context entry for future Overlord sessions.',
|
|
241
258
|
inputSchema: {
|
|
242
259
|
type: 'object',
|
|
@@ -259,9 +276,9 @@ const tools = [
|
|
|
259
276
|
subcommand: 'write-context'
|
|
260
277
|
},
|
|
261
278
|
{
|
|
262
|
-
name: '
|
|
279
|
+
name: 'deliver',
|
|
263
280
|
description:
|
|
264
|
-
'Deliver final work back into Overlord with summary, artifacts, and change rationales. Large payloads are streamed to the CLI through stdin, so this tool does not create delivery scratch files.',
|
|
281
|
+
'Deliver final work back into Overlord with summary, optional artifacts, and optional change rationales (matches POST /api/protocol/deliver). Large payloads are streamed to the CLI through stdin, so this tool does not create delivery scratch files.',
|
|
265
282
|
inputSchema: {
|
|
266
283
|
type: 'object',
|
|
267
284
|
properties: {
|
|
@@ -280,11 +297,6 @@ const tools = [
|
|
|
280
297
|
description:
|
|
281
298
|
'Optional checkpoint metadata. The local CLI creates this automatically for JJ/Git workspaces during deliver.'
|
|
282
299
|
},
|
|
283
|
-
checkpoint_backend: {
|
|
284
|
-
type: 'string',
|
|
285
|
-
enum: ['auto', 'jj', 'git'],
|
|
286
|
-
description: 'Backend for automatic local checkpointing when routed through the CLI.'
|
|
287
|
-
},
|
|
288
300
|
skip_checkpoint: { type: 'boolean' },
|
|
289
301
|
skip_file_change_check: { type: 'boolean' }
|
|
290
302
|
},
|
|
@@ -294,7 +306,6 @@ const tools = [
|
|
|
294
306
|
'session-key': args.session_key,
|
|
295
307
|
'ticket-id': args.ticket_id,
|
|
296
308
|
'payload-file': '-',
|
|
297
|
-
'checkpoint-backend': args.checkpoint_backend,
|
|
298
309
|
'skip-checkpoint': args.skip_checkpoint,
|
|
299
310
|
'skip-file-change-check': args.skip_file_change_check
|
|
300
311
|
}),
|
|
@@ -466,7 +477,7 @@ const searchTicketsTool = {
|
|
|
466
477
|
},
|
|
467
478
|
limit: {
|
|
468
479
|
type: 'number',
|
|
469
|
-
description: 'Maximum number of results to return (1–
|
|
480
|
+
description: 'Maximum number of results to return (1–50, default 8).'
|
|
470
481
|
}
|
|
471
482
|
}
|
|
472
483
|
},
|
|
@@ -479,10 +490,9 @@ const searchTicketsTool = {
|
|
|
479
490
|
subcommand: 'search-tickets'
|
|
480
491
|
};
|
|
481
492
|
|
|
482
|
-
const
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
]);
|
|
493
|
+
const allListedTools = [...tools, searchTicketsTool];
|
|
494
|
+
|
|
495
|
+
const toolMap = new Map(allListedTools.map(tool => [tool.name, tool]));
|
|
486
496
|
let buffer = Buffer.alloc(0);
|
|
487
497
|
|
|
488
498
|
function serializeMessage(message) {
|
|
@@ -645,10 +655,10 @@ async function handleRequest(message) {
|
|
|
645
655
|
},
|
|
646
656
|
serverInfo: {
|
|
647
657
|
name: 'overlord',
|
|
648
|
-
version: '0.1.
|
|
658
|
+
version: '0.1.4'
|
|
649
659
|
},
|
|
650
660
|
instructions:
|
|
651
|
-
'Use these tools to drive Overlord ticket workflows through the installed ovld CLI. Most operations expect a session key from attach or connect.'
|
|
661
|
+
'Use these tools to drive Overlord ticket workflows through the installed ovld CLI. Tool names match the hosted Overlord MCP server (attach, update, deliver, …). Most operations expect a session key from attach or connect.'
|
|
652
662
|
});
|
|
653
663
|
return;
|
|
654
664
|
}
|
|
@@ -660,7 +670,7 @@ async function handleRequest(message) {
|
|
|
660
670
|
|
|
661
671
|
if (method === 'tools/list') {
|
|
662
672
|
success(id, {
|
|
663
|
-
tools:
|
|
673
|
+
tools: allListedTools.map(tool => ({
|
|
664
674
|
name: tool.name,
|
|
665
675
|
description: tool.description,
|
|
666
676
|
inputSchema: tool.inputSchema
|
|
@@ -138,7 +138,8 @@ The `attach` and `load-context` responses already include `attachments` and `obj
|
|
|
138
138
|
|
|
139
139
|
## Defaults And Notes
|
|
140
140
|
|
|
141
|
-
- API requires `agentIdentifier` and `connectionMethod` on attach/connect/prompt. The CLI defaults them to `codex`/`cli`; the MCP
|
|
141
|
+
- API requires `agentIdentifier` and `connectionMethod` on attach/connect/prompt. The CLI defaults them to `codex`/`cli`; the hosted MCP defaults to `mcp`. Override with `--agent` / `--method` when calling from a different runtime.
|
|
142
|
+
- Hosted MCP and the local `overlord-mcp.mjs` shim use the same **tool names** (\`attach\`, \`update\`, \`deliver\`, …). The shim uses **snake_case** JSON keys (\`ticket_id\`, \`session_key\`) that map to CLI flags; hosted MCP uses **camelCase** (\`ticketId\`, \`sessionKey\`) matching REST bodies.
|
|
142
143
|
- `permission-request` is invoked by the local Codex plugin's permission rules; agents do not normally call it directly.
|
|
143
144
|
- `record_change_rationales` (MCP) and `ovld protocol record-change-rationales` (CLI) both write to the `file_changes` table; the dedicated route is `POST /api/protocol/record-change-rationales`.
|
|
144
145
|
- Objective attachment MCP tools use `<verb>_<noun>` names — `list_attachments`, `prepare_attachment_upload`, `finalize_attachment_upload`, `get_attachment_download_url`, `upload_attachment_file`. CLI commands use `attachment-*` and require `--objective-id` for upload/finalize.
|
|
@@ -154,4 +155,4 @@ The `attach` and `load-context` responses already include `attachments` and `obj
|
|
|
154
155
|
- If a protocol or MCP call fails with auth/session errors, run `ovld auth repair` yourself before asking the user to log in again or proceed without Overlord updates.
|
|
155
156
|
- If you must run `ovld auth login`, always include `--organization-id <id>` — use the organization ID from the ticket prompt context to select the organization non-interactively and avoid a blocking TTY prompt.
|
|
156
157
|
|
|
157
|
-
<!-- version: 0.4.
|
|
158
|
+
<!-- version: 0.4.7 -->
|