@probelabs/probe 0.6.0-rc201 → 0.6.0-rc202

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.
Files changed (46) hide show
  1. package/README.md +31 -1
  2. package/bin/binaries/probe-v0.6.0-rc202-aarch64-apple-darwin.tar.gz +0 -0
  3. package/bin/binaries/probe-v0.6.0-rc202-aarch64-unknown-linux-musl.tar.gz +0 -0
  4. package/bin/binaries/probe-v0.6.0-rc202-x86_64-apple-darwin.tar.gz +0 -0
  5. package/bin/binaries/probe-v0.6.0-rc202-x86_64-pc-windows-msvc.zip +0 -0
  6. package/bin/binaries/probe-v0.6.0-rc202-x86_64-unknown-linux-musl.tar.gz +0 -0
  7. package/build/agent/ProbeAgent.d.ts +11 -1
  8. package/build/agent/ProbeAgent.js +310 -14
  9. package/build/agent/index.js +8615 -394
  10. package/build/agent/probeTool.js +2 -2
  11. package/build/agent/schemaUtils.js +37 -18
  12. package/build/agent/shared/prompts.js +17 -0
  13. package/build/agent/skills/formatting.js +23 -0
  14. package/build/agent/skills/parser.js +162 -0
  15. package/build/agent/skills/registry.js +185 -0
  16. package/build/agent/skills/tools.js +65 -0
  17. package/build/agent/tools.js +44 -0
  18. package/build/delegate.js +27 -7
  19. package/build/tools/common.js +17 -4
  20. package/build/tools/system-message.js +4 -4
  21. package/build/tools/vercel.js +243 -36
  22. package/cjs/agent/ProbeAgent.cjs +14990 -7892
  23. package/cjs/index.cjs +15003 -7905
  24. package/index.d.ts +8 -0
  25. package/package.json +2 -1
  26. package/scripts/postinstall.js +10 -4
  27. package/src/agent/ProbeAgent.d.ts +11 -1
  28. package/src/agent/ProbeAgent.js +310 -14
  29. package/src/agent/index.js +21 -1
  30. package/src/agent/probeTool.js +2 -2
  31. package/src/agent/schemaUtils.js +37 -18
  32. package/src/agent/shared/prompts.js +17 -0
  33. package/src/agent/skills/formatting.js +23 -0
  34. package/src/agent/skills/parser.js +162 -0
  35. package/src/agent/skills/registry.js +185 -0
  36. package/src/agent/skills/tools.js +65 -0
  37. package/src/agent/tools.js +44 -0
  38. package/src/delegate.js +27 -7
  39. package/src/tools/common.js +17 -4
  40. package/src/tools/system-message.js +4 -4
  41. package/src/tools/vercel.js +243 -36
  42. package/bin/binaries/probe-v0.6.0-rc201-aarch64-apple-darwin.tar.gz +0 -0
  43. package/bin/binaries/probe-v0.6.0-rc201-aarch64-unknown-linux-musl.tar.gz +0 -0
  44. package/bin/binaries/probe-v0.6.0-rc201-x86_64-apple-darwin.tar.gz +0 -0
  45. package/bin/binaries/probe-v0.6.0-rc201-x86_64-pc-windows-msvc.zip +0 -0
  46. package/bin/binaries/probe-v0.6.0-rc201-x86_64-unknown-linux-musl.tar.gz +0 -0
package/README.md CHANGED
@@ -140,6 +140,36 @@ export MODEL_NAME=claude-3-5-sonnet-20241022
140
140
  - **Token tracking** - Monitor usage and costs
141
141
  - **Configurable personas** - Engineer, architect, code-review, and more
142
142
 
143
+ ### Agent Skills (repo-local)
144
+
145
+ ProbeAgent can discover and activate Agent Skills stored inside your repository. Place skills under:
146
+ - `.claude/skills/<skill-name>/SKILL.md`
147
+ - `.codex/skills/<skill-name>/SKILL.md`
148
+ - `skills/<skill-name>/SKILL.md`
149
+ - `.skills/<skill-name>/SKILL.md`
150
+
151
+ `SKILL.md` should contain YAML frontmatter followed by Markdown instructions. Example:
152
+
153
+ ```markdown
154
+ ---
155
+ name: onboarding
156
+ description: Help new engineers understand the repo structure and conventions.
157
+ ---
158
+
159
+ Use this skill to explain key modules, build steps, and common workflows.
160
+ ```
161
+
162
+ Then in the agent loop you can call:
163
+ ```xml
164
+ <listSkills></listSkills>
165
+ ```
166
+ or:
167
+ ```xml
168
+ <useSkill>
169
+ <name>onboarding</name>
170
+ </useSkill>
171
+ ```
172
+
143
173
  ### Retry and Fallback Support
