@gh-symphony/cli 0.3.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/README.md +2 -2
- package/dist/{chunk-GTNWFVFU.js → chunk-3EFLX75C.js} +5 -5
- package/dist/{chunk-RZ3WO7OV.js → chunk-3IRPSPAF.js} +1 -1
- package/dist/{chunk-VHEGRYK7.js → chunk-BBUCDKSL.js} +285 -23
- package/dist/{chunk-A7EFL6EJ.js → chunk-GOR4JGJT.js} +1 -1
- package/dist/{chunk-2ZIPQ7ZS.js → chunk-MUN7QSFF.js} +6 -3
- package/dist/{chunk-GL55AKBI.js → chunk-RU5GQG6A.js} +1112 -934
- package/dist/{chunk-5HQLPZR5.js → chunk-YKC6CGD6.js} +42 -1
- 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-3IIM4UYS.js → doctor-3WDODAKW.js} +7 -7
- package/dist/index.js +10 -10
- package/dist/{repo-SLK4DDXH.js → repo-3JLOCGRJ.js} +23 -6
- package/dist/{setup-G5VYN6FV.js → setup-AADOLSW5.js} +12 -6
- package/dist/{upgrade-BXIHAENU.js → upgrade-KTTYQQFB.js} +2 -2
- package/dist/{version-MC2KSPJB.js → version-HXAMSXVG.js} +1 -1
- package/dist/worker-entry.js +2 -2
- package/dist/{workflow-4OFPSVZ3.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,1090 +816,1273 @@ 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(
|
|
1013
|
+
lines.push("## Supported Template Variables");
|
|
1060
1014
|
lines.push("");
|
|
1061
|
-
lines.push("
|
|
1015
|
+
lines.push("Use these in the WORKFLOW.md prompt body (double-brace syntax):");
|
|
1016
|
+
lines.push("");
|
|
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) |");
|
|
1062
1027
|
lines.push("");
|
|
1063
|
-
lines.push("# \u2550\u2550\u2550 PROMPT BODY REFERENCE \u2550\u2550\u2550");
|
|
1064
1028
|
lines.push(
|
|
1065
|
-
"
|
|
1029
|
+
"**Important**: Only these 8 variables are supported. Using any other variable"
|
|
1066
1030
|
);
|
|
1031
|
+
lines.push("will cause a runtime error (strict mode validation).");
|
|
1067
1032
|
lines.push("");
|
|
1068
|
-
lines.push("##
|
|
1069
|
-
lines.push("");
|
|
1070
|
-
lines.push("| Status | Role | Agent Action |");
|
|
1071
|
-
lines.push("| ------ | ---- | ------------ |");
|
|
1072
|
-
for (const col of input.statusColumns) {
|
|
1073
|
-
const roleLabel = col.role ?? "unset";
|
|
1074
|
-
const action = resolveRoleAction(col.role);
|
|
1075
|
-
lines.push(`| ${col.name} | ${roleLabel} | ${action} |`);
|
|
1076
|
-
}
|
|
1077
|
-
if (waitColumns.length > 0) {
|
|
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
|
-
}
|
|
1033
|
+
lines.push("## Related Skills");
|
|
1086
1034
|
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");
|
|
1096
|
-
lines.push("");
|
|
1097
|
-
lines.push(
|
|
1098
|
-
"1. This is an unattended orchestration session. Do not ask humans for follow-up tasks."
|
|
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
1035
|
lines.push(
|
|
1123
|
-
"
|
|
1036
|
+
"- `/gh-project` \u2014 interact with GitHub Project v2 board (status transitions, workpad comments)"
|
|
1124
1037
|
);
|
|
1125
1038
|
lines.push(
|
|
1126
|
-
"
|
|
1039
|
+
"- `/commit` \u2014 produce clean, logical commits during implementation"
|
|
1127
1040
|
);
|
|
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");
|
|
1128
1055
|
lines.push("");
|
|
1129
|
-
lines.push("##
|
|
1056
|
+
lines.push("## Purpose");
|
|
1130
1057
|
lines.push("");
|
|
1131
1058
|
lines.push(
|
|
1132
|
-
"
|
|
1133
|
-
);
|
|
1134
|
-
lines.push(
|
|
1135
|
-
"- **commit**: Create logical-unit commits (conventional commit format)"
|
|
1059
|
+
"Interact with the GitHub Project v2 board to manage issue status,"
|
|
1136
1060
|
);
|
|
1137
|
-
lines.push("
|
|
1138
|
-
lines.push("- **pull**: Fetch latest changes and resolve conflicts");
|
|
1139
|
-
lines.push("- **land**: Create PR, request review, and handle merge");
|
|
1140
|
-
lines.push("");
|
|
1141
|
-
lines.push("## Step 0: Determine current state and route");
|
|
1061
|
+
lines.push("create workpad comments, and handle follow-up issues.");
|
|
1142
1062
|
lines.push("");
|
|
1143
|
-
lines.push(
|
|
1144
|
-
"Check the current issue state and route to the appropriate step:"
|
|
1145
|
-
);
|
|
1063
|
+
lines.push("## Prerequisites");
|
|
1146
1064
|
lines.push("");
|
|
1147
|
-
|
|
1148
|
-
const terminalNames = terminalColumns.map((c) => c.name).join(", ");
|
|
1149
|
-
lines.push(`- **${terminalNames}** \u2192 Exit immediately. Do nothing.`);
|
|
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
|
-
}
|
|
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("##
|
|
1270
|
-
lines.push("");
|
|
1271
|
-
lines.push("Workpad format to create as an issue comment:");
|
|
1169
|
+
lines.push("## Trigger");
|
|
1272
1170
|
lines.push("");
|
|
1273
|
-
lines.push("
|
|
1274
|
-
lines.push("## Workpad \u2014 {issue.identifier}");
|
|
1171
|
+
lines.push("Use this skill when creating commits during implementation.");
|
|
1275
1172
|
lines.push("");
|
|
1276
|
-
lines.push("
|
|
1277
|
-
lines.push("**Branch**: {branch name}");
|
|
1278
|
-
lines.push("**PR**: {PR URL or not created}");
|
|
1173
|
+
lines.push("## Rules");
|
|
1279
1174
|
lines.push("");
|
|
1280
|
-
lines.push("
|
|
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 { 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.map((template) => ({
|
|
1397
|
-
path: join2(skillsDir, template.name, template.fileName),
|
|
1398
|
-
content: template.generate(context)
|
|
1399
|
-
}))
|
|
1400
|
-
};
|
|
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
|
+
});
|
|
1401
1210
|
}
|
|
1402
1211
|
|
|
1403
|
-
// src/skills/templates/
|
|
1404
|
-
function
|
|
1405
|
-
const
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
"
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1212
|
+
// src/skills/templates/push.ts
|
|
1213
|
+
function generatePushSkill(_ctx) {
|
|
1214
|
+
const lines = [];
|
|
1215
|
+
lines.push("# /push \u2014 Git Push Workflow");
|
|
1216
|
+
lines.push("");
|
|
1217
|
+
lines.push("## Trigger");
|
|
1218
|
+
lines.push("");
|
|
1219
|
+
lines.push(
|
|
1220
|
+
"Use this skill when publishing local commits to the remote branch."
|
|
1221
|
+
);
|
|
1222
|
+
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");
|
|
1239
|
+
lines.push("");
|
|
1240
|
+
lines.push("- Never use `--force` (destructive)");
|
|
1241
|
+
lines.push(
|
|
1242
|
+
"- Only use `--force-with-lease` if absolutely necessary \u2014 record the reason in workpad"
|
|
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
|
+
});
|
|
1419
1251
|
}
|
|
1420
1252
|
|
|
1421
|
-
// src/skills/templates/
|
|
1422
|
-
function
|
|
1253
|
+
// src/skills/templates/pull.ts
|
|
1254
|
+
function generatePullSkill(_ctx) {
|
|
1423
1255
|
const lines = [];
|
|
1424
|
-
lines.push("# /
|
|
1256
|
+
lines.push("# /pull \u2014 Git Pull / Sync Workflow");
|
|
1425
1257
|
lines.push("");
|
|
1426
1258
|
lines.push("## Trigger");
|
|
1427
1259
|
lines.push("");
|
|
1428
|
-
lines.push(
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
lines.push("
|
|
1260
|
+
lines.push(
|
|
1261
|
+
"Use this skill to sync the current branch with the latest `origin/main`"
|
|
1262
|
+
);
|
|
1263
|
+
lines.push("before starting work or before creating a PR.");
|
|
1432
1264
|
lines.push("");
|
|
1433
|
-
lines.push("##
|
|
1265
|
+
lines.push("## Flow");
|
|
1434
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");
|
|
1435
1278
|
lines.push(
|
|
1436
|
-
|
|
1279
|
+
" - Commit the merge: `git commit` (merge commit message is auto-generated)"
|
|
1437
1280
|
);
|
|
1438
1281
|
lines.push(
|
|
1439
|
-
|
|
1282
|
+
"4. Re-run tests after merge to confirm the integrated state is clean"
|
|
1440
1283
|
);
|
|
1441
|
-
lines.push("
|
|
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`");
|
|
1442
1288
|
lines.push("");
|
|
1443
|
-
lines.push("##
|
|
1289
|
+
lines.push("## Rules");
|
|
1290
|
+
lines.push("");
|
|
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");
|
|
1305
|
+
lines.push("");
|
|
1306
|
+
lines.push("## Trigger");
|
|
1444
1307
|
lines.push("");
|
|
1445
1308
|
lines.push(
|
|
1446
|
-
"
|
|
1309
|
+
"Use this skill when the issue is in the Merging state (PR approved by human)."
|
|
1310
|
+
);
|
|
1311
|
+
lines.push(
|
|
1312
|
+
"Do NOT call `gh pr merge` directly \u2014 always go through this flow."
|
|
1447
1313
|
);
|
|
1448
|
-
for (const line of buildRepositoryValidationGuidance(
|
|
1449
|
-
ctx.detectedEnvironment
|
|
1450
|
-
)) {
|
|
1451
|
-
lines.push(`- ${line}`);
|
|
1452
|
-
}
|
|
1453
1314
|
lines.push("");
|
|
1454
|
-
lines.push("##
|
|
1315
|
+
lines.push("## Pre-flight Checks");
|
|
1455
1316
|
lines.push("");
|
|
1456
|
-
lines.push("
|
|
1457
|
-
lines.push("- **Not found** \u2192 enter **Design Mode** (create from scratch)");
|
|
1458
|
-
lines.push("- **Found** \u2192 ask user: refine existing or validate only?");
|
|
1459
|
-
lines.push(" - Refine \u2192 enter **Refine Mode**");
|
|
1460
|
-
lines.push(" - Validate \u2192 enter **Validate Mode**");
|
|
1317
|
+
lines.push("Before merging, verify ALL of the following:");
|
|
1461
1318
|
lines.push("");
|
|
1462
|
-
lines.push("
|
|
1319
|
+
lines.push("1. **PR is approved**:");
|
|
1320
|
+
lines.push(" ```bash");
|
|
1321
|
+
lines.push(
|
|
1322
|
+
` gh pr view --json reviews --jq '.reviews[] | select(.state == "APPROVED")'`
|
|
1323
|
+
);
|
|
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");
|
|
1331
|
+
lines.push(
|
|
1332
|
+
" git fetch origin && git merge-base --is-ancestor origin/main HEAD"
|
|
1333
|
+
);
|
|
1334
|
+
lines.push(" ```");
|
|
1335
|
+
lines.push(" If not up-to-date, run the `/pull` skill first.");
|
|
1463
1336
|
lines.push("");
|
|
1337
|
+
lines.push("## Flow");
|
|
1338
|
+
lines.push("");
|
|
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:");
|
|
1464
1348
|
lines.push(
|
|
1465
|
-
|
|
1349
|
+
" - Use the **gh-project skill** to transition the issue status to Done"
|
|
1466
1350
|
);
|
|
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");
|
|
1357
|
+
lines.push("");
|
|
1358
|
+
lines.push("## Rules");
|
|
1359
|
+
lines.push("");
|
|
1360
|
+
lines.push("- Never call `gh pr merge` without completing pre-flight checks");
|
|
1467
1361
|
lines.push(
|
|
1468
|
-
|
|
1362
|
+
"- Status transition to Done MUST go through the gh-project skill"
|
|
1469
1363
|
);
|
|
1470
|
-
lines.push("3. Ask the user these key questions:");
|
|
1471
|
-
lines.push(" - Which status columns should be **active** (agent works)?");
|
|
1472
|
-
lines.push(" - Which should be **wait** (agent pauses for human)?");
|
|
1473
|
-
lines.push(" - Which should be **terminal** (agent stops)?");
|
|
1474
|
-
lines.push(" - What runtime is being used? (codex / claude-code / custom)");
|
|
1475
|
-
lines.push(" - Any custom hooks needed? (after_create, before_run, etc.)");
|
|
1476
1364
|
lines.push(
|
|
1477
|
-
"
|
|
1365
|
+
"- If any pre-flight check fails, do not merge \u2014 fix the issue first"
|
|
1366
|
+
);
|
|
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.");
|
|
1413
|
+
lines.push(
|
|
1414
|
+
"# AI agents reference this file (via the /gh-symphony skill) when designing WORKFLOW.md."
|
|
1478
1415
|
);
|
|
1479
|
-
lines.push("
|
|
1416
|
+
lines.push("# Do not edit this file directly.");
|
|
1480
1417
|
lines.push("");
|
|
1481
|
-
lines.push("
|
|
1418
|
+
lines.push("---");
|
|
1482
1419
|
lines.push("");
|
|
1483
|
-
lines.push("
|
|
1484
|
-
lines.push(
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
lines.push(" - Default Posture / Agent Instructions");
|
|
1488
|
-
lines.push(" - Guardrails section");
|
|
1489
|
-
lines.push(" - Workpad Template");
|
|
1490
|
-
lines.push(" - Step 0 routing logic");
|
|
1491
|
-
lines.push("4. Propose improvements and apply with user confirmation");
|
|
1492
|
-
lines.push("5. Validate the refined file");
|
|
1420
|
+
lines.push("# \u2550\u2550\u2550 FRONT MATTER FIELD REFERENCE \u2550\u2550\u2550");
|
|
1421
|
+
lines.push(
|
|
1422
|
+
"# All front matter fields supported by the gh-symphony parser are listed below."
|
|
1423
|
+
);
|
|
1493
1424
|
lines.push("");
|
|
1494
|
-
lines.push("
|
|
1425
|
+
lines.push("tracker:");
|
|
1426
|
+
lines.push(" kind: github-project");
|
|
1427
|
+
lines.push(` project_id: ${input.projectId}`);
|
|
1428
|
+
lines.push(" state_field: Status");
|
|
1429
|
+
lines.push(...buildReferencePriorityLines(input.priority));
|
|
1495
1430
|
lines.push("");
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
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"
|
|
1500
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
|
+
}
|
|
1501
1454
|
lines.push(
|
|
1502
|
-
"
|
|
1455
|
+
...buildReferenceStringList("blocker_check_states", blockerCheckStates)
|
|
1503
1456
|
);
|
|
1504
|
-
lines.push("
|
|
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");
|
|
1505
1472
|
lines.push(
|
|
1506
|
-
"
|
|
1473
|
+
"# Linear uses repository-local polling; gh-symphony does not provide"
|
|
1507
1474
|
);
|
|
1475
|
+
lines.push("# a Linear webhook setup command.");
|
|
1508
1476
|
lines.push("");
|
|
1509
|
-
lines.push("## Supported Front Matter Fields");
|
|
1510
|
-
lines.push("");
|
|
1511
|
-
lines.push("```yaml");
|
|
1512
|
-
lines.push("tracker:");
|
|
1513
|
-
lines.push(" kind: github-project");
|
|
1514
|
-
lines.push(" project_id: PVT_xxx");
|
|
1515
|
-
lines.push(" state_field: Status");
|
|
1516
|
-
lines.push(" active_states: [Todo, In Progress]");
|
|
1517
|
-
lines.push(" terminal_states: [Done, Cancelled]");
|
|
1518
|
-
lines.push(" blocker_check_states: [Blocked]");
|
|
1519
1477
|
lines.push("polling:");
|
|
1520
1478
|
lines.push(" interval_ms: 30000");
|
|
1479
|
+
lines.push("");
|
|
1521
1480
|
lines.push("workspace:");
|
|
1522
1481
|
lines.push(" root: .runtime/symphony-workspaces");
|
|
1482
|
+
lines.push("");
|
|
1523
1483
|
lines.push("hooks:");
|
|
1524
|
-
lines.push(
|
|
1525
|
-
|
|
1484
|
+
lines.push(
|
|
1485
|
+
` after_create: ${DEFAULT_AFTER_CREATE_HOOK_PATH} # ${DEFAULT_AFTER_CREATE_HOOK_COMMENT}`
|
|
1486
|
+
);
|
|
1526
1487
|
lines.push(" before_run: null");
|
|
1527
1488
|
lines.push(" after_run: null");
|
|
1528
1489
|
lines.push(" before_remove: null");
|
|
1529
1490
|
lines.push(" timeout_ms: 60000");
|
|
1491
|
+
lines.push("");
|
|
1530
1492
|
lines.push("agent:");
|
|
1531
1493
|
lines.push(" max_concurrent_agents: 10");
|
|
1532
1494
|
lines.push(" max_retry_backoff_ms: 30000");
|
|
1533
1495
|
lines.push(" retry_base_delay_ms: 10000");
|
|
1534
1496
|
lines.push(" max_turns: 20");
|
|
1535
|
-
lines.push("codex:");
|
|
1536
|
-
lines.push(" command: codex app-server");
|
|
1537
|
-
lines.push(" read_timeout_ms: 5000");
|
|
1538
|
-
lines.push(" turn_timeout_ms: 3600000");
|
|
1539
|
-
lines.push(" stall_timeout_ms: 300000");
|
|
1540
|
-
lines.push("```");
|
|
1541
|
-
lines.push("");
|
|
1542
|
-
lines.push("## Supported Template Variables");
|
|
1543
1497
|
lines.push("");
|
|
1544
|
-
lines.push(
|
|
1498
|
+
lines.push(...buildRuntimeFrontMatter(input.runtime));
|
|
1545
1499
|
lines.push("");
|
|
1546
|
-
lines.push("
|
|
1547
|
-
lines.push("|----------|-------------|");
|
|
1548
|
-
lines.push("| `issue.identifier` | e.g. `acme/platform#42` |");
|
|
1549
|
-
lines.push("| `issue.title` | Issue title |");
|
|
1550
|
-
lines.push("| `issue.state` | Current tracker state |");
|
|
1551
|
-
lines.push("| `issue.description` | Issue body |");
|
|
1552
|
-
lines.push("| `issue.url` | Issue URL |");
|
|
1553
|
-
lines.push("| `issue.repository` | `owner/name` |");
|
|
1554
|
-
lines.push("| `issue.number` | Issue number |");
|
|
1555
|
-
lines.push("| `attempt` | Retry attempt number (null on first run) |");
|
|
1500
|
+
lines.push("---");
|
|
1556
1501
|
lines.push("");
|
|
1502
|
+
lines.push("# \u2550\u2550\u2550 PROMPT BODY REFERENCE \u2550\u2550\u2550");
|
|
1557
1503
|
lines.push(
|
|
1558
|
-
"
|
|
1504
|
+
"# GitHub Project adaptation of the Elixir Symphony reference prompt."
|
|
1559
1505
|
);
|
|
1560
|
-
lines.push("will cause a runtime error (strict mode validation).");
|
|
1561
1506
|
lines.push("");
|
|
1562
|
-
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");
|
|
1563
1535
|
lines.push("");
|
|
1564
1536
|
lines.push(
|
|
1565
|
-
"
|
|
1537
|
+
"1. This is an unattended orchestration session. Do not ask humans for follow-up tasks."
|
|
1566
1538
|
);
|
|
1567
1539
|
lines.push(
|
|
1568
|
-
"
|
|
1540
|
+
"2. Exit early only for genuine blockers (missing required credentials or secrets)."
|
|
1569
1541
|
);
|
|
1570
|
-
lines.push("- `/push` \u2014 keep remote branch current and publish updates");
|
|
1571
|
-
lines.push("- `/pull` \u2014 sync branch with latest origin/main before handoff");
|
|
1572
|
-
lines.push("- `/land` \u2014 merge approved PR and transition issue to Done");
|
|
1573
|
-
return renderSkillDocument({
|
|
1574
|
-
name: "gh-symphony",
|
|
1575
|
-
description: "Design, refine, and validate repository WORKFLOW.md files for GitHub Symphony projects.",
|
|
1576
|
-
bodyLines: lines
|
|
1577
|
-
});
|
|
1578
|
-
}
|
|
1579
|
-
|
|
1580
|
-
// src/skills/templates/gh-project.ts
|
|
1581
|
-
function generateGhProjectSkill(ctx) {
|
|
1582
|
-
const lines = [];
|
|
1583
|
-
lines.push("# /gh-project \u2014 GitHub Project v2 Status Management");
|
|
1584
|
-
lines.push("");
|
|
1585
|
-
lines.push("## Purpose");
|
|
1586
|
-
lines.push("");
|
|
1587
1542
|
lines.push(
|
|
1588
|
-
|
|
1543
|
+
'3. In your final message, report only completed work and blockers. Do not include "next steps".'
|
|
1589
1544
|
);
|
|
1590
|
-
lines.push("create workpad comments, and handle follow-up issues.");
|
|
1591
|
-
lines.push("");
|
|
1592
|
-
lines.push("## Prerequisites");
|
|
1593
|
-
lines.push("");
|
|
1594
|
-
lines.push("- `gh` CLI is authenticated (`gh auth status`)");
|
|
1595
1545
|
lines.push(
|
|
1596
|
-
|
|
1546
|
+
"4. Do not modify the issue body for planning or progress-tracking purposes."
|
|
1597
1547
|
);
|
|
1598
|
-
lines.push("");
|
|
1599
|
-
lines.push("## Column ID Quick Reference");
|
|
1600
|
-
lines.push("");
|
|
1601
|
-
lines.push(`Status Field ID: \`${ctx.statusFieldId}\``);
|
|
1602
|
-
lines.push("");
|
|
1603
|
-
lines.push("| Column Name | Role | Option ID |");
|
|
1604
|
-
lines.push("|-------------|------|-----------|");
|
|
1605
|
-
for (const col of ctx.statusColumns) {
|
|
1606
|
-
const role = col.role ?? "unknown";
|
|
1607
|
-
lines.push(`| ${col.name} | ${role} | \`${col.id}\` |`);
|
|
1608
|
-
}
|
|
1609
|
-
lines.push("");
|
|
1610
|
-
lines.push("## Operations");
|
|
1611
|
-
lines.push("");
|
|
1612
|
-
lines.push("### Change Issue Status");
|
|
1613
|
-
lines.push("");
|
|
1614
1548
|
lines.push(
|
|
1615
|
-
"
|
|
1549
|
+
"5. If the issue is in a terminal state, do nothing and exit immediately."
|
|
1616
1550
|
);
|
|
1617
|
-
lines.push("");
|
|
1618
|
-
lines.push("```bash");
|
|
1619
|
-
lines.push("# Get the project item ID for an issue");
|
|
1620
1551
|
lines.push(
|
|
1621
|
-
"
|
|
1552
|
+
"6. If you discover out-of-scope improvements, open a separate issue rather than expanding the current scope."
|
|
1622
1553
|
);
|
|
1623
1554
|
lines.push(
|
|
1624
|
-
"
|
|
1555
|
+
"7. Keep all commits as logical units and follow conventional commit format."
|
|
1625
1556
|
);
|
|
1626
|
-
lines.push("");
|
|
1627
|
-
lines.push("
|
|
1628
|
-
lines.push(
|
|
1629
|
-
lines.push(
|
|
1630
|
-
lines.push(` --id <item-id> \\`);
|
|
1631
|
-
lines.push(` --field-id ${ctx.statusFieldId} \\`);
|
|
1632
|
-
lines.push(` --single-select-option-id <option-id-from-table-above>`);
|
|
1633
|
-
lines.push("```");
|
|
1634
|
-
lines.push("");
|
|
1635
|
-
lines.push("### Create Workpad Comment");
|
|
1636
|
-
lines.push("");
|
|
1637
|
-
lines.push("```bash");
|
|
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.");
|
|
1638
1561
|
lines.push(
|
|
1639
|
-
|
|
1562
|
+
"12. When a blocker is found, record it in an issue comment and transition the status appropriately."
|
|
1563
|
+
);
|
|
1564
|
+
lines.push(
|
|
1565
|
+
"13. Once your work is complete and the PR is merged, transition the issue to the Done state."
|
|
1640
1566
|
);
|
|
1641
|
-
lines.push("```");
|
|
1642
1567
|
lines.push("");
|
|
1643
|
-
lines.push("
|
|
1568
|
+
lines.push("## Related Skills");
|
|
1644
1569
|
lines.push("");
|
|
1645
|
-
lines.push("```bash");
|
|
1646
1570
|
lines.push(
|
|
1647
|
-
"gh
|
|
1571
|
+
"- **gh-project**: Manage GitHub Project v2 issue status and update fields"
|
|
1648
1572
|
);
|
|
1649
|
-
lines.push(
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
lines.push("
|
|
1573
|
+
lines.push(
|
|
1574
|
+
"- **commit**: Create logical-unit commits (conventional commit format)"
|
|
1575
|
+
);
|
|
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");
|
|
1653
1579
|
lines.push("");
|
|
1654
|
-
lines.push("
|
|
1655
|
-
lines.push("gh issue create --repo <owner>/<repo> \\");
|
|
1656
|
-
lines.push(' --title "Follow-up: <title>" \\');
|
|
1657
|
-
lines.push(' --body "<description>" \\');
|
|
1658
|
-
lines.push(' --label "backlog"');
|
|
1659
|
-
lines.push("```");
|
|
1580
|
+
lines.push("## Step 0: Determine current state and route");
|
|
1660
1581
|
lines.push("");
|
|
1661
|
-
lines.push(
|
|
1582
|
+
lines.push(
|
|
1583
|
+
"Check the current issue state and route to the appropriate step:"
|
|
1584
|
+
);
|
|
1662
1585
|
lines.push("");
|
|
1663
|
-
|
|
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
|
+
}
|
|
1664
1600
|
lines.push(
|
|
1665
|
-
|
|
1601
|
+
"- **Other states** \u2192 Log the unclear state in an issue comment and exit."
|
|
1666
1602
|
);
|
|
1667
|
-
lines.push("```");
|
|
1668
1603
|
lines.push("");
|
|
1669
|
-
lines.push("##
|
|
1604
|
+
lines.push("## Step 1: Start/continue execution");
|
|
1670
1605
|
lines.push("");
|
|
1671
1606
|
lines.push(
|
|
1672
|
-
"
|
|
1607
|
+
"1. Read the issue body and comments to understand current progress."
|
|
1673
1608
|
);
|
|
1674
1609
|
lines.push(
|
|
1675
|
-
"
|
|
1610
|
+
"2. If an existing workpad comment is found, continue from it; otherwise create a new workpad."
|
|
1676
1611
|
);
|
|
1677
|
-
lines.push(" - All acceptance criteria checked");
|
|
1678
|
-
lines.push(" - All tests passing");
|
|
1679
|
-
lines.push(" - PR merged (if applicable)");
|
|
1680
1612
|
lines.push(
|
|
1681
|
-
"
|
|
1613
|
+
"3. See the 'Workpad Template' section below for the workpad format."
|
|
1682
1614
|
);
|
|
1683
1615
|
lines.push(
|
|
1684
|
-
"
|
|
1616
|
+
"4. If no branch exists, create a feature branch based on `{issue.repository}`."
|
|
1685
1617
|
);
|
|
1686
|
-
|
|
1687
|
-
name: "gh-project",
|
|
1688
|
-
description: "Manage GitHub Project v2 issue states, workpad comments, and related follow-up actions.",
|
|
1689
|
-
bodyLines: lines
|
|
1690
|
-
});
|
|
1691
|
-
}
|
|
1692
|
-
|
|
1693
|
-
// src/skills/templates/commit.ts
|
|
1694
|
-
function generateCommitSkill(_ctx) {
|
|
1695
|
-
const lines = [];
|
|
1696
|
-
lines.push("# /commit \u2014 Clean Commit Workflow");
|
|
1697
|
-
lines.push("");
|
|
1698
|
-
lines.push("## Trigger");
|
|
1699
|
-
lines.push("");
|
|
1700
|
-
lines.push("Use this skill when creating commits during implementation.");
|
|
1701
|
-
lines.push("");
|
|
1702
|
-
lines.push("## Rules");
|
|
1703
|
-
lines.push("");
|
|
1704
|
-
lines.push("- Commit in logical units \u2014 one concern per commit");
|
|
1705
|
-
lines.push("- Never commit a broken intermediate state (tests must pass)");
|
|
1706
|
-
lines.push("- Never commit temporary debug code or commented-out blocks");
|
|
1707
|
-
lines.push("- Run tests before every commit");
|
|
1708
|
-
lines.push("");
|
|
1709
|
-
lines.push("## Format");
|
|
1618
|
+
lines.push("5. Proceed to Step 2.");
|
|
1710
1619
|
lines.push("");
|
|
1711
|
-
lines.push("
|
|
1620
|
+
lines.push("## Step 2: Execution phase");
|
|
1712
1621
|
lines.push("");
|
|
1713
|
-
lines.push("
|
|
1714
|
-
lines.push(
|
|
1622
|
+
lines.push("1. Implement according to the issue description.");
|
|
1623
|
+
lines.push(
|
|
1624
|
+
"2. Commit changes in logical units (conventional commit format)."
|
|
1625
|
+
);
|
|
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.");
|
|
1629
|
+
lines.push(
|
|
1630
|
+
"6. After creating the PR, transition the issue status to the Human Review state."
|
|
1631
|
+
);
|
|
1632
|
+
lines.push("7. Proceed to Step 3.");
|
|
1715
1633
|
lines.push("");
|
|
1716
|
-
lines.push("
|
|
1634
|
+
lines.push("## Step 3: Human Review and merge handling");
|
|
1717
1635
|
lines.push("");
|
|
1718
|
-
lines.push("
|
|
1719
|
-
lines.push(
|
|
1636
|
+
lines.push("1. If a PR already exists, check for review comments.");
|
|
1637
|
+
lines.push(
|
|
1638
|
+
"2. If no review comments are present, remain in the waiting state."
|
|
1639
|
+
);
|
|
1640
|
+
lines.push(
|
|
1641
|
+
"3. If the PR is merged, transition the issue to a terminal state."
|
|
1642
|
+
);
|
|
1643
|
+
lines.push("4. If review changes are requested, proceed to Step 4.");
|
|
1720
1644
|
lines.push("");
|
|
1721
|
-
lines.push("
|
|
1645
|
+
lines.push("## Step 4: Rework handling");
|
|
1722
1646
|
lines.push("");
|
|
1723
1647
|
lines.push(
|
|
1724
|
-
"
|
|
1648
|
+
"1. Read all PR review comments and identify the requested changes."
|
|
1725
1649
|
);
|
|
1650
|
+
lines.push(
|
|
1651
|
+
"2. Process the changes following the PR Feedback Sweep Protocol."
|
|
1652
|
+
);
|
|
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.");
|
|
1726
1656
|
lines.push("");
|
|
1727
|
-
lines.push("##
|
|
1728
|
-
lines.push("");
|
|
1729
|
-
lines.push("```");
|
|
1730
|
-
lines.push("feat(auth): add OAuth2 token refresh");
|
|
1731
|
-
lines.push("fix(api): handle null response from upstream");
|
|
1732
|
-
lines.push("test(worker): add retry exhaustion coverage");
|
|
1733
|
-
lines.push("```");
|
|
1734
|
-
return renderSkillDocument({
|
|
1735
|
-
name: "commit",
|
|
1736
|
-
description: "Create clean, logically scoped commits that keep the repository in a shippable state.",
|
|
1737
|
-
bodyLines: lines
|
|
1738
|
-
});
|
|
1739
|
-
}
|
|
1740
|
-
|
|
1741
|
-
// src/skills/templates/push.ts
|
|
1742
|
-
function generatePushSkill(_ctx) {
|
|
1743
|
-
const lines = [];
|
|
1744
|
-
lines.push("# /push \u2014 Git Push Workflow");
|
|
1657
|
+
lines.push("## PR Feedback Sweep Protocol");
|
|
1745
1658
|
lines.push("");
|
|
1746
|
-
lines.push("
|
|
1659
|
+
lines.push("Order for processing PR review feedback:");
|
|
1747
1660
|
lines.push("");
|
|
1748
1661
|
lines.push(
|
|
1749
|
-
"
|
|
1662
|
+
"1. **Collect all comments**: List all unresolved review comments."
|
|
1750
1663
|
);
|
|
1664
|
+
lines.push(
|
|
1665
|
+
"2. **Triage by priority**: Handle blocking comments before non-blocking ones."
|
|
1666
|
+
);
|
|
1667
|
+
lines.push(
|
|
1668
|
+
"3. **Implement changes**: Make the code changes corresponding to each comment."
|
|
1669
|
+
);
|
|
1670
|
+
lines.push(
|
|
1671
|
+
"4. **Reply to comments**: Respond to each review comment with a description of what was done."
|
|
1672
|
+
);
|
|
1673
|
+
lines.push(
|
|
1674
|
+
"5. **Commit**: Commit changes in `fix: address PR review feedback` format."
|
|
1675
|
+
);
|
|
1676
|
+
lines.push("6. **Request re-review**: Ask the reviewer for a re-review.");
|
|
1751
1677
|
lines.push("");
|
|
1752
|
-
lines.push("##
|
|
1753
|
-
lines.push("");
|
|
1754
|
-
lines.push("1. Run local tests and lint \u2014 ensure they pass before pushing");
|
|
1755
|
-
lines.push("2. Push to remote:");
|
|
1756
|
-
lines.push(" ```bash");
|
|
1757
|
-
lines.push(" git push origin <branch> # subsequent pushes");
|
|
1758
|
-
lines.push(" git push -u origin <branch> # first push (sets upstream)");
|
|
1759
|
-
lines.push(" ```");
|
|
1760
|
-
lines.push("3. If push is rejected (non-fast-forward):");
|
|
1761
|
-
lines.push(" - Run `git fetch origin && git merge origin/main`");
|
|
1762
|
-
lines.push(" - Resolve any conflicts");
|
|
1763
|
-
lines.push(" - Re-run tests");
|
|
1764
|
-
lines.push(" - Push again");
|
|
1765
|
-
lines.push("4. Record push result in workpad Notes");
|
|
1678
|
+
lines.push("## Completion Bar");
|
|
1766
1679
|
lines.push("");
|
|
1767
|
-
lines.push("
|
|
1680
|
+
lines.push("All of the following must be satisfied before creating a PR:");
|
|
1768
1681
|
lines.push("");
|
|
1769
|
-
lines.push("- Never use `--force` (destructive)");
|
|
1770
1682
|
lines.push(
|
|
1771
|
-
"-
|
|
1683
|
+
"- [ ] All requirements from the issue description are implemented."
|
|
1772
1684
|
);
|
|
1773
|
-
lines.push("-
|
|
1774
|
-
lines.push("-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
bodyLines: lines
|
|
1779
|
-
});
|
|
1780
|
-
}
|
|
1781
|
-
|
|
1782
|
-
// src/skills/templates/pull.ts
|
|
1783
|
-
function generatePullSkill(_ctx) {
|
|
1784
|
-
const lines = [];
|
|
1785
|
-
lines.push("# /pull \u2014 Git Pull / Sync Workflow");
|
|
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).");
|
|
1786
1690
|
lines.push("");
|
|
1787
|
-
lines.push("##
|
|
1691
|
+
lines.push("## Guardrails");
|
|
1788
1692
|
lines.push("");
|
|
1693
|
+
lines.push("- **Scope**: Never make changes outside the scope of the issue.");
|
|
1789
1694
|
lines.push(
|
|
1790
|
-
"
|
|
1695
|
+
"- **Secrets**: Never hardcode tokens, passwords, or API keys in code."
|
|
1791
1696
|
);
|
|
1792
|
-
lines.push("before starting work or before creating a PR.");
|
|
1793
|
-
lines.push("");
|
|
1794
|
-
lines.push("## Flow");
|
|
1795
|
-
lines.push("");
|
|
1796
|
-
lines.push("1. Fetch latest from remote:");
|
|
1797
|
-
lines.push(" ```bash");
|
|
1798
|
-
lines.push(" git fetch origin");
|
|
1799
|
-
lines.push(" ```");
|
|
1800
|
-
lines.push("2. Merge into current branch:");
|
|
1801
|
-
lines.push(" ```bash");
|
|
1802
|
-
lines.push(" git merge origin/main");
|
|
1803
|
-
lines.push(" ```");
|
|
1804
|
-
lines.push("3. If conflicts arise:");
|
|
1805
|
-
lines.push(" - Resolve each conflict file");
|
|
1806
|
-
lines.push(" - Run tests to confirm nothing broke");
|
|
1807
1697
|
lines.push(
|
|
1808
|
-
"
|
|
1698
|
+
"- **Breaking changes**: Do not modify existing APIs or interfaces without explicit authorization."
|
|
1809
1699
|
);
|
|
1700
|
+
lines.push("- **Force push**: Do not force-push to the main/master branch.");
|
|
1810
1701
|
lines.push(
|
|
1811
|
-
"
|
|
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."
|
|
1812
1706
|
);
|
|
1813
|
-
lines.push("5. Record pull skill evidence in workpad Notes:");
|
|
1814
|
-
lines.push(" - merge source (e.g. `origin/main`)");
|
|
1815
|
-
lines.push(" - result: `clean` or `conflicts resolved`");
|
|
1816
|
-
lines.push(" - resulting HEAD short SHA: `git rev-parse --short HEAD`");
|
|
1817
1707
|
lines.push("");
|
|
1818
|
-
lines.push("##
|
|
1708
|
+
lines.push("## Workpad Template");
|
|
1819
1709
|
lines.push("");
|
|
1820
|
-
lines.push("
|
|
1821
|
-
lines.push("- Always pull at the start of a new work session");
|
|
1822
|
-
lines.push("- Record the pull evidence in the workpad before proceeding");
|
|
1823
|
-
return renderSkillDocument({
|
|
1824
|
-
name: "pull",
|
|
1825
|
-
description: "Sync the current branch with the latest remote base before implementation or review handoff.",
|
|
1826
|
-
bodyLines: lines
|
|
1827
|
-
});
|
|
1828
|
-
}
|
|
1829
|
-
|
|
1830
|
-
// src/skills/templates/land.ts
|
|
1831
|
-
function generateLandSkill(_ctx) {
|
|
1832
|
-
const lines = [];
|
|
1833
|
-
lines.push("# /land \u2014 PR Merge Workflow");
|
|
1710
|
+
lines.push("Workpad format to create as an issue comment:");
|
|
1834
1711
|
lines.push("");
|
|
1835
|
-
lines.push("
|
|
1712
|
+
lines.push("```markdown");
|
|
1713
|
+
lines.push("## Workpad \u2014 {issue.identifier}");
|
|
1836
1714
|
lines.push("");
|
|
1837
|
-
lines.push(
|
|
1838
|
-
|
|
1839
|
-
);
|
|
1840
|
-
lines.push(
|
|
1841
|
-
"Do NOT call `gh pr merge` directly \u2014 always go through this flow."
|
|
1842
|
-
);
|
|
1715
|
+
lines.push("**Status**: {current phase}");
|
|
1716
|
+
lines.push("**Branch**: {branch name}");
|
|
1717
|
+
lines.push("**PR**: {PR URL or not created}");
|
|
1843
1718
|
lines.push("");
|
|
1844
|
-
lines.push("
|
|
1719
|
+
lines.push("### Plan");
|
|
1845
1720
|
lines.push("");
|
|
1846
|
-
lines.push("
|
|
1721
|
+
lines.push("- [ ] {task 1}");
|
|
1722
|
+
lines.push("- [ ] {task 2}");
|
|
1847
1723
|
lines.push("");
|
|
1848
|
-
lines.push("
|
|
1849
|
-
lines.push("
|
|
1850
|
-
lines.push(
|
|
1851
|
-
|
|
1852
|
-
);
|
|
1853
|
-
lines.push("
|
|
1854
|
-
lines.push("
|
|
1855
|
-
lines.push("
|
|
1856
|
-
lines.push("
|
|
1857
|
-
lines.
|
|
1858
|
-
|
|
1859
|
-
|
|
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");
|
|
1734
|
+
}
|
|
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
|
+
}
|
|
1860
1752
|
lines.push(
|
|
1861
|
-
"
|
|
1753
|
+
" # See docs/adr/2026-05-18_explicit-dispatch-priority-mappings.md"
|
|
1862
1754
|
);
|
|
1863
|
-
|
|
1864
|
-
|
|
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");
|
|
1865
1776
|
lines.push("");
|
|
1866
|
-
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");
|
|
1867
1784
|
lines.push("");
|
|
1868
|
-
lines.push("
|
|
1869
|
-
lines.push("
|
|
1870
|
-
lines.push("
|
|
1871
|
-
lines.push("
|
|
1872
|
-
lines.push("
|
|
1873
|
-
lines.push("
|
|
1874
|
-
lines
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
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
|
+
}
|
|
1804
|
+
}
|
|
1805
|
+
|
|
1806
|
+
// src/skills/templates/gh-symphony-references/workflow-schema.ts
|
|
1807
|
+
function generateWorkflowSchemaReference(ctx) {
|
|
1808
|
+
const reference = generateReferenceWorkflow({
|
|
1809
|
+
runtime: ctx.runtime,
|
|
1810
|
+
statusColumns: ctx.statusColumns.map((column) => ({
|
|
1811
|
+
name: column.name,
|
|
1812
|
+
role: column.role
|
|
1813
|
+
})),
|
|
1814
|
+
projectId: ctx.projectId,
|
|
1815
|
+
priority: null,
|
|
1816
|
+
detectedEnvironment: ctx.detectedEnvironment
|
|
1817
|
+
});
|
|
1818
|
+
return [
|
|
1819
|
+
reference,
|
|
1820
|
+
"",
|
|
1821
|
+
"## Supported Template Variables",
|
|
1822
|
+
"",
|
|
1823
|
+
"Use these in the WORKFLOW.md prompt body with double-brace syntax.",
|
|
1824
|
+
"",
|
|
1825
|
+
"| Variable | Description |",
|
|
1826
|
+
"| -------- | ----------- |",
|
|
1827
|
+
"| `issue.identifier` | Issue identifier, for example `acme/platform#42`. |",
|
|
1828
|
+
"| `issue.title` | Issue title. |",
|
|
1829
|
+
"| `issue.state` | Current tracker state. |",
|
|
1830
|
+
"| `issue.description` | Issue body. |",
|
|
1831
|
+
"| `issue.url` | Issue URL. |",
|
|
1832
|
+
"| `issue.repository` | Repository in `owner/name` form. |",
|
|
1833
|
+
"| `issue.number` | Issue number. |",
|
|
1834
|
+
"| `attempt` | Retry attempt number, or null on the first run. |",
|
|
1835
|
+
"",
|
|
1836
|
+
"Only these variables are supported by strict-mode prompt rendering."
|
|
1837
|
+
].join("\n");
|
|
1838
|
+
}
|
|
1839
|
+
|
|
1840
|
+
// src/skills/templates/gh-symphony-references/workflow-posture-implement.ts
|
|
1841
|
+
function generateWorkflowPostureImplementReference(ctx) {
|
|
1842
|
+
const validationGuidance = buildRepositoryValidationGuidance(
|
|
1843
|
+
ctx.detectedEnvironment
|
|
1879
1844
|
);
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
"
|
|
1845
|
+
return [
|
|
1846
|
+
"# Workflow posture: implement",
|
|
1847
|
+
"",
|
|
1848
|
+
"Use this prompt-body posture when the agent should write features or fix bugs.",
|
|
1849
|
+
"This is the default posture and preserves the current generated WORKFLOW.md",
|
|
1850
|
+
"prompt-body behavior.",
|
|
1851
|
+
"",
|
|
1852
|
+
"## Agent Instructions",
|
|
1853
|
+
"",
|
|
1854
|
+
'You are an AI coding agent working on issue `{issue.identifier}`: "`{issue.title}`".',
|
|
1855
|
+
"",
|
|
1856
|
+
"**Repository:** `{issue.repository}`",
|
|
1857
|
+
"**Current state:** `{issue.state}`",
|
|
1858
|
+
"",
|
|
1859
|
+
"### Task",
|
|
1860
|
+
"",
|
|
1861
|
+
"`{issue.description}`",
|
|
1862
|
+
"",
|
|
1863
|
+
"### Default Posture",
|
|
1864
|
+
"",
|
|
1865
|
+
"1. This is an unattended orchestration session. Do not ask humans for follow-up actions.",
|
|
1866
|
+
"2. Only abort early if there is a genuine blocker (missing required credentials or secrets).",
|
|
1867
|
+
'3. In your final message, report only what was completed and any blockers. Do not include "next steps".',
|
|
1868
|
+
"",
|
|
1869
|
+
"### Repository Validation Guidance",
|
|
1870
|
+
"",
|
|
1871
|
+
...validationGuidance.map((line, index) => `${index + 1}. ${line}`),
|
|
1872
|
+
"",
|
|
1873
|
+
"### Workflow",
|
|
1874
|
+
"",
|
|
1875
|
+
"1. Read the issue description and understand the requirements.",
|
|
1876
|
+
"2. Explore the codebase to understand the relevant code structure.",
|
|
1877
|
+
"3. Implement the changes following the project's coding conventions.",
|
|
1878
|
+
"4. Write or update tests to cover the changes.",
|
|
1879
|
+
"5. Verify that all existing tests pass.",
|
|
1880
|
+
"6. Create a PR with a clear description of the changes.",
|
|
1881
|
+
"",
|
|
1882
|
+
"### Guardrails",
|
|
1883
|
+
"",
|
|
1884
|
+
"- Do not edit the issue body for planning or progress tracking.",
|
|
1885
|
+
"- If the issue is in a terminal state, do nothing and exit.",
|
|
1886
|
+
"- If you find out-of-scope improvements, open a separate issue rather than expanding the current scope.",
|
|
1887
|
+
"",
|
|
1888
|
+
"### Workpad Template",
|
|
1889
|
+
"",
|
|
1890
|
+
"Create a workpad comment on the issue with the following structure to track progress:",
|
|
1891
|
+
"",
|
|
1892
|
+
"```md",
|
|
1893
|
+
"## Workpad",
|
|
1894
|
+
"",
|
|
1895
|
+
"### Plan",
|
|
1896
|
+
"",
|
|
1897
|
+
"- [ ] 1. Task item",
|
|
1898
|
+
"",
|
|
1899
|
+
"### Acceptance Criteria",
|
|
1900
|
+
"",
|
|
1901
|
+
"- [ ] Criterion 1",
|
|
1902
|
+
"",
|
|
1903
|
+
"### Validation",
|
|
1904
|
+
"",
|
|
1905
|
+
"- [ ] Test: `command`",
|
|
1906
|
+
"",
|
|
1907
|
+
"### Notes",
|
|
1908
|
+
"",
|
|
1909
|
+
"- Progress notes",
|
|
1910
|
+
"```"
|
|
1911
|
+
].join("\n");
|
|
1912
|
+
}
|
|
1913
|
+
|
|
1914
|
+
// src/skills/templates/gh-symphony-references/workflow-posture-review.ts
|
|
1915
|
+
function generateWorkflowPostureReviewReference(ctx) {
|
|
1916
|
+
const validationGuidance = buildRepositoryValidationGuidance(
|
|
1917
|
+
ctx.detectedEnvironment
|
|
1892
1918
|
);
|
|
1893
|
-
|
|
1894
|
-
"
|
|
1919
|
+
return [
|
|
1920
|
+
"# Workflow posture: review",
|
|
1921
|
+
"",
|
|
1922
|
+
"Use this prompt-body posture when the agent should review PRs and leave",
|
|
1923
|
+
"comments. This posture is read-only for repository code.",
|
|
1924
|
+
"",
|
|
1925
|
+
"## Agent Instructions",
|
|
1926
|
+
"",
|
|
1927
|
+
'You are an AI code-review agent working on issue `{issue.identifier}`: "`{issue.title}`".',
|
|
1928
|
+
"",
|
|
1929
|
+
"**Repository:** `{issue.repository}`",
|
|
1930
|
+
"**Current state:** `{issue.state}`",
|
|
1931
|
+
"",
|
|
1932
|
+
"### Task",
|
|
1933
|
+
"",
|
|
1934
|
+
"`{issue.description}`",
|
|
1935
|
+
"",
|
|
1936
|
+
"### Default Posture",
|
|
1937
|
+
"",
|
|
1938
|
+
"1. Review linked pull requests. Do NOT write code, push commits, or open new PRs.",
|
|
1939
|
+
"2. Treat failing required tests as grounds to request changes unless the failure is clearly unrelated and documented.",
|
|
1940
|
+
"3. In your final message, report only the review outcome and any blockers. Do not include follow-up work for the human unless it is required to unblock review.",
|
|
1941
|
+
"",
|
|
1942
|
+
"### Repository Validation Guidance",
|
|
1943
|
+
"",
|
|
1944
|
+
...validationGuidance.map((line, index) => `${index + 1}. ${line}`),
|
|
1945
|
+
"",
|
|
1946
|
+
"### Workflow",
|
|
1947
|
+
"",
|
|
1948
|
+
"1. Find the PR linked from the issue, project item, or issue timeline.",
|
|
1949
|
+
"2. Read the PR title, body, diff, linked issue, existing reviews, inline comments, and check status.",
|
|
1950
|
+
"3. Run the repository's relevant tests, lint, typecheck, or build commands when available and practical.",
|
|
1951
|
+
"4. Leave inline review comments for concrete, actionable findings.",
|
|
1952
|
+
"5. Submit a summary review: approve only when the change is correct and validation is acceptable; otherwise request changes.",
|
|
1953
|
+
"",
|
|
1954
|
+
"### Guardrails",
|
|
1955
|
+
"",
|
|
1956
|
+
"- Never push code from this posture.",
|
|
1957
|
+
"- Never approve PRs that introduce new dependencies without explicitly noting the dependency risk and why it is acceptable.",
|
|
1958
|
+
"- If relevant tests fail and the failure is not proven unrelated, request changes.",
|
|
1959
|
+
"- Keep comments specific to correctness, maintainability, tests, security, and issue fit.",
|
|
1960
|
+
"- Do not create a workpad; the review threads on the PR are the audit trail."
|
|
1961
|
+
].join("\n");
|
|
1962
|
+
}
|
|
1963
|
+
|
|
1964
|
+
// src/skills/templates/gh-symphony-references/workflow-posture-maintain.ts
|
|
1965
|
+
function generateWorkflowPostureMaintainReference(ctx) {
|
|
1966
|
+
const validationGuidance = buildRepositoryValidationGuidance(
|
|
1967
|
+
ctx.detectedEnvironment
|
|
1895
1968
|
);
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1969
|
+
return [
|
|
1970
|
+
"# Workflow posture: maintain",
|
|
1971
|
+
"",
|
|
1972
|
+
"Use this prompt-body posture for low-risk maintenance such as dependency",
|
|
1973
|
+
"bumps, lint sweeps, small chores, and repository hygiene.",
|
|
1974
|
+
"",
|
|
1975
|
+
"## Agent Instructions",
|
|
1976
|
+
"",
|
|
1977
|
+
'You are a maintenance coding agent working on issue `{issue.identifier}`: "`{issue.title}`".',
|
|
1978
|
+
"",
|
|
1979
|
+
"**Repository:** `{issue.repository}`",
|
|
1980
|
+
"**Current state:** `{issue.state}`",
|
|
1981
|
+
"",
|
|
1982
|
+
"### Task",
|
|
1983
|
+
"",
|
|
1984
|
+
"`{issue.description}`",
|
|
1985
|
+
"",
|
|
1986
|
+
"### Default Posture",
|
|
1987
|
+
"",
|
|
1988
|
+
"1. Make the smallest possible change that satisfies the maintenance request.",
|
|
1989
|
+
"2. Defer human-judgment calls instead of broadening scope.",
|
|
1990
|
+
"3. In your final message, report only what was completed and any blockers. Do not include optional next steps.",
|
|
1991
|
+
"",
|
|
1992
|
+
"### Repository Validation Guidance",
|
|
1993
|
+
"",
|
|
1994
|
+
...validationGuidance.map((line, index) => `${index + 1}. ${line}`),
|
|
1995
|
+
"",
|
|
1996
|
+
"### Workflow",
|
|
1997
|
+
"",
|
|
1998
|
+
"1. Identify the exact maintenance task and affected files.",
|
|
1999
|
+
"2. Make the minimal change needed; avoid drive-by refactors.",
|
|
2000
|
+
"3. Run the relevant tests, lint, typecheck, or build commands for the affected area.",
|
|
2001
|
+
"4. Create a PR when the change is complete, or exit with a blocker note if approval is required.",
|
|
2002
|
+
"",
|
|
2003
|
+
"### Guardrails",
|
|
2004
|
+
"",
|
|
2005
|
+
"- Do not perform major dependency bumps without explicit approval.",
|
|
2006
|
+
"- Do not delete files without confirmation unless the issue explicitly requests it.",
|
|
2007
|
+
"- If the implementation exceeds 50 lines of non-generated code, stop and ask for human confirmation before continuing.",
|
|
2008
|
+
"- Do not mix unrelated cleanup into the maintenance change.",
|
|
2009
|
+
"",
|
|
2010
|
+
"### Workpad Template",
|
|
2011
|
+
"",
|
|
2012
|
+
"Create a compact workpad comment on the issue with the following structure:",
|
|
2013
|
+
"",
|
|
2014
|
+
"```md",
|
|
2015
|
+
"## Workpad",
|
|
2016
|
+
"",
|
|
2017
|
+
"### Plan",
|
|
2018
|
+
"",
|
|
2019
|
+
"- [ ] Minimal maintenance change",
|
|
2020
|
+
"- [ ] Validation and PR handoff",
|
|
2021
|
+
"",
|
|
2022
|
+
"### Validation",
|
|
2023
|
+
"",
|
|
2024
|
+
"- [ ] Test/lint/typecheck/build command",
|
|
2025
|
+
"",
|
|
2026
|
+
"### Blockers",
|
|
2027
|
+
"",
|
|
2028
|
+
"None",
|
|
2029
|
+
"```"
|
|
2030
|
+
].join("\n");
|
|
1902
2031
|
}
|
|
1903
2032
|
|
|
2033
|
+
// src/skills/templates/gh-symphony-references/index.ts
|
|
2034
|
+
var GH_SYMPHONY_REFERENCE_FILES = [
|
|
2035
|
+
{
|
|
2036
|
+
relativePath: "references/README.md",
|
|
2037
|
+
generate: generateGhSymphonyReferencesReadme
|
|
2038
|
+
},
|
|
2039
|
+
{
|
|
2040
|
+
relativePath: "references/workflow-schema.md",
|
|
2041
|
+
generate: generateWorkflowSchemaReference
|
|
2042
|
+
},
|
|
2043
|
+
{
|
|
2044
|
+
relativePath: "references/workflow-posture-implement.md",
|
|
2045
|
+
generate: generateWorkflowPostureImplementReference
|
|
2046
|
+
},
|
|
2047
|
+
{
|
|
2048
|
+
relativePath: "references/workflow-posture-review.md",
|
|
2049
|
+
generate: generateWorkflowPostureReviewReference
|
|
2050
|
+
},
|
|
2051
|
+
{
|
|
2052
|
+
relativePath: "references/workflow-posture-maintain.md",
|
|
2053
|
+
generate: generateWorkflowPostureMaintainReference
|
|
2054
|
+
}
|
|
2055
|
+
];
|
|
2056
|
+
|
|
1904
2057
|
// src/skills/templates/index.ts
|
|
1905
2058
|
var ALL_SKILL_TEMPLATES = [
|
|
1906
2059
|
{
|
|
1907
2060
|
name: "gh-symphony",
|
|
1908
|
-
|
|
1909
|
-
|
|
2061
|
+
files: [
|
|
2062
|
+
{ relativePath: "SKILL.md", generate: generateGhSymphonySkill },
|
|
2063
|
+
...GH_SYMPHONY_REFERENCE_FILES
|
|
2064
|
+
]
|
|
1910
2065
|
},
|
|
1911
2066
|
{
|
|
1912
2067
|
name: "gh-project",
|
|
1913
|
-
|
|
1914
|
-
|
|
2068
|
+
files: [{ relativePath: "SKILL.md", generate: generateGhProjectSkill }]
|
|
2069
|
+
},
|
|
2070
|
+
{
|
|
2071
|
+
name: "commit",
|
|
2072
|
+
files: [{ relativePath: "SKILL.md", generate: generateCommitSkill }]
|
|
2073
|
+
},
|
|
2074
|
+
{
|
|
2075
|
+
name: "push",
|
|
2076
|
+
files: [{ relativePath: "SKILL.md", generate: generatePushSkill }]
|
|
2077
|
+
},
|
|
2078
|
+
{
|
|
2079
|
+
name: "pull",
|
|
2080
|
+
files: [{ relativePath: "SKILL.md", generate: generatePullSkill }]
|
|
1915
2081
|
},
|
|
1916
|
-
{
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
2082
|
+
{
|
|
2083
|
+
name: "land",
|
|
2084
|
+
files: [{ relativePath: "SKILL.md", generate: generateLandSkill }]
|
|
2085
|
+
}
|
|
1920
2086
|
];
|
|
1921
2087
|
|
|
1922
2088
|
// src/commands/workflow-init.ts
|
|
@@ -1953,6 +2119,17 @@ async function abortIfCancelled(input) {
|
|
|
1953
2119
|
}
|
|
1954
2120
|
return result;
|
|
1955
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
|
+
];
|
|
1956
2133
|
function parseInitFlags(args) {
|
|
1957
2134
|
const flags = {
|
|
1958
2135
|
dryRun: false,
|
|
@@ -1993,6 +2170,9 @@ function parseInitFlags(args) {
|
|
|
1993
2170
|
}
|
|
1994
2171
|
return flags;
|
|
1995
2172
|
}
|
|
2173
|
+
function warnDeprecatedSkipContext() {
|
|
2174
|
+
p.log.warn(SKIP_CONTEXT_DEPRECATION);
|
|
2175
|
+
}
|
|
1996
2176
|
async function runInitRuntimePreflight(runtime) {
|
|
1997
2177
|
if (!isClaudeRuntime(runtime)) {
|
|
1998
2178
|
return true;
|
|
@@ -2015,6 +2195,9 @@ async function runInitRuntimePreflight(runtime) {
|
|
|
2015
2195
|
}
|
|
2016
2196
|
var handler = async (args, options) => {
|
|
2017
2197
|
const flags = parseInitFlags(args);
|
|
2198
|
+
if (flags.skipContext) {
|
|
2199
|
+
warnDeprecatedSkipContext();
|
|
2200
|
+
}
|
|
2018
2201
|
if (flags.nonInteractive) {
|
|
2019
2202
|
await runNonInteractive(flags, options);
|
|
2020
2203
|
return;
|
|
@@ -2087,6 +2270,62 @@ function runRuntimePreflight(runtime) {
|
|
|
2087
2270
|
);
|
|
2088
2271
|
}
|
|
2089
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
|
+
}
|
|
2090
2329
|
async function resolveChangeStatus(path, content, mode) {
|
|
2091
2330
|
try {
|
|
2092
2331
|
const existing = await readFile3(path, "utf8");
|
|
@@ -2112,15 +2351,18 @@ async function writePlannedFile(file) {
|
|
|
2112
2351
|
if (file.status === "unchanged") {
|
|
2113
2352
|
return false;
|
|
2114
2353
|
}
|
|
2115
|
-
await
|
|
2354
|
+
await mkdir2(dirname2(file.path), { recursive: true });
|
|
2116
2355
|
const temporaryPath = `${file.path}.tmp`;
|
|
2117
|
-
await
|
|
2356
|
+
await writeFile2(temporaryPath, file.content, "utf8");
|
|
2118
2357
|
await rename2(temporaryPath, file.path);
|
|
2119
2358
|
if (file.executable) {
|
|
2120
2359
|
await chmod(file.path, 493);
|
|
2121
2360
|
}
|
|
2122
2361
|
return true;
|
|
2123
2362
|
}
|
|
2363
|
+
function skillNameForPath(skillsDir, filePath) {
|
|
2364
|
+
return relative(skillsDir, filePath).split(/[\\/]/)[0] ?? "";
|
|
2365
|
+
}
|
|
2124
2366
|
function resolveStatusField(projectDetail) {
|
|
2125
2367
|
return projectDetail.statusFields.find((f) => f.name.toLowerCase() === "status") ?? projectDetail.statusFields[0] ?? null;
|
|
2126
2368
|
}
|
|
@@ -2450,8 +2692,7 @@ async function planEcosystem(opts) {
|
|
|
2450
2692
|
statusField,
|
|
2451
2693
|
priorityField,
|
|
2452
2694
|
runtime,
|
|
2453
|
-
skipSkills
|
|
2454
|
-
skipContext
|
|
2695
|
+
skipSkills
|
|
2455
2696
|
} = opts;
|
|
2456
2697
|
const priority = opts.priority ?? (priorityField ? buildProjectFieldPriority(priorityField) : buildDisabledPriority());
|
|
2457
2698
|
const automaticLifecycle = toWorkflowLifecycleConfig(
|
|
@@ -2467,7 +2708,6 @@ async function planEcosystem(opts) {
|
|
|
2467
2708
|
planningStates: defaultBlockerCheckStates
|
|
2468
2709
|
}
|
|
2469
2710
|
);
|
|
2470
|
-
const ghSymphonyDir = join3(cwd, ".gh-symphony");
|
|
2471
2711
|
const environment = opts.environment ?? await detectEnvironment(cwd);
|
|
2472
2712
|
const files = [];
|
|
2473
2713
|
files.push(
|
|
@@ -2479,44 +2719,6 @@ async function planEcosystem(opts) {
|
|
|
2479
2719
|
executable: true
|
|
2480
2720
|
})
|
|
2481
2721
|
);
|
|
2482
|
-
if (!skipContext) {
|
|
2483
|
-
const contextYaml = buildContextYaml({
|
|
2484
|
-
projectDetail,
|
|
2485
|
-
statusField,
|
|
2486
|
-
detectedEnvironment: environment,
|
|
2487
|
-
runtime: {
|
|
2488
|
-
agent: runtime,
|
|
2489
|
-
agent_command: resolveShellAgentCommand(runtime)
|
|
2490
|
-
}
|
|
2491
|
-
});
|
|
2492
|
-
files.push(
|
|
2493
|
-
await planFileChange({
|
|
2494
|
-
path: join3(ghSymphonyDir, "context.yaml"),
|
|
2495
|
-
label: "Context metadata",
|
|
2496
|
-
content: generateContextYamlString(contextYaml),
|
|
2497
|
-
mode: "overwrite"
|
|
2498
|
-
})
|
|
2499
|
-
);
|
|
2500
|
-
}
|
|
2501
|
-
const referenceWorkflow = generateReferenceWorkflow({
|
|
2502
|
-
runtime,
|
|
2503
|
-
statusColumns: statusField.options.map((o) => ({
|
|
2504
|
-
name: o.name,
|
|
2505
|
-
role: null
|
|
2506
|
-
})),
|
|
2507
|
-
projectId: projectDetail.id,
|
|
2508
|
-
priority,
|
|
2509
|
-
lifecycle,
|
|
2510
|
-
detectedEnvironment: environment
|
|
2511
|
-
});
|
|
2512
|
-
files.push(
|
|
2513
|
-
await planFileChange({
|
|
2514
|
-
path: join3(ghSymphonyDir, "reference-workflow.md"),
|
|
2515
|
-
label: "Reference workflow",
|
|
2516
|
-
content: referenceWorkflow,
|
|
2517
|
-
mode: "overwrite"
|
|
2518
|
-
})
|
|
2519
|
-
);
|
|
2520
2722
|
const skillsDir = skipSkills ? null : resolveSkillsDir(cwd, runtime);
|
|
2521
2723
|
if (!skipSkills && skillsDir) {
|
|
2522
2724
|
const { files: plannedSkills } = buildSkillFilePlans(
|
|
@@ -2537,16 +2739,15 @@ async function planEcosystem(opts) {
|
|
|
2537
2739
|
role: null
|
|
2538
2740
|
})),
|
|
2539
2741
|
statusFieldId: statusField.id,
|
|
2540
|
-
contextYamlPath: ".gh-symphony/context.yaml",
|
|
2541
|
-
referenceWorkflowPath: ".gh-symphony/reference-workflow.md",
|
|
2542
2742
|
detectedEnvironment: environment
|
|
2543
2743
|
}
|
|
2544
2744
|
);
|
|
2545
2745
|
for (const plannedSkill of plannedSkills) {
|
|
2746
|
+
const skillName = skillNameForPath(skillsDir, plannedSkill.path);
|
|
2546
2747
|
files.push(
|
|
2547
2748
|
await planFileChange({
|
|
2548
2749
|
path: plannedSkill.path,
|
|
2549
|
-
label: `Skill ${
|
|
2750
|
+
label: `Skill ${skillName}`,
|
|
2550
2751
|
content: plannedSkill.content,
|
|
2551
2752
|
mode: "create-only"
|
|
2552
2753
|
})
|
|
@@ -2568,17 +2769,8 @@ async function planEcosystem(opts) {
|
|
|
2568
2769
|
}
|
|
2569
2770
|
async function writeEcosystem(opts) {
|
|
2570
2771
|
const plan = await planEcosystem(opts);
|
|
2571
|
-
await mkdir3(join3(opts.cwd, ".gh-symphony"), { recursive: true });
|
|
2572
2772
|
const afterCreateHookPath = join3(opts.cwd, DEFAULT_AFTER_CREATE_HOOK_PATH);
|
|
2573
|
-
const contextYamlPath = join3(opts.cwd, ".gh-symphony", "context.yaml");
|
|
2574
|
-
const referenceWorkflowPath = join3(
|
|
2575
|
-
opts.cwd,
|
|
2576
|
-
".gh-symphony",
|
|
2577
|
-
"reference-workflow.md"
|
|
2578
|
-
);
|
|
2579
2773
|
let afterCreateHookWritten = false;
|
|
2580
|
-
let contextYamlWritten = false;
|
|
2581
|
-
let referenceWorkflowWritten = false;
|
|
2582
2774
|
const skillsWritten = [];
|
|
2583
2775
|
const skillsSkipped = [];
|
|
2584
2776
|
for (const file of plan.files) {
|
|
@@ -2587,16 +2779,8 @@ async function writeEcosystem(opts) {
|
|
|
2587
2779
|
afterCreateHookWritten = written;
|
|
2588
2780
|
continue;
|
|
2589
2781
|
}
|
|
2590
|
-
if (file.path === contextYamlPath) {
|
|
2591
|
-
contextYamlWritten = written;
|
|
2592
|
-
continue;
|
|
2593
|
-
}
|
|
2594
|
-
if (file.path === referenceWorkflowPath) {
|
|
2595
|
-
referenceWorkflowWritten = written;
|
|
2596
|
-
continue;
|
|
2597
|
-
}
|
|
2598
2782
|
if (file.label.startsWith("Skill ")) {
|
|
2599
|
-
const skillName =
|
|
2783
|
+
const skillName = file.label.slice("Skill ".length);
|
|
2600
2784
|
if (written) {
|
|
2601
2785
|
skillsWritten.push(skillName);
|
|
2602
2786
|
} else {
|
|
@@ -2614,10 +2798,11 @@ async function writeEcosystem(opts) {
|
|
|
2614
2798
|
skillsDir: plan.skillsDir,
|
|
2615
2799
|
skipSkills: plan.skipSkills,
|
|
2616
2800
|
afterCreateHookWritten,
|
|
2617
|
-
contextYamlWritten,
|
|
2618
|
-
referenceWorkflowWritten,
|
|
2619
|
-
skillsWritten: skillsWritten.sort(),
|
|
2620
|
-
skillsSkipped: skillsSkipped.sort()
|
|
2801
|
+
contextYamlWritten: false,
|
|
2802
|
+
referenceWorkflowWritten: false,
|
|
2803
|
+
skillsWritten: [...new Set(skillsWritten)].sort(),
|
|
2804
|
+
skillsSkipped: [...new Set(skillsSkipped)].sort(),
|
|
2805
|
+
legacyFilesRemoved: []
|
|
2621
2806
|
};
|
|
2622
2807
|
}
|
|
2623
2808
|
function formatPrioritySummaryLines(priority) {
|
|
@@ -2669,16 +2854,6 @@ function printEcosystemSummary(result, workflowPath, opts) {
|
|
|
2669
2854
|
` \u2713 ${DEFAULT_AFTER_CREATE_HOOK_LABEL.padEnd(36)} ${DEFAULT_AFTER_CREATE_HOOK_PATH}`
|
|
2670
2855
|
);
|
|
2671
2856
|
}
|
|
2672
|
-
if (result.contextYamlWritten) {
|
|
2673
|
-
lines.push(
|
|
2674
|
-
" \u2713 Context metadata .gh-symphony/context.yaml"
|
|
2675
|
-
);
|
|
2676
|
-
}
|
|
2677
|
-
if (result.referenceWorkflowWritten) {
|
|
2678
|
-
lines.push(
|
|
2679
|
-
" \u2713 Reference workflow .gh-symphony/reference-workflow.md"
|
|
2680
|
-
);
|
|
2681
|
-
}
|
|
2682
2857
|
if (result.skillsDir) {
|
|
2683
2858
|
const relSkillsDir = relative(cwd, result.skillsDir);
|
|
2684
2859
|
lines.push("");
|
|
@@ -3052,6 +3227,7 @@ async function runInteractiveStandalone(flags, _options) {
|
|
|
3052
3227
|
printDryRunPreview(outputPath, workflowPlan, ecosystemPlan);
|
|
3053
3228
|
return;
|
|
3054
3229
|
}
|
|
3230
|
+
await promptLegacyGhSymphonyCleanup(process.cwd());
|
|
3055
3231
|
await writeWorkflowPlan(workflowPlan);
|
|
3056
3232
|
const ecosystemResult = await writeEcosystem({
|
|
3057
3233
|
cwd: process.cwd(),
|
|
@@ -3075,7 +3251,9 @@ export {
|
|
|
3075
3251
|
toWorkflowLifecycleConfig,
|
|
3076
3252
|
validateStateMapping,
|
|
3077
3253
|
abortIfCancelled,
|
|
3254
|
+
warnDeprecatedSkipContext,
|
|
3078
3255
|
workflow_init_default,
|
|
3256
|
+
promptLegacyGhSymphonyCleanup,
|
|
3079
3257
|
resolveStatusField,
|
|
3080
3258
|
buildAutomaticStateMappings,
|
|
3081
3259
|
resolvePriorityField,
|