-
Notifications
You must be signed in to change notification settings - Fork 0
feat: add ai agent configs, ci improvements, and csv/md export #12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| # subtrack | ||
|
|
||
| Monorepo for **subtrack** — a CLI tool to manage subscription services from the terminal. | ||
|
|
||
| ## Repository Structure | ||
|
|
||
| ``` | ||
| . | ||
| ├── subtrack/ # CLI tool (Node.js/TypeScript, published as npm package) | ||
| ├── docs/ # Documentation site (SvelteKit) | ||
| ├── .agents/ # OpenCode agent and skill definitions | ||
| ├── .claude/ # Claude Code configuration | ||
| ├── flake.nix # Nix devShell / CI shell | ||
| └── pnpm-workspace.yaml | ||
| ``` | ||
|
|
||
| ## Package Management | ||
|
|
||
| - Use `pnpm add <package>` instead of `npm install <package>` or `bun add <package>` | ||
| - Use `pnpm remove <package>` instead of `npm uninstall <package>` or `bun remove <package>` | ||
| - Use `pnpm update` instead of `npm update` or `bun update` | ||
| - Use `pnpm run <script>` or `pnpm <script>` instead of `npm run` or `bun run` | ||
| - Use `pnpmx <package>` instead of `npx` or `bunx` | ||
|
|
||
| ## Root Commands | ||
|
|
||
| ```bash | ||
| pnpm install # install all workspace dependencies | ||
| pnpm build # build all packages (pnpm -r build) | ||
| pnpm test # test all packages (pnpm -r test) | ||
| ``` | ||
|
|
||
| ## Nix DevShell | ||
|
|
||
| ```bash | ||
| nix develop # enter devShell (node, pnpm, typos, typescript, nixfmt) | ||
| nix fmt # format nix files (nixfmt-rfc-style) | ||
| ``` | ||
|
|
||
| ## Key Conventions | ||
|
|
||
| - **Runtime**: Node.js, **NOT** Bun or Deno | ||
| - **Language**: TypeScript (strict mode, ESM, `verbatimModuleSyntax`) | ||
| - **Database**: `sql.js` (SQLite via WASM), **NOT** `better-sqlite3` or `bun:sqlite` | ||
| - **Node built-ins**: Use `node:` prefix (`node:fs`, `node:path`, `node:os`) | ||
| - **Local imports**: Use `.ts` extension (`import { x } from "./foo.ts"`) | ||
| - **Type imports**: Use `type` prefix (`import type { X } from "./foo.ts"`) | ||
| - **No semicolons** in imports/exports | ||
| - **API**: Prefer native `fetch` for HTTP, native `WebSocket` for WebSocket | ||
|
|
||
| ## Architecture (subtrack/src/) | ||
|
|
||
| | Layer | File | Responsibility | | ||
| |---|---|---| | ||
| | Entry | `index.ts` | CLI definition (commander), command routing | | ||
| | Commands | `commands.ts` | Command handlers, workflow logic, user interaction | | ||
| | Database | `db.ts` | SQLite CRUD, schema, persistence | | ||
| | Display | `display.ts` | Table rendering with cli-table3, FX rate conversion | | ||
| | Prompts | `prompts.ts` | Input validation, interactive prompts | | ||
|
|
||
| ## Environment Variables | ||
|
|
||
| | Variable | Description | | ||
| |---|---| | ||
| | `SUBSC_CLI_DB_DIR` | Override database directory (default: `~/.config/subtrack`) | | ||
|
|
||
| ## Links | ||
|
|
||
| - **Published package**: `subtrack` on npm | ||
| - **GitHub**: https://github.com/nazozokc/subtrack | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1 @@ | ||
| .agents/skills/ | ||
| ../.agents/skills |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| # subtrack — GitHub Copilot Instructions | ||
|
|
||
| ## Project | ||
|
|
||
| A CLI tool to manage subscription services from the terminal. Node.js + TypeScript, SQLite via | ||
| sql.js (WASM). | ||
|
|
||
| ## Tech Stack | ||
|
|
||
| - **Runtime**: Node.js (not Bun or Deno) | ||
| - **Language**: TypeScript (strict mode, ESM, `verbatimModuleSyntax`) | ||
| - **Database**: `sql.js` (SQLite via WASM) | ||
| - **CLI**: `commander` | ||
| - **Prompts**: `@inquirer/prompts` | ||
| - **Logging**: `consola` | ||
| - **Tables**: `cli-table3` | ||
| - **Build**: `tsdown` | ||
| - **Test**: `vitest` | ||
|
|
||
| ## Architecture (4-layer separation) | ||
|
|
||
| | Layer | File | Responsibility | | ||
| |-------------|-------------------|---------------------------------------------| | ||
| | Entry | `src/index.ts` | CLI definition (commander), command routing | | ||
| | Commands | `src/commands.ts` | Command handlers, workflow logic | | ||
| | Database | `src/db.ts` | SQLite CRUD, schema, persistence | | ||
| | Display | `src/display.ts` | Table rendering, FX rate conversion | | ||
| | Prompts | `src/prompts.ts` | Input validation, interactive prompts | | ||
|
|
||
| ## Key Conventions | ||
|
|
||
| - **Local imports**: use `.ts` extension (`import { x } from "./foo.ts"`) | ||
| - **Node built-ins**: use `node:` prefix (`node:fs`, `node:path`) | ||
| - **Type imports**: use `type` prefix (`import type { X } from "./foo.ts"`) | ||
| - **No semicolons** in imports/exports | ||
| - **Prices**: stored as integers (smallest unit: JPY has no decimal, USD stored as cents) | ||
| - **Cycles**: weekly / bi-weekly / monthly / quarterly / semi-annual / yearly | ||
| - **DB transactions**: always use `BEGIN TRANSACTION` / `COMMIT` / `ROLLBACK` | ||
| - **PRAGMA**: `PRAGMA foreign_keys = ON` at connection time | ||
|
|
||
| ## Environment Variables | ||
|
|
||
| - `SUBSC_CLI_DB_DIR`: override database directory (default: `~/.config/subtrack/`) | ||
| - Data file: `subtrack.db` (SQLite) | ||
|
|
||
| ## Commands | ||
|
|
||
| | Command | Description | | ||
| |------------------------------------|------------------------------------| | ||
| | `subtrack list` | List all subscriptions | | ||
| | `subtrack add` | Add a subscription | | ||
| | `subtrack delete` | Delete subscriptions (interactive) | | ||
| | `subtrack tags <taglist...>` | Filter by tags | | ||
| | `subtrack backup <destination>` | Backup database | | ||
| | `subtrack payment [period]` | Show payment totals | | ||
| | `subtrack export csv` | Export subscriptions as CSV | | ||
| | `subtrack export md` | Export subscriptions as Markdown | |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,22 +5,28 @@ on: | |
| branches: [main] | ||
| paths: | ||
| - "subtrack/**" | ||
| - ".github/workflows/app-ci.yml" | ||
| - ".github/**" | ||
| - ".agents/**" | ||
| - ".claude/**" | ||
| - "opencode.jsonc" | ||
| - "pnpm-workspace.yaml" | ||
| - "package.json" | ||
| pull_request: | ||
| branches: [main] | ||
| paths: | ||
| - "subtrack/**" | ||
| - ".github/workflows/app-ci.yml" | ||
| - ".github/**" | ||
| - ".agents/**" | ||
| - ".claude/**" | ||
| - "opencode.jsonc" | ||
| - "pnpm-workspace.yaml" | ||
| - "package.json" | ||
|
|
||
| jobs: | ||
| test: | ||
| strategy: | ||
| matrix: | ||
| node-version: [22, 23] | ||
| node-version: [22, 23, 24] | ||
| os: [ubuntu-latest, macos-latest, windows-latest] | ||
| runs-on: ${{ matrix.os }} | ||
|
|
||
|
|
@@ -59,9 +65,21 @@ jobs: | |
| working-directory: ./subtrack | ||
| run: pnpm start list | ||
|
|
||
| - name: Verify CLI export csv command | ||
| working-directory: ./subtrack | ||
| run: pnpm start export csv | ||
|
|
||
| - name: Verify CLI export md command | ||
| working-directory: ./subtrack | ||
| run: pnpm start export md | ||
|
|
||
| - name: Spell check with typos | ||
| if: runner.os == 'Linux' | ||
| uses: crate-ci/typos@v1.47.2 | ||
| with: | ||
| files: subtrack/ | ||
| config: typos.toml | ||
|
Comment on lines
76
to
81
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Pin GitHub Action to a commit SHA. Line 82 uses Suggested hardening- - name: Spell check with typos
- if: runner.os == 'Linux'
- uses: crate-ci/typos@v1.47.2
+ - name: Spell check with typos
+ if: runner.os == 'Linux'
+ uses: crate-ci/typos@<full_commit_sha>
with:
files: subtrack/
config: typos.toml🧰 Tools🪛 zizmor (1.25.2)[error] 82-82: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy) (unpinned-uses) 🤖 Prompt for AI AgentsSource: Linters/SAST tools |
||
|
|
||
| - name: Audit dependencies | ||
| if: runner.os == 'Linux' | ||
| run: pnpm audit --audit-level=high | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| { | ||
| "$schema": "https://opencode.ai/config.json", | ||
| "skills": [ | ||
| { | ||
| "name": "subtrack-rules", | ||
| "description": "When working on the subtrack project, editing source code, running tests, or managing dependencies", | ||
| "location": "file:///home/nazozokc/ghq/github.com/nazozokc/subtrack/.agents/skills/subtrack-rules/SKILL.md" | ||
| }, | ||
| { | ||
| "name": "subtrack-commit", | ||
| "description": "When committing changes to subtrack, creating PRs, or pushing to remote", | ||
| "location": "file:///home/nazozokc/ghq/github.com/nazozokc/subtrack/.agents/skills/subtrack-commit/SKILL.md" | ||
|
Comment on lines
+7
to
+12
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 Result: In OpenCode, when defining custom skill locations via the Citations:
Use repository-relative paths for skill locations instead of absolute machine-specific paths Lines 7 and 12 hard-code 🤖 Prompt for AI Agents |
||
| } | ||
| ], | ||
| "permissions": [ | ||
| { | ||
| "type": "read", | ||
| "path": "." | ||
| }, | ||
| { | ||
| "type": "write", | ||
| "path": "subtrack/src/**" | ||
| }, | ||
| { | ||
| "type": "write", | ||
| "path": ".github/**" | ||
| }, | ||
| { | ||
| "type": "write", | ||
| "path": ".agents/**" | ||
| }, | ||
| { | ||
| "type": "write", | ||
| "path": ".claude/**" | ||
| }, | ||
| { | ||
| "type": "exec", | ||
| "command": "pnpm *" | ||
| }, | ||
| { | ||
| "type": "exec", | ||
| "command": "nix *" | ||
| }, | ||
| { | ||
| "type": "exec", | ||
| "command": "git *" | ||
| }, | ||
| { | ||
| "type": "exec", | ||
| "command": "typos" | ||
| } | ||
| ] | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,42 +1,75 @@ | ||
| # subtrack | ||
|
|
||
| Use pnpm for package management and Node.js for runtime. | ||
| CLI tool to manage subscription services from the terminal. Node.js + TypeScript. | ||
|
|
||
| ## Tech Stack | ||
|
|
||
| - **Runtime**: Node.js (not Bun or Deno) | ||
| - **Language**: TypeScript (strict mode, ESM, `verbatimModuleSyntax`) | ||
| - **Database**: `sql.js` (SQLite via WASM) | ||
| - **CLI**: `commander` | ||
| - **Prompts**: `@inquirer/prompts` | ||
| - **Logging**: `consola` | ||
| - **Tables**: `cli-table3` | ||
| - **Build**: `tsdown` | ||
| - **Test**: `vitest` | ||
|
|
||
| ## Package Management | ||
|
|
||
| - Use `pnpm add <package>` instead of `npm install <package>` or `bun add <package>` | ||
| - Use `pnpm remove <package>` instead of `npm uninstall <package>` or `bun remove <package>` | ||
| - Use `pnpm update` instead of `npm update` or `bun update` | ||
| - Use `pnpm run <script>` or `pnpm <script>` instead of `npm run` or `bun run` | ||
| - Use `pnpm add <package>` | ||
| - Use `pnpm remove <package>` | ||
| - Use `pnpm update` | ||
| - Use `pnpm run <script>` or `pnpm <script>` | ||
| - Use `pnpmx <package>` instead of `npx` or `bunx` | ||
|
|
||
| ## Running | ||
|
|
||
| - Use `tsx src/index.ts` for development (TypeScript execution) | ||
| - Use `tsdown` for building (already configured in scripts.build) | ||
| - Don't use `bun` or `node --loader` for running TypeScript directly | ||
| - Development: `pnpm start` (tsx src/index.ts) | ||
| - Build: `pnpm build` (tsdown) | ||
| - Output: `dist/index.mjs` | ||
|
|
||
| ## Testing | ||
|
|
||
| - Use `vitest` for running tests (`pnpm test` or `vitest run`) | ||
| - Test files are co-located next to source files as `*.test.ts` | ||
| - Use `pnpm test:watch` for watch mode | ||
| - `pnpm test` (vitest) | ||
| - Test files co-located as `*.test.ts` | ||
| - Use `__setDb()` from `db.ts` to inject in-memory SQLite for tests | ||
| - Mock `consola` via `consola.mockTypes()` | ||
| - Mock `globalThis.fetch` for FX rate API | ||
|
|
||
| ## Architecture (4 layers) | ||
|
|
||
| | Layer | File | Responsibility | | ||
| |---|---|---| | ||
| | Entry | `src/index.ts` | CLI definition (commander), command routing | | ||
| | Commands | `src/commands.ts` | Command handlers, workflow logic | | ||
| | Database | `src/db.ts` | SQLite CRUD, schema, persistence | | ||
| | Display | `src/display.ts` | Table rendering, FX rate conversion | | ||
| | Prompts | `src/prompts.ts` | Input validation, interactive prompts | | ||
|
|
||
| ## SQLite | ||
| ## Key Conventions | ||
|
|
||
| - Use `better-sqlite3` for SQLite | ||
| - Don't use `sqlite` (the npm package) or `bun:sqlite` | ||
| - Import: `import Database from "better-sqlite3"` | ||
| - **Local imports**: `.ts` extension (`import { x } from "./foo.ts"`) | ||
| - **Node built-ins**: `node:` prefix (`node:fs`, `node:path`, `node:os`) | ||
| - **Type imports**: `type` prefix (`import type { X } from "./foo.ts"`) | ||
| - **No semicolons** in imports/exports | ||
| - **Prices**: integers (smallest unit — JPY no decimal, USD cents) | ||
| - **Cycles**: weekly / bi-weekly / monthly / quarterly / semi-annual / yearly | ||
| - **DB**: `sql.js` with `PRAGMA foreign_keys = ON`, use transactions for multi-step writes | ||
|
|
||
| ## APIs | ||
| ## Environment Variables | ||
|
|
||
| - Prefer `node:fs` for file system operations | ||
| - Prefer `node:path` for path operations | ||
| - Prefer `node:os` for OS-level operations | ||
| - Prefer native `fetch` for HTTP requests | ||
| - Prefer native `WebSocket` for WebSocket connections | ||
| | Variable | Description | | ||
| |---|---| | ||
| | `SUBSC_CLI_DB_DIR` | Override database directory (default: `~/.config/subtrack`) | | ||
|
|
||
| ## Code Style | ||
| ## Commands | ||
|
|
||
| - TypeScript with strict mode enabled | ||
| - ESM modules (`"type": "module"` in package.json) | ||
| - No semicolons in imports/exports | ||
| | Command | Description | | ||
| |---|---| | ||
| | `subtrack list` | List all subscriptions | | ||
| | `subtrack add` | Add a subscription | | ||
| | `subtrack delete` | Delete subscriptions (interactive) | | ||
| | `subtrack tags <taglist...>` | Filter by tags | | ||
| | `subtrack backup <destination>` | Backup database | | ||
| | `subtrack payment [period]` | Show payment totals | | ||
| | `subtrack export csv` | Export subscriptions as CSV | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a language tag to the fenced code block
The code fence starting at Line 7 has no language identifier (MD040).
Suggested fix
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.claude/CLAUDE.md around lines 7 - 15, The fenced code block displaying the
directory structure (starting with the
.symbol and containing the subtrack/,docs/, .agents/, .claude/, flake.nix, and pnpm-workspace.yaml entries) is
missing a language identifier tag, which violates the MD040 rule. Add "text" as
the language identifier immediately after the opening triple backticks (```) of
this code fence to properly indicate the code block type and satisfy the
markdown linting requirement.