@mrclrchtr/supi-context 1.3.0 → 1.4.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.
@@ -1,4 +1,3 @@
1
- import { existsSync, readFileSync } from "node:fs";
2
1
  import type {
3
2
  BuildSystemPromptOptions,
4
3
  ExtensionCommandContext,
@@ -75,12 +74,28 @@ function deriveContextFiles(systemPrompt: string): Array<{ path: string; content
75
74
 
76
75
  const contextFiles: Array<{ path: string; content: string }> = [];
77
76
  const headingRegex = /^##\s+(.+)$/gm;
78
- for (const match of projectContext.matchAll(headingRegex)) {
77
+
78
+ let match = headingRegex.exec(projectContext);
79
+ while (match !== null) {
79
80
  const filePath = match[1].trim();
80
- if (!isLikelyContextFileHeading(filePath) || !existsSync(filePath)) {
81
- continue;
81
+ const contentStart = match.index + match[0].length;
82
+
83
+ const nextMatch = headingRegex.exec(projectContext);
84
+ const contentEnd = nextMatch ? nextMatch.index : projectContext.length;
85
+
86
+ let content = projectContext.slice(contentStart, contentEnd);
87
+ if (content.startsWith("\n\n")) {
88
+ content = content.slice(2);
89
+ }
90
+ if (content.endsWith("\n\n")) {
91
+ content = content.slice(0, -2);
92
+ }
93
+
94
+ if (isLikelyContextFileHeading(filePath)) {
95
+ contextFiles.push({ path: filePath, content });
82
96
  }
83
- contextFiles.push({ path: filePath, content: readFileSync(filePath, "utf-8") });
97
+
98
+ match = nextMatch;
84
99
  }
85
100
 
86
101
  return contextFiles;
@@ -111,21 +126,30 @@ export function deriveOptionsFromSystemPrompt(
111
126
  ctx: ExtensionCommandContext,
112
127
  cachedOptions: BuildSystemPromptOptions | undefined,
113
128
  ): BuildSystemPromptOptions | undefined {
129
+ const systemPrompt = ctx.getSystemPrompt();
130
+ const derivedFiles = deriveContextFiles(systemPrompt);
131
+ const derivedSkills = deriveSkills(systemPrompt);
132
+
114
133
  if (cachedOptions) {
115
- return cachedOptions;
134
+ const hasFiles = cachedOptions.contextFiles && cachedOptions.contextFiles.length > 0;
135
+ const hasSkills = cachedOptions.skills && cachedOptions.skills.length > 0;
136
+ if (hasFiles && hasSkills) {
137
+ return cachedOptions;
138
+ }
139
+ return {
140
+ ...cachedOptions,
141
+ contextFiles: hasFiles ? cachedOptions.contextFiles : derivedFiles,
142
+ skills: hasSkills ? cachedOptions.skills : derivedSkills,
143
+ };
116
144
  }
117
145
 
118
- const systemPrompt = ctx.getSystemPrompt();
119
- const contextFiles = deriveContextFiles(systemPrompt);
120
- const skills = deriveSkills(systemPrompt);
121
-
122
- if (contextFiles.length === 0 && skills.length === 0) {
146
+ if (derivedFiles.length === 0 && derivedSkills.length === 0) {
123
147
  return undefined;
124
148
  }
125
149
 
126
150
  return {
127
151
  cwd: ctx.cwd,
128
- contextFiles,
129
- skills,
152
+ contextFiles: derivedFiles,
153
+ skills: derivedSkills,
130
154
  };
131
155
  }
package/src/renderer.ts CHANGED
@@ -1,8 +1,36 @@
1
1
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
- import { Container, Spacer, Text } from "@earendil-works/pi-tui";
2
+ import { Text } from "@earendil-works/pi-tui";
3
3
  import type { ContextAnalysis } from "./analysis.ts";
4
4
  import { formatContextReport } from "./format.ts";
5
5
 
6
+ type Theme = Parameters<Parameters<ExtensionAPI["registerMessageRenderer"]>[1]>[2];
7
+
8
+ class ContextReportComponent {
9
+ private cachedWidth?: number;
10
+ private cachedLines?: string[];
11
+
12
+ constructor(
13
+ private readonly analysis: ContextAnalysis,
14
+ private readonly theme: Theme,
15
+ ) {}
16
+
17
+ render(width: number): string[] {
18
+ if (this.cachedLines && this.cachedWidth === width) {
19
+ return this.cachedLines;
20
+ }
21
+
22
+ const lines = formatContextReport(this.analysis, this.theme, width);
23
+ this.cachedWidth = width;
24
+ this.cachedLines = lines;
25
+ return lines;
26
+ }
27
+
28
+ invalidate(): void {
29
+ this.cachedWidth = undefined;
30
+ this.cachedLines = undefined;
31
+ }
32
+ }
33
+
6
34
  export function registerContextRenderer(pi: ExtensionAPI): void {
7
35
  pi.registerMessageRenderer("supi-context", (message, _renderOptions, theme) => {
8
36
  const analysis = (message.details as { analysis?: ContextAnalysis } | undefined)?.analysis;
@@ -10,17 +38,6 @@ export function registerContextRenderer(pi: ExtensionAPI): void {
10
38
  return new Text(theme.fg("dim", "No context analysis data"), 1, 0);
11
39
  }
12
40
 
13
- const lines = formatContextReport(analysis, theme);
14
- const container = new Container();
15
-
16
- for (const line of lines) {
17
- if (line === "") {
18
- container.addChild(new Spacer(1));
19
- } else {
20
- container.addChild(new Text(line, 0, 0));
21
- }
22
- }
23
-
24
- return container;
41
+ return new ContextReportComponent(analysis, theme);
25
42
  });
26
43
  }