makeless 0.1.1__tar.gz → 0.1.2__tar.gz
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.
- {makeless-0.1.1 → makeless-0.1.2}/PKG-INFO +1 -1
- makeless-0.1.2/main.py +410 -0
- {makeless-0.1.1 → makeless-0.1.2}/makeless.egg-info/PKG-INFO +1 -1
- {makeless-0.1.1 → makeless-0.1.2}/makeless.egg-info/SOURCES.txt +1 -0
- {makeless-0.1.1 → makeless-0.1.2}/pyproject.toml +1 -1
- makeless-0.1.2/tools/app_finder.py +104 -0
- makeless-0.1.2/tools/database.py +62 -0
- {makeless-0.1.1 → makeless-0.1.2}/tools/filesystem.py +38 -14
- {makeless-0.1.1 → makeless-0.1.2}/tools/indexer.py +32 -44
- {makeless-0.1.1 → makeless-0.1.2}/tools/watcher.py +31 -38
- makeless-0.1.1/main.py +0 -337
- makeless-0.1.1/tools/app_finder.py +0 -80
- {makeless-0.1.1 → makeless-0.1.2}/LICENSE +0 -0
- {makeless-0.1.1 → makeless-0.1.2}/README.md +0 -0
- {makeless-0.1.1 → makeless-0.1.2}/agent/planner.py +0 -0
- {makeless-0.1.1 → makeless-0.1.2}/agent/runtime.py +0 -0
- {makeless-0.1.1 → makeless-0.1.2}/agent/state.py +0 -0
- {makeless-0.1.1 → makeless-0.1.2}/app/main.py +0 -0
- {makeless-0.1.1 → makeless-0.1.2}/execution/executor.py +0 -0
- {makeless-0.1.1 → makeless-0.1.2}/makeless.egg-info/dependency_links.txt +0 -0
- {makeless-0.1.1 → makeless-0.1.2}/makeless.egg-info/entry_points.txt +0 -0
- {makeless-0.1.1 → makeless-0.1.2}/makeless.egg-info/requires.txt +0 -0
- {makeless-0.1.1 → makeless-0.1.2}/makeless.egg-info/top_level.txt +0 -0
- {makeless-0.1.1 → makeless-0.1.2}/memory/store.py +0 -0
- {makeless-0.1.1 → makeless-0.1.2}/observability/logger.py +0 -0
- {makeless-0.1.1 → makeless-0.1.2}/requirements.txt +0 -0
- {makeless-0.1.1 → makeless-0.1.2}/security/permissions.py +0 -0
- {makeless-0.1.1 → makeless-0.1.2}/setup.cfg +0 -0
- {makeless-0.1.1 → makeless-0.1.2}/tests/test_state.py +0 -0
- {makeless-0.1.1 → makeless-0.1.2}/tools/app_launcher.py +0 -0
- {makeless-0.1.1 → makeless-0.1.2}/tools/builtin.py +0 -0
- {makeless-0.1.1 → makeless-0.1.2}/tools/launcher.py +0 -0
- {makeless-0.1.1 → makeless-0.1.2}/tools/locations.py +0 -0
- {makeless-0.1.1 → makeless-0.1.2}/tools/open_router.py +0 -0
- {makeless-0.1.1 → makeless-0.1.2}/tools/opener.py +0 -0
- {makeless-0.1.1 → makeless-0.1.2}/tools/reader.py +0 -0
- {makeless-0.1.1 → makeless-0.1.2}/tools/registry.py +0 -0
- {makeless-0.1.1 → makeless-0.1.2}/tools/selector.py +0 -0
makeless-0.1.2/main.py
ADDED
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
from tools.filesystem import search_files
|
|
2
|
+
from tools.watcher import start_watcher
|
|
3
|
+
from tools.locations import get_system_location
|
|
4
|
+
from tools.reader import read_file
|
|
5
|
+
from tools.selector import select_result
|
|
6
|
+
from tools.open_router import open_item
|
|
7
|
+
from tools.app_finder import find_apps
|
|
8
|
+
from tools.app_launcher import launch_app
|
|
9
|
+
from tools.launcher import search_in_browser
|
|
10
|
+
from tools.indexer import ensure_index
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def choose_path(query):
|
|
14
|
+
"""
|
|
15
|
+
Search for an item and let the user choose if
|
|
16
|
+
multiple matches are found.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
results = search_files(
|
|
20
|
+
query,
|
|
21
|
+
max_results=10
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
if not results:
|
|
25
|
+
return None
|
|
26
|
+
|
|
27
|
+
if len(results) == 1:
|
|
28
|
+
return results[0]
|
|
29
|
+
|
|
30
|
+
return select_result(results)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def choose_app(apps):
|
|
34
|
+
"""
|
|
35
|
+
Choose the best application automatically.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
if not apps:
|
|
39
|
+
return None
|
|
40
|
+
|
|
41
|
+
# app_finder removes duplicate application names
|
|
42
|
+
# and returns the best matches first.
|
|
43
|
+
return apps[0]
|
|
44
|
+
|
|
45
|
+
def handle_instruction(instruction):
|
|
46
|
+
instruction = instruction.strip()
|
|
47
|
+
lower_instruction = instruction.casefold()
|
|
48
|
+
|
|
49
|
+
# Read a file.
|
|
50
|
+
if lower_instruction.startswith("read "):
|
|
51
|
+
query = instruction[5:].strip()
|
|
52
|
+
|
|
53
|
+
if not query:
|
|
54
|
+
print(
|
|
55
|
+
"Makeless: Tell me what you want me to read."
|
|
56
|
+
)
|
|
57
|
+
return
|
|
58
|
+
|
|
59
|
+
print(
|
|
60
|
+
f"\nMakeless: Looking for '{query}'..."
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
path = choose_path(query)
|
|
64
|
+
|
|
65
|
+
if not path:
|
|
66
|
+
print(
|
|
67
|
+
"Makeless: I couldn't find that file."
|
|
68
|
+
)
|
|
69
|
+
return
|
|
70
|
+
|
|
71
|
+
success, content = read_file(path)
|
|
72
|
+
|
|
73
|
+
if not success:
|
|
74
|
+
print(
|
|
75
|
+
f"Makeless: {content}"
|
|
76
|
+
)
|
|
77
|
+
return
|
|
78
|
+
|
|
79
|
+
print("\n" + "=" * 50)
|
|
80
|
+
print(f"READING: {path}")
|
|
81
|
+
print("=" * 50 + "\n")
|
|
82
|
+
print(content)
|
|
83
|
+
print("\n" + "=" * 50)
|
|
84
|
+
print("End of file.")
|
|
85
|
+
print("=" * 50)
|
|
86
|
+
|
|
87
|
+
return
|
|
88
|
+
|
|
89
|
+
# Open a browser and search the web.
|
|
90
|
+
if lower_instruction.startswith("open "):
|
|
91
|
+
|
|
92
|
+
search_separators = [
|
|
93
|
+
" and search for ",
|
|
94
|
+
" and search "
|
|
95
|
+
]
|
|
96
|
+
|
|
97
|
+
separator_found = None
|
|
98
|
+
|
|
99
|
+
for separator in search_separators:
|
|
100
|
+
if separator in lower_instruction:
|
|
101
|
+
separator_found = separator
|
|
102
|
+
break
|
|
103
|
+
|
|
104
|
+
if separator_found:
|
|
105
|
+
split_index = lower_instruction.index(
|
|
106
|
+
separator_found
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
browser_name = instruction[
|
|
110
|
+
5:split_index
|
|
111
|
+
].strip()
|
|
112
|
+
|
|
113
|
+
search_query = instruction[
|
|
114
|
+
split_index + len(separator_found):
|
|
115
|
+
].strip()
|
|
116
|
+
|
|
117
|
+
if not browser_name:
|
|
118
|
+
print(
|
|
119
|
+
"Makeless: Tell me which browser to use."
|
|
120
|
+
)
|
|
121
|
+
return
|
|
122
|
+
|
|
123
|
+
if not search_query:
|
|
124
|
+
print(
|
|
125
|
+
"Makeless: Tell me what you want to search for."
|
|
126
|
+
)
|
|
127
|
+
return
|
|
128
|
+
|
|
129
|
+
print(
|
|
130
|
+
f"\nMakeless: Searching for "
|
|
131
|
+
f"'{search_query}' in "
|
|
132
|
+
f"{browser_name.title()}..."
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
success, message = search_in_browser(
|
|
136
|
+
search_query,
|
|
137
|
+
browser_name
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
print(f"Makeless: {message}")
|
|
141
|
+
return
|
|
142
|
+
|
|
143
|
+
# Find files and folders.
|
|
144
|
+
if lower_instruction.startswith("find "):
|
|
145
|
+
query = instruction[5:].strip()
|
|
146
|
+
|
|
147
|
+
if not query:
|
|
148
|
+
print(
|
|
149
|
+
"Makeless: Tell me what you want to find."
|
|
150
|
+
)
|
|
151
|
+
return
|
|
152
|
+
|
|
153
|
+
print(
|
|
154
|
+
f"\nMakeless: Searching for '{query}'...\n"
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
results = search_files(query)
|
|
158
|
+
|
|
159
|
+
if not results:
|
|
160
|
+
print(
|
|
161
|
+
"Makeless: Nothing found."
|
|
162
|
+
)
|
|
163
|
+
return
|
|
164
|
+
|
|
165
|
+
print(
|
|
166
|
+
f"Makeless: Found {len(results)} result(s)."
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
for index, result in enumerate(
|
|
170
|
+
results,
|
|
171
|
+
start=1
|
|
172
|
+
):
|
|
173
|
+
print(
|
|
174
|
+
f"{index}. {result}"
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
return
|
|
178
|
+
|
|
179
|
+
# Open a browser and search the web.
|
|
180
|
+
separator = " and search for "
|
|
181
|
+
|
|
182
|
+
if (
|
|
183
|
+
lower_instruction.startswith("open ")
|
|
184
|
+
and separator in lower_instruction
|
|
185
|
+
):
|
|
186
|
+
split_index = lower_instruction.index(
|
|
187
|
+
separator
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
browser_name = instruction[
|
|
191
|
+
5:split_index
|
|
192
|
+
].strip()
|
|
193
|
+
|
|
194
|
+
search_query = instruction[
|
|
195
|
+
split_index + len(separator):
|
|
196
|
+
].strip()
|
|
197
|
+
|
|
198
|
+
if not browser_name:
|
|
199
|
+
print(
|
|
200
|
+
"Makeless: Tell me which browser to use."
|
|
201
|
+
)
|
|
202
|
+
return
|
|
203
|
+
|
|
204
|
+
if not search_query:
|
|
205
|
+
print(
|
|
206
|
+
"Makeless: Tell me what you want to search for."
|
|
207
|
+
)
|
|
208
|
+
return
|
|
209
|
+
|
|
210
|
+
print(
|
|
211
|
+
f"\nMakeless: Searching for "
|
|
212
|
+
f"'{search_query}' in "
|
|
213
|
+
f"{browser_name.title()}..."
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
success, message = search_in_browser(
|
|
217
|
+
search_query,
|
|
218
|
+
browser_name
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
print(f"Makeless: {message}")
|
|
222
|
+
return
|
|
223
|
+
|
|
224
|
+
# Open something in VS Code.
|
|
225
|
+
if (
|
|
226
|
+
lower_instruction.startswith("open ")
|
|
227
|
+
and (
|
|
228
|
+
lower_instruction.endswith(" in vs code")
|
|
229
|
+
or lower_instruction.endswith(" in vscode")
|
|
230
|
+
)
|
|
231
|
+
):
|
|
232
|
+
if lower_instruction.endswith(
|
|
233
|
+
" in vs code"
|
|
234
|
+
):
|
|
235
|
+
query = instruction[5:-11].strip()
|
|
236
|
+
else:
|
|
237
|
+
query = instruction[5:-10].strip()
|
|
238
|
+
|
|
239
|
+
if not query:
|
|
240
|
+
print(
|
|
241
|
+
"Makeless: Tell me what you want to open."
|
|
242
|
+
)
|
|
243
|
+
return
|
|
244
|
+
|
|
245
|
+
print(
|
|
246
|
+
f"\nMakeless: Looking for '{query}'..."
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
system_location = get_system_location(
|
|
250
|
+
query
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
if system_location:
|
|
254
|
+
path = str(system_location)
|
|
255
|
+
else:
|
|
256
|
+
path = choose_path(query)
|
|
257
|
+
|
|
258
|
+
if not path:
|
|
259
|
+
print(
|
|
260
|
+
"Makeless: I couldn't find that."
|
|
261
|
+
)
|
|
262
|
+
return
|
|
263
|
+
|
|
264
|
+
success, message = open_item(
|
|
265
|
+
path,
|
|
266
|
+
app="vscode"
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
print(f"Makeless: {message}")
|
|
270
|
+
return
|
|
271
|
+
|
|
272
|
+
# Open an app, file, or folder.
|
|
273
|
+
if lower_instruction.startswith("open "):
|
|
274
|
+
query = instruction[5:].strip()
|
|
275
|
+
|
|
276
|
+
if not query:
|
|
277
|
+
print(
|
|
278
|
+
"Makeless: Tell me what you want to open."
|
|
279
|
+
)
|
|
280
|
+
return
|
|
281
|
+
|
|
282
|
+
print(
|
|
283
|
+
f"\nMakeless: Looking for '{query}'..."
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
# 1. Check installed applications FIRST.
|
|
287
|
+
apps = find_apps(query)
|
|
288
|
+
|
|
289
|
+
if apps:
|
|
290
|
+
app = choose_app(apps)
|
|
291
|
+
|
|
292
|
+
if app:
|
|
293
|
+
success, message = launch_app(
|
|
294
|
+
app["path"]
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
if success:
|
|
298
|
+
print(
|
|
299
|
+
f"Makeless: Launched "
|
|
300
|
+
f"{app['name']}"
|
|
301
|
+
)
|
|
302
|
+
else:
|
|
303
|
+
print(
|
|
304
|
+
f"Makeless: {message}"
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
return
|
|
308
|
+
|
|
309
|
+
# 2. Check known system locations.
|
|
310
|
+
system_location = get_system_location(
|
|
311
|
+
query
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
if system_location:
|
|
315
|
+
path = str(system_location)
|
|
316
|
+
|
|
317
|
+
else:
|
|
318
|
+
# 3. Search indexed files and folders.
|
|
319
|
+
path = choose_path(query)
|
|
320
|
+
|
|
321
|
+
if not path:
|
|
322
|
+
print(
|
|
323
|
+
"Makeless: I couldn't find that."
|
|
324
|
+
)
|
|
325
|
+
return
|
|
326
|
+
|
|
327
|
+
success, message = open_item(path)
|
|
328
|
+
|
|
329
|
+
print(f"Makeless: {message}")
|
|
330
|
+
return
|
|
331
|
+
|
|
332
|
+
print(
|
|
333
|
+
"Makeless: I don't know how to perform "
|
|
334
|
+
"that task yet."
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
def main():
|
|
339
|
+
print("================================")
|
|
340
|
+
print(" Makeless")
|
|
341
|
+
print("================================")
|
|
342
|
+
print("Starting local computer operator...")
|
|
343
|
+
|
|
344
|
+
try:
|
|
345
|
+
ensure_index()
|
|
346
|
+
|
|
347
|
+
observer = start_watcher()
|
|
348
|
+
|
|
349
|
+
except Exception as error:
|
|
350
|
+
print(
|
|
351
|
+
f"\nMakeless: Startup error: {error}"
|
|
352
|
+
)
|
|
353
|
+
return
|
|
354
|
+
|
|
355
|
+
print("\nReady.")
|
|
356
|
+
print("Tell me what you want to do.")
|
|
357
|
+
print("Type 'exit' to close.\n")
|
|
358
|
+
|
|
359
|
+
try:
|
|
360
|
+
while True:
|
|
361
|
+
try:
|
|
362
|
+
instruction = input("You: ")
|
|
363
|
+
|
|
364
|
+
if (
|
|
365
|
+
instruction.strip().casefold()
|
|
366
|
+
== "exit"
|
|
367
|
+
):
|
|
368
|
+
print(
|
|
369
|
+
"\nMakeless: Shutting down..."
|
|
370
|
+
)
|
|
371
|
+
break
|
|
372
|
+
|
|
373
|
+
if not instruction.strip():
|
|
374
|
+
continue
|
|
375
|
+
|
|
376
|
+
# Run one command safely.
|
|
377
|
+
# If it fails, Makeless stays alive.
|
|
378
|
+
try:
|
|
379
|
+
handle_instruction(instruction)
|
|
380
|
+
|
|
381
|
+
except Exception as error:
|
|
382
|
+
print(
|
|
383
|
+
"\nMakeless: Something went wrong "
|
|
384
|
+
f"while handling that command: {error}"
|
|
385
|
+
)
|
|
386
|
+
print(
|
|
387
|
+
"Makeless: Try another command."
|
|
388
|
+
)
|
|
389
|
+
|
|
390
|
+
except EOFError:
|
|
391
|
+
print(
|
|
392
|
+
"\nMakeless: Input closed."
|
|
393
|
+
)
|
|
394
|
+
break
|
|
395
|
+
|
|
396
|
+
except KeyboardInterrupt:
|
|
397
|
+
print(
|
|
398
|
+
"\n\nMakeless: Interrupted. "
|
|
399
|
+
"Shutting down..."
|
|
400
|
+
)
|
|
401
|
+
|
|
402
|
+
finally:
|
|
403
|
+
observer.stop()
|
|
404
|
+
observer.join()
|
|
405
|
+
|
|
406
|
+
print("Makeless: Goodbye!")
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
if __name__ == "__main__":
|
|
410
|
+
main()
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
APP_ALIASES = {
|
|
5
|
+
"vscode": "visual studio code",
|
|
6
|
+
"vs code": "visual studio code",
|
|
7
|
+
"code": "visual studio code",
|
|
8
|
+
"chrome": "google chrome",
|
|
9
|
+
"brave": "brave",
|
|
10
|
+
"edge": "microsoft edge"
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def get_start_menu_locations():
|
|
15
|
+
locations = []
|
|
16
|
+
|
|
17
|
+
program_data = os.environ.get("PROGRAMDATA")
|
|
18
|
+
|
|
19
|
+
if program_data:
|
|
20
|
+
locations.append(
|
|
21
|
+
os.path.join(
|
|
22
|
+
program_data,
|
|
23
|
+
"Microsoft",
|
|
24
|
+
"Windows",
|
|
25
|
+
"Start Menu",
|
|
26
|
+
"Programs"
|
|
27
|
+
)
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
app_data = os.environ.get("APPDATA")
|
|
31
|
+
|
|
32
|
+
if app_data:
|
|
33
|
+
locations.append(
|
|
34
|
+
os.path.join(
|
|
35
|
+
app_data,
|
|
36
|
+
"Microsoft",
|
|
37
|
+
"Windows",
|
|
38
|
+
"Start Menu",
|
|
39
|
+
"Programs"
|
|
40
|
+
)
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
return locations
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def find_apps(query, max_results=10):
|
|
47
|
+
"""
|
|
48
|
+
Search Windows Start Menu shortcuts for applications.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
query = query.casefold().strip()
|
|
52
|
+
|
|
53
|
+
if not query:
|
|
54
|
+
return []
|
|
55
|
+
|
|
56
|
+
search_query = APP_ALIASES.get(
|
|
57
|
+
query,
|
|
58
|
+
query
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
results = []
|
|
62
|
+
seen_names = set()
|
|
63
|
+
|
|
64
|
+
for location in get_start_menu_locations():
|
|
65
|
+
|
|
66
|
+
if not os.path.exists(location):
|
|
67
|
+
continue
|
|
68
|
+
|
|
69
|
+
for root, directories, files in os.walk(location):
|
|
70
|
+
|
|
71
|
+
for file_name in files:
|
|
72
|
+
|
|
73
|
+
if not file_name.casefold().endswith(".lnk"):
|
|
74
|
+
continue
|
|
75
|
+
|
|
76
|
+
app_name = os.path.splitext(
|
|
77
|
+
file_name
|
|
78
|
+
)[0]
|
|
79
|
+
|
|
80
|
+
normalized_name = app_name.casefold()
|
|
81
|
+
|
|
82
|
+
if search_query not in normalized_name:
|
|
83
|
+
continue
|
|
84
|
+
|
|
85
|
+
# Avoid duplicate shortcuts for the same app.
|
|
86
|
+
if normalized_name in seen_names:
|
|
87
|
+
continue
|
|
88
|
+
|
|
89
|
+
seen_names.add(normalized_name)
|
|
90
|
+
|
|
91
|
+
results.append(
|
|
92
|
+
{
|
|
93
|
+
"name": app_name,
|
|
94
|
+
"path": os.path.join(
|
|
95
|
+
root,
|
|
96
|
+
file_name
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
if len(results) >= max_results:
|
|
102
|
+
return results
|
|
103
|
+
|
|
104
|
+
return results
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sqlite3
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def get_data_directory():
|
|
7
|
+
"""
|
|
8
|
+
Return a writable location for Makeless user data.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
local_app_data = os.environ.get("LOCALAPPDATA")
|
|
12
|
+
|
|
13
|
+
if local_app_data:
|
|
14
|
+
data_directory = Path(local_app_data) / "Makeless"
|
|
15
|
+
else:
|
|
16
|
+
data_directory = Path.home() / ".makeless"
|
|
17
|
+
|
|
18
|
+
data_directory.mkdir(parents=True, exist_ok=True)
|
|
19
|
+
|
|
20
|
+
return data_directory
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
DATABASE_PATH = get_data_directory() / "agentcore.db"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def get_database_connection():
|
|
27
|
+
"""
|
|
28
|
+
Open the Makeless SQLite database.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
DATABASE_PATH.parent.mkdir(parents=True, exist_ok=True)
|
|
32
|
+
|
|
33
|
+
return sqlite3.connect(DATABASE_PATH)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def create_database():
|
|
37
|
+
"""
|
|
38
|
+
Create the database and required tables.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
connection = get_database_connection()
|
|
42
|
+
cursor = connection.cursor()
|
|
43
|
+
|
|
44
|
+
cursor.execute(
|
|
45
|
+
"""
|
|
46
|
+
CREATE TABLE IF NOT EXISTS file_index (
|
|
47
|
+
path TEXT PRIMARY KEY,
|
|
48
|
+
name TEXT NOT NULL,
|
|
49
|
+
item_type TEXT NOT NULL
|
|
50
|
+
)
|
|
51
|
+
"""
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
cursor.execute(
|
|
55
|
+
"""
|
|
56
|
+
CREATE INDEX IF NOT EXISTS idx_file_name
|
|
57
|
+
ON file_index(name)
|
|
58
|
+
"""
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
connection.commit()
|
|
62
|
+
connection.close()
|
|
@@ -3,26 +3,45 @@ import sqlite3
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
) / "Makeless"
|
|
6
|
+
APP_DATA_DIRECTORY = Path.home() / ".makeless"
|
|
7
|
+
DATABASE_PATH = APP_DATA_DIRECTORY / "agentcore.db"
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
|
|
10
|
+
def ensure_database_directory():
|
|
11
|
+
"""
|
|
12
|
+
Create the Makeless data directory if it does not exist.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
APP_DATA_DIRECTORY.mkdir(
|
|
16
|
+
parents=True,
|
|
17
|
+
exist_ok=True
|
|
18
|
+
)
|
|
11
19
|
|
|
12
20
|
|
|
13
21
|
def get_database_connection():
|
|
14
|
-
|
|
22
|
+
"""
|
|
23
|
+
Return a connection to the Makeless database.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
ensure_database_directory()
|
|
15
27
|
|
|
16
28
|
return sqlite3.connect(DATABASE_PATH)
|
|
17
29
|
|
|
18
30
|
|
|
19
31
|
def search_files(query, max_results=20):
|
|
20
|
-
|
|
32
|
+
"""
|
|
33
|
+
Search the local file index.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
query = query.casefold().strip()
|
|
21
37
|
|
|
22
|
-
|
|
23
|
-
|
|
38
|
+
if not query:
|
|
39
|
+
return []
|
|
24
40
|
|
|
25
41
|
try:
|
|
42
|
+
connection = get_database_connection()
|
|
43
|
+
cursor = connection.cursor()
|
|
44
|
+
|
|
26
45
|
cursor.execute(
|
|
27
46
|
"""
|
|
28
47
|
SELECT path, name, item_type
|
|
@@ -34,13 +53,11 @@ def search_files(query, max_results=20):
|
|
|
34
53
|
)
|
|
35
54
|
|
|
36
55
|
rows = cursor.fetchall()
|
|
37
|
-
|
|
38
|
-
except sqlite3.OperationalError:
|
|
39
|
-
rows = []
|
|
40
|
-
|
|
41
|
-
finally:
|
|
42
56
|
connection.close()
|
|
43
57
|
|
|
58
|
+
except sqlite3.Error:
|
|
59
|
+
return []
|
|
60
|
+
|
|
44
61
|
results = []
|
|
45
62
|
|
|
46
63
|
ignored_paths = [
|
|
@@ -70,7 +87,14 @@ def search_files(query, max_results=20):
|
|
|
70
87
|
|
|
71
88
|
|
|
72
89
|
def find_best_match(query):
|
|
73
|
-
|
|
90
|
+
"""
|
|
91
|
+
Find the best matching file or folder.
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
results = search_files(
|
|
95
|
+
query,
|
|
96
|
+
max_results=100
|
|
97
|
+
)
|
|
74
98
|
|
|
75
99
|
if not results:
|
|
76
100
|
return None
|