diff --git a/.github/workflows/release-cli.yml b/.github/workflows/release-cli.yml index 2109a89b..6d9a889b 100644 --- a/.github/workflows/release-cli.yml +++ b/.github/workflows/release-cli.yml @@ -94,6 +94,89 @@ jobs: tdn-cli/dist/tdn-${{ matrix.target }}.zip.sha256 if-no-files-found: error + publish-npm: + name: Publish to npm + needs: build + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 24.x + # Do NOT set registry-url — it breaks OIDC trusted publishing + + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + path: artifacts + merge-multiple: true + + - name: Extract version from tag + id: version + run: echo "version=${GITHUB_REF#refs/tags/tdn-cli-v}" >> "$GITHUB_OUTPUT" + + - name: Publish platform packages + run: | + VERSION="${{ steps.version.outputs.version }}" + + publish_unix() { + local target="$1" + echo "Publishing @taskdn/cli-${target}@${VERSION}..." + tar -xzf "../artifacts/tdn-${target}.tar.gz" -C "npm/cli-${target}/" + chmod +x "npm/cli-${target}/tdn" + node -e " + const fs = require('fs'); + const p = JSON.parse(fs.readFileSync('npm/cli-${target}/package.json')); + p.version = '${VERSION}'; + fs.writeFileSync('npm/cli-${target}/package.json', JSON.stringify(p, null, 2) + '\n'); + " + cd "npm/cli-${target}" + npm publish --access public --provenance + cd ../.. + } + + publish_windows() { + local artifact_target="$1" + local npm_target="$2" + echo "Publishing @taskdn/cli-${npm_target}@${VERSION}..." + unzip -o "../artifacts/tdn-${artifact_target}.zip" -d "npm/cli-${npm_target}/" + node -e " + const fs = require('fs'); + const p = JSON.parse(fs.readFileSync('npm/cli-${npm_target}/package.json')); + p.version = '${VERSION}'; + fs.writeFileSync('npm/cli-${npm_target}/package.json', JSON.stringify(p, null, 2) + '\n'); + " + cd "npm/cli-${npm_target}" + npm publish --access public --provenance + cd ../.. + } + + publish_unix darwin-arm64 + publish_unix darwin-x64 + publish_unix linux-arm64 + publish_unix linux-x64 + publish_windows windows-x64 win32-x64 + + - name: Publish main wrapper package + run: | + VERSION="${{ steps.version.outputs.version }}" + echo "Publishing @taskdn/cli@${VERSION}..." + node -e " + const fs = require('fs'); + const p = JSON.parse(fs.readFileSync('npm/cli/package.json')); + p.version = '${VERSION}'; + for (const dep of Object.keys(p.optionalDependencies || {})) { + p.optionalDependencies[dep] = '${VERSION}'; + } + fs.writeFileSync('npm/cli/package.json', JSON.stringify(p, null, 2) + '\n'); + " + cd npm/cli + npm publish --access public --provenance + release: name: Create GitHub Release needs: build diff --git a/docs/tasks-todo/task-x-claude-cowork-integration.md b/docs/tasks-todo/task-x-claude-cowork-integration.md index 9a7493b8..40bf1bf1 100644 --- a/docs/tasks-todo/task-x-claude-cowork-integration.md +++ b/docs/tasks-todo/task-x-claude-cowork-integration.md @@ -35,10 +35,21 @@ Proof of concept tested on Linux ARM64 (2026-03-14): ### Revised Approach: Install at Session Start -Instead of bundling, have Claude install `tdn` from GitHub Releases at the start of each Cowork session. The user mounts their vault folders, and the skill guides Claude through setup. +Instead of bundling, have Claude install `tdn` at the start of each Cowork session. The user mounts their vault folders, and the skill guides Claude through setup. + +**Primary method: npm** (preferred since `registry.npmjs.org` is on Cowork's default allowlist): +```bash +npm install -g @taskdn/cli +``` + +**Fallback: GitHub Releases** (if npm is unavailable): +```bash +curl -fsSL https://github.com/dannysmith/taskdn/releases/latest/download/install.sh | bash +``` **Why this works:** -- `curl` and `tar` are available in the Cowork VM +- npm's registry is on the Cowork allowlist (GitHub release assets are blocked) +- `curl` and `tar` are available as a fallback - The install script already supports `linux-arm64` - Local `.taskdn.json` in the working directory takes precedence over global config - No changes needed to the binary or release pipeline @@ -103,13 +114,16 @@ Instead of bundling, have Claude install `tdn` from GitHub Releases at the start 2. Claude checks: which tdn ├─ Found → normal flow (Claude Code) └─ Not found → Cowork setup: - a. Install: curl -fsSL | bash + a. Try npm: npm install -g @taskdn/cli + ├─ Success → continue + └─ Failure → try curl + b. Try curl: curl -fsSL | bash ├─ Success → continue └─ Failure → look for binary in mounted dirs - b. Discover vault dirs in mounted paths - c. Create .taskdn.json with discovered paths - d. Verify: tdn config --ai - e. Continue with normal priming + c. Discover vault dirs in mounted paths + d. Create .taskdn.json with discovered paths + e. Verify: tdn config --ai + f. Continue with normal priming ``` ## Path Mapping Reference @@ -149,5 +163,5 @@ Just mount directories and work with files directly. Acceptable as degraded fall ## Open Questions -1. **Internet in Cowork VM** — Can the VM always reach GitHub to download releases? If not, the "pre-download binary" fallback becomes the primary path. +1. ~~**Internet in Cowork VM** — Can the VM always reach GitHub to download releases?~~ **Resolved:** The Cowork proxy blocks `release-assets.githubusercontent.com` but allows `registry.npmjs.org`. npm install is now the primary method; curl install script is the fallback for environments where npm isn't available. 2. **Install location persistence** — Does `~/.local/bin/` persist across Cowork sessions or is it wiped? If wiped, install runs every session (acceptable — takes seconds). diff --git a/docs/tasks-todo/task-x-npm-distribution.md b/docs/tasks-todo/task-x-npm-distribution.md index a8ceebcf..a4fb1f4e 100644 --- a/docs/tasks-todo/task-x-npm-distribution.md +++ b/docs/tasks-todo/task-x-npm-distribution.md @@ -85,122 +85,67 @@ Instead of storing an `NPM_TOKEN` secret, configure each package on npmjs.com to ## Implementation -### Phase 1: npm Org & Package Setup (manual) +### Phase 1: npm Org & Package Setup (manual) ✅ -- [ ] Create the `@taskdn` org on npmjs.com -- [ ] Delete (or deprecate) the old `taskdn-sdk` package -- [ ] Create placeholder packages to reserve the names — each needs an initial publish before trusted publishing can be configured: +- [x] Create the `@taskdn` org on npmjs.com +- [x] Delete (or deprecate) the old `taskdn-sdk` package +- [x] Create placeholder packages (v0.0.0) to reserve the names: - `@taskdn/cli` - `@taskdn/cli-darwin-arm64` - `@taskdn/cli-darwin-x64` - `@taskdn/cli-linux-arm64` - `@taskdn/cli-linux-x64` - `@taskdn/cli-win32-x64` -- [ ] Configure trusted publishing on each package: +- [x] Configure trusted publishing on each package: - Repository owner: `dannysmith` - Repository: `taskdn` - Workflow: `release-cli.yml` -### Phase 2: Create npm Package Files +### Phase 2: Create npm Package Files ✅ -Create the `tdn-cli/npm/` directory structure. +Created `tdn-cli/npm/` directory structure with: -**For each platform package** (`cli-darwin-arm64`, etc.): +- [x] 5 platform `package.json` files with `os`/`cpu`/`preferUnplugged` fields +- [x] Main `@taskdn/cli` `package.json` with `bin`, `optionalDependencies`, `engines` +- [x] `bin/tdn` wrapper script using `spawnSync` with passthrough stdio -- [ ] Create `package.json` with: - - `name`, `version` (matching CLI version) - - `os` and `cpu` fields - - `description`, `license`, `repository` - - `preferUnplugged: true` (for Yarn PnP compatibility) - - No `bin` field (main package handles that) -- [ ] The binary itself is NOT committed — CI copies it in at build time +### Phase 3: Update Release Workflow ✅ -**For the main package** (`cli/`): +- [x] Added `publish-npm` job to `release-cli.yml` (parallel with `release`, both need `build`) +- [x] OIDC trusted publishing via `permissions.id-token: write` +- [x] `actions/setup-node@v4` with `node-version: 24.x` (no `registry-url`) +- [x] Extracts binaries from build artifacts, sets versions, publishes platform packages then main wrapper -- [ ] Create `package.json` with: - - `name: "@taskdn/cli"` - - `bin: { "tdn": "bin/tdn" }` - - `optionalDependencies` listing all 5 platform packages at the same version - - `engines: { "node": ">=18" }` -- [ ] Create `bin/tdn` wrapper script (Node.js, ~50 lines): - - Platform/arch detection via `process.platform` and `process.arch` - - Map to package name - - `require.resolve()` to find the binary - - `execFileSync()` to run it with passthrough stdio - - Clear error message if no matching platform package is installed +### Phase 4: Update prepare-release Script ✅ -### Phase 3: Update Release Workflow +- [x] `prepare-release.js` now bumps versions in all `npm/*/package.json` files +- [x] Added npm publish to post-push output messages -Extend `.github/workflows/release-cli.yml` with a new `publish-npm` job. The workflow structure becomes: +### Phase 5: Fix Existing URL Issues ✅ -``` -build (existing matrix, unchanged) -├── publish-npm (new, needs: build) ← publishes all 6 npm packages -└── release (existing, needs: build) ← GitHub Release + Homebrew -``` - -`publish-npm` and `release` run in parallel — both only need `build` to complete. - -**New `publish-npm` job:** - -- [ ] Add job with `needs: build`, `runs-on: ubuntu-latest` -- [ ] Set `permissions.id-token: write` (required for trusted publishing OIDC) -- [ ] Use `actions/setup-node@v4` with `node-version: 24.x` (do NOT set `registry-url` — it breaks OIDC) -- [ ] Download all build artifacts -- [ ] Extract version from git tag -- [ ] For each platform package: - - Copy the binary from the artifact into `npm/cli-/` - - Set the version in its `package.json` - - Run `npm publish --access public` -- [ ] After all platform packages are published: - - Set the version + `optionalDependencies` versions in `npm/cli/package.json` - - Run `npm publish --access public` for the main `@taskdn/cli` package -- [ ] Ensure the existing `release` job (GitHub Release + Homebrew) is unaffected - -### Phase 4: Update prepare-release Script - -- [ ] Update `tdn-cli/scripts/prepare-release.js` to also bump versions in all `npm/*/package.json` files -- [ ] Fix repo URL: `taskdn/taskdn` → `dannysmith/taskdn` in the script's output messages - -### Phase 5: Fix Existing URL Issues - -These are wrong in multiple places and should be fixed regardless of npm publishing. - -- [ ] `tdn-cli/scripts/install.sh` line 16: `REPO="taskdn/taskdn"` → `REPO="dannysmith/taskdn"` -- [ ] `tdn-claude-plugin/commands/prime.md` line 13: fix GitHub URL to `dannysmith/taskdn` -- [ ] `tdn-claude-plugin/skills/task-management/cowork.md` line 28: fix GitHub URL -- [ ] `tdn-cli/scripts/prepare-release.js` lines 132-136: fix GitHub URLs - -### Phase 6: Update Claude Plugin for npm Install - -Update the plugin's install flow to prefer npm when available. +- [x] `tdn-cli/scripts/install.sh`: `REPO="dannysmith/taskdn"` +- [x] `tdn-claude-plugin/commands/prime.md`: fixed GitHub URL +- [x] `tdn-claude-plugin/skills/task-management/cowork.md`: fixed GitHub URL +- [x] `tdn-cli/scripts/prepare-release.js`: fixed GitHub URLs -- [ ] Update `tdn-claude-plugin/commands/prime.md`: - - Step 3a: try `npm install -g @taskdn/cli` first - - Step 3b: fall back to curl install script (with corrected URL) - - Step 3c: search mounted dirs for binary - - Step 3d: fall back to direct file access -- [ ] Update `tdn-claude-plugin/skills/task-management/cowork.md`: - - Primary method: `npm install -g @taskdn/cli` - - Fallback: curl install script - - Fallback: pre-placed binary in mounted folder - - Fallback: direct file access (degraded mode) -- [ ] Update `docs/tasks-todo/task-x-claude-cowork-integration.md` to reflect npm as the primary Cowork install method +### Phase 6: Update Claude Plugin for npm Install ✅ -### Phase 7: Dual-Install Conflict Detection +- [x] Updated `tdn-claude-plugin/commands/prime.md`: npm first, curl fallback, mounted binary fallback, direct file access fallback +- [x] Updated `tdn-claude-plugin/skills/task-management/cowork.md`: npm as primary install method with curl and mounted binary fallbacks +- [x] Updated `docs/tasks-todo/task-x-claude-cowork-integration.md`: npm as primary method, resolved open question about Cowork proxy -Users might have `tdn` installed via both Homebrew and npm, which can cause confusion about which binary is being used. +### Phase 7: Dual-Install Conflict Detection ✅ -- [ ] Research how other CLI tools handle this (e.g. how `biome`, `eslint`, `prettier` handle Homebrew vs npm coexistence) -- [ ] Add a check in the npm wrapper script (`bin/tdn`): if `tdn` exists at a Homebrew path (e.g. from `brew --prefix`), print a warning explaining the situation and which binary is being used -- [ ] Consider whether the Homebrew formula should do the inverse check — likely not worth it since Homebrew formulas can't easily detect npm globals, and the npm wrapper is the more common entry point +- [x] Most tools (biome, esbuild, etc.) don't cross-check — PATH order determines which runs +- [x] Added macOS-only check in `bin/tdn`: warns if Homebrew `tdn` exists at `/opt/homebrew/bin/tdn` or `/usr/local/bin/tdn`, suppressible via `TDN_NO_DUAL_WARN=1` +- [x] Homebrew formula does not need an inverse check (can't easily detect npm globals) -### Phase 8: Update Documentation +### Phase 8: Update Documentation ✅ -- [ ] Update `tdn-cli/README.md`: add npm as an installation method alongside Homebrew and the install script -- [ ] Update `tdn-cli/docs/developer/releases.md`: document the npm publishing step in the release process -- [ ] Update `website/` user-facing docs: add npm installation instructions to the CLI installation page -- [ ] Update `tdn-claude-plugin/README.md`: mention npm install as the primary method for Cowork environments +- [x] `tdn-cli/README.md`: added npm as first installation method +- [x] `tdn-cli/docs/developer/releases.md`: documented npm publishing step, prerequisites, troubleshooting +- [x] `website/src/content/docs/cli/overview.mdx`: already had npm tab (no change needed) +- [x] `tdn-claude-plugin/README.md`: updated Cowork section to list npm as primary install method ## Testing diff --git a/tdn-claude-plugin/.claude-plugin/plugin.json b/tdn-claude-plugin/.claude-plugin/plugin.json index abda6d59..6148d18e 100644 --- a/tdn-claude-plugin/.claude-plugin/plugin.json +++ b/tdn-claude-plugin/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "tdn", - "version": "0.1.1", + "version": "0.1.2", "description": "Task and project management with the tdn CLI. Helps Claude work as a productivity assistant, managing tasks, projects, and areas stored as markdown files on disk.", "author": { "name": "Danny Smith" diff --git a/tdn-claude-plugin/README.md b/tdn-claude-plugin/README.md index dc3e552e..3e16f0ec 100644 --- a/tdn-claude-plugin/README.md +++ b/tdn-claude-plugin/README.md @@ -115,16 +115,17 @@ You must **share/mount your vault folders** with the Cowork session: When you run `/tdn:prime` (or ask about your tasks), Claude will: 1. Detect that `tdn` isn't installed -2. Download and install it automatically from GitHub Releases -3. Discover your mounted vault directories -4. Create a local config file pointing to them -5. Proceed normally +2. Install it via `npm install -g @taskdn/cli` (preferred — npm registry is on the Cowork allowlist) +3. If npm fails, fall back to downloading from GitHub Releases +4. Discover your mounted vault directories +5. Create a local config file pointing to them +6. Proceed normally -If the download fails (e.g. no internet), Claude will look for a `tdn` binary you've placed in one of your mounted folders. If no binary is available at all, Claude falls back to working with your task files directly. +If both install methods fail, Claude will look for a `tdn` binary you've placed in one of your mounted folders. If no binary is available at all, Claude falls back to working with your task files directly. -### Pre-downloading the Binary (Optional) +### Pre-placing the Binary (Optional) -If your Cowork sessions don't have internet access, download the Linux ARM64 binary from [GitHub Releases](https://github.com/taskdn/taskdn/releases) and place it in one of the folders you share with Cowork. +If neither npm nor the install script works, download the Linux ARM64 binary from [GitHub Releases](https://github.com/dannysmith/taskdn/releases) and place it in one of the folders you share with Cowork. ### Notes diff --git a/tdn-claude-plugin/commands/prime.md b/tdn-claude-plugin/commands/prime.md index c128e000..788fd0b5 100644 --- a/tdn-claude-plugin/commands/prime.md +++ b/tdn-claude-plugin/commands/prime.md @@ -1,6 +1,6 @@ --- description: Prime the current session with general context from taskdn and load the task-management skill -allowed-tools: Bash(tdn:*), Bash(which:*), Bash(curl:*), Bash(export:*), Bash(find:*), Bash(ls:*), Bash(cat:*), Bash(echo:*), Skill(tdn:task-management) +allowed-tools: Bash(tdn:*), Bash(which:*), Bash(npm:*), Bash(curl:*), Bash(export:*), Bash(find:*), Bash(ls:*), Bash(cat:*), Bash(echo:*), Skill(tdn:task-management) --- This session is being primed with context on the user's personal task management system: tasks, projects and life areas. Do this: @@ -10,11 +10,12 @@ This session is being primed with context on the user's personal task management - **If found** → skip to step 4. - **If not found** → continue to step 3. 3. Set up tdn for this environment (likely Claude Cowork). See [cowork.md](../skills/task-management/cowork.md) for full details. In short: - a. Install: `curl -fsSL https://github.com/taskdn/taskdn/releases/latest/download/install.sh | bash && export PATH="$PATH:$HOME/.local/bin"` - b. If install fails, search mounted dirs for a `tdn` binary: `find /sessions/*/mnt/ /mnt/ -name 'tdn' -o -name 'tdn-linux-arm64' 2>/dev/null` - c. Discover vault directories in mounted paths (look for `tasks`, `projects`, `areas` folders) - d. Create a `.taskdn.json` in the current directory with the discovered paths - e. Verify with `tdn config --ai` - f. If none of this works, tell the user and fall back to direct file access using the skill's specification and templates docs. + a. Try npm: `npm install -g @taskdn/cli && export PATH="$PATH:$(npm prefix -g)/bin"` + b. If npm fails, try curl: `curl -fsSL https://github.com/dannysmith/taskdn/releases/latest/download/install.sh | bash && export PATH="$PATH:$HOME/.local/bin"` + c. If both fail, search mounted dirs for a `tdn` binary: `find /sessions/*/mnt/ /mnt/ -name 'tdn' -o -name 'tdn-linux-arm64' 2>/dev/null` + d. Discover vault directories in mounted paths (look for `tasks`, `projects`, `areas` folders) + e. Create a `.taskdn.json` in the current directory with the discovered paths + f. Verify with `tdn config --ai` + g. If none of this works, tell the user and fall back to direct file access using the skill's specification and templates docs. 4. Run `tdn config --ai && tdn context --ai` (so output → your context) 5. Report only: "Got it... areas, active projects... 🚀" (where x & y are based on the output of step 4) diff --git a/tdn-claude-plugin/skills/task-management/cowork.md b/tdn-claude-plugin/skills/task-management/cowork.md index 41b7a222..a027be6d 100644 --- a/tdn-claude-plugin/skills/task-management/cowork.md +++ b/tdn-claude-plugin/skills/task-management/cowork.md @@ -22,14 +22,23 @@ When you detect you're in a Cowork environment (no `tdn` in PATH, Linux platform ### Step 1: Install the tdn Binary -**Primary method — download from GitHub Releases:** +**Primary method — install via npm:** ```bash -curl -fsSL https://github.com/taskdn/taskdn/releases/latest/download/install.sh | bash +npm install -g @taskdn/cli +export PATH="$PATH:$(npm prefix -g)/bin" +``` + +This is the most reliable method in Cowork, since `registry.npmjs.org` is on the default allowlist. + +**Fallback 1 — download from GitHub Releases:** + +```bash +curl -fsSL https://github.com/dannysmith/taskdn/releases/latest/download/install.sh | bash export PATH="$PATH:$HOME/.local/bin" ``` -**Fallback — if download fails (no internet, etc.):** +**Fallback 2 — pre-placed binary in mounted folder:** Search mounted directories for a pre-placed binary: @@ -40,7 +49,7 @@ find /sessions/*/mnt/ -name 'tdn' -o -name 'tdn-linux-arm64' 2>/dev/null If found, use it directly by its full path. -If neither method works, fall back to direct file access mode (see "Degraded Mode" below). +If no method works, fall back to direct file access mode (see "Degraded Mode" below). ### Step 2: Discover Mounted Vault Directories diff --git a/tdn-cli/README.md b/tdn-cli/README.md index 136f9206..1c401fbe 100644 --- a/tdn-cli/README.md +++ b/tdn-cli/README.md @@ -21,6 +21,12 @@ Key features: ## Installation +### npm (any platform with Node.js 18+) + +```bash +npm install -g @taskdn/cli +``` + ### Homebrew (macOS/Linux) ```bash diff --git a/tdn-cli/docs/developer/releases.md b/tdn-cli/docs/developer/releases.md index b038ad00..1f31fe31 100644 --- a/tdn-cli/docs/developer/releases.md +++ b/tdn-cli/docs/developer/releases.md @@ -8,15 +8,19 @@ Releases are triggered by pushing a git tag in the format `tdn-cli-v`. 1. Builds binaries for all 5 platforms 2. Creates archives with SHA256 checksums -3. Publishes a GitHub Release with all assets -4. Triggers an auto-update PR in the Homebrew tap +3. Publishes all 6 npm packages (`@taskdn/cli` + 5 platform packages) via OIDC trusted publishing +4. Publishes a GitHub Release with all assets +5. Triggers an auto-update PR in the Homebrew tap + +Steps 3-5 run in parallel after the build completes. ## Prerequisites Before your first release, ensure: 1. **Homebrew tap exists**: `dannysmith/homebrew-taproom` with the formula and update workflow -2. **GitHub secret configured**: `HOMEBREW_TAP_TOKEN` in `taskdn/taskdn` repo settings +2. **GitHub secret configured**: `HOMEBREW_TAP_TOKEN` in `dannysmith/taskdn` repo settings +3. **npm trusted publishing**: All 6 `@taskdn/*` packages on npmjs.com have trusted publishing configured for the `release-cli.yml` workflow ## Release Process @@ -36,6 +40,7 @@ bun run release:prepare 1.0.0 This script: - Updates version in `package.json` - Updates version in `crates/core/Cargo.toml` +- Updates version in all `npm/*/package.json` files - Runs all checks (`bun run check`) - Prompts to create a git commit and tag @@ -68,17 +73,30 @@ git push origin tdn-cli-v1.0.0 - `tdn-windows-x64.zip` + `.sha256` - `install.sh` -### 4. Merge the Homebrew PR +### 4. Verify npm packages + +Check that all packages were published: + +```bash +npm view @taskdn/cli version +``` + +This should show the version you just released. + +### 5. Merge the Homebrew PR 1. Go to [homebrew-taproom PRs](https://github.com/dannysmith/homebrew-taproom/pulls) 2. Review the auto-generated PR (version and checksums should be updated) 3. Merge it -### 5. Verify installation +### 6. Verify installation Test that users can install: ```bash +# npm +npm install -g @taskdn/cli + # Homebrew (if tap already added) brew upgrade tdn @@ -100,11 +118,19 @@ git push origin tdn-cli-v1.0.0-beta.1 ``` Pre-releases (versions containing `-`) will: +- Publish npm packages (pre-release versions are fine on npm) - Create a GitHub Release (marked as pre-release) - **Not** trigger the Homebrew formula update ## Troubleshooting +### npm publish fails + +- Verify trusted publishing is configured on all 6 `@taskdn/*` packages on npmjs.com +- Check that the workflow has `permissions.id-token: write` +- Ensure `actions/setup-node` does **not** have `registry-url` set (breaks OIDC) +- npm CLI must be v11.5.1+ (Node.js 24.x ships with this) + ### Build fails on a specific platform Check the Actions logs. Common issues: diff --git a/tdn-cli/npm/cli-darwin-arm64/package.json b/tdn-cli/npm/cli-darwin-arm64/package.json new file mode 100644 index 00000000..0b214c04 --- /dev/null +++ b/tdn-cli/npm/cli-darwin-arm64/package.json @@ -0,0 +1,14 @@ +{ + "name": "@taskdn/cli-darwin-arm64", + "version": "1.0.0", + "description": "tdn CLI binary for macOS ARM64 (Apple Silicon)", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/dannysmith/taskdn.git", + "directory": "tdn-cli" + }, + "os": ["darwin"], + "cpu": ["arm64"], + "preferUnplugged": true +} diff --git a/tdn-cli/npm/cli-darwin-x64/package.json b/tdn-cli/npm/cli-darwin-x64/package.json new file mode 100644 index 00000000..d2772c38 --- /dev/null +++ b/tdn-cli/npm/cli-darwin-x64/package.json @@ -0,0 +1,14 @@ +{ + "name": "@taskdn/cli-darwin-x64", + "version": "1.0.0", + "description": "tdn CLI binary for macOS x64 (Intel)", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/dannysmith/taskdn.git", + "directory": "tdn-cli" + }, + "os": ["darwin"], + "cpu": ["x64"], + "preferUnplugged": true +} diff --git a/tdn-cli/npm/cli-linux-arm64/package.json b/tdn-cli/npm/cli-linux-arm64/package.json new file mode 100644 index 00000000..136f1517 --- /dev/null +++ b/tdn-cli/npm/cli-linux-arm64/package.json @@ -0,0 +1,14 @@ +{ + "name": "@taskdn/cli-linux-arm64", + "version": "1.0.0", + "description": "tdn CLI binary for Linux ARM64", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/dannysmith/taskdn.git", + "directory": "tdn-cli" + }, + "os": ["linux"], + "cpu": ["arm64"], + "preferUnplugged": true +} diff --git a/tdn-cli/npm/cli-linux-x64/package.json b/tdn-cli/npm/cli-linux-x64/package.json new file mode 100644 index 00000000..a40223ec --- /dev/null +++ b/tdn-cli/npm/cli-linux-x64/package.json @@ -0,0 +1,14 @@ +{ + "name": "@taskdn/cli-linux-x64", + "version": "1.0.0", + "description": "tdn CLI binary for Linux x64", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/dannysmith/taskdn.git", + "directory": "tdn-cli" + }, + "os": ["linux"], + "cpu": ["x64"], + "preferUnplugged": true +} diff --git a/tdn-cli/npm/cli-win32-x64/package.json b/tdn-cli/npm/cli-win32-x64/package.json new file mode 100644 index 00000000..12b83e82 --- /dev/null +++ b/tdn-cli/npm/cli-win32-x64/package.json @@ -0,0 +1,14 @@ +{ + "name": "@taskdn/cli-win32-x64", + "version": "1.0.0", + "description": "tdn CLI binary for Windows x64", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/dannysmith/taskdn.git", + "directory": "tdn-cli" + }, + "os": ["win32"], + "cpu": ["x64"], + "preferUnplugged": true +} diff --git a/tdn-cli/npm/cli/bin/tdn b/tdn-cli/npm/cli/bin/tdn new file mode 100755 index 00000000..0b44e0cc --- /dev/null +++ b/tdn-cli/npm/cli/bin/tdn @@ -0,0 +1,79 @@ +#!/usr/bin/env node + +"use strict"; + +const { spawnSync } = require("child_process"); +const { join, dirname } = require("path"); + +const PLATFORMS = { + "darwin arm64": { pkg: "@taskdn/cli-darwin-arm64", exe: "tdn" }, + "darwin x64": { pkg: "@taskdn/cli-darwin-x64", exe: "tdn" }, + "linux arm64": { pkg: "@taskdn/cli-linux-arm64", exe: "tdn" }, + "linux x64": { pkg: "@taskdn/cli-linux-x64", exe: "tdn" }, + "win32 x64": { pkg: "@taskdn/cli-win32-x64", exe: "tdn.exe" }, +}; + +const key = `${process.platform} ${process.arch}`; +const platform = PLATFORMS[key]; + +if (!platform) { + console.error( + `Unsupported platform: ${process.platform} ${process.arch}\n` + + `tdn supports: ${Object.keys(PLATFORMS).join(", ")}` + ); + process.exit(1); +} + +let binPath; +try { + binPath = join( + dirname(require.resolve(`${platform.pkg}/package.json`)), + platform.exe + ); +} catch { + console.error( + `Could not find package ${platform.pkg}.\n` + + `The platform-specific binary package was not installed.\n` + + `Try reinstalling: npm install -g @taskdn/cli` + ); + process.exit(1); +} + +// Warn if tdn is also installed via Homebrew (macOS only) +if (process.platform === "darwin" && !process.env.TDN_NO_DUAL_WARN) { + const { existsSync } = require("fs"); + const brewPaths = ["/opt/homebrew/bin/tdn", "/usr/local/bin/tdn"]; + const brewPath = brewPaths.find((p) => existsSync(p)); + if (brewPath) { + console.error( + `Note: tdn is also installed via Homebrew at ${brewPath}\n` + + `You are running the npm-installed version. To suppress this warning:\n` + + ` export TDN_NO_DUAL_WARN=1\n` + ); + } +} + +const result = spawnSync(binPath, process.argv.slice(2), { + stdio: "inherit", +}); + +if (result.error) { + if (result.error.code === "ENOENT") { + console.error( + `Binary not found at ${binPath}.\n` + + `Try reinstalling: npm install -g @taskdn/cli` + ); + } else { + throw result.error; + } + process.exit(1); +} + +if (result.status !== null) { + process.exit(result.status); +} + +// Process was killed by a signal — use conventional 128 + signal exit code +const os = require("os"); +const signum = os.constants.signals[result.signal]; +process.exit(signum ? 128 + signum : 1); diff --git a/tdn-cli/npm/cli/package.json b/tdn-cli/npm/cli/package.json new file mode 100644 index 00000000..cdf3f5d4 --- /dev/null +++ b/tdn-cli/npm/cli/package.json @@ -0,0 +1,24 @@ +{ + "name": "@taskdn/cli", + "version": "1.0.0", + "description": "Taskdn CLI — task management from the command line", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/dannysmith/taskdn.git", + "directory": "tdn-cli" + }, + "bin": { + "tdn": "bin/tdn" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@taskdn/cli-darwin-arm64": "1.0.0", + "@taskdn/cli-darwin-x64": "1.0.0", + "@taskdn/cli-linux-arm64": "1.0.0", + "@taskdn/cli-linux-x64": "1.0.0", + "@taskdn/cli-win32-x64": "1.0.0" + } +} diff --git a/tdn-cli/scripts/install.sh b/tdn-cli/scripts/install.sh index b08bf205..3245a266 100755 --- a/tdn-cli/scripts/install.sh +++ b/tdn-cli/scripts/install.sh @@ -2,7 +2,7 @@ set -euo pipefail # tdn-cli installer -# Usage: curl -fsSL https://github.com/taskdn/taskdn/releases/latest/download/install.sh | bash +# Usage: curl -fsSL https://github.com/dannysmith/taskdn/releases/latest/download/install.sh | bash # # Environment variables: # TDN_VERSION - Version to install (default: latest) @@ -13,7 +13,7 @@ VERSION="${TDN_VERSION:-latest}" INSTALL_DIR="${TDN_INSTALL_DIR:-$HOME/.local/bin}" SKIP_VERIFY="${TDN_SKIP_VERIFY:-0}" -REPO="taskdn/taskdn" +REPO="dannysmith/taskdn" # Colors (disabled if not a terminal) if [ -t 1 ]; then diff --git a/tdn-cli/scripts/prepare-release.js b/tdn-cli/scripts/prepare-release.js index 24c6e14f..c4492f0f 100755 --- a/tdn-cli/scripts/prepare-release.js +++ b/tdn-cli/scripts/prepare-release.js @@ -85,6 +85,29 @@ async function prepareRelease() { ` ${oldCargoVersion ? oldCargoVersion[1] : 'unknown'} -> ${cleanVersion}` ) + // Update npm package versions + console.log('Updating npm package versions...') + const npmPackages = [ + 'cli', + 'cli-darwin-arm64', + 'cli-darwin-x64', + 'cli-linux-arm64', + 'cli-linux-x64', + 'cli-win32-x64', + ] + for (const dir of npmPackages) { + const npmPkgPath = `npm/${dir}/package.json` + const npmPkg = JSON.parse(fs.readFileSync(npmPkgPath, 'utf8')) + npmPkg.version = cleanVersion + if (npmPkg.optionalDependencies) { + for (const dep of Object.keys(npmPkg.optionalDependencies)) { + npmPkg.optionalDependencies[dep] = cleanVersion + } + } + fs.writeFileSync(npmPkgPath, JSON.stringify(npmPkg, null, 2) + '\n') + } + console.log(` All npm packages -> ${cleanVersion}`) + // Run bun install to update lock files console.log('\nUpdating lock files...') exec('bun install', { silent: true }) @@ -105,6 +128,7 @@ async function prepareRelease() { console.log('\nAfter pushing:') console.log(' - GitHub Actions will automatically build the release') console.log(' - Binaries for all platforms will be uploaded to GitHub Releases') + console.log(' - npm packages will be published to @taskdn/*') console.log(' - Homebrew formula update will be triggered (if configured)') // Interactive execution option @@ -130,10 +154,10 @@ async function prepareRelease() { console.log(`\nRelease ${tagVersion} has been published!`) console.log( - 'Check GitHub Actions: https://github.com/taskdn/taskdn/actions' + 'Check GitHub Actions: https://github.com/dannysmith/taskdn/actions' ) console.log( - 'Release will appear at: https://github.com/taskdn/taskdn/releases' + 'Release will appear at: https://github.com/dannysmith/taskdn/releases' ) } else { console.log('\nGit commands saved for manual execution.')