nightshift-cli 0.1.0__py3-none-any.whl
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.
- nightshift/__init__.py +8 -0
- nightshift/__main__.py +4 -0
- nightshift/adapters/__init__.py +61 -0
- nightshift/adapters/_process.py +219 -0
- nightshift/adapters/base.py +171 -0
- nightshift/adapters/claude_code.py +372 -0
- nightshift/adapters/codex.py +360 -0
- nightshift/adapters/copilot.py +51 -0
- nightshift/budget.py +150 -0
- nightshift/cli.py +602 -0
- nightshift/config.py +447 -0
- nightshift/cron.py +96 -0
- nightshift/events.py +293 -0
- nightshift/lock.py +197 -0
- nightshift/prompts/code_review.md +24 -0
- nightshift/prompts/dead_links.md +21 -0
- nightshift/prompts/deps_audit.md +23 -0
- nightshift/prompts/docs_drift.md +21 -0
- nightshift/prompts/security_audit.md +23 -0
- nightshift/prompts.py +66 -0
- nightshift/queue.py +92 -0
- nightshift/report.py +394 -0
- nightshift/scheduler.py +425 -0
- nightshift/sessions.py +62 -0
- nightshift/store.py +48 -0
- nightshift_cli-0.1.0.dist-info/METADATA +430 -0
- nightshift_cli-0.1.0.dist-info/RECORD +30 -0
- nightshift_cli-0.1.0.dist-info/WHEEL +4 -0
- nightshift_cli-0.1.0.dist-info/entry_points.txt +2 -0
- nightshift_cli-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nightshift-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Your AI works the night shift — read-only reviews of your projects while you're busy, one digest every morning.
|
|
5
|
+
Project-URL: Homepage, https://github.com/kishormorol/nightshift
|
|
6
|
+
Project-URL: Source, https://github.com/kishormorol/nightshift
|
|
7
|
+
Author: Md. Kishor Morol
|
|
8
|
+
License: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: ai,automation,claude-code,cli,code-review
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Requires-Dist: click>=8.1
|
|
21
|
+
Requires-Dist: pyyaml>=6.0
|
|
22
|
+
Provides-Extra: dev
|
|
23
|
+
Requires-Dist: pytest-cov>=4.1; extra == 'dev'
|
|
24
|
+
Requires-Dist: pytest>=7.4; extra == 'dev'
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
<div align="center">
|
|
28
|
+
|
|
29
|
+
# nightshift
|
|
30
|
+
|
|
31
|
+
**Your AI works the night shift.**
|
|
32
|
+
|
|
33
|
+
Put your idle Claude Code subscription to work — read-only reviews of your
|
|
34
|
+
projects while you're busy, one digest every morning.
|
|
35
|
+
|
|
36
|
+

