agentplane 0.1.6 → 0.1.8
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/assets/AGENTS.md +1 -1
- package/assets/agents/ORCHESTRATOR.json +1 -1
- package/assets/agents/UPGRADER.json +1 -1
- package/dist/backends/task-backend.d.ts +16 -0
- package/dist/backends/task-backend.d.ts.map +1 -1
- package/dist/backends/task-backend.js +44 -0
- package/dist/backends/task-index.d.ts.map +1 -1
- package/dist/backends/task-index.js +3 -6
- package/dist/cli/command-guide.d.ts.map +1 -1
- package/dist/cli/command-guide.js +4 -4
- package/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/help.js +7 -5
- package/dist/cli/run-cli.d.ts.map +1 -1
- package/dist/cli/run-cli.js +39 -78
- package/dist/commands/backend.d.ts.map +1 -1
- package/dist/commands/backend.js +17 -2
- package/dist/commands/branch/index.d.ts +60 -0
- package/dist/commands/branch/index.d.ts.map +1 -0
- package/dist/commands/branch/index.js +513 -0
- package/dist/commands/guard/index.d.ts +67 -0
- package/dist/commands/guard/index.d.ts.map +1 -0
- package/dist/commands/guard/index.js +367 -0
- package/dist/commands/hooks/index.d.ts +18 -0
- package/dist/commands/hooks/index.d.ts.map +1 -0
- package/dist/commands/hooks/index.js +290 -0
- package/dist/commands/pr/index.d.ts +46 -0
- package/dist/commands/pr/index.d.ts.map +1 -0
- package/dist/commands/pr/index.js +857 -0
- package/dist/commands/recipes.d.ts.map +1 -1
- package/dist/commands/recipes.js +67 -23
- package/dist/commands/shared/git-diff.d.ts +9 -0
- package/dist/commands/shared/git-diff.d.ts.map +1 -0
- package/dist/commands/shared/git-diff.js +41 -0
- package/dist/commands/shared/git-ops.d.ts +24 -0
- package/dist/commands/shared/git-ops.d.ts.map +1 -0
- package/dist/commands/shared/git-ops.js +181 -0
- package/dist/commands/shared/git-worktree.d.ts +8 -0
- package/dist/commands/shared/git-worktree.d.ts.map +1 -0
- package/dist/commands/shared/git-worktree.js +48 -0
- package/dist/commands/shared/git.d.ts +4 -0
- package/dist/commands/shared/git.d.ts.map +1 -0
- package/dist/commands/shared/git.js +14 -0
- package/dist/commands/shared/network-approval.d.ts +8 -0
- package/dist/commands/shared/network-approval.d.ts.map +1 -0
- package/dist/commands/shared/network-approval.js +25 -0
- package/dist/commands/shared/path.d.ts +3 -0
- package/dist/commands/shared/path.d.ts.map +1 -0
- package/dist/commands/shared/path.js +14 -0
- package/dist/commands/shared/pr-meta.d.ts +21 -0
- package/dist/commands/shared/pr-meta.d.ts.map +1 -0
- package/dist/commands/shared/pr-meta.js +72 -0
- package/dist/commands/shared/task-backend.d.ts +15 -0
- package/dist/commands/shared/task-backend.d.ts.map +1 -0
- package/dist/commands/shared/task-backend.js +61 -0
- package/dist/commands/task/add.d.ts +8 -0
- package/dist/commands/task/add.d.ts.map +1 -0
- package/dist/commands/task/add.js +164 -0
- package/dist/commands/task/block.d.ts +19 -0
- package/dist/commands/task/block.d.ts.map +1 -0
- package/dist/commands/task/block.js +86 -0
- package/dist/commands/task/comment.d.ts +8 -0
- package/dist/commands/task/comment.d.ts.map +1 -0
- package/dist/commands/task/comment.js +29 -0
- package/dist/commands/task/doc.d.ts +17 -0
- package/dist/commands/task/doc.d.ts.map +1 -0
- package/dist/commands/task/doc.js +220 -0
- package/dist/commands/task/export.d.ts +5 -0
- package/dist/commands/task/export.d.ts.map +1 -0
- package/dist/commands/task/export.js +27 -0
- package/dist/commands/task/finish.d.ts +27 -0
- package/dist/commands/task/finish.d.ts.map +1 -0
- package/dist/commands/task/finish.js +132 -0
- package/dist/commands/task/index.d.ts +26 -0
- package/dist/commands/task/index.d.ts.map +1 -0
- package/dist/commands/task/index.js +25 -0
- package/dist/commands/task/lint.d.ts +5 -0
- package/dist/commands/task/lint.d.ts.map +1 -0
- package/dist/commands/task/lint.js +22 -0
- package/dist/commands/task/list.d.ts +11 -0
- package/dist/commands/task/list.d.ts.map +1 -0
- package/dist/commands/task/list.js +54 -0
- package/dist/commands/task/migrate-doc.d.ts +8 -0
- package/dist/commands/task/migrate-doc.d.ts.map +1 -0
- package/dist/commands/task/migrate-doc.js +147 -0
- package/dist/commands/task/migrate.d.ts +6 -0
- package/dist/commands/task/migrate.d.ts.map +1 -0
- package/dist/commands/task/migrate.js +70 -0
- package/dist/commands/task/new.d.ts +8 -0
- package/dist/commands/task/new.d.ts.map +1 -0
- package/dist/commands/task/new.js +117 -0
- package/dist/commands/task/next.d.ts +6 -0
- package/dist/commands/task/next.d.ts.map +1 -0
- package/dist/commands/task/next.js +45 -0
- package/dist/commands/task/normalize.d.ts +6 -0
- package/dist/commands/task/normalize.d.ts.map +1 -0
- package/dist/commands/task/normalize.js +46 -0
- package/dist/commands/task/plan.d.ts +14 -0
- package/dist/commands/task/plan.d.ts.map +1 -0
- package/dist/commands/task/plan.js +217 -0
- package/dist/commands/task/ready.d.ts +6 -0
- package/dist/commands/task/ready.d.ts.map +1 -0
- package/dist/commands/task/ready.js +57 -0
- package/dist/commands/task/scaffold.d.ts +8 -0
- package/dist/commands/task/scaffold.d.ts.map +1 -0
- package/dist/commands/task/scaffold.js +142 -0
- package/dist/commands/task/scrub.d.ts +8 -0
- package/dist/commands/task/scrub.d.ts.map +1 -0
- package/dist/commands/task/scrub.js +121 -0
- package/dist/commands/task/search.d.ts +7 -0
- package/dist/commands/task/search.d.ts.map +1 -0
- package/dist/commands/task/search.js +79 -0
- package/dist/commands/task/set-status.d.ts +19 -0
- package/dist/commands/task/set-status.d.ts.map +1 -0
- package/dist/commands/task/set-status.js +123 -0
- package/dist/commands/task/shared.d.ts +48 -0
- package/dist/commands/task/shared.d.ts.map +1 -0
- package/dist/commands/task/shared.js +312 -0
- package/dist/commands/task/show.d.ts +6 -0
- package/dist/commands/task/show.d.ts.map +1 -0
- package/dist/commands/task/show.js +35 -0
- package/dist/commands/task/start.d.ts +19 -0
- package/dist/commands/task/start.d.ts.map +1 -0
- package/dist/commands/task/start.js +110 -0
- package/dist/commands/task/update.d.ts +8 -0
- package/dist/commands/task/update.d.ts.map +1 -0
- package/dist/commands/task/update.js +144 -0
- package/dist/commands/task/verify-record.d.ts +16 -0
- package/dist/commands/task/verify-record.d.ts.map +1 -0
- package/dist/commands/task/verify-record.js +277 -0
- package/dist/commands/task/verify.d.ts +2 -0
- package/dist/commands/task/verify.d.ts.map +1 -0
- package/dist/commands/task/verify.js +1 -0
- package/dist/commands/upgrade.d.ts.map +1 -1
- package/dist/commands/upgrade.js +17 -2
- package/dist/commands/workflow.d.ts +5 -364
- package/dist/commands/workflow.d.ts.map +1 -1
- package/dist/commands/workflow.js +6 -4617
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recipes.d.ts","sourceRoot":"","sources":["../../src/commands/recipes.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"recipes.d.ts","sourceRoot":"","sources":["../../src/commands/recipes.ts"],"names":[],"mappings":"AAinEA,wBAAsB,UAAU,CAAC,IAAI,EAAE;IACrC,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB,GAAG,OAAO,CAAC,MAAM,CAAC,CAyGlB;AAED,wBAAsB,WAAW,CAAC,IAAI,EAAE;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB,GAAG,OAAO,CAAC,MAAM,CAAC,CAoDlB"}
|
package/dist/commands/recipes.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { execFile } from "node:child_process";
|
|
2
2
|
import { createPublicKey, verify } from "node:crypto";
|
|
3
|
-
import { cp, mkdir, mkdtemp, readdir, readFile, realpath, rename, rm
|
|
3
|
+
import { cp, mkdir, mkdtemp, readdir, readFile, realpath, rename, rm } from "node:fs/promises";
|
|
4
4
|
import os from "node:os";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { promisify } from "node:util";
|
|
7
|
-
import { resolveProject } from "@agentplaneorg/core";
|
|
7
|
+
import { atomicWriteFile, defaultConfig, loadConfig, resolveProject } from "@agentplaneorg/core";
|
|
8
8
|
import { extractArchive } from "../cli/archive.js";
|
|
9
9
|
import { sha256File } from "../cli/checksum.js";
|
|
10
10
|
import { mapCoreError } from "../cli/error-map.js";
|
|
@@ -12,6 +12,7 @@ import { fileExists, getPathKind } from "../cli/fs-utils.js";
|
|
|
12
12
|
import { downloadToFile, fetchJson, fetchText } from "../cli/http.js";
|
|
13
13
|
import { emptyStateMessage, infoMessage, invalidFieldMessage, invalidPathMessage, missingValueMessage, missingFileMessage, requiredFieldMessage, successMessage, usageMessage, } from "../cli/output.js";
|
|
14
14
|
import { CliError } from "../shared/errors.js";
|
|
15
|
+
import { ensureNetworkApproved } from "./shared/network-approval.js";
|
|
15
16
|
const execFileAsync = promisify(execFile);
|
|
16
17
|
const INSTALLED_RECIPES_NAME = "recipes.json";
|
|
17
18
|
const RECIPES_DIR_NAME = "recipes";
|
|
@@ -25,7 +26,7 @@ const RECIPE_INFO_USAGE = "Usage: agentplane recipes info <id>";
|
|
|
25
26
|
const RECIPE_INFO_USAGE_EXAMPLE = "agentplane recipes info viewer";
|
|
26
27
|
const RECIPE_EXPLAIN_USAGE = "Usage: agentplane recipes explain <id>";
|
|
27
28
|
const RECIPE_EXPLAIN_USAGE_EXAMPLE = "agentplane recipes explain viewer";
|
|
28
|
-
const RECIPE_INSTALL_USAGE = "Usage: agentplane recipes install --name <id> [--index <path|url>] [--refresh] | --path <path> | --url <url>";
|
|
29
|
+
const RECIPE_INSTALL_USAGE = "Usage: agentplane recipes install --name <id> [--index <path|url>] [--refresh] [--yes] | --path <path> | --url <url> [--yes]";
|
|
29
30
|
const RECIPE_INSTALL_USAGE_EXAMPLE = "agentplane recipes install --name viewer";
|
|
30
31
|
const RECIPE_REMOVE_USAGE = "Usage: agentplane recipes remove <id>";
|
|
31
32
|
const RECIPE_REMOVE_USAGE_EXAMPLE = "agentplane recipes remove viewer";
|
|
@@ -33,7 +34,7 @@ const RECIPE_CACHE_USAGE = "Usage: agentplane recipes cache <prune> [args]";
|
|
|
33
34
|
const RECIPE_CACHE_USAGE_EXAMPLE = "agentplane recipes cache prune --dry-run";
|
|
34
35
|
const RECIPE_CACHE_PRUNE_USAGE = "Usage: agentplane recipes cache prune [--dry-run] [--all]";
|
|
35
36
|
const RECIPE_CACHE_PRUNE_USAGE_EXAMPLE = "agentplane recipes cache prune --dry-run";
|
|
36
|
-
const RECIPE_LIST_REMOTE_USAGE = "Usage: agentplane recipes list-remote [--refresh] [--index <path|url>]";
|
|
37
|
+
const RECIPE_LIST_REMOTE_USAGE = "Usage: agentplane recipes list-remote [--refresh] [--index <path|url>] [--yes]";
|
|
37
38
|
const RECIPE_LIST_REMOTE_USAGE_EXAMPLE = "agentplane recipes list-remote --refresh";
|
|
38
39
|
const DEFAULT_RECIPES_INDEX_URL = "https://raw.githubusercontent.com/basilisk-labs/agentplane-recipes/main/index.json";
|
|
39
40
|
const RECIPES_INDEX_PUBLIC_KEYS_ENV = "AGENTPLANE_RECIPES_INDEX_PUBLIC_KEYS";
|
|
@@ -166,7 +167,7 @@ async function writeScenarioReport(opts) {
|
|
|
166
167
|
steps: opts.steps,
|
|
167
168
|
git: opts.gitSummary,
|
|
168
169
|
};
|
|
169
|
-
await
|
|
170
|
+
await atomicWriteFile(path.join(opts.runDir, SCENARIO_REPORT_NAME), `${JSON.stringify(report, null, 2)}\n`, "utf8");
|
|
170
171
|
}
|
|
171
172
|
async function maybeResolveProject(opts) {
|
|
172
173
|
try {
|
|
@@ -185,7 +186,7 @@ async function maybeResolveProject(opts) {
|
|
|
185
186
|
}
|
|
186
187
|
}
|
|
187
188
|
function parseRecipeListRemoteFlags(args) {
|
|
188
|
-
const out = { refresh: false };
|
|
189
|
+
const out = { refresh: false, yes: false };
|
|
189
190
|
for (let i = 0; i < args.length; i++) {
|
|
190
191
|
const arg = args[i];
|
|
191
192
|
if (!arg)
|
|
@@ -201,6 +202,10 @@ function parseRecipeListRemoteFlags(args) {
|
|
|
201
202
|
out.refresh = true;
|
|
202
203
|
continue;
|
|
203
204
|
}
|
|
205
|
+
if (arg === "--yes") {
|
|
206
|
+
out.yes = true;
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
204
209
|
const next = args[i + 1];
|
|
205
210
|
if (!next) {
|
|
206
211
|
throw new CliError({ exitCode: 2, code: "E_USAGE", message: missingValueMessage(arg) });
|
|
@@ -575,7 +580,7 @@ async function writeInstalledRecipesFile(filePath, file) {
|
|
|
575
580
|
updated_at: new Date().toISOString(),
|
|
576
581
|
});
|
|
577
582
|
await mkdir(path.dirname(filePath), { recursive: true });
|
|
578
|
-
await
|
|
583
|
+
await atomicWriteFile(filePath, `${JSON.stringify(sorted, null, 2)}\n`, "utf8");
|
|
579
584
|
}
|
|
580
585
|
async function readRecipeManifest(manifestPath) {
|
|
581
586
|
const raw = JSON.parse(await readFile(manifestPath, "utf8"));
|
|
@@ -624,6 +629,7 @@ function parseRecipeInstallArgs(args) {
|
|
|
624
629
|
let url = null;
|
|
625
630
|
let index;
|
|
626
631
|
let refresh = false;
|
|
632
|
+
let yes = false;
|
|
627
633
|
const positional = [];
|
|
628
634
|
for (let i = 0; i < args.length; i++) {
|
|
629
635
|
const arg = args[i];
|
|
@@ -681,6 +687,10 @@ function parseRecipeInstallArgs(args) {
|
|
|
681
687
|
refresh = true;
|
|
682
688
|
continue;
|
|
683
689
|
}
|
|
690
|
+
if (arg === "--yes") {
|
|
691
|
+
yes = true;
|
|
692
|
+
continue;
|
|
693
|
+
}
|
|
684
694
|
if (arg === "--on-conflict") {
|
|
685
695
|
const next = args[i + 1];
|
|
686
696
|
if (!next)
|
|
@@ -732,13 +742,13 @@ function parseRecipeInstallArgs(args) {
|
|
|
732
742
|
});
|
|
733
743
|
}
|
|
734
744
|
if (name)
|
|
735
|
-
return { source: { type: "name", value: name }, index, refresh, onConflict };
|
|
745
|
+
return { source: { type: "name", value: name }, index, refresh, onConflict, yes };
|
|
736
746
|
if (localPath)
|
|
737
|
-
return { source: { type: "path", value: localPath }, index, refresh, onConflict };
|
|
747
|
+
return { source: { type: "path", value: localPath }, index, refresh, onConflict, yes };
|
|
738
748
|
if (url)
|
|
739
|
-
return { source: { type: "url", value: url }, index, refresh, onConflict };
|
|
749
|
+
return { source: { type: "url", value: url }, index, refresh, onConflict, yes };
|
|
740
750
|
if (positional.length === 1) {
|
|
741
|
-
return { source: { type: "auto", value: positional[0] }, index, refresh, onConflict };
|
|
751
|
+
return { source: { type: "auto", value: positional[0] }, index, refresh, onConflict, yes };
|
|
742
752
|
}
|
|
743
753
|
throw new CliError({
|
|
744
754
|
exitCode: 2,
|
|
@@ -853,7 +863,7 @@ async function applyRecipeAgents(opts) {
|
|
|
853
863
|
}
|
|
854
864
|
}
|
|
855
865
|
rawAgent.id = targetId;
|
|
856
|
-
await
|
|
866
|
+
await atomicWriteFile(targetPath, `${JSON.stringify(rawAgent, null, 2)}\n`, "utf8");
|
|
857
867
|
}
|
|
858
868
|
}
|
|
859
869
|
async function applyRecipeScenarios(opts) {
|
|
@@ -881,7 +891,7 @@ async function applyRecipeScenarios(opts) {
|
|
|
881
891
|
}
|
|
882
892
|
if (payload.scenarios.length === 0)
|
|
883
893
|
return;
|
|
884
|
-
await
|
|
894
|
+
await atomicWriteFile(scenariosIndexPath, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
|
|
885
895
|
}
|
|
886
896
|
function isHttpUrl(value) {
|
|
887
897
|
return value.startsWith("http://") || value.startsWith("https://");
|
|
@@ -913,8 +923,8 @@ async function loadRecipesRemoteIndex(opts) {
|
|
|
913
923
|
verifyRecipesIndexSignature(indexText, signature);
|
|
914
924
|
rawIndex = JSON.parse(indexText);
|
|
915
925
|
await mkdir(cacheDir, { recursive: true });
|
|
916
|
-
await
|
|
917
|
-
await
|
|
926
|
+
await atomicWriteFile(cachePath, indexText, "utf8");
|
|
927
|
+
await atomicWriteFile(cacheSigPath, `${JSON.stringify(signature, null, 2)}\n`, "utf8");
|
|
918
928
|
}
|
|
919
929
|
else {
|
|
920
930
|
const [indexText, sigText] = await Promise.all([
|
|
@@ -968,6 +978,22 @@ async function cmdRecipeList(opts) {
|
|
|
968
978
|
async function cmdRecipeListRemote(opts) {
|
|
969
979
|
const flags = parseRecipeListRemoteFlags(opts.args);
|
|
970
980
|
try {
|
|
981
|
+
const project = await maybeResolveProject({ cwd: opts.cwd, rootOverride: opts.rootOverride });
|
|
982
|
+
let config = defaultConfig();
|
|
983
|
+
if (project) {
|
|
984
|
+
const loaded = await loadConfig(project.agentplaneDir);
|
|
985
|
+
config = loaded.config;
|
|
986
|
+
}
|
|
987
|
+
const source = flags.index ?? DEFAULT_RECIPES_INDEX_URL;
|
|
988
|
+
const cachePath = resolveRecipesIndexCachePath();
|
|
989
|
+
const willFetchRemote = (flags.refresh || !(await fileExists(cachePath))) && isHttpUrl(source);
|
|
990
|
+
if (willFetchRemote) {
|
|
991
|
+
await ensureNetworkApproved({
|
|
992
|
+
config,
|
|
993
|
+
yes: flags.yes,
|
|
994
|
+
reason: "recipes list-remote fetches the remote recipes index",
|
|
995
|
+
});
|
|
996
|
+
}
|
|
971
997
|
const index = await loadRecipesRemoteIndex({
|
|
972
998
|
cwd: opts.cwd,
|
|
973
999
|
source: flags.index,
|
|
@@ -1256,8 +1282,8 @@ async function cmdScenarioRun(opts) {
|
|
|
1256
1282
|
env,
|
|
1257
1283
|
});
|
|
1258
1284
|
const durationMs = Date.now() - startedAt;
|
|
1259
|
-
await
|
|
1260
|
-
await
|
|
1285
|
+
await atomicWriteFile(path.join(stepDir, "stdout.log"), result.stdout, "utf8");
|
|
1286
|
+
await atomicWriteFile(path.join(stepDir, "stderr.log"), result.stderr, "utf8");
|
|
1261
1287
|
stepsMeta.push({
|
|
1262
1288
|
tool: step.tool,
|
|
1263
1289
|
runtime,
|
|
@@ -1287,7 +1313,7 @@ async function cmdScenarioRun(opts) {
|
|
|
1287
1313
|
steps: stepsReport,
|
|
1288
1314
|
gitSummary,
|
|
1289
1315
|
});
|
|
1290
|
-
await
|
|
1316
|
+
await atomicWriteFile(path.join(runDir, "meta.json"), `${JSON.stringify({
|
|
1291
1317
|
recipe: recipeId,
|
|
1292
1318
|
scenario: scenarioId,
|
|
1293
1319
|
run_id: runId,
|
|
@@ -1311,7 +1337,7 @@ async function cmdScenarioRun(opts) {
|
|
|
1311
1337
|
steps: stepsReport,
|
|
1312
1338
|
gitSummary,
|
|
1313
1339
|
});
|
|
1314
|
-
await
|
|
1340
|
+
await atomicWriteFile(path.join(runDir, "meta.json"), `${JSON.stringify({
|
|
1315
1341
|
recipe: recipeId,
|
|
1316
1342
|
scenario: scenarioId,
|
|
1317
1343
|
run_id: runId,
|
|
@@ -1464,6 +1490,19 @@ async function cmdRecipeExplain(opts) {
|
|
|
1464
1490
|
}
|
|
1465
1491
|
async function cmdRecipeInstall(opts) {
|
|
1466
1492
|
try {
|
|
1493
|
+
const project = await maybeResolveProject({ cwd: opts.cwd, rootOverride: opts.rootOverride });
|
|
1494
|
+
let config = defaultConfig();
|
|
1495
|
+
if (project) {
|
|
1496
|
+
const loaded = await loadConfig(project.agentplaneDir);
|
|
1497
|
+
config = loaded.config;
|
|
1498
|
+
}
|
|
1499
|
+
let networkApproved = false;
|
|
1500
|
+
const ensureApproved = async (reason) => {
|
|
1501
|
+
if (networkApproved)
|
|
1502
|
+
return;
|
|
1503
|
+
await ensureNetworkApproved({ config, yes: opts.yes, reason });
|
|
1504
|
+
networkApproved = true;
|
|
1505
|
+
};
|
|
1467
1506
|
const tempRoot = await mkdtemp(path.join(os.tmpdir(), "agentplane-recipe-"));
|
|
1468
1507
|
try {
|
|
1469
1508
|
let sourcePath = "";
|
|
@@ -1471,6 +1510,12 @@ async function cmdRecipeInstall(opts) {
|
|
|
1471
1510
|
let expectedSha = "";
|
|
1472
1511
|
let indexTags = [];
|
|
1473
1512
|
const resolveFromIndex = async (recipeId) => {
|
|
1513
|
+
const indexSource = opts.index ?? DEFAULT_RECIPES_INDEX_URL;
|
|
1514
|
+
const cachePath = resolveRecipesIndexCachePath();
|
|
1515
|
+
const willFetchRemote = (opts.refresh || !(await fileExists(cachePath))) && isHttpUrl(indexSource);
|
|
1516
|
+
if (willFetchRemote) {
|
|
1517
|
+
await ensureApproved("recipes install fetches the remote recipes index");
|
|
1518
|
+
}
|
|
1474
1519
|
const index = await loadRecipesRemoteIndex({
|
|
1475
1520
|
cwd: opts.cwd,
|
|
1476
1521
|
source: opts.index,
|
|
@@ -1498,6 +1543,7 @@ async function cmdRecipeInstall(opts) {
|
|
|
1498
1543
|
sourceLabel = `${entry.id}@${latest.version}`;
|
|
1499
1544
|
indexTags = normalizeRecipeTags(latest.tags ?? []);
|
|
1500
1545
|
if (isHttpUrl(latest.url)) {
|
|
1546
|
+
await ensureApproved("recipes install downloads a recipe archive");
|
|
1501
1547
|
const url = new URL(latest.url);
|
|
1502
1548
|
const filename = path.basename(url.pathname) || "recipe.tar.gz";
|
|
1503
1549
|
const target = path.join(tempRoot, filename);
|
|
@@ -1518,6 +1564,7 @@ async function cmdRecipeInstall(opts) {
|
|
|
1518
1564
|
if (source.type === "name")
|
|
1519
1565
|
return await resolveFromIndex(source.value);
|
|
1520
1566
|
if (source.type === "url") {
|
|
1567
|
+
await ensureApproved("recipes install downloads a recipe archive");
|
|
1521
1568
|
const url = new URL(source.value);
|
|
1522
1569
|
const filename = path.basename(url.pathname) || "recipe.tar.gz";
|
|
1523
1570
|
const target = path.join(tempRoot, filename);
|
|
@@ -1595,10 +1642,6 @@ async function cmdRecipeInstall(opts) {
|
|
|
1595
1642
|
}
|
|
1596
1643
|
}
|
|
1597
1644
|
try {
|
|
1598
|
-
const project = await maybeResolveProject({
|
|
1599
|
-
cwd: opts.cwd,
|
|
1600
|
-
rootOverride: opts.rootOverride,
|
|
1601
|
-
});
|
|
1602
1645
|
if (project) {
|
|
1603
1646
|
await applyRecipeAgents({
|
|
1604
1647
|
manifest: manifestWithTags,
|
|
@@ -1819,6 +1862,7 @@ export async function cmdRecipes(opts) {
|
|
|
1819
1862
|
index: parsed.index,
|
|
1820
1863
|
refresh: parsed.refresh,
|
|
1821
1864
|
onConflict: parsed.onConflict,
|
|
1865
|
+
yes: parsed.yes,
|
|
1822
1866
|
});
|
|
1823
1867
|
}
|
|
1824
1868
|
if (subcommand === "remove") {
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare function toGitPath(filePath: string): string;
|
|
2
|
+
export declare function gitShowFile(cwd: string, ref: string, relPath: string): Promise<string>;
|
|
3
|
+
export declare function gitDiffNames(cwd: string, base: string, branch: string): Promise<string[]>;
|
|
4
|
+
export declare function gitDiffStat(cwd: string, base: string, branch: string): Promise<string>;
|
|
5
|
+
export declare function gitAheadBehind(cwd: string, base: string, branch: string): Promise<{
|
|
6
|
+
ahead: number;
|
|
7
|
+
behind: number;
|
|
8
|
+
}>;
|
|
9
|
+
//# sourceMappingURL=git-diff.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-diff.d.ts","sourceRoot":"","sources":["../../../src/commands/shared/git-diff.ts"],"names":[],"mappings":"AAIA,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,wBAAsB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAM5F;AAED,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAS/F;AAED,wBAAsB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAM5F;AAED,wBAAsB,cAAc,CAClC,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,GACb,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAa5C"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { execFileAsync, gitEnv } from "./git.js";
|
|
3
|
+
export function toGitPath(filePath) {
|
|
4
|
+
return filePath.split(path.sep).join("/");
|
|
5
|
+
}
|
|
6
|
+
export async function gitShowFile(cwd, ref, relPath) {
|
|
7
|
+
const { stdout } = await execFileAsync("git", ["show", `${ref}:${relPath}`], {
|
|
8
|
+
cwd,
|
|
9
|
+
env: gitEnv(),
|
|
10
|
+
});
|
|
11
|
+
return stdout;
|
|
12
|
+
}
|
|
13
|
+
export async function gitDiffNames(cwd, base, branch) {
|
|
14
|
+
const { stdout } = await execFileAsync("git", ["diff", "--name-only", `${base}...${branch}`], {
|
|
15
|
+
cwd,
|
|
16
|
+
env: gitEnv(),
|
|
17
|
+
});
|
|
18
|
+
return stdout
|
|
19
|
+
.split("\n")
|
|
20
|
+
.map((line) => line.trim())
|
|
21
|
+
.filter((line) => line.length > 0);
|
|
22
|
+
}
|
|
23
|
+
export async function gitDiffStat(cwd, base, branch) {
|
|
24
|
+
const { stdout } = await execFileAsync("git", ["diff", "--stat", `${base}...${branch}`], {
|
|
25
|
+
cwd,
|
|
26
|
+
env: gitEnv(),
|
|
27
|
+
});
|
|
28
|
+
return stdout.trimEnd();
|
|
29
|
+
}
|
|
30
|
+
export async function gitAheadBehind(cwd, base, branch) {
|
|
31
|
+
const { stdout } = await execFileAsync("git", ["rev-list", "--left-right", "--count", `${base}...${branch}`], { cwd, env: gitEnv() });
|
|
32
|
+
const trimmed = stdout.trim();
|
|
33
|
+
if (!trimmed)
|
|
34
|
+
return { ahead: 0, behind: 0 };
|
|
35
|
+
const parts = trimmed.split(/\s+/);
|
|
36
|
+
if (parts.length !== 2)
|
|
37
|
+
return { ahead: 0, behind: 0 };
|
|
38
|
+
const behind = Number.parseInt(parts[0] ?? "0", 10) || 0;
|
|
39
|
+
const ahead = Number.parseInt(parts[1] ?? "0", 10) || 0;
|
|
40
|
+
return { ahead, behind };
|
|
41
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export declare function gitRevParse(cwd: string, args: string[]): Promise<string>;
|
|
2
|
+
export declare function gitCurrentBranch(cwd: string): Promise<string>;
|
|
3
|
+
export declare function gitBranchExists(cwd: string, branch: string): Promise<boolean>;
|
|
4
|
+
export declare function gitListBranches(cwd: string): Promise<string[]>;
|
|
5
|
+
export declare function gitStagedPaths(cwd: string): Promise<string[]>;
|
|
6
|
+
export declare function gitAddPaths(cwd: string, paths: string[]): Promise<void>;
|
|
7
|
+
export declare function gitCommit(cwd: string, message: string, opts?: {
|
|
8
|
+
env?: NodeJS.ProcessEnv;
|
|
9
|
+
skipHooks?: boolean;
|
|
10
|
+
}): Promise<void>;
|
|
11
|
+
export declare function gitInitRepo(cwd: string, branch: string): Promise<void>;
|
|
12
|
+
export declare function resolveInitBaseBranch(gitRoot: string, fallback: string): Promise<string>;
|
|
13
|
+
export declare function promptInitBaseBranch(opts: {
|
|
14
|
+
gitRoot: string;
|
|
15
|
+
fallback: string;
|
|
16
|
+
}): Promise<string>;
|
|
17
|
+
export declare function ensureInitCommit(opts: {
|
|
18
|
+
gitRoot: string;
|
|
19
|
+
baseBranch: string;
|
|
20
|
+
installPaths: string[];
|
|
21
|
+
version: string;
|
|
22
|
+
skipHooks: boolean;
|
|
23
|
+
}): Promise<void>;
|
|
24
|
+
//# sourceMappingURL=git-ops.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-ops.d.ts","sourceRoot":"","sources":["../../../src/commands/shared/git-ops.ts"],"names":[],"mappings":"AAKA,wBAAsB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAK9E;AAED,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAkBnE;AAED,wBAAsB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAYnF;AAED,wBAAsB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CASpE;AAED,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CASnE;AAED,wBAAsB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAG7E;AAED,wBAAsB,SAAS,CAC7B,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,CAAA;CAAE,GACtD,OAAO,CAAC,IAAI,CAAC,CAKf;AAED,wBAAsB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAgB5E;AAED,wBAAsB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAe9F;AAED,wBAAsB,oBAAoB,CAAC,IAAI,EAAE;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,OAAO,CAAC,MAAM,CAAC,CAiDlB;AAED,wBAAsB,gBAAgB,CAAC,IAAI,EAAE;IAC3C,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;CACpB,GAAG,OAAO,CAAC,IAAI,CAAC,CAwBhB"}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { execFileAsync, gitEnv } from "./git.js";
|
|
2
|
+
import { setPinnedBaseBranch } from "@agentplaneorg/core";
|
|
3
|
+
import { promptChoice, promptInput } from "../../cli/prompts.js";
|
|
4
|
+
import { CliError } from "../../shared/errors.js";
|
|
5
|
+
export async function gitRevParse(cwd, args) {
|
|
6
|
+
const { stdout } = await execFileAsync("git", ["rev-parse", ...args], { cwd, env: gitEnv() });
|
|
7
|
+
const trimmed = stdout.trim();
|
|
8
|
+
if (!trimmed)
|
|
9
|
+
throw new Error("Failed to resolve git path");
|
|
10
|
+
return trimmed;
|
|
11
|
+
}
|
|
12
|
+
export async function gitCurrentBranch(cwd) {
|
|
13
|
+
try {
|
|
14
|
+
const { stdout } = await execFileAsync("git", ["symbolic-ref", "--short", "HEAD"], {
|
|
15
|
+
cwd,
|
|
16
|
+
env: gitEnv(),
|
|
17
|
+
});
|
|
18
|
+
const trimmed = stdout.trim();
|
|
19
|
+
if (trimmed)
|
|
20
|
+
return trimmed;
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
// fall through
|
|
24
|
+
}
|
|
25
|
+
const { stdout } = await execFileAsync("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
|
|
26
|
+
cwd,
|
|
27
|
+
env: gitEnv(),
|
|
28
|
+
});
|
|
29
|
+
const trimmed = stdout.trim();
|
|
30
|
+
if (!trimmed || trimmed === "HEAD")
|
|
31
|
+
throw new Error("Failed to resolve git branch");
|
|
32
|
+
return trimmed;
|
|
33
|
+
}
|
|
34
|
+
export async function gitBranchExists(cwd, branch) {
|
|
35
|
+
try {
|
|
36
|
+
await execFileAsync("git", ["show-ref", "--verify", "--quiet", `refs/heads/${branch}`], {
|
|
37
|
+
cwd,
|
|
38
|
+
env: gitEnv(),
|
|
39
|
+
});
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
const code = err?.code;
|
|
44
|
+
if (code === 1)
|
|
45
|
+
return false;
|
|
46
|
+
throw err;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
export async function gitListBranches(cwd) {
|
|
50
|
+
const { stdout } = await execFileAsync("git", ["branch", "--format=%(refname:short)"], {
|
|
51
|
+
cwd,
|
|
52
|
+
env: gitEnv(),
|
|
53
|
+
});
|
|
54
|
+
return stdout
|
|
55
|
+
.split("\n")
|
|
56
|
+
.map((line) => line.trim())
|
|
57
|
+
.filter((line) => line.length > 0);
|
|
58
|
+
}
|
|
59
|
+
export async function gitStagedPaths(cwd) {
|
|
60
|
+
const { stdout } = await execFileAsync("git", ["diff", "--cached", "--name-only"], {
|
|
61
|
+
cwd,
|
|
62
|
+
env: gitEnv(),
|
|
63
|
+
});
|
|
64
|
+
return stdout
|
|
65
|
+
.split("\n")
|
|
66
|
+
.map((line) => line.trim())
|
|
67
|
+
.filter((line) => line.length > 0);
|
|
68
|
+
}
|
|
69
|
+
export async function gitAddPaths(cwd, paths) {
|
|
70
|
+
if (paths.length === 0)
|
|
71
|
+
return;
|
|
72
|
+
await execFileAsync("git", ["add", "--", ...paths], { cwd, env: gitEnv() });
|
|
73
|
+
}
|
|
74
|
+
export async function gitCommit(cwd, message, opts) {
|
|
75
|
+
const args = ["commit", "-m", message];
|
|
76
|
+
if (opts?.skipHooks)
|
|
77
|
+
args.push("--no-verify");
|
|
78
|
+
const env = opts?.env ? { ...gitEnv(), ...opts.env } : gitEnv();
|
|
79
|
+
await execFileAsync("git", args, { cwd, env });
|
|
80
|
+
}
|
|
81
|
+
export async function gitInitRepo(cwd, branch) {
|
|
82
|
+
try {
|
|
83
|
+
await execFileAsync("git", ["init", "-q", "-b", branch], { cwd, env: gitEnv() });
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
await execFileAsync("git", ["init", "-q"], { cwd, env: gitEnv() });
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
const current = await gitCurrentBranch(cwd);
|
|
91
|
+
if (current !== branch) {
|
|
92
|
+
await execFileAsync("git", ["checkout", "-q", "-b", branch], { cwd, env: gitEnv() });
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
await execFileAsync("git", ["checkout", "-q", "-b", branch], { cwd, env: gitEnv() });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
export async function resolveInitBaseBranch(gitRoot, fallback) {
|
|
100
|
+
let current = null;
|
|
101
|
+
try {
|
|
102
|
+
current = await gitCurrentBranch(gitRoot);
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
current = null;
|
|
106
|
+
}
|
|
107
|
+
const branches = await gitListBranches(gitRoot);
|
|
108
|
+
if (current)
|
|
109
|
+
return current;
|
|
110
|
+
if (branches.includes(fallback))
|
|
111
|
+
return fallback;
|
|
112
|
+
if (branches.length > 0) {
|
|
113
|
+
const first = branches[0];
|
|
114
|
+
if (first)
|
|
115
|
+
return first;
|
|
116
|
+
}
|
|
117
|
+
return fallback;
|
|
118
|
+
}
|
|
119
|
+
export async function promptInitBaseBranch(opts) {
|
|
120
|
+
const branches = await gitListBranches(opts.gitRoot);
|
|
121
|
+
let current = null;
|
|
122
|
+
try {
|
|
123
|
+
current = await gitCurrentBranch(opts.gitRoot);
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
current = null;
|
|
127
|
+
}
|
|
128
|
+
const promptNewBranch = async (hasBranches) => {
|
|
129
|
+
const raw = await promptInput(`Enter new base branch name (default ${opts.fallback}): `);
|
|
130
|
+
const candidate = raw.trim() || opts.fallback;
|
|
131
|
+
if (!candidate) {
|
|
132
|
+
throw new CliError({
|
|
133
|
+
exitCode: 2,
|
|
134
|
+
code: "E_USAGE",
|
|
135
|
+
message: "Base branch name cannot be empty",
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
if (await gitBranchExists(opts.gitRoot, candidate))
|
|
139
|
+
return candidate;
|
|
140
|
+
try {
|
|
141
|
+
await execFileAsync("git", hasBranches ? ["branch", candidate] : ["checkout", "-q", "-b", candidate], { cwd: opts.gitRoot, env: gitEnv() });
|
|
142
|
+
}
|
|
143
|
+
catch (err) {
|
|
144
|
+
const message = err instanceof Error ? err.message : `Failed to create branch ${candidate}`;
|
|
145
|
+
throw new CliError({ exitCode: 5, code: "E_GIT", message });
|
|
146
|
+
}
|
|
147
|
+
return candidate;
|
|
148
|
+
};
|
|
149
|
+
if (branches.length === 0) {
|
|
150
|
+
return await promptNewBranch(false);
|
|
151
|
+
}
|
|
152
|
+
const createLabel = "Create new branch";
|
|
153
|
+
const defaultChoice = current && branches.includes(current) ? current : (branches[0] ?? opts.fallback);
|
|
154
|
+
const choice = await promptChoice("Select base branch", [...branches, createLabel], defaultChoice);
|
|
155
|
+
if (choice === createLabel) {
|
|
156
|
+
return await promptNewBranch(true);
|
|
157
|
+
}
|
|
158
|
+
return choice;
|
|
159
|
+
}
|
|
160
|
+
export async function ensureInitCommit(opts) {
|
|
161
|
+
const stagedBefore = await gitStagedPaths(opts.gitRoot);
|
|
162
|
+
if (stagedBefore.length > 0) {
|
|
163
|
+
throw new CliError({
|
|
164
|
+
exitCode: 5,
|
|
165
|
+
code: "E_GIT",
|
|
166
|
+
message: "Git index has staged changes; commit or unstage them before running agentplane init.",
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
await setPinnedBaseBranch({
|
|
170
|
+
cwd: opts.gitRoot,
|
|
171
|
+
rootOverride: opts.gitRoot,
|
|
172
|
+
value: opts.baseBranch,
|
|
173
|
+
});
|
|
174
|
+
const dedupedPaths = [...new Set(opts.installPaths)].filter((entry) => entry.length > 0);
|
|
175
|
+
await gitAddPaths(opts.gitRoot, dedupedPaths);
|
|
176
|
+
const staged = await gitStagedPaths(opts.gitRoot);
|
|
177
|
+
if (staged.length === 0)
|
|
178
|
+
return;
|
|
179
|
+
const message = `chore: install agentplane ${opts.version}`;
|
|
180
|
+
await gitCommit(opts.gitRoot, message, { skipHooks: opts.skipHooks });
|
|
181
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare function listWorktrees(cwd: string): Promise<{
|
|
2
|
+
path: string;
|
|
3
|
+
branch: string | null;
|
|
4
|
+
}[]>;
|
|
5
|
+
export declare function findWorktreeForBranch(cwd: string, branch: string): Promise<string | null>;
|
|
6
|
+
export declare function parseTaskIdFromBranch(prefix: string, branch: string): string | null;
|
|
7
|
+
export declare function gitListTaskBranches(cwd: string, prefix: string): Promise<string[]>;
|
|
8
|
+
//# sourceMappingURL=git-worktree.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-worktree.d.ts","sourceRoot":"","sources":["../../../src/commands/shared/git-worktree.ts"],"names":[],"mappings":"AAEA,wBAAsB,aAAa,CACjC,GAAG,EAAE,MAAM,GACV,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,EAAE,CAAC,CAoBpD;AAED,wBAAsB,qBAAqB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAQ/F;AAMD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAMnF;AAED,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAUxF"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { execFileAsync, gitEnv } from "./git.js";
|
|
2
|
+
export async function listWorktrees(cwd) {
|
|
3
|
+
const { stdout } = await execFileAsync("git", ["worktree", "list", "--porcelain"], {
|
|
4
|
+
cwd,
|
|
5
|
+
env: gitEnv(),
|
|
6
|
+
});
|
|
7
|
+
const worktrees = [];
|
|
8
|
+
const lines = stdout.split("\n");
|
|
9
|
+
let current = null;
|
|
10
|
+
for (const line of lines) {
|
|
11
|
+
if (line.startsWith("worktree ")) {
|
|
12
|
+
if (current)
|
|
13
|
+
worktrees.push(current);
|
|
14
|
+
current = { path: line.slice("worktree ".length).trim(), branch: null };
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
if (line.startsWith("branch ") && current) {
|
|
18
|
+
current.branch = line.slice("branch ".length).trim();
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
if (current)
|
|
22
|
+
worktrees.push(current);
|
|
23
|
+
return worktrees;
|
|
24
|
+
}
|
|
25
|
+
export async function findWorktreeForBranch(cwd, branch) {
|
|
26
|
+
const target = branch.startsWith("refs/heads/") ? branch : `refs/heads/${branch}`;
|
|
27
|
+
const worktrees = await listWorktrees(cwd);
|
|
28
|
+
const match = worktrees.find((entry) => entry.branch === branch || entry.branch === target || entry.branch === `refs/heads/${branch}`);
|
|
29
|
+
return match ? match.path : null;
|
|
30
|
+
}
|
|
31
|
+
function stripBranchRef(branch) {
|
|
32
|
+
return branch.startsWith("refs/heads/") ? branch.slice("refs/heads/".length) : branch;
|
|
33
|
+
}
|
|
34
|
+
export function parseTaskIdFromBranch(prefix, branch) {
|
|
35
|
+
const normalized = stripBranchRef(branch);
|
|
36
|
+
if (!normalized.startsWith(`${prefix}/`))
|
|
37
|
+
return null;
|
|
38
|
+
const rest = normalized.slice(prefix.length + 1);
|
|
39
|
+
const taskId = rest.split("/", 1)[0];
|
|
40
|
+
return taskId ? taskId.trim() : null;
|
|
41
|
+
}
|
|
42
|
+
export async function gitListTaskBranches(cwd, prefix) {
|
|
43
|
+
const { stdout } = await execFileAsync("git", ["for-each-ref", "--format=%(refname:short)", `refs/heads/${prefix}`], { cwd, env: gitEnv() });
|
|
44
|
+
return stdout
|
|
45
|
+
.split("\n")
|
|
46
|
+
.map((line) => line.trim())
|
|
47
|
+
.filter((line) => line.length > 0);
|
|
48
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../../src/commands/shared/git.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAG9C,eAAO,MAAM,aAAa,+BAAsB,CAAC;AAGjD,wBAAgB,MAAM,IAAI,MAAM,CAAC,UAAU,CAS1C"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
export const execFileAsync = promisify(execFile);
|
|
4
|
+
// Avoid leaking worktree-index overrides into git subprocesses.
|
|
5
|
+
export function gitEnv() {
|
|
6
|
+
const env = { ...process.env };
|
|
7
|
+
delete env.GIT_DIR;
|
|
8
|
+
delete env.GIT_WORK_TREE;
|
|
9
|
+
delete env.GIT_COMMON_DIR;
|
|
10
|
+
delete env.GIT_INDEX_FILE;
|
|
11
|
+
delete env.GIT_OBJECT_DIRECTORY;
|
|
12
|
+
delete env.GIT_ALTERNATE_OBJECT_DIRECTORIES;
|
|
13
|
+
return env;
|
|
14
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { AgentplaneConfig } from "@agentplaneorg/core";
|
|
2
|
+
export declare function ensureNetworkApproved(opts: {
|
|
3
|
+
config: AgentplaneConfig;
|
|
4
|
+
yes: boolean;
|
|
5
|
+
reason: string;
|
|
6
|
+
interactive?: boolean;
|
|
7
|
+
}): Promise<void>;
|
|
8
|
+
//# sourceMappingURL=network-approval.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"network-approval.d.ts","sourceRoot":"","sources":["../../../src/commands/shared/network-approval.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAK5D,wBAAsB,qBAAqB,CAAC,IAAI,EAAE;IAChD,MAAM,EAAE,gBAAgB,CAAC;IACzB,GAAG,EAAE,OAAO,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,GAAG,OAAO,CAAC,IAAI,CAAC,CAsBhB"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { promptYesNo } from "../../cli/prompts.js";
|
|
2
|
+
import { CliError } from "../../shared/errors.js";
|
|
3
|
+
export async function ensureNetworkApproved(opts) {
|
|
4
|
+
const requireNetwork = opts.config.agents?.approvals.require_network === true;
|
|
5
|
+
if (!requireNetwork)
|
|
6
|
+
return;
|
|
7
|
+
if (opts.yes)
|
|
8
|
+
return;
|
|
9
|
+
const interactive = opts.interactive ?? Boolean(process.stdin.isTTY);
|
|
10
|
+
if (!interactive) {
|
|
11
|
+
throw new CliError({
|
|
12
|
+
exitCode: 3,
|
|
13
|
+
code: "E_VALIDATION",
|
|
14
|
+
message: `Network access requires explicit approval (pass --yes): ${opts.reason}`,
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
const approved = await promptYesNo(`Allow network access? ${opts.reason}`, false);
|
|
18
|
+
if (!approved) {
|
|
19
|
+
throw new CliError({
|
|
20
|
+
exitCode: 3,
|
|
21
|
+
code: "E_VALIDATION",
|
|
22
|
+
message: `Network access denied: ${opts.reason}`,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
}
|