noah-code 0.2.2__tar.gz → 0.2.4__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 (156) hide show
  1. {noah_code-0.2.2 → noah_code-0.2.4}/.github/workflows/ci.yml +54 -0
  2. {noah_code-0.2.2 → noah_code-0.2.4}/.github/workflows/release.yml +3 -0
  3. {noah_code-0.2.2 → noah_code-0.2.4}/.gitignore +3 -0
  4. noah_code-0.2.4/.pre-commit-config.yaml +21 -0
  5. {noah_code-0.2.2 → noah_code-0.2.4}/PKG-INFO +60 -26
  6. {noah_code-0.2.2 → noah_code-0.2.4}/README.md +48 -15
  7. noah_code-0.2.4/docs/assets/noah-logo.svg +15 -0
  8. {noah_code-0.2.2 → noah_code-0.2.4}/docs/configuration.md +108 -24
  9. {noah_code-0.2.2 → noah_code-0.2.4}/docs/development.md +28 -12
  10. {noah_code-0.2.2 → noah_code-0.2.4}/docs/extensions.md +51 -9
  11. noah_code-0.2.4/docs/interactive-reference.md +235 -0
  12. {noah_code-0.2.2 → noah_code-0.2.4}/docs/releases/v0.1.1.md +1 -1
  13. {noah_code-0.2.2 → noah_code-0.2.4}/docs/releases/v0.2.0.md +3 -3
  14. noah_code-0.2.4/docs/releases/v0.2.3.md +50 -0
  15. noah_code-0.2.4/docs/releases/v0.2.4.md +100 -0
  16. noah_code-0.2.4/docs/reliability.md +148 -0
  17. noah_code-0.2.4/docs/security.md +67 -0
  18. {noah_code-0.2.2 → noah_code-0.2.4}/pyproject.toml +39 -14
  19. noah_code-0.2.4/src/noah_code/__init__.py +3 -0
  20. {noah_code-0.2.2 → noah_code-0.2.4}/src/noah_code/__main__.py +1 -1
  21. {noah_code-0.2.2 → noah_code-0.2.4}/src/noah_code/agent.py +373 -56
  22. {noah_code-0.2.2 → noah_code-0.2.4}/src/noah_code/agents.py +41 -17
  23. {noah_code-0.2.2 → noah_code-0.2.4}/src/noah_code/approvals.py +67 -7
  24. noah_code-0.2.4/src/noah_code/budget.py +319 -0
  25. noah_code-0.2.4/src/noah_code/checkpoints.py +310 -0
  26. {noah_code-0.2.2 → noah_code-0.2.4}/src/noah_code/cli.py +355 -45
  27. {noah_code-0.2.2 → noah_code-0.2.4}/src/noah_code/commands.py +116 -53
  28. {noah_code-0.2.2 → noah_code-0.2.4}/src/noah_code/composer.py +32 -9
  29. {noah_code-0.2.2 → noah_code-0.2.4}/src/noah_code/config.py +190 -23
  30. noah_code-0.2.4/src/noah_code/custom_commands.py +222 -0
  31. {noah_code-0.2.2 → noah_code-0.2.4}/src/noah_code/event_bridge.py +40 -2
  32. {noah_code-0.2.2 → noah_code-0.2.4}/src/noah_code/events.py +6 -1
  33. noah_code-0.2.4/src/noah_code/github.py +221 -0
  34. noah_code-0.2.4/src/noah_code/hooks.py +136 -0
  35. noah_code-0.2.4/src/noah_code/host.py +2474 -0
  36. noah_code-0.2.4/src/noah_code/llm.py +289 -0
  37. noah_code-0.2.4/src/noah_code/llm_replies.py +96 -0
  38. {noah_code-0.2.2 → noah_code-0.2.4}/src/noah_code/macos_sandbox.py +35 -4
  39. {noah_code-0.2.2 → noah_code-0.2.4}/src/noah_code/mcp_setup.py +45 -2
  40. noah_code-0.2.4/src/noah_code/nooa_compat.py +75 -0
  41. noah_code-0.2.4/src/noah_code/permissions.py +1141 -0
  42. noah_code-0.2.4/src/noah_code/project_notes.py +115 -0
  43. noah_code-0.2.4/src/noah_code/redaction.py +75 -0
  44. noah_code-0.2.4/src/noah_code/runtime_state.py +832 -0
  45. noah_code-0.2.4/src/noah_code/secure_files.py +265 -0
  46. {noah_code-0.2.2 → noah_code-0.2.4}/src/noah_code/sessions.py +147 -10
  47. {noah_code-0.2.2 → noah_code-0.2.4}/src/noah_code/skills_setup.py +2 -1
  48. {noah_code-0.2.2 → noah_code-0.2.4}/src/noah_code/snapshots.py +4 -2
  49. noah_code-0.2.4/src/noah_code/steer.py +107 -0
  50. noah_code-0.2.4/src/noah_code/summarization.py +120 -0
  51. {noah_code-0.2.2 → noah_code-0.2.4}/src/noah_code/tool_output.py +42 -8
  52. noah_code-0.2.4/src/noah_code/tools/__init__.py +1 -0
  53. noah_code-0.2.4/src/noah_code/tools/diff_tools.py +254 -0
  54. {noah_code-0.2.2 → noah_code-0.2.4}/src/noah_code/tools/git_tools.py +1 -1
  55. noah_code-0.2.4/src/noah_code/tools/github_tools.py +137 -0
  56. {noah_code-0.2.2 → noah_code-0.2.4}/src/noah_code/tools/lsp_tools.py +87 -43
  57. noah_code-0.2.4/src/noah_code/tools/memory_tools.py +62 -0
  58. noah_code-0.2.4/src/noah_code/tools/plan_tools.py +90 -0
  59. {noah_code-0.2.2 → noah_code-0.2.4}/src/noah_code/tools/process_tools.py +230 -13
  60. {noah_code-0.2.2 → noah_code-0.2.4}/src/noah_code/tools/question_tools.py +49 -2
  61. noah_code-0.2.4/src/noah_code/tools/task_tools.py +260 -0
  62. noah_code-0.2.4/src/noah_code/tools/web_tools.py +384 -0
  63. noah_code-0.2.4/src/noah_code/tools/workspace_tools.py +1327 -0
  64. noah_code-0.2.4/src/noah_code/ui/__init__.py +1 -0
  65. {noah_code-0.2.2 → noah_code-0.2.4}/src/noah_code/ui/textual.css +12 -6
  66. {noah_code-0.2.2 → noah_code-0.2.4}/src/noah_code/ui/textual_app.py +1081 -113
  67. {noah_code-0.2.2 → noah_code-0.2.4}/src/noah_code/usage.py +69 -0
  68. {noah_code-0.2.2 → noah_code-0.2.4}/src/noah_code/workspace.py +3 -0
  69. noah_code-0.2.4/src/noah_code/worktree.py +242 -0
  70. {noah_code-0.2.2 → noah_code-0.2.4}/tests/conftest.py +12 -0
  71. noah_code-0.2.4/tests/test_agent_security.py +265 -0
  72. noah_code-0.2.4/tests/test_agents.py +130 -0
  73. noah_code-0.2.4/tests/test_approvals.py +54 -0
  74. noah_code-0.2.4/tests/test_budget.py +241 -0
  75. noah_code-0.2.4/tests/test_cache_context.py +262 -0
  76. noah_code-0.2.4/tests/test_checkpoints.py +278 -0
  77. noah_code-0.2.4/tests/test_cli.py +380 -0
  78. noah_code-0.2.4/tests/test_compaction_eviction.py +136 -0
  79. {noah_code-0.2.2 → noah_code-0.2.4}/tests/test_composer.py +64 -0
  80. {noah_code-0.2.2 → noah_code-0.2.4}/tests/test_config.py +118 -3
  81. noah_code-0.2.4/tests/test_console_ui.py +140 -0
  82. noah_code-0.2.4/tests/test_custom_commands.py +150 -0
  83. noah_code-0.2.4/tests/test_diff_tools.py +108 -0
  84. {noah_code-0.2.2 → noah_code-0.2.4}/tests/test_efficiency.py +11 -8
  85. {noah_code-0.2.2 → noah_code-0.2.4}/tests/test_event_bridge.py +46 -1
  86. noah_code-0.2.4/tests/test_github.py +239 -0
  87. noah_code-0.2.4/tests/test_hooks.py +200 -0
  88. noah_code-0.2.4/tests/test_host.py +1718 -0
  89. noah_code-0.2.4/tests/test_llm.py +178 -0
  90. noah_code-0.2.4/tests/test_llm_replies.py +128 -0
  91. {noah_code-0.2.2 → noah_code-0.2.4}/tests/test_lsp_tools.py +2 -2
  92. {noah_code-0.2.2 → noah_code-0.2.4}/tests/test_mcp_setup.py +48 -0
  93. noah_code-0.2.4/tests/test_permissions.py +702 -0
  94. noah_code-0.2.4/tests/test_plan_memory_tools.py +82 -0
  95. noah_code-0.2.4/tests/test_process_tools.py +195 -0
  96. noah_code-0.2.4/tests/test_project_notes.py +134 -0
  97. {noah_code-0.2.2 → noah_code-0.2.4}/tests/test_question_tools.py +1 -1
  98. noah_code-0.2.4/tests/test_redaction.py +60 -0
  99. {noah_code-0.2.2 → noah_code-0.2.4}/tests/test_run_exit.py +10 -0
  100. noah_code-0.2.4/tests/test_runtime_state.py +139 -0
  101. {noah_code-0.2.2 → noah_code-0.2.4}/tests/test_sessions.py +95 -0
  102. noah_code-0.2.4/tests/test_steer.py +74 -0
  103. noah_code-0.2.4/tests/test_task_tools.py +304 -0
  104. {noah_code-0.2.2 → noah_code-0.2.4}/tests/test_textual_tui.py +987 -19
  105. {noah_code-0.2.2 → noah_code-0.2.4}/tests/test_updates.py +4 -4
  106. {noah_code-0.2.2 → noah_code-0.2.4}/tests/test_wave1_e2e.py +8 -3
  107. noah_code-0.2.4/tests/test_web_tools.py +166 -0
  108. noah_code-0.2.4/tests/test_workspace_tools.py +978 -0
  109. noah_code-0.2.4/tests/test_worktree.py +92 -0
  110. {noah_code-0.2.2 → noah_code-0.2.4}/uv.lock +315 -17
  111. noah_code-0.2.2/docs/interactive-reference.md +0 -156
  112. noah_code-0.2.2/docs/security.md +0 -36
  113. noah_code-0.2.2/src/noah_code/__init__.py +0 -3
  114. noah_code-0.2.2/src/noah_code/benchmark.py +0 -140
  115. noah_code-0.2.2/src/noah_code/custom_commands.py +0 -103
  116. noah_code-0.2.2/src/noah_code/host.py +0 -1366
  117. noah_code-0.2.2/src/noah_code/llm.py +0 -61
  118. noah_code-0.2.2/src/noah_code/permissions.py +0 -483
  119. noah_code-0.2.2/src/noah_code/summarization.py +0 -34
  120. noah_code-0.2.2/src/noah_code/tools/__init__.py +0 -17
  121. noah_code-0.2.2/src/noah_code/tools/task_tools.py +0 -126
  122. noah_code-0.2.2/src/noah_code/tools/web_tools.py +0 -176
  123. noah_code-0.2.2/src/noah_code/tools/workspace_tools.py +0 -772
  124. noah_code-0.2.2/src/noah_code/ui/__init__.py +0 -6
  125. noah_code-0.2.2/tests/test_agent_security.py +0 -99
  126. noah_code-0.2.2/tests/test_agents.py +0 -52
  127. noah_code-0.2.2/tests/test_approvals.py +0 -29
  128. noah_code-0.2.2/tests/test_cli.py +0 -155
  129. noah_code-0.2.2/tests/test_custom_commands.py +0 -50
  130. noah_code-0.2.2/tests/test_host.py +0 -610
  131. noah_code-0.2.2/tests/test_llm.py +0 -75
  132. noah_code-0.2.2/tests/test_permissions.py +0 -152
  133. noah_code-0.2.2/tests/test_process_tools.py +0 -83
  134. noah_code-0.2.2/tests/test_task_tools.py +0 -77
  135. noah_code-0.2.2/tests/test_web_tools.py +0 -86
  136. noah_code-0.2.2/tests/test_workspace_tools.py +0 -403
  137. {noah_code-0.2.2 → noah_code-0.2.4}/docs/assets/noah-in-action.svg +0 -0
  138. {noah_code-0.2.2 → noah_code-0.2.4}/docs/releases/v0.1.0.md +0 -0
  139. {noah_code-0.2.2 → noah_code-0.2.4}/docs/releases/v0.2.1.md +0 -0
  140. {noah_code-0.2.2 → noah_code-0.2.4}/docs/releases/v0.2.2.md +0 -0
  141. {noah_code-0.2.2 → noah_code-0.2.4}/install.sh +0 -0
  142. {noah_code-0.2.2 → noah_code-0.2.4}/src/noah_code/credentials.py +0 -0
  143. {noah_code-0.2.2 → noah_code-0.2.4}/src/noah_code/providers.py +0 -0
  144. {noah_code-0.2.2 → noah_code-0.2.4}/src/noah_code/themes.py +0 -0
  145. {noah_code-0.2.2 → noah_code-0.2.4}/src/noah_code/tools/media_tools.py +0 -0
  146. {noah_code-0.2.2 → noah_code-0.2.4}/src/noah_code/ui/console.py +0 -0
  147. {noah_code-0.2.2 → noah_code-0.2.4}/src/noah_code/ui/protocol.py +0 -0
  148. {noah_code-0.2.2 → noah_code-0.2.4}/src/noah_code/updates.py +0 -0
  149. {noah_code-0.2.2 → noah_code-0.2.4}/tests/test_credentials.py +0 -0
  150. {noah_code-0.2.2 → noah_code-0.2.4}/tests/test_event_bridge_and_shell.py +0 -0
  151. {noah_code-0.2.2 → noah_code-0.2.4}/tests/test_git_tools.py +0 -0
  152. {noah_code-0.2.2 → noah_code-0.2.4}/tests/test_installer.py +0 -0
  153. {noah_code-0.2.2 → noah_code-0.2.4}/tests/test_providers.py +0 -0
  154. {noah_code-0.2.2 → noah_code-0.2.4}/tests/test_skills_setup.py +0 -0
  155. {noah_code-0.2.2 → noah_code-0.2.4}/tests/test_snapshots.py +0 -0
  156. {noah_code-0.2.2 → noah_code-0.2.4}/tests/test_summarization.py +0 -0
