@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.
- package/dist/cli.cjs +39 -13
- 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
|
|
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
|
-
|
|
5199
|
+
var import_node_path5 = require("node:path");
|
|
5200
|
+
function getFilePathFromInput(hookInput) {
|
|
5200
5201
|
try {
|
|
5201
|
-
const data = JSON.parse(
|
|
5202
|
-
|
|
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
|
|
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,
|
|
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,
|
|
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,
|
|
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");
|