@indiccoder/mentis-cli 1.1.3 → 1.1.5

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 (82) hide show
  1. package/.claude/settings.local.json +8 -0
  2. package/.mentis/session.json +15 -0
  3. package/.mentis/sessions/1769189035730.json +23 -0
  4. package/.mentis/sessions/1769189569160.json +23 -0
  5. package/.mentis/sessions/1769767538672.json +23 -0
  6. package/.mentis/sessions/1769767785155.json +23 -0
  7. package/.mentis/sessions/1769768745802.json +23 -0
  8. package/.mentis/sessions/1769769600884.json +31 -0
  9. package/.mentis/sessions/1769770030160.json +31 -0
  10. package/.mentis/sessions/1769770606004.json +78 -0
  11. package/.mentis/sessions/1769771084515.json +141 -0
  12. package/.mentis/sessions/1769881926630.json +57 -0
  13. package/ARCHITECTURE.md +267 -0
  14. package/CONTRIBUTING.md +209 -0
  15. package/README.md +17 -0
  16. package/dist/checkpoint/CheckpointManager.js +92 -0
  17. package/dist/commands/Command.js +15 -1
  18. package/dist/commands/CommandManager.js +30 -5
  19. package/dist/commands/__tests__/CommandManager.test.js +70 -0
  20. package/dist/debug_google.js +61 -0
  21. package/dist/debug_lite.js +49 -0
  22. package/dist/debug_lite_headers.js +57 -0
  23. package/dist/debug_search.js +16 -0
  24. package/dist/index.js +33 -0
  25. package/dist/mcp/JsonRpcClient.js +16 -0
  26. package/dist/mcp/McpConfig.js +132 -0
  27. package/dist/mcp/McpManager.js +189 -0
  28. package/dist/repl/PersistentShell.js +20 -1
  29. package/dist/repl/ReplManager.js +410 -138
  30. package/dist/skills/Skill.js +17 -2
  31. package/dist/skills/SkillsManager.js +28 -3
  32. package/dist/skills/__tests__/SkillsManager.test.js +62 -0
  33. package/dist/tools/AskQuestionTool.js +172 -0
  34. package/dist/tools/EditFileTool.js +141 -0
  35. package/dist/tools/FileTools.js +7 -1
  36. package/dist/tools/PlanModeTool.js +53 -0
  37. package/dist/tools/WebSearchTool.js +190 -27
  38. package/dist/ui/DiffViewer.js +110 -0
  39. package/dist/ui/InputBox.js +16 -2
  40. package/dist/ui/MultiFileSelector.js +123 -0
  41. package/dist/ui/PlanModeUI.js +105 -0
  42. package/dist/ui/ToolExecutor.js +154 -0
  43. package/dist/ui/UIManager.js +12 -2
  44. package/dist/utils/__mocks__/chalk.js +20 -0
  45. package/dist/utils/__tests__/ContextVisualizer.test.js +95 -0
  46. package/docs/MCP_INTEGRATION.md +290 -0
  47. package/google_dump.html +18 -0
  48. package/lite_dump.html +176 -0
  49. package/lite_headers_dump.html +176 -0
  50. package/package.json +34 -2
  51. package/scripts/test_exa_mcp.ts +90 -0
  52. package/src/checkpoint/CheckpointManager.ts +102 -0
  53. package/src/commands/Command.ts +64 -13
  54. package/src/commands/CommandManager.ts +26 -5
  55. package/src/commands/__tests__/CommandManager.test.ts +83 -0
  56. package/src/debug_google.ts +30 -0
  57. package/src/debug_lite.ts +18 -0
  58. package/src/debug_lite_headers.ts +25 -0
  59. package/src/debug_search.ts +18 -0
  60. package/src/index.ts +45 -1
  61. package/src/mcp/JsonRpcClient.ts +19 -0
  62. package/src/mcp/McpConfig.ts +153 -0
  63. package/src/mcp/McpManager.ts +224 -0
  64. package/src/repl/PersistentShell.ts +24 -1
  65. package/src/repl/ReplManager.ts +1521 -1204
  66. package/src/skills/Skill.ts +91 -11
  67. package/src/skills/SkillsManager.ts +25 -3
  68. package/src/skills/__tests__/SkillsManager.test.ts +73 -0
  69. package/src/tools/AskQuestionTool.ts +197 -0
  70. package/src/tools/EditFileTool.ts +172 -0
  71. package/src/tools/FileTools.ts +3 -0
  72. package/src/tools/PlanModeTool.ts +50 -0
  73. package/src/tools/WebSearchTool.ts +235 -63
  74. package/src/ui/DiffViewer.ts +117 -0
  75. package/src/ui/InputBox.ts +17 -2
  76. package/src/ui/MultiFileSelector.ts +135 -0
  77. package/src/ui/PlanModeUI.ts +121 -0
  78. package/src/ui/ToolExecutor.ts +182 -0
  79. package/src/ui/UIManager.ts +15 -2
  80. package/src/utils/__mocks__/chalk.ts +19 -0
  81. package/src/utils/__tests__/ContextVisualizer.test.ts +118 -0
  82. package/console.log(tick) +0 -0
