spets 0.1.0 → 0.1.2
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
CHANGED
|
@@ -5,23 +5,27 @@ Spec Driven Development Execution Framework - 유저가 정의한 스텝대로 S
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
+
# 글로벌 설치 (선택)
|
|
8
9
|
npm install -g spets
|
|
10
|
+
|
|
11
|
+
# 또는 npx로 바로 사용 (설치 불필요)
|
|
12
|
+
npx spets init
|
|
9
13
|
```
|
|
10
14
|
|
|
11
15
|
## Quick Start
|
|
12
16
|
|
|
13
17
|
```bash
|
|
14
18
|
# 프로젝트에서 초기화
|
|
15
|
-
spets init
|
|
19
|
+
npx spets init
|
|
16
20
|
|
|
17
21
|
# 워크플로우 시작
|
|
18
|
-
spets start "TODO 앱 만들어줘"
|
|
22
|
+
npx spets start "TODO 앱 만들어줘"
|
|
19
23
|
|
|
20
24
|
# 상태 확인
|
|
21
|
-
spets status
|
|
25
|
+
npx spets status
|
|
22
26
|
|
|
23
27
|
# 중단된 워크플로우 재개
|
|
24
|
-
spets resume
|
|
28
|
+
npx spets resume
|
|
25
29
|
```
|
|
26
30
|
|
|
27
31
|
## How it Works
|
|
@@ -56,10 +60,10 @@ PR/Issue 코멘트로 워크플로우 제어:
|
|
|
56
60
|
|
|
57
61
|
```bash
|
|
58
62
|
# GitHub Actions 워크플로우 포함해서 초기화
|
|
59
|
-
spets init --github
|
|
63
|
+
npx spets init --github
|
|
60
64
|
|
|
61
65
|
# GitHub 플랫폼으로 시작
|
|
62
|
-
spets start "task" --platform github --owner myorg --repo myrepo --issue 42
|
|
66
|
+
npx spets start "task" --platform github --owner myorg --repo myrepo --issue 42
|
|
63
67
|
```
|
|
64
68
|
|
|
65
69
|
코멘트 명령어:
|
|
@@ -71,12 +75,14 @@ spets start "task" --platform github --owner myorg --repo myrepo --issue 42
|
|
|
71
75
|
|
|
72
76
|
```bash
|
|
73
77
|
# Claude Code 스킬 설치
|
|
74
|
-
spets plugin install claude
|
|
78
|
+
npx spets plugin install claude
|
|
75
79
|
|
|
76
80
|
# Claude Code에서 사용
|
|
77
81
|
/spets start "task description"
|
|
78
82
|
```
|
|
79
83
|
|
|
84
|
+
스킬이 설치되면 Claude Code 내에서 `npx spets` 명령어를 자동으로 실행합니다. 글로벌 설치 없이도 동작합니다.
|
|
85
|
+
|
|
80
86
|
## Configuration
|
|
81
87
|
|
|
82
88
|
`.sept/config.yml`:
|
|
@@ -7,28 +7,28 @@ import matter from "gray-matter";
|
|
|
7
7
|
import { readFileSync, existsSync } from "fs";
|
|
8
8
|
import { join } from "path";
|
|
9
9
|
import { parse as parseYaml } from "yaml";
|
|
10
|
-
var
|
|
10
|
+
var SPETS_DIR = ".spets";
|
|
11
11
|
var CONFIG_FILE = "config.yml";
|
|
12
12
|
var STEPS_DIR = "steps";
|
|
13
|
-
function
|
|
14
|
-
return join(cwd,
|
|
13
|
+
function getSpetsDir(cwd = process.cwd()) {
|
|
14
|
+
return join(cwd, SPETS_DIR);
|
|
15
15
|
}
|
|
16
16
|
function getConfigPath(cwd = process.cwd()) {
|
|
17
|
-
return join(
|
|
17
|
+
return join(getSpetsDir(cwd), CONFIG_FILE);
|
|
18
18
|
}
|
|
19
19
|
function getStepsDir(cwd = process.cwd()) {
|
|
20
|
-
return join(
|
|
20
|
+
return join(getSpetsDir(cwd), STEPS_DIR);
|
|
21
21
|
}
|
|
22
22
|
function getOutputsDir(cwd = process.cwd()) {
|
|
23
|
-
return join(
|
|
23
|
+
return join(getSpetsDir(cwd), "outputs");
|
|
24
24
|
}
|
|
25
|
-
function
|
|
25
|
+
function spetsExists(cwd = process.cwd()) {
|
|
26
26
|
return existsSync(getConfigPath(cwd));
|
|
27
27
|
}
|
|
28
28
|
function loadConfig(cwd = process.cwd()) {
|
|
29
29
|
const configPath = getConfigPath(cwd);
|
|
30
30
|
if (!existsSync(configPath)) {
|
|
31
|
-
throw new Error(`
|
|
31
|
+
throw new Error(`Spets config not found. Run 'spets init' first.`);
|
|
32
32
|
}
|
|
33
33
|
const content = readFileSync(configPath, "utf-8");
|
|
34
34
|
const config = parseYaml(content);
|
|
@@ -52,6 +52,10 @@ function loadStepDefinition(stepName, cwd = process.cwd()) {
|
|
|
52
52
|
template
|
|
53
53
|
};
|
|
54
54
|
}
|
|
55
|
+
function getGitHubConfig(cwd = process.cwd()) {
|
|
56
|
+
const config = loadConfig(cwd);
|
|
57
|
+
return config.github;
|
|
58
|
+
}
|
|
55
59
|
|
|
56
60
|
// src/core/state.ts
|
|
57
61
|
function generateTaskId() {
|
|
@@ -190,10 +194,11 @@ function loadTaskMetadata(taskId, cwd = process.cwd()) {
|
|
|
190
194
|
}
|
|
191
195
|
|
|
192
196
|
export {
|
|
193
|
-
|
|
194
|
-
|
|
197
|
+
getSpetsDir,
|
|
198
|
+
spetsExists,
|
|
195
199
|
loadConfig,
|
|
196
200
|
loadStepDefinition,
|
|
201
|
+
getGitHubConfig,
|
|
197
202
|
generateTaskId,
|
|
198
203
|
getTaskDir,
|
|
199
204
|
getOutputPath,
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
generateTaskId,
|
|
4
|
+
getGitHubConfig,
|
|
4
5
|
getOutputPath,
|
|
5
|
-
|
|
6
|
+
getSpetsDir,
|
|
6
7
|
getWorkflowState,
|
|
7
8
|
listTasks,
|
|
8
9
|
loadConfig,
|
|
@@ -11,9 +12,9 @@ import {
|
|
|
11
12
|
loadTaskMetadata,
|
|
12
13
|
saveDocument,
|
|
13
14
|
saveTaskMetadata,
|
|
14
|
-
|
|
15
|
+
spetsExists,
|
|
15
16
|
updateDocumentStatus
|
|
16
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-XYU22TND.js";
|
|
17
18
|
|
|
18
19
|
// src/index.ts
|
|
19
20
|
import { Command } from "commander";
|
|
@@ -25,32 +26,32 @@ import { fileURLToPath } from "url";
|
|
|
25
26
|
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
26
27
|
async function initCommand(options) {
|
|
27
28
|
const cwd = process.cwd();
|
|
28
|
-
const
|
|
29
|
-
if (
|
|
29
|
+
const spetsDir = getSpetsDir(cwd);
|
|
30
|
+
if (spetsExists(cwd) && !options.force) {
|
|
30
31
|
console.error("Spets already initialized. Use --force to overwrite.");
|
|
31
32
|
process.exit(1);
|
|
32
33
|
}
|
|
33
|
-
mkdirSync(
|
|
34
|
-
mkdirSync(join(
|
|
35
|
-
mkdirSync(join(
|
|
36
|
-
mkdirSync(join(
|
|
34
|
+
mkdirSync(spetsDir, { recursive: true });
|
|
35
|
+
mkdirSync(join(spetsDir, "steps"), { recursive: true });
|
|
36
|
+
mkdirSync(join(spetsDir, "outputs"), { recursive: true });
|
|
37
|
+
mkdirSync(join(spetsDir, "hooks"), { recursive: true });
|
|
37
38
|
const templatesDir = join(__dirname, "..", "templates");
|
|
38
|
-
writeFileSync(join(
|
|
39
|
-
createDefaultSteps(
|
|
40
|
-
console.log("Initialized spets in .
|
|
39
|
+
writeFileSync(join(spetsDir, "config.yml"), getDefaultConfig());
|
|
40
|
+
createDefaultSteps(spetsDir);
|
|
41
|
+
console.log("Initialized spets in .spets/");
|
|
41
42
|
console.log("");
|
|
42
43
|
console.log("Created:");
|
|
43
|
-
console.log(" .
|
|
44
|
-
console.log(" .
|
|
45
|
-
console.log(" .
|
|
44
|
+
console.log(" .spets/config.yml - Workflow configuration");
|
|
45
|
+
console.log(" .spets/steps/01-plan/ - Planning step");
|
|
46
|
+
console.log(" .spets/steps/02-implement/ - Implementation step");
|
|
46
47
|
if (options.github) {
|
|
47
48
|
createGitHubWorkflow(cwd);
|
|
48
|
-
console.log(" .github/workflows/
|
|
49
|
+
console.log(" .github/workflows/spets.yml - GitHub Actions workflow");
|
|
49
50
|
}
|
|
50
51
|
console.log("");
|
|
51
52
|
console.log("Next steps:");
|
|
52
|
-
console.log(" 1. Edit .
|
|
53
|
-
console.log(" 2. Customize step instructions in .
|
|
53
|
+
console.log(" 1. Edit .spets/config.yml to customize your workflow");
|
|
54
|
+
console.log(" 2. Customize step instructions in .spets/steps/");
|
|
54
55
|
if (options.github) {
|
|
55
56
|
console.log(" 3. Add ANTHROPIC_API_KEY to your repo secrets");
|
|
56
57
|
console.log(' 4. Run: spets start "task" --platform github --owner <owner> --repo <repo> --issue <n>');
|
|
@@ -75,12 +76,12 @@ steps:
|
|
|
75
76
|
# onComplete: "./hooks/on-complete.sh"
|
|
76
77
|
`;
|
|
77
78
|
}
|
|
78
|
-
function createDefaultSteps(
|
|
79
|
-
const planDir = join(
|
|
79
|
+
function createDefaultSteps(spetsDir) {
|
|
80
|
+
const planDir = join(spetsDir, "steps", "01-plan");
|
|
80
81
|
mkdirSync(planDir, { recursive: true });
|
|
81
82
|
writeFileSync(join(planDir, "instruction.md"), getPlanInstruction());
|
|
82
83
|
writeFileSync(join(planDir, "template.md"), getPlanTemplate());
|
|
83
|
-
const implementDir = join(
|
|
84
|
+
const implementDir = join(spetsDir, "steps", "02-implement");
|
|
84
85
|
mkdirSync(implementDir, { recursive: true });
|
|
85
86
|
writeFileSync(join(implementDir, "instruction.md"), getImplementInstruction());
|
|
86
87
|
writeFileSync(join(implementDir, "template.md"), getImplementTemplate());
|
|
@@ -239,7 +240,7 @@ None / List any deviations with justification.
|
|
|
239
240
|
function createGitHubWorkflow(cwd) {
|
|
240
241
|
const workflowDir = join(cwd, ".github", "workflows");
|
|
241
242
|
mkdirSync(workflowDir, { recursive: true });
|
|
242
|
-
writeFileSync(join(workflowDir, "
|
|
243
|
+
writeFileSync(join(workflowDir, "spets.yml"), getGitHubWorkflow());
|
|
243
244
|
}
|
|
244
245
|
function getGitHubWorkflow() {
|
|
245
246
|
const gh = (expr) => `\${{ ${expr} }}`;
|
|
@@ -253,8 +254,8 @@ on:
|
|
|
253
254
|
types: [created]
|
|
254
255
|
|
|
255
256
|
jobs:
|
|
256
|
-
handle-
|
|
257
|
-
# Only run if comment contains a
|
|
257
|
+
handle-spets-command:
|
|
258
|
+
# Only run if comment contains a spets command
|
|
258
259
|
if: |
|
|
259
260
|
contains(github.event.comment.body, '/approve') ||
|
|
260
261
|
contains(github.event.comment.body, '/revise') ||
|
|
@@ -303,7 +304,7 @@ jobs:
|
|
|
303
304
|
|
|
304
305
|
Instructions:
|
|
305
306
|
1. Parse the command from the comment (/approve, /revise, /reject, or /answer)
|
|
306
|
-
2. Find the active
|
|
307
|
+
2. Find the active spets task in .spets/outputs/
|
|
307
308
|
3. Execute the appropriate action:
|
|
308
309
|
- /approve: Mark current step as approved, generate next step
|
|
309
310
|
- /revise <feedback>: Regenerate current step with feedback
|
|
@@ -325,7 +326,7 @@ jobs:
|
|
|
325
326
|
git config user.name "github-actions[bot]"
|
|
326
327
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
327
328
|
git add -A
|
|
328
|
-
git diff --staged --quiet || git commit -m "
|
|
329
|
+
git diff --staged --quiet || git commit -m "Spets: Update from workflow"
|
|
329
330
|
git push
|
|
330
331
|
env:
|
|
331
332
|
GH_TOKEN: ${gh("secrets.GITHUB_TOKEN")}
|
|
@@ -335,7 +336,7 @@ jobs:
|
|
|
335
336
|
// src/commands/status.ts
|
|
336
337
|
async function statusCommand(options) {
|
|
337
338
|
const cwd = process.cwd();
|
|
338
|
-
if (!
|
|
339
|
+
if (!spetsExists(cwd)) {
|
|
339
340
|
console.error('Spets not initialized. Run "spets init" first.');
|
|
340
341
|
process.exit(1);
|
|
341
342
|
}
|
|
@@ -373,7 +374,7 @@ function showAllTasks(config, cwd) {
|
|
|
373
374
|
if (tasks.length === 0) {
|
|
374
375
|
console.log("No tasks found.");
|
|
375
376
|
console.log("");
|
|
376
|
-
console.log('Start a new task with:
|
|
377
|
+
console.log('Start a new task with: spets start "your task description"');
|
|
377
378
|
return;
|
|
378
379
|
}
|
|
379
380
|
console.log("Tasks:");
|
|
@@ -393,7 +394,7 @@ function showAllTasks(config, cwd) {
|
|
|
393
394
|
console.log(` ... and ${tasks.length - 10} more tasks`);
|
|
394
395
|
}
|
|
395
396
|
console.log("");
|
|
396
|
-
console.log('Use "
|
|
397
|
+
console.log('Use "spets status -t <taskId>" for details');
|
|
397
398
|
}
|
|
398
399
|
function formatStatus(status) {
|
|
399
400
|
const icons = {
|
|
@@ -418,18 +419,18 @@ import { spawn } from "child_process";
|
|
|
418
419
|
import { existsSync as existsSync2 } from "fs";
|
|
419
420
|
import { join as join2, isAbsolute } from "path";
|
|
420
421
|
async function runHook(hookPath, context, cwd = process.cwd()) {
|
|
421
|
-
const resolvedPath = isAbsolute(hookPath) ? hookPath : join2(
|
|
422
|
+
const resolvedPath = isAbsolute(hookPath) ? hookPath : join2(getSpetsDir(cwd), hookPath);
|
|
422
423
|
if (!existsSync2(resolvedPath)) {
|
|
423
424
|
console.warn(`Hook not found: ${resolvedPath}`);
|
|
424
425
|
return;
|
|
425
426
|
}
|
|
426
427
|
const env = {
|
|
427
428
|
...process.env,
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
429
|
+
SPETS_TASK_ID: context.taskId,
|
|
430
|
+
SPETS_STEP_NAME: context.stepName,
|
|
431
|
+
SPETS_STEP_INDEX: String(context.stepIndex),
|
|
432
|
+
SPETS_OUTPUT_PATH: context.outputPath,
|
|
433
|
+
SPETS_CWD: cwd
|
|
433
434
|
};
|
|
434
435
|
return new Promise((resolve, reject) => {
|
|
435
436
|
const proc = spawn(resolvedPath, [], {
|
|
@@ -516,7 +517,7 @@ var Executor = class {
|
|
|
516
517
|
return;
|
|
517
518
|
}
|
|
518
519
|
if (result.paused) {
|
|
519
|
-
console.log("\n\u23F8\uFE0F Workflow paused. Resume with:
|
|
520
|
+
console.log("\n\u23F8\uFE0F Workflow paused. Resume with: spets resume");
|
|
520
521
|
return;
|
|
521
522
|
}
|
|
522
523
|
if (result.approved) {
|
|
@@ -591,7 +592,7 @@ var Executor = class {
|
|
|
591
592
|
};
|
|
592
593
|
|
|
593
594
|
// src/platform/cli.ts
|
|
594
|
-
import { spawn as spawn2 } from "child_process";
|
|
595
|
+
import { spawn as spawn2, execSync } from "child_process";
|
|
595
596
|
import { input, select, confirm } from "@inquirer/prompts";
|
|
596
597
|
|
|
597
598
|
// src/platform/interface.ts
|
|
@@ -710,9 +711,15 @@ var CliPlatform = class extends BasePlatform {
|
|
|
710
711
|
}
|
|
711
712
|
async callClaude(prompt) {
|
|
712
713
|
return new Promise((resolve, reject) => {
|
|
713
|
-
const proc = spawn2(this.claudeCommand, [
|
|
714
|
+
const proc = spawn2(this.claudeCommand, [
|
|
715
|
+
"--print",
|
|
716
|
+
"--permission-mode",
|
|
717
|
+
"bypassPermissions"
|
|
718
|
+
], {
|
|
714
719
|
stdio: ["pipe", "pipe", "pipe"]
|
|
715
720
|
});
|
|
721
|
+
proc.stdin.write(prompt);
|
|
722
|
+
proc.stdin.end();
|
|
716
723
|
let stdout = "";
|
|
717
724
|
let stderr = "";
|
|
718
725
|
proc.stdout.on("data", (data) => {
|
|
@@ -734,6 +741,24 @@ var CliPlatform = class extends BasePlatform {
|
|
|
734
741
|
});
|
|
735
742
|
});
|
|
736
743
|
}
|
|
744
|
+
isClaudeInstalled() {
|
|
745
|
+
try {
|
|
746
|
+
const command = process.platform === "win32" ? "where" : "which";
|
|
747
|
+
execSync(`${command} claude`, { stdio: "ignore" });
|
|
748
|
+
return true;
|
|
749
|
+
} catch {
|
|
750
|
+
return false;
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
getClaudeExecutable() {
|
|
754
|
+
if (this.claudeCommand !== "claude") {
|
|
755
|
+
return { command: this.claudeCommand, args: [] };
|
|
756
|
+
}
|
|
757
|
+
if (this.isClaudeInstalled()) {
|
|
758
|
+
return { command: "claude", args: [] };
|
|
759
|
+
}
|
|
760
|
+
return { command: "npx", args: ["--yes", "@anthropic-ai/claude-code"] };
|
|
761
|
+
}
|
|
737
762
|
parseResponse(response) {
|
|
738
763
|
const questions = [];
|
|
739
764
|
const questionMatch = response.match(/```questions\n([\s\S]*?)```/);
|
|
@@ -834,7 +859,7 @@ var GitHubPlatform = class extends BasePlatform {
|
|
|
834
859
|
}
|
|
835
860
|
formatQuestionsComment(questions, taskId) {
|
|
836
861
|
const lines = [
|
|
837
|
-
"## \u{1F4CB}
|
|
862
|
+
"## \u{1F4CB} Spets: Questions Need Answers",
|
|
838
863
|
"",
|
|
839
864
|
`> Task ID: \`${taskId}\``,
|
|
840
865
|
"",
|
|
@@ -861,7 +886,7 @@ var GitHubPlatform = class extends BasePlatform {
|
|
|
861
886
|
}
|
|
862
887
|
formatApprovalComment(doc, stepName, taskId) {
|
|
863
888
|
const lines = [
|
|
864
|
-
`## \u{1F4C4}
|
|
889
|
+
`## \u{1F4C4} Spets: ${stepName} - Review Required`,
|
|
865
890
|
"",
|
|
866
891
|
`> Task ID: \`${taskId}\``,
|
|
867
892
|
"",
|
|
@@ -945,7 +970,13 @@ var GitHubPlatform = class extends BasePlatform {
|
|
|
945
970
|
}
|
|
946
971
|
async callClaude(prompt) {
|
|
947
972
|
return new Promise((resolve, reject) => {
|
|
948
|
-
const proc = spawn3("claude", [
|
|
973
|
+
const proc = spawn3("claude", [
|
|
974
|
+
"--print",
|
|
975
|
+
"--permission-mode",
|
|
976
|
+
"bypassPermissions"
|
|
977
|
+
], { stdio: ["pipe", "pipe", "pipe"] });
|
|
978
|
+
proc.stdin.write(prompt);
|
|
979
|
+
proc.stdin.end();
|
|
949
980
|
let stdout = "";
|
|
950
981
|
let stderr = "";
|
|
951
982
|
proc.stdout.on("data", (data) => {
|
|
@@ -990,7 +1021,7 @@ var GitHubPlatform = class extends BasePlatform {
|
|
|
990
1021
|
// src/commands/start.ts
|
|
991
1022
|
async function startCommand(query, options) {
|
|
992
1023
|
const cwd = process.cwd();
|
|
993
|
-
if (!
|
|
1024
|
+
if (!spetsExists(cwd)) {
|
|
994
1025
|
console.error('Spets not initialized. Run "spets init" first.');
|
|
995
1026
|
process.exit(1);
|
|
996
1027
|
}
|
|
@@ -1003,19 +1034,24 @@ async function startCommand(query, options) {
|
|
|
1003
1034
|
saveTaskMetadata(taskId, query, cwd);
|
|
1004
1035
|
let platform;
|
|
1005
1036
|
if (options.platform === "github") {
|
|
1006
|
-
|
|
1007
|
-
|
|
1037
|
+
const githubConfig = getGitHubConfig(cwd);
|
|
1038
|
+
const owner = options.owner || githubConfig?.owner;
|
|
1039
|
+
const repo = options.repo || githubConfig?.repo;
|
|
1040
|
+
const pr = options.pr || (githubConfig?.defaultPr ? String(githubConfig.defaultPr) : void 0);
|
|
1041
|
+
const issue = options.issue || (githubConfig?.defaultIssue ? String(githubConfig.defaultIssue) : void 0);
|
|
1042
|
+
if (!owner || !repo) {
|
|
1043
|
+
console.error("GitHub platform requires --owner and --repo (or set in .spets/config.yml)");
|
|
1008
1044
|
process.exit(1);
|
|
1009
1045
|
}
|
|
1010
|
-
if (!
|
|
1011
|
-
console.error("GitHub platform requires --pr or --issue");
|
|
1046
|
+
if (!pr && !issue) {
|
|
1047
|
+
console.error("GitHub platform requires --pr or --issue (or set defaultPr/defaultIssue in .spets/config.yml)");
|
|
1012
1048
|
process.exit(1);
|
|
1013
1049
|
}
|
|
1014
1050
|
platform = new GitHubPlatform({
|
|
1015
|
-
owner
|
|
1016
|
-
repo
|
|
1017
|
-
prNumber:
|
|
1018
|
-
issueNumber:
|
|
1051
|
+
owner,
|
|
1052
|
+
repo,
|
|
1053
|
+
prNumber: pr ? parseInt(pr, 10) : void 0,
|
|
1054
|
+
issueNumber: issue ? parseInt(issue, 10) : void 0
|
|
1019
1055
|
});
|
|
1020
1056
|
} else {
|
|
1021
1057
|
platform = new CliPlatform();
|
|
@@ -1038,7 +1074,7 @@ async function startCommand(query, options) {
|
|
|
1038
1074
|
import { select as select2 } from "@inquirer/prompts";
|
|
1039
1075
|
async function resumeCommand(options) {
|
|
1040
1076
|
const cwd = process.cwd();
|
|
1041
|
-
if (!
|
|
1077
|
+
if (!spetsExists(cwd)) {
|
|
1042
1078
|
console.error('Spets not initialized. Run "spets init" first.');
|
|
1043
1079
|
process.exit(1);
|
|
1044
1080
|
}
|
|
@@ -1060,7 +1096,7 @@ async function resumeCommand(options) {
|
|
|
1060
1096
|
}
|
|
1061
1097
|
if (resumableTasks.length === 0) {
|
|
1062
1098
|
console.log("No tasks to resume.");
|
|
1063
|
-
console.log('Start a new task with:
|
|
1099
|
+
console.log('Start a new task with: spets start "your task description"');
|
|
1064
1100
|
return;
|
|
1065
1101
|
}
|
|
1066
1102
|
if (resumableTasks.length === 1) {
|
|
@@ -1176,6 +1212,8 @@ function installClaudePlugin() {
|
|
|
1176
1212
|
console.log(' /spets start "your task description"');
|
|
1177
1213
|
console.log(" /spets status");
|
|
1178
1214
|
console.log(" /spets resume");
|
|
1215
|
+
console.log("");
|
|
1216
|
+
console.log("Note: The plugin uses npx to run spets, so global installation is not required.");
|
|
1179
1217
|
}
|
|
1180
1218
|
async function uninstallPlugin(name) {
|
|
1181
1219
|
if (name === "claude") {
|
|
@@ -1226,21 +1264,23 @@ When the user invokes this command:
|
|
|
1226
1264
|
2. Execute the appropriate spets CLI command using Bash
|
|
1227
1265
|
3. For 'start', read the generated documents and help iterate
|
|
1228
1266
|
|
|
1267
|
+
**Important**: Use \`npx spets\` to run commands. This ensures the command works even if spets is not globally installed.
|
|
1268
|
+
|
|
1229
1269
|
### Start Flow
|
|
1230
1270
|
|
|
1231
|
-
1. Run: \`spets start "<query>"\`
|
|
1232
|
-
2. Read the generated plan document from .
|
|
1271
|
+
1. Run: \`npx spets start "<query>"\`
|
|
1272
|
+
2. Read the generated plan document from .spets/outputs/<taskId>/
|
|
1233
1273
|
3. Present the plan to the user
|
|
1234
1274
|
4. If user approves, continue. If they want changes, provide feedback.
|
|
1235
1275
|
|
|
1236
1276
|
### Status Flow
|
|
1237
1277
|
|
|
1238
|
-
1. Run: \`spets status\`
|
|
1278
|
+
1. Run: \`npx spets status\`
|
|
1239
1279
|
2. Present the current workflow state
|
|
1240
1280
|
|
|
1241
1281
|
### Resume Flow
|
|
1242
1282
|
|
|
1243
|
-
1. Run: \`spets resume\`
|
|
1283
|
+
1. Run: \`npx spets resume\`
|
|
1244
1284
|
2. Continue the workflow from where it paused
|
|
1245
1285
|
|
|
1246
1286
|
## Example Session
|
|
@@ -1248,11 +1288,11 @@ When the user invokes this command:
|
|
|
1248
1288
|
User: /spets start "Create a REST API for user management"
|
|
1249
1289
|
|
|
1250
1290
|
Claude:
|
|
1251
|
-
1. Runs \`spets start "Create a REST API for user management"\`
|
|
1291
|
+
1. Runs \`npx spets start "Create a REST API for user management"\`
|
|
1252
1292
|
2. Reads the generated plan
|
|
1253
1293
|
3. Asks user: "Here's the plan. [shows plan] Would you like to approve or revise?"
|
|
1254
|
-
4. On approve: \`spets resume --approve\`
|
|
1255
|
-
5. On revise: \`spets resume --revise "user feedback"\`
|
|
1294
|
+
4. On approve: \`npx spets resume --approve\`
|
|
1295
|
+
5. On revise: \`npx spets resume --revise "user feedback"\`
|
|
1256
1296
|
|
|
1257
1297
|
$ARGUMENTS
|
|
1258
1298
|
command: The spets command to run (start, status, resume)
|
|
@@ -1263,18 +1303,27 @@ args: Additional arguments for the command
|
|
|
1263
1303
|
// src/commands/github.ts
|
|
1264
1304
|
async function githubCommand(options) {
|
|
1265
1305
|
const cwd = process.cwd();
|
|
1266
|
-
if (!
|
|
1306
|
+
if (!spetsExists(cwd)) {
|
|
1267
1307
|
console.error("Spets not initialized.");
|
|
1268
1308
|
process.exit(1);
|
|
1269
1309
|
}
|
|
1270
|
-
const
|
|
1310
|
+
const configGitHub = getGitHubConfig(cwd);
|
|
1311
|
+
const owner = options.owner || configGitHub?.owner;
|
|
1312
|
+
const repo = options.repo || configGitHub?.repo;
|
|
1313
|
+
const pr = options.pr || (configGitHub?.defaultPr ? String(configGitHub.defaultPr) : void 0);
|
|
1314
|
+
const issue = options.issue || (configGitHub?.defaultIssue ? String(configGitHub.defaultIssue) : void 0);
|
|
1315
|
+
const { comment, task } = options;
|
|
1316
|
+
if (!owner || !repo) {
|
|
1317
|
+
console.error("GitHub requires --owner and --repo (or set in .spets/config.yml)");
|
|
1318
|
+
process.exit(1);
|
|
1319
|
+
}
|
|
1271
1320
|
if (!pr && !issue) {
|
|
1272
|
-
console.error("Either --pr or --issue is required");
|
|
1321
|
+
console.error("Either --pr or --issue is required (or set defaultPr/defaultIssue in .spets/config.yml)");
|
|
1273
1322
|
process.exit(1);
|
|
1274
1323
|
}
|
|
1275
1324
|
const parsed = parseGitHubCommand(comment);
|
|
1276
1325
|
if (!parsed.command) {
|
|
1277
|
-
console.log("No
|
|
1326
|
+
console.log("No spets command found in comment. Ignoring.");
|
|
1278
1327
|
return;
|
|
1279
1328
|
}
|
|
1280
1329
|
console.log(`Received command: ${parsed.command}`);
|
|
@@ -1287,7 +1336,7 @@ async function githubCommand(options) {
|
|
|
1287
1336
|
}
|
|
1288
1337
|
if (!taskId) {
|
|
1289
1338
|
const config2 = loadConfig(cwd);
|
|
1290
|
-
const { listTasks: listTasks2 } = await import("./state-
|
|
1339
|
+
const { listTasks: listTasks2 } = await import("./state-H2GQS43T.js");
|
|
1291
1340
|
const tasks = listTasks2(cwd);
|
|
1292
1341
|
for (const tid of tasks) {
|
|
1293
1342
|
const state2 = getWorkflowState(tid, config2, cwd);
|
|
@@ -1415,10 +1464,10 @@ async function postComment(config, body) {
|
|
|
1415
1464
|
// src/index.ts
|
|
1416
1465
|
var program = new Command();
|
|
1417
1466
|
program.name("spets").description("Spec Driven Development Execution Framework").version("0.1.0");
|
|
1418
|
-
program.command("init").description("Initialize
|
|
1467
|
+
program.command("init").description("Initialize spets in current directory").option("-f, --force", "Overwrite existing config").option("--github", "Add GitHub Actions workflow for PR/Issue integration").action(initCommand);
|
|
1419
1468
|
program.command("status").description("Show current workflow status").option("-t, --task <taskId>", "Show status for specific task").action(statusCommand);
|
|
1420
1469
|
program.command("start").description("Start a new workflow").argument("<query>", "User query describing the task").option("-p, --platform <platform>", "Platform to use (cli, github)", "cli").option("--owner <owner>", "GitHub owner (for github platform)").option("--repo <repo>", "GitHub repo (for github platform)").option("--pr <number>", "GitHub PR number (for github platform)").option("--issue <number>", "GitHub issue number (for github platform)").action(startCommand);
|
|
1421
1470
|
program.command("resume").description("Resume paused workflow").option("-t, --task <taskId>", "Resume specific task").option("--approve", "Approve current document and proceed").option("--revise <feedback>", "Request revision with feedback").action(resumeCommand);
|
|
1422
|
-
program.command("github").description("Handle GitHub Action callback (internal)").
|
|
1471
|
+
program.command("github").description("Handle GitHub Action callback (internal)").option("--owner <owner>", "GitHub owner (or set in .spets/config.yml)").option("--repo <repo>", "GitHub repo (or set in .spets/config.yml)").option("--pr <number>", "PR number (or set defaultPr in .spets/config.yml)").option("--issue <number>", "Issue number (or set defaultIssue in .spets/config.yml)").option("-t, --task <taskId>", "Task ID").requiredOption("--comment <comment>", "Comment body").action(githubCommand);
|
|
1423
1472
|
program.command("plugin").description("Manage plugins").argument("<action>", "Action: install, uninstall, list").argument("[name]", "Plugin name").action(pluginCommand);
|
|
1424
1473
|
program.parse();
|