sprucelab 0.1.0__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.
Files changed (57) hide show
  1. sprucelab-0.1.0/.gitignore +291 -0
  2. sprucelab-0.1.0/PKG-INFO +188 -0
  3. sprucelab-0.1.0/README.md +158 -0
  4. sprucelab-0.1.0/pyproject.toml +64 -0
  5. sprucelab-0.1.0/pytest.ini +13 -0
  6. sprucelab-0.1.0/sprucelab/__init__.py +19 -0
  7. sprucelab-0.1.0/sprucelab/cli/__init__.py +3 -0
  8. sprucelab-0.1.0/sprucelab/cli/__main__.py +5 -0
  9. sprucelab-0.1.0/sprucelab/cli/_auth.py +44 -0
  10. sprucelab-0.1.0/sprucelab/cli/_errors.py +174 -0
  11. sprucelab-0.1.0/sprucelab/cli/api_client.py +226 -0
  12. sprucelab-0.1.0/sprucelab/cli/app.py +547 -0
  13. sprucelab-0.1.0/sprucelab/cli/capabilities.py +159 -0
  14. sprucelab-0.1.0/sprucelab/cli/cde/__init__.py +1 -0
  15. sprucelab-0.1.0/sprucelab/cli/claims.py +330 -0
  16. sprucelab-0.1.0/sprucelab/cli/config.py +99 -0
  17. sprucelab-0.1.0/sprucelab/cli/dev.py +441 -0
  18. sprucelab-0.1.0/sprucelab/cli/embed.py +211 -0
  19. sprucelab-0.1.0/sprucelab/cli/executor.py +343 -0
  20. sprucelab-0.1.0/sprucelab/cli/files.py +915 -0
  21. sprucelab-0.1.0/sprucelab/cli/issues.py +271 -0
  22. sprucelab-0.1.0/sprucelab/cli/log.py +160 -0
  23. sprucelab-0.1.0/sprucelab/cli/models.py +102 -0
  24. sprucelab-0.1.0/sprucelab/cli/projects.py +839 -0
  25. sprucelab-0.1.0/sprucelab/cli/scopes.py +148 -0
  26. sprucelab-0.1.0/sprucelab/cli/scripts.py +146 -0
  27. sprucelab-0.1.0/sprucelab/cli/steps/__init__.py +1 -0
  28. sprucelab-0.1.0/sprucelab/cli/types.py +319 -0
  29. sprucelab-0.1.0/sprucelab/cli/utils/__init__.py +1 -0
  30. sprucelab-0.1.0/sprucelab/cli/verify.py +100 -0
  31. sprucelab-0.1.0/sprucelab/cli/webhooks.py +524 -0
  32. sprucelab-0.1.0/sprucelab/mcp/__init__.py +8 -0
  33. sprucelab-0.1.0/sprucelab/mcp/__main__.py +6 -0
  34. sprucelab-0.1.0/sprucelab/mcp/client.py +102 -0
  35. sprucelab-0.1.0/sprucelab/mcp/server.py +653 -0
  36. sprucelab-0.1.0/tests/__init__.py +0 -0
  37. sprucelab-0.1.0/tests/conftest.py +57 -0
  38. sprucelab-0.1.0/tests/integration/__init__.py +0 -0
  39. sprucelab-0.1.0/tests/integration/conftest.py +57 -0
  40. sprucelab-0.1.0/tests/integration/test_smoke_live.py +253 -0
  41. sprucelab-0.1.0/tests/integration/test_smoke_live_full_chain.py +432 -0
  42. sprucelab-0.1.0/tests/integration/test_smoke_live_tenant_loop.py +203 -0
  43. sprucelab-0.1.0/tests/test_auth_tokens_cli.py +88 -0
  44. sprucelab-0.1.0/tests/test_capabilities_cli.py +101 -0
  45. sprucelab-0.1.0/tests/test_claims_cli.py +422 -0
  46. sprucelab-0.1.0/tests/test_errors.py +230 -0
  47. sprucelab-0.1.0/tests/test_files_cli.py +1013 -0
  48. sprucelab-0.1.0/tests/test_issues_cli.py +149 -0
  49. sprucelab-0.1.0/tests/test_log_cli.py +189 -0
  50. sprucelab-0.1.0/tests/test_mcp_server_tools.py +339 -0
  51. sprucelab-0.1.0/tests/test_models_cli.py +153 -0
  52. sprucelab-0.1.0/tests/test_projects_cli.py +623 -0
  53. sprucelab-0.1.0/tests/test_scopes_cli.py +58 -0
  54. sprucelab-0.1.0/tests/test_scripts_cli.py +162 -0
  55. sprucelab-0.1.0/tests/test_types_cli.py +312 -0
  56. sprucelab-0.1.0/tests/test_verify_cli.py +192 -0
  57. sprucelab-0.1.0/tests/test_webhooks_cli.py +528 -0