@@ -1,6 +1,21 @@
1
1
  "use strict";
2
2
  /**
3
- * Skill data structure for Agent Skills system
4
- * Based on Claude Code's Agent Skills format
3
+ * Agent Skills System
4
+ *
5
+ * Based on Claude Code's Agent Skills format. Skills are reusable AI agent
6
+ * configurations stored as SKILL.md files in dedicated directories.
7
+ *
8
+ * @packageDocumentation
9
+ *
10
+ * @example
11
+ * ```markdown
12
+ * ---
13
+ * name: code-reviewer
14
+ * description: Use when the user asks for a code review. Examines code for bugs, style issues, and improvements.
15
+ * allowed-tools: [Read, Grep, Glob]
16
+ * ---
17
+ *
18
+ * Review the code for...
19
+ * ```
5
20
  */
6
21
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -60,10 +60,20 @@ class SkillsManager {
60
60
  } = options;
61
61
  const discovered = [];
62
62
  if (includePersonal) {
63
- discovered.push(...await this.discoverSkillsInDirectory(this.personalSkillsDir, 'personal'));
63
+ try {
64
+ discovered.push(...await this.discoverSkillsInDirectory(this.personalSkillsDir, 'personal'));
65
+ }
66
+ catch (error) {
67
+ console.warn(`Warning: Failed to load personal skills from ${this.personalSkillsDir}: ${error.message}`);
68
+ }
64
69
  }
65
70
  if (includeProject) {
66
- discovered.push(...await this.discoverSkillsInDirectory(this.projectSkillsDir, 'project'));
71
+ try {
72
+ discovered.push(...await this.discoverSkillsInDirectory(this.projectSkillsDir, 'project'));
73
+ }
74
+ catch (error) {
75
+ console.warn(`Warning: Failed to load project skills from ${this.projectSkillsDir}: ${error.message}`);
76
+ }
67
77
  }
68
78
  // Store skills in map for quick lookup
