@gh-symphony/cli 0.2.5 → 0.4.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 +2 -2
- package/dist/{chunk-3SKN5L3I.js → chunk-6OPRRC2J.js} +36 -7
- package/dist/{chunk-NRABQNAX.js → chunk-B6G3KGBB.js} +212 -17
- package/dist/{chunk-DLZ2XHWY.js → chunk-QOX5UGUE.js} +1 -1
- package/dist/{chunk-5U36B7FC.js → chunk-TTVGBHZI.js} +8 -4
- package/dist/{chunk-FAU72YC2.js → chunk-Z7CDL3T2.js} +1 -1
- package/dist/{chunk-DTPIJO6S.js → chunk-ZPS4CQZJ.js} +524 -63
- package/dist/{doctor-TQR54KNZ.js → doctor-CCUTNEYN.js} +5 -5
- package/dist/index.js +6 -6
- package/dist/{repo-Y6EF2DZP.js → repo-C2APQR2P.js} +21 -4
- package/dist/{setup-T2QENR26.js → setup-JINI7HBM.js} +18 -6
- package/dist/{upgrade-7452LZXX.js → upgrade-EBD4LX5W.js} +2 -2
- package/dist/{version-D3FB3PXO.js → version-6Z354HHH.js} +1 -1
- package/dist/worker-entry.js +5 -5
- package/dist/{workflow-AV676KAP.js → workflow-BOZ25AJ2.js} +5 -5
- package/package.json +3 -3
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
formatClaudePreflightText,
|
|
17
17
|
resolveClaudeCommandBinary,
|
|
18
18
|
runClaudePreflight
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-6OPRRC2J.js";
|
|
20
20
|
|
|
21
21
|
// src/mapping/smart-defaults.ts
|
|
22
22
|
var ROLE_PATTERNS = [
|
|
@@ -45,10 +45,9 @@ function inferStateRole(columnName) {
|
|
|
45
45
|
function inferAllStateRoles(columnNames) {
|
|
46
46
|
return columnNames.map(inferStateRole);
|
|
47
47
|
}
|
|
48
|
-
function toWorkflowLifecycleConfig(stateFieldName, mappings) {
|
|
48
|
+
function toWorkflowLifecycleConfig(stateFieldName, mappings, options = {}) {
|
|
49
49
|
const activeStates = [];
|
|
50
50
|
const terminalStates = [];
|
|
51
|
-
const blockerCheckStates = [];
|
|
52
51
|
for (const [columnName, mapping] of Object.entries(mappings)) {
|
|
53
52
|
switch (mapping.role) {
|
|
54
53
|
case "active":
|
|
@@ -61,14 +60,13 @@ function toWorkflowLifecycleConfig(stateFieldName, mappings) {
|
|
|
61
60
|
break;
|
|
62
61
|
}
|
|
63
62
|
}
|
|
64
|
-
|
|
65
|
-
blockerCheckStates.push(activeStates[0]);
|
|
66
|
-
}
|
|
63
|
+
const blockerCheckStates = options.blockerCheckStates ?? [];
|
|
67
64
|
return {
|
|
68
65
|
stateFieldName,
|
|
69
66
|
activeStates,
|
|
70
67
|
terminalStates,
|
|
71
|
-
blockerCheckStates
|
|
68
|
+
blockerCheckStates,
|
|
69
|
+
planningStates: options.planningStates ?? blockerCheckStates
|
|
72
70
|
};
|
|
73
71
|
}
|
|
74
72
|
function validateStateMapping(mappings) {
|
|
@@ -100,7 +98,7 @@ import * as p from "@clack/prompts";
|
|
|
100
98
|
import { spawnSync } from "child_process";
|
|
101
99
|
import { createHash } from "crypto";
|
|
102
100
|
import { chmod, mkdir as mkdir3, readFile as readFile3, rename as rename2, writeFile as writeFile3 } from "fs/promises";
|
|
103
|
-
import {
|
|
101
|
+
import { dirname as dirname3, join as join3, relative, resolve } from "path";
|
|
104
102
|
|
|
105
103
|
// src/prompts/runtime-claude-constraints.ts
|
|
106
104
|
var CLAUDE_RUNTIME_CONSTRAINTS_SECTION = `## Runtime Constraints
|
|
@@ -361,12 +359,18 @@ function buildFrontMatter(input) {
|
|
|
361
359
|
lines.push(` - ${state}`);
|
|
362
360
|
}
|
|
363
361
|
}
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
362
|
+
lines.push(
|
|
363
|
+
...buildStringListFrontMatter(
|
|
364
|
+
"blocker_check_states",
|
|
365
|
+
input.lifecycle.blockerCheckStates
|
|
366
|
+
)
|
|
367
|
+
);
|
|
368
|
+
lines.push(
|
|
369
|
+
...buildStringListFrontMatter(
|
|
370
|
+
"planning_states",
|
|
371
|
+
input.lifecycle.planningStates
|
|
372
|
+
)
|
|
373
|
+
);
|
|
370
374
|
lines.push("polling:");
|
|
371
375
|
lines.push(` interval_ms: ${input.pollIntervalMs ?? 3e4}`);
|
|
372
376
|
lines.push("workspace:");
|
|
@@ -380,6 +384,12 @@ function buildFrontMatter(input) {
|
|
|
380
384
|
lines.push(...buildRuntimeFrontMatter(input.runtime));
|
|
381
385
|
return lines.join("\n") + "\n";
|
|
382
386
|
}
|
|
387
|
+
function buildStringListFrontMatter(key, values) {
|
|
388
|
+
if (values.length === 0) {
|
|
389
|
+
return [` ${key}: []`];
|
|
390
|
+
}
|
|
391
|
+
return [` ${key}:`, ...values.map((value) => ` - ${value}`)];
|
|
392
|
+
}
|
|
383
393
|
function buildPriorityFrontMatter(input) {
|
|
384
394
|
const lines = [];
|
|
385
395
|
if (!input.priority) {
|
|
@@ -984,7 +994,8 @@ function generateReferenceWorkflow(input) {
|
|
|
984
994
|
const terminalColumns = input.statusColumns.filter(
|
|
985
995
|
(c) => c.role === "terminal"
|
|
986
996
|
);
|
|
987
|
-
const
|
|
997
|
+
const blockerCheckStates = input.lifecycle?.blockerCheckStates ?? [];
|
|
998
|
+
const planningStates = input.lifecycle?.planningStates ?? blockerCheckStates;
|
|
988
999
|
if (activeColumns.length > 0) {
|
|
989
1000
|
lines.push(" active_states:");
|
|
990
1001
|
for (const col of activeColumns) {
|
|
@@ -1001,12 +1012,10 @@ function generateReferenceWorkflow(input) {
|
|
|
1001
1012
|
} else {
|
|
1002
1013
|
lines.push(" terminal_states: [{terminal column names}]");
|
|
1003
1014
|
}
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
lines.push(" blocker_check_states: [{first active state}]");
|
|
1009
|
-
}
|
|
1015
|
+
lines.push(
|
|
1016
|
+
...buildReferenceStringList("blocker_check_states", blockerCheckStates)
|
|
1017
|
+
);
|
|
1018
|
+
lines.push(...buildReferenceStringList("planning_states", planningStates));
|
|
1010
1019
|
lines.push("");
|
|
1011
1020
|
lines.push("# Linear tracker example:");
|
|
1012
1021
|
lines.push("# tracker:");
|
|
@@ -1021,7 +1030,9 @@ function generateReferenceWorkflow(input) {
|
|
|
1021
1030
|
lines.push("# - Done");
|
|
1022
1031
|
lines.push("# - Canceled");
|
|
1023
1032
|
lines.push("# - Duplicate");
|
|
1024
|
-
lines.push(
|
|
1033
|
+
lines.push(
|
|
1034
|
+
"# Linear uses repository-local polling; gh-symphony does not provide"
|
|
1035
|
+
);
|
|
1025
1036
|
lines.push("# a Linear webhook setup command.");
|
|
1026
1037
|
lines.push("");
|
|
1027
1038
|
lines.push("polling:");
|
|
@@ -1282,6 +1293,12 @@ function generateReferenceWorkflow(input) {
|
|
|
1282
1293
|
lines.push("");
|
|
1283
1294
|
return lines.join("\n");
|
|
1284
1295
|
}
|
|
1296
|
+
function buildReferenceStringList(key, values) {
|
|
1297
|
+
if (values.length === 0) {
|
|
1298
|
+
return [` ${key}: []`];
|
|
1299
|
+
}
|
|
1300
|
+
return [` ${key}:`, ...values.map((value) => ` - ${value}`)];
|
|
1301
|
+
}
|
|
1285
1302
|
function buildReferencePriorityLines(priority) {
|
|
1286
1303
|
const lines = [];
|
|
1287
1304
|
if (priority?.source === "project-field" || priority?.source === "labels") {
|
|
@@ -1349,7 +1366,7 @@ function resolveRoleAction(role) {
|
|
|
1349
1366
|
|
|
1350
1367
|
// src/skills/skill-writer.ts
|
|
1351
1368
|
import { mkdir as mkdir2, readFile as readFile2, rename, writeFile as writeFile2 } from "fs/promises";
|
|
1352
|
-
import { join as join2 } from "path";
|
|
1369
|
+
import { dirname as dirname2, join as join2 } from "path";
|
|
1353
1370
|
function normalizeRuntimeForSkills(runtime) {
|
|
1354
1371
|
if (isClaudeRuntime(runtime)) {
|
|
1355
1372
|
return "claude-code";
|
|
@@ -1376,10 +1393,12 @@ function buildSkillFilePlans(repoRoot, runtime, templates, context) {
|
|
|
1376
1393
|
}
|
|
1377
1394
|
return {
|
|
1378
1395
|
skillsDir,
|
|
1379
|
-
files: templates.
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1396
|
+
files: templates.flatMap(
|
|
1397
|
+
(template) => template.files.map((file) => ({
|
|
1398
|
+
path: join2(skillsDir, template.name, file.relativePath),
|
|
1399
|
+
content: file.generate(context)
|
|
1400
|
+
}))
|
|
1401
|
+
)
|
|
1383
1402
|
};
|
|
1384
1403
|
}
|
|
1385
1404
|
|
|
@@ -1419,8 +1438,9 @@ function generateGhSymphonySkill(ctx) {
|
|
|
1419
1438
|
`- \`${ctx.contextYamlPath}\` must exist (contains GitHub Project metadata)`
|
|
1420
1439
|
);
|
|
1421
1440
|
lines.push(
|
|
1422
|
-
`- \`${ctx.referenceWorkflowPath}\`
|
|
1441
|
+
`- \`${ctx.referenceWorkflowPath}\` may exist for compatibility with older generated ecosystems`
|
|
1423
1442
|
);
|
|
1443
|
+
lines.push("- `references/README.md` must exist beside this skill");
|
|
1424
1444
|
lines.push("- `gh` CLI must be authenticated");
|
|
1425
1445
|
lines.push("");
|
|
1426
1446
|
lines.push("## Repository Validation Guidance");
|
|
@@ -1445,49 +1465,70 @@ function generateGhSymphonySkill(ctx) {
|
|
|
1445
1465
|
lines.push("## Design Mode");
|
|
1446
1466
|
lines.push("");
|
|
1447
1467
|
lines.push(
|
|
1448
|
-
|
|
1468
|
+
"1. Read `WORKFLOW.md` if it exists and extract repository conventions:"
|
|
1449
1469
|
);
|
|
1470
|
+
lines.push(" - test / lint / build commands from the prompt body");
|
|
1471
|
+
lines.push(" - lifecycle states from front matter");
|
|
1450
1472
|
lines.push(
|
|
1451
|
-
|
|
1473
|
+
"2. Read `references/README.md` for the available reference files."
|
|
1452
1474
|
);
|
|
1453
|
-
lines.push(
|
|
1475
|
+
lines.push('3. Ask the user: "What should this orchestration accomplish?"');
|
|
1476
|
+
lines.push(" - implement (default) \u2014 write features / fix bugs");
|
|
1477
|
+
lines.push(" - review \u2014 review PRs and leave comments");
|
|
1478
|
+
lines.push(" - maintain \u2014 dependency bumps, chores, hygiene");
|
|
1479
|
+
lines.push(" - custom \u2014 describe in their own words");
|
|
1480
|
+
lines.push(
|
|
1481
|
+
"4. Pick the matching `references/workflow-posture-*.md` file(s); compose multiple files when the intent spans categories."
|
|
1482
|
+
);
|
|
1483
|
+
lines.push("5. Ask these setup questions:");
|
|
1454
1484
|
lines.push(" - Which status columns should be **active** (agent works)?");
|
|
1455
1485
|
lines.push(" - Which should be **wait** (agent pauses for human)?");
|
|
1456
1486
|
lines.push(" - Which should be **terminal** (agent stops)?");
|
|
1457
1487
|
lines.push(" - What runtime is being used? (codex / claude-code / custom)");
|
|
1458
1488
|
lines.push(" - Any custom hooks needed? (after_create, before_run, etc.)");
|
|
1489
|
+
lines.push("6. Generate WORKFLOW.md:");
|
|
1490
|
+
lines.push(" - front matter from `references/workflow-schema.md`");
|
|
1459
1491
|
lines.push(
|
|
1460
|
-
"
|
|
1492
|
+
" - prompt body from the selected posture file(s), adapted to actual repository commands"
|
|
1461
1493
|
);
|
|
1462
|
-
lines.push("
|
|
1494
|
+
lines.push("7. Show a diff or preview and confirm with the user.");
|
|
1495
|
+
lines.push("8. Validate via `gh-symphony workflow validate`.");
|
|
1463
1496
|
lines.push("");
|
|
1464
1497
|
lines.push("## Refine Mode");
|
|
1465
1498
|
lines.push("");
|
|
1466
1499
|
lines.push("1. Read the current `WORKFLOW.md`");
|
|
1467
|
-
lines.push(
|
|
1468
|
-
|
|
1500
|
+
lines.push(
|
|
1501
|
+
"2. Read `references/README.md` and select the relevant posture file(s)"
|
|
1502
|
+
);
|
|
1503
|
+
lines.push(
|
|
1504
|
+
"3. Compare the current prompt body against the selected posture references"
|
|
1505
|
+
);
|
|
1506
|
+
lines.push("4. Identify missing or incomplete sections:");
|
|
1469
1507
|
lines.push(" - Status Map with role annotations");
|
|
1470
1508
|
lines.push(" - Default Posture / Agent Instructions");
|
|
1471
1509
|
lines.push(" - Guardrails section");
|
|
1472
1510
|
lines.push(" - Workpad Template");
|
|
1473
1511
|
lines.push(" - Step 0 routing logic");
|
|
1474
|
-
lines.push("
|
|
1475
|
-
lines.push("
|
|
1512
|
+
lines.push("5. Propose improvements and apply with user confirmation");
|
|
1513
|
+
lines.push("6. Validate the refined file");
|
|
1476
1514
|
lines.push("");
|
|
1477
1515
|
lines.push("## Validate Mode");
|
|
1478
1516
|
lines.push("");
|
|
1479
1517
|
lines.push("Check the WORKFLOW.md for:");
|
|
1480
1518
|
lines.push("- Front matter is valid YAML");
|
|
1481
1519
|
lines.push(
|
|
1482
|
-
"- Required fields are present (see
|
|
1520
|
+
"- Required fields are present (see `references/workflow-schema.md`)"
|
|
1483
1521
|
);
|
|
1484
1522
|
lines.push(
|
|
1485
|
-
"- Template variables use only supported names (see
|
|
1523
|
+
"- Template variables use only supported names (see `references/workflow-schema.md`)"
|
|
1486
1524
|
);
|
|
1487
1525
|
lines.push("- Status Map matches the lifecycle configuration");
|
|
1488
1526
|
lines.push(
|
|
1489
1527
|
"- No unsupported double-brace variable patterns (only the 8 listed below are valid)"
|
|
1490
1528
|
);
|
|
1529
|
+
lines.push(
|
|
1530
|
+
"- Prompt body posture is consistent with the selected `references/workflow-posture-*.md` file(s)"
|
|
1531
|
+
);
|
|
1491
1532
|
lines.push("");
|
|
1492
1533
|
lines.push("## Supported Front Matter Fields");
|
|
1493
1534
|
lines.push("");
|
|
@@ -1884,22 +1925,319 @@ function generateLandSkill(_ctx) {
|
|
|
1884
1925
|
});
|
|
1885
1926
|
}
|
|
1886
1927
|
|
|
1928
|
+
// src/skills/templates/gh-symphony-references/readme.ts
|
|
1929
|
+
function generateGhSymphonyReferencesReadme(_ctx) {
|
|
1930
|
+
return [
|
|
1931
|
+
"# /gh-symphony references",
|
|
1932
|
+
"",
|
|
1933
|
+
"The /gh-symphony skill consults these files when designing or refining",
|
|
1934
|
+
"WORKFLOW.md.",
|
|
1935
|
+
"",
|
|
1936
|
+
"## Schema",
|
|
1937
|
+
"",
|
|
1938
|
+
"| File | What it is |",
|
|
1939
|
+
"| ---- | ---------- |",
|
|
1940
|
+
"| `workflow-schema.md` | All supported front matter fields and their types. |",
|
|
1941
|
+
"",
|
|
1942
|
+
"## Workflow prompt body postures",
|
|
1943
|
+
"",
|
|
1944
|
+
"When the user describes what the orchestration should do, pick the matching",
|
|
1945
|
+
"posture file(s) and use its prompt-body sections as the seed. Postures can be",
|
|
1946
|
+
"combined when the user's intent spans multiple categories.",
|
|
1947
|
+
"",
|
|
1948
|
+
"| File | Use when the user wants... |",
|
|
1949
|
+
"| ---- | -------------------------- |",
|
|
1950
|
+
"| `workflow-posture-implement.md` | Coding agent writes features / bug fixes (default). |",
|
|
1951
|
+
"| `workflow-posture-review.md` | Agent reviews PRs and leaves comments. No code writes. |",
|
|
1952
|
+
"| `workflow-posture-maintain.md` | Minimal-change maintenance: deps, lint sweeps, hygiene. |",
|
|
1953
|
+
"",
|
|
1954
|
+
"## Adding your own reference",
|
|
1955
|
+
"",
|
|
1956
|
+
"Drop a markdown file here with a descriptive name. The skill discovers files",
|
|
1957
|
+
"on each invocation; no code changes needed."
|
|
1958
|
+
].join("\n");
|
|
1959
|
+
}
|
|
1960
|
+
|
|
1961
|
+
// src/skills/templates/gh-symphony-references/workflow-schema.ts
|
|
1962
|
+
function generateWorkflowSchemaReference(ctx) {
|
|
1963
|
+
const reference = generateReferenceWorkflow({
|
|
1964
|
+
runtime: ctx.runtime,
|
|
1965
|
+
statusColumns: ctx.statusColumns.map((column) => ({
|
|
1966
|
+
name: column.name,
|
|
1967
|
+
role: column.role
|
|
1968
|
+
})),
|
|
1969
|
+
projectId: ctx.projectId,
|
|
1970
|
+
priority: null,
|
|
1971
|
+
detectedEnvironment: ctx.detectedEnvironment
|
|
1972
|
+
});
|
|
1973
|
+
return [
|
|
1974
|
+
reference,
|
|
1975
|
+
"",
|
|
1976
|
+
"## Supported Template Variables",
|
|
1977
|
+
"",
|
|
1978
|
+
"Use these in the WORKFLOW.md prompt body with double-brace syntax.",
|
|
1979
|
+
"",
|
|
1980
|
+
"| Variable | Description |",
|
|
1981
|
+
"| -------- | ----------- |",
|
|
1982
|
+
"| `issue.identifier` | Issue identifier, for example `acme/platform#42`. |",
|
|
1983
|
+
"| `issue.title` | Issue title. |",
|
|
1984
|
+
"| `issue.state` | Current tracker state. |",
|
|
1985
|
+
"| `issue.description` | Issue body. |",
|
|
1986
|
+
"| `issue.url` | Issue URL. |",
|
|
1987
|
+
"| `issue.repository` | Repository in `owner/name` form. |",
|
|
1988
|
+
"| `issue.number` | Issue number. |",
|
|
1989
|
+
"| `attempt` | Retry attempt number, or null on the first run. |",
|
|
1990
|
+
"",
|
|
1991
|
+
"Only these variables are supported by strict-mode prompt rendering."
|
|
1992
|
+
].join("\n");
|
|
1993
|
+
}
|
|
1994
|
+
|
|
1995
|
+
// src/skills/templates/gh-symphony-references/workflow-posture-implement.ts
|
|
1996
|
+
function generateWorkflowPostureImplementReference(ctx) {
|
|
1997
|
+
const validationGuidance = buildRepositoryValidationGuidance(
|
|
1998
|
+
ctx.detectedEnvironment
|
|
1999
|
+
);
|
|
2000
|
+
return [
|
|
2001
|
+
"# Workflow posture: implement",
|
|
2002
|
+
"",
|
|
2003
|
+
"Use this prompt-body posture when the agent should write features or fix bugs.",
|
|
2004
|
+
"This is the default posture and preserves the current generated WORKFLOW.md",
|
|
2005
|
+
"prompt-body behavior.",
|
|
2006
|
+
"",
|
|
2007
|
+
"## Agent Instructions",
|
|
2008
|
+
"",
|
|
2009
|
+
'You are an AI coding agent working on issue `{issue.identifier}`: "`{issue.title}`".',
|
|
2010
|
+
"",
|
|
2011
|
+
"**Repository:** `{issue.repository}`",
|
|
2012
|
+
"**Current state:** `{issue.state}`",
|
|
2013
|
+
"",
|
|
2014
|
+
"### Task",
|
|
2015
|
+
"",
|
|
2016
|
+
"`{issue.description}`",
|
|
2017
|
+
"",
|
|
2018
|
+
"### Default Posture",
|
|
2019
|
+
"",
|
|
2020
|
+
"1. This is an unattended orchestration session. Do not ask humans for follow-up actions.",
|
|
2021
|
+
"2. Only abort early if there is a genuine blocker (missing required credentials or secrets).",
|
|
2022
|
+
'3. In your final message, report only what was completed and any blockers. Do not include "next steps".',
|
|
2023
|
+
"",
|
|
2024
|
+
"### Repository Validation Guidance",
|
|
2025
|
+
"",
|
|
2026
|
+
...validationGuidance.map((line, index) => `${index + 1}. ${line}`),
|
|
2027
|
+
"",
|
|
2028
|
+
"### Workflow",
|
|
2029
|
+
"",
|
|
2030
|
+
"1. Read the issue description and understand the requirements.",
|
|
2031
|
+
"2. Explore the codebase to understand the relevant code structure.",
|
|
2032
|
+
"3. Implement the changes following the project's coding conventions.",
|
|
2033
|
+
"4. Write or update tests to cover the changes.",
|
|
2034
|
+
"5. Verify that all existing tests pass.",
|
|
2035
|
+
"6. Create a PR with a clear description of the changes.",
|
|
2036
|
+
"",
|
|
2037
|
+
"### Guardrails",
|
|
2038
|
+
"",
|
|
2039
|
+
"- Do not edit the issue body for planning or progress tracking.",
|
|
2040
|
+
"- If the issue is in a terminal state, do nothing and exit.",
|
|
2041
|
+
"- If you find out-of-scope improvements, open a separate issue rather than expanding the current scope.",
|
|
2042
|
+
"",
|
|
2043
|
+
"### Workpad Template",
|
|
2044
|
+
"",
|
|
2045
|
+
"Create a workpad comment on the issue with the following structure to track progress:",
|
|
2046
|
+
"",
|
|
2047
|
+
"```md",
|
|
2048
|
+
"## Workpad",
|
|
2049
|
+
"",
|
|
2050
|
+
"### Plan",
|
|
2051
|
+
"",
|
|
2052
|
+
"- [ ] 1. Task item",
|
|
2053
|
+
"",
|
|
2054
|
+
"### Acceptance Criteria",
|
|
2055
|
+
"",
|
|
2056
|
+
"- [ ] Criterion 1",
|
|
2057
|
+
"",
|
|
2058
|
+
"### Validation",
|
|
2059
|
+
"",
|
|
2060
|
+
"- [ ] Test: `command`",
|
|
2061
|
+
"",
|
|
2062
|
+
"### Notes",
|
|
2063
|
+
"",
|
|
2064
|
+
"- Progress notes",
|
|
2065
|
+
"```"
|
|
2066
|
+
].join("\n");
|
|
2067
|
+
}
|
|
2068
|
+
|
|
2069
|
+
// src/skills/templates/gh-symphony-references/workflow-posture-review.ts
|
|
2070
|
+
function generateWorkflowPostureReviewReference(ctx) {
|
|
2071
|
+
const validationGuidance = buildRepositoryValidationGuidance(
|
|
2072
|
+
ctx.detectedEnvironment
|
|
2073
|
+
);
|
|
2074
|
+
return [
|
|
2075
|
+
"# Workflow posture: review",
|
|
2076
|
+
"",
|
|
2077
|
+
"Use this prompt-body posture when the agent should review PRs and leave",
|
|
2078
|
+
"comments. This posture is read-only for repository code.",
|
|
2079
|
+
"",
|
|
2080
|
+
"## Agent Instructions",
|
|
2081
|
+
"",
|
|
2082
|
+
'You are an AI code-review agent working on issue `{issue.identifier}`: "`{issue.title}`".',
|
|
2083
|
+
"",
|
|
2084
|
+
"**Repository:** `{issue.repository}`",
|
|
2085
|
+
"**Current state:** `{issue.state}`",
|
|
2086
|
+
"",
|
|
2087
|
+
"### Task",
|
|
2088
|
+
"",
|
|
2089
|
+
"`{issue.description}`",
|
|
2090
|
+
"",
|
|
2091
|
+
"### Default Posture",
|
|
2092
|
+
"",
|
|
2093
|
+
"1. Review linked pull requests. Do NOT write code, push commits, or open new PRs.",
|
|
2094
|
+
"2. Treat failing required tests as grounds to request changes unless the failure is clearly unrelated and documented.",
|
|
2095
|
+
"3. In your final message, report only the review outcome and any blockers. Do not include follow-up work for the human unless it is required to unblock review.",
|
|
2096
|
+
"",
|
|
2097
|
+
"### Repository Validation Guidance",
|
|
2098
|
+
"",
|
|
2099
|
+
...validationGuidance.map((line, index) => `${index + 1}. ${line}`),
|
|
2100
|
+
"",
|
|
2101
|
+
"### Workflow",
|
|
2102
|
+
"",
|
|
2103
|
+
"1. Find the PR linked from the issue, project item, or issue timeline.",
|
|
2104
|
+
"2. Read the PR title, body, diff, linked issue, existing reviews, inline comments, and check status.",
|
|
2105
|
+
"3. Run the repository's relevant tests, lint, typecheck, or build commands when available and practical.",
|
|
2106
|
+
"4. Leave inline review comments for concrete, actionable findings.",
|
|
2107
|
+
"5. Submit a summary review: approve only when the change is correct and validation is acceptable; otherwise request changes.",
|
|
2108
|
+
"",
|
|
2109
|
+
"### Guardrails",
|
|
2110
|
+
"",
|
|
2111
|
+
"- Never push code from this posture.",
|
|
2112
|
+
"- Never approve PRs that introduce new dependencies without explicitly noting the dependency risk and why it is acceptable.",
|
|
2113
|
+
"- If relevant tests fail and the failure is not proven unrelated, request changes.",
|
|
2114
|
+
"- Keep comments specific to correctness, maintainability, tests, security, and issue fit.",
|
|
2115
|
+
"- Do not create a workpad; the review threads on the PR are the audit trail."
|
|
2116
|
+
].join("\n");
|
|
2117
|
+
}
|
|
2118
|
+
|
|
2119
|
+
// src/skills/templates/gh-symphony-references/workflow-posture-maintain.ts
|
|
2120
|
+
function generateWorkflowPostureMaintainReference(ctx) {
|
|
2121
|
+
const validationGuidance = buildRepositoryValidationGuidance(
|
|
2122
|
+
ctx.detectedEnvironment
|
|
2123
|
+
);
|
|
2124
|
+
return [
|
|
2125
|
+
"# Workflow posture: maintain",
|
|
2126
|
+
"",
|
|
2127
|
+
"Use this prompt-body posture for low-risk maintenance such as dependency",
|
|
2128
|
+
"bumps, lint sweeps, small chores, and repository hygiene.",
|
|
2129
|
+
"",
|
|
2130
|
+
"## Agent Instructions",
|
|
2131
|
+
"",
|
|
2132
|
+
'You are a maintenance coding agent working on issue `{issue.identifier}`: "`{issue.title}`".',
|
|
2133
|
+
"",
|
|
2134
|
+
"**Repository:** `{issue.repository}`",
|
|
2135
|
+
"**Current state:** `{issue.state}`",
|
|
2136
|
+
"",
|
|
2137
|
+
"### Task",
|
|
2138
|
+
"",
|
|
2139
|
+
"`{issue.description}`",
|
|
2140
|
+
"",
|
|
2141
|
+
"### Default Posture",
|
|
2142
|
+
"",
|
|
2143
|
+
"1. Make the smallest possible change that satisfies the maintenance request.",
|
|
2144
|
+
"2. Defer human-judgment calls instead of broadening scope.",
|
|
2145
|
+
"3. In your final message, report only what was completed and any blockers. Do not include optional next steps.",
|
|
2146
|
+
"",
|
|
2147
|
+
"### Repository Validation Guidance",
|
|
2148
|
+
"",
|
|
2149
|
+
...validationGuidance.map((line, index) => `${index + 1}. ${line}`),
|
|
2150
|
+
"",
|
|
2151
|
+
"### Workflow",
|
|
2152
|
+
"",
|
|
2153
|
+
"1. Identify the exact maintenance task and affected files.",
|
|
2154
|
+
"2. Make the minimal change needed; avoid drive-by refactors.",
|
|
2155
|
+
"3. Run the relevant tests, lint, typecheck, or build commands for the affected area.",
|
|
2156
|
+
"4. Create a PR when the change is complete, or exit with a blocker note if approval is required.",
|
|
2157
|
+
"",
|
|
2158
|
+
"### Guardrails",
|
|
2159
|
+
"",
|
|
2160
|
+
"- Do not perform major dependency bumps without explicit approval.",
|
|
2161
|
+
"- Do not delete files without confirmation unless the issue explicitly requests it.",
|
|
2162
|
+
"- If the implementation exceeds 50 lines of non-generated code, stop and ask for human confirmation before continuing.",
|
|
2163
|
+
"- Do not mix unrelated cleanup into the maintenance change.",
|
|
2164
|
+
"",
|
|
2165
|
+
"### Workpad Template",
|
|
2166
|
+
"",
|
|
2167
|
+
"Create a compact workpad comment on the issue with the following structure:",
|
|
2168
|
+
"",
|
|
2169
|
+
"```md",
|
|
2170
|
+
"## Workpad",
|
|
2171
|
+
"",
|
|
2172
|
+
"### Plan",
|
|
2173
|
+
"",
|
|
2174
|
+
"- [ ] Minimal maintenance change",
|
|
2175
|
+
"- [ ] Validation and PR handoff",
|
|
2176
|
+
"",
|
|
2177
|
+
"### Validation",
|
|
2178
|
+
"",
|
|
2179
|
+
"- [ ] Test/lint/typecheck/build command",
|
|
2180
|
+
"",
|
|
2181
|
+
"### Blockers",
|
|
2182
|
+
"",
|
|
2183
|
+
"None",
|
|
2184
|
+
"```"
|
|
2185
|
+
].join("\n");
|
|
2186
|
+
}
|
|
2187
|
+
|
|
2188
|
+
// src/skills/templates/gh-symphony-references/index.ts
|
|
2189
|
+
var GH_SYMPHONY_REFERENCE_FILES = [
|
|
2190
|
+
{
|
|
2191
|
+
relativePath: "references/README.md",
|
|
2192
|
+
generate: generateGhSymphonyReferencesReadme
|
|
2193
|
+
},
|
|
2194
|
+
{
|
|
2195
|
+
relativePath: "references/workflow-schema.md",
|
|
2196
|
+
generate: generateWorkflowSchemaReference
|
|
2197
|
+
},
|
|
2198
|
+
{
|
|
2199
|
+
relativePath: "references/workflow-posture-implement.md",
|
|
2200
|
+
generate: generateWorkflowPostureImplementReference
|
|
2201
|
+
},
|
|
2202
|
+
{
|
|
2203
|
+
relativePath: "references/workflow-posture-review.md",
|
|
2204
|
+
generate: generateWorkflowPostureReviewReference
|
|
2205
|
+
},
|
|
2206
|
+
{
|
|
2207
|
+
relativePath: "references/workflow-posture-maintain.md",
|
|
2208
|
+
generate: generateWorkflowPostureMaintainReference
|
|
2209
|
+
}
|
|
2210
|
+
];
|
|
2211
|
+
|
|
1887
2212
|
// src/skills/templates/index.ts
|
|
1888
2213
|
var ALL_SKILL_TEMPLATES = [
|
|
1889
2214
|
{
|
|
1890
2215
|
name: "gh-symphony",
|
|
1891
|
-
|
|
1892
|
-
|
|
2216
|
+
files: [
|
|
2217
|
+
{ relativePath: "SKILL.md", generate: generateGhSymphonySkill },
|
|
2218
|
+
...GH_SYMPHONY_REFERENCE_FILES
|
|
2219
|
+
]
|
|
1893
2220
|
},
|
|
1894
2221
|
{
|
|
1895
2222
|
name: "gh-project",
|
|
1896
|
-
|
|
1897
|
-
|
|
2223
|
+
files: [{ relativePath: "SKILL.md", generate: generateGhProjectSkill }]
|
|
2224
|
+
},
|
|
2225
|
+
{
|
|
2226
|
+
name: "commit",
|
|
2227
|
+
files: [{ relativePath: "SKILL.md", generate: generateCommitSkill }]
|
|
2228
|
+
},
|
|
2229
|
+
{
|
|
2230
|
+
name: "push",
|
|
2231
|
+
files: [{ relativePath: "SKILL.md", generate: generatePushSkill }]
|
|
2232
|
+
},
|
|
2233
|
+
{
|
|
2234
|
+
name: "pull",
|
|
2235
|
+
files: [{ relativePath: "SKILL.md", generate: generatePullSkill }]
|
|
1898
2236
|
},
|
|
1899
|
-
{
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
2237
|
+
{
|
|
2238
|
+
name: "land",
|
|
2239
|
+
files: [{ relativePath: "SKILL.md", generate: generateLandSkill }]
|
|
2240
|
+
}
|
|
1903
2241
|
];
|
|
1904
2242
|
|
|
1905
2243
|
// src/commands/workflow-init.ts
|
|
@@ -2017,7 +2355,7 @@ function validateInitRuntime(runtime) {
|
|
|
2017
2355
|
async function promptRuntimeSelection() {
|
|
2018
2356
|
return abortIfCancelled(
|
|
2019
2357
|
p.select({
|
|
2020
|
-
message: "Step 1/
|
|
2358
|
+
message: "Step 1/5 \u2014 Select the agent runtime:",
|
|
2021
2359
|
options: [
|
|
2022
2360
|
{
|
|
2023
2361
|
value: "codex-app-server",
|
|
@@ -2095,7 +2433,7 @@ async function writePlannedFile(file) {
|
|
|
2095
2433
|
if (file.status === "unchanged") {
|
|
2096
2434
|
return false;
|
|
2097
2435
|
}
|
|
2098
|
-
await mkdir3(
|
|
2436
|
+
await mkdir3(dirname3(file.path), { recursive: true });
|
|
2099
2437
|
const temporaryPath = `${file.path}.tmp`;
|
|
2100
2438
|
await writeFile3(temporaryPath, file.content, "utf8");
|
|
2101
2439
|
await rename2(temporaryPath, file.path);
|
|
@@ -2104,6 +2442,9 @@ async function writePlannedFile(file) {
|
|
|
2104
2442
|
}
|
|
2105
2443
|
return true;
|
|
2106
2444
|
}
|
|
2445
|
+
function skillNameForPath(skillsDir, filePath) {
|
|
2446
|
+
return relative(skillsDir, filePath).split(/[\\/]/)[0] ?? "";
|
|
2447
|
+
}
|
|
2107
2448
|
function resolveStatusField(projectDetail) {
|
|
2108
2449
|
return projectDetail.statusFields.find((f) => f.name.toLowerCase() === "status") ?? projectDetail.statusFields[0] ?? null;
|
|
2109
2450
|
}
|
|
@@ -2318,16 +2659,68 @@ async function promptStateMappings(statusField, options) {
|
|
|
2318
2659
|
}
|
|
2319
2660
|
return mappings;
|
|
2320
2661
|
}
|
|
2662
|
+
function getDefaultBlockerCheckStates(lifecycle) {
|
|
2663
|
+
const firstActive = lifecycle.activeStates[0];
|
|
2664
|
+
return firstActive ? [firstActive] : [];
|
|
2665
|
+
}
|
|
2666
|
+
async function promptBlockerCheck(lifecycle, options) {
|
|
2667
|
+
const stepLabel = options?.stepLabel ?? "Step 3/5";
|
|
2668
|
+
const activeStates = lifecycle.activeStates;
|
|
2669
|
+
const defaultStates = getDefaultBlockerCheckStates(lifecycle);
|
|
2670
|
+
if (activeStates.length === 0) {
|
|
2671
|
+
p.log.warn("No active states; blocker check cannot be enabled.");
|
|
2672
|
+
p.log.info("Blocker check: disabled");
|
|
2673
|
+
return [];
|
|
2674
|
+
}
|
|
2675
|
+
const activeStateSummary = activeStates.length === 1 ? `"${activeStates[0]}"` : "selected active states";
|
|
2676
|
+
const enabled = await abortIfCancelled(
|
|
2677
|
+
p.confirm({
|
|
2678
|
+
message: `${stepLabel} \u2014 Enable blocker check? Issues with unresolved "blocked by" dependencies will be held back from dispatch on ${activeStateSummary}.`,
|
|
2679
|
+
initialValue: true
|
|
2680
|
+
})
|
|
2681
|
+
);
|
|
2682
|
+
if (!enabled) {
|
|
2683
|
+
p.log.info("Blocker check: disabled");
|
|
2684
|
+
return [];
|
|
2685
|
+
}
|
|
2686
|
+
if (activeStates.length === 1) {
|
|
2687
|
+
p.log.info(`Blocker check applies to: ${activeStates[0]}`);
|
|
2688
|
+
return [activeStates[0]];
|
|
2689
|
+
}
|
|
2690
|
+
const selectedStates = await abortIfCancelled(
|
|
2691
|
+
p.multiselect({
|
|
2692
|
+
message: `${stepLabel} \u2014 Which active states should be blocker-checked?`,
|
|
2693
|
+
options: activeStates.map((state) => ({
|
|
2694
|
+
value: state,
|
|
2695
|
+
label: state,
|
|
2696
|
+
hint: defaultStates.includes(state) ? "default" : void 0
|
|
2697
|
+
})),
|
|
2698
|
+
initialValues: defaultStates,
|
|
2699
|
+
required: true
|
|
2700
|
+
})
|
|
2701
|
+
);
|
|
2702
|
+
p.log.info(`Blocker check applies to: ${selectedStates.join(", ")}`);
|
|
2703
|
+
return [...selectedStates];
|
|
2704
|
+
}
|
|
2321
2705
|
async function planWorkflowArtifacts(opts) {
|
|
2322
2706
|
const environment = opts.environment ?? await detectEnvironment(opts.cwd);
|
|
2323
2707
|
const priority = opts.priority ?? (opts.priorityField ? buildProjectFieldPriority(opts.priorityField) : buildDisabledPriority());
|
|
2708
|
+
const defaultLifecycle = toWorkflowLifecycleConfig(
|
|
2709
|
+
opts.statusField.name,
|
|
2710
|
+
opts.mappings
|
|
2711
|
+
);
|
|
2712
|
+
const defaultBlockerCheckStates = getDefaultBlockerCheckStates(defaultLifecycle);
|
|
2713
|
+
const lifecycle = opts.lifecycle ?? toWorkflowLifecycleConfig(opts.statusField.name, opts.mappings, {
|
|
2714
|
+
blockerCheckStates: defaultBlockerCheckStates,
|
|
2715
|
+
planningStates: defaultBlockerCheckStates
|
|
2716
|
+
});
|
|
2324
2717
|
const workflowMd = generateWorkflowMarkdown({
|
|
2325
2718
|
projectId: opts.projectDetail.id,
|
|
2326
2719
|
stateFieldName: opts.statusField.name,
|
|
2327
2720
|
priority,
|
|
2328
2721
|
includePriorityTemplates: opts.includePriorityTemplates ?? priority.source === "disabled",
|
|
2329
2722
|
mappings: opts.mappings,
|
|
2330
|
-
lifecycle
|
|
2723
|
+
lifecycle,
|
|
2331
2724
|
runtime: opts.runtime,
|
|
2332
2725
|
detectedEnvironment: environment
|
|
2333
2726
|
});
|
|
@@ -2343,6 +2736,7 @@ async function planWorkflowArtifacts(opts) {
|
|
|
2343
2736
|
statusField: opts.statusField,
|
|
2344
2737
|
priorityField: opts.priorityField,
|
|
2345
2738
|
priority,
|
|
2739
|
+
lifecycle,
|
|
2346
2740
|
includePriorityTemplates: opts.includePriorityTemplates ?? priority.source === "disabled",
|
|
2347
2741
|
runtime: opts.runtime,
|
|
2348
2742
|
skipSkills: opts.skipSkills,
|
|
@@ -2368,6 +2762,11 @@ function summarizeEnvironment(env) {
|
|
|
2368
2762
|
`Existing skills ${env.existingSkills.length === 0 ? "none" : env.existingSkills.join(", ")}`
|
|
2369
2763
|
];
|
|
2370
2764
|
}
|
|
2765
|
+
function deriveWaitStates(statusField, lifecycle) {
|
|
2766
|
+
const active = new Set(lifecycle.activeStates);
|
|
2767
|
+
const terminal = new Set(lifecycle.terminalStates);
|
|
2768
|
+
return statusField.options.map((option) => option.name).filter((state) => !active.has(state) && !terminal.has(state));
|
|
2769
|
+
}
|
|
2371
2770
|
async function planEcosystem(opts) {
|
|
2372
2771
|
const {
|
|
2373
2772
|
cwd,
|
|
@@ -2379,6 +2778,19 @@ async function planEcosystem(opts) {
|
|
|
2379
2778
|
skipContext
|
|
2380
2779
|
} = opts;
|
|
2381
2780
|
const priority = opts.priority ?? (priorityField ? buildProjectFieldPriority(priorityField) : buildDisabledPriority());
|
|
2781
|
+
const automaticLifecycle = toWorkflowLifecycleConfig(
|
|
2782
|
+
statusField.name,
|
|
2783
|
+
buildAutomaticStateMappings(statusField)
|
|
2784
|
+
);
|
|
2785
|
+
const defaultBlockerCheckStates = getDefaultBlockerCheckStates(automaticLifecycle);
|
|
2786
|
+
const lifecycle = opts.lifecycle ?? toWorkflowLifecycleConfig(
|
|
2787
|
+
statusField.name,
|
|
2788
|
+
buildAutomaticStateMappings(statusField),
|
|
2789
|
+
{
|
|
2790
|
+
blockerCheckStates: defaultBlockerCheckStates,
|
|
2791
|
+
planningStates: defaultBlockerCheckStates
|
|
2792
|
+
}
|
|
2793
|
+
);
|
|
2382
2794
|
const ghSymphonyDir = join3(cwd, ".gh-symphony");
|
|
2383
2795
|
const environment = opts.environment ?? await detectEnvironment(cwd);
|
|
2384
2796
|
const files = [];
|
|
@@ -2418,6 +2830,7 @@ async function planEcosystem(opts) {
|
|
|
2418
2830
|
})),
|
|
2419
2831
|
projectId: projectDetail.id,
|
|
2420
2832
|
priority,
|
|
2833
|
+
lifecycle,
|
|
2421
2834
|
detectedEnvironment: environment
|
|
2422
2835
|
});
|
|
2423
2836
|
files.push(
|
|
@@ -2454,10 +2867,11 @@ async function planEcosystem(opts) {
|
|
|
2454
2867
|
}
|
|
2455
2868
|
);
|
|
2456
2869
|
for (const plannedSkill of plannedSkills) {
|
|
2870
|
+
const skillName = skillNameForPath(skillsDir, plannedSkill.path);
|
|
2457
2871
|
files.push(
|
|
2458
2872
|
await planFileChange({
|
|
2459
2873
|
path: plannedSkill.path,
|
|
2460
|
-
label: `Skill ${
|
|
2874
|
+
label: `Skill ${skillName}`,
|
|
2461
2875
|
content: plannedSkill.content,
|
|
2462
2876
|
mode: "create-only"
|
|
2463
2877
|
})
|
|
@@ -2469,6 +2883,8 @@ async function planEcosystem(opts) {
|
|
|
2469
2883
|
githubProjectTitle: projectDetail.title,
|
|
2470
2884
|
runtime,
|
|
2471
2885
|
priority,
|
|
2886
|
+
lifecycle,
|
|
2887
|
+
waitStates: deriveWaitStates(statusField, lifecycle),
|
|
2472
2888
|
skillsDir,
|
|
2473
2889
|
skipSkills,
|
|
2474
2890
|
environment,
|
|
@@ -2505,7 +2921,7 @@ async function writeEcosystem(opts) {
|
|
|
2505
2921
|
continue;
|
|
2506
2922
|
}
|
|
2507
2923
|
if (file.label.startsWith("Skill ")) {
|
|
2508
|
-
const skillName =
|
|
2924
|
+
const skillName = file.label.slice("Skill ".length);
|
|
2509
2925
|
if (written) {
|
|
2510
2926
|
skillsWritten.push(skillName);
|
|
2511
2927
|
} else {
|
|
@@ -2518,13 +2934,15 @@ async function writeEcosystem(opts) {
|
|
|
2518
2934
|
githubProjectTitle: plan.githubProjectTitle,
|
|
2519
2935
|
runtime: plan.runtime,
|
|
2520
2936
|
priority: plan.priority,
|
|
2937
|
+
lifecycle: plan.lifecycle,
|
|
2938
|
+
waitStates: plan.waitStates,
|
|
2521
2939
|
skillsDir: plan.skillsDir,
|
|
2522
2940
|
skipSkills: plan.skipSkills,
|
|
2523
2941
|
afterCreateHookWritten,
|
|
2524
2942
|
contextYamlWritten,
|
|
2525
2943
|
referenceWorkflowWritten,
|
|
2526
|
-
skillsWritten: skillsWritten.sort(),
|
|
2527
|
-
skillsSkipped: skillsSkipped.sort()
|
|
2944
|
+
skillsWritten: [...new Set(skillsWritten)].sort(),
|
|
2945
|
+
skillsSkipped: [...new Set(skillsSkipped)].sort()
|
|
2528
2946
|
};
|
|
2529
2947
|
}
|
|
2530
2948
|
function formatPrioritySummaryLines(priority) {
|
|
@@ -2539,9 +2957,20 @@ function formatPrioritySummaryLines(priority) {
|
|
|
2539
2957
|
];
|
|
2540
2958
|
}
|
|
2541
2959
|
const mapping = Object.entries(priority.labels).map(([name, value]) => `${name}=${value}`).join(", ");
|
|
2960
|
+
return ["Priority source labels", `Priority mapping ${mapping || "none"}`];
|
|
2961
|
+
}
|
|
2962
|
+
function formatLifecycleValue(states) {
|
|
2963
|
+
return states.length > 0 ? states.join(", ") : "disabled";
|
|
2964
|
+
}
|
|
2965
|
+
function formatLifecycleSummaryLines(lifecycle, waitStates) {
|
|
2542
2966
|
return [
|
|
2543
|
-
"
|
|
2544
|
-
`
|
|
2967
|
+
"Lifecycle",
|
|
2968
|
+
` Status field ${lifecycle.stateFieldName}`,
|
|
2969
|
+
` Active ${lifecycle.activeStates.join(", ") || "(none)"}`,
|
|
2970
|
+
` Wait ${waitStates.join(", ") || "(none)"}`,
|
|
2971
|
+
` Terminal ${lifecycle.terminalStates.join(", ") || "(none)"}`,
|
|
2972
|
+
` Blocker check ${formatLifecycleValue(lifecycle.blockerCheckStates)}`,
|
|
2973
|
+
` Planning ${formatLifecycleValue(lifecycle.planningStates)}`
|
|
2545
2974
|
];
|
|
2546
2975
|
}
|
|
2547
2976
|
function printEcosystemSummary(result, workflowPath, opts) {
|
|
@@ -2554,6 +2983,10 @@ function printEcosystemSummary(result, workflowPath, opts) {
|
|
|
2554
2983
|
lines.push(`Runtime ${result.runtime}`);
|
|
2555
2984
|
lines.push(...formatPrioritySummaryLines(result.priority));
|
|
2556
2985
|
lines.push("");
|
|
2986
|
+
lines.push(
|
|
2987
|
+
...formatLifecycleSummaryLines(result.lifecycle, result.waitStates)
|
|
2988
|
+
);
|
|
2989
|
+
lines.push("");
|
|
2557
2990
|
lines.push("Generated files");
|
|
2558
2991
|
lines.push(` \u2713 WORKFLOW.md ${relWorkflow}`);
|
|
2559
2992
|
if (result.afterCreateHookWritten) {
|
|
@@ -2608,6 +3041,13 @@ function renderDryRunPreview(workflowPath, workflowPlan, ecosystemPlan) {
|
|
|
2608
3041
|
lines.push(`Runtime ${ecosystemPlan.runtime}`);
|
|
2609
3042
|
lines.push(...formatPrioritySummaryLines(ecosystemPlan.priority));
|
|
2610
3043
|
lines.push("");
|
|
3044
|
+
lines.push(
|
|
3045
|
+
...formatLifecycleSummaryLines(
|
|
3046
|
+
ecosystemPlan.lifecycle,
|
|
3047
|
+
ecosystemPlan.waitStates
|
|
3048
|
+
)
|
|
3049
|
+
);
|
|
3050
|
+
lines.push("");
|
|
2611
3051
|
lines.push("Planned file changes");
|
|
2612
3052
|
lines.push(
|
|
2613
3053
|
` ${statusIcon[workflowPlan.status]} ${workflowPlan.status.padEnd(9)} WORKFLOW.md ${relWorkflow}`
|
|
@@ -2737,6 +3177,13 @@ Run without --non-interactive for manual mapping.
|
|
|
2737
3177
|
process.exitCode = 1;
|
|
2738
3178
|
return;
|
|
2739
3179
|
}
|
|
3180
|
+
const defaultBlockerCheckStates = getDefaultBlockerCheckStates(
|
|
3181
|
+
toWorkflowLifecycleConfig(statusField.name, mappings)
|
|
3182
|
+
);
|
|
3183
|
+
const lifecycle = toWorkflowLifecycleConfig(statusField.name, mappings, {
|
|
3184
|
+
blockerCheckStates: defaultBlockerCheckStates,
|
|
3185
|
+
planningStates: defaultBlockerCheckStates
|
|
3186
|
+
});
|
|
2740
3187
|
const outputPath = resolve(flags.output ?? "WORKFLOW.md");
|
|
2741
3188
|
const { workflowPlan, ecosystemPlan } = await planWorkflowArtifacts({
|
|
2742
3189
|
cwd: process.cwd(),
|
|
@@ -2747,6 +3194,7 @@ Run without --non-interactive for manual mapping.
|
|
|
2747
3194
|
priority,
|
|
2748
3195
|
includePriorityTemplates: !autoPriorityField,
|
|
2749
3196
|
mappings,
|
|
3197
|
+
lifecycle,
|
|
2750
3198
|
runtime,
|
|
2751
3199
|
skipSkills: flags.skipSkills,
|
|
2752
3200
|
skipContext: flags.skipContext
|
|
@@ -2771,6 +3219,7 @@ Run without --non-interactive for manual mapping.
|
|
|
2771
3219
|
priorityField: autoPriorityField,
|
|
2772
3220
|
priority,
|
|
2773
3221
|
includePriorityTemplates: !autoPriorityField,
|
|
3222
|
+
lifecycle,
|
|
2774
3223
|
runtime,
|
|
2775
3224
|
skipSkills: flags.skipSkills,
|
|
2776
3225
|
skipContext: flags.skipContext
|
|
@@ -2847,7 +3296,7 @@ async function runInteractiveStandalone(flags, _options) {
|
|
|
2847
3296
|
}
|
|
2848
3297
|
const selectedGithubProjectId = await abortIfCancelled(
|
|
2849
3298
|
p.select({
|
|
2850
|
-
message: "Step 2/
|
|
3299
|
+
message: "Step 2/5 \u2014 Select a GitHub Project board:",
|
|
2851
3300
|
options: projects.map((proj) => ({
|
|
2852
3301
|
value: proj.id,
|
|
2853
3302
|
label: `${proj.owner.login}/${proj.title}`,
|
|
@@ -2882,12 +3331,7 @@ async function runInteractiveStandalone(flags, _options) {
|
|
|
2882
3331
|
projectDetail.linkedRepositories
|
|
2883
3332
|
);
|
|
2884
3333
|
const mappings = await promptStateMappings(statusField, {
|
|
2885
|
-
stepLabel: "Step 3/
|
|
2886
|
-
});
|
|
2887
|
-
const { priority, priorityField } = await promptPriorityConfig({
|
|
2888
|
-
priorityResolution,
|
|
2889
|
-
labelNames: priorityLabelNames,
|
|
2890
|
-
stepLabel: "Step 4/4"
|
|
3334
|
+
stepLabel: "Step 3/5"
|
|
2891
3335
|
});
|
|
2892
3336
|
const validation = validateStateMapping(mappings);
|
|
2893
3337
|
if (!validation.valid) {
|
|
@@ -2901,6 +3345,19 @@ async function runInteractiveStandalone(flags, _options) {
|
|
|
2901
3345
|
for (const warn of validation.warnings) {
|
|
2902
3346
|
p.log.warn(` \u26A0 ${warn}`);
|
|
2903
3347
|
}
|
|
3348
|
+
const lifecycleBase = toWorkflowLifecycleConfig(statusField.name, mappings);
|
|
3349
|
+
const blockerCheckStates = await promptBlockerCheck(lifecycleBase, {
|
|
3350
|
+
stepLabel: "Step 4/5"
|
|
3351
|
+
});
|
|
3352
|
+
const lifecycle = toWorkflowLifecycleConfig(statusField.name, mappings, {
|
|
3353
|
+
blockerCheckStates,
|
|
3354
|
+
planningStates: blockerCheckStates
|
|
3355
|
+
});
|
|
3356
|
+
const { priority, priorityField } = await promptPriorityConfig({
|
|
3357
|
+
priorityResolution,
|
|
3358
|
+
labelNames: priorityLabelNames,
|
|
3359
|
+
stepLabel: "Step 5/5"
|
|
3360
|
+
});
|
|
2904
3361
|
const outputPath = resolve(flags.output ?? "WORKFLOW.md");
|
|
2905
3362
|
const { workflowPlan, ecosystemPlan } = await planWorkflowArtifacts({
|
|
2906
3363
|
cwd: process.cwd(),
|
|
@@ -2911,6 +3368,7 @@ async function runInteractiveStandalone(flags, _options) {
|
|
|
2911
3368
|
priority,
|
|
2912
3369
|
includePriorityTemplates: priority.source === "disabled",
|
|
2913
3370
|
mappings,
|
|
3371
|
+
lifecycle,
|
|
2914
3372
|
runtime,
|
|
2915
3373
|
skipSkills: flags.skipSkills,
|
|
2916
3374
|
skipContext: flags.skipContext
|
|
@@ -2927,6 +3385,7 @@ async function runInteractiveStandalone(flags, _options) {
|
|
|
2927
3385
|
priorityField,
|
|
2928
3386
|
priority,
|
|
2929
3387
|
includePriorityTemplates: priority.source === "disabled",
|
|
3388
|
+
lifecycle,
|
|
2930
3389
|
runtime,
|
|
2931
3390
|
skipSkills: flags.skipSkills,
|
|
2932
3391
|
skipContext: flags.skipContext
|
|
@@ -2938,6 +3397,7 @@ async function runInteractiveStandalone(flags, _options) {
|
|
|
2938
3397
|
}
|
|
2939
3398
|
|
|
2940
3399
|
export {
|
|
3400
|
+
toWorkflowLifecycleConfig,
|
|
2941
3401
|
validateStateMapping,
|
|
2942
3402
|
abortIfCancelled,
|
|
2943
3403
|
workflow_init_default,
|
|
@@ -2947,6 +3407,7 @@ export {
|
|
|
2947
3407
|
collectPriorityLabelNames,
|
|
2948
3408
|
promptPriorityConfig,
|
|
2949
3409
|
promptStateMappings,
|
|
3410
|
+
promptBlockerCheck,
|
|
2950
3411
|
planWorkflowArtifacts,
|
|
2951
3412
|
writeWorkflowPlan,
|
|
2952
3413
|
writeEcosystem,
|