@@ -0,0 +1,291 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ build/
8
+ develop-eggs/
9
+ dist/
10
+ downloads/
11
+ eggs/
12
+ .eggs/
13
+ /lib/
14
+ /lib64/
15
+ parts/
16
+ sdist/
17
+ var/
18
+ wheels/
19
+ pip-wheel-metadata/
20
+ share/python-wheels/
21
+ *.egg-info/
22
+ .installed.cfg
23
+ *.egg
24
+ MANIFEST
25
+
26
+ # Django
27
+ *.log
28
+ local_settings.py
29
+ db.sqlite3
30
+ db.sqlite3-journal
31
+ database.sqlite
32
+ database.sqlite-journal
33
+ /media
34
+ /staticfiles
35
+ /backend/logs/
36
+ /backend/media/
37
+ /backend/staticfiles/
38
+ /backend/database.sqlite*
39
+
40
+ # Flask
41
+ instance/
42
+ .webassets-cache
43
+
44
+ # Scrapy
45
+ .scrapy
46
+
47
+ # Sphinx documentation
48
+ docs/_build/
49
+
50
+ # PyBuilder
51
+ target/
52
+
53
+ # Jupyter Notebook
54
+ .ipynb_checkpoints
55
+
56
+ # IPython
57
+ profile_default/
58
+ ipython_config.py
59
+
60
+ # pyenv
61
+ .python-version
62
+
63
+ # pipenv
64
+ Pipfile.lock
65
+
66
+ # PEP 582
67
+ __pypackages__/
68
+
69
+ # Celery
70
+ celerybeat-schedule
71
+ celerybeat.pid
72
+
73
+ # SageMath
74
+ *.sage.py
75
+
76
+ # Environments
77
+ .env
78
+ .venv
79
+ env/
80
+ venv/
81
+ ENV/
82
+ env.bak/
83
+ venv.bak/
84
+
85
+ # Spyder
86
+ .spyderproject
87
+ .spyproject
88
+
89
+ # Rope
90
+ .ropeproject
91
+
92
+ # mkdocs
93
+ /site
94
+
95
+ # mypy
96
+ .mypy_cache/
97
+ .dmypy.json
98
+ dmypy.json
99
+
100
+ # Pyre
101
+ .pyre/
102
+
103
+ # Conda
104
+ .conda/
105
+
106
+ # Node.js / Frontend
107
+ node_modules/
108
+ npm-debug.log*
109
+ yarn-debug.log*
110
+ yarn-error.log*
111
+ lerna-debug.log*
112
+ .pnpm-debug.log*
113
+
114
+ # Diagnostic reports
115
+ report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
116
+
117
+ # Runtime data
118
+ pids
119
+ *.pid
120
+ *.seed
121
+ *.pid.lock
122
+
123
+ # Directory for instrumented libs
124
+ lib-cov
125
+
126
+ # Coverage
127
+ coverage
128
+ *.lcov
129
+ .nyc_output
130
+
131
+ # Grunt
132
+ .grunt
133
+
134
+ # Bower
135
+ bower_components
136
+
137
+ # node-waf
138
+ .lock-wscript
139
+
140
+ # Compiled binary addons
141
+ build/Release
142
+
143
+ # Dependency directories
144
+ jspm_packages/
145
+
146
+ # Snowpack
147
+ web_modules/
148
+
149
+ # TypeScript cache
150
+ *.tsbuildinfo
151
+
152
+ # Optional npm cache
153
+ .npm
154
+
155
+ # Optional eslint cache
156
+ .eslintcache
157
+
158
+ # Optional stylelint cache
159
+ .stylelintcache
160
+
161
+ # Microbundle cache
162
+ .rpt2_cache/
163
+ .rts2_cache_cjs/
164
+ .rts2_cache_es/
165
+ .rts2_cache_umd/
166
+
167
+ # Optional REPL history
168
+ .node_repl_history
169
+
170
+ # Output of 'npm pack'
171
+ *.tgz
172
+
173
+ # Yarn
174
+ .yarn-integrity
175
+ .yarn/cache
176
+ .yarn/unplugged
177
+ .yarn/build-state.yml
178
+ .yarn/install-state.gz
179
+ .pnp.*
180
+
181
+ # parcel-bundler
182
+ .cache
183
+ .parcel-cache
184
+
185
+ # Next.js
186
+ .next/
187
+ out/
188
+
189
+ # Nuxt.js
190
+ .nuxt
191
+ dist
192
+
193
+ # Gatsby
194
+ .cache/
195
+ public
196
+
197
+ # vuepress
198
+ .vuepress/dist
199
+
200
+ # Serverless
201
+ .serverless/
202
+
203
+ # FuseBox
204
+ .fusebox/
205
+
206
+ # DynamoDB Local
207
+ .dynamodb/
208
+
209
+ # TernJS
210
+ .tern-port
211
+
212
+ # Stores VSCode versions
213
+ .vscode-test
214
+
215
+ # yarn v2
216
+ .yarn/cache
217
+ .yarn/unplugged
218
+ .yarn/build-state.yml
219
+ .yarn/install-state.gz
220
+ .pnp.*
221
+
222
+ # IDEs
223
+ .vscode/
224
+ .idea/
225
+ *.swp
226
+ *.swo
227
+ *~
228
+ .DS_Store
229
+
230
+ # Vercel CLI link (project metadata, not for commit)
231
+ .vercel/
232
+
233
+ # Project specific
234
+ /output/
235
+ /versions/
236
+ *.ifc
237
+ /output/*.json
238
+ *.npz
239
+ simplified*.ifc
240
+ LBK_RIV_C*.ifc
241
+ LBK_RIV_C*.json
242
+
243
+ # Exception: checked-in test fixtures for the live smoke harness
244
+ !cli/tests/integration/fixtures/*.ifc
245
+
246
+ # Temp files
247
+ *.tmp
248
+ *.bak
249
+ *.log
250
+
251
+ # OS files
252
+ Thumbs.db
253
+ .DS_Store
254
+
255
+ # Vite
256
+ dist-ssr
257
+ *.local
258
+
259
+ # Frontend build
260
+ /frontend/dist/
261
+ /frontend/build/
262
+ /frontend/.vite/
263
+
264
+ # Environment files (keep .env.example, ignore others)
265
+ .env.local
266
+ .env.dev
267
+ .env.development.local
268
+ .env.test.local
269
+ .env.production.local
270
+ .env.production
271
+ frontend/.env.production
272
+ backend/.env*
273
+
274
+ # Test coverage
275
+ htmlcov/
276
+ .coverage
277
+ .coverage.*
278
+
279
+ # Generated by supabase CLI
280
+ supabase/
281
+ backend/supabase/
282
+
283
+ # Claude Code runtime state
284
+ .claude/scheduled_tasks.lock
285
+
286
+ # Stray install logs
287
+ sunshine-install-log.txt
288
+
289
+ # Claude Code agent worktrees (transient git worktrees from subagent runs)
290
+ .claude/worktrees/
291
+
@@ -0,0 +1,188 @@
1
+ Metadata-Version: 2.4
2
+ Name: sprucelab
3
+ Version: 0.1.0
4
+ Summary: Agent-first surface for Sprucelab — typed CLI + MCP server over the same public HTTP API.
5
+ Project-URL: Homepage, https://www.sprucelab.io/agents
6
+ Project-URL: Benchmarks, https://www.sprucelab.io/benchmarks
7
+ Project-URL: Source, https://github.com/EdvardGK/sprucelab
8
+ Author: Sprucelab
9
+ License-Expression: MIT
10
+ Keywords: agent,agentic,automation,bim,claude,cli,cursor,ifc,mcp
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Requires-Python: >=3.10
19
+ Requires-Dist: httpx>=0.25.0
20
+ Requires-Dist: keyring>=24.0.0
21
+ Requires-Dist: mcp>=1.0.0
22
+ Requires-Dist: pyyaml>=6.0
23
+ Requires-Dist: rich>=13.0.0
24
+ Requires-Dist: typer>=0.9.0
25
+ Provides-Extra: dev
26
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
27
+ Requires-Dist: pytest>=7.0.0; extra == 'dev'
28
+ Requires-Dist: respx>=0.20.0; extra == 'dev'
29
+ Description-Content-Type: text/markdown
30
+
31
+ # sprucelab
32
+
33
+ > Agent-first surface for Sprucelab. One install ships a typed CLI and an MCP
34
+ > server — both wrapping the same public HTTP API. Drop in whichever transport
35
+ > your agent host speaks.
36
+
37
+ ```bash
38
+ pip install sprucelab
39
+ ```
40
+
41
+ ## Two transports, one package
42
+
43
+ ### `spruce` — typed CLI
44
+
45
+ For terminal-resident agents (Claude Code, Aider, anything that drives a
46
+ shell) and humans at the prompt. Every command supports `--json` for
47
+ machine-readable output; mutations support `--dry-run` where applicable.
48
+
49
+ ```bash
50
+ spruce auth register --token <YOUR_KEY> --url https://www.sprucelab.io
51
+ spruce projects list --json
52
+ spruce files upload ./models/foo.ifc --project-id <uuid> --wait
53
+ spruce verify --model-id <uuid> --dry-run
54
+ ```
55
+
56
+ Agents move through `spruce` faster than humans do — the rendering is for
57
+ humans; agents read the JSON. Once an agent has worked out the right sequence
58
+ of calls, the same commands run unattended from cron / GitHub Actions /
59
+ Airflow without burning tokens — that's the whole point of headless-first.
60
+ The AI budget stays reserved for actual reasoning work (claim extraction,
61
+ type classification, verification), not for re-deriving the same script every
62
+ run. See `spruce --help` for the full command tree.
63
+
64
+ ### `sprucelab-mcp` — MCP server over stdio
65
+
66
+ For host-resident agents (Claude Desktop, Cursor, Continue, …) that launch
67
+ tools as subprocesses and call them as typed tool calls.
68
+
69
+ ```json
70
+ {
71
+ "mcpServers": {
72
+ "sprucelab": {
73
+ "command": "sprucelab-mcp",
74
+ "env": {
75
+ "SPRUCELAB_API_URL": "https://www.sprucelab.io",
76
+ "SPRUCELAB_API_TOKEN": "your-token-here"
77
+ }
78
+ }
79
+ }
80
+ }
81
+ ```
82
+
83
+ The MCP server is a thin wrapper over the same HTTP surface the CLI talks
84
+ to — the one advertised at <https://www.sprucelab.io/api/capabilities/>. No
85
+ custom protocol, no surprises. If a tool fails, the error body is the literal
86
+ API response.
87
+
88
+ ## Auth
89
+
90
+ Read-only discovery (`capabilities`, `agent_tools_manifest`, EIR catalog,
91
+ EIR presets) works without auth on either transport. Everything else needs
92
+ a token.
93
+
94
+ Get one via the web app at <https://www.sprucelab.io/agents>, then either:
95
+
96
+ ```bash
97
+ spruce auth register --token <YOUR_KEY> --url https://www.sprucelab.io
98
+ ```
99
+
100
+ …or set `SPRUCELAB_API_TOKEN` in the MCP env block above. The CLI and the
101
+ MCP server read the same token store.
102
+
103
+ For experimentation, the public sandbox token (read-only) is published on
104
+ <https://www.sprucelab.io/agents>.
105
+
106
+ ## MCP tools
107
+
108
+ Read-only discovery (no auth):
109
+
110
+ | Tool | Purpose |
111
+ | ---- | ------- |
112
+ | `capabilities()` | Fetch the full capabilities manifest. Always start here. |
113
+ | `agent_tools_manifest()` | Fetch `/.well-known/agent-tools.json`. |
114
+ | `eir_catalog()` | EIR rule grammar — every kind, fields, singleton flag. |
115
+ | `eir_presets(preset?)` | Ready-to-seed EIR bundles. |
116
+
117
+ Projects + EIR (Bearer):
118
+
119
+ | Tool | Purpose |
120
+ | ---- | ------- |
121
+ | `list_projects()`, `show_project(id)` | Project listing + detail. |
122
+ | `scaffold_project(name, …)` | Create project + seed EIR in one call. |
123
+ | `list_members(id)`, `add_member(id, email, role)`, `remove_member(id, member_id)` | Per-project membership. |
124
+ | `mint_tenant_token(id, name, scope)`, `list_tenant_tokens(id)`, `revoke_tenant_token(id, token_id)` | Project-scoped agent tokens. |
125
+ | `eir_rules(id)` | List the active EIR config's rules. |
126
+ | `eir_apply_preset(id, preset, mode)` | Apply preset in merge or replace mode. |
127
+ | `eir_add_rule(id, kind, config, upsert)` | Append one rule; singleton 409 unless `upsert=True`. |
128
+ | `eir_remove_rule(id, rule_id)` | Idempotent rule delete. |
129
+ | `eir_set_rules(id, rules)` | Atomic full replace. |
130
+
131
+ Models + types + verification (Bearer):
132
+
133
+ | Tool | Purpose |
134
+ | ---- | ------- |
135
+ | `list_models(project_id?)` | List models in a project. |
136
+ | `list_types(model_id)`, `classify_types(mappings)` | Type list + batch classify. |
137
+ | `verify_dry_run(model_id)`, `verify_model(model_id)` | Verification, with persistence opt-in. |
138
+
139
+ Files (Bearer):
140
+
141
+ | Tool | Purpose |
142
+ | ---- | ------- |
143
+ | `list_files(project_id?)`, `show_file(id)` | List + detail. |
144
+ | `upload_file(project_id, file_path, on_duplicate)` | Multipart upload from a local path. |
145
+ | `reprocess_file(id)` | Trigger a fresh ExtractionRun. |
146
+ | `extraction_log(file_id, run_id?)` | Full ExtractionRun (log_entries + quality_report). |
147
+ | `list_observations(project_id?)` | Layer-1 observation log. |
148
+
149
+ Issues + scopes (Bearer):
150
+
151
+ | Tool | Purpose |
152
+ | ---- | ------- |
153
+ | `list_issues(…)`, `show_issue(id)`, `create_issue(…)`, `update_issue(…)`, `resolve_issue(id)` | Issue CRUD. |
154
+ | `issues_from_verification(model_id)` | Route verification failures into issues. |
155
+ | `list_scopes(project_id, as_tree)`, `create_scope(…)` | Federation scopes. |
156
+
157
+ Webhooks (Bearer):
158
+
159
+ | Tool | Purpose |
160
+ | ---- | ------- |
161
+ | `list_webhooks(project_id?)` | List subscriptions. |
162
+ | `create_webhook(project_id, target_url, events)` | Subscribe + receive signing secret ONCE. |
163
+ | `disable_webhook(id, enable?)` | Toggle `is_active`. |
164
+ | `delete_webhook(id)` | Permanent delete. |
165
+ | `list_deliveries(id)`, `redeliver(delivery_id)`, `test_webhook(id)` | Delivery log + replay + synthetic test. |
166
+
167
+ ## Local-dev surface (`spruce dev …`)
168
+
169
+ The CLI also ships a `dev` subcommand that bypasses the API and talks
170
+ directly to a local Django install — for working *on* Sprucelab, not against
171
+ prod. Same agent-first invariants (`--json` everywhere, `--dry-run` on
172
+ mutations).
173
+
174
+ ```bash
175
+ spruce dev env # repo + service status
176
+ spruce dev db stats --json # row counts for key tables
177
+ spruce dev db materials --project <uuid> --top 15 # top materials per project
178
+ spruce dev seed materials --project <uuid> --dry-run # preview seed plan
179
+ spruce dev test tsc # frontend type-check
180
+ spruce dev test e2e materials --headed # Playwright smoke
181
+ ```
182
+
183
+ ## Powered by
184
+
185
+ Sprucelab's IFC extraction layer is powered by
186
+ [`ifcfast`](https://github.com/EdvardGK/ifcfast) — 25–47× faster than
187
+ `ifcopenshell` on production IFCs. See
188
+ <https://www.sprucelab.io/benchmarks>.
@@ -0,0 +1,158 @@
1
+ # sprucelab
2
+
3
+ > Agent-first surface for Sprucelab. One install ships a typed CLI and an MCP
4
+ > server — both wrapping the same public HTTP API. Drop in whichever transport
5
+ > your agent host speaks.
6
+
7
+ ```bash
8
+ pip install sprucelab
9
+ ```
10
+
11
+ ## Two transports, one package
12
+
13
+ ### `spruce` — typed CLI
14
+
15
+ For terminal-resident agents (Claude Code, Aider, anything that drives a
16
+ shell) and humans at the prompt. Every command supports `--json` for
17
+ machine-readable output; mutations support `--dry-run` where applicable.
18
+
19
+ ```bash
20
+ spruce auth register --token <YOUR_KEY> --url https://www.sprucelab.io
21
+ spruce projects list --json
22
+ spruce files upload ./models/foo.ifc --project-id <uuid> --wait
23
+ spruce verify --model-id <uuid> --dry-run
24
+ ```
25
+
26
+ Agents move through `spruce` faster than humans do — the rendering is for
27
+ humans; agents read the JSON. Once an agent has worked out the right sequence
28
+ of calls, the same commands run unattended from cron / GitHub Actions /
29
+ Airflow without burning tokens — that's the whole point of headless-first.
30
+ The AI budget stays reserved for actual reasoning work (claim extraction,
31
+ type classification, verification), not for re-deriving the same script every
32
+ run. See `spruce --help` for the full command tree.
33
+
34
+ ### `sprucelab-mcp` — MCP server over stdio
35
+
36
+ For host-resident agents (Claude Desktop, Cursor, Continue, …) that launch
37
+ tools as subprocesses and call them as typed tool calls.
38
+
39
+ ```json
40
+ {
41
+ "mcpServers": {
42
+ "sprucelab": {
43
+ "command": "sprucelab-mcp",
44
+ "env": {
45
+ "SPRUCELAB_API_URL": "https://www.sprucelab.io",
46
+ "SPRUCELAB_API_TOKEN": "your-token-here"
47
+ }
48
+ }
49
+ }
50
+ }
51
+ ```
52
+
53
+ The MCP server is a thin wrapper over the same HTTP surface the CLI talks
54
+ to — the one advertised at <https://www.sprucelab.io/api/capabilities/>. No
55
+ custom protocol, no surprises. If a tool fails, the error body is the literal
56
+ API response.
57
+
58
+ ## Auth
59
+
60
+ Read-only discovery (`capabilities`, `agent_tools_manifest`, EIR catalog,
61
+ EIR presets) works without auth on either transport. Everything else needs
62
+ a token.
63
+
64
+ Get one via the web app at <https://www.sprucelab.io/agents>, then either:
65
+
66
+ ```bash
67
+ spruce auth register --token <YOUR_KEY> --url https://www.sprucelab.io
68
+ ```
69
+
70
+ …or set `SPRUCELAB_API_TOKEN` in the MCP env block above. The CLI and the
71
+ MCP server read the same token store.
72
+
73
+ For experimentation, the public sandbox token (read-only) is published on
74
+ <https://www.sprucelab.io/agents>.
75
+
76
+ ## MCP tools
77
+
78
+ Read-only discovery (no auth):
79
+
80
+ | Tool | Purpose |
81
+ | ---- | ------- |
82
+ | `capabilities()` | Fetch the full capabilities manifest. Always start here. |
83
+ | `agent_tools_manifest()` | Fetch `/.well-known/agent-tools.json`. |
84
+ | `eir_catalog()` | EIR rule grammar — every kind, fields, singleton flag. |
85
+ | `eir_presets(preset?)` | Ready-to-seed EIR bundles. |
86
+
87
+ Projects + EIR (Bearer):
88
+
89
+ | Tool | Purpose |
90
+ | ---- | ------- |
91
+ | `list_projects()`, `show_project(id)` | Project listing + detail. |
92
+ | `scaffold_project(name, …)` | Create project + seed EIR in one call. |
93
+ | `list_members(id)`, `add_member(id, email, role)`, `remove_member(id, member_id)` | Per-project membership. |
94
+ | `mint_tenant_token(id, name, scope)`, `list_tenant_tokens(id)`, `revoke_tenant_token(id, token_id)` | Project-scoped agent tokens. |
95
+ | `eir_rules(id)` | List the active EIR config's rules. |
96
+ | `eir_apply_preset(id, preset, mode)` | Apply preset in merge or replace mode. |
97
+ | `eir_add_rule(id, kind, config, upsert)` | Append one rule; singleton 409 unless `upsert=True`. |
98
+ | `eir_remove_rule(id, rule_id)` | Idempotent rule delete. |
99
+ | `eir_set_rules(id, rules)` | Atomic full replace. |
100
+
101
+ Models + types + verification (Bearer):
102
+
103
+ | Tool | Purpose |
104
+ | ---- | ------- |
105
+ | `list_models(project_id?)` | List models in a project. |
106
+ | `list_types(model_id)`, `classify_types(mappings)` | Type list + batch classify. |
107
+ | `verify_dry_run(model_id)`, `verify_model(model_id)` | Verification, with persistence opt-in. |
108
+
109
+ Files (Bearer):
110
+
111
+ | Tool | Purpose |
112
+ | ---- | ------- |
113
+ | `list_files(project_id?)`, `show_file(id)` | List + detail. |
114
+ | `upload_file(project_id, file_path, on_duplicate)` | Multipart upload from a local path. |
115
+ | `reprocess_file(id)` | Trigger a fresh ExtractionRun. |
116
+ | `extraction_log(file_id, run_id?)` | Full ExtractionRun (log_entries + quality_report). |
117
+ | `list_observations(project_id?)` | Layer-1 observation log. |
118
+
119
+ Issues + scopes (Bearer):
120
+
121
+ | Tool | Purpose |
122
+ | ---- | ------- |
123
+ | `list_issues(…)`, `show_issue(id)`, `create_issue(…)`, `update_issue(…)`, `resolve_issue(id)` | Issue CRUD. |
124
+ | `issues_from_verification(model_id)` | Route verification failures into issues. |
125
+ | `list_scopes(project_id, as_tree)`, `create_scope(…)` | Federation scopes. |
126
+
127
+ Webhooks (Bearer):
128
+
129
+ | Tool | Purpose |
130
+ | ---- | ------- |
131
+ | `list_webhooks(project_id?)` | List subscriptions. |
132
+ | `create_webhook(project_id, target_url, events)` | Subscribe + receive signing secret ONCE. |
133
+ | `disable_webhook(id, enable?)` | Toggle `is_active`. |
134
+ | `delete_webhook(id)` | Permanent delete. |
135
+ | `list_deliveries(id)`, `redeliver(delivery_id)`, `test_webhook(id)` | Delivery log + replay + synthetic test. |
136
+
137
+ ## Local-dev surface (`spruce dev …`)
138
+
139
+ The CLI also ships a `dev` subcommand that bypasses the API and talks
140
+ directly to a local Django install — for working *on* Sprucelab, not against
141
+ prod. Same agent-first invariants (`--json` everywhere, `--dry-run` on
142
+ mutations).
143
+
144
+ ```bash
145
+ spruce dev env # repo + service status
146
+ spruce dev db stats --json # row counts for key tables
147
+ spruce dev db materials --project <uuid> --top 15 # top materials per project
148
+ spruce dev seed materials --project <uuid> --dry-run # preview seed plan
149
+ spruce dev test tsc # frontend type-check
150
+ spruce dev test e2e materials --headed # Playwright smoke
151
+ ```
152
+
153
+ ## Powered by
154
+
155
+ Sprucelab's IFC extraction layer is powered by
156
+ [`ifcfast`](https://github.com/EdvardGK/ifcfast) — 25–47× faster than
157
+ `ifcopenshell` on production IFCs. See
158
+ <https://www.sprucelab.io/benchmarks>.
@@ -0,0 +1,64 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "sprucelab"
7
+ version = "0.1.0"
8
+ description = "Agent-first surface for Sprucelab — typed CLI + MCP server over the same public HTTP API."
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ requires-python = ">=3.10"
12
+ authors = [{ name = "Sprucelab" }]
13
+ keywords = [
14
+ "bim",
15
+ "ifc",
16
+ "mcp",
17
+ "agent",
18
+ "agentic",
19
+ "automation",
20
+ "claude",
21
+ "cursor",
22
+ "cli",
23
+ ]
24
+ classifiers = [
25
+ "Development Status :: 4 - Beta",
26
+ "Intended Audience :: Developers",
27
+ "License :: OSI Approved :: MIT License",
28
+ "Programming Language :: Python :: 3",
29
+ "Programming Language :: Python :: 3.10",
30
+ "Programming Language :: Python :: 3.11",
31
+ "Programming Language :: Python :: 3.12",
32
+ ]
33
+
34
+ dependencies = [
35
+ # MCP transport — host-resident agents (Claude Desktop, Cursor, …).
36
+ "mcp>=1.0.0",
37
+ # CLI transport — terminal-resident agents (Claude Code, Aider, …)
38
+ # and humans at the prompt.
39
+ "typer>=0.9.0",
40
+ "rich>=13.0.0",
41
+ "pyyaml>=6.0",
42
+ "keyring>=24.0.0",
43
+ # Shared HTTP layer.
44
+ "httpx>=0.25.0",
45
+ ]
46
+
47
+ [project.optional-dependencies]
48
+ dev = [
49
+ "pytest>=7.0.0",
50
+ "pytest-asyncio>=0.21.0",
51
+ "respx>=0.20.0",
52
+ ]
53
+
54
+ [project.urls]
55
+ Homepage = "https://www.sprucelab.io/agents"
56
+ Benchmarks = "https://www.sprucelab.io/benchmarks"
57
+ Source = "https://github.com/EdvardGK/sprucelab"
58
+
59
+ [project.scripts]
60
+ sprucelab-mcp = "sprucelab.mcp.server:main"
61
+ spruce = "sprucelab.cli.app:app"
62
+
63
+ [tool.hatch.build.targets.wheel]
64
+ packages = ["sprucelab"]
@@ -0,0 +1,13 @@
1
+ [pytest]
2
+ python_files = test_*.py
3
+ python_classes = Test*
4
+ python_functions = test_*
5
+ testpaths = tests
6
+ addopts =
7
+ -ra
8
+ --strict-markers
9
+ --tb=short
10
+ markers =
11
+ live: opt-in live-API smoke test (requires SPRUCE_LIVE_API_URL; never runs in CI)
12
+ filterwarnings =
13
+ ignore::DeprecationWarning