@@ -5,14 +5,22 @@ on:
5
5
  push:
6
6
  branches:
7
7
  - main
8
+ # Also run on version tags so the exact released ref is checked.
9
+ tags:
10
+ - "v*"
8
11
 
9
12
  permissions:
10
13
  contents: read
11
14
 
15
+ concurrency:
16
+ group: ${{ github.workflow }}-${{ github.ref }}
17
+ cancel-in-progress: true
18
+
12
19
  jobs:
13
20
  quality:
14
21
  name: Python ${{ matrix.python-version }} quality
15
22
  runs-on: ubuntu-latest
23
+ timeout-minutes: 30
16
24
  strategy:
17
25
  fail-fast: false
18
26
  matrix:
@@ -46,15 +54,51 @@ jobs:
46
54
  - name: Lint
47
55
  run: uv run ruff check src tests
48
56
 
57
+ - name: Type check
58
+ run: uv run mypy src/noah_code
59
+
60
+ - name: Test with coverage (3.12 only)
61
+ if: matrix.python-version == '3.12'
62
+ run: |
63
+ uv run pytest tests --cov=noah_code --cov-report=term-missing \
64
+ --cov-fail-under=70 -W error::pytest.PytestUnraisableExceptionWarning
65
+
49
66
  - name: Test
