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
@@ -27,7 +27,7 @@ const { accumulateToolCallDeltas, finalizeToolCalls } = require('../utils/stream
27
27
  const { createPasteSafeInput, createOutputFilter, enableBracketedPaste, disableBracketedPaste, restoreNewlines, clearPasteContext } = require('../utils/paste-safe-input');
28
28
  const { getMemoryStore } = require('../utils/memory-store');
29
29
  const { buildSkillIndex } = require('../utils/skill-index');
30
- const { buildTiers, assemble } = require('../utils/system-prompt');
30
+ const { buildTiers, assemble, discoverProjectRules } = require('../utils/system-prompt');
31
31
  const { ToolGuardrails } = require('../utils/tool-guardrails');
32
32
 
33
33
  // v5.4.6: Model adi sizintisini engelle — global'e ata, callback'lerden erisebilir olsun
@@ -71,7 +71,8 @@ function rebuildSystemPrompt(opts) {
71
71
  _cachedTierOpts.botName !== opts.botName ||
72
72
  _cachedTierOpts.soulSummary !== opts.soulSummary ||
73
73
  _cachedTierOpts.skillsIndexBlock !== opts.skillsIndexBlock ||
74
- _cachedTierOpts.crossSessionContext !== opts.crossSessionContext;
74
+ _cachedTierOpts.crossSessionContext !== opts.crossSessionContext ||
75
+ _cachedTierOpts.projectRules !== opts.projectRules;
75
76
 
76
77
  if (needsFullRebuild || !_cachedStable) {
77
78
  const tiers = buildTiers(opts);
@@ -82,6 +83,7 @@ function rebuildSystemPrompt(opts) {
82
83
  soulSummary: opts.soulSummary,
83
84
  skillsIndexBlock: opts.skillsIndexBlock,
84
85
  crossSessionContext: opts.crossSessionContext,
86
+ projectRules: opts.projectRules,
85
87
  };
86
88
  }
87
89
  // Volatile always rebuilt fresh
@@ -92,6 +94,7 @@ function rebuildSystemPrompt(opts) {
92
94
  soulSummary: '',
93
95
  skillsIndexBlock: '',
94
96
  crossSessionContext: '',
97
+ projectRules: '',
95
98
  });
96
99
  return assemble(_cachedStable, _cachedContext, volatileOnly.volatile);
97
100
  }
@@ -115,7 +118,7 @@ const CLI_COMMANDS = {
115
118
  '/models': { desc: 'Modeller', run: ['models', 'list'] },
116
119
  '/memory-ls': { desc: 'Memory dosyaları', run: ['memory', 'list'] },
117
120
  '/seo': { desc: 'SEO denetimi (URL gerek)', needsArg: true, run: ['seo', 'audit'] },
118
- '/naturehub': { desc: 'Nature Hub post (text gerek)', needsArg: true, run: ['naturehub', 'post'] },
121
+ '/naturehub': { desc: 'Bota mesaj gönder (text gerek)', needsArg: true, run: ['naturehub', 'post'] },
119
122
  '/dashboard': { desc: 'Web dashboard başlat (port 7421)', run: ['dashboard', 'start'] },
120
123
  };
121
124
 
