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.
Files changed (196) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/package.json +1 -1
  3. package/skills/a-b-testing/SKILL.md +34 -0
  4. package/skills/accessibility-audit/SKILL.md +34 -0
  5. package/skills/agent-memory/SKILL.md +34 -0
  6. package/skills/agent-orchestration/SKILL.md +34 -0
  7. package/skills/analytics-setup/SKILL.md +34 -0
  8. package/skills/api-design/SKILL.md +34 -0
  9. package/skills/api-versioning/SKILL.md +34 -0
  10. package/skills/audio-transcription/SKILL.md +34 -0
  11. package/skills/authentication-patterns/SKILL.md +34 -0
  12. package/skills/authorization-rbac/SKILL.md +34 -0
  13. package/skills/backup-strategy/SKILL.md +34 -0
  14. package/skills/bash-scripting/SKILL.md +34 -0
  15. package/skills/batch-api-calls/SKILL.md +34 -0
  16. package/skills/batch-processing/SKILL.md +34 -0
  17. package/skills/bulk-file-operations/SKILL.md +34 -0
  18. package/skills/cache-responses/SKILL.md +34 -0
  19. package/skills/caching-strategy/SKILL.md +34 -0
  20. package/skills/calendar-optimization/SKILL.md +34 -0
  21. package/skills/changelog-generation/SKILL.md +34 -0
  22. package/skills/chat-bot-design/SKILL.md +34 -0
  23. package/skills/chunking-strategy/SKILL.md +34 -0
  24. package/skills/ci-cd-pipeline/SKILL.md +34 -0
  25. package/skills/classification-pipeline/SKILL.md +34 -0
  26. package/skills/clipboard-master/SKILL.md +34 -0
  27. package/skills/code-explanation/SKILL.md +34 -0
  28. package/skills/code-generation/SKILL.md +34 -0
  29. package/skills/code-migration/SKILL.md +34 -0
  30. package/skills/code-review-checklist/SKILL.md +34 -0
  31. package/skills/contact-management/SKILL.md +34 -0
  32. package/skills/content-generation/SKILL.md +34 -0
  33. package/skills/context-pruning/SKILL.md +34 -0
  34. package/skills/context-window-management/SKILL.md +34 -0
  35. package/skills/continuous-improvement/SKILL.md +34 -0
  36. package/skills/cron-scheduling/SKILL.md +34 -0
  37. package/skills/cross-platform-compat/SKILL.md +34 -0
  38. package/skills/csv-processing/SKILL.md +34 -0
  39. package/skills/daily-journal/SKILL.md +34 -0
  40. package/skills/data-analysis/SKILL.md +34 -0
  41. package/skills/data-backup/SKILL.md +34 -0
  42. package/skills/data-cleaning/SKILL.md +34 -0
  43. package/skills/data-extraction/SKILL.md +34 -0
  44. package/skills/data-validation/SKILL.md +34 -0
  45. package/skills/database-migrations/SKILL.md +34 -0
  46. package/skills/database-schema-design/SKILL.md +34 -0
  47. package/skills/decision-matrix/SKILL.md +34 -0
  48. package/skills/dependency-management/SKILL.md +34 -0
  49. package/skills/dev-environment-setup/SKILL.md +34 -0
  50. package/skills/distraction-blocker/SKILL.md +34 -0
  51. package/skills/docker-optimization/SKILL.md +34 -0
  52. package/skills/document-template/SKILL.md +34 -0
  53. package/skills/documentation-gen/SKILL.md +34 -0
  54. package/skills/efficient-formatting/SKILL.md +34 -0
  55. package/skills/email-management/SKILL.md +34 -0
  56. package/skills/email-service/SKILL.md +34 -0
  57. package/skills/embedding-generation/SKILL.md +34 -0
  58. package/skills/entity-extraction/SKILL.md +34 -0
  59. package/skills/environment-config/SKILL.md +34 -0
  60. package/skills/error-handling/SKILL.md +34 -0
  61. package/skills/estimation-techniques/SKILL.md +34 -0
  62. package/skills/event-driven-architecture/SKILL.md +34 -0
  63. package/skills/excel-automation/SKILL.md +34 -0
  64. package/skills/execution-monitoring/SKILL.md +34 -0
  65. package/skills/expense-tracking/SKILL.md +34 -0
  66. package/skills/feature-engineering/SKILL.md +34 -0
  67. package/skills/feature-flags/SKILL.md +34 -0
  68. package/skills/file-conversion/SKILL.md +34 -0
  69. package/skills/file-upload-handling/SKILL.md +34 -0
  70. package/skills/fine-tuning-prep/SKILL.md +34 -0
  71. package/skills/focus-mode/SKILL.md +34 -0
  72. package/skills/form-validation/SKILL.md +34 -0
  73. package/skills/function-calling/SKILL.md +34 -0
  74. package/skills/git-workflow/SKILL.md +34 -0
  75. package/skills/goal-setting/SKILL.md +34 -0
  76. package/skills/graphql-design/SKILL.md +34 -0
  77. package/skills/habit-tracker/SKILL.md +34 -0
  78. package/skills/hallucination-detection/SKILL.md +34 -0
  79. package/skills/image-processing/SKILL.md +34 -0
  80. package/skills/json-transformation/SKILL.md +34 -0
  81. package/skills/keyboard-shortcuts/SKILL.md +34 -0
  82. package/skills/knowledge-base/SKILL.md +34 -0
  83. package/skills/knowledge-graph/SKILL.md +34 -0
  84. package/skills/kubernetes-deployment/SKILL.md +34 -0
  85. package/skills/language-translation/SKILL.md +34 -0
  86. package/skills/llm-chaining/SKILL.md +34 -0
  87. package/skills/localization-i18n/SKILL.md +34 -0
  88. package/skills/logging-best-practices/SKILL.md +34 -0
  89. package/skills/machine-learning-pipeline/SKILL.md +34 -0
  90. package/skills/meeting-notes/SKILL.md +34 -0
  91. package/skills/meeting-scheduler/SKILL.md +34 -0
  92. package/skills/message-queues/SKILL.md +34 -0
  93. package/skills/message-summarization/SKILL.md +34 -0
  94. package/skills/microservices-architecture/SKILL.md +34 -0
  95. package/skills/milestone-tracking/SKILL.md +34 -0
  96. package/skills/model-deployment/SKILL.md +34 -0
  97. package/skills/model-evaluation/SKILL.md +34 -0
  98. package/skills/monitoring-setup/SKILL.md +34 -0
  99. package/skills/monorepo-setup/SKILL.md +34 -0
  100. package/skills/multi-agent-systems/SKILL.md +34 -0
  101. package/skills/network-troubleshooting/SKILL.md +34 -0
  102. package/skills/note-organization/SKILL.md +34 -0
  103. package/skills/package-publishing/SKILL.md +34 -0
  104. package/skills/password-generator/SKILL.md +34 -0
  105. package/skills/pdf-processing/SKILL.md +34 -0
  106. package/skills/performance-optimization/SKILL.md +34 -0
  107. package/skills/pomodoro-timer/SKILL.md +34 -0
  108. package/skills/powershell-automation/SKILL.md +34 -0
  109. package/skills/priority-filtering/SKILL.md +34 -0
  110. package/skills/project-tracking/SKILL.md +34 -0
  111. package/skills/prompt-compression/SKILL.md +34 -0
  112. package/skills/prompt-engineering/SKILL.md +34 -0
  113. package/skills/push-notifications/SKILL.md +34 -0
  114. package/skills/rag-implementation/SKILL.md +34 -0
  115. package/skills/rate-limiting/SKILL.md +34 -0
  116. package/skills/real-time-communication/SKILL.md +34 -0
  117. package/skills/refactoring-strategy/SKILL.md +34 -0
  118. package/skills/regex-mastery/SKILL.md +34 -0
  119. package/skills/report-generation/SKILL.md +34 -0
  120. package/skills/research-compiler/SKILL.md +34 -0
  121. package/skills/responsive-design/SKILL.md +34 -0
  122. package/skills/rest-api-patterns/SKILL.md +34 -0
  123. package/skills/retrospectives/SKILL.md +34 -0
  124. package/skills/risk-assessment/SKILL.md +34 -0
  125. package/skills/roadmap-creation/SKILL.md +34 -0
  126. package/skills/search-implementation/SKILL.md +34 -0
  127. package/skills/secrets-management/SKILL.md +34 -0
  128. package/skills/security-audit/SKILL.md +34 -0
  129. package/skills/selective-memory/SKILL.md +34 -0
  130. package/skills/semantic-search/SKILL.md +34 -0
  131. package/skills/semantic-versioning/SKILL.md +34 -0
  132. package/skills/sentiment-analysis/SKILL.md +34 -0
  133. package/skills/seo-optimization/SKILL.md +34 -0
  134. package/skills/snippet-manager/SKILL.md +34 -0
  135. package/skills/sprint-planning/SKILL.md +34 -0
  136. package/skills/sql-query-optimization/SKILL.md +34 -0
  137. package/skills/stakeholder-communication/SKILL.md +34 -0
  138. package/skills/state-management/SKILL.md +34 -0
  139. package/skills/statistical-analysis/SKILL.md +34 -0
  140. package/skills/streaming-responses/SKILL.md +34 -0
  141. package/skills/structured-output/SKILL.md +34 -0
  142. package/skills/summarization-techniques/SKILL.md +34 -0
  143. package/skills/system-diagnostics/SKILL.md +34 -0
  144. package/skills/task-decomposition/SKILL.md +34 -0
  145. package/skills/task-prioritization/SKILL.md +34 -0
  146. package/skills/template-library/SKILL.md +34 -0
  147. package/skills/test-generation/SKILL.md +34 -0
  148. package/skills/testing-strategy/SKILL.md +34 -0
  149. package/skills/text-expansion/SKILL.md +34 -0
  150. package/skills/time-blocking/SKILL.md +34 -0
  151. package/skills/token-budgeting/SKILL.md +34 -0
  152. package/skills/token-optimization/SKILL.md +34 -0
  153. package/skills/tool-calling-patterns/SKILL.md +34 -0
  154. package/skills/vector-database-setup/SKILL.md +34 -0
  155. package/skills/video-editing/SKILL.md +34 -0
  156. package/skills/visualization-creation/SKILL.md +34 -0
  157. package/skills/web-scraping/SKILL.md +34 -0
  158. package/skills/webhook-handling/SKILL.md +34 -0
  159. package/skills/weekly-review/SKILL.md +34 -0
  160. package/skills/workflow-automation/SKILL.md +34 -0
  161. package/src/commands/code_v5.js +377 -73
  162. package/src/commands/help.js +3 -1
  163. package/src/commands/naturehub.js +105 -55
  164. package/src/commands/repl.js +506 -87
  165. package/src/providers/model/anthropic.js +84 -0
  166. package/src/providers/model/gemini.js +49 -0
  167. package/src/providers/model/minimax.js +48 -0
  168. package/src/providers/model/ollama.js +42 -0
  169. package/src/providers/model/openai.js +49 -0
  170. package/src/providers/search/duckduckgo.js +45 -0
  171. package/src/providers/search/exa.js +52 -0
  172. package/src/providers/search/searxng.js +51 -0
  173. package/src/providers/search/tavily.js +47 -0
  174. package/src/tools/model_provider.js +71 -0
  175. package/src/tools/parallel_search.js +40 -49
  176. package/src/tools/search_provider.js +79 -0
  177. package/src/tools/web_search.js +42 -62
  178. package/src/tools/workflow.js +27 -10
  179. package/src/utils/cron.js +82 -126
  180. package/src/utils/effort-levels.js +42 -0
  181. package/src/utils/fallback-chain.js +72 -0
  182. package/src/utils/file-history.js +78 -0
  183. package/src/utils/model-provider.js +113 -0
  184. package/src/utils/permissions.js +147 -0
  185. package/src/utils/plan-mode.js +164 -0
  186. package/src/utils/provider-detect.js +7 -32
  187. package/src/utils/sandbox.js +60 -0
  188. package/src/utils/search-provider.js +62 -0
  189. package/src/utils/session-search.js +66 -0
  190. package/src/utils/structured-output.js +20 -0
  191. package/src/utils/system-prompt.js +28 -2
  192. package/src/utils/tasks.js +132 -0
  193. package/src/utils/tool-hooks.js +154 -0
  194. package/src/utils/tools.js +16 -0
  195. package/src/utils/ultra-review.js +59 -0
  196. 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 };