144
174
 
145
175
  ProbeAgent includes comprehensive retry and fallback capabilities for maximum reliability:
@@ -821,4 +851,4 @@ probe mcp --timeout 60
821
851
 
822
852
  ## Related Projects
823
853
 
824
- - [probe](https://github.com/probelabs/probe) - The core probe code search tool
854
+ - [probe](https://github.com/probelabs/probe) - The core probe code search tool
@@ -14,11 +14,13 @@ export interface ProbeAgentOptions {
14
14
  /** Alias for customPrompt. More intuitive naming for system prompts. */
15
15
  systemPrompt?: string;
16
16
  /** Predefined prompt type (persona) */
17
- promptType?: 'code-explorer' | 'engineer' | 'code-review' | 'support' | 'architect';
17
+ promptType?: 'code-explorer' | 'code-searcher' | 'engineer' | 'code-review' | 'support' | 'architect';
18
18
  /** Allow the use of the 'implement' tool for code editing */
19
19
  allowEdit?: boolean;
20
20
  /** Enable the delegate tool for task distribution to subagents */
21
21
  enableDelegate?: boolean;
22
+ /** Architecture context filename to embed from repo root (defaults to AGENTS.md with CLAUDE.md fallback; ARCHITECTURE.md is always included when present) */
23
+ architectureFileName?: string;
22
24
  /** Enable bash tool for command execution */
23
25
  enableBash?: boolean;
24
26
  /** Bash tool configuration (allow/deny patterns) */
@@ -36,6 +38,8 @@ export interface ProbeAgentOptions {
36
38
  };
37
39
  /** Search directory path */
38
40
  path?: string;
41
+ /** Use a delegated code-search subagent for the search tool (default: true) */
42
+ searchDelegate?: boolean;
39
43
  /** Force specific AI provider */
40
44
  provider?: 'anthropic' | 'openai' | 'google' | 'bedrock';
41
45
  /** Override model name */
@@ -64,6 +68,12 @@ export interface ProbeAgentOptions {
64
68
  disableMermaidValidation?: boolean;
65
69
  /** Disable automatic JSON validation and fixing (prevents infinite recursion in JsonFixingAgent) */
66
70
  disableJsonValidation?: boolean;
71
+ /** Enable agent skills discovery and activation */
72
+ enableSkills?: boolean;
73
+ /** Disable agent skills (overrides enableSkills) */
74
+ disableSkills?: boolean;
75
+ /** Skill directories to scan relative to repo root */
76
+ skillDirs?: string[];
67
77
  /** Custom prompt to run after attempt_completion for validation/review (runs before mermaid/JSON validation) */
68
78
  completionPrompt?: string;
69
79
  }
@@ -12,7 +12,7 @@ import { streamText } from 'ai';
12
12
  import { randomUUID } from 'crypto';
13
13
  import { EventEmitter } from 'events';
14
14
  import { existsSync } from 'fs';
15
- import { readFile, stat } from 'fs/promises';
15
+ import { readFile, stat, readdir } from 'fs/promises';
16
16
  import { resolve, isAbsolute, dirname, basename, normalize, sep } from 'path';
17
17
  import { TokenCounter } from './tokenCounter.js';
18
18
  import { InMemoryStorageAdapter } from './storage/InMemoryStorageAdapter.js';
@@ -27,6 +27,8 @@ import {
27
27
  bashToolDefinition,
28
28
  listFilesToolDefinition,
29
29
  searchFilesToolDefinition,
30
+ listSkillsToolDefinition,
31
+ useSkillToolDefinition,
30
32
  readImageToolDefinition,
31
33
  attemptCompletionToolDefinition,
32
34
  implementToolDefinition,
@@ -61,6 +63,9 @@ import {
61
63
  parseHybridXmlToolCall,
62
64
  loadMCPConfigurationFromPath
63
65
  } from './mcp/index.js';
66
+ import { SkillRegistry } from './skills/registry.js';
67
+ import { formatAvailableSkillsXml } from './skills/formatting.js';
68
+ import { createSkillToolInstances } from './skills/tools.js';
64
69
  import { RetryManager, createRetryManagerFromEnv } from './RetryManager.js';
65
70
  import { FallbackManager, createFallbackManagerFromEnv, buildFallbackProvidersFromEnv } from './FallbackManager.js';
66
71
  import { handleContextLimitError } from './contextCompactor.js';
@@ -91,19 +96,24 @@ export class ProbeAgent {
91
96
  * @param {string} [options.sessionId] - Optional session ID
92
97
  * @param {string} [options.customPrompt] - Custom prompt to replace the default system message
93
98
  * @param {string} [options.systemPrompt] - Alias for customPrompt; takes precedence when both are provided
94
- * @param {string} [options.promptType] - Predefined prompt type (architect, code-review, support)
99
+ * @param {string} [options.promptType] - Predefined prompt type (code-explorer, code-searcher, architect, code-review, support)
95
100
  * @param {boolean} [options.allowEdit=false] - Allow the use of the 'implement' tool
96
101
  * @param {boolean} [options.enableDelegate=false] - Enable the delegate tool for task distribution to subagents
102
+ * @param {string} [options.architectureFileName] - Architecture context filename to embed from repo root (defaults to AGENTS.md with CLAUDE.md fallback; ARCHITECTURE.md is always included when present)
97
103
  * @param {string} [options.path] - Search directory path
98
104
  * @param {string} [options.cwd] - Working directory for resolving relative paths (independent of allowedFolders)
99
105
  * @param {string} [options.provider] - Force specific AI provider
100
106
  * @param {string} [options.model] - Override model name
101
107
  * @param {boolean} [options.debug] - Enable debug mode
102
108
  * @param {boolean} [options.outline] - Enable outline-xml format for search results
109
+ * @param {boolean} [options.searchDelegate=true] - Use a delegated code-search subagent for the search tool
103
110
  * @param {number} [options.maxResponseTokens] - Maximum tokens for AI responses
104
111
  * @param {number} [options.maxIterations] - Maximum tool iterations (overrides MAX_TOOL_ITERATIONS env var)
105
112
  * @param {boolean} [options.disableMermaidValidation=false] - Disable automatic mermaid diagram validation and fixing
106
113
  * @param {boolean} [options.disableJsonValidation=false] - Disable automatic JSON validation and fixing (prevents infinite recursion in JsonFixingAgent)
114
+ * @param {boolean} [options.enableSkills=true] - Enable agent skills discovery and activation
115
+ * @param {boolean} [options.disableSkills=false] - Disable agent skills (overrides enableSkills)
116
+ * @param {Array<string>} [options.skillDirs] - Skill directories to scan relative to repo root
107
117
  * @param {boolean} [options.enableMcp=false] - Enable MCP tool integration
108
118
  * @param {string} [options.mcpConfigPath] - Path to MCP configuration file
109
119
  * @param {Object} [options.mcpConfig] - MCP configuration object (overrides mcpConfigPath)
@@ -138,6 +148,7 @@ export class ProbeAgent {
138
148
  this.cancelled = false;
139
149
  this.tracer = options.tracer || null;
140
150
  this.outline = !!options.outline;
151
+ this.searchDelegate = options.searchDelegate !== undefined ? !!options.searchDelegate : true;
141
152
  this.maxResponseTokens = options.maxResponseTokens || (() => {
142
153
  const val = parseInt(process.env.MAX_RESPONSE_TOKENS || '0', 10);
143
154
  if (isNaN(val) || val < 0 || val > 200000) {
@@ -148,6 +159,16 @@ export class ProbeAgent {
148
159
  this.maxIterations = options.maxIterations || null;
149
160
  this.disableMermaidValidation = !!options.disableMermaidValidation;
150
161
  this.disableJsonValidation = !!options.disableJsonValidation;
162
+ this.enableSkills = options.disableSkills ? false : (options.enableSkills !== undefined ? !!options.enableSkills : true);
163
+ if (Array.isArray(options.skillDirs)) {
164
+ this.skillDirs = options.skillDirs;
165
+ } else if (typeof options.skillDirs === 'string') {
166
+ this.skillDirs = options.skillDirs.split(',').map(dir => dir.trim()).filter(Boolean);
167
+ } else {
168
+ this.skillDirs = null;
169
+ }
170
+ this.skillsRegistry = null;
171
+ this.activeSkills = new Map();
151
172
 
152
173
  // Completion prompt for post-completion validation/review
153
174
  this.completionPrompt = options.completionPrompt || null;
@@ -176,6 +197,15 @@ export class ProbeAgent {
176
197
  this.enableBash = !!options.enableBash;
177
198
  this.bashConfig = options.bashConfig || {};
178
199
 
200
+ // Architecture context configuration
201
+ const configuredArchitectureFileName =
202
+ typeof options.architectureFileName === 'string' && options.architectureFileName.trim()
203
+ ? options.architectureFileName
204
+ : null;
205
+ this.architectureFileName = configuredArchitectureFileName;
206
+ this.architectureContext = null;
207
+ this._architectureContextLoaded = false;
208
+
179
209
  // Search configuration - support both path (single) and allowedFolders (array)
180
210
  if (options.allowedFolders && Array.isArray(options.allowedFolders)) {
181
211
  this.allowedFolders = options.allowedFolders;
@@ -201,6 +231,7 @@ export class ProbeAgent {
201
231
  console.log(`[DEBUG] Generated session ID for agent: ${this.sessionId}`);
202
232
  console.log(`[DEBUG] Maximum tool iterations configured: ${MAX_TOOL_ITERATIONS}`);
203
233
  console.log(`[DEBUG] Allow Edit (implement tool): ${this.allowEdit}`);
234
+ console.log(`[DEBUG] Search delegation enabled: ${this.searchDelegate}`);
204
235
  }
205
236
 
206
237
  // Initialize tools
@@ -437,11 +468,16 @@ export class ProbeAgent {
437
468
  cwd: this.cwd || (this.allowedFolders.length > 0 ? this.allowedFolders[0] : process.cwd()),
438
469
  allowedFolders: this.allowedFolders,
439
470
  outline: this.outline,
471
+ searchDelegate: this.searchDelegate,
440
472
  allowEdit: this.allowEdit,
441
473
  enableDelegate: this.enableDelegate,
442
474
  enableBash: this.enableBash,
443
475
  bashConfig: this.bashConfig,
476
+ tracer: this.tracer,
444
477
  allowedTools: this.allowedTools,
478
+ architectureFileName: this.architectureFileName,
479
+ provider: this.clientApiProvider,
480
+ model: this.clientApiModel,
445
481
  isToolAllowed
446
482
  };
447
483
 
@@ -475,6 +511,21 @@ export class ProbeAgent {
475
511
  this.toolImplementations.searchFiles = searchFilesToolInstance;
476
512
  }
477
513
 
514
+ if (this.enableSkills) {
515
+ const registry = this._getSkillsRegistry();
516
+ const { listSkillsToolInstance, useSkillToolInstance } = createSkillToolInstances({
517
+ registry,
518
+ activeSkills: this.activeSkills
519
+ });
520
+
521
+ if (isToolAllowed('listSkills')) {
522
+ this.toolImplementations.listSkills = listSkillsToolInstance;
523
+ }
524
+ if (isToolAllowed('useSkill')) {
525
+ this.toolImplementations.useSkill = useSkillToolInstance;
526
+ }
527
+ }
528
+
478
529
  // Image loading tool
479
530
  if (isToolAllowed('readImage')) {
480
531
  this.toolImplementations.readImage = {
@@ -1043,7 +1094,7 @@ export class ProbeAgent {
1043
1094
 
1044
1095
  // For Claude Code, use a cleaner system prompt without XML formatting
1045
1096
  // since it has native MCP support for tools
1046
- const systemPrompt = this.customPrompt || this.getClaudeNativeSystemPrompt();
1097
+ const systemPrompt = await this.getClaudeNativeSystemPrompt();
1047
1098
 
1048
1099
  this.engine = await createEnhancedClaudeCLIEngine({
1049
1100
  agent: this, // Pass reference to ProbeAgent for tool access
@@ -1074,7 +1125,7 @@ export class ProbeAgent {
1074
1125
 
1075
1126
  // For Codex CLI, use a cleaner system prompt without XML formatting
1076
1127
  // since it has native MCP support for tools
1077
- const systemPrompt = this.customPrompt || this.getCodexNativeSystemPrompt();
1128
+ const systemPrompt = await this.getCodexNativeSystemPrompt();
1078
1129
 
1079
1130
  this.engine = await createCodexEngine({
1080
1131
  agent: this, // Pass reference to ProbeAgent for tool access
@@ -1535,11 +1586,207 @@ export class ProbeAgent {
1535
1586
  }
1536
1587
  }
1537
1588
 
1589
+ /**
1590
+ * Load architecture context from repository root (case-insensitive filename match)
1591
+ * @returns {Promise<Object|null>} Architecture context with { name, path, content, sources, primarySource, guidanceSource, architectureSource } or null
1592
+ */
1593
+ async loadArchitectureContext() {
1594
+ if (this._architectureContextLoaded) {
1595
+ return this.architectureContext;
1596
+ }
1597
+
1598
+ const rootDirectory = this.allowedFolders.length > 0 ? this.allowedFolders[0] : process.cwd();
1599
+ const configuredName =
1600
+ typeof this.architectureFileName === 'string' ? this.architectureFileName.trim() : '';
1601
+ const hasConfiguredName = !!configuredName;
1602
+ let guidanceCandidates = [];
1603
+
1604
+ if (hasConfiguredName) {
1605
+ const targetName = basename(configuredName);
1606
+
1607
+ // Only allow simple filenames (no path separators or traversal)
1608
+ if (
1609
+ configuredName !== targetName ||
1610
+ configuredName.includes('/') ||
1611
+ configuredName.includes('\\') ||
1612
+ configuredName.includes('..') ||
1613
+ isAbsolute(configuredName)
1614
+ ) {
1615
+ console.warn(`[WARN] Invalid architectureFileName (must be a simple filename): ${configuredName}`);
1616
+ } else if (targetName) {
1617
+ const targetLower = targetName.toLowerCase();
1618
+ if (targetLower === 'agents.md') {
1619
+ guidanceCandidates = ['agents.md', 'claude.md'];
1620
+ } else {
1621
+ guidanceCandidates = [targetName];
1622
+ }
1623
+ }
1624
+ } else {
1625
+ guidanceCandidates = ['agents.md', 'claude.md'];
1626
+ }
1627
+
1628
+ if (!existsSync(rootDirectory)) {
1629
+ this._architectureContextLoaded = true;
1630
+ return null;
1631
+ }
1632
+
1633
+ let entries;
1634
+ try {
1635
+ entries = await readdir(rootDirectory, { withFileTypes: true });
1636
+ } catch (error) {
1637
+ this.architectureContext = null;
1638
+ if (error && (error.code === 'EACCES' || error.code === 'EPERM')) {
1639
+ console.warn(`[WARN] Cannot read architecture context directory: ${rootDirectory} (${error.code})`);
1640
+ } else if (this.debug) {
1641
+ console.log(`[DEBUG] Could not list architecture context directory: ${error.message}`);
1642
+ }
1643
+ return null;
1644
+ }
1645
+
1646
+ const entryByLower = new Map();
1647
+ for (const entry of entries) {
1648
+ if (entry.isSymbolicLink()) {
1649
+ continue;
1650
+ }
1651
+ if (!entry.isFile()) {
1652
+ continue;
1653
+ }
1654
+ entryByLower.set(entry.name.toLowerCase(), entry);
1655
+ }
1656
+
1657
+ let guidanceMatch = null;
1658
+ for (const candidateName of guidanceCandidates) {
1659
+ const entry = entryByLower.get(candidateName.toLowerCase());
1660
+ if (entry) {
1661
+ guidanceMatch = entry;
1662
+ break;
1663
+ }
1664
+ }
1665
+
1666
+ const architectureMatch = entryByLower.get('architecture.md');
1667
+ const guidanceKey = guidanceMatch ? guidanceMatch.name.toLowerCase() : null;
1668
+ const architectureKey = architectureMatch ? architectureMatch.name.toLowerCase() : null;
1669
+
1670
+ if (!guidanceMatch && !architectureMatch) {
1671
+ this._architectureContextLoaded = true;
1672
+ return null;
1673
+ }
1674
+
1675
+ const uniqueEntries = [];
1676
+ const seen = new Set();
1677
+ const pushEntry = (entry) => {
1678
+ if (!entry) return;
1679
+ const key = entry.name.toLowerCase();
1680
+ if (seen.has(key)) return;
1681
+ seen.add(key);
1682
+ uniqueEntries.push(entry);
1683
+ };
1684
+
1685
+ pushEntry(guidanceMatch);
1686
+ pushEntry(architectureMatch);
1687
+
1688
+ const contexts = [];
1689
+ for (const entry of uniqueEntries) {
1690
+ const filePath = resolve(rootDirectory, entry.name);
1691
+ try {
1692
+ const content = await readFile(filePath, 'utf8');
1693
+ let kind = 'other';
1694
+ const entryKey = entry.name.toLowerCase();
1695
+ if (guidanceKey && entryKey === guidanceKey) {
1696
+ kind = 'guidance';
1697
+ } else if (architectureKey && entryKey === architectureKey) {
1698
+ kind = 'architecture';
1699
+ }
1700
+ contexts.push({
1701
+ name: entry.name,
1702
+ path: filePath,
1703
+ content,
1704
+ kind
1705
+ });
1706
+ } catch (error) {
1707
+ if (error && (error.code === 'EACCES' || error.code === 'EPERM')) {
1708
+ console.warn(`[WARN] Cannot read architecture context file: ${filePath} (${error.code})`);
1709
+ } else if (error && error.code === 'ENOENT') {
1710
+ if (this.debug) {
1711
+ console.log(`[DEBUG] Architecture context file disappeared: ${filePath}`);
1712
+ }
1713
+ } else {
1714
+ console.warn(`[WARN] Failed to read architecture context file: ${filePath} (${error.message})`);
1715
+ }
1716
+ }
1717
+ }
1718
+
1719
+ if (!contexts.length) {
1720
+ this.architectureContext = null;
1721
+ this._architectureContextLoaded = true;
1722
+ return null;
1723
+ }
1724
+
1725
+ const guidanceSource = contexts.find((context) => context.kind === 'guidance') || null;
1726
+ const architectureSource = contexts.find((context) => context.kind === 'architecture') || null;
1727
+ const primarySource = guidanceSource || architectureSource || contexts[0];
1728
+
1729
+ this.architectureContext = {
1730
+ name: primarySource?.name || null,
1731
+ path: primarySource?.path || null,
1732
+ content: contexts.map((context) => context.content).join('\n\n'),
1733
+ sources: contexts,
1734
+ primarySource,
1735
+ guidanceSource,
1736
+ architectureSource
1737
+ };
1738
+ this._architectureContextLoaded = true;
1739
+
1740
+ return this.architectureContext;
1741
+ }
1742
+
1743
+ /**
1744
+ * Format architecture context for prompt inclusion
1745
+ * @returns {string} Architecture section or empty string
1746
+ */
1747
+ getArchitectureSection() {
1748
+ if (!this.architectureContext?.content) {
1749
+ return '';
1750
+ }
1751
+
1752
+ return `\n\n# Architecture\n\n${this.architectureContext.content}\n`;
1753
+ }
1754
+
1755
+ _getSkillsRepoRoot() {
1756
+ if (this.allowedFolders && this.allowedFolders.length > 0) {
1757
+ return resolve(this.allowedFolders[0]);
1758
+ }
1759
+ return process.cwd();
1760
+ }
1761
+
1762
+ _getSkillsRegistry() {
1763
+ if (!this.skillsRegistry) {
1764
+ this.skillsRegistry = new SkillRegistry({
1765
+ repoRoot: this._getSkillsRepoRoot(),
1766
+ skillDirs: this.skillDirs || undefined,
1767
+ debug: this.debug
1768
+ });
1769
+ }
1770
+ return this.skillsRegistry;
1771
+ }
1772
+
1773
+ async _loadSkillsMetadata() {
1774
+ if (!this.enableSkills) return [];
1775
+ return await this._getSkillsRegistry().loadSkills();
1776
+ }
1777
+
1778
+ async _getAvailableSkillsXml() {
1779
+ const skills = await this._loadSkillsMetadata();
1780
+ if (!skills.length) return '';
1781
+ return formatAvailableSkillsXml(skills);
1782
+ }
1783
+
1538
1784
  /**
1539
1785
  * Get system prompt for Claude native engines (CLI/SDK) without XML formatting
1540
1786
  * These engines have native MCP support and don't need XML instructions
1541
1787
  */
1542
- getClaudeNativeSystemPrompt() {
1788
+ async getClaudeNativeSystemPrompt() {
1789
+ await this.loadArchitectureContext();
1543
1790
  let systemPrompt = '';
1544
1791
 
1545
1792
  // Add persona/role if configured
@@ -1564,10 +1811,17 @@ export class ProbeAgent {
1564
1811
  systemPrompt += `\n- bash: Execute bash commands for system operations`;
1565
1812
  }
1566
1813
 
1814
+ const searchGuidance = this.searchDelegate
1815
+ ? '1. Start with search to retrieve extracted code blocks'
1816
+ : '1. Start with search to find relevant code patterns';
1817
+ const extractGuidance = this.searchDelegate
1818
+ ? '2. Use extract only if you need more context or a full file'
1819
+ : '2. Use extract to get detailed context when needed';
1820
+
1567
1821
  systemPrompt += `\n
1568
1822
  When exploring code:
1569
- 1. Start with search to find relevant code patterns
1570
- 2. Use extract to get detailed context when needed
1823
+ ${searchGuidance}
1824
+ ${extractGuidance}
1571
1825
  3. Prefer focused, specific searches over broad queries
1572
1826
  4. Combine multiple tools to build complete understanding`;
1573
1827
 
@@ -1584,13 +1838,17 @@ When exploring code:
1584
1838
  systemPrompt += '```\n' + this.fileList + '\n```\n';
1585
1839
  }
1586
1840
 
1841
+ // Add architecture context if available
1842
+ systemPrompt += this.getArchitectureSection();
1843
+
1587
1844
  return systemPrompt;
1588
1845
  }
1589
1846
 
1590
1847
  /**
1591
1848
  * Get system prompt for Codex CLI (similar to Claude but optimized for Codex)
1592
1849
  */
1593
- getCodexNativeSystemPrompt() {
1850
+ async getCodexNativeSystemPrompt() {
1851
+ await this.loadArchitectureContext();
1594
1852
  let systemPrompt = '';
1595
1853
 
1596
1854
  // Add persona/role if configured
@@ -1615,10 +1873,17 @@ When exploring code:
1615
1873
  systemPrompt += `\n- bash: Execute bash commands for system operations`;
1616
1874
  }
1617
1875
 
1876
+ const searchGuidance = this.searchDelegate
1877
+ ? '1. Start with search to retrieve extracted code blocks'
1878
+ : '1. Start with search to find relevant code patterns';
1879
+ const extractGuidance = this.searchDelegate
1880
+ ? '2. Use extract only if you need more context or a full file'
1881
+ : '2. Use extract to get detailed context when needed';
1882
+
1618
1883
  systemPrompt += `\n
1619
1884
  When exploring code:
1620
- 1. Start with search to find relevant code patterns
1621
- 2. Use extract to get detailed context when needed
1885
+ ${searchGuidance}
1886
+ ${extractGuidance}
1622
1887
  3. Prefer focused, specific searches over broad queries
1623
1888
  4. Combine multiple tools to build complete understanding`;
1624
1889
 
@@ -1635,6 +1900,9 @@ When exploring code:
1635
1900
  systemPrompt += '```\n' + this.fileList + '\n```\n';
1636
1901
  }
1637
1902
 
1903
+ // Add architecture context if available
1904
+ systemPrompt += this.getArchitectureSection();
1905
+
1638
1906
  return systemPrompt;
1639
1907
  }
1640
1908
 
@@ -1676,7 +1944,10 @@ When exploring code:
1676
1944
 
1677
1945
  // Core tools (filtered by allowedTools)
1678
1946
  if (isToolAllowed('search')) {
1679
- toolDefinitions += `${searchToolDefinition}\n`;
1947
+ const searchDefinition = this.searchDelegate
1948
+ ? `${searchToolDefinition}\n**Note:** This search tool delegates code searching to a dedicated subagent and returns extracted code blocks. Use extract only to expand context or if search returns no code.`
1949
+ : searchToolDefinition;
1950
+ toolDefinitions += `${searchDefinition}\n`;
1680
1951
  }
1681
1952
  if (isToolAllowed('query')) {
1682
1953
  toolDefinitions += `${queryToolDefinition}\n`;
@@ -1690,6 +1961,12 @@ When exploring code:
1690
1961
  if (isToolAllowed('searchFiles')) {
1691
1962
  toolDefinitions += `${searchFilesToolDefinition}\n`;
1692
1963
  }
1964
+ if (this.enableSkills && isToolAllowed('listSkills')) {
1965
+ toolDefinitions += `${listSkillsToolDefinition}\n`;
1966
+ }
1967
+ if (this.enableSkills && isToolAllowed('useSkill')) {
1968
+ toolDefinitions += `${useSkillToolDefinition}\n`;
1969
+ }
1693
1970
  if (isToolAllowed('readImage')) {
1694
1971
  toolDefinitions += `${readImageToolDefinition}\n`;
1695
1972
  }
@@ -1778,12 +2055,12 @@ I need to find code related to error handling in the search module. The most app
1778
2055
  10. If your previous response was already correct and complete, you may use \`<attempt_complete>\` as a shorthand.
1779
2056
 
1780
2057
  Available Tools:
1781
- - search: Search code using keyword queries.
2058
+ - search: Search code using keyword queries${this.searchDelegate ? ' (returns extracted code blocks via a dedicated subagent)' : ''}.
1782
2059
  - query: Search code using structural AST patterns.
1783
2060
  - extract: Extract specific code blocks or lines from files.
1784
2061
  - listFiles: List files and directories in a specified location.
1785
2062
  - searchFiles: Find files matching a glob pattern with recursive search capability.
1786
- - readImage: Read and load an image file for AI analysis.
2063
+ ${this.enableSkills ? '- listSkills: List available agent skills discovered in the repository.\n- useSkill: Load and activate a specific skill\'s instructions.\n' : ''}- readImage: Read and load an image file for AI analysis.
1787
2064
  ${this.allowEdit ? '- implement: Implement a feature or fix a bug using aider.\n- edit: Edit files using exact string replacement.\n- create: Create new files with specified content.\n' : ''}${this.enableDelegate ? '- delegate: Delegate big distinct tasks to specialized probe subagents.\n' : ''}${this.enableBash ? '- bash: Execute bash commands for system operations.\n' : ''}
1788
2065
  - attempt_completion: Finalize the task and provide the result to the user.
1789
2066
  - attempt_complete: Quick completion using previous response (shorthand).
@@ -1795,7 +2072,7 @@ Follow these instructions carefully:
1795
2072
  1. Analyze the user's request.
1796
2073
  2. Use <thinking></thinking> tags to analyze the situation and determine the appropriate tool for each step.
1797
2074
  3. Use the available tools step-by-step to fulfill the request.
1798
- 4. You should always prefer the \`search\` tool for code-related questions. Read full files only if really necessary.
2075
+ 4. You should always prefer the \`search\` tool for code-related questions.${this.searchDelegate ? ' It already returns extracted code blocks; use \`extract\` only to expand context or read full files.' : ' Read full files only if really necessary.'}
1799
2076
  5. Ensure to get really deep and understand the full picture before answering.
1800
2077
  6. You MUST respond with exactly ONE tool call per message, using the specified XML format, until the task is complete.
1801
2078
  7. Wait for the tool execution result (provided in the next user message in a <tool_result> block) before proceeding to the next step.
@@ -1841,6 +2118,14 @@ Follow these instructions carefully:
1841
2118
  // Add Tool Definitions
1842
2119
  systemMessage += `\n# Tools Available\n${toolDefinitions}\n`;
1843
2120
 
2121
+ // Add available skills (metadata only)
2122
+ if (this.enableSkills) {
2123
+ const skillsXml = await this._getAvailableSkillsXml();
2124
+ if (skillsXml) {
2125
+ systemMessage += `\n# Available Skills\n${skillsXml}\n\nTo use a skill, call the useSkill tool with its name.\n`;
2126
+ }
2127
+ }
2128
+
1844
2129
  // Add MCP tools if available (filtered by allowedTools)
1845
2130
  if (this.mcpBridge && this.mcpBridge.getToolNames().length > 0) {
1846
2131
  const allMcpTools = this.mcpBridge.getToolNames();
@@ -1876,6 +2161,10 @@ Follow these instructions carefully:
1876
2161
  systemMessage += `\n# Repository Structure\n\nYou are working with a repository located at: ${searchDirectory}\n\n`;
1877
2162
  }
1878
2163
 
2164
+ // Add architecture context if available
2165
+ await this.loadArchitectureContext();
2166
+ systemMessage += this.getArchitectureSection();
2167
+
1879
2168
  if (this.allowedFolders.length > 0) {
1880
2169
  systemMessage += `\n**Important**: For security reasons, you can only search within these allowed folders: ${this.allowedFolders.join(', ')}\n\n`;
1881
2170
  }
@@ -2330,6 +2619,8 @@ Follow these instructions carefully:
2330
2619
  if (this.allowedTools.isEnabled('extract')) validTools.push('extract');
2331
2620
  if (this.allowedTools.isEnabled('listFiles')) validTools.push('listFiles');
2332
2621
  if (this.allowedTools.isEnabled('searchFiles')) validTools.push('searchFiles');
2622
+ if (this.enableSkills && this.allowedTools.isEnabled('listSkills')) validTools.push('listSkills');
2623
+ if (this.enableSkills && this.allowedTools.isEnabled('useSkill')) validTools.push('useSkill');
2333
2624
  if (this.allowedTools.isEnabled('readImage')) validTools.push('readImage');
2334
2625
  // Always allow attempt_completion - it's a completion signal, not a tool
2335
2626
  // This ensures agents can complete even when disableTools: true is set (fixes #333)
@@ -2518,6 +2809,7 @@ Follow these instructions carefully:
2518
2809
  path: this.searchPath, // Inherit search path
2519
2810
  provider: this.apiType, // Inherit AI provider (string identifier)
2520
2811
  model: this.model, // Inherit model
2812
+ searchDelegate: this.searchDelegate,
2521
2813
  debug: this.debug,
2522
2814
  tracer: this.tracer
2523
2815
  };
@@ -3371,6 +3663,7 @@ Convert your previous response content into actual JSON data that follows this s
3371
3663
  promptType: this.promptType,
3372
3664
  allowEdit: this.allowEdit,
3373
3665
  enableDelegate: this.enableDelegate,
3666
+ architectureFileName: this.architectureFileName,
3374
3667
  path: this.allowedFolders[0], // Use first allowed folder as primary path
3375
3668
  allowedFolders: [...this.allowedFolders],
3376
3669
  cwd: this.cwd, // Preserve explicit working directory
@@ -3378,11 +3671,14 @@ Convert your previous response content into actual JSON data that follows this s
3378
3671
  model: this.clientApiModel,
3379
3672
  debug: this.debug,
3380
3673
  outline: this.outline,
3674
+ searchDelegate: this.searchDelegate,
3381
3675
  maxResponseTokens: this.maxResponseTokens,
3382
3676
  maxIterations: this.maxIterations,
3383
3677
  disableMermaidValidation: this.disableMermaidValidation,
3384
3678
  disableJsonValidation: this.disableJsonValidation,
3385
3679
  completionPrompt: this.completionPrompt,
3680
+ enableSkills: this.enableSkills,
3681
+ skillDirs: this.skillDirs ? [...this.skillDirs] : null,
3386
3682
  allowedTools: allowedToolsArray,
3387
3683
  enableMcp: !!this.mcpBridge,
3388
3684
  mcpConfig: this.mcpConfig,