@compilr-dev/sdk 0.7.22 → 0.7.24

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.
@@ -7,6 +7,7 @@
7
7
  * - getTeamCheckpointer()/recordTeamActivity()/getCurrentProject() → artifacts.*
8
8
  */
9
9
  import { defineTool, createSuccessResult, createErrorResult } from '@compilr-dev/agents';
10
+ import { truncateContent } from './truncate.js';
10
11
  // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
11
12
  export function createArtifactTools(config) {
12
13
  // Safe: callers guard with `if (config.context.artifacts)` before calling
@@ -131,6 +132,8 @@ export function createArtifactTools(config) {
131
132
  }
132
133
  return createErrorResult(message);
133
134
  }
135
+ // Auto-truncate large artifact content
136
+ const tc = truncateContent(artifact.content);
134
137
  const output = [
135
138
  `Artifact: ${artifact.name}`,
136
139
  ``,
@@ -140,9 +143,12 @@ export function createArtifactTools(config) {
140
143
  `Summary: ${artifact.summary}`,
141
144
  `Created: ${artifact.createdAt.toISOString()}`,
142
145
  `Updated: ${artifact.updatedAt.toISOString()}`,
146
+ ...(tc.truncated
147
+ ? [``, `[Content truncated: ${String(tc.originalChars)} chars total]`]
148
+ : []),
143
149
  ``,
144
150
  `--- Content ---`,
145
- artifact.content,
151
+ tc.content,
146
152
  ].join('\n');
147
153
  return createSuccessResult(output);
148
154
  }
@@ -8,6 +8,7 @@
8
8
  * Ported from CLI's src/tools/document-db.ts.
9
9
  */
10
10
  import { defineTool, createSuccessResult, createErrorResult } from '@compilr-dev/agents';
11
+ import { truncateContent } from './truncate.js';
11
12
  /** All valid document types — keep in sync with DocumentType in types.ts. */
