@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.
Files changed (42) hide show
  1. package/.claude/rules/use-bun-instead-of-node-vite-npm-pnpm.md +109 -0
  2. package/.claude/settings.local.json +12 -0
  3. package/.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc +111 -0
  4. package/.devcontainer/Dockerfile +102 -0
  5. package/.devcontainer/devcontainer.json +58 -0
  6. package/.devcontainer/init-firewall.sh +137 -0
  7. package/.github/workflows/publish.yml +76 -0
  8. package/CLAUDE.md +44 -0
  9. package/README.md +15 -0
  10. package/index.ts +2 -0
  11. package/loop.sh +206 -0
  12. package/package.json +27 -0
  13. package/specs/chop.md +313 -0
  14. package/src/commands/add.ts +74 -0
  15. package/src/commands/archive.ts +72 -0
  16. package/src/commands/completion.ts +232 -0
  17. package/src/commands/done.ts +38 -0
  18. package/src/commands/edit.ts +228 -0
  19. package/src/commands/init.ts +72 -0
  20. package/src/commands/list.ts +48 -0
  21. package/src/commands/move.ts +92 -0
  22. package/src/commands/pop.ts +45 -0
  23. package/src/commands/purge.ts +41 -0
  24. package/src/commands/show.ts +32 -0
  25. package/src/commands/status.ts +43 -0
  26. package/src/config/paths.ts +61 -0
  27. package/src/errors.ts +56 -0
  28. package/src/index.ts +41 -0
  29. package/src/models/id-generator.ts +39 -0
  30. package/src/models/task.ts +98 -0
  31. package/src/storage/file-lock.ts +124 -0
  32. package/src/storage/storage-resolver.ts +63 -0
  33. package/src/storage/task-store.ts +173 -0
  34. package/src/types.ts +42 -0
  35. package/src/utils/display.ts +139 -0
  36. package/src/utils/git.ts +80 -0
  37. package/src/utils/prompts.ts +88 -0
  38. package/tests/errors.test.ts +86 -0
  39. package/tests/models/id-generator.test.ts +46 -0
  40. package/tests/models/task.test.ts +186 -0
  41. package/tests/storage/file-lock.test.ts +152 -0
  42. 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,12 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "WebSearch",
5
+ "Bash(bun run:*)",
6
+ "Bash(bun test:*)",
7
+ "Bash(bunx tsc:*)",
8
+ "WebFetch(domain:github.com)",
9
+ "Bash(gh pr view:*)"
10
+ ]
11
+ }
12
+ }
@@ -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
@@ -0,0 +1,15 @@
1
+ # chop
2
+
3
+ To install dependencies:
4
+
5
+ ```bash
6
+ bun install
7
+ ```
8
+
9
+ To run:
10
+
11
+ ```bash
12
+ bun run index.ts
13
+ ```
14
+
15
+ This project was created using `bun init` in bun v1.3.4. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.
package/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env bun
2
+ import "./src/index.ts";