67
+ if: matrix.python-version != '3.12'
50
68
  run: uv run pytest tests -W error::pytest.PytestUnraisableExceptionWarning
51
69
 
52
70
  - name: Build distributions
53
71
  run: uv build --out-dir build-dist
54
72
 
73
+ integration:
74
+ name: Integration tests (network)
75
+ runs-on: ubuntu-latest
76
+ timeout-minutes: 15
77
+ # Integration-marked tests hit the live network and can flake for reasons
78
+ # unrelated to the code; keep the signal visible without blocking PRs.
79
+ continue-on-error: true
80
+ steps:
81
+ - name: Check out source
82
+ uses: actions/checkout@v7
83
+ with:
84
+ persist-credentials: false
85
+
86
+ - name: Install uv and Python
87
+ uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
88
+ with:
89
+ enable-cache: true
90
+ python-version: "3.12"
91
+
92
+ - name: Install locked dependencies
93
+ run: uv sync --locked --all-extras --dev
94
+
95
+ - name: Run integration tests
96
+ run: uv run pytest -m integration -q
97
+
55
98
  platform:
56
99
  name: ${{ matrix.name }} smoke test
57
100
  runs-on: ${{ matrix.runner }}
101
+ timeout-minutes: 40
58
102
  strategy:
59
103
  fail-fast: false
60
104
  matrix:
@@ -93,8 +137,18 @@ jobs:
93
137
  rg --version
94
138
 
95
139
  - name: Run tests
140
+ if: runner.os != 'macOS'
96
141
  run: uv run pytest tests -W error::pytest.PytestUnraisableExceptionWarning
97
142
 
143
+ - name: Run tests with coverage (macOS)
144
+ if: runner.os == 'macOS'
145
+ # Collect coverage on macOS too so mac-only paths are measured, but
146
+ # without a fail-under gate: per-platform coverage totals differ,
147
+ # and the 70% gate stays on the ubuntu quality job.
148
+ run: |
149
+ uv run pytest tests --cov=noah_code --cov-report=term \
150
+ -W error::pytest.PytestUnraisableExceptionWarning
151
+
98
152
  - name: Verify command entry point
99
153
  run: uv run noah --version
100
154
 
@@ -53,6 +53,9 @@ jobs:
53
53
  sudo apt-get install -y ripgrep
54
54
  rg --version
55
55
 
56
+ - name: Type check
57
+ run: uv run mypy src/noah_code
58
+
56
59
  - name: Run release checks
57
60
  run: |
58
61
  uv run ruff check src tests
@@ -11,3 +11,6 @@ build/
11
11
  htmlcov/
12
12
  traces/
13
13
  .DS_Store
14
+ docs/superpowers/plans/
15
+ docs/superpowers/specs/
16
+ .noah-code/
@@ -0,0 +1,21 @@
1
+ # Pre-commit hooks mirroring the CI lint gate.
2
+ # Install with: uv run pre-commit install (or `pre-commit install` once installed)
3
+ repos:
4
+ - repo: https://github.com/astral-sh/ruff-pre-commit
5
+ rev: v0.16.1
6
+ hooks:
7
+ - id: ruff-check
8
+ args: [--fix]
9
+
10
+ - repo: local
11
+ hooks:
12
+ - id: mypy
13
+ name: mypy
14
+ entry: uv run mypy
15
+ language: system
16
+ pass_filenames: false
17
+ - id: uv-lock-check
18
+ name: uv lock --check
19
+ entry: uv lock --check
20
+ language: system
21
+ pass_filenames: false
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: noah-code
3
- Version: 0.2.2
3
+ Version: 0.2.4
4
4
  Summary: Noah Code terminal coding agent, built on NVIDIA OO Agents (NOOA)
