makeless 0.1.0__tar.gz → 0.1.1__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/LICENSE +21 -0
- {makeless-0.1.0 → makeless-0.1.1}/PKG-INFO +22 -2
- {makeless-0.1.0 → makeless-0.1.1}/main.py +115 -50
- {makeless-0.1.0 → makeless-0.1.1}/makeless.egg-info/PKG-INFO +22 -2
- {makeless-0.1.0 → makeless-0.1.1}/pyproject.toml +1 -1
- {makeless-0.1.0 → makeless-0.1.1}/tools/filesystem.py +29 -25
- {makeless-0.1.0 → makeless-0.1.1}/tools/indexer.py +83 -19
- {makeless-0.1.0 → makeless-0.1.1}/tools/watcher.py +22 -22
- makeless-0.1.0/LICENSE +0 -1
- {makeless-0.1.0 → makeless-0.1.1}/README.md +0 -0
- {makeless-0.1.0 → makeless-0.1.1}/agent/planner.py +0 -0
- {makeless-0.1.0 → makeless-0.1.1}/agent/runtime.py +0 -0
- {makeless-0.1.0 → makeless-0.1.1}/agent/state.py +0 -0
- {makeless-0.1.0 → makeless-0.1.1}/app/main.py +0 -0
- {makeless-0.1.0 → makeless-0.1.1}/execution/executor.py +0 -0
- {makeless-0.1.0 → makeless-0.1.1}/makeless.egg-info/SOURCES.txt +0 -0
- {makeless-0.1.0 → makeless-0.1.1}/makeless.egg-info/dependency_links.txt +0 -0
- {makeless-0.1.0 → makeless-0.1.1}/makeless.egg-info/entry_points.txt +0 -0
- {makeless-0.1.0 → makeless-0.1.1}/makeless.egg-info/requires.txt +0 -0
- {makeless-0.1.0 → makeless-0.1.1}/makeless.egg-info/top_level.txt +0 -0
- {makeless-0.1.0 → makeless-0.1.1}/memory/store.py +0 -0
- {makeless-0.1.0 → makeless-0.1.1}/observability/logger.py +0 -0
- {makeless-0.1.0 → makeless-0.1.1}/requirements.txt +0 -0
- {makeless-0.1.0 → makeless-0.1.1}/security/permissions.py +0 -0
- {makeless-0.1.0 → makeless-0.1.1}/setup.cfg +0 -0
- {makeless-0.1.0 → makeless-0.1.1}/tests/test_state.py +0 -0
- {makeless-0.1.0 → makeless-0.1.1}/tools/app_finder.py +0 -0
- {makeless-0.1.0 → makeless-0.1.1}/tools/app_launcher.py +0 -0
- {makeless-0.1.0 → makeless-0.1.1}/tools/builtin.py +0 -0
- {makeless-0.1.0 → makeless-0.1.1}/tools/launcher.py +0 -0
- {makeless-0.1.0 → makeless-0.1.1}/tools/locations.py +0 -0
- {makeless-0.1.0 → makeless-0.1.1}/tools/open_router.py +0 -0
- {makeless-0.1.0 → makeless-0.1.1}/tools/opener.py +0 -0
- {makeless-0.1.0 → makeless-0.1.1}/tools/reader.py +0 -0
- {makeless-0.1.0 → makeless-0.1.1}/tools/registry.py +0 -0
- {makeless-0.1.0 → makeless-0.1.1}/tools/selector.py +0 -0
makeless-0.1.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Avinash Manikonda
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -1,9 +1,29 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: makeless
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.1
|
|
4
4
|
Summary: A local-first computer operator for Windows.
|
|
5
5
|
Author: Avinash
|
|
6
|
-
License:
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2026 Avinash Manikonda
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
7
27
|
Project-URL: Homepage, https://github.com/kkj4u2t7/agentcore
|
|
8
28
|
Project-URL: Repository, https://github.com/kkj4u2t7/agentcore
|
|
9
29
|
Requires-Python: >=3.9
|
|
@@ -7,6 +7,7 @@ from tools.open_router import open_item
|
|
|
7
7
|
from tools.app_finder import find_apps
|
|
8
8
|
from tools.app_launcher import launch_app
|
|
9
9
|
from tools.launcher import search_in_browser
|
|
10
|
+
from tools.indexer import ensure_index
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
def choose_path(query):
|
|
@@ -60,15 +61,21 @@ def handle_instruction(instruction):
|
|
|
60
61
|
query = instruction[5:].strip()
|
|
61
62
|
|
|
62
63
|
if not query:
|
|
63
|
-
print(
|
|
64
|
+
print(
|
|
65
|
+
"AgentCore: Tell me what you want me to read."
|
|
66
|
+
)
|
|
64
67
|
return
|
|
65
68
|
|
|
66
|
-
print(
|
|
69
|
+
print(
|
|
70
|
+
f"\nAgentCore: Looking for '{query}'..."
|
|
71
|
+
)
|
|
67
72
|
|
|
68
73
|
path = choose_path(query)
|
|
69
74
|
|
|
70
75
|
if not path:
|
|
71
|
-
print(
|
|
76
|
+
print(
|
|
77
|
+
"AgentCore: I couldn't find that file."
|
|
78
|
+
)
|
|
72
79
|
return
|
|
73
80
|
|
|
74
81
|
success, content = read_file(path)
|
|
@@ -94,10 +101,14 @@ def handle_instruction(instruction):
|
|
|
94
101
|
query = instruction[5:].strip()
|
|
95
102
|
|
|
96
103
|
if not query:
|
|
97
|
-
print(
|
|
104
|
+
print(
|
|
105
|
+
"AgentCore: Tell me what you want to find."
|
|
106
|
+
)
|
|
98
107
|
return
|
|
99
108
|
|
|
100
|
-
print(
|
|
109
|
+
print(
|
|
110
|
+
f"\nAgentCore: Searching for '{query}'...\n"
|
|
111
|
+
)
|
|
101
112
|
|
|
102
113
|
results = search_files(query)
|
|
103
114
|
|
|
@@ -105,47 +116,72 @@ def handle_instruction(instruction):
|
|
|
105
116
|
print("AgentCore: Nothing found.")
|
|
106
117
|
return
|
|
107
118
|
|
|
108
|
-
print(
|
|
119
|
+
print(
|
|
120
|
+
f"AgentCore: Found {len(results)} result(s)."
|
|
121
|
+
)
|
|
109
122
|
|
|
110
|
-
for index, result in enumerate(
|
|
123
|
+
for index, result in enumerate(
|
|
124
|
+
results,
|
|
125
|
+
start=1
|
|
126
|
+
):
|
|
111
127
|
print(f"{index}. {result}")
|
|
112
128
|
|
|
113
129
|
return
|
|
114
130
|
|
|
115
131
|
# Open a browser and search the web.
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
):
|
|
122
|
-
split_index = lower_instruction.index(separator)
|
|
132
|
+
if lower_instruction.startswith("open "):
|
|
133
|
+
search_separators = [
|
|
134
|
+
" and search for ",
|
|
135
|
+
" and search "
|
|
136
|
+
]
|
|
123
137
|
|
|
124
|
-
|
|
125
|
-
search_query = instruction[split_index + len(separator):].strip()
|
|
138
|
+
separator_found = None
|
|
126
139
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
140
|
+
for separator in search_separators:
|
|
141
|
+
if separator in lower_instruction:
|
|
142
|
+
separator_found = separator
|
|
143
|
+
break
|
|
130
144
|
|
|
131
|
-
if
|
|
132
|
-
|
|
145
|
+
if separator_found:
|
|
146
|
+
split_index = lower_instruction.index(
|
|
147
|
+
separator_found
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
browser_name = instruction[
|
|
151
|
+
5:split_index
|
|
152
|
+
].strip()
|
|
153
|
+
|
|
154
|
+
search_query = instruction[
|
|
155
|
+
split_index + len(separator_found):
|
|
156
|
+
].strip()
|
|
157
|
+
|
|
158
|
+
if not browser_name:
|
|
159
|
+
print(
|
|
160
|
+
"AgentCore: Tell me which browser to use."
|
|
161
|
+
)
|
|
162
|
+
return
|
|
163
|
+
|
|
164
|
+
if not search_query:
|
|
165
|
+
print(
|
|
166
|
+
"AgentCore: Tell me what you want to search for."
|
|
167
|
+
)
|
|
168
|
+
return
|
|
169
|
+
|
|
170
|
+
print(
|
|
171
|
+
f"\nAgentCore: Searching for "
|
|
172
|
+
f"'{search_query}' in "
|
|
173
|
+
f"{browser_name.title()}..."
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
success, message = search_in_browser(
|
|
177
|
+
search_query,
|
|
178
|
+
browser_name
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
print(f"AgentCore: {message}")
|
|
133
182
|
return
|
|
134
183
|
|
|
135
|
-
|
|
136
|
-
f"\nAgentCore: Searching for "
|
|
137
|
-
f"'{search_query}' in {browser_name.title()}..."
|
|
138
|
-
)
|
|
139
|
-
|
|
140
|
-
success, message = search_in_browser(
|
|
141
|
-
search_query,
|
|
142
|
-
browser_name
|
|
143
|
-
)
|
|
144
|
-
|
|
145
|
-
print(f"AgentCore: {message}")
|
|
146
|
-
return
|
|
147
|
-
|
|
148
|
-
# Open something in a specific application.
|
|
184
|
+
# Open something in VS Code.
|
|
149
185
|
if (
|
|
150
186
|
lower_instruction.startswith("open ")
|
|
151
187
|
and (
|
|
@@ -159,10 +195,14 @@ def handle_instruction(instruction):
|
|
|
159
195
|
query = instruction[5:-10].strip()
|
|
160
196
|
|
|
161
197
|
if not query:
|
|
162
|
-
print(
|
|
198
|
+
print(
|
|
199
|
+
"AgentCore: Tell me what you want to open."
|
|
200
|
+
)
|
|
163
201
|
return
|
|
164
202
|
|
|
165
|
-
print(
|
|
203
|
+
print(
|
|
204
|
+
f"\nAgentCore: Looking for '{query}'..."
|
|
205
|
+
)
|
|
166
206
|
|
|
167
207
|
system_location = get_system_location(query)
|
|
168
208
|
|
|
@@ -172,7 +212,9 @@ def handle_instruction(instruction):
|
|
|
172
212
|
path = choose_path(query)
|
|
173
213
|
|
|
174
214
|
if not path:
|
|
175
|
-
print(
|
|
215
|
+
print(
|
|
216
|
+
"AgentCore: I couldn't find that."
|
|
217
|
+
)
|
|
176
218
|
return
|
|
177
219
|
|
|
178
220
|
success, message = open_item(
|
|
@@ -188,18 +230,23 @@ def handle_instruction(instruction):
|
|
|
188
230
|
query = instruction[5:].strip()
|
|
189
231
|
|
|
190
232
|
if not query:
|
|
191
|
-
print(
|
|
233
|
+
print(
|
|
234
|
+
"AgentCore: Tell me what you want to open."
|
|
235
|
+
)
|
|
192
236
|
return
|
|
193
237
|
|
|
194
|
-
print(
|
|
238
|
+
print(
|
|
239
|
+
f"\nAgentCore: Looking for '{query}'..."
|
|
240
|
+
)
|
|
195
241
|
|
|
196
|
-
# 1. Check known locations
|
|
242
|
+
# 1. Check known Windows locations.
|
|
197
243
|
system_location = get_system_location(query)
|
|
198
244
|
|
|
199
245
|
if system_location:
|
|
200
246
|
path = str(system_location)
|
|
247
|
+
|
|
201
248
|
else:
|
|
202
|
-
# 2. Check installed
|
|
249
|
+
# 2. Check installed applications.
|
|
203
250
|
apps = find_apps(query)
|
|
204
251
|
|
|
205
252
|
if apps:
|
|
@@ -212,18 +259,23 @@ def handle_instruction(instruction):
|
|
|
212
259
|
|
|
213
260
|
if success:
|
|
214
261
|
print(
|
|
215
|
-
f"AgentCore: Launched
|
|
262
|
+
f"AgentCore: Launched "
|
|
263
|
+
f"{app['name']}"
|
|
216
264
|
)
|
|
217
265
|
else:
|
|
218
|
-
print(
|
|
266
|
+
print(
|
|
267
|
+
f"AgentCore: {message}"
|
|
268
|
+
)
|
|
219
269
|
|
|
220
270
|
return
|
|
221
271
|
|
|
222
|
-
# 3.
|
|
272
|
+
# 3. Search indexed files.
|
|
223
273
|
path = choose_path(query)
|
|
224
274
|
|
|
225
275
|
if not path:
|
|
226
|
-
print(
|
|
276
|
+
print(
|
|
277
|
+
"AgentCore: I couldn't find that."
|
|
278
|
+
)
|
|
227
279
|
return
|
|
228
280
|
|
|
229
281
|
success, message = open_item(path)
|
|
@@ -231,15 +283,22 @@ def handle_instruction(instruction):
|
|
|
231
283
|
print(f"AgentCore: {message}")
|
|
232
284
|
return
|
|
233
285
|
|
|
234
|
-
print(
|
|
286
|
+
print(
|
|
287
|
+
"AgentCore: I don't know how to perform "
|
|
288
|
+
"that task yet."
|
|
289
|
+
)
|
|
235
290
|
|
|
236
291
|
|
|
237
292
|
def main():
|
|
238
293
|
print("================================")
|
|
239
|
-
print("
|
|
294
|
+
print(" Makeless")
|
|
240
295
|
print("================================")
|
|
241
296
|
print("Starting local computer operator...")
|
|
242
297
|
|
|
298
|
+
# Automatically create and build the file index
|
|
299
|
+
# when Makeless runs for the first time.
|
|
300
|
+
ensure_index()
|
|
301
|
+
|
|
243
302
|
observer = start_watcher()
|
|
244
303
|
|
|
245
304
|
print("\nReady.")
|
|
@@ -251,7 +310,9 @@ def main():
|
|
|
251
310
|
instruction = input("You: ")
|
|
252
311
|
|
|
253
312
|
if instruction.strip().casefold() == "exit":
|
|
254
|
-
print(
|
|
313
|
+
print(
|
|
314
|
+
"\nAgentCore: Shutting down..."
|
|
315
|
+
)
|
|
255
316
|
break
|
|
256
317
|
|
|
257
318
|
if not instruction.strip():
|
|
@@ -260,11 +321,15 @@ def main():
|
|
|
260
321
|
handle_instruction(instruction)
|
|
261
322
|
|
|
262
323
|
except KeyboardInterrupt:
|
|
263
|
-
print(
|
|
324
|
+
print(
|
|
325
|
+
"\n\nAgentCore: Interrupted. "
|
|
326
|
+
"Shutting down..."
|
|
327
|
+
)
|
|
264
328
|
|
|
265
329
|
finally:
|
|
266
330
|
observer.stop()
|
|
267
331
|
observer.join()
|
|
332
|
+
|
|
268
333
|
print("AgentCore: Goodbye!")
|
|
269
334
|
|
|
270
335
|
|
|
@@ -1,9 +1,29 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: makeless
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.1
|
|
4
4
|
Summary: A local-first computer operator for Windows.
|
|
5
5
|
Author: Avinash
|
|
6
|
-
License:
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2026 Avinash Manikonda
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
7
27
|
Project-URL: Homepage, https://github.com/kkj4u2t7/agentcore
|
|
8
28
|
Project-URL: Repository, https://github.com/kkj4u2t7/agentcore
|
|
9
29
|
Requires-Python: >=3.9
|
|
@@ -3,28 +3,43 @@ import sqlite3
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
APP_DATA_DIR = Path(
|
|
7
|
+
os.environ.get("LOCALAPPDATA", Path.home())
|
|
8
|
+
) / "Makeless"
|
|
9
|
+
|
|
10
|
+
DATABASE_PATH = APP_DATA_DIR / "makeless.db"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def get_database_connection():
|
|
14
|
+
APP_DATA_DIR.mkdir(parents=True, exist_ok=True)
|
|
15
|
+
|
|
16
|
+
return sqlite3.connect(DATABASE_PATH)
|
|
8
17
|
|
|
9
18
|
|
|
10
19
|
def search_files(query, max_results=20):
|
|
11
20
|
query = query.casefold()
|
|
12
21
|
|
|
13
|
-
connection =
|
|
22
|
+
connection = get_database_connection()
|
|
14
23
|
cursor = connection.cursor()
|
|
15
24
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
try:
|
|
26
|
+
cursor.execute(
|
|
27
|
+
"""
|
|
28
|
+
SELECT path, name, item_type
|
|
29
|
+
FROM file_index
|
|
30
|
+
WHERE LOWER(name) LIKE ?
|
|
31
|
+
LIMIT ?
|
|
32
|
+
""",
|
|
33
|
+
(f"%{query}%", 200)
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
rows = cursor.fetchall()
|
|
25
37
|
|
|
26
|
-
|
|
27
|
-
|
|
38
|
+
except sqlite3.OperationalError:
|
|
39
|
+
rows = []
|
|
40
|
+
|
|
41
|
+
finally:
|
|
42
|
+
connection.close()
|
|
28
43
|
|
|
29
44
|
results = []
|
|
30
45
|
|
|
@@ -37,14 +52,12 @@ def search_files(query, max_results=20):
|
|
|
37
52
|
for path, name, item_type in rows:
|
|
38
53
|
normalized_path = path.casefold()
|
|
39
54
|
|
|
40
|
-
# Ignore locations that are almost never useful to a user.
|
|
41
55
|
if any(
|
|
42
56
|
ignored in normalized_path
|
|
43
57
|
for ignored in ignored_paths
|
|
44
58
|
):
|
|
45
59
|
continue
|
|
46
60
|
|
|
47
|
-
# Ignore files that no longer exist.
|
|
48
61
|
if not os.path.exists(path):
|
|
49
62
|
continue
|
|
50
63
|
|
|
@@ -63,7 +76,6 @@ def find_best_match(query):
|
|
|
63
76
|
return None
|
|
64
77
|
|
|
65
78
|
query = query.casefold()
|
|
66
|
-
project_root = str(PROJECT_ROOT).casefold()
|
|
67
79
|
|
|
68
80
|
def score_result(path):
|
|
69
81
|
normalized_path = path.casefold()
|
|
@@ -71,25 +83,17 @@ def find_best_match(query):
|
|
|
71
83
|
|
|
72
84
|
score = 0
|
|
73
85
|
|
|
74
|
-
# Exact filename match.
|
|
75
86
|
if name == query:
|
|
76
87
|
score += 1000
|
|
77
88
|
|
|
78
|
-
# Filename starts with the query.
|
|
79
89
|
elif name.startswith(query):
|
|
80
90
|
score += 500
|
|
81
91
|
|
|
82
|
-
# The AgentCore project gets a strong preference.
|
|
83
|
-
if normalized_path.startswith(project_root):
|
|
84
|
-
score += 2000
|
|
85
|
-
|
|
86
|
-
# Prefer normal user files.
|
|
87
92
|
user_home = str(Path.home()).casefold()
|
|
88
93
|
|
|
89
94
|
if normalized_path.startswith(user_home):
|
|
90
95
|
score += 200
|
|
91
96
|
|
|
92
|
-
# Penalize shortcuts.
|
|
93
97
|
if name.endswith(".lnk"):
|
|
94
98
|
score -= 500
|
|
95
99
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import sqlite3
|
|
3
3
|
import string
|
|
4
|
-
from pathlib import Path
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
from tools.filesystem import (
|
|
6
|
+
DATABASE_PATH,
|
|
7
|
+
APP_DATA_DIR
|
|
8
|
+
)
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
def get_available_drives():
|
|
@@ -21,28 +21,60 @@ def get_available_drives():
|
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
def create_database():
|
|
24
|
-
|
|
24
|
+
APP_DATA_DIR.mkdir(
|
|
25
|
+
parents=True,
|
|
26
|
+
exist_ok=True
|
|
27
|
+
)
|
|
25
28
|
|
|
26
29
|
connection = sqlite3.connect(DATABASE_PATH)
|
|
27
30
|
cursor = connection.cursor()
|
|
28
31
|
|
|
29
|
-
cursor.execute(
|
|
32
|
+
cursor.execute(
|
|
33
|
+
"""
|
|
30
34
|
CREATE TABLE IF NOT EXISTS file_index (
|
|
31
35
|
path TEXT PRIMARY KEY,
|
|
32
36
|
name TEXT NOT NULL,
|
|
33
37
|
item_type TEXT NOT NULL
|
|
34
38
|
)
|
|
35
|
-
|
|
39
|
+
"""
|
|
40
|
+
)
|
|
36
41
|
|
|
37
|
-
cursor.execute(
|
|
42
|
+
cursor.execute(
|
|
43
|
+
"""
|
|
38
44
|
CREATE INDEX IF NOT EXISTS idx_file_name
|
|
39
45
|
ON file_index(name)
|
|
40
|
-
|
|
46
|
+
"""
|
|
47
|
+
)
|
|
41
48
|
|
|
42
49
|
connection.commit()
|
|
43
50
|
connection.close()
|
|
44
51
|
|
|
45
52
|
|
|
53
|
+
def is_index_ready():
|
|
54
|
+
if not DATABASE_PATH.exists():
|
|
55
|
+
return False
|
|
56
|
+
|
|
57
|
+
try:
|
|
58
|
+
connection = sqlite3.connect(DATABASE_PATH)
|
|
59
|
+
cursor = connection.cursor()
|
|
60
|
+
|
|
61
|
+
cursor.execute(
|
|
62
|
+
"""
|
|
63
|
+
SELECT COUNT(*)
|
|
64
|
+
FROM file_index
|
|
65
|
+
"""
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
count = cursor.fetchone()[0]
|
|
69
|
+
|
|
70
|
+
connection.close()
|
|
71
|
+
|
|
72
|
+
return count > 0
|
|
73
|
+
|
|
74
|
+
except sqlite3.Error:
|
|
75
|
+
return False
|
|
76
|
+
|
|
77
|
+
|
|
46
78
|
def index_drive(drive):
|
|
47
79
|
connection = sqlite3.connect(DATABASE_PATH)
|
|
48
80
|
cursor = connection.cursor()
|
|
@@ -57,7 +89,6 @@ def index_drive(drive):
|
|
|
57
89
|
topdown=True,
|
|
58
90
|
followlinks=False
|
|
59
91
|
):
|
|
60
|
-
# Don't follow Windows links.
|
|
61
92
|
directories[:] = [
|
|
62
93
|
directory
|
|
63
94
|
for directory in directories
|
|
@@ -67,7 +98,10 @@ def index_drive(drive):
|
|
|
67
98
|
]
|
|
68
99
|
|
|
69
100
|
for directory in directories:
|
|
70
|
-
full_path = os.path.join(
|
|
101
|
+
full_path = os.path.join(
|
|
102
|
+
root,
|
|
103
|
+
directory
|
|
104
|
+
)
|
|
71
105
|
|
|
72
106
|
try:
|
|
73
107
|
cursor.execute(
|
|
@@ -76,7 +110,11 @@ def index_drive(drive):
|
|
|
76
110
|
(path, name, item_type)
|
|
77
111
|
VALUES (?, ?, ?)
|
|
78
112
|
""",
|
|
79
|
-
(
|
|
113
|
+
(
|
|
114
|
+
full_path,
|
|
115
|
+
directory,
|
|
116
|
+
"folder"
|
|
117
|
+
)
|
|
80
118
|
)
|
|
81
119
|
|
|
82
120
|
indexed_count += 1
|
|
@@ -84,8 +122,11 @@ def index_drive(drive):
|
|
|
84
122
|
except sqlite3.Error:
|
|
85
123
|
continue
|
|
86
124
|
|
|
87
|
-
for
|
|
88
|
-
full_path = os.path.join(
|
|
125
|
+
for file_name in files:
|
|
126
|
+
full_path = os.path.join(
|
|
127
|
+
root,
|
|
128
|
+
file_name
|
|
129
|
+
)
|
|
89
130
|
|
|
90
131
|
try:
|
|
91
132
|
cursor.execute(
|
|
@@ -94,7 +135,11 @@ def index_drive(drive):
|
|
|
94
135
|
(path, name, item_type)
|
|
95
136
|
VALUES (?, ?, ?)
|
|
96
137
|
""",
|
|
97
|
-
(
|
|
138
|
+
(
|
|
139
|
+
full_path,
|
|
140
|
+
file_name,
|
|
141
|
+
"file"
|
|
142
|
+
)
|
|
98
143
|
)
|
|
99
144
|
|
|
100
145
|
indexed_count += 1
|
|
@@ -102,8 +147,10 @@ def index_drive(drive):
|
|
|
102
147
|
except sqlite3.Error:
|
|
103
148
|
continue
|
|
104
149
|
|
|
105
|
-
|
|
106
|
-
|
|
150
|
+
if (
|
|
151
|
+
indexed_count > 0
|
|
152
|
+
and indexed_count % 1000 == 0
|
|
153
|
+
):
|
|
107
154
|
connection.commit()
|
|
108
155
|
|
|
109
156
|
except PermissionError:
|
|
@@ -112,11 +159,14 @@ def index_drive(drive):
|
|
|
112
159
|
connection.commit()
|
|
113
160
|
connection.close()
|
|
114
161
|
|
|
115
|
-
print(
|
|
162
|
+
print(
|
|
163
|
+
f"Finished {drive}: "
|
|
164
|
+
f"{indexed_count} items indexed."
|
|
165
|
+
)
|
|
116
166
|
|
|
117
167
|
|
|
118
168
|
def build_index():
|
|
119
|
-
print("\
|
|
169
|
+
print("\nMakeless File Indexer")
|
|
120
170
|
print("----------------------")
|
|
121
171
|
|
|
122
172
|
create_database()
|
|
@@ -129,5 +179,19 @@ def build_index():
|
|
|
129
179
|
print("\nIndexing complete.")
|
|
130
180
|
|
|
131
181
|
|
|
182
|
+
def ensure_index():
|
|
183
|
+
create_database()
|
|
184
|
+
|
|
185
|
+
if is_index_ready():
|
|
186
|
+
return
|
|
187
|
+
|
|
188
|
+
print(
|
|
189
|
+
"\nMakeless is setting up your "
|
|
190
|
+
"local file index for the first time..."
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
build_index()
|
|
194
|
+
|
|
195
|
+
|
|
132
196
|
if __name__ == "__main__":
|
|
133
197
|
build_index()
|
|
@@ -5,13 +5,7 @@ from pathlib import Path
|
|
|
5
5
|
from watchdog.events import FileSystemEventHandler
|
|
6
6
|
from watchdog.observers import Observer
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
PROJECT_ROOT = Path(__file__).resolve().parent.parent
|
|
10
|
-
DATABASE_PATH = PROJECT_ROOT / "data" / "agentcore.db"
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def get_database_connection():
|
|
14
|
-
return sqlite3.connect(DATABASE_PATH)
|
|
8
|
+
from tools.filesystem import get_database_connection
|
|
15
9
|
|
|
16
10
|
|
|
17
11
|
def add_to_index(path):
|
|
@@ -42,7 +36,10 @@ def add_to_index(path):
|
|
|
42
36
|
print(f"[INDEX] Added: {path}")
|
|
43
37
|
|
|
44
38
|
except sqlite3.Error as error:
|
|
45
|
-
print(
|
|
39
|
+
print(
|
|
40
|
+
f"[WATCHER ERROR] "
|
|
41
|
+
f"Could not add {path}: {error}"
|
|
42
|
+
)
|
|
46
43
|
|
|
47
44
|
|
|
48
45
|
def remove_from_index(path):
|
|
@@ -63,10 +60,13 @@ def remove_from_index(path):
|
|
|
63
60
|
print(f"[INDEX] Removed: {path}")
|
|
64
61
|
|
|
65
62
|
except sqlite3.Error as error:
|
|
66
|
-
print(
|
|
63
|
+
print(
|
|
64
|
+
f"[WATCHER ERROR] "
|
|
65
|
+
f"Could not remove {path}: {error}"
|
|
66
|
+
)
|
|
67
67
|
|
|
68
68
|
|
|
69
|
-
class
|
|
69
|
+
class MakelessFileHandler(FileSystemEventHandler):
|
|
70
70
|
|
|
71
71
|
def on_created(self, event):
|
|
72
72
|
add_to_index(event.src_path)
|
|
@@ -81,8 +81,9 @@ class AgentCoreFileHandler(FileSystemEventHandler):
|
|
|
81
81
|
|
|
82
82
|
def get_windows_folder(folder_name):
|
|
83
83
|
"""
|
|
84
|
-
Ask Windows for the actual location of a special
|
|
85
|
-
This supports folders redirected to
|
|
84
|
+
Ask Windows for the actual location of a special
|
|
85
|
+
user folder. This supports folders redirected to
|
|
86
|
+
OneDrive.
|
|
86
87
|
"""
|
|
87
88
|
|
|
88
89
|
try:
|
|
@@ -97,6 +98,7 @@ def get_windows_folder(folder_name):
|
|
|
97
98
|
winreg.HKEY_CURRENT_USER,
|
|
98
99
|
registry_key
|
|
99
100
|
) as key:
|
|
101
|
+
|
|
100
102
|
value, _ = winreg.QueryValueEx(
|
|
101
103
|
key,
|
|
102
104
|
folder_name
|
|
@@ -113,12 +115,9 @@ def get_windows_folder(folder_name):
|
|
|
113
115
|
def get_watch_paths():
|
|
114
116
|
home = Path.home()
|
|
115
117
|
|
|
116
|
-
# Ask Windows for these locations instead of guessing.
|
|
117
118
|
desktop = get_windows_folder("Desktop")
|
|
118
119
|
documents = get_windows_folder("Personal")
|
|
119
120
|
|
|
120
|
-
# Downloads is usually stored under the user's home folder.
|
|
121
|
-
# We'll make this dynamically discoverable in the next improvement.
|
|
122
121
|
downloads = home / "Downloads"
|
|
123
122
|
|
|
124
123
|
paths = [
|
|
@@ -142,11 +141,11 @@ def get_watch_paths():
|
|
|
142
141
|
|
|
143
142
|
def start_watcher():
|
|
144
143
|
observer = Observer()
|
|
145
|
-
handler =
|
|
144
|
+
handler = MakelessFileHandler()
|
|
146
145
|
|
|
147
146
|
watch_paths = get_watch_paths()
|
|
148
147
|
|
|
149
|
-
print("\
|
|
148
|
+
print("\nMakeless File Watcher")
|
|
150
149
|
print("----------------------")
|
|
151
150
|
|
|
152
151
|
for path in watch_paths:
|
|
@@ -167,12 +166,13 @@ if __name__ == "__main__":
|
|
|
167
166
|
observer = start_watcher()
|
|
168
167
|
|
|
169
168
|
try:
|
|
170
|
-
print(
|
|
169
|
+
print(
|
|
170
|
+
"\nWatcher is running. "
|
|
171
|
+
"Press Ctrl + C to stop."
|
|
172
|
+
)
|
|
171
173
|
|
|
172
|
-
|
|
173
|
-
pass
|
|
174
|
+
observer.join()
|
|
174
175
|
|
|
175
176
|
except KeyboardInterrupt:
|
|
176
177
|
observer.stop()
|
|
177
|
-
|
|
178
|
-
observer.join()
|
|
178
|
+
observer.join()
|
makeless-0.1.0/LICENSE
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
Copyright (c) 2026 Avinash
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|