@mrclrchtr/supi-claude-md 1.1.3 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -10,11 +10,7 @@ Then it helps you keep those files in shape — audit quality, flag stale sectio
10
10
 
11
11
  ### Context that travels
12
12
 
13
- Reads, writes, edits, LSP operations — any time the agent touches a file, it picks up the nearest `CLAUDE.md` or `AGENTS.md` in that directory. Conventions arrive exactly when they're needed, not dumped upfront.
14
-
15
- ### Smart about when to refresh
16
-
17
- First-time discovery always injects. Re-reads wait a configurable number of turns and skip when the context window is too full. No flooding.
13
+ Reads, writes, edits, LSP operations — any time the agent touches a file, it picks up the nearest `CLAUDE.md` or `AGENTS.md` in that directory. Each subdirectory's context is injected once (on first discovery) and available for the rest of the session.
18
14
 
19
15
  ### CLAUDE.md maintenance
20
16
 
@@ -37,14 +33,10 @@ Configure via `/supi-settings` or directly in config:
37
33
  {
38
34
  "claude-md": {
39
35
  "subdirs": true,
40
- "rereadInterval": 3,
41
- "contextThreshold": 80,
42
36
  "fileNames": ["CLAUDE.md", "AGENTS.md"]
43
37
  }
44
38
  }
45
39
  ```
46
40
 
47
41
  - `subdirs` — toggle subdirectory discovery on/off
48
- - `rereadInterval` — turns between re-reading a directory's context (0 = never re-read)
49
- - `contextThreshold` — skip re-reads when context usage is above this percent
50
42
  - `fileNames` — which filenames to look for (comma-separated)
@@ -12,7 +12,12 @@ pnpm add @mrclrchtr/supi-core
12
12
 
13
13
  ## Package role
14
14
 
15
- `@mrclrchtr/supi-core` is a library package. It does **not** register a pi extension and is not meant to be installed as a standalone pi package.
15
+ `@mrclrchtr/supi-core` now has two explicit surfaces:
16
+
17
+ - `@mrclrchtr/supi-core/api` — shared library helpers for other SuPi packages
18
+ - `@mrclrchtr/supi-core/extension` — a minimal pi extension that registers `/supi-settings`
19
+
20
+ `pi.extensions` still points at the real file path `./src/extension.ts` inside the package. The `/api` and `/extension` paths are consumer-facing package exports, not manifest aliases.
16
21
 
17
22
  ## What it provides
18
23
 
@@ -59,7 +64,7 @@ Main helpers:
59
64
  ## Example
60
65
 
61
66
  ```ts
62
- import { loadSupiConfig, registerConfigSettings, wrapExtensionContext } from "@mrclrchtr/supi-core";
67
+ import { loadSupiConfig, registerConfigSettings, wrapExtensionContext } from "@mrclrchtr/supi-core/api";
63
68
 
