natureco-cli 5.19.0 → 5.20.1
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/CHANGELOG.md +32 -0
- package/package.json +1 -1
- package/skills/a-b-testing/SKILL.md +34 -0
- package/skills/accessibility-audit/SKILL.md +34 -0
- package/skills/agent-memory/SKILL.md +34 -0
- package/skills/agent-orchestration/SKILL.md +34 -0
- package/skills/analytics-setup/SKILL.md +34 -0
- package/skills/api-design/SKILL.md +34 -0
- package/skills/api-versioning/SKILL.md +34 -0
- package/skills/audio-transcription/SKILL.md +34 -0
- package/skills/authentication-patterns/SKILL.md +34 -0
- package/skills/authorization-rbac/SKILL.md +34 -0
- package/skills/backup-strategy/SKILL.md +34 -0
- package/skills/bash-scripting/SKILL.md +34 -0
- package/skills/batch-api-calls/SKILL.md +34 -0
- package/skills/batch-processing/SKILL.md +34 -0
- package/skills/bulk-file-operations/SKILL.md +34 -0
- package/skills/cache-responses/SKILL.md +34 -0
- package/skills/caching-strategy/SKILL.md +34 -0
- package/skills/calendar-optimization/SKILL.md +34 -0
- package/skills/changelog-generation/SKILL.md +34 -0
- package/skills/chat-bot-design/SKILL.md +34 -0
- package/skills/chunking-strategy/SKILL.md +34 -0
- package/skills/ci-cd-pipeline/SKILL.md +34 -0
- package/skills/classification-pipeline/SKILL.md +34 -0
- package/skills/clipboard-master/SKILL.md +34 -0
- package/skills/code-explanation/SKILL.md +34 -0
- package/skills/code-generation/SKILL.md +34 -0
- package/skills/code-migration/SKILL.md +34 -0
- package/skills/code-review-checklist/SKILL.md +34 -0
- package/skills/contact-management/SKILL.md +34 -0
- package/skills/content-generation/SKILL.md +34 -0
- package/skills/context-pruning/SKILL.md +34 -0
- package/skills/context-window-management/SKILL.md +34 -0
- package/skills/continuous-improvement/SKILL.md +34 -0
- package/skills/cron-scheduling/SKILL.md +34 -0
- package/skills/cross-platform-compat/SKILL.md +34 -0
- package/skills/csv-processing/SKILL.md +34 -0
- package/skills/daily-journal/SKILL.md +34 -0
- package/skills/data-analysis/SKILL.md +34 -0
- package/skills/data-backup/SKILL.md +34 -0
- package/skills/data-cleaning/SKILL.md +34 -0
- package/skills/data-extraction/SKILL.md +34 -0
- package/skills/data-validation/SKILL.md +34 -0
- package/skills/database-migrations/SKILL.md +34 -0
- package/skills/database-schema-design/SKILL.md +34 -0
- package/skills/decision-matrix/SKILL.md +34 -0
- package/skills/dependency-management/SKILL.md +34 -0
- package/skills/dev-environment-setup/SKILL.md +34 -0
- package/skills/distraction-blocker/SKILL.md +34 -0
- package/skills/docker-optimization/SKILL.md +34 -0
- package/skills/document-template/SKILL.md +34 -0
- package/skills/documentation-gen/SKILL.md +34 -0
- package/skills/efficient-formatting/SKILL.md +34 -0
- package/skills/email-management/SKILL.md +34 -0
- package/skills/email-service/SKILL.md +34 -0
- package/skills/embedding-generation/SKILL.md +34 -0
- package/skills/entity-extraction/SKILL.md +34 -0
- package/skills/environment-config/SKILL.md +34 -0
- package/skills/error-handling/SKILL.md +34 -0
- package/skills/estimation-techniques/SKILL.md +34 -0
- package/skills/event-driven-architecture/SKILL.md +34 -0
- package/skills/excel-automation/SKILL.md +34 -0
- package/skills/execution-monitoring/SKILL.md +34 -0
- package/skills/expense-tracking/SKILL.md +34 -0
- package/skills/feature-engineering/SKILL.md +34 -0
- package/skills/feature-flags/SKILL.md +34 -0
- package/skills/file-conversion/SKILL.md +34 -0
- package/skills/file-upload-handling/SKILL.md +34 -0
- package/skills/fine-tuning-prep/SKILL.md +34 -0
- package/skills/focus-mode/SKILL.md +34 -0
- package/skills/form-validation/SKILL.md +34 -0
- package/skills/function-calling/SKILL.md +34 -0
- package/skills/git-workflow/SKILL.md +34 -0
- package/skills/goal-setting/SKILL.md +34 -0
- package/skills/graphql-design/SKILL.md +34 -0
- package/skills/habit-tracker/SKILL.md +34 -0
- package/skills/hallucination-detection/SKILL.md +34 -0
- package/skills/image-processing/SKILL.md +34 -0
- package/skills/json-transformation/SKILL.md +34 -0
- package/skills/keyboard-shortcuts/SKILL.md +34 -0
- package/skills/knowledge-base/SKILL.md +34 -0
- package/skills/knowledge-graph/SKILL.md +34 -0
- package/skills/kubernetes-deployment/SKILL.md +34 -0
- package/skills/language-translation/SKILL.md +34 -0
- package/skills/llm-chaining/SKILL.md +34 -0
- package/skills/localization-i18n/SKILL.md +34 -0
- package/skills/logging-best-practices/SKILL.md +34 -0
- package/skills/machine-learning-pipeline/SKILL.md +34 -0
- package/skills/meeting-notes/SKILL.md +34 -0
- package/skills/meeting-scheduler/SKILL.md +34 -0
- package/skills/message-queues/SKILL.md +34 -0
- package/skills/message-summarization/SKILL.md +34 -0
- package/skills/microservices-architecture/SKILL.md +34 -0
- package/skills/milestone-tracking/SKILL.md +34 -0
- package/skills/model-deployment/SKILL.md +34 -0
- package/skills/model-evaluation/SKILL.md +34 -0
- package/skills/monitoring-setup/SKILL.md +34 -0
- package/skills/monorepo-setup/SKILL.md +34 -0
- package/skills/multi-agent-systems/SKILL.md +34 -0
- package/skills/network-troubleshooting/SKILL.md +34 -0
- package/skills/note-organization/SKILL.md +34 -0
- package/skills/package-publishing/SKILL.md +34 -0
- package/skills/password-generator/SKILL.md +34 -0
- package/skills/pdf-processing/SKILL.md +34 -0
- package/skills/performance-optimization/SKILL.md +34 -0
- package/skills/pomodoro-timer/SKILL.md +34 -0
- package/skills/powershell-automation/SKILL.md +34 -0
- package/skills/priority-filtering/SKILL.md +34 -0
- package/skills/project-tracking/SKILL.md +34 -0
- package/skills/prompt-compression/SKILL.md +34 -0
- package/skills/prompt-engineering/SKILL.md +34 -0
- package/skills/push-notifications/SKILL.md +34 -0
- package/skills/rag-implementation/SKILL.md +34 -0
- package/skills/rate-limiting/SKILL.md +34 -0
- package/skills/real-time-communication/SKILL.md +34 -0
- package/skills/refactoring-strategy/SKILL.md +34 -0
- package/skills/regex-mastery/SKILL.md +34 -0
- package/skills/report-generation/SKILL.md +34 -0
- package/skills/research-compiler/SKILL.md +34 -0
- package/skills/responsive-design/SKILL.md +34 -0
- package/skills/rest-api-patterns/SKILL.md +34 -0
- package/skills/retrospectives/SKILL.md +34 -0
- package/skills/risk-assessment/SKILL.md +34 -0
- package/skills/roadmap-creation/SKILL.md +34 -0
- package/skills/search-implementation/SKILL.md +34 -0
- package/skills/secrets-management/SKILL.md +34 -0
- package/skills/security-audit/SKILL.md +34 -0
- package/skills/selective-memory/SKILL.md +34 -0
- package/skills/semantic-search/SKILL.md +34 -0
- package/skills/semantic-versioning/SKILL.md +34 -0
- package/skills/sentiment-analysis/SKILL.md +34 -0
- package/skills/seo-optimization/SKILL.md +34 -0
- package/skills/snippet-manager/SKILL.md +34 -0
- package/skills/sprint-planning/SKILL.md +34 -0
- package/skills/sql-query-optimization/SKILL.md +34 -0
- package/skills/stakeholder-communication/SKILL.md +34 -0
- package/skills/state-management/SKILL.md +34 -0
- package/skills/statistical-analysis/SKILL.md +34 -0
- package/skills/streaming-responses/SKILL.md +34 -0
- package/skills/structured-output/SKILL.md +34 -0
- package/skills/summarization-techniques/SKILL.md +34 -0
- package/skills/system-diagnostics/SKILL.md +34 -0
- package/skills/task-decomposition/SKILL.md +34 -0
- package/skills/task-prioritization/SKILL.md +34 -0
- package/skills/template-library/SKILL.md +34 -0
- package/skills/test-generation/SKILL.md +34 -0
- package/skills/testing-strategy/SKILL.md +34 -0
- package/skills/text-expansion/SKILL.md +34 -0
- package/skills/time-blocking/SKILL.md +34 -0
- package/skills/token-budgeting/SKILL.md +34 -0
- package/skills/token-optimization/SKILL.md +34 -0
- package/skills/tool-calling-patterns/SKILL.md +34 -0
- package/skills/vector-database-setup/SKILL.md +34 -0
- package/skills/video-editing/SKILL.md +34 -0
- package/skills/visualization-creation/SKILL.md +34 -0
- package/skills/web-scraping/SKILL.md +34 -0
- package/skills/webhook-handling/SKILL.md +34 -0
- package/skills/weekly-review/SKILL.md +34 -0
- package/skills/workflow-automation/SKILL.md +34 -0
- package/src/commands/code_v5.js +377 -73
- package/src/commands/help.js +3 -1
- package/src/commands/naturehub.js +105 -55
- package/src/commands/repl.js +506 -87
- package/src/providers/model/anthropic.js +84 -0
- package/src/providers/model/gemini.js +49 -0
- package/src/providers/model/minimax.js +48 -0
- package/src/providers/model/ollama.js +42 -0
- package/src/providers/model/openai.js +49 -0
- package/src/providers/search/duckduckgo.js +45 -0
- package/src/providers/search/exa.js +52 -0
- package/src/providers/search/searxng.js +51 -0
- package/src/providers/search/tavily.js +47 -0
- package/src/tools/model_provider.js +71 -0
- package/src/tools/parallel_search.js +40 -49
- package/src/tools/search_provider.js +79 -0
- package/src/tools/web_search.js +42 -62
- package/src/tools/workflow.js +27 -10
- package/src/utils/cron.js +82 -126
- package/src/utils/effort-levels.js +42 -0
- package/src/utils/fallback-chain.js +72 -0
- package/src/utils/file-history.js +78 -0
- package/src/utils/model-provider.js +113 -0
- package/src/utils/permissions.js +147 -0
- package/src/utils/plan-mode.js +164 -0
- package/src/utils/provider-detect.js +7 -32
- package/src/utils/sandbox.js +60 -0
- package/src/utils/search-provider.js +62 -0
- package/src/utils/session-search.js +66 -0
- package/src/utils/structured-output.js +20 -0
- package/src/utils/system-prompt.js +28 -2
- package/src/utils/tasks.js +132 -0
- package/src/utils/tool-hooks.js +154 -0
- package/src/utils/tools.js +16 -0
- package/src/utils/ultra-review.js +59 -0
- package/src/utils/worktree.js +192 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
const loadConfig = () => {
|
|
2
|
+
try { return JSON.parse(require('fs').readFileSync(require('path').join(require('os').homedir(), '.natureco', 'config.json'), 'utf8')); } catch { return {}; }
|
|
3
|
+
};
|
|
4
|
+
|
|
5
|
+
require('../providers/search/tavily');
|
|
6
|
+
require('../providers/search/duckduckgo');
|
|
7
|
+
require('../providers/search/searxng');
|
|
8
|
+
require('../providers/search/exa');
|
|
9
|
+
|
|
10
|
+
const name = 'search_provider';
|
|
11
|
+
const description = 'Unified search backend with pluggable providers. search/status/switch/list. Supports Tavily, DuckDuckGo, SearXNG, Exa. Switch via NATURECO_SEARCH_PROVIDER env or config.searchProvider. Default: tavily (falls back to others if no API key).';
|
|
12
|
+
const parameters = {
|
|
13
|
+
type: 'object',
|
|
14
|
+
properties: {
|
|
15
|
+
action: {
|
|
16
|
+
type: 'string',
|
|
17
|
+
enum: ['search', 'status', 'list', 'switch'],
|
|
18
|
+
description: 'Operation: search, status, list, or switch',
|
|
19
|
+
},
|
|
20
|
+
query: { type: 'string', description: '(search) Search query' },
|
|
21
|
+
maxResults: { type: 'number', description: '(search) Max results (default: 5)' },
|
|
22
|
+
provider: { type: 'string', description: '(search/switch) Provider name to use or switch to' },
|
|
23
|
+
},
|
|
24
|
+
required: ['action'],
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
async function execute(params) {
|
|
28
|
+
const cfg = loadConfig();
|
|
29
|
+
const { getActiveProvider, getProviderNames, resolveProviderConfig, getProvider } = require('../utils/search-provider');
|
|
30
|
+
|
|
31
|
+
switch (params.action) {
|
|
32
|
+
case 'search': {
|
|
33
|
+
if (!params.query) return { success: false, error: 'query gerekli' };
|
|
34
|
+
const providerName = params.provider || process.env.NATURECO_SEARCH_PROVIDER || cfg.searchProvider;
|
|
35
|
+
let Provider;
|
|
36
|
+
if (providerName) Provider = getProvider(providerName);
|
|
37
|
+
if (!Provider) Provider = getActiveProvider(cfg);
|
|
38
|
+
const provider = new Provider(cfg);
|
|
39
|
+
return provider.search(params.query, { maxResults: params.maxResults || 5 });
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
case 'status': {
|
|
43
|
+
const active = resolveProviderConfig(cfg);
|
|
44
|
+
const avail = getProviderNames().sort();
|
|
45
|
+
return {
|
|
46
|
+
active,
|
|
47
|
+
available: avail,
|
|
48
|
+
config: cfg.searchProvider || '(not set, using default)',
|
|
49
|
+
env: process.env.NATURECO_SEARCH_PROVIDER || '(not set)',
|
|
50
|
+
default: 'tavily',
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
case 'list': {
|
|
55
|
+
const avail = getProviderNames().sort();
|
|
56
|
+
return {
|
|
57
|
+
success: true,
|
|
58
|
+
providers: avail.map(name => {
|
|
59
|
+
const Provider = getProvider(name);
|
|
60
|
+
const inst = new Provider(cfg);
|
|
61
|
+
const hasKey = name === 'duckduckgo' || name === 'searxng' || !!(cfg[name + 'ApiKey'] || process.env[name.toUpperCase() + '_API_KEY']);
|
|
62
|
+
return { name, available: hasKey };
|
|
63
|
+
}),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
case 'switch': {
|
|
68
|
+
if (!params.provider) return { success: false, error: 'provider (name) gerekli' };
|
|
69
|
+
const { setConfigValue } = require('../utils/config');
|
|
70
|
+
setConfigValue('searchProvider', params.provider);
|
|
71
|
+
return { success: true, message: `Search provider switched to: ${params.provider}` };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
default:
|
|
75
|
+
return { success: false, error: `Unknown action: ${params.action}` };
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
module.exports = { name, description, parameters, execute };
|
package/src/tools/web_search.js
CHANGED
|
@@ -1,82 +1,62 @@
|
|
|
1
1
|
const { getConfig } = require('../utils/config');
|
|
2
|
+
const { getActiveProvider, getProviderNames, DEFAULT_PROVIDER } = require('../utils/search-provider');
|
|
3
|
+
require('../providers/search/tavily');
|
|
4
|
+
require('../providers/search/duckduckgo');
|
|
5
|
+
require('../providers/search/searxng');
|
|
6
|
+
require('../providers/search/exa');
|
|
2
7
|
|
|
3
8
|
module.exports = {
|
|
4
9
|
name: 'web_search',
|
|
5
|
-
description: 'Search the web for current information using Tavily',
|
|
10
|
+
description: 'Search the web for current information using the configured search provider (Tavily, DuckDuckGo, SearXNG, Exa). Switch via NATURECO_SEARCH_PROVIDER env or config.searchProvider.',
|
|
6
11
|
inputSchema: {
|
|
7
12
|
type: 'object',
|
|
8
13
|
properties: {
|
|
9
|
-
query: {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
14
|
+
query: { type: 'string', description: 'Search query' },
|
|
15
|
+
maxResults: { type: 'number', description: 'Maximum results (default: 5)' },
|
|
16
|
+
provider: { type: 'string', description: 'Override provider: tavily, duckduckgo, searxng, exa (default: configured or tavily)' },
|
|
13
17
|
},
|
|
14
18
|
required: ['query']
|
|
15
19
|
},
|
|
16
|
-
|
|
20
|
+
|
|
17
21
|
async execute(params) {
|
|
18
22
|
try {
|
|
19
23
|
const config = getConfig();
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const response = await fetch('https://api.tavily.com/search', {
|
|
30
|
-
method: 'POST',
|
|
31
|
-
headers: { 'Content-Type': 'application/json' },
|
|
32
|
-
body: JSON.stringify({
|
|
33
|
-
api_key: apiKey,
|
|
34
|
-
query: params.query,
|
|
35
|
-
search_depth: 'basic',
|
|
36
|
-
max_results: 5
|
|
37
|
-
})
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
if (!response.ok) {
|
|
41
|
-
return {
|
|
42
|
-
success: false,
|
|
43
|
-
error: `Tavily API error: ${response.status} ${response.statusText}`
|
|
44
|
-
};
|
|
24
|
+
const providerName = params.provider || process.env.NATURECO_SEARCH_PROVIDER || config.searchProvider;
|
|
25
|
+
let Provider;
|
|
26
|
+
|
|
27
|
+
if (providerName) {
|
|
28
|
+
const { getProvider } = require('../utils/search-provider');
|
|
29
|
+
Provider = getProvider(providerName);
|
|
45
30
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
if (data.error) {
|
|
50
|
-
return {
|
|
51
|
-
success: false,
|
|
52
|
-
error: data.error
|
|
53
|
-
};
|
|
31
|
+
if (!Provider) {
|
|
32
|
+
Provider = getActiveProvider(config);
|
|
54
33
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
34
|
+
|
|
35
|
+
const provider = new Provider(config);
|
|
36
|
+
const result = await provider.search(params.query, {
|
|
37
|
+
maxResults: params.maxResults || 5,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
if (!result.success && providerName && !params.provider) {
|
|
41
|
+
const allProviders = getProviderNames();
|
|
42
|
+
for (const name of allProviders) {
|
|
43
|
+
if (name === providerName) continue;
|
|
44
|
+
const { getProvider } = require('../utils/search-provider');
|
|
45
|
+
const FallbackProvider = getProvider(name);
|
|
46
|
+
if (!FallbackProvider) continue;
|
|
47
|
+
const fallback = new FallbackProvider(config);
|
|
48
|
+
const fallbackResult = await fallback.search(params.query, { maxResults: params.maxResults || 5 });
|
|
49
|
+
if (fallbackResult.success) {
|
|
50
|
+
fallbackResult.fallback = true;
|
|
51
|
+
fallbackResult.fallbackReason = result.error;
|
|
52
|
+
return fallbackResult;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
63
55
|
}
|
|
64
|
-
|
|
65
|
-
return
|
|
66
|
-
success: true,
|
|
67
|
-
query: params.query,
|
|
68
|
-
results: data.results.map(r => ({
|
|
69
|
-
title: r.title,
|
|
70
|
-
snippet: r.content,
|
|
71
|
-
url: r.url
|
|
72
|
-
})),
|
|
73
|
-
count: data.results.length
|
|
74
|
-
};
|
|
56
|
+
|
|
57
|
+
return result;
|
|
75
58
|
} catch (error) {
|
|
76
|
-
return {
|
|
77
|
-
success: false,
|
|
78
|
-
error: error.message
|
|
79
|
-
};
|
|
59
|
+
return { success: false, error: error.message };
|
|
80
60
|
}
|
|
81
61
|
}
|
|
82
62
|
};
|
package/src/tools/workflow.js
CHANGED
|
@@ -6,6 +6,8 @@ const os = require('os');
|
|
|
6
6
|
const WORKFLOW_DIR = path.join(os.homedir(), '.natureco', 'workflows');
|
|
7
7
|
const WORKFLOW_HISTORY = path.join(os.homedir(), '.natureco', 'workflow-history.json');
|
|
8
8
|
|
|
9
|
+
const { buildSkillIndex } = require('../utils/skill-index');
|
|
10
|
+
|
|
9
11
|
function ensureDir(dir) { try { fs.mkdirSync(dir, { recursive: true }); } catch {} }
|
|
10
12
|
function loadConfig() {
|
|
11
13
|
try { return JSON.parse(fs.readFileSync(path.join(os.homedir(), '.natureco', 'config.json'), 'utf8')); } catch { return {}; }
|
|
@@ -94,6 +96,8 @@ async function workflow(params) {
|
|
|
94
96
|
return { success: false, error: 'Provider ayarli degil. Once: natureco setup' };
|
|
95
97
|
}
|
|
96
98
|
|
|
99
|
+
const skillsIndexBlock = buildSkillIndex();
|
|
100
|
+
|
|
97
101
|
// ── RUN: Execute a complete workflow ──────────────────────────────────
|
|
98
102
|
if (action === 'run') {
|
|
99
103
|
if (!task) return { success: false, error: 'task gerekli' };
|
|
@@ -114,7 +118,7 @@ async function workflow(params) {
|
|
|
114
118
|
if (isSimple) {
|
|
115
119
|
// Passthrough: just chat with LLM, no tools — include user memory
|
|
116
120
|
const memCtx = memoryContext();
|
|
117
|
-
const sysMsg = 'Sen yardimci bir asistansin. Kisa ve oz yanit ver.' + (memCtx ? '\n\nKullanici bilgisi:\n' + memCtx : '');
|
|
121
|
+
const sysMsg = 'Sen yardimci bir asistansin. Kisa ve oz yanit ver.' + (memCtx ? '\n\nKullanici bilgisi:\n' + memCtx : '') + '\n\n' + skillsIndexBlock;
|
|
118
122
|
const chatBody = { model, stream: false, messages: [{ role: 'system', content: sysMsg }, { role: 'user', content: task }], temperature: 0.7, max_tokens: 1000 };
|
|
119
123
|
try {
|
|
120
124
|
const chatResult = await apiCall(providerUrl, providerApiKey, chatBody);
|
|
@@ -127,12 +131,13 @@ async function workflow(params) {
|
|
|
127
131
|
|
|
128
132
|
// Phase 1: LLM plans the workflow
|
|
129
133
|
const memCtx = memoryContext();
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
134
|
+
const planPrompt = {
|
|
135
|
+
role: 'system',
|
|
136
|
+
content: 'Sen bir workflow planlama asistanisin. Verilen gorev icin hangi tool\'larin kullanilacagini ve hangi sirayla calisacagini belirle. SADECE JSON formatinda yanit ver, baska bir sey yazma.\n\nKullanilabilir tool\'lar:\n' +
|
|
137
|
+
tools.map(t => '- ' + t).join('\n') +
|
|
138
|
+
'\n\nKullanilabilir skill\'ler (gorevle ilgili skill varsa plana skill_view adimi olarak ekle):\n' + skillsIndexBlock +
|
|
139
|
+
(memCtx ? '\n\nKullanici bilgisi:\n' + memCtx : '') +
|
|
140
|
+
'\n\nJSON format:\n{\n "workflowName": "...",\n "description": "...",\n "skillsLoaded": ["..."],\n "steps": [\n { "step": 1, "tool": "tool_name", "purpose": "...", "params": { ... } }\n ]\n}\n\nHer adim icin params kismina tool\'un gerektirdigi parametreleri ekle. Adimlar birbirine bagimli olabilir, onceki adimin outputu sonraki adimin inputu olarak kullanilabilir.\n\nGorevle ilgili skill varsa ILK adim olarak skill_view ile yukle, ardindan diger adimlara gec.'
|
|
136
141
|
};
|
|
137
142
|
const planBody = {
|
|
138
143
|
model, stream: false,
|
|
@@ -172,7 +177,12 @@ async function workflow(params) {
|
|
|
172
177
|
plan = JSON.parse(jsonStr);
|
|
173
178
|
if (!plan.steps || !Array.isArray(plan.steps)) throw new Error('Steps bulunamadi');
|
|
174
179
|
} catch (e) {
|
|
175
|
-
|
|
180
|
+
// JSON parse hatasi — modelin ham ciktisini passthrough chat olarak goster
|
|
181
|
+
const rawReply = planResult.choices?.[0]?.message?.content || '';
|
|
182
|
+
if (rawReply) {
|
|
183
|
+
return { success: true, workflowId: 'passthrough', name: 'Direct Chat', status: 'completed', totalSteps: 0, completedSteps: 0, results: [{ step: 0, tool: 'chat', status: 'done', result: { reply: rawReply } }], passthrough: true, reply: rawReply };
|
|
184
|
+
}
|
|
185
|
+
return { success: false, error: 'Plan cozumlenemedi: ' + e.message, raw: rawReply.slice(0, 500) };
|
|
176
186
|
}
|
|
177
187
|
|
|
178
188
|
// Save plan
|
|
@@ -203,7 +213,8 @@ async function workflow(params) {
|
|
|
203
213
|
role: 'system',
|
|
204
214
|
content: 'Bir sonraki adimi calistiriyorsun. Sana verilen tool\'u ve parametreleri kullanarak islemi gerceklestir. Tool cagrisini dogru formatta yap.\n\nTool: ' + step.tool + '\nAmac: ' + (step.purpose || '') + '\nPlanlanan parametreler: ' + JSON.stringify(step.params || {}) +
|
|
205
215
|
'\n\nOnceki adim sonuclari:\n' + stepResults.map(r => 'Adim ' + r.step + ' (' + r.tool + '): ' + (r.status === 'done' ? JSON.stringify(r.result).slice(0, 300) : r.status)).join('\n') +
|
|
206
|
-
'\n\
|
|
216
|
+
'\n\nKullanilabilir skill\'ler:\n' + skillsIndexBlock +
|
|
217
|
+
'\n\nTek bir tool cagrisi yap ve sonucu bekle. Tool cagrisi yaparken Onceki adim sonuclarindaki gerekli verileri parametre olarak kullan. Ilgili skill varsa once skill_view ile yukle, sonra asil tool\'u cagir.'
|
|
207
218
|
};
|
|
208
219
|
const executeBody = {
|
|
209
220
|
model, stream: false,
|
|
@@ -251,6 +262,10 @@ async function workflow(params) {
|
|
|
251
262
|
history.unshift({ id: wfId, name: plan.workflowName || task.slice(0, 50), task, status: wfEntry.status, steps: plan.steps.length, completedAt: wfEntry.completedAt });
|
|
252
263
|
fs.writeFileSync(WORKFLOW_HISTORY, JSON.stringify(history.slice(0, 50), null, 2));
|
|
253
264
|
|
|
265
|
+
const skillsLoaded = stepResults
|
|
266
|
+
.filter(r => r.tool === 'skill_view' && r.status === 'done')
|
|
267
|
+
.map(r => r.args?.name || 'bilinmeyen-skill');
|
|
268
|
+
|
|
254
269
|
return {
|
|
255
270
|
success: true,
|
|
256
271
|
workflowId: wfId,
|
|
@@ -260,6 +275,7 @@ async function workflow(params) {
|
|
|
260
275
|
completedSteps: stepResults.filter(r => r.status === 'done').length,
|
|
261
276
|
failedSteps: stepResults.filter(r => r.status === 'error' || r.status === 'skipped').length,
|
|
262
277
|
status: wfEntry.status,
|
|
278
|
+
skillsLoaded: skillsLoaded.length > 0 ? skillsLoaded : undefined,
|
|
263
279
|
plan: plan.steps.map(s => ({ step: s.step, tool: s.tool, purpose: s.purpose })),
|
|
264
280
|
results: stepResults.map(r => ({
|
|
265
281
|
step: r.step, tool: r.tool, status: r.status,
|
|
@@ -276,7 +292,8 @@ async function workflow(params) {
|
|
|
276
292
|
const planPrompt = {
|
|
277
293
|
role: 'system',
|
|
278
294
|
content: 'Kullanilabilir tool\'lar:\n' + tools.map(t => '- ' + t).join('\n') +
|
|
279
|
-
'\n\
|
|
295
|
+
'\n\nKullanilabilir skill\'ler:\n' + skillsIndexBlock +
|
|
296
|
+
'\n\nGorev icin bir workflow plani JSON formatinda olustur. JSON disinda hicbir sey yazma.\nFormat: { "workflowName": "...", "description": "...", "estimatedSteps": N, "skillsLoaded": ["..."], "steps": [{ "step": 1, "tool": "...", "purpose": "...", "params": {...}, "expectedOutput": "..." }] }\n\nGorevle ilgili skill varsa plana skill_view adimi olarak ekle.'
|
|
280
297
|
};
|
|
281
298
|
const planBody = {
|
|
282
299
|
model, stream: false,
|
package/src/utils/cron.js
CHANGED
|
@@ -1,126 +1,82 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
return
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
if (schedule.startsWith('daily at ')) {
|
|
87
|
-
const time = schedule.replace('daily at ', '');
|
|
88
|
-
const [hour, minute = '0'] = time.split(':');
|
|
89
|
-
return `${minute} ${hour} * * *`;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Zaten cron expression ise olduğu gibi döndür
|
|
93
|
-
return schedule;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function validateCronExpression(expression) {
|
|
97
|
-
return cron.validate(expression);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
function logCronOutput(jobId, output) {
|
|
101
|
-
const logFile = path.join(CRON_LOGS_DIR, `${jobId}.log`);
|
|
102
|
-
const timestamp = new Date().toISOString();
|
|
103
|
-
const logEntry = `[${timestamp}]\n${output}\n\n`;
|
|
104
|
-
fs.appendFileSync(logFile, logEntry, 'utf-8');
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function getCronLog(jobId) {
|
|
108
|
-
const logFile = path.join(CRON_LOGS_DIR, `${jobId}.log`);
|
|
109
|
-
if (!fs.existsSync(logFile)) {
|
|
110
|
-
return null;
|
|
111
|
-
}
|
|
112
|
-
return fs.readFileSync(logFile, 'utf-8');
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
module.exports = {
|
|
116
|
-
loadCronJobs,
|
|
117
|
-
saveCronJobs,
|
|
118
|
-
addCronJob,
|
|
119
|
-
removeCronJob,
|
|
120
|
-
updateCronJob,
|
|
121
|
-
getCronJob,
|
|
122
|
-
parseCronSchedule,
|
|
123
|
-
validateCronExpression,
|
|
124
|
-
logCronOutput,
|
|
125
|
-
getCronLog,
|
|
126
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* cron — Scheduled task monitoring (cron-like expressions)
|
|
3
|
+
*
|
|
4
|
+
* Format (standard cron): min hour dom mon dow
|
|
5
|
+
* e.g., "every 5 min" -> minute field = "star/5"
|
|
6
|
+
* Checks if a task should run based on its schedule.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const os = require('os');
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
|
|
13
|
+
const CRON_FILE = path.join(os.homedir(), '.natureco', 'cron-jobs.json');
|
|
14
|
+
|
|
15
|
+
function parseCron(expr) {
|
|
16
|
+
const parts = expr.trim().split(/\s+/);
|
|
17
|
+
if (parts.length !== 5) return null;
|
|
18
|
+
const [min, hour, dom, mon, dow] = parts;
|
|
19
|
+
return { min, hour, dom, mon, dow };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function matchesField(value, pattern) {
|
|
23
|
+
if (pattern === '*') return true;
|
|
24
|
+
if (pattern.startsWith('*/')) {
|
|
25
|
+
const step = parseInt(pattern.slice(2), 10);
|
|
26
|
+
return value % step === 0;
|
|
27
|
+
}
|
|
28
|
+
if (pattern.includes(',')) return pattern.split(',').some(p => matchesField(value, p));
|
|
29
|
+
if (pattern.includes('-')) {
|
|
30
|
+
const [lo, hi] = pattern.split('-').map(Number);
|
|
31
|
+
return value >= lo && value <= hi;
|
|
32
|
+
}
|
|
33
|
+
return parseInt(pattern, 10) === value;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function shouldRun(expr) {
|
|
37
|
+
const parsed = parseCron(expr);
|
|
38
|
+
if (!parsed) return false;
|
|
39
|
+
const now = new Date();
|
|
40
|
+
return (
|
|
41
|
+
matchesField(now.getMinutes(), parsed.min) &&
|
|
42
|
+
matchesField(now.getHours(), parsed.hour) &&
|
|
43
|
+
matchesField(now.getDate(), parsed.dom) &&
|
|
44
|
+
matchesField(now.getMonth() + 1, parsed.mon) &&
|
|
45
|
+
matchesField(now.getDay(), parsed.dow)
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function loadJobs() {
|
|
50
|
+
try {
|
|
51
|
+
if (fs.existsSync(CRON_FILE)) return JSON.parse(fs.readFileSync(CRON_FILE, 'utf8'));
|
|
52
|
+
} catch {}
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function addJob(job) {
|
|
57
|
+
const jobs = loadJobs();
|
|
58
|
+
const entry = {
|
|
59
|
+
id: `cron_${Date.now()}`,
|
|
60
|
+
schedule: job.schedule,
|
|
61
|
+
command: job.command,
|
|
62
|
+
description: job.description || '',
|
|
63
|
+
createdAt: Date.now(),
|
|
64
|
+
lastRun: null,
|
|
65
|
+
enabled: true,
|
|
66
|
+
};
|
|
67
|
+
jobs.push(entry);
|
|
68
|
+
const dir = path.dirname(CRON_FILE);
|
|
69
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
70
|
+
fs.writeFileSync(CRON_FILE, JSON.stringify(jobs, null, 2));
|
|
71
|
+
return entry;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function removeJob(id) {
|
|
75
|
+
const jobs = loadJobs().filter(j => j.id !== id);
|
|
76
|
+
const dir = path.dirname(CRON_FILE);
|
|
77
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
78
|
+
fs.writeFileSync(CRON_FILE, JSON.stringify(jobs, null, 2));
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
module.exports = { parseCron, shouldRun, loadJobs, addJob, removeJob };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* effort-levels — Effort level control (Claude Code: low/medium/high)
|
|
3
|
+
*
|
|
4
|
+
* Controls token budget, iteration limits, and tool breadth:
|
|
5
|
+
* low: quick answers, 2 tool iterations, 1024 max_tokens, minimal tools
|
|
6
|
+
* medium: balanced, 5 iterations, 2048 max_tokens, standard tools (default)
|
|
7
|
+
* high: thorough, 12 iterations, 4096 max_tokens, all tools
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const LEVELS = { low: 'low', medium: 'medium', high: 'high' };
|
|
11
|
+
|
|
12
|
+
const CONFIG = {
|
|
13
|
+
low: {
|
|
14
|
+
maxTokens: 1024,
|
|
15
|
+
maxToolIterations: 2,
|
|
16
|
+
temperature: 0.1,
|
|
17
|
+
toolBreadth: 'minimal',
|
|
18
|
+
},
|
|
19
|
+
medium: {
|
|
20
|
+
maxTokens: 2048,
|
|
21
|
+
maxToolIterations: 5,
|
|
22
|
+
temperature: 0.3,
|
|
23
|
+
toolBreadth: 'standard',
|
|
24
|
+
},
|
|
25
|
+
high: {
|
|
26
|
+
maxTokens: 4096,
|
|
27
|
+
maxToolIterations: 12,
|
|
28
|
+
temperature: 0.5,
|
|
29
|
+
toolBreadth: 'all',
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
function getLevel(cfg = {}) {
|
|
34
|
+
const level = (cfg.effort || cfg.effortLevel || 'medium').toLowerCase();
|
|
35
|
+
return LEVELS[level] || 'medium';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function getConfig(level) {
|
|
39
|
+
return CONFIG[level] || CONFIG.medium;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = { LEVELS, CONFIG, getLevel, getConfig };
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fallback-chain — Multi-model fallback chain (3 models max)
|
|
3
|
+
*
|
|
4
|
+
* Config:
|
|
5
|
+
* "models": ["gpt-4", "claude-3", "gemini-pro"]
|
|
6
|
+
* "fallback": {
|
|
7
|
+
* "enabled": true,
|
|
8
|
+
* "onError": true,
|
|
9
|
+
* "onRateLimit": true,
|
|
10
|
+
* "onTimeout": true
|
|
11
|
+
* }
|
|
12
|
+
*
|
|
13
|
+
* Returns which model to use for each turn, rotating through the chain
|
|
14
|
+
* when errors occur.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
class FallbackChain {
|
|
18
|
+
constructor() {
|
|
19
|
+
this.models = [];
|
|
20
|
+
this.currentIndex = 0;
|
|
21
|
+
this.config = { onError: true, onRateLimit: true, onTimeout: true };
|
|
22
|
+
this.fallbacksUsed = 0;
|
|
23
|
+
this.history = [];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
configure(models, config = {}) {
|
|
27
|
+
this.models = Array.isArray(models) ? models.filter(Boolean) : [];
|
|
28
|
+
this.currentIndex = 0;
|
|
29
|
+
this.fallbacksUsed = 0;
|
|
30
|
+
this.config = { ...this.config, ...config };
|
|
31
|
+
this.history = [];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
get current() {
|
|
35
|
+
return this.models[this.currentIndex] || null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
get hasFallback() {
|
|
39
|
+
return this.currentIndex < this.models.length - 1;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
get remaining() {
|
|
43
|
+
return Math.max(0, this.models.length - this.currentIndex - 1);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
get list() {
|
|
47
|
+
return [...this.models];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
recordError(model, error) {
|
|
51
|
+
this.history.push({ model, error: error.message || String(error), at: Date.now() });
|
|
52
|
+
if (this.hasFallback) {
|
|
53
|
+
this.currentIndex++;
|
|
54
|
+
this.fallbacksUsed++;
|
|
55
|
+
return { fallback: true, nextModel: this.current, from: model };
|
|
56
|
+
}
|
|
57
|
+
return { fallback: false, nextModel: null, from: model };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
reset() {
|
|
61
|
+
this.currentIndex = 0;
|
|
62
|
+
this.fallbacksUsed = 0;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
let _instance = null;
|
|
67
|
+
function getFallbackChain() {
|
|
68
|
+
if (!_instance) _instance = new FallbackChain();
|
|
69
|
+
return _instance;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
module.exports = { FallbackChain, getFallbackChain };
|