@amistio/cli 0.1.15 → 0.1.17

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 CHANGED
@@ -39,6 +39,8 @@ Provider-backed model preferences use sanitized catalog fields: `--provider`, `-
39
39
 
40
40
  When `--tool copilot` uses the GitHub Copilot SDK, Amistio approves read-only permission requests by default and denies mutating, network, MCP, hook, memory, and shell requests. Set `AMISTIO_COPILOT_APPROVE_ALL=1` only on a local machine where broad Copilot SDK approval is intentional.
41
41
 
42
+ When `--tool codex` uses the Codex SDK, intermediate progress can be quiet until the final response. For live Codex CLI logs, run `amistio run --watch --tool codex --invocation-channel command`.
43
+
42
44
  `amistio runner status` reports local background runner state, latest heartbeat, and bounded resource usage when available. Resource usage is latest-sample runner process memory/CPU plus safe aggregate system memory/load signals; it does not include source files, environment variables, command lines, process lists, credentials, or arbitrary local paths.
43
45
 
44
46
  The runner advertises its supported work kinds in heartbeats. Current runners can claim read-only `projectContextRefresh` jobs from the workspace Context panel and create due runner-driven refreshes when no fresh approved map exists. Context refreshes inspect the paired checkout locally without modifying files and submit only bounded summaries, slices, entities, relations, safe citations, confidence, freshness, and repo-relative paths. If a submitted context refresh contains unsafe evidence, unsafe paths, or a map too large to store safely, Amistio marks the refresh failed with a safe reason instead of storing the rejected raw result. Approved maps are reused as context packs for source-aware assistant and impact-preview work. Current runners can also claim read-only issue diagnosis jobs from the web Issues panel, generate root-cause analysis and a proposed fix, and submit that result without modifying source. They can claim manual read-only `appEvaluationScan` jobs from the workspace Evaluate panel and create at most one due hourly evaluation during normal watch/background polling when app evaluation is enabled for the repository link. Evaluation results contain bounded summaries, safe evidence, suggested actions, lifecycle proposals, and repo-relative paths only. Current runners can also claim manual read-only `securityPostureScan` jobs from the workspace Security panel and create due daily posture checks during normal watch/background polling. Security scan results contain bounded summaries, standard references, safe evidence, and repo-relative paths only; source, secrets, environment variables, command lines, process lists, credentials, provider sessions, and arbitrary local paths stay local. Implementation or cleanup is queued separately only after the user approves an issue analysis, app evaluation finding, or security remediation plan in the app.
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@
4
4
  import { createHash as createHash7, randomUUID } from "node:crypto";
5
5
  import { writeFile as writeFile9 } from "node:fs/promises";
6
6
  import os7 from "node:os";
7
- import path14 from "node:path";
7
+ import path15 from "node:path";
8
8
  import { Command } from "commander";
9
9
 
10
10
  // ../shared/src/schemas.ts
@@ -773,6 +773,9 @@ var projectContextRepoPathSchema = z.string().trim().min(1).max(300).refine((val
773
773
  }
774
774
  return !value.split(/[\\/]+/).includes("..");
775
775
  }, "Path must be repository-relative without traversal");
776
+ var projectContextCitationSchema = assistantCitationSchema.extend({
777
+ repoPath: projectContextRepoPathSchema.optional()
778
+ });
776
779
  var projectContextCoverageSchema = z.object({
777
780
  status: projectContextFreshnessSchema.default("missing"),
778
781
  sliceCount: z.number().int().nonnegative().default(0),
@@ -789,7 +792,7 @@ var projectContextSliceSchema = z.object({
789
792
  repoPaths: z.array(projectContextRepoPathSchema).max(80).default([]),
790
793
  ownerHints: z.array(z.string().trim().min(1).max(160)).max(20).default([]),
791
794
  tags: z.array(z.string().trim().min(1).max(80)).max(30).default([]),
792
- citations: z.array(assistantCitationSchema).default([]),
795
+ citations: z.array(projectContextCitationSchema).default([]),
793
796
  dependsOn: z.array(z.string().trim().min(1).max(160)).max(50).default([]),
794
797
  freshness: projectContextFreshnessSchema.default("fresh"),
795
798
  confidence: z.number().min(0).max(1).default(0.6),
@@ -803,7 +806,7 @@ var projectContextEntitySchema = z.object({
803
806
  summary: z.string().trim().min(1).max(1200).optional(),
804
807
  aliases: z.array(z.string().trim().min(1).max(200)).default([]),
805
808
  sliceIds: z.array(z.string().trim().min(1).max(160)).default([]),
806
- citations: z.array(assistantCitationSchema).default([]),
809
+ citations: z.array(projectContextCitationSchema).default([]),
807
810
  confidence: z.number().min(0).max(1).default(0.6)
808
811
  });
809
812
  var projectContextRelationSchema = z.object({
@@ -812,7 +815,7 @@ var projectContextRelationSchema = z.object({
812
815
  fromId: z.string().trim().min(1).max(200),
813
816
  toId: z.string().trim().min(1).max(200),
814
817
  summary: z.string().trim().min(1).max(1200),
815
- citations: z.array(assistantCitationSchema).default([]),
818
+ citations: z.array(projectContextCitationSchema).default([]),
816
819
  confidence: z.number().min(0).max(1).default(0.6)
817
820
  });
818
821
  var projectSystemDiagramStatusSchema = z.enum(["draft", "proposed", "approved", "stale", "archived"]);
@@ -829,7 +832,7 @@ var projectSystemDiagramNodeSchema = z.object({
829
832
  lane: z.string().trim().min(1).max(120).optional(),
830
833
  sliceIds: z.array(z.string().trim().min(1).max(160)).default([]),
831
834
  entityIds: z.array(z.string().trim().min(1).max(200)).default([]),
832
- citations: z.array(assistantCitationSchema).min(1),
835
+ citations: z.array(projectContextCitationSchema).min(1),
833
836
  confidence: z.number().min(0).max(1).default(0.6),
834
837
  freshness: projectContextFreshnessSchema.default("fresh")
835
838
  });
@@ -840,7 +843,7 @@ var projectSystemDiagramEdgeSchema = z.object({
840
843
  relationType: knowledgeRelationTypeSchema.default("mentions"),
841
844
  label: z.string().trim().min(1).max(120).optional(),
842
845
  summary: z.string().trim().min(1).max(1200),
843
- citations: z.array(assistantCitationSchema).min(1),
846
+ citations: z.array(projectContextCitationSchema).min(1),
844
847
  confidence: z.number().min(0).max(1).default(0.6),
845
848
  freshness: projectContextFreshnessSchema.default("fresh")
846
849
  });
@@ -849,7 +852,7 @@ var projectSystemDiagramGroupSchema = z.object({
849
852
  label: z.string().trim().min(1).max(120),
850
853
  kind: projectSystemDiagramGroupKindSchema.default("group"),
851
854
  summary: z.string().trim().min(1).max(1200).optional(),
852
- citations: z.array(assistantCitationSchema).default([])
855
+ citations: z.array(projectContextCitationSchema).default([])
853
856
  });
854
857
  var projectSystemDiagramViewSchema = z.object({
855
858
  viewId: z.string().trim().min(1).max(160),
@@ -871,7 +874,7 @@ var projectSystemDiagramGraphManifestSchema = z.object({
871
874
  edges: z.array(projectSystemDiagramEdgeSchema).default([]),
872
875
  groups: z.array(projectSystemDiagramGroupSchema).default([]),
873
876
  views: z.array(projectSystemDiagramViewSchema).min(1),
874
- citations: z.array(assistantCitationSchema).default([]),
877
+ citations: z.array(projectContextCitationSchema).default([]),
875
878
  unknowns: z.array(z.string().trim().min(1).max(300)).default([]),
876
879
  warnings: z.array(z.string().trim().min(1).max(600)).default([])
877
880
  });
@@ -957,7 +960,7 @@ var projectContextPackSchema = z.object({
957
960
  slices: z.array(projectContextSliceSchema).default([]),
958
961
  entities: z.array(projectContextEntitySchema).default([]),
959
962
  relations: z.array(projectContextRelationSchema).default([]),
960
- citations: z.array(assistantCitationSchema).default([]),
963
+ citations: z.array(projectContextCitationSchema).default([]),
961
964
  freshnessWarnings: z.array(z.string().trim().min(1).max(600)).default([]),
962
965
  tokenEstimate: z.number().int().nonnegative().default(0),
963
966
  generatedAt: isoDateTimeSchema
@@ -2382,8 +2385,8 @@ var toolSessionMutationSchema = z3.object({
2382
2385
  });
2383
2386
  function resolveApiUrl(apiUrl, urlPath) {
2384
2387
  const base = apiUrl.endsWith("/") ? apiUrl.slice(0, -1) : apiUrl;
2385
- const path15 = urlPath.startsWith("/") ? urlPath : `/${urlPath}`;
2386
- return new URL(`${base}${path15}`);
2388
+ const path16 = urlPath.startsWith("/") ? urlPath : `/${urlPath}`;
2389
+ return new URL(`${base}${path16}`);
2387
2390
  }
2388
2391
 
2389
2392
  // src/orchestrator.ts
@@ -3195,6 +3198,9 @@ async function runCodexSdk(input) {
3195
3198
  approvalPolicy: "on-request",
3196
3199
  skipGitRepoCheck: true
3197
3200
  });
3201
+ if (input.streamOutput) {
3202
+ process.stderr.write("Codex SDK is running; intermediate progress may be quiet until the final response. Use --invocation-channel command for Codex CLI logs.\n");
3203
+ }
3198
3204
  const result = await thread.run(input.prompt);
3199
3205
  if (input.streamOutput && result.finalResponse) {
3200
3206
  process.stdout.write(result.finalResponse);
@@ -4371,6 +4377,7 @@ function defaultSessionStorePath() {
4371
4377
  }
4372
4378
 
4373
4379
  // src/work-runner.ts
4380
+ import path10 from "node:path";
4374
4381
  var generationResultStart = "AMISTIO_BRAIN_GENERATION_RESULT_START";
4375
4382
  var generationResultEnd = "AMISTIO_BRAIN_GENERATION_RESULT_END";
4376
4383
  var assistantAnswerStart = "AMISTIO_ASSISTANT_ANSWER_START";
@@ -4385,6 +4392,7 @@ var appEvaluationStart = "AMISTIO_APP_EVALUATION_START";
4385
4392
  var appEvaluationEnd = "AMISTIO_APP_EVALUATION_END";
4386
4393
  var projectContextRefreshStart = "AMISTIO_PROJECT_CONTEXT_REFRESH_START";
4387
4394
  var projectContextRefreshEnd = "AMISTIO_PROJECT_CONTEXT_REFRESH_END";
4395
+ var validProjectContextSliceKinds = /* @__PURE__ */ new Set(["overview", "architecture", "domain", "data", "api", "frontend", "backend", "cli", "workflow", "operations", "security", "testing", "unknown"]);
4388
4396
  var validProjectContextEntityTypes = /* @__PURE__ */ new Set(["project", "system", "component", "domain", "tool", "decision", "feature", "risk", "team", "workflow", "unknown"]);
4389
4397
  var validProjectContextRelationTypes = /* @__PURE__ */ new Set(["uses", "depends_on", "decides", "supersedes", "touches", "blocks", "implements", "mentions"]);
4390
4398
  function createWorkExecutionPrompt(workItem, context) {
@@ -4489,10 +4497,12 @@ function createProjectContextRefreshPrompt(workItem, context) {
4489
4497
  "## Mapping Requirements",
4490
4498
  "",
4491
4499
  "- Create slices for architecture, domain, data, API, frontend, backend, CLI, workflows, operations, security, and testing when those surfaces exist.",
4500
+ "- Use only these exact singular slice kind values: overview, architecture, domain, data, api, frontend, backend, cli, workflow, operations, security, testing, unknown.",
4492
4501
  "- Capture entities and relations that explain how the app is put together and where future work should look first.",
4493
4502
  "- Prefer summaries, repository-relative paths, short citations, tags, and freshness status over raw source excerpts.",
4494
4503
  "- Mark stale or missing areas explicitly instead of guessing.",
4495
4504
  "- Keep repoPaths repository-relative; never include /absolute paths or ../ traversal.",
4505
+ "- If local inspection prints a path inside this checkout, convert it to the repository-relative path before returning JSON.",
4496
4506
  "",
4497
4507
  "## Data Safety",
4498
4508
  "",
@@ -4949,7 +4959,7 @@ function parseAppEvaluationScanResult(output) {
4949
4959
  const parsed = JSON.parse(stripJsonFence(payload));
4950
4960
  return appEvaluationScanResultSchema.parse(parsed);
4951
4961
  }
4952
- function parseProjectContextRefreshResult(output) {
4962
+ function parseProjectContextRefreshResult(output, options = {}) {
4953
4963
  const start = output.indexOf(projectContextRefreshStart);
4954
4964
  const end = output.indexOf(projectContextRefreshEnd, start + projectContextRefreshStart.length);
4955
4965
  if (start === -1 || end === -1 || end <= start) {
@@ -4957,7 +4967,8 @@ function parseProjectContextRefreshResult(output) {
4957
4967
  }
4958
4968
  const payload = output.slice(start + projectContextRefreshStart.length, end).trim();
4959
4969
  const parsed = JSON.parse(stripJsonFence(payload));
4960
- return projectContextRefreshResultSchema.parse(normalizeProjectContextRefreshEnums(parsed));
4970
+ const normalized = normalizeProjectContextRefreshPaths(normalizeProjectContextRefreshEnums(parsed), options);
4971
+ return projectContextRefreshResultSchema.parse(normalized);
4961
4972
  }
4962
4973
  function projectContextRefreshSubmissionFailureSummary(result) {
4963
4974
  if (result.refresh.status !== "failed" && result.workItem.status !== "failed") {
@@ -5026,6 +5037,16 @@ function normalizeProjectContextRefreshEnums(value) {
5026
5037
  return value;
5027
5038
  }
5028
5039
  const normalized = { ...value };
5040
+ if (Array.isArray(normalized.slices)) {
5041
+ normalized.slices = normalized.slices.map((slice) => {
5042
+ if (!isObjectRecord(slice)) {
5043
+ return slice;
5044
+ }
5045
+ const normalizedSlice = { ...slice };
5046
+ normalizedSlice.kind = normalizeProjectContextSliceKind(slice.kind);
5047
+ return normalizedSlice;
5048
+ });
5049
+ }
5029
5050
  if (Array.isArray(normalized.entities)) {
5030
5051
  normalized.entities = normalized.entities.map((entity) => {
5031
5052
  if (!isObjectRecord(entity)) {
@@ -5048,6 +5069,94 @@ function normalizeProjectContextRefreshEnums(value) {
5048
5069
  }
5049
5070
  return normalized;
5050
5071
  }
5072
+ function normalizeProjectContextSliceKind(value) {
5073
+ if (typeof value !== "string") {
5074
+ return "unknown";
5075
+ }
5076
+ const normalized = value.trim().toLowerCase();
5077
+ if (validProjectContextSliceKinds.has(normalized)) {
5078
+ return normalized;
5079
+ }
5080
+ if (normalized.endsWith("s")) {
5081
+ const singular = normalized.slice(0, -1);
5082
+ if (validProjectContextSliceKinds.has(singular)) {
5083
+ return singular;
5084
+ }
5085
+ }
5086
+ return "unknown";
5087
+ }
5088
+ function normalizeProjectContextRefreshPaths(value, options) {
5089
+ if (!isObjectRecord(value)) {
5090
+ return value;
5091
+ }
5092
+ const normalized = { ...value };
5093
+ if (Array.isArray(normalized.slices)) {
5094
+ normalized.slices = normalized.slices.map((slice) => normalizeProjectContextSlicePaths(slice, options));
5095
+ }
5096
+ if (Array.isArray(normalized.entities)) {
5097
+ normalized.entities = normalized.entities.map((entity) => normalizeProjectContextCitationPaths(entity, options));
5098
+ }
5099
+ if (Array.isArray(normalized.relations)) {
5100
+ normalized.relations = normalized.relations.map((relation) => normalizeProjectContextCitationPaths(relation, options));
5101
+ }
5102
+ return normalized;
5103
+ }
5104
+ function normalizeProjectContextSlicePaths(value, options) {
5105
+ if (!isObjectRecord(value)) {
5106
+ return value;
5107
+ }
5108
+ const normalized = normalizeProjectContextCitationPaths(value, options);
5109
+ if (Array.isArray(normalized.repoPaths)) {
5110
+ normalized.repoPaths = normalized.repoPaths.map((repoPath) => typeof repoPath === "string" ? normalizeProjectContextRepoPath(repoPath, options) : repoPath);
5111
+ }
5112
+ return normalized;
5113
+ }
5114
+ function normalizeProjectContextCitationPaths(value, options) {
5115
+ if (!isObjectRecord(value)) {
5116
+ return value;
5117
+ }
5118
+ const normalized = { ...value };
5119
+ if (Array.isArray(normalized.citations)) {
5120
+ normalized.citations = normalized.citations.map((citation) => {
5121
+ if (!isObjectRecord(citation)) {
5122
+ return citation;
5123
+ }
5124
+ const normalizedCitation = { ...citation };
5125
+ if (typeof normalizedCitation.repoPath === "string") {
5126
+ normalizedCitation.repoPath = normalizeProjectContextRepoPath(normalizedCitation.repoPath, options);
5127
+ }
5128
+ return normalizedCitation;
5129
+ });
5130
+ }
5131
+ return normalized;
5132
+ }
5133
+ function normalizeProjectContextRepoPath(value, options) {
5134
+ const trimmed = value.trim();
5135
+ if (!trimmed || /^[A-Za-z][A-Za-z0-9+.-]*:\/\//.test(trimmed) || /^file:/i.test(trimmed) || /^[A-Za-z]:($|[^\\/])/.test(trimmed)) {
5136
+ throwUnsafeProjectContextPath();
5137
+ }
5138
+ const absolute = trimmed.startsWith("/") || trimmed.startsWith("\\\\") || path10.isAbsolute(trimmed) || path10.win32.isAbsolute(trimmed);
5139
+ if (!absolute) {
5140
+ return normalizeRelativeProjectContextPath(trimmed);
5141
+ }
5142
+ if (!options.repositoryRoot) {
5143
+ throwUnsafeProjectContextPath();
5144
+ }
5145
+ const useWindowsPathRules = path10.win32.isAbsolute(trimmed) && !trimmed.startsWith("/");
5146
+ const relativePath = useWindowsPathRules ? path10.win32.relative(path10.win32.resolve(options.repositoryRoot), path10.win32.resolve(trimmed)) : path10.relative(path10.resolve(options.repositoryRoot), path10.resolve(trimmed));
5147
+ return normalizeRelativeProjectContextPath(relativePath);
5148
+ }
5149
+ function normalizeRelativeProjectContextPath(value) {
5150
+ const normalized = value.trim().replace(/\\/g, "/");
5151
+ const segments = normalized.split("/").filter((segment) => segment.length > 0 && segment !== ".");
5152
+ if (!segments.length || normalized.startsWith("/") || normalized === "~" || normalized.startsWith("~/") || /^[A-Za-z]:/.test(normalized) || segments.includes("..")) {
5153
+ throwUnsafeProjectContextPath();
5154
+ }
5155
+ return segments.join("/");
5156
+ }
5157
+ function throwUnsafeProjectContextPath() {
5158
+ throw new Error("Project context refresh contains an unsafe repository path.");
5159
+ }
5051
5160
  function normalizeProjectContextEntityType(value) {
5052
5161
  if (typeof value !== "string") {
5053
5162
  return "unknown";
@@ -5240,7 +5349,7 @@ function roundNumber(value, digits) {
5240
5349
  import { execFile as execFile4 } from "node:child_process";
5241
5350
  import { createHash as createHash6 } from "node:crypto";
5242
5351
  import { readdir as readdir4, readFile as readFile8, stat as stat4 } from "node:fs/promises";
5243
- import path10 from "node:path";
5352
+ import path11 from "node:path";
5244
5353
  import { promisify as promisify4 } from "node:util";
5245
5354
  var execFileAsync4 = promisify4(execFile4);
5246
5355
  var defaultMaxFileKb = 256;
@@ -5259,12 +5368,12 @@ var documentFolderByType = {
5259
5368
  workflow: "docs/workflows"
5260
5369
  };
5261
5370
  async function inspectLocalRepository(rootDir, defaultBranch) {
5262
- const requestedRoot = path10.resolve(rootDir);
5371
+ const requestedRoot = path11.resolve(rootDir);
5263
5372
  const root = await runGit2(["-C", requestedRoot, "rev-parse", "--show-toplevel"]).catch(() => requestedRoot);
5264
5373
  const detectedBranch = await runGit2(["-C", root, "symbolic-ref", "--quiet", "--short", "HEAD"]).catch(() => defaultBranch);
5265
5374
  const originUrl = await runGit2(["-C", root, "remote", "get-url", "origin"]).catch(() => void 0);
5266
5375
  const parsedCloneUrl = originUrl ? parseOptionalOriginCloneUrl(originUrl) : void 0;
5267
- const repoName = (parsedCloneUrl?.repoName ?? path10.basename(root)) || "repository";
5376
+ const repoName = (parsedCloneUrl?.repoName ?? path11.basename(root)) || "repository";
5268
5377
  const fingerprintSeed = parsedCloneUrl ? `origin:${parsedCloneUrl.normalizedKey}` : `repo:${repoName}:${detectedBranch || defaultBranch}`;
5269
5378
  return {
5270
5379
  rootDir: root,
@@ -5276,7 +5385,7 @@ async function inspectLocalRepository(rootDir, defaultBranch) {
5276
5385
  };
5277
5386
  }
5278
5387
  async function scanLegacyDocuments(options) {
5279
- const rootDir = path10.resolve(options.rootDir);
5388
+ const rootDir = path11.resolve(options.rootDir);
5280
5389
  const maxBytes = (options.maxFileKb ?? defaultMaxFileKb) * 1024;
5281
5390
  const skipped = [];
5282
5391
  const candidates = [];
@@ -5296,7 +5405,7 @@ async function scanLegacyDocuments(options) {
5296
5405
  skipped.push({ repoPath, reason: "excluded" });
5297
5406
  continue;
5298
5407
  }
5299
- const fullPath = path10.join(rootDir, ...repoPath.split("/"));
5408
+ const fullPath = path11.join(rootDir, ...repoPath.split("/"));
5300
5409
  const fileStat = await stat4(fullPath).catch(() => void 0);
5301
5410
  if (!fileStat?.isFile()) {
5302
5411
  skipped.push({ repoPath, reason: "unreadable" });
@@ -5398,8 +5507,8 @@ async function listRepositoryPaths(rootDir) {
5398
5507
  async function walkRepository(rootDir, directory, files) {
5399
5508
  const entries = await readdir4(directory, { withFileTypes: true }).catch(() => []);
5400
5509
  for (const entry of entries) {
5401
- const fullPath = path10.join(directory, entry.name);
5402
- const repoPath = normalizeRepoPath4(path10.relative(rootDir, fullPath));
5510
+ const fullPath = path11.join(directory, entry.name);
5511
+ const repoPath = normalizeRepoPath4(path11.relative(rootDir, fullPath));
5403
5512
  if (entry.isDirectory()) {
5404
5513
  if (!excludedDirectoryNames.has(entry.name)) {
5405
5514
  await walkRepository(rootDir, fullPath, files);
@@ -5465,9 +5574,9 @@ function uniqueDestinationPath(basePath, sourcePath, usedPaths) {
5465
5574
  usedPaths.add(basePath);
5466
5575
  return basePath;
5467
5576
  }
5468
- const extension = path10.posix.extname(basePath) || ".md";
5469
- const directory = path10.posix.dirname(basePath);
5470
- const basename = path10.posix.basename(basePath, extension);
5577
+ const extension = path11.posix.extname(basePath) || ".md";
5578
+ const directory = path11.posix.dirname(basePath);
5579
+ const basename = path11.posix.basename(basePath, extension);
5471
5580
  const uniquePath = `${directory}/${basename}-${hashText(sourcePath, 8)}${extension}`;
5472
5581
  usedPaths.add(uniquePath);
5473
5582
  return uniquePath;
@@ -5486,7 +5595,7 @@ function inferTitle2(content, repoPath) {
5486
5595
  if (heading) return heading;
5487
5596
  const htmlHeading = body.match(/<h1\b[^>]*>([\s\S]*?)<\/h1>/i)?.[1]?.replace(/<[^>]+>/g, "").trim();
5488
5597
  if (htmlHeading) return htmlHeading;
5489
- const basename = path10.posix.basename(repoPath, path10.posix.extname(repoPath)).replace(/[-_]+/g, " ").trim();
5598
+ const basename = path11.posix.basename(repoPath, path11.posix.extname(repoPath)).replace(/[-_]+/g, " ").trim();
5490
5599
  return titleCase(basename || "Imported Document");
5491
5600
  }
5492
5601
  function stripFrontmatter(content) {
@@ -5520,7 +5629,7 @@ async function runGit2(args) {
5520
5629
 
5521
5630
  // src/runner-actions.ts
5522
5631
  import { spawn as spawn4 } from "node:child_process";
5523
- import path11 from "node:path";
5632
+ import path12 from "node:path";
5524
5633
  function buildBackgroundRunnerArgs(options) {
5525
5634
  const args = [
5526
5635
  "run",
@@ -5530,7 +5639,7 @@ function buildBackgroundRunnerArgs(options) {
5530
5639
  "--runner-id",
5531
5640
  options.runnerId,
5532
5641
  "--root",
5533
- path11.resolve(options.root),
5642
+ path12.resolve(options.root),
5534
5643
  "--session",
5535
5644
  options.session,
5536
5645
  "--interval-seconds",
@@ -5648,7 +5757,7 @@ function truncateProcessOutput(value) {
5648
5757
  // src/git-worktree.ts
5649
5758
  import { execFile as execFile5 } from "node:child_process";
5650
5759
  import { mkdir as mkdir9, stat as stat5 } from "node:fs/promises";
5651
- import path12 from "node:path";
5760
+ import path13 from "node:path";
5652
5761
  import { promisify as promisify5 } from "node:util";
5653
5762
  var execFileAsync5 = promisify5(execFile5);
5654
5763
  function needsGitWorktreeIsolation(workItem) {
@@ -5677,7 +5786,7 @@ async function prepareGitWorktreeIsolation(rootDir, workItem) {
5677
5786
  await assertExistingWorktree(worktreePath, identity.branch);
5678
5787
  return { ...identity, baseRevision, worktreePath };
5679
5788
  }
5680
- await mkdir9(path12.dirname(worktreePath), { recursive: true });
5789
+ await mkdir9(path13.dirname(worktreePath), { recursive: true });
5681
5790
  const branchExists = await gitCommandSucceeds(repoRoot, ["show-ref", "--verify", "--quiet", `refs/heads/${identity.branch}`]);
5682
5791
  const worktreeArgs = branchExists ? ["worktree", "add", worktreePath, identity.branch] : ["worktree", "add", "-b", identity.branch, worktreePath, baseRevision];
5683
5792
  await gitOutput(repoRoot, worktreeArgs).catch((error) => {
@@ -5686,9 +5795,9 @@ async function prepareGitWorktreeIsolation(rootDir, workItem) {
5686
5795
  return { ...identity, baseRevision, worktreePath };
5687
5796
  }
5688
5797
  function localWorktreePath(repoRoot, worktreeKey) {
5689
- const repoName = path12.basename(repoRoot);
5798
+ const repoName = path13.basename(repoRoot);
5690
5799
  const worktreeSlug = worktreeKey.split("/").filter(Boolean).pop() ?? "work";
5691
- return path12.join(path12.dirname(repoRoot), `${repoName}.worktrees`, worktreeSlug);
5800
+ return path13.join(path13.dirname(repoRoot), `${repoName}.worktrees`, worktreeSlug);
5692
5801
  }
5693
5802
  async function assertExistingWorktree(worktreePath, branch) {
5694
5803
  await gitOutput(worktreePath, ["rev-parse", "--is-inside-work-tree"]);
@@ -5735,7 +5844,7 @@ function errorMessage2(error) {
5735
5844
 
5736
5845
  // src/implementation-handoff.ts
5737
5846
  import { execFile as execFile6 } from "node:child_process";
5738
- import path13 from "node:path";
5847
+ import path14 from "node:path";
5739
5848
  import { promisify as promisify6 } from "node:util";
5740
5849
  var execFileAsync6 = promisify6(execFile6);
5741
5850
  async function completeImplementationHandoff(input) {
@@ -5821,7 +5930,7 @@ async function cleanupWorktree(run, input) {
5821
5930
  return { status: "failed", message: "Cleanup skipped because the worktree is not clean after PR handoff." };
5822
5931
  }
5823
5932
  try {
5824
- await gitOutput2(run, input.primaryRepoRoot || path13.dirname(input.worktreePath), ["worktree", "remove", input.worktreePath]);
5933
+ await gitOutput2(run, input.primaryRepoRoot || path14.dirname(input.worktreePath), ["worktree", "remove", input.worktreePath]);
5825
5934
  return { status: "completed" };
5826
5935
  } catch (error) {
5827
5936
  return { status: "failed", message: `Cleanup failed: ${safeErrorMessage(error)}` };
@@ -6310,7 +6419,7 @@ program.command("run").description("Claim and run approved Amistio work locally"
6310
6419
  projectId: context.metadata.amistioProjectId,
6311
6420
  repositoryLinkId: context.metadata.repositoryLinkId,
6312
6421
  runnerId,
6313
- rootDir: path14.resolve(options.root),
6422
+ rootDir: path15.resolve(options.root),
6314
6423
  apiUrl: options.apiUrl,
6315
6424
  args: buildBackgroundRunnerArgs(resolvedOptions)
6316
6425
  });
@@ -6482,7 +6591,7 @@ runnerService.command("install").description("Install a user-level startup servi
6482
6591
  projectId: context.metadata.amistioProjectId,
6483
6592
  repositoryLinkId: context.metadata.repositoryLinkId,
6484
6593
  runnerId,
6485
- rootDir: path14.resolve(options.root),
6594
+ rootDir: path15.resolve(options.root),
6486
6595
  apiUrl: options.apiUrl,
6487
6596
  args,
6488
6597
  platform
@@ -6954,6 +7063,7 @@ async function runNextWorkItem({
6954
7063
  return await finalizeProjectContextRefreshWork({
6955
7064
  apiClient,
6956
7065
  durationMs: Date.now() - startedAt,
7066
+ executionRoot,
6957
7067
  projectId,
6958
7068
  repositoryLinkId,
6959
7069
  runnerId,
@@ -7808,6 +7918,7 @@ ${toolResult.stderr}`);
7808
7918
  async function finalizeProjectContextRefreshWork({
7809
7919
  apiClient,
7810
7920
  durationMs,
7921
+ executionRoot,
7811
7922
  projectId,
7812
7923
  repositoryLinkId,
7813
7924
  runnerId,
@@ -7822,7 +7933,7 @@ async function finalizeProjectContextRefreshWork({
7822
7933
  if (toolResult.exitCode === 0) {
7823
7934
  try {
7824
7935
  refreshResult = parseProjectContextRefreshResult(`${toolResult.stdout}
7825
- ${toolResult.stderr}`);
7936
+ ${toolResult.stderr}`, { repositoryRoot: executionRoot });
7826
7937
  } catch (error) {
7827
7938
  refreshError = errorMessage3(error);
7828
7939
  }
@@ -8199,7 +8310,7 @@ function parseReasoningEffort(value) {
8199
8310
  throw new Error(`Expected reasoning effort auto, low, medium, high, or xhigh; received ${value}.`);
8200
8311
  }
8201
8312
  function inferRepoName(root) {
8202
- return path14.basename(path14.resolve(root)) || "repository";
8313
+ return path15.basename(path15.resolve(root)) || "repository";
8203
8314
  }
8204
8315
  function createRepoFingerprint(accountId, projectId, repositoryLinkId) {
8205
8316
  return createHash7("sha256").update(`${accountId}:${projectId}:${repositoryLinkId}`).digest("hex");