|
|
37
|
+
|
|
38
|
+
[](LICENSE)
|
|
39
|
+
[](pyproject.toml)
|
|
40
|
+
|
|
41
|
+
</div>
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## What this is
|
|
46
|
+
|
|
47
|
+
You already pay for Claude Code. It sits idle most of the day, and definitely
|
|
48
|
+
all night. nightshift spends that idle time reviewing the projects you point it
|
|
49
|
+
at, and leaves the results in one Markdown file you read with your coffee.
|
|
50
|
+
|
|
51
|
+
It never edits your code. It has no daemon and no server — cron calls it, it
|
|
52
|
+
decides whether to run, and it goes back to sleep. Everything it knows lives in
|
|
53
|
+
two directories you can delete at any time.
|
|
54
|
+
|
|
55
|
+
That's the whole tool. The rest of this page is how to use it.
|
|
56
|
+
|
|
57
|
+
## Before you start
|
|
58
|
+
|
|
59
|
+
- **Python 3.10+**
|
|
60
|
+
- **At least one AI CLI**, installed and already logged in — either
|
|
61
|
+
[Claude Code](https://claude.com/claude-code) or
|
|
62
|
+
[Codex](https://developers.openai.com/codex). Run `claude --version` or
|
|
63
|
+
`codex --version`; if that works, nightshift will find it. Have both and it
|
|
64
|
+
will use both, each with its own budget.
|
|
65
|
+
- **cron** — standard on macOS and Linux. Windows works via WSL.
|
|
66
|
+
|
|
67
|
+
You do not need an API key. nightshift drives the same CLIs you use by hand, on
|
|
68
|
+
the subscriptions you already have.
|
|
69
|
+
|
|
70
|
+
## Install it
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
pipx install nightshift-cli
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
(`pip install nightshift-cli` works too; pipx just keeps it out of your other
|
|
77
|
+
environments. The command is `nightshift` either way.)
|
|
78
|
+
|
|
79
|
+
## Set it up
|
|
80
|
+
|
|
81
|
+
Run `nightshift init` once. It finds your AI CLIs, asks which projects to
|
|
82
|
+
review and when, writes a config file, and offers to install the cron lines
|
|
83
|
+
that drive everything:
|
|
84
|
+
|
|
85
|
+

|
|
86
|
+
|
|
87
|
+
The project path is the only thing it needs from you. Everything else has a
|
|
88
|
+
working default you can accept with Enter:
|
|
89
|
+
|
|
90
|
+
| It asks | It means | Default |
|
|
91
|
+
| --- | --- | --- |
|
|
92
|
+
| **project path** | A repo to review. Enter as many as you like; blank line to finish. | — |
|
|
93
|
+
| **name** | What to call it in the digest. | the directory's name |
|
|
94
|
+
| **tasks** | Which reviews to run on it. | `code_review, security_audit, deps_audit` |
|
|
95
|
+
| **windows** | Hours it's allowed to run, local time. | `00:00-06:00` |
|
|
96
|
+
| **idle minutes** | How long you must be away from Claude Code first. | `60` |
|
|
97
|
+
| **digest dir** | Where the morning digest lands. | `~/nightshift-reports` |
|
|
98
|
+
|
|
99
|
+
Everything it writes goes to `~/.nightshift/config.yaml`. Edit it by hand
|
|
100
|
+
whenever you like — it's plain YAML, and `nightshift status` validates it.
|
|
101
|
+
|
|
102
|
+
## See it work
|
|
103
|
+
|
|
104
|
+
Don't wait until tonight. Force a run right now:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
nightshift run --now
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
`--now` skips the window and idle checks, so you get a review immediately. At a
|
|
111
|
+
terminal, nightshift streams it live — you see the same reads and reasoning you
|
|
112
|
+
would if you'd run `claude` yourself:
|
|
113
|
+
|
|
114
|
+

|
|
115
|
+
|
|
116
|
+
That is a real run of nightshift against its own repository, and those are real
|
|
117
|
+
bugs. Two of them became commits the same evening: a run that could hang forever
|
|
118
|
+
holding the scheduler's lock (`claude_code.py:366`), and a lock that could be
|
|
119
|
+
released by the wrong owner (`lock.py:121`).
|
|
120
|
+
|
|
121
|
+
Findings are ranked 🔴 HIGH, 🟠 MED, 🟡 LOW, and each one cites a `file:line`
|
|
122
|
+
so you can jump straight to it.
|
|
123
|
+
|
|
124
|
+
## Then forget about it
|
|
125
|
+
|
|
126
|
+
If you let `init` install the cron lines, you're already done. Cron calls
|
|
127
|
+
`nightshift run` every hour; the command decides for itself whether to act and
|
|
128
|
+
exits quietly when the answer is no:
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
0 * * * * ~/.local/bin/nightshift run >> /tmp/nightshift-cron.log 2>&1
|
|
132
|
+
30 7 * * * ~/.local/bin/nightshift digest >> /tmp/nightshift-cron.log 2>&1
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
The hourly line is the gated one; the 07:30 line renders yesterday's findings.
|
|
136
|
+
`init` writes the absolute path to the binary because cron runs with a minimal
|
|
137
|
+
`PATH` that almost certainly doesn't include yours, and redirects both streams
|
|
138
|
+
to a log because there is nowhere else for cron's output to go.
|
|
139
|
+
|
|
140
|
+
A run happens only when **all four gates** open:
|
|
141
|
+
|
|
142
|
+
1. **Window** — the clock is inside one of your `schedule.windows`.
|
|
143
|
+
2. **Idle** — you haven't touched Claude Code for `idle_minutes`. nightshift
|
|
144
|
+
watches `~/.claude/projects` and stays out of your way while you're working.
|
|
145
|
+
3. **Budget** — you have runs left today and this week.
|
|
146
|
+
4. **Lock** — no other run is already in flight.
|
|
147
|
+
|
|
148
|
+
Any gate saying no is normal, not an error. It prints one line and exits 0:
|
|
149
|
+
|
|
150
|
+
```
|
|
151
|
+
$ nightshift run
|
|
152
|
+
nothing to do — outside configured windows (00:00-06:00)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Each run pops one `(project, task)` pair from a persistent round-robin queue,
|
|
156
|
+
so every project gets its turn and a noisy one can't starve the rest.
|
|
157
|
+
|
|
158
|
+
To check on it any time, ask:
|
|
159
|
+
|
|
160
|
+

|
|
161
|
+
|
|
162
|
+
And to watch a run that cron started — including one already in progress —
|
|
163
|
+
`nightshift watch` follows along live and replays the last finished run first.
|
|
164
|
+
|
|
165
|
+
## Read the digest
|
|
166
|
+
|
|
167
|
+
Every run appends to `~/nightshift-reports/YYYY-MM-DD/`. Once a day
|
|
168
|
+
`nightshift digest` renders those into one file, `DIGEST-YYYY-MM-DD.md`:
|
|
169
|
+
|
|
170
|
+
```markdown
|
|
171
|
+
# Nightshift · morning digest
|
|
172
|
+
|
|
173
|
+
Wed Jul 15, 2026 · generated 17:57 local · 1 project · 1 run
|
|
174
|
+
|
|
175
|
+
## Budget remaining
|
|
176
|
+
|
|
177
|
+
- `claude_code` ▓░░░░░ 1/6 today · 1/30 week
|
|
178
|
+
|
|
179
|
+
## Highlights
|
|
180
|
+
|
|
181
|
+
- 🔴 Replace the buffered `subprocess.run(..., timeout=...)` with the same `Popen` +
|
|
182
|
+
`os.killpg` treatment the streaming path uses … — _nightshift · code_review_ ·
|
|
183
|
+
`nightshift/adapters/claude_code.py:267`
|
|
184
|
+
- 🟠 Have `release()` re-read the lockfile and unlink only when the recorded pid is
|
|
185
|
+
still our own … — _nightshift · code_review_ · `nightshift/lock.py:121`
|
|
186
|
+
|
|
187
|
+
## Run log
|
|
188
|
+
|
|
189
|
+
| project | task | provider | status | dur | time |
|
|
190
|
+
| --- | --- | --- | --- | --- | --- |
|
|
191
|
+
| nightshift | code_review | claude_code | ok | 2m18s | 15:23 |
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Highest severity first, grouped by project, read in twenty seconds.
|
|
195
|
+
**[Here is that digest in full](docs/sample-digest.md)** — a real one, not a
|
|
196
|
+
mock-up.
|
|
197
|
+
|
|
198
|
+
Skipped and failed runs stay in the log. A run that didn't happen is
|
|
199
|
+
information too, and silently dropping it is how you stop trusting the tool.
|
|
200
|
+
|
|
201
|
+
Want it early, or for a specific day?
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
nightshift digest # today, written to the digest dir
|
|
205
|
+
nightshift digest --date 2026-07-14 # a past day
|
|
206
|
+
nightshift digest --stdout # print it instead of writing it
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## 0 files touched
|
|
210
|
+
|
|
211
|
+
nightshift's entire value depends on being safe to leave unattended, so
|
|
212
|
+
read-only isn't a promise in the docs — it's enforced at the layer that
|
|
213
|
+
actually executes tools.
|
|
214
|
+
|
|
215
|
+
The Claude Code adapter invokes the CLI with its own permission flags:
|
|
216
|
+
|
|
217
|
+
```
|
|
218
|
+
claude --print "<prompt>"
|
|
219
|
+
--output-format json
|
|
220
|
+
--allowed-tools Read Grep Glob NotebookRead
|
|
221
|
+
--disallowed-tools Bash Edit MultiEdit Write NotebookEdit WebFetch WebSearch Task
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Three things are true because of that:
|
|
225
|
+
|
|
226
|
+
- **The allowlist is the whole tool budget.** Claude Code cannot call a tool
|
|
227
|
+
that isn't on it. There is no Edit, no Write, no shell.
|
|
228
|
+
- **The denylist is belt-and-braces.** It exists so that a future CLI release
|
|
229
|
+
adding a new write-capable tool to the defaults can't silently widen what
|
|
230
|
+
nightshift can do.
|
|
231
|
+
- **No fallback.** If the flags are rejected, the run fails and is logged as
|
|
232
|
+
failed. nightshift never retries an unrestricted invocation.
|
|
233
|
+
|
|
234
|
+
The prompts reinforce it, but prompts are not a security boundary and aren't
|
|
235
|
+
treated as one. The flags are.
|
|
236
|
+
|
|
237
|
+
nightshift also never touches your git state: no commits, no branches, no
|
|
238
|
+
pushes. It reads, and it writes exactly one place — the digest directory.
|
|
239
|
+
|
|
240
|
+
## Don't let it burn your quota
|
|
241
|
+
|
|
242
|
+
nightshift runs on the subscription you already pay for, which means the
|
|
243
|
+
fastest way for it to become a problem is to burn through your quota. So it
|
|
244
|
+
counts.
|
|
245
|
+
|
|
246
|
+
```yaml
|
|
247
|
+
providers:
|
|
248
|
+
claude_code:
|
|
249
|
+
enabled: true
|
|
250
|
+
budget:
|
|
251
|
+
max_runs_per_day: 6
|
|
252
|
+
max_runs_per_week: 30
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
- **Every attempt counts** — including failures and timeouts. They spent your
|
|
256
|
+
quota, so they cost budget. Counting only successes would let a broken
|
|
257
|
+
project drain the account in a loop.
|
|
258
|
+
- **Both caps bind.** Under the daily cap but at the weekly one? It stops.
|
|
259
|
+
- **`--now` skips the window and idle checks, never the budget check.**
|
|
260
|
+
- **At the cap it stops and says so**, once, as a `skipped` row in the digest.
|
|
261
|
+
|
|
262
|
+
Start low. Six runs a day is already a lot of review.
|
|
263
|
+
|
|
264
|
+
## Configuration
|
|
265
|
+
|
|
266
|
+
Lives at `~/.nightshift/config.yaml`. Here it is in full — this is every knob
|
|
267
|
+
there is:
|
|
268
|
+
|
|
269
|
+
```yaml
|
|
270
|
+
providers:
|
|
271
|
+
claude_code:
|
|
272
|
+
enabled: true
|
|
273
|
+
budget: { max_runs_per_day: 6, max_runs_per_week: 30 }
|
|
274
|
+
codex:
|
|
275
|
+
enabled: true
|
|
276
|
+
budget: { max_runs_per_day: 6, max_runs_per_week: 30 }
|
|
277
|
+
# Optional. Only needed when the CLI isn't on PATH under its own name —
|
|
278
|
+
# e.g. the Codex bundled inside ChatGPT.app:
|
|
279
|
+
binary: /Applications/ChatGPT.app/Contents/Resources/codex
|
|
280
|
+
|
|
281
|
+
projects:
|
|
282
|
+
- name: gradagent
|
|
283
|
+
path: ~/projects/gradagent
|
|
284
|
+
tasks: [code_review, deps_audit, docs_drift]
|
|
285
|
+
- name: nightshift
|
|
286
|
+
path: ~/projects/nightshift
|
|
287
|
+
tasks: [code_review]
|
|
288
|
+
# Optional. Pin this project to one provider. Without it, whichever enabled
|
|
289
|
+
# provider is idle and under budget takes the project.
|
|
290
|
+
provider: codex
|
|
291
|
+
|
|
292
|
+
schedule:
|
|
293
|
+
windows: ["09:00-18:00", "00:00-06:00"] # local time; may cross midnight
|
|
294
|
+
idle_minutes: 60
|
|
295
|
+
|
|
296
|
+
digest:
|
|
297
|
+
dir: ~/nightshift-reports
|
|
298
|
+
run:
|
|
299
|
+
timeout_s: 600
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
Run `nightshift status` after editing — it validates the file and tells you
|
|
303
|
+
exactly what's wrong if anything is.
|
|
304
|
+
|
|
305
|
+
Set `NIGHTSHIFT_HOME` to move the whole state directory somewhere else.
|
|
306
|
+
|
|
307
|
+
## Tasks
|
|
308
|
+
|
|
309
|
+
A task is just a prompt template. Five ship with nightshift:
|
|
310
|
+
|
|
311
|
+
| task | what it looks for |
|
|
312
|
+
| --- | --- |
|
|
313
|
+
| `code_review` | Bugs, races, and correctness problems |
|
|
314
|
+
| `security_audit` | Injection, authz gaps, unsafe defaults |
|
|
315
|
+
| `deps_audit` | Unpinned, stale, or risky dependencies |
|
|
316
|
+
| `docs_drift` | Docs that no longer match the code |
|
|
317
|
+
| `dead_links` | Links and image paths pointing at things that aren't there |
|
|
318
|
+
|
|
319
|
+
Give each project the tasks that suit it — a Terraform repo probably wants
|
|
320
|
+
`security_audit` and `deps_audit`, not `dead_links`.
|
|
321
|
+
|
|
322
|
+
**Write your own:** drop any `.md` file into `~/.nightshift/prompts/` and its
|
|
323
|
+
filename becomes a valid task name. Use a shipped name to override that
|
|
324
|
+
template.
|
|
325
|
+
|
|
326
|
+
Templates must tell the model to prefix each finding with `HIGH`, `MED`, or
|
|
327
|
+
`LOW` and cite a `file:line`. Parsing is lenient — an unlabelled finding is
|
|
328
|
+
kept and filed as `LOW` rather than dropped.
|
|
329
|
+
|
|
330
|
+
## Commands
|
|
331
|
+
|
|
332
|
+
| command | what it does |
|
|
333
|
+
| --- | --- |
|
|
334
|
+
| `nightshift init` | Detect CLIs, register projects, write config, offer to install cron |
|
|
335
|
+
| `nightshift run [--now]` | One gated run. `--now` skips window+idle checks |
|
|
336
|
+
| `nightshift watch [-n N]` | Follow runs live, including ones cron started |
|
|
337
|
+
| `nightshift digest [--date]` | Render `DIGEST-YYYY-MM-DD.md` |
|
|
338
|
+
| `nightshift status` | Budget bars, recent runs, next window, provider health |
|
|
339
|
+
|
|
340
|
+
Each takes `--help`.
|
|
341
|
+
|
|
342
|
+
## Troubleshooting
|
|
343
|
+
|
|
344
|
+
**`nothing to do — outside configured windows (00:00-06:00)`**
|
|
345
|
+
Working as designed — it's not in a window. Use `nightshift run --now` to run
|
|
346
|
+
anyway, or widen `schedule.windows`.
|
|
347
|
+
|
|
348
|
+
**`nothing to do — claude_code used 12m ago (needs 60m idle)`**
|
|
349
|
+
You've been using Claude Code, so nightshift is staying out of your way. Use
|
|
350
|
+
`--now`, or lower `idle_minutes` (`0` disables the check).
|
|
351
|
+
|
|
352
|
+
**`nothing to do — claude_code: daily budget spent (6/6 today)`**
|
|
353
|
+
Out of quota for today. Raise `max_runs_per_day` if you want more.
|
|
354
|
+
|
|
355
|
+
**``No usable AI CLI found. Install Claude Code or Codex and re-run `nightshift init`.``**
|
|
356
|
+
Neither CLI is on your `PATH`. Check `claude --version` / `codex --version` in
|
|
357
|
+
the same shell.
|
|
358
|
+
|
|
359
|
+
**Cron never runs it.** Cron uses a minimal `PATH`, which is why `init` writes
|
|
360
|
+
the absolute path to the binary into your crontab. Check
|
|
361
|
+
`/tmp/nightshift-cron.log` — everything cron runs is logged there. On macOS,
|
|
362
|
+
cron may also need Full Disk Access to read your projects.
|
|
363
|
+
|
|
364
|
+
**A run is stuck.** Runs are killed at `run.timeout_s` (default 600s) and
|
|
365
|
+
recorded as `timeout`. There is nothing to clean up by hand.
|
|
366
|
+
|
|
367
|
+
## Uninstall
|
|
368
|
+
|
|
369
|
+
nightshift keeps no state anywhere else, so removing it is three lines:
|
|
370
|
+
|
|
371
|
+
```bash
|
|
372
|
+
crontab -e # delete the block (see below)
|
|
373
|
+
rm -rf ~/.nightshift # config, ledger, queue, event logs
|
|
374
|
+
pipx uninstall nightshift-cli
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
`init` fences its crontab lines between two markers — delete them and
|
|
378
|
+
everything between:
|
|
379
|
+
|
|
380
|
+
```
|
|
381
|
+
# nightshift (managed — edit via `nightshift init`)
|
|
382
|
+
...
|
|
383
|
+
# end nightshift
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
Your digests in `~/nightshift-reports` are yours — delete them or don't.
|
|
387
|
+
|
|
388
|
+
## Providers
|
|
389
|
+
|
|
390
|
+
The scheduler, ledger, queue, and digest are all provider-agnostic. Enable
|
|
391
|
+
whichever CLIs you have; each gets its own budget.
|
|
392
|
+
|
|
393
|
+
| provider | status | how read-only is enforced |
|
|
394
|
+
| --- | --- | --- |
|
|
395
|
+
| `claude_code` | working | CLI permission flags — an allowlist of read-class tools, plus a denylist of every mutating one |
|
|
396
|
+
| `codex` | working | Codex's own OS sandbox — Seatbelt on macOS, Landlock + seccomp on Linux |
|
|
397
|
+
| `copilot` | stub | — see below |
|
|
398
|
+
|
|
399
|
+
One hard requirement, and it's the whole reason that last column exists: **the
|
|
400
|
+
read-only guarantee must be enforced by the CLI's own permission system**, not
|
|
401
|
+
by asking the model nicely. An adapter that can't do that won't be merged,
|
|
402
|
+
because "0 files touched" is the whole product.
|
|
403
|
+
|
|
404
|
+
**Copilot: help wanted, but blocked upstream.** It ships as a documented stub
|
|
405
|
+
(`nightshift/adapters/copilot.py`). The obstacle isn't effort — it's that
|
|
406
|
+
Copilot CLI has no enforcement primitive that clears the bar. Its file-level
|
|
407
|
+
denials don't apply across tools, so `shell(cat x)` walks around a denied
|
|
408
|
+
`read(x)`, and [an open issue](https://github.com/github/copilot-cli/issues/2722)
|
|
409
|
+
reports `--deny-tool="read(...)"` blocking *all* reads regardless of pattern.
|
|
410
|
+
Denials one tool honors and another ignores aren't a permission system. If that
|
|
411
|
+
changes upstream, the adapter is an afternoon's work — `codex.py` and
|
|
412
|
+
`claude_code.py` are both reference shapes.
|
|
413
|
+
|
|
414
|
+
## Development
|
|
415
|
+
|
|
416
|
+
```bash
|
|
417
|
+
python -m venv .venv && .venv/bin/pip install -e ".[dev]"
|
|
418
|
+
.venv/bin/pytest
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
The test suite spends zero quota: the scheduler, budget, queue, and digest are
|
|
422
|
+
covered against a `FakeAdapter`, and the Claude Code adapter is tested with a
|
|
423
|
+
mocked `subprocess`. No test ever shells out to a real AI CLI.
|
|
424
|
+
|
|
425
|
+
The images on this page are generated from real captured output — see
|
|
426
|
+
[docs/RECORDING.md](docs/RECORDING.md) if you change what the CLI prints.
|
|
427
|
+
|
|
428
|
+
## License
|
|
429
|
+
|
|
430
|
+
MIT
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
nightshift/__init__.py,sha256=GyOUhY4XPP8mihZSot0EEBLfQ9B72hlKA2HR51y61z0,182
|
|
2
|
+
nightshift/__main__.py,sha256=eRvrBBksxLt3oS2RPdh9tVibE3MS60o6NnTV23CG86Y,71
|
|
3
|
+
nightshift/budget.py,sha256=lq-vA-WOU4WRvOFLnEvA4nE0fwpoUyCG1quGWhtrg_k,4856
|
|
4
|
+
nightshift/cli.py,sha256=MFby2PA-9mJo5K57PCWWuQTiKGt7pk7xteQqtF5nMXY,20992
|
|
5
|
+
nightshift/config.py,sha256=XvZ7nz-lJ7Xo7ykbfeekOxQQ5sZg67mIvGEeSaozXIY,16340
|
|
6
|
+
nightshift/cron.py,sha256=qtCMDbJf7QwakFpK7yXwmrUCJwvbEG4ALaxuP6KiTwc,2992
|
|
7
|
+
nightshift/events.py,sha256=CeS7EWjrYDuwJr8WsfKgvG5UGe55qVsLKErCEC8UnN4,9198
|
|
8
|
+
nightshift/lock.py,sha256=P9P8tj8eR1oFX3sDro4nhxzsSEcHQka6BceKcp--iVU,6975
|
|
9
|
+
nightshift/prompts.py,sha256=GK3m5cvQqC4pOPsZNPVCxchIMlgwhm9KsTUASDeV6Dg,1916
|
|
10
|
+
nightshift/queue.py,sha256=2tNUQ12v1a4jWYVjcKb12eS6KASP4UsCsThYujmENeU,3106
|
|
11
|
+
nightshift/report.py,sha256=r2B5FMT7UFMmfgeQUJtq-6sYlSck3gwGT9AckMfbfs4,13127
|
|
12
|
+
nightshift/scheduler.py,sha256=5euLIh3Arn9sk7cIltpyY-OkdUhzmp32fKPBUwUpd_A,14920
|
|
13
|
+
nightshift/sessions.py,sha256=9lWbBBWYDIK5g9TLHh4ojXxIOMNAu2bdHn_Z_DAcFhY,2479
|
|
14
|
+
nightshift/store.py,sha256=I4gBnMO8uHNC4MeWCZKH3BCQO8KJlq1m_LENDl9JBpY,1453
|
|
15
|
+
nightshift/adapters/__init__.py,sha256=9QOJ2wqFNGYa8kVqEe-zN_JoHXZSro8H8-AtpkMcLJA,1891
|
|
16
|
+
nightshift/adapters/_process.py,sha256=2PrAVed86ewhfkjh3xRsBRcweE73eAsZSD9S99d_7ac,7040
|
|
17
|
+
nightshift/adapters/base.py,sha256=kVf5sXiwTKyIAhs-GKbkgMxUQNp7nuOS8Ukoki6Q11Y,5062
|
|
18
|
+
nightshift/adapters/claude_code.py,sha256=TEbaFXe7cuXUe5JpWyDeGOX2Hu4nOcnZoBtlAXh7VbI,13914
|
|
19
|
+
nightshift/adapters/codex.py,sha256=rmASV0MXrfvtL7feDdoYcxj3re46UyKsrjSCR-W_BZI,14668
|
|
20
|
+
nightshift/adapters/copilot.py,sha256=JMLhiPLVEhjY4GyRFC2aYn25YTSsfVkdoth-XSEph7c,2352
|
|
21
|
+
nightshift/prompts/code_review.md,sha256=TvOEQqXsFXkt8UCqiGitRAre_oUX9d9l2GGzV3bPpxU,1138
|
|
22
|
+
nightshift/prompts/dead_links.md,sha256=fZj4tKI4h4bVfqtUae6Ls1JO1V52KJFBKl0_0YO5598,1103
|
|
23
|
+
nightshift/prompts/deps_audit.md,sha256=LRj3oBLnR9DZDNyCIEK-nAgIntqXBWr8UzItMoGeiAM,1125
|
|
24
|
+
nightshift/prompts/docs_drift.md,sha256=P3y_1rI7qjbbvjZQIizRt5aMRC3Ju0mKGMnAfBL2w88,1061
|
|
25
|
+
nightshift/prompts/security_audit.md,sha256=LScoW9cth4MB4xaokPtMOCRjboA2-Y2XQ05YaNY8d2U,1095
|
|
26
|
+
nightshift_cli-0.1.0.dist-info/METADATA,sha256=7vMPTB5T5_XhkUeN0ux79D6SG7tSPG51M1yVsLs_Y0k,15930
|
|
27
|
+
nightshift_cli-0.1.0.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
|
|
28
|
+
nightshift_cli-0.1.0.dist-info/entry_points.txt,sha256=SX_Ru6Yvt-zk18NtePegg1D9gNGw1XCVd0CVzrss4nA,51
|
|
29
|
+
nightshift_cli-0.1.0.dist-info/licenses/LICENSE,sha256=EYqMCn5BztgpX_wxO_p1CUfliftVkcEYe-qCUqeLlK8,1073
|
|
30
|
+
nightshift_cli-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Md. Kishor Morol
|
|
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.
|