hypernote 0.1.3__tar.gz → 0.2.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 (49) hide show
  1. hypernote-0.2.0/.github/workflows/release.yml +161 -0
  2. {hypernote-0.1.3 → hypernote-0.2.0}/AGENTS.md +40 -13
  3. {hypernote-0.1.3 → hypernote-0.2.0}/CHANGELOG.md +18 -4
  4. {hypernote-0.1.3 → hypernote-0.2.0}/PKG-INFO +11 -4
  5. {hypernote-0.1.3 → hypernote-0.2.0}/README.md +10 -3
  6. {hypernote-0.1.3 → hypernote-0.2.0}/SKILL.md +23 -12
  7. hypernote-0.2.0/dev/README.md +29 -0
  8. hypernote-0.2.0/dev/cli-agent-ergonomics-rollout.md +69 -0
  9. hypernote-0.2.0/dev/current-architecture.md +85 -0
  10. {hypernote-0.1.3 → hypernote-0.2.0}/dev/module-map.md +8 -8
  11. {hypernote-0.1.3 → hypernote-0.2.0}/dev/testing-and-verification.md +1 -1
  12. {hypernote-0.1.3 → hypernote-0.2.0}/docs/README.md +3 -0
  13. {hypernote-0.1.3 → hypernote-0.2.0}/docs/browser-regression-spec.md +27 -1
  14. {hypernote-0.1.3 → hypernote-0.2.0}/docs/cli.md +38 -4
  15. hypernote-0.2.0/docs/runtime-model.md +84 -0
  16. {hypernote-0.1.3 → hypernote-0.2.0}/docs/sdk.md +28 -0
  17. {hypernote-0.1.3 → hypernote-0.2.0}/pyproject.toml +5 -1
  18. {hypernote-0.1.3 → hypernote-0.2.0/src}/hypernote/cli/main.py +619 -48
  19. {hypernote-0.1.3 → hypernote-0.2.0/src}/hypernote/execution_orchestrator.py +53 -1
  20. {hypernote-0.1.3 → hypernote-0.2.0/src}/hypernote/runtime_manager.py +5 -0
  21. {hypernote-0.1.3 → hypernote-0.2.0/src}/hypernote/sdk.py +336 -23
  22. hypernote-0.2.0/src/hypernote/server/extension.py +259 -0
  23. {hypernote-0.1.3 → hypernote-0.2.0/src}/hypernote/server/handlers.py +92 -0
  24. hypernote-0.2.0/src/hypernote/server/subshell.py +395 -0
  25. hypernote-0.2.0/tests/test_browser_regression.py +386 -0
  26. {hypernote-0.1.3 → hypernote-0.2.0}/tests/test_cli.py +420 -0
  27. {hypernote-0.1.3 → hypernote-0.2.0}/tests/test_sdk.py +32 -0
  28. hypernote-0.2.0/tests/test_subshell.py +360 -0
  29. {hypernote-0.1.3 → hypernote-0.2.0}/uv.lock +1 -1
  30. hypernote-0.1.3/.github/workflows/release.yml +0 -87
  31. hypernote-0.1.3/dev/README.md +0 -24
  32. hypernote-0.1.3/dev/current-architecture.md +0 -34
  33. hypernote-0.1.3/docs/runtime-model.md +0 -50
  34. hypernote-0.1.3/hypernote/server/extension.py +0 -156
  35. hypernote-0.1.3/tests/test_browser_regression.py +0 -207
  36. {hypernote-0.1.3 → hypernote-0.2.0}/.gitignore +0 -0
  37. {hypernote-0.1.3 → hypernote-0.2.0}/CLAUDE.md +0 -0
  38. {hypernote-0.1.3 → hypernote-0.2.0}/docs/getting-started.md +0 -0
  39. {hypernote-0.1.3 → hypernote-0.2.0/src}/hypernote/__init__.py +0 -0
  40. {hypernote-0.1.3 → hypernote-0.2.0/src}/hypernote/actor_ledger.py +0 -0
  41. {hypernote-0.1.3 → hypernote-0.2.0/src}/hypernote/cli/__init__.py +0 -0
  42. {hypernote-0.1.3 → hypernote-0.2.0/src}/hypernote/errors.py +0 -0
  43. {hypernote-0.1.3 → hypernote-0.2.0/src}/hypernote/server/__init__.py +0 -0
  44. {hypernote-0.1.3 → hypernote-0.2.0}/tests/__init__.py +0 -0
  45. {hypernote-0.1.3 → hypernote-0.2.0}/tests/conftest.py +0 -0
  46. {hypernote-0.1.3 → hypernote-0.2.0}/tests/helpers.py +0 -0
  47. {hypernote-0.1.3 → hypernote-0.2.0}/tests/test_actor_ledger.py +0 -0
  48. {hypernote-0.1.3 → hypernote-0.2.0}/tests/test_live_server.py +0 -0
  49. {hypernote-0.1.3 → hypernote-0.2.0}/tests/test_runtime_manager.py +0 -0
