spets 0.1.0 → 0.1.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.
|
@@ -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);
|
|
@@ -190,8 +190,8 @@ function loadTaskMetadata(taskId, cwd = process.cwd()) {
|
|
|
190
190
|
}
|
|
191
191
|
|
|
192
192
|
export {
|
|
193
|
-
|
|
194
|
-
|
|
193
|
+
getSpetsDir,
|
|
194
|
+
spetsExists,
|
|
195
195
|
loadConfig,
|
|
196
196
|
loadStepDefinition,
|
|
197
197
|
generateTaskId,
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
generateTaskId,
|
|
4
4
|
getOutputPath,
|
|
5
|
-
|
|
5
|
+
getSpetsDir,
|
|
6
6
|
getWorkflowState,
|
|
7
7
|
listTasks,
|
|
8
8
|
loadConfig,
|
|
@@ -11,9 +11,9 @@ import {
|
|
|
11
11
|
loadTaskMetadata,
|
|
12
12
|
saveDocument,
|
|
13
13
|
saveTaskMetadata,
|
|
14
|
-
|
|
14
|
+
spetsExists,
|
|
15
15
|
updateDocumentStatus
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-RW22VIUI.js";
|
|
17
17
|
|
|
18
18
|
// src/index.ts
|
|
19
19
|
import { Command } from "commander";
|
|
@@ -25,32 +25,32 @@ import { fileURLToPath } from "url";
|
|
|
25
25
|
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
26
26
|
async function initCommand(options) {
|
|
27
27
|
const cwd = process.cwd();
|
|
28
|
-
const
|
|
29
|
-
if (
|
|
28
|
+
const spetsDir = getSpetsDir(cwd);
|
|
29
|
+
if (spetsExists(cwd) && !options.force) {
|
|
30
30
|
console.error("Spets already initialized. Use --force to overwrite.");
|
|
31
31
|
process.exit(1);
|
|
32
32
|
}
|
|
33
|
-
mkdirSync(
|
|
34
|
-
mkdirSync(join(
|
|
35
|
-
mkdirSync(join(
|
|
36
|
-
mkdirSync(join(
|
|
33
|
+
mkdirSync(spetsDir, { recursive: true });
|
|
34
|
+
mkdirSync(join(spetsDir, "steps"), { recursive: true });
|
|
35
|
+
mkdirSync(join(spetsDir, "outputs"), { recursive: true });
|
|
36
|
+
mkdirSync(join(spetsDir, "hooks"), { recursive: true });
|
|
37
37
|
const templatesDir = join(__dirname, "..", "templates");
|
|
38
|
-
writeFileSync(join(
|
|
39
|
-
createDefaultSteps(
|
|
40
|
-
console.log("Initialized spets in .
|
|
38
|
+
writeFileSync(join(spetsDir, "config.yml"), getDefaultConfig());
|
|
39
|
+
createDefaultSteps(spetsDir);
|
|
40
|
+
console.log("Initialized spets in .spets/");
|
|
41
41
|
console.log("");
|
|
42
42
|
console.log("Created:");
|
|
43
|
-
console.log(" .
|
|
44
|
-
console.log(" .
|
|
45
|
-
console.log(" .
|
|
43
|
+
console.log(" .spets/config.yml - Workflow configuration");
|
|
44
|
+
console.log(" .spets/steps/01-plan/ - Planning step");
|
|
45
|
+
console.log(" .spets/steps/02-implement/ - Implementation step");
|
|
46
46
|
if (options.github) {
|
|
47
47
|
createGitHubWorkflow(cwd);
|
|
48
|
-
console.log(" .github/workflows/
|
|
48
|
+
console.log(" .github/workflows/spets.yml - GitHub Actions workflow");
|
|
49
49
|
}
|
|
50
50
|
console.log("");
|
|
51
51
|
console.log("Next steps:");
|
|
52
|
-
console.log(" 1. Edit .
|
|
53
|
-
console.log(" 2. Customize step instructions in .
|
|
52
|
+
console.log(" 1. Edit .spets/config.yml to customize your workflow");
|
|
53
|
+
console.log(" 2. Customize step instructions in .spets/steps/");
|
|
54
54
|
if (options.github) {
|
|
55
55
|
console.log(" 3. Add ANTHROPIC_API_KEY to your repo secrets");
|
|
56
56
|
console.log(' 4. Run: spets start "task" --platform github --owner <owner> --repo <repo> --issue <n>');
|
|
@@ -75,12 +75,12 @@ steps:
|
|
|
75
75
|
# onComplete: "./hooks/on-complete.sh"
|
|
76
76
|
`;
|
|
77
77
|
}
|
|
78
|
-
function createDefaultSteps(
|
|
79
|
-
const planDir = join(
|
|
78
|
+
function createDefaultSteps(spetsDir) {
|
|
79
|
+
const planDir = join(spetsDir, "steps", "01-plan");
|
|
80
80
|
mkdirSync(planDir, { recursive: true });
|
|
81
81
|
writeFileSync(join(planDir, "instruction.md"), getPlanInstruction());
|
|
82
82
|
writeFileSync(join(planDir, "template.md"), getPlanTemplate());
|
|
83
|
-
const implementDir = join(
|
|
83
|
+
const implementDir = join(spetsDir, "steps", "02-implement");
|
|
84
84
|
mkdirSync(implementDir, { recursive: true });
|
|
85
85
|
writeFileSync(join(implementDir, "instruction.md"), getImplementInstruction());
|
|
86
86
|
writeFileSync(join(implementDir, "template.md"), getImplementTemplate());
|
|
@@ -239,7 +239,7 @@ None / List any deviations with justification.
|
|
|
239
239
|
function createGitHubWorkflow(cwd) {
|
|
240
240
|
const workflowDir = join(cwd, ".github", "workflows");
|
|
241
241
|
mkdirSync(workflowDir, { recursive: true });
|
|
242
|
-
writeFileSync(join(workflowDir, "
|
|
242
|
+
writeFileSync(join(workflowDir, "spets.yml"), getGitHubWorkflow());
|
|
243
243
|
}
|
|
244
244
|
function getGitHubWorkflow() {
|
|
245
245
|
const gh = (expr) => `\${{ ${expr} }}`;
|
|
@@ -253,8 +253,8 @@ on:
|
|
|
253
253
|
types: [created]
|
|
254
254
|
|
|
255
255
|
jobs:
|
|
256
|
-
handle-
|
|
257
|
-
# Only run if comment contains a
|
|
256
|
+
handle-spets-command:
|
|
257
|
+
# Only run if comment contains a spets command
|
|
258
258
|
if: |
|
|
259
259
|
contains(github.event.comment.body, '/approve') ||
|
|
260
260
|
contains(github.event.comment.body, '/revise') ||
|
|
@@ -303,7 +303,7 @@ jobs:
|
|
|
303
303
|
|
|
304
304
|
Instructions:
|
|
305
305
|
1. Parse the command from the comment (/approve, /revise, /reject, or /answer)
|
|
306
|
-
2. Find the active
|
|
306
|
+
2. Find the active spets task in .spets/outputs/
|
|
307
307
|
3. Execute the appropriate action:
|
|
308
308
|
- /approve: Mark current step as approved, generate next step
|
|
309
309
|
- /revise <feedback>: Regenerate current step with feedback
|
|
@@ -325,7 +325,7 @@ jobs:
|
|
|
325
325
|
git config user.name "github-actions[bot]"
|
|
326
326
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
327
327
|
git add -A
|
|
328
|
-
git diff --staged --quiet || git commit -m "
|
|
328
|
+
git diff --staged --quiet || git commit -m "Spets: Update from workflow"
|
|
329
329
|
git push
|
|
330
330
|
env:
|
|
331
331
|
GH_TOKEN: ${gh("secrets.GITHUB_TOKEN")}
|
|
@@ -335,7 +335,7 @@ jobs:
|
|
|
335
335
|
// src/commands/status.ts
|
|
336
336
|
async function statusCommand(options) {
|
|
337
337
|
const cwd = process.cwd();
|
|
338
|
-
if (!
|
|
338
|
+
if (!spetsExists(cwd)) {
|
|
339
339
|
console.error('Spets not initialized. Run "spets init" first.');
|
|
340
340
|
process.exit(1);
|
|
341
341
|
}
|
|
@@ -373,7 +373,7 @@ function showAllTasks(config, cwd) {
|
|
|
373
373
|
if (tasks.length === 0) {
|
|
374
374
|
console.log("No tasks found.");
|
|
375
375
|
console.log("");
|
|
376
|
-
console.log('Start a new task with:
|
|
376
|
+
console.log('Start a new task with: spets start "your task description"');
|
|
377
377
|
return;
|
|
378
378
|
}
|
|
379
379
|
console.log("Tasks:");
|
|
@@ -393,7 +393,7 @@ function showAllTasks(config, cwd) {
|
|
|
393
393
|
console.log(` ... and ${tasks.length - 10} more tasks`);
|
|
394
394
|
}
|
|
395
395
|
console.log("");
|
|
396
|
-
console.log('Use "
|
|
396
|
+
console.log('Use "spets status -t <taskId>" for details');
|
|
397
397
|
}
|
|
398
398
|
function formatStatus(status) {
|
|
399
399
|
const icons = {
|
|
@@ -418,18 +418,18 @@ import { spawn } from "child_process";
|
|
|
418
418
|
import { existsSync as existsSync2 } from "fs";
|
|
419
419
|
import { join as join2, isAbsolute } from "path";
|
|
420
420
|
async function runHook(hookPath, context, cwd = process.cwd()) {
|
|
421
|
-
const resolvedPath = isAbsolute(hookPath) ? hookPath : join2(
|
|
421
|
+
const resolvedPath = isAbsolute(hookPath) ? hookPath : join2(getSpetsDir(cwd), hookPath);
|
|
422
422
|
if (!existsSync2(resolvedPath)) {
|
|
423
423
|
console.warn(`Hook not found: ${resolvedPath}`);
|
|
424
424
|
return;
|
|
425
425
|
}
|
|
426
426
|
const env = {
|
|
427
427
|
...process.env,
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
428
|
+
SPETS_TASK_ID: context.taskId,
|
|
429
|
+
SPETS_STEP_NAME: context.stepName,
|
|
430
|
+
SPETS_STEP_INDEX: String(context.stepIndex),
|
|
431
|
+
SPETS_OUTPUT_PATH: context.outputPath,
|
|
432
|
+
SPETS_CWD: cwd
|
|
433
433
|
};
|
|
434
434
|
return new Promise((resolve, reject) => {
|
|
435
435
|
const proc = spawn(resolvedPath, [], {
|
|
@@ -516,7 +516,7 @@ var Executor = class {
|
|
|
516
516
|
return;
|
|
517
517
|
}
|
|
518
518
|
if (result.paused) {
|
|
519
|
-
console.log("\n\u23F8\uFE0F Workflow paused. Resume with:
|
|
519
|
+
console.log("\n\u23F8\uFE0F Workflow paused. Resume with: spets resume");
|
|
520
520
|
return;
|
|
521
521
|
}
|
|
522
522
|
if (result.approved) {
|
|
@@ -710,9 +710,15 @@ var CliPlatform = class extends BasePlatform {
|
|
|
710
710
|
}
|
|
711
711
|
async callClaude(prompt) {
|
|
712
712
|
return new Promise((resolve, reject) => {
|
|
713
|
-
const proc = spawn2(this.claudeCommand, [
|
|
713
|
+
const proc = spawn2(this.claudeCommand, [
|
|
714
|
+
"--print",
|
|
715
|
+
"--permission-mode",
|
|
716
|
+
"bypassPermissions"
|
|
717
|
+
], {
|
|
714
718
|
stdio: ["pipe", "pipe", "pipe"]
|
|
715
719
|
});
|
|
720
|
+
proc.stdin.write(prompt);
|
|
721
|
+
proc.stdin.end();
|
|
716
722
|
let stdout = "";
|
|
717
723
|
let stderr = "";
|
|
718
724
|
proc.stdout.on("data", (data) => {
|
|
@@ -834,7 +840,7 @@ var GitHubPlatform = class extends BasePlatform {
|
|
|
834
840
|
}
|
|
835
841
|
formatQuestionsComment(questions, taskId) {
|
|
836
842
|
const lines = [
|
|
837
|
-
"## \u{1F4CB}
|
|
843
|
+
"## \u{1F4CB} Spets: Questions Need Answers",
|
|
838
844
|
"",
|
|
839
845
|
`> Task ID: \`${taskId}\``,
|
|
840
846
|
"",
|
|
@@ -861,7 +867,7 @@ var GitHubPlatform = class extends BasePlatform {
|
|
|
861
867
|
}
|
|
862
868
|
formatApprovalComment(doc, stepName, taskId) {
|
|
863
869
|
const lines = [
|
|
864
|
-
`## \u{1F4C4}
|
|
870
|
+
`## \u{1F4C4} Spets: ${stepName} - Review Required`,
|
|
865
871
|
"",
|
|
866
872
|
`> Task ID: \`${taskId}\``,
|
|
867
873
|
"",
|
|
@@ -945,7 +951,13 @@ var GitHubPlatform = class extends BasePlatform {
|
|
|
945
951
|
}
|
|
946
952
|
async callClaude(prompt) {
|
|
947
953
|
return new Promise((resolve, reject) => {
|
|
948
|
-
const proc = spawn3("claude", [
|
|
954
|
+
const proc = spawn3("claude", [
|
|
955
|
+
"--print",
|
|
956
|
+
"--permission-mode",
|
|
957
|
+
"bypassPermissions"
|
|
958
|
+
], { stdio: ["pipe", "pipe", "pipe"] });
|
|
959
|
+
proc.stdin.write(prompt);
|
|
960
|
+
proc.stdin.end();
|
|
949
961
|
let stdout = "";
|
|
950
962
|
let stderr = "";
|
|
951
963
|
proc.stdout.on("data", (data) => {
|
|
@@ -990,7 +1002,7 @@ var GitHubPlatform = class extends BasePlatform {
|
|
|
990
1002
|
// src/commands/start.ts
|
|
991
1003
|
async function startCommand(query, options) {
|
|
992
1004
|
const cwd = process.cwd();
|
|
993
|
-
if (!
|
|
1005
|
+
if (!spetsExists(cwd)) {
|
|
994
1006
|
console.error('Spets not initialized. Run "spets init" first.');
|
|
995
1007
|
process.exit(1);
|
|
996
1008
|
}
|
|
@@ -1038,7 +1050,7 @@ async function startCommand(query, options) {
|
|
|
1038
1050
|
import { select as select2 } from "@inquirer/prompts";
|
|
1039
1051
|
async function resumeCommand(options) {
|
|
1040
1052
|
const cwd = process.cwd();
|
|
1041
|
-
if (!
|
|
1053
|
+
if (!spetsExists(cwd)) {
|
|
1042
1054
|
console.error('Spets not initialized. Run "spets init" first.');
|
|
1043
1055
|
process.exit(1);
|
|
1044
1056
|
}
|
|
@@ -1060,7 +1072,7 @@ async function resumeCommand(options) {
|
|
|
1060
1072
|
}
|
|
1061
1073
|
if (resumableTasks.length === 0) {
|
|
1062
1074
|
console.log("No tasks to resume.");
|
|
1063
|
-
console.log('Start a new task with:
|
|
1075
|
+
console.log('Start a new task with: spets start "your task description"');
|
|
1064
1076
|
return;
|
|
1065
1077
|
}
|
|
1066
1078
|
if (resumableTasks.length === 1) {
|
|
@@ -1229,7 +1241,7 @@ When the user invokes this command:
|
|
|
1229
1241
|
### Start Flow
|
|
1230
1242
|
|
|
1231
1243
|
1. Run: \`spets start "<query>"\`
|
|
1232
|
-
2. Read the generated plan document from .
|
|
1244
|
+
2. Read the generated plan document from .spets/outputs/<taskId>/
|
|
1233
1245
|
3. Present the plan to the user
|
|
1234
1246
|
4. If user approves, continue. If they want changes, provide feedback.
|
|
1235
1247
|
|
|
@@ -1263,7 +1275,7 @@ args: Additional arguments for the command
|
|
|
1263
1275
|
// src/commands/github.ts
|
|
1264
1276
|
async function githubCommand(options) {
|
|
1265
1277
|
const cwd = process.cwd();
|
|
1266
|
-
if (!
|
|
1278
|
+
if (!spetsExists(cwd)) {
|
|
1267
1279
|
console.error("Spets not initialized.");
|
|
1268
1280
|
process.exit(1);
|
|
1269
1281
|
}
|
|
@@ -1274,7 +1286,7 @@ async function githubCommand(options) {
|
|
|
1274
1286
|
}
|
|
1275
1287
|
const parsed = parseGitHubCommand(comment);
|
|
1276
1288
|
if (!parsed.command) {
|
|
1277
|
-
console.log("No
|
|
1289
|
+
console.log("No spets command found in comment. Ignoring.");
|
|
1278
1290
|
return;
|
|
1279
1291
|
}
|
|
1280
1292
|
console.log(`Received command: ${parsed.command}`);
|
|
@@ -1287,7 +1299,7 @@ async function githubCommand(options) {
|
|
|
1287
1299
|
}
|
|
1288
1300
|
if (!taskId) {
|
|
1289
1301
|
const config2 = loadConfig(cwd);
|
|
1290
|
-
const { listTasks: listTasks2 } = await import("./state-
|
|
1302
|
+
const { listTasks: listTasks2 } = await import("./state-34RYXOKQ.js");
|
|
1291
1303
|
const tasks = listTasks2(cwd);
|
|
1292
1304
|
for (const tid of tasks) {
|
|
1293
1305
|
const state2 = getWorkflowState(tid, config2, cwd);
|
|
@@ -1415,7 +1427,7 @@ async function postComment(config, body) {
|
|
|
1415
1427
|
// src/index.ts
|
|
1416
1428
|
var program = new Command();
|
|
1417
1429
|
program.name("spets").description("Spec Driven Development Execution Framework").version("0.1.0");
|
|
1418
|
-
program.command("init").description("Initialize
|
|
1430
|
+
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
1431
|
program.command("status").description("Show current workflow status").option("-t, --task <taskId>", "Show status for specific task").action(statusCommand);
|
|
1420
1432
|
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
1433
|
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);
|