spectackl 0.0.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.
Files changed (34) hide show
  1. spectackl-0.0.1/MANIFEST.in +27 -0
  2. spectackl-0.0.1/PKG-INFO +92 -0
  3. spectackl-0.0.1/README.md +59 -0
  4. spectackl-0.0.1/pyproject.toml +98 -0
  5. spectackl-0.0.1/setup.cfg +4 -0
  6. spectackl-0.0.1/src/spectackl/__init__.py +3 -0
  7. spectackl-0.0.1/src/spectackl/auth.py +74 -0
  8. spectackl-0.0.1/src/spectackl/cli.py +108 -0
  9. spectackl-0.0.1/src/spectackl/commands/__init__.py +1 -0
  10. spectackl-0.0.1/src/spectackl/commands/artifact.py +335 -0
  11. spectackl-0.0.1/src/spectackl/commands/assets.py +260 -0
  12. spectackl-0.0.1/src/spectackl/commands/context.py +283 -0
  13. spectackl-0.0.1/src/spectackl/commands/daemon.py +687 -0
  14. spectackl-0.0.1/src/spectackl/commands/init.py +251 -0
  15. spectackl-0.0.1/src/spectackl/commands/relation.py +146 -0
  16. spectackl-0.0.1/src/spectackl/commands/rsync.py +821 -0
  17. spectackl-0.0.1/src/spectackl/commands/search.py +69 -0
  18. spectackl-0.0.1/src/spectackl/commands/task.py +263 -0
  19. spectackl-0.0.1/src/spectackl/config.py +369 -0
  20. spectackl-0.0.1/src/spectackl/exit_codes.py +68 -0
  21. spectackl-0.0.1/src/spectackl/http_client.py +95 -0
  22. spectackl-0.0.1/src/spectackl/output.py +181 -0
  23. spectackl-0.0.1/src/spectackl.egg-info/PKG-INFO +92 -0
  24. spectackl-0.0.1/src/spectackl.egg-info/SOURCES.txt +32 -0
  25. spectackl-0.0.1/src/spectackl.egg-info/dependency_links.txt +1 -0
  26. spectackl-0.0.1/src/spectackl.egg-info/entry_points.txt +2 -0
  27. spectackl-0.0.1/src/spectackl.egg-info/requires.txt +12 -0
  28. spectackl-0.0.1/src/spectackl.egg-info/top_level.txt +1 -0
  29. spectackl-0.0.1/tests/test_auth.py +75 -0
  30. spectackl-0.0.1/tests/test_commands.py +977 -0
  31. spectackl-0.0.1/tests/test_config.py +318 -0
  32. spectackl-0.0.1/tests/test_exit_codes.py +37 -0
  33. spectackl-0.0.1/tests/test_output.py +105 -0
  34. spectackl-0.0.1/tests/test_rsync.py +350 -0
