@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.
Files changed (3) hide show
  1. package/README.md +18 -0
  2. package/dist/cli.cjs +106 -4
  3. 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 import_node_path5 = require("node:path");
3041
- var import_node_fs5 = require("node:fs");
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, import_node_path5.join)(__dirname, "..", "package.json");
5312
- var packageJson = JSON.parse((0, import_node_fs5.readFileSync)(packageJsonPath, "utf-8"));
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({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saga-ai/cli",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "CLI for SAGA - Structured Autonomous Goal Achievement",
5
5
  "type": "module",
6
6
  "bin": {