69
79
  for (const skill of discovered) {
@@ -109,6 +119,7 @@ class SkillsManager {
109
119
  const content = fs.readFileSync(skillPath, 'utf-8');
110
120
  const frontmatter = this.extractFrontmatter(content);
111
121
  if (!frontmatter) {
122
+ console.warn(`Warning: Invalid or missing frontmatter in ${skillPath} (skipping)`);
112
123
  return null;
113
124
  }
114
125
  // Convert SkillFrontmatter to SkillMetadata for validation
@@ -128,10 +139,24 @@ class SkillsManager {
128
139
  isValid: validation.isValid,
129
140
  errors: validation.errors.length > 0 ? validation.errors : undefined
130
141
  };
142
+ // Log validation warnings
143
+ if (validation.warnings.length > 0) {
144
+ for (const warning of validation.warnings) {
145
+ console.warn(`Warning (${skill.name}): ${warning}`);
146
+ }
147
+ }
131
148
  return skill;
132
149
  }
133
150
  catch (error) {
134
- console.error(`Error loading skill metadata from ${skillPath}: ${error.message}`);
151
+ if (error.code === 'ENOENT') {
152
+ console.warn(`Warning: Skill file not found: ${skillPath}`);
153
+ }
154
+ else if (error.code === 'EACCES') {
155
+ console.warn(`Warning: Permission denied reading skill: ${skillPath}`);
156
+ }
157
+ else {
158
+ console.error(`Error loading skill metadata from ${skillPath}: ${error.message}`);
159
+ }
135
160
  return null;
136
161
  }
137
162
  }
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ /**
3
+ * Tests for SkillsManager
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const SkillsManager_1 = require("../SkillsManager");
7
+ describe('SkillsManager', () => {
8
+ let manager;
9
+ beforeEach(() => {
10
+ manager = new SkillsManager_1.SkillsManager();
11
+ });
12
+ describe('getAllSkills', () => {
13
+ it('should return empty array initially', () => {
14
+ const skills = manager.getAllSkills();
15
+ expect(skills).toEqual([]);
16
+ });
17
+ });
18
+ describe('getSkill', () => {
19
+ it('should return undefined for non-existent skill', () => {
20
+ const skill = manager.getSkill('non-existent');
21
+ expect(skill).toBeUndefined();
22
+ });
23
+ });
24
+ describe('getSkillsContext', () => {
25
+ it('should return empty string when no skills', () => {
26
+ const context = manager.getSkillsContext();
27
+ expect(context).toBe('');
28
+ });
29
+ });
30
+ describe('addSkill', () => {
31
+ it('should add skill to manager', () => {
32
+ const skill = {
33
+ name: 'test-skill',
34
+ description: 'Test skill',
35
+ path: '/skills/test.md',
36
+ type: 'personal',
37
+ directory: '/skills',
38
+ isValid: true,
39
+ content: 'Test content'
40
+ };
41
+ manager['skills'].set('test-skill', skill);
42
+ const retrieved = manager.getSkill('test-skill');
43
+ expect(retrieved).toBeDefined();
44
+ expect(retrieved?.name).toBe('test-skill');
45
+ });
46
+ it('should return skills context with skills', () => {
47
+ const skill = {
48
+ name: 'reviewer',
49
+ description: 'Code reviewer skill',
50
+ path: '/skills/reviewer.md',
51
+ type: 'personal',
52
+ directory: '/skills',
53
+ isValid: true,
54
+ content: 'Review code'
55
+ };
56
+ manager['skills'].set('reviewer', skill);
57
+ const context = manager.getSkillsContext();
58
+ expect(context).toContain('Available Skills');
59
+ expect(context).toContain('reviewer');
60
+ });
61
+ });
62
+ });
@@ -0,0 +1,172 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.AskQuestionTool = void 0;
7
+ const inquirer_1 = __importDefault(require("inquirer"));
8
+ const chalk_1 = __importDefault(require("chalk"));
9
+ /**
10
+ * AskQuestionTool - Allows the AI to ask clarifying questions
11
+ * This enables interactive plan mode where AI can gather requirements
12
+ */
13
+ class AskQuestionTool {
14
+ constructor() {
15
+ this.name = 'ask_question';
16
+ this.description = 'Ask the user a clarifying question. Use this in plan mode to gather requirements before implementation. Supports: text, confirm (yes/no), list (single choice), checkbox (multi-select).';
17
+ this.parameters = {
18
+ type: 'object',
19
+ properties: {
20
+ question: {
21
+ type: 'string',
22
+ description: 'The question to ask the user'
23
+ },
24
+ type: {
25
+ type: 'string',
26
+ enum: ['text', 'confirm', 'list', 'checkbox'],
27
+ description: 'Type of question: text (free input), confirm (yes/no), list (single choice), checkbox (multi-select)'
28
+ },
29
+ options: {
30
+ type: 'array',
31
+ items: {
32
+ type: 'object',
33
+ properties: {
34
+ label: { type: 'string', description: 'Display text for the option' },
35
+ description: { type: 'string', description: 'Additional context (optional)' }
36
+ }
37
+ },
38
+ description: 'Options for list/checkbox questions. Required for list/checkbox types.'
39
+ },
40
+ default: {
41
+ oneOf: [{ type: 'string' }, { type: 'boolean' }, { type: 'array' }],
42
+ description: 'Default value (optional)'
43
+ }
44
+ },
45
+ required: ['question', 'type']
46
+ };
47
+ }
48
+ /**
49
+ * Execute the question and return the user's answer
50
+ */
51
+ async execute(args) {
52
+ const questionType = args.type || 'text';
53
+ // Show question header
54
+ console.log('');
55
+ console.log(chalk_1.default.cyan('🤔 Question from AI:'));
56
+ console.log(chalk_1.default.gray('─'.repeat(60)));
57
+ try {
58
+ let result;
59
+ switch (questionType) {
60
+ case 'text':
61
+ result = await this.askText(args.question, args.default);
62
+ break;
63
+ case 'confirm':
64
+ result = await this.askConfirm(args.question, args.default);
65
+ break;
66
+ case 'list':
67
+ if (!args.options || args.options.length === 0) {
68
+ return 'Error: list questions require options';
69
+ }
70
+ result = await this.askList(args.question, args.options, args.default);
71
+ break;
72
+ case 'checkbox':
73
+ if (!args.options || args.options.length === 0) {
74
+ return 'Error: checkbox questions require options';
75
+ }
76
+ result = await this.askCheckbox(args.question, args.options, args.default);
77
+ break;
78
+ default:
79
+ return `Error: Unknown question type: ${questionType}`;
80
+ }
81
+ console.log(chalk_1.default.gray('─'.repeat(60)));
82
+ // Format result as string for return to LLM
83
+ return this.formatResult(result, questionType);
84
+ }
85
+ catch (error) {
86
+ return `Error asking question: ${error.message}`;
87
+ }
88
+ }
89
+ /**
90
+ * Ask a free-text question
91
+ */
92
+ async askText(question, defaultValue) {
93
+ const { answer } = await inquirer_1.default.prompt([
94
+ {
95
+ type: 'input',
96
+ name: 'answer',
97
+ message: question,
98
+ default: defaultValue
99
+ }
100
+ ]);
101
+ return answer;
102
+ }
103
+ /**
104
+ * Ask a yes/no confirmation
105
+ */
106
+ async askConfirm(question, defaultValue) {
107
+ const { answer } = await inquirer_1.default.prompt([
108
+ {
109
+ type: 'confirm',
110
+ name: 'answer',
111
+ message: question,
112
+ default: defaultValue ?? true
113
+ }
114
+ ]);
115
+ return answer;
116
+ }
117
+ /**
118
+ * Ask a single-choice list question
119
+ */
120
+ async askList(question, options, defaultValue) {
121
+ const { answer } = await inquirer_1.default.prompt([
122
+ {
123
+ type: 'list',
124
+ name: 'answer',
125
+ message: question,
126
+ choices: options.map((opt, i) => ({
127
+ name: opt.label,
128
+ value: opt.label,
129
+ short: opt.label
130
+ })),
131
+ default: defaultValue
132
+ }
133
+ ]);
134
+ return answer;
135
+ }
136
+ /**
137
+ * Ask a multi-select checkbox question
138
+ */
139
+ async askCheckbox(question, options, defaultValue) {
140
+ const { answer } = await inquirer_1.default.prompt([
141
+ {
142
+ type: 'checkbox',
143
+ name: 'answer',
144
+ message: question,
145
+ choices: options.map((opt, i) => ({
146
+ name: opt.label,
147
+ value: opt.label,
148
+ checked: defaultValue?.includes(opt.label),
149
+ short: opt.label
150
+ }))
151
+ }
152
+ ]);
153
+ return answer;
154
+ }
155
+ /**
156
+ * Format the result for return to the LLM
157
+ */
158
+ formatResult(result, questionType) {
159
+ switch (questionType) {
160
+ case 'confirm':
161
+ return result === true ? 'Yes' : 'No';
162
+ case 'checkbox':
163
+ if (Array.isArray(result) && result.length > 0) {
164
+ return `Selected: ${result.join(', ')}`;
165
+ }
166
+ return 'None selected';
167
+ default:
168
+ return String(result);
169
+ }
170
+ }
171
+ }
172
+ exports.AskQuestionTool = AskQuestionTool;
@@ -0,0 +1,141 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EditFileTool = void 0;
4
+ const fs_1 = require("fs");
5
+ const path_1 = require("path");
6
+ /**
7
+ * EditFileTool - Performs string replacement in files (like Claude's Edit tool)
8
+ * Returns a unified diff preview instead of writing immediately
9
+ */
10
+ class EditFileTool {
11
+ constructor() {
12
+ this.name = 'edit_file';
13
+ this.description = 'Make targeted edits to files using string replacement. Returns diff preview. Requires approval before writing.';
14
+ this.parameters = {
15
+ type: 'object',
16
+ properties: {
17
+ file_path: {
18
+ type: 'string',
19
+ description: 'The path to the file to edit'
20
+ },
21
+ old_string: {
22
+ type: 'string',
23
+ description: 'The exact string to replace. Must match exactly (including whitespace).'
24
+ },
25
+ new_string: {
26
+ type: 'string',
27
+ description: 'The new string to replace old_string with.'
28
+ },
29
+ auto_format: {
30
+ type: 'boolean',
31
+ description: 'Auto-format code after edit (default: false)'
32
+ }
33
+ },
34
+ required: ['file_path', 'old_string', 'new_string']
35
+ };
36
+ }
37
+ /**
38
+ * Execute the edit and return a diff preview
39
+ * Note: This does NOT write the file - it returns what WOULD change
40
+ * The caller (ReplManager) should handle approval before calling applyEdit()
41
+ */
42
+ async execute(args) {
43
+ const filePath = (0, path_1.resolve)(process.cwd(), args.file_path);
44
+ if (!(0, fs_1.existsSync)(filePath)) {
45
+ return `Error: File not found: ${args.file_path}`;
46
+ }
47
+ const originalContent = (0, fs_1.readFileSync)(filePath, 'utf-8');
48
+ if (!originalContent.includes(args.old_string)) {
49
+ return `Error: old_string not found in file. The string must match exactly (including whitespace and indentation).`;
50
+ }
51
+ // Count occurrences
52
+ const occurrences = (originalContent.match(new RegExp(this.escapeRegex(args.old_string), 'g')) || []).length;
53
+ if (occurrences > 1) {
54
+ return `Warning: old_string found ${occurrences} times. All occurrences will be replaced.\n\n${this.generateDiff(originalContent, args.old_string, args.new_string, args.file_path)}`;
55
+ }
56
+ // Generate and return diff
57
+ return this.generateDiff(originalContent, args.old_string, args.new_string, args.file_path);
58
+ }
59
+ /**
60
+ * Apply the edit after approval
61
+ * This should be called after user approves the diff
62
+ */
63
+ applyEdit(args) {
64
+ const filePath = (0, path_1.resolve)(process.cwd(), args.file_path);
65
+ if (!(0, fs_1.existsSync)(filePath)) {
66
+ return { success: false, message: `File not found: ${args.file_path}` };
67
+ }
68
+ const originalContent = (0, fs_1.readFileSync)(filePath, 'utf-8');
69
+ if (!originalContent.includes(args.old_string)) {
70
+ return { success: false, message: 'old_string not found in file' };
71
+ }
72
+ const newContent = originalContent.replace(args.old_string, args.new_string);
73
+ (0, fs_1.writeFileSync)(filePath, newContent, 'utf-8');
74
+ return {
75
+ success: true,
76
+ message: `Successfully edited ${args.file_path}`
77
+ };
78
+ }
79
+ /**
80
+ * Generate a unified diff preview
81
+ */
82
+ generateDiff(content, oldString, newString, filePath) {
83
+ const lines = content.split('\n');
84
+ const oldLines = oldString.split('\n');
85
+ const newLines = newString.split('\n');
86
+ // Find the line number where old_string starts
87
+ let startLine = -1;
88
+ for (let i = 0; i <= lines.length - oldLines.length; i++) {
89
+ let match = true;
90
+ for (let j = 0; j < oldLines.length; j++) {
91
+ if (lines[i + j] !== oldLines[j]) {
92
+ match = false;
93
+ break;
94
+ }
95
+ }
96
+ if (match) {
97
+ startLine = i;
98
+ break;
99
+ }
100
+ }
101
+ if (startLine === -1) {
102
+ return 'Error: Could not locate old_string in file';
103
+ }
104
+ // Build unified diff
105
+ let diff = `\n${'─'.repeat(60)}\n`;
106
+ diff += `📝 Edit Preview: ${filePath}\n`;
107
+ diff += `${'─'.repeat(60)}\n`;
108
+ diff += `Line ${startLine + 1}:\n\n`;
109
+ // Show context (2 lines before)
110
+ const contextStart = Math.max(0, startLine - 2);
111
+ if (contextStart < startLine) {
112
+ for (let i = contextStart; i < startLine; i++) {
113
+ diff += ` ${lines[i]}\n`;
114
+ }
115
+ }
116
+ // Show removed lines (red)
117
+ for (const line of oldLines) {
118
+ diff += `\x1b[31m- ${line}\x1b[0m\n`;
119
+ }
120
+ // Show added lines (green)
121
+ for (const line of newLines) {
122
+ diff += `\x1b[32m+ ${line}\x1b[0m\n`;
123
+ }
124
+ // Show context (2 lines after)
125
+ const contextEnd = Math.min(lines.length, startLine + oldLines.length + 2);
126
+ if (startLine + oldLines.length < contextEnd) {
127
+ for (let i = startLine + oldLines.length; i < contextEnd; i++) {
128
+ diff += ` ${lines[i]}\n`;
129
+ }
130
+ }
131
+ diff += `${'─'.repeat(60)}\n`;
132
+ return diff;
133
+ }
134
+ /**
135
+ * Escape special regex characters
136
+ */
137
+ escapeRegex(str) {
138
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
139
+ }
140
+ }
141
+ exports.EditFileTool = EditFileTool;
@@ -3,9 +3,15 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.ListDirTool = exports.ReadFileTool = exports.WriteFileTool = void 0;
6
+ exports.ListDirTool = exports.ReadFileTool = exports.WriteFileTool = exports.PlanModeTool = exports.AskQuestionTool = exports.EditFileTool = void 0;
7
7
  const fs_extra_1 = __importDefault(require("fs-extra"));
