@compilr-dev/sdk 0.7.23 → 0.7.25

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);
@@ -243,21 +243,31 @@ Project documents (drafts, outlines, analyses, plans) are stored in the **databa
243
243
  export const DELEGATION_MODULE = {
244
244
  id: 'delegation',
245
245
  name: 'Token Efficiency & Delegation',
246
- estimatedTokens: 200,
247
- content: `## Token Efficiency & Delegation
248
-
249
- Use the **task** tool (sub-agent) for:
250
- - **File exploration**: "find files", "what's in this directory", "explore codebase"
251
- - **Code search**: "where is X defined", "find usages of Y"
252
- - **Analysis**: "analyze module", "understand architecture", "review code"
253
- - **Multi-file operations**: Tasks touching 5+ files
254
-
255
- ### Rules
256
- 1. Exploration/search questions → task(explore)
257
- 2. Code review → task(code-review)
258
- 3. Planning → task(plan)
259
- 4. Simple answers (no file access) → respond directly
260
- 5. Code edits → do it yourself (never delegate edits)`,
246
+ estimatedTokens: 350,
247
+ content: `## CRITICAL: Subagent Delegation
248
+
249
+ The **task** tool spawns a subagent that works in isolated context and returns a summary. This is your primary tool for exploration, search, and analysis.
250
+
251
+ **Why this matters:** Reading 5 files directly costs ~25,000 tokens in YOUR context. Delegating to a subagent costs ~200 tokens (tool call + summary). The subagent sees the full content but only the summary enters your history. This 100x reduction keeps your context clean for the actual work.
252
+
253
+ ### ALWAYS delegate these:
254
+ - **File exploration**: "find files", "what's in this directory", "explore the codebase" → \`task(explore)\`
255
+ - **Code search**: "where is X defined", "find usages of Y", "how does Z work" → \`task(explore)\`
256
+ - **Analysis**: "analyze this module", "understand the architecture", "review this code" \`task(code-review)\`
257
+ - **Multi-file reads**: Any task requiring 3+ file reads \`task(explore)\`
258
+ - **Research**: web searches, documentation lookups \`task(explore)\`
259
+
260
+ ### NEVER delegate these:
261
+ - **Code edits**: Writing/editing files — do it yourself
262
+ - **Simple answers**: Questions you can answer from memory
263
+ - **User interaction**: Asking the user questions
264
+ - **Single file reads**: Reading 1-2 specific files the user pointed to
265
+
266
+ ### Examples
267
+ - User: "What files implement the auth system?" → \`task({ type: "explore", message: "Find all files related to authentication..." })\`
268
+ - User: "How does the payment module work?" → \`task({ type: "explore", message: "Analyze the payment module..." })\`
269
+ - User: "Fix the bug in login.tsx" → Read login.tsx yourself, then edit it
270
+ - User: "Add a logout button" → Read the relevant file yourself, then edit it`,
261
271
  };
262
272
  /**
263
273
  * Git safety module - only if hasGit=true
@@ -326,9 +336,9 @@ export const IMPORTANT_RULES_MODULE = {
326
336
  4. Use todo_write for multi-step tasks (3+ steps)
327
337
  5. Don't over-engineer - implement exactly what was asked
328
338
  6. Check for security issues - fix insecure code immediately
329
- 7. **DELEGATE exploration to sub-agents** - use task(explore) for file searches, code navigation, and analysis. NEVER accumulate exploration results in your own context.
330
- 8. Call suggest tool after completing tasks (don't write "suggest:" as text)
331
- 9. When user asks "what files...", "where is...", "find..." - ALWAYS use task(explore), not direct tool calls`,
339
+ 7. **DELEGATE exploration to sub-agents** - use task(explore) for searches, analysis, and multi-file reads. Each file you read directly costs thousands of tokens in YOUR context. A subagent reads files in isolation and returns a compact summary.
340
+ 8. When user asks "what files...", "where is...", "find...", "how does X work..." ALWAYS use task(explore), never read files directly to answer
341
+ 9. Call suggest tool after completing tasks (don't write "suggest:" as text)`,
332
342
  };
333
343
  /**
334
344
  * Environment module - always included
@@ -431,6 +431,7 @@ export const TOOL_PROFILES = {
431
431
  'interaction',
432
432
  'handoff',
433
433
  'guide',
434
+ 'subagents',
434
435
  'meta',
435
436
  'git_read',
436
437
  'project',
@@ -444,6 +445,7 @@ export const TOOL_PROFILES = {
444
445
  'interaction',
445
446
  'handoff',
446
447
  'guide',
448
+ 'subagents',
447
449
  'meta',
448
450
  'project',
449
451
  'search',
@@ -460,6 +462,7 @@ export const TOOL_PROFILES = {
460
462
  'interaction',
461
463
  'handoff',
462
464
  'guide',
465
+ 'subagents',
463
466
  'meta',
464
467
  'git_read',
465
468
  'git_write',
@@ -476,6 +479,7 @@ export const TOOL_PROFILES = {
476
479
  'interaction',
477
480
  'handoff',
478
481
  'guide',
482
+ 'subagents',
479
483
  'meta',
480
484
  'git_read',
481
485
  'project',
@@ -490,6 +494,7 @@ export const TOOL_PROFILES = {
490
494
  'interaction',
491
495
  'handoff',
492
496
  'guide',
497
+ 'subagents',
493
498
  'meta',
494
499
  'git_read',
495
500
  'project',
@@ -511,6 +516,7 @@ export const TOOL_PROFILES = {
511
516
  'interaction',
512
517
  'handoff',
513
518
  'guide',
519
+ 'subagents',
514
520
  'meta',
515
521
  'git_read',
516
522
  'project',
@@ -532,6 +538,7 @@ export const TOOL_PROFILES = {
532
538
  'interaction',
533
539
  'handoff',
534
540
  'guide',
541
+ 'subagents',
535
542
  'meta',
536
543
  'project',
537
544
  'backlog_read',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@compilr-dev/sdk",
3
- "version": "0.7.23",
3
+ "version": "0.7.25",
4
4
  "description": "Universal agent runtime for building AI-powered applications",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",