12
13
  const DOC_TYPE_ENUM = [
13
14
  // Software
@@ -253,15 +254,24 @@ export function createDocumentTools(config) {
253
254
  content = allLines.slice(start, end).join('\n');
254
255
  truncated = end < allLines.length;
255
256
  }
257
+ // Auto-truncate large full reads to prevent context blowup
258
+ const tc = truncateContent(content);
259
+ if (tc.truncated)
260
+ truncated = true;
256
261
  return createSuccessResult({
257
262
  success: true,
258
263
  document: {
259
264
  id: doc.id,
260
265
  docType: doc.docType,
261
266
  title: doc.title,
262
- content,
267
+ content: tc.content,
263
268
  totalLines,
264
- ...(truncated ? { truncated: true, hint: 'Use startLine/maxLines to read more' } : {}),
269
+ ...(truncated
270
+ ? {
271
+ truncated: true,
272
+ hint: 'Use summary_only, section, or startLine/maxLines for targeted reads',
273
+ }
274
+ : {}),
265
275
  createdAt: doc.createdAt.toISOString(),
266
276
  updatedAt: doc.updatedAt.toISOString(),
267
277
  },
@@ -19,6 +19,7 @@ export declare function createPlanTools(config: PlatformToolsConfig): (import("@
19
19
  }> | import("@compilr-dev/agents").Tool<{
20
20
  plan_id?: number;
21
21
  name?: string;
22
+ summary_only?: boolean;
22
23
  project_id?: number;
23
24
  }> | import("@compilr-dev/agents").Tool<{
24
25
  project_id?: number;
@@ -6,6 +6,7 @@
6
6
  * Ported from CLI's src/tools/plan-tools.ts.
7
7
  */
8
8
  import { defineTool, createSuccessResult, createErrorResult } from '@compilr-dev/agents';
9
+ import { truncateContent } from './truncate.js';
9
10
  // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
10
11
  export function createPlanTools(config) {
11
12
  const { context: ctx } = config;
@@ -140,7 +141,7 @@ export function createPlanTools(config) {
140
141
  // ---------------------------------------------------------------------------
141
142
  const planGetTool = defineTool({
142
143
  name: 'plan_get',
143
- description: 'Get a plan by ID or name. Returns the full plan content and linked work item details.',
144
+ description: 'Get a plan by ID or name. Returns plan content and linked work item. Large plans are auto-truncated.',
144
145
  inputSchema: {
145
146
  type: 'object',
146
147
  properties: {
@@ -152,6 +153,10 @@ export function createPlanTools(config) {
152
153
  type: 'string',
153
154
  description: 'Plan name (alternative to ID)',
154
155
  },
156
+ summary_only: {
157
+ type: 'boolean',
158
+ description: 'If true, returns plan metadata and headings without full content.',
159
+ },
155
160
  project_id: {
156
161
  type: 'number',
157
162
  description: 'Override active project (required for name lookup).',
@@ -185,13 +190,44 @@ export function createPlanTools(config) {
185
190
  message: 'Plan not found',
186
191
  });
187
192
  }
193
+ // summary_only: return metadata + headings without full content
194
+ if (input.summary_only) {
195
+ const lines = plan.content.split('\n');
196
+ const headings = lines
197
+ .map((l, i) => ({ line: i + 1, heading: l.trim() }))
198
+ .filter((h) => h.heading.match(/^#{1,6}\s/));
199
+ return createSuccessResult({
200
+ success: true,
201
+ plan: {
202
+ id: plan.id,
203
+ name: plan.name,
204
+ status: plan.status,
205
+ totalChars: plan.content.length,
206
+ headings,
207
+ workItemId: plan.workItemId,
208
+ workItem: plan.workItem
209
+ ? {
210
+ id: plan.workItem.id,
211
+ itemId: plan.workItem.itemId,
212
+ title: plan.workItem.title,
213
+ status: plan.workItem.status,
214
+ }
215
+ : null,
216
+ createdAt: plan.createdAt.toISOString(),
217
+ updatedAt: plan.updatedAt.toISOString(),
218
+ },
219
+ });
220
+ }
221
+ // Auto-truncate large plans
222
+ const tc = truncateContent(plan.content);
188
223
  return createSuccessResult({
189
224
  success: true,
190
225
  plan: {
191
226
  id: plan.id,
192
227
  name: plan.name,
193
- content: plan.content,
228
+ content: tc.content,
194
229
  status: plan.status,
230
+ ...(tc.truncated ? { truncated: true, originalChars: tc.originalChars } : {}),
195
231
  workItemId: plan.workItemId,
196
232
  workItem: plan.workItem
197
233
  ? {
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Shared output truncation for platform tools.
3
+ *
4
+ * Uses head-and-tail strategy (70% head / 20% tail) matching
5
+ * the pattern used by read_file and bash in the agents library.
6
+ */
7
+ /** Default max content size for tool output (~25K chars ≈ 6K tokens) */
8
+ export declare const DEFAULT_MAX_CONTENT_CHARS = 25000;
9
+ /**
10
+ * Truncate content using head-and-tail strategy.
11
+ * Returns the original content if under the limit.
12
+ */
13
+ export declare function truncateContent(content: string, maxChars?: number): {
14
+ content: string;
15
+ truncated: boolean;
16
+ originalChars: number;
17
+ };
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Shared output truncation for platform tools.
3
+ *
4
+ * Uses head-and-tail strategy (70% head / 20% tail) matching
5
+ * the pattern used by read_file and bash in the agents library.
6
+ */
7
+ /** Default max content size for tool output (~25K chars ≈ 6K tokens) */
8
+ export const DEFAULT_MAX_CONTENT_CHARS = 25_000;
9
+ /**
10
+ * Truncate content using head-and-tail strategy.
11
+ * Returns the original content if under the limit.
12
+ */
13
+ export function truncateContent(content, maxChars = DEFAULT_MAX_CONTENT_CHARS) {
14
+ if (content.length <= maxChars) {
15
+ return { content, truncated: false, originalChars: content.length };
16
+ }
17
+ const headSize = Math.floor(maxChars * 0.7);
18
+ const tailSize = Math.floor(maxChars * 0.2);
19
+ const omitted = content.length - headSize - tailSize;
20
+ const head = content.slice(0, headSize);
21
+ const tail = content.slice(-tailSize);
22
+ return {
23
+ content: `${head}\n\n... [${String(omitted)} chars truncated — use startLine/maxLines or section for targeted reads] ...\n\n${tail}`,
24
+ truncated: true,
25
+ originalChars: content.length,
26
+ };
27
+ }
@@ -102,15 +102,21 @@ export function createWorkItemTools(config) {
102
102
  commitHash: item.commitHash,
103
103
  createdAt: item.createdAt.toISOString(),
104
104
  };
105
- // Always include full comments for single-item lookup
105
+ // Include recent comments for single-item lookup (max 10)
106
106
  if (comments) {
107
107
  const itemComments = await comments.listByWorkItem(item.id);
108
- itemData.comments = itemComments.map((c) => ({
108
+ const MAX_COMMENTS = 10;
109
+ const limited = itemComments.slice(-MAX_COMMENTS);
110
+ itemData.comments = limited.map((c) => ({
109
111
  id: c.id,
110
112
  author: c.author,
111
- content: c.content,
113
+ content: c.content.length > 2000 ? c.content.slice(0, 2000) + '... [truncated]' : c.content,
112
114
  createdAt: c.createdAt.toISOString(),
113
115
  }));
116
+ if (itemComments.length > MAX_COMMENTS) {
117
+ itemData.totalComments = itemComments.length;
118
+ itemData.commentsShown = MAX_COMMENTS;
119
+ }
114
120
  }
115
121
  return createSuccessResult({
116
122
  success: true,
@@ -157,12 +163,19 @@ export function createWorkItemTools(config) {
157
163
  return base;
158
164
  if (wantFullComments) {
159
165
  const itemComments = await comments.listByWorkItem(item.id);
160
- base.comments = itemComments.map((c) => ({
166
+ const MAX_COMMENTS_PER_ITEM = 5;
167
+ const limited = itemComments.slice(-MAX_COMMENTS_PER_ITEM);
168
+ base.comments = limited.map((c) => ({
161
169
  id: c.id,
162
170
  author: c.author,
163
- content: c.content,
171
+ content: c.content.length > 2000
172
+ ? c.content.slice(0, 2000) + '... [truncated]'
173
+ : c.content,
164
174
  createdAt: c.createdAt.toISOString(),
165
175
  }));
176
+ if (itemComments.length > MAX_COMMENTS_PER_ITEM) {
177
+ base.totalComments = itemComments.length;
178
+ }
166
179
  }
167
180
  else {
168
181
  base.commentCount = await comments.countByWorkItem(item.id);
package/dist/tools.js CHANGED
@@ -29,9 +29,17 @@ const WEB_TOOL_NAMES = new Set(codingWebTools.map((t) => t.definition.name));
29
29
  */
30
30
  export function assembleTools(preset, toolConfig) {
31
31
  const presetTools = preset.tools;
32
- // If no config, return all preset tools
32
+ // If no config, return all preset tools + web tools (included by default)
33
33
  if (!toolConfig) {
34
- return [...presetTools];
34
+ const result = [...presetTools];
35
+ // Only add web tools if the preset actually has tools (skip for 'none' preset)
36
+ if (result.length > 0) {
37
+ const hasWeb = result.some((t) => WEB_TOOL_NAMES.has(t.definition.name));
38
+ if (!hasWeb) {
39
+ result.push(...codingWebTools);
40
+ }
41
+ }
42
+ return result;
35
43
  }
36
44
  // If filter (allowlist) is specified, it takes precedence over flags
37
45
  if (toolConfig.filter) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@compilr-dev/sdk",
3
- "version": "0.7.22",
3
+ "version": "0.7.24",
4
4
  "description": "Universal agent runtime for building AI-powered applications",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",