janito 1.7.0__py3-none-any.whl → 1.8.1__py3-none-any.whl

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 (115) hide show
  1. janito/__init__.py +1 -1
  2. janito/agent/config.py +1 -1
  3. janito/agent/config_defaults.py +2 -2
  4. janito/agent/conversation.py +70 -27
  5. janito/agent/conversation_api.py +104 -4
  6. janito/agent/conversation_exceptions.py +6 -0
  7. janito/agent/conversation_tool_calls.py +17 -3
  8. janito/agent/event.py +24 -0
  9. janito/agent/event_dispatcher.py +24 -0
  10. janito/agent/event_handler_protocol.py +5 -0
  11. janito/agent/event_system.py +15 -0
  12. janito/agent/message_handler.py +4 -1
  13. janito/agent/message_handler_protocol.py +5 -0
  14. janito/agent/openai_client.py +5 -8
  15. janito/agent/openai_schema_generator.py +23 -4
  16. janito/agent/profile_manager.py +15 -83
  17. janito/agent/queued_message_handler.py +22 -3
  18. janito/agent/rich_message_handler.py +66 -72
  19. janito/agent/templates/profiles/system_prompt_template_base.txt.j2 +14 -0
  20. janito/agent/templates/profiles/system_prompt_template_base_pt.txt.j2 +13 -0
  21. janito/agent/test_handler_protocols.py +47 -0
  22. janito/agent/tests/__init__.py +1 -0
  23. janito/agent/tool_base.py +1 -1
  24. janito/agent/tool_executor.py +109 -0
  25. janito/agent/tool_registry.py +3 -75
  26. janito/agent/tool_use_tracker.py +46 -0
  27. janito/agent/tools/__init__.py +8 -9
  28. janito/agent/tools/ask_user.py +19 -11
  29. janito/agent/tools/create_directory.py +43 -28
  30. janito/agent/tools/create_file.py +60 -29
  31. janito/agent/tools/dir_walk_utils.py +16 -0
  32. janito/agent/tools/fetch_url.py +10 -11
  33. janito/agent/tools/find_files.py +49 -32
  34. janito/agent/tools/get_lines.py +54 -18
  35. janito/agent/tools/memory.py +32 -52
  36. janito/agent/tools/move_file.py +72 -23
  37. janito/agent/tools/outline_file/__init__.py +85 -0
  38. janito/agent/tools/outline_file/formatting.py +20 -0
  39. janito/agent/tools/outline_file/markdown_outline.py +14 -0
  40. janito/agent/tools/outline_file/python_outline.py +71 -0
  41. janito/agent/tools/present_choices.py +62 -0
  42. janito/agent/tools/present_choices_test.py +18 -0
  43. janito/agent/tools/remove_directory.py +31 -26
  44. janito/agent/tools/remove_file.py +31 -13
  45. janito/agent/tools/replace_text_in_file.py +135 -36
  46. janito/agent/tools/run_bash_command.py +47 -50
  47. janito/agent/tools/run_powershell_command.py +52 -36
  48. janito/agent/tools/run_python_command.py +49 -29
  49. janito/agent/tools/search_outline.py +17 -0
  50. janito/agent/tools/search_text.py +208 -0
  51. janito/agent/tools/tools_utils.py +47 -4
  52. janito/agent/tools/utils.py +14 -15
  53. janito/agent/tools/validate_file_syntax.py +163 -0
  54. janito/cli/arg_parser.py +36 -4
  55. janito/cli/logging_setup.py +7 -2
  56. janito/cli/main.py +96 -2
  57. janito/cli/runner/_termweb_log_utils.py +17 -0
  58. janito/cli/runner/cli_main.py +119 -77
  59. janito/cli/runner/config.py +2 -2
  60. janito/cli/termweb_starter.py +73 -0
  61. janito/cli_chat_shell/chat_loop.py +42 -7
  62. janito/cli_chat_shell/chat_state.py +1 -1
  63. janito/cli_chat_shell/chat_ui.py +0 -1
  64. janito/cli_chat_shell/commands/__init__.py +15 -6
  65. janito/cli_chat_shell/commands/{history_reset.py → history_start.py} +13 -5
  66. janito/cli_chat_shell/commands/lang.py +16 -0
  67. janito/cli_chat_shell/commands/prompt.py +42 -0
  68. janito/cli_chat_shell/commands/session_control.py +36 -1
  69. janito/cli_chat_shell/commands/termweb_log.py +86 -0
  70. janito/cli_chat_shell/commands/utility.py +5 -2
  71. janito/cli_chat_shell/commands/verbose.py +29 -0
  72. janito/cli_chat_shell/session_manager.py +9 -1
  73. janito/cli_chat_shell/shell_command_completer.py +20 -0
  74. janito/cli_chat_shell/ui.py +110 -99
  75. janito/i18n/__init__.py +35 -0
  76. janito/i18n/messages.py +23 -0
  77. janito/i18n/pt.py +46 -0
  78. janito/rich_utils.py +43 -43
  79. janito/termweb/app.py +95 -0
  80. janito/termweb/static/editor.html +238 -0
  81. janito/termweb/static/editor.html.bak +238 -0
  82. janito/termweb/static/explorer.html.bak +59 -0
  83. janito/termweb/static/favicon.ico +0 -0
  84. janito/termweb/static/favicon.ico.bak +0 -0
  85. janito/termweb/static/index.html +55 -0
  86. janito/termweb/static/index.html.bak +55 -0
  87. janito/termweb/static/index.html.bak.bak +175 -0
  88. janito/termweb/static/landing.html.bak +36 -0
  89. janito/termweb/static/termicon.svg +1 -0
  90. janito/termweb/static/termweb.css +235 -0
  91. janito/termweb/static/termweb.css.bak +286 -0
  92. janito/termweb/static/termweb.js +187 -0
  93. janito/termweb/static/termweb.js.bak +187 -0
  94. janito/termweb/static/termweb.js.bak.bak +157 -0
  95. janito/termweb/static/termweb_quickopen.js +135 -0
  96. janito/termweb/static/termweb_quickopen.js.bak +125 -0
  97. janito/web/app.py +4 -4
  98. {janito-1.7.0.dist-info → janito-1.8.1.dist-info}/METADATA +61 -26
  99. janito-1.8.1.dist-info/RECORD +127 -0
  100. {janito-1.7.0.dist-info → janito-1.8.1.dist-info}/WHEEL +1 -1
  101. janito/agent/templates/profiles/system_prompt_template_base.toml +0 -76
  102. janito/agent/templates/profiles/system_prompt_template_default.toml +0 -3
  103. janito/agent/templates/profiles/system_prompt_template_technical.toml +0 -13
  104. janito/agent/tests/test_prompt_toml.py +0 -61
  105. janito/agent/tool_registry_core.py +0 -2
  106. janito/agent/tools/get_file_outline.py +0 -146
  107. janito/agent/tools/py_compile_file.py +0 -40
  108. janito/agent/tools/replace_file.py +0 -51
  109. janito/agent/tools/search_files.py +0 -65
  110. janito/cli/runner/scan.py +0 -57
  111. janito/cli_chat_shell/commands/system.py +0 -73
  112. janito-1.7.0.dist-info/RECORD +0 -89
  113. {janito-1.7.0.dist-info → janito-1.8.1.dist-info}/entry_points.txt +0 -0
  114. {janito-1.7.0.dist-info → janito-1.8.1.dist-info}/licenses/LICENSE +0 -0
  115. {janito-1.7.0.dist-info → janito-1.8.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,157 @@
1
+ // Directory/File browser logic for explorer with right-side preview and URL sync (using /path)
2
+ let explorerView = localStorage.getItem('explorerView') || 'list';
3
+ let currentExplorerPath = '.';
4
+
5
+ function getParentPath(path) {
6
+ // Normalize slashes
7
+ path = (path || '').replace(/\\/g, '/').replace(/^\/+|\/+$/g, '');
8
+ if (!path || path === '.' || path === '') return '.';
9
+ const parts = path.split('/');
10
+ if (parts.length <= 1) return '.';
11
+ parts.pop();
12
+ const parent = parts.join('/');
13
+ return parent === '' ? '.' : parent;
14
+ }
15
+
16
+
17
+ function setExplorerView(view) {
18
+ explorerView = view;
19
+ localStorage.setItem('explorerView', view);
20
+ document.getElementById('view-list').classList.toggle('active', view === 'list');
21
+ document.getElementById('view-icons').classList.toggle('active', view === 'icons');
22
+ }
23
+
24
+ function normalizeExplorerPath(path) {
25
+ if (!path || path === '/' || path === '' || path === '.') return '.';
26
+ return path.replace(/^\/+|\/+$/g, '');
27
+ }
28
+
29
+ function updateExplorerUrl(path, push=true) {
30
+ let url = '/';
31
+ if (path && path !== '.' && path !== '/') {
32
+ url = '/' + path.replace(/^\/+|\/+$/g, '');
33
+ }
34
+ if (push) {
35
+ console.log('[DEBUG] updateExplorerUrl: pushing url', url, 'for path', path);
36
+ window.history.pushState({ explorerPath: path }, '', url);
37
+ } else {
38
+ console.log('[DEBUG] updateExplorerUrl: not pushing, url would be', url, 'for path', path);
39
+ }
40
+ }
41
+
42
+ function renderExplorer(path, pushUrl=true) {
43
+ currentExplorerPath = normalizeExplorerPath(path);
44
+ console.log('[DEBUG] renderExplorer: path', path, 'normalized to', currentExplorerPath, 'pushUrl:', pushUrl);
45
+ fetch(`/api/explorer/${encodeURIComponent(currentExplorerPath)}`)
46
+ .then(resp => resp.json())
47
+ .then(data => {
48
+ const main = document.getElementById('explorer-main');
49
+ if (!main) return;
50
+ if (data.error) {
51
+ main.innerHTML = `<div class='error'>${data.error}</div>`;
52
+ return;
53
+ }
54
+ if (data.type === 'dir') {
55
+ let html = `<h3>Directory: ${data.path}</h3>`;
56
+ if (explorerView === 'list') {
57
+ html += `<ul class='explorer-list'>`;
58
+ if (data.path !== '.') {
59
+ const parent = getParentPath(data.path);
60
+ html += `<li><a href='#' data-path='${parent}' class='explorer-link'>(.. parent)</a></li>`;
61
+ }
62
+ for (const entry of data.entries) {
63
+ const entryPath = data.path === '.' ? entry.name : data.path + '/' + entry.name;
64
+ if (entry.is_dir) {
65
+ html += `<li><a href='#' data-path='${entryPath}' class='explorer-link'>📁 ${entry.name}</a></li>`;
66
+ } else {
67
+ html += `<li><a href='#' data-path='${entryPath}' class='explorer-link file-link'>📄 ${entry.name}</a></li>`;
68
+ }
69
+ }
70
+ html += '</ul>';
71
+ } else {
72
+ html += `<div class='explorer-icons'>`;
73
+ if (data.path !== '.') {
74
+ const parent = getParentPath(data.path);
75
+ html += `<div class='explorer-icon'><a href='#' data-path='${parent}' class='explorer-link' title='Parent'>(..)</a></div>`;
76
+ }
77
+ for (const entry of data.entries) {
78
+ const entryPath = data.path === '.' ? entry.name : data.path + '/' + entry.name;
79
+ if (entry.is_dir) {
80
+ html += `<div class='explorer-icon'><a href='#' data-path='${entryPath}' class='explorer-link' title='${entry.name}'>📁<br>${entry.name}</a></div>`;
81
+ } else {
82
+ html += `<div class='explorer-icon'><a href='#' data-path='${entryPath}' class='explorer-link file-link' title='${entry.name}'>📄<br>${entry.name}</a></div>`;
83
+ }
84
+ }
85
+ html += '</div>';
86
+ }
87
+ main.innerHTML = html;
88
+ // Clear preview panel when changing directories
89
+ const preview = document.getElementById('explorer-preview');
90
+ if (preview) preview.innerHTML = '';
91
+ if (pushUrl) updateExplorerUrl(currentExplorerPath, true);
92
+ }
93
+ // Attach click handlers
94
+ document.querySelectorAll('.explorer-link').forEach(link => {
95
+ link.onclick = function(e) {
96
+ e.preventDefault();
97
+ const p = this.getAttribute('data-path');
98
+ // If file, show preview; if dir, update explorer
99
+ if (this.classList.contains('file-link')) {
100
+ const preview = document.getElementById('explorer-preview');
101
+ if (preview) {
102
+ preview.innerHTML = `<div class='spinner' style='display:inline-block;vertical-align:middle;'></div> <span style='vertical-align:middle;'>Loading file...</span>`;
103
+ }
104
+ fetch(`/api/explorer/${encodeURIComponent(p)}`)
105
+ .then(resp => resp.json())
106
+ .then(fileData => {
107
+ if (preview && fileData.type === 'file') {
108
+ preview.innerHTML = `<h3>File: ${fileData.path}</h3><pre class='explorer-file'>${escapeHtml(fileData.content)}</pre>`;
109
+ }
110
+ });
111
+ } else {
112
+ console.log('[DEBUG] explorer-link click: navigating to dir', p);
113
+ renderExplorer(p, true);
114
+ }
115
+ };
116
+ });
117
+ });
118
+ }
119
+
120
+ function escapeHtml(text) {
121
+ const map = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#039;' };
122
+ return text.replace(/[&<>"']'/g, m => map[m]);
123
+ }
124
+
125
+ function getExplorerPathFromLocation() {
126
+ let path = window.location.pathname;
127
+ // Remove leading slash
128
+ if (path.startsWith('/')) path = path.slice(1);
129
+ // If empty, root, or dot, treat as '.'
130
+ if (!path || path === '' || path === '.' || path === '/') return '.';
131
+ // Remove trailing slashes
132
+ path = path.replace(/\/+$/g, '');
133
+ console.log('[DEBUG] getExplorerPathFromLocation: extracted', path, 'from', window.location.pathname);
134
+ return path;
135
+ }
136
+
137
+ document.addEventListener('DOMContentLoaded', function() {
138
+ const initialPath = getExplorerPathFromLocation();
139
+ console.log('[DEBUG] DOMContentLoaded: initialPath', initialPath);
140
+ currentExplorerPath = initialPath; // Ensure currentExplorerPath matches URL
141
+ setExplorerView(localStorage.getItem('explorerView') || 'list');
142
+ renderExplorer(initialPath, false);
143
+ document.getElementById('view-list').onclick = function() {
144
+ setExplorerView('list');
145
+ renderExplorer(currentExplorerPath, false);
146
+ };
147
+ document.getElementById('view-icons').onclick = function() {
148
+ setExplorerView('icons');
149
+ renderExplorer(currentExplorerPath, false);
150
+ };
151
+ });
152
+
153
+ window.addEventListener('popstate', function(event) {
154
+ const path = (event.state && event.state.explorerPath) || getExplorerPathFromLocation();
155
+ console.log('[DEBUG] popstate: path', path, 'event.state:', event.state);
156
+ renderExplorer(path, false);
157
+ });
@@ -0,0 +1,135 @@
1
+ // Quick Open (Ctrl+P) modal logic for TermWeb
2
+ (function() {
3
+ let allFiles = [];
4
+ let modal = document.getElementById('quickopen-modal');
5
+ let input = document.getElementById('quickopen-input');
6
+ let results = document.getElementById('quickopen-results');
7
+ let selectedIdx = -1;
8
+ let loaded = false;
9
+
10
+ // Recursively fetch all file paths from /api/explorer/
11
+ async function fetchAllFiles(path = '.') {
12
+ let files = [];
13
+ try {
14
+ let resp = await fetch(`/api/explorer/${encodeURIComponent(path)}`);
15
+ let data = await resp.json();
16
+ if (data.type === 'dir' && data.entries) {
17
+ for (const entry of data.entries) {
18
+ const entryPath = path === '.' ? entry.name : path + '/' + entry.name;
19
+ if (entry.is_dir) {
20
+ files = files.concat(await fetchAllFiles(entryPath));
21
+ } else {
22
+ files.push(entryPath);
23
+ }
24
+ }
25
+ }
26
+ } catch (e) { console.error('QuickOpen fetch error', e); }
27
+ return files;
28
+ }
29
+
30
+ async function ensureLoaded() {
31
+ if (!loaded) {
32
+ allFiles = await fetchAllFiles('.');
33
+ loaded = true;
34
+ }
35
+ }
36
+
37
+ function showModal() {
38
+ modal.style.display = 'flex';
39
+ input.value = '';
40
+ results.innerHTML = '';
41
+ selectedIdx = -1;
42
+ setTimeout(() => input.focus(), 50);
43
+ }
44
+ function hideModal() {
45
+ modal.style.display = 'none';
46
+ }
47
+
48
+ function renderResults(filtered) {
49
+ results.innerHTML = '';
50
+ filtered.slice(0, 15).forEach((file, idx) => {
51
+ let li = document.createElement('li');
52
+ li.textContent = file;
53
+ li.tabIndex = -1;
54
+ li.className = 'quickopen-result' + (idx === selectedIdx ? ' selected' : '');
55
+ li.onclick = () => openFile(file);
56
+ results.appendChild(li);
57
+ });
58
+ }
59
+
60
+ // Improved openFile: poll for file link and click when ready
61
+ function openFile(path) {
62
+ hideModal();
63
+ window.renderExplorer && window.renderExplorer(getParentPath(path), false);
64
+ const maxAttempts = 20; // 20 x 100ms = 2s max
65
+ let attempts = 0;
66
+ function tryClickFile() {
67
+ let fileLinks = document.querySelectorAll(`a.file-link[data-path='${path}']`);
68
+ if (fileLinks.length) {
69
+ fileLinks[0].click();
70
+ // Optionally, add a highlight class
71
+ fileLinks[0].classList.add('quickopen-selected');
72
+ } else if (attempts < maxAttempts) {
73
+ attempts++;
74
+ setTimeout(tryClickFile, 100);
75
+ }
76
+ }
77
+ tryClickFile();
78
+ }
79
+
80
+ input.addEventListener('input', async function() {
81
+ await ensureLoaded();
82
+ let q = input.value.trim().toLowerCase();
83
+ let filtered = allFiles.filter(f => f.toLowerCase().includes(q));
84
+ selectedIdx = -1;
85
+ renderResults(filtered);
86
+ });
87
+ input.addEventListener('keydown', function(e) {
88
+ let items = results.querySelectorAll('li');
89
+ if (e.key === 'ArrowDown') {
90
+ selectedIdx = Math.min(selectedIdx + 1, items.length - 1);
91
+ renderResults(Array.from(items).map(li => li.textContent));
92
+ e.preventDefault();
93
+ } else if (e.key === 'ArrowUp') {
94
+ selectedIdx = Math.max(selectedIdx - 1, 0);
95
+ renderResults(Array.from(items).map(li => li.textContent));
96
+ e.preventDefault();
97
+ } else if (e.key === 'Enter') {
98
+ if (selectedIdx >= 0 && items[selectedIdx]) {
99
+ openFile(items[selectedIdx].textContent);
100
+ } else if (items.length === 1) {
101
+ openFile(items[0].textContent);
102
+ }
103
+ } else if (e.key === 'Escape') {
104
+ hideModal();
105
+ }
106
+ });
107
+
108
+ document.addEventListener('keydown', function(e) {
109
+ if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'p' && !e.shiftKey && !e.altKey) {
110
+ showModal();
111
+ e.preventDefault();
112
+ } else if (e.key === 'Escape' && modal.style.display === 'flex') {
113
+ hideModal();
114
+ }
115
+ });
116
+ modal.addEventListener('mousedown', function(e) {
117
+ if (e.target === modal) hideModal();
118
+ });
119
+
120
+ // Helper: get parent path
121
+ function getParentPath(path) {
122
+ path = (path || '').replace(/\\/g, '/').replace(/^\/+/g, '').replace(/\/+$/g, '');
123
+ if (!path || path === '.' || path === '') return '.';
124
+ const parts = path.split('/');
125
+ if (parts.length <= 1) return '.';
126
+ parts.pop();
127
+ const parent = parts.join('/');
128
+ return parent === '' ? '.' : parent;
129
+ }
130
+
131
+ // Style for selected result and quickopen-selected file link
132
+ let style = document.createElement('style');
133
+ style.textContent = `.quickopen-result.selected { background:#333; color:#8be9fd; } .quickopen-result { padding:0.3em 0.7em; cursor:pointer; border-radius:0.3em; } .quickopen-result:hover { background:#222; } a.file-link.quickopen-selected { background:#444 !important; color:#8be9fd !important; border-radius:0.3em; }`;
134
+ document.head.appendChild(style);
135
+ })();
@@ -0,0 +1,125 @@
1
+ // Quick Open (Ctrl+P) modal logic for TermWeb
2
+ (function() {
3
+ let allFiles = [];
4
+ let modal = document.getElementById('quickopen-modal');
5
+ let input = document.getElementById('quickopen-input');
6
+ let results = document.getElementById('quickopen-results');
7
+ let selectedIdx = -1;
8
+ let loaded = false;
9
+
10
+ // Recursively fetch all file paths from /api/explorer/
11
+ async function fetchAllFiles(path = '.') {
12
+ let files = [];
13
+ try {
14
+ let resp = await fetch(`/api/explorer/${encodeURIComponent(path)}`);
15
+ let data = await resp.json();
16
+ if (data.type === 'dir' && data.entries) {
17
+ for (const entry of data.entries) {
18
+ const entryPath = path === '.' ? entry.name : path + '/' + entry.name;
19
+ if (entry.is_dir) {
20
+ files = files.concat(await fetchAllFiles(entryPath));
21
+ } else {
22
+ files.push(entryPath);
23
+ }
24
+ }
25
+ }
26
+ } catch (e) { console.error('QuickOpen fetch error', e); }
27
+ return files;
28
+ }
29
+
30
+ async function ensureLoaded() {
31
+ if (!loaded) {
32
+ allFiles = await fetchAllFiles('.');
33
+ loaded = true;
34
+ }
35
+ }
36
+
37
+ function showModal() {
38
+ modal.style.display = 'flex';
39
+ input.value = '';
40
+ results.innerHTML = '';
41
+ selectedIdx = -1;
42
+ setTimeout(() => input.focus(), 50);
43
+ }
44
+ function hideModal() {
45
+ modal.style.display = 'none';
46
+ }
47
+
48
+ function renderResults(filtered) {
49
+ results.innerHTML = '';
50
+ filtered.slice(0, 15).forEach((file, idx) => {
51
+ let li = document.createElement('li');
52
+ li.textContent = file;
53
+ li.tabIndex = -1;
54
+ li.className = 'quickopen-result' + (idx === selectedIdx ? ' selected' : '');
55
+ li.onclick = () => openFile(file);
56
+ results.appendChild(li);
57
+ });
58
+ }
59
+
60
+ function openFile(path) {
61
+ hideModal();
62
+ // Simulate clicking the file in explorer
63
+ window.renderExplorer && window.renderExplorer(getParentPath(path), false);
64
+ setTimeout(() => {
65
+ let fileLinks = document.querySelectorAll(`a.file-link[data-path='${path}']`);
66
+ if (fileLinks.length) fileLinks[0].click();
67
+ }, 250);
68
+ }
69
+
70
+ input.addEventListener('input', async function() {
71
+ await ensureLoaded();
72
+ let q = input.value.trim().toLowerCase();
73
+ let filtered = allFiles.filter(f => f.toLowerCase().includes(q));
74
+ selectedIdx = -1;
75
+ renderResults(filtered);
76
+ });
77
+ input.addEventListener('keydown', function(e) {
78
+ let items = results.querySelectorAll('li');
79
+ if (e.key === 'ArrowDown') {
80
+ selectedIdx = Math.min(selectedIdx + 1, items.length - 1);
81
+ renderResults(Array.from(items).map(li => li.textContent));
82
+ e.preventDefault();
83
+ } else if (e.key === 'ArrowUp') {
84
+ selectedIdx = Math.max(selectedIdx - 1, 0);
85
+ renderResults(Array.from(items).map(li => li.textContent));
86
+ e.preventDefault();
87
+ } else if (e.key === 'Enter') {
88
+ if (selectedIdx >= 0 && items[selectedIdx]) {
89
+ openFile(items[selectedIdx].textContent);
90
+ } else if (items.length === 1) {
91
+ openFile(items[0].textContent);
92
+ }
93
+ } else if (e.key === 'Escape') {
94
+ hideModal();
95
+ }
96
+ });
97
+
98
+ document.addEventListener('keydown', function(e) {
99
+ if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'p' && !e.shiftKey && !e.altKey) {
100
+ showModal();
101
+ e.preventDefault();
102
+ } else if (e.key === 'Escape' && modal.style.display === 'flex') {
103
+ hideModal();
104
+ }
105
+ });
106
+ modal.addEventListener('mousedown', function(e) {
107
+ if (e.target === modal) hideModal();
108
+ });
109
+
110
+ // Helper: get parent path
111
+ function getParentPath(path) {
112
+ path = (path || '').replace(/\\/g, '/').replace(/^\/+|\/+$/g, '');
113
+ if (!path || path === '.' || path === '') return '.';
114
+ const parts = path.split('/');
115
+ if (parts.length <= 1) return '.';
116
+ parts.pop();
117
+ const parent = parts.join('/');
118
+ return parent === '' ? '.' : parent;
119
+ }
120
+
121
+ // Style for selected result
122
+ let style = document.createElement('style');
123
+ style.textContent = `.quickopen-result.selected { background:#333; color:#8be9fd; } .quickopen-result { padding:0.3em 0.7em; cursor:pointer; border-radius:0.3em; } .quickopen-result:hover { background:#222; }`;
124
+ document.head.appendChild(style);
125
+ })();
janito/web/app.py CHANGED
@@ -15,7 +15,7 @@ import threading
15
15
  import traceback
16
16
  import sys
17
17
 
18
- from janito.agent.runtime_config import unified_config
18
+ from janito.agent.runtime_config import unified_config, runtime_config
19
19
 
20
20
  # Render system prompt from config
21
21
  role = unified_config.get("role", "software engineer")
@@ -27,16 +27,16 @@ else:
27
27
  api_key=unified_config.get("api_key"),
28
28
  model=unified_config.get("model"),
29
29
  role=role,
30
- interaction_style=unified_config.get("interaction_style", "default"),
30
+ profile_name="base",
31
31
  interaction_mode=unified_config.get("interaction_mode", "prompt"),
32
- verbose_tools=unified_config.get("verbose_tools", False),
32
+ verbose_tools=runtime_config.get("verbose_tools", False),
33
33
  base_url=unified_config.get("base_url", None),
34
34
  azure_openai_api_version=unified_config.get(
35
35
  "azure_openai_api_version", "2023-05-15"
36
36
  ),
37
37
  use_azure_openai=unified_config.get("use_azure_openai", False),
38
38
  )