8
8
  const path_1 = __importDefault(require("path"));
9
+ var EditFileTool_1 = require("./EditFileTool");
10
+ Object.defineProperty(exports, "EditFileTool", { enumerable: true, get: function () { return EditFileTool_1.EditFileTool; } });
11
+ var AskQuestionTool_1 = require("./AskQuestionTool");
12
+ Object.defineProperty(exports, "AskQuestionTool", { enumerable: true, get: function () { return AskQuestionTool_1.AskQuestionTool; } });
13
+ var PlanModeTool_1 = require("./PlanModeTool");
14
+ Object.defineProperty(exports, "PlanModeTool", { enumerable: true, get: function () { return PlanModeTool_1.PlanModeTool; } });
9
15
  class WriteFileTool {
10
16
  constructor() {
11
17
  this.name = 'write_file';
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.PlanModeTool = void 0;
7
+ const inquirer_1 = __importDefault(require("inquirer"));
8
+ const chalk_1 = __importDefault(require("chalk"));
9
+ const PlanModeUI_1 = require("../ui/PlanModeUI");
10
+ /**
11
+ * PlanModeTool - Allows AI to suggest switching to plan mode
12
+ * Use this when the task is complex, requires architecture design, or needs requirements gathering
13
+ */
14
+ class PlanModeTool {
15
+ constructor() {
16
+ this.name = 'enter_plan_mode';
17
+ this.description = 'Suggest switching to plan mode for complex tasks. Call this when you need to gather requirements, design architecture, or break down a complex implementation before coding.';
18
+ this.parameters = {
19
+ type: 'object',
20
+ properties: {
21
+ reason: {
22
+ type: 'string',
23
+ description: 'Explain why plan mode is recommended for this task'
24
+ }
25
+ },
26
+ required: ['reason']
27
+ };
28
+ }
29
+ /**
30
+ * Execute - Ask user if they want to switch to plan mode
31
+ */
32
+ async execute(args) {
33
+ console.log('');
34
+ console.log(chalk_1.default.cyan('🎯 AI suggests entering PLAN MODE'));
35
+ console.log(chalk_1.default.gray('─'.repeat(60)));
36
+ console.log(chalk_1.default.dim(`Reason: ${args.reason}`));
37
+ console.log(chalk_1.default.gray('─'.repeat(60)));
38
+ const { confirm } = await inquirer_1.default.prompt([
39
+ {
40
+ type: 'confirm',
41
+ name: 'confirm',
42
+ message: 'Switch to plan mode?',
43
+ default: true
44
+ }
45
+ ]);
46
+ if (confirm) {
47
+ PlanModeUI_1.PlanModeUI.showPlanHeader();
48
+ return 'User approved. Switching to plan mode for requirements gathering and planning.';
49
+ }
50
+ return 'User declined. Continuing in current mode.';
51
+ }
52
+ }
53
+ exports.PlanModeTool = PlanModeTool;