natureco-cli 5.19.0 → 5.20.0

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 (194) 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/repl.js +505 -86
  163. package/src/providers/model/anthropic.js +84 -0
  164. package/src/providers/model/gemini.js +49 -0
  165. package/src/providers/model/minimax.js +48 -0
  166. package/src/providers/model/ollama.js +42 -0
  167. package/src/providers/model/openai.js +49 -0
  168. package/src/providers/search/duckduckgo.js +45 -0
  169. package/src/providers/search/exa.js +52 -0
  170. package/src/providers/search/searxng.js +51 -0
  171. package/src/providers/search/tavily.js +47 -0
  172. package/src/tools/model_provider.js +71 -0
  173. package/src/tools/parallel_search.js +40 -49
  174. package/src/tools/search_provider.js +79 -0
  175. package/src/tools/web_search.js +42 -62
  176. package/src/tools/workflow.js +21 -9
  177. package/src/utils/cron.js +82 -126
  178. package/src/utils/effort-levels.js +42 -0
  179. package/src/utils/fallback-chain.js +72 -0
  180. package/src/utils/file-history.js +78 -0
  181. package/src/utils/model-provider.js +113 -0
  182. package/src/utils/permissions.js +147 -0
  183. package/src/utils/plan-mode.js +164 -0
  184. package/src/utils/provider-detect.js +7 -32
  185. package/src/utils/sandbox.js +60 -0
  186. package/src/utils/search-provider.js +62 -0
  187. package/src/utils/session-search.js +66 -0
  188. package/src/utils/structured-output.js +20 -0
  189. package/src/utils/system-prompt.js +28 -2
  190. package/src/utils/tasks.js +132 -0
  191. package/src/utils/tool-hooks.js +154 -0
  192. package/src/utils/tools.js +16 -0
  193. package/src/utils/ultra-review.js +59 -0
  194. package/src/utils/worktree.js +192 -0
@@ -20,7 +20,17 @@ const readline = require("readline");
20
20
  const chalk = require("chalk");
21
21
  const tui = require("../utils/tui");
22
22
  const { getConfig } = require("../utils/config");
23
- const { loadToolDefinitions, executeTool } = require("../utils/tools");
23
+ const { loadToolDefinitions, executeTool, toOpenAIFormat } = require("../utils/tools");
24
+ const { checkPreHooks, runPostHooks, permissionSummary } = require("../utils/tool-hooks");
25
+ const { checkPermission, isApproved, markApproved, formatPermissionPrompt } = require("../utils/permissions");
26
+ const { getPlanMode } = require("../utils/plan-mode");
27
+ const { getWorktree } = require("../utils/worktree");
28
+ const { getLevel: getEffortLevel, getConfig: getEffortConfig } = require("../utils/effort-levels");
29
+ const { getResponseFormat } = require("../utils/structured-output");
30
+ const { getFallbackChain } = require("../utils/fallback-chain");
31
+ const { getTaskManager } = require("../utils/tasks");
32
+ const { buildTiers, assemble, discoverProjectRules } = require("../utils/system-prompt");
33
+ const { buildSkillIndex } = require("../utils/skill-index");
24
34
 
25
35
  const IS_MAC = os.platform() === "darwin";
26
36
  const MAX_ITERATIONS = 10;
