@da1z/chop 0.0.1
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.
- package/.claude/rules/use-bun-instead-of-node-vite-npm-pnpm.md +109 -0
- package/.claude/settings.local.json +12 -0
- package/.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc +111 -0
- package/.devcontainer/Dockerfile +102 -0
- package/.devcontainer/devcontainer.json +58 -0
- package/.devcontainer/init-firewall.sh +137 -0
- package/.github/workflows/publish.yml +76 -0
- package/CLAUDE.md +44 -0
- package/README.md +15 -0
- package/index.ts +2 -0
- package/loop.sh +206 -0
- package/package.json +27 -0
- package/specs/chop.md +313 -0
- package/src/commands/add.ts +74 -0
- package/src/commands/archive.ts +72 -0
- package/src/commands/completion.ts +232 -0
- package/src/commands/done.ts +38 -0
- package/src/commands/edit.ts +228 -0
- package/src/commands/init.ts +72 -0
- package/src/commands/list.ts +48 -0
- package/src/commands/move.ts +92 -0
- package/src/commands/pop.ts +45 -0
- package/src/commands/purge.ts +41 -0
- package/src/commands/show.ts +32 -0
- package/src/commands/status.ts +43 -0
- package/src/config/paths.ts +61 -0
- package/src/errors.ts +56 -0
- package/src/index.ts +41 -0
- package/src/models/id-generator.ts +39 -0
- package/src/models/task.ts +98 -0
- package/src/storage/file-lock.ts +124 -0
- package/src/storage/storage-resolver.ts +63 -0
- package/src/storage/task-store.ts +173 -0
- package/src/types.ts +42 -0
- package/src/utils/display.ts +139 -0
- package/src/utils/git.ts +80 -0
- package/src/utils/prompts.ts +88 -0
- package/tests/errors.test.ts +86 -0
- package/tests/models/id-generator.test.ts +46 -0
- package/tests/models/task.test.ts +186 -0
- package/tests/storage/file-lock.test.ts +152 -0
- package/tsconfig.json +9 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
---
|
|
2
|
+
paths: *.ts, *.tsx, *.html, *.css, *.js, *.jsx, package.json
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Default to using Bun instead of Node.js.
|
|
6
|
+
|
|
7
|
+
- Use `bun <file>` instead of `node <file>` or `ts-node <file>`
|
|
8
|
+
- Use `bun test` instead of `jest` or `vitest`
|
|
9
|
+
- Use `bun build <file.html|file.ts|file.css>` instead of `webpack` or `esbuild`
|
|
10
|
+
- Use `bun install` instead of `npm install` or `yarn install` or `pnpm install`
|
|
11
|
+
- Use `bun run <script>` instead of `npm run <script>` or `yarn run <script>` or `pnpm run <script>`
|
|
12
|
+
- Use `bunx <package> <command>` instead of `npx <package> <command>`
|
|
13
|
+
- Bun automatically loads .env, so don't use dotenv.
|
|
14
|
+
|
|
15
|
+
## APIs
|
|
16
|
+
|
|
17
|
+
- `Bun.serve()` supports WebSockets, HTTPS, and routes. Don't use `express`.
|
|
18
|
+
- `bun:sqlite` for SQLite. Don't use `better-sqlite3`.
|
|
19
|
+
- `Bun.redis` for Redis. Don't use `ioredis`.
|
|
20
|
+
- `Bun.sql` for Postgres. Don't use `pg` or `postgres.js`.
|
|
21
|
+
- `WebSocket` is built-in. Don't use `ws`.
|
|
22
|
+
- Prefer `Bun.file` over `node:fs`'s readFile/writeFile
|
|
23
|
+
- Bun.$`ls` instead of execa.
|
|
24
|
+
|
|
25
|
+
## Testing
|
|
26
|
+
|
|
27
|
+
Use `bun test` to run tests.
|
|
28
|
+
|
|
29
|
+
```ts#index.test.ts
|
|
30
|
+
import { test, expect } from "bun:test";
|
|
31
|
+
|
|
32
|
+
test("hello world", () => {
|
|
33
|
+
expect(1).toBe(1);
|
|
34
|
+
});
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Frontend
|
|
38
|
+
|
|
39
|
+
Use HTML imports with `Bun.serve()`. Don't use `vite`. HTML imports fully support React, CSS, Tailwind.
|
|
40
|
+
|
|
41
|
+
Server:
|
|
42
|
+
|
|
43
|
+
```ts#index.ts
|
|
44
|
+
import index from "./index.html"
|
|
45
|
+
|
|
46
|
+
Bun.serve({
|
|
47
|
+
routes: {
|
|
48
|
+
"/": index,
|
|
49
|
+
"/api/users/:id": {
|
|
50
|
+
GET: (req) => {
|
|
51
|
+
return new Response(JSON.stringify({ id: req.params.id }));
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
// optional websocket support
|
|
56
|
+
websocket: {
|
|
57
|
+
open: (ws) => {
|
|
58
|
+
ws.send("Hello, world!");
|
|
59
|
+
},
|
|
60
|
+
message: (ws, message) => {
|
|
61
|
+
ws.send(message);
|
|
62
|
+
},
|
|
63
|
+
close: (ws) => {
|
|
64
|
+
// handle close
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
development: {
|
|
68
|
+
hmr: true,
|
|
69
|
+
console: true,
|
|
70
|
+
}
|
|
71
|
+
})
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
HTML files can import .tsx, .jsx or .js files directly and Bun's bundler will transpile & bundle automatically. `<link>` tags can point to stylesheets and Bun's CSS bundler will bundle.
|
|
75
|
+
|
|
76
|
+
```html#index.html
|
|
77
|
+
<html>
|
|
78
|
+
<body>
|
|
79
|
+
<h1>Hello, world!</h1>
|
|
80
|
+
<script type="module" src="./frontend.tsx"></script>
|
|
81
|
+
</body>
|
|
82
|
+
</html>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
With the following `frontend.tsx`:
|
|
86
|
+
|
|
87
|
+
```tsx#frontend.tsx
|
|
88
|
+
import React from "react";
|
|
89
|
+
import { createRoot } from "react-dom/client";
|
|
90
|
+
|
|
91
|
+
// import .css files directly and it works
|
|
92
|
+
import './index.css';
|
|
93
|
+
|
|
94
|
+
const root = createRoot(document.body);
|
|
95
|
+
|
|
96
|
+
export default function Frontend() {
|
|
97
|
+
return <h1>Hello, world!</h1>;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
root.render(<Frontend />);
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Then, run index.ts
|
|
104
|
+
|
|
105
|
+
```sh
|
|
106
|
+
bun --hot ./index.ts
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
For more information, read the Bun API docs in `node_modules/bun-types/docs/**.mdx`.
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Use Bun instead of Node.js, npm, pnpm, or vite.
|
|
3
|
+
globs: "*.ts, *.tsx, *.html, *.css, *.js, *.jsx, package.json"
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Default to using Bun instead of Node.js.
|
|
8
|
+
|
|
9
|
+
- Use `bun <file>` instead of `node <file>` or `ts-node <file>`
|
|
10
|
+
- Use `bun test` instead of `jest` or `vitest`
|
|
11
|
+
- Use `bun build <file.html|file.ts|file.css>` instead of `webpack` or `esbuild`
|
|
12
|
+
- Use `bun install` instead of `npm install` or `yarn install` or `pnpm install`
|
|
13
|
+
- Use `bun run <script>` instead of `npm run <script>` or `yarn run <script>` or `pnpm run <script>`
|
|
14
|
+
- Use `bunx <package> <command>` instead of `npx <package> <command>`
|
|
15
|
+
- Bun automatically loads .env, so don't use dotenv.
|
|
16
|
+
|
|
17
|
+
## APIs
|
|
18
|
+
|
|
19
|
+
- `Bun.serve()` supports WebSockets, HTTPS, and routes. Don't use `express`.
|
|
20
|
+
- `bun:sqlite` for SQLite. Don't use `better-sqlite3`.
|
|
21
|
+
- `Bun.redis` for Redis. Don't use `ioredis`.
|
|
22
|
+
- `Bun.sql` for Postgres. Don't use `pg` or `postgres.js`.
|
|
23
|
+
- `WebSocket` is built-in. Don't use `ws`.
|
|
24
|
+
- Prefer `Bun.file` over `node:fs`'s readFile/writeFile
|
|
25
|
+
- Bun.$`ls` instead of execa.
|
|
26
|
+
|
|
27
|
+
## Testing
|
|
28
|
+
|
|
29
|
+
Use `bun test` to run tests.
|
|
30
|
+
|
|
31
|
+
```ts#index.test.ts
|
|
32
|
+
import { test, expect } from "bun:test";
|
|
33
|
+
|
|
34
|
+
test("hello world", () => {
|
|
35
|
+
expect(1).toBe(1);
|
|
36
|
+
});
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Frontend
|
|
40
|
+
|
|
41
|
+
Use HTML imports with `Bun.serve()`. Don't use `vite`. HTML imports fully support React, CSS, Tailwind.
|
|
42
|
+
|
|
43
|
+
Server:
|
|
44
|
+
|
|
45
|
+
```ts#index.ts
|
|
46
|
+
import index from "./index.html"
|
|
47
|
+
|
|
48
|
+
Bun.serve({
|
|
49
|
+
routes: {
|
|
50
|
+
"/": index,
|
|
51
|
+
"/api/users/:id": {
|
|
52
|
+
GET: (req) => {
|
|
53
|
+
return new Response(JSON.stringify({ id: req.params.id }));
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
// optional websocket support
|
|
58
|
+
websocket: {
|
|
59
|
+
open: (ws) => {
|
|
60
|
+
ws.send("Hello, world!");
|
|
61
|
+
},
|
|
62
|
+
message: (ws, message) => {
|
|
63
|
+
ws.send(message);
|
|
64
|
+
},
|
|
65
|
+
close: (ws) => {
|
|
66
|
+
// handle close
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
development: {
|
|
70
|
+
hmr: true,
|
|
71
|
+
console: true,
|
|
72
|
+
}
|
|
73
|
+
})
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
HTML files can import .tsx, .jsx or .js files directly and Bun's bundler will transpile & bundle automatically. `<link>` tags can point to stylesheets and Bun's CSS bundler will bundle.
|
|
77
|
+
|
|
78
|
+
```html#index.html
|
|
79
|
+
<html>
|
|
80
|
+
<body>
|
|
81
|
+
<h1>Hello, world!</h1>
|
|
82
|
+
<script type="module" src="./frontend.tsx"></script>
|
|
83
|
+
</body>
|
|
84
|
+
</html>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
With the following `frontend.tsx`:
|
|
88
|
+
|
|
89
|
+
```tsx#frontend.tsx
|
|
90
|
+
import React from "react";
|
|
91
|
+
import { createRoot } from "react-dom/client";
|
|
92
|
+
|
|
93
|
+
// import .css files directly and it works
|
|
94
|
+
import './index.css';
|
|
95
|
+
|
|
96
|
+
const root = createRoot(document.body);
|
|
97
|
+
|
|
98
|
+
export default function Frontend() {
|
|
99
|
+
return <h1>Hello, world!</h1>;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
root.render(<Frontend />);
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Then, run index.ts
|
|
106
|
+
|
|
107
|
+
```sh
|
|
108
|
+
bun --hot ./index.ts
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
For more information, read the Bun API docs in `node_modules/bun-types/docs/**.mdx`.
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
FROM node:20
|
|
2
|
+
|
|
3
|
+
ARG TZ
|
|
4
|
+
ENV TZ="$TZ"
|
|
5
|
+
|
|
6
|
+
ARG CLAUDE_CODE_VERSION=latest
|
|
7
|
+
ARG BUN_VERSION=latest
|
|
8
|
+
|
|
9
|
+
# Install basic development tools and iptables/ipset
|
|
10
|
+
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
11
|
+
less \
|
|
12
|
+
git \
|
|
13
|
+
procps \
|
|
14
|
+
sudo \
|
|
15
|
+
fzf \
|
|
16
|
+
zsh \
|
|
17
|
+
man-db \
|
|
18
|
+
unzip \
|
|
19
|
+
gnupg2 \
|
|
20
|
+
gh \
|
|
21
|
+
iptables \
|
|
22
|
+
ipset \
|
|
23
|
+
iproute2 \
|
|
24
|
+
dnsutils \
|
|
25
|
+
aggregate \
|
|
26
|
+
jq \
|
|
27
|
+
nano \
|
|
28
|
+
vim \
|
|
29
|
+
&& apt-get clean && rm -rf /var/lib/apt/lists/*
|
|
30
|
+
|
|
31
|
+
# Install Bun
|
|
32
|
+
RUN if [ "$BUN_VERSION" = "latest" ]; then \
|
|
33
|
+
curl -fsSL https://bun.sh/install | bash; \
|
|
34
|
+
else \
|
|
35
|
+
curl -fsSL https://bun.sh/install | bash -s "bun-v${BUN_VERSION}"; \
|
|
36
|
+
fi \
|
|
37
|
+
&& mv /root/.bun /usr/local/bun \
|
|
38
|
+
&& ln -s /usr/local/bun/bin/bun /usr/local/bin/bun \
|
|
39
|
+
&& ln -s /usr/local/bun/bin/bunx /usr/local/bin/bunx
|
|
40
|
+
|
|
41
|
+
# Ensure default node user has access to /usr/local/share
|
|
42
|
+
RUN mkdir -p /usr/local/share/npm-global && \
|
|
43
|
+
chown -R node:node /usr/local/share
|
|
44
|
+
|
|
45
|
+
ARG USERNAME=node
|
|
46
|
+
|
|
47
|
+
# Persist bash history.
|
|
48
|
+
RUN SNIPPET="export PROMPT_COMMAND='history -a' && export HISTFILE=/commandhistory/.bash_history" \
|
|
49
|
+
&& mkdir /commandhistory \
|
|
50
|
+
&& touch /commandhistory/.bash_history \
|
|
51
|
+
&& chown -R $USERNAME /commandhistory
|
|
52
|
+
|
|
53
|
+
# Set `DEVCONTAINER` environment variable to help with orientation
|
|
54
|
+
ENV DEVCONTAINER=true
|
|
55
|
+
|
|
56
|
+
# Create workspace and config directories and set permissions
|
|
57
|
+
RUN mkdir -p /workspace /home/node/.claude && \
|
|
58
|
+
chown -R node:node /workspace /home/node/.claude
|
|
59
|
+
|
|
60
|
+
WORKDIR /workspace
|
|
61
|
+
|
|
62
|
+
ARG GIT_DELTA_VERSION=0.18.2
|
|
63
|
+
RUN ARCH=$(dpkg --print-architecture) && \
|
|
64
|
+
wget "https://github.com/dandavison/delta/releases/download/${GIT_DELTA_VERSION}/git-delta_${GIT_DELTA_VERSION}_${ARCH}.deb" && \
|
|
65
|
+
sudo dpkg -i "git-delta_${GIT_DELTA_VERSION}_${ARCH}.deb" && \
|
|
66
|
+
rm "git-delta_${GIT_DELTA_VERSION}_${ARCH}.deb"
|
|
67
|
+
|
|
68
|
+
# Set up non-root user
|
|
69
|
+
USER node
|
|
70
|
+
|
|
71
|
+
# Install global packages
|
|
72
|
+
ENV NPM_CONFIG_PREFIX=/usr/local/share/npm-global
|
|
73
|
+
ENV PATH=$PATH:/usr/local/share/npm-global/bin:/usr/local/bun/bin
|
|
74
|
+
|
|
75
|
+
# Set the default shell to zsh rather than sh
|
|
76
|
+
ENV SHELL=/bin/zsh
|
|
77
|
+
|
|
78
|
+
# Set the default editor and visual
|
|
79
|
+
ENV EDITOR=nano
|
|
80
|
+
ENV VISUAL=nano
|
|
81
|
+
|
|
82
|
+
# Default powerline10k theme
|
|
83
|
+
ARG ZSH_IN_DOCKER_VERSION=1.2.0
|
|
84
|
+
RUN sh -c "$(wget -O- https://github.com/deluan/zsh-in-docker/releases/download/v${ZSH_IN_DOCKER_VERSION}/zsh-in-docker.sh)" -- \
|
|
85
|
+
-p git \
|
|
86
|
+
-p fzf \
|
|
87
|
+
-a "source /usr/share/doc/fzf/examples/key-bindings.zsh" \
|
|
88
|
+
-a "source /usr/share/doc/fzf/examples/completion.zsh" \
|
|
89
|
+
-a "export PROMPT_COMMAND='history -a' && export HISTFILE=/commandhistory/.bash_history" \
|
|
90
|
+
-x
|
|
91
|
+
|
|
92
|
+
# Install Claude
|
|
93
|
+
RUN npm install -g @anthropic-ai/claude-code@${CLAUDE_CODE_VERSION}
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
# Copy and set up firewall script
|
|
97
|
+
COPY init-firewall.sh /usr/local/bin/
|
|
98
|
+
USER root
|
|
99
|
+
RUN chmod +x /usr/local/bin/init-firewall.sh && \
|
|
100
|
+
echo "node ALL=(root) NOPASSWD: /usr/local/bin/init-firewall.sh" > /etc/sudoers.d/node-firewall && \
|
|
101
|
+
chmod 0440 /etc/sudoers.d/node-firewall
|
|
102
|
+
USER node
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Chop Dev Container",
|
|
3
|
+
"build": {
|
|
4
|
+
"dockerfile": "Dockerfile",
|
|
5
|
+
"args": {
|
|
6
|
+
"TZ": "${localEnv:TZ:America/Los_Angeles}",
|
|
7
|
+
"CLAUDE_CODE_VERSION": "latest",
|
|
8
|
+
"GIT_DELTA_VERSION": "0.18.2",
|
|
9
|
+
"ZSH_IN_DOCKER_VERSION": "1.2.0",
|
|
10
|
+
"BUN_VERSION": "latest"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"runArgs": [
|
|
14
|
+
"--cap-add=NET_ADMIN",
|
|
15
|
+
"--cap-add=NET_RAW"
|
|
16
|
+
],
|
|
17
|
+
"customizations": {
|
|
18
|
+
"vscode": {
|
|
19
|
+
"extensions": [
|
|
20
|
+
"anthropic.claude-code",
|
|
21
|
+
"dbaeumer.vscode-eslint",
|
|
22
|
+
"esbenp.prettier-vscode",
|
|
23
|
+
"eamodio.gitlens"
|
|
24
|
+
],
|
|
25
|
+
"settings": {
|
|
26
|
+
"editor.formatOnSave": true,
|
|
27
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
|
28
|
+
"editor.codeActionsOnSave": {
|
|
29
|
+
"source.fixAll.eslint": "explicit"
|
|
30
|
+
},
|
|
31
|
+
"terminal.integrated.defaultProfile.linux": "zsh",
|
|
32
|
+
"terminal.integrated.profiles.linux": {
|
|
33
|
+
"bash": {
|
|
34
|
+
"path": "bash",
|
|
35
|
+
"icon": "terminal-bash"
|
|
36
|
+
},
|
|
37
|
+
"zsh": {
|
|
38
|
+
"path": "zsh"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"remoteUser": "node",
|
|
45
|
+
"mounts": [
|
|
46
|
+
"source=chop-bashhistory-${devcontainerId},target=/commandhistory,type=volume",
|
|
47
|
+
"source=chop-claude-config-${devcontainerId},target=/home/node/.claude,type=volume"
|
|
48
|
+
],
|
|
49
|
+
"containerEnv": {
|
|
50
|
+
"NODE_OPTIONS": "--max-old-space-size=4096",
|
|
51
|
+
"CLAUDE_CONFIG_DIR": "/home/node/.claude",
|
|
52
|
+
"POWERLEVEL9K_DISABLE_GITSTATUS": "true"
|
|
53
|
+
},
|
|
54
|
+
"workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind,consistency=delegated",
|
|
55
|
+
"workspaceFolder": "/workspace",
|
|
56
|
+
"postStartCommand": "sudo /usr/local/bin/init-firewall.sh",
|
|
57
|
+
"waitFor": "postStartCommand"
|
|
58
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -euo pipefail # Exit on error, undefined vars, and pipeline failures
|
|
3
|
+
IFS=$'\n\t' # Stricter word splitting
|
|
4
|
+
|
|
5
|
+
# 1. Extract Docker DNS info BEFORE any flushing
|
|
6
|
+
DOCKER_DNS_RULES=$(iptables-save -t nat | grep "127\.0\.0\.11" || true)
|
|
7
|
+
|
|
8
|
+
# Flush existing rules and delete existing ipsets
|
|
9
|
+
iptables -F
|
|
10
|
+
iptables -X
|
|
11
|
+
iptables -t nat -F
|
|
12
|
+
iptables -t nat -X
|
|
13
|
+
iptables -t mangle -F
|
|
14
|
+
iptables -t mangle -X
|
|
15
|
+
ipset destroy allowed-domains 2>/dev/null || true
|
|
16
|
+
|
|
17
|
+
# 2. Selectively restore ONLY internal Docker DNS resolution
|
|
18
|
+
if [ -n "$DOCKER_DNS_RULES" ]; then
|
|
19
|
+
echo "Restoring Docker DNS rules..."
|
|
20
|
+
iptables -t nat -N DOCKER_OUTPUT 2>/dev/null || true
|
|
21
|
+
iptables -t nat -N DOCKER_POSTROUTING 2>/dev/null || true
|
|
22
|
+
echo "$DOCKER_DNS_RULES" | xargs -L 1 iptables -t nat
|
|
23
|
+
else
|
|
24
|
+
echo "No Docker DNS rules to restore"
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
# First allow DNS and localhost before any restrictions
|
|
28
|
+
# Allow outbound DNS
|
|
29
|
+
iptables -A OUTPUT -p udp --dport 53 -j ACCEPT
|
|
30
|
+
# Allow inbound DNS responses
|
|
31
|
+
iptables -A INPUT -p udp --sport 53 -j ACCEPT
|
|
32
|
+
# Allow outbound SSH
|
|
33
|
+
iptables -A OUTPUT -p tcp --dport 22 -j ACCEPT
|
|
34
|
+
# Allow inbound SSH responses
|
|
35
|
+
iptables -A INPUT -p tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT
|
|
36
|
+
# Allow localhost
|
|
37
|
+
iptables -A INPUT -i lo -j ACCEPT
|
|
38
|
+
iptables -A OUTPUT -o lo -j ACCEPT
|
|
39
|
+
|
|
40
|
+
# Create ipset with CIDR support
|
|
41
|
+
ipset create allowed-domains hash:net
|
|
42
|
+
|
|
43
|
+
# Fetch GitHub meta information and aggregate + add their IP ranges
|
|
44
|
+
echo "Fetching GitHub IP ranges..."
|
|
45
|
+
gh_ranges=$(curl -s https://api.github.com/meta)
|
|
46
|
+
if [ -z "$gh_ranges" ]; then
|
|
47
|
+
echo "ERROR: Failed to fetch GitHub IP ranges"
|
|
48
|
+
exit 1
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
if ! echo "$gh_ranges" | jq -e '.web and .api and .git' >/dev/null; then
|
|
52
|
+
echo "ERROR: GitHub API response missing required fields"
|
|
53
|
+
exit 1
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
echo "Processing GitHub IPs..."
|
|
57
|
+
while read -r cidr; do
|
|
58
|
+
if [[ ! "$cidr" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/[0-9]{1,2}$ ]]; then
|
|
59
|
+
echo "ERROR: Invalid CIDR range from GitHub meta: $cidr"
|
|
60
|
+
exit 1
|
|
61
|
+
fi
|
|
62
|
+
echo "Adding GitHub range $cidr"
|
|
63
|
+
ipset add allowed-domains "$cidr"
|
|
64
|
+
done < <(echo "$gh_ranges" | jq -r '(.web + .api + .git)[]' | aggregate -q)
|
|
65
|
+
|
|
66
|
+
# Resolve and add other allowed domains
|
|
67
|
+
for domain in \
|
|
68
|
+
"registry.npmjs.org" \
|
|
69
|
+
"api.anthropic.com" \
|
|
70
|
+
"sentry.io" \
|
|
71
|
+
"statsig.anthropic.com" \
|
|
72
|
+
"statsig.com" \
|
|
73
|
+
"marketplace.visualstudio.com" \
|
|
74
|
+
"vscode.blob.core.windows.net" \
|
|
75
|
+
"update.code.visualstudio.com"; do
|
|
76
|
+
echo "Resolving $domain..."
|
|
77
|
+
ips=$(dig +noall +answer A "$domain" | awk '$4 == "A" {print $5}')
|
|
78
|
+
if [ -z "$ips" ]; then
|
|
79
|
+
echo "ERROR: Failed to resolve $domain"
|
|
80
|
+
exit 1
|
|
81
|
+
fi
|
|
82
|
+
|
|
83
|
+
while read -r ip; do
|
|
84
|
+
if [[ ! "$ip" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
|
|
85
|
+
echo "ERROR: Invalid IP from DNS for $domain: $ip"
|
|
86
|
+
exit 1
|
|
87
|
+
fi
|
|
88
|
+
echo "Adding $ip for $domain"
|
|
89
|
+
ipset add allowed-domains "$ip"
|
|
90
|
+
done < <(echo "$ips")
|
|
91
|
+
done
|
|
92
|
+
|
|
93
|
+
# Get host IP from default route
|
|
94
|
+
HOST_IP=$(ip route | grep default | cut -d" " -f3)
|
|
95
|
+
if [ -z "$HOST_IP" ]; then
|
|
96
|
+
echo "ERROR: Failed to detect host IP"
|
|
97
|
+
exit 1
|
|
98
|
+
fi
|
|
99
|
+
|
|
100
|
+
HOST_NETWORK=$(echo "$HOST_IP" | sed "s/\.[0-9]*$/.0\/24/")
|
|
101
|
+
echo "Host network detected as: $HOST_NETWORK"
|
|
102
|
+
|
|
103
|
+
# Set up remaining iptables rules
|
|
104
|
+
iptables -A INPUT -s "$HOST_NETWORK" -j ACCEPT
|
|
105
|
+
iptables -A OUTPUT -d "$HOST_NETWORK" -j ACCEPT
|
|
106
|
+
|
|
107
|
+
# Set default policies to DROP first
|
|
108
|
+
iptables -P INPUT DROP
|
|
109
|
+
iptables -P FORWARD DROP
|
|
110
|
+
iptables -P OUTPUT DROP
|
|
111
|
+
|
|
112
|
+
# First allow established connections for already approved traffic
|
|
113
|
+
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
|
|
114
|
+
iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
|
|
115
|
+
|
|
116
|
+
# Then allow only specific outbound traffic to allowed domains
|
|
117
|
+
iptables -A OUTPUT -m set --match-set allowed-domains dst -j ACCEPT
|
|
118
|
+
|
|
119
|
+
# Explicitly REJECT all other outbound traffic for immediate feedback
|
|
120
|
+
iptables -A OUTPUT -j REJECT --reject-with icmp-admin-prohibited
|
|
121
|
+
|
|
122
|
+
echo "Firewall configuration complete"
|
|
123
|
+
echo "Verifying firewall rules..."
|
|
124
|
+
if curl --connect-timeout 5 https://example.com >/dev/null 2>&1; then
|
|
125
|
+
echo "ERROR: Firewall verification failed - was able to reach https://example.com"
|
|
126
|
+
exit 1
|
|
127
|
+
else
|
|
128
|
+
echo "Firewall verification passed - unable to reach https://example.com as expected"
|
|
129
|
+
fi
|
|
130
|
+
|
|
131
|
+
# Verify GitHub API access
|
|
132
|
+
if ! curl --connect-timeout 5 https://api.github.com/zen >/dev/null 2>&1; then
|
|
133
|
+
echo "ERROR: Firewall verification failed - unable to reach https://api.github.com"
|
|
134
|
+
exit 1
|
|
135
|
+
else
|
|
136
|
+
echo "Firewall verification passed - able to reach https://api.github.com as expected"
|
|
137
|
+
fi
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
name: Publish to npm
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
inputs:
|
|
6
|
+
version_type:
|
|
7
|
+
description: 'Version bump type'
|
|
8
|
+
required: true
|
|
9
|
+
type: choice
|
|
10
|
+
options:
|
|
11
|
+
- patch
|
|
12
|
+
- minor
|
|
13
|
+
|
|
14
|
+
concurrency:
|
|
15
|
+
group: publish
|
|
16
|
+
cancel-in-progress: false
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
publish:
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
if: github.ref == 'refs/heads/main'
|
|
22
|
+
permissions:
|
|
23
|
+
contents: write
|
|
24
|
+
pull-requests: write
|
|
25
|
+
steps:
|
|
26
|
+
- name: Checkout
|
|
27
|
+
uses: actions/checkout@v4
|
|
28
|
+
|
|
29
|
+
- name: Setup Bun
|
|
30
|
+
uses: oven-sh/setup-bun@v2
|
|
31
|
+
|
|
32
|
+
- name: Install dependencies
|
|
33
|
+
run: bun install --frozen-lockfile
|
|
34
|
+
|
|
35
|
+
- name: Typecheck
|
|
36
|
+
run: bun run typecheck
|
|
37
|
+
|
|
38
|
+
- name: Run tests
|
|
39
|
+
run: bun test
|
|
40
|
+
|
|
41
|
+
- name: Bump version
|
|
42
|
+
id: version
|
|
43
|
+
run: |
|
|
44
|
+
bun pm version ${{ inputs.version_type }} --no-git-tag-version
|
|
45
|
+
NEW_VERSION=$(jq -r '.version' package.json)
|
|
46
|
+
echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT
|
|
47
|
+
|
|
48
|
+
- name: Build
|
|
49
|
+
run: bun run build
|
|
50
|
+
|
|
51
|
+
- name: Publish to npm
|
|
52
|
+
run: bun publish --access public
|
|
53
|
+
env:
|
|
54
|
+
NPM_CONFIG_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
55
|
+
|
|
56
|
+
- name: Create version bump PR
|
|
57
|
+
env:
|
|
58
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
59
|
+
run: |
|
|
60
|
+
BRANCH_NAME="release/v${{ steps.version.outputs.new_version }}"
|
|
61
|
+
git config user.name "github-actions[bot]"
|
|
62
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
63
|
+
git checkout -b "$BRANCH_NAME"
|
|
64
|
+
git add package.json
|
|
65
|
+
git commit -m "chore: bump version to ${{ steps.version.outputs.new_version }}"
|
|
66
|
+
git push origin "$BRANCH_NAME"
|
|
67
|
+
gh pr create \
|
|
68
|
+
--title "chore: bump version to ${{ steps.version.outputs.new_version }}" \
|
|
69
|
+
--body "Automated version bump after publishing v${{ steps.version.outputs.new_version }} to npm." \
|
|
70
|
+
--base main \
|
|
71
|
+
--head "$BRANCH_NAME"
|
|
72
|
+
|
|
73
|
+
- name: Summary
|
|
74
|
+
run: |
|
|
75
|
+
echo "## Published v${{ steps.version.outputs.new_version }}" >> $GITHUB_STEP_SUMMARY
|
|
76
|
+
echo "PR created for version bump" >> $GITHUB_STEP_SUMMARY
|
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
**chop** is a queue-based task management CLI for developers. It stores tasks per git repository with support for both local (`.chop/`) and global (`~/.local/share/chop/`) storage. Tasks support dependencies and statuses: `draft`, `open`, `in-progress`, `done`, `archived`.
|
|
8
|
+
|
|
9
|
+
## Commands
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
bun install # Install dependencies
|
|
13
|
+
bun test # Run all tests
|
|
14
|
+
bun test tests/models/task.test.ts # Run a single test file
|
|
15
|
+
bun run typecheck # Type check with tsc
|
|
16
|
+
bun run build # Build to ./dist
|
|
17
|
+
bun run index.ts # Run the CLI directly
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Architecture
|
|
21
|
+
|
|
22
|
+
**Entry Point**: `index.ts` → `src/index.ts` (Commander.js CLI setup)
|
|
23
|
+
|
|
24
|
+
**Core Layers**:
|
|
25
|
+
- `src/commands/` - CLI command handlers (init, add, list, pop, done, status, move, archive, purge, edit, show, completion)
|
|
26
|
+
- `src/storage/` - Data persistence layer
|
|
27
|
+
- `task-store.ts` - Main API for reading/writing tasks with atomic operations
|
|
28
|
+
- `storage-resolver.ts` - Determines local vs global storage location
|
|
29
|
+
- `file-lock.ts` - Cross-process file locking with exponential backoff
|
|
30
|
+
- `src/models/` - Domain logic
|
|
31
|
+
- `task.ts` - Task operations (create, find, dependency checking)
|
|
32
|
+
- `id-generator.ts` - Generates `{7-char-hash}-{sequence}` task IDs
|
|
33
|
+
- `src/types.ts` - TypeScript interfaces for Task, TasksFile, Config
|
|
34
|
+
- `src/errors.ts` - Custom error classes (ChopError subclasses)
|
|
35
|
+
- `src/config/paths.ts` - Path resolution for storage files
|
|
36
|
+
- `src/utils/` - Display formatting and interactive prompts
|
|
37
|
+
|
|
38
|
+
**Concurrency**: All task modifications use `withLock()` for atomic read-modify-write operations. The `TaskStore.atomicUpdate()` method is the primary way to safely modify tasks.
|
|
39
|
+
|
|
40
|
+
**Storage**: Tasks stored in `tasks.json`, archived tasks in `tasks.archived.json`. Project identification uses git remote URL or repo path.
|
|
41
|
+
|
|
42
|
+
## Specification
|
|
43
|
+
|
|
44
|
+
See `specs/chop.md` for the complete CLI specification including all commands, options, and behaviors.
|
package/README.md
ADDED
package/index.ts
ADDED