@@ -287,14 +290,240 @@ function apiRequest(providerUrl, providerApiKey, body, stream = false, retries =
287
290
  async function sendStreaming(providerUrl, providerApiKey, messages, model, onChunk, onToolCall) {
288
291
  const isMM = isMiniMax(providerUrl);
289
292
  const isGM = isGemini(providerUrl);
293
+ const planMode = getPlanMode();
294
+
295
+ // Simple stdin prompt fallback (no rl dependency)
296
+ function stdinPrompt(question, cb) {
297
+ process.stdout.write(question);
298
+ const stdin = process.stdin;
299
+ const onData = (data) => {
300
+ stdin.removeListener('data', onData);
301
+ stdin.pause();
302
+ cb(data.toString().trim());
303
+ };
304
+ stdin.resume();
305
+ stdin.on('data', onData);
306
+ }
307
+
290
308
  const toolDefs = getToolDefs();
309
+ // Inject plan mode virtual tools
310
+ const planToolDefs = [
311
+ {
312
+ name: 'EnterPlanMode',
313
+ description: 'Switch to plan-only mode. Research, explore, and create a detailed plan without making changes. Use ExitPlanMode when ready.',
314
+ parameters: { type: 'object', properties: {}, required: [] },
315
+ execute: async () => {
316
+ if (planMode.enter()) return { result: 'Plan mode activated. You can now research and plan. No changes will be made. Use ExitPlanMode when your plan is ready.' };
317
+ return { result: 'Already in plan mode.' };
318
+ },
319
+ },
320
+ {
321
+ name: 'ExitPlanMode',
322
+ description: 'Exit plan mode and present your plan for review. The plan must include steps, files to modify, and expected changes.',
323
+ parameters: {
324
+ type: 'object',
325
+ properties: {
326
+ plan: { type: 'string', description: 'Markdown plan with ## Plan title, ### Step N sections listing files and changes' },
327
+ summary: { type: 'string', description: 'One-sentence summary of the plan' },
328
+ },
329
+ required: ['plan'],
330
+ },
331
+ execute: async (args) => {
332
+ const ok = planMode.exit(args.plan);
333
+ if (!ok) return { error: 'Not in plan mode.' };
334
+ return {
335
+ result: `Plan submitted for review.\n\n${args.plan}`,
336
+ _plan_summary: args.summary || '',
337
+ };
338
+ },
339
+ },
340
+ ];
341
+ toolDefs.push(...planToolDefs);
342
+
343
+ // Inject worktree virtual tools
344
+ const wt = getWorktree();
345
+ const worktreeToolDefs = [
346
+ {
347
+ name: 'EnterWorktree',
348
+ description: 'Create an isolated worktree for experimental changes without affecting the main project. Use ExitWorktree to merge changes back.',
349
+ parameters: {
350
+ type: 'object',
351
+ properties: {
352
+ branch: { type: 'string', description: 'Optional branch name for the worktree' },
353
+ },
354
+ },
355
+ execute: async (args) => wt.enter(args),
356
+ },
357
+ {
358
+ name: 'ExitWorktree',
359
+ description: 'Exit the current worktree and merge changes back to the main branch.',
360
+ parameters: {
361
+ type: 'object',
362
+ properties: {
363
+ merge: { type: 'boolean', description: 'Merge changes back (default: true)' },
364
+ },
365
+ },
366
+ execute: async (args) => wt.exit(args),
367
+ },
368
+ ];
369
+ toolDefs.push(...worktreeToolDefs);
370
+
371
+ // Inject tool-level virtual tools
372
+ const { getTaskManager } = require('../utils/tasks');
373
+ const taskMgr = getTaskManager();
374
+ const toolVirtualTools = [
375
+ {
376
+ name: 'CreateTask',
377
+ description: 'Run a command in the background. Returns immediately with a task ID. Use GetTaskResult or ListTasks to check status.',
378
+ parameters: {
379
+ type: 'object',
380
+ properties: {
381
+ command: { type: 'string', description: 'Shell command to run' },
382
+ timeout: { type: 'number', description: 'Timeout in ms (default: 300000)' },
383
+ },
384
+ required: ['command'],
385
+ },
386
+ execute: async (args) => taskMgr.create(args.command, args),
387
+ },
388
+ {
389
+ name: 'ListTasks',
390
+ description: 'List all background tasks with their status.',
391
+ parameters: { type: 'object', properties: {}, required: [] },
392
+ execute: async () => ({ tasks: taskMgr.list() }),
393
+ },
394
+ {
395
+ name: 'GetTaskResult',
396
+ description: 'Get the full result (stdout/stderr) of a completed or running task.',
397
+ parameters: {
398
+ type: 'object',
399
+ properties: { taskId: { type: 'string', description: 'Task ID' } },
400
+ required: ['taskId'],
401
+ },
402
+ execute: async (args) => {
403
+ const task = taskMgr.get(args.taskId);
404
+ if (!task) return { error: 'Task not found' };
405
+ return { result: task.stdout, error: task.stderr, status: task.status, exitCode: task.exitCode };
406
+ },
407
+ },
408
+ {
409
+ name: 'StopTask',
410
+ description: 'Stop a running background task.',
411
+ parameters: {
412
+ type: 'object',
413
+ properties: { taskId: { type: 'string' } },
414
+ required: ['taskId'],
415
+ },
416
+ execute: async (args) => taskMgr.stop(args.taskId),
417
+ },
418
+ {
419
+ name: 'SearchSessions',
420
+ description: 'Search past conversation sessions for information. Useful for finding previously discussed topics or decisions.',
421
+ parameters: {
422
+ type: 'object',
423
+ properties: { query: { type: 'string', description: 'Search query' } },
424
+ required: ['query'],
425
+ },
426
+ execute: async (args) => {
427
+ const { search } = require('../utils/session-search');
428
+ return { results: search(args.query, 5) };
429
+ },
430
+ },
431
+ {
432
+ name: 'RestoreFile',
433
+ description: 'Restore a file from its snapshot history. Use FileHistory first to list available snapshots.',
434
+ parameters: {
435
+ type: 'object',
436
+ properties: {
437
+ filePath: { type: 'string', description: 'File path to restore' },
438
+ timestamp: { type: 'number', description: 'Snapshot timestamp (from FileHistory). If omitted, restores previous version.' },
439
+ },
440
+ required: ['filePath'],
441
+ },
442
+ execute: async (args) => {
443
+ const fh = require('../utils/file-history');
444
+ const snaps = fh.getHistory(args.filePath);
445
+ if (snaps.length === 0) return { error: 'No history for this file' };
446
+ const ts = args.timestamp || snaps[0].timestamp;
447
+ return fh.restore(args.filePath, ts);
448
+ },
449
+ },
450
+ {
451
+ name: 'FileHistory',
452
+ description: 'List snapshot history for a file, showing timestamps and sizes of previous versions.',
453
+ parameters: {
454
+ type: 'object',
455
+ properties: { filePath: { type: 'string' } },
456
+ required: ['filePath'],
457
+ },
458
+ execute: async (args) => {
459
+ const fh = require('../utils/file-history');
460
+ return { snapshots: fh.getHistory(args.filePath) };
461
+ },
462
+ },
463
+ {
464
+ name: 'UltraReview',
465
+ description: 'Run a multi-focus code review (security, style, logic, performance) on specified files or git diff.',
466
+ parameters: {
467
+ type: 'object',
468
+ properties: {
469
+ files: { type: 'array', items: { type: 'string' }, description: 'File paths to review' },
470
+ diff: { type: 'string', description: 'Git diff content to review instead of files' },
471
+ },
472
+ },
473
+ execute: async (args) => {
474
+ const ur = require('../utils/ultra-review');
475
+ if (args.diff) return ur.reviewDiff(args.diff);
476
+ if (args.files) {
477
+ const fs = require('fs');
478
+ return {
479
+ reviews: args.files.map(f => {
480
+ const content = fs.existsSync(f) ? fs.readFileSync(f, 'utf8') : '';
481
+ return ur.reviewFile(f, content);
482
+ }),
483
+ };
484
+ }
485
+ return { error: 'Specify files or diff' };
486
+ },
487
+ },
488
+ {
489
+ name: 'ScheduleTask',
490
+ description: 'Schedule a recurring task using cron expressions. E.g., "*/5 * * * *" for every 5 min.',
491
+ parameters: {
492
+ type: 'object',
493
+ properties: {
494
+ schedule: { type: 'string', description: 'Cron expression: min hour dom mon dow' },
495
+ command: { type: 'string', description: 'Command to run' },
496
+ description: { type: 'string', description: 'Optional description' },
497
+ },
498
+ required: ['schedule', 'command'],
499
+ },
500
+ execute: async (args) => require('../utils/cron').addJob(args),
501
+ },
502
+ {
503
+ name: 'ListScheduledTasks',
504
+ description: 'List all scheduled cron tasks.',
505
+ parameters: { type: 'object', properties: {} },
506
+ execute: async () => ({ jobs: require('../utils/cron').loadJobs() }),
507
+ },
508
+ {
509
+ name: 'RemoveScheduledTask',
510
+ description: 'Remove a scheduled task by ID.',
511
+ parameters: { type: 'object', properties: { id: { type: 'string' } }, required: ['id'] },
512
+ execute: async (args) => {
513
+ require('../utils/cron').removeJob(args.id);
514
+ return { removed: true };
515
+ },
516
+ },
517
+ ];
518
+ toolDefs.push(...toolVirtualTools);
519
+
291
520
  const toolParam = toOpenAIFormat(toolDefs);
292
521
  guardrails.reset();
293
522
 
294
523
  let currentMessages = messages;
295
524
  let fullText = '';
296
525
  let iterations = 0;
297
- const MAX_TOOL_ITERATIONS = isMM ? 12 : 8;
526
+ const MAX_TOOL_ITERATIONS = 50;
298
527
  const MAX_CONTEXT_TOKENS = 32000; // safety limit before compression
299
528
 
300
529
  // v5.7.18: Preflight compression — if context too long, compress middle messages
@@ -323,18 +552,53 @@ async function sendStreaming(providerUrl, providerApiKey, messages, model, onChu
323
552
  // Apply preflight on entry
324
553
  currentMessages = preflightCompress(currentMessages);
325
554
 
326
- while (iterations < MAX_TOOL_ITERATIONS) {
555
+ while (iterations < 50) {
556
+ let effortLevel = 'medium', effortCfg;
557
+ let cfg;
558
+ try {
559
+ cfg = require('../utils/config').getConfig();
560
+ effortLevel = getEffortLevel(cfg);
561
+ effortCfg = getEffortConfig(effortLevel);
562
+ } catch { effortCfg = getEffortConfig(effortLevel); }
563
+ const maxIter = effortCfg ? effortCfg.maxToolIterations : 50;
327
564
  iterations++;
328
565
  // v5.7.18: Preflight compress before each iteration to prevent context bloat
329
566
  currentMessages = preflightCompress(currentMessages);
330
567
  const shouldStream = !isMM && !isGM; // MiniMax + Gemini non-stream (tool_calls reliability)
568
+ // Inject plan mode prompt into system message
569
+ const planModePrompt = planMode.getSystemPrompt();
570
+ if (planModePrompt) {
571
+ const sysIdx = currentMessages.findIndex(m => m.role === 'system');
572
+ if (sysIdx >= 0) {
573
+ const base = currentMessages[sysIdx].content;
574
+ if (!base.endsWith(planModePrompt)) {
575
+ currentMessages[sysIdx] = { ...currentMessages[sysIdx], content: base + '\n\n' + planModePrompt };
576
+ }
577
+ }
578
+ } else {
579
+ // Clean up any stale plan mode prompt from previous iterations
580
+ const sysIdx = currentMessages.findIndex(m => m.role === 'system');
581
+ if (sysIdx >= 0) {
582
+ const base = currentMessages[sysIdx].content;
583
+ const marker = '\n\nYou are in PLAN MODE.';
584
+ if (base.includes(marker)) {
585
+ currentMessages[sysIdx] = { ...currentMessages[sysIdx], content: base.split('\n\nYou are in PLAN MODE.')[0] };
586
+ }
587
+ }
588
+ }
589
+
590
+ const fallbackChain = getFallbackChain();
591
+
331
592
  const body = {
332
- model,
593
+ model: fallbackChain.current || model,
333
594
  messages: currentMessages,
334
595
  stream: shouldStream,
335
- temperature: 0.3,
336
- max_tokens: 2048,
596
+ temperature: effortCfg.temperature,
597
+ max_tokens: effortCfg.maxTokens,
337
598
  };
599
+ // Structured output support
600
+ const respFmt = getResponseFormat(cfg);
601
+ if (respFmt) body.response_format = respFmt;
338
602
  if (toolParam) body.tools = toolParam;
339
603
  if (isMM || isGM) body.tool_choice = 'auto'; // MiniMax + Gemini için explicit
340
604
 
@@ -350,7 +614,7 @@ async function sendStreaming(providerUrl, providerApiKey, messages, model, onChu
350
614
  fullText = content;
351
615
  // Non-stream tool call desteği
352
616
  if (msg.tool_calls && msg.tool_calls.length > 0) {
353
- const toolResults = await processToolCalls(msg.tool_calls, onToolCall);
617
+ const toolResults = await processToolCalls(msg.tool_calls, onToolCall, stdinPrompt);
354
618
  currentMessages.push(msg);
355
619
  currentMessages.push(...toolResults);
356
620
  continue; // Tekrar API çağır
@@ -365,60 +629,71 @@ async function sendStreaming(providerUrl, providerApiKey, messages, model, onChu
365
629
  : isGemini(providerUrl)
366
630
  ? `${providerUrl.replace(/\/+$/, '')}/openai/chat/completions`
367
631
  : `${providerUrl.replace(/\/+$/, '')}/chat/completions`;
368
- const result = await new Promise((resolve, reject) => {
369
- const req = https.request(endpoint, {
370
- method: 'POST',
371
- headers: { 'Authorization': `Bearer ${providerApiKey}`, 'Content-Type': 'application/json' },
372
- timeout: 60000,
373
- }, (res) => {
374
- if (res.statusCode !== 200) {
375
- let data = '';
376
- res.on('data', c => data += c);
377
- res.on('end', () => reject(new Error(`HTTP ${res.statusCode}: ${data.slice(0, 200)}`)));
378
- return;
379
- }
380
- let buffer = '';
381
- let streamText = '';
382
- const toolCalls = []; // { index, id, name, args }
383
- res.on('data', (chunk) => {
384
- buffer += chunk.toString('utf8');
385
- const lines = buffer.split('\n');
386
- buffer = lines.pop() || '';
387
- for (const line of lines) {
388
- const trimmed = line.trim();
389
- if (!trimmed || !trimmed.startsWith('data:')) continue;
390
- const data = trimmed.slice(5).trim();
391
- if (data === '[DONE]') {
392
- resolve({ streamText, toolCalls });
393
- return;
394
- }
395
- try {
396
- const parsed = JSON.parse(data);
397
- const choice = parsed.choices?.[0];
398
- if (!choice) continue;
399
- const delta = choice.delta;
400
-
401
- // Text content
402
- if (delta.content) {
403
- streamText += delta.content;
404
- onChunk(delta.content);
405
- }
406
-
407
- // Tool calls (streaming delta) — shared accumulator,
408
- // see src/utils/streaming-tools.js for the per-index pattern.
409
- if (delta.tool_calls) {
410
- accumulateToolCallDeltas(toolCalls, delta.tool_calls);
411
- }
412
- } catch {}
632
+ let result;
633
+ try {
634
+ result = await new Promise((resolve, reject) => {
635
+ const req = https.request(endpoint, {
636
+ method: 'POST',
637
+ headers: { 'Authorization': `Bearer ${providerApiKey}`, 'Content-Type': 'application/json' },
638
+ timeout: 60000,
639
+ }, (res) => {
640
+ if (res.statusCode !== 200) {
641
+ let data = '';
642
+ res.on('data', c => data += c);
643
+ res.on('end', () => reject(new Error(`HTTP ${res.statusCode}: ${data.slice(0, 200)}`)));
644
+ return;
413
645
  }
646
+ let buffer = '';
647
+ let streamText = '';
648
+ const toolCalls = []; // { index, id, name, args }
649
+ res.on('data', (chunk) => {
650
+ buffer += chunk.toString('utf8');
651
+ const lines = buffer.split('\n');
652
+ buffer = lines.pop() || '';
653
+ for (const line of lines) {
654
+ const trimmed = line.trim();
655
+ if (!trimmed || !trimmed.startsWith('data:')) continue;
656
+ const data = trimmed.slice(5).trim();
657
+ if (data === '[DONE]') {
658
+ resolve({ streamText, toolCalls });
659
+ return;
660
+ }
661
+ try {
662
+ const parsed = JSON.parse(data);
663
+ const choice = parsed.choices?.[0];
664
+ if (!choice) continue;
665
+ const delta = choice.delta;
666
+
667
+ // Text content
668
+ if (delta.content) {
669
+ streamText += delta.content;
670
+ onChunk(delta.content);
671
+ }
672
+
673
+ // Tool calls (streaming delta) — shared accumulator,
674
+ // see src/utils/streaming-tools.js for the per-index pattern.
675
+ if (delta.tool_calls) {
676
+ accumulateToolCallDeltas(toolCalls, delta.tool_calls);
677
+ }
678
+ } catch {}
679
+ }
680
+ });
681
+ res.on('end', () => resolve({ streamText, toolCalls }));
414
682
  });
415
- res.on('end', () => resolve({ streamText, toolCalls }));
683
+ req.on('error', reject);
684
+ req.on('timeout', () => { req.destroy(); reject(new Error('Timeout')); });
685
+ req.write(JSON.stringify(body));
686
+ req.end();
416
687
  });
417
- req.on('error', reject);
418
- req.on('timeout', () => { req.destroy(); reject(new Error('Timeout')); });
419
- req.write(JSON.stringify(body));
420
- req.end();
421
- });
688
+ } catch (err) {
689
+ // Fallback chain: try next model on error
690
+ const fb = fallbackChain.recordError(body.model, err);
691
+ if (fb.fallback) {
692
+ console.log(tui.C.yellow(`\n ⚠ ${body.model} başarısız → ${fb.nextModel} deneniyor...\n`));
693
+ continue; // Retry with next model
694
+ }
695
+ throw err;
696
+ }
422
697
 
423
698
  fullText = result.streamText;
424
699
 
@@ -433,8 +708,39 @@ async function sendStreaming(providerUrl, providerApiKey, messages, model, onChu
433
708
  tool_calls: finalized,
434
709
  });
435
710
  // Her tool call'ı çalıştır, sonuçları tool mesajı olarak ekle
436
- const toolResults = await processToolCalls(finalized, onToolCall);
711
+ const toolResults = await processToolCalls(finalized, onToolCall, stdinPrompt);
437
712
  currentMessages.push(...toolResults);
713
+
714
+ // Plan mode review: plan submitted, wait for user approval
715
+ if (planMode.inReview()) {
716
+ const { plan } = planMode.planHistory[planMode.planHistory.length - 1] || {};
717
+ console.log('\n' + tui.C.cyan(' 📋 Plan sunuldu — onay bekleniyor...'));
718
+ console.log(tui.C.muted(' ' + '─'.repeat(56)));
719
+ console.log(plan ? `\n ${plan.replace(/\n/g, '\n ')}` : '');
720
+ console.log('\n' + tui.C.muted(' ─'.repeat(28)));
721
+ const approved = await new Promise(resolve => {
722
+ stdinPrompt(tui.C.yellow(' Planı onaylıyor musun? [Y=exec, n=reddet, e=düzenle]: '), answer => {
723
+ const key = answer.trim().toLowerCase();
724
+ if (key === 'n' || key === 'no') { planMode.reject(); resolve(false); }
725
+ else if (key === 'e' || key === 'edit') { planMode.reject(); resolve('edit'); }
726
+ else { planMode.approve(); resolve(true); }
727
+ });
728
+ });
729
+ if (approved === true) {
730
+ console.log(tui.C.green(' ✓ Plan onaylandı. Plan uygulanıyor...\n'));
731
+ // Devam — model cevap versin
732
+ } else if (approved === 'edit') {
733
+ console.log(tui.C.yellow(' 📝 Planı düzenleyin ve /plan ile yeniden gönderin.\n'));
734
+ // Plan modunda kal, mesaj ekle
735
+ currentMessages.push({ role: 'user', content: 'Planı düzenle ve yeniden sun.' });
736
+ continue;
737
+ } else {
738
+ console.log(tui.C.amber(' ⨯ Plan reddedildi. Yeniden plan yapılıyor...\n'));
739
+ currentMessages.push({ role: 'user', content: 'Plan reddedildi. Lütfen farklı bir yaklaşım dene.' });
740
+ continue;
741
+ }
742
+ }
743
+
438
744
  // Devam — model sonuçları görsün, cevap versin
439
745
  continue;
440
746
  }
@@ -451,8 +757,37 @@ async function sendStreaming(providerUrl, providerApiKey, messages, model, onChu
451
757
  */
452
758
  const UNTRUSTED_TOOLS = new Set(['browser', 'web_search', 'duckduckgo_search', 'searxng_search', 'exa_search', 'firecrawl', 'web_readability']);
453
759
  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']);
760
+ const { checkPreHooks, runPostHooks, permissionSummary } = require('../utils/tool-hooks');
761
+ const { checkPermission, isApproved, markApproved, formatPermissionPrompt } = require('../utils/permissions');
762
+ const { getPlanMode } = require('../utils/plan-mode');
763
+ const { getWorktree } = require('../utils/worktree');
764
+ const { getLevel: getEffortLevel, getConfig: getEffortConfig } = require('../utils/effort-levels');
765
+ const { getResponseFormat, hasStructuredOutput } = require('../utils/structured-output');
766
+ const { getFallbackChain } = require('../utils/fallback-chain');
767
+
768
+ /**
769
+ * Prompt user for permission approval.
770
+ * Returns: true (once), 'session', 'persistent', or false (denied).
771
+ */
772
+ async function askPermissionPrompt(question, hint, prompter) {
773
+ const full = `${tui.C.yellow('⚠')} ${tui.C.bold('İzin gerekiyor')}: ${question}\n${tui.C.muted(hint)}`;
774
+ return new Promise(resolve => {
775
+ prompter(full, answer => {
776
+ const key = answer.trim();
777
+ if (key === 'y') resolve(true); // once
778
+ else if (key === 'Y') resolve('session'); // session
779
+ else if (key === 'p') resolve('persistent'); // disk
780
+ else if (key === 'a') { _permSessionCache.set('__ALL__', true); resolve(true); } // all (legacy)
781
+ else resolve(false); // no
782
+ });
783
+ });
784
+ }
454
785
 
455
- async function processToolCalls(toolCalls, onToolCall) {
786
+ /** Session permission cache (for ask hooks) */
787
+ const _permSessionCache = new Map();
788
+
789
+
790
+ async function processToolCalls(toolCalls, onToolCall, onAsk) {
456
791
  const toolDefs = getToolDefs();
457
792
  const results = [];
458
793
 
@@ -492,23 +827,79 @@ async function processToolCalls(toolCalls, onToolCall) {
492
827
  if (onToolCall) onToolCall({ name: p.name, args: p.args, status: 'running' });
493
828
  }
494
829
 
830
+ // Shared tool execution with: planCheck → permissions → hooks → execute → post-hooks
831
+ async function executeOne(p) {
832
+ // 0. Plan mode check
833
+ const pm = getPlanMode();
834
+ const planCheck = pm.checkTool(p.name, p.args);
835
+ if (!planCheck.allowed) {
836
+ const denied = { ...p, result: { error: planCheck.reason }, _plan_blocked: true };
837
+ if (onToolCall) onToolCall({ name: p.name, args: p.args, status: 'done', result: { error: planCheck.reason } });
838
+ return denied;
839
+ }
840
+ pm.recordTool(p.name, p.args);
841
+
842
+ // 1. Permission check (config-based granular rules)
843
+ const perm = checkPermission(p.name, p.args);
844
+ if (perm.action === 'deny') {
845
+ const denied = { ...p, result: { error: perm.reason }, _perm_blocked: true };
846
+ if (onToolCall) onToolCall({ name: p.name, args: p.args, status: 'done', result: { error: perm.reason } });
847
+ return denied;
848
+ }
849
+ if (perm.action === 'ask') {
850
+ const permKey = `${perm.rule.raw}:${JSON.stringify(p.args)}`;
851
+ if (!isApproved(permKey)) {
852
+ const ok = await askPermissionPrompt(
853
+ `${formatPermissionPrompt(p.name, p.args, perm.reason)}\n ${perm.reason}`,
854
+ `Bu işleme izin ver? [y=once, Y=session, p=persistent, n=no] `,
855
+ onAsk
856
+ );
857
+ if (ok === 'persistent') markApproved(permKey, true);
858
+ else if (ok === 'session') markApproved(permKey, false);
859
+ else if (!ok) {
860
+ const denied = { ...p, result: { error: `İzin reddedildi: ${perm.rule.raw}` }, _perm_blocked: true };
861
+ if (onToolCall) onToolCall({ name: p.name, args: p.args, status: 'done', result: { error: `İzin reddedildi: ${perm.rule.raw}` } });
862
+ return denied;
863
+ }
864
+ }
865
+ }
866
+
867
+ // 2. Pre-hook check
868
+ const hook = checkPreHooks(p.name, p.args);
869
+ if (hook.action === 'deny') {
870
+ const denied = { ...p, result: { error: hook.reason }, _hook_blocked: true };
871
+ if (onToolCall) onToolCall({ name: p.name, args: p.args, status: 'done', result: { error: hook.reason } });
872
+ return denied;
873
+ }
874
+ if (hook.action === 'ask') {
875
+ const ok = await askPermissionPrompt(
876
+ permissionSummary(hook.rule, p.name, p.args),
877
+ `Hook onayı [y=once, Y=session, n=no]: `,
878
+ onAsk
879
+ );
880
+ if (!ok) {
881
+ const denied = { ...p, result: { error: `Hook reddetti: ${hook.rule.raw}` }, _hook_blocked: true };
882
+ if (onToolCall) onToolCall({ name: p.name, args: p.args, status: 'done', result: { error: `Hook reddetti: ${hook.rule.raw}` } });
883
+ return denied;
884
+ }
885
+ }
886
+
887
+ const result = await executeTool(p.name, p.args, toolDefs);
888
+ const success = result?.success !== false;
889
+ guardrails.record(p.name, p.args, success);
890
+ return { ...p, result: runPostHooks(p.name, p.args, result) };
891
+ }
892
+
495
893
  // Run parallel batch concurrently
496
894
  if (parallelBatch.length > 0) {
497
- const parallelResults = await Promise.all(parallelBatch.map(async (p) => {
498
- const result = await executeTool(p.name, p.args, toolDefs);
499
- const success = result?.success !== false;
500
- guardrails.record(p.name, p.args, success);
501
- return { ...p, result };
502
- }));
895
+ const parallelResults = await Promise.all(parallelBatch.map(executeOne));
503
896
  results.push(...parallelResults);
504
897
  }
505
898
 
506
899
  // Run sequential batch one at a time
507
900
  for (const p of sequentialBatch) {
508
- const result = await executeTool(p.name, p.args, toolDefs);
509
- const success = result?.success !== false;
510
- guardrails.record(p.name, p.args, success);
511
- results.push({ ...p, result });
901
+ const r = await executeOne(p);
902
+ results.push(r);
512
903
  }
513
904
 
514
905
  // No-progress check: if all tools failed, inject warning
@@ -576,6 +967,7 @@ function printHelp() {
576
967
  console.log(' ' + chalk.yellow('/clear'.padEnd(22)) + chalk.gray('Ekranı temizle'));
577
968
  console.log(' ' + chalk.yellow('/history'.padEnd(22)) + chalk.gray('Bu oturumun geçmişi'));
578
969
  console.log(' ' + chalk.yellow('/memory'.padEnd(22)) + chalk.gray('Memory\'i göster'));
970
+ console.log(' ' + chalk.yellow('/plan [on|off|show]'.padEnd(22)) + chalk.gray('Plan modu'));
579
971
  console.log(' ' + chalk.yellow('/forget'.padEnd(22)) + chalk.gray('Memory\'i temizle'));
580
972
  console.log(' ' + chalk.yellow('/sessions'.padEnd(22)) + chalk.gray('Geçmiş oturumları listele'));
581
973
  console.log(' ' + chalk.yellow('/resume [id|last]'.padEnd(22)) + chalk.gray('Önceki oturuma dön'));
@@ -696,10 +1088,16 @@ async function startRepl(args) {
696
1088
  (cfg.providerUrl || '').includes('mistral.ai') ||
697
1089
  (cfg.providerUrl || '').includes('localhost') ||
698
1090
  (cfg.providerUrl || '').includes('ollama');
1091
+ // Discover project rules (CLAUDE.md)
1092
+ const projectRules = discoverProjectRules(process.cwd());
1093
+ if (projectRules) {
1094
+ console.log(chalk.cyan(` 📋 Proje kurallari bulundu (CLAUDE.md)\n`));
1095
+ }
1096
+
699
1097
  // Build system prompt with tier caching (stable+context cached, volatile fresh)
700
1098
  const promptOpts = {
701
1099
  botName, userName, soulSummary, isSmallModel,
702
- memorySnapshotBlock, skillsIndexBlock,
1100
+ memorySnapshotBlock, skillsIndexBlock, projectRules,
703
1101
  crossSessionContext: crossSessionContext || '',
704
1102
  userHome: cfg.userHome || '',
705
1103
  hasHistory: messages.length > 0,
@@ -980,6 +1378,29 @@ async function startRepl(args) {
980
1378
  case 'tokens':
981
1379
  console.log(chalk.gray(` Token: ~${totalInputTokens} in / ~${totalOutputTokens} out`));
982
1380
  break;
1381
+ case 'plan':
1382
+ if (arg === 'on' || arg === 'enter') {
1383
+ if (getPlanMode().enter()) console.log(tui.C.cyan('\n 📋 Plan modu aktif. Plan yapın ve /plan off ile çıkın.\n'));
1384
+ else console.log(tui.C.yellow(' Zaten plan modunda.'));
1385
+ } else if (arg === 'off' || arg === 'exit') {
1386
+ if (getPlanMode().isPlanning()) {
1387
+ console.log(tui.C.yellow(' Plan modundan çıkılıyor. Plan yazılıp ExitPlanMode ile sunulmalı.'));
1388
+ getPlanMode().approve();
1389
+ } else {
1390
+ console.log(tui.C.yellow(' Plan modunda değil.'));
1391
+ }
1392
+ } else if (arg === 'show') {
1393
+ if (getPlanMode().planHistory.length > 0) {
1394
+ const last = getPlanMode().planHistory[getPlanMode().planHistory.length - 1];
1395
+ console.log(tui.C.cyan('\n 📋 Son Plan:\n'));
1396
+ console.log(` ${last.plan.replace(/\n/g, '\n ')}`);
1397
+ } else {
1398
+ console.log(tui.C.yellow(' Henüz plan yok.'));
1399
+ }
1400
+ } else {
1401
+ console.log(tui.C.yellow(' Kullanım: /plan on|off|show'));
1402
+ }
1403
+ break;
983
1404
  case 'save':
984
1405
  const sessId = saveSession(messages, {
985
1406
  provider: providerUrl, model, user: cfg.userName, bot: memory.botName,
@@ -1044,12 +1465,13 @@ async function startRepl(args) {
1044
1465
  const wfResult = await executeTool('workflow', { action: 'run', task: line }, wfToolDefs);
1045
1466
  const wf = wfResult?.result || {};
1046
1467
  if (wf.success !== false) {
1047
- process.stdout.write(tui.styled(' ✓ workflow\n', { color: tui.PALETTE.success }));
1468
+ const loaded = wf.skillsLoaded && wf.skillsLoaded.length > 0 ? ` [skill: ${wf.skillsLoaded.join(', ')}]` : '';
1469
+ process.stdout.write(tui.styled(` ✓ workflow${loaded}\n`, { color: tui.PALETTE.success }));
1048
1470
  } else {
1049
1471
  process.stdout.write(tui.styled(' ✗ workflow\n', { color: tui.PALETTE.danger }));
1050
1472
  }
1051
1473
 
1052
- if (wf.passthrough && wf.reply) {
1474
+ if (wf.passthrough && wf.reply !== undefined && wf.reply !== null) {
1053
1475
  // Simple chat — workflow handled it directly
1054
1476
  const fullReply = String(wf.reply);
1055
1477
  const displayBotName = memory.botName || 'Asistan';
@@ -1082,16 +1504,18 @@ async function startRepl(args) {
1082
1504
  }
1083
1505
  return ` ${s} ${t}: ${summary}`;
1084
1506
  }).join('\n');
1507
+ const skillInfo = wf.skillsLoaded && wf.skillsLoaded.length > 0
1508
+ ? `\n\nKullanilan skill'ler: ${wf.skillsLoaded.join(', ')}`
1509
+ : '';
1510
+ const preWfLen = messages.length;
1085
1511
  messages.push({
1086
1512
  role: 'system',
1087
- content: `=== WORKFLOW SONUCLARI ===\nSu araclar calisti:\n${report}\n\nKullaniciya bu sonuclari anlamli bir sekilde ozetle.\n=== SONUC BITTI ===`,
1088
- _internal: true
1513
+ content: `=== WORKFLOW SONUCLARI ===\nSu araclar calisti:\n${report}${skillInfo}\n\nKullaniciya bu sonuclari anlamli bir sekilde ozetle.\n=== SONUC BITTI ===`,
1089
1514
  });
1090
- const apiMessages = messages.filter(m => !m._internal);
1091
1515
  const reply = await sendStreaming(
1092
1516
  providerUrl,
1093
1517
  providerApiKey,
1094
- apiMessages,
1518
+ messages,
1095
1519
  model,
1096
1520
  // v5.6.12: Callback bos - tam metin 'reply' olarak gelecek (non-stream mode)
1097
1521
  () => {},
@@ -1109,6 +1533,8 @@ async function startRepl(args) {
1109
1533
  }
1110
1534
  })
1111
1535
  );
1536
+ // Remove workflow results message (already served its purpose)
1537
+ messages.splice(preWfLen, 1);
1112
1538
  // v5.6.12: Tam metin 'reply' olarak zaten geldi (non-stream mode)
1113
1539
  const fullReply = String(reply || '');
1114
1540
  // Bot adini al
@@ -1128,15 +1554,8 @@ async function startRepl(args) {
1128
1554
  fixedReply = fixedReply.replace(/Ben\s+Asistan[\s\w\.]*/gi, 'Ben ' + displayBotName);
1129
1555
  fixedReply = fixedReply.replace(/\*\*(?:MiniMax|Claude|GPT|M2\.5|M2)[^\*]*\*\*/gi, '**' + displayBotName + '**');
1130
1556
  process.stdout.write('\n' + fixedReply + '\n');
1131
- const existingLen = messages.filter(m => !m._internal).length;
1132
- const toolCallHistory = apiMessages.slice(existingLen);
1133
- for (const m of toolCallHistory) {
1134
- if (m.role === 'assistant' || m.role === 'tool') {
1135
- messages.push(m);
1136
- }
1137
- }
1138
1557
  messages.push({ role: 'assistant', content: fixedReply });
1139
- totalInputTokens += apiMessages.reduce((s, m) => s + Math.ceil((m.content || '').length / 4), 0);
1558
+ totalInputTokens += Math.ceil(((fullReply || '') + report + skillInfo).length / 4);
1140
1559
  totalOutputTokens += Math.ceil((fullReply || '').length / 4);
1141
1560
  } else {
1142
1561
  // Workflow failed or returned unexpected format