@gh-symphony/cli 0.4.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-TTVGBHZI.js → chunk-3EFLX75C.js} +5 -5
- package/dist/{chunk-RZ3WO7OV.js → chunk-3IRPSPAF.js} +1 -1
- package/dist/{chunk-B6G3KGBB.js → chunk-BBUCDKSL.js} +76 -8
- package/dist/{chunk-Z7CDL3T2.js → chunk-GOR4JGJT.js} +1 -1
- package/dist/{chunk-QOX5UGUE.js → chunk-MUN7QSFF.js} +6 -3
- package/dist/{chunk-ZPS4CQZJ.js → chunk-RU5GQG6A.js} +824 -971
- package/dist/{chunk-6OPRRC2J.js → chunk-YKC6CGD6.js} +21 -0
- package/dist/{chunk-4ICDSQCJ.js → chunk-YZP5N5XP.js} +3 -1
- package/dist/{config-cmd-AOZVS6GU.js → config-cmd-OIVIUKG7.js} +1 -1
- package/dist/{doctor-CCUTNEYN.js → doctor-3WDODAKW.js} +7 -7
- package/dist/index.js +10 -10
- package/dist/{repo-C2APQR2P.js → repo-3JLOCGRJ.js} +6 -6
- package/dist/{setup-JINI7HBM.js → setup-AADOLSW5.js} +12 -6
- package/dist/{upgrade-EBD4LX5W.js → upgrade-KTTYQQFB.js} +2 -2
- package/dist/{version-6Z354HHH.js → version-HXAMSXVG.js} +1 -1
- package/dist/worker-entry.js +2 -2
- package/dist/{workflow-BOZ25AJ2.js → workflow-URPIYFYQ.js} +6 -6
- package/package.json +4 -4
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
formatClaudePreflightText,
|
|
17
17
|
resolveClaudeCommandBinary,
|
|
18
18
|
runClaudePreflight
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-YKC6CGD6.js";
|
|
20
20
|
|
|
21
21
|
// src/mapping/smart-defaults.ts
|
|
22
22
|
var ROLE_PATTERNS = [
|
|
@@ -97,8 +97,17 @@ function validateStateMapping(mappings) {
|
|
|
97
97
|
import * as p from "@clack/prompts";
|
|
98
98
|
import { spawnSync } from "child_process";
|
|
99
99
|
import { createHash } from "crypto";
|
|
100
|
-
import {
|
|
101
|
-
|
|
100
|
+
import {
|
|
101
|
+
chmod,
|
|
102
|
+
mkdir as mkdir2,
|
|
103
|
+
readFile as readFile3,
|
|
104
|
+
readdir as readdir2,
|
|
105
|
+
rename as rename2,
|
|
106
|
+
rm,
|
|
107
|
+
rmdir,
|
|
108
|
+
writeFile as writeFile2
|
|
109
|
+
} from "fs/promises";
|
|
110
|
+
import { dirname as dirname2, join as join3, relative, resolve } from "path";
|
|
102
111
|
|
|
103
112
|
// src/prompts/runtime-claude-constraints.ts
|
|
104
113
|
var CLAUDE_RUNTIME_CONSTRAINTS_SECTION = `## Runtime Constraints
|
|
@@ -209,7 +218,6 @@ function buildRepositoryValidationGuidance(input) {
|
|
|
209
218
|
// src/workflow/workflow-runtime.ts
|
|
210
219
|
var CODEX_RUNTIME_TOKENS = ["codex-app-server", "codex"];
|
|
211
220
|
var CLAUDE_RUNTIME_TOKENS = ["claude-print", "claude-code"];
|
|
212
|
-
var DEFAULT_CODEX_APP_SERVER_ARGS = ["app-server"];
|
|
213
221
|
var DEFAULT_CLAUDE_PRINT_ARGS = [
|
|
214
222
|
"-p",
|
|
215
223
|
"--output-format",
|
|
@@ -258,31 +266,6 @@ function resolveRuntimeCommand(runtime) {
|
|
|
258
266
|
}
|
|
259
267
|
return runtime;
|
|
260
268
|
}
|
|
261
|
-
function resolveRuntimeArgs(runtime) {
|
|
262
|
-
const normalized = normalizeInitRuntime(runtime);
|
|
263
|
-
if (normalized === "codex-app-server") {
|
|
264
|
-
return DEFAULT_CODEX_APP_SERVER_ARGS;
|
|
265
|
-
}
|
|
266
|
-
if (normalized === "claude-print") {
|
|
267
|
-
return DEFAULT_CLAUDE_PRINT_ARGS;
|
|
268
|
-
}
|
|
269
|
-
return [];
|
|
270
|
-
}
|
|
271
|
-
function resolveRuntimeAgentCommand(runtime) {
|
|
272
|
-
const command = resolveRuntimeCommand(runtime);
|
|
273
|
-
const args = resolveRuntimeArgs(runtime);
|
|
274
|
-
return args.length === 0 ? command : [command, ...args].join(" ");
|
|
275
|
-
}
|
|
276
|
-
function resolveShellAgentCommand(runtime) {
|
|
277
|
-
if (/^\s*bash\s+-lc\s+/.test(runtime)) {
|
|
278
|
-
return runtime;
|
|
279
|
-
}
|
|
280
|
-
const normalized = normalizeInitRuntime(runtime);
|
|
281
|
-
if (normalized === "codex-app-server" || normalized === "claude-print") {
|
|
282
|
-
return `bash -lc ${resolveRuntimeAgentCommand(normalized)}`;
|
|
283
|
-
}
|
|
284
|
-
return runtime;
|
|
285
|
-
}
|
|
286
269
|
function buildRuntimeFrontMatter(runtime) {
|
|
287
270
|
const normalized = normalizeInitRuntime(runtime);
|
|
288
271
|
if (normalized === "codex-app-server") {
|
|
@@ -833,1129 +816,991 @@ async function detectEnvironment(cwd) {
|
|
|
833
816
|
};
|
|
834
817
|
}
|
|
835
818
|
|
|
836
|
-
// src/
|
|
837
|
-
import { mkdir, writeFile } from "fs/promises";
|
|
838
|
-
import { dirname } from "path";
|
|
839
|
-
function
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
819
|
+
// src/skills/skill-writer.ts
|
|
820
|
+
import { mkdir, readFile as readFile2, rename, writeFile } from "fs/promises";
|
|
821
|
+
import { dirname, join as join2 } from "path";
|
|
822
|
+
function normalizeRuntimeForSkills(runtime) {
|
|
823
|
+
if (isClaudeRuntime(runtime)) {
|
|
824
|
+
return "claude-code";
|
|
825
|
+
}
|
|
826
|
+
if (isCodexRuntime(runtime)) {
|
|
827
|
+
return "codex";
|
|
843
828
|
}
|
|
844
|
-
return
|
|
829
|
+
return null;
|
|
845
830
|
}
|
|
846
|
-
function
|
|
847
|
-
const
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
lines.push("");
|
|
851
|
-
lines.push("project:");
|
|
852
|
-
lines.push(` id: ${context.project.id}`);
|
|
853
|
-
lines.push(` title: ${yamlQuote(context.project.title)}`);
|
|
854
|
-
lines.push(` url: ${context.project.url}`);
|
|
855
|
-
lines.push("");
|
|
856
|
-
lines.push("status_field:");
|
|
857
|
-
lines.push(` id: ${context.status_field.id}`);
|
|
858
|
-
lines.push(` name: ${yamlQuote(context.status_field.name)}`);
|
|
859
|
-
lines.push(" columns:");
|
|
860
|
-
for (const column of context.status_field.columns) {
|
|
861
|
-
lines.push(` - id: ${column.id}`);
|
|
862
|
-
lines.push(` name: ${yamlQuote(column.name)}`);
|
|
863
|
-
lines.push(
|
|
864
|
-
` color: ${column.color === null ? "null" : yamlQuote(column.color)}`
|
|
865
|
-
);
|
|
866
|
-
lines.push(
|
|
867
|
-
` inferred_role: ${column.inferred_role === null ? "null" : column.inferred_role}`
|
|
868
|
-
);
|
|
869
|
-
lines.push(` confidence: ${column.confidence}`);
|
|
831
|
+
function resolveSkillsDir(repoRoot, runtime) {
|
|
832
|
+
const normalizedRuntime = normalizeRuntimeForSkills(runtime);
|
|
833
|
+
if (normalizedRuntime === "claude-code") {
|
|
834
|
+
return join2(repoRoot, ".claude", "skills");
|
|
870
835
|
}
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
if (context.text_fields.length === 0) {
|
|
874
|
-
lines.push(" []");
|
|
875
|
-
} else {
|
|
876
|
-
for (const field of context.text_fields) {
|
|
877
|
-
lines.push(` - id: ${field.id}`);
|
|
878
|
-
lines.push(` name: ${yamlQuote(field.name)}`);
|
|
879
|
-
lines.push(` data_type: ${field.data_type}`);
|
|
880
|
-
}
|
|
836
|
+
if (normalizedRuntime === "codex") {
|
|
837
|
+
return join2(repoRoot, ".codex", "skills");
|
|
881
838
|
}
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
lines.push(` - owner: ${repo.owner}`);
|
|
889
|
-
lines.push(` name: ${repo.name}`);
|
|
890
|
-
lines.push(` clone_url: ${repo.clone_url}`);
|
|
891
|
-
}
|
|
839
|
+
return null;
|
|
840
|
+
}
|
|
841
|
+
function buildSkillFilePlans(repoRoot, runtime, templates, context) {
|
|
842
|
+
const skillsDir = resolveSkillsDir(repoRoot, runtime);
|
|
843
|
+
if (!skillsDir) {
|
|
844
|
+
return { skillsDir: null, files: [] };
|
|
892
845
|
}
|
|
846
|
+
return {
|
|
847
|
+
skillsDir,
|
|
848
|
+
files: templates.flatMap(
|
|
849
|
+
(template) => template.files.map((file) => ({
|
|
850
|
+
path: join2(skillsDir, template.name, file.relativePath),
|
|
851
|
+
content: file.generate(context)
|
|
852
|
+
}))
|
|
853
|
+
)
|
|
854
|
+
};
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
// src/skills/templates/document.ts
|
|
858
|
+
function renderSkillDocument(options) {
|
|
859
|
+
const { name, description, bodyLines } = options;
|
|
860
|
+
return [
|
|
861
|
+
"---",
|
|
862
|
+
`name: ${name}`,
|
|
863
|
+
`description: ${description}`,
|
|
864
|
+
"license: MIT",
|
|
865
|
+
"metadata:",
|
|
866
|
+
" author: gh-symphony",
|
|
867
|
+
' version: "1.0"',
|
|
868
|
+
' generatedBy: "gh-symphony"',
|
|
869
|
+
"---",
|
|
870
|
+
"",
|
|
871
|
+
...bodyLines
|
|
872
|
+
].join("\n");
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
// src/skills/templates/gh-symphony.ts
|
|
876
|
+
function generateGhSymphonySkill(ctx) {
|
|
877
|
+
const lines = [];
|
|
878
|
+
lines.push("# /gh-symphony \u2014 WORKFLOW.md Design & Refinement");
|
|
879
|
+
lines.push("");
|
|
880
|
+
lines.push("## Trigger");
|
|
881
|
+
lines.push("");
|
|
882
|
+
lines.push("Use this skill when you want to:");
|
|
883
|
+
lines.push("- Create a new WORKFLOW.md for a GitHub Symphony project");
|
|
884
|
+
lines.push("- Refine or improve an existing WORKFLOW.md");
|
|
885
|
+
lines.push("- Validate that a WORKFLOW.md is correctly structured");
|
|
886
|
+
lines.push("");
|
|
887
|
+
lines.push("## Prerequisites");
|
|
888
|
+
lines.push("");
|
|
889
|
+
lines.push("- `WORKFLOW.md` is the repository policy and config source");
|
|
890
|
+
lines.push("- `references/README.md` must exist beside this skill");
|
|
891
|
+
lines.push("- `gh` CLI must be authenticated");
|
|
892
|
+
lines.push("");
|
|
893
|
+
lines.push("## Repository Validation Guidance");
|
|
893
894
|
lines.push("");
|
|
894
|
-
lines.push("detected_environment:");
|
|
895
895
|
lines.push(
|
|
896
|
-
`
|
|
896
|
+
"Carry the detected repository validation posture into any new or refined `WORKFLOW.md` instead of falling back to generic instructions."
|
|
897
897
|
);
|
|
898
|
+
for (const line of buildRepositoryValidationGuidance(
|
|
899
|
+
ctx.detectedEnvironment
|
|
900
|
+
)) {
|
|
901
|
+
lines.push(`- ${line}`);
|
|
902
|
+
}
|
|
903
|
+
lines.push("");
|
|
904
|
+
lines.push("## Mode Detection");
|
|
905
|
+
lines.push("");
|
|
906
|
+
lines.push("Check if `WORKFLOW.md` exists in the current directory:");
|
|
907
|
+
lines.push("- **Not found** \u2192 enter **Design Mode** (create from scratch)");
|
|
908
|
+
lines.push("- **Found** \u2192 ask user: refine existing or validate only?");
|
|
909
|
+
lines.push(" - Refine \u2192 enter **Refine Mode**");
|
|
910
|
+
lines.push(" - Validate \u2192 enter **Validate Mode**");
|
|
911
|
+
lines.push("");
|
|
912
|
+
lines.push("## Design Mode");
|
|
913
|
+
lines.push("");
|
|
898
914
|
lines.push(
|
|
899
|
-
`
|
|
915
|
+
"1. Read `WORKFLOW.md` if it exists and extract repository conventions:"
|
|
900
916
|
);
|
|
917
|
+
lines.push(" - test / lint / build commands from the prompt body");
|
|
918
|
+
lines.push(" - lifecycle states from front matter");
|
|
901
919
|
lines.push(
|
|
902
|
-
`
|
|
920
|
+
"2. Read `references/README.md` for the available reference files."
|
|
903
921
|
);
|
|
922
|
+
lines.push('3. Ask the user: "What should this orchestration accomplish?"');
|
|
923
|
+
lines.push(" - implement (default) \u2014 write features / fix bugs");
|
|
924
|
+
lines.push(" - review \u2014 review PRs and leave comments");
|
|
925
|
+
lines.push(" - maintain \u2014 dependency bumps, chores, hygiene");
|
|
926
|
+
lines.push(" - custom \u2014 describe in their own words");
|
|
904
927
|
lines.push(
|
|
905
|
-
`
|
|
928
|
+
"4. Pick the matching `references/workflow-posture-*.md` file(s); compose multiple files when the intent spans categories."
|
|
906
929
|
);
|
|
930
|
+
lines.push("5. Ask these setup questions:");
|
|
931
|
+
lines.push(" - Which status columns should be **active** (agent works)?");
|
|
932
|
+
lines.push(" - Which should be **wait** (agent pauses for human)?");
|
|
933
|
+
lines.push(" - Which should be **terminal** (agent stops)?");
|
|
934
|
+
lines.push(" - What runtime is being used? (codex / claude-code / custom)");
|
|
935
|
+
lines.push(" - Any custom hooks needed? (after_create, before_run, etc.)");
|
|
936
|
+
lines.push("6. Generate WORKFLOW.md:");
|
|
937
|
+
lines.push(" - front matter from `references/workflow-schema.md`");
|
|
907
938
|
lines.push(
|
|
908
|
-
|
|
939
|
+
" - prompt body from the selected posture file(s), adapted to actual repository commands"
|
|
909
940
|
);
|
|
941
|
+
lines.push("7. Show a diff or preview and confirm with the user.");
|
|
942
|
+
lines.push("8. Validate via `gh-symphony workflow validate`.");
|
|
943
|
+
lines.push("");
|
|
944
|
+
lines.push("## Refine Mode");
|
|
945
|
+
lines.push("");
|
|
946
|
+
lines.push("1. Read the current `WORKFLOW.md`");
|
|
910
947
|
lines.push(
|
|
911
|
-
`
|
|
948
|
+
"2. Read `references/README.md` and select the relevant posture file(s)"
|
|
912
949
|
);
|
|
913
|
-
lines.push(` monorepo: ${context.detected_environment.monorepo}`);
|
|
914
|
-
lines.push(" existingSkills:");
|
|
915
|
-
if (context.detected_environment.existingSkills.length === 0) {
|
|
916
|
-
lines.push(" []");
|
|
917
|
-
} else {
|
|
918
|
-
for (const skill of context.detected_environment.existingSkills) {
|
|
919
|
-
lines.push(` - ${yamlQuote(skill)}`);
|
|
920
|
-
}
|
|
921
|
-
}
|
|
922
|
-
lines.push("");
|
|
923
|
-
lines.push("runtime:");
|
|
924
|
-
lines.push(` agent: ${yamlQuote(context.runtime.agent)}`);
|
|
925
|
-
lines.push(` agent_command: ${yamlQuote(context.runtime.agent_command)}`);
|
|
926
|
-
return lines.join("\n") + "\n";
|
|
927
|
-
}
|
|
928
|
-
function buildContextYaml(params) {
|
|
929
|
-
const columns = params.statusField.options.map((option) => {
|
|
930
|
-
const roleMapping = inferStateRole(option.name);
|
|
931
|
-
return {
|
|
932
|
-
id: option.id,
|
|
933
|
-
name: option.name,
|
|
934
|
-
color: option.color,
|
|
935
|
-
inferred_role: roleMapping.role,
|
|
936
|
-
confidence: roleMapping.confidence
|
|
937
|
-
};
|
|
938
|
-
});
|
|
939
|
-
const textFields = params.projectDetail.textFields.map((field) => ({
|
|
940
|
-
id: field.id,
|
|
941
|
-
name: field.name,
|
|
942
|
-
data_type: field.dataType
|
|
943
|
-
}));
|
|
944
|
-
const repositories = params.projectDetail.linkedRepositories.map((repo) => ({
|
|
945
|
-
owner: repo.owner,
|
|
946
|
-
name: repo.name,
|
|
947
|
-
clone_url: repo.cloneUrl
|
|
948
|
-
}));
|
|
949
|
-
return {
|
|
950
|
-
schema_version: 1,
|
|
951
|
-
collected_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
952
|
-
project: {
|
|
953
|
-
id: params.projectDetail.id,
|
|
954
|
-
title: params.projectDetail.title,
|
|
955
|
-
url: params.projectDetail.url
|
|
956
|
-
},
|
|
957
|
-
status_field: {
|
|
958
|
-
id: params.statusField.id,
|
|
959
|
-
name: params.statusField.name,
|
|
960
|
-
columns
|
|
961
|
-
},
|
|
962
|
-
text_fields: textFields,
|
|
963
|
-
repositories,
|
|
964
|
-
detected_environment: params.detectedEnvironment,
|
|
965
|
-
runtime: params.runtime
|
|
966
|
-
};
|
|
967
|
-
}
|
|
968
|
-
|
|
969
|
-
// src/workflow/generate-reference-workflow.ts
|
|
970
|
-
function generateReferenceWorkflow(input) {
|
|
971
|
-
const lines = [];
|
|
972
|
-
lines.push("# Reference WORKFLOW.md \u2014 gh-symphony");
|
|
973
|
-
lines.push("# This file is a reference template for authoring WORKFLOW.md.");
|
|
974
950
|
lines.push(
|
|
975
|
-
"
|
|
951
|
+
"3. Compare the current prompt body against the selected posture references"
|
|
976
952
|
);
|
|
977
|
-
lines.push("
|
|
953
|
+
lines.push("4. Identify missing or incomplete sections:");
|
|
954
|
+
lines.push(" - Status Map with role annotations");
|
|
955
|
+
lines.push(" - Default Posture / Agent Instructions");
|
|
956
|
+
lines.push(" - Guardrails section");
|
|
957
|
+
lines.push(" - Workpad Template");
|
|
958
|
+
lines.push(" - Step 0 routing logic");
|
|
959
|
+
lines.push("5. Propose improvements and apply with user confirmation");
|
|
960
|
+
lines.push("6. Validate the refined file");
|
|
978
961
|
lines.push("");
|
|
979
|
-
lines.push("
|
|
962
|
+
lines.push("## Validate Mode");
|
|
980
963
|
lines.push("");
|
|
981
|
-
lines.push("
|
|
964
|
+
lines.push("Check the WORKFLOW.md for:");
|
|
965
|
+
lines.push("- Front matter is valid YAML");
|
|
982
966
|
lines.push(
|
|
983
|
-
"
|
|
967
|
+
"- Required fields are present (see `references/workflow-schema.md`)"
|
|
984
968
|
);
|
|
985
|
-
lines.push(
|
|
986
|
-
|
|
987
|
-
lines.push(" kind: github-project");
|
|
988
|
-
lines.push(` project_id: ${input.projectId}`);
|
|
989
|
-
lines.push(" state_field: Status");
|
|
990
|
-
lines.push(...buildReferencePriorityLines(input.priority));
|
|
991
|
-
lines.push("");
|
|
992
|
-
const activeColumns = input.statusColumns.filter((c) => c.role === "active");
|
|
993
|
-
const waitColumns = input.statusColumns.filter((c) => c.role === "wait");
|
|
994
|
-
const terminalColumns = input.statusColumns.filter(
|
|
995
|
-
(c) => c.role === "terminal"
|
|
969
|
+
lines.push(
|
|
970
|
+
"- Template variables use only supported names (see `references/workflow-schema.md`)"
|
|
996
971
|
);
|
|
997
|
-
|
|
998
|
-
const planningStates = input.lifecycle?.planningStates ?? blockerCheckStates;
|
|
999
|
-
if (activeColumns.length > 0) {
|
|
1000
|
-
lines.push(" active_states:");
|
|
1001
|
-
for (const col of activeColumns) {
|
|
1002
|
-
lines.push(` - ${col.name}`);
|
|
1003
|
-
}
|
|
1004
|
-
} else {
|
|
1005
|
-
lines.push(" active_states: [{active column names}]");
|
|
1006
|
-
}
|
|
1007
|
-
if (terminalColumns.length > 0) {
|
|
1008
|
-
lines.push(" terminal_states:");
|
|
1009
|
-
for (const col of terminalColumns) {
|
|
1010
|
-
lines.push(` - ${col.name}`);
|
|
1011
|
-
}
|
|
1012
|
-
} else {
|
|
1013
|
-
lines.push(" terminal_states: [{terminal column names}]");
|
|
1014
|
-
}
|
|
972
|
+
lines.push("- Status Map matches the lifecycle configuration");
|
|
1015
973
|
lines.push(
|
|
1016
|
-
|
|
974
|
+
"- No unsupported double-brace variable patterns (only the 8 listed below are valid)"
|
|
1017
975
|
);
|
|
1018
|
-
lines.push(...buildReferenceStringList("planning_states", planningStates));
|
|
1019
|
-
lines.push("");
|
|
1020
|
-
lines.push("# Linear tracker example:");
|
|
1021
|
-
lines.push("# tracker:");
|
|
1022
|
-
lines.push("# kind: linear");
|
|
1023
|
-
lines.push("# endpoint: https://api.linear.app/graphql");
|
|
1024
|
-
lines.push("# api_key: $LINEAR_API_KEY");
|
|
1025
|
-
lines.push("# project_slug: symphony-0c79b11b75ea");
|
|
1026
|
-
lines.push("# active_states:");
|
|
1027
|
-
lines.push("# - Todo");
|
|
1028
|
-
lines.push("# - In Progress");
|
|
1029
|
-
lines.push("# terminal_states:");
|
|
1030
|
-
lines.push("# - Done");
|
|
1031
|
-
lines.push("# - Canceled");
|
|
1032
|
-
lines.push("# - Duplicate");
|
|
1033
976
|
lines.push(
|
|
1034
|
-
"
|
|
977
|
+
"- Prompt body posture is consistent with the selected `references/workflow-posture-*.md` file(s)"
|
|
1035
978
|
);
|
|
1036
|
-
lines.push("# a Linear webhook setup command.");
|
|
1037
979
|
lines.push("");
|
|
980
|
+
lines.push("## Supported Front Matter Fields");
|
|
981
|
+
lines.push("");
|
|
982
|
+
lines.push("```yaml");
|
|
983
|
+
lines.push("tracker:");
|
|
984
|
+
lines.push(" kind: github-project");
|
|
985
|
+
lines.push(" project_id: PVT_xxx");
|
|
986
|
+
lines.push(" state_field: Status");
|
|
987
|
+
lines.push(" active_states: [Todo, In Progress]");
|
|
988
|
+
lines.push(" terminal_states: [Done, Cancelled]");
|
|
989
|
+
lines.push(" blocker_check_states: [Blocked]");
|
|
1038
990
|
lines.push("polling:");
|
|
1039
991
|
lines.push(" interval_ms: 30000");
|
|
1040
|
-
lines.push("");
|
|
1041
992
|
lines.push("workspace:");
|
|
1042
993
|
lines.push(" root: .runtime/symphony-workspaces");
|
|
1043
|
-
lines.push("");
|
|
1044
994
|
lines.push("hooks:");
|
|
1045
|
-
lines.push(
|
|
1046
|
-
|
|
1047
|
-
);
|
|
995
|
+
lines.push(" after_create: |");
|
|
996
|
+
lines.push(" git clone --depth 1 https://github.com/owner/repo .");
|
|
1048
997
|
lines.push(" before_run: null");
|
|
1049
998
|
lines.push(" after_run: null");
|
|
1050
999
|
lines.push(" before_remove: null");
|
|
1051
1000
|
lines.push(" timeout_ms: 60000");
|
|
1052
|
-
lines.push("");
|
|
1053
1001
|
lines.push("agent:");
|
|
1054
1002
|
lines.push(" max_concurrent_agents: 10");
|
|
1055
1003
|
lines.push(" max_retry_backoff_ms: 30000");
|
|
1056
1004
|
lines.push(" retry_base_delay_ms: 10000");
|
|
1057
1005
|
lines.push(" max_turns: 20");
|
|
1006
|
+
lines.push("codex:");
|
|
1007
|
+
lines.push(" command: codex app-server");
|
|
1008
|
+
lines.push(" read_timeout_ms: 5000");
|
|
1009
|
+
lines.push(" turn_timeout_ms: 3600000");
|
|
1010
|
+
lines.push(" stall_timeout_ms: 300000");
|
|
1011
|
+
lines.push("```");
|
|
1058
1012
|
lines.push("");
|
|
1059
|
-
lines.push(
|
|
1060
|
-
lines.push("");
|
|
1061
|
-
lines.push("---");
|
|
1013
|
+
lines.push("## Supported Template Variables");
|
|
1062
1014
|
lines.push("");
|
|
1063
|
-
lines.push("
|
|
1064
|
-
lines.push(
|
|
1065
|
-
"# GitHub Project adaptation of the Elixir Symphony reference prompt."
|
|
1066
|
-
);
|
|
1015
|
+
lines.push("Use these in the WORKFLOW.md prompt body (double-brace syntax):");
|
|
1067
1016
|
lines.push("");
|
|
1068
|
-
lines.push("
|
|
1069
|
-
lines.push("");
|
|
1070
|
-
lines.push("|
|
|
1071
|
-
lines.push("|
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
lines.push("");
|
|
1079
|
-
lines.push("**Wait States (awaiting PR review):**");
|
|
1080
|
-
for (const col of waitColumns) {
|
|
1081
|
-
lines.push(
|
|
1082
|
-
`- **${col.name}**: PR created. Awaiting human review. Agent is idle.`
|
|
1083
|
-
);
|
|
1084
|
-
}
|
|
1085
|
-
}
|
|
1086
|
-
lines.push("");
|
|
1087
|
-
lines.push("## Repository Validation Guidance");
|
|
1088
|
-
lines.push("");
|
|
1089
|
-
for (const line of buildRepositoryValidationGuidance(
|
|
1090
|
-
input.detectedEnvironment
|
|
1091
|
-
)) {
|
|
1092
|
-
lines.push(`- ${line}`);
|
|
1093
|
-
}
|
|
1094
|
-
lines.push("");
|
|
1095
|
-
lines.push("## Default Posture");
|
|
1017
|
+
lines.push("| Variable | Description |");
|
|
1018
|
+
lines.push("|----------|-------------|");
|
|
1019
|
+
lines.push("| `issue.identifier` | e.g. `acme/platform#42` |");
|
|
1020
|
+
lines.push("| `issue.title` | Issue title |");
|
|
1021
|
+
lines.push("| `issue.state` | Current tracker state |");
|
|
1022
|
+
lines.push("| `issue.description` | Issue body |");
|
|
1023
|
+
lines.push("| `issue.url` | Issue URL |");
|
|
1024
|
+
lines.push("| `issue.repository` | `owner/name` |");
|
|
1025
|
+
lines.push("| `issue.number` | Issue number |");
|
|
1026
|
+
lines.push("| `attempt` | Retry attempt number (null on first run) |");
|
|
1096
1027
|
lines.push("");
|
|
1097
1028
|
lines.push(
|
|
1098
|
-
"
|
|
1099
|
-
);
|
|
1100
|
-
lines.push(
|
|
1101
|
-
"2. Exit early only for genuine blockers (missing required credentials or secrets)."
|
|
1102
|
-
);
|
|
1103
|
-
lines.push(
|
|
1104
|
-
'3. In your final message, report only completed work and blockers. Do not include "next steps".'
|
|
1105
|
-
);
|
|
1106
|
-
lines.push(
|
|
1107
|
-
"4. Do not modify the issue body for planning or progress-tracking purposes."
|
|
1108
|
-
);
|
|
1109
|
-
lines.push(
|
|
1110
|
-
"5. If the issue is in a terminal state, do nothing and exit immediately."
|
|
1111
|
-
);
|
|
1112
|
-
lines.push(
|
|
1113
|
-
"6. If you discover out-of-scope improvements, open a separate issue rather than expanding the current scope."
|
|
1114
|
-
);
|
|
1115
|
-
lines.push(
|
|
1116
|
-
"7. Keep all commits as logical units and follow conventional commit format."
|
|
1117
|
-
);
|
|
1118
|
-
lines.push("8. Do not make commits that break existing tests.");
|
|
1119
|
-
lines.push("9. Verify all existing tests pass before creating a PR.");
|
|
1120
|
-
lines.push("10. Create a workpad as an issue comment to track progress.");
|
|
1121
|
-
lines.push("11. Use the gh-project skill to manage issue status.");
|
|
1122
|
-
lines.push(
|
|
1123
|
-
"12. When a blocker is found, record it in an issue comment and transition the status appropriately."
|
|
1124
|
-
);
|
|
1125
|
-
lines.push(
|
|
1126
|
-
"13. Once your work is complete and the PR is merged, transition the issue to the Done state."
|
|
1029
|
+
"**Important**: Only these 8 variables are supported. Using any other variable"
|
|
1127
1030
|
);
|
|
1031
|
+
lines.push("will cause a runtime error (strict mode validation).");
|
|
1128
1032
|
lines.push("");
|
|
1129
1033
|
lines.push("## Related Skills");
|
|
1130
1034
|
lines.push("");
|
|
1131
1035
|
lines.push(
|
|
1132
|
-
"-
|
|
1036
|
+
"- `/gh-project` \u2014 interact with GitHub Project v2 board (status transitions, workpad comments)"
|
|
1133
1037
|
);
|
|
1134
1038
|
lines.push(
|
|
1135
|
-
"-
|
|
1039
|
+
"- `/commit` \u2014 produce clean, logical commits during implementation"
|
|
1136
1040
|
);
|
|
1137
|
-
lines.push("-
|
|
1138
|
-
lines.push("-
|
|
1139
|
-
lines.push("-
|
|
1041
|
+
lines.push("- `/push` \u2014 keep remote branch current and publish updates");
|
|
1042
|
+
lines.push("- `/pull` \u2014 sync branch with latest origin/main before handoff");
|
|
1043
|
+
lines.push("- `/land` \u2014 merge approved PR and transition issue to Done");
|
|
1044
|
+
return renderSkillDocument({
|
|
1045
|
+
name: "gh-symphony",
|
|
1046
|
+
description: "Design, refine, and validate repository WORKFLOW.md files for GitHub Symphony projects.",
|
|
1047
|
+
bodyLines: lines
|
|
1048
|
+
});
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
// src/skills/templates/gh-project.ts
|
|
1052
|
+
function generateGhProjectSkill(ctx) {
|
|
1053
|
+
const lines = [];
|
|
1054
|
+
lines.push("# /gh-project \u2014 GitHub Project v2 Status Management");
|
|
1140
1055
|
lines.push("");
|
|
1141
|
-
lines.push("##
|
|
1056
|
+
lines.push("## Purpose");
|
|
1142
1057
|
lines.push("");
|
|
1143
1058
|
lines.push(
|
|
1144
|
-
"
|
|
1059
|
+
"Interact with the GitHub Project v2 board to manage issue status,"
|
|
1145
1060
|
);
|
|
1061
|
+
lines.push("create workpad comments, and handle follow-up issues.");
|
|
1146
1062
|
lines.push("");
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
}
|
|
1151
|
-
if (waitColumns.length > 0) {
|
|
1152
|
-
const waitNames = waitColumns.map((c) => c.name).join(", ");
|
|
1153
|
-
lines.push(`- **${waitNames}** \u2192 Go to Step 3 (handle awaiting review).`);
|
|
1154
|
-
}
|
|
1155
|
-
if (activeColumns.length > 0) {
|
|
1156
|
-
const activeNames = activeColumns.map((c) => c.name).join(", ");
|
|
1157
|
-
lines.push(
|
|
1158
|
-
`- **${activeNames}** \u2192 Go to Step 1 (start or continue execution).`
|
|
1159
|
-
);
|
|
1160
|
-
}
|
|
1063
|
+
lines.push("## Prerequisites");
|
|
1064
|
+
lines.push("");
|
|
1065
|
+
lines.push("- `gh` CLI is authenticated (`gh auth status`)");
|
|
1161
1066
|
lines.push(
|
|
1162
|
-
"-
|
|
1067
|
+
"- This generated skill contains the status field and option IDs below"
|
|
1163
1068
|
);
|
|
1164
1069
|
lines.push("");
|
|
1165
|
-
lines.push("##
|
|
1070
|
+
lines.push("## Column ID Quick Reference");
|
|
1166
1071
|
lines.push("");
|
|
1167
|
-
lines.push(
|
|
1168
|
-
"1. Read the issue body and comments to understand current progress."
|
|
1169
|
-
);
|
|
1170
|
-
lines.push(
|
|
1171
|
-
"2. If an existing workpad comment is found, continue from it; otherwise create a new workpad."
|
|
1172
|
-
);
|
|
1173
|
-
lines.push(
|
|
1174
|
-
"3. See the 'Workpad Template' section below for the workpad format."
|
|
1175
|
-
);
|
|
1176
|
-
lines.push(
|
|
1177
|
-
"4. If no branch exists, create a feature branch based on `{issue.repository}`."
|
|
1178
|
-
);
|
|
1179
|
-
lines.push("5. Proceed to Step 2.");
|
|
1072
|
+
lines.push(`Status Field ID: \`${ctx.statusFieldId}\``);
|
|
1180
1073
|
lines.push("");
|
|
1181
|
-
lines.push("
|
|
1074
|
+
lines.push("| Column Name | Role | Option ID |");
|
|
1075
|
+
lines.push("|-------------|------|-----------|");
|
|
1076
|
+
for (const col of ctx.statusColumns) {
|
|
1077
|
+
const role = col.role ?? "unknown";
|
|
1078
|
+
lines.push(`| ${col.name} | ${role} | \`${col.id}\` |`);
|
|
1079
|
+
}
|
|
1182
1080
|
lines.push("");
|
|
1183
|
-
lines.push("
|
|
1184
|
-
lines.push(
|
|
1185
|
-
"2. Commit changes in logical units (conventional commit format)."
|
|
1186
|
-
);
|
|
1187
|
-
lines.push("3. Verify all existing tests pass.");
|
|
1188
|
-
lines.push("4. Write tests for new functionality.");
|
|
1189
|
-
lines.push("5. Once all Completion Bar criteria are met, create a PR.");
|
|
1190
|
-
lines.push(
|
|
1191
|
-
"6. After creating the PR, transition the issue status to the Human Review state."
|
|
1192
|
-
);
|
|
1193
|
-
lines.push("7. Proceed to Step 3.");
|
|
1081
|
+
lines.push("## Operations");
|
|
1194
1082
|
lines.push("");
|
|
1195
|
-
lines.push("
|
|
1083
|
+
lines.push("### Change Issue Status");
|
|
1196
1084
|
lines.push("");
|
|
1197
|
-
lines.push("1. If a PR already exists, check for review comments.");
|
|
1198
|
-
lines.push(
|
|
1199
|
-
"2. If no review comments are present, remain in the waiting state."
|
|
1200
|
-
);
|
|
1201
1085
|
lines.push(
|
|
1202
|
-
"
|
|
1086
|
+
"Use `gh project item-edit` with the field ID and option ID from the table above:"
|
|
1203
1087
|
);
|
|
1204
|
-
lines.push("4. If review changes are requested, proceed to Step 4.");
|
|
1205
|
-
lines.push("");
|
|
1206
|
-
lines.push("## Step 4: Rework handling");
|
|
1207
1088
|
lines.push("");
|
|
1089
|
+
lines.push("```bash");
|
|
1090
|
+
lines.push("# Get the project item ID for an issue");
|
|
1208
1091
|
lines.push(
|
|
1209
|
-
"
|
|
1092
|
+
"gh project item-list <project-number> --owner <owner> --format json \\"
|
|
1210
1093
|
);
|
|
1211
1094
|
lines.push(
|
|
1212
|
-
"
|
|
1095
|
+
" | jq '.items[] | select(.content.number == <issue-number>) | .id'"
|
|
1213
1096
|
);
|
|
1214
|
-
lines.push("3. After implementing changes, commit and update the PR.");
|
|
1215
|
-
lines.push("4. Transition the issue status back to the Human Review state.");
|
|
1216
|
-
lines.push("5. Return to Step 3.");
|
|
1217
1097
|
lines.push("");
|
|
1218
|
-
lines.push("
|
|
1098
|
+
lines.push("# Update the status field");
|
|
1099
|
+
lines.push(`gh project item-edit \\`);
|
|
1100
|
+
lines.push(` --project-id ${ctx.projectId} \\`);
|
|
1101
|
+
lines.push(` --id <item-id> \\`);
|
|
1102
|
+
lines.push(` --field-id ${ctx.statusFieldId} \\`);
|
|
1103
|
+
lines.push(` --single-select-option-id <option-id-from-table-above>`);
|
|
1104
|
+
lines.push("```");
|
|
1219
1105
|
lines.push("");
|
|
1220
|
-
lines.push("
|
|
1106
|
+
lines.push("### Create Workpad Comment");
|
|
1221
1107
|
lines.push("");
|
|
1108
|
+
lines.push("```bash");
|
|
1222
1109
|
lines.push(
|
|
1223
|
-
|
|
1224
|
-
);
|
|
1225
|
-
lines.push(
|
|
1226
|
-
"2. **Triage by priority**: Handle blocking comments before non-blocking ones."
|
|
1227
|
-
);
|
|
1228
|
-
lines.push(
|
|
1229
|
-
"3. **Implement changes**: Make the code changes corresponding to each comment."
|
|
1230
|
-
);
|
|
1231
|
-
lines.push(
|
|
1232
|
-
"4. **Reply to comments**: Respond to each review comment with a description of what was done."
|
|
1110
|
+
'gh issue comment <issue-number> --repo <owner>/<repo> --body "## Workpad\\n\\n### Plan\\n- [ ] Task 1"'
|
|
1233
1111
|
);
|
|
1112
|
+
lines.push("```");
|
|
1113
|
+
lines.push("");
|
|
1114
|
+
lines.push("### Update Existing Comment");
|
|
1115
|
+
lines.push("");
|
|
1116
|
+
lines.push("```bash");
|
|
1234
1117
|
lines.push(
|
|
1235
|
-
"
|
|
1118
|
+
"gh api -X PATCH /repos/<owner>/<repo>/issues/comments/<comment-id> \\"
|
|
1236
1119
|
);
|
|
1237
|
-
lines.push("
|
|
1120
|
+
lines.push(' -f body="## Workpad\\n\\n### Plan\\n- [x] Task 1 (done)"');
|
|
1121
|
+
lines.push("```");
|
|
1238
1122
|
lines.push("");
|
|
1239
|
-
lines.push("
|
|
1123
|
+
lines.push("### Create Follow-up Issue");
|
|
1240
1124
|
lines.push("");
|
|
1241
|
-
lines.push("
|
|
1125
|
+
lines.push("```bash");
|
|
1126
|
+
lines.push("gh issue create --repo <owner>/<repo> \\");
|
|
1127
|
+
lines.push(' --title "Follow-up: <title>" \\');
|
|
1128
|
+
lines.push(' --body "<description>" \\');
|
|
1129
|
+
lines.push(' --label "backlog"');
|
|
1130
|
+
lines.push("```");
|
|
1131
|
+
lines.push("");
|
|
1132
|
+
lines.push("### Add Label");
|
|
1242
1133
|
lines.push("");
|
|
1134
|
+
lines.push("```bash");
|
|
1243
1135
|
lines.push(
|
|
1244
|
-
|
|
1136
|
+
'gh issue edit <issue-number> --repo <owner>/<repo> --add-label "<label>"'
|
|
1245
1137
|
);
|
|
1246
|
-
lines.push("
|
|
1247
|
-
lines.push("- [ ] Tests are written for new functionality.");
|
|
1248
|
-
lines.push("- [ ] Code style follows project conventions.");
|
|
1249
|
-
lines.push("- [ ] The PR description clearly explains the changes.");
|
|
1250
|
-
lines.push("- [ ] Related documentation is updated (if applicable).");
|
|
1138
|
+
lines.push("```");
|
|
1251
1139
|
lines.push("");
|
|
1252
|
-
lines.push("##
|
|
1140
|
+
lines.push("## Rules");
|
|
1253
1141
|
lines.push("");
|
|
1254
|
-
lines.push("- **Scope**: Never make changes outside the scope of the issue.");
|
|
1255
1142
|
lines.push(
|
|
1256
|
-
"-
|
|
1143
|
+
"- Always follow the WORKFLOW.md status map flow for state transitions"
|
|
1257
1144
|
);
|
|
1258
1145
|
lines.push(
|
|
1259
|
-
"-
|
|
1146
|
+
"- Before transitioning to a terminal state, verify the Completion Bar is satisfied:"
|
|
1260
1147
|
);
|
|
1261
|
-
lines.push("-
|
|
1148
|
+
lines.push(" - All acceptance criteria checked");
|
|
1149
|
+
lines.push(" - All tests passing");
|
|
1150
|
+
lines.push(" - PR merged (if applicable)");
|
|
1262
1151
|
lines.push(
|
|
1263
|
-
"-
|
|
1152
|
+
"- Use the Column ID Quick Reference table above for all status transitions"
|
|
1264
1153
|
);
|
|
1265
1154
|
lines.push(
|
|
1266
|
-
"-
|
|
1155
|
+
"- Do not transition issues to terminal states without explicit completion verification"
|
|
1267
1156
|
);
|
|
1157
|
+
return renderSkillDocument({
|
|
1158
|
+
name: "gh-project",
|
|
1159
|
+
description: "Manage GitHub Project v2 issue states, workpad comments, and related follow-up actions.",
|
|
1160
|
+
bodyLines: lines
|
|
1161
|
+
});
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
// src/skills/templates/commit.ts
|
|
1165
|
+
function generateCommitSkill(_ctx) {
|
|
1166
|
+
const lines = [];
|
|
1167
|
+
lines.push("# /commit \u2014 Clean Commit Workflow");
|
|
1268
1168
|
lines.push("");
|
|
1269
|
-
lines.push("##
|
|
1169
|
+
lines.push("## Trigger");
|
|
1270
1170
|
lines.push("");
|
|
1271
|
-
lines.push("
|
|
1171
|
+
lines.push("Use this skill when creating commits during implementation.");
|
|
1272
1172
|
lines.push("");
|
|
1273
|
-
lines.push("
|
|
1274
|
-
lines.push("## Workpad \u2014 {issue.identifier}");
|
|
1173
|
+
lines.push("## Rules");
|
|
1275
1174
|
lines.push("");
|
|
1276
|
-
lines.push("
|
|
1277
|
-
lines.push("
|
|
1278
|
-
lines.push("
|
|
1279
|
-
lines.push("");
|
|
1280
|
-
lines.push("### Plan");
|
|
1175
|
+
lines.push("- Commit in logical units \u2014 one concern per commit");
|
|
1176
|
+
lines.push("- Never commit a broken intermediate state (tests must pass)");
|
|
1177
|
+
lines.push("- Never commit temporary debug code or commented-out blocks");
|
|
1178
|
+
lines.push("- Run tests before every commit");
|
|
1281
1179
|
lines.push("");
|
|
1282
|
-
lines.push("
|
|
1283
|
-
lines.push("- [ ] {task 2}");
|
|
1180
|
+
lines.push("## Format");
|
|
1284
1181
|
lines.push("");
|
|
1285
|
-
lines.push("
|
|
1182
|
+
lines.push("Use Conventional Commit format:");
|
|
1286
1183
|
lines.push("");
|
|
1287
|
-
lines.push("
|
|
1184
|
+
lines.push("```");
|
|
1185
|
+
lines.push("<type>(<scope>): <description>");
|
|
1288
1186
|
lines.push("");
|
|
1289
|
-
lines.push("
|
|
1187
|
+
lines.push("[optional body \u2014 explain WHY, not WHAT, 72 chars/line]");
|
|
1290
1188
|
lines.push("");
|
|
1291
|
-
lines.push("
|
|
1189
|
+
lines.push("[optional footer: Closes #N]");
|
|
1292
1190
|
lines.push("```");
|
|
1293
1191
|
lines.push("");
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
function buildReferenceStringList(key, values) {
|
|
1297
|
-
if (values.length === 0) {
|
|
1298
|
-
return [` ${key}: []`];
|
|
1299
|
-
}
|
|
1300
|
-
return [` ${key}:`, ...values.map((value) => ` - ${value}`)];
|
|
1301
|
-
}
|
|
1302
|
-
function buildReferencePriorityLines(priority) {
|
|
1303
|
-
const lines = [];
|
|
1304
|
-
if (priority?.source === "project-field" || priority?.source === "labels") {
|
|
1305
|
-
lines.push(
|
|
1306
|
-
" # Priority is explicit. Numbers below are editable policy (lower = higher priority)."
|
|
1307
|
-
);
|
|
1308
|
-
} else {
|
|
1309
|
-
lines.push(
|
|
1310
|
-
" # Priority dispatch is disabled until an operator chooses one explicit source."
|
|
1311
|
-
);
|
|
1312
|
-
}
|
|
1192
|
+
lines.push("**Types**: `feat`, `fix`, `refactor`, `test`, `docs`, `chore`");
|
|
1193
|
+
lines.push("");
|
|
1313
1194
|
lines.push(
|
|
1314
|
-
"
|
|
1195
|
+
"**Description**: imperative mood, 50 chars max, no period at end"
|
|
1315
1196
|
);
|
|
1316
|
-
if (priority?.source === "project-field") {
|
|
1317
|
-
lines.push(" priority:");
|
|
1318
|
-
lines.push(" source: project-field");
|
|
1319
|
-
lines.push(` field: ${JSON.stringify(priority.field)}`);
|
|
1320
|
-
lines.push(" values:");
|
|
1321
|
-
for (const [name, value] of Object.entries(priority.values)) {
|
|
1322
|
-
lines.push(` ${JSON.stringify(name)}: ${value}`);
|
|
1323
|
-
}
|
|
1324
|
-
return lines;
|
|
1325
|
-
}
|
|
1326
|
-
if (priority?.source === "labels") {
|
|
1327
|
-
lines.push(" priority:");
|
|
1328
|
-
lines.push(" source: labels");
|
|
1329
|
-
lines.push(" labels:");
|
|
1330
|
-
for (const [name, value] of Object.entries(priority.labels)) {
|
|
1331
|
-
lines.push(` ${JSON.stringify(name)}: ${value}`);
|
|
1332
|
-
}
|
|
1333
|
-
return lines;
|
|
1334
|
-
}
|
|
1335
|
-
lines.push(" priority:");
|
|
1336
|
-
lines.push(" source: disabled");
|
|
1337
1197
|
lines.push("");
|
|
1338
|
-
lines.push("
|
|
1339
|
-
lines.push(" # priority:");
|
|
1340
|
-
lines.push(" # source: project-field");
|
|
1341
|
-
lines.push(" # field: Priority");
|
|
1342
|
-
lines.push(" # values:");
|
|
1343
|
-
lines.push(" # Urgent: 0");
|
|
1344
|
-
lines.push(" # High: 1");
|
|
1198
|
+
lines.push("## Examples");
|
|
1345
1199
|
lines.push("");
|
|
1346
|
-
lines.push("
|
|
1347
|
-
lines.push("
|
|
1348
|
-
lines.push("
|
|
1349
|
-
lines.push("
|
|
1350
|
-
lines.push("
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
case "active":
|
|
1357
|
-
return "Agent starts work immediately. Creates workpad and proceeds with implementation.";
|
|
1358
|
-
case "wait":
|
|
1359
|
-
return "PR created. Awaiting human review. Agent is idle.";
|
|
1360
|
-
case "terminal":
|
|
1361
|
-
return "Completed state. Agent exits.";
|
|
1362
|
-
case null:
|
|
1363
|
-
return "Role unset. Must be explicitly configured in WORKFLOW.md.";
|
|
1364
|
-
}
|
|
1365
|
-
}
|
|
1366
|
-
|
|
1367
|
-
// src/skills/skill-writer.ts
|
|
1368
|
-
import { mkdir as mkdir2, readFile as readFile2, rename, writeFile as writeFile2 } from "fs/promises";
|
|
1369
|
-
import { dirname as dirname2, join as join2 } from "path";
|
|
1370
|
-
function normalizeRuntimeForSkills(runtime) {
|
|
1371
|
-
if (isClaudeRuntime(runtime)) {
|
|
1372
|
-
return "claude-code";
|
|
1373
|
-
}
|
|
1374
|
-
if (isCodexRuntime(runtime)) {
|
|
1375
|
-
return "codex";
|
|
1376
|
-
}
|
|
1377
|
-
return null;
|
|
1378
|
-
}
|
|
1379
|
-
function resolveSkillsDir(repoRoot, runtime) {
|
|
1380
|
-
const normalizedRuntime = normalizeRuntimeForSkills(runtime);
|
|
1381
|
-
if (normalizedRuntime === "claude-code") {
|
|
1382
|
-
return join2(repoRoot, ".claude", "skills");
|
|
1383
|
-
}
|
|
1384
|
-
if (normalizedRuntime === "codex") {
|
|
1385
|
-
return join2(repoRoot, ".codex", "skills");
|
|
1386
|
-
}
|
|
1387
|
-
return null;
|
|
1388
|
-
}
|
|
1389
|
-
function buildSkillFilePlans(repoRoot, runtime, templates, context) {
|
|
1390
|
-
const skillsDir = resolveSkillsDir(repoRoot, runtime);
|
|
1391
|
-
if (!skillsDir) {
|
|
1392
|
-
return { skillsDir: null, files: [] };
|
|
1393
|
-
}
|
|
1394
|
-
return {
|
|
1395
|
-
skillsDir,
|
|
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
|
-
)
|
|
1402
|
-
};
|
|
1403
|
-
}
|
|
1404
|
-
|
|
1405
|
-
// src/skills/templates/document.ts
|
|
1406
|
-
function renderSkillDocument(options) {
|
|
1407
|
-
const { name, description, bodyLines } = options;
|
|
1408
|
-
return [
|
|
1409
|
-
"---",
|
|
1410
|
-
`name: ${name}`,
|
|
1411
|
-
`description: ${description}`,
|
|
1412
|
-
"license: MIT",
|
|
1413
|
-
"metadata:",
|
|
1414
|
-
" author: gh-symphony",
|
|
1415
|
-
' version: "1.0"',
|
|
1416
|
-
' generatedBy: "gh-symphony"',
|
|
1417
|
-
"---",
|
|
1418
|
-
"",
|
|
1419
|
-
...bodyLines
|
|
1420
|
-
].join("\n");
|
|
1200
|
+
lines.push("```");
|
|
1201
|
+
lines.push("feat(auth): add OAuth2 token refresh");
|
|
1202
|
+
lines.push("fix(api): handle null response from upstream");
|
|
1203
|
+
lines.push("test(worker): add retry exhaustion coverage");
|
|
1204
|
+
lines.push("```");
|
|
1205
|
+
return renderSkillDocument({
|
|
1206
|
+
name: "commit",
|
|
1207
|
+
description: "Create clean, logically scoped commits that keep the repository in a shippable state.",
|
|
1208
|
+
bodyLines: lines
|
|
1209
|
+
});
|
|
1421
1210
|
}
|
|
1422
1211
|
|
|
1423
|
-
// src/skills/templates/
|
|
1424
|
-
function
|
|
1212
|
+
// src/skills/templates/push.ts
|
|
1213
|
+
function generatePushSkill(_ctx) {
|
|
1425
1214
|
const lines = [];
|
|
1426
|
-
lines.push("# /
|
|
1215
|
+
lines.push("# /push \u2014 Git Push Workflow");
|
|
1427
1216
|
lines.push("");
|
|
1428
1217
|
lines.push("## Trigger");
|
|
1429
1218
|
lines.push("");
|
|
1430
|
-
lines.push(
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
lines.push("- Validate that a WORKFLOW.md is correctly structured");
|
|
1219
|
+
lines.push(
|
|
1220
|
+
"Use this skill when publishing local commits to the remote branch."
|
|
1221
|
+
);
|
|
1434
1222
|
lines.push("");
|
|
1435
|
-
lines.push("##
|
|
1223
|
+
lines.push("## Flow");
|
|
1224
|
+
lines.push("");
|
|
1225
|
+
lines.push("1. Run local tests and lint \u2014 ensure they pass before pushing");
|
|
1226
|
+
lines.push("2. Push to remote:");
|
|
1227
|
+
lines.push(" ```bash");
|
|
1228
|
+
lines.push(" git push origin <branch> # subsequent pushes");
|
|
1229
|
+
lines.push(" git push -u origin <branch> # first push (sets upstream)");
|
|
1230
|
+
lines.push(" ```");
|
|
1231
|
+
lines.push("3. If push is rejected (non-fast-forward):");
|
|
1232
|
+
lines.push(" - Run `git fetch origin && git merge origin/main`");
|
|
1233
|
+
lines.push(" - Resolve any conflicts");
|
|
1234
|
+
lines.push(" - Re-run tests");
|
|
1235
|
+
lines.push(" - Push again");
|
|
1236
|
+
lines.push("4. Record push result in workpad Notes");
|
|
1237
|
+
lines.push("");
|
|
1238
|
+
lines.push("## Rules");
|
|
1436
1239
|
lines.push("");
|
|
1240
|
+
lines.push("- Never use `--force` (destructive)");
|
|
1437
1241
|
lines.push(
|
|
1438
|
-
|
|
1242
|
+
"- Only use `--force-with-lease` if absolutely necessary \u2014 record the reason in workpad"
|
|
1439
1243
|
);
|
|
1244
|
+
lines.push("- Verify CI starts after push (check GitHub Actions tab)");
|
|
1245
|
+
lines.push("- Do not push directly to `main` or `master`");
|
|
1246
|
+
return renderSkillDocument({
|
|
1247
|
+
name: "push",
|
|
1248
|
+
description: "Publish verified local commits to the remote branch without unsafe force pushes.",
|
|
1249
|
+
bodyLines: lines
|
|
1250
|
+
});
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
// src/skills/templates/pull.ts
|
|
1254
|
+
function generatePullSkill(_ctx) {
|
|
1255
|
+
const lines = [];
|
|
1256
|
+
lines.push("# /pull \u2014 Git Pull / Sync Workflow");
|
|
1257
|
+
lines.push("");
|
|
1258
|
+
lines.push("## Trigger");
|
|
1259
|
+
lines.push("");
|
|
1440
1260
|
lines.push(
|
|
1441
|
-
|
|
1261
|
+
"Use this skill to sync the current branch with the latest `origin/main`"
|
|
1442
1262
|
);
|
|
1443
|
-
lines.push("
|
|
1444
|
-
lines.push("- `gh` CLI must be authenticated");
|
|
1263
|
+
lines.push("before starting work or before creating a PR.");
|
|
1445
1264
|
lines.push("");
|
|
1446
|
-
lines.push("##
|
|
1265
|
+
lines.push("## Flow");
|
|
1447
1266
|
lines.push("");
|
|
1267
|
+
lines.push("1. Fetch latest from remote:");
|
|
1268
|
+
lines.push(" ```bash");
|
|
1269
|
+
lines.push(" git fetch origin");
|
|
1270
|
+
lines.push(" ```");
|
|
1271
|
+
lines.push("2. Merge into current branch:");
|
|
1272
|
+
lines.push(" ```bash");
|
|
1273
|
+
lines.push(" git merge origin/main");
|
|
1274
|
+
lines.push(" ```");
|
|
1275
|
+
lines.push("3. If conflicts arise:");
|
|
1276
|
+
lines.push(" - Resolve each conflict file");
|
|
1277
|
+
lines.push(" - Run tests to confirm nothing broke");
|
|
1448
1278
|
lines.push(
|
|
1449
|
-
"
|
|
1279
|
+
" - Commit the merge: `git commit` (merge commit message is auto-generated)"
|
|
1450
1280
|
);
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
)
|
|
1454
|
-
|
|
1455
|
-
|
|
1281
|
+
lines.push(
|
|
1282
|
+
"4. Re-run tests after merge to confirm the integrated state is clean"
|
|
1283
|
+
);
|
|
1284
|
+
lines.push("5. Record pull skill evidence in workpad Notes:");
|
|
1285
|
+
lines.push(" - merge source (e.g. `origin/main`)");
|
|
1286
|
+
lines.push(" - result: `clean` or `conflicts resolved`");
|
|
1287
|
+
lines.push(" - resulting HEAD short SHA: `git rev-parse --short HEAD`");
|
|
1456
1288
|
lines.push("");
|
|
1457
|
-
lines.push("##
|
|
1289
|
+
lines.push("## Rules");
|
|
1458
1290
|
lines.push("");
|
|
1459
|
-
lines.push("
|
|
1460
|
-
lines.push("-
|
|
1461
|
-
lines.push("-
|
|
1462
|
-
|
|
1463
|
-
|
|
1291
|
+
lines.push("- Always pull before creating a PR");
|
|
1292
|
+
lines.push("- Always pull at the start of a new work session");
|
|
1293
|
+
lines.push("- Record the pull evidence in the workpad before proceeding");
|
|
1294
|
+
return renderSkillDocument({
|
|
1295
|
+
name: "pull",
|
|
1296
|
+
description: "Sync the current branch with the latest remote base before implementation or review handoff.",
|
|
1297
|
+
bodyLines: lines
|
|
1298
|
+
});
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
// src/skills/templates/land.ts
|
|
1302
|
+
function generateLandSkill(_ctx) {
|
|
1303
|
+
const lines = [];
|
|
1304
|
+
lines.push("# /land \u2014 PR Merge Workflow");
|
|
1464
1305
|
lines.push("");
|
|
1465
|
-
lines.push("##
|
|
1306
|
+
lines.push("## Trigger");
|
|
1466
1307
|
lines.push("");
|
|
1467
1308
|
lines.push(
|
|
1468
|
-
"
|
|
1309
|
+
"Use this skill when the issue is in the Merging state (PR approved by human)."
|
|
1469
1310
|
);
|
|
1470
|
-
lines.push(" - test / lint / build commands from the prompt body");
|
|
1471
|
-
lines.push(" - lifecycle states from front matter");
|
|
1472
1311
|
lines.push(
|
|
1473
|
-
"
|
|
1312
|
+
"Do NOT call `gh pr merge` directly \u2014 always go through this flow."
|
|
1474
1313
|
);
|
|
1475
|
-
lines.push(
|
|
1476
|
-
lines.push("
|
|
1477
|
-
lines.push("
|
|
1478
|
-
lines.push("
|
|
1479
|
-
lines.push("
|
|
1314
|
+
lines.push("");
|
|
1315
|
+
lines.push("## Pre-flight Checks");
|
|
1316
|
+
lines.push("");
|
|
1317
|
+
lines.push("Before merging, verify ALL of the following:");
|
|
1318
|
+
lines.push("");
|
|
1319
|
+
lines.push("1. **PR is approved**:");
|
|
1320
|
+
lines.push(" ```bash");
|
|
1480
1321
|
lines.push(
|
|
1481
|
-
|
|
1322
|
+
` gh pr view --json reviews --jq '.reviews[] | select(.state == "APPROVED")'`
|
|
1482
1323
|
);
|
|
1483
|
-
lines.push("
|
|
1484
|
-
lines.push("
|
|
1485
|
-
lines.push("
|
|
1486
|
-
lines.push("
|
|
1487
|
-
lines.push("
|
|
1488
|
-
lines.push("
|
|
1489
|
-
lines.push("
|
|
1490
|
-
lines.push(" - front matter from `references/workflow-schema.md`");
|
|
1324
|
+
lines.push(" ```");
|
|
1325
|
+
lines.push("2. **All CI checks are green**:");
|
|
1326
|
+
lines.push(" ```bash");
|
|
1327
|
+
lines.push(" gh pr checks");
|
|
1328
|
+
lines.push(" ```");
|
|
1329
|
+
lines.push("3. **Branch is up-to-date with base**:");
|
|
1330
|
+
lines.push(" ```bash");
|
|
1491
1331
|
lines.push(
|
|
1492
|
-
"
|
|
1332
|
+
" git fetch origin && git merge-base --is-ancestor origin/main HEAD"
|
|
1493
1333
|
);
|
|
1494
|
-
lines.push("
|
|
1495
|
-
lines.push("
|
|
1334
|
+
lines.push(" ```");
|
|
1335
|
+
lines.push(" If not up-to-date, run the `/pull` skill first.");
|
|
1496
1336
|
lines.push("");
|
|
1497
|
-
lines.push("##
|
|
1337
|
+
lines.push("## Flow");
|
|
1498
1338
|
lines.push("");
|
|
1499
|
-
lines.push("1.
|
|
1500
|
-
lines.push(
|
|
1501
|
-
|
|
1502
|
-
);
|
|
1339
|
+
lines.push("1. Run all pre-flight checks above");
|
|
1340
|
+
lines.push("2. If all checks pass, merge the PR:");
|
|
1341
|
+
lines.push(" ```bash");
|
|
1342
|
+
lines.push(" gh pr merge --squash # squash merge (default)");
|
|
1343
|
+
lines.push(" # or: gh pr merge --merge # merge commit");
|
|
1344
|
+
lines.push(" # or: gh pr merge --rebase # rebase merge");
|
|
1345
|
+
lines.push(" ```");
|
|
1346
|
+
lines.push(" Choose the merge strategy per project policy.");
|
|
1347
|
+
lines.push("3. On merge success:");
|
|
1503
1348
|
lines.push(
|
|
1504
|
-
"
|
|
1349
|
+
" - Use the **gh-project skill** to transition the issue status to Done"
|
|
1505
1350
|
);
|
|
1506
|
-
lines.push("
|
|
1507
|
-
lines.push("
|
|
1508
|
-
lines.push(" -
|
|
1509
|
-
lines.push(" -
|
|
1510
|
-
lines.push(" -
|
|
1511
|
-
lines.push("
|
|
1512
|
-
lines.push("5. Propose improvements and apply with user confirmation");
|
|
1513
|
-
lines.push("6. Validate the refined file");
|
|
1351
|
+
lines.push(" - Do NOT call status APIs directly \u2014 delegate to gh-project");
|
|
1352
|
+
lines.push("4. On merge failure:");
|
|
1353
|
+
lines.push(" - Record the failure reason in workpad Notes");
|
|
1354
|
+
lines.push(" - Resolve the blocking issue (re-run pre-flight checks)");
|
|
1355
|
+
lines.push(" - Retry the merge");
|
|
1356
|
+
lines.push("5. Loop until merged or blocked by an unresolvable issue");
|
|
1514
1357
|
lines.push("");
|
|
1515
|
-
lines.push("##
|
|
1358
|
+
lines.push("## Rules");
|
|
1516
1359
|
lines.push("");
|
|
1517
|
-
lines.push("
|
|
1518
|
-
lines.push("- Front matter is valid YAML");
|
|
1360
|
+
lines.push("- Never call `gh pr merge` without completing pre-flight checks");
|
|
1519
1361
|
lines.push(
|
|
1520
|
-
"-
|
|
1362
|
+
"- Status transition to Done MUST go through the gh-project skill"
|
|
1521
1363
|
);
|
|
1522
1364
|
lines.push(
|
|
1523
|
-
"-
|
|
1365
|
+
"- If any pre-flight check fails, do not merge \u2014 fix the issue first"
|
|
1524
1366
|
);
|
|
1525
|
-
lines.push("-
|
|
1367
|
+
lines.push("- Record all merge attempts and outcomes in the workpad");
|
|
1368
|
+
return renderSkillDocument({
|
|
1369
|
+
name: "land",
|
|
1370
|
+
description: "Merge approved pull requests safely after verifying approvals, CI, and branch freshness.",
|
|
1371
|
+
bodyLines: lines
|
|
1372
|
+
});
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
// src/skills/templates/gh-symphony-references/readme.ts
|
|
1376
|
+
function generateGhSymphonyReferencesReadme(_ctx) {
|
|
1377
|
+
return [
|
|
1378
|
+
"# /gh-symphony references",
|
|
1379
|
+
"",
|
|
1380
|
+
"The /gh-symphony skill consults these files when designing or refining",
|
|
1381
|
+
"WORKFLOW.md.",
|
|
1382
|
+
"",
|
|
1383
|
+
"## Schema",
|
|
1384
|
+
"",
|
|
1385
|
+
"| File | What it is |",
|
|
1386
|
+
"| ---- | ---------- |",
|
|
1387
|
+
"| `workflow-schema.md` | All supported front matter fields and their types. |",
|
|
1388
|
+
"",
|
|
1389
|
+
"## Workflow prompt body postures",
|
|
1390
|
+
"",
|
|
1391
|
+
"When the user describes what the orchestration should do, pick the matching",
|
|
1392
|
+
"posture file(s) and use its prompt-body sections as the seed. Postures can be",
|
|
1393
|
+
"combined when the user's intent spans multiple categories.",
|
|
1394
|
+
"",
|
|
1395
|
+
"| File | Use when the user wants... |",
|
|
1396
|
+
"| ---- | -------------------------- |",
|
|
1397
|
+
"| `workflow-posture-implement.md` | Coding agent writes features / bug fixes (default). |",
|
|
1398
|
+
"| `workflow-posture-review.md` | Agent reviews PRs and leaves comments. No code writes. |",
|
|
1399
|
+
"| `workflow-posture-maintain.md` | Minimal-change maintenance: deps, lint sweeps, hygiene. |",
|
|
1400
|
+
"",
|
|
1401
|
+
"## Adding your own reference",
|
|
1402
|
+
"",
|
|
1403
|
+
"Drop a markdown file here with a descriptive name. The skill discovers files",
|
|
1404
|
+
"on each invocation; no code changes needed."
|
|
1405
|
+
].join("\n");
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
// src/workflow/generate-reference-workflow.ts
|
|
1409
|
+
function generateReferenceWorkflow(input) {
|
|
1410
|
+
const lines = [];
|
|
1411
|
+
lines.push("# Reference WORKFLOW.md \u2014 gh-symphony");
|
|
1412
|
+
lines.push("# This file is a reference template for authoring WORKFLOW.md.");
|
|
1526
1413
|
lines.push(
|
|
1527
|
-
"
|
|
1414
|
+
"# AI agents reference this file (via the /gh-symphony skill) when designing WORKFLOW.md."
|
|
1528
1415
|
);
|
|
1416
|
+
lines.push("# Do not edit this file directly.");
|
|
1417
|
+
lines.push("");
|
|
1418
|
+
lines.push("---");
|
|
1419
|
+
lines.push("");
|
|
1420
|
+
lines.push("# \u2550\u2550\u2550 FRONT MATTER FIELD REFERENCE \u2550\u2550\u2550");
|
|
1529
1421
|
lines.push(
|
|
1530
|
-
"
|
|
1422
|
+
"# All front matter fields supported by the gh-symphony parser are listed below."
|
|
1531
1423
|
);
|
|
1532
1424
|
lines.push("");
|
|
1533
|
-
lines.push("## Supported Front Matter Fields");
|
|
1534
|
-
lines.push("");
|
|
1535
|
-
lines.push("```yaml");
|
|
1536
1425
|
lines.push("tracker:");
|
|
1537
1426
|
lines.push(" kind: github-project");
|
|
1538
|
-
lines.push(
|
|
1427
|
+
lines.push(` project_id: ${input.projectId}`);
|
|
1539
1428
|
lines.push(" state_field: Status");
|
|
1540
|
-
lines.push(
|
|
1541
|
-
lines.push("
|
|
1542
|
-
|
|
1429
|
+
lines.push(...buildReferencePriorityLines(input.priority));
|
|
1430
|
+
lines.push("");
|
|
1431
|
+
const activeColumns = input.statusColumns.filter((c) => c.role === "active");
|
|
1432
|
+
const waitColumns = input.statusColumns.filter((c) => c.role === "wait");
|
|
1433
|
+
const terminalColumns = input.statusColumns.filter(
|
|
1434
|
+
(c) => c.role === "terminal"
|
|
1435
|
+
);
|
|
1436
|
+
const blockerCheckStates = input.lifecycle?.blockerCheckStates ?? [];
|
|
1437
|
+
const planningStates = input.lifecycle?.planningStates ?? blockerCheckStates;
|
|
1438
|
+
if (activeColumns.length > 0) {
|
|
1439
|
+
lines.push(" active_states:");
|
|
1440
|
+
for (const col of activeColumns) {
|
|
1441
|
+
lines.push(` - ${col.name}`);
|
|
1442
|
+
}
|
|
1443
|
+
} else {
|
|
1444
|
+
lines.push(" active_states: [{active column names}]");
|
|
1445
|
+
}
|
|
1446
|
+
if (terminalColumns.length > 0) {
|
|
1447
|
+
lines.push(" terminal_states:");
|
|
1448
|
+
for (const col of terminalColumns) {
|
|
1449
|
+
lines.push(` - ${col.name}`);
|
|
1450
|
+
}
|
|
1451
|
+
} else {
|
|
1452
|
+
lines.push(" terminal_states: [{terminal column names}]");
|
|
1453
|
+
}
|
|
1454
|
+
lines.push(
|
|
1455
|
+
...buildReferenceStringList("blocker_check_states", blockerCheckStates)
|
|
1456
|
+
);
|
|
1457
|
+
lines.push(...buildReferenceStringList("planning_states", planningStates));
|
|
1458
|
+
lines.push("");
|
|
1459
|
+
lines.push("# Linear tracker example:");
|
|
1460
|
+
lines.push("# tracker:");
|
|
1461
|
+
lines.push("# kind: linear");
|
|
1462
|
+
lines.push("# endpoint: https://api.linear.app/graphql");
|
|
1463
|
+
lines.push("# api_key: $LINEAR_API_KEY");
|
|
1464
|
+
lines.push("# project_slug: symphony-0c79b11b75ea");
|
|
1465
|
+
lines.push("# active_states:");
|
|
1466
|
+
lines.push("# - Todo");
|
|
1467
|
+
lines.push("# - In Progress");
|
|
1468
|
+
lines.push("# terminal_states:");
|
|
1469
|
+
lines.push("# - Done");
|
|
1470
|
+
lines.push("# - Canceled");
|
|
1471
|
+
lines.push("# - Duplicate");
|
|
1472
|
+
lines.push(
|
|
1473
|
+
"# Linear uses repository-local polling; gh-symphony does not provide"
|
|
1474
|
+
);
|
|
1475
|
+
lines.push("# a Linear webhook setup command.");
|
|
1476
|
+
lines.push("");
|
|
1543
1477
|
lines.push("polling:");
|
|
1544
1478
|
lines.push(" interval_ms: 30000");
|
|
1479
|
+
lines.push("");
|
|
1545
1480
|
lines.push("workspace:");
|
|
1546
1481
|
lines.push(" root: .runtime/symphony-workspaces");
|
|
1482
|
+
lines.push("");
|
|
1547
1483
|
lines.push("hooks:");
|
|
1548
|
-
lines.push(
|
|
1549
|
-
|
|
1484
|
+
lines.push(
|
|
1485
|
+
` after_create: ${DEFAULT_AFTER_CREATE_HOOK_PATH} # ${DEFAULT_AFTER_CREATE_HOOK_COMMENT}`
|
|
1486
|
+
);
|
|
1550
1487
|
lines.push(" before_run: null");
|
|
1551
1488
|
lines.push(" after_run: null");
|
|
1552
1489
|
lines.push(" before_remove: null");
|
|
1553
1490
|
lines.push(" timeout_ms: 60000");
|
|
1491
|
+
lines.push("");
|
|
1554
1492
|
lines.push("agent:");
|
|
1555
1493
|
lines.push(" max_concurrent_agents: 10");
|
|
1556
1494
|
lines.push(" max_retry_backoff_ms: 30000");
|
|
1557
1495
|
lines.push(" retry_base_delay_ms: 10000");
|
|
1558
1496
|
lines.push(" max_turns: 20");
|
|
1559
|
-
lines.push("codex:");
|
|
1560
|
-
lines.push(" command: codex app-server");
|
|
1561
|
-
lines.push(" read_timeout_ms: 5000");
|
|
1562
|
-
lines.push(" turn_timeout_ms: 3600000");
|
|
1563
|
-
lines.push(" stall_timeout_ms: 300000");
|
|
1564
|
-
lines.push("```");
|
|
1565
|
-
lines.push("");
|
|
1566
|
-
lines.push("## Supported Template Variables");
|
|
1567
1497
|
lines.push("");
|
|
1568
|
-
lines.push(
|
|
1498
|
+
lines.push(...buildRuntimeFrontMatter(input.runtime));
|
|
1569
1499
|
lines.push("");
|
|
1570
|
-
lines.push("
|
|
1571
|
-
lines.push("|----------|-------------|");
|
|
1572
|
-
lines.push("| `issue.identifier` | e.g. `acme/platform#42` |");
|
|
1573
|
-
lines.push("| `issue.title` | Issue title |");
|
|
1574
|
-
lines.push("| `issue.state` | Current tracker state |");
|
|
1575
|
-
lines.push("| `issue.description` | Issue body |");
|
|
1576
|
-
lines.push("| `issue.url` | Issue URL |");
|
|
1577
|
-
lines.push("| `issue.repository` | `owner/name` |");
|
|
1578
|
-
lines.push("| `issue.number` | Issue number |");
|
|
1579
|
-
lines.push("| `attempt` | Retry attempt number (null on first run) |");
|
|
1500
|
+
lines.push("---");
|
|
1580
1501
|
lines.push("");
|
|
1502
|
+
lines.push("# \u2550\u2550\u2550 PROMPT BODY REFERENCE \u2550\u2550\u2550");
|
|
1581
1503
|
lines.push(
|
|
1582
|
-
"
|
|
1504
|
+
"# GitHub Project adaptation of the Elixir Symphony reference prompt."
|
|
1583
1505
|
);
|
|
1584
|
-
lines.push("will cause a runtime error (strict mode validation).");
|
|
1585
1506
|
lines.push("");
|
|
1586
|
-
lines.push("##
|
|
1507
|
+
lines.push("## Status Map");
|
|
1508
|
+
lines.push("");
|
|
1509
|
+
lines.push("| Status | Role | Agent Action |");
|
|
1510
|
+
lines.push("| ------ | ---- | ------------ |");
|
|
1511
|
+
for (const col of input.statusColumns) {
|
|
1512
|
+
const roleLabel = col.role ?? "unset";
|
|
1513
|
+
const action = resolveRoleAction(col.role);
|
|
1514
|
+
lines.push(`| ${col.name} | ${roleLabel} | ${action} |`);
|
|
1515
|
+
}
|
|
1516
|
+
if (waitColumns.length > 0) {
|
|
1517
|
+
lines.push("");
|
|
1518
|
+
lines.push("**Wait States (awaiting PR review):**");
|
|
1519
|
+
for (const col of waitColumns) {
|
|
1520
|
+
lines.push(
|
|
1521
|
+
`- **${col.name}**: PR created. Awaiting human review. Agent is idle.`
|
|
1522
|
+
);
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
lines.push("");
|
|
1526
|
+
lines.push("## Repository Validation Guidance");
|
|
1527
|
+
lines.push("");
|
|
1528
|
+
for (const line of buildRepositoryValidationGuidance(
|
|
1529
|
+
input.detectedEnvironment
|
|
1530
|
+
)) {
|
|
1531
|
+
lines.push(`- ${line}`);
|
|
1532
|
+
}
|
|
1533
|
+
lines.push("");
|
|
1534
|
+
lines.push("## Default Posture");
|
|
1587
1535
|
lines.push("");
|
|
1588
1536
|
lines.push(
|
|
1589
|
-
"
|
|
1537
|
+
"1. This is an unattended orchestration session. Do not ask humans for follow-up tasks."
|
|
1590
1538
|
);
|
|
1591
1539
|
lines.push(
|
|
1592
|
-
"
|
|
1540
|
+
"2. Exit early only for genuine blockers (missing required credentials or secrets)."
|
|
1593
1541
|
);
|
|
1594
|
-
lines.push("- `/push` \u2014 keep remote branch current and publish updates");
|
|
1595
|
-
lines.push("- `/pull` \u2014 sync branch with latest origin/main before handoff");
|
|
1596
|
-
lines.push("- `/land` \u2014 merge approved PR and transition issue to Done");
|
|
1597
|
-
return renderSkillDocument({
|
|
1598
|
-
name: "gh-symphony",
|
|
1599
|
-
description: "Design, refine, and validate repository WORKFLOW.md files for GitHub Symphony projects.",
|
|
1600
|
-
bodyLines: lines
|
|
1601
|
-
});
|
|
1602
|
-
}
|
|
1603
|
-
|
|
1604
|
-
// src/skills/templates/gh-project.ts
|
|
1605
|
-
function generateGhProjectSkill(ctx) {
|
|
1606
|
-
const lines = [];
|
|
1607
|
-
lines.push("# /gh-project \u2014 GitHub Project v2 Status Management");
|
|
1608
|
-
lines.push("");
|
|
1609
|
-
lines.push("## Purpose");
|
|
1610
|
-
lines.push("");
|
|
1611
1542
|
lines.push(
|
|
1612
|
-
|
|
1543
|
+
'3. In your final message, report only completed work and blockers. Do not include "next steps".'
|
|
1544
|
+
);
|
|
1545
|
+
lines.push(
|
|
1546
|
+
"4. Do not modify the issue body for planning or progress-tracking purposes."
|
|
1547
|
+
);
|
|
1548
|
+
lines.push(
|
|
1549
|
+
"5. If the issue is in a terminal state, do nothing and exit immediately."
|
|
1613
1550
|
);
|
|
1614
|
-
lines.push("create workpad comments, and handle follow-up issues.");
|
|
1615
|
-
lines.push("");
|
|
1616
|
-
lines.push("## Prerequisites");
|
|
1617
|
-
lines.push("");
|
|
1618
|
-
lines.push("- `gh` CLI is authenticated (`gh auth status`)");
|
|
1619
1551
|
lines.push(
|
|
1620
|
-
|
|
1552
|
+
"6. If you discover out-of-scope improvements, open a separate issue rather than expanding the current scope."
|
|
1621
1553
|
);
|
|
1622
|
-
lines.push("");
|
|
1623
|
-
lines.push("## Column ID Quick Reference");
|
|
1624
|
-
lines.push("");
|
|
1625
|
-
lines.push(`Status Field ID: \`${ctx.statusFieldId}\``);
|
|
1626
|
-
lines.push("");
|
|
1627
|
-
lines.push("| Column Name | Role | Option ID |");
|
|
1628
|
-
lines.push("|-------------|------|-----------|");
|
|
1629
|
-
for (const col of ctx.statusColumns) {
|
|
1630
|
-
const role = col.role ?? "unknown";
|
|
1631
|
-
lines.push(`| ${col.name} | ${role} | \`${col.id}\` |`);
|
|
1632
|
-
}
|
|
1633
|
-
lines.push("");
|
|
1634
|
-
lines.push("## Operations");
|
|
1635
|
-
lines.push("");
|
|
1636
|
-
lines.push("### Change Issue Status");
|
|
1637
|
-
lines.push("");
|
|
1638
1554
|
lines.push(
|
|
1639
|
-
"
|
|
1555
|
+
"7. Keep all commits as logical units and follow conventional commit format."
|
|
1640
1556
|
);
|
|
1641
|
-
lines.push("");
|
|
1642
|
-
lines.push("
|
|
1643
|
-
lines.push("
|
|
1557
|
+
lines.push("8. Do not make commits that break existing tests.");
|
|
1558
|
+
lines.push("9. Verify all existing tests pass before creating a PR.");
|
|
1559
|
+
lines.push("10. Create a workpad as an issue comment to track progress.");
|
|
1560
|
+
lines.push("11. Use the gh-project skill to manage issue status.");
|
|
1644
1561
|
lines.push(
|
|
1645
|
-
"
|
|
1562
|
+
"12. When a blocker is found, record it in an issue comment and transition the status appropriately."
|
|
1646
1563
|
);
|
|
1647
1564
|
lines.push(
|
|
1648
|
-
"
|
|
1565
|
+
"13. Once your work is complete and the PR is merged, transition the issue to the Done state."
|
|
1649
1566
|
);
|
|
1650
1567
|
lines.push("");
|
|
1651
|
-
lines.push("
|
|
1652
|
-
lines.push(`gh project item-edit \\`);
|
|
1653
|
-
lines.push(` --project-id ${ctx.projectId} \\`);
|
|
1654
|
-
lines.push(` --id <item-id> \\`);
|
|
1655
|
-
lines.push(` --field-id ${ctx.statusFieldId} \\`);
|
|
1656
|
-
lines.push(` --single-select-option-id <option-id-from-table-above>`);
|
|
1657
|
-
lines.push("```");
|
|
1658
|
-
lines.push("");
|
|
1659
|
-
lines.push("### Create Workpad Comment");
|
|
1568
|
+
lines.push("## Related Skills");
|
|
1660
1569
|
lines.push("");
|
|
1661
|
-
lines.push("```bash");
|
|
1662
1570
|
lines.push(
|
|
1663
|
-
|
|
1571
|
+
"- **gh-project**: Manage GitHub Project v2 issue status and update fields"
|
|
1664
1572
|
);
|
|
1665
|
-
lines.push("```");
|
|
1666
|
-
lines.push("");
|
|
1667
|
-
lines.push("### Update Existing Comment");
|
|
1668
|
-
lines.push("");
|
|
1669
|
-
lines.push("```bash");
|
|
1670
1573
|
lines.push(
|
|
1671
|
-
"
|
|
1574
|
+
"- **commit**: Create logical-unit commits (conventional commit format)"
|
|
1672
1575
|
);
|
|
1673
|
-
lines.push(
|
|
1674
|
-
lines.push("
|
|
1675
|
-
lines.push("");
|
|
1676
|
-
lines.push("### Create Follow-up Issue");
|
|
1576
|
+
lines.push("- **push**: Push branch and sync with the remote repository");
|
|
1577
|
+
lines.push("- **pull**: Fetch latest changes and resolve conflicts");
|
|
1578
|
+
lines.push("- **land**: Create PR, request review, and handle merge");
|
|
1677
1579
|
lines.push("");
|
|
1678
|
-
lines.push("
|
|
1679
|
-
lines.push("gh issue create --repo <owner>/<repo> \\");
|
|
1680
|
-
lines.push(' --title "Follow-up: <title>" \\');
|
|
1681
|
-
lines.push(' --body "<description>" \\');
|
|
1682
|
-
lines.push(' --label "backlog"');
|
|
1683
|
-
lines.push("```");
|
|
1580
|
+
lines.push("## Step 0: Determine current state and route");
|
|
1684
1581
|
lines.push("");
|
|
1685
|
-
lines.push(
|
|
1582
|
+
lines.push(
|
|
1583
|
+
"Check the current issue state and route to the appropriate step:"
|
|
1584
|
+
);
|
|
1686
1585
|
lines.push("");
|
|
1687
|
-
|
|
1586
|
+
if (terminalColumns.length > 0) {
|
|
1587
|
+
const terminalNames = terminalColumns.map((c) => c.name).join(", ");
|
|
1588
|
+
lines.push(`- **${terminalNames}** \u2192 Exit immediately. Do nothing.`);
|
|
1589
|
+
}
|
|
1590
|
+
if (waitColumns.length > 0) {
|
|
1591
|
+
const waitNames = waitColumns.map((c) => c.name).join(", ");
|
|
1592
|
+
lines.push(`- **${waitNames}** \u2192 Go to Step 3 (handle awaiting review).`);
|
|
1593
|
+
}
|
|
1594
|
+
if (activeColumns.length > 0) {
|
|
1595
|
+
const activeNames = activeColumns.map((c) => c.name).join(", ");
|
|
1596
|
+
lines.push(
|
|
1597
|
+
`- **${activeNames}** \u2192 Go to Step 1 (start or continue execution).`
|
|
1598
|
+
);
|
|
1599
|
+
}
|
|
1688
1600
|
lines.push(
|
|
1689
|
-
|
|
1601
|
+
"- **Other states** \u2192 Log the unclear state in an issue comment and exit."
|
|
1690
1602
|
);
|
|
1691
|
-
lines.push("```");
|
|
1692
1603
|
lines.push("");
|
|
1693
|
-
lines.push("##
|
|
1604
|
+
lines.push("## Step 1: Start/continue execution");
|
|
1694
1605
|
lines.push("");
|
|
1695
1606
|
lines.push(
|
|
1696
|
-
"
|
|
1607
|
+
"1. Read the issue body and comments to understand current progress."
|
|
1697
1608
|
);
|
|
1698
1609
|
lines.push(
|
|
1699
|
-
"
|
|
1610
|
+
"2. If an existing workpad comment is found, continue from it; otherwise create a new workpad."
|
|
1700
1611
|
);
|
|
1701
|
-
lines.push(" - All acceptance criteria checked");
|
|
1702
|
-
lines.push(" - All tests passing");
|
|
1703
|
-
lines.push(" - PR merged (if applicable)");
|
|
1704
1612
|
lines.push(
|
|
1705
|
-
"
|
|
1613
|
+
"3. See the 'Workpad Template' section below for the workpad format."
|
|
1706
1614
|
);
|
|
1707
1615
|
lines.push(
|
|
1708
|
-
"
|
|
1616
|
+
"4. If no branch exists, create a feature branch based on `{issue.repository}`."
|
|
1709
1617
|
);
|
|
1710
|
-
|
|
1711
|
-
name: "gh-project",
|
|
1712
|
-
description: "Manage GitHub Project v2 issue states, workpad comments, and related follow-up actions.",
|
|
1713
|
-
bodyLines: lines
|
|
1714
|
-
});
|
|
1715
|
-
}
|
|
1716
|
-
|
|
1717
|
-
// src/skills/templates/commit.ts
|
|
1718
|
-
function generateCommitSkill(_ctx) {
|
|
1719
|
-
const lines = [];
|
|
1720
|
-
lines.push("# /commit \u2014 Clean Commit Workflow");
|
|
1721
|
-
lines.push("");
|
|
1722
|
-
lines.push("## Trigger");
|
|
1723
|
-
lines.push("");
|
|
1724
|
-
lines.push("Use this skill when creating commits during implementation.");
|
|
1725
|
-
lines.push("");
|
|
1726
|
-
lines.push("## Rules");
|
|
1727
|
-
lines.push("");
|
|
1728
|
-
lines.push("- Commit in logical units \u2014 one concern per commit");
|
|
1729
|
-
lines.push("- Never commit a broken intermediate state (tests must pass)");
|
|
1730
|
-
lines.push("- Never commit temporary debug code or commented-out blocks");
|
|
1731
|
-
lines.push("- Run tests before every commit");
|
|
1732
|
-
lines.push("");
|
|
1733
|
-
lines.push("## Format");
|
|
1734
|
-
lines.push("");
|
|
1735
|
-
lines.push("Use Conventional Commit format:");
|
|
1736
|
-
lines.push("");
|
|
1737
|
-
lines.push("```");
|
|
1738
|
-
lines.push("<type>(<scope>): <description>");
|
|
1739
|
-
lines.push("");
|
|
1740
|
-
lines.push("[optional body \u2014 explain WHY, not WHAT, 72 chars/line]");
|
|
1741
|
-
lines.push("");
|
|
1742
|
-
lines.push("[optional footer: Closes #N]");
|
|
1743
|
-
lines.push("```");
|
|
1618
|
+
lines.push("5. Proceed to Step 2.");
|
|
1744
1619
|
lines.push("");
|
|
1745
|
-
lines.push("
|
|
1620
|
+
lines.push("## Step 2: Execution phase");
|
|
1746
1621
|
lines.push("");
|
|
1622
|
+
lines.push("1. Implement according to the issue description.");
|
|
1747
1623
|
lines.push(
|
|
1748
|
-
"
|
|
1624
|
+
"2. Commit changes in logical units (conventional commit format)."
|
|
1749
1625
|
);
|
|
1750
|
-
lines.push("");
|
|
1751
|
-
lines.push("
|
|
1752
|
-
lines.push("");
|
|
1753
|
-
lines.push("```");
|
|
1754
|
-
lines.push("feat(auth): add OAuth2 token refresh");
|
|
1755
|
-
lines.push("fix(api): handle null response from upstream");
|
|
1756
|
-
lines.push("test(worker): add retry exhaustion coverage");
|
|
1757
|
-
lines.push("```");
|
|
1758
|
-
return renderSkillDocument({
|
|
1759
|
-
name: "commit",
|
|
1760
|
-
description: "Create clean, logically scoped commits that keep the repository in a shippable state.",
|
|
1761
|
-
bodyLines: lines
|
|
1762
|
-
});
|
|
1763
|
-
}
|
|
1764
|
-
|
|
1765
|
-
// src/skills/templates/push.ts
|
|
1766
|
-
function generatePushSkill(_ctx) {
|
|
1767
|
-
const lines = [];
|
|
1768
|
-
lines.push("# /push \u2014 Git Push Workflow");
|
|
1769
|
-
lines.push("");
|
|
1770
|
-
lines.push("## Trigger");
|
|
1771
|
-
lines.push("");
|
|
1626
|
+
lines.push("3. Verify all existing tests pass.");
|
|
1627
|
+
lines.push("4. Write tests for new functionality.");
|
|
1628
|
+
lines.push("5. Once all Completion Bar criteria are met, create a PR.");
|
|
1772
1629
|
lines.push(
|
|
1773
|
-
"
|
|
1630
|
+
"6. After creating the PR, transition the issue status to the Human Review state."
|
|
1774
1631
|
);
|
|
1632
|
+
lines.push("7. Proceed to Step 3.");
|
|
1775
1633
|
lines.push("");
|
|
1776
|
-
lines.push("##
|
|
1777
|
-
lines.push("");
|
|
1778
|
-
lines.push("1. Run local tests and lint \u2014 ensure they pass before pushing");
|
|
1779
|
-
lines.push("2. Push to remote:");
|
|
1780
|
-
lines.push(" ```bash");
|
|
1781
|
-
lines.push(" git push origin <branch> # subsequent pushes");
|
|
1782
|
-
lines.push(" git push -u origin <branch> # first push (sets upstream)");
|
|
1783
|
-
lines.push(" ```");
|
|
1784
|
-
lines.push("3. If push is rejected (non-fast-forward):");
|
|
1785
|
-
lines.push(" - Run `git fetch origin && git merge origin/main`");
|
|
1786
|
-
lines.push(" - Resolve any conflicts");
|
|
1787
|
-
lines.push(" - Re-run tests");
|
|
1788
|
-
lines.push(" - Push again");
|
|
1789
|
-
lines.push("4. Record push result in workpad Notes");
|
|
1790
|
-
lines.push("");
|
|
1791
|
-
lines.push("## Rules");
|
|
1634
|
+
lines.push("## Step 3: Human Review and merge handling");
|
|
1792
1635
|
lines.push("");
|
|
1793
|
-
lines.push("
|
|
1636
|
+
lines.push("1. If a PR already exists, check for review comments.");
|
|
1794
1637
|
lines.push(
|
|
1795
|
-
"
|
|
1638
|
+
"2. If no review comments are present, remain in the waiting state."
|
|
1796
1639
|
);
|
|
1797
|
-
lines.push("- Verify CI starts after push (check GitHub Actions tab)");
|
|
1798
|
-
lines.push("- Do not push directly to `main` or `master`");
|
|
1799
|
-
return renderSkillDocument({
|
|
1800
|
-
name: "push",
|
|
1801
|
-
description: "Publish verified local commits to the remote branch without unsafe force pushes.",
|
|
1802
|
-
bodyLines: lines
|
|
1803
|
-
});
|
|
1804
|
-
}
|
|
1805
|
-
|
|
1806
|
-
// src/skills/templates/pull.ts
|
|
1807
|
-
function generatePullSkill(_ctx) {
|
|
1808
|
-
const lines = [];
|
|
1809
|
-
lines.push("# /pull \u2014 Git Pull / Sync Workflow");
|
|
1810
|
-
lines.push("");
|
|
1811
|
-
lines.push("## Trigger");
|
|
1812
|
-
lines.push("");
|
|
1813
1640
|
lines.push(
|
|
1814
|
-
"
|
|
1641
|
+
"3. If the PR is merged, transition the issue to a terminal state."
|
|
1815
1642
|
);
|
|
1816
|
-
lines.push("
|
|
1643
|
+
lines.push("4. If review changes are requested, proceed to Step 4.");
|
|
1817
1644
|
lines.push("");
|
|
1818
|
-
lines.push("##
|
|
1645
|
+
lines.push("## Step 4: Rework handling");
|
|
1819
1646
|
lines.push("");
|
|
1820
|
-
lines.push("1. Fetch latest from remote:");
|
|
1821
|
-
lines.push(" ```bash");
|
|
1822
|
-
lines.push(" git fetch origin");
|
|
1823
|
-
lines.push(" ```");
|
|
1824
|
-
lines.push("2. Merge into current branch:");
|
|
1825
|
-
lines.push(" ```bash");
|
|
1826
|
-
lines.push(" git merge origin/main");
|
|
1827
|
-
lines.push(" ```");
|
|
1828
|
-
lines.push("3. If conflicts arise:");
|
|
1829
|
-
lines.push(" - Resolve each conflict file");
|
|
1830
|
-
lines.push(" - Run tests to confirm nothing broke");
|
|
1831
1647
|
lines.push(
|
|
1832
|
-
"
|
|
1648
|
+
"1. Read all PR review comments and identify the requested changes."
|
|
1833
1649
|
);
|
|
1834
1650
|
lines.push(
|
|
1835
|
-
"
|
|
1651
|
+
"2. Process the changes following the PR Feedback Sweep Protocol."
|
|
1836
1652
|
);
|
|
1837
|
-
lines.push("
|
|
1838
|
-
lines.push("
|
|
1839
|
-
lines.push("
|
|
1840
|
-
lines.push(" - resulting HEAD short SHA: `git rev-parse --short HEAD`");
|
|
1841
|
-
lines.push("");
|
|
1842
|
-
lines.push("## Rules");
|
|
1653
|
+
lines.push("3. After implementing changes, commit and update the PR.");
|
|
1654
|
+
lines.push("4. Transition the issue status back to the Human Review state.");
|
|
1655
|
+
lines.push("5. Return to Step 3.");
|
|
1843
1656
|
lines.push("");
|
|
1844
|
-
lines.push("
|
|
1845
|
-
lines.push("- Always pull at the start of a new work session");
|
|
1846
|
-
lines.push("- Record the pull evidence in the workpad before proceeding");
|
|
1847
|
-
return renderSkillDocument({
|
|
1848
|
-
name: "pull",
|
|
1849
|
-
description: "Sync the current branch with the latest remote base before implementation or review handoff.",
|
|
1850
|
-
bodyLines: lines
|
|
1851
|
-
});
|
|
1852
|
-
}
|
|
1853
|
-
|
|
1854
|
-
// src/skills/templates/land.ts
|
|
1855
|
-
function generateLandSkill(_ctx) {
|
|
1856
|
-
const lines = [];
|
|
1857
|
-
lines.push("# /land \u2014 PR Merge Workflow");
|
|
1657
|
+
lines.push("## PR Feedback Sweep Protocol");
|
|
1858
1658
|
lines.push("");
|
|
1859
|
-
lines.push("
|
|
1659
|
+
lines.push("Order for processing PR review feedback:");
|
|
1860
1660
|
lines.push("");
|
|
1861
1661
|
lines.push(
|
|
1862
|
-
"
|
|
1662
|
+
"1. **Collect all comments**: List all unresolved review comments."
|
|
1663
|
+
);
|
|
1664
|
+
lines.push(
|
|
1665
|
+
"2. **Triage by priority**: Handle blocking comments before non-blocking ones."
|
|
1863
1666
|
);
|
|
1864
1667
|
lines.push(
|
|
1865
|
-
"
|
|
1668
|
+
"3. **Implement changes**: Make the code changes corresponding to each comment."
|
|
1866
1669
|
);
|
|
1867
|
-
lines.push("");
|
|
1868
|
-
lines.push("## Pre-flight Checks");
|
|
1869
|
-
lines.push("");
|
|
1870
|
-
lines.push("Before merging, verify ALL of the following:");
|
|
1871
|
-
lines.push("");
|
|
1872
|
-
lines.push("1. **PR is approved**:");
|
|
1873
|
-
lines.push(" ```bash");
|
|
1874
1670
|
lines.push(
|
|
1875
|
-
|
|
1671
|
+
"4. **Reply to comments**: Respond to each review comment with a description of what was done."
|
|
1876
1672
|
);
|
|
1877
|
-
lines.push(" ```");
|
|
1878
|
-
lines.push("2. **All CI checks are green**:");
|
|
1879
|
-
lines.push(" ```bash");
|
|
1880
|
-
lines.push(" gh pr checks");
|
|
1881
|
-
lines.push(" ```");
|
|
1882
|
-
lines.push("3. **Branch is up-to-date with base**:");
|
|
1883
|
-
lines.push(" ```bash");
|
|
1884
1673
|
lines.push(
|
|
1885
|
-
"
|
|
1674
|
+
"5. **Commit**: Commit changes in `fix: address PR review feedback` format."
|
|
1886
1675
|
);
|
|
1887
|
-
lines.push("
|
|
1888
|
-
lines.push(" If not up-to-date, run the `/pull` skill first.");
|
|
1676
|
+
lines.push("6. **Request re-review**: Ask the reviewer for a re-review.");
|
|
1889
1677
|
lines.push("");
|
|
1890
|
-
lines.push("##
|
|
1678
|
+
lines.push("## Completion Bar");
|
|
1679
|
+
lines.push("");
|
|
1680
|
+
lines.push("All of the following must be satisfied before creating a PR:");
|
|
1891
1681
|
lines.push("");
|
|
1892
|
-
lines.push("1. Run all pre-flight checks above");
|
|
1893
|
-
lines.push("2. If all checks pass, merge the PR:");
|
|
1894
|
-
lines.push(" ```bash");
|
|
1895
|
-
lines.push(" gh pr merge --squash # squash merge (default)");
|
|
1896
|
-
lines.push(" # or: gh pr merge --merge # merge commit");
|
|
1897
|
-
lines.push(" # or: gh pr merge --rebase # rebase merge");
|
|
1898
|
-
lines.push(" ```");
|
|
1899
|
-
lines.push(" Choose the merge strategy per project policy.");
|
|
1900
|
-
lines.push("3. On merge success:");
|
|
1901
1682
|
lines.push(
|
|
1902
|
-
"
|
|
1683
|
+
"- [ ] All requirements from the issue description are implemented."
|
|
1903
1684
|
);
|
|
1904
|
-
lines.push("
|
|
1905
|
-
lines.push("
|
|
1906
|
-
lines.push("
|
|
1907
|
-
lines.push("
|
|
1908
|
-
lines.push("
|
|
1909
|
-
lines.push("5. Loop until merged or blocked by an unresolvable issue");
|
|
1685
|
+
lines.push("- [ ] All existing tests pass.");
|
|
1686
|
+
lines.push("- [ ] Tests are written for new functionality.");
|
|
1687
|
+
lines.push("- [ ] Code style follows project conventions.");
|
|
1688
|
+
lines.push("- [ ] The PR description clearly explains the changes.");
|
|
1689
|
+
lines.push("- [ ] Related documentation is updated (if applicable).");
|
|
1910
1690
|
lines.push("");
|
|
1911
|
-
lines.push("##
|
|
1691
|
+
lines.push("## Guardrails");
|
|
1912
1692
|
lines.push("");
|
|
1913
|
-
lines.push("- Never
|
|
1693
|
+
lines.push("- **Scope**: Never make changes outside the scope of the issue.");
|
|
1914
1694
|
lines.push(
|
|
1915
|
-
"-
|
|
1695
|
+
"- **Secrets**: Never hardcode tokens, passwords, or API keys in code."
|
|
1916
1696
|
);
|
|
1917
1697
|
lines.push(
|
|
1918
|
-
"-
|
|
1698
|
+
"- **Breaking changes**: Do not modify existing APIs or interfaces without explicit authorization."
|
|
1919
1699
|
);
|
|
1920
|
-
lines.push("-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1700
|
+
lines.push("- **Force push**: Do not force-push to the main/master branch.");
|
|
1701
|
+
lines.push(
|
|
1702
|
+
"- **Issue body**: Do not modify the issue body for progress tracking."
|
|
1703
|
+
);
|
|
1704
|
+
lines.push(
|
|
1705
|
+
"- **Infinite loops**: If the same task fails 3 or more consecutive times, log it as a blocker and exit."
|
|
1706
|
+
);
|
|
1707
|
+
lines.push("");
|
|
1708
|
+
lines.push("## Workpad Template");
|
|
1709
|
+
lines.push("");
|
|
1710
|
+
lines.push("Workpad format to create as an issue comment:");
|
|
1711
|
+
lines.push("");
|
|
1712
|
+
lines.push("```markdown");
|
|
1713
|
+
lines.push("## Workpad \u2014 {issue.identifier}");
|
|
1714
|
+
lines.push("");
|
|
1715
|
+
lines.push("**Status**: {current phase}");
|
|
1716
|
+
lines.push("**Branch**: {branch name}");
|
|
1717
|
+
lines.push("**PR**: {PR URL or not created}");
|
|
1718
|
+
lines.push("");
|
|
1719
|
+
lines.push("### Plan");
|
|
1720
|
+
lines.push("");
|
|
1721
|
+
lines.push("- [ ] {task 1}");
|
|
1722
|
+
lines.push("- [ ] {task 2}");
|
|
1723
|
+
lines.push("");
|
|
1724
|
+
lines.push("### Progress Log");
|
|
1725
|
+
lines.push("");
|
|
1726
|
+
lines.push("- {timestamp}: {action taken}");
|
|
1727
|
+
lines.push("");
|
|
1728
|
+
lines.push("### Blockers");
|
|
1729
|
+
lines.push("");
|
|
1730
|
+
lines.push("None");
|
|
1731
|
+
lines.push("```");
|
|
1732
|
+
lines.push("");
|
|
1733
|
+
return lines.join("\n");
|
|
1926
1734
|
}
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
"
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
"
|
|
1949
|
-
"
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1735
|
+
function buildReferenceStringList(key, values) {
|
|
1736
|
+
if (values.length === 0) {
|
|
1737
|
+
return [` ${key}: []`];
|
|
1738
|
+
}
|
|
1739
|
+
return [` ${key}:`, ...values.map((value) => ` - ${value}`)];
|
|
1740
|
+
}
|
|
1741
|
+
function buildReferencePriorityLines(priority) {
|
|
1742
|
+
const lines = [];
|
|
1743
|
+
if (priority?.source === "project-field" || priority?.source === "labels") {
|
|
1744
|
+
lines.push(
|
|
1745
|
+
" # Priority is explicit. Numbers below are editable policy (lower = higher priority)."
|
|
1746
|
+
);
|
|
1747
|
+
} else {
|
|
1748
|
+
lines.push(
|
|
1749
|
+
" # Priority dispatch is disabled until an operator chooses one explicit source."
|
|
1750
|
+
);
|
|
1751
|
+
}
|
|
1752
|
+
lines.push(
|
|
1753
|
+
" # See docs/adr/2026-05-18_explicit-dispatch-priority-mappings.md"
|
|
1754
|
+
);
|
|
1755
|
+
if (priority?.source === "project-field") {
|
|
1756
|
+
lines.push(" priority:");
|
|
1757
|
+
lines.push(" source: project-field");
|
|
1758
|
+
lines.push(` field: ${JSON.stringify(priority.field)}`);
|
|
1759
|
+
lines.push(" values:");
|
|
1760
|
+
for (const [name, value] of Object.entries(priority.values)) {
|
|
1761
|
+
lines.push(` ${JSON.stringify(name)}: ${value}`);
|
|
1762
|
+
}
|
|
1763
|
+
return lines;
|
|
1764
|
+
}
|
|
1765
|
+
if (priority?.source === "labels") {
|
|
1766
|
+
lines.push(" priority:");
|
|
1767
|
+
lines.push(" source: labels");
|
|
1768
|
+
lines.push(" labels:");
|
|
1769
|
+
for (const [name, value] of Object.entries(priority.labels)) {
|
|
1770
|
+
lines.push(` ${JSON.stringify(name)}: ${value}`);
|
|
1771
|
+
}
|
|
1772
|
+
return lines;
|
|
1773
|
+
}
|
|
1774
|
+
lines.push(" priority:");
|
|
1775
|
+
lines.push(" source: disabled");
|
|
1776
|
+
lines.push("");
|
|
1777
|
+
lines.push(" # Optional template: project-field priority source.");
|
|
1778
|
+
lines.push(" # priority:");
|
|
1779
|
+
lines.push(" # source: project-field");
|
|
1780
|
+
lines.push(" # field: Priority");
|
|
1781
|
+
lines.push(" # values:");
|
|
1782
|
+
lines.push(" # Urgent: 0");
|
|
1783
|
+
lines.push(" # High: 1");
|
|
1784
|
+
lines.push("");
|
|
1785
|
+
lines.push(" # Optional template: labels priority source.");
|
|
1786
|
+
lines.push(" # priority:");
|
|
1787
|
+
lines.push(" # source: labels");
|
|
1788
|
+
lines.push(" # labels:");
|
|
1789
|
+
lines.push(" # P0: 0");
|
|
1790
|
+
lines.push(" # P1: 1");
|
|
1791
|
+
return lines;
|
|
1792
|
+
}
|
|
1793
|
+
function resolveRoleAction(role) {
|
|
1794
|
+
switch (role) {
|
|
1795
|
+
case "active":
|
|
1796
|
+
return "Agent starts work immediately. Creates workpad and proceeds with implementation.";
|
|
1797
|
+
case "wait":
|
|
1798
|
+
return "PR created. Awaiting human review. Agent is idle.";
|
|
1799
|
+
case "terminal":
|
|
1800
|
+
return "Completed state. Agent exits.";
|
|
1801
|
+
case null:
|
|
1802
|
+
return "Role unset. Must be explicitly configured in WORKFLOW.md.";
|
|
1803
|
+
}
|
|
1959
1804
|
}
|
|
1960
1805
|
|
|
1961
1806
|
// src/skills/templates/gh-symphony-references/workflow-schema.ts
|
|
@@ -2274,6 +2119,17 @@ async function abortIfCancelled(input) {
|
|
|
2274
2119
|
}
|
|
2275
2120
|
return result;
|
|
2276
2121
|
}
|
|
2122
|
+
var SKIP_CONTEXT_DEPRECATION = "--skip-context is deprecated and is now a no-op. Repo-local .gh-symphony/context.yaml is no longer generated.";
|
|
2123
|
+
var LEGACY_GH_SYMPHONY_FILES = [
|
|
2124
|
+
{
|
|
2125
|
+
relativePath: ".gh-symphony/context.yaml",
|
|
2126
|
+
reason: "replaced by skill-local references/"
|
|
2127
|
+
},
|
|
2128
|
+
{
|
|
2129
|
+
relativePath: ".gh-symphony/reference-workflow.md",
|
|
2130
|
+
reason: null
|
|
2131
|
+
}
|
|
2132
|
+
];
|
|
2277
2133
|
function parseInitFlags(args) {
|
|
2278
2134
|
const flags = {
|
|
2279
2135
|
dryRun: false,
|
|
@@ -2314,6 +2170,9 @@ function parseInitFlags(args) {
|
|
|
2314
2170
|
}
|
|
2315
2171
|
return flags;
|
|
2316
2172
|
}
|
|
2173
|
+
function warnDeprecatedSkipContext() {
|
|
2174
|
+
p.log.warn(SKIP_CONTEXT_DEPRECATION);
|
|
2175
|
+
}
|
|
2317
2176
|
async function runInitRuntimePreflight(runtime) {
|
|
2318
2177
|
if (!isClaudeRuntime(runtime)) {
|
|
2319
2178
|
return true;
|
|
@@ -2336,6 +2195,9 @@ async function runInitRuntimePreflight(runtime) {
|
|
|
2336
2195
|
}
|
|
2337
2196
|
var handler = async (args, options) => {
|
|
2338
2197
|
const flags = parseInitFlags(args);
|
|
2198
|
+
if (flags.skipContext) {
|
|
2199
|
+
warnDeprecatedSkipContext();
|
|
2200
|
+
}
|
|
2339
2201
|
if (flags.nonInteractive) {
|
|
2340
2202
|
await runNonInteractive(flags, options);
|
|
2341
2203
|
return;
|
|
@@ -2408,6 +2270,62 @@ function runRuntimePreflight(runtime) {
|
|
|
2408
2270
|
);
|
|
2409
2271
|
}
|
|
2410
2272
|
}
|
|
2273
|
+
async function findLegacyGhSymphonyFiles(cwd) {
|
|
2274
|
+
const found = [];
|
|
2275
|
+
for (const legacyFile of LEGACY_GH_SYMPHONY_FILES) {
|
|
2276
|
+
try {
|
|
2277
|
+
await readFile3(join3(cwd, legacyFile.relativePath), "utf8");
|
|
2278
|
+
found.push(legacyFile.relativePath);
|
|
2279
|
+
} catch {
|
|
2280
|
+
}
|
|
2281
|
+
}
|
|
2282
|
+
return found;
|
|
2283
|
+
}
|
|
2284
|
+
async function removeLegacyGhSymphonyFiles(cwd, legacyFiles) {
|
|
2285
|
+
const removed = [];
|
|
2286
|
+
for (const relativePath of legacyFiles) {
|
|
2287
|
+
await rm(join3(cwd, relativePath), { force: true });
|
|
2288
|
+
removed.push(relativePath);
|
|
2289
|
+
}
|
|
2290
|
+
const legacyDir = join3(cwd, ".gh-symphony");
|
|
2291
|
+
try {
|
|
2292
|
+
const remaining = await readdir2(legacyDir);
|
|
2293
|
+
if (remaining.length === 0) {
|
|
2294
|
+
await rmdir(legacyDir);
|
|
2295
|
+
}
|
|
2296
|
+
} catch {
|
|
2297
|
+
}
|
|
2298
|
+
return removed;
|
|
2299
|
+
}
|
|
2300
|
+
async function promptLegacyGhSymphonyCleanup(cwd) {
|
|
2301
|
+
const legacyFiles = await findLegacyGhSymphonyFiles(cwd);
|
|
2302
|
+
if (legacyFiles.length === 0) {
|
|
2303
|
+
return [];
|
|
2304
|
+
}
|
|
2305
|
+
const lines = [
|
|
2306
|
+
"Found legacy .gh-symphony/ directory.",
|
|
2307
|
+
"These files are no longer used:"
|
|
2308
|
+
];
|
|
2309
|
+
for (const legacyFile of LEGACY_GH_SYMPHONY_FILES) {
|
|
2310
|
+
if (!legacyFiles.includes(legacyFile.relativePath)) {
|
|
2311
|
+
continue;
|
|
2312
|
+
}
|
|
2313
|
+
const suffix = legacyFile.reason ? ` (${legacyFile.reason})` : "";
|
|
2314
|
+
lines.push(` \u2022 ${legacyFile.relativePath}${suffix}`);
|
|
2315
|
+
}
|
|
2316
|
+
lines.push("Safe to delete.");
|
|
2317
|
+
p.log.info(lines.join("\n"));
|
|
2318
|
+
const removeFiles = await abortIfCancelled(
|
|
2319
|
+
p.confirm({
|
|
2320
|
+
message: "Remove legacy .gh-symphony/ files?",
|
|
2321
|
+
initialValue: false
|
|
2322
|
+
})
|
|
2323
|
+
);
|
|
2324
|
+
if (!removeFiles) {
|
|
2325
|
+
return [];
|
|
2326
|
+
}
|
|
2327
|
+
return removeLegacyGhSymphonyFiles(cwd, legacyFiles);
|
|
2328
|
+
}
|
|
2411
2329
|
async function resolveChangeStatus(path, content, mode) {
|
|
2412
2330
|
try {
|
|
2413
2331
|
const existing = await readFile3(path, "utf8");
|
|
@@ -2433,9 +2351,9 @@ async function writePlannedFile(file) {
|
|
|
2433
2351
|
if (file.status === "unchanged") {
|
|
2434
2352
|
return false;
|
|
2435
2353
|
}
|
|
2436
|
-
await
|
|
2354
|
+
await mkdir2(dirname2(file.path), { recursive: true });
|
|
2437
2355
|
const temporaryPath = `${file.path}.tmp`;
|
|
2438
|
-
await
|
|
2356
|
+
await writeFile2(temporaryPath, file.content, "utf8");
|
|
2439
2357
|
await rename2(temporaryPath, file.path);
|
|
2440
2358
|
if (file.executable) {
|
|
2441
2359
|
await chmod(file.path, 493);
|
|
@@ -2774,8 +2692,7 @@ async function planEcosystem(opts) {
|
|
|
2774
2692
|
statusField,
|
|
2775
2693
|
priorityField,
|
|
2776
2694
|
runtime,
|
|
2777
|
-
skipSkills
|
|
2778
|
-
skipContext
|
|
2695
|
+
skipSkills
|
|
2779
2696
|
} = opts;
|
|
2780
2697
|
const priority = opts.priority ?? (priorityField ? buildProjectFieldPriority(priorityField) : buildDisabledPriority());
|
|
2781
2698
|
const automaticLifecycle = toWorkflowLifecycleConfig(
|
|
@@ -2791,7 +2708,6 @@ async function planEcosystem(opts) {
|
|
|
2791
2708
|
planningStates: defaultBlockerCheckStates
|
|
2792
2709
|
}
|
|
2793
2710
|
);
|
|
2794
|
-
const ghSymphonyDir = join3(cwd, ".gh-symphony");
|
|
2795
2711
|
const environment = opts.environment ?? await detectEnvironment(cwd);
|
|
2796
2712
|
const files = [];
|
|
2797
2713
|
files.push(
|
|
@@ -2803,44 +2719,6 @@ async function planEcosystem(opts) {
|
|
|
2803
2719
|
executable: true
|
|
2804
2720
|
})
|
|
2805
2721
|
);
|
|
2806
|
-
if (!skipContext) {
|
|
2807
|
-
const contextYaml = buildContextYaml({
|
|
2808
|
-
projectDetail,
|
|
2809
|
-
statusField,
|
|
2810
|
-
detectedEnvironment: environment,
|
|
2811
|
-
runtime: {
|
|
2812
|
-
agent: runtime,
|
|
2813
|
-
agent_command: resolveShellAgentCommand(runtime)
|
|
2814
|
-
}
|
|
2815
|
-
});
|
|
2816
|
-
files.push(
|
|
2817
|
-
await planFileChange({
|
|
2818
|
-
path: join3(ghSymphonyDir, "context.yaml"),
|
|
2819
|
-
label: "Context metadata",
|
|
2820
|
-
content: generateContextYamlString(contextYaml),
|
|
2821
|
-
mode: "overwrite"
|
|
2822
|
-
})
|
|
2823
|
-
);
|
|
2824
|
-
}
|
|
2825
|
-
const referenceWorkflow = generateReferenceWorkflow({
|
|
2826
|
-
runtime,
|
|
2827
|
-
statusColumns: statusField.options.map((o) => ({
|
|
2828
|
-
name: o.name,
|
|
2829
|
-
role: null
|
|
2830
|
-
})),
|
|
2831
|
-
projectId: projectDetail.id,
|
|
2832
|
-
priority,
|
|
2833
|
-
lifecycle,
|
|
2834
|
-
detectedEnvironment: environment
|
|
2835
|
-
});
|
|
2836
|
-
files.push(
|
|
2837
|
-
await planFileChange({
|
|
2838
|
-
path: join3(ghSymphonyDir, "reference-workflow.md"),
|
|
2839
|
-
label: "Reference workflow",
|
|
2840
|
-
content: referenceWorkflow,
|
|
2841
|
-
mode: "overwrite"
|
|
2842
|
-
})
|
|
2843
|
-
);
|
|
2844
2722
|
const skillsDir = skipSkills ? null : resolveSkillsDir(cwd, runtime);
|
|
2845
2723
|
if (!skipSkills && skillsDir) {
|
|
2846
2724
|
const { files: plannedSkills } = buildSkillFilePlans(
|
|
@@ -2861,8 +2739,6 @@ async function planEcosystem(opts) {
|
|
|
2861
2739
|
role: null
|
|
2862
2740
|
})),
|
|
2863
2741
|
statusFieldId: statusField.id,
|
|
2864
|
-
contextYamlPath: ".gh-symphony/context.yaml",
|
|
2865
|
-
referenceWorkflowPath: ".gh-symphony/reference-workflow.md",
|
|
2866
2742
|
detectedEnvironment: environment
|
|
2867
2743
|
}
|
|
2868
2744
|
);
|
|
@@ -2893,17 +2769,8 @@ async function planEcosystem(opts) {
|
|
|
2893
2769
|
}
|
|
2894
2770
|
async function writeEcosystem(opts) {
|
|
2895
2771
|
const plan = await planEcosystem(opts);
|
|
2896
|
-
await mkdir3(join3(opts.cwd, ".gh-symphony"), { recursive: true });
|
|
2897
2772
|
const afterCreateHookPath = join3(opts.cwd, DEFAULT_AFTER_CREATE_HOOK_PATH);
|
|
2898
|
-
const contextYamlPath = join3(opts.cwd, ".gh-symphony", "context.yaml");
|
|
2899
|
-
const referenceWorkflowPath = join3(
|
|
2900
|
-
opts.cwd,
|
|
2901
|
-
".gh-symphony",
|
|
2902
|
-
"reference-workflow.md"
|
|
2903
|
-
);
|
|
2904
2773
|
let afterCreateHookWritten = false;
|
|
2905
|
-
let contextYamlWritten = false;
|
|
2906
|
-
let referenceWorkflowWritten = false;
|
|
2907
2774
|
const skillsWritten = [];
|
|
2908
2775
|
const skillsSkipped = [];
|
|
2909
2776
|
for (const file of plan.files) {
|
|
@@ -2912,14 +2779,6 @@ async function writeEcosystem(opts) {
|
|
|
2912
2779
|
afterCreateHookWritten = written;
|
|
2913
2780
|
continue;
|
|
2914
2781
|
}
|
|
2915
|
-
if (file.path === contextYamlPath) {
|
|
2916
|
-
contextYamlWritten = written;
|
|
2917
|
-
continue;
|
|
2918
|
-
}
|
|
2919
|
-
if (file.path === referenceWorkflowPath) {
|
|
2920
|
-
referenceWorkflowWritten = written;
|
|
2921
|
-
continue;
|
|
2922
|
-
}
|
|
2923
2782
|
if (file.label.startsWith("Skill ")) {
|
|
2924
2783
|
const skillName = file.label.slice("Skill ".length);
|
|
2925
2784
|
if (written) {
|
|
@@ -2939,10 +2798,11 @@ async function writeEcosystem(opts) {
|
|
|
2939
2798
|
skillsDir: plan.skillsDir,
|
|
2940
2799
|
skipSkills: plan.skipSkills,
|
|
2941
2800
|
afterCreateHookWritten,
|
|
2942
|
-
contextYamlWritten,
|
|
2943
|
-
referenceWorkflowWritten,
|
|
2801
|
+
contextYamlWritten: false,
|
|
2802
|
+
referenceWorkflowWritten: false,
|
|
2944
2803
|
skillsWritten: [...new Set(skillsWritten)].sort(),
|
|
2945
|
-
skillsSkipped: [...new Set(skillsSkipped)].sort()
|
|
2804
|
+
skillsSkipped: [...new Set(skillsSkipped)].sort(),
|
|
2805
|
+
legacyFilesRemoved: []
|
|
2946
2806
|
};
|
|
2947
2807
|
}
|
|
2948
2808
|
function formatPrioritySummaryLines(priority) {
|
|
@@ -2994,16 +2854,6 @@ function printEcosystemSummary(result, workflowPath, opts) {
|
|
|
2994
2854
|
` \u2713 ${DEFAULT_AFTER_CREATE_HOOK_LABEL.padEnd(36)} ${DEFAULT_AFTER_CREATE_HOOK_PATH}`
|
|
2995
2855
|
);
|
|
2996
2856
|
}
|
|
2997
|
-
if (result.contextYamlWritten) {
|
|
2998
|
-
lines.push(
|
|
2999
|
-
" \u2713 Context metadata .gh-symphony/context.yaml"
|
|
3000
|
-
);
|
|
3001
|
-
}
|
|
3002
|
-
if (result.referenceWorkflowWritten) {
|
|
3003
|
-
lines.push(
|
|
3004
|
-
" \u2713 Reference workflow .gh-symphony/reference-workflow.md"
|
|
3005
|
-
);
|
|
3006
|
-
}
|
|
3007
2857
|
if (result.skillsDir) {
|
|
3008
2858
|
const relSkillsDir = relative(cwd, result.skillsDir);
|
|
3009
2859
|
lines.push("");
|
|
@@ -3377,6 +3227,7 @@ async function runInteractiveStandalone(flags, _options) {
|
|
|
3377
3227
|
printDryRunPreview(outputPath, workflowPlan, ecosystemPlan);
|
|
3378
3228
|
return;
|
|
3379
3229
|
}
|
|
3230
|
+
await promptLegacyGhSymphonyCleanup(process.cwd());
|
|
3380
3231
|
await writeWorkflowPlan(workflowPlan);
|
|
3381
3232
|
const ecosystemResult = await writeEcosystem({
|
|
3382
3233
|
cwd: process.cwd(),
|
|
@@ -3400,7 +3251,9 @@ export {
|
|
|
3400
3251
|
toWorkflowLifecycleConfig,
|
|
3401
3252
|
validateStateMapping,
|
|
3402
3253
|
abortIfCancelled,
|
|
3254
|
+
warnDeprecatedSkipContext,
|
|
3403
3255
|
workflow_init_default,
|
|
3256
|
+
promptLegacyGhSymphonyCleanup,
|
|
3404
3257
|
resolveStatusField,
|
|
3405
3258
|
buildAutomaticStateMappings,
|
|
3406
3259
|
resolvePriorityField,
|