@@ -0,0 +1,27 @@
1
+ # Source-distribution contents.
2
+ #
3
+ # The wheel is controlled by [tool.setuptools.packages.find] in pyproject.toml, but an
4
+ # sdist defaults to sweeping in far more than that — so anything that must not be
5
+ # published has to be excluded here as well.
6
+
7
+ # The generated OpenAPI client is repo-only: it exists for the CI drift check
8
+ # (scripts/regenerate-client.sh) and nothing in the CLI imports it. Keeping it out of
9
+ # the sdist matches the wheel, so both distributions carry the same code.
10
+ prune src/spectackl_client
11
+
12
+ # Credentials must never reach a published artifact. .env.local holds real PyPI tokens
13
+ # and is gitignored; the sdist defaults happen not to pick up dotfiles today, and this
14
+ # makes that a rule rather than a coincidence. A token published inside an sdist cannot
15
+ # be recalled — only revoked, after it is already public.
16
+ #
17
+ # These are three exact patterns, deliberately NOT a `.env*` glob: `.env.example` is
18
+ # committed and must keep working. Nothing here matches it — `.env.*.local` requires the
19
+ # `.local` suffix. Add new patterns one at a time for the same reason.
20
+ global-exclude .env .env.local .env.*.local
21
+
22
+ # Build and tooling noise.
23
+ global-exclude *.pyc
24
+ prune **/__pycache__
25
+ prune .mypy_cache
26
+ prune .pytest_cache
27
+ prune .ruff_cache
@@ -0,0 +1,92 @@
1
+ Metadata-Version: 2.4
2
+ Name: spectackl
3
+ Version: 0.0.1
4
+ Summary: CLI for the Spectackl Public REST API — for AI agents and operators
5
+ Author-email: Spectackl Engineering <engineering@spectackl.ai>
6
+ License: MIT
7
+ Project-URL: Homepage, https://spectackl.ai
8
+ Project-URL: Documentation, https://docs.spectackl.ai
9
+ Keywords: spectackl,cli,ai,agents
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Environment :: Console
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
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
20
+ Requires-Python: >=3.10
21
+ Description-Content-Type: text/markdown
22
+ Requires-Dist: typer>=0.12
23
+ Requires-Dist: httpx>=0.27
24
+ Requires-Dist: rich>=13.0
25
+ Requires-Dist: pyyaml>=6.0
26
+ Provides-Extra: dev
27
+ Requires-Dist: pytest>=8.0; extra == "dev"
28
+ Requires-Dist: pytest-httpx>=0.30; extra == "dev"
29
+ Requires-Dist: mypy>=1.10; extra == "dev"
30
+ Requires-Dist: ruff>=0.5; extra == "dev"
31
+ Requires-Dist: build>=1.0; extra == "dev"
32
+ Requires-Dist: twine>=5.0; extra == "dev"
33
+
34
+ # spectackl
35
+
36
+ Command-line interface to the [Spectackl](https://spectackl.ai) Public REST API — built
37
+ for AI agents and operators who need programmatic access to artifacts, scenes and task
38
+ executions without going through the web UI.
39
+
40
+ ```bash
41
+ pip install spectackl
42
+ ```
43
+
44
+ ## Quick start
45
+
46
+ ```bash
47
+ spectackl init --api-key spc_… --backend-url https://api.spectackl.ai --project my-project
48
+ spectackl artifact list --json
49
+ ```
50
+
51
+ API keys are managed in the Spectackl Admin Console. `init` writes
52
+ `.spectackl/settings.yaml` in the current directory and a user-global
53
+ `~/.config/spectackl/settings.yaml`; both are created `0600` and neither should be
54
+ committed.
55
+
56
+ ## What it does
57
+
58
+ | Command | Purpose |
59
+ |---|---|
60
+ | `spectackl search <query>` | Hybrid full-text + semantic search across a project's content |
61
+ | `spectackl artifact` | Get, list, create and update artifacts |
62
+ | `spectackl relation` | Manage the artifact graph (`INCLUDES`, `DOCUMENTS`, `DEPENDS_ON`, …) |
63
+ | `spectackl context` | Inspect scenes (work contexts) and the artifacts they scope |
64
+ | `spectackl task` | Claim, progress and complete task executions |
65
+ | `spectackl assets sync` | Materialize the project's agents and skills into `./.claude/` |
66
+ | `spectackl rsync` | Reconcile artifact content with local files, in both directions |
67
+ | `spectackl daemon` | Run agent sessions on this machine |
68
+
69
+ Every command accepts `--json` for machine-readable output, and every command returns a
70
+ documented exit code — `0` success, `10` auth required, `20` validation, `30` not found,
71
+ `40` upstream unavailable, and so on — so scripts can branch on failures precisely
72
+ rather than parsing text.
73
+
74
+ ## Designed for agents
75
+
76
+ Output is stable and parseable, errors are structured, and the exit-code scheme is part
77
+ of the public contract. `spectackl assets sync` writes agent and skill definitions in the
78
+ exact layout Claude Code reads, built from the same artifacts the platform provisions
79
+ into a sandbox — so an agent invoked on a workstation is the one that runs in production.
80
+
81
+ ## Requirements
82
+
83
+ - Python 3.10+
84
+ - A Spectackl tenant and an API key (`spc_` prefix)
85
+
86
+ ## Documentation
87
+
88
+ Full guide: **https://docs.spectackl.ai**
89
+
90
+ ## License
91
+
92
+ MIT
@@ -0,0 +1,59 @@
1
+ # spectackl
2
+
3
+ Command-line interface to the [Spectackl](https://spectackl.ai) Public REST API — built
4
+ for AI agents and operators who need programmatic access to artifacts, scenes and task
5
+ executions without going through the web UI.
6
+
7
+ ```bash
8
+ pip install spectackl
9
+ ```
10
+
11
+ ## Quick start
12
+
13
+ ```bash
14
+ spectackl init --api-key spc_… --backend-url https://api.spectackl.ai --project my-project
15
+ spectackl artifact list --json
16
+ ```
17
+
18
+ API keys are managed in the Spectackl Admin Console. `init` writes
19
+ `.spectackl/settings.yaml` in the current directory and a user-global
20
+ `~/.config/spectackl/settings.yaml`; both are created `0600` and neither should be
21
+ committed.
22
+
23
+ ## What it does
24
+
25
+ | Command | Purpose |
26
+ |---|---|
27
+ | `spectackl search <query>` | Hybrid full-text + semantic search across a project's content |
28
+ | `spectackl artifact` | Get, list, create and update artifacts |
29
+ | `spectackl relation` | Manage the artifact graph (`INCLUDES`, `DOCUMENTS`, `DEPENDS_ON`, …) |
30
+ | `spectackl context` | Inspect scenes (work contexts) and the artifacts they scope |
31
+ | `spectackl task` | Claim, progress and complete task executions |
32
+ | `spectackl assets sync` | Materialize the project's agents and skills into `./.claude/` |
33
+ | `spectackl rsync` | Reconcile artifact content with local files, in both directions |
34
+ | `spectackl daemon` | Run agent sessions on this machine |
35
+
36
+ Every command accepts `--json` for machine-readable output, and every command returns a
37
+ documented exit code — `0` success, `10` auth required, `20` validation, `30` not found,
38
+ `40` upstream unavailable, and so on — so scripts can branch on failures precisely
39
+ rather than parsing text.
40
+
41
+ ## Designed for agents
42
+
43
+ Output is stable and parseable, errors are structured, and the exit-code scheme is part
44
+ of the public contract. `spectackl assets sync` writes agent and skill definitions in the
45
+ exact layout Claude Code reads, built from the same artifacts the platform provisions
46
+ into a sandbox — so an agent invoked on a workstation is the one that runs in production.
47
+
48
+ ## Requirements
49
+
50
+ - Python 3.10+
51
+ - A Spectackl tenant and an API key (`spc_` prefix)
52
+
53
+ ## Documentation
54
+
55
+ Full guide: **https://docs.spectackl.ai**
56
+
57
+ ## License
58
+
59
+ MIT
@@ -0,0 +1,98 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "spectackl"
7
+ # Single-sourced from spectackl.__version__ so the package metadata and the
8
+ # `spectackl --version` output can never disagree. Bump it in src/spectackl/__init__.py.
9
+ dynamic = ["version"]
10
+ description = "CLI for the Spectackl Public REST API — for AI agents and operators"
11
+ readme = "README.md"
12
+ requires-python = ">=3.10"
13
+ license = {text = "MIT"}
14
+ authors = [
15
+ {name = "Spectackl Engineering", email = "engineering@spectackl.ai"},
16
+ ]
17
+ keywords = ["spectackl", "cli", "ai", "agents"]
18
+ classifiers = [
19
+ "Development Status :: 4 - Beta",
20
+ "Environment :: Console",
21
+ "Intended Audience :: Developers",
22
+ "License :: OSI Approved :: MIT License",
23
+ "Programming Language :: Python :: 3",
24
+ "Programming Language :: Python :: 3.10",
25
+ "Programming Language :: Python :: 3.11",
26
+ "Programming Language :: Python :: 3.12",
27
+ "Programming Language :: Python :: 3.13",
28
+ "Topic :: Software Development :: Libraries :: Application Frameworks",
29
+ ]
30
+ # Every entry here is imported by src/spectackl. pydantic used to be listed for the
31
+ # generated OpenAPI client, which is no longer distributed — the CLI itself never
32
+ # imported it, and it is a compiled dependency, so carrying it taxed every install.
33
+ dependencies = [
34
+ "typer>=0.12",
35
+ "httpx>=0.27",
36
+ "rich>=13.0",
37
+ "pyyaml>=6.0",
38
+ ]
39
+
40
+ [project.urls]
41
+ Homepage = "https://spectackl.ai"
42
+ Documentation = "https://docs.spectackl.ai"
43
+
44
+ [project.scripts]
45
+ spectackl = "spectackl.cli:app"
46
+
47
+ [project.optional-dependencies]
48
+ dev = [
49
+ "pytest>=8.0",
50
+ "pytest-httpx>=0.30",
51
+ "mypy>=1.10",
52
+ "ruff>=0.5",
53
+ "build>=1.0",
54
+ "twine>=5.0",
55
+ ]
56
+
57
+ [tool.setuptools.dynamic]
58
+ version = {attr = "spectackl.__version__"}
59
+
60
+ # Ship ONLY the CLI package. `src/spectackl_client/` is a separate, generated
61
+ # OpenAPI client kept in the repo for the CI drift check (scripts/regenerate-client.sh)
62
+ # — nothing in the CLI imports it; every request goes through httpx directly. Shipping
63
+ # it put two unused top-level packages (`spectackl_client`, `spectackl_public_api_client`)
64
+ # into the site-packages of everyone who installs us, one of them twice.
65
+ #
66
+ # The explicit include is what keeps it out: a bare `where = ["src"]` would pick up
67
+ # `spectackl_client` again, and a glob like "spectackl*" would match it too.
68
+ [tool.setuptools.packages.find]
69
+ where = ["src"]
70
+ include = ["spectackl", "spectackl.*"]
71
+
72
+ [tool.setuptools.package-dir]
73
+ "" = "src"
74
+
75
+ [tool.ruff]
76
+ line-length = 100
77
+ target-version = "py310"
78
+
79
+ [tool.ruff.lint]
80
+ select = ["E", "F", "I", "UP", "B", "C4", "SIM"]
81
+ ignore = [
82
+ "E501",
83
+ # B904: typer.Exit is not a re-raise; it is a flow-control mechanism, not a chained exception
84
+ "B904",
85
+ ]
86
+
87
+ [tool.mypy]
88
+ python_version = "3.10"
89
+ strict = false
90
+ warn_return_any = true
91
+ warn_unused_ignores = true
92
+ ignore_missing_imports = true
93
+
94
+ [tool.pytest.ini_options]
95
+ testpaths = ["tests"]
96
+ python_files = ["test_*.py"]
97
+ python_functions = ["test_*"]
98
+ addopts = ["-v"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,3 @@
1
+ """Spectackl CLI — public REST API client for AI agents and operators."""
2
+
3
+ __version__ = "0.0.1"
@@ -0,0 +1,74 @@
1
+ """API key resolution for the Spectackl CLI.
2
+
3
+ Resolution order (highest to lowest priority):
4
+ 1. --api-key CLI flag
5
+ 2. SPECTACKL_API_KEY environment variable
6
+ 3. api_key field in .spectackl/settings.yaml (repo root, walked up from CWD)
7
+ 4. api_key field in ~/.config/spectackl/settings.yaml
8
+ 5. Error if none found (exit code 10)
9
+
10
+ Any of the file-based values may be a ${VAR} reference resolved from the named
11
+ scalars in ~/.config/spectackl/settings.yaml (see spectackl.config.load_config).
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import typer
17
+
18
+ from spectackl import exit_codes
19
+ from spectackl.config import SpectacklConfig
20
+
21
+
22
+ def resolve_api_key(config: SpectacklConfig) -> str:
23
+ """Return the API key or exit with code 10.
24
+
25
+ Raises:
26
+ typer.Exit: with code 10 (AUTH_REQUIRED) if no API key is found.
27
+ """
28
+ if config.api_key:
29
+ return config.api_key
30
+
31
+ typer.echo(
32
+ "Error: API key not found.\n"
33
+ " Run 'spectackl init' to create the config files, or\n"
34
+ " set --api-key, the SPECTACKL_API_KEY env var,\n"
35
+ " or api_key in .spectackl/settings.yaml (repo root) / ~/.config/spectackl/settings.yaml.\n"
36
+ " api_key may be a ${VAR} reference resolved from ~/.config/spectackl/settings.yaml.",
37
+ err=True,
38
+ )
39
+ raise typer.Exit(code=exit_codes.AUTH_REQUIRED)
40
+
41
+
42
+ def resolve_project_id(config: SpectacklConfig) -> str:
43
+ """Return the project ID or exit with code 2 (usage error).
44
+
45
+ Resolution order is already applied by load_config:
46
+ --project flag > SPECTACKL_PROJECT_ID env > .spectackl/settings.yaml > global config.
47
+
48
+ Raises:
49
+ typer.Exit: with code 2 (USAGE_ERROR) if no project_id is found.
50
+ """
51
+ if config.project_id:
52
+ return config.project_id
53
+
54
+ typer.echo(
55
+ "Error: project_id not found.\n"
56
+ " Run 'spectackl init --project <key>', or\n"
57
+ " set --project, the SPECTACKL_PROJECT_ID env var,\n"
58
+ " or add project_id (or project_key) to .spectackl/settings.yaml in the repo root.",
59
+ err=True,
60
+ )
61
+ raise typer.Exit(code=exit_codes.USAGE_ERROR)
62
+
63
+
64
+ def validate_api_key_format(api_key: str) -> bool:
65
+ """Return True if the key matches the spc_<64hex> format.
66
+
67
+ Does not contact the server. Used for local pre-flight validation only.
68
+ """
69
+ if not api_key.startswith("spc_"):
70
+ return False
71
+ suffix = api_key[4:]
72
+ if len(suffix) != 64:
73
+ return False
74
+ return all(c in "0123456789abcdefABCDEF" for c in suffix)
@@ -0,0 +1,108 @@
1
+ """Spectackl CLI entry point.
2
+
3
+ The Spectackl CLI provides commands for AI agents and operators to interact
4
+ with the Spectackl Public REST API.
5
+
6
+ Usage:
7
+ spectackl init # create the config files, first thing on a new machine
8
+ spectackl assets sync # pull the project's agents and skills into ./.claude/
9
+ spectackl --help
10
+ spectackl search "SSO login" # hybrid content search over the project
11
+ spectackl artifact --help
12
+ spectackl context --help
13
+ spectackl task --help
14
+
15
+ Global flags (available on all commands):
16
+ --api-key TEXT Spectackl API key (spc_<64hex>)
17
+ --base-url TEXT API base URL (default: http://localhost:8080/api/v1)
18
+ --json / --no-json Output raw JSON (recommended for scripts and agents)
19
+ --no-color Disable color output
20
+ -v / -vv Increase verbosity
21
+
22
+ Authentication priority:
23
+ 1. --api-key flag
24
+ 2. SPECTACKL_API_KEY environment variable
25
+ 3. api_key in ~/.config/spectackl/settings.yaml
26
+
27
+ Exit codes:
28
+ 0 Success
29
+ 1 Generic error
30
+ 2 Usage error (bad flag, missing argument)
31
+ 10 Authentication required or invalid (HTTP 401)
32
+ 11 Authorization denied (HTTP 403)
33
+ 20 Validation error (HTTP 400)
34
+ 30 Not found (HTTP 404)
35
+ 31 Conflict (HTTP 409)
36
+ 40 Upstream unavailable or timeout (HTTP 502/504)
37
+ 50 Internal facade error (HTTP 5xx)
38
+ """
39
+
40
+ from __future__ import annotations
41
+
42
+ from typing import Annotated
43
+
44
+ import typer
45
+
46
+ from spectackl import __version__
47
+ from spectackl.commands import artifact, assets, context, daemon, init, relation
48
+ from spectackl.commands import rsync as rsync_cmd
49
+ from spectackl.commands import search as search_cmd
50
+ from spectackl.commands import task
51
+
52
+ # Root typer app
53
+ app = typer.Typer(
54
+ name="spectackl",
55
+ help=(
56
+ "Spectackl CLI — interact with the Spectackl Public REST API.\n\n"
57
+ "Designed for AI agents and operators. Use --json for machine-readable output."
58
+ ),
59
+ no_args_is_help=True,
60
+ add_completion=True,
61
+ )
62
+
63
+ # Register subcommand groups
64
+ app.add_typer(init.app, name="init")
65
+ app.add_typer(artifact.app, name="artifact")
66
+ app.add_typer(context.app, name="context")
67
+ app.add_typer(assets.app, name="assets")
68
+ app.add_typer(relation.app, name="relation")
69
+ app.add_typer(task.app, name="task")
70
+ app.add_typer(daemon.app, name="daemon")
71
+
72
+ # Top-level content search command (mirrors GET /api/v1/search)
73
+ app.command(name="search")(search_cmd.search)
74
+
75
+ # Bi-directional file synchronization. Distinct from `assets sync`, which pulls
76
+ # agent/skill definitions one way.
77
+ app.command(name="rsync")(rsync_cmd.rsync)
78
+
79
+ # Deprecated: `spectackl sync` was renamed to `spectackl assets sync`. The same
80
+ # function is registered twice so the two entry points cannot drift; it is hidden
81
+ # from --help and warns when invoked, and will be removed in a later release.
82
+ app.command(
83
+ name=assets.DEPRECATED_ALIAS,
84
+ hidden=True,
85
+ help="Deprecated alias for `spectackl assets sync`.",
86
+ )(assets.sync)
87
+
88
+
89
+ def _version_callback(value: bool) -> None:
90
+ if value:
91
+ typer.echo(f"spectackl {__version__}")
92
+ raise typer.Exit()
93
+
94
+
95
+ @app.callback()
96
+ def main(
97
+ version: Annotated[
98
+ bool | None,
99
+ typer.Option(
100
+ "--version",
101
+ "-V",
102
+ help="Show version and exit.",
103
+ callback=_version_callback,
104
+ is_eager=True,
105
+ ),
106
+ ] = None,
107
+ ) -> None:
108
+ """Spectackl CLI — AI agent and operator interface to the Spectackl platform."""
@@ -0,0 +1 @@
1
+ # spectackl.commands package