64
69
  const config = loadSupiConfig("my-extension", process.cwd(), {
65
70
  enabled: true,
@@ -87,4 +92,5 @@ const message = wrapExtensionContext("my-extension", "hello", {
87
92
 
88
93
  ## Source
89
94
 
90
- - Main exports: `src/index.ts`
95
+ - Library surface: `src/api.ts`
96
+ - Extension surface: `src/extension.ts`
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrclrchtr/supi-core",
3
- "version": "1.1.3",
3
+ "version": "1.3.0",
4
4
  "description": "SuPi core — shared infrastructure for SuPi extensions (XML context tags, config system)",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -30,5 +30,15 @@
30
30
  "optional": true
31
31
  }
32
32
  },
33
- "main": "src/index.ts"
33
+ "main": "src/api.ts",
34
+ "exports": {
35
+ "./api": "./src/api.ts",
36
+ "./extension": "./src/extension.ts",
37
+ "./package.json": "./package.json"
38
+ },
39
+ "pi": {
40
+ "extensions": [
41
+ "./src/extension.ts"
42
+ ]
43
+ }
34
44
  }
@@ -0,0 +1,83 @@
1
+ // supi-core — shared infrastructure for SuPi extensions.
2
+ // Provides XML context tag wrapping, unified config system, context-message utilities,
3
+ // and settings registry for supi-wide TUI settings.
4
+
5
+ export type { SupiConfigLocation, SupiConfigOptions } from "./config.ts";
6
+ export {
7
+ loadSupiConfig,
8
+ loadSupiConfigForScope,
9
+ removeSupiConfigKey,
10
+ writeSupiConfig,
11
+ } from "./config.ts";
12
+ export type { ConfigSettingsHelpers, ConfigSettingsOptions } from "./config-settings.ts";
13
+ export { registerConfigSettings } from "./config-settings.ts";
14
+ export type { ContextMessageLike } from "./context-messages.ts";
15
+ export {
16
+ findLastUserMessageIndex,
17
+ getContextToken,
18
+ getPromptContent,
19
+ pruneAndReorderContextMessages,
20
+ restorePromptContent,
21
+ } from "./context-messages.ts";
22
+ export type { ContextProvider } from "./context-provider-registry.ts";
23
+ export {
24
+ clearRegisteredContextProviders,
25
+ getRegisteredContextProviders,
26
+ registerContextProvider,
27
+ } from "./context-provider-registry.ts";
28
+ export { wrapExtensionContext } from "./context-tag.ts";
29
+ export type {
30
+ DebugAgentAccess,
31
+ DebugEvent,
32
+ DebugEventInput,
33
+ DebugEventQuery,
34
+ DebugEventQueryResult,
35
+ DebugEventView,
36
+ DebugLevel,
37
+ DebugNotifyLevel,
38
+ DebugRegistryConfig,
39
+ DebugSummary,
40
+ } from "./debug-registry.ts";
41
+ export {
42
+ clearDebugEvents,
43
+ configureDebugRegistry,
44
+ DEBUG_REGISTRY_DEFAULTS,
45
+ getDebugEvents,
46
+ getDebugRegistryConfig,
47
+ getDebugSummary,
48
+ recordDebugEvent,
49
+ redactDebugData,
50
+ resetDebugRegistry,
51
+ } from "./debug-registry.ts";
52
+ export type { KnownRootEntry } from "./project-roots.ts";
53
+ export {
54
+ buildKnownRootsMap,
55
+ byPathDepth,
56
+ dedupeTopmostRoots,
57
+ findProjectRoot,
58
+ isWithin,
59
+ isWithinOrEqual,
60
+ mergeKnownRoots,
61
+ resolveKnownRoot,
62
+ segmentCount,
63
+ sortRootsBySpecificity,
64
+ walkProject,
65
+ } from "./project-roots.ts";
66
+ export { getActiveBranchEntries } from "./session-utils.ts";
67
+ export { registerSettingsCommand } from "./settings-command.ts";
68
+ export type { SettingsScope, SettingsSection } from "./settings-registry.ts";
69
+ export {
70
+ clearRegisteredSettings,
71
+ getRegisteredSettings,
72
+ registerSettings,
73
+ } from "./settings-registry.ts";
74
+ export { createInputSubmenu, openSettingsOverlay } from "./settings-ui.ts";
75
+ export type { TitleTarget } from "./terminal.ts";
76
+ export {
77
+ DONE_SYMBOL,
78
+ formatTitle,
79
+ signalBell,
80
+ signalDone,
81
+ signalWaiting,
82
+ WAITING_SYMBOL,
83
+ } from "./terminal.ts";
@@ -0,0 +1 @@
1
+ export { registerSettingsCommand as default } from "./settings-command.ts";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrclrchtr/supi-claude-md",
3
- "version": "1.1.3",
3
+ "version": "1.3.0",
4
4
  "description": "SuPi claude-md extension — automatic subdirectory context injection for pi",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -21,7 +21,7 @@
21
21
  "!__tests__"
22
22
  ],
23
23
  "dependencies": {
24
- "@mrclrchtr/supi-core": "1.1.3"
24
+ "@mrclrchtr/supi-core": "1.3.0"
25
25
  },
26
26
  "bundledDependencies": [
27
27
  "@mrclrchtr/supi-core"
@@ -40,8 +40,13 @@
40
40
  },
41
41
  "pi": {
42
42
  "extensions": [
43
- "./src/claude-md.ts"
43
+ "./src/extension.ts"
44
44
  ]
45
45
  },
46
- "main": "src/index.ts"
46
+ "main": "src/api.ts",
47
+ "exports": {
48
+ "./api": "./src/api.ts",
49
+ "./extension": "./src/extension.ts",
50
+ "./package.json": "./package.json"
51
+ }
47
52
  }
