@saga-ai/cli 0.3.0 → 0.4.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 (2) hide show
  1. package/dist/cli.cjs +39 -13
  2. package/package.json +1 -1
package/dist/cli.cjs CHANGED
@@ -3037,7 +3037,7 @@ var {
3037
3037
  } = import_index.default;
3038
3038
 
3039
3039
  // src/cli.ts
3040
- var import_node_path6 = require("node:path");
3040
+ var import_node_path7 = require("node:path");
3041
3041
  var import_node_fs6 = require("node:fs");
3042
3042
 
3043
3043
  // src/commands/init.ts
@@ -4957,7 +4957,7 @@ function buildScopeSettings() {
4957
4957
  hooks: {
4958
4958
  PreToolUse: [
4959
4959
  {
4960
- matcher: "Read|Write|Edit",
4960
+ matcher: "Read|Write|Edit|Glob|Grep",
4961
4961
  hooks: [hookCommand]
4962
4962
  }
4963
4963
  ]
@@ -5196,10 +5196,12 @@ async function dashboardCommand(options) {
5196
5196
  }
5197
5197
 
5198
5198
  // src/commands/scope-validator.ts
5199
- function getFilePathFromInput(toolInput) {
5199
+ var import_node_path5 = require("node:path");
5200
+ function getFilePathFromInput(hookInput) {
5200
5201
  try {
5201
- const data = JSON.parse(toolInput);
5202
- return data.file_path || data.path || null;
5202
+ const data = JSON.parse(hookInput);
5203
+ const toolInput = data.tool_input || {};
5204
+ return toolInput.file_path || toolInput.path || null;
5203
5205
  } catch {
5204
5206
  return null;
5205
5207
  }
@@ -5213,6 +5215,15 @@ function normalizePath(path) {
5213
5215
  function isArchiveAccess(path) {
5214
5216
  return path.includes(".saga/archive");
5215
5217
  }
5218
+ function isWithinWorktree(filePath, worktreePath) {
5219
+ const absoluteFilePath = (0, import_node_path5.resolve)(filePath);
5220
+ const absoluteWorktree = (0, import_node_path5.resolve)(worktreePath);
5221
+ const relativePath = (0, import_node_path5.relative)(absoluteWorktree, absoluteFilePath);
5222
+ if (relativePath.startsWith("..") || (0, import_node_path5.resolve)(relativePath) === relativePath) {
5223
+ return false;
5224
+ }
5225
+ return true;
5226
+ }
5216
5227
  function checkStoryAccess(path, allowedEpic, allowedStory) {
5217
5228
  if (!path.includes(".saga/epics/")) {
5218
5229
  return true;
@@ -5233,24 +5244,27 @@ function checkStoryAccess(path, allowedEpic, allowedStory) {
5233
5244
  return pathEpic === allowedEpic;
5234
5245
  }
5235
5246
  }
5236
- function printScopeViolation(filePath, epicSlug, storySlug, reason) {
5247
+ function printScopeViolation(filePath, epicSlug, storySlug, worktreePath, reason) {
5237
5248
  console.error(`SCOPE VIOLATION: ${reason}
5238
5249
 
5239
5250
  Attempted path: ${filePath}
5240
5251
 
5241
5252
  Your scope is limited to:
5253
+ Worktree: ${worktreePath}
5242
5254
  Epic: ${epicSlug}
5243
5255
  Story: ${storySlug}
5244
- Allowed: .saga/epics/${epicSlug}/stories/${storySlug}/
5256
+ Allowed story files: .saga/epics/${epicSlug}/stories/${storySlug}/
5245
5257
 
5258
+ Workers cannot access files outside the worktree directory.
5246
5259
  To access other stories, start a new /implement session for that story.`);
5247
5260
  }
5248
5261
  async function scopeValidatorCommand() {
5262
+ const worktreePath = process.env.SAGA_PROJECT_DIR || "";
5249
5263
  const epicSlug = process.env.SAGA_EPIC_SLUG || "";
5250
5264
  const storySlug = process.env.SAGA_STORY_SLUG || "";
5251
- if (!epicSlug || !storySlug) {
5265
+ if (!worktreePath || !epicSlug || !storySlug) {
5252
5266
  console.error(
5253
- "ERROR: scope-validator requires SAGA_EPIC_SLUG and SAGA_STORY_SLUG environment variables"
5267
+ "ERROR: scope-validator requires SAGA_PROJECT_DIR, SAGA_EPIC_SLUG, and SAGA_STORY_SLUG environment variables"
5254
5268
  );
5255
5269
  process.exit(2);
5256
5270
  }
@@ -5264,11 +5278,22 @@ async function scopeValidatorCommand() {
5264
5278
  process.exit(0);
5265
5279
  }
5266
5280
  const normPath = normalizePath(filePath);
5281
+ if (!isWithinWorktree(normPath, worktreePath)) {
5282
+ printScopeViolation(
5283
+ filePath,
5284
+ epicSlug,
5285
+ storySlug,
5286
+ worktreePath,
5287
+ "Access outside worktree blocked\nReason: Workers can only access files within their assigned worktree directory."
5288
+ );
5289
+ process.exit(2);
5290
+ }
5267
5291
  if (isArchiveAccess(normPath)) {
5268
5292
  printScopeViolation(
5269
5293
  filePath,
5270
5294
  epicSlug,
5271
5295
  storySlug,
5296
+ worktreePath,
5272
5297
  "Access to archive folder blocked\nReason: The archive folder contains completed stories and is read-only during execution."
5273
5298
  );
5274
5299
  process.exit(2);
@@ -5278,6 +5303,7 @@ async function scopeValidatorCommand() {
5278
5303
  filePath,
5279
5304
  epicSlug,
5280
5305
  storySlug,
5306
+ worktreePath,
5281
5307
  "Access to other story blocked\nReason: Workers can only access their assigned story's files."
5282
5308
  );
5283
5309
  process.exit(2);
@@ -5308,7 +5334,7 @@ async function findCommand(query, options) {
5308
5334
  }
5309
5335
 
5310
5336
  // src/commands/worktree.ts
5311
- var import_node_path5 = require("node:path");
5337
+ var import_node_path6 = require("node:path");
5312
5338
  var import_node_fs5 = require("node:fs");
5313
5339
  var import_node_child_process2 = require("node:child_process");
5314
5340
  function runGitCommand(args, cwd) {
@@ -5340,7 +5366,7 @@ function getMainBranch(cwd) {
5340
5366
  }
5341
5367
  function createWorktree(projectPath, epicSlug, storySlug) {
5342
5368
  const branchName = `story-${storySlug}-epic-${epicSlug}`;
5343
- const worktreePath = (0, import_node_path5.join)(
5369
+ const worktreePath = (0, import_node_path6.join)(
5344
5370
  projectPath,
5345
5371
  ".saga",
5346
5372
  "worktrees",
@@ -5371,7 +5397,7 @@ function createWorktree(projectPath, epicSlug, storySlug) {
5371
5397
  error: `Failed to create branch: ${createBranchResult.output}`
5372
5398
  };
5373
5399
  }
5374
- const worktreeParent = (0, import_node_path5.join)(projectPath, ".saga", "worktrees", epicSlug);
5400
+ const worktreeParent = (0, import_node_path6.join)(projectPath, ".saga", "worktrees", epicSlug);
5375
5401
  (0, import_node_fs5.mkdirSync)(worktreeParent, { recursive: true });
5376
5402
  const createWorktreeResult = runGitCommand(
5377
5403
  ["worktree", "add", worktreePath, branchName],
@@ -5406,7 +5432,7 @@ async function worktreeCommand(epicSlug, storySlug, options) {
5406
5432
  }
5407
5433
 
5408
5434
  // src/cli.ts
5409
- var packageJsonPath = (0, import_node_path6.join)(__dirname, "..", "package.json");
5435
+ var packageJsonPath = (0, import_node_path7.join)(__dirname, "..", "package.json");
5410
5436
  var packageJson = JSON.parse((0, import_node_fs6.readFileSync)(packageJsonPath, "utf-8"));
5411
5437
  var program2 = new Command();
5412
5438
  program2.name("saga").description("CLI for SAGA - Structured Autonomous Goal Achievement").version(packageJson.version).addHelpCommand("help [command]", "Display help for a command");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saga-ai/cli",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "CLI for SAGA - Structured Autonomous Goal Achievement",
5
5
  "type": "module",
6
6
  "bin": {