diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml index a5e1a91..a8cf86e 100644 --- a/.github/workflows/copilot-setup-steps.yml +++ b/.github/workflows/copilot-setup-steps.yml @@ -31,3 +31,6 @@ jobs: - name: Install dependencies run: npm ci + + - name: Run tests + run: npm test diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..4fb75ee --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,48 @@ +name: Release + +on: + push: + tags: + - 'v*.*.*' + +jobs: + release: + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + timeout-minutes: 15 + steps: + - name: Checkout code + uses: actions/checkout@v5 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '24' + cache: npm + registry-url: 'https://registry.npmjs.org' + + - name: Verify tag matches package version + run: | + PACKAGE_VERSION="$(node -p \"require('./package.json').version\")" + EXPECTED_TAG="v${PACKAGE_VERSION}" + + if [ "$GITHUB_REF_NAME" != "$EXPECTED_TAG" ]; then + echo "Expected tag $EXPECTED_TAG but got $GITHUB_REF_NAME" >&2 + exit 1 + fi + + - name: Install dependencies + run: npm ci + + - name: Run tests + run: npm test + + - name: Verify package contents + run: npm pack --dry-run + + - name: Publish package + run: npm publish --provenance + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/README.md b/README.md index fc2e9fb..2188ea7 100644 --- a/README.md +++ b/README.md @@ -12,24 +12,18 @@ ## Install the CLI ```bash -npm install -g git-tasks -npx git-tasks --help +npm install -g @atena-reply/git-tasks +npx @atena-reply/git-tasks --help ``` -Until the npm package is published, install the current GitHub source with: - -```bash -npm install -g github:Atena-IT/git-tasks -``` - -The package and skill are branded as `git-tasks`, while the GitHub repository URL remains `Atena-IT/git-tasks`. +The npm package is published as `@atena-reply/git-tasks`, while the installed command remains `git-tasks`. The GitHub repository URL remains `Atena-IT/git-tasks`. ## Install the Agent Skill ### One-command installer ```bash -npx git-tasks skill install --target all +npx @atena-reply/git-tasks skill install --target all ``` Available targets: `claude`, `copilot`, `codex`, `gemini`, `cline`, `all`. diff --git a/package-lock.json b/package-lock.json index 44f6ea7..91d6eb2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "git-tasks", + "name": "@atena-reply/git-tasks", "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "git-tasks", + "name": "@atena-reply/git-tasks", "version": "1.0.0", "license": "ISC", "dependencies": { diff --git a/package.json b/package.json index eb00d41..3cb3836 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "git-tasks", + "name": "@atena-reply/git-tasks", "version": "1.0.0", "description": "AI-native GitHub issue planning via GitHub issues", "type": "module", @@ -8,8 +8,16 @@ }, "scripts": { "test": "node --test test/cli.test.js", - "test:coverage": "node --test --experimental-test-coverage test/cli.test.js" + "test:coverage": "node --test --experimental-test-coverage test/cli.test.js", + "pack:dry-run": "npm pack --dry-run" }, + "files": [ + "bin/", + "src/", + "skills/", + ".claude-plugin/", + "README.md" + ], "repository": { "type": "git", "url": "git+https://github.com/Atena-IT/git-tasks.git" @@ -29,6 +37,9 @@ "url": "https://github.com/Atena-IT/git-tasks/issues" }, "homepage": "https://github.com/Atena-IT/git-tasks#readme", + "publishConfig": { + "access": "public" + }, "dependencies": { "chalk": "^5.6.2", "commander": "^14.0.3" diff --git a/skills/git-tasks/SKILL.md b/skills/git-tasks/SKILL.md index f1c6f83..134debd 100644 --- a/skills/git-tasks/SKILL.md +++ b/skills/git-tasks/SKILL.md @@ -1,7 +1,7 @@ --- name: git-tasks description: AI-native project management CLI for GitHub. Use when the user wants to inspect or update the planning hierarchy (epics, sprints, user stories), drive story lifecycle transitions (start work, ready for review, close), or integrate raw inputs such as client meeting notes or feature transcripts into the existing plan — either by updating open items or creating new ones as a diff. Do not use for reading a single issue or PR without any project-management intent; prefer standard gh commands for those one-off lookups. Triggers include "what's the current sprint status", "create a story for this feature", "move this story to in-progress", "close out the sprint", "update the plan based on today's meeting notes", or any request involving epics, sprints, or story lifecycle management. -allowed-tools: Bash(git-tasks:*), Bash(npx git-tasks:*) +allowed-tools: Bash(git-tasks:*), Bash(npx @atena-reply/git-tasks:*) hidden: true --- @@ -22,7 +22,7 @@ gh pr list - Ensure `gh auth status` succeeds. - Prefer `--short` output unless you need full issue bodies or comments. - Start with `git-tasks overview --depth 2` before drilling into individual issues. -- Install the skill anywhere with `npx git-tasks skill install --target all`. +- Install the skill anywhere with `npx @atena-reply/git-tasks skill install --target all`. ## Recommended workflow diff --git a/test/cli.test.js b/test/cli.test.js index 0722b3d..383d300 100644 --- a/test/cli.test.js +++ b/test/cli.test.js @@ -126,10 +126,11 @@ test('skill install fails for unknown targets', async () => { } }); -test('package metadata targets git-tasks on Node.js 24+', async () => { +test('package metadata targets the scoped npm package on Node.js 24+', async () => { const pkg = JSON.parse(fs.readFileSync(join(REPO_ROOT, 'package.json'), 'utf8')); - assert.equal(pkg.name, 'git-tasks'); + assert.equal(pkg.name, '@atena-reply/git-tasks'); assert.equal(pkg.bin['git-tasks'], './bin/git-tasks.js'); + assert.equal(pkg.publishConfig.access, 'public'); assert.equal(pkg.engines.node, '>=24'); }); @@ -137,7 +138,7 @@ test('agent skill is packaged in the installable repo layout', () => { const skillPath = join(REPO_ROOT, 'skills', 'git-tasks', 'SKILL.md'); const skill = fs.readFileSync(skillPath, 'utf8'); - assert.match(skill, /^---\nname: git-tasks\ndescription:/); + assert.match(skill, /^---\r?\nname: git-tasks\r?\ndescription:/); assert.ok(skill.includes('git-tasks overview --depth 2')); assert.ok(skill.includes('allowed-tools:')); assert.ok(skill.includes('hidden: true'));