package/src/api.ts ADDED
@@ -0,0 +1 @@
1
+ export { default } from "./claude-md.ts";
package/src/claude-md.ts CHANGED
@@ -13,17 +13,17 @@ import type {
13
13
  ExtensionContext,
14
14
  SessionCompactEvent,
15
15
  SessionStartEvent,
16
- TurnEndEvent,
17
16
  } from "@earendil-works/pi-coding-agent";
18
17
  import { loadClaudeMdConfig } from "./config.ts";
18
+ import type { DiscoveredContextFile } from "./discovery.ts";
19
19
  import {
20
20
  extractPathFromToolEvent,
21
21
  filterAlreadyLoaded,
22
22
  findSubdirContextFiles,
23
23
  } from "./discovery.ts";
24
24
  import { registerClaudeMdSettings } from "./settings-registration.ts";
25
- import { type ClaudeMdState, createInitialState, reconstructState } from "./state.ts";
26
- import type { ContextUsage, InjectionCheckOptions } from "./subdirectory.ts";
25
+ import type { ClaudeMdState } from "./state.ts";
26
+ import { createInitialState, reconstructState } from "./state.ts";
27
27
  import { formatSubdirContext, shouldInjectSubdir } from "./subdirectory.ts";
28
28
 
29
29
  const baseDir = dirname(dirname(fileURLToPath(import.meta.url)));
@@ -39,10 +39,8 @@ export default function claudeMdExtension(pi: ExtensionAPI) {
39
39
 
40
40
  try {
41
41
  const branch = ctx.sessionManager.getBranch();
42
-
43
42
  if (branch.length > 0) {
44
43
  const reconstructed = reconstructState(branch);
45
- state.completedTurns = reconstructed.completedTurns;
46
44
  state.injectedDirs = reconstructed.injectedDirs;
47
45
  }
48
46
  } catch {
@@ -50,15 +48,6 @@ export default function claudeMdExtension(pi: ExtensionAPI) {
50
48
  }
51
49
  });
52
50
 
53
- // ── Turn tracking ──────────────────────────────────────────
54
-
55
- pi.on("turn_end", async (event: TurnEndEvent, _ctx: ExtensionContext) => {
56
- const msg = event.message as { stopReason?: string };
57
- if (msg?.stopReason === "stop") {
58
- state.completedTurns++;
59
- }
60
- });
61
-
62
51
  // ── Compaction ─────────────────────────────────────────────
63
52
 
