tachibot-mcp 2.0.2
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/.env.example +260 -0
- package/CHANGELOG.md +54 -0
- package/CODE_OF_CONDUCT.md +56 -0
- package/CONTRIBUTING.md +54 -0
- package/Dockerfile +36 -0
- package/LICENSE +644 -0
- package/README.md +201 -0
- package/SECURITY.md +95 -0
- package/dist/personality/komaai-expressions.js +12 -0
- package/dist/profiles/balanced.json +33 -0
- package/dist/profiles/code_focus.json +33 -0
- package/dist/profiles/full.json +33 -0
- package/dist/profiles/minimal.json +33 -0
- package/dist/profiles/research_power.json +33 -0
- package/dist/scripts/build-profiles.js +46 -0
- package/dist/src/application/services/focus/FocusModeRegistry.js +46 -0
- package/dist/src/application/services/focus/FocusTool.service.js +109 -0
- package/dist/src/application/services/focus/ModeRegistry.js +46 -0
- package/dist/src/application/services/focus/modes/focus-deep.mode.js +27 -0
- package/dist/src/application/services/focus/modes/status.mode.js +50 -0
- package/dist/src/application/services/focus/modes/tachibot-status.mode.js +50 -0
- package/dist/src/collaborative-orchestrator.js +391 -0
- package/dist/src/config/model-constants.js +188 -0
- package/dist/src/config/model-defaults.js +57 -0
- package/dist/src/config/model-preferences.js +382 -0
- package/dist/src/config/timeout-config.js +130 -0
- package/dist/src/config.js +173 -0
- package/dist/src/domain/interfaces/IFocusMode.js +5 -0
- package/dist/src/domain/interfaces/IProvider.js +6 -0
- package/dist/src/domain/interfaces/ITool.js +5 -0
- package/dist/src/focus-deep.js +245 -0
- package/dist/src/infrastructure/ascii/art/robots.ascii.js +16 -0
- package/dist/src/mcp-client.js +90 -0
- package/dist/src/memory/index.js +17 -0
- package/dist/src/memory/memory-config.js +135 -0
- package/dist/src/memory/memory-interface.js +174 -0
- package/dist/src/memory/memory-manager.js +383 -0
- package/dist/src/memory/providers/devlog-provider.js +385 -0
- package/dist/src/memory/providers/hybrid-provider.js +399 -0
- package/dist/src/memory/providers/local-provider.js +388 -0
- package/dist/src/memory/providers/mem0-provider.js +337 -0
- package/dist/src/modes/architect.js +477 -0
- package/dist/src/modes/auditor.js +362 -0
- package/dist/src/modes/challenger.js +841 -0
- package/dist/src/modes/code-reviewer.js +382 -0
- package/dist/src/modes/commit-guardian.js +424 -0
- package/dist/src/modes/documentation-writer.js +572 -0
- package/dist/src/modes/scout.js +587 -0
- package/dist/src/modes/shared/helpers/challenger-helpers.js +454 -0
- package/dist/src/modes/shared/helpers/index.js +17 -0
- package/dist/src/modes/shared/helpers/scout-helpers.js +270 -0
- package/dist/src/modes/shared/helpers/verifier-helpers.js +332 -0
- package/dist/src/modes/test-architect.js +767 -0
- package/dist/src/modes/verifier.js +378 -0
- package/dist/src/monitoring/performance-monitor.js +435 -0
- package/dist/src/optimization/batch-executor.js +121 -0
- package/dist/src/optimization/context-pruner.js +196 -0
- package/dist/src/optimization/cost-monitor.js +338 -0
- package/dist/src/optimization/index.js +65 -0
- package/dist/src/optimization/model-router.js +264 -0
- package/dist/src/optimization/result-cache.js +114 -0
- package/dist/src/optimization/token-optimizer.js +257 -0
- package/dist/src/optimization/token-tracker.js +118 -0
- package/dist/src/orchestrator-instructions.js +128 -0
- package/dist/src/orchestrator-lite.js +139 -0
- package/dist/src/orchestrator.js +191 -0
- package/dist/src/orchestrators/collaborative/interfaces/IToolExecutionEngine.js +1 -0
- package/dist/src/orchestrators/collaborative/interfaces/IToolExecutionStrategy.js +5 -0
- package/dist/src/orchestrators/collaborative/interfaces/IVisualizationRenderer.js +1 -0
- package/dist/src/orchestrators/collaborative/registries/ModelProviderRegistry.js +95 -0
- package/dist/src/orchestrators/collaborative/registries/ToolAdapterRegistry.js +64 -0
- package/dist/src/orchestrators/collaborative/services/tool-execution/ToolExecutionService.js +502 -0
- package/dist/src/orchestrators/collaborative/services/visualization/VisualizationService.js +206 -0
- package/dist/src/orchestrators/collaborative/types/session-types.js +5 -0
- package/dist/src/profiles/balanced.js +37 -0
- package/dist/src/profiles/code_focus.js +37 -0
- package/dist/src/profiles/debug_intensive.js +59 -0
- package/dist/src/profiles/full.js +37 -0
- package/dist/src/profiles/minimal.js +37 -0
- package/dist/src/profiles/research_code.js +59 -0
- package/dist/src/profiles/research_power.js +37 -0
- package/dist/src/profiles/types.js +5 -0
- package/dist/src/profiles/workflow_builder.js +53 -0
- package/dist/src/prompt-engineer-lite.js +78 -0
- package/dist/src/prompt-engineer.js +399 -0
- package/dist/src/reasoning-chain.js +508 -0
- package/dist/src/sequential-thinking.js +291 -0
- package/dist/src/server-diagnostic.js +74 -0
- package/dist/src/server-raw.js +158 -0
- package/dist/src/server-simple.js +58 -0
- package/dist/src/server.js +514 -0
- package/dist/src/session/session-logger.js +617 -0
- package/dist/src/session/session-manager.js +571 -0
- package/dist/src/session/session-tools.js +400 -0
- package/dist/src/tools/advanced-modes.js +200 -0
- package/dist/src/tools/claude-integration.js +356 -0
- package/dist/src/tools/consolidated/ai-router.js +174 -0
- package/dist/src/tools/consolidated/ai-tool.js +48 -0
- package/dist/src/tools/consolidated/brainstorm-tool.js +87 -0
- package/dist/src/tools/consolidated/environment-detector.js +80 -0
- package/dist/src/tools/consolidated/index.js +50 -0
- package/dist/src/tools/consolidated/search-tool.js +110 -0
- package/dist/src/tools/consolidated/workflow-tool.js +238 -0
- package/dist/src/tools/gemini-tools.js +329 -0
- package/dist/src/tools/grok-enhanced.js +376 -0
- package/dist/src/tools/grok-tools.js +299 -0
- package/dist/src/tools/lmstudio-tools.js +223 -0
- package/dist/src/tools/openai-tools.js +498 -0
- package/dist/src/tools/openrouter-tools.js +317 -0
- package/dist/src/tools/optimized-wrapper.js +204 -0
- package/dist/src/tools/perplexity-tools.js +294 -0
- package/dist/src/tools/pingpong-tool.js +343 -0
- package/dist/src/tools/qwen-wrapper.js +74 -0
- package/dist/src/tools/tool-router.js +444 -0
- package/dist/src/tools/unified-ai-provider.js +260 -0
- package/dist/src/tools/workflow-runner.js +425 -0
- package/dist/src/tools/workflow-validator-tool.js +107 -0
- package/dist/src/types.js +23 -0
- package/dist/src/utils/input-validator.js +130 -0
- package/dist/src/utils/model-router.js +91 -0
- package/dist/src/utils/progress-stream.js +255 -0
- package/dist/src/utils/provider-router.js +88 -0
- package/dist/src/utils/smart-api-client.js +146 -0
- package/dist/src/utils/table-builder.js +218 -0
- package/dist/src/utils/timestamp-formatter.js +134 -0
- package/dist/src/utils/tool-compressor.js +257 -0
- package/dist/src/utils/tool-config.js +201 -0
- package/dist/src/validators/dependency-graph-validator.js +147 -0
- package/dist/src/validators/interpolation-validator.js +222 -0
- package/dist/src/validators/output-usage-validator.js +151 -0
- package/dist/src/validators/syntax-validator.js +102 -0
- package/dist/src/validators/tool-registry-validator.js +123 -0
- package/dist/src/validators/tool-types.js +97 -0
- package/dist/src/validators/types.js +8 -0
- package/dist/src/validators/workflow-validator.js +134 -0
- package/dist/src/visualizer-lite.js +42 -0
- package/dist/src/visualizer.js +179 -0
- package/dist/src/workflows/circuit-breaker.js +199 -0
- package/dist/src/workflows/custom-workflows.js +451 -0
- package/dist/src/workflows/engine/AutoSynthesizer.js +97 -0
- package/dist/src/workflows/engine/StepParameterResolver.js +74 -0
- package/dist/src/workflows/engine/VariableInterpolator.js +123 -0
- package/dist/src/workflows/engine/WorkflowDiscovery.js +125 -0
- package/dist/src/workflows/engine/WorkflowExecutionEngine.js +485 -0
- package/dist/src/workflows/engine/WorkflowExecutor.js +113 -0
- package/dist/src/workflows/engine/WorkflowFileManager.js +244 -0
- package/dist/src/workflows/engine/WorkflowHelpers.js +114 -0
- package/dist/src/workflows/engine/WorkflowOutputFormatter.js +83 -0
- package/dist/src/workflows/engine/events/WorkflowEventBus.js +132 -0
- package/dist/src/workflows/engine/events/interfaces/IEventBus.js +5 -0
- package/dist/src/workflows/engine/handlers/ErrorRecoveryHandler.js +162 -0
- package/dist/src/workflows/engine/handlers/PromptEnhancementHandler.js +115 -0
- package/dist/src/workflows/engine/handlers/SessionPersistenceHandler.js +167 -0
- package/dist/src/workflows/engine/handlers/StepExecutionHandler.js +231 -0
- package/dist/src/workflows/engine/handlers/ToolInvocationHandler.js +46 -0
- package/dist/src/workflows/engine/interfaces/IAutoSynthesizer.js +5 -0
- package/dist/src/workflows/engine/interfaces/IStepParameterResolver.js +5 -0
- package/dist/src/workflows/engine/interfaces/IVariableInterpolator.js +5 -0
- package/dist/src/workflows/engine/interfaces/IWorkflowDiscovery.js +4 -0
- package/dist/src/workflows/engine/interfaces/IWorkflowFileManager.js +5 -0
- package/dist/src/workflows/engine/interfaces/IWorkflowOutputFormatter.js +5 -0
- package/dist/src/workflows/engine/state/WorkflowStateMachine.js +194 -0
- package/dist/src/workflows/engine/state/interfaces/IStateMachine.js +17 -0
- package/dist/src/workflows/fallback-strategies.js +373 -0
- package/dist/src/workflows/message-queue.js +455 -0
- package/dist/src/workflows/model-router.js +189 -0
- package/dist/src/workflows/orchestrator-examples.js +174 -0
- package/dist/src/workflows/orchestrator-integration.js +200 -0
- package/dist/src/workflows/self-healing.js +524 -0
- package/dist/src/workflows/tool-mapper.js +407 -0
- package/dist/src/workflows/tool-orchestrator.js +796 -0
- package/dist/src/workflows/workflow-engine.js +573 -0
- package/dist/src/workflows/workflow-parser.js +283 -0
- package/dist/src/workflows/workflow-types.js +95 -0
- package/dist/src/workflows.js +568 -0
- package/dist/test-workflow-file-output.js +93 -0
- package/docs/API_KEYS.md +570 -0
- package/docs/CLAUDE_CODE_SETUP.md +181 -0
- package/docs/CLAUDE_DESKTOP_MANUAL.md +127 -0
- package/docs/CONFIGURATION.md +745 -0
- package/docs/FOCUS_MODES.md +240 -0
- package/docs/INSTALLATION_BOTH.md +145 -0
- package/docs/TERMS.md +352 -0
- package/docs/TOOLS_REFERENCE.md +1622 -0
- package/docs/TOOL_PARAMETERS.md +496 -0
- package/docs/TOOL_PROFILES.md +236 -0
- package/docs/WORKFLOWS.md +987 -0
- package/docs/WORKFLOW_OUTPUT.md +198 -0
- package/docs/WORKFLOW_PROGRESS_TRACKING.md +305 -0
- package/docs/workflows/design-brainstorm.md +335 -0
- package/package.json +97 -0
- package/profiles/balanced.json +37 -0
- package/profiles/code_focus.json +37 -0
- package/profiles/debug_intensive.json +34 -0
- package/profiles/full.json +37 -0
- package/profiles/minimal.json +37 -0
- package/profiles/research_power.json +37 -0
- package/profiles/workflow_builder.json +37 -0
- package/smithery.yaml +66 -0
- package/start.sh +8 -0
- package/tools.config.json +81 -0
- package/tsconfig.json +18 -0
- package/workflows/accessibility-code-audit.yaml +92 -0
- package/workflows/code-architecture-review.yaml +202 -0
- package/workflows/code-review.yaml +142 -0
- package/workflows/core/iterative-problem-solver.yaml +283 -0
- package/workflows/creative-brainstorm-yaml.yaml +215 -0
- package/workflows/pingpong.yaml +141 -0
- package/workflows/system/README.md +412 -0
- package/workflows/system/challenger.yaml +175 -0
- package/workflows/system/scout.yaml +164 -0
- package/workflows/system/verifier.yaml +133 -0
- package/workflows/ultra-creative-brainstorm.yaml +318 -0
- package/workflows/ux-research-flow.yaml +92 -0
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool Compressor - Aggressive token reduction for MCP tools
|
|
3
|
+
* Target: Reduce from ~400 tokens per tool to ~40 tokens
|
|
4
|
+
*/
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
/**
|
|
7
|
+
* Compress a tool description to minimal tokens
|
|
8
|
+
*/
|
|
9
|
+
export function compressToolDescription(tool) {
|
|
10
|
+
return {
|
|
11
|
+
n: compressName(tool.name),
|
|
12
|
+
d: compressDescription(tool.description),
|
|
13
|
+
p: compressParameters(tool.parameters)
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Compress tool name
|
|
18
|
+
*/
|
|
19
|
+
function compressName(name) {
|
|
20
|
+
// Remove underscores, use abbreviations
|
|
21
|
+
const abbreviations = {
|
|
22
|
+
'perplexity': 'pplx',
|
|
23
|
+
'gemini': 'gem',
|
|
24
|
+
'openai': 'oai',
|
|
25
|
+
'research': 'rsch',
|
|
26
|
+
'analyze': 'anlz',
|
|
27
|
+
'brainstorm': 'bstm',
|
|
28
|
+
'workflow': 'wf',
|
|
29
|
+
'visualize': 'viz',
|
|
30
|
+
'create': 'mk',
|
|
31
|
+
'list': 'ls'
|
|
32
|
+
};
|
|
33
|
+
let compressed = name.toLowerCase();
|
|
34
|
+
Object.entries(abbreviations).forEach(([full, abbr]) => {
|
|
35
|
+
compressed = compressed.replace(full, abbr);
|
|
36
|
+
});
|
|
37
|
+
return compressed.replace(/_/g, '');
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Compress description to max 10 words
|
|
41
|
+
*/
|
|
42
|
+
function compressDescription(description) {
|
|
43
|
+
// Remove common filler words
|
|
44
|
+
const fillers = ['the', 'a', 'an', 'to', 'for', 'with', 'and', 'or', 'but', 'in', 'on', 'at', 'from', 'by'];
|
|
45
|
+
const words = description
|
|
46
|
+
.toLowerCase()
|
|
47
|
+
.split(/\s+/)
|
|
48
|
+
.filter(word => !fillers.includes(word))
|
|
49
|
+
.slice(0, 7); // Max 7 meaningful words
|
|
50
|
+
return words.join(' ');
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Compress parameters to minimal schema notation
|
|
54
|
+
*/
|
|
55
|
+
function compressParameters(params) {
|
|
56
|
+
if (!params)
|
|
57
|
+
return {};
|
|
58
|
+
const compressed = {};
|
|
59
|
+
// Handle Zod schema
|
|
60
|
+
if (params._def) {
|
|
61
|
+
const shape = params._def.shape?.() || {};
|
|
62
|
+
Object.entries(shape).forEach(([key, value]) => {
|
|
63
|
+
compressed[compressParamName(key)] = compressParamType(value);
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
// Handle plain object
|
|
67
|
+
else if (typeof params === 'object') {
|
|
68
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
69
|
+
compressed[compressParamName(key)] =
|
|
70
|
+
typeof value === 'object' ? compressParamType(value) : 's';
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
return compressed;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Compress parameter names
|
|
77
|
+
*/
|
|
78
|
+
function compressParamName(name) {
|
|
79
|
+
const mapping = {
|
|
80
|
+
'query': 'q',
|
|
81
|
+
'prompt': 'p',
|
|
82
|
+
'model': 'm',
|
|
83
|
+
'temperature': 't',
|
|
84
|
+
'maxTokens': 'mx',
|
|
85
|
+
'max_tokens': 'mx',
|
|
86
|
+
'context': 'c',
|
|
87
|
+
'content': 'c',
|
|
88
|
+
'message': 'msg',
|
|
89
|
+
'depth': 'd',
|
|
90
|
+
'name': 'n',
|
|
91
|
+
'type': 'typ',
|
|
92
|
+
'projectPath': 'path',
|
|
93
|
+
'input': 'i',
|
|
94
|
+
'output': 'o'
|
|
95
|
+
};
|
|
96
|
+
return mapping[name] || name.slice(0, 3);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Compress parameter type to shorthand
|
|
100
|
+
*/
|
|
101
|
+
function compressParamType(zodType) {
|
|
102
|
+
if (!zodType)
|
|
103
|
+
return 's'; // Default to string
|
|
104
|
+
const typeName = zodType._def?.typeName || zodType.type || 'string';
|
|
105
|
+
const typeMap = {
|
|
106
|
+
'ZodString': 's',
|
|
107
|
+
'ZodNumber': 'n',
|
|
108
|
+
'ZodBoolean': 'b',
|
|
109
|
+
'ZodArray': 'a',
|
|
110
|
+
'ZodObject': 'o',
|
|
111
|
+
'ZodEnum': 'e',
|
|
112
|
+
'ZodOptional': '?',
|
|
113
|
+
'string': 's',
|
|
114
|
+
'number': 'n',
|
|
115
|
+
'boolean': 'b',
|
|
116
|
+
'array': 'a',
|
|
117
|
+
'object': 'o'
|
|
118
|
+
};
|
|
119
|
+
let compressed = typeMap[typeName] || 's';
|
|
120
|
+
// Handle optional
|
|
121
|
+
if (zodType._def?.typeName === 'ZodOptional') {
|
|
122
|
+
const innerType = compressParamType(zodType._def.innerType);
|
|
123
|
+
compressed = innerType + '?';
|
|
124
|
+
}
|
|
125
|
+
// Handle enum values
|
|
126
|
+
if (zodType._def?.values) {
|
|
127
|
+
const values = zodType._def.values.slice(0, 3).join('|');
|
|
128
|
+
compressed = `e[${values}]`;
|
|
129
|
+
}
|
|
130
|
+
return compressed;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Consolidate multiple tools into one
|
|
134
|
+
*/
|
|
135
|
+
export function consolidateTools(tools, groupName) {
|
|
136
|
+
const consolidationMap = {
|
|
137
|
+
'ai_models': {
|
|
138
|
+
n: 'ai',
|
|
139
|
+
d: 'query ai models',
|
|
140
|
+
p: {
|
|
141
|
+
m: 's', // model
|
|
142
|
+
p: 's', // prompt
|
|
143
|
+
t: 'n?', // temperature (optional)
|
|
144
|
+
mx: 'n?' // maxTokens (optional)
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
'research': {
|
|
148
|
+
n: 'rsch',
|
|
149
|
+
d: 'web research citations',
|
|
150
|
+
p: {
|
|
151
|
+
q: 's', // query
|
|
152
|
+
d: 'e[quick|standard|deep]?', // depth
|
|
153
|
+
s: 'a?' // sources (optional array)
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
'code': {
|
|
157
|
+
n: 'code',
|
|
158
|
+
d: 'analyze review code',
|
|
159
|
+
p: {
|
|
160
|
+
c: 's', // code
|
|
161
|
+
m: 'e[review|analyze|fix]?', // mode
|
|
162
|
+
f: 's?' // focus area (optional)
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
'workflow': {
|
|
166
|
+
n: 'wf',
|
|
167
|
+
d: 'execute workflows',
|
|
168
|
+
p: {
|
|
169
|
+
n: 's', // name
|
|
170
|
+
i: 's', // input
|
|
171
|
+
o: 'o?' // options (optional object)
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
return consolidationMap[groupName] || {
|
|
176
|
+
n: groupName.slice(0, 5),
|
|
177
|
+
d: `${groupName} tools`,
|
|
178
|
+
p: { i: 's' }
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Expand compressed tool back to full MCP format
|
|
183
|
+
*/
|
|
184
|
+
export function expandCompressedTool(compressed) {
|
|
185
|
+
// Expand parameter names
|
|
186
|
+
const expandParamName = (short) => {
|
|
187
|
+
const mapping = {
|
|
188
|
+
'q': 'query',
|
|
189
|
+
'p': 'prompt',
|
|
190
|
+
'm': 'model',
|
|
191
|
+
't': 'temperature',
|
|
192
|
+
'mx': 'maxTokens',
|
|
193
|
+
'c': 'content',
|
|
194
|
+
'd': 'depth',
|
|
195
|
+
'n': 'name',
|
|
196
|
+
'i': 'input',
|
|
197
|
+
'o': 'output'
|
|
198
|
+
};
|
|
199
|
+
return mapping[short] || short;
|
|
200
|
+
};
|
|
201
|
+
// Expand parameter types
|
|
202
|
+
const expandParamType = (short) => {
|
|
203
|
+
if (short.endsWith('?')) {
|
|
204
|
+
return z.optional(expandParamType(short.slice(0, -1)));
|
|
205
|
+
}
|
|
206
|
+
const typeMap = {
|
|
207
|
+
's': z.string(),
|
|
208
|
+
'n': z.number(),
|
|
209
|
+
'b': z.boolean(),
|
|
210
|
+
'a': z.array(z.string()),
|
|
211
|
+
'o': z.object({})
|
|
212
|
+
};
|
|
213
|
+
// Handle enums
|
|
214
|
+
if (short.startsWith('e[')) {
|
|
215
|
+
const values = short.slice(2, -1).split('|');
|
|
216
|
+
return z.enum(values);
|
|
217
|
+
}
|
|
218
|
+
return typeMap[short] || z.string();
|
|
219
|
+
};
|
|
220
|
+
// Build expanded parameters
|
|
221
|
+
const parameters = {};
|
|
222
|
+
Object.entries(compressed.p).forEach(([key, type]) => {
|
|
223
|
+
parameters[expandParamName(key)] = expandParamType(type);
|
|
224
|
+
});
|
|
225
|
+
return {
|
|
226
|
+
name: compressed.n,
|
|
227
|
+
description: compressed.d,
|
|
228
|
+
parameters: z.object(parameters)
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Calculate estimated token count for a tool
|
|
233
|
+
*/
|
|
234
|
+
export function estimateTokens(tool) {
|
|
235
|
+
const str = JSON.stringify(tool);
|
|
236
|
+
// Rough estimate: 1 token ≈ 4 characters
|
|
237
|
+
return Math.ceil(str.length / 4);
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Batch compress all tools with token budget
|
|
241
|
+
*/
|
|
242
|
+
export function compressAllTools(tools, maxTokens = 10000) {
|
|
243
|
+
const compressed = [];
|
|
244
|
+
let totalTokens = 0;
|
|
245
|
+
for (const tool of tools) {
|
|
246
|
+
const comp = compressToolDescription(tool);
|
|
247
|
+
const tokens = estimateTokens(comp);
|
|
248
|
+
if (totalTokens + tokens <= maxTokens) {
|
|
249
|
+
compressed.push(comp);
|
|
250
|
+
totalTokens += tokens;
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
console.warn(`Skipping tool ${tool.name} - would exceed token budget`);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return { compressed, totalTokens };
|
|
257
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool configuration manager with profile support
|
|
3
|
+
* Allows disabling specific tools via config file or environment variables
|
|
4
|
+
*
|
|
5
|
+
* Precedence (highest to lowest):
|
|
6
|
+
* 1. customProfile.enabled = true in tools.config.json
|
|
7
|
+
* 2. TACHIBOT_PROFILE environment variable
|
|
8
|
+
* 3. activeProfile in tools.config.json
|
|
9
|
+
* 4. Fallback: all tools enabled
|
|
10
|
+
*/
|
|
11
|
+
import { readFileSync, existsSync } from 'fs';
|
|
12
|
+
import { join, dirname } from 'path';
|
|
13
|
+
import { fileURLToPath } from 'url';
|
|
14
|
+
// Get __dirname equivalent in ES modules
|
|
15
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
16
|
+
const __dirname = dirname(__filename);
|
|
17
|
+
let toolConfig = { tools: {} };
|
|
18
|
+
let activeTools = {};
|
|
19
|
+
let profileSource = 'default';
|
|
20
|
+
/**
|
|
21
|
+
* Load a profile from the profiles/ directory
|
|
22
|
+
*/
|
|
23
|
+
function loadProfileFromFile(profileName) {
|
|
24
|
+
try {
|
|
25
|
+
const profilePath = join(__dirname, '../../../profiles', `${profileName}.json`);
|
|
26
|
+
if (existsSync(profilePath)) {
|
|
27
|
+
const content = readFileSync(profilePath, 'utf-8');
|
|
28
|
+
return JSON.parse(content);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
console.warn(`⚠️ Could not load profile '${profileName}' from profiles/ directory`);
|
|
33
|
+
}
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
// Load config once at startup
|
|
37
|
+
try {
|
|
38
|
+
// Use __dirname to find config relative to source code location, not cwd
|
|
39
|
+
// When built: dist/src/utils/tool-config.js -> go up 3 levels to project root
|
|
40
|
+
const configPath = join(__dirname, '../../../tools.config.json');
|
|
41
|
+
if (existsSync(configPath)) {
|
|
42
|
+
const configContent = readFileSync(configPath, 'utf-8');
|
|
43
|
+
toolConfig = JSON.parse(configContent);
|
|
44
|
+
// Priority 1: Custom profile enabled in tools.config.json
|
|
45
|
+
if (toolConfig.customProfile?.enabled) {
|
|
46
|
+
activeTools = toolConfig.customProfile.tools;
|
|
47
|
+
profileSource = 'custom';
|
|
48
|
+
console.error(`📋 Using custom profile from tools.config.json`);
|
|
49
|
+
}
|
|
50
|
+
// Priority 2: TACHIBOT_PROFILE environment variable
|
|
51
|
+
else if (process.env.TACHIBOT_PROFILE) {
|
|
52
|
+
const envProfile = process.env.TACHIBOT_PROFILE;
|
|
53
|
+
const profile = loadProfileFromFile(envProfile);
|
|
54
|
+
if (profile) {
|
|
55
|
+
activeTools = profile.tools;
|
|
56
|
+
profileSource = envProfile;
|
|
57
|
+
console.error(`📋 Using profile '${envProfile}' from TACHIBOT_PROFILE env var`);
|
|
58
|
+
console.error(` ${profile.description}`);
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
console.warn(`⚠️ Profile '${envProfile}' not found, falling back to activeProfile`);
|
|
62
|
+
// Fall through to next priority
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// Priority 3: activeProfile in tools.config.json
|
|
66
|
+
if (!activeTools || Object.keys(activeTools).length === 0) {
|
|
67
|
+
if (toolConfig.activeProfile) {
|
|
68
|
+
// Try to load from profiles/ directory first (new structure)
|
|
69
|
+
const profile = loadProfileFromFile(toolConfig.activeProfile);
|
|
70
|
+
if (profile) {
|
|
71
|
+
activeTools = profile.tools;
|
|
72
|
+
profileSource = toolConfig.activeProfile;
|
|
73
|
+
console.error(`📋 Using profile '${toolConfig.activeProfile}': ${profile.description}`);
|
|
74
|
+
}
|
|
75
|
+
// Fall back to embedded profile in tools.config.json (legacy)
|
|
76
|
+
else if (toolConfig.profiles?.[toolConfig.activeProfile]) {
|
|
77
|
+
const profile = toolConfig.profiles[toolConfig.activeProfile];
|
|
78
|
+
activeTools = profile.tools;
|
|
79
|
+
profileSource = toolConfig.activeProfile;
|
|
80
|
+
console.error(`📋 Using profile '${toolConfig.activeProfile}' from tools.config.json (legacy)`);
|
|
81
|
+
console.error(` ${profile.description}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
else if (toolConfig.tools) {
|
|
85
|
+
// Fallback to legacy flat tools config
|
|
86
|
+
activeTools = toolConfig.tools;
|
|
87
|
+
profileSource = 'legacy';
|
|
88
|
+
console.error(`📋 Using legacy flat configuration from tools.config.json`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// Final fallback: all tools enabled
|
|
93
|
+
if (!activeTools || Object.keys(activeTools).length === 0) {
|
|
94
|
+
console.warn(`⚠️ No valid configuration found, all tools enabled by default`);
|
|
95
|
+
profileSource = 'default';
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
console.warn(`⚠️ Could not load tools.config.json, all tools enabled by default`);
|
|
100
|
+
profileSource = 'default';
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Check if a tool should be registered
|
|
104
|
+
* Priority: Environment variables > Profile config > Default (enabled)
|
|
105
|
+
*/
|
|
106
|
+
export function isToolEnabled(toolName) {
|
|
107
|
+
// Environment variable override (highest priority)
|
|
108
|
+
const envEnable = process.env[`ENABLE_TOOL_${toolName.toUpperCase()}`];
|
|
109
|
+
if (envEnable === 'true')
|
|
110
|
+
return true;
|
|
111
|
+
const envDisable = process.env[`DISABLE_TOOL_${toolName.toUpperCase()}`];
|
|
112
|
+
if (envDisable === 'true')
|
|
113
|
+
return false;
|
|
114
|
+
// Check if globally disabled
|
|
115
|
+
if (process.env.DISABLE_ALL_TOOLS === 'true')
|
|
116
|
+
return false;
|
|
117
|
+
// Check active profile
|
|
118
|
+
const toolStatus = activeTools[toolName];
|
|
119
|
+
// If no profile loaded (default mode), enable all tools
|
|
120
|
+
if (profileSource === 'default') {
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
// Simple rule: If tool not in profile config, it's DISABLED
|
|
124
|
+
// Profiles are explicit allowlists - if it's not listed, it's off
|
|
125
|
+
if (toolStatus === undefined) {
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
// Use the config value
|
|
129
|
+
if (!toolStatus) {
|
|
130
|
+
console.error(` ⏭️ Skipping ${toolName} (disabled in profile)`);
|
|
131
|
+
}
|
|
132
|
+
return toolStatus;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Get list of disabled tools for logging
|
|
136
|
+
*/
|
|
137
|
+
export function getDisabledTools() {
|
|
138
|
+
if (!activeTools)
|
|
139
|
+
return [];
|
|
140
|
+
return Object.entries(activeTools)
|
|
141
|
+
.filter(([_, enabled]) => !enabled)
|
|
142
|
+
.map(([name]) => name);
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Get list of enabled tools for logging
|
|
146
|
+
*/
|
|
147
|
+
export function getEnabledTools() {
|
|
148
|
+
if (!activeTools)
|
|
149
|
+
return [];
|
|
150
|
+
return Object.entries(activeTools)
|
|
151
|
+
.filter(([_, enabled]) => enabled)
|
|
152
|
+
.map(([name]) => name);
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Get active profile info
|
|
156
|
+
*/
|
|
157
|
+
export function getActiveProfile() {
|
|
158
|
+
if (profileSource === 'default') {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
if (profileSource === 'custom') {
|
|
162
|
+
return {
|
|
163
|
+
name: 'custom',
|
|
164
|
+
description: toolConfig.customProfile?.description
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
if (profileSource === 'legacy') {
|
|
168
|
+
return {
|
|
169
|
+
name: 'legacy',
|
|
170
|
+
description: 'Legacy flat configuration'
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
// For named profiles, try to get description
|
|
174
|
+
const profile = loadProfileFromFile(profileSource);
|
|
175
|
+
return {
|
|
176
|
+
name: profileSource,
|
|
177
|
+
description: profile?.description
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Log configuration summary
|
|
182
|
+
*/
|
|
183
|
+
export function logToolConfiguration() {
|
|
184
|
+
const profile = getActiveProfile();
|
|
185
|
+
if (profile) {
|
|
186
|
+
console.error(`🎯 Active profile: ${profile.name}`);
|
|
187
|
+
if (profile.description) {
|
|
188
|
+
console.error(` ${profile.description}`);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
const disabled = getDisabledTools();
|
|
192
|
+
if (disabled.length > 0) {
|
|
193
|
+
console.error(`🚫 Disabled tools: ${disabled.join(', ')}`);
|
|
194
|
+
}
|
|
195
|
+
// Check for environment overrides
|
|
196
|
+
const envVars = Object.keys(process.env);
|
|
197
|
+
const toolOverrides = envVars.filter(key => key.startsWith('ENABLE_TOOL_') || key.startsWith('DISABLE_TOOL_'));
|
|
198
|
+
if (toolOverrides.length > 0) {
|
|
199
|
+
console.error(`🔧 Environment overrides active: ${toolOverrides.join(', ')}`);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dependency graph validator - detects circular dependencies and validates execution order
|
|
3
|
+
*/
|
|
4
|
+
export class DependencyGraphValidator {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.interpolationRegex = /\$\{([^.}]+)(?:\.[^}]+)?\}/g;
|
|
7
|
+
}
|
|
8
|
+
validate(context) {
|
|
9
|
+
const errors = [];
|
|
10
|
+
// Build dependency graph
|
|
11
|
+
const graph = this.buildDependencyGraph(context.workflow.steps);
|
|
12
|
+
// Check for circular dependencies
|
|
13
|
+
const cycle = this.detectCycle(graph);
|
|
14
|
+
if (cycle) {
|
|
15
|
+
errors.push({
|
|
16
|
+
type: 'dependency',
|
|
17
|
+
severity: 'error',
|
|
18
|
+
message: `Circular dependency detected: ${cycle.join(' → ')} → ${cycle[0]}`,
|
|
19
|
+
path: '$.steps',
|
|
20
|
+
suggestion: 'Reorganize steps to remove circular dependencies'
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
// Validate execution order
|
|
24
|
+
const orderErrors = this.validateExecutionOrder(context.workflow.steps, graph);
|
|
25
|
+
errors.push(...orderErrors);
|
|
26
|
+
return errors;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Build dependency graph from workflow steps
|
|
30
|
+
* Returns Map<stepName, Set<dependencies>>
|
|
31
|
+
*/
|
|
32
|
+
buildDependencyGraph(steps) {
|
|
33
|
+
const graph = new Map();
|
|
34
|
+
steps.forEach(step => {
|
|
35
|
+
graph.set(step.name, new Set());
|
|
36
|
+
// Add explicit dependencies
|
|
37
|
+
if (step.dependsOn && Array.isArray(step.dependsOn)) {
|
|
38
|
+
step.dependsOn.forEach((dep) => graph.get(step.name).add(dep));
|
|
39
|
+
}
|
|
40
|
+
// Add dependencies from loadFiles
|
|
41
|
+
if (step.loadFiles && Array.isArray(step.loadFiles)) {
|
|
42
|
+
step.loadFiles.forEach((fileRef) => graph.get(step.name).add(fileRef));
|
|
43
|
+
}
|
|
44
|
+
// Add implicit dependencies from interpolations in input
|
|
45
|
+
if (step.input) {
|
|
46
|
+
const inputStr = JSON.stringify(step.input);
|
|
47
|
+
const matches = inputStr.matchAll(this.interpolationRegex);
|
|
48
|
+
for (const match of matches) {
|
|
49
|
+
const reference = match[1]; // Get the step name part
|
|
50
|
+
// Only add if it's referencing another step (not a variable)
|
|
51
|
+
if (steps.some(s => s.name === reference)) {
|
|
52
|
+
graph.get(step.name).add(reference);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// Add implicit dependencies from 'when' condition
|
|
57
|
+
if (step.when) {
|
|
58
|
+
const matches = step.when.matchAll(this.interpolationRegex);
|
|
59
|
+
for (const match of matches) {
|
|
60
|
+
const reference = match[1];
|
|
61
|
+
if (steps.some(s => s.name === reference)) {
|
|
62
|
+
graph.get(step.name).add(reference);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
return graph;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Detect circular dependencies using DFS
|
|
71
|
+
* Returns the cycle path if found, null otherwise
|
|
72
|
+
*/
|
|
73
|
+
detectCycle(graph) {
|
|
74
|
+
const visiting = new Set();
|
|
75
|
+
const visited = new Set();
|
|
76
|
+
const path = [];
|
|
77
|
+
for (const node of graph.keys()) {
|
|
78
|
+
if (!visited.has(node)) {
|
|
79
|
+
if (this.hasCycleDFS(node, graph, visiting, visited, path)) {
|
|
80
|
+
// Extract just the cycle from the path
|
|
81
|
+
const cycleStart = path.indexOf(path[path.length - 1]);
|
|
82
|
+
return path.slice(cycleStart);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* DFS helper to detect cycles
|
|
90
|
+
*/
|
|
91
|
+
hasCycleDFS(node, graph, visiting, visited, path) {
|
|
92
|
+
if (visiting.has(node)) {
|
|
93
|
+
// Found a cycle - add the node that creates the cycle
|
|
94
|
+
path.push(node);
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
if (visited.has(node)) {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
visiting.add(node);
|
|
101
|
+
path.push(node);
|
|
102
|
+
const neighbors = graph.get(node) || new Set();
|
|
103
|
+
for (const neighbor of neighbors) {
|
|
104
|
+
if (this.hasCycleDFS(neighbor, graph, visiting, visited, path)) {
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
visiting.delete(node);
|
|
109
|
+
visited.add(node);
|
|
110
|
+
path.pop();
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Validate that dependencies appear before dependent steps
|
|
115
|
+
*/
|
|
116
|
+
validateExecutionOrder(steps, graph) {
|
|
117
|
+
const errors = [];
|
|
118
|
+
const stepPositions = new Map(steps.map((s, i) => [s.name, i]));
|
|
119
|
+
for (const [stepName, dependencies] of graph.entries()) {
|
|
120
|
+
const currentPos = stepPositions.get(stepName);
|
|
121
|
+
if (currentPos === undefined)
|
|
122
|
+
continue;
|
|
123
|
+
for (const dep of dependencies) {
|
|
124
|
+
const depPos = stepPositions.get(dep);
|
|
125
|
+
if (depPos === undefined) {
|
|
126
|
+
errors.push({
|
|
127
|
+
type: 'dependency',
|
|
128
|
+
severity: 'error',
|
|
129
|
+
message: `Step '${stepName}' depends on undefined step '${dep}'`,
|
|
130
|
+
path: `$.steps[${currentPos}]`,
|
|
131
|
+
suggestion: `Define step '${dep}' or remove the dependency`
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
else if (depPos >= currentPos) {
|
|
135
|
+
errors.push({
|
|
136
|
+
type: 'dependency',
|
|
137
|
+
severity: 'error',
|
|
138
|
+
message: `Step '${stepName}' depends on '${dep}' which appears later in the workflow`,
|
|
139
|
+
path: `$.steps[${currentPos}]`,
|
|
140
|
+
suggestion: `Move step '${dep}' (currently at position ${depPos + 1}) before step '${stepName}' (position ${currentPos + 1})`
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return errors;
|
|
146
|
+
}
|
|
147
|
+
}
|