cawdev-cli 1.0.10-beta → 1.1.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/README.md +11 -1
- package/lib/background-shells.mjs +45 -3
- package/lib/roadmap-format.mjs +31 -8
- package/lib/stage-tools.mjs +2 -0
- package/mcp/README.md +44 -30
- package/mcp/server.mjs +113 -49
- package/package.json +1 -1
- package/runner/README.md +29 -27
- package/runner/attach.mjs +19 -8
- package/runner/git-host.mjs +263 -0
- package/runner/runner.mjs +324 -117
- package/runner/split.mjs +46 -6
- package/runner/workspace-check.mjs +78 -0
package/mcp/server.mjs
CHANGED
|
@@ -375,6 +375,41 @@ const TOOLS = [
|
|
|
375
375
|
},
|
|
376
376
|
},
|
|
377
377
|
|
|
378
|
+
{
|
|
379
|
+
name: 'sprint_list',
|
|
380
|
+
description:
|
|
381
|
+
'The sprints a project\'s cards can be filed into, with how far each has got — R489. Sprints ' +
|
|
382
|
+
'are a team\'s: written `S7` inside the team and `Core team · S7` outside it. Read this before ' +
|
|
383
|
+
'filing a card into one with roadmap_create / roadmap_update `sprint`. State narrows it ' +
|
|
384
|
+
'(ACTIVE, UPCOMING, COMPLETED; comma-separated); the default is ACTIVE and UPCOMING.',
|
|
385
|
+
inputSchema: {
|
|
386
|
+
type: 'object',
|
|
387
|
+
properties: {
|
|
388
|
+
...PROJECT_ARGUMENT,
|
|
389
|
+
state: {
|
|
390
|
+
type: 'string',
|
|
391
|
+
description: 'ACTIVE, UPCOMING, COMPLETED — one or several, comma-separated. Default ACTIVE,UPCOMING.',
|
|
392
|
+
},
|
|
393
|
+
},
|
|
394
|
+
},
|
|
395
|
+
handler: async (config, args) => {
|
|
396
|
+
const slug = await resolveProject(config, args.project);
|
|
397
|
+
const state = args.state || 'ACTIVE,UPCOMING';
|
|
398
|
+
const sprints = await api(config, `/api/projects/${slug}/sprints?state=${encodeURIComponent(state)}`);
|
|
399
|
+
if (!sprints.length) return `${slug} has no sprints in ${state}.`;
|
|
400
|
+
return sprints
|
|
401
|
+
.map((sprint) => {
|
|
402
|
+
const counts = sprint.counts ?? {};
|
|
403
|
+
return (
|
|
404
|
+
`${sprint.label ?? sprint.ref}${sprint.name ? ` ${sprint.name}` : ''} — ` +
|
|
405
|
+
`${String(sprint.state).toLowerCase()} — ${counts.landed ?? 0} of ${counts.cards ?? 0} landed` +
|
|
406
|
+
(sprint.goal ? `\n goal: ${sprint.goal}` : '')
|
|
407
|
+
);
|
|
408
|
+
})
|
|
409
|
+
.join('\n');
|
|
410
|
+
},
|
|
411
|
+
},
|
|
412
|
+
|
|
378
413
|
{
|
|
379
414
|
name: 'roadmap_statuses',
|
|
380
415
|
description:
|
|
@@ -396,18 +431,17 @@ const TOOLS = [
|
|
|
396
431
|
{
|
|
397
432
|
name: 'roadmap_list',
|
|
398
433
|
description:
|
|
399
|
-
'A project\'s roadmap
|
|
400
|
-
'to survey without pulling every
|
|
434
|
+
'A project\'s roadmap cards, optionally filtered by status or by sprint. Use brief=true ' +
|
|
435
|
+
'to survey without pulling every card\'s body into context.',
|
|
401
436
|
inputSchema: {
|
|
402
437
|
type: 'object',
|
|
403
438
|
properties: {
|
|
404
439
|
...PROJECT_ARGUMENT,
|
|
405
440
|
sprint: {
|
|
406
|
-
type: 'integer',
|
|
407
|
-
minimum: 1,
|
|
441
|
+
type: ['integer', 'string'],
|
|
408
442
|
description:
|
|
409
|
-
'Only cards in this sprint,
|
|
410
|
-
'sprint for is refused naming the ones it has.',
|
|
443
|
+
'Only cards in this sprint: `S7` (or 7), "Core team · S7" when two teams have an S7, or a ' +
|
|
444
|
+
'sprint id. A sprint the project has none for is refused naming the ones it has.',
|
|
411
445
|
},
|
|
412
446
|
status: {
|
|
413
447
|
type: 'string',
|
|
@@ -439,7 +473,9 @@ const TOOLS = [
|
|
|
439
473
|
if (args.status) query.set('status', args.status);
|
|
440
474
|
// R257. The API refuses a number with no sprint behind it, naming the
|
|
441
475
|
// sprints there are; that sentence is passed through as the tool error.
|
|
442
|
-
if (args.sprint
|
|
476
|
+
if (args.sprint !== undefined && args.sprint !== '' && args.sprint !== 0) {
|
|
477
|
+
query.set('sprint', String(args.sprint));
|
|
478
|
+
}
|
|
443
479
|
if (args.assignee) query.set('assignee', args.assignee);
|
|
444
480
|
query.set('brief', String(brief));
|
|
445
481
|
|
|
@@ -454,7 +490,7 @@ const TOOLS = [
|
|
|
454
490
|
{
|
|
455
491
|
name: 'roadmap_get',
|
|
456
492
|
description:
|
|
457
|
-
'One roadmap
|
|
493
|
+
'One roadmap card in full: its body, THE PLAN somebody agreed for it, and the ' +
|
|
458
494
|
'discussion on it. Read the plan before doing any of the work — it is what was decided, ' +
|
|
459
495
|
'and it names the files. Read the comments before proposing anything: they are where an ' +
|
|
460
496
|
'objection was answered, and re-proposing what was talked out three months ago is the ' +
|
|
@@ -594,7 +630,7 @@ const TOOLS = [
|
|
|
594
630
|
{
|
|
595
631
|
name: 'roadmap_comment',
|
|
596
632
|
description:
|
|
597
|
-
'Say something about
|
|
633
|
+
'Say something about a card, beside the card rather than inside it. Use it for the ' +
|
|
598
634
|
'argument: an objection, a measurement, why an obvious approach was not taken. The body ' +
|
|
599
635
|
'is where a settled conclusion is written down — edit that when the discussion reaches ' +
|
|
600
636
|
'one. Comments cannot be deleted, by you or by anyone.',
|
|
@@ -680,12 +716,13 @@ const TOOLS = [
|
|
|
680
716
|
{
|
|
681
717
|
name: 'roadmap_create',
|
|
682
718
|
description:
|
|
683
|
-
'Create a roadmap
|
|
684
|
-
'a roadmap card and i91 on an issue, one
|
|
719
|
+
'Create a roadmap card. The platform allocates its permanent number — written R91 on ' +
|
|
720
|
+
'a roadmap card and i91 on an issue, two sequences, one per kind — and the ' +
|
|
685
721
|
'project decides where a card you file starts — the BACKLOG by default for a card an ' +
|
|
686
722
|
'agent files, waiting until a person or a sprint brings it in — unless you say ' +
|
|
687
723
|
'otherwise; a status that requires something must be given it. A card in the BACKLOG ' +
|
|
688
|
-
'holds no sprint: giving it one moves it to CONSIDERING.'
|
|
724
|
+
'holds no sprint: giving it one moves it to CONSIDERING. A plan run\'s card lands ' +
|
|
725
|
+
'where the console\'s do (R380).',
|
|
689
726
|
inputSchema: {
|
|
690
727
|
type: 'object',
|
|
691
728
|
properties: {
|
|
@@ -713,12 +750,12 @@ const TOOLS = [
|
|
|
713
750
|
reason: { type: 'string' },
|
|
714
751
|
section: { type: 'string', description: 'Which part of the roadmap, e.g. "Phase 2 — …".' },
|
|
715
752
|
sprint: {
|
|
716
|
-
type: 'integer',
|
|
717
|
-
minimum: 0,
|
|
753
|
+
type: ['integer', 'string'],
|
|
718
754
|
description:
|
|
719
|
-
'The sprint to file this card into
|
|
720
|
-
'
|
|
721
|
-
'
|
|
755
|
+
'The sprint to file this card into: `S7` (or 7) when the project\'s teams have one S7, ' +
|
|
756
|
+
'"Core team · S7" when two teams do, a sprint id, or 0 / "" to take it out of its sprint; ' +
|
|
757
|
+
'leave it out to leave it alone. A COMPLETED sprint refuses a card that is not already in it, ' +
|
|
758
|
+
'and only a person can reopen one — do not retry. Sprint_list shows what there is.',
|
|
722
759
|
},
|
|
723
760
|
related: {
|
|
724
761
|
type: 'array',
|
|
@@ -866,6 +903,12 @@ const TOOLS = [
|
|
|
866
903
|
description:
|
|
867
904
|
'How much it matters; it does not reorder the board. Leave out for MEDIUM.',
|
|
868
905
|
},
|
|
906
|
+
affectedVersions: {
|
|
907
|
+
type: 'array',
|
|
908
|
+
items: { type: 'string' },
|
|
909
|
+
description:
|
|
910
|
+
'Versions the project defined or shipped in where this issue was found — R378. Any other name is refused.',
|
|
911
|
+
},
|
|
869
912
|
},
|
|
870
913
|
required: ['title', 'severity'],
|
|
871
914
|
},
|
|
@@ -873,7 +916,7 @@ const TOOLS = [
|
|
|
873
916
|
const slug = await resolveProject(config, args.project);
|
|
874
917
|
const issue = await api(config, `/api/projects/${slug}/issues`, {
|
|
875
918
|
method: 'POST',
|
|
876
|
-
body: pick(args, ['title', 'body', 'severity', 'status', 'related', 'tags', 'assignee', 'priority']),
|
|
919
|
+
body: pick(args, ['title', 'body', 'severity', 'status', 'related', 'tags', 'assignee', 'priority', 'affectedVersions']),
|
|
877
920
|
});
|
|
878
921
|
return `Filed ${refOf(issue)} in ${slug}.\n\n${formatEntry(issue, {})}`;
|
|
879
922
|
},
|
|
@@ -922,7 +965,7 @@ const TOOLS = [
|
|
|
922
965
|
{
|
|
923
966
|
name: 'roadmap_update',
|
|
924
967
|
description:
|
|
925
|
-
'Edit
|
|
968
|
+
'Edit a card\'s title, body, section, sprint, tags, parent, related refs or the cards it ' +
|
|
926
969
|
'starts after. Use roadmap_set_status to move it.',
|
|
927
970
|
inputSchema: {
|
|
928
971
|
type: 'object',
|
|
@@ -936,12 +979,12 @@ const TOOLS = [
|
|
|
936
979
|
body: { type: 'string' },
|
|
937
980
|
section: { type: 'string' },
|
|
938
981
|
sprint: {
|
|
939
|
-
type: 'integer',
|
|
940
|
-
minimum: 0,
|
|
982
|
+
type: ['integer', 'string'],
|
|
941
983
|
description:
|
|
942
|
-
'The sprint to file this card into
|
|
943
|
-
'
|
|
944
|
-
'
|
|
984
|
+
'The sprint to file this card into: `S7` (or 7) when the project\'s teams have one S7, ' +
|
|
985
|
+
'"Core team · S7" when two teams do, a sprint id, or 0 / "" to take it out of its sprint; ' +
|
|
986
|
+
'leave it out to leave it alone. A COMPLETED sprint refuses a card that is not already in it, ' +
|
|
987
|
+
'and only a person can reopen one — do not retry. Sprint_list shows what there is.',
|
|
945
988
|
},
|
|
946
989
|
related: {
|
|
947
990
|
type: 'array',
|
|
@@ -993,6 +1036,13 @@ const TOOLS = [
|
|
|
993
1036
|
'The card this one sits under (an epic), e.g. "R300". "" takes the card out of its ' +
|
|
994
1037
|
'parent; leave out to leave it alone. Same rules as roadmap_create.',
|
|
995
1038
|
},
|
|
1039
|
+
affectedVersions: {
|
|
1040
|
+
type: 'array',
|
|
1041
|
+
items: { type: 'string' },
|
|
1042
|
+
description:
|
|
1043
|
+
'Issues only (R378): the versions the project defined or shipped in where it was found. ' +
|
|
1044
|
+
'Leave out to leave them alone; [] clears them. A roadmap card refuses a non-empty list.',
|
|
1045
|
+
},
|
|
996
1046
|
},
|
|
997
1047
|
required: ['number'],
|
|
998
1048
|
},
|
|
@@ -1003,7 +1053,7 @@ const TOOLS = [
|
|
|
1003
1053
|
// pick keeps a present 0, which is how a card leaves its sprint.
|
|
1004
1054
|
body: pick(args, [
|
|
1005
1055
|
'title', 'body', 'section', 'related', 'after', 'sprint', 'tags', 'assignee', 'priority',
|
|
1006
|
-
'due', 'estimate', 'targetVersion', 'parent',
|
|
1056
|
+
'due', 'estimate', 'targetVersion', 'parent', 'affectedVersions',
|
|
1007
1057
|
]),
|
|
1008
1058
|
});
|
|
1009
1059
|
return `Updated ${refOf(entry)}.\n\n${formatEntry(entry, {})}`;
|
|
@@ -1013,7 +1063,7 @@ const TOOLS = [
|
|
|
1013
1063
|
{
|
|
1014
1064
|
name: 'roadmap_set_status',
|
|
1015
1065
|
description:
|
|
1016
|
-
'Move
|
|
1066
|
+
'Move a card to a status. Any status may move to any other — the rules are about what ' +
|
|
1017
1067
|
'a status must carry, not a permitted path. IN_DEVELOPMENT needs a branch, MERGED the ' +
|
|
1018
1068
|
'merge — the PR, the merge commit or the sha — and SHIPPED a version. Move a card to ' +
|
|
1019
1069
|
'MERGED when its pull request lands; the branch may then be deleted. ' +
|
|
@@ -1062,7 +1112,7 @@ const TOOLS = [
|
|
|
1062
1112
|
{
|
|
1063
1113
|
name: 'roadmap_decline',
|
|
1064
1114
|
description:
|
|
1065
|
-
'Decline
|
|
1115
|
+
'Decline a card, with a reason. This is the only exit a card has — there is no delete. ' +
|
|
1066
1116
|
'The reason is the point: it stops the idea being proposed again.',
|
|
1067
1117
|
inputSchema: {
|
|
1068
1118
|
type: 'object',
|
|
@@ -1139,10 +1189,10 @@ const TOOLS = [
|
|
|
1139
1189
|
entryNumber: {
|
|
1140
1190
|
type: ['string', 'integer'],
|
|
1141
1191
|
description:
|
|
1142
|
-
'The roadmap card or issue this entry describes — R156 — by ref: R91, i91, or a '
|
|
1192
|
+
'The roadmap card or issue this changelog entry describes — R156 — by ref: R91, i91, or a '
|
|
1143
1193
|
+ 'bare number for a roadmap card (R221). Set it. Until R156 the only link was the '
|
|
1144
1194
|
+ 'ref you type into the prose, which cannot be queried, so a release could not '
|
|
1145
|
-
+ 'tell whether a card already had
|
|
1195
|
+
+ 'tell whether a card already had a changelog entry and would write a second one beside '
|
|
1146
1196
|
+ 'the first. Put the ref in the text as well; that is what a reader sees.',
|
|
1147
1197
|
},
|
|
1148
1198
|
},
|
|
@@ -1175,8 +1225,8 @@ const TOOLS = [
|
|
|
1175
1225
|
entryNumber: {
|
|
1176
1226
|
type: ['string', 'integer'],
|
|
1177
1227
|
description:
|
|
1178
|
-
'The roadmap card or issue this entry describes — R156 — by ref (R91, i91; a bare '
|
|
1179
|
-
+ 'number is a roadmap card). Set it on an older entry that has never been linked; '
|
|
1228
|
+
'The roadmap card or issue this changelog entry describes — R156 — by ref (R91, i91; a bare '
|
|
1229
|
+
+ 'number is a roadmap card). Set it on an older changelog entry that has never been linked; '
|
|
1180
1230
|
+ 'omitting it leaves whatever link is already there.',
|
|
1181
1231
|
},
|
|
1182
1232
|
},
|
|
@@ -1197,9 +1247,9 @@ const TOOLS = [
|
|
|
1197
1247
|
{
|
|
1198
1248
|
name: 'task_current',
|
|
1199
1249
|
description:
|
|
1200
|
-
'What you are working on: the roadmap
|
|
1250
|
+
'What you are working on: the roadmap card, THE PLAN agreed for it, its branch, and ' +
|
|
1201
1251
|
'everything already said and asked on this run. Call it first, and again whenever you ' +
|
|
1202
|
-
'are unsure where you are — a resumed or confused session re-orients from this alone.',
|
|
1252
|
+
'are unsure where you are — a resumed or confused agent session re-orients from this alone.',
|
|
1203
1253
|
inputSchema: { type: 'object', properties: {} },
|
|
1204
1254
|
handler: async (config) => {
|
|
1205
1255
|
const { runId, project } = await requireRun(config);
|
|
@@ -1421,20 +1471,20 @@ const TOOLS = [
|
|
|
1421
1471
|
{
|
|
1422
1472
|
name: 'propose_entry',
|
|
1423
1473
|
description:
|
|
1424
|
-
'Record something an audit found, or a card a scoping
|
|
1474
|
+
'Record something an audit found, or a card a scoping run cut, as a proposed card. ' +
|
|
1425
1475
|
'`kind` is your opinion: "issue" ' +
|
|
1426
1476
|
'(broken, unsafe, or loses data — with a severity) or "roadmap" (it should also do this, ' +
|
|
1427
1477
|
'or do it better — no severity). A PERSON decides, and may file it the other way — you ' +
|
|
1428
|
-
'are not creating
|
|
1478
|
+
'are not creating a card, you are suggesting it. Default "issue", so an audit written ' +
|
|
1429
1479
|
'before there was a choice files what it always did. Severity is "critical" (broken, ' +
|
|
1430
1480
|
'unsafe, or loses data), "medium" (it will hurt, but not today) or "minor" (worth doing, ' +
|
|
1431
|
-
'nobody is bleeding). `section` is where you think it belongs; a scoping
|
|
1481
|
+
'nobody is bleeding). `section` is where you think it belongs; a scoping run names ' +
|
|
1432
1482
|
'ONE for every card of the idea. A roadmap card MUST say what it starts after — `after`: ' +
|
|
1433
1483
|
'`[]` for a card that starts after none, `"#2"` for the second card you proposed in this ' +
|
|
1434
1484
|
'run (the number this tool answered with), `"R12"` for a card already on the roadmap. ' +
|
|
1435
1485
|
'You can only name what has already been proposed, so file in build order. Write each ' +
|
|
1436
|
-
'one as
|
|
1437
|
-
'or a scoping
|
|
1486
|
+
'one as a card would be written, and say where in the code you saw it. Only an audit ' +
|
|
1487
|
+
'or a scoping run may use this.',
|
|
1438
1488
|
inputSchema: {
|
|
1439
1489
|
type: 'object',
|
|
1440
1490
|
properties: {
|
|
@@ -1459,7 +1509,7 @@ const TOOLS = [
|
|
|
1459
1509
|
type: 'string',
|
|
1460
1510
|
description:
|
|
1461
1511
|
'The roadmap section this belongs under, in your opinion. Optional. The person ' +
|
|
1462
|
-
'accepting sees it as the default and may file it elsewhere. A scoping
|
|
1512
|
+
'accepting sees it as the default and may file it elsewhere. A scoping run ' +
|
|
1463
1513
|
'uses the same section on every card it cuts from one idea.',
|
|
1464
1514
|
},
|
|
1465
1515
|
after: {
|
|
@@ -1471,7 +1521,7 @@ const TOOLS = [
|
|
|
1471
1521
|
'card you proposed in THIS run, by the number this tool answered with; it must ' +
|
|
1472
1522
|
'already have been proposed, so propose what a card depends on before the card. ' +
|
|
1473
1523
|
'`"R12"` or `"i7"` names a card already on the roadmap. A person adds the cards ' +
|
|
1474
|
-
'in this order, and a coding
|
|
1524
|
+
'in this order, and a coding run on one waits until what it starts after has ' +
|
|
1475
1525
|
'landed.',
|
|
1476
1526
|
},
|
|
1477
1527
|
},
|
|
@@ -1551,7 +1601,7 @@ const TOOLS = [
|
|
|
1551
1601
|
name: 'ask_group',
|
|
1552
1602
|
description:
|
|
1553
1603
|
'Ask a ROUND of questions at once, and wait for all of them. Use it when you have several ' +
|
|
1554
|
-
'things to settle that belong together — they arrive in the person\'s
|
|
1604
|
+
'things to settle that belong together — they arrive in the person\'s Waiting on you page under one ' +
|
|
1555
1605
|
'title, on one form, instead of interrupting them once per question. At most 12 in a ' +
|
|
1556
1606
|
'round; ask the rest in the next one. Blocks for up to ten minutes; if the round is ' +
|
|
1557
1607
|
'unfinished by then it returns a group_id for await_group.',
|
|
@@ -1561,7 +1611,7 @@ const TOOLS = [
|
|
|
1561
1611
|
title: {
|
|
1562
1612
|
type: 'string',
|
|
1563
1613
|
description:
|
|
1564
|
-
'What this round is called in
|
|
1614
|
+
'What this round is called in Waiting on you. A CTO Interview uses "CTO Interview".',
|
|
1565
1615
|
},
|
|
1566
1616
|
intro: {
|
|
1567
1617
|
type: 'string',
|
|
@@ -1691,7 +1741,7 @@ const TOOLS = [
|
|
|
1691
1741
|
name: 'await_answer',
|
|
1692
1742
|
description:
|
|
1693
1743
|
'Resume waiting for a question ask_user handed back. Between the two you experience one ' +
|
|
1694
|
-
'natural blocking ask; the person experiences
|
|
1744
|
+
'natural blocking ask; the person experiences a Waiting on you item.',
|
|
1695
1745
|
inputSchema: {
|
|
1696
1746
|
type: 'object',
|
|
1697
1747
|
properties: { question_id: { type: 'string' } },
|
|
@@ -2051,11 +2101,23 @@ function approvalTimeoutSeconds() {
|
|
|
2051
2101
|
// The same goes for comments — nothing here removes one, and nothing there
|
|
2052
2102
|
// does either.
|
|
2053
2103
|
//
|
|
2054
|
-
// Nor is there a sprint_open,
|
|
2055
|
-
//
|
|
2056
|
-
//
|
|
2057
|
-
//
|
|
2058
|
-
//
|
|
2104
|
+
// Nor is there a sprint_open, sprint_start, sprint_complete or sprint_rename —
|
|
2105
|
+
// R257, and a team's since R474. Opening, starting, completing and renaming a
|
|
2106
|
+
// sprint are a person's acts on the page, and the API says so structurally
|
|
2107
|
+
// (the /api/teams/{id}/sprints writes take a session, not a token). An
|
|
2108
|
+
// agent's whole reach here is reading the sprints (sprint_list) and a card's
|
|
2109
|
+
// sprint, and filing a card into one with roadmap_create / roadmap_update
|
|
2110
|
+
// `sprint`.
|
|
2111
|
+
|
|
2112
|
+
/**
|
|
2113
|
+
* R489. A card's sprint as a line: `Core team · S7 Name (active)`. The label
|
|
2114
|
+
* is the server's one spelling; `S7` alone means nothing outside its team.
|
|
2115
|
+
*/
|
|
2116
|
+
function sprintLine(sprint) {
|
|
2117
|
+
const label = sprint.label ?? sprint.ref ?? `S${sprint.number}`;
|
|
2118
|
+
const state = String(sprint.state ?? '').toLowerCase();
|
|
2119
|
+
return `${label}${sprint.name ? ` ${sprint.name}` : ''}${state ? ` (${state})` : ''}`;
|
|
2120
|
+
}
|
|
2059
2121
|
|
|
2060
2122
|
function pick(source, keys) {
|
|
2061
2123
|
const out = {};
|
|
@@ -2341,11 +2403,13 @@ function formatEntry(entry, { brief, comments, plan }) {
|
|
|
2341
2403
|
if (entry.estimate) lines.push(` estimate: ${entry.estimate}`);
|
|
2342
2404
|
// R358. Printed until the card ships; `version` says where it did.
|
|
2343
2405
|
if (entry.targetVersion && !entry.version) lines.push(` target: ${entry.targetVersion}`);
|
|
2406
|
+
// R378. History, not plan: still printed once the issue has shipped.
|
|
2407
|
+
if (entry.affectedVersions?.length) lines.push(` affects: ${entry.affectedVersions.join(', ')}`);
|
|
2344
2408
|
// R257. Always printed when the card has one, so a reader's shape does not
|
|
2345
2409
|
// depend on which tool printed the card; a closed sprint says so.
|
|
2346
2410
|
if (entry.sprint) {
|
|
2347
2411
|
lines.push(
|
|
2348
|
-
` sprint: ${entry.sprint
|
|
2412
|
+
` sprint: ${sprintLine(entry.sprint)}`,
|
|
2349
2413
|
);
|
|
2350
2414
|
}
|
|
2351
2415
|
if (entry.related?.length) {
|
package/package.json
CHANGED
package/runner/README.md
CHANGED
|
@@ -158,8 +158,9 @@ served like any added project, and the rest of the console's setup walk —
|
|
|
158
158
|
rules, members, the CTO Interview — is linked at
|
|
159
159
|
`<url>/projects/<slug>/setup`.
|
|
160
160
|
|
|
161
|
-
**What takes effect when** (R294).
|
|
162
|
-
without a restart — `agentCommands`, `grantable`, `acceptsRulesFromConsole
|
|
161
|
+
**What takes effect when** (R294). Four settings a running daemon takes
|
|
162
|
+
without a restart — `agentCommands`, `grantable`, `acceptsRulesFromConsole`
|
|
163
|
+
and `autoWorkspaces`,
|
|
163
164
|
the `HOT_SWAPPABLE` list in `tools/lib/runner-config.mjs`, spelled once —
|
|
164
165
|
because the daemon reads them fresh on every use. Everything else in the file
|
|
165
166
|
(`projects` and their workspaces, `url`, `name`, the token, `browser`, the
|
|
@@ -197,7 +198,7 @@ and its copy all work exactly as they always have, and scrolling up reaches the
|
|
|
197
198
|
start of the session. The only thing pinned is the footer at the bottom, and it
|
|
198
199
|
never scrolls away: which cawdev, who you are signed in as, this machine's
|
|
199
200
|
runner, the per-project session counts against their checkouts and the machine's
|
|
200
|
-
total
|
|
201
|
+
total — plus the run you are watching and the keys that
|
|
201
202
|
work right now. Anything *stopping* a session sits above all of that, because it
|
|
202
203
|
is the only thing in there waiting on a person.
|
|
203
204
|
|
|
@@ -283,7 +284,7 @@ the shape of the decision, and the whole value of asking a person is that they
|
|
|
283
284
|
can say the thing that was not on the list — so getting there costs one key, and
|
|
284
285
|
`esc` from it comes back to the list rather than abandoning the answer. A chosen
|
|
285
286
|
option and a typed sentence resolve the same question the same way, through the
|
|
286
|
-
same endpoint the
|
|
287
|
+
same endpoint the Waiting on you page and the run page post to: one record, the same
|
|
287
288
|
`answeredBy`, and the badge clears at once.
|
|
288
289
|
|
|
289
290
|
A permission request is the same widget — R60's lengths of yes as rows, with the
|
|
@@ -469,7 +470,7 @@ this machine serves — unless the command names it with `@project`, or
|
|
|
469
470
|
advanced mode is narrowed to ONE project (`/projects`), which is a choice
|
|
470
471
|
already made rather than a guess. Never guessed from the focused window, nor
|
|
471
472
|
from a machine serving just one, since a session on the wrong project is a
|
|
472
|
-
card planned or a branch cut in the wrong repository. `/new dev` alone and `d` list the planned cards of every project,
|
|
473
|
+
card planned or a branch cut in the wrong repository. `/new dev` alone and `d` list the planned cards and the confirmed issues of every project,
|
|
473
474
|
each with its own, so they ask nothing more. A usage window over this machine's threshold is refused in a
|
|
474
475
|
sentence; `--anyway` starts it regardless, the same decision the console's
|
|
475
476
|
panel asks for. All four commands need you signed in (`/login`), and they work
|
|
@@ -575,7 +576,12 @@ serves as a small board — Considering, Planned and In development side by
|
|
|
575
576
|
side, one line per card, `●` on a card with a session running now; Merged,
|
|
576
577
|
Shipped and Declined — work that is over, and only grows — are counts beside
|
|
577
578
|
the project's name (`1 merged · 2 shipped`), as the console's board folds its
|
|
578
|
-
own last columns.
|
|
579
|
+
own last columns. The Backlog is a count there too, first (`3 backlog`): since
|
|
580
|
+
R371 it is where a new issue lands, and it would be the first column to go. **Issues are on it too** (R85's other board, read from
|
|
581
|
+
`/issues` beside `/roadmap`): a NEW issue sits in Considering and a CONFIRMED
|
|
582
|
+
one in Planned — their counterparts — first in the column and most severe first,
|
|
583
|
+
so a `+N more` never hides a critical defect, with the ref (`i12`) in the colour
|
|
584
|
+
of its severity. As tall as the fullest column, never more than a
|
|
579
585
|
third of the screen, and gone before the windows are on a short terminal.
|
|
580
586
|
When the room runs out it is SAID: a column ends in `+N more`, empty columns
|
|
581
587
|
go first when the width does, then whole columns (Considering, then Planned —
|
|
@@ -689,7 +695,7 @@ it ends.
|
|
|
689
695
|
A bare path means one workspace, which is what every config meant before this
|
|
690
696
|
existed — nothing changes for a machine that serves one checkout per project.
|
|
691
697
|
|
|
692
|
-
How many coding runs go at once is
|
|
698
|
+
How many coding runs go at once is the number of workspaces. A run that
|
|
693
699
|
waits now says **"no free workspace in cawdev (2 here, all busy)"** instead of
|
|
694
700
|
"that project already has a run here", which was a proxy for it.
|
|
695
701
|
|
|
@@ -712,18 +718,16 @@ says about knowing it.
|
|
|
712
718
|
|
|
713
719
|
**This number caps coding and nothing else** (R70). `ASK`, `ROADMAP`, `AUDIT`
|
|
714
720
|
and `SCOPE` runs take no workspace, so a project whose checkouts are all busy
|
|
715
|
-
still starts a question,
|
|
716
|
-
|
|
721
|
+
still starts a question, a card-writing run, an audit and a scoping
|
|
722
|
+
run at once. `INTERVIEW`, `MERGE`
|
|
717
723
|
and `RELEASE` do take one — each commits something — and count against this
|
|
718
724
|
number; what each may *write* in it is far narrower than a coding run's and is
|
|
719
725
|
decided by the profile, not by the prompt: an interview writes `docs/brief/`,
|
|
720
726
|
a merge writes the files git put conflict markers in, and a release (R187)
|
|
721
727
|
writes exactly the version files the platform names on the claim, with `git`,
|
|
722
728
|
`gh pr create`, `gh pr view` and `node` for the exports — no build tools, no
|
|
723
|
-
permission prompt, no `gh pr merge`. What holds
|
|
724
|
-
is
|
|
725
|
-
(every profile counts)"** is the other thing a waiting run can say, and the log
|
|
726
|
-
always says which of the two it was.
|
|
729
|
+
permission prompt, no `gh pr merge`. What holds a coding run back
|
|
730
|
+
is a free workspace, and the log says so (R109 removed `maxSessions`).
|
|
727
731
|
|
|
728
732
|
**One thing this gate cannot express, and does not try to.** It serialises on a
|
|
729
733
|
checkout, so two runs on *the same branch* in two checkouts are, to this daemon,
|
|
@@ -792,8 +796,8 @@ whether the browser is allowed. Those five answer nearly every "why did that
|
|
|
792
796
|
not happen", and they used to be spread across a config file and a shrug.
|
|
793
797
|
|
|
794
798
|
Attaching adds a second bar row: the URL, each project as **coding sessions over
|
|
795
|
-
checkouts** (`cawdev 1/2`), and the machine's total
|
|
796
|
-
|
|
799
|
+
checkouts** (`cawdev 1/2`), and the machine's total.
|
|
800
|
+
The gate, on screen, counting what it actually bounds — a question is in the
|
|
797
801
|
total on the right and not in any project's figure, because it took no checkout.
|
|
798
802
|
A run that is waiting is explained by the bar
|
|
799
803
|
above it rather than by reading the source. A narrow terminal drops projects
|
|
@@ -844,11 +848,11 @@ than twenty-six. A machine that wants it unattended puts that string in its own
|
|
|
844
848
|
### What a person can ask of a checkout
|
|
845
849
|
|
|
846
850
|
R57. The daemon polls `workspace-requests/claim` every few seconds and does one
|
|
847
|
-
of **
|
|
851
|
+
of **eleven** things to a checkout it serves. It said six for two entries, and
|
|
848
852
|
`HANDOFF` had been missing from the list since R148 — a table that quietly does
|
|
849
853
|
not describe one of the things a person can ask for is worse than no table. So
|
|
850
854
|
count the rows below against that number before you trust either. `CUT_BRANCH`
|
|
851
|
-
is the ninth, R260's; `OPEN_PR` the tenth, R261's.
|
|
855
|
+
is the ninth, R260's; `OPEN_PR` the tenth, R261's, `CREATE_BRANCH` the eleventh, R379's.
|
|
852
856
|
|
|
853
857
|
| | |
|
|
854
858
|
|---|---|
|
|
@@ -862,6 +866,7 @@ is the ninth, R260's; `OPEN_PR` the tenth, R261's.
|
|
|
862
866
|
| `TAG` | R156's release: `git ls-remote --tags origin refs/tags/<version>` for the version in `message`. **The remote, not this clone** — a local tag nobody pushed is exactly the state a release is trying to rule out, and `git tag --list` cannot tell the two apart. A read; it fetches nothing and moves no ref. The sha is reported on the result's first line, the way `MERGE` reports its URL, because the platform will not confirm a release on `ok: true` alone. **A tag that is not there yet is `ok: false` with a sentence, and that is a normal answer** — the release procedure pushes the tag last, so the first check correctly finds nothing. Addressed to a **release** rather than to a directory alone, which is why it is asked for from the roadmap board and refused on this channel. |
|
|
863
867
|
| `CUT_BRANCH` | R260's sprint branch: `git push origin origin/<default>:refs/heads/<name>` for the branch in `message`, after a fetch — so the sprint's cards are cut from what everyone else has, not from whatever this checkout was left on. The second kind after `MERGE` whose effect is **not** in the checkout: the push names two refs and this clone's HEAD is not one of them, so any live checkout of the project will do and nothing local moves. **A branch already on origin is `ok: true`** ("already on origin at <sha>") — the sprint wanted one to exist, and one does. The sha is reported on the result's first line, the way `TAG` reports its. Addressed to a **sprint** and asked for from the sprint page by an OWNER, for `MERGE`'s reason — it reaches the remote — and refused on this channel. A `MERGE` of a card's pull request into a sprint branch still runs `--squash --delete-branch`: the card's branch goes, the sprint's stays. Landing the sprint branch itself is `OPEN_PR` then `MERGE` below. |
|
|
864
868
|
| `OPEN_PR` | R261's sprint merge, first half: `gh pr create --head <branch> --base <base> --title <title>` for the branch in `message` — the sprint's — to `baseBranch ?? defaultBranch`, titled as the claim's `title` (*S1 Notifications*). **An existing pull request is `ok: true` with its URL** — the same `gh pr view` lookup the `auto_pr` rule makes, and the reason the platform can ask this every time: it holds no git-host credential and cannot know whether one exists. The URL is reported on the result's first line, and the platform queues the `MERGE` to this same checkout when it reads one. Nothing is pushed: the branch is on origin already, cut there by `CUT_BRANCH` and merged into by every card. Addressed to a **work item** — the sprint's — and asked for by Merge on the development board or *Merge sprint* on the sprint page, by an OWNER; refused on this channel for `MERGE`'s reason. |
|
|
869
|
+
| `CREATE_BRANCH` | R379's New branch from the console: pushes `origin/<fromBranch>` to `refs/heads/<name>` for the name in `message`. Unlike `CUT_BRANCH` a name already on origin is a **refusal**, because the person asked for a new branch and the existing one may be somebody else's work. Touches no working tree; a daemon that knows the kind says `createBranch: true` in its hello. |
|
|
865
870
|
|
|
866
871
|
**A `MERGE` that fails also says which KIND of failure it was** — R155, in one
|
|
867
872
|
of five words beside `gh`'s own unchanged text:
|
|
@@ -1252,12 +1257,9 @@ rest queue.
|
|
|
1252
1257
|
|
|
1253
1258
|
**Questions are not serialised.** An `ASK` run prepares nothing and writes
|
|
1254
1259
|
nothing, so it runs alongside whatever else is going on — you can ask about a
|
|
1255
|
-
project while an agent is working in it.
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
this daemon has claimed, not children it has spawned: a claim takes a second or
|
|
1259
|
-
two to become a process, and counting processes let one pass of the loop claim
|
|
1260
|
-
the whole queue.
|
|
1260
|
+
project while an agent is working in it. Nothing bounds a question, a roadmap session or an audit but the platform's
|
|
1261
|
+
own queue: `maxSessions` was removed in R109, and the workspace gate above is
|
|
1262
|
+
the only one.
|
|
1261
1263
|
|
|
1262
1264
|
## If the daemon dies mid-run
|
|
1263
1265
|
|
|
@@ -1386,7 +1388,7 @@ permission stops forever.
|
|
|
1386
1388
|
|
|
1387
1389
|
- `--permission-mode acceptEdits` lets it write files.
|
|
1388
1390
|
- `--allowedTools mcp__cawdev__…` lets it use the cawdev tools. **Without this
|
|
1389
|
-
the entire loop is unreachable** — no reading the task, no moving the
|
|
1391
|
+
the entire loop is unreachable** — no reading the task, no moving the card,
|
|
1390
1392
|
no reporting, no asking. A real session found this by being denied
|
|
1391
1393
|
`task_current` and stopping rather than guessing, which was the right call
|
|
1392
1394
|
and a good sign for the prompt.
|
|
@@ -1412,7 +1414,7 @@ never asks you anything — an unlisted tool is refused, not raised (i138).
|
|
|
1412
1414
|
|
|
1413
1415
|
**Anything else stops and asks you** (R51). Rather than being denied in
|
|
1414
1416
|
silence, a session that needs `mvn` or `npm` posts a request that appears in
|
|
1415
|
-
your
|
|
1417
|
+
your Waiting on you page and on the run, and waits for an answer. Allow it once, or allow it
|
|
1416
1418
|
always here — which writes a rule on the project that every runner serving it
|
|
1417
1419
|
inherits.
|
|
1418
1420
|
|
|
@@ -1543,8 +1545,8 @@ after `--mcp-config` fails with `ENAMETOOLONG`, which names neither the flag nor
|
|
|
1543
1545
|
the prompt.
|
|
1544
1546
|
|
|
1545
1547
|
The prompt deliberately teaches the **method**, not the task. The task is in the
|
|
1546
|
-
roadmap
|
|
1547
|
-
in the prompt too would be a second copy that can disagree with the
|
|
1548
|
+
roadmap card, which the agent reads for itself with `task_current` — putting it
|
|
1549
|
+
in the prompt too would be a second copy that can disagree with the card.
|
|
1548
1550
|
|
|
1549
1551
|
### Codex CLI
|
|
1550
1552
|
|
package/runner/attach.mjs
CHANGED
|
@@ -61,7 +61,7 @@ import { clip, keyList, padVisible, painter, stripAnsi, visibleWidth, wrap } fro
|
|
|
61
61
|
import { restartNeededFor } from '../lib/runner-config.mjs';
|
|
62
62
|
import {
|
|
63
63
|
BOARD_COLUMNS, MIN_ZONE_HEIGHT, PROMPT_ROWS, SplitScreen, readerFrame, tabsLine, visibleSessions, actionsFor, composeFrame,
|
|
64
|
-
carriesOnHere, builtNothing, cardLines, cardTitleFor, commandWords, wordsText, subtitleFor, agentModelFor, wordWrap, isAutoCheckout, isPullRequest, parseNew, roadmapStrip, sessionTag, splitLayout, titleFor, toneFor,
|
|
64
|
+
carriesOnHere, builtNothing, cardLines, columnOf, cardTitleFor, commandWords, wordsText, subtitleFor, agentModelFor, wordWrap, isAutoCheckout, isPullRequest, parseNew, roadmapStrip, sessionTag, splitLayout, titleFor, toneFor,
|
|
65
65
|
windowLines,
|
|
66
66
|
} from './split.mjs';
|
|
67
67
|
import { KNOWN_AGENTS, agentOfCommand } from '../lib/agents.mjs';
|
|
@@ -3413,9 +3413,10 @@ export class Attached {
|
|
|
3413
3413
|
}
|
|
3414
3414
|
|
|
3415
3415
|
/**
|
|
3416
|
-
* Every card of every project this machine serves, whatever its
|
|
3417
|
-
* the strip draws them all, and `d` offers the PLANNED
|
|
3418
|
-
*
|
|
3416
|
+
* Every card AND ISSUE of every project this machine serves, whatever its
|
|
3417
|
+
* status — the strip draws them all, and `d` offers the PLANNED cards and
|
|
3418
|
+
* the CONFIRMED issues. Two requests per project, the two boards' own
|
|
3419
|
+
* (`brief`, so no bodies). Needs a person's
|
|
3419
3420
|
* session, like everything read off the roadmap here.
|
|
3420
3421
|
*
|
|
3421
3422
|
* A project that cannot be read keeps ITS last cards and no other's: this
|
|
@@ -3435,6 +3436,15 @@ export class Attached {
|
|
|
3435
3436
|
for (const card of Array.isArray(cards) ? cards : []) {
|
|
3436
3437
|
all.push({ ref: card.ref, title: card.title, slug, status: card.status, live: Boolean(card.activeRun) });
|
|
3437
3438
|
}
|
|
3439
|
+
// The issues are another list on the platform (R85), and were never
|
|
3440
|
+
// read here: the strip drew a project's ideas and not one of its bugs.
|
|
3441
|
+
const issues = await this.session.request(`/api/projects/${slug}/issues?brief=true`);
|
|
3442
|
+
for (const issue of Array.isArray(issues) ? issues : []) {
|
|
3443
|
+
all.push({
|
|
3444
|
+
ref: issue.ref, title: issue.title, slug, status: issue.status,
|
|
3445
|
+
live: Boolean(issue.activeRun), severity: issue.severity ?? 'MINOR',
|
|
3446
|
+
});
|
|
3447
|
+
}
|
|
3438
3448
|
read.push(slug);
|
|
3439
3449
|
this.gone.delete(slug);
|
|
3440
3450
|
} catch (failure) {
|
|
@@ -3447,7 +3457,8 @@ export class Attached {
|
|
|
3447
3457
|
}
|
|
3448
3458
|
}
|
|
3449
3459
|
this.cards = all;
|
|
3450
|
-
|
|
3460
|
+
// Ready to be developed: a planned card, or a confirmed issue.
|
|
3461
|
+
this.planned = all.filter((card) => columnOf(card.status) === 'PLANNED');
|
|
3451
3462
|
this.dirty = true;
|
|
3452
3463
|
if (!hold) this.sayGone();
|
|
3453
3464
|
return { signedIn: true, read, failed };
|
|
@@ -3482,7 +3493,7 @@ export class Attached {
|
|
|
3482
3493
|
const counts = read.filter((slug) => shown.has(slug)).map((slug) => {
|
|
3483
3494
|
const mine = this.cards.filter((card) => card.slug === slug);
|
|
3484
3495
|
const tally = BOARD_COLUMNS
|
|
3485
|
-
.map(([status, label]) => `${mine.filter((card) => card.status === status).length} ${label.toLowerCase()}`)
|
|
3496
|
+
.map(([status, label]) => `${mine.filter((card) => columnOf(card.status) === status).length} ${label.toLowerCase()}`)
|
|
3486
3497
|
.join(' · ');
|
|
3487
3498
|
return `${slug}: ${tally}`;
|
|
3488
3499
|
});
|
|
@@ -3498,8 +3509,8 @@ export class Attached {
|
|
|
3498
3509
|
}
|
|
3499
3510
|
|
|
3500
3511
|
/**
|
|
3501
|
-
* `d`, or `/new dev` with nothing after it: the planned cards
|
|
3502
|
-
* the one picked is developed — `/new dev R3 @slug`, on this machine.
|
|
3512
|
+
* `d`, or `/new dev` with nothing after it: the planned cards — and the
|
|
3513
|
+
* confirmed issues — as a list, and the one picked is developed — `/new dev R3 @slug`, on this machine.
|
|
3503
3514
|
*/
|
|
3504
3515
|
async pickPlanned(slug = null) {
|
|
3505
3516
|
if (!this.requireSignIn('start a session')) return;
|