runbrief 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +25 -3
  2. package/dist/cli.js +1501 -691
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -6,7 +6,7 @@ var __esm = (fn, res) => function __init() {
6
6
 
7
7
  // ../contracts/dist/index.js
8
8
  import { z } from "zod";
9
- var privacyModeSchema, enforcementSchema, skillStatusSchema, learningStatusSchema, findingSeveritySchema, prReadinessVerdictSchema, preCodingContextProofSchema, prReadinessCheckStatusSchema, prReadinessGateIdSchema, prReadinessGateStatusSchema, prReadinessStatusSchema, prReadinessDeliveryModeSchema, githubCheckConclusionSchema, agentSurfaceModeSchema, agentSurfaceCategorySchema, agentSurfaceCredentialStorageSchema, agentSurfaceRepoResolutionSchema, agentSurfaceTrustBoundarySchema, briefProofStateSchema, briefToolNameSchema, agentSurfaceContractSchema, briefRunPhaseSchema, briefRunContractSchema, briefProofContractSchema, repoRefSchema, scanStatsSchema, sourceDocumentSchema, ruleSchema, betaReadinessSchema, skillSchema, mapNodeSchema, reviewMemorySchema, wikiEvidenceItemSchema, wikiPageTypeSchema, wikiPageEvidenceMixSchema, wikiPageEntitySchema, wikiPageFreshnessSchema, wikiPageSourceProofSchema, wikiPageAgentBriefSchema, wikiPageQualitySchema, wikiPageSchema, prReadinessFindingSchema, prReadinessPullRequestSchema, prReadinessContextProofInputSchema, prReadinessCheckSchema, prReadinessGateResultSchema, prReadinessContractSchema, prReadinessReportRequestSchema, severityCountsSchema, prReadinessAnnotationSchema, prReadinessGithubCheckSchema, prReadinessReportSchema, prReadinessReportResponseSchema, repoScanSummarySchema, syncScanRequestSchema, teamGuidanceItemSchema, teamGuidanceDeliverySchema, contextPacketSchema, contextReceiptRequestSchema, missionRecipeIdSchema, missionWorkItemSchema, missionRuntimeEvidenceSchema, runtimeEvidencePullInstructionSchema, runtimeEvidencePacketSchema, missionHandoffValidationSchema, missionHandoffReviewReadinessSchema, missionHandoffReviewSchema, missionHandoffProofStatusSchema, missionHandoffValidationSummarySchema, missionHandoffArtifactEvidenceSchema, missionHandoffArtifactProofSchema, missionHandoffChangedFileAttributionSchema, missionHandoffProofBundleSchema, missionHandoffRequestSchema, evidenceRedactionFindingSchema, evidenceRedactionReportSchema, learningCreateRequestSchema, teamContextRequestSchema, teamContextResponseSchema, apiAcceptedResponseSchema;
9
+ var privacyModeSchema, loopRunStatusSchema, loopRunProofSchema, loopRunSummarySchema, createLoopRunRequestSchema, claimLoopRunRequestSchema, updateLoopRunRequestSchema, enforcementSchema, skillStatusSchema, learningStatusSchema, findingSeveritySchema, prReadinessVerdictSchema, preCodingContextProofSchema, prReadinessCheckStatusSchema, prReadinessGateIdSchema, prReadinessGateStatusSchema, prReadinessStatusSchema, prReadinessDeliveryModeSchema, githubCheckConclusionSchema, agentSurfaceModeSchema, agentSurfaceCategorySchema, agentSurfaceCredentialStorageSchema, agentSurfaceRepoResolutionSchema, agentSurfaceTrustBoundarySchema, briefProofStateSchema, briefToolNameSchema, agentSurfaceContractSchema, briefRunPhaseSchema, briefRunContractSchema, briefProofContractSchema, repoRefSchema, scanStatsSchema, sourceDocumentSchema, ruleSchema, betaReadinessSchema, skillSchema, mapNodeSchema, reviewMemorySchema, wikiEvidenceItemSchema, wikiPageTypeSchema, wikiPageEvidenceMixSchema, wikiPageEntitySchema, wikiPageFreshnessSchema, wikiPageSourceProofSchema, wikiPageAgentBriefSchema, wikiPageQualitySchema, wikiPageSchema, prReadinessFindingSchema, prReadinessPullRequestSchema, prReadinessContextProofInputSchema, prReadinessCheckSchema, prReadinessGateResultSchema, prReadinessContractSchema, prReadinessReportRequestSchema, severityCountsSchema, prReadinessAnnotationSchema, prReadinessGithubCheckSchema, prReadinessReportSchema, prReadinessReportResponseSchema, repoScanSummarySchema, syncScanRequestSchema, teamGuidanceItemSchema, teamGuidanceDeliverySchema, dependencyPackageSignalSchema, dependencyRemediationSchema, contextPacketSchema, contextReceiptRequestSchema, missionRecipeIdSchema, missionWorkItemSchema, missionRuntimeEvidenceSchema, runtimeEvidencePullInstructionSchema, runtimeEvidencePacketSchema, missionHandoffValidationSchema, missionHandoffReviewReadinessSchema, missionHandoffReviewSchema, missionHandoffProofStatusSchema, missionHandoffValidationSummarySchema, missionHandoffArtifactEvidenceSchema, missionHandoffArtifactProofSchema, missionHandoffChangedFileAttributionSchema, missionHandoffProofBundleSchema, missionHandoffRequestSchema, evidenceRedactionFindingSchema, evidenceRedactionReportSchema, learningCreateRequestSchema, teamContextRequestSchema, teamContextResponseSchema, apiAcceptedResponseSchema;
10
10
  var init_dist = __esm({
11
11
  "../contracts/dist/index.js"() {
12
12
  "use strict";
@@ -15,6 +15,56 @@ var init_dist = __esm({
15
15
  "cloud-sync-derived",
16
16
  "team-cloud"
17
17
  ]);
18
+ loopRunStatusSchema = z.enum([
19
+ "queued",
20
+ "running",
21
+ "succeeded",
22
+ "failed",
23
+ "cancelled"
24
+ ]);
25
+ loopRunProofSchema = z.record(z.string(), z.unknown());
26
+ loopRunSummarySchema = z.object({
27
+ id: z.string().uuid(),
28
+ workspaceId: z.string().uuid(),
29
+ loopId: z.string().uuid(),
30
+ loopName: z.string().min(1),
31
+ loopBody: z.string().min(1),
32
+ guardrails: z.array(z.string().min(1)).max(30),
33
+ loopVersion: z.number().int().positive(),
34
+ task: z.string().min(1).max(12e3),
35
+ repoFullName: z.string().min(1).max(240).optional(),
36
+ autonomy: z.enum(["read_only", "repo_edit", "validate", "handoff"]),
37
+ status: loopRunStatusSchema,
38
+ attemptCount: z.number().int().nonnegative(),
39
+ runnerId: z.string().min(1).max(160).nullable(),
40
+ leaseExpiresAt: z.string().datetime().nullable(),
41
+ lastEvent: z.string().max(2e3).nullable(),
42
+ resultSummary: z.string().max(12e3).nullable(),
43
+ proof: loopRunProofSchema,
44
+ errorMessage: z.string().max(4e3).nullable(),
45
+ createdAt: z.string().datetime(),
46
+ startedAt: z.string().datetime().nullable(),
47
+ completedAt: z.string().datetime().nullable()
48
+ });
49
+ createLoopRunRequestSchema = z.object({
50
+ loopId: z.string().uuid(),
51
+ task: z.string().trim().min(1).max(12e3),
52
+ repoFullName: z.string().trim().min(1).max(240).optional(),
53
+ idempotencyKey: z.string().trim().min(16).max(160)
54
+ });
55
+ claimLoopRunRequestSchema = z.object({
56
+ runnerId: z.string().trim().min(1).max(160),
57
+ leaseSeconds: z.number().int().min(30).max(900).default(180)
58
+ });
59
+ updateLoopRunRequestSchema = z.object({
60
+ runnerId: z.string().trim().min(1).max(160),
61
+ status: loopRunStatusSchema,
62
+ lastEvent: z.string().trim().max(2e3).optional(),
63
+ resultSummary: z.string().trim().max(12e3).optional(),
64
+ proof: loopRunProofSchema.optional(),
65
+ errorMessage: z.string().trim().max(4e3).optional(),
66
+ leaseSeconds: z.number().int().min(30).max(900).optional()
67
+ });
18
68
  enforcementSchema = z.enum([
19
69
  "advisory",
20
70
  "warn",
@@ -621,6 +671,46 @@ var init_dist = __esm({
621
671
  applied: z.array(teamGuidanceItemSchema).max(100),
622
672
  omittedCount: z.number().int().nonnegative()
623
673
  });
674
+ dependencyPackageSignalSchema = z.object({
675
+ name: z.string().min(1).max(160),
676
+ changeKind: z.enum([
677
+ "patch_or_minor",
678
+ "major",
679
+ "added_or_overridden",
680
+ "unknown"
681
+ ]),
682
+ manifestPaths: z.array(z.string().min(1).max(500)).max(20),
683
+ lockfilePaths: z.array(z.string().min(1).max(500)).max(20),
684
+ evidence: z.array(z.string().min(1).max(120)).max(10)
685
+ });
686
+ dependencyRemediationSchema = z.object({
687
+ id: z.string().min(1).max(120),
688
+ status: z.enum(["ready", "needs_evidence"]),
689
+ packageManager: z.enum([
690
+ "pnpm",
691
+ "npm",
692
+ "yarn",
693
+ "bun",
694
+ "pip",
695
+ "bundler",
696
+ "go",
697
+ "cargo",
698
+ "unknown"
699
+ ]),
700
+ manifestFiles: z.array(z.string().min(1).max(500)).max(30),
701
+ lockfiles: z.array(z.string().min(1).max(500)).max(20),
702
+ workspaceFiles: z.array(z.string().min(1).max(500)).max(20),
703
+ changedDependencyFiles: z.array(z.string().min(1).max(500)).max(30),
704
+ directDependencyFiles: z.array(z.string().min(1).max(500)).max(30),
705
+ packageSignals: z.array(dependencyPackageSignalSchema).max(20),
706
+ graphCommands: z.array(z.string().min(1).max(500)).max(20),
707
+ remediationCommands: z.array(z.string().min(1).max(500)).max(20),
708
+ validationCommands: z.array(z.string().min(1).max(500)).max(20),
709
+ residualRiskChecks: z.array(z.string().min(1).max(800)).max(20),
710
+ handoffRequirements: z.array(z.string().min(1).max(800)).max(20),
711
+ summary: z.string().max(1200),
712
+ confidence: z.number().min(0).max(100)
713
+ });
624
714
  contextPacketSchema = z.object({
625
715
  packetId: z.string().min(1),
626
716
  createdAt: z.string().datetime(),
@@ -643,7 +733,8 @@ var init_dist = __esm({
643
733
  appliedTeamGuidanceCount: z.number().int().nonnegative(),
644
734
  suggestedTestCount: z.number().int().nonnegative()
645
735
  }).optional(),
646
- teamGuidance: teamGuidanceDeliverySchema.optional()
736
+ teamGuidance: teamGuidanceDeliverySchema.optional(),
737
+ dependencyRemediation: dependencyRemediationSchema.optional()
647
738
  });
648
739
  contextReceiptRequestSchema = z.object({
649
740
  workspaceId: z.string().uuid().optional(),
@@ -5296,22 +5387,22 @@ function likelyRuleLine(line) {
5296
5387
  return hasStrongRule || hasOperationalRule;
5297
5388
  }
5298
5389
  function scopeFromPath(sourcePath, kind) {
5299
- const path20 = sourcePath.replaceAll("\\", "/");
5300
- const directoryScope = scopedDirectory(path20);
5301
- if (directoryScope && (path20.endsWith("/AGENTS.md") || path20.endsWith("/CLAUDE.md")))
5390
+ const path21 = sourcePath.replaceAll("\\", "/");
5391
+ const directoryScope = scopedDirectory(path21);
5392
+ if (directoryScope && (path21.endsWith("/AGENTS.md") || path21.endsWith("/CLAUDE.md")))
5302
5393
  return [directoryScope];
5303
- if (path20.includes("client/") || kind === "frontend")
5394
+ if (path21.includes("client/") || kind === "frontend")
5304
5395
  return ["client/**", "packages/ui/**"];
5305
- if (path20.includes("server/") || kind === "persistence")
5396
+ if (path21.includes("server/") || kind === "persistence")
5306
5397
  return ["server/**"];
5307
- if (path20.includes(".github/") || kind === "workflow")
5398
+ if (path21.includes(".github/") || kind === "workflow")
5308
5399
  return ["**/*"];
5309
- if (path20.includes("test") || kind === "testing")
5400
+ if (path21.includes("test") || kind === "testing")
5310
5401
  return ["**/*.test.*", "**/*.spec.*", "tests/**"];
5311
5402
  return ["**/*"];
5312
5403
  }
5313
- function scopedDirectory(path20) {
5314
- const parts = path20.split("/");
5404
+ function scopedDirectory(path21) {
5405
+ const parts = path21.split("/");
5315
5406
  if (parts.length <= 1)
5316
5407
  return null;
5317
5408
  const directory = parts.slice(0, -1).join("/");
@@ -7491,15 +7582,15 @@ function criticalPathBoost(filePath) {
7491
7582
  return 0;
7492
7583
  }
7493
7584
  function compactMetadata(metadata) {
7494
- const compact = {};
7585
+ const compact2 = {};
7495
7586
  for (const [key, value] of Object.entries(metadata)) {
7496
7587
  if (Array.isArray(value) && value.length === 0)
7497
7588
  continue;
7498
7589
  if (value === "" || value === null)
7499
7590
  continue;
7500
- compact[key] = value;
7591
+ compact2[key] = value;
7501
7592
  }
7502
- return Object.keys(compact).length > 0 ? compact : void 0;
7593
+ return Object.keys(compact2).length > 0 ? compact2 : void 0;
7503
7594
  }
7504
7595
  function isCodeFilePath(filePath) {
7505
7596
  return /\.(?:[tj]sx?|py|go|rb|sql|graphql|gql)$/.test(filePath.replaceAll("\\", "/"));
@@ -8605,7 +8696,7 @@ function agentPacketPrompt(input) {
8605
8696
  ].filter(Boolean).join(" ");
8606
8697
  }
8607
8698
  function inspectReasonForNode(node) {
8608
- const path20 = pathKey(node);
8699
+ const path21 = pathKey(node);
8609
8700
  const systems = metadataStrings2(node, "systems");
8610
8701
  const tables = metadataStrings2(node, "tables");
8611
8702
  const access = metadataStrings2(node, "accessControls");
@@ -8625,7 +8716,7 @@ function inspectReasonForNode(node) {
8625
8716
  return "Entrypoint that frames runtime flow.";
8626
8717
  if (node.type === "component")
8627
8718
  return "UI component surface in mapped scope.";
8628
- return `Mapped ${node.type} node for ${path20}.`;
8719
+ return `Mapped ${node.type} node for ${path21}.`;
8629
8720
  }
8630
8721
  function strongestEntityEvidenceKind(nodes, kind, name) {
8631
8722
  const matchingNodes = nodes.filter((node) => metadataStrings2(node, metadataKeyForEntity(kind)).includes(name));
@@ -8698,17 +8789,17 @@ function topFacet(nodes, key, limit) {
8698
8789
  function topAreaFacet(nodes, limit) {
8699
8790
  const areas = /* @__PURE__ */ new Map();
8700
8791
  for (const node of nodes) {
8701
- const path20 = pathKey(node);
8702
- const prefix = areaPrefix(path20);
8792
+ const path21 = pathKey(node);
8793
+ const prefix = areaPrefix(path21);
8703
8794
  const entry = areas.get(prefix) ?? {
8704
8795
  paths: /* @__PURE__ */ new Set(),
8705
8796
  codeSurfaceCount: 0,
8706
8797
  systems: /* @__PURE__ */ new Set()
8707
8798
  };
8708
- if (!entry.paths.has(path20) && CODE_SURFACE_TYPES.has(node.type)) {
8799
+ if (!entry.paths.has(path21) && CODE_SURFACE_TYPES.has(node.type)) {
8709
8800
  entry.codeSurfaceCount += 1;
8710
8801
  }
8711
- entry.paths.add(path20);
8802
+ entry.paths.add(path21);
8712
8803
  for (const system of metadataStrings2(node, "systems")) {
8713
8804
  entry.systems.add(system);
8714
8805
  }
@@ -10564,7 +10655,7 @@ function wikiBuildPipeline(scan, pages, operationalAtlas, loopPlaybooks, refresh
10564
10655
  },
10565
10656
  {
10566
10657
  id: "publish",
10567
- title: "Publish for loops",
10658
+ title: "Share with loops",
10568
10659
  status: "ready",
10569
10660
  summary: "Mission planning and context packets can cite wiki pages as source-grounded operating context.",
10570
10661
  evidence: ["brief.wiki_outline", "brief.context_for_task"]
@@ -10992,7 +11083,7 @@ function loopPlaybookSpec(loopId) {
10992
11083
  if (loopId === "session_library") {
10993
11084
  return {
10994
11085
  title: "Session library loop",
10995
- summary: "Promote proven agent runs into reusable team skills, loops, styles, and run cards without publishing raw transcripts by default.",
11086
+ summary: "Promote proven agent runs into reusable team skills, loops, styles, and run cards without sharing raw transcripts by default.",
10996
11087
  tools: [
10997
11088
  "brief.agent_session_insights",
10998
11089
  "brief.value_report",
@@ -11013,7 +11104,7 @@ function loopPlaybookSpec(loopId) {
11013
11104
  ],
11014
11105
  privacyBoundary: [
11015
11106
  ...common.privacyBoundary,
11016
- "Publish concise run cards and styles, not raw private sessions, unless explicitly approved."
11107
+ "Share concise run cards and styles, not raw private sessions, unless explicitly approved."
11017
11108
  ],
11018
11109
  systemHints: ["github", "ci"],
11019
11110
  queryHint: "agent session receipts run cards skills loops styles",
@@ -11204,7 +11295,7 @@ function wikiUsageReceipts(pages, options2) {
11204
11295
  loopId: "session_library",
11205
11296
  tool: "brief.mission_plan",
11206
11297
  pageIds: pageIds("Session Library", "Review Memory", "Team Standards", "Maintainers"),
11207
- reason: "Session library loops need approved examples, privacy decisions, maintainer context, and reusable team standards before publishing run cards or skills."
11298
+ reason: "Session library loops need approved examples, privacy decisions, maintainer context, and reusable team standards before sharing run cards or skills."
11208
11299
  },
11209
11300
  {
11210
11301
  id: `wiki_use_${contentHash("docs:overview:structure")}`,
@@ -11387,9 +11478,9 @@ var init_contextWiki = __esm({
11387
11478
  });
11388
11479
 
11389
11480
  // ../core/dist/pathIntent.js
11390
- function namedCodePathIntentScore(path20, task) {
11481
+ function namedCodePathIntentScore(path21, task) {
11391
11482
  const normalizedTask = task.toLowerCase().replace(/[_-]+/g, " ");
11392
- const normalizedPath = (path20.split("#", 1)[0] ?? path20).toLowerCase();
11483
+ const normalizedPath = (path21.split("#", 1)[0] ?? path21).toLowerCase();
11393
11484
  let score = 0;
11394
11485
  const namedTargets = [
11395
11486
  [/\breview diff\b/, /diffreview|diff[-_]review/],
@@ -11535,9 +11626,476 @@ var init_pathIntent = __esm({
11535
11626
  }
11536
11627
  });
11537
11628
 
11629
+ // ../core/dist/dependencyRemediation.js
11630
+ import path6 from "node:path";
11631
+ function buildDependencyRemediationPlan(repoRoot, request) {
11632
+ const files = walkFiles(repoRoot, request.maxFiles ?? 6e3);
11633
+ const manifests = files.filter(isDependencyManifest);
11634
+ const lockfiles = files.filter(isDependencyLockfile);
11635
+ const workspaceFiles = files.filter(isDependencyWorkspaceFile);
11636
+ const changedDependencyFiles = unique((request.changedFiles ?? []).filter(isDependencyFile));
11637
+ const packageManager = inferPackageManager(repoRoot, manifests, lockfiles);
11638
+ const packageSignalInput = {
11639
+ task: request.task,
11640
+ manifests,
11641
+ changedDependencyFiles
11642
+ };
11643
+ if (request.diff)
11644
+ packageSignalInput.diff = request.diff;
11645
+ const packageSignals = packageSignalsFor(repoRoot, packageSignalInput);
11646
+ const directDependencyFiles = unique(packageSignals.flatMap((signal2) => signal2.manifestPaths));
11647
+ const packageNames = packageSignals.map((signal2) => signal2.name);
11648
+ const validationCommands2 = validationCommandsFor2(repoRoot, packageManager);
11649
+ const graphCommands = dependencyGraphCommandsFor(packageManager, packageNames);
11650
+ const remediationCommands = remediationCommandsFor(packageManager, packageNames);
11651
+ const residualRiskChecks = residualRiskChecksFor(packageManager, packageNames, changedDependencyFiles);
11652
+ const confidence = confidenceFor({
11653
+ changedDependencyFiles,
11654
+ packageSignals,
11655
+ lockfiles,
11656
+ validationCommands: validationCommands2
11657
+ });
11658
+ return {
11659
+ id: `dep_${contentHash([
11660
+ request.task,
11661
+ packageManager,
11662
+ changedDependencyFiles.join(","),
11663
+ packageNames.join(",")
11664
+ ].join(":"))}`,
11665
+ status: changedDependencyFiles.length > 0 || packageSignals.length > 0 ? "ready" : "needs_evidence",
11666
+ packageManager,
11667
+ manifestFiles: manifests.slice(0, 20),
11668
+ lockfiles,
11669
+ workspaceFiles,
11670
+ changedDependencyFiles,
11671
+ directDependencyFiles,
11672
+ packageSignals,
11673
+ graphCommands,
11674
+ remediationCommands,
11675
+ validationCommands: validationCommands2,
11676
+ residualRiskChecks,
11677
+ handoffRequirements: [
11678
+ "List advisory ids or scanner finding ids that were fixed.",
11679
+ "Name packages still present in the lockfile with vulnerable versions, or state that the scan/audit is clean.",
11680
+ "Explain any major-version upgrades, overrides, peer dependency changes, skipped advisories, or remaining advisory ids.",
11681
+ "Attach validation commands and CI follow-up results before marking the security mission done."
11682
+ ],
11683
+ summary: summaryFor3({
11684
+ packageManager,
11685
+ manifests,
11686
+ lockfiles,
11687
+ changedDependencyFiles,
11688
+ packageSignals,
11689
+ validationCommands: validationCommands2
11690
+ }),
11691
+ confidence
11692
+ };
11693
+ }
11694
+ function isDependencyRemediationTask(task, files = []) {
11695
+ const normalized = task.toLowerCase();
11696
+ const hasSecuritySignal = /\b(dependabot|vulnerab\w*|cve-|ghsa-|security advisory|security finding|audit finding|osv|sca|dependency-review)\b/.test(normalized);
11697
+ const hasDependencyChangeSignal = /\b(dependency|dependencies|lockfile|package upgrade|package update|package bump|transitive|override|resolution)\b/.test(normalized);
11698
+ return hasSecuritySignal || hasDependencyChangeSignal && files.some(isDependencyFile);
11699
+ }
11700
+ function isDependencyFile(filePath) {
11701
+ const normalized = filePath.replaceAll("\\", "/");
11702
+ return dependencyFilePatterns.some((pattern) => normalized === pattern || normalized.endsWith(`/${pattern}`) || pattern === "package.json" && normalized.endsWith("/package.json"));
11703
+ }
11704
+ function isDependencyLockfile(filePath) {
11705
+ const normalized = filePath.replaceAll("\\", "/");
11706
+ return normalized === "pnpm-lock.yaml" || normalized.endsWith("/pnpm-lock.yaml") || normalized === "package-lock.json" || normalized.endsWith("/package-lock.json") || normalized === "yarn.lock" || normalized.endsWith("/yarn.lock") || normalized === "bun.lock" || normalized.endsWith("/bun.lock") || normalized === "npm-shrinkwrap.json" || normalized.endsWith("/npm-shrinkwrap.json") || normalized === "poetry.lock" || normalized.endsWith("/poetry.lock") || normalized === "Pipfile.lock" || normalized.endsWith("/Pipfile.lock") || normalized === "Gemfile.lock" || normalized.endsWith("/Gemfile.lock") || normalized === "go.sum" || normalized.endsWith("/go.sum") || normalized === "Cargo.lock" || normalized.endsWith("/Cargo.lock");
11707
+ }
11708
+ function isDependencyManifest(filePath) {
11709
+ const normalized = filePath.replaceAll("\\", "/");
11710
+ return normalized === "package.json" || normalized.endsWith("/package.json") || normalized === "requirements.txt" || normalized.endsWith("/requirements.txt") || normalized === "pyproject.toml" || normalized.endsWith("/pyproject.toml") || normalized === "Gemfile" || normalized.endsWith("/Gemfile") || normalized === "go.mod" || normalized.endsWith("/go.mod") || normalized === "Cargo.toml" || normalized.endsWith("/Cargo.toml");
11711
+ }
11712
+ function isDependencyWorkspaceFile(filePath) {
11713
+ const normalized = filePath.replaceAll("\\", "/");
11714
+ return normalized === "pnpm-workspace.yaml" || normalized.endsWith("/pnpm-workspace.yaml") || normalized === "lerna.json" || normalized.endsWith("/lerna.json") || normalized === "turbo.json" || normalized.endsWith("/turbo.json") || normalized === "nx.json" || normalized.endsWith("/nx.json");
11715
+ }
11716
+ function inferPackageManager(repoRoot, manifests, lockfiles) {
11717
+ const rootPackageJson = readPackageJson(repoRoot, "package.json");
11718
+ const packageManager = rootPackageJson?.packageManager;
11719
+ if (typeof packageManager === "string") {
11720
+ if (packageManager.startsWith("pnpm@"))
11721
+ return "pnpm";
11722
+ if (packageManager.startsWith("yarn@"))
11723
+ return "yarn";
11724
+ if (packageManager.startsWith("npm@"))
11725
+ return "npm";
11726
+ if (packageManager.startsWith("bun@"))
11727
+ return "bun";
11728
+ }
11729
+ if (lockfiles.some((file) => file.endsWith("pnpm-lock.yaml")))
11730
+ return "pnpm";
11731
+ if (lockfiles.some((file) => file.endsWith("yarn.lock")))
11732
+ return "yarn";
11733
+ if (lockfiles.some((file) => file.endsWith("package-lock.json"))) {
11734
+ return "npm";
11735
+ }
11736
+ if (lockfiles.some((file) => file.endsWith("bun.lock")))
11737
+ return "bun";
11738
+ if (manifests.some((file) => file.endsWith("requirements.txt") || file.endsWith("pyproject.toml") || file.endsWith("Pipfile"))) {
11739
+ return "pip";
11740
+ }
11741
+ if (manifests.some((file) => file.endsWith("Gemfile")))
11742
+ return "bundler";
11743
+ if (manifests.some((file) => file.endsWith("go.mod")))
11744
+ return "go";
11745
+ if (manifests.some((file) => file.endsWith("Cargo.toml")))
11746
+ return "cargo";
11747
+ return "unknown";
11748
+ }
11749
+ function packageSignalsFor(repoRoot, input) {
11750
+ const manifestDeps = dependencyNamesByManifest(repoRoot, input.manifests);
11751
+ const explicitPackageNames = unique([
11752
+ ...packagesMentionedInText(input.task),
11753
+ ...packagesMentionedInDiff(input.diff ?? "")
11754
+ ]).filter((name) => !looksLikeVersionOrAdvisory(name));
11755
+ const fallbackPackageNames = input.changedDependencyFiles.flatMap((file) => packagesFromChangedDependencyFile(repoRoot, file));
11756
+ const packageNames = unique(explicitPackageNames.length > 0 ? explicitPackageNames : fallbackPackageNames).filter((name) => !looksLikeVersionOrAdvisory(name));
11757
+ return packageNames.slice(0, 16).map((name) => ({
11758
+ name,
11759
+ changeKind: changeKindFor(name, input.diff ?? ""),
11760
+ manifestPaths: manifestsForPackage(name, manifestDeps),
11761
+ lockfilePaths: input.changedDependencyFiles.filter(isDependencyLockfile),
11762
+ evidence: evidenceForPackage(name, input.task, input.diff ?? "")
11763
+ }));
11764
+ }
11765
+ function dependencyNamesByManifest(repoRoot, manifests) {
11766
+ const byPackage = /* @__PURE__ */ new Map();
11767
+ for (const manifest of manifests) {
11768
+ if (!manifest.endsWith("package.json"))
11769
+ continue;
11770
+ const parsed = readPackageJson(repoRoot, manifest);
11771
+ if (!parsed)
11772
+ continue;
11773
+ for (const name of packageJsonDependencyNames(parsed)) {
11774
+ const current = byPackage.get(name) ?? [];
11775
+ current.push(manifest);
11776
+ byPackage.set(name, current);
11777
+ }
11778
+ }
11779
+ return byPackage;
11780
+ }
11781
+ function packageJsonDependencyNames(parsed) {
11782
+ return unique([
11783
+ "dependencies",
11784
+ "devDependencies",
11785
+ "optionalDependencies",
11786
+ "peerDependencies",
11787
+ "overrides",
11788
+ "resolutions",
11789
+ "pnpm"
11790
+ ].flatMap((field) => nestedDependencyNames(parsed[field])));
11791
+ }
11792
+ function nestedDependencyNames(value) {
11793
+ if (!value || typeof value !== "object")
11794
+ return [];
11795
+ return Object.entries(value).flatMap(([key, nested]) => [
11796
+ looksLikePackageName(key) ? key : "",
11797
+ ...nestedDependencyNames(nested)
11798
+ ]);
11799
+ }
11800
+ function packagesMentionedInText(text) {
11801
+ const names = [];
11802
+ const quoted = text.matchAll(/[`'"](@?[a-z0-9][a-z0-9._-]*(?:\/[a-z0-9._-]+)?)[`'"]/gi);
11803
+ for (const match of quoted)
11804
+ names.push(match[1] ?? "");
11805
+ const advisoryContext = text.matchAll(/\b(?:package|dependency|upgrade|update|override|transitive|vulnerable|dependabot|cve|ghsa|audit)\s+(@?[a-z0-9][a-z0-9._-]*(?:\/[a-z0-9._-]+)?)/gi);
11806
+ for (const match of advisoryContext)
11807
+ names.push(match[1] ?? "");
11808
+ const atVersion = text.matchAll(/\b(@?[a-z0-9][a-z0-9._-]*(?:\/[a-z0-9._-]+)?)@(?:\^|~)?\d/gi);
11809
+ for (const match of atVersion)
11810
+ names.push(match[1] ?? "");
11811
+ if (/\b(dependabot|cve-|ghsa-|audit|vulnerab|advisory)\b/i.test(text)) {
11812
+ const affectedPackageContext = text.matchAll(/\b(?:for|affects?|in)\s+(@?[a-z0-9][a-z0-9._-]*(?:\/[a-z0-9._-]+)?)/gi);
11813
+ for (const match of affectedPackageContext)
11814
+ names.push(match[1] ?? "");
11815
+ }
11816
+ return unique(names.map(cleanPackageName).filter(looksLikePackageName).filter((name) => !genericDependencyWords.has(name.toLowerCase())));
11817
+ }
11818
+ function packagesMentionedInDiff(diff) {
11819
+ const names = [];
11820
+ for (const line of diff.split(/\r?\n/)) {
11821
+ if (!/^[+-]/.test(line) || /^[+-]{3}/.test(line))
11822
+ continue;
11823
+ const packageJsonMatch = line.match(/"(@?[a-z0-9][a-z0-9._-]*(?:\/[a-z0-9._-]+)?)"\s*:/i);
11824
+ if (packageJsonMatch?.[1])
11825
+ names.push(packageJsonMatch[1]);
11826
+ const pnpmMatch = line.match(/^\s*[+-]\s{0,8}(@?[a-z0-9][a-z0-9._-]*(?:\/[a-z0-9._-]+)?)@(?:\^|~)?\d/i);
11827
+ if (pnpmMatch?.[1])
11828
+ names.push(pnpmMatch[1]);
11829
+ const lockPathMatch = line.match(/\/(@?[a-z0-9][a-z0-9._-]*(?:\/[a-z0-9._-]+)?)@(?:\^|~)?\d/i);
11830
+ if (lockPathMatch?.[1])
11831
+ names.push(lockPathMatch[1]);
11832
+ }
11833
+ return unique(names.map(cleanPackageName).filter(looksLikePackageName).filter((name) => !genericDependencyWords.has(name.toLowerCase())));
11834
+ }
11835
+ function packagesFromChangedDependencyFile(repoRoot, filePath) {
11836
+ if (!filePath.endsWith("package.json"))
11837
+ return [];
11838
+ const parsed = readPackageJson(repoRoot, filePath);
11839
+ return parsed ? packageJsonDependencyNames(parsed).slice(0, 20) : [];
11840
+ }
11841
+ function readPackageJson(repoRoot, filePath) {
11842
+ const content = safeRead(path6.join(repoRoot, filePath), 4e5);
11843
+ if (!content)
11844
+ return null;
11845
+ try {
11846
+ const parsed = JSON.parse(content);
11847
+ return parsed && typeof parsed === "object" ? parsed : null;
11848
+ } catch {
11849
+ return null;
11850
+ }
11851
+ }
11852
+ function changeKindFor(packageName, diff) {
11853
+ const escaped = packageName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
11854
+ const removedVersions = [
11855
+ ...diff.matchAll(new RegExp(`^-.*${escaped}.*?(\\d+\\.\\d+\\.\\d+)`, "gim"))
11856
+ ].map((match) => match[1] ?? "");
11857
+ const addedVersions = [
11858
+ ...diff.matchAll(new RegExp(`^\\+.*${escaped}.*?(\\d+\\.\\d+\\.\\d+)`, "gim"))
11859
+ ].map((match) => match[1] ?? "");
11860
+ const removed = removedVersions[0];
11861
+ const added = addedVersions[0];
11862
+ if (removed && added) {
11863
+ const [removedMajor] = removed.split(".");
11864
+ const [addedMajor] = added.split(".");
11865
+ return removedMajor && addedMajor && removedMajor !== addedMajor ? "major" : "patch_or_minor";
11866
+ }
11867
+ if (added && !removed)
11868
+ return "added_or_overridden";
11869
+ return "unknown";
11870
+ }
11871
+ function manifestsForPackage(packageName, manifestDeps) {
11872
+ return unique([
11873
+ ...manifestDeps.get(packageName) ?? [],
11874
+ ...[...manifestDeps.entries()].filter(([name]) => name.endsWith(`/${packageName}`)).flatMap(([, manifests]) => manifests)
11875
+ ]).slice(0, 8);
11876
+ }
11877
+ function evidenceForPackage(packageName, task, diff) {
11878
+ const evidence = [];
11879
+ if (task.toLowerCase().includes(packageName.toLowerCase())) {
11880
+ evidence.push("mentioned_in_task");
11881
+ }
11882
+ if (diff.toLowerCase().includes(packageName.toLowerCase())) {
11883
+ evidence.push("mentioned_in_diff");
11884
+ }
11885
+ if (evidence.length === 0)
11886
+ evidence.push("manifest_candidate");
11887
+ return evidence;
11888
+ }
11889
+ function validationCommandsFor2(repoRoot, packageManager) {
11890
+ const scripts = packageScripts(repoRoot);
11891
+ const run = packageRunCommand(packageManager);
11892
+ return unique([
11893
+ ...auditCommandsFor(packageManager),
11894
+ ...scriptCommand(scripts, "check", run),
11895
+ ...scriptCommand(scripts, "test", run),
11896
+ ...scriptCommand(scripts, "test:client", run),
11897
+ ...scriptCommand(scripts, "test:e2e:smoke", run),
11898
+ ...scriptCommand(scripts, "build", run)
11899
+ ]).slice(0, 10);
11900
+ }
11901
+ function packageScripts(repoRoot) {
11902
+ const parsed = readPackageJson(repoRoot, "package.json");
11903
+ const scripts = parsed?.scripts;
11904
+ return scripts && typeof scripts === "object" ? Object.fromEntries(Object.entries(scripts).filter((entry) => typeof entry[1] === "string")) : {};
11905
+ }
11906
+ function scriptCommand(scripts, scriptName, runCommand) {
11907
+ return scripts[scriptName] ? [`${runCommand} ${scriptName}`] : [];
11908
+ }
11909
+ function packageRunCommand(packageManager) {
11910
+ switch (packageManager) {
11911
+ case "npm":
11912
+ return "npm run";
11913
+ case "yarn":
11914
+ return "yarn";
11915
+ case "bun":
11916
+ return "bun run";
11917
+ default:
11918
+ return "pnpm";
11919
+ }
11920
+ }
11921
+ function auditCommandsFor(packageManager) {
11922
+ switch (packageManager) {
11923
+ case "pnpm":
11924
+ return ["pnpm audit --prod"];
11925
+ case "npm":
11926
+ return ["npm audit --omit=dev"];
11927
+ case "yarn":
11928
+ return ["yarn npm audit --environment production"];
11929
+ case "bun":
11930
+ return ["bun audit"];
11931
+ case "pip":
11932
+ return ["pip-audit"];
11933
+ case "bundler":
11934
+ return ["bundle audit check"];
11935
+ case "go":
11936
+ return ["govulncheck ./..."];
11937
+ case "cargo":
11938
+ return ["cargo audit"];
11939
+ default:
11940
+ return [];
11941
+ }
11942
+ }
11943
+ function dependencyGraphCommandsFor(packageManager, packageNames) {
11944
+ const selected = packageNames.slice(0, 5);
11945
+ if (selected.length === 0)
11946
+ return [];
11947
+ switch (packageManager) {
11948
+ case "pnpm":
11949
+ return selected.map((name) => `pnpm why ${name}`);
11950
+ case "npm":
11951
+ return selected.map((name) => `npm explain ${name}`);
11952
+ case "yarn":
11953
+ return selected.map((name) => `yarn why ${name}`);
11954
+ case "bun":
11955
+ return selected.map((name) => `bun pm why ${name}`);
11956
+ case "go":
11957
+ return selected.map((name) => `go mod why -m ${name}`);
11958
+ case "cargo":
11959
+ return selected.map((name) => `cargo tree -i ${name}`);
11960
+ default:
11961
+ return [];
11962
+ }
11963
+ }
11964
+ function remediationCommandsFor(packageManager, packageNames) {
11965
+ const selected = packageNames.slice(0, 5);
11966
+ if (selected.length === 0)
11967
+ return [];
11968
+ switch (packageManager) {
11969
+ case "pnpm":
11970
+ return selected.map((name) => `pnpm up ${name} --latest --interactive=false`);
11971
+ case "npm":
11972
+ return selected.map((name) => `npm update ${name}`);
11973
+ case "yarn":
11974
+ return selected.map((name) => `yarn up ${name}`);
11975
+ case "bun":
11976
+ return selected.map((name) => `bun update ${name}`);
11977
+ case "go":
11978
+ return selected.map((name) => `go get ${name}@latest`);
11979
+ case "cargo":
11980
+ return selected.map((name) => `cargo update -p ${name}`);
11981
+ default:
11982
+ return [];
11983
+ }
11984
+ }
11985
+ function residualRiskChecksFor(packageManager, packageNames, changedDependencyFiles) {
11986
+ return unique([
11987
+ packageNames.length > 0 ? `Confirm vulnerable versions are gone for: ${packageNames.slice(0, 8).join(", ")}.` : "Name each advisory/package from the scanner before editing.",
11988
+ changedDependencyFiles.some(isDependencyLockfile) ? "Inspect the lockfile diff for unexpected package churn outside the advisory path." : "Regenerate and inspect the lockfile if the fix changes dependency resolution.",
11989
+ "Split major-version upgrades or broad framework upgrades into a separate mission unless the advisory requires them.",
11990
+ "Record any remaining advisory ids, ignored dev-only findings, or transitive paths that still need owner judgment.",
11991
+ ...auditCommandsFor(packageManager).map((command2) => `Rerun ${command2} after the lockfile update.`)
11992
+ ]);
11993
+ }
11994
+ function confidenceFor(input) {
11995
+ let score = 35;
11996
+ if (input.changedDependencyFiles.length > 0)
11997
+ score += 20;
11998
+ if (input.packageSignals.length > 0)
11999
+ score += 20;
12000
+ if (input.lockfiles.length > 0)
12001
+ score += 10;
12002
+ if (input.validationCommands.length > 0)
12003
+ score += 10;
12004
+ return Math.min(95, score);
12005
+ }
12006
+ function summaryFor3(input) {
12007
+ const packages = input.packageSignals.map((signal2) => signal2.name);
12008
+ return [
12009
+ `Detected ${input.packageManager} dependency remediation context.`,
12010
+ `${input.manifests.length} manifest${input.manifests.length === 1 ? "" : "s"}, ${input.lockfiles.length} lockfile${input.lockfiles.length === 1 ? "" : "s"}.`,
12011
+ input.changedDependencyFiles.length > 0 ? `Changed dependency files: ${input.changedDependencyFiles.slice(0, 6).join(", ")}.` : "No dependency files are changed yet.",
12012
+ packages.length > 0 ? `Likely packages: ${packages.slice(0, 8).join(", ")}.` : "Package names still need advisory/scanner evidence.",
12013
+ input.validationCommands.length > 0 ? `Validation starts with ${input.validationCommands[0]}.` : "Validation commands need repo-specific confirmation."
12014
+ ].join(" ");
12015
+ }
12016
+ function cleanPackageName(input) {
12017
+ return input.replace(/[),.;\]]+$/g, "").trim();
12018
+ }
12019
+ function looksLikePackageName(input) {
12020
+ return /^@?[a-z0-9][a-z0-9._-]*(?:\/[a-z0-9._-]+)?$/i.test(input);
12021
+ }
12022
+ function looksLikeVersionOrAdvisory(input) {
12023
+ const normalized = input.toLowerCase();
12024
+ return /^\d/.test(input) || /^cve-\d/i.test(input) || /^ghsa-/i.test(input) || [
12025
+ "advisory",
12026
+ "advisories",
12027
+ "change",
12028
+ "changes",
12029
+ "dependency",
12030
+ "dependencies",
12031
+ "dependabot",
12032
+ "finding",
12033
+ "findings",
12034
+ "package",
12035
+ "packages",
12036
+ "security",
12037
+ "transitive",
12038
+ "upgrade",
12039
+ "upgrades",
12040
+ "vulnerabilities",
12041
+ "vulnerability",
12042
+ "vulnerable"
12043
+ ].includes(normalized);
12044
+ }
12045
+ var dependencyFilePatterns, genericDependencyWords;
12046
+ var init_dependencyRemediation = __esm({
12047
+ "../core/dist/dependencyRemediation.js"() {
12048
+ "use strict";
12049
+ init_utils();
12050
+ dependencyFilePatterns = [
12051
+ "package.json",
12052
+ "pnpm-lock.yaml",
12053
+ "pnpm-workspace.yaml",
12054
+ "package-lock.json",
12055
+ "yarn.lock",
12056
+ "bun.lock",
12057
+ "npm-shrinkwrap.json",
12058
+ "requirements.txt",
12059
+ "poetry.lock",
12060
+ "pyproject.toml",
12061
+ "Pipfile.lock",
12062
+ "Gemfile",
12063
+ "Gemfile.lock",
12064
+ "go.mod",
12065
+ "go.sum",
12066
+ "Cargo.toml",
12067
+ "Cargo.lock"
12068
+ ];
12069
+ genericDependencyWords = /* @__PURE__ */ new Set([
12070
+ "advisory",
12071
+ "audit",
12072
+ "build",
12073
+ "change",
12074
+ "dependency",
12075
+ "dev",
12076
+ "exact",
12077
+ "existing",
12078
+ "focused",
12079
+ "for",
12080
+ "finding",
12081
+ "lockfile",
12082
+ "package",
12083
+ "path",
12084
+ "security",
12085
+ "test",
12086
+ "the",
12087
+ "toolchain",
12088
+ "transitive",
12089
+ "vulnerability",
12090
+ "workflow",
12091
+ "without"
12092
+ ]);
12093
+ }
12094
+ });
12095
+
11538
12096
  // ../core/dist/testRecommendations.js
11539
12097
  import { existsSync as existsSync3 } from "node:fs";
11540
- import path6 from "node:path";
12098
+ import path7 from "node:path";
11541
12099
  function recommendExactTestsForFiles(input) {
11542
12100
  const packageManager = detectPackageManager(input.repoRoot);
11543
12101
  if (isDocsOnlyChange(input.changedFiles)) {
@@ -11691,7 +12249,7 @@ function taskRelevantTests(repoRoot, scanMap, scanSources, task, excluded) {
11691
12249
  }));
11692
12250
  return [...mapTests, ...sourceTests].filter((test) => !excludedSet.has(test.path)).map((test) => {
11693
12251
  const pathScore = tokens.reduce((total, token) => total + (test.seed.includes(token) ? 2 : 0), 0);
11694
- const content = pathScore > 0 ? (safeRead(path6.join(repoRoot, test.path), 6e4) ?? "").toLowerCase() : "";
12252
+ const content = pathScore > 0 ? (safeRead(path7.join(repoRoot, test.path), 6e4) ?? "").toLowerCase() : "";
11695
12253
  return {
11696
12254
  path: test.path,
11697
12255
  score: pathScore + tokens.reduce((total, token) => total + (test.context.includes(token) ? 1 : 0), 0) + tokens.reduce((total, token) => total + (content.includes(token) ? 1 : 0), 0)
@@ -11755,8 +12313,8 @@ function testCommandForFile(repoRoot, file, packageManager) {
11755
12313
  if (packageManager === "pnpm") {
11756
12314
  const packageInfo = nearestPackageInfo(repoRoot, file);
11757
12315
  if (packageInfo?.name && packageInfo.root !== repoRoot) {
11758
- const packageRelativeFile = path6.relative(packageInfo.root, path6.join(repoRoot, file)).replaceAll("\\", "/");
11759
- const testScript = readPackageJson(packageInfo.root)?.scripts?.test;
12316
+ const packageRelativeFile = path7.relative(packageInfo.root, path7.join(repoRoot, file)).replaceAll("\\", "/");
12317
+ const testScript = readPackageJson2(packageInfo.root)?.scripts?.test;
11760
12318
  if (typeof testScript === "string") {
11761
12319
  if (/\bvitest\b/.test(testScript)) {
11762
12320
  return `pnpm --filter ${packageInfo.name} exec vitest run ${packageRelativeFile}`;
@@ -11774,11 +12332,11 @@ function testCommandForFile(repoRoot, file, packageManager) {
11774
12332
  return `${packageManager} test -- ${file}`;
11775
12333
  }
11776
12334
  function nearestPackageInfo(repoRoot, file) {
11777
- let current = path6.dirname(path6.join(repoRoot, file));
12335
+ let current = path7.dirname(path7.join(repoRoot, file));
11778
12336
  while (current.startsWith(repoRoot)) {
11779
- const packageJsonPath = path6.join(current, "package.json");
12337
+ const packageJsonPath = path7.join(current, "package.json");
11780
12338
  if (existsSync3(packageJsonPath)) {
11781
- const pkg = readPackageJson(current);
12339
+ const pkg = readPackageJson2(current);
11782
12340
  return {
11783
12341
  root: current,
11784
12342
  ...typeof pkg?.name === "string" ? { name: pkg.name } : {}
@@ -11786,18 +12344,18 @@ function nearestPackageInfo(repoRoot, file) {
11786
12344
  }
11787
12345
  if (current === repoRoot)
11788
12346
  break;
11789
- current = path6.dirname(current);
12347
+ current = path7.dirname(current);
11790
12348
  }
11791
12349
  return null;
11792
12350
  }
11793
12351
  function coLocatedTestCandidates(repoRoot, file) {
11794
- const parsed = path6.parse(file);
12352
+ const parsed = path7.parse(file);
11795
12353
  const extensions = parsed.ext === ".tsx" ? [".test.tsx", ".spec.tsx", ".test.ts", ".spec.ts"] : parsed.ext === ".jsx" ? [".test.jsx", ".spec.jsx", ".test.js", ".spec.js"] : [`.test${parsed.ext}`, `.spec${parsed.ext}`];
11796
12354
  const candidates = [
11797
- ...extensions.map((extension) => path6.join(parsed.dir, `${parsed.name}${extension}`)),
11798
- ...extensions.map((extension) => path6.join(parsed.dir, "__tests__", `${parsed.name}${extension}`))
12355
+ ...extensions.map((extension) => path7.join(parsed.dir, `${parsed.name}${extension}`)),
12356
+ ...extensions.map((extension) => path7.join(parsed.dir, "__tests__", `${parsed.name}${extension}`))
11799
12357
  ].map((candidate) => candidate.replaceAll("\\", "/"));
11800
- return candidates.filter((candidate) => existsSync3(path6.join(repoRoot, candidate)));
12358
+ return candidates.filter((candidate) => existsSync3(path7.join(repoRoot, candidate)));
11801
12359
  }
11802
12360
  function importedByTests(scanMap, sourceFiles) {
11803
12361
  const fromSourceMetadata = scanMap.filter((node) => sourceFiles.includes(sourcePathForNode(node))).flatMap((node) => metadataStrings3(node, "importedBy")).filter(isTestPath);
@@ -11819,11 +12377,11 @@ function isDocsOnlyChange(files) {
11819
12377
  }
11820
12378
  function isDocsOrProductDocPath(file) {
11821
12379
  const normalized = file.replaceAll("\\", "/");
11822
- const basename = path6.basename(normalized).toLowerCase();
12380
+ const basename = path7.basename(normalized).toLowerCase();
11823
12381
  return normalized.startsWith("docs/") || normalized.includes("/docs/") || normalized.startsWith(".github/") || basename === "readme.md" || basename === "agents.md" || basename === "claude.md" || basename.endsWith(".md") || basename.endsWith(".mdx") || basename.endsWith(".rst") || basename.endsWith(".adoc");
11824
12382
  }
11825
12383
  function docsOnlyRecommendations(repoRoot, packageManager, changedFiles) {
11826
- const pkg = readPackageJson(repoRoot);
12384
+ const pkg = readPackageJson2(repoRoot);
11827
12385
  const scripts = pkg?.scripts ?? {};
11828
12386
  const recommendations = [
11829
12387
  {
@@ -11847,7 +12405,7 @@ function docsOnlyRecommendations(repoRoot, packageManager, changedFiles) {
11847
12405
  if (typeof scripts[script] === "string") {
11848
12406
  recommendations.push({
11849
12407
  command: `${packageManager} ${script}`,
11850
- reason: `Repo package.json defines ${script}; run it when docs or agent guidance can affect generated output or published docs.`,
12408
+ reason: `Repo package.json defines ${script}; run it when docs or agent guidance can affect generated output or shared docs.`,
11851
12409
  files: [],
11852
12410
  source: "script",
11853
12411
  confidence: script === "check" ? "low" : "medium"
@@ -11857,7 +12415,7 @@ function docsOnlyRecommendations(repoRoot, packageManager, changedFiles) {
11857
12415
  return recommendations.slice(0, 4);
11858
12416
  }
11859
12417
  function packageScriptRecommendations(repoRoot, packageManager) {
11860
- const pkg = readPackageJson(repoRoot);
12418
+ const pkg = readPackageJson2(repoRoot);
11861
12419
  const scripts = pkg?.scripts ?? {};
11862
12420
  const commands = [];
11863
12421
  for (const script of [
@@ -11890,7 +12448,7 @@ function taskRelevantPackageScriptRecommendations(repoRoot, packageManager, task
11890
12448
  const packageRoots = unique(sourceFiles.map((file) => nearestPackageInfo(repoRoot, file)?.root).filter((root) => Boolean(root)));
11891
12449
  const recommendations = [];
11892
12450
  for (const packageRoot of packageRoots) {
11893
- const pkg = readPackageJson(packageRoot);
12451
+ const pkg = readPackageJson2(packageRoot);
11894
12452
  const scripts = pkg?.scripts ?? {};
11895
12453
  const packageName = typeof pkg?.name === "string" ? pkg.name : void 0;
11896
12454
  if (wantsPackageRelease) {
@@ -11923,8 +12481,8 @@ function packageScriptCommand(packageManager, packageRoot, repoRoot, packageName
11923
12481
  }
11924
12482
  return packageManager === "npm" ? `npm run ${script}` : `${packageManager} ${script}`;
11925
12483
  }
11926
- function readPackageJson(packageRoot) {
11927
- const content = safeRead(path6.join(packageRoot, "package.json"), 2e5);
12484
+ function readPackageJson2(packageRoot) {
12485
+ const content = safeRead(path7.join(packageRoot, "package.json"), 2e5);
11928
12486
  if (!content)
11929
12487
  return null;
11930
12488
  try {
@@ -11940,17 +12498,17 @@ function shellQuote(value) {
11940
12498
  return `'${value.replaceAll("'", `'"'"'`)}'`;
11941
12499
  }
11942
12500
  function detectPackageManager(repoRoot) {
11943
- const pkg = readPackageJson(repoRoot);
12501
+ const pkg = readPackageJson2(repoRoot);
11944
12502
  if (typeof pkg?.packageManager === "string") {
11945
12503
  const name = pkg.packageManager.split("@")[0];
11946
12504
  if (name)
11947
12505
  return name;
11948
12506
  }
11949
- if (existsSync3(path6.join(repoRoot, "pnpm-lock.yaml")))
12507
+ if (existsSync3(path7.join(repoRoot, "pnpm-lock.yaml")))
11950
12508
  return "pnpm";
11951
- if (existsSync3(path6.join(repoRoot, "yarn.lock")))
12509
+ if (existsSync3(path7.join(repoRoot, "yarn.lock")))
11952
12510
  return "yarn";
11953
- if (existsSync3(path6.join(repoRoot, "bun.lockb")))
12511
+ if (existsSync3(path7.join(repoRoot, "bun.lockb")))
11954
12512
  return "bun";
11955
12513
  return "npm";
11956
12514
  }
@@ -11967,6 +12525,10 @@ function contextForTask(scan, request) {
11967
12525
  const files = request.files ?? [];
11968
12526
  const maxTokens = request.maxTokens ?? 6e3;
11969
12527
  const scoped = files.length > 0;
12528
+ const dependencyRemediation = isDependencyRemediationTask(task, files) ? buildDependencyRemediationPlan(request.repoRoot ?? scan.repoRoot, {
12529
+ task,
12530
+ changedFiles: files
12531
+ }) : void 0;
11970
12532
  const rankedRules = rankRules(scan.rules, task, files);
11971
12533
  const initialRules = rankedRules.slice(0, scoped ? 8 : 12);
11972
12534
  const initialSkills = selectSkills(scan.skills, task, files, scoped).map(compactSkillForContext);
@@ -11974,13 +12536,18 @@ function contextForTask(scan, request) {
11974
12536
  const rankedMap = rankMap(mapCandidates, task, files);
11975
12537
  const selectedMap = takeGuidanceGroundedMapNodes(mapCandidates, scan.sources, rulesStronglyRelevantForGrounding(initialRules, task), initialSkills, rankedMap, files, scoped ? 14 : 18).map(compactMapNodeForContext);
11976
12538
  const suggestedTests = selectValidationForContext(scan.map, scan.sources, task, files, selectedMap, request.repoRoot);
12539
+ const dependencyTests = dependencyValidationForContext(dependencyRemediation, task);
12540
+ const focusedSuggestedTests = dependencyRemediation ? dependencyTests : suggestedTests;
11977
12541
  const initialSelection = {
11978
12542
  rules: initialRules,
11979
12543
  skills: initialSkills,
11980
12544
  map: selectedMap,
11981
- wikiPages: selectWikiPages(buildContextWiki(scan).pages, task, files, scoped, suggestedTests).map(compactWikiPageForContext),
12545
+ wikiPages: selectWikiPages(buildContextWiki(scan).pages, task, files, scoped, focusedSuggestedTests).map(compactWikiPageForContext),
11982
12546
  memories: rankMemories(scan.memories, task, files).slice(0, scoped ? 4 : 8).map(compactMemoryForContext),
11983
- suggestedTests
12547
+ suggestedTests: unique([...dependencyTests, ...focusedSuggestedTests]),
12548
+ ...dependencyRemediation ? {
12549
+ dependencyRemediation: compactDependencyRemediationForContext(dependencyRemediation)
12550
+ } : {}
11984
12551
  };
11985
12552
  const { selection, tokenEstimate, omitted } = fitSelectionToBudget(initialSelection, {
11986
12553
  maxTokens,
@@ -11991,32 +12558,39 @@ function contextForTask(scan, request) {
11991
12558
  const { rules: selectedRules, skills: selectedSkills, map: budgetedMap, wikiPages: selectedWikiPages, memories: selectedMemories } = selection;
11992
12559
  const finalMap = takeGuidanceGroundedMapNodes(mapCandidates, scan.sources, rulesStronglyRelevantForGrounding(selectedRules, task), selectedSkills, budgetedMap, files, Math.max(budgetedMap.length, scoped ? 2 : 4)).map(compactMapNodeForContext);
11993
12560
  const finalSuggestedTests = selectValidationForContext(scan.map, scan.sources, task, files, finalMap, request.repoRoot);
12561
+ const packetSuggestedTests = unique([
12562
+ ...dependencyRemediation ? dependencyValidationForContext(dependencyRemediation, task) : finalSuggestedTests
12563
+ ]);
11994
12564
  const owners = selectOwners(scan.map, files);
11995
12565
  const sources = unique([
11996
12566
  ...selectedRules.flatMap((rule) => rule.sourcePaths),
11997
12567
  ...selectedSkills.map((skill) => skill.sourcePath),
11998
12568
  ...finalMap.map(mapNodeSourcePath),
11999
12569
  ...selectedWikiPages.flatMap((page2) => page2.sourcePaths),
12000
- ...selectedMemories.flatMap((memory) => memory.sourcePaths)
12570
+ ...selectedMemories.flatMap((memory) => memory.sourcePaths),
12571
+ ...dependencySourcePaths(dependencyRemediation)
12001
12572
  ]);
12002
12573
  const teamGuidance = scan.teamGuidanceCatalog ? teamGuidanceDeliveryFor(scan, selectedRules, selectedSkills, selectedMemories) : void 0;
12003
12574
  const packet = {
12004
12575
  packetId: packetId(),
12005
12576
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
12006
12577
  task,
12007
- summary: summarizeContext(task, selectedRules, finalSuggestedTests, finalMap),
12578
+ summary: summarizeContext(task, selectedRules, packetSuggestedTests, finalMap),
12008
12579
  rules: selectedRules,
12009
12580
  skills: selectedSkills,
12010
12581
  map: finalMap,
12011
12582
  wikiPages: selectedWikiPages,
12012
12583
  memories: selectedMemories,
12013
- suggestedTests: finalSuggestedTests,
12584
+ suggestedTests: packetSuggestedTests,
12014
12585
  owners,
12015
12586
  sources,
12016
12587
  tokenEstimate,
12017
12588
  omitted,
12018
- valueSignals: contextValueSignals(finalMap, finalSuggestedTests, sources.length, teamGuidance?.applied.length ?? 0),
12019
- ...teamGuidance ? { teamGuidance } : {}
12589
+ valueSignals: contextValueSignals(finalMap, packetSuggestedTests, sources.length, teamGuidance?.applied.length ?? 0),
12590
+ ...teamGuidance ? { teamGuidance } : {},
12591
+ ...dependencyRemediation ? {
12592
+ dependencyRemediation: compactDependencyRemediationForContext(dependencyRemediation)
12593
+ } : {}
12020
12594
  };
12021
12595
  return enforceContextPacketBudget(packet, maxTokens, files);
12022
12596
  }
@@ -12050,6 +12624,47 @@ function contextValueSignals(map, suggestedTests, relevantSourceCount, appliedTe
12050
12624
  suggestedTestCount: suggestedTests.length
12051
12625
  };
12052
12626
  }
12627
+ function dependencyValidationForContext(plan, task) {
12628
+ if (!plan)
12629
+ return [];
12630
+ const allowsRuntimeProof = /\b(runtime|smoke|e2e|browser|production|integration)\b/i.test(task);
12631
+ return plan.validationCommands.filter((command2) => allowsRuntimeProof ? true : !/\b(e2e|smoke|ux)\b/i.test(command2)).slice(0, allowsRuntimeProof ? 5 : 4);
12632
+ }
12633
+ function dependencySourcePaths(plan) {
12634
+ if (!plan)
12635
+ return [];
12636
+ return unique([
12637
+ ...plan.manifestFiles,
12638
+ ...plan.lockfiles,
12639
+ ...plan.workspaceFiles,
12640
+ ...plan.changedDependencyFiles,
12641
+ ...plan.directDependencyFiles
12642
+ ]);
12643
+ }
12644
+ function compactDependencyRemediationForContext(plan) {
12645
+ if (!plan)
12646
+ return void 0;
12647
+ return {
12648
+ ...plan,
12649
+ manifestFiles: plan.manifestFiles.slice(0, 8),
12650
+ lockfiles: plan.lockfiles.slice(0, 6),
12651
+ workspaceFiles: plan.workspaceFiles.slice(0, 4),
12652
+ changedDependencyFiles: plan.changedDependencyFiles.slice(0, 8),
12653
+ directDependencyFiles: plan.directDependencyFiles.slice(0, 8),
12654
+ packageSignals: plan.packageSignals.slice(0, 8).map((signal2) => ({
12655
+ ...signal2,
12656
+ manifestPaths: signal2.manifestPaths.slice(0, 4),
12657
+ lockfilePaths: signal2.lockfilePaths.slice(0, 4),
12658
+ evidence: signal2.evidence.slice(0, 3)
12659
+ })),
12660
+ graphCommands: plan.graphCommands.slice(0, 6),
12661
+ remediationCommands: plan.remediationCommands.slice(0, 6),
12662
+ validationCommands: plan.validationCommands.slice(0, 8),
12663
+ residualRiskChecks: plan.residualRiskChecks.slice(0, 6),
12664
+ handoffRequirements: plan.handoffRequirements.slice(0, 4),
12665
+ summary: truncateText(plan.summary, 520)
12666
+ };
12667
+ }
12053
12668
  function codebaseMapForTask(scan, request, limit = 80) {
12054
12669
  const files = request.files ?? [];
12055
12670
  const hasSpecificScope = files.length > 0 || wordsFor3(request.task).length > 1;
@@ -12927,7 +13542,8 @@ function cloneSelection(selection) {
12927
13542
  map: [...selection.map],
12928
13543
  wikiPages: [...selection.wikiPages],
12929
13544
  memories: [...selection.memories],
12930
- suggestedTests: [...selection.suggestedTests]
13545
+ suggestedTests: [...selection.suggestedTests],
13546
+ ...selection.dependencyRemediation ? { dependencyRemediation: selection.dependencyRemediation } : {}
12931
13547
  };
12932
13548
  }
12933
13549
  function estimateSelectionTokens(selection, task) {
@@ -12942,6 +13558,7 @@ function estimateSelectionTokens(selection, task) {
12942
13558
  wikiPages: selection.wikiPages,
12943
13559
  memories: selection.memories,
12944
13560
  suggestedTests: selection.suggestedTests,
13561
+ ...selection.dependencyRemediation ? { dependencyRemediation: selection.dependencyRemediation } : {},
12945
13562
  owners: [],
12946
13563
  sources: selectionSources(selection),
12947
13564
  tokenEstimate: 0,
@@ -13038,10 +13655,6 @@ function trimContextPacket(packet, protectedMapPaths) {
13038
13655
  packet.suggestedTests.pop();
13039
13656
  return true;
13040
13657
  }
13041
- if (packet.rules.length > 0) {
13042
- packet.rules.pop();
13043
- return true;
13044
- }
13045
13658
  if (packet.map.length > protectedMapPaths.size) {
13046
13659
  packet.map.pop();
13047
13660
  return true;
@@ -13084,7 +13697,7 @@ function compactProtectedMapNodes(map, protectedMapPaths) {
13084
13697
  function compactProtectedMapMetadata(metadata) {
13085
13698
  if (!metadata)
13086
13699
  return void 0;
13087
- const compact = {};
13700
+ const compact2 = {};
13088
13701
  for (const key of [
13089
13702
  "sourcePath",
13090
13703
  "symbolKind",
@@ -13095,7 +13708,7 @@ function compactProtectedMapMetadata(metadata) {
13095
13708
  ]) {
13096
13709
  const value = metadata[key];
13097
13710
  if (value !== void 0)
13098
- compact[key] = value;
13711
+ compact2[key] = value;
13099
13712
  }
13100
13713
  for (const key of [
13101
13714
  "evidenceKinds",
@@ -13124,9 +13737,9 @@ function compactProtectedMapMetadata(metadata) {
13124
13737
  ]) {
13125
13738
  const value = metadata[key];
13126
13739
  if (Array.isArray(value))
13127
- compact[key] = value.slice(0, 2);
13740
+ compact2[key] = value.slice(0, 2);
13128
13741
  }
13129
- return compact;
13742
+ return compact2;
13130
13743
  }
13131
13744
  function refreshContextPacketDerivedFields(packet) {
13132
13745
  packet.summary = summarizeContext(packet.task, packet.rules, packet.suggestedTests, packet.map);
@@ -13136,7 +13749,8 @@ function refreshContextPacketDerivedFields(packet) {
13136
13749
  map: packet.map,
13137
13750
  wikiPages: packet.wikiPages,
13138
13751
  memories: packet.memories,
13139
- suggestedTests: packet.suggestedTests
13752
+ suggestedTests: packet.suggestedTests,
13753
+ ...packet.dependencyRemediation ? { dependencyRemediation: packet.dependencyRemediation } : {}
13140
13754
  });
13141
13755
  refreshTeamGuidanceDelivery(packet);
13142
13756
  packet.valueSignals = contextValueSignals(packet.map, packet.suggestedTests, packet.sources.length, packet.teamGuidance?.applied.length ?? 0);
@@ -13188,7 +13802,8 @@ function selectionSources(selection) {
13188
13802
  ...selection.skills.map((skill) => skill.sourcePath),
13189
13803
  ...selection.map.map((node) => node.path),
13190
13804
  ...selection.wikiPages.flatMap((page2) => page2.sourcePaths),
13191
- ...selection.memories.flatMap((memory) => memory.sourcePaths)
13805
+ ...selection.memories.flatMap((memory) => memory.sourcePaths),
13806
+ ...dependencySourcePaths(selection.dependencyRemediation)
13192
13807
  ]);
13193
13808
  }
13194
13809
  function trimSelection(selection, minimums, protectedMapPaths) {
@@ -13302,17 +13917,17 @@ function compactWikiPageForContext(page2) {
13302
13917
  function compactMetadata2(metadata) {
13303
13918
  if (!metadata)
13304
13919
  return void 0;
13305
- const compact = {};
13920
+ const compact2 = {};
13306
13921
  for (const [key, value] of Object.entries(metadata)) {
13307
13922
  if (!contextMetadataKeys.has(key))
13308
13923
  continue;
13309
13924
  if (Array.isArray(value)) {
13310
- compact[key] = value.slice(0, metadataArrayLimit(key));
13925
+ compact2[key] = value.slice(0, metadataArrayLimit(key));
13311
13926
  } else {
13312
- compact[key] = value;
13927
+ compact2[key] = value;
13313
13928
  }
13314
13929
  }
13315
- return compact;
13930
+ return compact2;
13316
13931
  }
13317
13932
  function metadataArrayLimit(key) {
13318
13933
  if (["imports", "importedBy"].includes(key))
@@ -13333,6 +13948,7 @@ var init_context = __esm({
13333
13948
  init_scanner();
13334
13949
  init_contextWiki();
13335
13950
  init_pathIntent();
13951
+ init_dependencyRemediation();
13336
13952
  init_testRecommendations();
13337
13953
  init_utils();
13338
13954
  contextStopWords = /* @__PURE__ */ new Set([
@@ -13405,443 +14021,6 @@ var init_context = __esm({
13405
14021
  }
13406
14022
  });
13407
14023
 
13408
- // ../core/dist/dependencyRemediation.js
13409
- import path7 from "node:path";
13410
- function buildDependencyRemediationPlan(repoRoot, request) {
13411
- const files = walkFiles(repoRoot, request.maxFiles ?? 6e3);
13412
- const manifests = files.filter(isDependencyManifest);
13413
- const lockfiles = files.filter(isDependencyLockfile);
13414
- const workspaceFiles = files.filter(isDependencyWorkspaceFile);
13415
- const changedDependencyFiles = unique((request.changedFiles ?? []).filter(isDependencyFile));
13416
- const packageManager = inferPackageManager(repoRoot, manifests, lockfiles);
13417
- const packageSignalInput = {
13418
- task: request.task,
13419
- manifests,
13420
- changedDependencyFiles
13421
- };
13422
- if (request.diff)
13423
- packageSignalInput.diff = request.diff;
13424
- const packageSignals = packageSignalsFor(repoRoot, packageSignalInput);
13425
- const directDependencyFiles = unique(packageSignals.flatMap((signal2) => signal2.manifestPaths));
13426
- const packageNames = packageSignals.map((signal2) => signal2.name);
13427
- const validationCommands2 = validationCommandsFor2(repoRoot, packageManager);
13428
- const graphCommands = dependencyGraphCommandsFor(packageManager, packageNames);
13429
- const remediationCommands = remediationCommandsFor(packageManager, packageNames);
13430
- const residualRiskChecks = residualRiskChecksFor(packageManager, packageNames, changedDependencyFiles);
13431
- const confidence = confidenceFor({
13432
- changedDependencyFiles,
13433
- packageSignals,
13434
- lockfiles,
13435
- validationCommands: validationCommands2
13436
- });
13437
- return {
13438
- id: `dep_${contentHash([
13439
- request.task,
13440
- packageManager,
13441
- changedDependencyFiles.join(","),
13442
- packageNames.join(",")
13443
- ].join(":"))}`,
13444
- status: changedDependencyFiles.length > 0 || packageSignals.length > 0 ? "ready" : "needs_evidence",
13445
- packageManager,
13446
- manifestFiles: manifests.slice(0, 20),
13447
- lockfiles,
13448
- workspaceFiles,
13449
- changedDependencyFiles,
13450
- directDependencyFiles,
13451
- packageSignals,
13452
- graphCommands,
13453
- remediationCommands,
13454
- validationCommands: validationCommands2,
13455
- residualRiskChecks,
13456
- handoffRequirements: [
13457
- "List advisory ids or scanner finding ids that were fixed.",
13458
- "Name packages still present in the lockfile with vulnerable versions, or state that the scan/audit is clean.",
13459
- "Explain any major-version upgrades, overrides, peer dependency changes, skipped advisories, or remaining advisory ids.",
13460
- "Attach validation commands and CI follow-up results before marking the security mission done."
13461
- ],
13462
- summary: summaryFor3({
13463
- packageManager,
13464
- manifests,
13465
- lockfiles,
13466
- changedDependencyFiles,
13467
- packageSignals,
13468
- validationCommands: validationCommands2
13469
- }),
13470
- confidence
13471
- };
13472
- }
13473
- function isDependencyFile(filePath) {
13474
- const normalized = filePath.replaceAll("\\", "/");
13475
- return dependencyFilePatterns.some((pattern) => normalized === pattern || normalized.endsWith(`/${pattern}`) || pattern === "package.json" && normalized.endsWith("/package.json"));
13476
- }
13477
- function isDependencyLockfile(filePath) {
13478
- const normalized = filePath.replaceAll("\\", "/");
13479
- return normalized === "pnpm-lock.yaml" || normalized.endsWith("/pnpm-lock.yaml") || normalized === "package-lock.json" || normalized.endsWith("/package-lock.json") || normalized === "yarn.lock" || normalized.endsWith("/yarn.lock") || normalized === "bun.lock" || normalized.endsWith("/bun.lock") || normalized === "npm-shrinkwrap.json" || normalized.endsWith("/npm-shrinkwrap.json") || normalized === "poetry.lock" || normalized.endsWith("/poetry.lock") || normalized === "Pipfile.lock" || normalized.endsWith("/Pipfile.lock") || normalized === "Gemfile.lock" || normalized.endsWith("/Gemfile.lock") || normalized === "go.sum" || normalized.endsWith("/go.sum") || normalized === "Cargo.lock" || normalized.endsWith("/Cargo.lock");
13480
- }
13481
- function isDependencyManifest(filePath) {
13482
- const normalized = filePath.replaceAll("\\", "/");
13483
- return normalized === "package.json" || normalized.endsWith("/package.json") || normalized === "requirements.txt" || normalized.endsWith("/requirements.txt") || normalized === "pyproject.toml" || normalized.endsWith("/pyproject.toml") || normalized === "Gemfile" || normalized.endsWith("/Gemfile") || normalized === "go.mod" || normalized.endsWith("/go.mod") || normalized === "Cargo.toml" || normalized.endsWith("/Cargo.toml");
13484
- }
13485
- function isDependencyWorkspaceFile(filePath) {
13486
- const normalized = filePath.replaceAll("\\", "/");
13487
- return normalized === "pnpm-workspace.yaml" || normalized.endsWith("/pnpm-workspace.yaml") || normalized === "lerna.json" || normalized.endsWith("/lerna.json") || normalized === "turbo.json" || normalized.endsWith("/turbo.json") || normalized === "nx.json" || normalized.endsWith("/nx.json");
13488
- }
13489
- function inferPackageManager(repoRoot, manifests, lockfiles) {
13490
- const rootPackageJson = readPackageJson2(repoRoot, "package.json");
13491
- const packageManager = rootPackageJson?.packageManager;
13492
- if (typeof packageManager === "string") {
13493
- if (packageManager.startsWith("pnpm@"))
13494
- return "pnpm";
13495
- if (packageManager.startsWith("yarn@"))
13496
- return "yarn";
13497
- if (packageManager.startsWith("npm@"))
13498
- return "npm";
13499
- if (packageManager.startsWith("bun@"))
13500
- return "bun";
13501
- }
13502
- if (lockfiles.some((file) => file.endsWith("pnpm-lock.yaml")))
13503
- return "pnpm";
13504
- if (lockfiles.some((file) => file.endsWith("yarn.lock")))
13505
- return "yarn";
13506
- if (lockfiles.some((file) => file.endsWith("package-lock.json"))) {
13507
- return "npm";
13508
- }
13509
- if (lockfiles.some((file) => file.endsWith("bun.lock")))
13510
- return "bun";
13511
- if (manifests.some((file) => file.endsWith("requirements.txt") || file.endsWith("pyproject.toml") || file.endsWith("Pipfile"))) {
13512
- return "pip";
13513
- }
13514
- if (manifests.some((file) => file.endsWith("Gemfile")))
13515
- return "bundler";
13516
- if (manifests.some((file) => file.endsWith("go.mod")))
13517
- return "go";
13518
- if (manifests.some((file) => file.endsWith("Cargo.toml")))
13519
- return "cargo";
13520
- return "unknown";
13521
- }
13522
- function packageSignalsFor(repoRoot, input) {
13523
- const manifestDeps = dependencyNamesByManifest(repoRoot, input.manifests);
13524
- const explicitPackageNames = unique([
13525
- ...packagesMentionedInText(input.task),
13526
- ...packagesMentionedInDiff(input.diff ?? "")
13527
- ]).filter((name) => !looksLikeVersionOrAdvisory(name));
13528
- const fallbackPackageNames = input.changedDependencyFiles.flatMap((file) => packagesFromChangedDependencyFile(repoRoot, file));
13529
- const packageNames = unique(explicitPackageNames.length > 0 ? explicitPackageNames : fallbackPackageNames).filter((name) => !looksLikeVersionOrAdvisory(name));
13530
- return packageNames.slice(0, 16).map((name) => ({
13531
- name,
13532
- changeKind: changeKindFor(name, input.diff ?? ""),
13533
- manifestPaths: manifestsForPackage(name, manifestDeps),
13534
- lockfilePaths: input.changedDependencyFiles.filter(isDependencyLockfile),
13535
- evidence: evidenceForPackage(name, input.task, input.diff ?? "")
13536
- }));
13537
- }
13538
- function dependencyNamesByManifest(repoRoot, manifests) {
13539
- const byPackage = /* @__PURE__ */ new Map();
13540
- for (const manifest of manifests) {
13541
- if (!manifest.endsWith("package.json"))
13542
- continue;
13543
- const parsed = readPackageJson2(repoRoot, manifest);
13544
- if (!parsed)
13545
- continue;
13546
- for (const name of packageJsonDependencyNames(parsed)) {
13547
- const current = byPackage.get(name) ?? [];
13548
- current.push(manifest);
13549
- byPackage.set(name, current);
13550
- }
13551
- }
13552
- return byPackage;
13553
- }
13554
- function packageJsonDependencyNames(parsed) {
13555
- return unique([
13556
- "dependencies",
13557
- "devDependencies",
13558
- "optionalDependencies",
13559
- "peerDependencies",
13560
- "overrides",
13561
- "resolutions",
13562
- "pnpm"
13563
- ].flatMap((field) => nestedDependencyNames(parsed[field])));
13564
- }
13565
- function nestedDependencyNames(value) {
13566
- if (!value || typeof value !== "object")
13567
- return [];
13568
- return Object.entries(value).flatMap(([key, nested]) => [
13569
- looksLikePackageName(key) ? key : "",
13570
- ...nestedDependencyNames(nested)
13571
- ]);
13572
- }
13573
- function packagesMentionedInText(text) {
13574
- const names = [];
13575
- const quoted = text.matchAll(/[`'"](@?[a-z0-9][a-z0-9._-]*(?:\/[a-z0-9._-]+)?)[`'"]/gi);
13576
- for (const match of quoted)
13577
- names.push(match[1] ?? "");
13578
- const advisoryContext = text.matchAll(/\b(?:package|dependency|upgrade|update|override|transitive|vulnerable|dependabot|cve|ghsa|audit)\s+(@?[a-z0-9][a-z0-9._-]*(?:\/[a-z0-9._-]+)?)/gi);
13579
- for (const match of advisoryContext)
13580
- names.push(match[1] ?? "");
13581
- const atVersion = text.matchAll(/\b(@?[a-z0-9][a-z0-9._-]*(?:\/[a-z0-9._-]+)?)@(?:\^|~)?\d/gi);
13582
- for (const match of atVersion)
13583
- names.push(match[1] ?? "");
13584
- if (/\b(dependabot|cve-|ghsa-|audit|vulnerab|advisory)\b/i.test(text)) {
13585
- const affectedPackageContext = text.matchAll(/\b(?:for|affects?|in)\s+(@?[a-z0-9][a-z0-9._-]*(?:\/[a-z0-9._-]+)?)/gi);
13586
- for (const match of affectedPackageContext)
13587
- names.push(match[1] ?? "");
13588
- }
13589
- return unique(names.map(cleanPackageName).filter(looksLikePackageName));
13590
- }
13591
- function packagesMentionedInDiff(diff) {
13592
- const names = [];
13593
- for (const line of diff.split(/\r?\n/)) {
13594
- if (!/^[+-]/.test(line) || /^[+-]{3}/.test(line))
13595
- continue;
13596
- const packageJsonMatch = line.match(/"(@?[a-z0-9][a-z0-9._-]*(?:\/[a-z0-9._-]+)?)"\s*:/i);
13597
- if (packageJsonMatch?.[1])
13598
- names.push(packageJsonMatch[1]);
13599
- const pnpmMatch = line.match(/^\s*[+-]\s{0,8}(@?[a-z0-9][a-z0-9._-]*(?:\/[a-z0-9._-]+)?)@(?:\^|~)?\d/i);
13600
- if (pnpmMatch?.[1])
13601
- names.push(pnpmMatch[1]);
13602
- const lockPathMatch = line.match(/\/(@?[a-z0-9][a-z0-9._-]*(?:\/[a-z0-9._-]+)?)@(?:\^|~)?\d/i);
13603
- if (lockPathMatch?.[1])
13604
- names.push(lockPathMatch[1]);
13605
- }
13606
- return unique(names.map(cleanPackageName).filter(looksLikePackageName));
13607
- }
13608
- function packagesFromChangedDependencyFile(repoRoot, filePath) {
13609
- if (!filePath.endsWith("package.json"))
13610
- return [];
13611
- const parsed = readPackageJson2(repoRoot, filePath);
13612
- return parsed ? packageJsonDependencyNames(parsed).slice(0, 20) : [];
13613
- }
13614
- function readPackageJson2(repoRoot, filePath) {
13615
- const content = safeRead(path7.join(repoRoot, filePath), 4e5);
13616
- if (!content)
13617
- return null;
13618
- try {
13619
- const parsed = JSON.parse(content);
13620
- return parsed && typeof parsed === "object" ? parsed : null;
13621
- } catch {
13622
- return null;
13623
- }
13624
- }
13625
- function changeKindFor(packageName, diff) {
13626
- const escaped = packageName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
13627
- const removedVersions = [
13628
- ...diff.matchAll(new RegExp(`^-.*${escaped}.*?(\\d+\\.\\d+\\.\\d+)`, "gim"))
13629
- ].map((match) => match[1] ?? "");
13630
- const addedVersions = [
13631
- ...diff.matchAll(new RegExp(`^\\+.*${escaped}.*?(\\d+\\.\\d+\\.\\d+)`, "gim"))
13632
- ].map((match) => match[1] ?? "");
13633
- const removed = removedVersions[0];
13634
- const added = addedVersions[0];
13635
- if (removed && added) {
13636
- const [removedMajor] = removed.split(".");
13637
- const [addedMajor] = added.split(".");
13638
- return removedMajor && addedMajor && removedMajor !== addedMajor ? "major" : "patch_or_minor";
13639
- }
13640
- if (added && !removed)
13641
- return "added_or_overridden";
13642
- return "unknown";
13643
- }
13644
- function manifestsForPackage(packageName, manifestDeps) {
13645
- return unique([
13646
- ...manifestDeps.get(packageName) ?? [],
13647
- ...[...manifestDeps.entries()].filter(([name]) => name.endsWith(`/${packageName}`)).flatMap(([, manifests]) => manifests)
13648
- ]).slice(0, 8);
13649
- }
13650
- function evidenceForPackage(packageName, task, diff) {
13651
- const evidence = [];
13652
- if (task.toLowerCase().includes(packageName.toLowerCase())) {
13653
- evidence.push("mentioned_in_task");
13654
- }
13655
- if (diff.toLowerCase().includes(packageName.toLowerCase())) {
13656
- evidence.push("mentioned_in_diff");
13657
- }
13658
- if (evidence.length === 0)
13659
- evidence.push("manifest_candidate");
13660
- return evidence;
13661
- }
13662
- function validationCommandsFor2(repoRoot, packageManager) {
13663
- const scripts = packageScripts(repoRoot);
13664
- const run = packageRunCommand(packageManager);
13665
- return unique([
13666
- ...auditCommandsFor(packageManager),
13667
- ...scriptCommand(scripts, "check", run),
13668
- ...scriptCommand(scripts, "test", run),
13669
- ...scriptCommand(scripts, "test:client", run),
13670
- ...scriptCommand(scripts, "test:e2e:smoke", run),
13671
- ...scriptCommand(scripts, "build", run)
13672
- ]).slice(0, 10);
13673
- }
13674
- function packageScripts(repoRoot) {
13675
- const parsed = readPackageJson2(repoRoot, "package.json");
13676
- const scripts = parsed?.scripts;
13677
- return scripts && typeof scripts === "object" ? Object.fromEntries(Object.entries(scripts).filter((entry) => typeof entry[1] === "string")) : {};
13678
- }
13679
- function scriptCommand(scripts, scriptName, runCommand) {
13680
- return scripts[scriptName] ? [`${runCommand} ${scriptName}`] : [];
13681
- }
13682
- function packageRunCommand(packageManager) {
13683
- switch (packageManager) {
13684
- case "npm":
13685
- return "npm run";
13686
- case "yarn":
13687
- return "yarn";
13688
- case "bun":
13689
- return "bun run";
13690
- default:
13691
- return "pnpm";
13692
- }
13693
- }
13694
- function auditCommandsFor(packageManager) {
13695
- switch (packageManager) {
13696
- case "pnpm":
13697
- return ["pnpm audit --prod"];
13698
- case "npm":
13699
- return ["npm audit --omit=dev"];
13700
- case "yarn":
13701
- return ["yarn npm audit --environment production"];
13702
- case "bun":
13703
- return ["bun audit"];
13704
- case "pip":
13705
- return ["pip-audit"];
13706
- case "bundler":
13707
- return ["bundle audit check"];
13708
- case "go":
13709
- return ["govulncheck ./..."];
13710
- case "cargo":
13711
- return ["cargo audit"];
13712
- default:
13713
- return [];
13714
- }
13715
- }
13716
- function dependencyGraphCommandsFor(packageManager, packageNames) {
13717
- const selected = packageNames.slice(0, 5);
13718
- if (selected.length === 0)
13719
- return [];
13720
- switch (packageManager) {
13721
- case "pnpm":
13722
- return selected.map((name) => `pnpm why ${name}`);
13723
- case "npm":
13724
- return selected.map((name) => `npm explain ${name}`);
13725
- case "yarn":
13726
- return selected.map((name) => `yarn why ${name}`);
13727
- case "bun":
13728
- return selected.map((name) => `bun pm why ${name}`);
13729
- case "go":
13730
- return selected.map((name) => `go mod why -m ${name}`);
13731
- case "cargo":
13732
- return selected.map((name) => `cargo tree -i ${name}`);
13733
- default:
13734
- return [];
13735
- }
13736
- }
13737
- function remediationCommandsFor(packageManager, packageNames) {
13738
- const selected = packageNames.slice(0, 5);
13739
- if (selected.length === 0)
13740
- return [];
13741
- switch (packageManager) {
13742
- case "pnpm":
13743
- return selected.map((name) => `pnpm up ${name} --latest --interactive=false`);
13744
- case "npm":
13745
- return selected.map((name) => `npm update ${name}`);
13746
- case "yarn":
13747
- return selected.map((name) => `yarn up ${name}`);
13748
- case "bun":
13749
- return selected.map((name) => `bun update ${name}`);
13750
- case "go":
13751
- return selected.map((name) => `go get ${name}@latest`);
13752
- case "cargo":
13753
- return selected.map((name) => `cargo update -p ${name}`);
13754
- default:
13755
- return [];
13756
- }
13757
- }
13758
- function residualRiskChecksFor(packageManager, packageNames, changedDependencyFiles) {
13759
- return unique([
13760
- packageNames.length > 0 ? `Confirm vulnerable versions are gone for: ${packageNames.slice(0, 8).join(", ")}.` : "Name each advisory/package from the scanner before editing.",
13761
- changedDependencyFiles.some(isDependencyLockfile) ? "Inspect the lockfile diff for unexpected package churn outside the advisory path." : "Regenerate and inspect the lockfile if the fix changes dependency resolution.",
13762
- "Split major-version upgrades or broad framework upgrades into a separate mission unless the advisory requires them.",
13763
- "Record any remaining advisory ids, ignored dev-only findings, or transitive paths that still need owner judgment.",
13764
- ...auditCommandsFor(packageManager).map((command2) => `Rerun ${command2} after the lockfile update.`)
13765
- ]);
13766
- }
13767
- function confidenceFor(input) {
13768
- let score = 35;
13769
- if (input.changedDependencyFiles.length > 0)
13770
- score += 20;
13771
- if (input.packageSignals.length > 0)
13772
- score += 20;
13773
- if (input.lockfiles.length > 0)
13774
- score += 10;
13775
- if (input.validationCommands.length > 0)
13776
- score += 10;
13777
- return Math.min(95, score);
13778
- }
13779
- function summaryFor3(input) {
13780
- const packages = input.packageSignals.map((signal2) => signal2.name);
13781
- return [
13782
- `Detected ${input.packageManager} dependency remediation context.`,
13783
- `${input.manifests.length} manifest${input.manifests.length === 1 ? "" : "s"}, ${input.lockfiles.length} lockfile${input.lockfiles.length === 1 ? "" : "s"}.`,
13784
- input.changedDependencyFiles.length > 0 ? `Changed dependency files: ${input.changedDependencyFiles.slice(0, 6).join(", ")}.` : "No dependency files are changed yet.",
13785
- packages.length > 0 ? `Likely packages: ${packages.slice(0, 8).join(", ")}.` : "Package names still need advisory/scanner evidence.",
13786
- input.validationCommands.length > 0 ? `Validation starts with ${input.validationCommands[0]}.` : "Validation commands need repo-specific confirmation."
13787
- ].join(" ");
13788
- }
13789
- function cleanPackageName(input) {
13790
- return input.replace(/[),.;\]]+$/g, "").trim();
13791
- }
13792
- function looksLikePackageName(input) {
13793
- return /^@?[a-z0-9][a-z0-9._-]*(?:\/[a-z0-9._-]+)?$/i.test(input);
13794
- }
13795
- function looksLikeVersionOrAdvisory(input) {
13796
- const normalized = input.toLowerCase();
13797
- return /^\d/.test(input) || /^cve-\d/i.test(input) || /^ghsa-/i.test(input) || [
13798
- "advisory",
13799
- "advisories",
13800
- "change",
13801
- "changes",
13802
- "dependency",
13803
- "dependencies",
13804
- "dependabot",
13805
- "finding",
13806
- "findings",
13807
- "package",
13808
- "packages",
13809
- "security",
13810
- "transitive",
13811
- "upgrade",
13812
- "upgrades",
13813
- "vulnerabilities",
13814
- "vulnerability",
13815
- "vulnerable"
13816
- ].includes(normalized);
13817
- }
13818
- var dependencyFilePatterns;
13819
- var init_dependencyRemediation = __esm({
13820
- "../core/dist/dependencyRemediation.js"() {
13821
- "use strict";
13822
- init_utils();
13823
- dependencyFilePatterns = [
13824
- "package.json",
13825
- "pnpm-lock.yaml",
13826
- "pnpm-workspace.yaml",
13827
- "package-lock.json",
13828
- "yarn.lock",
13829
- "bun.lock",
13830
- "npm-shrinkwrap.json",
13831
- "requirements.txt",
13832
- "poetry.lock",
13833
- "pyproject.toml",
13834
- "Pipfile.lock",
13835
- "Gemfile",
13836
- "Gemfile.lock",
13837
- "go.mod",
13838
- "go.sum",
13839
- "Cargo.toml",
13840
- "Cargo.lock"
13841
- ];
13842
- }
13843
- });
13844
-
13845
14024
  // ../core/dist/productOperatingContext.js
13846
14025
  function buildProductOperatingContext(scan, request) {
13847
14026
  const files = unique(request.files ?? []);
@@ -17164,7 +17343,7 @@ function missionGates(autonomy, recipe, options2) {
17164
17343
  label: "Library privacy gate",
17165
17344
  requiredTool: "brief.agent_session_insights",
17166
17345
  passCriteria: "Every candidate example is classified as derived-only, redacted transcript, or explicitly approved raw import before sharing.",
17167
- failureMode: "Do not publish raw prompts, secrets, customer data, private logs, or sensitive repo context without explicit approval."
17346
+ failureMode: "Do not share raw prompts, secrets, customer data, private logs, or sensitive repo context without explicit approval."
17168
17347
  }, {
17169
17348
  id: "reuse_quality",
17170
17349
  label: "Reuse quality gate",
@@ -17548,7 +17727,7 @@ function missionSteps(task, autonomy, recipe, options2) {
17548
17727
  id: "verify_receipts",
17549
17728
  title: "Verify ticket receipt ledger",
17550
17729
  objective: "Prove the ticket-writing receipt chain before another agent runs the ticket.",
17551
- agentAction: "Run Brief receipt verification and fix missing, mismatched, or broken-chain ticket proof before publishing the ticket.",
17730
+ agentAction: "Run Brief receipt verification and fix missing, mismatched, or broken-chain ticket proof before sending the ticket.",
17552
17731
  briefTool: "brief.verify_receipts",
17553
17732
  expectedOutput: "Receipt ledger verification with digest and chain-link status.",
17554
17733
  gateId: "ticket"
@@ -17777,7 +17956,7 @@ function automationLoopContract(autonomy, recipe, validation, options2, delivery
17777
17956
  "executionContract.evidenceContract.mapProof",
17778
17957
  "context.likelyFiles"
17779
17958
  ],
17780
- stopIfMissing: isTicketWriter ? "Do not publish the ticket until product intent, owner judgment, acceptance criteria, and stop conditions are explicit." : "Do not edit until likely files, rules, tests, owners, and affected systems are grounded in Brief context."
17959
+ stopIfMissing: isTicketWriter ? "Do not send the ticket until product intent, owner judgment, acceptance criteria, and stop conditions are explicit." : "Do not edit until likely files, rules, tests, owners, and affected systems are grounded in Brief context."
17781
17960
  },
17782
17961
  ...isRuntimeFix ? [
17783
17962
  {
@@ -18173,7 +18352,7 @@ function missionRunContract(autonomy, recipe, validation, options2, deliveryMode
18173
18352
  successCriteria: [
18174
18353
  "The context receipt cites the rules, source paths, tests, owners, and memories used by the agent.",
18175
18354
  recipe.id === "ticket_write" ? "The ticket separates human judgment from execution instructions and names unresolved decisions before automation starts." : recipe.id === "security_dependency" ? "The dependency vulnerability packet names advisory id, package, affected range, fixed range, manifest, lockfile, direct/transitive path, reachability, remediation choice, and rollback." : recipe.id === "security_finding" ? "The security finding packet names alert source, rule id, classification, source-to-sink or trust boundary, affected users/data, owner, exploitability, and validation path." : recipe.id === "security_testing" ? "The security test plan names the protected property, bad case, safe case, command, severity threshold, false-positive policy, owner, privacy boundary, and rollout mode." : recipe.id === "internal_app" ? "The app contract names the internal route, data source, auth context, allowed roles, row filters, audit events, and redaction rules before implementation." : recipe.id === "stale_pr" ? "The PR intent packet names original scope, unresolved review feedback, stale assumptions, validation, and handoff expectation." : recipe.id === "merge_conflict" ? "The conflict packet names conflicted files, both sides' intent, semantic conflicts, and validation before resolution." : recipe.id === "flaky_test" ? "The CI evidence packet names failing job or test, sample failure, platform, seed or timing signal, hypothesis, and validation path." : recipe.id === "small_refactor" ? "The mechanical scope packet names exact pattern, allowed files, exclusions, and expected behavior preservation." : recipe.id === "bug_investigation" ? "The hypothesis ledger names observed facts, prior attempts, plausible causes, disproven causes, next probes, and confidence." : recipe.id === "qa_edge_cases" ? "The edge-case inventory names current coverage, missing boundary cases, risky inputs, and validation commands." : recipe.id === "performance" ? "The benchmark packet names workload, command, metric, baseline, variance, success threshold, and rollback signal before edits." : recipe.id === "porting" ? "The source behavior packet names source paths, target paths, incompatible assumptions, owner decision, validation, and attribution notes." : recipe.id === "experiment" ? "The experiment contract names the hypothesis, success signal, throwaway boundary, rollback or discard path, and decision owner." : recipe.id === "session_review" ? "The session review uses receipt-derived activity and marks any need for raw prompts, logs, or transcripts explicitly." : recipe.id === "session_library" ? "The session library classifies every reusable example by proof quality, privacy posture, owner approval, and reuse trigger." : options2.needsRuntimeEvidence ? "The runtime evidence packet names the observed failure source, identifier, environment, time window, symptom, and open gaps." : options2.needsWorkItem ? "The work item packet names source, id, acceptance criteria, non-goals, and the update expected after execution." : recipe.id === "research" ? "The answer names sources, risks, and next steps without editing the repo." : "Required validation evidence is attached or the missing environment is explicitly explained.",
18176
- recipe.id === "ticket_write" ? "Acceptance criteria, constraints, validation plan, review bar, stop conditions, and update template are parseable from the ticket body." : recipe.id === "security_dependency" ? "Install integrity, build/typecheck, targeted tests, and scanner/audit evidence show the vulnerability is remediated without breaking the dependency path." : recipe.id === "security_finding" ? "The root cause is fixed with negative regression, safe abuse-case proof, or scanner rerun evidence; suppression-only fixes are explicitly rejected or owner-approved." : recipe.id === "security_testing" ? "The added test or scanner catches the intended bad case or has a clear preventive rationale, and rollout mode is calibrated for developer workflow." : recipe.id === "internal_app" ? "The built app follows repo-native UI/data patterns and validation proves the access-control contract." : recipe.id === "stale_pr" ? "The handoff names review feedback addressed, validation, remaining risk, and next reviewer action." : recipe.id === "merge_conflict" ? "The resolution preserves intended behavior from both sides or clearly escalates unresolved semantic conflicts." : recipe.id === "flaky_test" ? "The result stabilizes the test or explains the product root cause with evidence rather than hiding the signal." : recipe.id === "small_refactor" ? "The diff stays mechanical and validation proves no intended behavior changed." : recipe.id === "bug_investigation" ? "The root cause, fix, no-fix decision, or escalation is tied to evidence and includes a follow-up watch signal." : recipe.id === "qa_edge_cases" ? "The added or improved tests cover meaningful behavior risk and do not mute, skip, or obscure the signal." : recipe.id === "performance" ? "Before/after measurements, tradeoffs, and rollback signals are recorded before the optimization is accepted." : recipe.id === "porting" ? "The target behavior preserves source intent, attribution is handled, and intentional differences are explicit." : recipe.id === "experiment" ? "The keep, discard, or formalize decision is recorded with validation and remaining production-readiness gaps." : recipe.id === "session_review" ? "Run cards or skill drafts are tied to actual receipts, validation quality, review outcomes, and privacy posture." : recipe.id === "session_library" ? "The published examples are derived from approved receipts or explicitly approved imports and include first-run commands, stop conditions, and validation expectations." : options2.needsRuntimeEvidence ? "The root cause, fix, validation, and incident or ticket update are tied back to the runtime evidence." : "The mission output is tied back to the selected recipe and context receipt.",
18355
+ recipe.id === "ticket_write" ? "Acceptance criteria, constraints, validation plan, review bar, stop conditions, and update template are parseable from the ticket body." : recipe.id === "security_dependency" ? "Install integrity, build/typecheck, targeted tests, and scanner/audit evidence show the vulnerability is remediated without breaking the dependency path." : recipe.id === "security_finding" ? "The root cause is fixed with negative regression, safe abuse-case proof, or scanner rerun evidence; suppression-only fixes are explicitly rejected or owner-approved." : recipe.id === "security_testing" ? "The added test or scanner catches the intended bad case or has a clear preventive rationale, and rollout mode is calibrated for developer workflow." : recipe.id === "internal_app" ? "The built app follows repo-native UI/data patterns and validation proves the access-control contract." : recipe.id === "stale_pr" ? "The handoff names review feedback addressed, validation, remaining risk, and next reviewer action." : recipe.id === "merge_conflict" ? "The resolution preserves intended behavior from both sides or clearly escalates unresolved semantic conflicts." : recipe.id === "flaky_test" ? "The result stabilizes the test or explains the product root cause with evidence rather than hiding the signal." : recipe.id === "small_refactor" ? "The diff stays mechanical and validation proves no intended behavior changed." : recipe.id === "bug_investigation" ? "The root cause, fix, no-fix decision, or escalation is tied to evidence and includes a follow-up watch signal." : recipe.id === "qa_edge_cases" ? "The added or improved tests cover meaningful behavior risk and do not mute, skip, or obscure the signal." : recipe.id === "performance" ? "Before/after measurements, tradeoffs, and rollback signals are recorded before the optimization is accepted." : recipe.id === "porting" ? "The target behavior preserves source intent, attribution is handled, and intentional differences are explicit." : recipe.id === "experiment" ? "The keep, discard, or formalize decision is recorded with validation and remaining production-readiness gaps." : recipe.id === "session_review" ? "Run cards or skill drafts are tied to actual receipts, validation quality, review outcomes, and privacy posture." : recipe.id === "session_library" ? "The shared examples are derived from approved receipts or explicitly approved imports and include first-run commands, stop conditions, and validation expectations." : options2.needsRuntimeEvidence ? "The root cause, fix, validation, and incident or ticket update are tied back to the runtime evidence." : "The mission output is tied back to the selected recipe and context receipt.",
18177
18356
  autonomy === "read_only" ? "No repo files are changed." : "The local diff has no blocker or high Brief findings before human review.",
18178
18357
  "Useful repeated feedback is saved or intentionally skipped with a reason."
18179
18358
  ],
@@ -19012,10 +19191,10 @@ function missionHookEvents(recipe, autonomy, options2) {
19012
19191
  if (recipe.id === "session_library") {
19013
19192
  events.push({
19014
19193
  id: "before_session_library_publish",
19015
- label: "Before library publish",
19194
+ label: "Before team sharing",
19016
19195
  timing: "before",
19017
19196
  trigger: "An agent turns previous sessions, transcripts, prompts, or run cards into shared examples.",
19018
- action: "Require receipt proof, privacy classification, owner approval, and explicit raw-import approval before publishing examples to the team.",
19197
+ action: "Require receipt proof, privacy classification, owner approval, and explicit raw-import approval before sharing examples with the team.",
19019
19198
  mode: "blocking"
19020
19199
  });
19021
19200
  }
@@ -20251,7 +20430,7 @@ var init_missionPlan = __esm({
20251
20430
  guidance: [
20252
20431
  "Keep one curator responsible for privacy classification and final library shape.",
20253
20432
  "Use parallel agents only to summarize candidate receipts or draft examples from approved sources.",
20254
- "Do not publish raw transcripts, secrets, logs, or customer data without explicit approval."
20433
+ "Do not share raw transcripts, secrets, logs, or customer data without explicit approval."
20255
20434
  ]
20256
20435
  }
20257
20436
  }),
@@ -20758,7 +20937,7 @@ function privacyBoundaryForRecipe(recipeId) {
20758
20937
  if (recipeId === "security_finding") {
20759
20938
  return [
20760
20939
  "redact raw secrets, tokens, cookies, customer payloads, and private logs before agent use",
20761
- "do not publish exploit strings or abuse-case details outside the approved repo/security channel",
20940
+ "do not share exploit strings or abuse-case details outside the approved repo/security channel",
20762
20941
  "provider rotation or invalidation status is tracked without exposing the replacement credential",
20763
20942
  "false-positive and accepted-risk decisions need named owner approval and audit trail"
20764
20943
  ];
@@ -20789,7 +20968,7 @@ function privacyBoundaryForRecipe(recipeId) {
20789
20968
  return [
20790
20969
  "derive examples from receipts before raw transcripts",
20791
20970
  "require approval for prompts, logs, secrets, or customer data",
20792
- "publish concise run cards, not private session dumps"
20971
+ "share concise run cards, not private session dumps"
20793
20972
  ];
20794
20973
  }
20795
20974
  return [
@@ -26220,7 +26399,7 @@ var init_agentSetup = __esm({
26220
26399
  "ide",
26221
26400
  "generic"
26222
26401
  ];
26223
- briefPublishedPackageSpec = "runbrief@0.1.1";
26402
+ briefPublishedPackageSpec = "runbrief@0.1.3";
26224
26403
  TARGET_LABELS = {
26225
26404
  terminal: "Terminal agent",
26226
26405
  vscode: "VS Code",
@@ -27281,7 +27460,7 @@ function summarizeBeforeYouCodeSharedContext(delivery) {
27281
27460
  next: "Shared guidance is not available in this session; keep new learning personal until the workspace connection is ready."
27282
27461
  };
27283
27462
  }
27284
- const next = delivery.status === "applied" ? "Use the applied shared guidance, then save a focused learning or reference when the pass proves a reusable pattern." : delivery.status === "available_not_applied" ? "Shared guidance exists but did not match this task; inspect the relevant team item before creating a new pattern." : "No approved shared guidance exists yet; keep this pass personal and publish only after the pattern is proven.";
27463
+ const next = delivery.status === "applied" ? "Use the applied shared guidance, then save a focused learning or reference when the pass proves a reusable pattern." : delivery.status === "available_not_applied" ? "Shared guidance exists but did not match this task; inspect the relevant team item before creating a new pattern." : "No approved shared guidance exists yet; keep this pass personal and make it available to the team only after the pattern is proven.";
27285
27464
  return {
27286
27465
  status: delivery.status,
27287
27466
  workspaceId: delivery.workspaceId,
@@ -27424,11 +27603,13 @@ function buildBeforeYouCodeMap(scan, input) {
27424
27603
  files,
27425
27604
  maxLines: 18
27426
27605
  });
27606
+ const mapCoverage = mapCoverageFor2(scan);
27427
27607
  const stopSignals2 = unique([
27428
27608
  "Wrong repo, branch, or worktree.",
27429
27609
  ...target.branch?.startsWith("detached@") ? ["Detached HEAD; select the intended feature branch before editing."] : [],
27430
27610
  ...insights.agentPacket.stopSignals,
27431
27611
  ...productOperatingContext.stopSignals.slice(0, 3),
27612
+ ...mapCoverage.status === "partial" ? [mapCoverage.summary] : [],
27432
27613
  ...target.dirty && files.length === 0 ? ["Dirty worktree is broad; classify baseline before editing."] : []
27433
27614
  ]).slice(0, 5);
27434
27615
  const status = inspectFirst.length === 0 && reuseCandidates.length === 0 ? "needs_narrower_scope" : "ready";
@@ -27448,6 +27629,7 @@ function buildBeforeYouCodeMap(scan, input) {
27448
27629
  reuseCandidates,
27449
27630
  likelyTests,
27450
27631
  productOperatingContext,
27632
+ mapCoverage,
27451
27633
  ...context.teamGuidance ? { teamGuidance: context.teamGuidance } : {},
27452
27634
  referenceFirst,
27453
27635
  validationChecklist,
@@ -27460,6 +27642,7 @@ function buildBeforeYouCodeMap(scan, input) {
27460
27642
  reuseCandidates,
27461
27643
  likelyTests,
27462
27644
  productOperatingContext,
27645
+ mapCoverage,
27463
27646
  validationChecklist,
27464
27647
  stopSignals: stopSignals2,
27465
27648
  ...context.teamGuidance ? { teamGuidance: context.teamGuidance } : {},
@@ -27479,7 +27662,9 @@ function buildBeforeYouCodeMap(scan, input) {
27479
27662
  candidates: reuseCandidates,
27480
27663
  decision: reuseCandidates.length > 0 ? "For each candidate, choose reuse, adapt, or rule out and record the decision before creating a new primitive." : "No reuse candidate is grounded yet; run one focused search before creating a new primitive."
27481
27664
  },
27665
+ mapCoverage,
27482
27666
  ...context.teamGuidance ? { teamGuidance: context.teamGuidance } : {},
27667
+ rules: context.rules.slice(0, 8),
27483
27668
  referenceFirst,
27484
27669
  likelyTests,
27485
27670
  exactTests,
@@ -27491,6 +27676,19 @@ function buildBeforeYouCodeMap(scan, input) {
27491
27676
  next: status === "ready" ? "Inspect these files and reuse candidates before editing; cite the reuse decision in the final handoff." : "Run brief.codebase_map or brief.context_for_task with a narrower task, folder, or file list before editing."
27492
27677
  };
27493
27678
  }
27679
+ function mapCoverageFor2(scan) {
27680
+ const mappedNodes = scan.stats.mapNodeCount;
27681
+ const omittedNodes = Math.max(0, scan.stats.mapOmittedCount ?? 0);
27682
+ const candidateNodes = Math.max(mappedNodes, scan.stats.mapTotalCandidateCount ?? mappedNodes + omittedNodes);
27683
+ const partial = Boolean(scan.stats.mapPartial) || omittedNodes > 0;
27684
+ return {
27685
+ status: partial ? "partial" : "complete",
27686
+ mappedNodes,
27687
+ candidateNodes,
27688
+ omittedNodes,
27689
+ summary: partial ? `Code map is partial: ${mappedNodes} of ${candidateNodes} candidate nodes are available; narrow by feature, folder, or symbol before treating a missing reuse candidate as proof none exists.` : `Code map covers all ${mappedNodes} indexed candidate nodes for this repo.`
27690
+ };
27691
+ }
27494
27692
  function reuseStatusFor(candidates) {
27495
27693
  if (candidates.length === 0)
27496
27694
  return "missing";
@@ -27611,6 +27809,7 @@ function cockpitFor(input) {
27611
27809
  "Broad e2e or full-suite tests unless routing, auth, persistence, or cross-page state changed."
27612
27810
  ];
27613
27811
  const stopSignals2 = unique([
27812
+ ...input.mapCoverage.status === "partial" ? [input.mapCoverage.summary] : [],
27614
27813
  ...input.stopSignals.slice(0, 3),
27615
27814
  ...mode === "lightweight" ? [
27616
27815
  "The tiny pass starts touching behavior, state, data, auth, or multiple surfaces."
@@ -27798,55 +27997,55 @@ function diversifyJourneyStages(ranked, task) {
27798
27997
  ];
27799
27998
  }
27800
27999
  function journeyStagePathPreference(stage, filePath) {
27801
- const path20 = filePath.toLowerCase();
28000
+ const path21 = filePath.toLowerCase();
27802
28001
  if (stage === "acquisition") {
27803
- if (/landinginteractive\.[cm]?[tj]sx?$/.test(path20))
28002
+ if (/landinginteractive\.[cm]?[tj]sx?$/.test(path21))
27804
28003
  return 40;
27805
- if (/apps\/web\/src\/app\/page\.[cm]?[tj]sx?$/.test(path20))
28004
+ if (/apps\/web\/src\/app\/page\.[cm]?[tj]sx?$/.test(path21))
27806
28005
  return 35;
27807
28006
  }
27808
28007
  if (stage === "access") {
27809
- if (/apps\/web\/src\/app\/login\/page\.[cm]?[tj]sx?$/.test(path20))
28008
+ if (/apps\/web\/src\/app\/login\/page\.[cm]?[tj]sx?$/.test(path21))
27810
28009
  return 40;
27811
- if (/api\/auth\/magic-link\/route\.[cm]?[tj]s$/.test(path20))
28010
+ if (/api\/auth\/magic-link\/route\.[cm]?[tj]s$/.test(path21))
27812
28011
  return 35;
27813
28012
  }
27814
28013
  if (stage === "workspace") {
27815
- if (/api\/v1\/workspaces\/bootstrap\/route\.[cm]?[tj]s$/.test(path20)) {
28014
+ if (/api\/v1\/workspaces\/bootstrap\/route\.[cm]?[tj]s$/.test(path21)) {
27816
28015
  return 40;
27817
28016
  }
27818
28017
  }
27819
28018
  if (stage === "activation") {
27820
- if (/api\/v1\/workspaces\/[^/]+\/api-keys\/route\.[cm]?[tj]s$/.test(path20)) {
28019
+ if (/api\/v1\/workspaces\/[^/]+\/api-keys\/route\.[cm]?[tj]s$/.test(path21)) {
27821
28020
  return 40;
27822
28021
  }
27823
- if (/workspace-agent-actions\.[cm]?[tj]s$/.test(path20))
28022
+ if (/workspace-agent-actions\.[cm]?[tj]s$/.test(path21))
27824
28023
  return 35;
27825
- if (/agent-handoff-composer\.[cm]?[tj]s$/.test(path20))
28024
+ if (/agent-handoff-composer\.[cm]?[tj]s$/.test(path21))
27826
28025
  return 30;
27827
28026
  }
27828
28027
  if (stage === "collaboration") {
27829
- if (/api\/v1\/workspaces\/[^/]+\/invitations\/route\.[cm]?[tj]s$/.test(path20)) {
28028
+ if (/api\/v1\/workspaces\/[^/]+\/invitations\/route\.[cm]?[tj]s$/.test(path21)) {
27830
28029
  return 40;
27831
28030
  }
27832
- if (/api\/v1\/workspaces\/[^/]+\/members\/[^/]+\/route/.test(path20)) {
28031
+ if (/api\/v1\/workspaces\/[^/]+\/members\/[^/]+\/route/.test(path21)) {
27833
28032
  return 30;
27834
28033
  }
27835
28034
  }
27836
28035
  if (stage === "monetization") {
27837
- if (/billing\/checkout\/route\.[cm]?[tj]s$/.test(path20))
28036
+ if (/billing\/checkout\/route\.[cm]?[tj]s$/.test(path21))
27838
28037
  return 40;
27839
- if (/pricing\/page\.[cm]?[tj]sx?$/.test(path20))
28038
+ if (/pricing\/page\.[cm]?[tj]sx?$/.test(path21))
27840
28039
  return 35;
27841
28040
  }
27842
28041
  return 0;
27843
28042
  }
27844
- function beforeYouCodePathIntentScore(path20, task) {
27845
- const lowerPath = (path20.split("#", 1)[0] ?? path20).toLowerCase();
28043
+ function beforeYouCodePathIntentScore(path21, task) {
28044
+ const lowerPath = (path21.split("#", 1)[0] ?? path21).toLowerCase();
27846
28045
  const lowerTask = task.toLowerCase();
27847
- let score = namedCodePathIntentScore(path20, task);
28046
+ let score = namedCodePathIntentScore(path21, task);
27848
28047
  const wantsInterface = /\b(app|design|header|interface|landing|layout|mobile|navigation|page|responsive|spacing|ui|ux|visual|workspace)\b/.test(lowerTask);
27849
- score += lexicalPathTaskScore(path20, task);
28048
+ score += lexicalPathTaskScore(path21, task);
27850
28049
  const wantsLanding = /\b(hero|homepage|landing)\b/.test(lowerTask);
27851
28050
  const wantsAppShell = /\b(app|header|internal|mobile|navigation|responsive|sidebar|workspace)\b/.test(lowerTask);
27852
28051
  const wantsAuth = /\b(auth|authentication|callback|login|magic link|otp|session|sign[ -]?in|sign[ -]?out)\b/.test(lowerTask);
@@ -27996,8 +28195,8 @@ function beforeYouCodePathIntentScore(path20, task) {
27996
28195
  }
27997
28196
  return score;
27998
28197
  }
27999
- function beforeYouCodeIntentSummary(path20, task) {
28000
- const normalizedPath = (path20.split("#", 1)[0] ?? path20).toLowerCase();
28198
+ function beforeYouCodeIntentSummary(path21, task) {
28199
+ const normalizedPath = (path21.split("#", 1)[0] ?? path21).toLowerCase();
28001
28200
  if (!isInternalAppTask(task))
28002
28201
  return void 0;
28003
28202
  if (/apps\/web\/src\/app\/app\/page\.[cm]?[tj]sx?$/.test(normalizedPath)) {
@@ -28023,7 +28222,7 @@ function prefersPrimaryWorkspaceTask(task) {
28023
28222
  return true;
28024
28223
  return /\b(design|designer|figma|screenshot|visual|ui|ux|layout|spacing|typography|responsive|mobile)\b/i.test(task);
28025
28224
  }
28026
- function lexicalPathTaskScore(path20, task) {
28225
+ function lexicalPathTaskScore(path21, task) {
28027
28226
  const ignored = /* @__PURE__ */ new Set([
28028
28227
  "agent",
28029
28228
  "brief",
@@ -28043,7 +28242,7 @@ function lexicalPathTaskScore(path20, task) {
28043
28242
  taskWords.add("workspace");
28044
28243
  taskWords.add("pane");
28045
28244
  }
28046
- const pathWords = path20.replace(/([a-z])([A-Z])/g, "$1 $2").toLowerCase().split(/[^a-z0-9]+/).filter((word) => word.length >= 4 && !ignored.has(word));
28245
+ const pathWords = path21.replace(/([a-z])([A-Z])/g, "$1 $2").toLowerCase().split(/[^a-z0-9]+/).filter((word) => word.length >= 4 && !ignored.has(word));
28047
28246
  const matches = unique(pathWords.filter((word) => taskWords.has(word)));
28048
28247
  const structuralBonus = matches.reduce((score, word) => {
28049
28248
  if (!["header", "pane", "shell", "sidebar", "workspace"].includes(word)) {
@@ -28117,6 +28316,7 @@ function compactTextFor2(input) {
28117
28316
  `Before coding: ${input.headline}`,
28118
28317
  `Target: ${input.target.repoName} | ${input.target.branch ?? "branch unknown"} | ${input.target.repoRoot}`,
28119
28318
  `Worktree: ${input.target.dirty ? "dirty" : "clean"}${input.target.changedFiles.length ? `; changed ${input.target.changedFiles.slice(0, 4).join(", ")}` : ""}${input.target.untrackedFiles.length ? `; untracked ${input.target.untrackedFiles.slice(0, 3).join(", ")}` : ""}`,
28319
+ `Map coverage: ${input.mapCoverage.summary}`,
28120
28320
  ...input.target.requestedFiles.length ? [`Scope files: ${input.target.requestedFiles.slice(0, 5).join(", ")}`] : [],
28121
28321
  ...input.teamGuidance ? [
28122
28322
  `Shared guidance: ${input.teamGuidance.applied.length > 0 ? input.teamGuidance.applied.slice(0, 2).map((item) => item.label).join(", ") : input.teamGuidance.status === "available_not_applied" ? "available, but none matched this task" : "none approved yet"}`
@@ -28164,6 +28364,119 @@ var init_agentBeforeYouCode = __esm({
28164
28364
  }
28165
28365
  });
28166
28366
 
28367
+ // ../core/dist/agentWorkPacket.js
28368
+ function buildAgentWorkPacket(input) {
28369
+ const before = input.beforeYouCode;
28370
+ const rules = uniqueRules2([
28371
+ ...(before.teamGuidance?.applied ?? []).map((item) => ({
28372
+ rule: item.label,
28373
+ source: `team:${item.kind}`
28374
+ })),
28375
+ ...(before.rules ?? []).map((item) => ({
28376
+ rule: compact(item.body || item.title, 180),
28377
+ source: item.sourcePaths[0] ?? "repo guidance"
28378
+ })),
28379
+ ...before.productOperatingContext.productRules.map((item) => ({
28380
+ rule: item.rule,
28381
+ source: item.source
28382
+ }))
28383
+ ]).slice(0, 3);
28384
+ const tests = before.exactTests.length > 0 ? before.exactTests.slice(0, 3).map((test) => ({
28385
+ command: compact(test.command, 180),
28386
+ reason: compact(test.reason, 180),
28387
+ confidence: test.confidence
28388
+ })) : before.likelyTests.slice(0, 3).map((command2) => ({
28389
+ command: compact(command2, 180),
28390
+ reason: "Nearest validation signal Brief found for this task.",
28391
+ confidence: "medium"
28392
+ }));
28393
+ const acceptance = unique2([
28394
+ ...before.productOperatingContext.acceptanceCriteria,
28395
+ ...before.validationChecklist
28396
+ ]).slice(0, 3);
28397
+ const stopConditions = unique2([
28398
+ ...before.stopSignals,
28399
+ ...before.productOperatingContext.stopSignals,
28400
+ ...before.mapCoverage.status === "partial" ? [before.mapCoverage.summary] : []
28401
+ ]).slice(0, 3);
28402
+ const nextAction = input.nextAction ?? before.next ?? "Inspect the listed files and make the reuse decision before editing.";
28403
+ const packet = {
28404
+ schemaVersion: 1,
28405
+ objective: compact(input.task, 240),
28406
+ interpretation: compact(input.interpretation ?? before.productOperatingContext.summary ?? before.headline, 160),
28407
+ target: {
28408
+ repoRoot: before.target.repoRoot,
28409
+ repoName: before.target.repoName,
28410
+ ...before.target.branch ? { branch: before.target.branch } : {},
28411
+ ...before.target.baseRef ? { baseRef: before.target.baseRef } : {},
28412
+ dirty: before.target.dirty,
28413
+ changedFiles: before.target.changedFiles.slice(0, 4)
28414
+ },
28415
+ inspectFirst: before.inspectFirst.slice(0, 3).map((item) => ({
28416
+ path: item.path,
28417
+ why: compact(item.why, 160)
28418
+ })),
28419
+ reuse: {
28420
+ status: before.reuseFirst.status,
28421
+ decision: compact(before.reuseFirst.decision, 220),
28422
+ candidates: before.reuseFirst.candidates.slice(0, 2).map((item) => ({
28423
+ path: item.path,
28424
+ action: compact(item.action, 140),
28425
+ why: compact(item.why, 140)
28426
+ }))
28427
+ },
28428
+ mapCoverage: before.mapCoverage,
28429
+ rules,
28430
+ tests,
28431
+ acceptance,
28432
+ stopConditions,
28433
+ nextAction: compact(nextAction, 200)
28434
+ };
28435
+ const compactText = formatAgentWorkPacket(packet);
28436
+ return { ...packet, compactText, lineCount: compactText.split("\n").length };
28437
+ }
28438
+ function formatAgentWorkPacket(packet) {
28439
+ const lines = [
28440
+ `Objective: ${packet.objective}`,
28441
+ `Target: ${packet.target.repoName}${packet.target.branch ? ` @ ${packet.target.branch}` : ""} (${packet.target.dirty ? "dirty" : "clean"})`,
28442
+ `Interpretation: ${packet.interpretation}`,
28443
+ `Inspect first: ${inlineList(packet.inspectFirst.map((item) => `${item.path} - ${item.why}`))}`,
28444
+ `Reuse (${packet.reuse.status}): ${packet.reuse.decision}; ${inlineList(packet.reuse.candidates.map((item) => `${item.path} - ${item.action}`))}`,
28445
+ `Map: ${packet.mapCoverage.summary}`,
28446
+ `Rules: ${inlineList(packet.rules.map((item) => `${item.rule} [${item.source}]`))}`,
28447
+ `Focused tests: ${inlineList(packet.tests.map((item) => `${item.command} - ${item.reason}`))}`,
28448
+ `Acceptance: ${inlineList(packet.acceptance)}`,
28449
+ `Stop if: ${inlineList(packet.stopConditions)}`,
28450
+ `Next: ${packet.nextAction}`
28451
+ ];
28452
+ return lines.join("\n");
28453
+ }
28454
+ function inlineList(items) {
28455
+ return items.length > 0 ? items.join("; ") : "none found";
28456
+ }
28457
+ function compact(value, maxLength) {
28458
+ const normalized = value.replace(/\s+/g, " ").trim();
28459
+ return normalized.length <= maxLength ? normalized : `${normalized.slice(0, maxLength - 3).trimEnd()}...`;
28460
+ }
28461
+ function unique2(values) {
28462
+ return [...new Set(values.map((value) => value.trim()).filter(Boolean))];
28463
+ }
28464
+ function uniqueRules2(rules) {
28465
+ const seen = /* @__PURE__ */ new Set();
28466
+ return rules.filter((item) => {
28467
+ const key = item.rule.toLowerCase();
28468
+ if (!item.rule || seen.has(key))
28469
+ return false;
28470
+ seen.add(key);
28471
+ return true;
28472
+ });
28473
+ }
28474
+ var init_agentWorkPacket = __esm({
28475
+ "../core/dist/agentWorkPacket.js"() {
28476
+ "use strict";
28477
+ }
28478
+ });
28479
+
28167
28480
  // ../core/dist/ticketMissionRuntime.js
28168
28481
  var init_ticketMissionRuntime = __esm({
28169
28482
  "../core/dist/ticketMissionRuntime.js"() {
@@ -28803,7 +29116,7 @@ function codeMapBetaReadiness(input) {
28803
29116
  score: Math.min(100, Math.round((betaScore + input.qualityScore) / 2)),
28804
29117
  summary: status === "beta_plus" ? "Beta+ ready: agents have source-grounded code surfaces, parser facts, ownership, validation, operational context, and focused map proof." : status === "beta" ? "Beta: source-grounded enough to use, with remaining proof gaps that should be explicit in missions." : status === "alpha" ? "Alpha: useful orientation, but agents need narrower source, proof, or operational context before autonomous edits." : "Blocked: this map is not safe to use as architecture proof yet.",
28805
29118
  checks,
28806
- blockingGaps: unique2(blockingGaps),
29119
+ blockingGaps: unique3(blockingGaps),
28807
29120
  nextActions: status === "beta_plus" ? [
28808
29121
  "Use this map as the pre-edit context gate for agent missions.",
28809
29122
  "Preserve mapQuality.betaReadiness in review_diff and save_handoff proof."
@@ -28876,7 +29189,7 @@ function metadataList(node, key) {
28876
29189
  return [];
28877
29190
  return value.filter((item) => typeof item === "string");
28878
29191
  }
28879
- function unique2(items) {
29192
+ function unique3(items) {
28880
29193
  return [...new Set(items)];
28881
29194
  }
28882
29195
  var CODE_SURFACE_TYPES2, SOURCE_AWARE_TYPES, OBSERVABILITY_SYSTEMS, TICKET_SYSTEMS, DATA_SYSTEMS;
@@ -29907,12 +30220,12 @@ function humanQuestionsFor(advisory, omittedFindingCount) {
29907
30220
  return unique(questions.map((question) => compactReviewQuestion(question))).slice(0, 8);
29908
30221
  }
29909
30222
  function compactReviewQuestion(value, maxLength = 800) {
29910
- const compact = value.replace(/\s+/g, " ").trim();
29911
- if (compact.length <= maxLength)
29912
- return compact;
30223
+ const compact2 = value.replace(/\s+/g, " ").trim();
30224
+ if (compact2.length <= maxLength)
30225
+ return compact2;
29913
30226
  if (maxLength <= 3)
29914
- return compact.slice(0, maxLength);
29915
- return `${compact.slice(0, maxLength - 3).trimEnd()}...`;
30227
+ return compact2.slice(0, maxLength);
30228
+ return `${compact2.slice(0, maxLength - 3).trimEnd()}...`;
29916
30229
  }
29917
30230
  function reviewReadinessScore(counts, proofStatus, review) {
29918
30231
  const penalty = counts.blocker * 35 + counts.high * 28 + counts.medium * 12 + counts.low * 5 + counts.info * 1 + review.omittedFindingCount * 2;
@@ -31096,6 +31409,7 @@ var init_diffReview = __esm({
31096
31409
  "handler",
31097
31410
  "helpers",
31098
31411
  "delete",
31412
+ "dynamic",
31099
31413
  "get",
31100
31414
  "head",
31101
31415
  "input",
@@ -32978,6 +33292,7 @@ var init_dist2 = __esm({
32978
33292
  init_missionPlan();
32979
33293
  init_agentStart();
32980
33294
  init_agentBeforeYouCode();
33295
+ init_agentWorkPacket();
32981
33296
  init_productOperatingContext();
32982
33297
  init_ticketMissionRuntime();
32983
33298
  init_governance();
@@ -33005,7 +33320,7 @@ function cloudSyncStatus(env = process.env) {
33005
33320
  return {
33006
33321
  configured: false,
33007
33322
  privacyMode,
33008
- reason: privacyMode === "local-only" ? "Cloud sync is disabled by privacy mode." : "Set BRIEF_API_URL and BRIEF_API_KEY to enable MCP cloud sync."
33323
+ reason: privacyMode === "local-only" ? "Cloud sync is disabled by privacy mode." : "Connect Brief from the app to authorize this device, or provide BRIEF_API_URL and BRIEF_API_KEY for an approved legacy setup."
33009
33324
  };
33010
33325
  }
33011
33326
  const status = {
@@ -33162,6 +33477,64 @@ async function fetchTeamContext(repoRoot, env = process.env, fetcher = fetch) {
33162
33477
  });
33163
33478
  return getTeamContextCloud(config, payload, fetcher);
33164
33479
  }
33480
+ async function claimRemoteLoopRun(runnerId, leaseSeconds = 180, env = process.env, fetcher = fetch) {
33481
+ const config = cloudConfigFromEnv(env);
33482
+ if (!config) throw new Error("Brief cloud connection is not configured for the runner.");
33483
+ const payload = claimLoopRunRequestSchema.parse({ runnerId, leaseSeconds });
33484
+ const body = await loopRunRequest(
33485
+ config,
33486
+ "/api/v1/mcp/loop-runs/claim",
33487
+ "POST",
33488
+ payload,
33489
+ fetcher
33490
+ );
33491
+ if (!body.ok) throw new Error(body.message ?? "Brief could not claim a loop run.");
33492
+ if (!body.run) return null;
33493
+ const parsed = loopRunSummarySchema.safeParse(body.run);
33494
+ if (!parsed.success) throw new Error("Brief returned an invalid loop run.");
33495
+ return parsed.data;
33496
+ }
33497
+ async function updateRemoteLoopRun(runId, input, env = process.env, fetcher = fetch) {
33498
+ const config = cloudConfigFromEnv(env);
33499
+ if (!config) throw new Error("Brief cloud connection is not configured for the runner.");
33500
+ const payload = updateLoopRunRequestSchema.parse(input);
33501
+ const body = await loopRunRequest(
33502
+ config,
33503
+ `/api/v1/mcp/loop-runs/${encodeURIComponent(runId)}`,
33504
+ "PATCH",
33505
+ payload,
33506
+ fetcher
33507
+ );
33508
+ if (!body.ok) throw new Error(body.message ?? "Brief could not update the loop run.");
33509
+ const parsed = loopRunSummarySchema.safeParse(body.run);
33510
+ if (!parsed.success) throw new Error("Brief returned an invalid updated loop run.");
33511
+ return parsed.data;
33512
+ }
33513
+ async function loopRunRequest(config, route, method, payload, fetcher) {
33514
+ const controller = new AbortController();
33515
+ const timeout = setTimeout(() => controller.abort(), syncTimeoutMs);
33516
+ try {
33517
+ const response = await fetcher(`${config.apiUrl}${route}`, {
33518
+ method,
33519
+ headers: {
33520
+ authorization: `Bearer ${config.apiKey}`,
33521
+ "content-type": "application/json"
33522
+ },
33523
+ body: JSON.stringify(payload),
33524
+ signal: controller.signal
33525
+ });
33526
+ const body = await safeJson(response);
33527
+ const record = body && typeof body === "object" && !Array.isArray(body) ? body : {};
33528
+ const result = {
33529
+ ok: response.ok && record.ok === true
33530
+ };
33531
+ if (typeof record.message === "string") result.message = record.message;
33532
+ if (record.run !== void 0) result.run = record.run;
33533
+ return result;
33534
+ } finally {
33535
+ clearTimeout(timeout);
33536
+ }
33537
+ }
33165
33538
  function toRepoScanSummary(scan, privacyMode) {
33166
33539
  return {
33167
33540
  repoName: scan.repoName,
@@ -33207,7 +33580,7 @@ function skippedResult(env) {
33207
33580
  return {
33208
33581
  status: "skipped",
33209
33582
  privacyMode,
33210
- reason: privacyMode === "local-only" ? "Cloud sync skipped because privacy mode is local-only." : "Cloud sync skipped because BRIEF_API_URL or BRIEF_API_KEY is missing."
33583
+ reason: privacyMode === "local-only" ? "Cloud sync skipped because privacy mode is local-only." : "Cloud sync skipped because this device is not connected. Create a browser-authorized connection from the Brief app, then restart the agent."
33211
33584
  };
33212
33585
  }
33213
33586
  function skippedTeamContextResult(env) {
@@ -33215,7 +33588,7 @@ function skippedTeamContextResult(env) {
33215
33588
  return {
33216
33589
  status: "skipped",
33217
33590
  privacyMode,
33218
- reason: privacyMode === "local-only" ? "Cloud sync skipped because privacy mode is local-only." : "Cloud sync skipped because BRIEF_API_URL or BRIEF_API_KEY is missing."
33591
+ reason: privacyMode === "local-only" ? "Cloud sync skipped because privacy mode is local-only." : "Cloud sync skipped because this device is not connected. Create a browser-authorized connection from the Brief app, then restart the agent."
33219
33592
  };
33220
33593
  }
33221
33594
  function normalizeApiUrl(apiUrl) {
@@ -33639,9 +34012,9 @@ function issuesValue(value) {
33639
34012
  if (typeof item === "string") return item;
33640
34013
  if (!item || typeof item !== "object") return "";
33641
34014
  const record = item;
33642
- const path20 = Array.isArray(record.path) ? record.path.join(".") : "";
34015
+ const path21 = Array.isArray(record.path) ? record.path.join(".") : "";
33643
34016
  const message = stringValue(record.message);
33644
- return [path20, message].filter(Boolean).join(": ");
34017
+ return [path21, message].filter(Boolean).join(": ");
33645
34018
  }).filter(Boolean).slice(0, 5).join("; ");
33646
34019
  }
33647
34020
  var syncTimeoutMs, canonicalBriefApiUrl, legacyBriefApiUrls;
@@ -34032,14 +34405,178 @@ var init_missionInputOptions = __esm({
34032
34405
  }
34033
34406
  });
34034
34407
 
34035
- // ../mcp/src/workspace.ts
34036
- import { existsSync as existsSync5, readFileSync as readFileSync3, statSync as statSync4 } from "node:fs";
34408
+ // ../mcp/src/cloudCredentials.ts
34409
+ import { chmodSync, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync5 } from "node:fs";
34410
+ import os from "node:os";
34037
34411
  import path14 from "node:path";
34412
+ async function bootstrapCloudCredentials(env = process.env) {
34413
+ if (env.BRIEF_PRIVACY_MODE === "local-only") {
34414
+ return { status: "skipped", reason: "local_only" };
34415
+ }
34416
+ if (env.BRIEF_API_URL && env.BRIEF_API_KEY) {
34417
+ return { status: "configured", source: "env" };
34418
+ }
34419
+ const credentialPath = credentialsPath(env);
34420
+ const requestedOrigin = originFrom(
34421
+ env.BRIEF_API_URL ?? env.BRIEF_CONNECT_URL
34422
+ );
34423
+ const requestedWorkspace = env.BRIEF_WORKSPACE_ID?.trim();
34424
+ const stored = readCredentialFile(credentialPath);
34425
+ const existing = stored.credentials.find((credential2) => {
34426
+ if (requestedOrigin && originFrom(credential2.apiUrl) !== requestedOrigin) {
34427
+ return false;
34428
+ }
34429
+ return !requestedWorkspace || credential2.workspaceId === requestedWorkspace;
34430
+ });
34431
+ if (existing) {
34432
+ applyCredential(env, existing);
34433
+ return { status: "configured", source: "local_store" };
34434
+ }
34435
+ const connectUrl = env.BRIEF_CONNECT_URL?.trim();
34436
+ const connectCode = env.BRIEF_CONNECT_CODE?.trim();
34437
+ if (!connectUrl || !connectCode) {
34438
+ return {
34439
+ status: "skipped",
34440
+ reason: "no_cloud_credential_or_pairing_setup"
34441
+ };
34442
+ }
34443
+ let response;
34444
+ try {
34445
+ response = await fetch(connectUrl, {
34446
+ method: "POST",
34447
+ headers: {
34448
+ accept: "application/json",
34449
+ "content-type": "application/json"
34450
+ },
34451
+ body: JSON.stringify({ code: connectCode }),
34452
+ signal: AbortSignal.timeout(1e4)
34453
+ });
34454
+ } catch {
34455
+ return {
34456
+ status: "failed",
34457
+ reason: "Could not reach the Brief connection service. Check your network and try a fresh setup from the Brief app."
34458
+ };
34459
+ }
34460
+ const body = await response.json().catch(() => null);
34461
+ if (!response.ok || !body?.ok || !body.apiUrl || !body.apiKey || !apiKeyPattern.test(body.apiKey) || !body.workspaceId || !body.privacyMode) {
34462
+ return {
34463
+ status: "failed",
34464
+ reason: body?.message ?? "This Brief setup link is expired or already used. Create a fresh connection from the Brief app."
34465
+ };
34466
+ }
34467
+ const credential = {
34468
+ apiUrl: body.apiUrl,
34469
+ apiKey: body.apiKey,
34470
+ workspaceId: body.workspaceId,
34471
+ privacyMode: body.privacyMode,
34472
+ ...body.connectorId ? { connectorId: body.connectorId } : {},
34473
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
34474
+ };
34475
+ try {
34476
+ writeCredentialFile(credentialPath, [
34477
+ ...stored.credentials.filter(
34478
+ (item) => item.workspaceId !== credential.workspaceId || originFrom(item.apiUrl) !== originFrom(credential.apiUrl)
34479
+ ),
34480
+ credential
34481
+ ]);
34482
+ } catch {
34483
+ return {
34484
+ status: "failed",
34485
+ reason: "Brief connected, but could not save the local credential. Check permissions for the local Brief config directory and try again."
34486
+ };
34487
+ }
34488
+ applyCredential(env, credential);
34489
+ return { status: "configured", source: "pairing" };
34490
+ }
34491
+ function forgetCloudCredentials(env = process.env) {
34492
+ const credentialPath = credentialsPath(env);
34493
+ const stored = readCredentialFile(credentialPath);
34494
+ const requestedOrigin = originFrom(
34495
+ env.BRIEF_API_URL ?? env.BRIEF_CONNECT_URL
34496
+ );
34497
+ const requestedWorkspace = env.BRIEF_WORKSPACE_ID?.trim();
34498
+ const remaining = stored.credentials.filter((credential) => {
34499
+ if (requestedOrigin && originFrom(credential.apiUrl) !== requestedOrigin) {
34500
+ return true;
34501
+ }
34502
+ return Boolean(
34503
+ requestedWorkspace && credential.workspaceId !== requestedWorkspace
34504
+ );
34505
+ });
34506
+ writeCredentialFile(credentialPath, remaining);
34507
+ }
34508
+ function defaultCredentialsPath(env = process.env) {
34509
+ return credentialsPath(env);
34510
+ }
34511
+ function credentialsPath(env) {
34512
+ return env.BRIEF_CREDENTIALS_FILE?.trim() || path14.join(
34513
+ env.XDG_CONFIG_HOME?.trim() || path14.join(os.homedir(), ".config"),
34514
+ "runbrief",
34515
+ "credentials.json"
34516
+ );
34517
+ }
34518
+ function readCredentialFile(filePath) {
34519
+ try {
34520
+ const parsed = JSON.parse(readFileSync3(filePath, "utf8"));
34521
+ if (parsed.version !== 1 || !Array.isArray(parsed.credentials)) {
34522
+ return { version: 1, credentials: [] };
34523
+ }
34524
+ return {
34525
+ version: 1,
34526
+ credentials: parsed.credentials.filter(isStoredCredential)
34527
+ };
34528
+ } catch {
34529
+ return { version: 1, credentials: [] };
34530
+ }
34531
+ }
34532
+ function writeCredentialFile(filePath, credentials) {
34533
+ mkdirSync2(path14.dirname(filePath), { recursive: true, mode: 448 });
34534
+ chmodSync(path14.dirname(filePath), 448);
34535
+ writeFileSync5(
34536
+ filePath,
34537
+ `${JSON.stringify({ version: 1, credentials }, null, 2)}
34538
+ `,
34539
+ { encoding: "utf8", mode: 384 }
34540
+ );
34541
+ chmodSync(filePath, 384);
34542
+ }
34543
+ function applyCredential(env, credential) {
34544
+ env.BRIEF_API_URL = credential.apiUrl;
34545
+ env.BRIEF_API_KEY = credential.apiKey;
34546
+ env.BRIEF_WORKSPACE_ID = credential.workspaceId;
34547
+ env.BRIEF_PRIVACY_MODE = credential.privacyMode;
34548
+ }
34549
+ function originFrom(value) {
34550
+ if (!value) return void 0;
34551
+ try {
34552
+ return new URL(value).origin;
34553
+ } catch {
34554
+ return void 0;
34555
+ }
34556
+ }
34557
+ function isStoredCredential(value) {
34558
+ if (!value || typeof value !== "object") return false;
34559
+ const candidate = value;
34560
+ return Boolean(
34561
+ typeof candidate.apiUrl === "string" && typeof candidate.apiKey === "string" && apiKeyPattern.test(candidate.apiKey) && typeof candidate.workspaceId === "string" && typeof candidate.privacyMode === "string" && typeof candidate.updatedAt === "string"
34562
+ );
34563
+ }
34564
+ var apiKeyPattern;
34565
+ var init_cloudCredentials = __esm({
34566
+ "../mcp/src/cloudCredentials.ts"() {
34567
+ "use strict";
34568
+ apiKeyPattern = /^brf_(live|test)_[a-zA-Z0-9]{8,}_[a-zA-Z0-9]{24,}$/;
34569
+ }
34570
+ });
34571
+
34572
+ // ../mcp/src/workspace.ts
34573
+ import { existsSync as existsSync5, readFileSync as readFileSync4, statSync as statSync4 } from "node:fs";
34574
+ import path15 from "node:path";
34038
34575
  function resolveRepoRoot(repoPath, options2 = {}) {
34039
34576
  return resolveRepoRootDetails(repoPath, options2).repoRoot;
34040
34577
  }
34041
34578
  function resolveRepoRootDetails(repoPath, options2 = {}) {
34042
- const cwd = path14.resolve(options2.cwd ?? process.cwd());
34579
+ const cwd = path15.resolve(options2.cwd ?? process.cwd());
34043
34580
  const env = options2.env ?? process.env;
34044
34581
  const cwdResolution = resolveExistingRoot(cwd, "cwd");
34045
34582
  const cwdIsCustomerWorktree = isGitWorktree(cwdResolution.repoRoot) && !looksLikeBriefSourceCheckout(cwdResolution.repoRoot);
@@ -34053,7 +34590,7 @@ function resolveRepoRootDetails(repoPath, options2 = {}) {
34053
34590
  ]);
34054
34591
  const explicitRepoPath = repoPath?.trim();
34055
34592
  if (explicitRepoPath) {
34056
- const explicitPathKind = path14.isAbsolute(explicitRepoPath) ? "absolute" : "relative";
34593
+ const explicitPathKind = path15.isAbsolute(explicitRepoPath) ? "absolute" : "relative";
34057
34594
  if (explicitPathKind === "relative" && envCandidates.length > 1) {
34058
34595
  return {
34059
34596
  ...cwdResolution,
@@ -34065,7 +34602,7 @@ function resolveRepoRootDetails(repoPath, options2 = {}) {
34065
34602
  const relativeBase = cwdIsCustomerWorktree ? cwdResolution.repoRoot : envCandidates[0]?.repoRoot ?? cwd;
34066
34603
  return {
34067
34604
  ...resolveExistingRoot(
34068
- path14.resolve(
34605
+ path15.resolve(
34069
34606
  explicitPathKind === "absolute" ? cwd : relativeBase,
34070
34607
  explicitRepoPath
34071
34608
  ),
@@ -34103,7 +34640,7 @@ function resolveFromEnvVars(cwd, env, envVars, options2 = {}) {
34103
34640
  for (const envVar of envVars) {
34104
34641
  const value = cleanWorkspacePath(env[envVar]);
34105
34642
  if (!value) continue;
34106
- const start = path14.resolve(cwd, value);
34643
+ const start = path15.resolve(cwd, value);
34107
34644
  if (existsSync5(start)) {
34108
34645
  const resolution = resolveExistingRoot(start, "environment", envVar);
34109
34646
  if (options2.skipBriefSource && looksLikeBriefSourceCheckout(resolution.repoRoot)) {
@@ -34116,7 +34653,7 @@ function resolveFromEnvVars(cwd, env, envVars, options2 = {}) {
34116
34653
  }
34117
34654
  function repoTargetHealth(resolution, options2 = {}) {
34118
34655
  const env = options2.env ?? process.env;
34119
- const cwd = path14.resolve(options2.cwd ?? process.cwd());
34656
+ const cwd = path15.resolve(options2.cwd ?? process.cwd());
34120
34657
  const gitRoot = runGit(resolution.repoRoot, ["rev-parse", "--show-toplevel"]);
34121
34658
  const evidence = [
34122
34659
  ...rootEvidence(resolution, cwd, env),
@@ -34177,7 +34714,7 @@ function repoTargetHealth(resolution, options2 = {}) {
34177
34714
  };
34178
34715
  }
34179
34716
  function resolveExistingRoot(start, source, envVar) {
34180
- const gitStart = existsSync5(start) && !statSync4(start).isDirectory() ? path14.dirname(start) : start;
34717
+ const gitStart = existsSync5(start) && !statSync4(start).isDirectory() ? path15.dirname(start) : start;
34181
34718
  const gitRoot = runGit(gitStart, ["rev-parse", "--show-toplevel"]);
34182
34719
  return {
34183
34720
  repoRoot: gitRoot && existsSync5(gitRoot) ? gitRoot : start,
@@ -34228,12 +34765,12 @@ function preferPinnedRepoRoot(env) {
34228
34765
  return env.BRIEF_REPO_ROOT_MODE === "pinned" || env.BRIEF_REPO_ROOT_PINNED === "true";
34229
34766
  }
34230
34767
  function looksLikeBriefSourceCheckout(repoRoot) {
34231
- if (!existsSync5(path14.join(repoRoot, "packages", "mcp", "src", "server.ts"))) {
34768
+ if (!existsSync5(path15.join(repoRoot, "packages", "mcp", "src", "server.ts"))) {
34232
34769
  return false;
34233
34770
  }
34234
34771
  try {
34235
34772
  const pkg = JSON.parse(
34236
- readFileSync3(path14.join(repoRoot, "package.json"), "utf8")
34773
+ readFileSync4(path15.join(repoRoot, "package.json"), "utf8")
34237
34774
  );
34238
34775
  return pkg.name === "brief";
34239
34776
  } catch {
@@ -34262,7 +34799,7 @@ var init_workspace = __esm({
34262
34799
  });
34263
34800
 
34264
34801
  // ../mcp/src/connectorReadiness.ts
34265
- import path15 from "node:path";
34802
+ import path16 from "node:path";
34266
34803
  function connectorReadiness(input) {
34267
34804
  const runtimeLabel = input.mode === "cli" ? "Brief CLI process" : "Brief MCP connector";
34268
34805
  const installStatusTool = input.mode === "cli" ? "install-status or brief.install_status" : "brief.install_status";
@@ -34356,8 +34893,8 @@ function connectorReadiness(input) {
34356
34893
  };
34357
34894
  }
34358
34895
  function briefSourceCheckoutMismatch(input) {
34359
- const repoRoot = input.repoRoot ? path15.resolve(input.repoRoot) : void 0;
34360
- const sourceRoot = input.freshness.sourceRoot ? path15.resolve(input.freshness.sourceRoot) : void 0;
34896
+ const repoRoot = input.repoRoot ? path16.resolve(input.repoRoot) : void 0;
34897
+ const sourceRoot = input.freshness.sourceRoot ? path16.resolve(input.freshness.sourceRoot) : void 0;
34361
34898
  if (!repoRoot || !sourceRoot) return void 0;
34362
34899
  if (samePath(repoRoot, sourceRoot)) return void 0;
34363
34900
  if (!looksLikeBriefSourceCheckout(repoRoot)) return void 0;
@@ -34372,7 +34909,7 @@ function briefSourceCheckoutMismatch(input) {
34372
34909
  };
34373
34910
  }
34374
34911
  function samePath(left, right) {
34375
- return path15.normalize(left) === path15.normalize(right);
34912
+ return path16.normalize(left) === path16.normalize(right);
34376
34913
  }
34377
34914
  var init_connectorReadiness = __esm({
34378
34915
  "../mcp/src/connectorReadiness.ts"() {
@@ -34506,8 +35043,8 @@ var init_connectorRecovery = __esm({
34506
35043
  });
34507
35044
 
34508
35045
  // ../mcp/src/serverFreshness.ts
34509
- import { existsSync as existsSync6, readdirSync as readdirSync2, readFileSync as readFileSync4, statSync as statSync5 } from "node:fs";
34510
- import path16 from "node:path";
35046
+ import { existsSync as existsSync6, readdirSync as readdirSync2, readFileSync as readFileSync5, statSync as statSync5 } from "node:fs";
35047
+ import path17 from "node:path";
34511
35048
  function mcpServerFreshness(options2) {
34512
35049
  const cwd = options2.cwd ?? process.cwd();
34513
35050
  const now = options2.now ?? /* @__PURE__ */ new Date();
@@ -34518,10 +35055,12 @@ function mcpServerFreshness(options2) {
34518
35055
  cwd,
34519
35056
  sourceRoot: options2.sourceRoot
34520
35057
  });
35058
+ const sourceKind = options2.distribution ?? connectorDistributionFor(options2.entrypoint);
34521
35059
  const watchedGlobs = watchedSourceGlobs();
34522
35060
  const base = {
34523
35061
  serverStartedAt: options2.serverStartedAt.toISOString(),
34524
35062
  checkedAt: now.toISOString(),
35063
+ sourceKind,
34525
35064
  process: {
34526
35065
  pid: process.pid,
34527
35066
  cwd,
@@ -34536,6 +35075,15 @@ function mcpServerFreshness(options2) {
34536
35075
  changedSinceStartCount: 0
34537
35076
  };
34538
35077
  if (!sourceRoot) {
35078
+ if (sourceKind === "package") {
35079
+ return {
35080
+ status: "current",
35081
+ ...base,
35082
+ action: freshnessAction("current"),
35083
+ summary: "The published Brief connector is running from an immutable package; source-checkout freshness checks are not applicable.",
35084
+ next: "Continue using Brief normally. Update the package when you want a newer connector release."
35085
+ };
35086
+ }
34539
35087
  return {
34540
35088
  status: "unknown",
34541
35089
  ...base,
@@ -34576,6 +35124,21 @@ function mcpServerFreshness(options2) {
34576
35124
  next: status === "restart_recommended" ? "Call brief.restart_connector from the coding agent, then call brief.install_status again and confirm serverFreshness.status is current." : "Continue using Brief normally."
34577
35125
  };
34578
35126
  }
35127
+ function connectorDistributionFor(entrypoint) {
35128
+ const configured = process.env.BRIEF_CONNECTOR_DISTRIBUTION;
35129
+ if (configured === "package" || configured === "source_checkout") {
35130
+ return configured;
35131
+ }
35132
+ const normalizedEntrypoint = path17.normalize(entrypoint);
35133
+ if (normalizedEntrypoint.includes(
35134
+ `${path17.sep}node_modules${path17.sep}runbrief${path17.sep}`
35135
+ ) || normalizedEntrypoint.includes(
35136
+ `${path17.sep}packages${path17.sep}runbrief${path17.sep}`
35137
+ )) {
35138
+ return "package";
35139
+ }
35140
+ return "unknown";
35141
+ }
34579
35142
  function freshnessAction(status) {
34580
35143
  if (status === "current") {
34581
35144
  return {
@@ -34610,30 +35173,30 @@ function findBriefSourceRoot(options2) {
34610
35173
  const candidates = [
34611
35174
  options2.sourceRoot,
34612
35175
  process.env.BRIEF_SOURCE_ROOT,
34613
- options2.entrypoint ? path16.dirname(options2.entrypoint) : void 0,
35176
+ options2.entrypoint ? path17.dirname(options2.entrypoint) : void 0,
34614
35177
  options2.cwd
34615
35178
  ].filter((candidate) => Boolean(candidate));
34616
35179
  for (const candidate of candidates) {
34617
- const root = walkUpForBriefRoot(path16.resolve(candidate));
35180
+ const root = walkUpForBriefRoot(path17.resolve(candidate));
34618
35181
  if (root) return root;
34619
35182
  }
34620
35183
  return void 0;
34621
35184
  }
34622
35185
  function walkUpForBriefRoot(start) {
34623
- let current = existsSync6(start) && statSafe(start)?.isFile() ? path16.dirname(start) : start;
35186
+ let current = existsSync6(start) && statSafe(start)?.isFile() ? path17.dirname(start) : start;
34624
35187
  while (true) {
34625
35188
  if (isBriefSourceRoot(current)) return current;
34626
- const parent = path16.dirname(current);
35189
+ const parent = path17.dirname(current);
34627
35190
  if (parent === current) return void 0;
34628
35191
  current = parent;
34629
35192
  }
34630
35193
  }
34631
35194
  function isBriefSourceRoot(candidate) {
34632
- const packageJsonPath = path16.join(candidate, "package.json");
35195
+ const packageJsonPath = path17.join(candidate, "package.json");
34633
35196
  if (!existsSync6(packageJsonPath)) return false;
34634
35197
  const packageJson = readJson(packageJsonPath);
34635
35198
  if (packageJson?.name !== "brief") return false;
34636
- return existsSync6(path16.join(candidate, "packages", "mcp")) && existsSync6(path16.join(candidate, "packages", "core")) && existsSync6(path16.join(candidate, "packages", "contracts"));
35199
+ return existsSync6(path17.join(candidate, "packages", "mcp")) && existsSync6(path17.join(candidate, "packages", "core")) && existsSync6(path17.join(candidate, "packages", "contracts"));
34637
35200
  }
34638
35201
  function watchedSourceGlobs() {
34639
35202
  return [
@@ -34671,7 +35234,7 @@ function collectWatchedFiles(sourceRoot, maxWatchedFiles) {
34671
35234
  truncated = true;
34672
35235
  break;
34673
35236
  }
34674
- const absolutePath = path16.join(sourceRoot, relativePath);
35237
+ const absolutePath = path17.join(sourceRoot, relativePath);
34675
35238
  const stats = statSafe(absolutePath);
34676
35239
  if (!stats) continue;
34677
35240
  if (stats.isDirectory()) {
@@ -34688,7 +35251,7 @@ function collectWatchedFiles(sourceRoot, maxWatchedFiles) {
34688
35251
  break;
34689
35252
  }
34690
35253
  files.push({
34691
- path: normalizePath(path16.relative(sourceRoot, absolutePath)),
35254
+ path: normalizePath(path17.relative(sourceRoot, absolutePath)),
34692
35255
  modifiedAtMs: stats.mtimeMs
34693
35256
  });
34694
35257
  }
@@ -34699,7 +35262,7 @@ function walkFiles2(sourceRoot, directory, files, maxWatchedFiles) {
34699
35262
  const entries = readdirSafe(directory);
34700
35263
  for (const entry of entries) {
34701
35264
  if (files.length >= maxWatchedFiles) return { truncated: true };
34702
- const absolutePath = path16.join(directory, entry.name);
35265
+ const absolutePath = path17.join(directory, entry.name);
34703
35266
  if (entry.isDirectory()) {
34704
35267
  if (shouldSkipDirectory(entry.name)) continue;
34705
35268
  const result = walkFiles2(
@@ -34715,7 +35278,7 @@ function walkFiles2(sourceRoot, directory, files, maxWatchedFiles) {
34715
35278
  const stats = statSafe(absolutePath);
34716
35279
  if (!stats) continue;
34717
35280
  files.push({
34718
- path: normalizePath(path16.relative(sourceRoot, absolutePath)),
35281
+ path: normalizePath(path17.relative(sourceRoot, absolutePath)),
34719
35282
  modifiedAtMs: stats.mtimeMs
34720
35283
  });
34721
35284
  }
@@ -34734,13 +35297,13 @@ function shouldSkipDirectory(name) {
34734
35297
  );
34735
35298
  }
34736
35299
  function shouldWatchFile(filePath) {
34737
- const base = path16.basename(filePath);
35300
+ const base = path17.basename(filePath);
34738
35301
  if (base.endsWith(".tsbuildinfo")) return false;
34739
35302
  if (base === ".DS_Store") return false;
34740
35303
  return true;
34741
35304
  }
34742
35305
  function normalizePath(value) {
34743
- return value.split(path16.sep).join("/");
35306
+ return value.split(path17.sep).join("/");
34744
35307
  }
34745
35308
  function statSafe(filePath) {
34746
35309
  try {
@@ -34758,7 +35321,7 @@ function readdirSafe(directory) {
34758
35321
  }
34759
35322
  function readJson(filePath) {
34760
35323
  try {
34761
- return JSON.parse(readFileSync4(filePath, "utf8"));
35324
+ return JSON.parse(readFileSync5(filePath, "utf8"));
34762
35325
  } catch {
34763
35326
  return void 0;
34764
35327
  }
@@ -34821,9 +35384,10 @@ var init_serverInstructions = __esm({
34821
35384
  "../mcp/src/serverInstructions.ts"() {
34822
35385
  "use strict";
34823
35386
  briefMcpInstructions = [
34824
- "Brief is the default repo-context, reuse, review, and handoff companion for non-trivial coding work. Do not wait for the user to repeat 'use Brief'.",
35387
+ "Brief is the default repo-context, reuse, review, and handoff companion for non-trivial coding work. This is the normal path, not an optional reminder. Do not wait for the user to repeat 'use Brief'.",
34825
35388
  "At the first Brief-relevant task in a session, call brief.install_status once and confirm the repo target and connector freshness.",
34826
- "Before non-trivial edits, call brief.next with the user's actual request and follow its compact recommendation. Keep micro changes lightweight.",
35389
+ "Local mode is the default and does not require an account. Team mode is optional: it adds approved shared guidance through browser-authorized sync. If team sync is unavailable, continue local repo work and mention the team-sync limitation only when it affects the task.",
35390
+ "When the user asks for non-trivial work, call brief.next with the user's actual request before inspecting or editing files, read the returned compact workPacket, and follow its recommendation before editing. Keep micro changes lightweight.",
34827
35391
  "Before creating a helper, component, route, workflow, command, or skill, use the returned reuse candidates or call brief.reference_implementations and brief.codebase_map.",
34828
35392
  "Refresh Brief context when the task, files, risk, or validation plan changes.",
34829
35393
  "Before a meaningful handoff, call brief.review_diff. Save and verify a handoff only for multi-step, high-risk, or durable work.",
@@ -34861,7 +35425,7 @@ function compactBlockedInstallStatus(input) {
34861
35425
  fresh: input.serverFreshness.status === "current",
34862
35426
  summary: input.connectorReadiness.summary
34863
35427
  },
34864
- next: unique3(input.repoTarget.next).slice(0, 2),
35428
+ next: unique4(input.repoTarget.next).slice(0, 2),
34865
35429
  detailHint: "Open a Git repository or pass its path, then run install-status again."
34866
35430
  };
34867
35431
  }
@@ -34871,12 +35435,14 @@ function compactInstallStatus(input) {
34871
35435
  const degraded = !blocked && (Boolean(cloudIssue) || input.readiness.verdict !== "agent_ready" || input.workflowWiring.status === "missing" || input.workflowWiring.status === "manual_workflow_only");
34872
35436
  const stats = input.scan.stats;
34873
35437
  const mcpContractActive = input.serverInstructionsActive && input.connectorReadiness.safeToUseBrief;
35438
+ const teamSyncStatus = input.cloudSync.configured ? input.teamContext.status === "failed" ? "unavailable" : input.teamContext.status === "fetched" ? "connected" : "checking" : "not_connected";
35439
+ const connectionMode = teamSyncStatus === "unavailable" ? "team_degraded" : teamSyncStatus === "connected" ? "team" : "local";
34874
35440
  const totalMapCandidates = stats.mapTotalCandidateCount ?? stats.mapNodeCount + (stats.mapOmittedCount ?? 0);
34875
35441
  const mapCoveragePercent = totalMapCandidates ? Math.round(stats.mapNodeCount / totalMapCandidates * 100) : 100;
34876
- const repoGuidanceSignals = unique3(
35442
+ const repoGuidanceSignals = unique4(
34877
35443
  input.workflowWiring.surfaces.filter((surface) => surface.kind === "always_on_guidance").flatMap((surface) => surface.signals)
34878
35444
  );
34879
- const next = unique3([
35445
+ const next = unique4([
34880
35446
  ...blocked ? [input.connectorReadiness.firstAction] : [],
34881
35447
  ...cloudIssue ? [cloudIssue] : [],
34882
35448
  ...input.readiness.nextBestActions,
@@ -34899,9 +35465,16 @@ function compactInstallStatus(input) {
34899
35465
  status: input.connectorReadiness.status,
34900
35466
  safeToUse: input.connectorReadiness.safeToUseBrief,
34901
35467
  fresh: input.serverFreshness.status === "current",
35468
+ distribution: input.serverFreshness.sourceKind,
34902
35469
  sourceRoot: input.serverFreshness.sourceRoot,
34903
35470
  summary: input.connectorReadiness.summary
34904
35471
  },
35472
+ connection: {
35473
+ mode: connectionMode,
35474
+ localAvailable: input.connectorReadiness.safeToUseBrief,
35475
+ teamSync: teamSyncStatus,
35476
+ summary: connectionMode === "team_degraded" ? "Local repo work is available. Team sync needs attention and will not block local analysis." : connectionMode === "team" ? "Local repo work and approved team context are available." : "Local repo work is available without an account. Team sync is optional."
35477
+ },
34905
35478
  cloud: {
34906
35479
  configured: input.cloudSync.configured,
34907
35480
  privacyMode: input.cloudSync.privacyMode,
@@ -34923,7 +35496,7 @@ function compactInstallStatus(input) {
34923
35496
  score: input.readiness.score,
34924
35497
  verdict: input.readiness.verdict,
34925
35498
  summary: input.readiness.summary,
34926
- gaps: unique3([
35499
+ gaps: unique4([
34927
35500
  ...input.readiness.quickWins,
34928
35501
  ...input.readiness.nextBestActions
34929
35502
  ]).slice(0, 3)
@@ -34950,7 +35523,7 @@ function compactInstallStatus(input) {
34950
35523
  detailHint: 'Use detail: "expanded" in MCP or --full in the CLI only when diagnosing setup, workflow wiring, or receipt history.'
34951
35524
  };
34952
35525
  }
34953
- function unique3(values) {
35526
+ function unique4(values) {
34954
35527
  return [...new Set(values.filter((value) => value.trim()))];
34955
35528
  }
34956
35529
  var briefConnectorVersion;
@@ -34958,7 +35531,7 @@ var init_installStatus = __esm({
34958
35531
  "../mcp/src/installStatus.ts"() {
34959
35532
  "use strict";
34960
35533
  init_serverInstructions();
34961
- briefConnectorVersion = "0.1.1";
35534
+ briefConnectorVersion = "0.1.3";
34962
35535
  }
34963
35536
  });
34964
35537
 
@@ -34978,27 +35551,27 @@ function buildCompactStartEnvelope(input) {
34978
35551
  route: {
34979
35552
  tool: start.intent.tool,
34980
35553
  ...start.intent.recipe ? { recipe: start.intent.recipe } : {},
34981
- ...start.intent.autonomy ? { autonomy: start.intent.autonomy } : {},
34982
- ...start.intent.reviewBar ? { reviewBar: start.intent.reviewBar } : {}
35554
+ ...start.intent.autonomy ? { autonomy: start.intent.autonomy } : {}
34983
35555
  },
35556
+ workPacket: (() => {
35557
+ const packet = buildAgentWorkPacket({
35558
+ task: start.normalizedInput.task,
35559
+ beforeYouCode,
35560
+ interpretation: start.summary,
35561
+ nextAction: input.next
35562
+ });
35563
+ return {
35564
+ compactText: packet.compactText
35565
+ };
35566
+ })(),
34984
35567
  inspectFirst: beforeYouCode.inspectFirst.slice(0, 3).map((item) => ({
34985
35568
  path: item.path,
34986
35569
  why: compactLine(item.why, 160)
34987
35570
  })),
34988
- reuse: beforeYouCode.reuseFirst.candidates.slice(0, 2).map((item) => ({
34989
- path: item.path,
34990
- action: compactLine(item.action, 160)
34991
- })),
34992
- reuseDecision: compactLine(beforeYouCode.reuseFirst.decision, 240),
34993
- sharedContext: summarizeBeforeYouCodeSharedContext(
34994
- beforeYouCode.teamGuidance
34995
- ),
34996
- referenceFirst: summarizeBeforeYouCodeReferences(beforeYouCode),
34997
35571
  validation: beforeYouCode.likelyTests.slice(0, 3),
34998
35572
  result: input.result,
34999
- stopSignals: beforeYouCode.stopSignals.slice(0, 2),
35000
- next: compactLine(input.next, 320),
35001
- detailHint: 'Request expanded detail only when needed: CLI --json/--full or MCP detail: "expanded".'
35573
+ next: compactLine(input.next, 240),
35574
+ detailHint: 'Expand with MCP detail: "expanded" or CLI --full.'
35002
35575
  };
35003
35576
  }
35004
35577
  function formatCompactStartText(envelope) {
@@ -35013,26 +35586,13 @@ function formatCompactStartText(envelope) {
35013
35586
  `Target: ${target} | ${envelope.target.dirty ? "dirty" : "clean"}`,
35014
35587
  `Job: ${envelope.job}`,
35015
35588
  `Route: ${route}`,
35016
- "Inspect first:",
35017
- ...listLines(
35018
- envelope.inspectFirst.map((item) => `${item.path} - ${item.why}`)
35019
- ),
35020
- "Reuse:",
35021
- ...listLines(envelope.reuse.map((item) => `${item.path} - ${item.action}`)),
35022
- `Reuse decision: ${envelope.reuseDecision}`,
35023
- `Shared context: ${envelope.sharedContext.status === "applied" ? envelope.sharedContext.applied.map((item) => item.label).join(", ") : envelope.sharedContext.status}`,
35024
- `Saved reference: ${envelope.referenceFirst.status === "matched" ? envelope.referenceFirst.references.map((reference) => reference.title).join(", ") : envelope.referenceFirst.status}`,
35025
- "Validate:",
35026
- ...listLines(envelope.validation),
35589
+ envelope.workPacket.compactText,
35027
35590
  `Result: ${envelope.result.summary}`,
35028
35591
  `Next: ${envelope.next}`,
35029
35592
  `Details: ${envelope.detailHint}`
35030
35593
  ];
35031
35594
  return lines.join("\n");
35032
35595
  }
35033
- function listLines(items) {
35034
- return items.length > 0 ? items.map((item) => `- ${item}`) : ["- none"];
35035
- }
35036
35596
  function compactLine(value, maxLength) {
35037
35597
  const normalized = value.replace(/\s+/g, " ").trim();
35038
35598
  if (normalized.length <= maxLength) return normalized;
@@ -35155,15 +35715,15 @@ var init_startSummary = __esm({
35155
35715
  });
35156
35716
 
35157
35717
  // ../mcp/src/wikiInit.ts
35158
- import { existsSync as existsSync7, mkdirSync as mkdirSync2, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "node:fs";
35159
- import path17 from "node:path";
35718
+ import { existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync6 } from "node:fs";
35719
+ import path18 from "node:path";
35160
35720
  function writeWikiInitArtifacts(repoRoot, wiki, options2) {
35161
- const wikiDir = path17.join(repoRoot, ".brief", "wiki");
35162
- const pagesDir = path17.join(wikiDir, "pages");
35163
- mkdirSync2(pagesDir, { recursive: true });
35721
+ const wikiDir = path18.join(repoRoot, ".brief", "wiki");
35722
+ const pagesDir = path18.join(wikiDir, "pages");
35723
+ mkdirSync3(pagesDir, { recursive: true });
35164
35724
  const pageArtifacts = wiki.pages.map((page2) => {
35165
- const pagePath = path17.join(pagesDir, `${wikiPageSlug(page2)}.md`);
35166
- writeFileSync5(pagePath, renderWikiPageMarkdown(wiki, page2));
35725
+ const pagePath = path18.join(pagesDir, `${wikiPageSlug(page2)}.md`);
35726
+ writeFileSync6(pagePath, renderWikiPageMarkdown(wiki, page2));
35167
35727
  return {
35168
35728
  id: page2.id,
35169
35729
  title: page2.title,
@@ -35172,32 +35732,32 @@ function writeWikiInitArtifacts(repoRoot, wiki, options2) {
35172
35732
  quality: page2.quality.status
35173
35733
  };
35174
35734
  });
35175
- const readmePath = path17.join(wikiDir, "README.md");
35176
- writeFileSync5(readmePath, renderWikiReadme(wiki, pageArtifacts));
35177
- const manifestPath = path17.join(wikiDir, "manifest.json");
35178
- writeFileSync5(
35735
+ const readmePath = path18.join(wikiDir, "README.md");
35736
+ writeFileSync6(readmePath, renderWikiReadme(wiki, pageArtifacts));
35737
+ const manifestPath = path18.join(wikiDir, "manifest.json");
35738
+ writeFileSync6(
35179
35739
  manifestPath,
35180
35740
  `${JSON.stringify(wikiInitManifest(wiki, pageArtifacts), null, 2)}
35181
35741
  `
35182
35742
  );
35183
- const workflowTemplatePath = path17.join(
35743
+ const workflowTemplatePath = path18.join(
35184
35744
  wikiDir,
35185
35745
  "brief-wiki-refresh.github-actions.yml"
35186
35746
  );
35187
- writeFileSync5(workflowTemplatePath, renderWikiRefreshWorkflow());
35747
+ writeFileSync6(workflowTemplatePath, renderWikiRefreshWorkflow());
35188
35748
  const agentFiles = options2.writeAgentFiles ? ["AGENTS.md", "CLAUDE.md"].map(
35189
35749
  (fileName) => upsertWikiAgentInstructions(repoRoot, fileName)
35190
35750
  ) : [];
35191
35751
  let githubWorkflow2;
35192
35752
  if (options2.writeGithubWorkflow) {
35193
- const workflowPath = path17.join(
35753
+ const workflowPath = path18.join(
35194
35754
  repoRoot,
35195
35755
  ".github",
35196
35756
  "workflows",
35197
35757
  "brief-wiki-refresh.yml"
35198
35758
  );
35199
- mkdirSync2(path17.dirname(workflowPath), { recursive: true });
35200
- writeFileSync5(workflowPath, renderWikiRefreshWorkflow({ active: true }));
35759
+ mkdirSync3(path18.dirname(workflowPath), { recursive: true });
35760
+ writeFileSync6(workflowPath, renderWikiRefreshWorkflow({ active: true }));
35201
35761
  githubWorkflow2 = {
35202
35762
  path: repoRelativePath(repoRoot, workflowPath),
35203
35763
  status: "written",
@@ -35387,12 +35947,12 @@ function wikiInitManifest(wiki, pageArtifacts) {
35387
35947
  };
35388
35948
  }
35389
35949
  function upsertWikiAgentInstructions(repoRoot, fileName) {
35390
- const filePath = path17.join(repoRoot, fileName);
35950
+ const filePath = path18.join(repoRoot, fileName);
35391
35951
  const status = existsSync7(filePath) ? "updated" : "created";
35392
- const existing = status === "updated" ? readFileSync5(filePath, "utf8") : "";
35952
+ const existing = status === "updated" ? readFileSync6(filePath, "utf8") : "";
35393
35953
  const initial = existing || `# ${fileName}
35394
35954
  `;
35395
- writeFileSync5(filePath, upsertMarkedBlock(initial, wikiAgentBlock()));
35955
+ writeFileSync6(filePath, upsertMarkedBlock(initial, wikiAgentBlock()));
35396
35956
  return {
35397
35957
  path: repoRelativePath(repoRoot, filePath),
35398
35958
  status,
@@ -35464,7 +36024,7 @@ function renderWikiRefreshWorkflow(options2 = {}) {
35464
36024
  " with:",
35465
36025
  " node-version: 22",
35466
36026
  " - name: Refresh wiki",
35467
- ' run: npx -y runbrief@0.1.1 wiki init --repo "$GITHUB_WORKSPACE" --update --no-agent-files',
36027
+ ' run: npx -y runbrief@0.1.3 wiki init --repo "$GITHUB_WORKSPACE" --update --no-agent-files',
35468
36028
  " - name: Open refresh PR",
35469
36029
  " uses: peter-evans/create-pull-request@22a9089034f40e5a961c8808d113e2c98fb63676 # v7.0.11",
35470
36030
  " with:",
@@ -35485,7 +36045,7 @@ function markdownList(items) {
35485
36045
  return items.map((item) => `- ${item}`).join("\n");
35486
36046
  }
35487
36047
  function repoRelativePath(repoRoot, filePath) {
35488
- return path17.relative(repoRoot, filePath).split(path17.sep).join("/");
36048
+ return path18.relative(repoRoot, filePath).split(path18.sep).join("/");
35489
36049
  }
35490
36050
  function wikiReadmeRelativePath(repoRelativeFilePath) {
35491
36051
  return repoRelativeFilePath.replace(/^\.brief\/wiki\//, "");
@@ -35564,7 +36124,7 @@ function resolveHandoffAutofill(repoRoot, input) {
35564
36124
  sources,
35565
36125
  missionReceiptId,
35566
36126
  reviewReceiptId,
35567
- autoFilled: unique4(autoFilled)
36127
+ autoFilled: unique5(autoFilled)
35568
36128
  });
35569
36129
  }
35570
36130
  function bestMatchingReceipt(receipts, kind, input) {
@@ -35644,10 +36204,10 @@ function compactResult(result) {
35644
36204
  }
35645
36205
  function cleanValues(values, limit = 300) {
35646
36206
  if (!values) return void 0;
35647
- const cleaned = unique4(values.map((value) => value.trim()).filter(Boolean)).sort().slice(0, limit);
36207
+ const cleaned = unique5(values.map((value) => value.trim()).filter(Boolean)).sort().slice(0, limit);
35648
36208
  return cleaned.length > 0 ? cleaned : void 0;
35649
36209
  }
35650
- function unique4(values) {
36210
+ function unique5(values) {
35651
36211
  return [...new Set(values)];
35652
36212
  }
35653
36213
  function normalizeText(value) {
@@ -35704,35 +36264,35 @@ function compactCodebaseMapResponse(input) {
35704
36264
  };
35705
36265
  }
35706
36266
  function formatCompactCodebaseMap(input) {
35707
- const compact = compactCodebaseMapResponse(input);
36267
+ const compact2 = compactCodebaseMapResponse(input);
35708
36268
  const lines = [
35709
- `Codebase map: ${compact.summary}`,
35710
- `Target: ${compact.target.repoName} | ${compact.target.repoRoot}${compact.target.mapPartial ? " | partial map" : ""}`,
36269
+ `Codebase map: ${compact2.summary}`,
36270
+ `Target: ${compact2.target.repoName} | ${compact2.target.repoRoot}${compact2.target.mapPartial ? " | partial map" : ""}`,
35711
36271
  "Inspect first:",
35712
- ...listLines2(
35713
- compact.inspectFirst,
36272
+ ...listLines(
36273
+ compact2.inspectFirst,
35714
36274
  (item) => `${item.path} - ${item.reason}`,
35715
36275
  "No focused source matched; narrow the query before editing."
35716
36276
  ),
35717
36277
  "Reuse before creating:",
35718
- ...listLines2(
35719
- compact.reuse.candidates,
36278
+ ...listLines(
36279
+ compact2.reuse.candidates,
35720
36280
  (item) => `${item.path}${item.symbols.length > 0 ? ` - ${item.symbols.join(", ")}` : ""}`,
35721
36281
  "No reusable primitive is proven yet; run a narrower map query."
35722
36282
  ),
35723
36283
  "Validate:",
35724
- ...listLines2(
35725
- compact.validate,
36284
+ ...listLines(
36285
+ compact2.validate,
35726
36286
  (item) => item,
35727
36287
  "No focused validation command was found."
35728
36288
  ),
35729
- ...compact.warnings.length > 0 ? ["Warnings:", ...listLines2(compact.warnings, (item) => item, "")] : [],
36289
+ ...compact2.warnings.length > 0 ? ["Warnings:", ...listLines(compact2.warnings, (item) => item, "")] : [],
35730
36290
  "Next:",
35731
- ...listLines2(compact.next, (item) => item, "Inspect the first source.")
36291
+ ...listLines(compact2.next, (item) => item, "Inspect the first source.")
35732
36292
  ];
35733
36293
  return lines.slice(0, 30).join("\n");
35734
36294
  }
35735
- function listLines2(items, render, empty) {
36295
+ function listLines(items, render, empty) {
35736
36296
  if (items.length === 0) return empty ? [`- ${empty}`] : [];
35737
36297
  return items.map((item) => `- ${render(item)}`);
35738
36298
  }
@@ -35789,8 +36349,8 @@ var init_workflowWiringResponse = __esm({
35789
36349
 
35790
36350
  // ../mcp/src/server.ts
35791
36351
  var server_exports = {};
35792
- import { chmodSync, mkdirSync as mkdirSync3, writeFileSync as writeFileSync6 } from "node:fs";
35793
- import path18 from "node:path";
36352
+ import { chmodSync as chmodSync2, mkdirSync as mkdirSync4, writeFileSync as writeFileSync7 } from "node:fs";
36353
+ import path19 from "node:path";
35794
36354
  import { fileURLToPath } from "node:url";
35795
36355
  import {
35796
36356
  McpServer
@@ -36051,7 +36611,8 @@ function serverFreshness() {
36051
36611
  return mcpServerFreshness({
36052
36612
  serverStartedAt,
36053
36613
  entrypoint: serverModulePath,
36054
- mode: "server"
36614
+ mode: "server",
36615
+ distribution: connectorDistributionFor(serverModulePath)
36055
36616
  });
36056
36617
  }
36057
36618
  function summarizePacket(packet) {
@@ -36069,7 +36630,8 @@ function summarizePacket(packet) {
36069
36630
  sources: packet.sources,
36070
36631
  omitted: packet.omitted,
36071
36632
  ...packet.valueSignals ? { valueSignals: packet.valueSignals } : {},
36072
- ...packet.teamGuidance ? { teamGuidance: packet.teamGuidance } : {}
36633
+ ...packet.teamGuidance ? { teamGuidance: packet.teamGuidance } : {},
36634
+ ...packet.dependencyRemediation ? { dependencyRemediation: packet.dependencyRemediation } : {}
36073
36635
  };
36074
36636
  }
36075
36637
  function summarizeReview(review, detail = "auto") {
@@ -36507,6 +37069,7 @@ function summarizeBeforeYouCodeForStart(beforeYouCode) {
36507
37069
  decision: beforeYouCode.reuseFirst.decision,
36508
37070
  candidates: beforeYouCode.reuseFirst.candidates.slice(0, 2)
36509
37071
  },
37072
+ mapCoverage: beforeYouCode.mapCoverage,
36510
37073
  sharedContext: summarizeBeforeYouCodeSharedContext(
36511
37074
  beforeYouCode.teamGuidance
36512
37075
  ),
@@ -36570,7 +37133,7 @@ function summarizeWorkflowWiringForStart(wiring) {
36570
37133
  defaultAsk: wiring.defaultAsk,
36571
37134
  gaps: wiring.gaps.slice(0, 3),
36572
37135
  chatFeedback: wiring.chatFeedback.slice(0, 2),
36573
- finalRecapPolicy: wiring.finalRecapPolicy,
37136
+ finalRecapPolicy: compactFinalRecapPolicy(wiring.finalRecapPolicy),
36574
37137
  nextActions: wiring.nextActions.slice(0, 3)
36575
37138
  };
36576
37139
  }
@@ -36579,7 +37142,7 @@ function summarizeAutoEngageForStart(autoEngage) {
36579
37142
  status: autoEngage.status,
36580
37143
  summary: autoEngage.summary,
36581
37144
  chatFeedback: autoEngage.chatFeedback.slice(0, 1),
36582
- finalRecapPolicy: autoEngage.finalRecapPolicy,
37145
+ finalRecapPolicy: compactFinalRecapPolicy(autoEngage.finalRecapPolicy),
36583
37146
  triggers: autoEngage.triggerMatrix.slice(0, 3).map((trigger) => ({
36584
37147
  id: trigger.id,
36585
37148
  label: trigger.label,
@@ -36588,6 +37151,13 @@ function summarizeAutoEngageForStart(autoEngage) {
36588
37151
  nextActions: autoEngage.nextActions.slice(0, 2)
36589
37152
  };
36590
37153
  }
37154
+ function compactFinalRecapPolicy(policy) {
37155
+ return {
37156
+ maxLines: policy.maxLines,
37157
+ template: policy.defaultTemplate,
37158
+ fallbackWhenNotHelpful: policy.fallbackWhenNotHelpful
37159
+ };
37160
+ }
36591
37161
  function summarizeWiki(wiki) {
36592
37162
  return {
36593
37163
  wikiId: wiki.wikiId,
@@ -36640,21 +37210,22 @@ function summarizeLocalReceipt(receipt) {
36640
37210
  function writeHookFiles(repoRoot, files) {
36641
37211
  const written = [];
36642
37212
  for (const file of files) {
36643
- const targetPath = path18.join(repoRoot, file.path);
36644
- mkdirSync3(path18.dirname(targetPath), { recursive: true });
36645
- writeFileSync6(targetPath, file.body);
36646
- if (file.path.endsWith(".sh")) chmodSync(targetPath, 493);
37213
+ const targetPath = path19.join(repoRoot, file.path);
37214
+ mkdirSync4(path19.dirname(targetPath), { recursive: true });
37215
+ writeFileSync7(targetPath, file.body);
37216
+ if (file.path.endsWith(".sh")) chmodSync2(targetPath, 493);
36647
37217
  written.push(file.path);
36648
37218
  }
36649
37219
  return written;
36650
37220
  }
36651
- var server, personalizationKindSchema, referenceImplementationKindSchema, referenceImplementationStatusSchema, savePersonalizationToolSchema, workItemSchema, runtimeEvidenceSchema, handoffValidationSchema, artifactEvidenceSchema, loopCatalogFocusSchema, missionRecipeIdSchema2, startIntentSchema, nextStageSchema, responseDetailSchema, serverStartedAt, serverModulePath, repoTargetGuardExemptTools;
37221
+ var cloudBootstrap, server, personalizationKindSchema, referenceImplementationKindSchema, referenceImplementationStatusSchema, savePersonalizationToolSchema, workItemSchema, runtimeEvidenceSchema, handoffValidationSchema, artifactEvidenceSchema, loopCatalogFocusSchema, missionRecipeIdSchema2, startIntentSchema, nextStageSchema, responseDetailSchema, serverStartedAt, serverModulePath, repoTargetGuardExemptTools;
36652
37222
  var init_server = __esm({
36653
- "../mcp/src/server.ts"() {
37223
+ async "../mcp/src/server.ts"() {
36654
37224
  "use strict";
36655
37225
  init_dist();
36656
37226
  init_dist2();
36657
37227
  init_cloud();
37228
+ init_cloudCredentials();
36658
37229
  init_teamContext();
36659
37230
  init_missionInputOptions();
36660
37231
  init_connectorReadiness();
@@ -36670,10 +37241,15 @@ var init_server = __esm({
36670
37241
  init_workspace();
36671
37242
  init_handoffAutofill();
36672
37243
  init_scanFreshness();
37244
+ cloudBootstrap = await bootstrapCloudCredentials();
37245
+ if (cloudBootstrap.status === "failed") {
37246
+ console.error(`Brief cloud setup: ${cloudBootstrap.reason}`);
37247
+ process.exit(1);
37248
+ }
36673
37249
  server = new McpServer(
36674
37250
  {
36675
37251
  name: "brief",
36676
- version: "0.1.1"
37252
+ version: "0.1.3"
36677
37253
  },
36678
37254
  {
36679
37255
  instructions: briefMcpInstructions
@@ -37128,9 +37704,12 @@ var init_server = __esm({
37128
37704
  {
37129
37705
  repoPath: z2.string().optional().describe(
37130
37706
  "Path inside the repo. Defaults to BRIEF_REPO_ROOT captured from the agent workspace, then current working directory."
37707
+ ),
37708
+ detail: z2.enum(["compact", "expanded"]).optional().describe(
37709
+ "Use compact by default for the agent loop; request expanded only when diagnosing wiring or setup."
37131
37710
  )
37132
37711
  },
37133
- async ({ repoPath }) => {
37712
+ async ({ repoPath, detail }) => {
37134
37713
  const repoTargetInfo = resolveRepoTarget(repoPath);
37135
37714
  if (repoTargetInfo.repoTarget.status === "needs_repo_target") {
37136
37715
  return repoTargetBlock(repoTargetInfo);
@@ -37140,14 +37719,19 @@ var init_server = __esm({
37140
37719
  const skillLearning = buildRepoSkillLearningReport(repoRoot, {
37141
37720
  maxFiles: 6e3
37142
37721
  });
37722
+ const autoEngage = buildAgentAutoEngagementPlan(scan, skillLearning);
37723
+ const workflowWiring = evaluateAgentWorkflowWiring(scan);
37724
+ const responseDetail = detail ?? "compact";
37143
37725
  return jsonText({
37144
37726
  repoRoot,
37145
37727
  rootResolution,
37146
37728
  repoTarget,
37147
- autoEngage: buildAgentAutoEngagementPlan(scan, skillLearning),
37148
- workflowWiring: evaluateAgentWorkflowWiring(scan),
37729
+ detail: responseDetail,
37730
+ autoEngage: responseDetail === "expanded" ? autoEngage : summarizeAutoEngageForStart(autoEngage),
37731
+ workflowWiring: responseDetail === "expanded" ? workflowWiring : summarizeWorkflowWiringForStart(workflowWiring),
37149
37732
  skillLearning: summarizeSkillLearningForStart(skillLearning, void 0),
37150
- next: "Install the default instruction into always-on agent guidance, then run the smoke test from the connected coding agent to prove Brief stays engaged from natural task asks."
37733
+ next: responseDetail === "expanded" ? "Install the default instruction into always-on agent guidance, then run the smoke test from the connected coding agent to prove Brief stays engaged from natural task asks." : "Install the default instruction, then give the connected agent a normal task and confirm it calls brief.next before editing. Request detail: expanded only to diagnose wiring.",
37734
+ detailHint: 'Call brief.auto_engage with detail: "expanded" only when diagnosing setup or workflow wiring.'
37151
37735
  });
37152
37736
  }
37153
37737
  );
@@ -37266,7 +37850,7 @@ var init_server = __esm({
37266
37850
  skills: scan.stats.skillCount
37267
37851
  }
37268
37852
  },
37269
- next: teamContext.status === "fetched" ? "Use brief.context_for_task to route this team context into the next task." : "Set BRIEF_API_URL, BRIEF_API_KEY, and BRIEF_WORKSPACE_ID to fetch approved team context from the Brief app."
37853
+ next: teamContext.status === "fetched" ? "Use brief.context_for_task to route this team context into the next task." : "Create a browser-authorized connection from the Brief app, then restart the agent. Approved legacy setups can provide BRIEF_API_URL, BRIEF_API_KEY, and BRIEF_WORKSPACE_ID."
37270
37854
  });
37271
37855
  }
37272
37856
  );
@@ -37439,6 +38023,12 @@ var init_server = __esm({
37439
38023
  }) : void 0;
37440
38024
  const autoEngage = buildAgentAutoEngagementPlan(scan, skillLearning);
37441
38025
  const workflowWiring = evaluateAgentWorkflowWiring(scan);
38026
+ const workPacket = buildAgentWorkPacket({
38027
+ task,
38028
+ beforeYouCode,
38029
+ interpretation: recommendation.reason,
38030
+ nextAction: `Call ${recommendation.mcpTool} next. ${recommendation.stopUntil}`
38031
+ });
37442
38032
  const fullResponse = {
37443
38033
  status: "brief_next",
37444
38034
  repoRoot,
@@ -37447,6 +38037,7 @@ var init_server = __esm({
37447
38037
  stage: resolvedStage,
37448
38038
  recommendation,
37449
38039
  cockpit: beforeYouCode.cockpit,
38040
+ workPacket,
37450
38041
  beforeYouCode: summarizeBeforeYouCodeForStart(beforeYouCode),
37451
38042
  ...start && recommendation.mcpTool !== "brief.before_you_code" ? { start: summarizeStartForStart(start) } : {},
37452
38043
  ...readinessPreview ? { readinessPreview } : {},
@@ -37470,6 +38061,7 @@ var init_server = __esm({
37470
38061
  },
37471
38062
  stage: resolvedStage,
37472
38063
  recommendation,
38064
+ workPacket,
37473
38065
  cockpit: {
37474
38066
  taskSize: beforeYouCode.cockpit.taskSize,
37475
38067
  mode: beforeYouCode.cockpit.mode,
@@ -37491,7 +38083,8 @@ var init_server = __esm({
37491
38083
  taskClasses: beforeYouCode.productOperatingContext.taskClasses
37492
38084
  },
37493
38085
  autoEngage: {
37494
- status: "active",
38086
+ status: autoEngage.status,
38087
+ workflowWiring: workflowWiring.status,
37495
38088
  firstCall: "brief.next",
37496
38089
  beforeEditing: "brief.before_you_code or brief.start",
37497
38090
  beforeHandoff: "brief.review_diff",
@@ -38032,7 +38625,7 @@ var init_server = __esm({
38032
38625
  const sourceRepos = sourceRepoPaths && sourceRepoPaths.length > 0 ? sourceRepoPaths : [repoRoot];
38033
38626
  const inventories = sourceRepos.map(
38034
38627
  (sourceRepo) => buildAgentPackInventory(
38035
- path18.isAbsolute(sourceRepo) ? sourceRepo : path18.resolve(repoRoot, sourceRepo),
38628
+ path19.isAbsolute(sourceRepo) ? sourceRepo : path19.resolve(repoRoot, sourceRepo),
38036
38629
  maxFiles === void 0 ? {} : { maxFiles }
38037
38630
  )
38038
38631
  );
@@ -39407,6 +40000,7 @@ init_dist2();
39407
40000
  init_teamContext();
39408
40001
  init_missionInputOptions();
39409
40002
  init_cloud();
40003
+ init_cloudCredentials();
39410
40004
  init_workspace();
39411
40005
  init_connectorReadiness();
39412
40006
  init_connectorRecovery();
@@ -39418,15 +40012,139 @@ init_wikiInit();
39418
40012
  init_handoffAutofill();
39419
40013
  init_mapResponse();
39420
40014
  init_scanFreshness();
39421
- import { chmodSync as chmodSync2, mkdirSync as mkdirSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync7 } from "node:fs";
39422
- import path19 from "node:path";
40015
+ import { chmodSync as chmodSync3, mkdirSync as mkdirSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync8 } from "node:fs";
40016
+ import path20 from "node:path";
39423
40017
  import { fileURLToPath as fileURLToPath2 } from "node:url";
40018
+
40019
+ // ../mcp/src/runner.ts
40020
+ init_cloud();
40021
+ import { hostname } from "node:os";
40022
+ import { spawn } from "node:child_process";
40023
+ async function runLoopRunner(options2, log = console.log) {
40024
+ const runnerId = options2.runnerId ?? `${hostname()}-${process.pid}`;
40025
+ const intervalMs = Math.max(options2.intervalMs ?? 4e3, 1e3);
40026
+ const env = options2.env ?? process.env;
40027
+ let didRun = false;
40028
+ do {
40029
+ const run = await claimRemoteLoopRun(runnerId, 180, env);
40030
+ if (!run) {
40031
+ if (options2.once) return;
40032
+ await sleep(intervalMs);
40033
+ continue;
40034
+ }
40035
+ didRun = true;
40036
+ log({ status: "running", runId: run.id, loop: run.loopName });
40037
+ try {
40038
+ const result = await executeAgentCommand(
40039
+ options2.agentCommand,
40040
+ run,
40041
+ options2.repoRoot,
40042
+ options2.commandTimeoutMs ?? 30 * 60 * 1e3
40043
+ );
40044
+ const proof = {
40045
+ runnerId,
40046
+ exitCode: result.exitCode,
40047
+ stdoutBytes: Buffer.byteLength(result.stdout, "utf8"),
40048
+ stderrBytes: Buffer.byteLength(result.stderr, "utf8"),
40049
+ completedLocallyAt: (/* @__PURE__ */ new Date()).toISOString()
40050
+ };
40051
+ const updated = await updateRemoteLoopRun(run.id, {
40052
+ runnerId,
40053
+ status: result.exitCode === 0 ? "succeeded" : "failed",
40054
+ lastEvent: result.exitCode === 0 ? "The local agent completed the loop." : "The local agent exited with a failure.",
40055
+ resultSummary: compactOutput(result.stdout || result.stderr),
40056
+ proof,
40057
+ ...result.exitCode === 0 ? {} : { errorMessage: compactOutput(result.stderr || result.stdout) }
40058
+ }, env);
40059
+ log({ status: updated.status, runId: updated.id, proof });
40060
+ } catch (caught) {
40061
+ const message = caught instanceof Error ? caught.message : "Local runner failed.";
40062
+ try {
40063
+ const updated = await updateRemoteLoopRun(run.id, {
40064
+ runnerId,
40065
+ status: "failed",
40066
+ lastEvent: "The local runner could not execute the agent command.",
40067
+ errorMessage: message,
40068
+ proof: { runnerId, completedLocallyAt: (/* @__PURE__ */ new Date()).toISOString() }
40069
+ }, env);
40070
+ log({ status: updated.status, runId: updated.id, error: message });
40071
+ } catch (updateError) {
40072
+ log({ status: "failed", runId: run.id, error: message, updateError });
40073
+ throw new Error(
40074
+ `The runner could not persist the failed loop state: ${updateError instanceof Error ? updateError.message : "unknown update error"}`
40075
+ );
40076
+ }
40077
+ }
40078
+ } while (!options2.once || !didRun);
40079
+ }
40080
+ async function executeAgentCommand(command2, run, repoRoot, timeoutMs) {
40081
+ const prompt = [
40082
+ `You are running the approved Brief team loop: ${run.loopName}.`,
40083
+ `Run id: ${run.id}`,
40084
+ `Repository: ${run.repoFullName ?? repoRoot}`,
40085
+ "",
40086
+ "Approved loop guidance:",
40087
+ run.loopBody,
40088
+ "",
40089
+ "Guardrails:",
40090
+ ...run.guardrails.length > 0 ? run.guardrails.map((item) => `- ${item}`) : ["- Stop before external side effects without approval."],
40091
+ "",
40092
+ "Task:",
40093
+ run.task,
40094
+ "",
40095
+ "Return a concise summary of what you changed, validation run, proof, and remaining risk."
40096
+ ].join("\n");
40097
+ return await new Promise((resolve, reject) => {
40098
+ const child = spawn("/bin/sh", ["-lc", command2], {
40099
+ cwd: repoRoot,
40100
+ env: process.env,
40101
+ stdio: ["pipe", "pipe", "pipe"]
40102
+ });
40103
+ let stdout = "";
40104
+ let stderr = "";
40105
+ const timer = setTimeout(() => {
40106
+ child.kill("SIGTERM");
40107
+ reject(new Error(`Agent command timed out after ${Math.round(timeoutMs / 6e4)} minutes.`));
40108
+ }, timeoutMs);
40109
+ child.stdout.on("data", (chunk) => {
40110
+ stdout = `${stdout}${chunk.toString()}`.slice(-6e4);
40111
+ });
40112
+ child.stderr.on("data", (chunk) => {
40113
+ stderr = `${stderr}${chunk.toString()}`.slice(-2e4);
40114
+ });
40115
+ child.on("error", (error) => {
40116
+ clearTimeout(timer);
40117
+ reject(error);
40118
+ });
40119
+ child.on("close", (code) => {
40120
+ clearTimeout(timer);
40121
+ resolve({ exitCode: code ?? 1, stdout, stderr });
40122
+ });
40123
+ child.stdin.end(prompt);
40124
+ });
40125
+ }
40126
+ function compactOutput(value) {
40127
+ return value.trim().slice(-12e3) || "The agent returned no summary.";
40128
+ }
40129
+ function sleep(ms) {
40130
+ return new Promise((resolve) => setTimeout(resolve, ms));
40131
+ }
40132
+
40133
+ // ../mcp/src/cli.ts
39424
40134
  var args = process.argv.slice(2);
39425
40135
  var command = args[0];
39426
40136
  var cliStartedAt = /* @__PURE__ */ new Date();
39427
40137
  var cliModulePath = fileURLToPath2(import.meta.url);
40138
+ if (!isCloudLogoutCommand(command)) {
40139
+ const cloudBootstrap2 = await bootstrapCloudCredentials();
40140
+ if (cloudBootstrap2.status === "failed") {
40141
+ console.error(
40142
+ `Brief team sync is unavailable: ${cloudBootstrap2.reason} Continuing in local mode.`
40143
+ );
40144
+ }
40145
+ }
39428
40146
  if (!command || command === "--stdio" || command === "stdio" || command === "mcp") {
39429
- await Promise.resolve().then(() => (init_server(), server_exports));
40147
+ await init_server().then(() => server_exports);
39430
40148
  } else {
39431
40149
  await runCli(command, args.slice(1));
39432
40150
  }
@@ -39435,11 +40153,41 @@ async function runCli(commandName, commandArgs) {
39435
40153
  printJson(buildCliHelp(commandName));
39436
40154
  return;
39437
40155
  }
40156
+ if (isCloudLogoutCommand(commandName)) {
40157
+ forgetCloudCredentials();
40158
+ printJson({
40159
+ status: "cloud_credentials_removed",
40160
+ path: defaultCredentialsPath(),
40161
+ next: "Create a fresh browser-authorized connection from the Brief app before using team sync again."
40162
+ });
40163
+ return;
40164
+ }
39438
40165
  const rootResolution = resolveRepoRootDetails(
39439
40166
  option2(commandArgs, "repo") ?? option2(commandArgs, "repoPath") ?? option2(commandArgs, "repo-path")
39440
40167
  );
39441
40168
  const repoRoot = rootResolution.repoRoot;
39442
40169
  const repoTarget = repoTargetHealth(rootResolution);
40170
+ if (isCommand(commandName, "runner", "loop-runner", "run-loop")) {
40171
+ const agentCommand = option2(commandArgs, "agent-command") ?? process.env.BRIEF_AGENT_COMMAND;
40172
+ if (!agentCommand) {
40173
+ fail(
40174
+ "Missing --agent-command. Set BRIEF_AGENT_COMMAND to the local Claude, Codex, or compatible agent command."
40175
+ );
40176
+ }
40177
+ const runnerOptions = {
40178
+ repoRoot,
40179
+ agentCommand,
40180
+ once: hasFlag(commandArgs, "once")
40181
+ };
40182
+ const runnerId = option2(commandArgs, "runner-id");
40183
+ const intervalMs = numberOption(commandArgs, "interval-ms");
40184
+ const commandTimeoutMs = numberOption(commandArgs, "timeout-ms");
40185
+ if (runnerId) runnerOptions.runnerId = runnerId;
40186
+ if (intervalMs) runnerOptions.intervalMs = intervalMs;
40187
+ if (commandTimeoutMs) runnerOptions.commandTimeoutMs = commandTimeoutMs;
40188
+ await runLoopRunner(runnerOptions);
40189
+ return;
40190
+ }
39443
40191
  if (isCommand(commandName, "install-status", "install_status")) {
39444
40192
  const freshness = cliServerFreshness();
39445
40193
  const connector = connectorReadiness({
@@ -39670,6 +40418,12 @@ async function runCli(commandName, commandArgs) {
39670
40418
  beforeYouCode,
39671
40419
  baseRef
39672
40420
  });
40421
+ const workPacket = buildAgentWorkPacket({
40422
+ task,
40423
+ beforeYouCode,
40424
+ interpretation: recommendation.reason,
40425
+ nextAction: recommendation.stopUntil
40426
+ });
39673
40427
  const fullResponse = {
39674
40428
  status: "brief_next",
39675
40429
  repoRoot,
@@ -39678,6 +40432,7 @@ async function runCli(commandName, commandArgs) {
39678
40432
  stage,
39679
40433
  recommendation,
39680
40434
  cockpit: beforeYouCode.cockpit,
40435
+ workPacket,
39681
40436
  beforeYouCode: summarizeBeforeYouCodeForCli(beforeYouCode),
39682
40437
  ...start && recommendation.mcpTool !== "brief.before_you_code" ? { start: summarizeStartForCli(start) } : {},
39683
40438
  workflowWiring: summarizeWorkflowWiringForCli(workflowWiring),
@@ -39700,6 +40455,11 @@ async function runCli(commandName, commandArgs) {
39700
40455
  },
39701
40456
  stage,
39702
40457
  recommendation,
40458
+ workPacket: {
40459
+ schemaVersion: workPacket.schemaVersion,
40460
+ compactText: workPacket.compactText,
40461
+ lineCount: workPacket.lineCount
40462
+ },
39703
40463
  cockpit: {
39704
40464
  taskSize: beforeYouCode.cockpit.taskSize,
39705
40465
  mode: beforeYouCode.cockpit.mode,
@@ -39726,7 +40486,11 @@ async function runCli(commandName, commandArgs) {
39726
40486
  autoEngage: {
39727
40487
  status: autoEngage.status,
39728
40488
  chatFeedback: autoEngage.chatFeedback.slice(0, 1),
39729
- finalRecapPolicy: autoEngage.finalRecapPolicy
40489
+ finalRecapPolicy: {
40490
+ maxLines: autoEngage.finalRecapPolicy.maxLines,
40491
+ defaultTemplate: autoEngage.finalRecapPolicy.defaultTemplate,
40492
+ fallbackWhenNotHelpful: autoEngage.finalRecapPolicy.fallbackWhenNotHelpful
40493
+ }
39730
40494
  },
39731
40495
  referenceFirst: summarizeBeforeYouCodeReferences(beforeYouCode),
39732
40496
  ...learnedWorkflow ? {
@@ -40089,7 +40853,7 @@ async function runCli(commandName, commandArgs) {
40089
40853
  const sourceRepos = filesOption2(commandArgs, "source-repos") ?? filesOption2(commandArgs, "sourceRepos") ?? filesOption2(commandArgs, "sources") ?? [repoRoot];
40090
40854
  const inventories = sourceRepos.map(
40091
40855
  (sourceRepo) => buildAgentPackInventory(
40092
- path19.isAbsolute(sourceRepo) ? sourceRepo : path19.resolve(repoRoot, sourceRepo),
40856
+ path20.isAbsolute(sourceRepo) ? sourceRepo : path20.resolve(repoRoot, sourceRepo),
40093
40857
  maxFiles === void 0 ? {} : { maxFiles }
40094
40858
  )
40095
40859
  );
@@ -40881,14 +41645,14 @@ async function runCli(commandName, commandArgs) {
40881
41645
  if (isCommand(commandName, "workflow-wiring", "workflow_wiring")) {
40882
41646
  const scan = scanRepo(repoRoot, { maxFiles: 400 });
40883
41647
  const workflowWiring = evaluateAgentWorkflowWiring(scan);
40884
- const compact = hasFlag(commandArgs, "compact") && !hasFlag(commandArgs, "full");
41648
+ const compact2 = hasFlag(commandArgs, "compact") && !hasFlag(commandArgs, "full");
40885
41649
  const explicitlyExpanded = hasFlag(commandArgs, "full");
40886
41650
  printJson({
40887
41651
  status: "workflow_wiring",
40888
41652
  repoRoot,
40889
41653
  repoName: scan.repoName,
40890
- workflowWiring: compact ? summarizeAgentWorkflowWiring(workflowWiring) : workflowWiring,
40891
- ...compact ? {
41654
+ workflowWiring: compact2 ? summarizeAgentWorkflowWiring(workflowWiring) : workflowWiring,
41655
+ ...compact2 ? {
40892
41656
  detailHint: "Use --full only when diagnosing wiring evidence, contracts, or copyable prompts."
40893
41657
  } : explicitlyExpanded ? { detail: "expanded" } : {
40894
41658
  compatibility: "Expanded detail was returned for backward compatibility. Use --compact for the agent-safe response."
@@ -40961,7 +41725,7 @@ async function runCli(commandName, commandArgs) {
40961
41725
  skills: scan.stats.skillCount
40962
41726
  }
40963
41727
  },
40964
- next: teamContext.status === "fetched" ? "Use context_for_task to route this team context into the next task." : "Set BRIEF_API_URL, BRIEF_API_KEY, and BRIEF_WORKSPACE_ID to fetch approved team context from the Brief app."
41728
+ next: teamContext.status === "fetched" ? "Use context_for_task to route this team context into the next task." : "Create a browser-authorized connection from the Brief app, then restart the agent. Approved legacy setups can provide BRIEF_API_URL, BRIEF_API_KEY, and BRIEF_WORKSPACE_ID."
40965
41729
  });
40966
41730
  return;
40967
41731
  }
@@ -41158,6 +41922,11 @@ async function runCli(commandName, commandArgs) {
41158
41922
  function isCommand(commandName, ...aliases) {
41159
41923
  return aliases.includes(commandName);
41160
41924
  }
41925
+ function isCloudLogoutCommand(commandName) {
41926
+ return Boolean(
41927
+ commandName && ["cloud-logout", "cloud_logout", "disconnect-cloud"].includes(commandName)
41928
+ );
41929
+ }
41161
41930
  function wikiInitMode(argsList) {
41162
41931
  if (hasFlag(argsList, "init")) return "init";
41163
41932
  if (hasFlag(argsList, "update")) return "update";
@@ -41241,6 +42010,33 @@ function cliCommandHelp() {
41241
42010
  "After changing Brief source to confirm the running connector is fresh."
41242
42011
  ]
41243
42012
  },
42013
+ {
42014
+ command: "cloud-logout",
42015
+ aliases: ["cloud_logout", "disconnect-cloud"],
42016
+ purpose: "Remove the locally stored cloud credential so the next setup can pair a device again.",
42017
+ repoBound: false,
42018
+ usage: "brief cloud-logout",
42019
+ examples: ["brief cloud-logout"],
42020
+ whenToUse: [
42021
+ "When a device needs to be disconnected from a Brief workspace.",
42022
+ "When a one-time browser pairing has expired before the connector started."
42023
+ ]
42024
+ },
42025
+ {
42026
+ command: "runner",
42027
+ aliases: ["loop-runner", "run-loop"],
42028
+ purpose: "Poll for an approved team loop and execute it with the developer's local agent command.",
42029
+ repoBound: true,
42030
+ usage: 'runbrief runner --repo $PWD --agent-command "claude -p"',
42031
+ examples: [
42032
+ 'runbrief runner --repo $PWD --agent-command "claude -p"',
42033
+ 'runbrief runner --repo $PWD --agent-command "codex exec" --once'
42034
+ ],
42035
+ whenToUse: [
42036
+ "After pairing a connector from the Brief app.",
42037
+ "When a team-approved loop is queued in the web app."
42038
+ ]
42039
+ },
41244
42040
  {
41245
42041
  command: "next",
41246
42042
  aliases: ["brief-next", "brief_next"],
@@ -41586,7 +42382,11 @@ function positionalText(argsList) {
41586
42382
  "owner",
41587
42383
  "receipts",
41588
42384
  "added-by",
41589
- "addedBy"
42385
+ "addedBy",
42386
+ "agent-command",
42387
+ "runner-id",
42388
+ "interval-ms",
42389
+ "timeout-ms"
41590
42390
  ]);
41591
42391
  const parts = [];
41592
42392
  for (let index = 0; index < argsList.length; index += 1) {
@@ -41842,7 +42642,7 @@ function artifactEvidenceFromOptions(argsList) {
41842
42642
  );
41843
42643
  const evidence = [];
41844
42644
  for (const raw of rawEvidenceValues) {
41845
- const json = raw.startsWith("@") ? readFileSync6(path19.resolve(raw.slice(1)), "utf8") : raw;
42645
+ const json = raw.startsWith("@") ? readFileSync7(path20.resolve(raw.slice(1)), "utf8") : raw;
41846
42646
  try {
41847
42647
  const parsed = JSON.parse(json);
41848
42648
  const items = Array.isArray(parsed) ? parsed : [parsed];
@@ -41920,7 +42720,7 @@ function missionSummaryFromLocalReceipt2(repoRoot, missionReceiptId) {
41920
42720
  function runtimeEvidencePacketFromOptions(argsList) {
41921
42721
  const raw = option2(argsList, "runtime-packet") ?? option2(argsList, "runtime-evidence-packet") ?? option2(argsList, "runtimeEvidencePacket");
41922
42722
  if (!raw) return void 0;
41923
- const json = raw.startsWith("@") ? readFileSync6(path19.resolve(raw.slice(1)), "utf8") : raw;
42723
+ const json = raw.startsWith("@") ? readFileSync7(path20.resolve(raw.slice(1)), "utf8") : raw;
41924
42724
  try {
41925
42725
  return cleanRuntimeEvidencePacket2(
41926
42726
  runtimeEvidencePacketSchema.parse(JSON.parse(json))
@@ -42089,6 +42889,15 @@ function printContextSummary(packet, localReceipt, teamContext) {
42089
42889
  teamContextLine(teamContext),
42090
42890
  teamGuidanceLine(packet),
42091
42891
  valueSignalLine(packet),
42892
+ ...packet.dependencyRemediation ? [
42893
+ "",
42894
+ "Dependency plan:",
42895
+ `- ${packet.dependencyRemediation.packageManager} \xB7 ${packet.dependencyRemediation.status}`,
42896
+ `- manifests: ${packet.dependencyRemediation.manifestFiles.slice(0, 4).join(", ") || "none found"}`,
42897
+ `- lockfiles: ${packet.dependencyRemediation.lockfiles.slice(0, 3).join(", ") || "none found"}`,
42898
+ `- packages: ${packet.dependencyRemediation.packageSignals.map((item) => item.name).join(", ") || "need advisory/scanner evidence"}`,
42899
+ `- graph proof: ${packet.dependencyRemediation.graphCommands.slice(0, 2).join(", ") || "name the direct/transitive path"}`
42900
+ ] : [],
42092
42901
  "",
42093
42902
  "Rules:",
42094
42903
  ...packet.rules.length > 0 ? packet.rules.slice(0, 8).map((rule) => `- [${rule.enforcement}] ${rule.title}`) : ["- none"],
@@ -42141,10 +42950,10 @@ function valueSignalLine(packet) {
42141
42950
  function writeHookFiles2(repoRoot, files) {
42142
42951
  const written = [];
42143
42952
  for (const file of files) {
42144
- const targetPath = path19.join(repoRoot, file.path);
42145
- mkdirSync4(path19.dirname(targetPath), { recursive: true });
42146
- writeFileSync7(targetPath, file.body);
42147
- if (file.path.endsWith(".sh")) chmodSync2(targetPath, 493);
42953
+ const targetPath = path20.join(repoRoot, file.path);
42954
+ mkdirSync5(path20.dirname(targetPath), { recursive: true });
42955
+ writeFileSync8(targetPath, file.body);
42956
+ if (file.path.endsWith(".sh")) chmodSync3(targetPath, 493);
42148
42957
  written.push(file.path);
42149
42958
  }
42150
42959
  return written;
@@ -42272,7 +43081,8 @@ function cliServerFreshness() {
42272
43081
  return mcpServerFreshness({
42273
43082
  serverStartedAt: cliStartedAt,
42274
43083
  entrypoint: cliModulePath,
42275
- mode: "cli"
43084
+ mode: "cli",
43085
+ distribution: connectorDistributionFor(cliModulePath)
42276
43086
  });
42277
43087
  }
42278
43088
  function printJson(value) {
@@ -42296,7 +43106,7 @@ function nextScanStep(cloudSync) {
42296
43106
  if (cloudSync.status === "failed") {
42297
43107
  return `The local scan succeeded, but the web app did not update because cloud sync failed: ${cloudSync.reason}. Fix cloud sync before trusting hosted freshness; refresh local wiki separately with brief.initialize_repo_wiki update: true.`;
42298
43108
  }
42299
- return "The local scan succeeded, but the web app did not update. Set BRIEF_API_URL, BRIEF_API_KEY, BRIEF_WORKSPACE_ID, and BRIEF_PRIVACY_MODE=cloud-sync-derived to sync scans to Brief. To refresh repo-local wiki docs, ask the agent to call brief.initialize_repo_wiki with update: true.";
43109
+ return "The local scan succeeded, but the web app did not update. Create a browser-authorized connection from the Brief app and restart the agent; approved legacy setups can provide BRIEF_API_URL, BRIEF_API_KEY, and BRIEF_WORKSPACE_ID. To refresh repo-local wiki docs, ask the agent to call brief.initialize_repo_wiki with update: true.";
42300
43110
  }
42301
43111
  function fail(message) {
42302
43112
  process.stderr.write(`${message}