64
53
  pi.on("session_compact", async (_event: SessionCompactEvent, _ctx: ExtensionContext) => {
@@ -96,17 +85,11 @@ export default function claudeMdExtension(pi: ExtensionAPI) {
96
85
  );
97
86
  if (found.length === 0) return;
98
87
 
99
- const dirsToInject = collectStaleDirs(found, {
100
- injectedDirs: state.injectedDirs,
101
- currentTurn: state.completedTurns,
102
- rereadInterval: config.rereadInterval,
103
- contextThreshold: config.contextThreshold,
104
- contextUsage: _ctx.getContextUsage() as ContextUsage | undefined,
105
- });
88
+ const dirsToInject = collectFreshDirs(found, state.injectedDirs);
106
89
  if (dirsToInject.size === 0) return;
107
90
 
108
91
  const filesToInject = Array.from(dirsToInject.values()).flat();
109
- const contextText = formatSubdirContext(filesToInject, state.completedTurns);
92
+ const contextText = formatSubdirContext(filesToInject);
110
93
  if (!contextText) return;
111
94
 
112
95
  updateInjectedDirTracking(state, dirsToInject);
@@ -135,13 +118,13 @@ function captureNativePaths(
135
118
  }
136
119
  }
137
120
 
138
- function collectStaleDirs(
139
- found: ReturnType<typeof findSubdirContextFiles>,
140
- injectionOpts: InjectionCheckOptions,
141
- ): Map<string, typeof found> {
142
- const dirsToInject = new Map<string, typeof found>();
121
+ function collectFreshDirs(
122
+ found: DiscoveredContextFile[],
123
+ injectedDirs: Set<string>,
124
+ ): Map<string, DiscoveredContextFile[]> {
125
+ const dirsToInject = new Map<string, DiscoveredContextFile[]>();
143
126
  for (const file of found) {
144
- if (shouldInjectSubdir(file.dir, injectionOpts)) {
127
+ if (shouldInjectSubdir(file.dir, injectedDirs)) {
145
128
  const existing = dirsToInject.get(file.dir) ?? [];
146
129
  existing.push(file);
147
130
  dirsToInject.set(file.dir, existing);
@@ -152,12 +135,9 @@ function collectStaleDirs(
152
135
 
153
136
  function updateInjectedDirTracking(
154
137
  state: ClaudeMdState,
155
- dirsToInject: Map<string, Array<{ dir: string; relativePath: string }>>,
138
+ dirsToInject: Map<string, DiscoveredContextFile[]>,
156
139
  ): void {
157
- for (const [dir, files] of dirsToInject) {
158
- const firstFile = files[0];
159
- if (firstFile) {
160
- state.injectedDirs.set(dir, { turn: state.completedTurns, file: firstFile.relativePath });
161
- }
140
+ for (const dir of dirsToInject.keys()) {
141
+ state.injectedDirs.add(dir);
162
142
  }
163
143
  }
package/src/config.ts CHANGED
@@ -2,19 +2,13 @@
2
2
  //
3
3
  // Config shape (in supi shared config, "claude-md" section):
4
4
  // {
5
- // "rereadInterval": 3, // turns between subdirectory re-reads (0 = off)
6
- // "contextThreshold": 80, // skip injection when context % >= threshold
7
5
  // "subdirs": true, // enable subdirectory context discovery
8
6
  // "fileNames": ["CLAUDE.md", "AGENTS.md"] // context file names to look for
9
7
  // }
10
8
 
11
- import { loadSupiConfig } from "@mrclrchtr/supi-core";
9
+ import { loadSupiConfig } from "@mrclrchtr/supi-core/api";
12
10
 
13
11
  export interface ClaudeMdConfig {
14
- /** Turns between re-reading previously injected subdirectory context. 0 = disabled. Default: 3 */
15
- rereadInterval: number;
16
- /** Skip injection when context window usage % >= threshold. 0 = always skip, 100 = never skip. Default: 80 */
17
- contextThreshold: number;
18
12
  /** Enable subdirectory context discovery. Default: true */
19
13
  subdirs: boolean;
20
14
  /** Context file names to look for (first match per directory). Default: ["CLAUDE.md", "AGENTS.md"] */
@@ -22,8 +16,6 @@ export interface ClaudeMdConfig {
22
16
  }
23
17
 
24
18
  export const CLAUDE_MD_DEFAULTS: ClaudeMdConfig = {
25
- rereadInterval: 3,
26
- contextThreshold: 80,
27
19
  subdirs: true,
28
20
  fileNames: ["CLAUDE.md", "AGENTS.md"],
29
21
  };
@@ -0,0 +1 @@
1
+ export { default } from "./claude-md.ts";
@@ -5,33 +5,9 @@ import {
5
5
  type ConfigSettingsHelpers,
6
6
  createInputSubmenu,
7
7
  registerConfigSettings,
8
- } from "@mrclrchtr/supi-core";
8
+ } from "@mrclrchtr/supi-core/api";
9
9
  import { CLAUDE_MD_DEFAULTS, type ClaudeMdConfig } from "./config.ts";
10
10
 
11
- const THRESHOLD_VALUES = [
12
- "0",
13
- "5",
14
- "10",
15
- "15",
16
- "20",
17
- "25",
18
- "30",
19
- "35",
20
- "40",
21
- "45",
22
- "50",
23
- "55",
24
- "60",
25
- "65",
26
- "70",
27
- "75",
28
- "80",
29
- "85",
30
- "90",
31
- "95",
32
- "100",
33
- ];
34
-
35
11
  // ── Settings registration ────────────────────────────────────
36
12
 
37
13
  export function registerClaudeMdSettings(): void {
@@ -58,16 +34,6 @@ function handleSettingChange(
58
34
  helpers.set("subdirs", value === "on");
59
35
  break;
60
36
  }
61
- case "rereadInterval": {
62
- const num = Number.parseInt(value, 10);
63
- helpers.set("rereadInterval", Number.isNaN(num) ? 0 : num);
64
- break;
65
- }
66
- case "contextThreshold": {
67
- const num = Number.parseInt(value, 10);
68
- helpers.set("contextThreshold", Number.isNaN(num) ? 80 : num);
69
- break;
70
- }
71
37
  case "fileNames": {
72
38
  const names = value
73
39
  .split(",")
@@ -92,21 +58,6 @@ function buildClaudeMdSettingItems(settings: ClaudeMdConfig): SettingItem[] {
92
58
  currentValue: settings.subdirs ? "on" : "off",
93
59
  values: ["on", "off"],
94
60
  },
95
- {
96
- id: "rereadInterval",
97
- label: "Subdirectory Re-read Interval",
98
- description: "Turns between re-reading previously injected subdirectory context (0 = off)",
99
- currentValue: String(settings.rereadInterval),
100
- submenu: (currentValue, done) =>
101
- createInputSubmenu(currentValue, "Interval (0 = off):", done),
102
- },
103
- {
104
- id: "contextThreshold",
105
- label: "Context Threshold",
106
- description: "Skip injection when context window usage % ≥ threshold (100 = never skip)",
107
- currentValue: String(settings.contextThreshold),
108
- values: THRESHOLD_VALUES,
109
- },
110
61
  {
111
62
  id: "fileNames",
112
63
  label: "Context File Names",
package/src/state.ts CHANGED
@@ -5,18 +5,9 @@
5
5
 
6
6
  import type { SessionEntry } from "@earendil-works/pi-coding-agent";
7
7
 
8
- export interface InjectedDir {
9
- /** Turn number when this directory's context was last injected */
10
- turn: number;
11
- /** Relative path of the context file that was injected */
12
- file: string;
13
- }
14
-
15
8
  export interface ClaudeMdState {
16
- /** Count of completed assistant turns (stopReason: "stop") */
17
- completedTurns: number;
18
- /** Map of directory path → injection info */
19
- injectedDirs: Map<string, InjectedDir>;
9
+ /** Set of directory paths whose context has already been injected */
10
+ injectedDirs: Set<string>;
20
11
  /** Set of paths already loaded by pi natively (dedup) */
21
12
  nativeContextPaths: Set<string>;
22
13
  /** Whether this is the first before_agent_start (for native path capture) */
@@ -25,41 +16,27 @@ export interface ClaudeMdState {
25
16
 
26
17
  export function createInitialState(): ClaudeMdState {
27
18
  return {
28
- completedTurns: 0,
29
- injectedDirs: new Map(),
19
+ injectedDirs: new Set(),
30
20
  nativeContextPaths: new Set(),
31
21
  firstAgentStart: true,
32
22
  };
33
23
  }
34
24
 
35
- const CONTEXT_TAG_REGEX =
36
- /<extension-context\s+source="supi-claude-md"\s+file="([^"]+)"\s+turn="(\d+)">/g;
25
+ const CONTEXT_TAG_REGEX = /<extension-context\s+source="supi-claude-md"\s+file="([^"]+)"[^>]*>/g;
37
26
 
38
27
  export function reconstructState(branch: SessionEntry[]): {
39
- completedTurns: number;
40
- injectedDirs: Map<string, InjectedDir>;
28
+ injectedDirs: Set<string>;
41
29
  } {
42
- let completedTurns = 0;
43
- const injectedDirs = new Map<string, InjectedDir>();
30
+ const injectedDirs = new Set<string>();
44
31
 
45
32
  for (const entry of branch) {
46
- if (isCompletedAssistantTurn(entry)) completedTurns++;
47
-
48
33
  const toolResultContent = getToolResultContent(entry);
49
34
  if (toolResultContent) {
50
35
  extractInjectedDirs(toolResultContent, injectedDirs);
51
36
  }
52
37
  }
53
38
 
54
- return { completedTurns, injectedDirs };
55
- }
56
-
57
- function isCompletedAssistantTurn(entry: SessionEntry): boolean {
58
- return (
59
- entry.type === "message" &&
60
- entry.message.role === "assistant" &&
61
- entry.message.stopReason === "stop"
62
- );
39
+ return { injectedDirs };
63
40
  }
64
41
 
65
42
  function getToolResultContent(entry: SessionEntry): unknown {
@@ -69,7 +46,7 @@ function getToolResultContent(entry: SessionEntry): unknown {
69
46
  return entry.message.content;
70
47
  }
71
48
 
72
- function extractInjectedDirs(content: unknown, injectedDirs: Map<string, InjectedDir>): void {
49
+ function extractInjectedDirs(content: unknown, injectedDirs: Set<string>): void {
73
50
  const parts = content as Array<{ type?: string; text?: string }> | undefined;
74
51
  if (!parts) return;
75
52
 
@@ -80,15 +57,14 @@ function extractInjectedDirs(content: unknown, injectedDirs: Map<string, Injecte
80
57
  }
81
58
  }
82
59
 
83
- function parseContextTags(text: string, injectedDirs: Map<string, InjectedDir>): void {
60
+ function parseContextTags(text: string, injectedDirs: Set<string>): void {
84
61
  const matches = text.matchAll(CONTEXT_TAG_REGEX);
85
62
  for (const match of matches) {
86
63
  const file = match[1];
87
- const turn = Number.parseInt(match[2] ?? "0", 10);
88
64
  if (file) {
89
65
  const lastSlash = Math.max(file.lastIndexOf("/"), file.lastIndexOf("\\"));
90
66
  const dir = lastSlash >= 0 ? file.substring(0, lastSlash) : ".";
91
- injectedDirs.set(dir, { turn, file });
67
+ injectedDirs.add(dir);
92
68
  }
93
69
  }
94
70
  }
@@ -1,27 +1,17 @@
1
1
  // Subdirectory context injection logic.
2
2
  //
3
3
  // Handles formatting discovered context files into <extension-context> blocks
4
- // and determining whether injection should occur based on staleness.
4
+ // and determining whether injection should occur.
5
5
 
6
6
  import * as fs from "node:fs";
7
- import { wrapExtensionContext } from "@mrclrchtr/supi-core";
7
+ import { wrapExtensionContext } from "@mrclrchtr/supi-core/api";
8
8
  import type { DiscoveredContextFile } from "./discovery.ts";
9
- import type { InjectedDir } from "./state.ts";
10
-
11
- /**
12
- * Context usage info from pi's ctx.getContextUsage().
13
- */
14
- export interface ContextUsage {
15
- tokens: number | null;
16
- contextWindow: number;
17
- percent: number | null;
18
- }
19
9
 
20
10
  /**
21
11
  * Format discovered context files into <extension-context> blocks.
22
12
  * Each file is read and wrapped individually.
23
13
  */
24
- export function formatSubdirContext(files: DiscoveredContextFile[], turn: number): string {
14
+ export function formatSubdirContext(files: DiscoveredContextFile[]): string {
25
15
  const parts: string[] = [];
26
16
 
27
17
  for (const file of files) {
@@ -31,7 +21,6 @@ export function formatSubdirContext(files: DiscoveredContextFile[], turn: number
31
21
  parts.push(
32
22
  wrapExtensionContext("supi-claude-md", content, {
33
23
  file: file.relativePath,
34
- turn,
35
24
  }),
36
25
  );
37
26
  }
@@ -43,42 +32,10 @@ export function formatSubdirContext(files: DiscoveredContextFile[], turn: number
43
32
  return parts.join("\n\n");
44
33
  }
45
34
 
46
- export interface InjectionCheckOptions {
47
- injectedDirs: Map<string, InjectedDir>;
48
- currentTurn: number;
49
- rereadInterval: number;
50
- contextThreshold: number;
51
- contextUsage?: ContextUsage;
52
- }
53
-
54
35
  /**
55
36
  * Determine if subdirectory context should be injected.
56
- * Returns true if:
57
- * - The directory has not been injected yet (always, even under context pressure)
58
- * - The directory was injected but is stale (turn delta >= rereadInterval)
59
- * AND context usage is below the threshold
60
- * - rereadInterval is 0 (disabled — always false for re-injections)
37
+ * Returns true only if the directory has not been injected yet.
61
38
  */
62
- export function shouldInjectSubdir(dir: string, options: InjectionCheckOptions): boolean {
63
- const { injectedDirs, currentTurn, rereadInterval, contextThreshold, contextUsage } = options;
64
-
65
- // Never-injected directory: always inject (even when rereadInterval is 0)
66
- // First-time discovery is always allowed regardless of context pressure
67
- const injected = injectedDirs.get(dir);
68
- if (!injected) return true;
69
-
70
- // Already-injected directory: skip if reread is disabled
71
- if (rereadInterval === 0) return false;
72
-
73
- // Re-injection: skip when context usage is at or above threshold
74
- if (
75
- contextThreshold < 100 &&
76
- contextUsage &&
77
- contextUsage.percent != null &&
78
- contextUsage.percent >= contextThreshold
79
- ) {
80
- return false;
81
- }
82
-
83
- return currentTurn - injected.turn >= rereadInterval;
39
+ export function shouldInjectSubdir(dir: string, injectedDirs: Set<string>): boolean {
40
+ return !injectedDirs.has(dir);
84
41
  }