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,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SmartAPIClient - Adaptive timeout and retry system for external API calls
|
|
3
|
+
*
|
|
4
|
+
* Features:
|
|
5
|
+
* - Fixed timeouts with priority-based adjustment (interactive vs batch)
|
|
6
|
+
* - Exponential backoff with full jitter
|
|
7
|
+
* - Proper resource cleanup with AbortController
|
|
8
|
+
* - NO metrics collection - privacy-first design
|
|
9
|
+
*/
|
|
10
|
+
export class SmartAPIClient {
|
|
11
|
+
constructor() {
|
|
12
|
+
// Default configuration
|
|
13
|
+
this.defaults = {
|
|
14
|
+
maxRetries: 3,
|
|
15
|
+
baseTimeoutMs: 30000, // 30 seconds
|
|
16
|
+
maxTimeoutMs: 120000, // 2 minutes
|
|
17
|
+
backoffBase: 2,
|
|
18
|
+
enableJitter: true
|
|
19
|
+
};
|
|
20
|
+
// Priority-specific timeout multipliers
|
|
21
|
+
this.priorityMultipliers = {
|
|
22
|
+
interactive: {
|
|
23
|
+
base: 0.5, // 50% of base timeout
|
|
24
|
+
max: 0.5 // 50% of max timeout
|
|
25
|
+
},
|
|
26
|
+
batch: {
|
|
27
|
+
base: 1.0, // 100% of base timeout
|
|
28
|
+
max: 1.0 // 100% of max timeout
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
// Singleton pattern
|
|
32
|
+
}
|
|
33
|
+
static getInstance() {
|
|
34
|
+
if (!SmartAPIClient.instance) {
|
|
35
|
+
SmartAPIClient.instance = new SmartAPIClient();
|
|
36
|
+
}
|
|
37
|
+
return SmartAPIClient.instance;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Call API with timeout and retry logic
|
|
41
|
+
*/
|
|
42
|
+
async callWithRetries(apiCall, config) {
|
|
43
|
+
const fullConfig = { ...this.defaults, ...config };
|
|
44
|
+
const startTime = Date.now();
|
|
45
|
+
let lastError;
|
|
46
|
+
let attempt = 0;
|
|
47
|
+
while (attempt < fullConfig.maxRetries) {
|
|
48
|
+
const controller = new AbortController();
|
|
49
|
+
const timeoutMs = this.calculateTimeout(fullConfig.priority, fullConfig.baseTimeoutMs, fullConfig.maxTimeoutMs);
|
|
50
|
+
const timeoutId = setTimeout(() => {
|
|
51
|
+
controller.abort();
|
|
52
|
+
}, timeoutMs);
|
|
53
|
+
try {
|
|
54
|
+
const attemptStart = Date.now();
|
|
55
|
+
// Execute API call
|
|
56
|
+
const result = await Promise.race([
|
|
57
|
+
apiCall(),
|
|
58
|
+
this.createTimeoutPromise(timeoutMs, fullConfig.provider)
|
|
59
|
+
]);
|
|
60
|
+
clearTimeout(timeoutId);
|
|
61
|
+
const latency = Date.now() - attemptStart;
|
|
62
|
+
console.log(`[SmartAPIClient] Success: ${fullConfig.provider} ` +
|
|
63
|
+
`(attempt ${attempt + 1}, ${latency}ms, timeout=${timeoutMs}ms)`);
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
clearTimeout(timeoutId);
|
|
68
|
+
lastError = error;
|
|
69
|
+
console.error(`[SmartAPIClient] Attempt ${attempt + 1} failed for ${fullConfig.provider}: ${error.message}`);
|
|
70
|
+
// Check if we should retry
|
|
71
|
+
if (!this.shouldRetry(error, attempt, fullConfig.maxRetries)) {
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
// Wait before retry with exponential backoff + jitter
|
|
75
|
+
await this.backoffWithJitter(attempt, fullConfig.backoffBase, fullConfig.enableJitter);
|
|
76
|
+
attempt++;
|
|
77
|
+
}
|
|
78
|
+
finally {
|
|
79
|
+
// Ensure cleanup
|
|
80
|
+
if (!controller.signal.aborted) {
|
|
81
|
+
controller.abort();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const totalTime = Date.now() - startTime;
|
|
86
|
+
throw new Error(`[SmartAPIClient] All retries exhausted for ${fullConfig.provider} ` +
|
|
87
|
+
`(${attempt} attempts in ${totalTime}ms): ${lastError?.message || 'Unknown error'}`);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Calculate timeout based on priority
|
|
91
|
+
*/
|
|
92
|
+
calculateTimeout(priority, baseTimeout, maxTimeout) {
|
|
93
|
+
const finalTimeout = Math.min(baseTimeout * this.priorityMultipliers[priority].base, maxTimeout * this.priorityMultipliers[priority].max);
|
|
94
|
+
return finalTimeout;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Exponential backoff with full jitter
|
|
98
|
+
*/
|
|
99
|
+
async backoffWithJitter(attempt, base, enableJitter) {
|
|
100
|
+
const exponentialDelay = Math.pow(base, attempt) * 1000;
|
|
101
|
+
const cappedDelay = Math.min(exponentialDelay, 10000); // Cap at 10 seconds
|
|
102
|
+
const finalDelay = enableJitter
|
|
103
|
+
? Math.random() * cappedDelay // Full jitter: random(0, cappedDelay)
|
|
104
|
+
: cappedDelay;
|
|
105
|
+
console.log(`[SmartAPIClient] Backing off for ${Math.round(finalDelay)}ms ` +
|
|
106
|
+
`(attempt ${attempt + 1}, ${enableJitter ? 'with' : 'without'} jitter)`);
|
|
107
|
+
await new Promise(resolve => setTimeout(resolve, finalDelay));
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Determine if we should retry based on error type and attempt count
|
|
111
|
+
*/
|
|
112
|
+
shouldRetry(error, attempt, maxRetries) {
|
|
113
|
+
if (attempt >= maxRetries - 1) {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
// Retry on timeout, network errors, 5xx errors
|
|
117
|
+
const retryablePatterns = [
|
|
118
|
+
'timeout',
|
|
119
|
+
'ETIMEDOUT',
|
|
120
|
+
'ECONNRESET',
|
|
121
|
+
'ENOTFOUND',
|
|
122
|
+
'ECONNREFUSED',
|
|
123
|
+
'500',
|
|
124
|
+
'502',
|
|
125
|
+
'503',
|
|
126
|
+
'504'
|
|
127
|
+
];
|
|
128
|
+
const errorMessage = error?.message?.toLowerCase() || '';
|
|
129
|
+
const isRetryable = retryablePatterns.some(pattern => errorMessage.includes(pattern.toLowerCase()));
|
|
130
|
+
console.log(`[SmartAPIClient] Error "${errorMessage.slice(0, 50)}..." is ` +
|
|
131
|
+
`${isRetryable ? 'retryable' : 'not retryable'}`);
|
|
132
|
+
return isRetryable;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Create a timeout promise for Promise.race
|
|
136
|
+
*/
|
|
137
|
+
createTimeoutPromise(timeoutMs, provider) {
|
|
138
|
+
return new Promise((_, reject) => {
|
|
139
|
+
setTimeout(() => {
|
|
140
|
+
reject(new Error(`Timeout after ${timeoutMs}ms for ${provider}`));
|
|
141
|
+
}, timeoutMs);
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
// Export singleton instance
|
|
146
|
+
export const smartAPIClient = SmartAPIClient.getInstance();
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Beautiful Unicode box-drawing table builder using cli-table3
|
|
3
|
+
* Provides a fluent API for creating readable, professional tables
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```typescript
|
|
7
|
+
* const table = new TableBuilder()
|
|
8
|
+
* .withHeaders(['Status', 'Model', 'Conclusion'])
|
|
9
|
+
* .withAlignments(['center', 'left', 'center'])
|
|
10
|
+
* .addRow(['✅', 'grok-4', '✓ true'])
|
|
11
|
+
* .build();
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
import Table from 'cli-table3';
|
|
15
|
+
/**
|
|
16
|
+
* Predefined table styles
|
|
17
|
+
*/
|
|
18
|
+
export const TableStyles = {
|
|
19
|
+
/**
|
|
20
|
+
* Beautiful Unicode box-drawing characters
|
|
21
|
+
* Uses double lines for outer border, single lines for inner
|
|
22
|
+
*/
|
|
23
|
+
unicode: {
|
|
24
|
+
chars: {
|
|
25
|
+
'top': '═',
|
|
26
|
+
'top-mid': '╤',
|
|
27
|
+
'top-left': '╔',
|
|
28
|
+
'top-right': '╗',
|
|
29
|
+
'bottom': '═',
|
|
30
|
+
'bottom-mid': '╧',
|
|
31
|
+
'bottom-left': '╚',
|
|
32
|
+
'bottom-right': '╝',
|
|
33
|
+
'left': '║',
|
|
34
|
+
'left-mid': '╟',
|
|
35
|
+
'mid': '─',
|
|
36
|
+
'mid-mid': '┼',
|
|
37
|
+
'right': '║',
|
|
38
|
+
'right-mid': '╢',
|
|
39
|
+
'middle': '│'
|
|
40
|
+
},
|
|
41
|
+
style: {
|
|
42
|
+
head: [], // No color styling for headers
|
|
43
|
+
border: [] // No color styling for borders
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
/**
|
|
47
|
+
* Compact Unicode style with single lines
|
|
48
|
+
*/
|
|
49
|
+
compact: {
|
|
50
|
+
chars: {
|
|
51
|
+
'top': '─',
|
|
52
|
+
'top-mid': '┬',
|
|
53
|
+
'top-left': '┌',
|
|
54
|
+
'top-right': '┐',
|
|
55
|
+
'bottom': '─',
|
|
56
|
+
'bottom-mid': '┴',
|
|
57
|
+
'bottom-left': '└',
|
|
58
|
+
'bottom-right': '┘',
|
|
59
|
+
'left': '│',
|
|
60
|
+
'left-mid': '├',
|
|
61
|
+
'mid': '─',
|
|
62
|
+
'mid-mid': '┼',
|
|
63
|
+
'right': '│',
|
|
64
|
+
'right-mid': '┤',
|
|
65
|
+
'middle': '│'
|
|
66
|
+
},
|
|
67
|
+
style: {
|
|
68
|
+
head: [],
|
|
69
|
+
border: []
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
/**
|
|
73
|
+
* ASCII-only style for maximum compatibility
|
|
74
|
+
*/
|
|
75
|
+
ascii: {
|
|
76
|
+
chars: {
|
|
77
|
+
'top': '-',
|
|
78
|
+
'top-mid': '+',
|
|
79
|
+
'top-left': '+',
|
|
80
|
+
'top-right': '+',
|
|
81
|
+
'bottom': '-',
|
|
82
|
+
'bottom-mid': '+',
|
|
83
|
+
'bottom-left': '+',
|
|
84
|
+
'bottom-right': '+',
|
|
85
|
+
'left': '|',
|
|
86
|
+
'left-mid': '+',
|
|
87
|
+
'mid': '-',
|
|
88
|
+
'mid-mid': '+',
|
|
89
|
+
'right': '|',
|
|
90
|
+
'right-mid': '+',
|
|
91
|
+
'middle': '|'
|
|
92
|
+
},
|
|
93
|
+
style: {
|
|
94
|
+
head: [],
|
|
95
|
+
border: []
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* Fluent API table builder with beautiful Unicode box-drawing
|
|
101
|
+
*/
|
|
102
|
+
export class TableBuilder {
|
|
103
|
+
constructor() {
|
|
104
|
+
this.headers = [];
|
|
105
|
+
this.alignments = [];
|
|
106
|
+
this.rows = [];
|
|
107
|
+
this.style = TableStyles.unicode;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Set table headers
|
|
111
|
+
*/
|
|
112
|
+
withHeaders(headers) {
|
|
113
|
+
this.headers = headers;
|
|
114
|
+
return this;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Set column alignments
|
|
118
|
+
* @param alignments - Array of 'left', 'center', or 'right'
|
|
119
|
+
*/
|
|
120
|
+
withAlignments(alignments) {
|
|
121
|
+
this.alignments = alignments;
|
|
122
|
+
return this;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Set explicit column widths
|
|
126
|
+
* @param widths - Array of column widths in characters
|
|
127
|
+
*/
|
|
128
|
+
withColumnWidths(widths) {
|
|
129
|
+
this.colWidths = widths;
|
|
130
|
+
return this;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Use a predefined style
|
|
134
|
+
* @param styleName - 'unicode', 'compact', or 'ascii'
|
|
135
|
+
*/
|
|
136
|
+
withStyle(styleName) {
|
|
137
|
+
this.style = TableStyles[styleName];
|
|
138
|
+
return this;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Use custom box-drawing characters
|
|
142
|
+
* @param chars - Custom character set
|
|
143
|
+
*/
|
|
144
|
+
withCustomChars(chars) {
|
|
145
|
+
this.style = {
|
|
146
|
+
...this.style,
|
|
147
|
+
chars: { ...this.style.chars, ...chars }
|
|
148
|
+
};
|
|
149
|
+
return this;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Add a single row to the table
|
|
153
|
+
* @param row - Array of cell values
|
|
154
|
+
*/
|
|
155
|
+
addRow(row) {
|
|
156
|
+
this.rows.push(row);
|
|
157
|
+
return this;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Add multiple rows at once
|
|
161
|
+
* @param rows - Array of row arrays
|
|
162
|
+
*/
|
|
163
|
+
addRows(rows) {
|
|
164
|
+
this.rows.push(...rows);
|
|
165
|
+
return this;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Build and return the formatted table string
|
|
169
|
+
* @returns Formatted table with newline prefix for spacing
|
|
170
|
+
*/
|
|
171
|
+
build() {
|
|
172
|
+
const options = {
|
|
173
|
+
head: this.headers.length > 0 ? this.headers : undefined,
|
|
174
|
+
colAligns: this.alignments.length > 0 ? this.alignments : undefined,
|
|
175
|
+
...this.style
|
|
176
|
+
};
|
|
177
|
+
// Only add colWidths if explicitly set
|
|
178
|
+
if (this.colWidths && this.colWidths.length > 0) {
|
|
179
|
+
options.colWidths = this.colWidths;
|
|
180
|
+
}
|
|
181
|
+
const table = new Table(options);
|
|
182
|
+
if (this.rows.length > 0) {
|
|
183
|
+
table.push(...this.rows);
|
|
184
|
+
}
|
|
185
|
+
// Add newline prefix for better spacing in output
|
|
186
|
+
return '\n' + table.toString();
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Build and return without the leading newline
|
|
190
|
+
* @returns Formatted table without prefix spacing
|
|
191
|
+
*/
|
|
192
|
+
buildRaw() {
|
|
193
|
+
return this.build().trimStart();
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Quick helper function for simple tables
|
|
198
|
+
* @param headers - Column headers
|
|
199
|
+
* @param rows - Table rows
|
|
200
|
+
* @param alignments - Optional column alignments
|
|
201
|
+
* @returns Formatted table string
|
|
202
|
+
*
|
|
203
|
+
* @example
|
|
204
|
+
* ```typescript
|
|
205
|
+
* const table = quickTable(
|
|
206
|
+
* ['Name', 'Age'],
|
|
207
|
+
* [['Alice', 30], ['Bob', 25]],
|
|
208
|
+
* ['left', 'right']
|
|
209
|
+
* );
|
|
210
|
+
* ```
|
|
211
|
+
*/
|
|
212
|
+
export function quickTable(headers, rows, alignments) {
|
|
213
|
+
const builder = new TableBuilder().withHeaders(headers).addRows(rows);
|
|
214
|
+
if (alignments) {
|
|
215
|
+
builder.withAlignments(alignments);
|
|
216
|
+
}
|
|
217
|
+
return builder.build();
|
|
218
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Timestamp Formatter - Human-readable timestamps for workflow/session output
|
|
3
|
+
* Format: YYYY-MM-DD-DayName-HH-MM-shortid
|
|
4
|
+
* Example: 2025-11-23-Sunday-22-44-a1b2c3d4
|
|
5
|
+
*/
|
|
6
|
+
import { randomUUID } from 'crypto';
|
|
7
|
+
/**
|
|
8
|
+
* Day names for human readability
|
|
9
|
+
*/
|
|
10
|
+
const DAY_NAMES = [
|
|
11
|
+
'Sunday',
|
|
12
|
+
'Monday',
|
|
13
|
+
'Tuesday',
|
|
14
|
+
'Wednesday',
|
|
15
|
+
'Thursday',
|
|
16
|
+
'Friday',
|
|
17
|
+
'Saturday'
|
|
18
|
+
];
|
|
19
|
+
/**
|
|
20
|
+
* Format a date as human-readable timestamp
|
|
21
|
+
* @param date - Date to format (defaults to now)
|
|
22
|
+
* @param includeShortId - Whether to include short UUID (default: true)
|
|
23
|
+
* @returns Formatted timestamp string
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* formatTimestamp() // "2025-11-23-Sunday-22-44-a1b2c3d4"
|
|
27
|
+
* formatTimestamp(new Date(), false) // "2025-11-23-Sunday-22-44"
|
|
28
|
+
*/
|
|
29
|
+
export function formatTimestamp(date = new Date(), includeShortId = true) {
|
|
30
|
+
const year = date.getFullYear();
|
|
31
|
+
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
32
|
+
const day = String(date.getDate()).padStart(2, '0');
|
|
33
|
+
const dayName = DAY_NAMES[date.getDay()];
|
|
34
|
+
const hours = String(date.getHours()).padStart(2, '0');
|
|
35
|
+
const minutes = String(date.getMinutes()).padStart(2, '0');
|
|
36
|
+
const parts = [year, month, day, dayName, hours, minutes];
|
|
37
|
+
if (includeShortId) {
|
|
38
|
+
const shortId = randomUUID().split('-')[0];
|
|
39
|
+
parts.push(shortId);
|
|
40
|
+
}
|
|
41
|
+
return parts.join('-');
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Parse a human-readable timestamp back to Date
|
|
45
|
+
* @param timestamp - Formatted timestamp string
|
|
46
|
+
* @returns Date object or null if invalid
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* parseTimestamp("2025-11-23-Sunday-22-44-a1b2c3d4") // Date object
|
|
50
|
+
*/
|
|
51
|
+
export function parseTimestamp(timestamp) {
|
|
52
|
+
try {
|
|
53
|
+
// Format: YYYY-MM-DD-DayName-HH-MM-shortid
|
|
54
|
+
const parts = timestamp.split('-');
|
|
55
|
+
if (parts.length < 6) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
const [year, month, day, _dayName, hours, minutes] = parts;
|
|
59
|
+
const date = new Date(parseInt(year), parseInt(month) - 1, // JS months are 0-indexed
|
|
60
|
+
parseInt(day), parseInt(hours), parseInt(minutes), 0, 0);
|
|
61
|
+
if (isNaN(date.getTime())) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
return date;
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Generate a workflow ID with human-readable timestamp
|
|
72
|
+
* @param workflowName - Optional workflow name to include
|
|
73
|
+
* @returns Workflow ID string
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* generateWorkflowId() // "2025-11-23-Sunday-22-44-a1b2c3d4"
|
|
77
|
+
* generateWorkflowId("pingpong") // "2025-11-23-Sunday-22-44-a1b2c3d4"
|
|
78
|
+
*/
|
|
79
|
+
export function generateWorkflowId(workflowName) {
|
|
80
|
+
// Don't include workflow name in the folder name - it's already in the parent path
|
|
81
|
+
// workflow-output/{workflowName}/{timestamp}/
|
|
82
|
+
return formatTimestamp();
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Generate a session ID with human-readable timestamp
|
|
86
|
+
* @param mode - Session mode (e.g., "advanced-pingpong", "workflow-test")
|
|
87
|
+
* @returns Session ID string
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* generateSessionId("advanced-pingpong") // "session-2025-11-23-Sunday-22-44-advanced-pingpong"
|
|
91
|
+
*/
|
|
92
|
+
export function generateSessionId(mode) {
|
|
93
|
+
const timestamp = formatTimestamp(new Date(), false); // No short ID in session names
|
|
94
|
+
return `session-${timestamp}-${mode}`;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Check if a timestamp is from today
|
|
98
|
+
*/
|
|
99
|
+
export function isToday(timestamp) {
|
|
100
|
+
const parsed = parseTimestamp(timestamp);
|
|
101
|
+
if (!parsed)
|
|
102
|
+
return false;
|
|
103
|
+
const today = new Date();
|
|
104
|
+
return (parsed.getFullYear() === today.getFullYear() &&
|
|
105
|
+
parsed.getMonth() === today.getMonth() &&
|
|
106
|
+
parsed.getDate() === today.getDate());
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Get a human-readable relative time string
|
|
110
|
+
* @example "2 hours ago", "just now", "yesterday"
|
|
111
|
+
*/
|
|
112
|
+
export function getRelativeTime(timestamp) {
|
|
113
|
+
const parsed = parseTimestamp(timestamp);
|
|
114
|
+
if (!parsed)
|
|
115
|
+
return 'unknown';
|
|
116
|
+
const now = new Date();
|
|
117
|
+
const diffMs = now.getTime() - parsed.getTime();
|
|
118
|
+
const diffMins = Math.floor(diffMs / (1000 * 60));
|
|
119
|
+
const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
|
|
120
|
+
const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
|
|
121
|
+
if (diffMins < 1)
|
|
122
|
+
return 'just now';
|
|
123
|
+
if (diffMins < 60)
|
|
124
|
+
return `${diffMins} minute${diffMins > 1 ? 's' : ''} ago`;
|
|
125
|
+
if (diffHours < 24)
|
|
126
|
+
return `${diffHours} hour${diffHours > 1 ? 's' : ''} ago`;
|
|
127
|
+
if (diffDays === 1)
|
|
128
|
+
return 'yesterday';
|
|
129
|
+
if (diffDays < 7)
|
|
130
|
+
return `${diffDays} days ago`;
|
|
131
|
+
if (diffDays < 30)
|
|
132
|
+
return `${Math.floor(diffDays / 7)} week${Math.floor(diffDays / 7) > 1 ? 's' : ''} ago`;
|
|
133
|
+
return parsed.toLocaleDateString();
|
|
134
|
+
}
|