5
5
  Project-URL: Homepage, https://github.com/skundu42/noah-code
6
6
  Project-URL: Documentation, https://github.com/skundu42/noah-code#readme
@@ -19,33 +19,36 @@ Classifier: Programming Language :: Python :: 3.12
19
19
  Classifier: Programming Language :: Python :: 3.13
20
20
  Classifier: Topic :: Software Development
21
21
  Requires-Python: <3.14,>=3.12
22
- Requires-Dist: click>=8.1.0
23
- Requires-Dist: litellm<1.92.0,>=1.84.0
22
+ Requires-Dist: click<9.0.0,>=8.1.0
23
+ Requires-Dist: litellm<1.99.0,>=1.96.0
24
24
  Requires-Dist: nooa-cli==0.0.9
25
25
  Requires-Dist: nooa==0.0.9
26
- Requires-Dist: packaging>=24.0
27
- Requires-Dist: pydantic>=2.5.0
28
- Requires-Dist: pyyaml>=6.0.0
29
- Requires-Dist: rich>=13.0.0
30
- Requires-Dist: textual>=1.0.0
31
- Requires-Dist: tomli>=2.0.0; python_version < '3.11'
26
+ Requires-Dist: packaging<27.0,>=24.0
27
+ Requires-Dist: pydantic<3.0.0,>=2.5.0
28
+ Requires-Dist: pyyaml<7.0.0,>=6.0.0
29
+ Requires-Dist: rich<16.0.0,>=13.0.0
30
+ Requires-Dist: textual<9.0.0,>=1.0.0
32
31
  Provides-Extra: dev
32
+ Requires-Dist: mypy>=2.3.1; extra == 'dev'
33
33
  Requires-Dist: pytest-asyncio>=0.24.0; extra == 'dev'
34
+ Requires-Dist: pytest-cov>=5.0.0; extra == 'dev'
35
+ Requires-Dist: pytest-socket>=0.7.0; extra == 'dev'
34
36
  Requires-Dist: pytest>=8.0.0; extra == 'dev'
35
37
  Requires-Dist: ruff>=0.8.0; extra == 'dev'
38
+ Requires-Dist: types-pyyaml>=6.0.12.20260815; extra == 'dev'
36
39
  Provides-Extra: mcp
37
40
  Requires-Dist: nooa[mcp]==0.0.9; extra == 'mcp'
38
41
  Provides-Extra: tracing
39
42
  Requires-Dist: nooa[tracing]==0.0.9; extra == 'tracing'
40
- Provides-Extra: tui
41
- Requires-Dist: textual>=1.0.0; extra == 'tui'
42
43
  Description-Content-Type: text/markdown
43
44
 
44
45
  <div align="center">
45
46
 
47
+ <img src="https://raw.githubusercontent.com/skundu42/noah-code/main/docs/assets/noah-logo.svg" alt="Noah Code terminal wordmark" width="760">
48
+
46
49
  # Noah Code
47
50
 
48
- **A terminal coding harness built on NVIDIA's NOAA framework**
51
+ **A durable, repository-aware terminal coding agent built on NVIDIA's NOOA framework**
49
52
 