@@ -59,23 +69,34 @@ async function apiRequest(url, key, body) {
59
69
 
60
70
  async function sendMessageWithTools(providerUrl, providerKey, model, messages, toolDefs) {
61
71
  const isMM = isMiniMax(providerUrl);
62
- const tools = toolDefs.map(t => ({
63
- type: "function",
64
- function: { name: t.name, description: t.description, parameters: t.parameters },
65
- }));
72
+ const effortLevel = getEffortLevel();
73
+ const effortCfg = getEffortConfig(effortLevel);
74
+ const fallbackChain = getFallbackChain();
75
+ const openaiTools = toOpenAIFormat(toolDefs);
66
76
  const body = {
67
- model,
77
+ model: fallbackChain.current || model,
68
78
  messages,
69
- temperature: 0.3,
70
- max_tokens: 4096,
79
+ temperature: effortCfg.temperature,
80
+ max_tokens: effortCfg.maxTokens,
71
81
  stream: false,
72
82
  };
73
- if (tools.length > 0) {
74
- body.tools = tools;
83
+ if (openaiTools.length > 0) {
84
+ body.tools = openaiTools;
75
85
  body.tool_choice = "auto";
76
86
  }
77
- const res = await apiRequest(providerUrl, providerKey, body);
78
- return res.choices?.[0]?.message || {};
87
+ const respFmt = getResponseFormat({});
88
+ if (respFmt) body.response_format = respFmt;
89
+ try {
90
+ const res = await apiRequest(providerUrl, providerKey, body);
91
+ return res.choices?.[0]?.message || {};
92
+ } catch (err) {
93
+ const fb = fallbackChain.recordError(body.model, err);
94
+ if (fb.fallback) {
95
+ console.log(tui.C.yellow(`\n ⚠ ${body.model} başarısız → ${fb.nextModel} deneniyor...\n`));
96
+ return sendMessageWithTools(providerUrl, providerKey, fb.nextModel, messages, toolDefs);
97
+ }
98
+ throw err;
99
+ }
79
100
  }
80
101
 
81
102
  function confirm(prompt) {
@@ -285,6 +306,126 @@ async function codeV5(targetPath) {
285
306
  const projectCtx = scanProject(cwd);
286
307
  const toolDefs = loadToolDefinitions();
287
308
 
309
+ // Inject virtual tools
310
+ const planMode = getPlanMode();
311
+ const wt = getWorktree();
312
+ const taskMgr = getTaskManager();
313
+
314
+ // File tracking for summary + snapshots
315
+ function trackFileChanges(toolName, args, result) {
316
+ if (toolName === 'write_file' || toolName === 'edit_file') {
317
+ const fp = args.filePath || args.path;
318
+ if (fp && result && result.success !== false) {
319
+ filesChanged++;
320
+ try {
321
+ const fh = require('../utils/file-history');
322
+ const content = fs.readFileSync(fp, 'utf8');
323
+ fh.snapshot(fp, content);
324
+ } catch {}
325
+ }
326
+ }
327
+ if (toolName === 'bash' || toolName === 'shell_command') commandsRun++;
328
+ }
329
+
330
+ function processToolCallsWithTracking(reply, config, toolDefs, messages) {
331
+ return processToolCalls(reply, config, toolDefs, messages, trackFileChanges);
332
+ }
333
+ const virtualTools = [
334
+ {
335
+ name: 'EnterPlanMode',
336
+ description: 'Switch to plan-only mode. Research and plan without making changes. Use ExitPlanMode when ready.',
337
+ parameters: { type: 'object', properties: {}, required: [] },
338
+ execute: async () => {
339
+ if (planMode.enter()) return { result: 'Plan mode activated. Use ExitPlanMode when your plan is ready.' };
340
+ return { result: 'Already in plan mode.' };
341
+ },
342
+ },
343
+ {
344
+ name: 'ExitPlanMode',
345
+ description: 'Exit plan mode and present your plan for review.',
346
+ parameters: { type: 'object', properties: { plan: { type: 'string' }, summary: { type: 'string' } }, required: ['plan'] },
347
+ execute: async (args) => {
348
+ const ok = planMode.exit(args.plan);
349
+ if (!ok) return { error: 'Not in plan mode.' };
350
+ console.log(tui.C.cyan('\n 📋 Plan sunuldu. Onay için /plan approve yazın, red için /plan reject.\n'));
351
+ return { result: `Plan submitted.\n\n${args.plan}` };
352
+ },
353
+ },
354
+ {
355
+ name: 'EnterWorktree',
356
+ description: 'Create an isolated worktree for experimental changes.',
357
+ parameters: { type: 'object', properties: { branch: { type: 'string' } } },
358
+ execute: async (args) => wt.enter(args),
359
+ },
360
+ {
361
+ name: 'ExitWorktree',
362
+ description: 'Exit the current worktree and merge changes back.',
363
+ parameters: { type: 'object', properties: { merge: { type: 'boolean' } } },
364
+ execute: async (args) => wt.exit(args),
365
+ },
366
+ {
367
+ name: 'CreateTask', description: 'Run a command in the background.',
368
+ parameters: { type: 'object', properties: { command: { type: 'string' }, timeout: { type: 'number' } }, required: ['command'] },
369
+ execute: async (args) => taskMgr.create(args.command, args),
370
+ },
371
+ {
372
+ name: 'ListTasks', description: 'List all background tasks.',
373
+ parameters: { type: 'object', properties: {} },
374
+ execute: async () => ({ tasks: taskMgr.list() }),
375
+ },
376
+ {
377
+ name: 'GetTaskResult', description: 'Get the full result of a task.',
378
+ parameters: { type: 'object', properties: { taskId: { type: 'string' } }, required: ['taskId'] },
379
+ execute: async (args) => { const t = taskMgr.get(args.taskId); return t || { error: 'Task not found' }; },
380
+ },
381
+ {
382
+ name: 'StopTask', description: 'Stop a running task.',
383
+ parameters: { type: 'object', properties: { taskId: { type: 'string' } }, required: ['taskId'] },
384
+ execute: async (args) => taskMgr.stop(args.taskId),
385
+ },
386
+ {
387
+ name: 'SearchSessions', description: 'Search past sessions.',
388
+ parameters: { type: 'object', properties: { query: { type: 'string' } }, required: ['query'] },
389
+ execute: async (args) => { const { search } = require('../utils/session-search'); return { results: search(args.query, 5) }; },
390
+ },
391
+ {
392
+ name: 'RestoreFile', description: 'Restore a file from snapshot history.',
393
+ parameters: { type: 'object', properties: { filePath: { type: 'string' }, timestamp: { type: 'number' } }, required: ['filePath'] },
394
+ execute: async (args) => { const fh = require('../utils/file-history'); const snaps = fh.getHistory(args.filePath); if (!snaps.length) return { error: 'No history' }; return fh.restore(args.filePath, args.timestamp || snaps[0].timestamp); },
395
+ },
396
+ {
397
+ name: 'FileHistory', description: 'List snapshot history for a file.',
398
+ parameters: { type: 'object', properties: { filePath: { type: 'string' } }, required: ['filePath'] },
399
+ execute: async (args) => { const fh = require('../utils/file-history'); return { snapshots: fh.getHistory(args.filePath) }; },
400
+ },
401
+ {
402
+ name: 'UltraReview', description: 'Multi-focus code review (security, style, logic, performance).',
403
+ parameters: { type: 'object', properties: { files: { type: 'array', items: { type: 'string' } }, diff: { type: 'string' } } },
404
+ execute: async (args) => {
405
+ const ur = require('../utils/ultra-review');
406
+ if (args.diff) return ur.reviewDiff(args.diff);
407
+ if (args.files) return { reviews: args.files.map(f => { try { return ur.reviewFile(f, fs.readFileSync(f, 'utf8')); } catch { return { file: f, error: 'Not found' }; } }) };
408
+ return { error: 'Specify files or diff' };
409
+ },
410
+ },
411
+ {
412
+ name: 'ScheduleTask', description: 'Schedule a recurring cron task.',
413
+ parameters: { type: 'object', properties: { schedule: { type: 'string' }, command: { type: 'string' }, description: { type: 'string' } }, required: ['schedule', 'command'] },
414
+ execute: async (args) => require('../utils/cron').addJob(args),
415
+ },
416
+ {
417
+ name: 'ListScheduledTasks', description: 'List cron tasks.',
418
+ parameters: { type: 'object', properties: {} },
419
+ execute: async () => ({ jobs: require('../utils/cron').loadJobs() }),
420
+ },
421
+ {
422
+ name: 'RemoveScheduledTask', description: 'Remove a cron task.',
423
+ parameters: { type: 'object', properties: { id: { type: 'string' } }, required: ['id'] },
424
+ execute: async (args) => { require('../utils/cron').removeJob(args.id); return { removed: true }; },
425
+ },
426
+ ];
427
+ toolDefs.push(...virtualTools);
428
+
288
429
  // Header / Welcome
289
430
  console.log("");
290
431
  console.log(tui.styled(" ╭" + "─".repeat(58) + "╮", { color: tui.PALETTE.primary }));
@@ -308,19 +449,26 @@ async function codeV5(targetPath) {
308
449
  }
309
450
 
310
451
  console.log("\n " + tui.C.muted("Komutlar:"));
452
+ console.log(" " + tui.C.amber("/plan on|approve|reject|show") + tui.C.muted(" — Plan modu"));
311
453
  console.log(" " + tui.C.amber("/summary") + tui.C.muted(" — Oturum özeti"));
312
454
  console.log(" " + tui.C.amber("/done") + tui.C.muted(" — Özet + çıkış"));
313
455
  console.log(" " + tui.C.amber("Ctrl+C") + tui.C.muted(" — Çıkış"));
314
456
  console.log("");
315
457
 
316
- // System prompt
317
- const systemPrompt = [
318
- "Sen NatureCo Code Agent v5'sin — Claude Code alternatifi bir terminal AI asistanısın.",
319
- "47 tool'un var: dosya okuma/yazma, bash, Python, web search, macOS native, image generation, TTS, vs.",
320
- "Kullanıcının isteğini analiz et, uygun tool'ları kullan, sonuçları özetle.",
321
- "Türkçe cevap ver.",
322
- "Kullanıcıdan onay almadan dosya yazma veya tehlikeli komut çalıştırma.",
323
- ].join(" ");
458
+ // Three-tier system prompt (Hermes-style)
459
+ const skillsIndexBlock = buildSkillIndex();
460
+ const cfg = getConfig();
461
+ const projectRules = discoverProjectRules(cwd);
462
+ const promptOpts = {
463
+ botName: 'Code Agent',
464
+ userName: cfg.userName || 'kullanıcı',
465
+ skillsIndexBlock,
466
+ projectRules,
467
+ hasHistory: false,
468
+ userHome: os.homedir(),
469
+ };
470
+ const { stable, context, volatile } = buildTiers(promptOpts);
471
+ const systemPrompt = assemble(stable, context, volatile);
324
472
 
325
473
  let messages = [{ role: "system", content: systemPrompt }];
326
474
  let totalIn = 0, totalOut = 0;
@@ -337,83 +485,133 @@ async function codeV5(targetPath) {
337
485
  if (!input) { process.stdout.write(" " + tui.styled("You ", { color: tui.PALETTE.primary, bold: true })); return ask(); }
338
486
  if (input === "/summary") {
339
487
  printSummary(filesChanged, commandsRun, messages.length - 1, startTime);
340
- // v5.6.22: /summary sadece gosterir, cikmaz
341
488
  process.stdout.write("\n " + tui.styled("You ", { color: tui.PALETTE.primary, bold: true }));
342
489
  return ask();
343
490
  }
344
491
  if (input === "/done" || input === "exit" || input === "quit") {
345
- // v5.6.22: /done cikinca summary goster
346
492
  printSummary(filesChanged, commandsRun, messages.length - 1, startTime);
347
493
  rl.close();
348
494
  return;
349
495
  }
496
+ if (input.startsWith("/plan ")) {
497
+ const arg = input.slice(6).trim();
498
+ const pm = getPlanMode();
499
+ if (arg === "on" || arg === "enter") {
500
+ if (pm.enter()) console.log(tui.C.cyan('\n 📋 Plan modu aktif.\n'));
501
+ else console.log(tui.C.yellow(' Zaten plan modunda.'));
502
+ } else if (arg === "approve") {
503
+ if (pm.inReview()) { pm.approve(); console.log(tui.C.green(' ✓ Plan onaylandı.\n')); }
504
+ else console.log(tui.C.yellow(' Onay bekleyen plan yok.'));
505
+ } else if (arg === "reject") {
506
+ if (pm.inReview()) { pm.reject(); console.log(tui.C.amber(' ⨯ Plan reddedildi.\n')); }
507
+ else console.log(tui.C.yellow(' Onay bekleyen plan yok.'));
508
+ } else if (arg === "show") {
509
+ if (pm.planHistory.length > 0) console.log(tui.C.cyan('\n 📋 Son Plan:\n ' + pm.planHistory[pm.planHistory.length - 1].plan.replace(/\n/g, '\n ') + '\n'));
510
+ else console.log(tui.C.yellow(' Henüz plan yok.'));
511
+ } else {
512
+ console.log(tui.C.yellow(' Kullanım: /plan on|approve|reject|show'));
513
+ }
514
+ process.stdout.write("\n " + tui.styled("You ", { color: tui.PALETTE.primary, bold: true }));
515
+ return ask();
516
+ }
517
+
518
+ // Workflow step — run before every request
519
+ process.stdout.write(tui.styled('\r 🔧 workflow... ', { color: tui.PALETTE.muted }));
520
+ const wfResult = await executeTool('workflow', { action: 'run', task: input }, toolDefs);
521
+ const wf = wfResult?.result || {};
522
+ if (wf.success !== false) {
523
+ const loaded = wf.skillsLoaded && wf.skillsLoaded.length > 0 ? ` [skill: ${wf.skillsLoaded.join(', ')}]` : '';
524
+ process.stdout.write(tui.styled(` ✓ workflow${loaded}\n`, { color: tui.PALETTE.success }));
525
+ } else {
526
+ process.stdout.write(tui.styled(' ✗ workflow\n', { color: tui.PALETTE.danger }));
527
+ }
350
528
 
351
529
  messages.push({ role: "user", content: input });
352
- process.stdout.write("\n " + tui.styled("AI ", { color: tui.PALETTE.secondary, bold: true }));
353
530
 
354
- // Multi-turn tool loop
355
- let iter = 0;
356
- while (iter < MAX_ITERATIONS) {
357
- iter++;
531
+ if (wf.passthrough && wf.reply !== undefined && wf.reply !== null) {
532
+ // Simple chat — workflow handled it directly
533
+ const fullReply = String(wf.reply);
534
+ process.stdout.write('\n' + fullReply + '\n');
535
+ messages.push({ role: 'assistant', content: fullReply });
536
+ totalIn += Math.ceil(input.length / 4);
537
+ totalOut += Math.ceil(fullReply.length / 4);
538
+ } else if (wf.status === 'completed' || (wf.results && wf.results.length > 0)) {
539
+ // Complex task — inject workflow report as context, then LLM crafts final reply
540
+ const workflowSteps = wf.results || [];
541
+ const report = workflowSteps.map(r => {
542
+ const t = r.tool || r.name || '?';
543
+ const s = r.status === 'done' ? '✓' : '✗';
544
+ let summary = '';
545
+ if (r.result) {
546
+ try { summary = typeof r.result === 'string' ? r.result.slice(0, 400) : JSON.stringify(r.result).slice(0, 400); } catch {}
547
+ }
548
+ return ` ${s} ${t}: ${summary}`;
549
+ }).join('\n');
550
+ const skillInfo = wf.skillsLoaded && wf.skillsLoaded.length > 0
551
+ ? `\n\nKullanilan skill'ler: ${wf.skillsLoaded.join(', ')}`
552
+ : '';
553
+ const preWfLen = messages.length;
554
+ messages.push({
555
+ role: 'system',
556
+ content: `=== WORKFLOW SONUCLARI ===\nSu araclar calisti:\n${report}${skillInfo}\n\nKullaniciya bu sonuclari anlamli bir sekilde ozetle.\n=== SONUC BITTI ===`,
557
+ });
558
+
559
+ process.stdout.write("\n " + tui.styled("AI ", { color: tui.PALETTE.secondary, bold: true }));
560
+
561
+ // Single LLM call to summarize workflow results
562
+ let wfReply = null;
358
563
  try {
359
- const reply = await sendMessageWithTools(
564
+ wfReply = await sendMessageWithTools(
360
565
  config.providerUrl, config.providerApiKey, config.providerModel,
361
566
  messages, toolDefs
362
567
  );
363
- if (reply.content) {
364
- process.stdout.write(reply.content);
365
- messages.push({ role: "assistant", content: reply.content });
366
- totalOut += Math.ceil(reply.content.length / 4);
568
+ // Remove workflow results after call
569
+ messages.splice(preWfLen, 1);
570
+ if (wfReply.content) {
571
+ process.stdout.write(wfReply.content);
572
+ messages.push({ role: "assistant", content: wfReply.content });
573
+ totalOut += Math.ceil(wfReply.content.length / 4);
574
+ }
575
+ // Handle any tool calls from the summary (rare but possible)
576
+ if (wfReply.tool_calls && wfReply.tool_calls.length > 0) {
577
+ await processToolCallsWithTracking(wfReply, config, toolDefs, messages);
367
578
  }
579
+ } catch (e) {
580
+ console.log("\n " + tui.C.red("❌ " + e.message));
581
+ }
368
582
 
369
- if (reply.tool_calls && reply.tool_calls.length > 0) {
370
- // v5.6.21: Akıllı onay - sadece riskli işlemlerde onay iste
371
- let approved = true;
372
- for (const tc of reply.tool_calls) {
373
- let args = {};
374
- try { args = JSON.parse(tc.function.arguments || "{}"); } catch {}
375
- const risk = assessRisk(tc.function.name, args);
376
- if (risk.requiresApproval) {
377
- process.stdout.write("\n");
378
- approved = await confirm(
379
- `⚠ ${tc.function.name}: ${risk.reason}\n Devam edilsin mi? (Y/n) `
380
- );
381
- if (!approved) break;
382
- }
383
- }
384
- if (!approved) {
385
- console.log("\n " + tui.C.yellow("⚠ Kullanıcı onayı iptal etti, tool çalıştırılmadı."));
386
- break;
583
+ totalIn += Math.ceil(((wfReply?.content || '') + report + skillInfo).length / 4) + Math.ceil(input.length / 4);
584
+ } else {
585
+ // Workflow failed or returned unexpected format — fallback to multi-turn LLM
586
+ process.stdout.write("\n " + tui.styled("AI ", { color: tui.PALETTE.secondary, bold: true }));
587
+ let iter = 0;
588
+ while (iter < MAX_ITERATIONS) {
589
+ iter++;
590
+ try {
591
+ const reply = await sendMessageWithTools(
592
+ config.providerUrl, config.providerApiKey, config.providerModel,
593
+ messages, toolDefs
594
+ );
595
+ if (reply.content) {
596
+ process.stdout.write(reply.content);
597
+ messages.push({ role: "assistant", content: reply.content });
598
+ totalOut += Math.ceil(reply.content.length / 4);
387
599
  }
388
600
 
389
- messages.push({ role: "assistant", content: reply.content || null, tool_calls: reply.tool_calls });
390
- for (const tc of reply.tool_calls) {
391
- const name = tc.function.name;
392
- let args = {};
393
- try { args = JSON.parse(tc.function.arguments || "{}"); } catch {}
394
- printToolCall(name, args);
395
- if (name === "bash" || name === "shell_command") commandsRun++;
396
- if (name === "write_file" || name === "edit_file") filesChanged++;
397
- const result = await executeTool(name, args, toolDefs);
398
- // v5.6.21: Sonuç gizleme - sadece status goster, yol/size gizle
399
- printToolCallSafe(name, args, result);
400
- const out = result.error
401
- ? "ERROR: " + result.error
402
- : (typeof result.result === "string" ? result.result : JSON.stringify(result.result));
403
- messages.push({ role: "tool", tool_call_id: tc.id, content: (out || "(empty)").slice(0, 8000) });
404
- totalIn += Math.ceil(out.length / 4);
601
+ if (reply.tool_calls && reply.tool_calls.length > 0) {
602
+ await processToolCallsWithTracking(reply, config, toolDefs, messages);
603
+ process.stdout.write("\n " + tui.styled("AI ", { color: tui.PALETTE.secondary, bold: true }));
604
+ continue;
405
605
  }
406
- process.stdout.write("\n " + tui.styled("AI ", { color: tui.PALETTE.secondary, bold: true }));
407
- continue;
606
+ break;
607
+ } catch (e) {
608
+ console.log("\n " + tui.C.red("❌ " + e.message));
609
+ break;
408
610
  }
409
- break;
410
- } catch (e) {
411
- console.log("\n " + tui.C.red("❌ " + e.message));
412
- break;
413
611
  }
612
+ totalIn += Math.ceil(input.length / 4);
414
613
  }
415
614
 
416
- totalIn += Math.ceil(input.length / 4);
417
615
  process.stdout.write("\n\n " + tui.styled("You ", { color: tui.PALETTE.primary, bold: true }));
418
616
  ask();
419
617
  });
@@ -421,6 +619,112 @@ async function codeV5(targetPath) {
421
619
  ask();
422
620
  }
423
621
 
622
+ const PARALLEL_SAFE_TOOLS = new Set(['read_file', 'file_search', 'grep_search', 'web_search', 'web_readability', 'duckduckgo_search', 'exa_search', 'searxng_search', 'firecrawl', 'memory_search', 'memory']);
623
+
624
+ async function processToolCalls(reply, config, toolDefs, messages, onToolResult) {
625
+ for (const tc of reply.tool_calls) {
626
+ let args = {};
627
+ try { args = JSON.parse(tc.function.arguments || "{}"); } catch {}
628
+ const risk = assessRisk(tc.function.name, args);
629
+
630
+ // Built-in risk assessment
631
+ if (risk.requiresApproval) {
632
+ process.stdout.write("\n");
633
+ const approved = await confirm(
634
+ `⚠ ${tc.function.name}: ${risk.reason}\n Devam edilsin mi? (Y/n) `
635
+ );
636
+ if (!approved) {
637
+ console.log("\n " + tui.C.yellow("⚠ Kullanıcı onayı iptal etti, tool çalıştırılmadı."));
638
+ return;
639
+ }
640
+ }
641
+
642
+ // Plan mode check
643
+ const pm = getPlanMode();
644
+ const planCheck = pm.checkTool(tc.function.name, args);
645
+ if (!planCheck.allowed) {
646
+ console.log("\n " + tui.C.red("⛔ Plan modunda engellendi: " + planCheck.reason));
647
+ return;
648
+ }
649
+ pm.recordTool(tc.function.name, args);
650
+
651
+ // Permission check
652
+ const perm = checkPermission(tc.function.name, args);
653
+ if (perm.action === 'deny') {
654
+ console.log("\n " + tui.C.red("⛔ İzin engelledi: " + perm.reason));
655
+ return;
656
+ }
657
+ if (perm.action === 'ask') {
658
+ const permKey = `${perm.rule.raw}:${JSON.stringify(args)}`;
659
+ if (!isApproved(permKey)) {
660
+ process.stdout.write("\n");
661
+ const ok = await confirm(`İzin gerekli: ${formatPermissionPrompt(tc.function.name, args, perm.reason)}\n İzin ver? [y=once, Y=session, p=persistent, n=no] `);
662
+ if (ok === 'persistent') markApproved(permKey, true);
663
+ else if (ok === 'session' || ok === true) markApproved(permKey, false);
664
+ else {
665
+ console.log("\n " + tui.C.yellow("⛔ İzin reddedildi"));
666
+ return;
667
+ }
668
+ }
669
+ }
670
+
671
+ // Pre-hook check
672
+ const hook = checkPreHooks(tc.function.name, args);
673
+ if (hook.action === 'deny') {
674
+ console.log("\n " + tui.C.red("⛔ Hook engelledi: " + hook.rule.raw));
675
+ return;
676
+ }
677
+ if (hook.action === 'ask') {
678
+ const ok = await confirm(`Hook onayı: ${permissionSummary(hook.rule, tc.function.name, args)}\n İzin verilsin mi? (y/N) `);
679
+ if (!ok) {
680
+ console.log("\n " + tui.C.yellow("⛔ Hook reddetti: " + hook.rule.raw));
681
+ return;
682
+ }
683
+ }
684
+ }
685
+
686
+ messages.push({ role: "assistant", content: reply.content || null, tool_calls: reply.tool_calls });
687
+
688
+ const parsed = reply.tool_calls.map(tc => {
689
+ let args = {};
690
+ try { args = JSON.parse(tc.function.arguments || "{}"); } catch {}
691
+ return { name: tc.function.name, args, id: tc.id };
692
+ });
693
+
694
+ const parallelSafe = parsed.filter(p => PARALLEL_SAFE_TOOLS.has(p.name));
695
+ const sequential = parsed.filter(p => !PARALLEL_SAFE_TOOLS.has(p.name));
696
+
697
+ // Print all tool calls
698
+ for (const p of parsed) printToolCall(p.name, p.args);
699
+
700
+ // Run parallel-safe tools concurrently
701
+ if (parallelSafe.length > 0) {
702
+ const results = await Promise.all(parallelSafe.map(async (p) => {
703
+ const result = runPostHooks(p.name, p.args, await executeTool(p.name, p.args, toolDefs));
704
+ printToolCallSafe(p.name, p.args, result);
705
+ if (onToolResult) onToolResult(p.name, p.args, result);
706
+ const out = result.error
707
+ ? "ERROR: " + result.error
708
+ : (typeof result.result === "string" ? result.result : JSON.stringify(result.result));
709
+ return { id: p.id, content: (out || "(empty)").slice(0, 8000) };
710
+ }));
711
+ for (const r of results) {
712
+ messages.push({ role: "tool", tool_call_id: r.id, content: r.content });
713
+ }
714
+ }
715
+
716
+ // Run sequential tools one at a time
717
+ for (const p of sequential) {
718
+ const result = runPostHooks(p.name, p.args, await executeTool(p.name, p.args, toolDefs));
719
+ printToolCallSafe(p.name, p.args, result);
720
+ if (onToolResult) onToolResult(p.name, p.args, result);
721
+ const out = result.error
722
+ ? "ERROR: " + result.error
723
+ : (typeof result.result === "string" ? result.result : JSON.stringify(result.result));
724
+ messages.push({ role: "tool", tool_call_id: p.id, content: (out || "(empty)").slice(0, 8000) });
725
+ }
726
+ }
727
+
424
728
  function printSummary(files, cmds, msgs, startTime) {
425
729
  const elapsed = Math.floor((Date.now() - startTime) / 1000);
426
730
  const min = Math.floor(elapsed / 60);