@@ -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
- type: 'string',
11
- description: 'Search query'
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 apiKey = config.tavilyApiKey || process.env.TAVILY_API_KEY;
21
-
22
- if (!apiKey) {
23
- return {
24
- success: false,
25
- error: 'Tavily API key gerekli. Kur: natureco config set tavilyApiKey tvly_xxx\nÜcretsiz key: https://tavily.com'
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
- const data = await response.json();
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
- if (!data.results || data.results.length === 0) {
57
- return {
58
- success: true,
59
- message: 'Sonuç bulunamadı',
60
- query: params.query,
61
- results: []
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
  };
@@ -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
- const planPrompt = {
131
- role: 'system',
132
- 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' +
133
- tools.map(t => '- ' + t).join('\n') +
134
- (memCtx ? '\n\nKullanici bilgisi:\n' + memCtx : '') +
135
- '\n\nJSON format:\n{\n "workflowName": "...",\n "description": "...",\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.'
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
- return { success: false, error: 'Plan cozumlenemedi: ' + e.message, raw: planResult.choices?.[0]?.message?.content?.slice(0, 500) };
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\nTek bir tool cagrisi yap ve sonucu bekle. Tool cagrisi yaparken Onceki adim sonuclarindaki gerekli verileri parametre olarak kullan.'
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\nGorev icin bir workflow plani JSON formatinda olustur. JSON disinda hicbir sey yazma.\nFormat: { "workflowName": "...", "description": "...", "estimatedSteps": N, "steps": [{ "step": 1, "tool": "...", "purpose": "...", "params": {...}, "expectedOutput": "..." }] }'
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
- const fs = require('fs');
2
- const path = require('path');
3
- const os = require('os');
4
- const cron = require('node-cron');
5
-
6
- const CRON_FILE = path.join(os.homedir(), '.natureco', 'cron.json');
7
- const CRON_LOGS_DIR = path.join(os.homedir(), '.natureco', 'cron-logs');
8
-
9
- function ensureCronDirs() {
10
- const dir = path.dirname(CRON_FILE);
11
- if (!fs.existsSync(dir)) {
12
- fs.mkdirSync(dir, { recursive: true });
13
- }
14
- if (!fs.existsSync(CRON_LOGS_DIR)) {
15
- fs.mkdirSync(CRON_LOGS_DIR, { recursive: true });
16
- }
17
- }
18
-
19
- function loadCronJobs() {
20
- ensureCronDirs();
21
- if (!fs.existsSync(CRON_FILE)) {
22
- return [];
23
- }
24
- try {
25
- const data = fs.readFileSync(CRON_FILE, 'utf-8');
26
- return JSON.parse(data);
27
- } catch {
28
- return [];
29
- }
30
- }
31
-
32
- function saveCronJobs(jobs) {
33
- ensureCronDirs();
34
- fs.writeFileSync(CRON_FILE, JSON.stringify(jobs, null, 2), 'utf-8');
35
- }
36
-
37
- function addCronJob(job) {
38
- const jobs = loadCronJobs();
39
- const id = Date.now().toString(36) + Math.random().toString(36).slice(2);
40
- const newJob = {
41
- id,
42
- ...job,
43
- enabled: true,
44
- createdAt: new Date().toISOString(),
45
- };
46
- jobs.push(newJob);
47
- saveCronJobs(jobs);
48
- return newJob;
49
- }
50
-
51
- function removeCronJob(id) {
52
- const jobs = loadCronJobs();
53
- const filtered = jobs.filter(j => j.id !== id);
54
- saveCronJobs(filtered);
55
- return filtered.length < jobs.length;
56
- }
57
-
58
- function updateCronJob(id, updates) {
59
- const jobs = loadCronJobs();
60
- const index = jobs.findIndex(j => j.id === id);
61
- if (index === -1) return false;
62
- jobs[index] = { ...jobs[index], ...updates };
63
- saveCronJobs(jobs);
64
- return true;
65
- }
66
-
67
- function getCronJob(id) {
68
- const jobs = loadCronJobs();
69
- return jobs.find(j => j.id === id);
70
- }
71
-
72
- function parseCronSchedule(schedule) {
73
- // Basit zamanlama formatlarını cron expression'a çevir
74
- if (schedule.startsWith('every ')) {
75
- const parts = schedule.split(' ');
76
- if (parts[2] === 'hours') {
77
- const hours = parseInt(parts[1]);
78
- return `0 */${hours} * * *`;
79
- }
80
- if (parts[2] === 'minutes') {
81
- const minutes = parseInt(parts[1]);
82
- return `*/${minutes} * * * *`;
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 };