@@ -0,0 +1,161 @@
1
+ name: Release
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ inputs:
6
+ version:
7
+ description: "Version to release (e.g., 0.1.3)"
8
+ required: true
9
+ type: string
10
+ publish_to_pypi:
11
+ description: "Publish to PyPI"
12
+ required: true
13
+ default: true
14
+ type: boolean
15
+ create_draft:
16
+ description: "Create as draft release"
17
+ required: false
18
+ default: false
19
+ type: boolean
20
+
21
+ permissions:
22
+ contents: write
23
+
24
+ jobs:
25
+ validate-inputs:
26
+ runs-on: ubuntu-latest
27
+ outputs:
28
+ version: ${{ steps.validate.outputs.version }}
29
+ steps:
30
+ - name: Validate version format
31
+ id: validate
32
+ run: |
33
+ VERSION="${{ inputs.version }}"
34
+ if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+(\.[a-zA-Z0-9]+)*)?$ ]]; then
35
+ echo "❌ Invalid version format: $VERSION"
36
+ echo "Expected semantic version (e.g., 0.1.3, 0.2.0-alpha.1)"
37
+ exit 1
38
+ fi
39
+ echo "✅ Version format is valid"
40
+ echo "version=$VERSION" >> "$GITHUB_OUTPUT"
41
+
42
+ release:
43
+ needs: validate-inputs
44
+ runs-on: ubuntu-latest
45
+ outputs:
46
+ version: ${{ steps.version_update.outputs.version }}
47
+ steps:
48
+ - uses: actions/checkout@v6
49
+ with:
50
+ fetch-depth: 0
51
+ token: ${{ secrets.GITHUB_TOKEN }}
52
+
53
+ - uses: astral-sh/setup-uv@v7
54
+ with:
55
+ enable-cache: true
56
+
57
+ - name: Update version and create commit
58
+ id: version_update
59
+ env:
60
+ VERSION: ${{ needs.validate-inputs.outputs.version }}
61
+ run: |
62
+ sed -i -E "0,/^version = \".*\"/s//version = \"$VERSION\"/" pyproject.toml
63
+
64
+ if ! grep -q "version = \"$VERSION\"" pyproject.toml; then
65
+ echo "❌ Failed to update version in pyproject.toml"
66
+ exit 1
67
+ fi
68
+
69
+ echo "✅ Version updated to $VERSION"
70
+
71
+ git config user.name "github-actions[bot]"
72
+ git config user.email "github-actions[bot]@users.noreply.github.com"
73
+
74
+ if ! git diff --quiet pyproject.toml; then
75
+ uv lock
76
+ git add pyproject.toml uv.lock
77
+ git commit -m "chore: bump version to v$VERSION"
78
+ CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
79
+ git push origin "$CURRENT_BRANCH"
80
+ echo "✅ Version commit pushed"
81
+ fi
82
+
83
+ echo "version=$VERSION" >> "$GITHUB_OUTPUT"
84
+
85
+ - name: Create and push tag
86
+ env:
87
+ VERSION: ${{ steps.version_update.outputs.version }}
88
+ run: |
89
+ TAG="v$VERSION"
90
+ if git tag -l | grep -q "^$TAG$"; then
91
+ git tag -d "$TAG" || true
92
+ git push origin ":refs/tags/$TAG" || true
93
+ fi
94
+ git tag -a "$TAG" -m "Release $TAG"
95
+ git push origin "$TAG"
96
+ echo "✅ Created and pushed tag $TAG"
97
+
98
+ - name: Build package
99
+ run: |
100
+ rm -rf dist/ build/ *.egg-info/
101
+ uv build
102
+ if [ ! -f dist/*.whl ] || [ ! -f dist/*.tar.gz ]; then
103
+ echo "❌ Build artifacts not found"
104
+ exit 1
105
+ fi
106
+ echo "✅ Package built"
107
+ ls -la dist/
108
+
109
+ - name: Verify package
110
+ run: |
111
+ uv run --isolated --no-project --with dist/*.whl python -c "import hypernote; print('ok')"
112
+ echo "✅ Package verified"
113
+
114
+ - uses: actions/upload-artifact@v6
115
+ with:
116
+ name: dist-${{ steps.version_update.outputs.version }}
117
+ path: dist/*
118
+ if-no-files-found: error
119
+
120
+ - name: Run tests
121
+ run: |
122
+ uv run --extra dev playwright install --with-deps chromium
123
+ uv run --extra dev python -m pytest -q
124
+
125
+ - name: Create GitHub Release
126
+ env:
127
+ GH_TOKEN: ${{ github.token }}
128
+ run: |
129
+ TAG="v${{ steps.version_update.outputs.version }}"
130
+ if gh release view "$TAG" >/dev/null 2>&1; then
131
+ gh release upload "$TAG" dist/* --clobber
132
+ else
133
+ gh release create "$TAG" dist/* \
134
+ --generate-notes \
135
+ --verify-tag \
136
+ ${{ inputs.create_draft && '--draft' || '' }}
137
+ fi
138
+
139
+ - name: Summary
140
+ run: |
141
+ echo "## Release Summary" >> $GITHUB_STEP_SUMMARY
142
+ echo "- **Version:** v${{ steps.version_update.outputs.version }}" >> $GITHUB_STEP_SUMMARY
143
+ echo "- **PyPI:** ${{ inputs.publish_to_pypi }}" >> $GITHUB_STEP_SUMMARY
144
+ echo "- **Draft:** ${{ inputs.create_draft }}" >> $GITHUB_STEP_SUMMARY
145
+
146
+ publish-pypi:
147
+ needs: release
148
+ if: ${{ inputs.publish_to_pypi }}
149
+ runs-on: ubuntu-latest
150
+ steps:
151
+ - uses: actions/download-artifact@v5
152
+ with:
153
+ name: dist-${{ needs.release.outputs.version }}
154
+ path: dist/
155
+
156
+ - uses: astral-sh/setup-uv@v7
157
+
158
+ - name: Publish to PyPI
159
+ env:
160
+ UV_PUBLISH_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
161
+ run: uv publish
@@ -6,6 +6,7 @@ Notebook-first execution system built on top of Jupyter shared documents
6
6
 
7
7
  ```bash
8
8
  uv sync --extra dev
9
+ uv run hypernote
9
10
  uv run hypernote --help
10
11
  uv run hypernote setup serve
11
12
  uv run hypernote setup doctor
@@ -13,7 +14,7 @@ uv run hypernote create tmp/demo.ipynb
13
14
  uv run hypernote ix tmp/demo.ipynb -s 'value = 20 + 22\nprint(value)'
14
15
  uv run hypernote status tmp/demo.ipynb --full
15
16
  uv run python -m pytest -q
16
- uv run ruff check hypernote tests
17
+ uv run ruff check src/hypernote tests
17
18
  ```
18
19
 
19
20
  ## Architecture
@@ -27,20 +28,23 @@ Jupyter owns notebook truth:
27
28
 
28
29
  Hypernote owns a thin control plane:
29
30
 
30
- - public SDK in [hypernote/sdk.py](hypernote/sdk.py)
31
- - public errors in [hypernote/errors.py](hypernote/errors.py)
32
- - agent-first CLI in [hypernote/cli/main.py](hypernote/cli/main.py)
33
- - execution orchestration and shared-document mutation in [hypernote/execution_orchestrator.py](hypernote/execution_orchestrator.py)
34
- - runtime ownership in [hypernote/runtime_manager.py](hypernote/runtime_manager.py)
35
- - HTTP handlers in [hypernote/server/handlers.py](hypernote/server/handlers.py)
36
- - Jupyter extension wiring in [hypernote/server/extension.py](hypernote/server/extension.py)
37
- - ephemeral job and attribution ledger in [hypernote/actor_ledger.py](hypernote/actor_ledger.py)
31
+ - public SDK in [src/hypernote/sdk.py](src/hypernote/sdk.py)
32
+ - public errors in [src/hypernote/errors.py](src/hypernote/errors.py)
33
+ - agent-first CLI in [src/hypernote/cli/main.py](src/hypernote/cli/main.py)
34
+ - execution orchestration and shared-document mutation in [src/hypernote/execution_orchestrator.py](src/hypernote/execution_orchestrator.py)
35
+ - runtime ownership in [src/hypernote/runtime_manager.py](src/hypernote/runtime_manager.py)
36
+ - HTTP handlers in [src/hypernote/server/handlers.py](src/hypernote/server/handlers.py)
37
+ - Jupyter extension wiring in [src/hypernote/server/extension.py](src/hypernote/server/extension.py)
38
+ - ephemeral job and attribution ledger in [src/hypernote/actor_ledger.py](src/hypernote/actor_ledger.py)
39
+ - subshell-aware execute/interrupt/restart in [src/hypernote/server/subshell.py](src/hypernote/server/subshell.py)
38
40
  - optional VS Code embedding surface in [vscode-extension/src/extension.ts](vscode-extension/src/extension.ts)
39
41
 
40
42
  Core rule: notebook edits and execution must operate on one logical document truth whether JupyterLab is closed, already open, or opened mid-run.
41
43
 
42
44
  Lifecycle rule: notebook contents and outputs persist through Jupyter's `.ipynb` model, but Hypernote's runtime state, job records, and cell attribution are intentionally in-memory and notebook-scoped.
43
45
 
46
+ Concurrent-actor rule: JupyterLab and Hypernote share one notebook session and one kernel. Hypernote-driven cells run in an ipykernel subshell so the kernel's main shell stays responsive to native Lab actions. Hypernote's extension overrides `/api/kernels/{id}/interrupt` and `/api/kernels/{id}/restart` so Lab's Stop and Restart toolbar buttons reach the subshell-routed cell or perform the right cleanup. See [dev/current-architecture.md](dev/current-architecture.md) for the full mechanism.
47
+
44
48
  ## Shipped Surface
45
49
 
46
50
  ### SDK
@@ -52,6 +56,7 @@ Lifecycle rule: notebook contents and outputs persist through Jupyter's `.ipynb`
52
56
 
53
57
  ### CLI
54
58
 
59
+ - `hypernote` — live workspace dashboard with hints
55
60
  - `create`
56
61
  - `ix`
57
62
  - `exec`
@@ -70,9 +75,11 @@ Lifecycle rule: notebook contents and outputs persist through Jupyter's `.ipynb`
70
75
 
71
76
  Default CLI contract:
72
77
 
78
+ - bare `hypernote` shows live workspace state and next-step hints
73
79
  - TTY: concise human-readable progress
74
80
  - non-TTY: one compact final JSON result
75
81
  - explicit streaming only through `--watch` or `--stream-json`
82
+ - summary-first read payloads should come from SDK-backed observation helpers, not CLI-only formatting rules
76
83
  - `setup serve` is the default local bootstrap path for a Hypernote-enabled Jupyter server
77
84
  - `setup doctor --path PATH` is the preferred first diagnostic when server reachability,
78
85
  kernelspec selection, or runtime mismatch is unclear
@@ -81,12 +88,16 @@ Default CLI contract:
81
88
 
82
89
  - Terminology: when the user says "our system", treat that as the maintained project-operating surface, including `AGENTS.md`, `SKILL.md`, `docs/`, `dev/`, tests, and other shared guidance or verification artifacts around the code.
83
90
  - SDK first. CLI behavior should come from the SDK, not duplicate raw HTTP semantics.
91
+ - When CLI and SDK both need compact observation behavior, define the summary/truncation/focused-read logic in the SDK and let the CLI adapt it.
92
+ - Shared logic needs one owner. When a helper or shaping rule moves into the SDK or another shared layer, delete the old copies instead of letting multiple versions drift.
84
93
  - One truth. Do not reintroduce a contents-vs-YDoc split for notebook reads or writes.
85
94
  - Runtime creation must honor the requested kernel first, otherwise notebook metadata
86
95
  `kernelspec.name`, otherwise `python3`.
87
96
  - Keep control-plane state ephemeral. Do not add durable job history unless the product explicitly needs it.
88
97
  - Our system is part of done. After every meaningful change, update the relevant parts of `AGENTS.md`, `SKILL.md`, `docs/`, `dev/`, tests, and adjacent shared project guidance so they stay in sync with implementation.
89
98
  - Keep adapters thin. CLI, JupyterLab, and tests should reuse shared contracts instead of re-encoding notebook behavior.
99
+ - Treat output and API shapes as contracts. Feature variants should preserve the same top-level envelope and field semantics unless there is a deliberate, documented exception.
100
+ - Normalize boundary inputs early. If upstream payloads can arrive in more than one valid shape, accept and normalize them at the adapter boundary rather than assuming a single representation.
90
101
  - Prefer unique notebook paths in tests and demos. Browser tests must also use unique JupyterLab workspace URLs.
91
102
  - Keep `tmp/` disposable. Durable notes belong in `docs/` or `dev/`, not `tmp/`.
92
103
 
@@ -106,20 +117,24 @@ Default CLI contract:
106
117
  - update [docs/vscode-extension.md](docs/vscode-extension.md)
107
118
  - update [dev/vscode-extension.md](dev/vscode-extension.md)
108
119
 
109
- ### `hypernote/sdk.py` or `hypernote/errors.py`
120
+ ### `src/hypernote/sdk.py` or `src/hypernote/errors.py`
110
121
 
111
122
  - preserve the notebook-first public object model
112
123
  - keep public enums and errors stable
124
+ - prefer adding reusable observation helpers on `NotebookStatus` / `CellStatus` over adding CLI-only shaping logic
113
125
  - update [docs/sdk.md](docs/sdk.md)
114
126
 
115
- ### `hypernote/cli/main.py`
127
+ ### `src/hypernote/cli/main.py`
116
128
 
117
129
  - keep non-TTY output compact by default
130
+ - keep bare `hypernote` as the live dashboard view
118
131
  - preserve `ix` as the happy-path command
132
+ - preserve summary-first `status` and compact `cat` with contextual hints
133
+ - prefer rendering SDK observation helpers over introducing new CLI-only data shaping
119
134
  - keep streaming explicit in non-TTY mode
120
135
  - update [docs/cli.md](docs/cli.md)
121
136
 
122
- ### `hypernote/execution_orchestrator.py`, `hypernote/runtime_manager.py`, or `hypernote/server/*`
137
+ ### `src/hypernote/execution_orchestrator.py`, `src/hypernote/runtime_manager.py`, or `src/hypernote/server/*`
123
138
 
124
139
  - preserve the single-truth shared-document path
125
140
  - verify open-tab and late-open behavior still hold
@@ -134,6 +149,11 @@ Default CLI contract:
134
149
  - CLI output contract
135
150
  - live server behavior
136
151
  - browser regression for streaming and late-open correctness
152
+ - for contract-heavy changes, add invariant coverage for:
153
+ - default and focused variants
154
+ - empty and failure states
155
+ - alternate valid input shapes from upstream payloads
156
+ - parity between real helpers and any fake/test-double implementations
137
157
 
138
158
  ## Verification
139
159
 
@@ -149,7 +169,7 @@ Install guidance:
149
169
  Minimum checks for most changes:
150
170
 
151
171
  ```bash
152
- uv run ruff check hypernote tests
172
+ uv run ruff check src/hypernote tests
153
173
  uv run python -m pytest -q
154
174
  ```
155
175
 
@@ -159,3 +179,10 @@ When browser or live-server behavior changes, also use:
159
179
  HYPERNOTE_INTEGRATION=1 uv run python -m pytest -q tests/test_live_server.py
160
180
  uv run python -m pytest -q tests/test_browser_regression.py
161
181
  ```
182
+
183
+ Before opening or updating a PR for a cross-surface change, do a short contract pass:
184
+
185
+ - check that sibling command variants still share one consistent envelope
186
+ - check that aggregate field names still match their exact semantics
187
+ - check that docs and hints only mention shipped commands and flags
188
+ - check that moved logic no longer has stale copies in adapters or tests
@@ -4,15 +4,29 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## 0.2.0 - 2026-05-07
8
+
9
+ Native JupyterLab as a first-class concurrent actor: open a notebook
10
+ mid-run and see streaming output, click Stop and the cell terminates,
11
+ click Restart and the kernel comes back ready. Plus the package now ships
12
+ in PyPA src layout and an experimental VS Code extension.
13
+
7
14
  ### Added
8
15
 
9
- - added a minimal VS Code extension under `vscode-extension/` that opens JupyterLab in a VS Code custom editor or panel
10
- - added managed local JupyterLab startup for the extension when no configured server is reachable
16
+ - subshell-routed kernel execution (JEP 91 / ipykernel 7+): Hypernote sends `execute_request` with a `subshell_id` so the kernel's main shell stays free to answer concurrent clients (e.g. JupyterLab) opening a notebook mid-run now renders cells immediately and continues to stream output without waiting for the running cell
17
+ - subshell-aware `POST /api/kernels/{id}/interrupt` override: JupyterLab's Stop button (and `nb.interrupt()`) now terminates a Hypernote-driven cell, raising `KeyboardInterrupt` in the subshell thread via a main-shell `PyThreadState_SetAsyncExc` injection. Falls back to default SIGINT for non-Hypernote-driven kernels.
18
+ - subshell- and nbmodel-aware `POST /api/kernels/{id}/restart` override: JupyterLab's Restart button now leaves the kernel ready to run new cells. Hypernote evicts nbmodel's stale per-kernel client and worker, and clears its cached subshell id, so the next execute rebuilds against the fresh kernel.
19
+ - new browser regression tests for Lab Stop and Lab Restart against subshell-routed cells; new real-kernel unit tests for subshell creation, routing, interrupt latency, and restart cleanup.
20
+
21
+ ### Changed
22
+
23
+ - package layout migrated to `src/hypernote/` (PyPA src layout). `pyproject.toml` now configures `[tool.hatch.build.targets.wheel] packages = ["src/hypernote"]` and `[tool.ruff] src = ["src", "tests"]`. The `hypernote` import name is unchanged — `import hypernote` continues to work.
24
+ - release workflow switched from tag-triggered to `workflow_dispatch` — run `gh workflow run release.yml -f version=X.Y.Z` to release
11
25
 
12
26
  ### Notes
13
27
 
14
- - the extension is intentionally decoupled from Hypernote-specific UI and connects to plain JupyterLab
15
- - managed extension launches override Jupyter's default frame-ancestor policy so the embedded view can load inside VS Code
28
+ - subshell routing requires ipykernel 7+ (IPython kernels). Other kernels fall back to the main shell late-open during a long-running cell will block the JupyterLab UI for those kernels and the subshell-targeted interrupt becomes a no-op (override falls through to SIGINT, which works on the main shell).
29
+ - `PyThreadState_SetAsyncExc`-based interrupt cannot terminate a thread inside a long blocking C call without GIL release (e.g. `requests.get` with no timeout). The `KeyboardInterrupt` only fires once control returns to Python. The interrupt snippet falls back to `os.kill(pid, SIGINT)` internally if the subshell thread cannot be found, so user-visible "Stop did nothing" cases degrade to default SIGINT behavior rather than silent failure.
16
30
 
17
31
  ## 0.1.3 - 2026-04-03
18
32
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hypernote
3
- Version: 0.1.3
3
+ Version: 0.2.0
4
4
  Summary: Thin control plane for Jupyter notebook execution with jobs, attribution, and runtime lifecycle
5
5
  Project-URL: Homepage, https://github.com/gilad-rubin/hypernote
6
6
  Project-URL: Repository, https://github.com/gilad-rubin/hypernote
@@ -33,8 +33,8 @@ Description-Content-Type: text/markdown
33
33
 
34
34
  ## What it ships
35
35
 
36
- - notebook-first SDK in `hypernote/sdk.py`
37
- - agent-first CLI in `hypernote/cli/main.py`
36
+ - notebook-first SDK in `src/hypernote/sdk.py`
37
+ - agent-first CLI in `src/hypernote/cli/main.py`
38
38
  - Jupyter server extension for execution and runtime control
39
39
  - experimental VS Code extension in `vscode-extension/` for embedding JupyterLab in VS Code
40
40
  - notebook-scoped runtime lifecycle with attach, detach, recovery, and stop
@@ -92,6 +92,13 @@ Hypernote owns:
92
92
  - actor attribution
93
93
  - SDK, CLI, and thin REST handlers
94
94
 
95
+ ## Design discipline
96
+
97
+ - shared behavior should have one owner, usually the SDK for agent-facing observation rules
98
+ - command and payload variants should preserve one contract unless a difference is explicit and documented
99
+ - adapters should normalize valid upstream shape differences at the boundary
100
+ - tests should cover invariants across variants, not only the main workflow
101
+
95
102
  ## Documentation
96
103
 
97
104
  - [Getting Started](docs/getting-started.md)
@@ -118,7 +125,7 @@ uv sync --extra dev
118
125
  ```
119
126
 
120
127
  ```bash
121
- uv run ruff check hypernote tests
128
+ uv run ruff check src/hypernote tests
122
129
  uv run python -m pytest -q
123
130
  ```
124
131
 
@@ -7,8 +7,8 @@
7
7
 
8
8
  ## What it ships
9
9
 
10
- - notebook-first SDK in `hypernote/sdk.py`
11
- - agent-first CLI in `hypernote/cli/main.py`
10
+ - notebook-first SDK in `src/hypernote/sdk.py`
11
+ - agent-first CLI in `src/hypernote/cli/main.py`
12
12
  - Jupyter server extension for execution and runtime control
13
13
  - experimental VS Code extension in `vscode-extension/` for embedding JupyterLab in VS Code
14
14
  - notebook-scoped runtime lifecycle with attach, detach, recovery, and stop
@@ -66,6 +66,13 @@ Hypernote owns:
66
66
  - actor attribution
67
67
  - SDK, CLI, and thin REST handlers
68
68
 
69
+ ## Design discipline
70
+
71
+ - shared behavior should have one owner, usually the SDK for agent-facing observation rules
72
+ - command and payload variants should preserve one contract unless a difference is explicit and documented
73
+ - adapters should normalize valid upstream shape differences at the boundary
74
+ - tests should cover invariants across variants, not only the main workflow
75
+
69
76
  ## Documentation
70
77
 
71
78
  - [Getting Started](docs/getting-started.md)
@@ -92,7 +99,7 @@ uv sync --extra dev
92
99
  ```
93
100
 
94
101
  ```bash
95
- uv run ruff check hypernote tests
102
+ uv run ruff check src/hypernote tests
96
103
  uv run python -m pytest -q
97
104
  ```
98
105
 
@@ -7,7 +7,11 @@ description: Work against Hypernote's notebook-first SDK and agent-first CLI. Us
7
7
 
8
8
  `hypernote` is the notebook runtime surface. The SDK is the core API. The CLI is a thin shell over that SDK.
9
9
 
10
- Run `uv run hypernote --help` for the current command list, and `uv run hypernote <command> --help` for exact syntax.
10
+ Design rule: if a compact read model, truncation rule, or focused observation flow is useful in the CLI and also generally useful to agents or other adapters, define it in the SDK first and let the CLI render it, rather than re-encoding the logic in the CLI.
11
+
12
+ Review rule: prefer finishing a feature with contract cleanup, not just feature coverage. Variants should share one envelope, aggregate names should match their exact semantics, and adapters should normalize boundary payload shapes instead of assuming one representation.
13
+
14
+ Run `uv run hypernote` for a live workspace dashboard, `uv run hypernote --help` for the current command list, and `uv run hypernote <command> --help` for exact syntax.
11
15
 
12
16
  ## Prerequisite
13
17
 
@@ -63,6 +67,7 @@ uv run hypernote --server http://127.0.0.1:8889 setup doctor
63
67
  ## Quick Start
64
68
 
65
69
  ```bash
70
+ uv run hypernote # live workspace dashboard and hints
66
71
  uv run hypernote setup doctor # check for existing server
67
72
  uv run hypernote setup serve & # only if no server is running
68
73
  uv run hypernote create tmp/demo.ipynb --empty
@@ -112,14 +117,15 @@ Do not use `-s` for multi-line code. Shell quoting will corrupt newlines.
112
117
  - `exec` — re-run an existing cell by id (useful after editing a failed cell)
113
118
  - `edit` — mutate cell source or structure without executing
114
119
  - `run-all` / `restart` / `restart-run-all` — notebook-wide execution
115
- - `status` / `diff` / `cat` — inspect notebook state and outputs
120
+ - `status` / `diff` / `cat` — inspect notebook state and outputs with summary-first reads, filtered cells, and focused output flags
116
121
 
117
122
  ### When a cell fails
118
123
 
119
124
  1. Read the error output from `ix`.
120
- 2. Fix the source with `edit replace`.
121
- 3. Re-run with `exec <cell-id>`.
122
- 4. Continue with the next `ix`.
125
+ 2. Use `cat --cell <cell-id>` or `cat --output <cell-id>` if you need a compact view of the failure.
126
+ 3. Fix the source with `edit replace`.
127
+ 4. Re-run with `exec <cell-id>`.
128
+ 5. Continue with the next `ix`.
123
129
 
124
130
  Do not re-insert a failed cell. Edit it in place and re-execute.
125
131
 
@@ -140,12 +146,17 @@ know exactly where to resume. Cells after the halt point were never inserted int
140
146
  4. Prefer the SDK and CLI over raw HTTP unless you are explicitly working on server routes.
141
147
  5. Treat Jupyter shared documents as the source of truth. Open or closed JupyterLab tabs must not change correctness.
142
148
  6. For agents, prefer default non-TTY JSON output unless you intentionally want background streaming.
143
- 7. Use `--stream-json` only when you plan to watch the process; otherwise it wastes context.
144
- 8. Start the server with `hypernote setup serve` instead of hand-writing Jupyter flags.
145
- 9. Skip large rich outputs such as `graph.visualize()` in headless automation unless the visualization is the point of the run.
146
- 10. Use unique notebook paths in tests and demos.
147
- 11. Move durable notes into `docs/` or `dev/`; keep `tmp/` disposable.
148
- 12. Treat Hypernote jobs, runtime state, and cell attribution as ephemeral coordination state, not durable history.
149
+ 7. Start with `hypernote` itself when you need workspace context and the next best action.
150
+ 8. Use `--stream-json` only when you plan to watch the process; otherwise it wastes context.
151
+ 9. Start the server with `hypernote setup serve` instead of hand-writing Jupyter flags.
152
+ 10. Skip large rich outputs such as `graph.visualize()` in headless automation unless the visualization is the point of the run.
153
+ 11. Use unique notebook paths in tests and demos.
154
+ 12. Move durable notes into `docs/` or `dev/`; keep `tmp/` disposable.
155
+ 13. Treat Hypernote jobs, runtime state, and cell attribution as ephemeral coordination state, not durable history.
156
+ 14. When changing read/inspection behavior, update the SDK observation helpers before or alongside the CLI so every adapter shares the same summary/truncation rules.
157
+ 15. Keep command hints grounded in shipped commands and actual runtime values. Do not document or suggest a focused read flag unless it exists in the CLI.
158
+ 16. For contract-heavy changes, test the focused variants, empty/failure states, and alternate valid payload shapes, not just the happy path.
159
+ 17. When a helper moves into the SDK or another shared layer, remove the old CLI/test copy in the same change.
149
160
 
150
161
  ## Before You Change Behavior
151
162
 
@@ -167,6 +178,6 @@ Use `uv sync` for base runtime work and `uv sync --extra lab` when you specifica
167
178
  Then run:
168
179
 
169
180
  ```bash
170
- uv run ruff check hypernote tests
181
+ uv run ruff check src/hypernote tests
171
182
  uv run python -m pytest -q
172
183
  ```
@@ -0,0 +1,29 @@
1
+ # Development Docs
2
+
3
+ This folder is internal reference for the shipped Hypernote architecture.
4
+
5
+ Public workflow docs live in `docs/`. This folder is for implementation-facing notes only.
6
+
7
+ Read these first:
8
+
9
+ - [Current Architecture](current-architecture.md)
10
+ - [Module Map](module-map.md)
11
+ - [Testing and Verification](testing-and-verification.md)
12
+ - [CLI Agent Ergonomics Rollout](cli-agent-ergonomics-rollout.md)
13
+ - [VS Code Extension Notes](vscode-extension.md)
14
+
15
+ Release path:
16
+
17
+ - trigger the release workflow from GitHub Actions UI or `gh workflow run release.yml -f version=X.Y.Z`
18
+ - the workflow bumps `pyproject.toml`, syncs `uv.lock`, commits, tags, builds, tests, creates a GitHub release, and publishes to PyPI
19
+ - PyPI auth comes from the GitHub Actions secret `PYPI_API_TOKEN`
20
+ - see [release.yml](../.github/workflows/release.yml)
21
+
22
+ Rules:
23
+
24
+ - keep this folder small
25
+ - document shipped behavior, not aspirational redesigns
26
+ - when behavior changes, update `AGENTS.md`, `SKILL.md`, `docs/`, and `dev/` together
27
+ - prefer one short source of truth per principle instead of repeating long process notes across many docs
28
+ - use this folder for implementation discipline and architecture notes; keep public contract wording in `docs/`
29
+ - for cross-surface changes, write down the invariants that must stay true across variants, not just the happy-path workflow
@@ -0,0 +1,69 @@
1
+ # CLI Agent Ergonomics Rollout
2
+
3
+ This document tracks the AXI-inspired improvements for Hypernote's CLI.
4
+
5
+ Status:
6
+
7
+ - Phase 1 is shipped and documented in the public CLI reference.
8
+ - Phase 2 and Phase 3 remain open.
9
+
10
+ Goals:
11
+
12
+ - reduce agent discovery turns
13
+ - keep default non-TTY reads compact
14
+ - make notebook state easier to act on without extra calls
15
+ - preserve JSON compatibility and the existing SDK-first CLI contract
16
+
17
+ Non-goals for now:
18
+
19
+ - replacing JSON with a custom format
20
+ - shell hook ambient context
21
+ - changing all error output conventions at once
22
+
23
+ ## Phase 1 Shipped
24
+
25
+ - [x] Add contextual `hint:` lines after high-value commands.
26
+ - [x] Make bare `hypernote` show a live home view instead of help text.
27
+ - [x] Add precomputed notebook aggregates to `status`.
28
+ - [x] Redesign default `cat` output to be summary-first.
29
+ - [x] Truncate large outputs by default in agent-oriented reads.
30
+ - [x] Add focused read flags for notebook inspection.
31
+ - [x] Make empty states explicit across notebook, runtime, and job reads.
32
+
33
+ Shipped behavior:
34
+
35
+ - `hypernote` with no subcommand returns actionable live state instead of Click help
36
+ - `status` answers "is this notebook healthy?" from top-level fields
37
+ - `cat` answers "what cells are here and which one should I inspect?" without full dumps
38
+ - large outputs are truncated in non-TTY reads unless `--full-output` is used
39
+ - hints only reference shipped commands and real runtime values
40
+ - the compact read model is now SDK-backed so the CLI is not the only place that knows the summary/truncation rules
41
+
42
+ ## Remaining Backlog
43
+
44
+ ### Phase 2
45
+
46
+ - [ ] Add a one-command repair path for failed cells.
47
+ - [ ] Add richer recovery hints for failed, timed out, and awaiting-input jobs.
48
+ - [ ] Make `job get` and `runtime status` summary-first.
49
+ - [ ] Make `setup doctor` more decision-oriented.
50
+ - [ ] Flatten agent-oriented JSON output where it helps avoid deep nesting.
51
+
52
+ ### Phase 3
53
+
54
+ - [ ] Add `--fields` support on read commands.
55
+ - [ ] Normalize `--full`, `--full-output`, and `--max-output` semantics.
56
+ - [ ] Add stable machine-readable error codes for JSON mode.
57
+ - [ ] Shorten subcommand help and make it example-led.
58
+ - [ ] Add concrete command examples to help output.
59
+ - [ ] Keep responses content-first and help-second.
60
+ - [ ] Include notebook-relative context in hints where possible.
61
+ - [ ] Consider a dedicated `home` or `dashboard` command if the bare root becomes overloaded.
62
+ - [ ] Consider optional ambient context hooks after core command outputs are strong.
63
+ - [ ] Add richer notebook health summaries for stale or mismatched runtime states.
64
+
65
+ ## Deferred
66
+
67
+ - TOON or other custom output formats
68
+ - shell/session hook ambient context
69
+ - sweeping stdout/stderr contract changes