39
- system_prompt_template = profile_manager.render_prompt()
39
+ system_prompt_template = profile_manager.system_prompt_template
40
40
 
41
41
  app = Flask(
42
42
  __name__,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: janito
3
- Version: 1.7.0
3
+ Version: 1.8.1
4
4
  Summary: Natural Language Coding Agent,
5
5
  Author-email: João Pinto <joao.pinto@gmail.com>
6
6
  License-Expression: MIT
@@ -29,19 +29,53 @@ Dynamic: license-file
29
29
 
30
30
  # 🚀 Janito: Natural Language Coding Agent
31
31
 
32
- **Current Version: 1.7.0**
32
+ **Current Version: 1.8.1**
33
33
  See [CHANGELOG.md](./CHANGELOG.md) for details on the latest release.
34
34
 
35
35
  Janito is an AI-powered assistant for the command line and web that interprets natural language system_prompt_template to edit code, manage files, and analyze projects using patterns and tools designed by experienced software engineers. It prioritizes transparency, interactive clarification, and precise, reviewable changes.
36
36
 
37
37
  For a technical overview, see the [Architecture Guide](docs/reference/architecture.md).
38
38
 
39
+ ## 🧪 Experimental Feature: termweb File Viewer
40
+
41
+ Janito now includes an experimental lightweight web file viewer for use with the CLI chat shell. This feature allows you to click on file paths in the terminal (when using a Rich-compatible terminal) and instantly preview file contents in your browser—no full IDE required!
42
+
43
+ ### How to Use
44
+
45
+ - Start the CLI chat shell with the `--termweb` flag:
46
+ ```bash
47
+ janito --termweb
48
+ ```
49
+ - To disable the termweb file viewer, use the `--no-termweb` flag.
50
+ - By default, the viewer runs at http://localhost:8088 (or the next available port up to 8100).
51
+ - To specify a port, use `--termweb-port 8090`.
52
+ - File paths in CLI output become clickable links that open the file in your browser.
53
+
54
+ **Note:** This feature is experimental. It is intended for quick file previews and review, not for editing or production use. Feedback is welcome!
55
+
56
+ ### Why is this useful?
57
+ - Enables instant file previews from the CLI without a full IDE.
58
+ - Works with all Janito file tools and outputs that display file paths.
59
+ - Uses the Rich library’s link markup for clickable terminal links.
60
+
61
+ ---
62
+
39
63
  ## 📖 Full Documentation & Overview
40
64
  - For structured and in-depth guides, visit the [Janito Documentation Site](https://docs.janito.dev).
41
65
  - For a high-level, user-friendly overview, see [janito.dev](https://janito.dev).
42
66
 
43
67
  ---
44
68
 
69
+ ## Listing Available Tools
70
+
71
+ To list all registered tools on the command line, use the option:
72
+
73
+ ```sh
74
+ janito --list-tools
75
+ ```
76
+
77
+ This will display a colorful table with the name, description, and parameters of each available tool.
78
+
45
79
  ## ⚡ Quick Start
46
80
 
47
81
  ## 🖥️ Supported Human Interfaces
@@ -61,6 +95,7 @@ You can alter Janito's behavior in any interface using these flags:
61
95
  - `--no-tools`: Disable all tool usage (Janito will only use the language model, no file/code/shell actions).
62
96
  - `--trust-tools`/`-T`: Trusted tools mode (suppresses all tool output, only shows output file locations).
63
97
  - `--vanilla`: Disables tools, system prompt, and temperature settings for a pure LLM chat experience.
98
+ - `--no-termweb`: Disables the termweb file viewer for terminal links.
64
99
 
65
100
  These modifiers can be combined with any interface mode for tailored workflows.
66
101
 
@@ -89,7 +124,7 @@ janito --web
89
124
  - 📁 **File & Directory Management:** Navigate, create, move, or remove files and folders.
90
125
  - 🧠 **Context-Aware:** Understands your project structure for precise edits.
91
126
  - 💬 **Interactive User Prompts:** Asks for clarification when needed.
92
- - 🧩 **Extensible Tooling:** Built-in tools for file operations, shell commands, directory and file management, Python code execution and validation, text replacement, and more.
127
+ - 🛠️ **Extensible Tooling:** Built-in tools for file operations, shell commands, directory and file management, Python code execution and validation, text replacement, and more.
93
128
  - See [janito/agent/tools/README.md](janito/agent/tools/README.md) for the full list of built-in tools and their usage details. For the message handler model, see [docs/MESSAGE_HANDLER_MODEL.md](docs/MESSAGE_HANDLER_MODEL.md).
94
129
  - 🌐 **Web Interface (In Development):** Simple web UI for streaming responses and tool progress.
95
130
 
@@ -101,7 +136,7 @@ janito --web
101
136
 
102
137
  ### Contributing & Developer Guide
103
138
 
104
- If you want to extend Janito or add new tools, see the [Developer Guide](docs/README_DEV.md) for system_prompt_template, tool registration requirements, and code style guidelines. For the full list of built-in tools and their usage, see the [Tools Reference](janito/agent/tools/README.md).
139
+ If you want to extend Janito or add new tools, see the [Developer Guide](docs/README_DEV.md) for system_prompt_template, tool registration requirements, and code profile guidelines. For the full list of built-in tools and their usage, see the [Tools Reference](janito/agent/tools/README.md).
105
140
 
106
141
 
107
142
 
@@ -138,12 +173,12 @@ For details on using models hosted on Azure OpenAI, see [docs/AZURE_OPENAI.md](d
138
173
 
139
174
  ---
140
175
 
141
- ## 🧩 System Prompt & Role
176
+ ## 🧑‍💻 System Prompt & Role
142
177
 
143
- Janito operates using a system prompt template that defines its behavior, communication style, and capabilities. By default, Janito assumes the role of a "software engineer"—this means its responses and actions are tailored to the expectations and best practices of professional software engineering.
178
+ Janito operates using a system prompt template that defines its behavior, communication profile, and capabilities. By default, Janito assumes the role of a "software engineer"—this means its responses and actions are tailored to the expectations and best practices of professional software engineering.
144
179
 
145
180
  - **Role:** You can customize the agent's role (e.g., "data scientist", "DevOps engineer") using the `--role` flag or config. The default is `software engineer`.
146
- - **System Prompt Template:** The system prompt is rendered from a Jinja2 template (see `janito/agent/templates/system_prompt_template.j2`). This template governs how the agent interprets system_prompt_template, interacts with files, and communicates with users.
181
+ - **System Prompt Template:** The system prompt is rendered from a Jinja2 template (see `janito/agent/templates/prompt_prompt_template.j2`). This template governs how the agent interprets system_prompt_template, interacts with files, and communicates with users.
147
182
  - **Customization & Precedence:** Advanced users can override the system prompt with the `--system` flag (raw string), or point to a custom file using `--system-file`. The precedence is: `--system-file` > `--system`/config > default template.
148
183
 
149
184
  The default template ensures the agent:
@@ -152,7 +187,7 @@ The default template ensures the agent:
152
187
  - Provides concise plans before taking action
153
188
  - Documents any changes made
154
189
 
155
- For more details or to customize the prompt, see the template file at `janito/agent/templates/system_prompt_template.j2` and the architecture overview in [docs/reference/architecture.md](docs/reference/architecture.md).
190
+ For more details or to customize the prompt, see the template file at `janito/agent/templates/prompt_prompt_template.j2` and the architecture overview in [docs/reference/architecture.md](docs/reference/architecture.md).
156
191
 
157
192
  ---
158
193
 
@@ -184,18 +219,18 @@ Vanilla mode is ideal for:
184
219
 
185
220
  > Note: Vanilla mode is a runtime switch and does not change the Agent API or class signatures. It is controlled via CLI/config only.
186
221
 
187
- ## 🧑‍💻 AgentProfileManager: Profile, Role, and Prompt Management
222
+ ## 👨‍💻 AgentProfileManager: Profile, Role, and Prompt Management
188
223
 
189
- Janito now uses a dedicated `AgentProfileManager` class to manage user profiles, roles, interaction styles, and system prompt selection. This manager:
190
- - Stores the current role (e.g., "software engineer") and interaction style (e.g., "default", "technical").
191
- - Renders the system prompt from the appropriate template based on interaction style.
224
+ Janito now uses a dedicated `AgentProfileManager` class to manage user profiles, roles, interaction profiles, and system prompt selection. This manager:
225
+ - Stores the current role (e.g., "software engineer") and interaction profile (e.g., "default", "technical").
226
+ - Renders the system prompt from the appropriate template based on interaction profile.
192
227
  - Instantiates and manages the low-level LLM Agent, passing the correct prompt.
193
- - Provides methods to update the role, interaction style, and refresh the prompt at runtime.
228
+ - Provides methods to update the role, interaction profile, and refresh the prompt at runtime.
194
229
 
195
230
  ### Multiple System Prompt Templates
196
- - The system prompt template is now selected based on the interaction style (e.g., `default` or `technical`).
231
+ - The system prompt template is now selected based on the interaction profile (e.g., `default` or `technical`).
197
232
  - Templates are located in `janito/agent/templates/` (see `system_prompt_template.j2` and `system_prompt_template_technical.j2`).
198
- - You can switch interaction styles at runtime using the profile manager, enabling different agent behaviors for different user needs.
233
+ - You can switch interaction profiles at runtime using the profile manager, enabling different agent behaviors for different user needs.
199
234
 
200
235
  This separation ensures that the LLM Agent remains focused on language model interaction and tool execution, while all profile, role, and prompt logic is managed at a higher level.
201
236
 
@@ -203,45 +238,45 @@ See `janito/agent/profile_manager.py` for implementation details.
203
238
 
204
239
  ### Agent Interaction Style
205
240
 
206
- You can control the agent's behavior and prompt style globally or per-project using the `interaction_style` config key. Supported values:
241
+ You can control the agent's behavior and prompt profile globally or per-project using the `profile` config key. See [Prompt Profiles Guide](docs/guides/prompt_profiles.md) for all available styles and combinations.
207
242
  - `default`: Concise, general-purpose agent (default)
208
243
  - `technical`: Strict, workflow-oriented for technical/developer use
209
244
 
210
245
  Set globally:
211
246
  ```bash
212
- janito --set-global-config interaction_style=technical
247
+ janito --set-global-config profile=technical
213
248
  ```
214
249
  Or per-project (in your project root):
215
250
  ```bash
216
- janito --set-local-config interaction_style=technical
251
+ janito --set-local-config profile=technical
217
252
  ```
218
253
 
219
254
  You can also override for a session with the CLI flag:
220
255
  ```bash
221
- janito --style technical
256
+ janito --profile technical
222
257
  ```
223
258
 
224
259
  See [docs/CONFIGURATION.md](docs/CONFIGURATION.md) for full details.
225
260
 
226
- ## 🧩 Combinatorial Style System
261
+ ## 🧑‍💻 Combinatorial Style System
227
262
 
228
- Janito now supports combinatorial styles for system prompts, allowing you to combine a main style (such as `default` or `technical`) with one or more feature extensions (such as `commit_all`).
263
+ Janito now supports combinatorial profiles for system prompts, allowing you to combine a main profile (such as `default` or `technical`) with one or more feature extensions (such as `commit_all`).
229
264
 
230
- - **Main style:** The base agent behavior and workflow (e.g., `default`, `technical`).
231
- - **Feature extensions:** Optional features that override or extend the main style (e.g., `commit_all`).
265
+ - **Main profile:** The base agent behavior and workflow (e.g., `default`, `technical`).
266
+ - **Feature extensions:** Optional features that override or extend the main profile (e.g., `commit_all`).
232
267
  - **Syntax:** Use a hyphen to combine, e.g., `technical-commit_all`.
233
268
 
234
269
  **How it works:**
235
- - The main style template is loaded first.
270
+ - The main profile template is loaded first.
236
271
  - Each feature extension template is layered on top, overriding or extending specific blocks in the main template.
237
272
  - Feature templates must use `{% extends parent_template %}` for dynamic inheritance.
238
273
 
239
274
  **Example usage:**
240
275
  ```bash
241
- janito --style technical-commit_all
276
+ janito --profile technical-commit_all
242
277
  ```
243
278
 
244
- This will apply the `technical` style with the `commit_all` feature enabled in the agent's system prompt.
279
+ This will apply the `technical` profile with the `commit_all` feature enabled in the agent's system prompt.
245
280
 
246
281
  See `janito/render_prompt.py` and `janito/agent/templates/` for implementation details and to create your own feature extensions.
247
282