snipara-companion 3.2.0 → 3.2.1

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/index.d.ts CHANGED
@@ -3869,6 +3869,9 @@ interface RuntimeDetectionReport {
3869
3869
  cliAvailable: boolean;
3870
3870
  command?: string;
3871
3871
  version?: string;
3872
+ source?: "path" | "workspace_venv";
3873
+ workspacePackageVersion?: string;
3874
+ versionMismatch?: boolean;
3872
3875
  };
3873
3876
  providerKeys: {
3874
3877
  openai: boolean;
package/dist/index.js CHANGED
@@ -437,14 +437,14 @@ function resolveProject(opts = {}) {
437
437
  }
438
438
  return { identifier: path2.basename(cwd), source: "cwd-basename" };
439
439
  }
440
- function findUpward(startDir, relative7) {
440
+ function findUpward(startDir, relative8) {
441
441
  let dir = path2.resolve(startDir);
442
442
  try {
443
443
  dir = fs2.realpathSync(dir);
444
444
  } catch {
445
445
  }
446
446
  while (true) {
447
- const candidate = path2.join(dir, relative7);
447
+ const candidate = path2.join(dir, relative8);
448
448
  if (fs2.existsSync(candidate)) return candidate;
449
449
  const parent = path2.dirname(dir);
450
450
  if (parent === dir) return null;
@@ -2281,8 +2281,8 @@ function normalizeRelativePath2(value) {
2281
2281
  return value.split(path5.sep).join("/");
2282
2282
  }
2283
2283
  function toRepoRelative(repoRoot, absolutePath) {
2284
- const relative7 = path5.relative(repoRoot, absolutePath);
2285
- return normalizeRelativePath2(relative7 || path5.basename(absolutePath));
2284
+ const relative8 = path5.relative(repoRoot, absolutePath);
2285
+ return normalizeRelativePath2(relative8 || path5.basename(absolutePath));
2286
2286
  }
2287
2287
  function sha2562(content) {
2288
2288
  return crypto2.createHash("sha256").update(content, "utf8").digest("hex");
@@ -10243,8 +10243,8 @@ function decisionPendingCount(cwd = process.cwd()) {
10243
10243
  return listPendingDecisionRequests(cwd).filter((entry) => entry.status === "pending").length;
10244
10244
  }
10245
10245
  function toProjectRelativePath(absolutePath, cwd) {
10246
- const relative7 = path11.relative(cwd, absolutePath).replace(/\\/g, "/");
10247
- return relative7.startsWith(".") ? relative7 : `.${path11.sep}${relative7}`.replace(/\\/g, "/");
10246
+ const relative8 = path11.relative(cwd, absolutePath).replace(/\\/g, "/");
10247
+ return relative8.startsWith(".") ? relative8 : `.${path11.sep}${relative8}`.replace(/\\/g, "/");
10248
10248
  }
10249
10249
 
10250
10250
  // src/commands/memory.ts
@@ -11228,6 +11228,8 @@ function detectRuntimeEnvironment(cwd = process.cwd(), env = process.env) {
11228
11228
  const mcpConfigPaths = findRuntimeMcpConfigPaths(cwd, workspaceRoot);
11229
11229
  const sandboxCli = detectSandboxCli();
11230
11230
  const orchestratorCli = detectOrchestratorCli(cwd, workspaceRoot);
11231
+ const workspaceOrchestratorVersion = findWorkspaceOrchestratorVersion(cwd, workspaceRoot);
11232
+ const orchestratorCliVersion = parseOrchestratorVersion(orchestratorCli.version);
11231
11233
  const parsedVersion = parseRuntimeVersion(sandboxCli.version);
11232
11234
  const providerKeys = detectProviderKeys(cwd, workspaceRoot, env);
11233
11235
  return {
@@ -11251,7 +11253,12 @@ function detectRuntimeEnvironment(cwd = process.cwd(), env = process.env) {
11251
11253
  orchestrator: {
11252
11254
  cliAvailable: Boolean(orchestratorCli.command),
11253
11255
  command: orchestratorCli.command,
11254
- version: orchestratorCli.version
11256
+ version: orchestratorCli.version,
11257
+ source: orchestratorCli.source,
11258
+ workspacePackageVersion: workspaceOrchestratorVersion,
11259
+ versionMismatch: Boolean(
11260
+ orchestratorCliVersion && workspaceOrchestratorVersion && orchestratorCliVersion !== workspaceOrchestratorVersion
11261
+ )
11255
11262
  },
11256
11263
  providerKeys,
11257
11264
  docker: {
@@ -11391,7 +11398,8 @@ function detectOrchestratorCli(cwd, workspaceRoot) {
11391
11398
  if (version || commandExists(ORCHESTRATOR_COMMAND)) {
11392
11399
  return {
11393
11400
  command: ORCHESTRATOR_COMMAND,
11394
- version
11401
+ version,
11402
+ source: "path"
11395
11403
  };
11396
11404
  }
11397
11405
  for (const candidate of findWorkspaceExecutableCandidates(
@@ -11404,11 +11412,46 @@ function detectOrchestratorCli(cwd, workspaceRoot) {
11404
11412
  }
11405
11413
  return {
11406
11414
  command: candidate,
11407
- version: getCommandVersion(candidate)
11415
+ version: getCommandVersion(candidate),
11416
+ source: "workspace_venv"
11408
11417
  };
11409
11418
  }
11410
11419
  return {};
11411
11420
  }
11421
+ function findWorkspaceOrchestratorVersion(cwd, workspaceRoot) {
11422
+ const roots = Array.from(
11423
+ new Set([workspaceRoot, path13.resolve(cwd)].filter((value) => Boolean(value)))
11424
+ );
11425
+ for (const root of roots) {
11426
+ const version = readPyprojectVersion(
11427
+ path13.join(root, "packages", "agentic-orchestrator", "pyproject.toml"),
11428
+ "snipara-orchestrator"
11429
+ );
11430
+ if (version) {
11431
+ return version;
11432
+ }
11433
+ }
11434
+ return void 0;
11435
+ }
11436
+ function readPyprojectVersion(filePath, packageName) {
11437
+ if (!fs14.existsSync(filePath)) {
11438
+ return void 0;
11439
+ }
11440
+ const content = fs14.readFileSync(filePath, "utf-8");
11441
+ const nameMatch = content.match(/^\s*name\s*=\s*["']([^"']+)["']/m);
11442
+ if (nameMatch && nameMatch[1] !== packageName) {
11443
+ return void 0;
11444
+ }
11445
+ const versionMatch = content.match(/^\s*version\s*=\s*["']([^"']+)["']/m);
11446
+ return versionMatch?.[1];
11447
+ }
11448
+ function parseOrchestratorVersion(version) {
11449
+ if (!version) {
11450
+ return void 0;
11451
+ }
11452
+ const match = version.match(/snipara-orchestrator\s+([^\s]+)/i);
11453
+ return match?.[1];
11454
+ }
11412
11455
  function findWorkspaceExecutableCandidates(command, cwd, workspaceRoot) {
11413
11456
  const executableName = process.platform === "win32" ? `${command}.exe` : command;
11414
11457
  const roots = Array.from(
@@ -11998,7 +12041,14 @@ function runtimeVersionSummary(report) {
11998
12041
  return `available (CLI ${report.runtime.cliVersion})`;
11999
12042
  }
12000
12043
  function orchestratorVersionSummary(report) {
12001
- return report.orchestrator.version ? `available (${report.orchestrator.version})` : "available";
12044
+ const parts = [
12045
+ report.orchestrator.version ?? "version unknown",
12046
+ report.orchestrator.command ? `path ${report.orchestrator.command}` : null,
12047
+ report.orchestrator.source ? `source ${report.orchestrator.source}` : null,
12048
+ report.orchestrator.workspacePackageVersion ? `workspace ${report.orchestrator.workspacePackageVersion}` : null,
12049
+ report.orchestrator.versionMismatch ? "source/install mismatch" : null
12050
+ ].filter((value) => Boolean(value));
12051
+ return `available (${parts.join(", ")})`;
12002
12052
  }
12003
12053
  function sourceLabel(source2) {
12004
12054
  return source2 === "env-file" ? ".env" : "environment";
@@ -12170,6 +12220,7 @@ function buildAdaptiveWorkRoutingRecommendation(options) {
12170
12220
  });
12171
12221
  const warnings = [
12172
12222
  "Recommendation-only dry run; companion does not launch or claim workers.",
12223
+ "Declare local runtimes with `snipara-companion workers local add ...` before expecting a local worker route.",
12173
12224
  ...preferredEndpointTypes.includes("local") ? ["Local endpoint preference requires a runtime catalog or gateway to confirm availability."] : []
12174
12225
  ];
12175
12226
  const routingCard = {
@@ -13555,11 +13606,7 @@ function normalizeAdapterTarget(value) {
13555
13606
  return "custom";
13556
13607
  }
13557
13608
  function normalizeAdapterValues(values) {
13558
- return [
13559
- ...new Set(
13560
- (values ?? []).flatMap((value) => value.split(",")).map((value) => value.trim()).filter(Boolean)
13561
- )
13562
- ];
13609
+ return [...new Set((values ?? []).map((value) => value.trim()).filter(Boolean))];
13563
13610
  }
13564
13611
  function normalizeConflictPosture(value, payload) {
13565
13612
  const normalized = value?.trim().toLowerCase().replace(/-/g, "_");
@@ -15040,6 +15087,15 @@ var FINAL_COMMIT_RETRY_SUMMARY_MAX_CHARS = 600;
15040
15087
  var DEFAULT_ADAPTIVE_ROUTING_CATALOG_LIMIT = 20;
15041
15088
  var DEFAULT_LOCAL_ORCHESTRATOR_TIMEOUT_MS = 8e3;
15042
15089
  var ADAPTIVE_ROUTING_POLICY_RELATIVE_PATH2 = path19.join(".snipara", "adaptive-routing.json");
15090
+ var MEMORY_DECISION_PRODUCER_ACTIONS = /* @__PURE__ */ new Set([
15091
+ "accept",
15092
+ "reject",
15093
+ "archive",
15094
+ "invalidate",
15095
+ "merge",
15096
+ "supersede",
15097
+ "verify"
15098
+ ]);
15043
15099
  var SHARED_CONTEXT_INTENT_PATTERN = /\b(standard|standards|convention|conventions|guideline|guidelines|best practice|best practices|policy|policies|compliance|compliant|security rules|team rules|style guide|playbook|checklist)\b/i;
15044
15100
  var DOCUMENT_SYNC_FORMATS = {
15045
15101
  ".adoc": { kind: "DOC", format: "adoc" },
@@ -16110,8 +16166,8 @@ function defaultWorkflowPlanOutputPath(preset, cwd = process.cwd()) {
16110
16166
  return path19.resolve(cwd, WORKFLOW_PLANS_RELATIVE_DIR, `${preset}-plan.json`);
16111
16167
  }
16112
16168
  function toProjectRelativePath2(absolutePath, cwd = process.cwd()) {
16113
- const relative7 = path19.relative(cwd, absolutePath);
16114
- return relative7 && !relative7.startsWith("..") ? relative7 : absolutePath;
16169
+ const relative8 = path19.relative(cwd, absolutePath);
16170
+ return relative8 && !relative8.startsWith("..") ? relative8 : absolutePath;
16115
16171
  }
16116
16172
  function buildWorkflowPlanScaffold(preset, options = {}) {
16117
16173
  const cwd = path19.resolve(options.cwd ?? process.cwd());
@@ -17004,6 +17060,30 @@ async function workflowProducerTriageCommand(options) {
17004
17060
  }
17005
17061
  async function workflowDecisionProducerMemoryCommand(options) {
17006
17062
  const action = options.action.trim();
17063
+ if (!MEMORY_DECISION_PRODUCER_ACTIONS.has(action)) {
17064
+ throw new Error(
17065
+ `Invalid memory decision action '${action}'. Use one of: ${[
17066
+ ...MEMORY_DECISION_PRODUCER_ACTIONS
17067
+ ].join(", ")}. Internal review item types such as review_queue_item are not human actions.`
17068
+ );
17069
+ }
17070
+ const reviewItem = await findMemoryReviewConnectorItem(options.memoryId);
17071
+ if (reviewItem) {
17072
+ const request2 = buildMemoryReviewDecisionRequest({
17073
+ ...reviewItem,
17074
+ action,
17075
+ recommendation: options.reviewerHint ?? reviewItem.recommendation,
17076
+ options: action === "verify" ? ["verify", "keep_pending", "invalidate"] : action === "invalidate" ? ["invalidate", "keep", "inspect"] : reviewItem.options.includes(action) ? reviewItem.options : [action, ...reviewItem.options.filter((option) => option !== action)]
17077
+ });
17078
+ const write2 = writeDecisionRequest(request2);
17079
+ if (options.json) {
17080
+ printJson2({ request: request2, write: write2, inheritedFrom: "memory reviews" });
17081
+ return;
17082
+ }
17083
+ console.log(`Decision request ${write2.status}: ${write2.requestId}`);
17084
+ console.log(request2.question);
17085
+ return;
17086
+ }
17007
17087
  const applyPath = action === "verify" ? "snipara_memory_verify" : action === "invalidate" ? "snipara_memory_invalidate" : "snipara_memory_resolve_queue_item";
17008
17088
  const request = buildDecisionRequest({
17009
17089
  producer: {
@@ -17031,6 +17111,20 @@ async function workflowDecisionProducerMemoryCommand(options) {
17031
17111
  }
17032
17112
  console.log(`Decision request ${write.status}: ${write.requestId}`);
17033
17113
  }
17114
+ async function findMemoryReviewConnectorItem(memoryId) {
17115
+ try {
17116
+ const result = await buildMemoryReviewConnector({
17117
+ limit: 50,
17118
+ includeCleanCandidates: false,
17119
+ includeDuplicates: false
17120
+ });
17121
+ return result.items.find(
17122
+ (item) => item.memoryId === memoryId || item.evidenceItem.ref === memoryId || item.evidenceItem.ref === `memory:${memoryId}`
17123
+ ) ?? null;
17124
+ } catch {
17125
+ return null;
17126
+ }
17127
+ }
17034
17128
  async function workflowDecisionProducerContextRiskCommand(options) {
17035
17129
  const kind = options.kind === "document_tombstone" ? "document_tombstone" : "unknown_registry_risk";
17036
17130
  const request = buildDecisionRequest({
@@ -19498,9 +19592,9 @@ function normalizeSyncDocumentsPayload(payload) {
19498
19592
  };
19499
19593
  }
19500
19594
  function toUploadPath(filePath, rootDir, prefix) {
19501
- const relative7 = path19.relative(rootDir, filePath).split(path19.sep).join("/");
19595
+ const relative8 = path19.relative(rootDir, filePath).split(path19.sep).join("/");
19502
19596
  const trimmedPrefix = prefix?.replace(/^\/+|\/+$/g, "");
19503
- return trimmedPrefix ? `${trimmedPrefix}/${relative7}` : relative7;
19597
+ return trimmedPrefix ? `${trimmedPrefix}/${relative8}` : relative8;
19504
19598
  }
19505
19599
  function collectDirectoryDocuments(options) {
19506
19600
  const rootDir = path19.resolve(options.dir);
@@ -22436,11 +22530,7 @@ function normalizeTarget(target) {
22436
22530
  return "custom";
22437
22531
  }
22438
22532
  function unique2(values) {
22439
- return [
22440
- ...new Set(
22441
- (values ?? []).flatMap((value) => value.split(",")).map((value) => value.trim()).filter(Boolean)
22442
- )
22443
- ];
22533
+ return [...new Set((values ?? []).map((value) => value.trim()).filter(Boolean))];
22444
22534
  }
22445
22535
  function normalizeTask(task) {
22446
22536
  const normalized = task?.trim();
@@ -22464,8 +22554,8 @@ function safeReadJson(filePath) {
22464
22554
  }
22465
22555
  }
22466
22556
  function relativePath(rootDir, filePath) {
22467
- const relative7 = path21.relative(rootDir, filePath);
22468
- return relative7 && !relative7.startsWith("..") ? relative7 : filePath;
22557
+ const relative8 = path21.relative(rootDir, filePath);
22558
+ return relative8 && !relative8.startsWith("..") ? relative8 : filePath;
22469
22559
  }
22470
22560
  function collectAgentReadinessLocalSignals(rootDir = process.cwd()) {
22471
22561
  const workflowPath = path21.join(rootDir, ".snipara", "workflow", "current.json");
@@ -22927,11 +23017,7 @@ function normalizeTask2(task) {
22927
23017
  return normalized ? normalized : void 0;
22928
23018
  }
22929
23019
  function unique3(values) {
22930
- return [
22931
- ...new Set(
22932
- (values ?? []).flatMap((value) => value.split(",")).map((value) => value.trim()).filter(Boolean)
22933
- )
22934
- ];
23020
+ return [...new Set((values ?? []).map((value) => value.trim()).filter(Boolean))];
22935
23021
  }
22936
23022
  function isRecord11(value) {
22937
23023
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -24285,6 +24371,15 @@ function writeReport2(outputPath, report) {
24285
24371
  `;
24286
24372
  fs23.writeFileSync(absolute, content, "utf8");
24287
24373
  }
24374
+ function defaultJsonOutputPath(cwd, report) {
24375
+ const timestamp = report.generatedAt.replace(/[:.]/g, "-");
24376
+ return path22.join(
24377
+ cwd,
24378
+ ".snipara",
24379
+ "lead-plans",
24380
+ `${timestamp}-${slug(report.task ?? "lead-plan", "lead-plan")}.json`
24381
+ );
24382
+ }
24288
24383
  async function leadPlanCommand(options) {
24289
24384
  const cwd = path22.resolve(options.dir ?? process.cwd());
24290
24385
  const report = buildCompanionEngineeringLeadPlanReport({
@@ -24292,11 +24387,26 @@ async function leadPlanCommand(options) {
24292
24387
  cwd,
24293
24388
  localSignals: collectAgentReadinessLocalSignals(cwd)
24294
24389
  });
24295
- if (options.output) {
24296
- writeReport2(options.output, report);
24390
+ const writtenOutput = options.output ?? (options.json ? defaultJsonOutputPath(cwd, report) : void 0);
24391
+ if (writtenOutput) {
24392
+ writeReport2(writtenOutput, report);
24297
24393
  }
24298
24394
  if (options.json) {
24299
- console.log(JSON.stringify(report, null, 2));
24395
+ console.log(
24396
+ JSON.stringify(
24397
+ {
24398
+ ...report,
24399
+ outputPath: writtenOutput ? path22.resolve(writtenOutput) : void 0,
24400
+ relativeOutputPath: writtenOutput ? path22.relative(cwd, path22.resolve(writtenOutput)) : void 0,
24401
+ nextCommand: writtenOutput ? `snipara-orchestrator team-sync gate --plan ${path22.relative(
24402
+ cwd,
24403
+ path22.resolve(writtenOutput)
24404
+ )} --json` : void 0
24405
+ },
24406
+ null,
24407
+ 2
24408
+ )
24409
+ );
24300
24410
  return;
24301
24411
  }
24302
24412
  console.log(formatCompanionEngineeringLeadPlanReport(report));
@@ -29763,7 +29873,7 @@ program.command("outcome-capture").description("Extract review-pending why/outco
29763
29873
  program.command("lead-plan").description("Create a fail-closed Companion Engineering Lead Plan").option("--task <task>", "Current task or work package summary").option(
29764
29874
  "--target <target>",
29765
29875
  "Target agent or ADE (codex|claude-code|cursor|orca|windsurf|custom)"
29766
- ).option("--changed-files <files...>", "Changed or relevant files").option("--context <refs...>", "Context references, decisions, docs, or source facts").option("--proof <proof...>", "Required proof gates or verification evidence").option("--acceptance <criteria...>", "Acceptance criteria for the delegated work").option("--risk <risks...>", "Known risks or caveats").option("--from-cockpit <file>", "Read a Project Health cockpit JSON export").option("--from-plan <file>", "Read a Companion or Project Health Engineering Lead Plan JSON").option("--reconcile", "Reconcile an imported lead plan against current local Companion signals").option("-d, --dir <directory>", "Project directory (default: current)").option("-o, --output <file>", "Write Markdown or JSON report").option("--json", "Print raw JSON").action(async (options) => {
29876
+ ).option("--changed-files <files...>", "Changed or relevant files").option("--context <refs...>", "Context references, decisions, docs, or source facts").option("--proof <proof...>", "Required proof gates or verification evidence").option("--acceptance <criteria...>", "Acceptance criteria for the delegated work").option("--risk <risks...>", "Known risks or caveats").option("--from-cockpit <file>", "Read a Project Health cockpit JSON export").option("--from-plan <file>", "Read a Companion or Project Health Engineering Lead Plan JSON").option("--reconcile", "Reconcile an imported lead plan against current local Companion signals").option("-d, --dir <directory>", "Project directory (default: current)").option("-o, --output <file>", "Write Markdown or JSON report").option("--out <file>", "Alias for --output").option("--json", "Print raw JSON").action(async (options) => {
29767
29877
  await leadPlanCommand({
29768
29878
  task: options.task,
29769
29879
  target: options.target,
@@ -29776,7 +29886,7 @@ program.command("lead-plan").description("Create a fail-closed Companion Enginee
29776
29886
  fromPlan: options.fromPlan,
29777
29887
  reconcile: Boolean(options.reconcile),
29778
29888
  dir: options.dir,
29779
- output: options.output,
29889
+ output: options.output ?? options.out,
29780
29890
  json: Boolean(options.json)
29781
29891
  });
29782
29892
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "snipara-companion",
3
- "version": "3.2.0",
3
+ "version": "3.2.1",
4
4
  "description": "Local-first CLI that asks your repo what breaks before an AI coding agent edits it.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {