spets 0.1.4 → 0.1.5
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 +4 -2
- package/dist/index.js +104 -16
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -100,13 +100,15 @@ npx spets start "task" --pr 42
|
|
|
100
100
|
Issue/PR 코멘트로 워크플로우를 제어합니다:
|
|
101
101
|
|
|
102
102
|
- `/approve` - 현재 스텝 승인하고 다음 스텝 진행
|
|
103
|
+
- `/approve --pr` - 승인하고 Pull Request 생성
|
|
104
|
+
- `/approve --issue` - 승인하고 Issue 생성/업데이트
|
|
103
105
|
- `/revise <feedback>` - 피드백과 함께 현재 스텝 재생성
|
|
104
106
|
- `/reject` - 워크플로우 중단
|
|
105
107
|
|
|
106
108
|
### 필요 설정
|
|
107
109
|
|
|
108
110
|
Repository Secrets에 추가:
|
|
109
|
-
- `
|
|
111
|
+
- `CLAUDE_CODE_OAUTH_TOKEN` - Claude 인증 토큰 (`claude setup-token`으로 생성)
|
|
110
112
|
|
|
111
113
|
## Claude Code Plugin
|
|
112
114
|
|
|
@@ -143,7 +145,7 @@ hooks:
|
|
|
143
145
|
## Requirements
|
|
144
146
|
|
|
145
147
|
- Node.js >= 18
|
|
146
|
-
- Claude CLI (`claude` command) or
|
|
148
|
+
- Claude CLI (`claude` command) or CLAUDE_CODE_OAUTH_TOKEN
|
|
147
149
|
- GitHub CLI (`gh`) - GitHub 연동 사용 시
|
|
148
150
|
|
|
149
151
|
## License
|
package/dist/index.js
CHANGED
|
@@ -76,7 +76,7 @@ async function initCommand(options) {
|
|
|
76
76
|
console.log(" 1. Edit .spets/config.yml to customize your workflow");
|
|
77
77
|
console.log(" 2. Customize step instructions in .spets/steps/");
|
|
78
78
|
if (options.github) {
|
|
79
|
-
console.log(" 3. Add
|
|
79
|
+
console.log(" 3. Add CLAUDE_CODE_OAUTH_TOKEN to your repo secrets");
|
|
80
80
|
console.log(' 4. Create a new Issue using the "Spets Task" template');
|
|
81
81
|
} else {
|
|
82
82
|
console.log(' 3. Run: spets start "your task description"');
|
|
@@ -443,6 +443,11 @@ on:
|
|
|
443
443
|
issue_comment:
|
|
444
444
|
types: [created]
|
|
445
445
|
|
|
446
|
+
permissions:
|
|
447
|
+
contents: write
|
|
448
|
+
issues: write
|
|
449
|
+
pull-requests: write
|
|
450
|
+
|
|
446
451
|
jobs:
|
|
447
452
|
# Start workflow when a spets Issue is created
|
|
448
453
|
start-workflow:
|
|
@@ -495,7 +500,7 @@ jobs:
|
|
|
495
500
|
npx spets start "$TASK" --github --issue ${gh("github.event.issue.number")}
|
|
496
501
|
env:
|
|
497
502
|
TASK: ${gh("steps.parse.outputs.task")}
|
|
498
|
-
|
|
503
|
+
CLAUDE_CODE_OAUTH_TOKEN: ${gh("secrets.CLAUDE_CODE_OAUTH_TOKEN")}
|
|
499
504
|
GH_TOKEN: ${gh("secrets.GITHUB_TOKEN")}
|
|
500
505
|
|
|
501
506
|
- name: Push changes
|
|
@@ -553,7 +558,7 @@ jobs:
|
|
|
553
558
|
npx spets github --issue ${gh("github.event.issue.number")} --comment "$COMMENT"
|
|
554
559
|
env:
|
|
555
560
|
COMMENT: ${gh("github.event.comment.body")}
|
|
556
|
-
|
|
561
|
+
CLAUDE_CODE_OAUTH_TOKEN: ${gh("secrets.CLAUDE_CODE_OAUTH_TOKEN")}
|
|
557
562
|
GH_TOKEN: ${gh("secrets.GITHUB_TOKEN")}
|
|
558
563
|
|
|
559
564
|
- name: Push changes
|
|
@@ -697,7 +702,7 @@ var Executor = class {
|
|
|
697
702
|
this.config = options.config;
|
|
698
703
|
this.cwd = options.cwd || process.cwd();
|
|
699
704
|
}
|
|
700
|
-
async executeWorkflow(taskId, userQuery, startIndex = 0) {
|
|
705
|
+
async executeWorkflow(taskId, userQuery, startIndex = 0, feedback) {
|
|
701
706
|
let previousOutput;
|
|
702
707
|
if (startIndex > 0) {
|
|
703
708
|
const prevStepName = this.config.steps[startIndex - 1];
|
|
@@ -717,7 +722,9 @@ var Executor = class {
|
|
|
717
722
|
instruction: stepDef.instruction,
|
|
718
723
|
template: stepDef.template,
|
|
719
724
|
previousOutput,
|
|
720
|
-
outputPath: getOutputPath(taskId, stepName, this.cwd)
|
|
725
|
+
outputPath: getOutputPath(taskId, stepName, this.cwd),
|
|
726
|
+
feedback: i === startIndex ? feedback : void 0
|
|
727
|
+
// Only apply feedback to the target step
|
|
721
728
|
};
|
|
722
729
|
if (this.config.hooks?.preStep) {
|
|
723
730
|
await runHook(this.config.hooks.preStep, {
|
|
@@ -1030,6 +1037,12 @@ function parseGitHubCommand(comment) {
|
|
|
1030
1037
|
if (trimmed === "/approve") {
|
|
1031
1038
|
return { command: "approve" };
|
|
1032
1039
|
}
|
|
1040
|
+
if (trimmed === "/approve --pr") {
|
|
1041
|
+
return { command: "approve", createPR: true };
|
|
1042
|
+
}
|
|
1043
|
+
if (trimmed === "/approve --issue") {
|
|
1044
|
+
return { command: "approve", createIssue: true };
|
|
1045
|
+
}
|
|
1033
1046
|
if (trimmed === "/reject") {
|
|
1034
1047
|
return { command: "reject" };
|
|
1035
1048
|
}
|
|
@@ -1055,6 +1068,7 @@ function parseGitHubCommand(comment) {
|
|
|
1055
1068
|
var GitHubPlatform = class extends BasePlatform {
|
|
1056
1069
|
config;
|
|
1057
1070
|
currentTaskId;
|
|
1071
|
+
currentOutputPath;
|
|
1058
1072
|
constructor(config) {
|
|
1059
1073
|
super();
|
|
1060
1074
|
this.config = config;
|
|
@@ -1064,6 +1078,7 @@ var GitHubPlatform = class extends BasePlatform {
|
|
|
1064
1078
|
}
|
|
1065
1079
|
async generateDocument(context) {
|
|
1066
1080
|
this.currentTaskId = context.taskId;
|
|
1081
|
+
this.currentOutputPath = context.outputPath;
|
|
1067
1082
|
const prompt = this.buildPrompt(context);
|
|
1068
1083
|
const response = await this.callClaude(prompt);
|
|
1069
1084
|
const { document, questions } = this.parseResponse(response);
|
|
@@ -1077,7 +1092,7 @@ var GitHubPlatform = class extends BasePlatform {
|
|
|
1077
1092
|
throw new PauseForInputError("questions", questions);
|
|
1078
1093
|
}
|
|
1079
1094
|
async requestApproval(doc, stepName) {
|
|
1080
|
-
const comment = this.formatApprovalComment(doc, stepName, this.currentTaskId);
|
|
1095
|
+
const comment = this.formatApprovalComment(doc, stepName, this.currentTaskId, this.currentOutputPath);
|
|
1081
1096
|
await this.postComment(comment);
|
|
1082
1097
|
console.log("\n\u23F8\uFE0F Waiting for approval on GitHub...");
|
|
1083
1098
|
console.log(" Comment /approve, /revise <feedback>, or /reject on the PR/Issue.");
|
|
@@ -1110,20 +1125,31 @@ var GitHubPlatform = class extends BasePlatform {
|
|
|
1110
1125
|
lines.push("");
|
|
1111
1126
|
}
|
|
1112
1127
|
lines.push("---");
|
|
1113
|
-
lines.push("
|
|
1128
|
+
lines.push("");
|
|
1129
|
+
lines.push("**How to answer:**");
|
|
1130
|
+
lines.push("");
|
|
1114
1131
|
lines.push("```");
|
|
1115
1132
|
lines.push("/answer");
|
|
1116
1133
|
for (let i = 0; i < questions.length; i++) {
|
|
1117
|
-
lines.push(`Q${i + 1}: your answer
|
|
1134
|
+
lines.push(`Q${i + 1}: <your answer for question ${i + 1}>`);
|
|
1118
1135
|
}
|
|
1119
1136
|
lines.push("```");
|
|
1137
|
+
lines.push("");
|
|
1138
|
+
lines.push("Example:");
|
|
1139
|
+
lines.push("```");
|
|
1140
|
+
lines.push("/answer");
|
|
1141
|
+
lines.push("Q1: Use TypeScript");
|
|
1142
|
+
lines.push("Q2: Yes, add unit tests");
|
|
1143
|
+
lines.push("```");
|
|
1120
1144
|
return lines.join("\n");
|
|
1121
1145
|
}
|
|
1122
|
-
formatApprovalComment(doc, stepName, taskId) {
|
|
1146
|
+
formatApprovalComment(doc, stepName, taskId, outputPath) {
|
|
1147
|
+
const relativePath = outputPath ? outputPath.replace(process.cwd() + "/", "") : `.spets/outputs/${taskId}/${stepName}.md`;
|
|
1123
1148
|
const lines = [
|
|
1124
1149
|
`## \u{1F4C4} Spets: ${stepName} - Review Required`,
|
|
1125
1150
|
"",
|
|
1126
1151
|
`> Task ID: \`${taskId}\``,
|
|
1152
|
+
`> Output: \`${relativePath}\``,
|
|
1127
1153
|
"",
|
|
1128
1154
|
"<details>",
|
|
1129
1155
|
"<summary>\u{1F4DD} View Document</summary>",
|
|
@@ -1140,8 +1166,12 @@ var GitHubPlatform = class extends BasePlatform {
|
|
|
1140
1166
|
"| Command | Description |",
|
|
1141
1167
|
"|---------|-------------|",
|
|
1142
1168
|
"| `/approve` | Approve and continue to next step |",
|
|
1169
|
+
"| `/approve --pr` | Approve and create a Pull Request |",
|
|
1170
|
+
"| `/approve --issue` | Approve and create/update an Issue |",
|
|
1143
1171
|
"| `/revise <feedback>` | Request changes with feedback |",
|
|
1144
|
-
"| `/reject` | Reject and stop workflow |"
|
|
1172
|
+
"| `/reject` | Reject and stop workflow |",
|
|
1173
|
+
"",
|
|
1174
|
+
"Example: `/revise Please add more details about error handling`"
|
|
1145
1175
|
];
|
|
1146
1176
|
return lines.join("\n");
|
|
1147
1177
|
}
|
|
@@ -1206,12 +1236,11 @@ var GitHubPlatform = class extends BasePlatform {
|
|
|
1206
1236
|
async callClaude(prompt) {
|
|
1207
1237
|
return new Promise((resolve, reject) => {
|
|
1208
1238
|
const proc = spawn3("claude", [
|
|
1209
|
-
"
|
|
1239
|
+
"-p",
|
|
1240
|
+
prompt,
|
|
1210
1241
|
"--permission-mode",
|
|
1211
1242
|
"bypassPermissions"
|
|
1212
|
-
], { stdio: ["
|
|
1213
|
-
proc.stdin.write(prompt);
|
|
1214
|
-
proc.stdin.end();
|
|
1243
|
+
], { stdio: ["ignore", "pipe", "pipe"] });
|
|
1215
1244
|
let stdout = "";
|
|
1216
1245
|
let stderr = "";
|
|
1217
1246
|
proc.stdout.on("data", (data) => {
|
|
@@ -1222,7 +1251,7 @@ var GitHubPlatform = class extends BasePlatform {
|
|
|
1222
1251
|
});
|
|
1223
1252
|
proc.on("close", (code) => {
|
|
1224
1253
|
if (code !== 0) {
|
|
1225
|
-
reject(new Error(`Claude CLI failed
|
|
1254
|
+
reject(new Error(`Claude CLI failed (code ${code}): stderr=${stderr}, stdout=${stdout}`));
|
|
1226
1255
|
} else {
|
|
1227
1256
|
resolve(stdout);
|
|
1228
1257
|
}
|
|
@@ -1843,6 +1872,15 @@ async function githubCommand(options) {
|
|
|
1843
1872
|
case "approve": {
|
|
1844
1873
|
console.log(`Approving step: ${state.currentStepName}`);
|
|
1845
1874
|
updateDocumentStatus(outputPath, "approved");
|
|
1875
|
+
if (parsed.createPR) {
|
|
1876
|
+
console.log("Creating Pull Request...");
|
|
1877
|
+
const prNumber = await createPullRequest(githubConfig, taskId, userQuery, state.currentStepName);
|
|
1878
|
+
console.log(`Created PR #${prNumber}`);
|
|
1879
|
+
}
|
|
1880
|
+
if (parsed.createIssue) {
|
|
1881
|
+
console.log("Creating/Updating Issue...");
|
|
1882
|
+
await createOrUpdateIssue(githubConfig, taskId, userQuery, state.currentStepName);
|
|
1883
|
+
}
|
|
1846
1884
|
const nextIndex = state.currentStepIndex + 1;
|
|
1847
1885
|
if (nextIndex >= config.steps.length) {
|
|
1848
1886
|
console.log("\u2705 Workflow completed!");
|
|
@@ -1865,7 +1903,7 @@ async function githubCommand(options) {
|
|
|
1865
1903
|
const platform = new GitHubPlatform(githubConfig);
|
|
1866
1904
|
const executor = new Executor({ platform, config, cwd });
|
|
1867
1905
|
try {
|
|
1868
|
-
await executor.executeWorkflow(taskId, userQuery, state.currentStepIndex);
|
|
1906
|
+
await executor.executeWorkflow(taskId, userQuery, state.currentStepIndex, parsed.feedback);
|
|
1869
1907
|
} catch (error) {
|
|
1870
1908
|
if (error.name !== "PauseForInputError") {
|
|
1871
1909
|
throw error;
|
|
@@ -1934,6 +1972,56 @@ async function postComment(config, body) {
|
|
|
1934
1972
|
proc.on("error", reject);
|
|
1935
1973
|
});
|
|
1936
1974
|
}
|
|
1975
|
+
async function createPullRequest(config, taskId, userQuery, stepName) {
|
|
1976
|
+
const { execSync: execSync5 } = await import("child_process");
|
|
1977
|
+
const { owner, repo } = config;
|
|
1978
|
+
const title = userQuery.slice(0, 50) + (userQuery.length > 50 ? "..." : "");
|
|
1979
|
+
const body = `## Spets Workflow
|
|
1980
|
+
|
|
1981
|
+
- Task ID: \`${taskId}\`
|
|
1982
|
+
- Current Step: **${stepName}**
|
|
1983
|
+
|
|
1984
|
+
### Description
|
|
1985
|
+
${userQuery}
|
|
1986
|
+
|
|
1987
|
+
---
|
|
1988
|
+
_Created by [Spets](https://github.com/eatnug/spets)_`;
|
|
1989
|
+
const result = execSync5(
|
|
1990
|
+
`gh pr create --repo ${owner}/${repo} --title "${title.replace(/"/g, '\\"')}" --body "${body.replace(/"/g, '\\"')}"`,
|
|
1991
|
+
{ encoding: "utf-8" }
|
|
1992
|
+
);
|
|
1993
|
+
const match = result.match(/\/pull\/(\d+)/);
|
|
1994
|
+
if (!match) {
|
|
1995
|
+
throw new Error("Failed to parse PR number from gh output");
|
|
1996
|
+
}
|
|
1997
|
+
return parseInt(match[1], 10);
|
|
1998
|
+
}
|
|
1999
|
+
async function createOrUpdateIssue(config, taskId, userQuery, stepName) {
|
|
2000
|
+
const { execSync: execSync5 } = await import("child_process");
|
|
2001
|
+
const { owner, repo, issueNumber } = config;
|
|
2002
|
+
const body = `## Spets Workflow Update
|
|
2003
|
+
|
|
2004
|
+
- Task ID: \`${taskId}\`
|
|
2005
|
+
- Current Step: **${stepName}** \u2705 Approved
|
|
2006
|
+
|
|
2007
|
+
### Description
|
|
2008
|
+
${userQuery}
|
|
2009
|
+
|
|
2010
|
+
---
|
|
2011
|
+
_Updated by [Spets](https://github.com/eatnug/spets)_`;
|
|
2012
|
+
if (issueNumber) {
|
|
2013
|
+
execSync5(
|
|
2014
|
+
`gh issue comment ${issueNumber} --repo ${owner}/${repo} --body "${body.replace(/"/g, '\\"')}"`,
|
|
2015
|
+
{ encoding: "utf-8" }
|
|
2016
|
+
);
|
|
2017
|
+
} else {
|
|
2018
|
+
const title = userQuery.slice(0, 50) + (userQuery.length > 50 ? "..." : "");
|
|
2019
|
+
execSync5(
|
|
2020
|
+
`gh issue create --repo ${owner}/${repo} --title "${title.replace(/"/g, '\\"')}" --body "${body.replace(/"/g, '\\"')}" --label spets`,
|
|
2021
|
+
{ encoding: "utf-8" }
|
|
2022
|
+
);
|
|
2023
|
+
}
|
|
2024
|
+
}
|
|
1937
2025
|
|
|
1938
2026
|
// src/orchestrator/index.ts
|
|
1939
2027
|
import { readFileSync, writeFileSync as writeFileSync3, existsSync as existsSync4, mkdirSync as mkdirSync3 } from "fs";
|