norns-ide 0.0.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.
- norns_ide-0.0.2/LICENSE +21 -0
- norns_ide-0.0.2/MANIFEST.in +10 -0
- norns_ide-0.0.2/PKG-INFO +196 -0
- norns_ide-0.0.2/README.md +159 -0
- norns_ide-0.0.2/backend/__init__.py +1 -0
- norns_ide-0.0.2/backend/app/__init__.py +1 -0
- norns_ide-0.0.2/backend/app/agents/__init__.py +3 -0
- norns_ide-0.0.2/backend/app/agents/runner.py +637 -0
- norns_ide-0.0.2/backend/app/api/__init__.py +3 -0
- norns_ide-0.0.2/backend/app/api/auth.py +88 -0
- norns_ide-0.0.2/backend/app/api/boards.py +626 -0
- norns_ide-0.0.2/backend/app/api/cards.py +220 -0
- norns_ide-0.0.2/backend/app/api/connectors.py +153 -0
- norns_ide-0.0.2/backend/app/api/plugins.py +21 -0
- norns_ide-0.0.2/backend/app/api/router.py +68 -0
- norns_ide-0.0.2/backend/app/card_preview.py +34 -0
- norns_ide-0.0.2/backend/app/config.py +88 -0
- norns_ide-0.0.2/backend/app/connector_config.py +442 -0
- norns_ide-0.0.2/backend/app/cursor_api.py +142 -0
- norns_ide-0.0.2/backend/app/database.py +134 -0
- norns_ide-0.0.2/backend/app/faults.py +40 -0
- norns_ide-0.0.2/backend/app/main.py +52 -0
- norns_ide-0.0.2/backend/app/models/__init__.py +15 -0
- norns_ide-0.0.2/backend/app/models/board.py +89 -0
- norns_ide-0.0.2/backend/app/models/card.py +73 -0
- norns_ide-0.0.2/backend/app/models/connector.py +66 -0
- norns_ide-0.0.2/backend/app/orchestrator/__init__.py +3 -0
- norns_ide-0.0.2/backend/app/orchestrator/enqueue.py +60 -0
- norns_ide-0.0.2/backend/app/orchestrator/gates.py +273 -0
- norns_ide-0.0.2/backend/app/orchestrator/progression.py +139 -0
- norns_ide-0.0.2/backend/app/orchestrator/recovery.py +32 -0
- norns_ide-0.0.2/backend/app/orchestrator/split.py +254 -0
- norns_ide-0.0.2/backend/app/orchestrator/state_machine.py +100 -0
- norns_ide-0.0.2/backend/app/plantuml/__init__.py +3 -0
- norns_ide-0.0.2/backend/app/plantuml/renderer.py +94 -0
- norns_ide-0.0.2/backend/app/plugins/__init__.py +11 -0
- norns_ide-0.0.2/backend/app/plugins/adapters.py +139 -0
- norns_ide-0.0.2/backend/app/plugins/base.py +82 -0
- norns_ide-0.0.2/backend/app/plugins/catalog.py +246 -0
- norns_ide-0.0.2/backend/app/plugins/mcp_protocol.py +173 -0
- norns_ide-0.0.2/backend/app/plugins/norns.py +589 -0
- norns_ide-0.0.2/backend/app/plugins/tool_policy.py +23 -0
- norns_ide-0.0.2/backend/app/tools/__init__.py +3 -0
- norns_ide-0.0.2/backend/app/tools/github.py +239 -0
- norns_ide-0.0.2/backend/app/tools/jira.py +283 -0
- norns_ide-0.0.2/backend/app/tools/polarion.py +433 -0
- norns_ide-0.0.2/backend/app/tools/registry.py +86 -0
- norns_ide-0.0.2/backend/app/ui_assets.py +92 -0
- norns_ide-0.0.2/backend/app/workspace.py +135 -0
- norns_ide-0.0.2/backend/tests/__init__.py +1 -0
- norns_ide-0.0.2/backend/tests/conftest.py +10 -0
- norns_ide-0.0.2/backend/tests/test_api.py +473 -0
- norns_ide-0.0.2/backend/tests/test_card_preview.py +48 -0
- norns_ide-0.0.2/backend/tests/test_config.py +63 -0
- norns_ide-0.0.2/backend/tests/test_connectors.py +157 -0
- norns_ide-0.0.2/backend/tests/test_cursor_api.py +229 -0
- norns_ide-0.0.2/backend/tests/test_database.py +176 -0
- norns_ide-0.0.2/backend/tests/test_faults.py +135 -0
- norns_ide-0.0.2/backend/tests/test_handoff.py +54 -0
- norns_ide-0.0.2/backend/tests/test_norns_cli.py +97 -0
- norns_ide-0.0.2/backend/tests/test_orchestrator.py +409 -0
- norns_ide-0.0.2/backend/tests/test_packaged_ui_matches_compose.py +127 -0
- norns_ide-0.0.2/backend/tests/test_packaging.py +215 -0
- norns_ide-0.0.2/backend/tests/test_pending_writes.py +215 -0
- norns_ide-0.0.2/backend/tests/test_plugins.py +410 -0
- norns_ide-0.0.2/backend/tests/test_polarion.py +152 -0
- norns_ide-0.0.2/backend/tests/test_progression.py +186 -0
- norns_ide-0.0.2/backend/tests/test_recovery.py +45 -0
- norns_ide-0.0.2/backend/tests/test_registry.py +23 -0
- norns_ide-0.0.2/backend/tests/test_runner.py +464 -0
- norns_ide-0.0.2/backend/tests/test_state_machine.py +67 -0
- norns_ide-0.0.2/backend/tests/test_tool_policy.py +16 -0
- norns_ide-0.0.2/backend/tests/test_ui_assets.py +40 -0
- norns_ide-0.0.2/backend/tests/test_workspace.py +75 -0
- norns_ide-0.0.2/frontend/.prettierignore +8 -0
- norns_ide-0.0.2/frontend/.prettierrc.json +8 -0
- norns_ide-0.0.2/frontend/dist/assets/index-BhxA4RYX.css +1 -0
- norns_ide-0.0.2/frontend/dist/assets/index-DTPm_GI-.js +71 -0
- norns_ide-0.0.2/frontend/dist/index.html +17 -0
- norns_ide-0.0.2/frontend/eslint.config.js +29 -0
- norns_ide-0.0.2/frontend/index.html +16 -0
- norns_ide-0.0.2/frontend/package-lock.json +3880 -0
- norns_ide-0.0.2/frontend/package.json +30 -0
- norns_ide-0.0.2/frontend/src/App.tsx +156 -0
- norns_ide-0.0.2/frontend/src/api/client.ts +30 -0
- norns_ide-0.0.2/frontend/src/boardLayout.test.ts +26 -0
- norns_ide-0.0.2/frontend/src/boardLayout.ts +19 -0
- norns_ide-0.0.2/frontend/src/cardJourney.test.ts +180 -0
- norns_ide-0.0.2/frontend/src/cardJourney.ts +195 -0
- norns_ide-0.0.2/frontend/src/cardPreview.test.ts +57 -0
- norns_ide-0.0.2/frontend/src/cardPreview.ts +107 -0
- norns_ide-0.0.2/frontend/src/components/AgentConfig.tsx +304 -0
- norns_ide-0.0.2/frontend/src/components/Board.tsx +261 -0
- norns_ide-0.0.2/frontend/src/components/CardDrawer.tsx +420 -0
- norns_ide-0.0.2/frontend/src/components/CardHistory.tsx +180 -0
- norns_ide-0.0.2/frontend/src/components/CardItem.tsx +34 -0
- norns_ide-0.0.2/frontend/src/components/Column.tsx +112 -0
- norns_ide-0.0.2/frontend/src/components/ConnectorHealth.tsx +510 -0
- norns_ide-0.0.2/frontend/src/components/Dialog.tsx +101 -0
- norns_ide-0.0.2/frontend/src/components/LoginForm.tsx +53 -0
- norns_ide-0.0.2/frontend/src/components/MarkdownPreview.tsx +82 -0
- norns_ide-0.0.2/frontend/src/components/Settings.tsx +77 -0
- norns_ide-0.0.2/frontend/src/components/StateMachineEditor.tsx +866 -0
- norns_ide-0.0.2/frontend/src/components/WaitLive.tsx +52 -0
- norns_ide-0.0.2/frontend/src/gateLabels.test.ts +79 -0
- norns_ide-0.0.2/frontend/src/gateLabels.ts +101 -0
- norns_ide-0.0.2/frontend/src/main.tsx +16 -0
- norns_ide-0.0.2/frontend/src/markdown.test.ts +55 -0
- norns_ide-0.0.2/frontend/src/runHints.test.ts +24 -0
- norns_ide-0.0.2/frontend/src/runHints.ts +20 -0
- norns_ide-0.0.2/frontend/src/status.ts +11 -0
- norns_ide-0.0.2/frontend/src/styles/control-room.css +1545 -0
- norns_ide-0.0.2/frontend/src/types/index.ts +94 -0
- norns_ide-0.0.2/frontend/src/waitProgress.test.ts +21 -0
- norns_ide-0.0.2/frontend/src/waitProgress.ts +42 -0
- norns_ide-0.0.2/frontend/src/workspaceLabel.test.ts +20 -0
- norns_ide-0.0.2/frontend/src/workspaceLabel.ts +23 -0
- norns_ide-0.0.2/frontend/style-lab.css +731 -0
- norns_ide-0.0.2/frontend/style-lab.html +33 -0
- norns_ide-0.0.2/frontend/style-lab.js +378 -0
- norns_ide-0.0.2/frontend/tsconfig.json +21 -0
- norns_ide-0.0.2/frontend/vite.config.ts +16 -0
- norns_ide-0.0.2/norns/__init__.py +3 -0
- norns_ide-0.0.2/norns/__main__.py +4 -0
- norns_ide-0.0.2/norns/cli.py +90 -0
- norns_ide-0.0.2/norns/desktop.py +167 -0
- norns_ide-0.0.2/norns/electron/main.js +27 -0
- norns_ide-0.0.2/norns/electron/package-lock.json +801 -0
- norns_ide-0.0.2/norns/electron/package.json +13 -0
- norns_ide-0.0.2/norns/home.py +122 -0
- norns_ide-0.0.2/norns/mcp.py +31 -0
- norns_ide-0.0.2/norns/web/assets/index-BhxA4RYX.css +1 -0
- norns_ide-0.0.2/norns/web/assets/index-DTPm_GI-.js +71 -0
- norns_ide-0.0.2/norns/web/index.html +17 -0
- norns_ide-0.0.2/norns_build.py +230 -0
- norns_ide-0.0.2/norns_ide.egg-info/PKG-INFO +196 -0
- norns_ide-0.0.2/norns_ide.egg-info/SOURCES.txt +145 -0
- norns_ide-0.0.2/norns_ide.egg-info/dependency_links.txt +1 -0
- norns_ide-0.0.2/norns_ide.egg-info/entry_points.txt +8 -0
- norns_ide-0.0.2/norns_ide.egg-info/requires.txt +24 -0
- norns_ide-0.0.2/norns_ide.egg-info/top_level.txt +4 -0
- norns_ide-0.0.2/pyproject.toml +92 -0
- norns_ide-0.0.2/scripts/stage-ui.sh +13 -0
- norns_ide-0.0.2/setup.cfg +4 -0
- norns_ide-0.0.2/worker/__init__.py +1 -0
- norns_ide-0.0.2/worker/main.py +14 -0
- norns_ide-0.0.2/worker/tasks.py +8 -0
norns_ide-0.0.2/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 YanChao1999
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
include norns_build.py
|
|
2
|
+
recursive-include norns/web *
|
|
3
|
+
include norns/electron/main.js
|
|
4
|
+
include norns/electron/package.json
|
|
5
|
+
include norns/electron/package-lock.json
|
|
6
|
+
include scripts/stage-ui.sh
|
|
7
|
+
graft frontend
|
|
8
|
+
prune frontend/node_modules
|
|
9
|
+
global-exclude __pycache__
|
|
10
|
+
global-exclude *.pyc
|
norns_ide-0.0.2/PKG-INFO
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: norns-ide
|
|
3
|
+
Version: 0.0.2
|
|
4
|
+
Summary: Kanban orchestration system with per-column LLM agents and human approval gates
|
|
5
|
+
Author: YanChao1999
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://yanchao1999.github.io/Norns/
|
|
8
|
+
Project-URL: Repository, https://github.com/YanChao1999/Norns
|
|
9
|
+
Project-URL: Issues, https://github.com/YanChao1999/Norns/issues
|
|
10
|
+
Requires-Python: >=3.12
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Requires-Dist: fastapi>=0.115.0
|
|
14
|
+
Requires-Dist: uvicorn[standard]>=0.30.0
|
|
15
|
+
Requires-Dist: sqlalchemy[asyncio]>=2.0.32
|
|
16
|
+
Requires-Dist: asyncpg>=0.29.0
|
|
17
|
+
Requires-Dist: aiosqlite>=0.20.0
|
|
18
|
+
Requires-Dist: arq>=0.26.0
|
|
19
|
+
Requires-Dist: openai>=1.40.0
|
|
20
|
+
Requires-Dist: PyGithub>=2.3.0
|
|
21
|
+
Requires-Dist: jira>=3.8.0
|
|
22
|
+
Requires-Dist: polarion>=1.0.0
|
|
23
|
+
Requires-Dist: cryptography>=43.0.0
|
|
24
|
+
Requires-Dist: itsdangerous>=2.2.0
|
|
25
|
+
Requires-Dist: pydantic-settings>=2.4.0
|
|
26
|
+
Requires-Dist: httpx>=0.27.0
|
|
27
|
+
Requires-Dist: plantuml>=0.3.0
|
|
28
|
+
Requires-Dist: cursor-sdk>=1.0.28
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: pytest>=8.3.0; extra == "dev"
|
|
31
|
+
Requires-Dist: pytest-asyncio>=0.23.8; extra == "dev"
|
|
32
|
+
Requires-Dist: pytest-cov>=5.0.0; extra == "dev"
|
|
33
|
+
Requires-Dist: ruff>=0.8.0; extra == "dev"
|
|
34
|
+
Requires-Dist: build>=1.2.0; extra == "dev"
|
|
35
|
+
Requires-Dist: twine>=6.0.0; extra == "dev"
|
|
36
|
+
Dynamic: license-file
|
|
37
|
+
|
|
38
|
+
# Norns
|
|
39
|
+
|
|
40
|
+
Norns is a Kanban orchestration system where each board column runs an isolated LLM agent and a human approval gate controls progression to the next stage. The name comes from the Norse Norns: Urd, Verdandi, and Skuld.
|
|
41
|
+
|
|
42
|
+
**v0.0.2** site: [yanchao1999.github.io/Norns](https://yanchao1999.github.io/Norns/). First-use backlog: [ROADMAP.md](ROADMAP.md).
|
|
43
|
+
|
|
44
|
+
## Features
|
|
45
|
+
- Local Control Room (`norns init` / `norns run`) — UI in the browser by default, optional Electron window, config under `~/.norns`
|
|
46
|
+
- Visual state machine editor for board stages, order, parallel tracks, and human-gate vs auto-advance
|
|
47
|
+
- FastAPI + async SQLAlchemy backend with PostgreSQL-ready configuration
|
|
48
|
+
- ARQ/Redis queue for isolated stage execution (optional; local IDE runs stages in-process)
|
|
49
|
+
- React + TypeScript Kanban UI with approval-aware movement
|
|
50
|
+
- Stage agents may recommend **approve** or **reject**; a human still confirms at the gate
|
|
51
|
+
- Encrypted connector secrets at rest with Fernet
|
|
52
|
+
- OpenAI-compatible stage agents with explicit per-stage tool allowlists
|
|
53
|
+
- PlantUML-first documentation and handoff previews
|
|
54
|
+
|
|
55
|
+
## Flow Diagram
|
|
56
|
+
```plantuml
|
|
57
|
+
@startuml
|
|
58
|
+
skinparam monochrome true
|
|
59
|
+
actor Human
|
|
60
|
+
rectangle Board {
|
|
61
|
+
rectangle "Stage 1\n(Urd agent)" as S1
|
|
62
|
+
rectangle "Stage 2\n(Verdandi agent)" as S2
|
|
63
|
+
rectangle "Stage 3\n(Skuld agent)" as S3
|
|
64
|
+
}
|
|
65
|
+
Human --> S1 : create / approve
|
|
66
|
+
S1 --> Human : handoff + recommend approve/reject
|
|
67
|
+
Human --> S2 : confirm approve or reject
|
|
68
|
+
S2 --> Human : handoff + recommend approve/reject
|
|
69
|
+
Human --> S3 : confirm approve or reject
|
|
70
|
+
@enduml
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Runtime Sequence
|
|
74
|
+
```plantuml
|
|
75
|
+
@startuml
|
|
76
|
+
skinparam monochrome true
|
|
77
|
+
actor Human
|
|
78
|
+
participant Frontend
|
|
79
|
+
participant Backend
|
|
80
|
+
participant Redis
|
|
81
|
+
participant Worker
|
|
82
|
+
participant Agent
|
|
83
|
+
|
|
84
|
+
Human -> Frontend : Approve card
|
|
85
|
+
Frontend -> Backend : POST /api/cards/{id}/approve
|
|
86
|
+
Backend -> Redis : enqueue next stage
|
|
87
|
+
Redis -> Worker : run_stage_task
|
|
88
|
+
Worker -> Agent : run_stage(card, stage, run)
|
|
89
|
+
Agent -> Backend : persist AgentRun + handoff (recommendation is advisory)
|
|
90
|
+
Backend -> Frontend : waiting_approval state
|
|
91
|
+
Human -> Frontend : confirm approve or reject
|
|
92
|
+
@enduml
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Architecture Overview
|
|
96
|
+
- **Boards / Stages** define the workflow and per-column agent configuration. Edit stages and **transition lines** in the Control Room **Machine** view. Two default lines from one stage **split** the card into parallel tracks (for example unit tests and software). Lines from two or more stages into one stage **join** those tracks when every track has finished.
|
|
97
|
+
- **Cards** carry the work item body and current stage pointer.
|
|
98
|
+
- **Agent runs** are isolated; no chat memory is shared between stages.
|
|
99
|
+
- **Handoffs** from stage _N_ are the only structured context for stage _N+1_. On a human gate, the agent may set `recommendation` to `approve` or `reject`; the card still waits until a person confirms. Draw an If on `recommendation` if that suggestion should choose the next stage after confirmation.
|
|
100
|
+
- **Connectors** are Python-library backed only (PyGithub, jira, polarion); raw credentials are never exposed to agents.
|
|
101
|
+
|
|
102
|
+
## Install (pip / uv)
|
|
103
|
+
|
|
104
|
+
Python 3.12+. `norns run` opens the Control Room in your browser. Electron is optional.
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
# From this checkout (needs Node.js 20+ or Docker once, to compile the UI into the package):
|
|
108
|
+
uv tool install .
|
|
109
|
+
# or
|
|
110
|
+
python3 -m pip install .
|
|
111
|
+
|
|
112
|
+
norns init # prints an admin password; also stored in ~/.norns/config.toml
|
|
113
|
+
norns run # http://127.0.0.1:8765
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
After this is published (see [PUBLISH.md](PUBLISH.md)):
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
uv tool install norns-ide
|
|
120
|
+
# or
|
|
121
|
+
python3 -m pip install norns-ide
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
The PyPI name is `norns-ide` because [`norns`](https://pypi.org/project/norns/) is already taken. The command is still `norns`.
|
|
125
|
+
|
|
126
|
+
From GitHub after this branch is merged:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
uv tool install git+https://github.com/YanChao1999/Norns.git
|
|
130
|
+
# or
|
|
131
|
+
python3 -m pip install git+https://github.com/YanChao1999/Norns.git
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
`norns run --no-window` starts the server without opening a browser (used by CI). Data lives under `~/.norns` unless you pass `--home` or set `NORNS_HOME`. Use `norns init --force` to replace config and delete `norns.db` (no schema back-compat before a published 0.0.1). Set `openai.api_key` in `~/.norns/config.toml` when you want a real model instead of a placeholder handoff.
|
|
135
|
+
|
|
136
|
+
### Optional desktop window
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
npm install --prefix norns/electron # from a git checkout
|
|
140
|
+
norns run
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Developer checkout
|
|
144
|
+
|
|
145
|
+
`uv run` is an editable install, so it does not run the wheel build that bakes the UI in. `norns run` compiles `frontend/` on first start with npm if it is on PATH (or `./.tools/node`), otherwise with Docker using the same `node:20` image as `docker compose up`. If docker compose left `frontend/node_modules` root-owned, the build uses `~/.cache/norns/ui-build` instead. Rebuild after UI source changes with `./scripts/stage-ui.sh` (same npm-or-Docker path; it always rebuilds, unlike `norns run`).
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
uv sync
|
|
149
|
+
uv run norns init
|
|
150
|
+
uv run norns run
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Docker compose (optional)
|
|
154
|
+
Use this when you want PostgreSQL, Redis, and a browser-based Vite dev server instead of the desktop IDE.
|
|
155
|
+
|
|
156
|
+
1. Copy `.env.example` to `.env` and set real secrets.
|
|
157
|
+
2. Generate unique secrets (do not keep the example values):
|
|
158
|
+
```bash
|
|
159
|
+
python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
|
|
160
|
+
python -c "import secrets; print(secrets.token_urlsafe(32))"
|
|
161
|
+
```
|
|
162
|
+
Put them in `ENCRYPTION_KEY` and `SECRET_KEY`. The API and worker must share the same encryption key.
|
|
163
|
+
3. Start the stack:
|
|
164
|
+
```bash
|
|
165
|
+
docker compose up
|
|
166
|
+
```
|
|
167
|
+
4. Open:
|
|
168
|
+
- Frontend: `http://localhost:5173`
|
|
169
|
+
- Backend API: `http://localhost:8000/docs`
|
|
170
|
+
5. Sign in with the `ADMIN_USERNAME` / `ADMIN_PASSWORD` from your `.env` (defaults are `admin` / `admin` only when `NORNS_ENV=local`). For a shared host set `NORNS_ENV=production`, a unique `ADMIN_PASSWORD`, and `SESSION_COOKIE_SECURE=true`.
|
|
171
|
+
6. Create a board, add a card, open it, and click **Run this stage**. Cards only move after approval unless a stage has auto-advance enabled.
|
|
172
|
+
|
|
173
|
+
`docker compose` builds a Python image once. Rebuild after dependency changes: `docker compose build`.
|
|
174
|
+
|
|
175
|
+
## Development Setup
|
|
176
|
+
```bash
|
|
177
|
+
pip install -e ".[dev]"
|
|
178
|
+
python -m pytest backend/tests/ -q
|
|
179
|
+
bash scripts/lint.sh
|
|
180
|
+
cd frontend && npm install && npm run dev
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Pull requests to `main` must pass the **CI** GitHub Actions check (`backend` tests, `frontend` typecheck/build, `lint` static analysis and format, `package` wheel/sdist plus `norns` CLI). Direct pushes to `main` are not blocked, but merges are.
|
|
184
|
+
|
|
185
|
+
## Notes
|
|
186
|
+
- Local IDE mode uses SQLite and `QUEUE_BACKEND=inline` (no Redis). Docker/production can keep Redis via `QUEUE_BACKEND=redis`.
|
|
187
|
+
- Schema is created with SQLAlchemy `create_all`. There is no upgrade migration until 0.0.1 is published; `norns init --force` deletes the local database.
|
|
188
|
+
- Use PostgreSQL in normal deployments via `DATABASE_URL`.
|
|
189
|
+
- Redis backs ARQ worker execution.
|
|
190
|
+
- PlantUML/Kroki rendering is opt-in via `PLANTUML_URL` / `KROKI_URL` (unset means no public egress).
|
|
191
|
+
- An empty per-stage tool allowlist grants **no** tools. Enable **norns** (create cards, edit stages/prompts, edit the state machine, inspect the git workspace), **github**, **jira**, or **polarion** on the column **Agent** dialog. Cursor stages receive those as MCP servers; OpenAI/DeepSeek stages use the same plugins as chat tools.
|
|
192
|
+
- **Workspace (git repo):** each board has a checkout path and/or `https://github.com/org/repo`. Stage agents inherit the board repo; a column **Agent** can override with its own path/URL. Cursor stages then run in that checkout (local agent) instead of a throwaway `/tmp` directory; GitHub tools default to that repo. If neither board nor agent is set, Norns uses the git root of the process working directory.
|
|
193
|
+
- Extra MCP servers: add an **MCP** connector in Settings (stdio command or HTTP URL), then enable it under Agent → Tools. Third-party Python plugins register the `norns.plugins` entry point.
|
|
194
|
+
- `norns mcp --plugins norns,github,jira` runs the plugin MCP server on stdin/stdout (Cursor attaches this automatically when those tools are enabled).
|
|
195
|
+
- `PUT /api/cards/{id}` updates title/body only; new cards always start on the first stage.
|
|
196
|
+
- Change `SECRET_KEY` and `ADMIN_PASSWORD` before any shared deployment. Sessions expire after 8 hours.
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# Norns
|
|
2
|
+
|
|
3
|
+
Norns is a Kanban orchestration system where each board column runs an isolated LLM agent and a human approval gate controls progression to the next stage. The name comes from the Norse Norns: Urd, Verdandi, and Skuld.
|
|
4
|
+
|
|
5
|
+
**v0.0.2** site: [yanchao1999.github.io/Norns](https://yanchao1999.github.io/Norns/). First-use backlog: [ROADMAP.md](ROADMAP.md).
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
- Local Control Room (`norns init` / `norns run`) — UI in the browser by default, optional Electron window, config under `~/.norns`
|
|
9
|
+
- Visual state machine editor for board stages, order, parallel tracks, and human-gate vs auto-advance
|
|
10
|
+
- FastAPI + async SQLAlchemy backend with PostgreSQL-ready configuration
|
|
11
|
+
- ARQ/Redis queue for isolated stage execution (optional; local IDE runs stages in-process)
|
|
12
|
+
- React + TypeScript Kanban UI with approval-aware movement
|
|
13
|
+
- Stage agents may recommend **approve** or **reject**; a human still confirms at the gate
|
|
14
|
+
- Encrypted connector secrets at rest with Fernet
|
|
15
|
+
- OpenAI-compatible stage agents with explicit per-stage tool allowlists
|
|
16
|
+
- PlantUML-first documentation and handoff previews
|
|
17
|
+
|
|
18
|
+
## Flow Diagram
|
|
19
|
+
```plantuml
|
|
20
|
+
@startuml
|
|
21
|
+
skinparam monochrome true
|
|
22
|
+
actor Human
|
|
23
|
+
rectangle Board {
|
|
24
|
+
rectangle "Stage 1\n(Urd agent)" as S1
|
|
25
|
+
rectangle "Stage 2\n(Verdandi agent)" as S2
|
|
26
|
+
rectangle "Stage 3\n(Skuld agent)" as S3
|
|
27
|
+
}
|
|
28
|
+
Human --> S1 : create / approve
|
|
29
|
+
S1 --> Human : handoff + recommend approve/reject
|
|
30
|
+
Human --> S2 : confirm approve or reject
|
|
31
|
+
S2 --> Human : handoff + recommend approve/reject
|
|
32
|
+
Human --> S3 : confirm approve or reject
|
|
33
|
+
@enduml
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Runtime Sequence
|
|
37
|
+
```plantuml
|
|
38
|
+
@startuml
|
|
39
|
+
skinparam monochrome true
|
|
40
|
+
actor Human
|
|
41
|
+
participant Frontend
|
|
42
|
+
participant Backend
|
|
43
|
+
participant Redis
|
|
44
|
+
participant Worker
|
|
45
|
+
participant Agent
|
|
46
|
+
|
|
47
|
+
Human -> Frontend : Approve card
|
|
48
|
+
Frontend -> Backend : POST /api/cards/{id}/approve
|
|
49
|
+
Backend -> Redis : enqueue next stage
|
|
50
|
+
Redis -> Worker : run_stage_task
|
|
51
|
+
Worker -> Agent : run_stage(card, stage, run)
|
|
52
|
+
Agent -> Backend : persist AgentRun + handoff (recommendation is advisory)
|
|
53
|
+
Backend -> Frontend : waiting_approval state
|
|
54
|
+
Human -> Frontend : confirm approve or reject
|
|
55
|
+
@enduml
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Architecture Overview
|
|
59
|
+
- **Boards / Stages** define the workflow and per-column agent configuration. Edit stages and **transition lines** in the Control Room **Machine** view. Two default lines from one stage **split** the card into parallel tracks (for example unit tests and software). Lines from two or more stages into one stage **join** those tracks when every track has finished.
|
|
60
|
+
- **Cards** carry the work item body and current stage pointer.
|
|
61
|
+
- **Agent runs** are isolated; no chat memory is shared between stages.
|
|
62
|
+
- **Handoffs** from stage _N_ are the only structured context for stage _N+1_. On a human gate, the agent may set `recommendation` to `approve` or `reject`; the card still waits until a person confirms. Draw an If on `recommendation` if that suggestion should choose the next stage after confirmation.
|
|
63
|
+
- **Connectors** are Python-library backed only (PyGithub, jira, polarion); raw credentials are never exposed to agents.
|
|
64
|
+
|
|
65
|
+
## Install (pip / uv)
|
|
66
|
+
|
|
67
|
+
Python 3.12+. `norns run` opens the Control Room in your browser. Electron is optional.
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
# From this checkout (needs Node.js 20+ or Docker once, to compile the UI into the package):
|
|
71
|
+
uv tool install .
|
|
72
|
+
# or
|
|
73
|
+
python3 -m pip install .
|
|
74
|
+
|
|
75
|
+
norns init # prints an admin password; also stored in ~/.norns/config.toml
|
|
76
|
+
norns run # http://127.0.0.1:8765
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
After this is published (see [PUBLISH.md](PUBLISH.md)):
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
uv tool install norns-ide
|
|
83
|
+
# or
|
|
84
|
+
python3 -m pip install norns-ide
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
The PyPI name is `norns-ide` because [`norns`](https://pypi.org/project/norns/) is already taken. The command is still `norns`.
|
|
88
|
+
|
|
89
|
+
From GitHub after this branch is merged:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
uv tool install git+https://github.com/YanChao1999/Norns.git
|
|
93
|
+
# or
|
|
94
|
+
python3 -m pip install git+https://github.com/YanChao1999/Norns.git
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
`norns run --no-window` starts the server without opening a browser (used by CI). Data lives under `~/.norns` unless you pass `--home` or set `NORNS_HOME`. Use `norns init --force` to replace config and delete `norns.db` (no schema back-compat before a published 0.0.1). Set `openai.api_key` in `~/.norns/config.toml` when you want a real model instead of a placeholder handoff.
|
|
98
|
+
|
|
99
|
+
### Optional desktop window
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
npm install --prefix norns/electron # from a git checkout
|
|
103
|
+
norns run
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Developer checkout
|
|
107
|
+
|
|
108
|
+
`uv run` is an editable install, so it does not run the wheel build that bakes the UI in. `norns run` compiles `frontend/` on first start with npm if it is on PATH (or `./.tools/node`), otherwise with Docker using the same `node:20` image as `docker compose up`. If docker compose left `frontend/node_modules` root-owned, the build uses `~/.cache/norns/ui-build` instead. Rebuild after UI source changes with `./scripts/stage-ui.sh` (same npm-or-Docker path; it always rebuilds, unlike `norns run`).
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
uv sync
|
|
112
|
+
uv run norns init
|
|
113
|
+
uv run norns run
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Docker compose (optional)
|
|
117
|
+
Use this when you want PostgreSQL, Redis, and a browser-based Vite dev server instead of the desktop IDE.
|
|
118
|
+
|
|
119
|
+
1. Copy `.env.example` to `.env` and set real secrets.
|
|
120
|
+
2. Generate unique secrets (do not keep the example values):
|
|
121
|
+
```bash
|
|
122
|
+
python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
|
|
123
|
+
python -c "import secrets; print(secrets.token_urlsafe(32))"
|
|
124
|
+
```
|
|
125
|
+
Put them in `ENCRYPTION_KEY` and `SECRET_KEY`. The API and worker must share the same encryption key.
|
|
126
|
+
3. Start the stack:
|
|
127
|
+
```bash
|
|
128
|
+
docker compose up
|
|
129
|
+
```
|
|
130
|
+
4. Open:
|
|
131
|
+
- Frontend: `http://localhost:5173`
|
|
132
|
+
- Backend API: `http://localhost:8000/docs`
|
|
133
|
+
5. Sign in with the `ADMIN_USERNAME` / `ADMIN_PASSWORD` from your `.env` (defaults are `admin` / `admin` only when `NORNS_ENV=local`). For a shared host set `NORNS_ENV=production`, a unique `ADMIN_PASSWORD`, and `SESSION_COOKIE_SECURE=true`.
|
|
134
|
+
6. Create a board, add a card, open it, and click **Run this stage**. Cards only move after approval unless a stage has auto-advance enabled.
|
|
135
|
+
|
|
136
|
+
`docker compose` builds a Python image once. Rebuild after dependency changes: `docker compose build`.
|
|
137
|
+
|
|
138
|
+
## Development Setup
|
|
139
|
+
```bash
|
|
140
|
+
pip install -e ".[dev]"
|
|
141
|
+
python -m pytest backend/tests/ -q
|
|
142
|
+
bash scripts/lint.sh
|
|
143
|
+
cd frontend && npm install && npm run dev
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Pull requests to `main` must pass the **CI** GitHub Actions check (`backend` tests, `frontend` typecheck/build, `lint` static analysis and format, `package` wheel/sdist plus `norns` CLI). Direct pushes to `main` are not blocked, but merges are.
|
|
147
|
+
|
|
148
|
+
## Notes
|
|
149
|
+
- Local IDE mode uses SQLite and `QUEUE_BACKEND=inline` (no Redis). Docker/production can keep Redis via `QUEUE_BACKEND=redis`.
|
|
150
|
+
- Schema is created with SQLAlchemy `create_all`. There is no upgrade migration until 0.0.1 is published; `norns init --force` deletes the local database.
|
|
151
|
+
- Use PostgreSQL in normal deployments via `DATABASE_URL`.
|
|
152
|
+
- Redis backs ARQ worker execution.
|
|
153
|
+
- PlantUML/Kroki rendering is opt-in via `PLANTUML_URL` / `KROKI_URL` (unset means no public egress).
|
|
154
|
+
- An empty per-stage tool allowlist grants **no** tools. Enable **norns** (create cards, edit stages/prompts, edit the state machine, inspect the git workspace), **github**, **jira**, or **polarion** on the column **Agent** dialog. Cursor stages receive those as MCP servers; OpenAI/DeepSeek stages use the same plugins as chat tools.
|
|
155
|
+
- **Workspace (git repo):** each board has a checkout path and/or `https://github.com/org/repo`. Stage agents inherit the board repo; a column **Agent** can override with its own path/URL. Cursor stages then run in that checkout (local agent) instead of a throwaway `/tmp` directory; GitHub tools default to that repo. If neither board nor agent is set, Norns uses the git root of the process working directory.
|
|
156
|
+
- Extra MCP servers: add an **MCP** connector in Settings (stdio command or HTTP URL), then enable it under Agent → Tools. Third-party Python plugins register the `norns.plugins` entry point.
|
|
157
|
+
- `norns mcp --plugins norns,github,jira` runs the plugin MCP server on stdin/stdout (Cursor attaches this automatically when those tools are enabled).
|
|
158
|
+
- `PUT /api/cards/{id}` updates title/body only; new cards always start on the first stage.
|
|
159
|
+
- Change `SECRET_KEY` and `ADMIN_PASSWORD` before any shared deployment. Sessions expire after 8 hours.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Norns backend package."""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Norns backend application package."""
|