50
53
  [![PyPI](https://img.shields.io/pypi/v/noah-code.svg)](https://pypi.org/project/noah-code/)
51
54
  [![CI](https://github.com/skundu42/noah-code/actions/workflows/ci.yml/badge.svg)](https://github.com/skundu42/noah-code/actions/workflows/ci.yml)
@@ -53,13 +56,13 @@ Description-Content-Type: text/markdown
53
56
 
54
57
  </div>
55
58
 
56
- ![Noah Code handling a repository change in its terminal interface](docs/assets/noah-in-action.svg)
59
+ ![Noah Code handling a repository change in its terminal interface](https://raw.githubusercontent.com/skundu42/noah-code/main/docs/assets/noah-in-action.svg)
57
60
 
58
61
  <p align="center"><sub>The real Textual interface, captured from a deterministic Noah Code session.</sub></p>
59
62
 
60
- Noah keeps the conversation central while the context rail tracks the workspace, model, session,
61
- token usage, and current plan. Tool execution stays visible, completed work compacts into readable
62
- records, and every session remains scoped to its repository.
63
+ Noah keeps the conversation central while the context rail tracks live work, Git changes, session,
64
+ model usage, and the current plan. Tool execution stays visible, completed work compacts into
65
+ readable records, and every session remains scoped to its repository.
63
66
 
64
67
  Built on the [NVIDIA OO Agents (NOOA)](https://github.com/NVIDIA-NeMo/labs-OO-Agents) runtime.
65
68
 
@@ -92,14 +95,20 @@ configuration or session history.
92
95
  preimages, concurrent-change detection, immediate diagnostics, and rollback.
93
96
  - **Visible execution.** Stream bounded shell output while commands run, keep servers and watchers
94
97
  as managed background jobs, and revisit activity details with `F2`.
95
- - **Recoverable work.** Review staged and unstaged changes in `/diff`, then undo or redo journaled
96
- file edits even after restarting Noah.
97
- - **Persistent sessions.** Resume repository-scoped conversations, todos, model choices, and
98
- automatically compacted context without losing the full underlying tool results.
98
+ - **Crash-safe work.** Durable file intents roll back interrupted workspace-tool writes, Git
99
+ checkpoints protect shell-driven changes, and interrupted model runs resume after restart.
100
+ - **Persistent sessions.** Resume repository-scoped conversations, steering, todos, model choices,
101
+ budgets, background-job logs, and compacted context without losing full tool results.
102
+ - **Token-efficient by construction.** Lean tool-output bounds with disk-backed recall, condensed
103
+ subagent results, cache-stable request prefixes (volatile status arrives as appended events),
104
+ and pointer-eviction compaction — measured live with `/tokens`.
99
105
  - **Explicit control.** Switch between implementation-focused **build** mode and read-only
100
106
  **plan** mode, with ordered `allow`, `ask`, and `deny` permission rules.
101
107
  - **Extensible workflows.** Add slash commands, opt-in skills, MCP servers, or markdown subagents;
102
108
  attach `@files` and images when the task needs more context.
109
+ - **Long-running by design.** Provider retries and configurable fallback models handle transient
110
+ failures, while workspace leases, bounded artifacts, durable process ownership, and `/health`
111
+ keep unattended sessions observable.
103
112
 
104
113
  Noah follows repository instructions from `AGENTS.md`, `CLAUDE.md`, and
105
114
  `.noah-code/instructions.md`.
@@ -143,7 +152,6 @@ noah --version
143
152
  noah doctor .
144
153
  noah config show .
145
154
  noah update --check
146
- noah benchmark .
147
155
  ```
148
156
 
149
157
  The package also installs `noah-code` and `nc` as equivalent entry points. Because `nc` commonly
@@ -157,6 +165,10 @@ Type `/` to search the full command and configuration reference. The most common
157
165
  | --- | --- |
158
166
  | `Enter` | Send the current prompt or accept a selected suggestion |
159
167
  | `Shift+Enter` | Insert a newline |
168
+ | Drag, then `Cmd+C` / `Ctrl+Shift+C` | Select and copy TUI text |
169
+ | `Ctrl+Shift+C` with no selection | Copy the latest Noah reply |
170
+ | `Ctrl+G` | Open the searchable skills picker |
171
+ | `Ctrl+]` | Return to live transcript output |
160
172
  | `Tab` | Switch between build and plan mode |
161
173
  | `F2` | Open execution activity |
162
174
  | `F3` | Open paginated conversation history |
@@ -164,12 +176,32 @@ Type `/` to search the full command and configuration reference. The most common
164
176
  | `/theme` | Choose Atom One Dark, Noah Ocean, Graphite, or High Contrast |
165
177
  | `/diff` | Review staged and unstaged changes |
166
178
  | `/undo` / `/redo` | Traverse the persistent edit journal |
167
- | `/tokens` | Inspect tokens, cache usage, model wait, and tool output |
179
+ | `/checkpoints` | List rolling Git worktree checkpoints |
180
+ | `/health` | Inspect durable run, job, inbox, event, and artifact state |
181
+ | `/tokens` | Inspect tokens, cache usage, prefix stability, model wait, and tool output |
168
182
  | `/efficiency` | Switch between `fast`, `balanced`, and `deep` budgets |
169
183
 
170
- On wide terminals, the side rail keeps the active workspace, session, model, tokens, update state,
171
- and plan in view. The main pane remains centered on the Noah mark until the first prompt, then
172
- becomes the conversation and execution timeline.
184
+ On wide terminals, the side rail prioritizes the active operation, Git branch and change counts,
185
+ session, model usage, update state, and plan. Git status is refreshed in the background at turn
186
+ boundaries, so the animated working state stays responsive. The main pane remains centered on the
187
+ large Noah wordmark until the first prompt, then becomes the conversation and execution timeline.
188
+
189
+ ## Sessions and crash recovery
190
+
191
+ Start a new session with `noah .`, resume the latest repository session with
192
+ `noah --continue .`, or reopen an exact session with `noah --session SESSION_ID .`. Inside the TUI,
193
+ use `Ctrl+O`, `/sessions`, or `/continue`.
194
+
195
+ Noah stores conversational history and host runtime state separately. If the process stops during
196
+ an active run, the next launch restores pending steering, usage and budget counters, durable job
197
+ logs, and the original request. Incomplete workspace-tool writes are rolled back, verified orphan
198
+ process groups are cleaned up, and non-interactive runs continue automatically. A request that was
199
+ waiting for user input remains paused for the next user message.
200
+
201
+ By default, only one Noah process may own a checkout at a time. Use `/worktree create` when
202
+ independent agents need to work concurrently. See
203
+ [Reliability and long-running sessions](docs/reliability.md) for the recovery model, provider retry
204
+ controls, quotas, and operational limits.
173
205
 
174
206
  ## Models and providers
175
207
 
@@ -198,7 +230,8 @@ noah --model openai/MODEL --reasoning-effort high .
198
230
  ```
199
231
 
200
232
  See the [provider configuration guide](docs/configuration.md#bring-your-own-api-provider) for
201
- gateway-specific setup.
233
+ gateway-specific setup. Provider request deadlines, exponential retry, and ordered fallback models
234
+ are configured under `[reliability.retries]`.
202
235
 
203
236
  ## Updates
204
237
 
@@ -215,6 +248,7 @@ noah update
215
248
 
216
249
  - [Interactive interface and sessions](docs/interactive-reference.md)
217
250
  - [Configuration, modes, permissions, and updates](docs/configuration.md)
251
+ - [Reliability and long-running sessions](docs/reliability.md)
218
252
  - [Generated-code security](docs/security.md)
219
253
  - [Custom commands, skills, MCP, and tracing](docs/extensions.md)
220
254
  - [Development, CI, and releases](docs/development.md)
@@ -1,8 +1,10 @@
1
1
  <div align="center">
2
2
 
3
+ <img src="https://raw.githubusercontent.com/skundu42/noah-code/main/docs/assets/noah-logo.svg" alt="Noah Code terminal wordmark" width="760">
4
+
3
5
  # Noah Code
4
6
 
5
- **A terminal coding harness built on NVIDIA's NOAA framework**
7
+ **A durable, repository-aware terminal coding agent built on NVIDIA's NOOA framework**
6
8
 
7
9
  [![PyPI](https://img.shields.io/pypi/v/noah-code.svg)](https://pypi.org/project/noah-code/)
8
10
  [![CI](https://github.com/skundu42/noah-code/actions/workflows/ci.yml/badge.svg)](https://github.com/skundu42/noah-code/actions/workflows/ci.yml)
@@ -10,13 +12,13 @@
10
12
 
11
13
  </div>
12
14
 
13
- ![Noah Code handling a repository change in its terminal interface](docs/assets/noah-in-action.svg)
15
+ ![Noah Code handling a repository change in its terminal interface](https://raw.githubusercontent.com/skundu42/noah-code/main/docs/assets/noah-in-action.svg)
14
16
 
15
17
  <p align="center"><sub>The real Textual interface, captured from a deterministic Noah Code session.</sub></p>
16
18
 
17
- Noah keeps the conversation central while the context rail tracks the workspace, model, session,
18
- token usage, and current plan. Tool execution stays visible, completed work compacts into readable
19
- records, and every session remains scoped to its repository.
19
+ Noah keeps the conversation central while the context rail tracks live work, Git changes, session,
20
+ model usage, and the current plan. Tool execution stays visible, completed work compacts into
21
+ readable records, and every session remains scoped to its repository.
20
22
 
21
23
  Built on the [NVIDIA OO Agents (NOOA)](https://github.com/NVIDIA-NeMo/labs-OO-Agents) runtime.
22
24
 
@@ -49,14 +51,20 @@ configuration or session history.
49
51
  preimages, concurrent-change detection, immediate diagnostics, and rollback.
50
52
  - **Visible execution.** Stream bounded shell output while commands run, keep servers and watchers
51
53
  as managed background jobs, and revisit activity details with `F2`.
52
- - **Recoverable work.** Review staged and unstaged changes in `/diff`, then undo or redo journaled
53
- file edits even after restarting Noah.
54
- - **Persistent sessions.** Resume repository-scoped conversations, todos, model choices, and
55
- automatically compacted context without losing the full underlying tool results.
54
+ - **Crash-safe work.** Durable file intents roll back interrupted workspace-tool writes, Git
55
+ checkpoints protect shell-driven changes, and interrupted model runs resume after restart.
56
+ - **Persistent sessions.** Resume repository-scoped conversations, steering, todos, model choices,
57
+ budgets, background-job logs, and compacted context without losing full tool results.
58
+ - **Token-efficient by construction.** Lean tool-output bounds with disk-backed recall, condensed
59
+ subagent results, cache-stable request prefixes (volatile status arrives as appended events),
60
+ and pointer-eviction compaction — measured live with `/tokens`.
56
61
  - **Explicit control.** Switch between implementation-focused **build** mode and read-only
57
62
  **plan** mode, with ordered `allow`, `ask`, and `deny` permission rules.
58
63
  - **Extensible workflows.** Add slash commands, opt-in skills, MCP servers, or markdown subagents;
59
64
  attach `@files` and images when the task needs more context.
65
+ - **Long-running by design.** Provider retries and configurable fallback models handle transient
66
+ failures, while workspace leases, bounded artifacts, durable process ownership, and `/health`
67
+ keep unattended sessions observable.
60
68
 
61
69
  Noah follows repository instructions from `AGENTS.md`, `CLAUDE.md`, and
62
70
  `.noah-code/instructions.md`.
@@ -100,7 +108,6 @@ noah --version
100
108
  noah doctor .
101
109
  noah config show .
102
110
  noah update --check
103
- noah benchmark .
104
111
  ```
105
112
 
106
113
  The package also installs `noah-code` and `nc` as equivalent entry points. Because `nc` commonly
@@ -114,6 +121,10 @@ Type `/` to search the full command and configuration reference. The most common
114
121
  | --- | --- |
115
122
  | `Enter` | Send the current prompt or accept a selected suggestion |
116
123
  | `Shift+Enter` | Insert a newline |
124
+ | Drag, then `Cmd+C` / `Ctrl+Shift+C` | Select and copy TUI text |
125
+ | `Ctrl+Shift+C` with no selection | Copy the latest Noah reply |
126
+ | `Ctrl+G` | Open the searchable skills picker |
127
+ | `Ctrl+]` | Return to live transcript output |
117
128
  | `Tab` | Switch between build and plan mode |
118
129
  | `F2` | Open execution activity |
119
130
  | `F3` | Open paginated conversation history |
@@ -121,12 +132,32 @@ Type `/` to search the full command and configuration reference. The most common
121
132
  | `/theme` | Choose Atom One Dark, Noah Ocean, Graphite, or High Contrast |
122
133
  | `/diff` | Review staged and unstaged changes |
123
134
  | `/undo` / `/redo` | Traverse the persistent edit journal |
124
- | `/tokens` | Inspect tokens, cache usage, model wait, and tool output |
135
+ | `/checkpoints` | List rolling Git worktree checkpoints |
136
+ | `/health` | Inspect durable run, job, inbox, event, and artifact state |
137
+ | `/tokens` | Inspect tokens, cache usage, prefix stability, model wait, and tool output |
125
138
  | `/efficiency` | Switch between `fast`, `balanced`, and `deep` budgets |
126
139
 
127
- On wide terminals, the side rail keeps the active workspace, session, model, tokens, update state,
128
- and plan in view. The main pane remains centered on the Noah mark until the first prompt, then
129
- becomes the conversation and execution timeline.
140
+ On wide terminals, the side rail prioritizes the active operation, Git branch and change counts,
141
+ session, model usage, update state, and plan. Git status is refreshed in the background at turn
142
+ boundaries, so the animated working state stays responsive. The main pane remains centered on the
143
+ large Noah wordmark until the first prompt, then becomes the conversation and execution timeline.
144
+
145
+ ## Sessions and crash recovery
146
+
147
+ Start a new session with `noah .`, resume the latest repository session with
148
+ `noah --continue .`, or reopen an exact session with `noah --session SESSION_ID .`. Inside the TUI,
149
+ use `Ctrl+O`, `/sessions`, or `/continue`.
150
+
151
+ Noah stores conversational history and host runtime state separately. If the process stops during
152
+ an active run, the next launch restores pending steering, usage and budget counters, durable job
153
+ logs, and the original request. Incomplete workspace-tool writes are rolled back, verified orphan
154
+ process groups are cleaned up, and non-interactive runs continue automatically. A request that was
155
+ waiting for user input remains paused for the next user message.
156
+
157
+ By default, only one Noah process may own a checkout at a time. Use `/worktree create` when
158
+ independent agents need to work concurrently. See
159
+ [Reliability and long-running sessions](docs/reliability.md) for the recovery model, provider retry
160
+ controls, quotas, and operational limits.
130
161
 
131
162
  ## Models and providers
132
163
 
@@ -155,7 +186,8 @@ noah --model openai/MODEL --reasoning-effort high .
155
186
  ```
156
187
 
157
188
  See the [provider configuration guide](docs/configuration.md#bring-your-own-api-provider) for
158
- gateway-specific setup.
189
+ gateway-specific setup. Provider request deadlines, exponential retry, and ordered fallback models
190
+ are configured under `[reliability.retries]`.
159
191
 
160
192
  ## Updates
161
193
 
@@ -172,6 +204,7 @@ noah update
172
204
 
173
205
  - [Interactive interface and sessions](docs/interactive-reference.md)
174
206
  - [Configuration, modes, permissions, and updates](docs/configuration.md)
207
+ - [Reliability and long-running sessions](docs/reliability.md)
175
208
  - [Generated-code security](docs/security.md)
176
209
  - [Custom commands, skills, MCP, and tracing](docs/extensions.md)
177
210
  - [Development, CI, and releases](docs/development.md)
@@ -0,0 +1,15 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 760 230" role="img" aria-labelledby="title description">
2
+ <title id="title">Noah Code</title>
3
+ <desc id="description">The six-line Noah terminal wordmark used by the Noah Code TUI.</desc>
4
+ <rect width="760" height="230" rx="22" fill="#07151d"/>
5
+ <rect x="1" y="1" width="758" height="228" rx="21" fill="none" stroke="#173746" stroke-width="2"/>
6
+ <g fill="#e6edf3" font-family="ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, monospace" font-size="20" font-weight="700" text-anchor="middle">
7
+ <text x="380" y="43">███╗ ██╗ ██████╗ █████╗ ██╗ ██╗</text>
8
+ <text x="380" y="69">████╗ ██║██╔═══██╗██╔══██╗██║ ██║</text>
9
+ <text x="380" y="95">██╔██╗ ██║██║ ██║███████║███████║</text>
10
+ <text x="380" y="121">██║╚██╗██║██║ ██║██╔══██║██╔══██║</text>
11
+ <text x="380" y="147">██║ ╚████║╚██████╔╝██║ ██║██║ ██║</text>
12
+ <text x="380" y="173">╚═╝ ╚═══╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝</text>
13
+ </g>
14
+ <text x="380" y="207" fill="#5bd1d7" font-family="ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, monospace" font-size="16" font-weight="700" letter-spacing="5" text-anchor="middle">NOAH / C O D E</text>
15
+ </svg>
@@ -112,6 +112,8 @@ max_output_lines = 250
112
112
  max_search_results = 100
113
113
  max_file_results = 500
114
114
  tool_output_retention_hours = 24
115
+ subagent_result_max_chars = 4000
116
+ max_concurrent_subagents = 3
115
117
 
116
118
  [lsp]
117
119
  enabled = true
@@ -122,15 +124,51 @@ max_symbols = 300
122
124
 
123
125
  [processes]
124
126
  max_jobs = 8
125
- max_runtime_seconds = 3600
127
+ max_runtime_seconds = 86400 # 24 hours
126
128
  max_buffer_chars = 64000
127
129
  stop_grace_seconds = 2
128
130
 
131
+ [sampling]
132
+ # Omit values to use provider defaults.
133
+ # temperature = 0.2
134
+ # top_p = 0.95
135
+ # seed = 42
136
+
137
+ [budget]
138
+ # Persistent session-wide limits; omitted values are unlimited.
139
+ # max_tokens = 500000
140
+ # max_cost_usd = 25
141
+ # max_seconds = 28800
142
+
143
+ [checkpoints]
144
+ enabled = true
145
+ max_per_session = 50
146
+ capture_before_mutation = true
147
+
148
+ [reliability]
149
+ auto_resume_interrupted_runs = true
150
+ interaction_timeout_seconds = 86400
151
+ artifact_max_bytes = 2000000000
152
+ session_max_bytes = 5000000000
153
+ max_runtime_events = 20000
154
+ workspace_lease = true
155
+
156
+ [reliability.retries]
157
+ max_attempts = 5
158
+ base_delay_seconds = 0.5
159
+ max_delay_seconds = 20
160
+ jitter_ratio = 0.2
161
+ request_timeout_seconds = 180
162
+ fallback_models = []
163
+
164
+ [hooks]
165
+ # pre_tool = [{ match = "ws_*", command = "./scripts/pre-tool-check" }]
166
+ # post_tool = [{ match = "*", command = "./scripts/audit-tool" }]
167
+
129
168
  [ui]
130
169
  theme = "atom-one-dark" # atom-one-dark, noah-ocean, graphite, or high-contrast
131
170
  frontend = "tui" # "tui" or "console"
132
171
  markdown = true
133
- stream_shell = true
134
172
  show_reasoning = false
135
173
 
136
174
  [summarization]
@@ -141,7 +179,7 @@ target_chars = 2500
141
179
 
142
180
  [tracing]
143
181
  enabled = true
144
- viewer = true
182
+ # By default JSONL traces live inside each session directory.
145
183
  # jsonl_dir = "~/.local/share/noah-code/traces"
146
184
 
147
185
  [updates]
@@ -162,10 +200,12 @@ Supported environment overrides include:
162
200
  - `NOAH_CODE_AUTO_UPDATE`
163
201
 
164
202
  Repository-controlled configuration cannot weaken the host trust boundary. Project config is
165
- ignored for `auto_approve`, `efficiency`, `enabled_skills`, `lsp`, `mcp`, `permission_rules`,
166
- `processes`, `session_dir`, `tracing`, `updates`, and `unsafe_inprocess_code_execution`. Put those
167
- settings in trusted user config, the environment, or an explicit CLI flag. Language-server
168
- overrides are user-only because they launch local executables.
203
+ ignored for `auto_approve`, `budget`, `efficiency`, `enabled_skills`, `hooks`, `lsp`, `mcp`,
204
+ `permission_rules`, `processes`, `reliability`, `session_dir`, `tracing`, `updates`, and
205
+ `unsafe_inprocess_code_execution`. Put those settings in trusted user config, the environment, or
206
+ an explicit CLI flag. Language-server overrides and hooks are user-only because they launch local
207
+ executables; reliability and budget settings are user-only so repository content cannot weaken
208
+ host limits.
169
209
 
170
210
  A user-configured `permission_rules` array replaces the default rule array. Copy forward every
171
211
  default you still want before adding overrides. Hard secret, destructive-shell, and plan-mode
@@ -175,6 +215,10 @@ Inspect the resolved configuration from the CLI or inside an interactive session
175
215
  lists every nested path, while an optional path scopes the output. Values whose names look like
176
216
  credentials are redacted.
177
217
 
218
+ Configuration is validated strictly: unknown keys (including typos such as `theem` under `[ui]`)
219
+ and invalid values or TOML syntax fail every command with a one-line error naming the file and
220
+ field, never a traceback. Fix the named key or remove it; `noah doctor` reports the same error.
221
+
178
222
  ```bash
179
223
  noah config show .
180
224
  ```
@@ -219,40 +263,80 @@ safety rail (default 40), not an efficiency-profile cap. Switch without restarti
219
263
  /efficiency deep
220
264
  ```
221
265
 
222
- Oversized results are not discarded. Noah writes the exact output to a private cache file for the
223
- configured retention period, returns a bounded head/tail preview, and gives the agent an output ID
224
- for focused line-range retrieval. A truncated file preview is never returned as an editable Match
225
- anchor.
266
+ Oversized results are not discarded. During a durable session Noah writes the exact output to a
267
+ private, content-addressed artifact store, returns a bounded head/tail preview, and gives the agent
268
+ an output ID for focused line-range retrieval. Artifacts remain available when the session is
269
+ resumed and count toward `reliability.artifact_max_bytes` and `session_max_bytes`. A truncated file
270
+ preview is never returned as an editable Match anchor. `tool_output_retention_hours` applies to the
271
+ fallback cache used when workspace tools are embedded without a durable session runtime.
226
272
 
227
273
  Set `lightweight_model` to route compaction to a faster or cheaper model. If it is omitted, that
228
274
  route follows live `/model` switches. Compaction starts at 35% of the active main model's context
229
275
  window by default, preserves the six newest events, and writes a coding checkpoint covering the
230
276
  objective, decisions, files, validation, blockers, and next steps.
231
277
 
278
+ ### Budgets, checkpoints, and reliability
279
+
280
+ `[budget]` limits are cumulative across the parent agent, lightweight route, and custom-model
281
+ subagents. Token, cost, and elapsed-time counters survive process restarts. When a configured cap
282
+ is exceeded, the breach is sticky and later model calls fail before contacting the provider.
283
+
284
+ Git checkpoints are enabled by default and use rolling retention: once the configured maximum is
285
+ reached, Noah removes the oldest ref and continues capturing instead of silently stopping.
286
+ `capture_before_mutation` protects shell-driven changes; workspace-tool edits additionally use
287
+ durable pre-images and the persistent `/undo` journal.
288
+
289
+ `[reliability.retries]` controls transient model-call retries and ordered fallback models. Noah does
290
+ not retry authentication, invalid-request, content-policy, or context-window failures through this
291
+ route. `[reliability]` also controls crash-run resumption, interaction timeouts, session and artifact
292
+ quotas, event retention, and the exclusive checkout lease. See
293
+ [Reliability and long-running sessions](reliability.md) for the recovery sequence and operational
294
+ limits.
295
+
232
296
  ## Modes and permissions
233
297
 
234
298
  | Mode | Behavior |
235
299
  |------|----------|
236
- | `build` | Reads are allowed; edits and shell commands follow permission rules and ask by default |
237
- | `plan` | Reads are allowed; file edits and mutating shell commands are denied |
300
+ | `build` | Routine workspace reads, edits, inspection, web research, skills, and delegation proceed automatically; higher-risk operations ask |
301
+ | `plan` | Reads are allowed; file edits and mutating shell commands are denied. `self.plan.write` may pin `.noah-code/plan.md` |
238
302
 
239
- Switch modes with `--mode`, `/mode build`, or `/mode plan`. The active mode is stored with the
240
- session.
303
+ Switch modes with `--mode`, `/mode build`, or `/mode plan`. The agent can propose a switch with
304
+ `self.plan.enter()` / `self.plan.exit_to_build()` after writing a plan; the transition always asks
305
+ for confirmation and is never auto-approved, so in `noah run --auto` it is refused and the session
306
+ stays in plan mode. The active mode is stored with the session.
241
307
 
242
308
  Permission rules are evaluated in order, and the last matching rule wins. The default policy:
243
309
 
244
310
  - Allows ordinary reads.
245
- - Denies likely secrets, including `.env` variants, private keys, `.git` internals, and session
246
- databases. `.env.example` remains readable.
247
- - Asks before workspace edits, shell commands, web fetches, web searches, and subagents.
311
+ - Denies likely secrets, including `.env` variants, private keys, `.git` internals, credential
312
+ stores (`.npmrc`, `.pypirc`, `.netrc`, `.pgpass`, `.kube/config`, `.docker/config.json`,
313
+ `.aws/credentials`), Java/JCEKS key stores, and session databases. `.env.example` remains
314
+ readable. The same denials apply to Git object syntax (`git show HEAD:.env`) and to patch-output
315
+ Git commands with no path scope (`git log -p`, bare `git show`), which ask instead of being
316
+ auto-approved.
317
+ - Allows workspace edits, read-only in-workspace shell inspection, web reads, skills, and
318
+ delegated tasks without interrupting the turn.
319
+ - Asks before arbitrary shell execution, external paths, MCP access, and remote mutations.
248
320
  - Allows the question tool so the agent can pause for a structured choice.
249
- - Denies `git push`, `git clean`, and `git reset --hard`.
250
- - Keeps file tools inside the active workspace and asks before skill or MCP access.
321
+ - Denies `git push`, `git clean`, `git reset --hard`, and mutating `gh pr`
322
+ (`create`, `checkout`, `merge`, `close`, `ready`, `review`). Push and PR
323
+ mutations go through `/pr` / `self.github` instead.
324
+ - Allows listing and viewing pull requests; asks before create, push, checkout,
325
+ or comment.
326
+ - Keeps file tools inside the active workspace and asks before MCP access.
251
327
  - Denies plan-mode mutations regardless of broader allow rules. Plan mode may still run
252
- read-only subagents.
253
-
254
- `--auto` changes ask decisions to allow but never overrides an explicit deny. Compound shell
255
- commands and mutating or unrecognized Git commands cannot be silently auto-approved.
328
+ read-only subagents. Its shell allowlist accepts only literal, unqualified read-only programs;
329
+ test collection and interpreter execution are not treated as read-only because they can load
330
+ repository code.
331
+
332
+ `--auto` changes routine ask decisions to allow but never overrides an explicit deny.
333
+ Elevated-risk commands such as file removal, downloads, and package installation still require
334
+ explicit approval: interactively they ask, and in non-interactive `noah run --auto` they are
335
+ rejected outright instead of being silently approved. Compound shell commands and mutating or
336
+ unrecognized Git commands cannot be silently auto-approved. Interpreters, eval/source commands, and
337
+ indirect execution wrappers, plus arguments hidden behind variable, command, ANSI-C, or brace
338
+ expansion, are denied under `--auto`; run without `--auto` when one of these commands needs
339
+ explicit approval.
256
340
 
257
341
  ## Installation and updates
258
342