spets 0.1.49 → 0.1.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +264 -32
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
|
-
import { readFileSync as
|
|
6
|
-
import { dirname as
|
|
5
|
+
import { readFileSync as readFileSync13 } from "fs";
|
|
6
|
+
import { dirname as dirname7, join as join11 } from "path";
|
|
7
7
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
8
8
|
|
|
9
9
|
// src/commands/init.ts
|
|
@@ -3095,6 +3095,135 @@ function createCodexAdapter(codexCommand = "codex") {
|
|
|
3095
3095
|
};
|
|
3096
3096
|
}
|
|
3097
3097
|
|
|
3098
|
+
// src/adapters/opencode.ts
|
|
3099
|
+
import { spawn as spawn4 } from "child_process";
|
|
3100
|
+
import { readFileSync as readFileSync10, existsSync as existsSync10, mkdirSync as mkdirSync7 } from "fs";
|
|
3101
|
+
import { dirname as dirname6 } from "path";
|
|
3102
|
+
var OpenCodeAIAdapter = class {
|
|
3103
|
+
opencodeCommand;
|
|
3104
|
+
constructor(opencodeCommand = "opencode") {
|
|
3105
|
+
this.opencodeCommand = opencodeCommand;
|
|
3106
|
+
}
|
|
3107
|
+
async execute(params) {
|
|
3108
|
+
console.log(`
|
|
3109
|
+
\u{1F4DD} Generating...`);
|
|
3110
|
+
if (params.outputPath) {
|
|
3111
|
+
const outputDir = dirname6(params.outputPath);
|
|
3112
|
+
if (!existsSync10(outputDir)) {
|
|
3113
|
+
mkdirSync7(outputDir, { recursive: true });
|
|
3114
|
+
}
|
|
3115
|
+
}
|
|
3116
|
+
const stdout = await this.callOpenCode(params.prompt);
|
|
3117
|
+
if (params.outputPath && existsSync10(params.outputPath)) {
|
|
3118
|
+
return readFileSync10(params.outputPath, "utf-8");
|
|
3119
|
+
}
|
|
3120
|
+
return stdout;
|
|
3121
|
+
}
|
|
3122
|
+
async callOpenCode(prompt, showSpinner = true) {
|
|
3123
|
+
return new Promise((resolve, reject) => {
|
|
3124
|
+
const proc = spawn4(this.opencodeCommand, [
|
|
3125
|
+
"run"
|
|
3126
|
+
], {
|
|
3127
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
3128
|
+
});
|
|
3129
|
+
proc.stdin.write(prompt);
|
|
3130
|
+
proc.stdin.end();
|
|
3131
|
+
let stdout = "";
|
|
3132
|
+
let stderr = "";
|
|
3133
|
+
let spinner;
|
|
3134
|
+
if (showSpinner) {
|
|
3135
|
+
const frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
3136
|
+
let frameIndex = 0;
|
|
3137
|
+
spinner = setInterval(() => {
|
|
3138
|
+
process.stdout.write(`\r${frames[frameIndex++ % frames.length]} OpenCode is working...`);
|
|
3139
|
+
}, 100);
|
|
3140
|
+
}
|
|
3141
|
+
const stopSpinner = (success) => {
|
|
3142
|
+
if (spinner) {
|
|
3143
|
+
clearInterval(spinner);
|
|
3144
|
+
process.stdout.write(`\r${success ? "\u2713" : "\u2717"} OpenCode ${success ? "done" : "failed"}
|
|
3145
|
+
`);
|
|
3146
|
+
}
|
|
3147
|
+
};
|
|
3148
|
+
proc.stdout.on("data", (data) => {
|
|
3149
|
+
stdout += data.toString();
|
|
3150
|
+
});
|
|
3151
|
+
proc.stderr.on("data", (data) => {
|
|
3152
|
+
stderr += data.toString();
|
|
3153
|
+
});
|
|
3154
|
+
proc.on("close", (code) => {
|
|
3155
|
+
stopSpinner(code === 0);
|
|
3156
|
+
if (code !== 0) {
|
|
3157
|
+
reject(new Error(`OpenCode CLI exited with code ${code}: ${stderr}`));
|
|
3158
|
+
} else {
|
|
3159
|
+
resolve(stdout);
|
|
3160
|
+
}
|
|
3161
|
+
});
|
|
3162
|
+
proc.on("error", (err) => {
|
|
3163
|
+
stopSpinner(false);
|
|
3164
|
+
reject(new Error(`Failed to run OpenCode CLI: ${err.message}`));
|
|
3165
|
+
});
|
|
3166
|
+
});
|
|
3167
|
+
}
|
|
3168
|
+
/**
|
|
3169
|
+
* Execute multiple prompts in parallel with shared progress display
|
|
3170
|
+
*/
|
|
3171
|
+
async executeParallel(params) {
|
|
3172
|
+
const total = params.length;
|
|
3173
|
+
let completed = 0;
|
|
3174
|
+
const results = /* @__PURE__ */ new Map();
|
|
3175
|
+
const frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
3176
|
+
let frameIndex = 0;
|
|
3177
|
+
const spinner = setInterval(() => {
|
|
3178
|
+
const progress = `${completed}/${total}`;
|
|
3179
|
+
process.stdout.write(`\r${frames[frameIndex++ % frames.length]} Generating sections in parallel... [${progress}]`);
|
|
3180
|
+
}, 100);
|
|
3181
|
+
const stopSpinner = (success) => {
|
|
3182
|
+
clearInterval(spinner);
|
|
3183
|
+
const status = success ? "\u2713" : "\u2717";
|
|
3184
|
+
const message = success ? `All ${total} sections generated` : `Generation completed with errors`;
|
|
3185
|
+
process.stdout.write(`\r${status} ${message}
|
|
3186
|
+
`);
|
|
3187
|
+
};
|
|
3188
|
+
await Promise.all(
|
|
3189
|
+
params.map(async (p) => {
|
|
3190
|
+
const id = p.id || p.outputPath;
|
|
3191
|
+
try {
|
|
3192
|
+
if (p.outputPath) {
|
|
3193
|
+
const outputDir = dirname6(p.outputPath);
|
|
3194
|
+
if (!existsSync10(outputDir)) {
|
|
3195
|
+
mkdirSync7(outputDir, { recursive: true });
|
|
3196
|
+
}
|
|
3197
|
+
}
|
|
3198
|
+
await this.callOpenCode(p.prompt, false);
|
|
3199
|
+
let result = "";
|
|
3200
|
+
if (p.outputPath && existsSync10(p.outputPath)) {
|
|
3201
|
+
result = readFileSync10(p.outputPath, "utf-8");
|
|
3202
|
+
}
|
|
3203
|
+
completed++;
|
|
3204
|
+
results.set(id, { id, success: true, result });
|
|
3205
|
+
} catch (error) {
|
|
3206
|
+
completed++;
|
|
3207
|
+
results.set(id, {
|
|
3208
|
+
id,
|
|
3209
|
+
success: false,
|
|
3210
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3211
|
+
});
|
|
3212
|
+
}
|
|
3213
|
+
})
|
|
3214
|
+
);
|
|
3215
|
+
stopSpinner(Array.from(results.values()).every((r) => r.success));
|
|
3216
|
+
return params.map((p) => results.get(p.id || p.outputPath));
|
|
3217
|
+
}
|
|
3218
|
+
};
|
|
3219
|
+
function createOpenCodeAdapter(opencodeCommand = "opencode") {
|
|
3220
|
+
return {
|
|
3221
|
+
ai: new OpenCodeAIAdapter(opencodeCommand),
|
|
3222
|
+
io: new CLIIOAdapter(),
|
|
3223
|
+
system: new CLISystemAdapter()
|
|
3224
|
+
};
|
|
3225
|
+
}
|
|
3226
|
+
|
|
3098
3227
|
// src/commands/start.ts
|
|
3099
3228
|
function getGitHubInfo(cwd) {
|
|
3100
3229
|
const config = getGitHubConfig(cwd);
|
|
@@ -3166,6 +3295,9 @@ async function startCommand(query, options) {
|
|
|
3166
3295
|
if (agentType === "codex") {
|
|
3167
3296
|
adapter = createCodexAdapter();
|
|
3168
3297
|
console.log("Platform: cli (codex)");
|
|
3298
|
+
} else if (agentType === "opencode") {
|
|
3299
|
+
adapter = createOpenCodeAdapter();
|
|
3300
|
+
console.log("Platform: cli (opencode)");
|
|
3169
3301
|
} else {
|
|
3170
3302
|
adapter = createCLIAdapter();
|
|
3171
3303
|
console.log("Platform: cli (claude)");
|
|
@@ -3349,15 +3481,15 @@ Resume with: spets resume --task ${taskId}`);
|
|
|
3349
3481
|
|
|
3350
3482
|
// src/commands/resume.ts
|
|
3351
3483
|
import { select as select2 } from "@inquirer/prompts";
|
|
3352
|
-
import { existsSync as
|
|
3484
|
+
import { existsSync as existsSync11, readdirSync as readdirSync2, readFileSync as readFileSync11 } from "fs";
|
|
3353
3485
|
import { join as join7 } from "path";
|
|
3354
3486
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
3355
3487
|
async function createPR(cwd, taskId, outputs) {
|
|
3356
3488
|
const outputContents = [];
|
|
3357
3489
|
for (const outputPath of outputs) {
|
|
3358
3490
|
const fullPath = join7(cwd, outputPath);
|
|
3359
|
-
if (
|
|
3360
|
-
const content =
|
|
3491
|
+
if (existsSync11(fullPath)) {
|
|
3492
|
+
const content = readFileSync11(fullPath, "utf-8");
|
|
3361
3493
|
outputContents.push(`## ${outputPath}
|
|
3362
3494
|
|
|
3363
3495
|
${content}`);
|
|
@@ -3379,7 +3511,7 @@ ${outputContents.join("\n\n---\n\n")}`;
|
|
|
3379
3511
|
}
|
|
3380
3512
|
function listResumableTasks(cwd) {
|
|
3381
3513
|
const outputsDir = getOutputsDir(cwd);
|
|
3382
|
-
if (!
|
|
3514
|
+
if (!existsSync11(outputsDir)) {
|
|
3383
3515
|
return [];
|
|
3384
3516
|
}
|
|
3385
3517
|
const tasks = [];
|
|
@@ -3387,7 +3519,7 @@ function listResumableTasks(cwd) {
|
|
|
3387
3519
|
const orchestrator = new Orchestrator(cwd);
|
|
3388
3520
|
for (const taskId of taskDirs) {
|
|
3389
3521
|
const stateFile = join7(outputsDir, taskId, ".state.json");
|
|
3390
|
-
if (!
|
|
3522
|
+
if (!existsSync11(stateFile)) continue;
|
|
3391
3523
|
try {
|
|
3392
3524
|
const response = orchestrator.cmdStatus(taskId);
|
|
3393
3525
|
if (response.type === "error") continue;
|
|
@@ -3638,7 +3770,7 @@ Resume with: spets resume --task ${taskId}`);
|
|
|
3638
3770
|
}
|
|
3639
3771
|
|
|
3640
3772
|
// src/commands/plugin.ts
|
|
3641
|
-
import { existsSync as
|
|
3773
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync8, writeFileSync as writeFileSync7, rmSync, readdirSync as readdirSync3 } from "fs";
|
|
3642
3774
|
import { join as join8 } from "path";
|
|
3643
3775
|
import { homedir } from "os";
|
|
3644
3776
|
async function pluginCommand(action, name) {
|
|
@@ -3671,12 +3803,13 @@ async function pluginCommand(action, name) {
|
|
|
3671
3803
|
async function installPlugin(name) {
|
|
3672
3804
|
const plugins = {
|
|
3673
3805
|
claude: installClaudePlugin,
|
|
3674
|
-
codex: installCodexPlugin
|
|
3806
|
+
codex: installCodexPlugin,
|
|
3807
|
+
opencode: installOpenCodePlugin
|
|
3675
3808
|
};
|
|
3676
3809
|
const installer = plugins[name];
|
|
3677
3810
|
if (!installer) {
|
|
3678
3811
|
console.error(`Unknown plugin: ${name}`);
|
|
3679
|
-
console.error("Available plugins: claude, codex");
|
|
3812
|
+
console.error("Available plugins: claude, codex, opencode");
|
|
3680
3813
|
process.exit(1);
|
|
3681
3814
|
}
|
|
3682
3815
|
installer();
|
|
@@ -3684,7 +3817,7 @@ async function installPlugin(name) {
|
|
|
3684
3817
|
function installClaudePlugin() {
|
|
3685
3818
|
const claudeDir = join8(homedir(), ".claude");
|
|
3686
3819
|
const commandsDir = join8(claudeDir, "commands");
|
|
3687
|
-
|
|
3820
|
+
mkdirSync8(commandsDir, { recursive: true });
|
|
3688
3821
|
const skillPath = join8(commandsDir, "spets.md");
|
|
3689
3822
|
writeFileSync7(skillPath, getClaudeSkillContent());
|
|
3690
3823
|
console.log("Installed Claude Code plugin.");
|
|
@@ -3700,7 +3833,7 @@ function installCodexPlugin() {
|
|
|
3700
3833
|
const codexDir = join8(homedir(), ".codex");
|
|
3701
3834
|
const skillsDir = join8(codexDir, "skills");
|
|
3702
3835
|
const spetsDir = join8(skillsDir, "spets");
|
|
3703
|
-
|
|
3836
|
+
mkdirSync8(spetsDir, { recursive: true });
|
|
3704
3837
|
const skillPath = join8(spetsDir, "SKILL.md");
|
|
3705
3838
|
writeFileSync7(skillPath, getCodexSkillContent());
|
|
3706
3839
|
console.log("Installed Codex CLI plugin.");
|
|
@@ -3712,16 +3845,32 @@ function installCodexPlugin() {
|
|
|
3712
3845
|
console.log("This skill runs deterministically within your Codex CLI session.");
|
|
3713
3846
|
console.log("No additional Codex processes are spawned.");
|
|
3714
3847
|
}
|
|
3848
|
+
function installOpenCodePlugin() {
|
|
3849
|
+
const configDir = join8(homedir(), ".config", "opencode");
|
|
3850
|
+
const skillsDir = join8(configDir, "skills");
|
|
3851
|
+
const spetsDir = join8(skillsDir, "spets");
|
|
3852
|
+
mkdirSync8(spetsDir, { recursive: true });
|
|
3853
|
+
const skillPath = join8(spetsDir, "SKILL.md");
|
|
3854
|
+
writeFileSync7(skillPath, getOpenCodeSkillContent());
|
|
3855
|
+
console.log("Installed OpenCode plugin.");
|
|
3856
|
+
console.log(`Location: ${skillPath}`);
|
|
3857
|
+
console.log("");
|
|
3858
|
+
console.log("Usage in OpenCode:");
|
|
3859
|
+
console.log(" Use the spets skill in your OpenCode session");
|
|
3860
|
+
console.log("");
|
|
3861
|
+
console.log("This skill runs deterministically within your OpenCode session.");
|
|
3862
|
+
console.log("No additional OpenCode processes are spawned.");
|
|
3863
|
+
}
|
|
3715
3864
|
async function uninstallPlugin(name) {
|
|
3716
3865
|
if (name === "claude") {
|
|
3717
3866
|
const skillPath = join8(homedir(), ".claude", "commands", "spets.md");
|
|
3718
3867
|
const legacySkillPath = join8(homedir(), ".claude", "commands", "sdd-do.md");
|
|
3719
3868
|
let uninstalled = false;
|
|
3720
|
-
if (
|
|
3869
|
+
if (existsSync12(skillPath)) {
|
|
3721
3870
|
rmSync(skillPath);
|
|
3722
3871
|
uninstalled = true;
|
|
3723
3872
|
}
|
|
3724
|
-
if (
|
|
3873
|
+
if (existsSync12(legacySkillPath)) {
|
|
3725
3874
|
rmSync(legacySkillPath);
|
|
3726
3875
|
uninstalled = true;
|
|
3727
3876
|
}
|
|
@@ -3734,7 +3883,7 @@ async function uninstallPlugin(name) {
|
|
|
3734
3883
|
}
|
|
3735
3884
|
if (name === "codex") {
|
|
3736
3885
|
const skillPath = join8(homedir(), ".codex", "skills", "spets", "SKILL.md");
|
|
3737
|
-
if (
|
|
3886
|
+
if (existsSync12(skillPath)) {
|
|
3738
3887
|
rmSync(skillPath);
|
|
3739
3888
|
try {
|
|
3740
3889
|
const skillDir = join8(homedir(), ".codex", "skills", "spets");
|
|
@@ -3752,24 +3901,48 @@ async function uninstallPlugin(name) {
|
|
|
3752
3901
|
}
|
|
3753
3902
|
return;
|
|
3754
3903
|
}
|
|
3904
|
+
if (name === "opencode") {
|
|
3905
|
+
const skillPath = join8(homedir(), ".config", "opencode", "skills", "spets", "SKILL.md");
|
|
3906
|
+
if (existsSync12(skillPath)) {
|
|
3907
|
+
rmSync(skillPath);
|
|
3908
|
+
try {
|
|
3909
|
+
const skillDir = join8(homedir(), ".config", "opencode", "skills", "spets");
|
|
3910
|
+
const skillsDir = join8(homedir(), ".config", "opencode", "skills");
|
|
3911
|
+
rmSync(skillDir, { recursive: true });
|
|
3912
|
+
const remaining = readdirSync3(skillsDir);
|
|
3913
|
+
if (remaining.length === 0) {
|
|
3914
|
+
rmSync(skillsDir);
|
|
3915
|
+
}
|
|
3916
|
+
} catch {
|
|
3917
|
+
}
|
|
3918
|
+
console.log("Uninstalled OpenCode plugin.");
|
|
3919
|
+
} else {
|
|
3920
|
+
console.log("OpenCode plugin not installed.");
|
|
3921
|
+
}
|
|
3922
|
+
return;
|
|
3923
|
+
}
|
|
3755
3924
|
console.error(`Unknown plugin: ${name}`);
|
|
3756
3925
|
process.exit(1);
|
|
3757
3926
|
}
|
|
3758
3927
|
async function listPlugins() {
|
|
3759
3928
|
console.log("Available plugins:");
|
|
3760
3929
|
console.log("");
|
|
3761
|
-
console.log(" claude
|
|
3762
|
-
console.log(" codex
|
|
3930
|
+
console.log(" claude - Claude Code /spets skill");
|
|
3931
|
+
console.log(" codex - Codex CLI $spets skill");
|
|
3932
|
+
console.log(" opencode - OpenCode spets skill");
|
|
3763
3933
|
console.log("");
|
|
3764
3934
|
const claudeSkillPath = join8(homedir(), ".claude", "commands", "spets.md");
|
|
3765
3935
|
const claudeLegacySkillPath = join8(homedir(), ".claude", "commands", "sdd-do.md");
|
|
3766
|
-
const claudeInstalled =
|
|
3936
|
+
const claudeInstalled = existsSync12(claudeSkillPath) || existsSync12(claudeLegacySkillPath);
|
|
3767
3937
|
const codexSkillPath = join8(homedir(), ".codex", "skills", "spets", "SKILL.md");
|
|
3768
|
-
const codexInstalled =
|
|
3938
|
+
const codexInstalled = existsSync12(codexSkillPath);
|
|
3939
|
+
const opencodeSkillPath = join8(homedir(), ".config", "opencode", "skills", "spets", "SKILL.md");
|
|
3940
|
+
const opencodeInstalled = existsSync12(opencodeSkillPath);
|
|
3769
3941
|
console.log("Installed:");
|
|
3770
3942
|
const installed = [];
|
|
3771
3943
|
if (claudeInstalled) installed.push("claude");
|
|
3772
3944
|
if (codexInstalled) installed.push("codex");
|
|
3945
|
+
if (opencodeInstalled) installed.push("opencode");
|
|
3773
3946
|
if (installed.length > 0) {
|
|
3774
3947
|
for (const plugin of installed) {
|
|
3775
3948
|
console.log(` - ${plugin}`);
|
|
@@ -4058,23 +4231,82 @@ npx spets orchestrate revise <taskId> "<feedback>"
|
|
|
4058
4231
|
npx spets orchestrate reject <taskId>
|
|
4059
4232
|
npx spets orchestrate stop <taskId>
|
|
4060
4233
|
|
|
4234
|
+
$ARGUMENTS
|
|
4235
|
+
description: Task description for the workflow
|
|
4236
|
+
`;
|
|
4237
|
+
}
|
|
4238
|
+
function getOpenCodeSkillContent() {
|
|
4239
|
+
return `---
|
|
4240
|
+
name: spets
|
|
4241
|
+
description: SDD workflow executor - runs spec-driven development workflows within OpenCode
|
|
4242
|
+
---
|
|
4243
|
+
|
|
4244
|
+
# Spets Command Executor
|
|
4245
|
+
|
|
4246
|
+
Execute orchestrator commands. Parse JSON response and execute matching action.
|
|
4247
|
+
|
|
4248
|
+
## Start
|
|
4249
|
+
|
|
4250
|
+
Run: npx spets orchestrate init "$ARGUMENTS"
|
|
4251
|
+
|
|
4252
|
+
Parse JSON response, then execute action from table below, loop until type: "complete" or "error".
|
|
4253
|
+
|
|
4254
|
+
## Command Table
|
|
4255
|
+
|
|
4256
|
+
### type: "phase"
|
|
4257
|
+
|
|
4258
|
+
| phase | Action |
|
|
4259
|
+
|-------|--------|
|
|
4260
|
+
| explore | Follow response.prompt to explore codebase |
|
|
4261
|
+
| clarify | Follow response.prompt to generate questions |
|
|
4262
|
+
| execute | Follow response.prompt to write document |
|
|
4263
|
+
| verify | Follow response.prompt to validate document |
|
|
4264
|
+
|
|
4265
|
+
### type: "checkpoint"
|
|
4266
|
+
|
|
4267
|
+
| checkpoint | Action |
|
|
4268
|
+
|------------|--------|
|
|
4269
|
+
| clarify | Ask user questions, collect answers |
|
|
4270
|
+
| approve | Ask user to review document |
|
|
4271
|
+
|
|
4272
|
+
### type: "complete"
|
|
4273
|
+
|
|
4274
|
+
Print completion status and outputs.
|
|
4275
|
+
|
|
4276
|
+
### type: "error"
|
|
4277
|
+
|
|
4278
|
+
Print error and stop.
|
|
4279
|
+
|
|
4280
|
+
## Orchestrator Commands
|
|
4281
|
+
|
|
4282
|
+
npx spets orchestrate init "<description>"
|
|
4283
|
+
npx spets orchestrate explore-done <taskId> '<json>'
|
|
4284
|
+
npx spets orchestrate clarify-done <taskId> '<json>'
|
|
4285
|
+
npx spets orchestrate clarified <taskId> '<json>'
|
|
4286
|
+
npx spets orchestrate execute-done <taskId>
|
|
4287
|
+
npx spets orchestrate verify-done <taskId> '<json>'
|
|
4288
|
+
npx spets orchestrate approve <taskId>
|
|
4289
|
+
npx spets orchestrate revise <taskId> "<feedback>"
|
|
4290
|
+
npx spets orchestrate reject <taskId>
|
|
4291
|
+
npx spets orchestrate stop <taskId>
|
|
4292
|
+
|
|
4061
4293
|
$ARGUMENTS
|
|
4062
4294
|
description: Task description for the workflow
|
|
4063
4295
|
`;
|
|
4064
4296
|
}
|
|
4065
4297
|
|
|
4066
4298
|
// src/commands/github.ts
|
|
4067
|
-
import { execSync as execSync4, spawn as
|
|
4068
|
-
import { readdirSync as readdirSync4, readFileSync as
|
|
4299
|
+
import { execSync as execSync4, spawn as spawn6, spawnSync as spawnSync4 } from "child_process";
|
|
4300
|
+
import { readdirSync as readdirSync4, readFileSync as readFileSync12, existsSync as existsSync14 } from "fs";
|
|
4069
4301
|
import { join as join10 } from "path";
|
|
4070
4302
|
|
|
4071
4303
|
// src/hooks/runner.ts
|
|
4072
|
-
import { spawn as
|
|
4073
|
-
import { existsSync as
|
|
4304
|
+
import { spawn as spawn5 } from "child_process";
|
|
4305
|
+
import { existsSync as existsSync13 } from "fs";
|
|
4074
4306
|
import { join as join9, isAbsolute } from "path";
|
|
4075
4307
|
async function runHook(hookPath, context, cwd = process.cwd()) {
|
|
4076
4308
|
const resolvedPath = isAbsolute(hookPath) ? hookPath : join9(getSpetsDir(cwd), hookPath);
|
|
4077
|
-
if (!
|
|
4309
|
+
if (!existsSync13(resolvedPath)) {
|
|
4078
4310
|
console.warn(`Hook not found: ${resolvedPath}`);
|
|
4079
4311
|
return;
|
|
4080
4312
|
}
|
|
@@ -4088,7 +4320,7 @@ async function runHook(hookPath, context, cwd = process.cwd()) {
|
|
|
4088
4320
|
SPETS_CWD: cwd
|
|
4089
4321
|
};
|
|
4090
4322
|
return new Promise((resolve, reject) => {
|
|
4091
|
-
const proc =
|
|
4323
|
+
const proc = spawn5(resolvedPath, [], {
|
|
4092
4324
|
cwd,
|
|
4093
4325
|
env,
|
|
4094
4326
|
stdio: "inherit",
|
|
@@ -4185,7 +4417,7 @@ function findTaskId(cwd, owner, repo, issueOrPr) {
|
|
|
4185
4417
|
}
|
|
4186
4418
|
}
|
|
4187
4419
|
const outputsDir = getOutputsDir(cwd);
|
|
4188
|
-
if (
|
|
4420
|
+
if (existsSync14(outputsDir)) {
|
|
4189
4421
|
const taskDirs = readdirSync4(outputsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort().reverse();
|
|
4190
4422
|
if (taskDirs.length > 0) {
|
|
4191
4423
|
return taskDirs[0];
|
|
@@ -4418,7 +4650,7 @@ async function postComment(config, body) {
|
|
|
4418
4650
|
const { owner, repo, prNumber, issueNumber } = config;
|
|
4419
4651
|
const args = prNumber ? ["pr", "comment", String(prNumber), "--body", body, "-R", `${owner}/${repo}`] : ["issue", "comment", String(issueNumber), "--body", body, "-R", `${owner}/${repo}`];
|
|
4420
4652
|
return new Promise((resolve, reject) => {
|
|
4421
|
-
const proc =
|
|
4653
|
+
const proc = spawn6("gh", args, { stdio: "inherit" });
|
|
4422
4654
|
proc.on("close", (code) => {
|
|
4423
4655
|
if (code !== 0) reject(new Error(`gh failed with code ${code}`));
|
|
4424
4656
|
else resolve();
|
|
@@ -4430,10 +4662,10 @@ async function createPullRequestWithAI(config, taskId, userQuery, cwd) {
|
|
|
4430
4662
|
const { owner, repo, issueNumber } = config;
|
|
4431
4663
|
const outputsDir = join10(getOutputsDir(cwd), taskId);
|
|
4432
4664
|
const outputs = [];
|
|
4433
|
-
if (
|
|
4665
|
+
if (existsSync14(outputsDir)) {
|
|
4434
4666
|
const files = readdirSync4(outputsDir).filter((f) => f.endsWith(".md"));
|
|
4435
4667
|
for (const file of files) {
|
|
4436
|
-
const content =
|
|
4668
|
+
const content = readFileSync12(join10(outputsDir, file), "utf-8");
|
|
4437
4669
|
outputs.push({ step: file.replace(".md", ""), content });
|
|
4438
4670
|
}
|
|
4439
4671
|
}
|
|
@@ -4519,7 +4751,7 @@ Closes #${issueNumber}`;
|
|
|
4519
4751
|
}
|
|
4520
4752
|
async function callClaude(prompt) {
|
|
4521
4753
|
return new Promise((resolve, reject) => {
|
|
4522
|
-
const proc =
|
|
4754
|
+
const proc = spawn6("claude", ["--print"], { stdio: ["pipe", "pipe", "pipe"] });
|
|
4523
4755
|
let stdout = "";
|
|
4524
4756
|
let stderr = "";
|
|
4525
4757
|
proc.stdout.on("data", (data) => {
|
|
@@ -4799,8 +5031,8 @@ async function orchestrateCommand(action, args) {
|
|
|
4799
5031
|
}
|
|
4800
5032
|
|
|
4801
5033
|
// src/index.ts
|
|
4802
|
-
var __dirname2 =
|
|
4803
|
-
var pkg = JSON.parse(
|
|
5034
|
+
var __dirname2 = dirname7(fileURLToPath2(import.meta.url));
|
|
5035
|
+
var pkg = JSON.parse(readFileSync13(join11(__dirname2, "..", "package.json"), "utf-8"));
|
|
4804
5036
|
var program = new Command();
|
|
4805
5037
|
program.name("spets").description("Spec Driven Development Execution Framework").version(pkg.version);
|
|
4806
5038
|
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);
|