agent-folder-workspace 0.1.0__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.
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/PKG-INFO +45 -10
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/README.md +44 -9
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/docs/architecture.md +19 -7
- agent_folder_workspace-0.1.2/docs/superpowers/plans/2026-07-22-console-logging.md +270 -0
- agent_folder_workspace-0.1.2/docs/superpowers/plans/2026-07-23-runtime-workspace-selection.md +384 -0
- agent_folder_workspace-0.1.2/docs/superpowers/specs/2026-07-22-console-logging-design.md +91 -0
- agent_folder_workspace-0.1.2/docs/superpowers/specs/2026-07-23-runtime-workspace-selection-design.md +141 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/docs/windows.md +39 -1
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/pyproject.toml +2 -2
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/scripts/check_wheel.py +17 -2
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/src/agent_folder_workspace/__init__.py +7 -1
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/src/agent_folder_workspace/__main__.py +2 -2
- agent_folder_workspace-0.1.2/src/agent_folder_workspace/cli.py +229 -0
- agent_folder_workspace-0.1.2/src/agent_folder_workspace/console_logging.py +209 -0
- agent_folder_workspace-0.1.2/src/agent_folder_workspace/mcp_server.py +304 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/src/agent_folder_workspace/models.py +20 -0
- agent_folder_workspace-0.1.2/src/agent_folder_workspace/opencode/skill/SKILL.md +30 -0
- agent_folder_workspace-0.1.2/src/agent_folder_workspace/schemas/WorkspaceCloseV1.schema.json +27 -0
- agent_folder_workspace-0.1.2/src/agent_folder_workspace/schemas/WorkspaceHandleV1.schema.json +509 -0
- agent_folder_workspace-0.1.2/src/agent_folder_workspace/schemas/WorkspaceListV1.schema.json +538 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/src/agent_folder_workspace/workspace.py +13 -4
- agent_folder_workspace-0.1.2/src/agent_folder_workspace/workspace_manager.py +234 -0
- agent_folder_workspace-0.1.2/tests/test_console_logging.py +266 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/tests/test_contracts.py +50 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/tests/test_distribution.py +13 -3
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/tests/test_mcp_cli_installer.py +149 -7
- agent_folder_workspace-0.1.2/tests/test_mcp_pdf_survival.py +195 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/tests/test_parser_workers.py +1 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/tests/test_search_cache.py +43 -0
- agent_folder_workspace-0.1.2/tests/test_workspace_manager.py +165 -0
- agent_folder_workspace-0.1.0/src/agent_folder_workspace/cli.py +0 -139
- agent_folder_workspace-0.1.0/src/agent_folder_workspace/mcp_server.py +0 -179
- agent_folder_workspace-0.1.0/src/agent_folder_workspace/opencode/skill/SKILL.md +0 -24
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/.github/workflows/ci.yml +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/.gitignore +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/CONTRIBUTING.md +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/LICENSE +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/SECURITY.md +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/docs/acceptance.md +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/docs/formats.md +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/scripts/generate_json_schemas.py +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/scripts/generate_probe_fixtures.py +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/src/agent_folder_workspace/adapters/__init__.py +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/src/agent_folder_workspace/adapters/base.py +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/src/agent_folder_workspace/adapters/legacy_office.py +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/src/agent_folder_workspace/adapters/manager.py +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/src/agent_folder_workspace/adapters/odf.py +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/src/agent_folder_workspace/adapters/ooxml.py +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/src/agent_folder_workspace/adapters/pdf.py +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/src/agent_folder_workspace/adapters/sqlite.py +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/src/agent_folder_workspace/adapters/structured.py +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/src/agent_folder_workspace/adapters/text.py +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/src/agent_folder_workspace/alternative_backends.py +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/src/agent_folder_workspace/backend_conversion_worker.py +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/src/agent_folder_workspace/backend_probe_worker.py +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/src/agent_folder_workspace/backends.py +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/src/agent_folder_workspace/cache.py +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/src/agent_folder_workspace/cfb.py +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/src/agent_folder_workspace/config.py +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/src/agent_folder_workspace/cursors.py +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/src/agent_folder_workspace/errors.py +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/src/agent_folder_workspace/ids.py +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/src/agent_folder_workspace/installer.py +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/src/agent_folder_workspace/legacy.py +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/src/agent_folder_workspace/opencode/plugin/agent-folder-workspace.ts.tmpl +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/src/agent_folder_workspace/parser_workers.py +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/src/agent_folder_workspace/path_safety.py +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/src/agent_folder_workspace/probe_fixtures/probe.doc +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/src/agent_folder_workspace/probe_fixtures/probe.ppt +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/src/agent_folder_workspace/probe_fixtures/probe.xls +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/src/agent_folder_workspace/py.typed +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/src/agent_folder_workspace/registry.py +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/src/agent_folder_workspace/schemas/CapabilityReportV1.schema.json +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/src/agent_folder_workspace/schemas/ContentPageV1.schema.json +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/src/agent_folder_workspace/schemas/DiagnosticV1.schema.json +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/src/agent_folder_workspace/schemas/NodePageV1.schema.json +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/src/agent_folder_workspace/schemas/NodeV1.schema.json +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/src/agent_folder_workspace/schemas/SearchPageV1.schema.json +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/tests/__init__.py +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/tests/cfb_fixture.py +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/tests/test_alternative_backends.py +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/tests/test_backends.py +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/tests/test_cfb.py +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/tests/test_content_adapters.py +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/tests/test_legacy_adapters.py +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/tests/test_legacy_fuzz.py +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/tests/test_legacy_parsers.py +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/tests/test_odf_pdf_adapters.py +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/tests/test_ooxml_adapters.py +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/tests/test_package.py +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/tests/test_structured_text.py +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/tests/test_windows_reparse_security.py +0 -0
- {agent_folder_workspace-0.1.0 → agent_folder_workspace-0.1.2}/tests/test_workspace_registry.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agent-folder-workspace
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: Offline, lazy, agent-oriented exploration of heterogeneous folders
|
|
5
5
|
Author-email: Dark Light <darklight@noreply.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -32,12 +32,12 @@ Description-Content-Type: text/markdown
|
|
|
32
32
|
|
|
33
33
|
# agent-folder-workspace
|
|
34
34
|
|
|
35
|
-
`agent-folder-workspace` presents
|
|
36
|
-
|
|
35
|
+
`agent-folder-workspace` presents local directories as stable, paginated node
|
|
36
|
+
hierarchies for agents. It discovers file metadata first and opens document
|
|
37
37
|
bodies only when a caller expands or reads a node. The default parser path is
|
|
38
38
|
local Python code: Microsoft Office and LibreOffice are not required.
|
|
39
39
|
|
|
40
|
-
> **Project status:** `0.1.
|
|
40
|
+
> **Project status:** `0.1.2` is an alpha contract. Read-only exploration and
|
|
41
41
|
> byte access are implemented; editing, rendering, OCR, and complete semantic
|
|
42
42
|
> coverage of every supported container are not.
|
|
43
43
|
|
|
@@ -92,7 +92,9 @@ adapters, and caching:
|
|
|
92
92
|
adapters and enforce one deadline across startup, queueing, IPC, and parsing.
|
|
93
93
|
5. `SizedLru` retains bounded decoded pages in memory. `ContentIndex` stores an
|
|
94
94
|
optional per-workspace SQLite search index in the application cache.
|
|
95
|
-
6.
|
|
95
|
+
6. `WorkspaceManager` gives one FastMCP process a bounded registry of active and
|
|
96
|
+
explicitly addressed workspace roots.
|
|
97
|
+
7. The Python API, Typer CLI, and FastMCP stdio server share the same workspace
|
|
96
98
|
methods and versioned response contracts.
|
|
97
99
|
|
|
98
100
|
Node IDs are deterministic hashes of relative and logical paths; they do not
|
|
@@ -131,7 +133,7 @@ archive, XML, and structural limits. It is frozen and rejects unknown fields.
|
|
|
131
133
|
The package exports a stable
|
|
132
134
|
error hierarchy (`FolderWorkspaceError`, `InputValidationError`,
|
|
133
135
|
`NodeNotFoundError`, `StaleCursorError`, and `ResourceLimitError`) and six
|
|
134
|
-
|
|
136
|
+
document wire models plus three workspace-selection models:
|
|
135
137
|
|
|
136
138
|
- `CapabilityReportV1`
|
|
137
139
|
- `ContentPageV1`
|
|
@@ -139,6 +141,9 @@ versioned wire models:
|
|
|
139
141
|
- `NodePageV1`
|
|
140
142
|
- `NodeV1`
|
|
141
143
|
- `SearchPageV1`
|
|
144
|
+
- `WorkspaceCloseV1`
|
|
145
|
+
- `WorkspaceHandleV1`
|
|
146
|
+
- `WorkspaceListV1`
|
|
142
147
|
|
|
143
148
|
Committed JSON Schemas live in
|
|
144
149
|
`src/agent_folder_workspace/schemas/`. Regenerate them with
|
|
@@ -147,7 +152,7 @@ Pydantic's `model_json_schema()` output.
|
|
|
147
152
|
|
|
148
153
|
## MCP server
|
|
149
154
|
|
|
150
|
-
Run one stdio server
|
|
155
|
+
Run one stdio server with an initial root:
|
|
151
156
|
|
|
152
157
|
```console
|
|
153
158
|
folderws mcp --root ./documents
|
|
@@ -155,6 +160,10 @@ folderws mcp --root ./documents
|
|
|
155
160
|
|
|
156
161
|
The server exposes exactly these tools:
|
|
157
162
|
|
|
163
|
+
- `open_workspace`
|
|
164
|
+
- `list_workspaces`
|
|
165
|
+
- `activate_workspace`
|
|
166
|
+
- `close_workspace`
|
|
158
167
|
- `workspace_info`
|
|
159
168
|
- `list_children`
|
|
160
169
|
- `get_node`
|
|
@@ -165,16 +174,40 @@ The server exposes exactly these tools:
|
|
|
165
174
|
- `refresh_workspace`
|
|
166
175
|
- `probe_backends`
|
|
167
176
|
|
|
177
|
+
`open_workspace(path)` accepts an explicit absolute directory, returns an opaque,
|
|
178
|
+
process-lifetime `workspace_id`, and makes that workspace active. Existing tools
|
|
179
|
+
accept an optional `workspace_id`; omission uses the active workspace. Opening
|
|
180
|
+
the same canonical root again reuses its ID. `list_workspaces`,
|
|
181
|
+
`activate_workspace`, and `close_workspace` manage the registry. One process
|
|
182
|
+
holds at most eight workspaces and never evicts one silently.
|
|
183
|
+
|
|
184
|
+
On Windows, runtime selection accepts `C:\Documents`, `C:/Documents`, UNC paths,
|
|
185
|
+
and Git Bash paths such as `/c/Documents`. Relative external paths are rejected.
|
|
186
|
+
The initial CLI root remains compatible with relative paths such as `.`.
|
|
187
|
+
|
|
168
188
|
Results use the same versioned models as the Python API. A method that is absent
|
|
169
189
|
from an injected workspace implementation returns a structured
|
|
170
190
|
`capability_unavailable` response instead of failing server construction.
|
|
191
|
+
Expected workspace/parser failures and unexpected tool exceptions become
|
|
192
|
+
structured results; a later MCP request remains available. PDF decoding remains
|
|
193
|
+
lazy and isolated in bounded parser workers.
|
|
171
194
|
|
|
172
195
|
The transport is stdio. There is no HTTP listener, authentication layer, or
|
|
173
|
-
|
|
174
|
-
permissions of the account that launched `folderws`;
|
|
196
|
+
network broker. The process and its MCP client share the operating-system
|
|
197
|
+
permissions of the account that launched `folderws`; open roots narrowly.
|
|
175
198
|
|
|
176
199
|
## CLI
|
|
177
200
|
|
|
201
|
+
All commands accept global options before the subcommand:
|
|
202
|
+
`folderws [--quiet | --verbose] [--log-file PATH] COMMAND ...`. Human status,
|
|
203
|
+
progress, warnings, and errors are written to stderr; the JSON reports from
|
|
204
|
+
`doctor`, `index`, `clear-cache`, and `install-opencode` remain unadorned on
|
|
205
|
+
stdout. MCP keeps stdout exclusively for JSON-RPC. By default, durable UTF-8
|
|
206
|
+
logs are written to `platformdirs`' user log directory as `folderws.log`, with
|
|
207
|
+
5 MiB rotation and three backups. An explicit log file must not be a symbolic
|
|
208
|
+
link, junction, or reparse-point destination; ordinary log permission failures
|
|
209
|
+
fall back to stderr-only operation.
|
|
210
|
+
|
|
178
211
|
```console
|
|
179
212
|
# Serve a folder to an MCP client over stdio
|
|
180
213
|
folderws mcp --root DIR
|
|
@@ -289,6 +322,8 @@ The normal Python parser path is offline and read-only. It does not upload
|
|
|
289
322
|
documents and does not execute document macros. Important controls include:
|
|
290
323
|
|
|
291
324
|
- roots that are symbolic links or reparse points are rejected;
|
|
325
|
+
- external roots are opened only by an explicit `open_workspace` call, are
|
|
326
|
+
bounded to eight per process, and receive process-lifetime opaque IDs;
|
|
292
327
|
- child links are not followed by default, and resolved sources must remain
|
|
293
328
|
under the selected root;
|
|
294
329
|
- SQLite files are opened with `mode=ro` and `PRAGMA query_only=ON`;
|
|
@@ -375,7 +410,7 @@ The detailed coverage and per-format caveats are in
|
|
|
375
410
|
rendering. A hard parent timeout cannot guarantee cleanup of a vendor process
|
|
376
411
|
that is itself stuck below COM or LibreOffice process control.
|
|
377
412
|
- The stated 100,000-node and warm p95 performance figures are design targets,
|
|
378
|
-
not certified guarantees in `0.1.
|
|
413
|
+
not certified guarantees in `0.1.2`; CI covers correctness across Windows,
|
|
379
414
|
Linux, and macOS but does not emulate a specific Windows-x64 SSD workload.
|
|
380
415
|
|
|
381
416
|
## Development
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# agent-folder-workspace
|
|
2
2
|
|
|
3
|
-
`agent-folder-workspace` presents
|
|
4
|
-
|
|
3
|
+
`agent-folder-workspace` presents local directories as stable, paginated node
|
|
4
|
+
hierarchies for agents. It discovers file metadata first and opens document
|
|
5
5
|
bodies only when a caller expands or reads a node. The default parser path is
|
|
6
6
|
local Python code: Microsoft Office and LibreOffice are not required.
|
|
7
7
|
|
|
8
|
-
> **Project status:** `0.1.
|
|
8
|
+
> **Project status:** `0.1.2` is an alpha contract. Read-only exploration and
|
|
9
9
|
> byte access are implemented; editing, rendering, OCR, and complete semantic
|
|
10
10
|
> coverage of every supported container are not.
|
|
11
11
|
|
|
@@ -60,7 +60,9 @@ adapters, and caching:
|
|
|
60
60
|
adapters and enforce one deadline across startup, queueing, IPC, and parsing.
|
|
61
61
|
5. `SizedLru` retains bounded decoded pages in memory. `ContentIndex` stores an
|
|
62
62
|
optional per-workspace SQLite search index in the application cache.
|
|
63
|
-
6.
|
|
63
|
+
6. `WorkspaceManager` gives one FastMCP process a bounded registry of active and
|
|
64
|
+
explicitly addressed workspace roots.
|
|
65
|
+
7. The Python API, Typer CLI, and FastMCP stdio server share the same workspace
|
|
64
66
|
methods and versioned response contracts.
|
|
65
67
|
|
|
66
68
|
Node IDs are deterministic hashes of relative and logical paths; they do not
|
|
@@ -99,7 +101,7 @@ archive, XML, and structural limits. It is frozen and rejects unknown fields.
|
|
|
99
101
|
The package exports a stable
|
|
100
102
|
error hierarchy (`FolderWorkspaceError`, `InputValidationError`,
|
|
101
103
|
`NodeNotFoundError`, `StaleCursorError`, and `ResourceLimitError`) and six
|
|
102
|
-
|
|
104
|
+
document wire models plus three workspace-selection models:
|
|
103
105
|
|
|
104
106
|
- `CapabilityReportV1`
|
|
105
107
|
- `ContentPageV1`
|
|
@@ -107,6 +109,9 @@ versioned wire models:
|
|
|
107
109
|
- `NodePageV1`
|
|
108
110
|
- `NodeV1`
|
|
109
111
|
- `SearchPageV1`
|
|
112
|
+
- `WorkspaceCloseV1`
|
|
113
|
+
- `WorkspaceHandleV1`
|
|
114
|
+
- `WorkspaceListV1`
|
|
110
115
|
|
|
111
116
|
Committed JSON Schemas live in
|
|
112
117
|
`src/agent_folder_workspace/schemas/`. Regenerate them with
|
|
@@ -115,7 +120,7 @@ Pydantic's `model_json_schema()` output.
|
|
|
115
120
|
|
|
116
121
|
## MCP server
|
|
117
122
|
|
|
118
|
-
Run one stdio server
|
|
123
|
+
Run one stdio server with an initial root:
|
|
119
124
|
|
|
120
125
|
```console
|
|
121
126
|
folderws mcp --root ./documents
|
|
@@ -123,6 +128,10 @@ folderws mcp --root ./documents
|
|
|
123
128
|
|
|
124
129
|
The server exposes exactly these tools:
|
|
125
130
|
|
|
131
|
+
- `open_workspace`
|
|
132
|
+
- `list_workspaces`
|
|
133
|
+
- `activate_workspace`
|
|
134
|
+
- `close_workspace`
|
|
126
135
|
- `workspace_info`
|
|
127
136
|
- `list_children`
|
|
128
137
|
- `get_node`
|
|
@@ -133,16 +142,40 @@ The server exposes exactly these tools:
|
|
|
133
142
|
- `refresh_workspace`
|
|
134
143
|
- `probe_backends`
|
|
135
144
|
|
|
145
|
+
`open_workspace(path)` accepts an explicit absolute directory, returns an opaque,
|
|
146
|
+
process-lifetime `workspace_id`, and makes that workspace active. Existing tools
|
|
147
|
+
accept an optional `workspace_id`; omission uses the active workspace. Opening
|
|
148
|
+
the same canonical root again reuses its ID. `list_workspaces`,
|
|
149
|
+
`activate_workspace`, and `close_workspace` manage the registry. One process
|
|
150
|
+
holds at most eight workspaces and never evicts one silently.
|
|
151
|
+
|
|
152
|
+
On Windows, runtime selection accepts `C:\Documents`, `C:/Documents`, UNC paths,
|
|
153
|
+
and Git Bash paths such as `/c/Documents`. Relative external paths are rejected.
|
|
154
|
+
The initial CLI root remains compatible with relative paths such as `.`.
|
|
155
|
+
|
|
136
156
|
Results use the same versioned models as the Python API. A method that is absent
|
|
137
157
|
from an injected workspace implementation returns a structured
|
|
138
158
|
`capability_unavailable` response instead of failing server construction.
|
|
159
|
+
Expected workspace/parser failures and unexpected tool exceptions become
|
|
160
|
+
structured results; a later MCP request remains available. PDF decoding remains
|
|
161
|
+
lazy and isolated in bounded parser workers.
|
|
139
162
|
|
|
140
163
|
The transport is stdio. There is no HTTP listener, authentication layer, or
|
|
141
|
-
|
|
142
|
-
permissions of the account that launched `folderws`;
|
|
164
|
+
network broker. The process and its MCP client share the operating-system
|
|
165
|
+
permissions of the account that launched `folderws`; open roots narrowly.
|
|
143
166
|
|
|
144
167
|
## CLI
|
|
145
168
|
|
|
169
|
+
All commands accept global options before the subcommand:
|
|
170
|
+
`folderws [--quiet | --verbose] [--log-file PATH] COMMAND ...`. Human status,
|
|
171
|
+
progress, warnings, and errors are written to stderr; the JSON reports from
|
|
172
|
+
`doctor`, `index`, `clear-cache`, and `install-opencode` remain unadorned on
|
|
173
|
+
stdout. MCP keeps stdout exclusively for JSON-RPC. By default, durable UTF-8
|
|
174
|
+
logs are written to `platformdirs`' user log directory as `folderws.log`, with
|
|
175
|
+
5 MiB rotation and three backups. An explicit log file must not be a symbolic
|
|
176
|
+
link, junction, or reparse-point destination; ordinary log permission failures
|
|
177
|
+
fall back to stderr-only operation.
|
|
178
|
+
|
|
146
179
|
```console
|
|
147
180
|
# Serve a folder to an MCP client over stdio
|
|
148
181
|
folderws mcp --root DIR
|
|
@@ -257,6 +290,8 @@ The normal Python parser path is offline and read-only. It does not upload
|
|
|
257
290
|
documents and does not execute document macros. Important controls include:
|
|
258
291
|
|
|
259
292
|
- roots that are symbolic links or reparse points are rejected;
|
|
293
|
+
- external roots are opened only by an explicit `open_workspace` call, are
|
|
294
|
+
bounded to eight per process, and receive process-lifetime opaque IDs;
|
|
260
295
|
- child links are not followed by default, and resolved sources must remain
|
|
261
296
|
under the selected root;
|
|
262
297
|
- SQLite files are opened with `mode=ro` and `PRAGMA query_only=ON`;
|
|
@@ -343,7 +378,7 @@ The detailed coverage and per-format caveats are in
|
|
|
343
378
|
rendering. A hard parent timeout cannot guarantee cleanup of a vendor process
|
|
344
379
|
that is itself stuck below COM or LibreOffice process control.
|
|
345
380
|
- The stated 100,000-node and warm p95 performance figures are design targets,
|
|
346
|
-
not certified guarantees in `0.1.
|
|
381
|
+
not certified guarantees in `0.1.2`; CI covers correctness across Windows,
|
|
347
382
|
Linux, and macOS but does not emulate a specific Windows-x64 SSD workload.
|
|
348
383
|
|
|
349
384
|
## Development
|
|
@@ -7,7 +7,8 @@ tests, not this prose alone, define the wire contract.
|
|
|
7
7
|
|
|
8
8
|
```mermaid
|
|
9
9
|
flowchart LR
|
|
10
|
-
Client["Python, CLI, or MCP client"] -->
|
|
10
|
+
Client["Python, CLI, or MCP client"] --> Session["WorkspaceManager"]
|
|
11
|
+
Session --> Workspace["FolderWorkspace"]
|
|
11
12
|
Workspace --> Registry["NodeRegistry metadata tree"]
|
|
12
13
|
Workspace --> Manager["AdapterManager"]
|
|
13
14
|
Manager --> Direct["Direct text, CSV, and binary paths"]
|
|
@@ -31,7 +32,7 @@ is an explicit diagnostic path.
|
|
|
31
32
|
|
|
32
33
|
`models.py` contains strict Pydantic models. Wire-level models carry
|
|
33
34
|
`schema_version="1.0"`; unknown fields are rejected and non-finite numbers are
|
|
34
|
-
not accepted. The
|
|
35
|
+
not accepted. The nine top-level exported models have committed JSON Schemas in
|
|
35
36
|
`src/agent_folder_workspace/schemas/`.
|
|
36
37
|
|
|
37
38
|
`errors.py` provides stable exception categories, and `ids.py` creates node IDs
|
|
@@ -192,9 +193,18 @@ copies.
|
|
|
192
193
|
|
|
193
194
|
The Python API returns model instances and raises the public error hierarchy.
|
|
194
195
|
The FastMCP server serializes model responses with `model_dump(mode="json")`
|
|
195
|
-
and exposes a fixed
|
|
196
|
-
|
|
197
|
-
|
|
196
|
+
and exposes a fixed thirteen-tool stdio surface. `WorkspaceManager` owns at
|
|
197
|
+
most eight roots, opaque process-lifetime IDs, canonical-root deduplication,
|
|
198
|
+
active selection, and deterministic cleanup. Existing tools accept an optional
|
|
199
|
+
workspace ID and otherwise route to the active root. Its capability dispatcher
|
|
200
|
+
resolves workspace methods at call time so reduced injected implementations
|
|
201
|
+
return a structured `capability_unavailable` error.
|
|
202
|
+
|
|
203
|
+
MCP tool boundaries translate public workspace errors to stable error codes and
|
|
204
|
+
sanitize unexpected failures behind a correlation ID. This leaves the stdio
|
|
205
|
+
transport usable for a later request. Risky PDF and container semantics remain
|
|
206
|
+
inside parser workers; opening or listing a filesystem root does not decode
|
|
207
|
+
their payloads.
|
|
198
208
|
|
|
199
209
|
The Typer CLI provides MCP serving, backend diagnostics, indexing, cache
|
|
200
210
|
clearing, and managed OpenCode integration installation. OpenCode resources are
|
|
@@ -218,8 +228,10 @@ enterprise policy, add-in, crash, or vendor defect.
|
|
|
218
228
|
|
|
219
229
|
## Security boundaries
|
|
220
230
|
|
|
221
|
-
-
|
|
222
|
-
implement per-node authorization.
|
|
231
|
+
- Every selected root is a separate filesystem authority boundary; the library
|
|
232
|
+
does not implement per-node authorization.
|
|
233
|
+
- Runtime roots require an explicit absolute path. Windows native, slash-style,
|
|
234
|
+
UNC, and Git Bash drive paths normalize before registry deduplication.
|
|
223
235
|
- The default MCP transport is local stdio; operating-system permissions govern
|
|
224
236
|
the process and client.
|
|
225
237
|
- `read_binary` intentionally reveals file bytes inside the root.
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
# Console Logging Implementation Plan
|
|
2
|
+
|
|
3
|
+
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
4
|
+
|
|
5
|
+
**Goal:** Add durable, safe, cross-platform console and file logging to `folderws` without changing JSON stdout or MCP stdio contracts.
|
|
6
|
+
|
|
7
|
+
**Architecture:** A focused `console_logging.py` module owns handler setup, safe log-path selection, redaction, lifecycle context, and throttled progress. The Typer root callback initializes it once, command functions emit lifecycle events, and a `main()` entrypoint translates package and unexpected exceptions into stable exits while preserving Click/Typer behavior. `FolderWorkspace.index_content()` exposes an optional callback so the CLI can report bounded progress without affecting Python or MCP callers.
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** Python 3.11+, stdlib `logging`/`RotatingFileHandler`, Typer/Click, platformdirs, pytest, Ruff, strict Mypy, Hatchling.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
### Task 1: Implement and verify the complete logging contract
|
|
14
|
+
|
|
15
|
+
**Files:**
|
|
16
|
+
- Create: `src/agent_folder_workspace/console_logging.py`
|
|
17
|
+
- Create: `tests/test_console_logging.py`
|
|
18
|
+
- Modify: `src/agent_folder_workspace/cli.py`
|
|
19
|
+
- Modify: `src/agent_folder_workspace/__main__.py`
|
|
20
|
+
- Modify: `src/agent_folder_workspace/workspace.py:1010`
|
|
21
|
+
- Modify: `pyproject.toml:41`
|
|
22
|
+
- Modify: `README.md:145`
|
|
23
|
+
- Modify: `docs/windows.md:20`
|
|
24
|
+
|
|
25
|
+
- [ ] **Step 1: Write failing unit tests for logging setup, path safety, rotation, and redaction**
|
|
26
|
+
|
|
27
|
+
Add tests which import the wished-for API below, configure it against `tmp_path`, write records, flush handlers, and assert that secrets are absent from both console and file output:
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
from agent_folder_workspace.console_logging import (
|
|
31
|
+
LoggingConfigurationError,
|
|
32
|
+
configure_logging,
|
|
33
|
+
default_log_file,
|
|
34
|
+
redact_text,
|
|
35
|
+
shutdown_logging,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
def test_redact_text_hides_known_secret_shapes() -> None:
|
|
39
|
+
value = "api-key=secret OPENAI_API_KEY=sk-test Authorization: Bearer token"
|
|
40
|
+
redacted = redact_text(value)
|
|
41
|
+
assert "secret" not in redacted
|
|
42
|
+
assert "sk-test" not in redacted
|
|
43
|
+
assert "token" not in redacted
|
|
44
|
+
|
|
45
|
+
def test_configure_logging_writes_human_console_to_stderr_and_debug_to_file(
|
|
46
|
+
tmp_path: Path, capsys: pytest.CaptureFixture[str]
|
|
47
|
+
) -> None:
|
|
48
|
+
state = configure_logging(log_file=tmp_path / "folderws.log")
|
|
49
|
+
logger = logging.getLogger("agent_folder_workspace.test")
|
|
50
|
+
logger.info("visible status")
|
|
51
|
+
logger.debug("durable detail")
|
|
52
|
+
shutdown_logging(state)
|
|
53
|
+
captured = capsys.readouterr()
|
|
54
|
+
assert captured.out == ""
|
|
55
|
+
assert "visible status" in captured.err
|
|
56
|
+
assert "durable detail" in (tmp_path / "folderws.log").read_text(encoding="utf-8")
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Cover default path resolution via a monkeypatched platform log directory, `quiet` showing only errors, `verbose` showing debug, three 5 MiB rotating backups, a linked/reparse terminal or parent raising `LoggingConfigurationError`, an unwritable file falling back without aborting, and Windows-style lexical path text not crashing formatting. Keep each test isolated by calling `shutdown_logging()`.
|
|
60
|
+
|
|
61
|
+
- [ ] **Step 2: Run the new logging tests and verify RED**
|
|
62
|
+
|
|
63
|
+
Run: `pytest tests/test_console_logging.py -v`
|
|
64
|
+
|
|
65
|
+
Expected: collection fails because `agent_folder_workspace.console_logging` does not exist.
|
|
66
|
+
|
|
67
|
+
- [ ] **Step 3: Implement the focused logging module minimally**
|
|
68
|
+
|
|
69
|
+
Implement these typed public interfaces and private helpers in `console_logging.py`:
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
@dataclass(frozen=True)
|
|
73
|
+
class LoggingState:
|
|
74
|
+
correlation_id: str
|
|
75
|
+
log_file: Path | None
|
|
76
|
+
handlers: tuple[logging.Handler, ...]
|
|
77
|
+
|
|
78
|
+
class LoggingConfigurationError(ValueError):
|
|
79
|
+
pass
|
|
80
|
+
|
|
81
|
+
def default_log_file() -> Path:
|
|
82
|
+
return Path(user_log_dir("agent-folder-workspace", appauthor=False)) / "folderws.log"
|
|
83
|
+
|
|
84
|
+
def redact_text(value: object) -> str: ...
|
|
85
|
+
|
|
86
|
+
def configure_logging(
|
|
87
|
+
*, quiet: bool = False, verbose: bool = False, log_file: Path | None = None
|
|
88
|
+
) -> LoggingState: ...
|
|
89
|
+
|
|
90
|
+
def shutdown_logging(state: LoggingState | None = None) -> None: ...
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Use a package-root logger with propagation disabled, a stderr `StreamHandler`, and a UTF-8 `RotatingFileHandler(maxBytes=5 * 1024 * 1024, backupCount=3)`. File level is DEBUG; console level is ERROR for quiet, DEBUG for verbose, INFO otherwise. Refuse any destination for which `first_unsafe_component()` returns a component. Create parent directories only after validation, revalidate before opening, and catch ordinary file-handler setup failures so console logging remains available with one warning. Redact bearer tokens and values associated with case-insensitive names containing `api_key`, `apikey`, `token`, `secret`, `password`, or `authorization`. Formatting and redaction must fall back to safe type names instead of raising.
|
|
94
|
+
|
|
95
|
+
- [ ] **Step 4: Run logging tests and verify GREEN**
|
|
96
|
+
|
|
97
|
+
Run: `pytest tests/test_console_logging.py -v`
|
|
98
|
+
|
|
99
|
+
Expected: all tests pass.
|
|
100
|
+
|
|
101
|
+
- [ ] **Step 5: Write failing tests for deterministic workspace index progress**
|
|
102
|
+
|
|
103
|
+
In `tests/test_search_cache.py`, create a workspace with two readable files and one adapter failure, then call the wished-for callback contract:
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
events: list[tuple[int, int, int]] = []
|
|
107
|
+
info = workspace.index_content(progress=lambda done, total, failed: events.append((done, total, failed)))
|
|
108
|
+
assert events[-1] == (3, 3, 1)
|
|
109
|
+
assert [event[0] for event in events] == [1, 2, 3]
|
|
110
|
+
assert info.total_files == 3
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Also assert that `workspace.index_content()` with no callback remains valid.
|
|
114
|
+
|
|
115
|
+
- [ ] **Step 6: Run the progress tests and verify RED**
|
|
116
|
+
|
|
117
|
+
Run: `pytest tests/test_search_cache.py -v`
|
|
118
|
+
|
|
119
|
+
Expected: the callback test fails because `index_content()` does not accept `progress`.
|
|
120
|
+
|
|
121
|
+
- [ ] **Step 7: Add the optional progress callback to the workspace API**
|
|
122
|
+
|
|
123
|
+
Change the signature and loop without changing exception swallowing:
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
def index_content(
|
|
127
|
+
self,
|
|
128
|
+
*,
|
|
129
|
+
progress: Callable[[int, int, int], None] | None = None,
|
|
130
|
+
) -> WorkspaceInfoV1:
|
|
131
|
+
files = self._file_nodes()
|
|
132
|
+
failed = 0
|
|
133
|
+
for completed, file_node in enumerate(files, start=1):
|
|
134
|
+
index.begin_file(file_node)
|
|
135
|
+
try:
|
|
136
|
+
self._index_file(file_node, index)
|
|
137
|
+
except (FolderWorkspaceError, OSError, ValueError):
|
|
138
|
+
failed += 1
|
|
139
|
+
else:
|
|
140
|
+
index.finish_file(file_node.id)
|
|
141
|
+
if progress is not None:
|
|
142
|
+
progress(completed, len(files), failed)
|
|
143
|
+
return self.workspace_info()
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Import `Callable` from `collections.abc`. Never invoke the callback with paths or content.
|
|
147
|
+
|
|
148
|
+
- [ ] **Step 8: Run the progress tests and verify GREEN**
|
|
149
|
+
|
|
150
|
+
Run: `pytest tests/test_search_cache.py -v`
|
|
151
|
+
|
|
152
|
+
Expected: all progress and existing search/cache tests pass.
|
|
153
|
+
|
|
154
|
+
- [ ] **Step 9: Write failing CLI contract tests before changing CLI code**
|
|
155
|
+
|
|
156
|
+
Extend `tests/test_console_logging.py` and `tests/test_mcp_cli_installer.py` to prove:
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
result = CliRunner().invoke(app, ["--log-file", str(log_file), "doctor"])
|
|
160
|
+
assert result.exit_code == 0
|
|
161
|
+
json.loads(result.stdout)
|
|
162
|
+
assert "command=doctor" in result.stderr
|
|
163
|
+
|
|
164
|
+
conflict = CliRunner().invoke(app, ["--quiet", "--verbose", "doctor"])
|
|
165
|
+
assert conflict.exit_code == 2
|
|
166
|
+
|
|
167
|
+
quiet = CliRunner().invoke(app, ["--quiet", "--log-file", str(log_file), "doctor"])
|
|
168
|
+
assert quiet.stderr == ""
|
|
169
|
+
json.loads(quiet.stdout)
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Add tests for command start/completion and elapsed time, selected roots/targets, install conflict exit 2, known `FolderWorkspaceError` declared exit code, an `OSError` exit 1 with concise stderr, unexpected failure exit 1 with a correlation ID and no traceback on stderr but a traceback in the file, and throttled `index` progress including final counts. Assert secrets passed in exception messages are redacted. Exercise `main()` directly for translated errors.
|
|
173
|
+
|
|
174
|
+
For MCP purity, run the command with a monkeypatched `run_mcp` that writes a JSON-RPC line to stdout and logs an INFO record; assert stdout contains only the JSON-RPC line and the human record is on stderr. Assert that Click usage errors and explicit `typer.Exit` retain native exit codes.
|
|
175
|
+
|
|
176
|
+
- [ ] **Step 10: Run CLI tests and verify RED**
|
|
177
|
+
|
|
178
|
+
Run: `pytest tests/test_console_logging.py tests/test_mcp_cli_installer.py -v`
|
|
179
|
+
|
|
180
|
+
Expected: failures show missing global options, lifecycle output, entrypoint wrapper, and CLI progress wiring.
|
|
181
|
+
|
|
182
|
+
- [ ] **Step 11: Integrate logging into Typer and the package entrypoints**
|
|
183
|
+
|
|
184
|
+
Add a root callback with `--quiet`, `--verbose`, and `--log-file`; reject the option conflict with `typer.BadParameter`. Store `LoggingState` in `typer.Context.obj`. Add a lifecycle context manager and throttled progress reporter in `console_logging.py` with these interfaces:
|
|
185
|
+
|
|
186
|
+
```python
|
|
187
|
+
@contextmanager
|
|
188
|
+
def command_lifecycle(command: str, **safe_fields: object) -> Iterator[None]: ...
|
|
189
|
+
|
|
190
|
+
class IndexProgressReporter:
|
|
191
|
+
def __call__(self, completed: int, total: int, failed: int) -> None: ...
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
The reporter emits the first event, final event, failures, and bounded intermediate events (at most once per second or every 100 files), while unit tests can inject a clock and interval. Wrap each command body in `command_lifecycle`, logging only safe paths/counts/flags. For `index`, call `workspace.index_content(progress=IndexProgressReporter(...))` directly and preserve the stable JSON result using `_jsonable` behavior; keep late capability handling if the method is unavailable.
|
|
195
|
+
|
|
196
|
+
Implement:
|
|
197
|
+
|
|
198
|
+
```python
|
|
199
|
+
def main() -> None:
|
|
200
|
+
try:
|
|
201
|
+
app(standalone_mode=False)
|
|
202
|
+
except (click.UsageError, click.Abort):
|
|
203
|
+
raise
|
|
204
|
+
except click.exceptions.Exit as error:
|
|
205
|
+
raise SystemExit(error.exit_code) from error
|
|
206
|
+
except FolderWorkspaceError as error:
|
|
207
|
+
log_expected_failure(error)
|
|
208
|
+
raise SystemExit(error.exit_code) from error
|
|
209
|
+
except (OSError, subprocess.SubprocessError) as error:
|
|
210
|
+
log_expected_failure(error)
|
|
211
|
+
raise SystemExit(1) from error
|
|
212
|
+
except Exception as error:
|
|
213
|
+
log_unexpected_failure(error)
|
|
214
|
+
raise SystemExit(1) from error
|
|
215
|
+
finally:
|
|
216
|
+
shutdown_logging()
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Adapt exception handling as required by actual Typer behavior, but preserve native usage/help and explicit exit behavior. Update `pyproject.toml` to `folderws = "agent_folder_workspace.cli:main"` and `__main__.py` to call `main()`. Ensure unexpected tracebacks use only the file handler; stderr gets a concise redacted correlation ID and log path.
|
|
220
|
+
|
|
221
|
+
- [ ] **Step 12: Run CLI tests and verify GREEN**
|
|
222
|
+
|
|
223
|
+
Run: `pytest tests/test_console_logging.py tests/test_mcp_cli_installer.py -v`
|
|
224
|
+
|
|
225
|
+
Expected: all tests pass and JSON/MCP stdout assertions remain exact.
|
|
226
|
+
|
|
227
|
+
- [ ] **Step 13: Document the global logging behavior**
|
|
228
|
+
|
|
229
|
+
Add the following command synopsis and operational notes to README and Windows docs:
|
|
230
|
+
|
|
231
|
+
```console
|
|
232
|
+
folderws [--quiet | --verbose] [--log-file PATH] COMMAND [ARGS]...
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
State that human output is stderr, JSON/MCP remains stdout, default logs use the platform user log directory with three 5 MiB backups, `--quiet` retains errors, `--verbose` adds console diagnostics, and a custom log path must not traverse symlinks/junctions/reparse points. Include PowerShell and Git Bash examples with paths containing spaces.
|
|
236
|
+
|
|
237
|
+
- [ ] **Step 14: Run focused static and formatting gates**
|
|
238
|
+
|
|
239
|
+
Run:
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
ruff format src/agent_folder_workspace/console_logging.py src/agent_folder_workspace/cli.py src/agent_folder_workspace/workspace.py tests/test_console_logging.py tests/test_mcp_cli_installer.py tests/test_search_cache.py
|
|
243
|
+
ruff check src/agent_folder_workspace/console_logging.py src/agent_folder_workspace/cli.py src/agent_folder_workspace/workspace.py tests/test_console_logging.py tests/test_mcp_cli_installer.py tests/test_search_cache.py
|
|
244
|
+
mypy --no-incremental
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
Expected: formatting makes no further changes on a second check, Ruff reports no errors, and Mypy reports success.
|
|
248
|
+
|
|
249
|
+
- [ ] **Step 15: Run all release gates and installed-wheel smoke tests**
|
|
250
|
+
|
|
251
|
+
Run:
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
python scripts/generate_json_schemas.py --check
|
|
255
|
+
ruff format --check src scripts tests
|
|
256
|
+
ruff check --no-cache .
|
|
257
|
+
mypy --no-incremental
|
|
258
|
+
pytest
|
|
259
|
+
rm -rf dist
|
|
260
|
+
python -m build --no-isolation
|
|
261
|
+
python -m twine check dist/*
|
|
262
|
+
python scripts/check_wheel.py dist --install-smoke
|
|
263
|
+
python scripts/check_wheel.py --reproducible
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
Expected: every command exits 0; the fresh-wheel CLI smoke confirms `folderws --help`, JSON stdout, stderr logging, explicit `--log-file`, and MCP importability. If Twine is not installed, use an isolated temporary environment to install it rather than adding it as a runtime dependency.
|
|
267
|
+
|
|
268
|
+
- [ ] **Step 16: Commit the implementation**
|
|
269
|
+
|
|
270
|
+
Stage only the files listed above and commit through the repository's required commit-policy helper with a title such as `Add durable folder workspace logging` and bullets covering protocol-safe stderr/file logging, progress/error diagnostics, and verification coverage.
|