@saga-ai/cli 0.2.0 → 0.3.0
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 +18 -0
- package/dist/cli.cjs +106 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,6 +14,7 @@ Run commands using npx (no global installation required):
|
|
|
14
14
|
```bash
|
|
15
15
|
npx @saga-ai/cli init
|
|
16
16
|
npx @saga-ai/cli find <query>
|
|
17
|
+
npx @saga-ai/cli worktree <epic-slug> <story-slug>
|
|
17
18
|
npx @saga-ai/cli implement <story-slug>
|
|
18
19
|
npx @saga-ai/cli dashboard
|
|
19
20
|
```
|
|
@@ -56,6 +57,23 @@ Returns JSON with:
|
|
|
56
57
|
|
|
57
58
|
Supports typo tolerance (e.g., "implment" matches "implement").
|
|
58
59
|
|
|
60
|
+
### `saga worktree <epic-slug> <story-slug>`
|
|
61
|
+
|
|
62
|
+
Create a git worktree and branch for story isolation.
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
saga worktree my-epic my-story
|
|
66
|
+
saga worktree auth-system login-form --path /path/to/project
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Creates:
|
|
70
|
+
- Branch: `story-<story-slug>-epic-<epic-slug>`
|
|
71
|
+
- Worktree: `.saga/worktrees/<epic-slug>/<story-slug>/`
|
|
72
|
+
|
|
73
|
+
Returns JSON with:
|
|
74
|
+
- `success: true` + `worktreePath`, `branch` - Worktree created
|
|
75
|
+
- `success: false` + `error` - Creation failed (branch exists, directory exists, etc.)
|
|
76
|
+
|
|
59
77
|
### `saga implement <story-slug>`
|
|
60
78
|
|
|
61
79
|
Run autonomous story implementation using Claude workers.
|
package/dist/cli.cjs
CHANGED
|
@@ -3037,8 +3037,8 @@ var {
|
|
|
3037
3037
|
} = import_index.default;
|
|
3038
3038
|
|
|
3039
3039
|
// src/cli.ts
|
|
3040
|
-
var
|
|
3041
|
-
var
|
|
3040
|
+
var import_node_path6 = require("node:path");
|
|
3041
|
+
var import_node_fs6 = require("node:fs");
|
|
3042
3042
|
|
|
3043
3043
|
// src/commands/init.ts
|
|
3044
3044
|
var import_node_path2 = require("node:path");
|
|
@@ -5307,9 +5307,107 @@ async function findCommand(query, options) {
|
|
|
5307
5307
|
}
|
|
5308
5308
|
}
|
|
5309
5309
|
|
|
5310
|
+
// src/commands/worktree.ts
|
|
5311
|
+
var import_node_path5 = require("node:path");
|
|
5312
|
+
var import_node_fs5 = require("node:fs");
|
|
5313
|
+
var import_node_child_process2 = require("node:child_process");
|
|
5314
|
+
function runGitCommand(args, cwd) {
|
|
5315
|
+
try {
|
|
5316
|
+
const output = (0, import_node_child_process2.execSync)(`git ${args.join(" ")}`, {
|
|
5317
|
+
cwd,
|
|
5318
|
+
encoding: "utf-8",
|
|
5319
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
5320
|
+
});
|
|
5321
|
+
return { success: true, output: output.trim() };
|
|
5322
|
+
} catch (error) {
|
|
5323
|
+
const stderr = error.stderr?.toString().trim() || error.message;
|
|
5324
|
+
return { success: false, output: stderr };
|
|
5325
|
+
}
|
|
5326
|
+
}
|
|
5327
|
+
function branchExists(branchName, cwd) {
|
|
5328
|
+
const result = runGitCommand(["rev-parse", "--verify", branchName], cwd);
|
|
5329
|
+
return result.success;
|
|
5330
|
+
}
|
|
5331
|
+
function getMainBranch(cwd) {
|
|
5332
|
+
const result = runGitCommand(
|
|
5333
|
+
["symbolic-ref", "refs/remotes/origin/HEAD"],
|
|
5334
|
+
cwd
|
|
5335
|
+
);
|
|
5336
|
+
if (result.success) {
|
|
5337
|
+
return result.output.replace("refs/remotes/origin/", "");
|
|
5338
|
+
}
|
|
5339
|
+
return "main";
|
|
5340
|
+
}
|
|
5341
|
+
function createWorktree(projectPath, epicSlug, storySlug) {
|
|
5342
|
+
const branchName = `story-${storySlug}-epic-${epicSlug}`;
|
|
5343
|
+
const worktreePath = (0, import_node_path5.join)(
|
|
5344
|
+
projectPath,
|
|
5345
|
+
".saga",
|
|
5346
|
+
"worktrees",
|
|
5347
|
+
epicSlug,
|
|
5348
|
+
storySlug
|
|
5349
|
+
);
|
|
5350
|
+
if (branchExists(branchName, projectPath)) {
|
|
5351
|
+
return {
|
|
5352
|
+
success: false,
|
|
5353
|
+
error: `Branch already exists: ${branchName}`
|
|
5354
|
+
};
|
|
5355
|
+
}
|
|
5356
|
+
if ((0, import_node_fs5.existsSync)(worktreePath)) {
|
|
5357
|
+
return {
|
|
5358
|
+
success: false,
|
|
5359
|
+
error: `Worktree directory already exists: ${worktreePath}`
|
|
5360
|
+
};
|
|
5361
|
+
}
|
|
5362
|
+
const mainBranch = getMainBranch(projectPath);
|
|
5363
|
+
runGitCommand(["fetch", "origin", mainBranch], projectPath);
|
|
5364
|
+
const createBranchResult = runGitCommand(
|
|
5365
|
+
["branch", branchName, `origin/${mainBranch}`],
|
|
5366
|
+
projectPath
|
|
5367
|
+
);
|
|
5368
|
+
if (!createBranchResult.success) {
|
|
5369
|
+
return {
|
|
5370
|
+
success: false,
|
|
5371
|
+
error: `Failed to create branch: ${createBranchResult.output}`
|
|
5372
|
+
};
|
|
5373
|
+
}
|
|
5374
|
+
const worktreeParent = (0, import_node_path5.join)(projectPath, ".saga", "worktrees", epicSlug);
|
|
5375
|
+
(0, import_node_fs5.mkdirSync)(worktreeParent, { recursive: true });
|
|
5376
|
+
const createWorktreeResult = runGitCommand(
|
|
5377
|
+
["worktree", "add", worktreePath, branchName],
|
|
5378
|
+
projectPath
|
|
5379
|
+
);
|
|
5380
|
+
if (!createWorktreeResult.success) {
|
|
5381
|
+
return {
|
|
5382
|
+
success: false,
|
|
5383
|
+
error: `Failed to create worktree: ${createWorktreeResult.output}`
|
|
5384
|
+
};
|
|
5385
|
+
}
|
|
5386
|
+
return {
|
|
5387
|
+
success: true,
|
|
5388
|
+
worktreePath,
|
|
5389
|
+
branch: branchName
|
|
5390
|
+
};
|
|
5391
|
+
}
|
|
5392
|
+
async function worktreeCommand(epicSlug, storySlug, options) {
|
|
5393
|
+
let projectPath;
|
|
5394
|
+
try {
|
|
5395
|
+
projectPath = resolveProjectPath(options.path);
|
|
5396
|
+
} catch (error) {
|
|
5397
|
+
const result2 = { success: false, error: error.message };
|
|
5398
|
+
console.log(JSON.stringify(result2));
|
|
5399
|
+
process.exit(1);
|
|
5400
|
+
}
|
|
5401
|
+
const result = createWorktree(projectPath, epicSlug, storySlug);
|
|
5402
|
+
console.log(JSON.stringify(result));
|
|
5403
|
+
if (!result.success) {
|
|
5404
|
+
process.exit(1);
|
|
5405
|
+
}
|
|
5406
|
+
}
|
|
5407
|
+
|
|
5310
5408
|
// src/cli.ts
|
|
5311
|
-
var packageJsonPath = (0,
|
|
5312
|
-
var packageJson = JSON.parse((0,
|
|
5409
|
+
var packageJsonPath = (0, import_node_path6.join)(__dirname, "..", "package.json");
|
|
5410
|
+
var packageJson = JSON.parse((0, import_node_fs6.readFileSync)(packageJsonPath, "utf-8"));
|
|
5313
5411
|
var program2 = new Command();
|
|
5314
5412
|
program2.name("saga").description("CLI for SAGA - Structured Autonomous Goal Achievement").version(packageJson.version).addHelpCommand("help [command]", "Display help for a command");
|
|
5315
5413
|
program2.option("-p, --path <dir>", "Path to SAGA project directory (overrides auto-discovery)");
|
|
@@ -5334,6 +5432,10 @@ program2.command("find <query>").description("Find an epic or story by slug/titl
|
|
|
5334
5432
|
type: options.type
|
|
5335
5433
|
});
|
|
5336
5434
|
});
|
|
5435
|
+
program2.command("worktree <epic-slug> <story-slug>").description("Create git worktree for a story").action(async (epicSlug, storySlug) => {
|
|
5436
|
+
const globalOpts = program2.opts();
|
|
5437
|
+
await worktreeCommand(epicSlug, storySlug, { path: globalOpts.path });
|
|
5438
|
+
});
|
|
5337
5439
|
program2.command("dashboard").description("Start the dashboard server").option("--port <n>", "Port to run the server on (default: 3847)", parseInt).action(async (options) => {
|
|
5338
5440
|
const globalOpts = program2.opts();
|
|
5339
5441
|
await dashboardCommand({
|