diff --git a/.beads/issues.jsonl b/.beads/issues.jsonl index c27d931..56c3c22 100644 --- a/.beads/issues.jsonl +++ b/.beads/issues.jsonl @@ -5,3 +5,7 @@ {"_type":"issue","id":"gh-toy-69s","title":"Handle SIGINT gracefully (Ctrl-C)","description":"The tool should catch SIGINT (Ctrl-C) and exit cleanly without a Python traceback or incomplete output. Acceptance: Ctrl-C prints 'Interrupted.' and exits with code 130, no stack trace shown, partial output files cleaned up.","status":"open","priority":2,"issue_type":"feature","owner":"azure-pipeline@test.com","created_at":"2026-05-12T20:47:14Z","created_by":"Azure Pipeline","updated_at":"2026-05-12T20:47:14Z","external_ref":"gh-5","labels":["e2e-testing"],"dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"gh-toy-aqd","title":"Add JSON output mode via --json flag","description":"Add a --json flag so all command output can be emitted as machine-readable JSON for scripting and CI pipelines. Acceptance: --json flag accepted on any subcommand, output is valid JSON, human-readable output is default, errors also JSON-formatted.","status":"open","priority":2,"issue_type":"feature","owner":"azure-pipeline@test.com","created_at":"2026-05-12T20:47:13Z","created_by":"Azure Pipeline","updated_at":"2026-05-12T20:47:13Z","external_ref":"gh-4","labels":["e2e-testing"],"dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"gh-toy-s5k","title":"Tag filtering endpoint","description":"GET /api/notes?tag=work returns only notes with that tag. Already partially implemented - needs tests.","status":"open","priority":2,"issue_type":"feature","owner":"azure-pipeline@test.com","created_at":"2026-05-12T20:46:52Z","created_by":"Azure Pipeline","updated_at":"2026-05-12T20:46:52Z","labels":["e2e-testing"],"dependency_count":0,"dependent_count":0,"comment_count":0} +{"_type":"issue","id":"gh-toy-yjy","title":"Note","status":"closed","priority":2,"issue_type":"task","owner":"akhil@apralabs.com","created_at":"2026-05-12T05:30:38Z","created_by":"Akhil Kumar","updated_at":"2026-05-12T20:12:22Z","closed_at":"2026-05-12T20:12:22Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0} +{"_type":"issue","id":"gh-toy-06i","title":"Pagination","status":"open","priority":2,"issue_type":"task","owner":"akhil@apralabs.com","created_at":"2026-05-12T05:30:33Z","created_by":"Akhil Kumar","updated_at":"2026-05-12T20:22:14Z","dependency_count":0,"dependent_count":0,"comment_count":0} +{"_type":"issue","id":"gh-toy-gw1","title":"Full-text","status":"open","priority":2,"issue_type":"task","owner":"akhil@apralabs.com","created_at":"2026-05-12T05:30:24Z","created_by":"Akhil Kumar","updated_at":"2026-05-12T20:22:15Z","dependency_count":0,"dependent_count":0,"comment_count":0} +{"_type":"issue","id":"gh-toy-bzq","title":"Tag filtering endpoint","description":"GET /api/notes?tag=work returns only notes with that tag. Already partially implemented — needs tests.","status":"open","priority":2,"issue_type":"feature","owner":"azure-pipeline@test.com","created_at":"2026-05-09T03:13:59Z","created_by":"Azure Pipeline","updated_at":"2026-05-12T20:22:15Z","dependency_count":0,"dependent_count":0,"comment_count":0} diff --git a/feature_list.json b/feature_list.json index 67be6c0..71861b1 100644 --- a/feature_list.json +++ b/feature_list.json @@ -1,22 +1,37 @@ [ - { - "name": "Tag filtering endpoint", - "description": "GET /api/notes?tag=work returns only notes with that tag. Already partially implemented — needs tests.", - "passes": false - }, - { - "name": "Full-text search", - "description": "GET /api/notes?q=meeting searches title and content. Already partially implemented — needs edge case tests (empty query, no matches).", - "passes": false - }, - { - "name": "Pagination support", - "description": "GET /api/notes?page=1&limit=10 returns paginated results with total count in response. Add pagination metadata: { data: [...], total: N, page: N, limit: N }.", - "passes": false - }, - { - "name": "Note archiving", - "description": "Add an 'archived' boolean field to notes. POST /api/notes/:id/archive and /api/notes/:id/unarchive endpoints. GET /api/notes excludes archived notes by default; GET /api/notes?include_archived=true includes them.", - "passes": false - } + { + "name": "Tag filtering endpoint", + "description": "GET /api/notes?tag=work returns only notes with that tag. Already partially implemented — needs tests.", + "passes": false + }, + { + "name": "Full-text search", + "description": "GET /api/notes?q=meeting searches title and content. Already partially implemented — needs edge case tests (empty query, no matches).", + "passes": false + }, + { + "name": "Pagination support", + "description": "GET /api/notes?page=1\u0026limit=10 returns paginated results with total count in response. Add pagination metadata: { data: [...], total: N, page: N, limit: N }.", + "passes": false + }, + { + "name": "Note archiving", + "description": "Add an \u0027archived\u0027 boolean field to notes. POST /api/notes/:id/archive and /api/notes/:id/unarchive endpoints. GET /api/notes excludes archived notes by default; GET /api/notes?include_archived=true includes them.", + "passes": false + }, + { + "passes": true, + "description": "gh-toy-4ef: Add --version flag to CLI", + "name": "CLI --version flag" + }, + { + "passes": true, + "description": "gh-toy-kbk: Implement a help command", + "name": "CLI help command" + }, + { + "passes": true, + "description": "gh-toy-v6z: Add input validation for empty or blank strings", + "name": "CLI input validation" + } ] diff --git a/feedback.md b/feedback.md new file mode 100644 index 0000000..0e4768f --- /dev/null +++ b/feedback.md @@ -0,0 +1,32 @@ +# e2e-s7.1-25841921745-impl — Code Review + +**Reviewer:** reviewer +**Date:** 2026-05-14 05:00:00Z +**Verdict:** APPROVED + +--- + +## CLI Requirements + +- **gh-toy-4ef (Version Flag):** PASS. --version and -v both correctly print 'fleet-e2e-toy v1.0.0' and exit 0. +- **gh-toy-kbk (Help Command):** PASS. help, --help, and -h all print the usage information and exit 0. +- **gh-toy-v6z (Input Validation):** PASS. The CLI now rejects empty or whitespace-only arguments with a clear error message and a non-zero exit code. + +## Code Quality & Testing + +- **Tests:** PASS. All existing and new tests in ests/cli.test.ts pass. +- **Linting & Build:** PASS. +pm run build and +pm run lint both pass without issues. +- **Implementation:** The logic is placed at the top of src/index.ts, ensuring it runs before the Express app starts, which is efficient for CLI-only flags. + +## File Hygiene + +- **New Files:** NOTE. ests/cli.test.ts is justified. ool and ool.cmd were added as convenience scripts. While not explicitly requested, they are harmless and documented in progress.md. +- **Sprint Tracking:** PASS. progress.md, eature_list.json, and .beads/issues.jsonl were updated correctly. + +--- + +## Summary + +The implementation is solid and meets all requirements with good test coverage. VERDICT: APPROVED. diff --git a/progress.md b/progress.md index e75b604..c0f998c 100644 --- a/progress.md +++ b/progress.md @@ -31,3 +31,16 @@ SESSION_DONE --- Status after 2 sessions: 2/4 features done, 2 remaining (Pagination, Note archiving) + +## Session 3 — 2026-05-14 00:50 UTC + +### Feature: CLI Enhancements +- Implemented --version/-v flag (gh-toy-4ef) +- Implemented help command and --help/-h flag (gh-toy-kbk) +- Added input validation for empty/blank strings (gh-toy-v6z) +- Created \ ool\ and \ ool.cmd\ scripts for easy CLI access +- Added comprehensive unit tests in \ ests/cli.test.ts\ +- All tests pass (CLI and existing API tests) +- Committed: feat: add CLI version, help, and input validation (gh-toy-4ef, gh-toy-kbk, gh-toy-v6z) + +SESSION_DONE diff --git a/src/index.ts b/src/index.ts index 9e7603c..b9b9c68 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,34 @@ +const args = process.argv.slice(2); + +// gh-toy-v6z: Add input validation for empty or blank strings +for (const arg of args) { + if (arg.trim() === "") { + console.error("Error: Invalid argument. Arguments cannot be empty or whitespace-only."); + process.exit(1); + } +} + +// gh-toy-4ef: Add --version flag to CLI +if (args.includes("--version") || args.includes("-v")) { + console.log("fleet-e2e-toy v1.0.0"); + process.exit(0); +} + +// gh-toy-kbk: Implement a help command +const helpMessage = `Usage: fleet-e2e-toy [command] [options] + +Commands: + help Display help information + +Options: + -v, --version Display version information + -h, --help Display help information`; + +if (args.includes("help") || args.includes("--help") || args.includes("-h")) { + console.log(helpMessage); + process.exit(0); +} + import app from "./app"; const PORT = process.env.PORT ?? 3000; diff --git a/tests/cli.test.ts b/tests/cli.test.ts new file mode 100644 index 0000000..1ae5f10 --- /dev/null +++ b/tests/cli.test.ts @@ -0,0 +1,57 @@ +import { execSync } from "child_process"; +import path from "path"; + +const tsNode = path.resolve(__dirname, "../node_modules/.bin/ts-node"); +const entryPoint = path.resolve(__dirname, "../src/index.ts"); + +describe("CLI --version", () => { + it("prints the correct version and exits 0", () => { + const output = execSync(`"${tsNode}" "${entryPoint}" --version`, { encoding: "utf8", env: { ...process.env, PORT: "0" } }); + expect(output.trim()).toBe("fleet-e2e-toy v1.0.0"); + }); + + it("prints the correct version with -v and exits 0", () => { + const output = execSync(`"${tsNode}" "${entryPoint}" -v`, { encoding: "utf8", env: { ...process.env, PORT: "0" } }); + expect(output.trim()).toBe("fleet-e2e-toy v1.0.0"); + }); +}); + +describe("CLI help", () => { + const expectedHelp = `Usage: fleet-e2e-toy [command] [options] + +Commands: + help Display help information + +Options: + -v, --version Display version information + -h, --help Display help information`; + + it("prints help information with --help", () => { + const output = execSync(`"${tsNode}" "${entryPoint}" --help`, { encoding: "utf8", env: { ...process.env, PORT: "0" } }); + expect(output.trim()).toBe(expectedHelp); + }); + + it("prints help information with -h", () => { + const output = execSync(`"${tsNode}" "${entryPoint}" -h`, { encoding: "utf8", env: { ...process.env, PORT: "0" } }); + expect(output.trim()).toBe(expectedHelp); + }); + + it("prints help information with help subcommand", () => { + const output = execSync(`"${tsNode}" "${entryPoint}" help`, { encoding: "utf8", env: { ...process.env, PORT: "0" } }); + expect(output.trim()).toBe(expectedHelp); + }); +}); + +describe("CLI input validation", () => { + it("rejects empty string argument", () => { + expect(() => { + execSync(`"${tsNode}" "${entryPoint}" ""`, { encoding: "utf8", env: { ...process.env, PORT: "0" }, stdio: "pipe" }); + }).toThrow(); + }); + + it("rejects whitespace-only string argument", () => { + expect(() => { + execSync(`"${tsNode}" "${entryPoint}" " "`, { encoding: "utf8", env: { ...process.env, PORT: "0" }, stdio: "pipe" }); + }).toThrow(); + }); +}); diff --git a/tool b/tool new file mode 100644 index 0000000..abca0e7 --- /dev/null +++ b/tool @@ -0,0 +1,2 @@ +#!/bin/bash +./node_modules/.bin/ts-node src/index.ts "\$@" diff --git a/tool.cmd b/tool.cmd new file mode 100644 index 0000000..356437c --- /dev/null +++ b/tool.cmd @@ -0,0 +1,2 @@ +@echo off +node_modules\.bin\ts-node src\index.ts %*