opencode-swarm 7.51.6 → 7.52.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -52,7 +52,7 @@ var package_default;
52
52
  var init_package = __esm(() => {
53
53
  package_default = {
54
54
  name: "opencode-swarm",
55
- version: "7.51.6",
55
+ version: "7.52.1",
56
56
  description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
57
57
  main: "dist/index.js",
58
58
  types: "dist/index.d.ts",
@@ -17275,7 +17275,7 @@ function getCanonicalAgentRole(agentName, generatedAgentNames) {
17275
17275
  function stripKnownSwarmPrefix(agentName) {
17276
17276
  return getCanonicalAgentRole(agentName);
17277
17277
  }
17278
- var SEPARATORS, CANONICAL_ROLES_LONGEST_FIRST, CANONICAL_ROLES_SET, AgentOverrideConfigSchema, SwarmConfigSchema, HooksConfigSchema, ScoringWeightsSchema, DecisionDecaySchema, TokenRatiosSchema, ScoringConfigSchema, ContextBudgetConfigSchema, EvidenceConfigSchema, GateFeatureSchema, PlaceholderScanConfigSchema, QualityBudgetConfigSchema, GateConfigSchema, PipelineConfigSchema, PhaseCompleteConfigSchema, SummaryConfigSchema, ReviewPassesConfigSchema, AdversarialDetectionConfigSchema, AdversarialTestingConfigSchemaBase, AdversarialTestingConfigSchema, IntegrationAnalysisConfigSchema, DocsConfigSchema, DesignDocsConfigSchema, UIReviewConfigSchema, CompactionAdvisoryConfigSchema, LintConfigSchema, SecretscanConfigSchema, GuardrailsProfileSchema, DEFAULT_AGENT_PROFILES, DEFAULT_ARCHITECT_PROFILE, GuardrailsConfigSchema, WatchdogConfigSchema, SelfReviewConfigSchema, ToolFilterConfigSchema, PlanCursorConfigSchema, CheckpointConfigSchema, AutomationModeSchema, AutomationCapabilitiesSchema, AutomationConfigSchemaBase, AutomationConfigSchema, KnowledgeConfigSchema, MemoryConfigSchema, CuratorConfigSchema, ArchitecturalSupervisionConfigSchema, KnowledgeApplicationConfigSchema, SkillImproverConfigSchema, SpecWriterConfigSchema, SlopDetectorConfigSchema, IncrementalVerifyConfigSchema, CompactionConfigSchema, PrmConfigSchema, AgentAuthorityRuleSchema, AuthorityConfigSchema, GeneralCouncilMemberConfigSchema, GeneralCouncilConfigSchema, CouncilConfigSchema, ParallelizationConfigSchema, LeanTurboConfigSchema, StandardTurboConfigSchema, LeanTurboStrategyConfigSchema, TurboConfigSchema, PluginConfigSchema;
17278
+ var SEPARATORS, CANONICAL_ROLES_LONGEST_FIRST, CANONICAL_ROLES_SET, AgentOverrideConfigSchema, SwarmConfigSchema, HooksConfigSchema, ScoringWeightsSchema, DecisionDecaySchema, TokenRatiosSchema, ScoringConfigSchema, ContextBudgetConfigSchema, EvidenceConfigSchema, GateFeatureSchema, PlaceholderScanConfigSchema, QualityBudgetConfigSchema, GateConfigSchema, PipelineConfigSchema, PhaseCompleteConfigSchema, SummaryConfigSchema, ReviewPassesConfigSchema, AdversarialDetectionConfigSchema, AdversarialTestingConfigSchemaBase, AdversarialTestingConfigSchema, IntegrationAnalysisConfigSchema, DocsConfigSchema, DesignDocsConfigSchema, UIReviewConfigSchema, CompactionAdvisoryConfigSchema, LintConfigSchema, SecretscanConfigSchema, GuardrailsProfileSchema, DEFAULT_AGENT_PROFILES, DEFAULT_ARCHITECT_PROFILE, GuardrailsConfigSchema, WatchdogConfigSchema, SelfReviewConfigSchema, ToolFilterConfigSchema, PlanCursorConfigSchema, ContextMapConfigSchema, CheckpointConfigSchema, AutomationModeSchema, AutomationCapabilitiesSchema, AutomationConfigSchemaBase, AutomationConfigSchema, KnowledgeConfigSchema, MemoryConfigSchema, CuratorConfigSchema, ArchitecturalSupervisionConfigSchema, KnowledgeApplicationConfigSchema, SkillImproverConfigSchema, SpecWriterConfigSchema, SlopDetectorConfigSchema, IncrementalVerifyConfigSchema, CompactionConfigSchema, PrmConfigSchema, AgentAuthorityRuleSchema, AuthorityConfigSchema, GeneralCouncilMemberConfigSchema, GeneralCouncilConfigSchema, CouncilConfigSchema, ParallelizationConfigSchema, LeanTurboConfigSchema, StandardTurboConfigSchema, LeanTurboStrategyConfigSchema, TurboConfigSchema, PluginConfigSchema;
17279
17279
  var init_schema = __esm(() => {
17280
17280
  init_zod();
17281
17281
  init_constants();
@@ -17678,6 +17678,13 @@ var init_schema = __esm(() => {
17678
17678
  max_tokens: exports_external.number().min(500).max(4000).default(1500),
17679
17679
  lookahead_tasks: exports_external.number().min(0).max(5).default(2)
17680
17680
  });
17681
+ ContextMapConfigSchema = exports_external.object({
17682
+ enabled: exports_external.boolean().default(false),
17683
+ mode: exports_external.enum(["conservative", "balanced", "aggressive"]).default("balanced"),
17684
+ max_capsule_tokens: exports_external.number().int().positive().default(2000),
17685
+ invalidate_on_hash_change: exports_external.boolean().default(true),
17686
+ agent_profiles: exports_external.record(exports_external.string(), exports_external.string()).default({})
17687
+ });
17681
17688
  CheckpointConfigSchema = exports_external.object({
17682
17689
  enabled: exports_external.boolean().default(true),
17683
17690
  auto_checkpoint_threshold: exports_external.number().int().min(1).max(20).default(3),
@@ -17996,6 +18003,7 @@ var init_schema = __esm(() => {
17996
18003
  tool_filter: ToolFilterConfigSchema.optional(),
17997
18004
  authority: AuthorityConfigSchema.optional(),
17998
18005
  plan_cursor: PlanCursorConfigSchema.optional(),
18006
+ context_map: ContextMapConfigSchema.optional(),
17999
18007
  evidence: EvidenceConfigSchema.optional(),
18000
18008
  summaries: SummaryConfigSchema.optional(),
18001
18009
  review_passes: ReviewPassesConfigSchema.optional(),
@@ -439,6 +439,18 @@ export declare const PlanCursorConfigSchema: z.ZodObject<{
439
439
  lookahead_tasks: z.ZodDefault<z.ZodNumber>;
440
440
  }, z.core.$strip>;
441
441
  export type PlanCursorConfig = z.infer<typeof PlanCursorConfigSchema>;
442
+ export declare const ContextMapConfigSchema: z.ZodObject<{
443
+ enabled: z.ZodDefault<z.ZodBoolean>;
444
+ mode: z.ZodDefault<z.ZodEnum<{
445
+ conservative: "conservative";
446
+ balanced: "balanced";
447
+ aggressive: "aggressive";
448
+ }>>;
449
+ max_capsule_tokens: z.ZodDefault<z.ZodNumber>;
450
+ invalidate_on_hash_change: z.ZodDefault<z.ZodBoolean>;
451
+ agent_profiles: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
452
+ }, z.core.$strip>;
453
+ export type ContextMapConfig = z.infer<typeof ContextMapConfigSchema>;
442
454
  export declare const CheckpointConfigSchema: z.ZodObject<{
443
455
  enabled: z.ZodDefault<z.ZodBoolean>;
444
456
  auto_checkpoint_threshold: z.ZodDefault<z.ZodNumber>;
@@ -928,8 +940,8 @@ export declare const PluginConfigSchema: z.ZodObject<{
928
940
  }, z.core.$strip>>;
929
941
  qa_retry_limit: z.ZodDefault<z.ZodNumber>;
930
942
  execution_mode: z.ZodDefault<z.ZodEnum<{
931
- strict: "strict";
932
943
  balanced: "balanced";
944
+ strict: "strict";
933
945
  fast: "fast";
934
946
  }>>;
935
947
  inject_phase_reminders: z.ZodDefault<z.ZodBoolean>;
@@ -1081,6 +1093,17 @@ export declare const PluginConfigSchema: z.ZodObject<{
1081
1093
  max_tokens: z.ZodDefault<z.ZodNumber>;
1082
1094
  lookahead_tasks: z.ZodDefault<z.ZodNumber>;
1083
1095
  }, z.core.$strip>>;
1096
+ context_map: z.ZodOptional<z.ZodObject<{
1097
+ enabled: z.ZodDefault<z.ZodBoolean>;
1098
+ mode: z.ZodDefault<z.ZodEnum<{
1099
+ conservative: "conservative";
1100
+ balanced: "balanced";
1101
+ aggressive: "aggressive";
1102
+ }>>;
1103
+ max_capsule_tokens: z.ZodDefault<z.ZodNumber>;
1104
+ invalidate_on_hash_change: z.ZodDefault<z.ZodBoolean>;
1105
+ agent_profiles: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
1106
+ }, z.core.$strip>>;
1084
1107
  evidence: z.ZodOptional<z.ZodObject<{
1085
1108
  enabled: z.ZodDefault<z.ZodBoolean>;
1086
1109
  max_age_days: z.ZodDefault<z.ZodNumber>;
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Context Capsule Builder
3
+ *
4
+ * Constructs role-specific Context Capsules that are injected into delegated
5
+ * agent system messages. Uses the Context Map to populate file summaries,
6
+ * builds a per-file read policy based on staleness, and formats the capsule
7
+ * as a structured markdown document.
8
+ *
9
+ * Each agent role (coder, reviewer, critic, test_engineer, sme) has a
10
+ * dedicated profile that controls which sections are included and how many
11
+ * files appear in the capsule.
12
+ *
13
+ * Uses the `_internals` DI seam pattern so tests can override filesystem
14
+ * and analysis dependencies without `mock.module` (which leaks across
15
+ * files in Bun's shared test-runner process).
16
+ *
17
+ * All functions accept an explicit `directory` parameter (Invariant 4).
18
+ * No `process.cwd()` usage. Never throws — returns best-effort capsules.
19
+ * No `bun:` imports — Node-ESM-loadable (Invariant 2).
20
+ */
21
+ import * as fs from 'node:fs';
22
+ import type { AgentRole, CapsuleDelegationReason, CapsuleMetadata, ContextCapsule, ReadPolicyEntry, RoleProfile } from '../types/context-capsule';
23
+ import type { ContextMap } from '../types/context-map';
24
+ import { extractFileSummary, isFileStale } from './file-summary';
25
+ import { computeContentHash, createEmptyContextMap, loadContextMap } from './persistence';
26
+ /**
27
+ * Reuse shared token estimator with capsule-specific floor of 1.
28
+ * The base estimator returns 0 for empty strings; capsules need at least 1
29
+ * token to avoid degenerate budget calculations.
30
+ */
31
+ export declare function estimateTokens(content: string): number;
32
+ /**
33
+ * Test-only dependency-injection seam. Production code calls through this
34
+ * object so tests can replace the underlying implementations without
35
+ * `mock.module` (which leaks across files in Bun's shared test-runner process).
36
+ * Mutating this local object is file-scoped and trivially restorable
37
+ * via `afterEach`.
38
+ */
39
+ export declare const _internals: {
40
+ readonly loadContextMap: typeof loadContextMap;
41
+ readonly createEmptyContextMap: typeof createEmptyContextMap;
42
+ readonly computeContentHash: typeof computeContentHash;
43
+ readonly isFileStale: typeof isFileStale;
44
+ readonly extractFileSummary: typeof extractFileSummary;
45
+ readonly estimateTokens: typeof estimateTokens;
46
+ readonly readFileSync: typeof fs.readFileSync;
47
+ readonly existsSync: typeof fs.existsSync;
48
+ };
49
+ /**
50
+ * Parameters for building a context capsule.
51
+ * Defined inline — not part of the public API surface.
52
+ */
53
+ interface BuildCapsuleParams {
54
+ /** Task ID this capsule is for (e.g. "1.1", "2.3") */
55
+ task_id: string;
56
+ /** Which agent role receives this capsule */
57
+ agent_role: AgentRole;
58
+ /** Why this capsule was generated */
59
+ delegation_reason: CapsuleDelegationReason;
60
+ /** Files relevant to this task */
61
+ files_in_scope: string[];
62
+ /** What the task aims to accomplish */
63
+ task_goal: string;
64
+ /** Prior rejection reason, if this capsule is for a fix iteration */
65
+ prior_rejection?: string;
66
+ /** What needs to be fixed, if applicable */
67
+ required_fix?: string;
68
+ /** Repository facts relevant to this task */
69
+ relevant_facts?: string[];
70
+ /** Review checklist items, included for reviewer capsules */
71
+ review_checklist?: string[];
72
+ /** Coverage targets, included for test_engineer capsules */
73
+ coverage_targets?: string[];
74
+ /** Project root directory */
75
+ directory: string;
76
+ /** Maximum token budget for the capsule content (default 2000) */
77
+ max_capsule_tokens?: number;
78
+ /** Capsule mode: conservative (more files, less pruning), balanced (default), aggressive (fewer files, more pruning) */
79
+ mode?: 'conservative' | 'balanced' | 'aggressive';
80
+ /** Whether content hash changes invalidate cached file summaries. Default: true. */
81
+ invalidate_on_hash_change?: boolean;
82
+ /** Custom agent profile overrides — maps role names to strategy names */
83
+ agent_profiles?: Record<string, string>;
84
+ }
85
+ /**
86
+ * Default capsule construction profiles for each agent role.
87
+ * Controls strategy, max file count, and which optional sections are included.
88
+ */
89
+ export declare const DEFAULT_ROLE_PROFILES: Record<AgentRole, RoleProfile>;
90
+ /**
91
+ * Build a per-file read policy that tells the consuming agent whether to
92
+ * trust cached summaries or read the original source.
93
+ *
94
+ * For each file:
95
+ * - Not in map → read original (no cached data exists)
96
+ * - In map but stale (content hash changed) → read original
97
+ * - In map and fresh → trust summary
98
+ *
99
+ * When `contentCache` is provided, the file contents read during staleness
100
+ * checking are stored in the map so callers can reuse them without re-reading
101
+ * the filesystem (avoids the redundant double-read in buildCapsule).
102
+ *
103
+ * @param files - Relative file paths to build policy for
104
+ * @param map - The loaded Context Map
105
+ * @param directory - Project root directory for file resolution
106
+ * @param invalidateOnHashChange - Whether content hash changes invalidate entries
107
+ * @param contentCache - Optional output map populated with `filePath → content`
108
+ * @returns Array of read policy entries, one per file
109
+ */
110
+ export declare function buildReadPolicy(files: string[], map: ContextMap, directory: string, invalidateOnHashChange?: boolean, contentCache?: Map<string, string | undefined>): ReadPolicyEntry[];
111
+ /**
112
+ * Build a role-specific Context Capsule for a delegated agent.
113
+ *
114
+ * Loads the context map, applies the role profile to determine which
115
+ * sections to include, builds a per-file read policy, and formats
116
+ * everything as a structured markdown document.
117
+ *
118
+ * Never throws — returns a best-effort capsule even if the context map
119
+ * is missing or corrupt.
120
+ *
121
+ * @param params - Build parameters including task ID, role, files, and directory
122
+ * @returns The constructed capsule and diagnostic metadata
123
+ */
124
+ export declare function buildCapsule(params: BuildCapsuleParams): {
125
+ capsule: ContextCapsule;
126
+ metadata: CapsuleMetadata;
127
+ };
128
+ export {};
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Capsule persistence module — handles saving, loading, deleting, and
3
+ * listing Context Capsules stored at `.swarm/capsules/{task_id}.json`.
4
+ *
5
+ * All functions are synchronous for simplicity and reliability. The module
6
+ * uses the `_internals` DI seam pattern so tests can override filesystem
7
+ * operations without `mock.module` (which leaks across files in Bun's
8
+ * shared test-runner process).
9
+ *
10
+ * State lives exclusively under `.swarm/` (Invariant 4). No `process.cwd()`
11
+ * usage — every function accepts an explicit `directory` parameter.
12
+ *
13
+ * No `bun:` imports — this module is Node-ESM-loadable (Invariant 2).
14
+ *
15
+ * No imports from capsule-builder to avoid circular dependencies.
16
+ */
17
+ import * as fs from 'node:fs';
18
+ import type { CapsuleMetadata, ContextCapsule } from '../types/context-capsule';
19
+ /**
20
+ * Test-only dependency-injection seam. Production code calls through this
21
+ * object so tests can replace the underlying implementations without
22
+ * `mock.module` (which leaks across files in Bun's shared test-runner process).
23
+ * Mutating this local object is file-scoped and trivially restorable
24
+ * via `afterEach`.
25
+ */
26
+ export declare const _internals: {
27
+ readonly writeFileSync: typeof fs.writeFileSync;
28
+ readonly readFileSync: typeof fs.readFileSync;
29
+ readonly existsSync: typeof fs.existsSync;
30
+ readonly mkdirSync: typeof fs.mkdirSync;
31
+ readonly readdirSync: typeof fs.readdirSync;
32
+ readonly unlinkSync: typeof fs.unlinkSync;
33
+ readonly renameSync: typeof fs.renameSync;
34
+ };
35
+ /**
36
+ * Save a capsule to `.swarm/capsules/{task_id}.json` using an atomic
37
+ * temp-then-rename write pattern to avoid partial-write corruption.
38
+ *
39
+ * Creates the `.swarm/capsules/` directory if it does not exist.
40
+ *
41
+ * Returns {@link CapsuleMetadata} with diagnostics. On error, returns
42
+ * metadata with `success: false` — never throws.
43
+ */
44
+ export declare function saveCapsule(capsule: ContextCapsule, directory: string): CapsuleMetadata;
45
+ /**
46
+ * Load a capsule from `.swarm/capsules/{task_id}.json`.
47
+ *
48
+ * Returns the parsed {@link ContextCapsule}, or `null` if the file
49
+ * doesn't exist or the JSON is invalid. Never throws.
50
+ */
51
+ export declare function loadCapsule(taskId: string, directory: string): ContextCapsule | null;
52
+ /**
53
+ * Delete a capsule from `.swarm/capsules/{task_id}.json`.
54
+ *
55
+ * Returns `true` if the file was deleted, `false` if it didn't exist.
56
+ * Never throws.
57
+ */
58
+ export declare function deleteCapsule(taskId: string, directory: string): boolean;
59
+ /**
60
+ * List all saved capsule task IDs in `.swarm/capsules/`.
61
+ *
62
+ * Returns an array of task IDs derived from filenames (stripping the
63
+ * `.json` extension). Returns an empty array if the directory doesn't
64
+ * exist. Never throws.
65
+ */
66
+ export declare function listCapsules(directory: string): string[];
@@ -0,0 +1,100 @@
1
+ /**
2
+ * File Summary Population Module
3
+ *
4
+ * Incrementally builds FileContextEntry records as agents interact with files.
5
+ * Uses lightweight regex-based analysis (no external tool calls) to extract
6
+ * exports, imports, language, and purpose summaries from source content.
7
+ *
8
+ * All functions are synchronous and bounded — no filesystem scans,
9
+ * no subprocess calls, no network requests. This module is designed
10
+ * to be fast enough to call on every file an agent touches.
11
+ */
12
+ import type { FileContextEntry } from '../types/context-map';
13
+ /**
14
+ * Detect the programming language of a file from its extension.
15
+ *
16
+ * @param filePath - Relative or absolute file path
17
+ * @returns Language identifier string, or undefined if the extension is unrecognized
18
+ */
19
+ export declare function detectLanguage(filePath: string): string | undefined;
20
+ /**
21
+ * Matches named ESM exports: "export function foo", "export class Bar", etc.
22
+ * Capture group 1: the exported symbol name.
23
+ */
24
+ export declare const RE_EXPORT_NAMED: RegExp;
25
+ /**
26
+ * Matches brace-delimited ESM re-exports: "export { foo, bar }".
27
+ * Capture group 1: the comma-separated symbol list inside braces.
28
+ */
29
+ export declare const RE_EXPORT_BRACE: RegExp;
30
+ /**
31
+ * Matches ESM import statements: "import ... from '...'".
32
+ * Capture group 1: the module specifier (source path).
33
+ */
34
+ export declare const RE_IMPORT_FROM: RegExp;
35
+ /**
36
+ * Matches CommonJS require calls: "require('...')".
37
+ * Capture group 1: the module specifier.
38
+ */
39
+ export declare const RE_IMPORT_REQUIRE: RegExp;
40
+ /**
41
+ * Matches the first JSDoc or block comment in a file.
42
+ * Capture group 1: the comment body (between slash-star and star-slash).
43
+ */
44
+ export declare const RE_FIRST_COMMENT: RegExp;
45
+ /**
46
+ * Maximum number of key symbols to retain per file entry.
47
+ */
48
+ export declare const MAX_KEY_SYMBOLS = 10;
49
+ /**
50
+ * Maximum character length for purpose summaries.
51
+ */
52
+ export declare const MAX_PURPOSE_LENGTH = 200;
53
+ /**
54
+ * Build a complete FileContextEntry from file content.
55
+ *
56
+ * Computes content hash, detects language, extracts exports/imports,
57
+ * generates a purpose summary from the first JSDoc comment, and
58
+ * identifies key symbols from export names.
59
+ *
60
+ * When an existing entry is provided, mutable fields (hash, language,
61
+ * purpose, exports, imports, summary, mtime) are refreshed while
62
+ * accumulated fields (invariants, risks, tests, last_seen_task_ids)
63
+ * are preserved.
64
+ *
65
+ * @param filePath - Relative file path within the project
66
+ * @param content - Full file content as a string
67
+ * @param absolutePath - Optional absolute path for filesystem stat (avoids process.cwd() resolution)
68
+ * @param existingEntry - Optional previous entry to merge with
69
+ * @returns A complete FileContextEntry
70
+ */
71
+ export declare function extractFileSummary(filePath: string, content: string, absolutePath?: string, existingEntry?: FileContextEntry): FileContextEntry;
72
+ /**
73
+ * Populate FileContextEntry records for multiple files at once.
74
+ *
75
+ * For each file path, reads content from directory + path, calls
76
+ * extractFileSummary, and collects the result. Skips files that
77
+ * don't exist or can't be read.
78
+ *
79
+ * When an existing map is provided, entries are merged: new extractions
80
+ * take precedence for mutable fields while accumulated fields from
81
+ * existing entries are preserved.
82
+ *
83
+ * @param filePaths - Array of relative file paths to populate
84
+ * @param directory - Project root directory to resolve file paths against
85
+ * @param existingMap - Optional map of previously populated entries to merge with
86
+ * @returns Record mapping file paths to their FileContextEntry instances
87
+ */
88
+ export declare function batchPopulateSummaries(filePaths: string[], directory: string, existingMap?: Record<string, FileContextEntry>): Record<string, FileContextEntry>;
89
+ /**
90
+ * Check whether a stored context map entry is stale relative to current content.
91
+ *
92
+ * Compares the stored content_hash against a fresh SHA-256 hash of the
93
+ * provided current content. Returns true if the hashes differ, indicating
94
+ * the file has been modified since the entry was last populated.
95
+ *
96
+ * @param entry - The stored FileContextEntry to check
97
+ * @param currentContent - The current file content to compare against
98
+ * @returns true if the entry is stale (hashes differ), false if still current
99
+ */
100
+ export declare function isFileStale(entry: FileContextEntry, currentContent: string): boolean;
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Context Map persistence module — handles reading, writing, and invalidating
3
+ * the durable Context Map stored at `.swarm/context-map.json`.
4
+ *
5
+ * All functions are synchronous for simplicity and reliability. The module
6
+ * uses the `_internals` DI seam pattern so tests can override filesystem
7
+ * and crypto operations without `mock.module` (which leaks across files in
8
+ * Bun's shared test-runner process).
9
+ *
10
+ * State lives exclusively under `.swarm/` (Invariant 4). No `process.cwd()`
11
+ * usage — every function accepts an explicit `directory` parameter.
12
+ *
13
+ * No `bun:` imports — this module is Node-ESM-loadable (Invariant 2).
14
+ */
15
+ import * as crypto from 'node:crypto';
16
+ import * as fs from 'node:fs';
17
+ import type { ContentHash, ContextMap, ContextMapStaleEntry, DecisionEntry, TaskContextSummary } from '../types/context-map';
18
+ /**
19
+ * Test-only dependency-injection seam. Production code calls through this
20
+ * object so tests can replace the underlying implementations without
21
+ * `mock.module` (which leaks across files in Bun's shared test-runner process).
22
+ * Mutating this local object is file-scoped and trivially restorable
23
+ * via `afterEach`.
24
+ */
25
+ export declare const _internals: {
26
+ readonly readFileSync: typeof fs.readFileSync;
27
+ readonly writeFileSync: typeof fs.writeFileSync;
28
+ readonly mkdirSync: typeof fs.mkdirSync;
29
+ readonly renameSync: typeof fs.renameSync;
30
+ readonly existsSync: typeof fs.existsSync;
31
+ readonly statSync: fs.StatSyncFn;
32
+ readonly createHash: typeof crypto.createHash;
33
+ };
34
+ /**
35
+ * Compute a SHA-256 content hash for the given string content.
36
+ * Returns the hash as a 64-character lowercase hex string.
37
+ */
38
+ export declare function computeContentHash(content: string): ContentHash;
39
+ /**
40
+ * Create a new empty ContextMap with sensible defaults.
41
+ * The `repo_fingerprint` is left empty and populated by the caller later.
42
+ */
43
+ export declare function createEmptyContextMap(): ContextMap;
44
+ /**
45
+ * Load the Context Map from `.swarm/context-map.json` in the given directory.
46
+ * Returns `null` if the file doesn't exist or cannot be parsed (corrupt file).
47
+ * Never throws.
48
+ */
49
+ export declare function loadContextMap(directory: string): ContextMap | null;
50
+ /**
51
+ * Save the Context Map to `.swarm/context-map.json` using an atomic
52
+ * temp-then-rename write pattern to avoid partial-write corruption.
53
+ *
54
+ * Updates `generated_at` to the current timestamp before writing.
55
+ * Creates the `.swarm/` directory if it does not exist.
56
+ */
57
+ export declare function saveContextMap(map: ContextMap, directory: string): void;
58
+ /**
59
+ * Scan all file entries in the context map and detect which ones have
60
+ * changed or been deleted since the map was last generated.
61
+ *
62
+ * Uses an mtime pre-check before computing the expensive SHA-256 hash —
63
+ * only files with a modified timestamp undergo hashing.
64
+ *
65
+ * Returns the list of stale entries. Files that don't exist on disk or
66
+ * whose content hash differs from the stored hash are included.
67
+ * This function is read-only and does NOT modify the input map.
68
+ */
69
+ export declare function markStaleEntries(map: ContextMap, directory: string): ContextMapStaleEntry[];
70
+ /**
71
+ * Add or update a task history entry in the context map.
72
+ * Returns a new ContextMap with the entry added/updated (input is not mutated).
73
+ */
74
+ export declare function appendTaskHistory(map: ContextMap, summary: TaskContextSummary): ContextMap;
75
+ /**
76
+ * Append an architectural decision to the context map's decisions array.
77
+ * Returns a new ContextMap with the decision appended (input is not mutated).
78
+ */
79
+ export declare function appendDecision(map: ContextMap, decision: DecisionEntry): ContextMap;
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Post-Agent Context Map Update Module
3
+ *
4
+ * After an agent completes a task, this module updates the Context Map with
5
+ * the task's results — files touched, implementation summary,
6
+ * rejection/review findings, and decisions made. This is the "write-back"
7
+ * half of the context map lifecycle.
8
+ *
9
+ * All functions use the `_internals` DI seam pattern so tests can override
10
+ * dependencies without `mock.module` (which leaks across files in Bun's
11
+ * shared test-runner process).
12
+ *
13
+ * State lives exclusively under `.swarm/` (Invariant 4). No `process.cwd()`
14
+ * usage — every function accepts an explicit `directory` parameter.
15
+ *
16
+ * No `bun:` imports — this module is Node-ESM-loadable (Invariant 2).
17
+ */
18
+ import * as fs from 'node:fs';
19
+ import type { ContextMap } from '../types/context-map';
20
+ import { extractFileSummary } from './file-summary';
21
+ import { appendDecision, appendTaskHistory, createEmptyContextMap, loadContextMap, saveContextMap } from './persistence';
22
+ /**
23
+ * Parameters for updating the Context Map after an agent completes a task.
24
+ * Captures all relevant context from the agent's work session so that
25
+ * future agents can understand what happened without re-reading evidence files.
26
+ */
27
+ export interface PostAgentUpdateParams {
28
+ /** Task ID (e.g. "1.1", "2.3") */
29
+ task_id: string;
30
+ /** Agent role that completed (coder, reviewer, etc.) */
31
+ agent_role: string;
32
+ /** Files the agent touched/modified */
33
+ files_touched: string[];
34
+ /** Brief summary of what the agent did */
35
+ implementation_summary: string;
36
+ /** Task goal description */
37
+ task_goal: string;
38
+ /** Final status of the task */
39
+ final_status: 'completed' | 'failed' | 'blocked' | 'cancelled';
40
+ /** Reviewer rejection reasons (if any) */
41
+ rejection_reasons?: string[];
42
+ /** Review findings (if any) */
43
+ review_findings?: string[];
44
+ /** Decisions made during this task */
45
+ decisions?: Array<{
46
+ decision: string;
47
+ rationale: string;
48
+ }>;
49
+ /** Project root directory */
50
+ directory: string;
51
+ }
52
+ /**
53
+ * Test-only dependency-injection seam. Production code calls through this
54
+ * object so tests can replace the underlying implementations without
55
+ * `mock.module` (which leaks across files in Bun's shared test-runner process).
56
+ * Mutating this local object is file-scoped and trivially restorable
57
+ * via `afterEach`.
58
+ */
59
+ export declare const _internals: {
60
+ loadContextMap: typeof loadContextMap;
61
+ saveContextMap: typeof saveContextMap;
62
+ createEmptyContextMap: typeof createEmptyContextMap;
63
+ extractFileSummary: typeof extractFileSummary;
64
+ existsSync: typeof fs.existsSync;
65
+ readFileSync: typeof fs.readFileSync;
66
+ readdirSync: typeof fs.readdirSync;
67
+ realpathSync: typeof fs.realpathSync;
68
+ appendTaskHistory: typeof appendTaskHistory;
69
+ appendDecision: typeof appendDecision;
70
+ };
71
+ /**
72
+ * Extract reviewer/critic findings from .swarm/evidence/ files for a task.
73
+ *
74
+ * Looks for reviewer and test-engineer evidence files under
75
+ * `.swarm/evidence/{taskId}/`, parses them, and returns consolidated
76
+ * rejection reasons and findings.
77
+ *
78
+ * Tries both `test-engineer.json` and `test_engineer.json` filenames to
79
+ * handle naming inconsistencies across the repo.
80
+ *
81
+ * Evidence files may contain:
82
+ * - `verdict`: string (e.g. "APPROVED", "approved", "pass", "REJECTED") —
83
+ * non-approval verdicts are collected as rejection reasons. Case-insensitive.
84
+ * - `issues`: array of objects with a `message`, `detail`, or `description`
85
+ * string field — each is collected as a finding.
86
+ * - `findings`: array of objects with a `message`, `detail`, or `description`
87
+ * string field — also collected as findings.
88
+ *
89
+ * Never throws — returns empty arrays on any error (missing directory,
90
+ * unreadable files, malformed JSON, etc.).
91
+ *
92
+ * @param taskId - Task ID (e.g. "1.1", "2.3")
93
+ * @param directory - Project root directory
94
+ * @returns Consolidated rejection reasons and review findings from evidence
95
+ */
96
+ export declare function extractEvidenceFindings(taskId: string, directory: string): {
97
+ rejection_reasons: string[];
98
+ review_findings: string[];
99
+ };
100
+ /**
101
+ * Update the Context Map after an agent completes a task.
102
+ *
103
+ * This is the "write-back" half of the context map lifecycle. It:
104
+ * 1. Loads the existing context map (or creates an empty one)
105
+ * 2. Refreshes file summaries for all touched files, preserving accumulated data
106
+ * 3. Appends a TaskContextSummary entry for the completed task
107
+ * 4. Appends any architectural decisions made during the task
108
+ * 5. Saves the updated map and returns it
109
+ *
110
+ * Never throws — on any error, returns the best-effort context map
111
+ * (either the existing one or a fresh empty one).
112
+ *
113
+ * @param params - Parameters describing the completed task and its results
114
+ * @returns The updated ContextMap
115
+ */
116
+ export declare function updateContextMapAfterAgent(params: PostAgentUpdateParams): ContextMap;