diff --git a/.beads/issues.jsonl b/.beads/issues.jsonl index c27d931..8d26c0f 100644 --- a/.beads/issues.jsonl +++ b/.beads/issues.jsonl @@ -1,6 +1,6 @@ -{"_type":"issue","id":"gh-toy-kbk","title":"Implement a help command","description":"Add a help subcommand (and --help / -h flag) that prints usage information for all available commands and flags. Acceptance: ./tool help and ./tool --help both work, lists every subcommand and flag, exit code 0.","status":"open","priority":1,"issue_type":"feature","owner":"azure-pipeline@test.com","created_at":"2026-05-12T20:47:12Z","created_by":"Azure Pipeline","updated_at":"2026-05-12T20:56:10Z","external_ref":"gh-3","labels":["e2e-testing"],"dependency_count":0,"dependent_count":0,"comment_count":0} -{"_type":"issue","id":"gh-toy-v6z","title":"Add input validation for empty or blank strings","description":"When a user passes an empty string or whitespace-only string as an argument, the tool should reject it with a clear error message instead of silently proceeding or crashing. Acceptance: passing empty or whitespace prints user-friendly error, non-zero exit, unit test added.","status":"open","priority":1,"issue_type":"bug","owner":"azure-pipeline@test.com","created_at":"2026-05-12T20:46:56Z","created_by":"Azure Pipeline","updated_at":"2026-05-12T20:56:10Z","external_ref":"gh-2","labels":["e2e-testing"],"dependency_count":0,"dependent_count":0,"comment_count":0} -{"_type":"issue","id":"gh-toy-4ef","title":"Add --version flag to CLI","description":"The CLI tool should support a --version (or -v) flag that prints the current version string and exits with code 0. Acceptance: running ./tool --version prints fleet-e2e-toy v1.0.0, exit code 0, works alongside other flags.","status":"open","priority":1,"issue_type":"feature","owner":"azure-pipeline@test.com","created_at":"2026-05-12T20:46:54Z","created_by":"Azure Pipeline","updated_at":"2026-05-12T20:56:08Z","external_ref":"gh-1","labels":["e2e-testing"],"dependency_count":0,"dependent_count":0,"comment_count":0} +{"_type":"issue","id":"gh-toy-kbk","title":"Implement a help command","description":"Add a help subcommand (and --help / -h flag) that prints usage information for all available commands and flags. Acceptance: ./tool help and ./tool --help both work, lists every subcommand and flag, exit code 0.","status":"closed","priority":1,"issue_type":"feature","assignee":"E2E Tester","owner":"azure-pipeline@test.com","created_at":"2026-05-12T20:47:12Z","created_by":"Azure Pipeline","updated_at":"2026-05-22T13:36:46Z","started_at":"2026-05-22T03:58:36Z","closed_at":"2026-05-22T13:36:46Z","close_reason":"Closed","external_ref":"gh-3","labels":["e2e-testing"],"dependency_count":0,"dependent_count":0,"comment_count":0} +{"_type":"issue","id":"gh-toy-v6z","title":"Add input validation for empty or blank strings","description":"When a user passes an empty string or whitespace-only string as an argument, the tool should reject it with a clear error message instead of silently proceeding or crashing. Acceptance: passing empty or whitespace prints user-friendly error, non-zero exit, unit test added.","status":"open","priority":1,"issue_type":"bug","owner":"azure-pipeline@test.com","created_at":"2026-05-12T20:46:56Z","created_by":"Azure Pipeline","updated_at":"2026-05-22T13:12:45Z","external_ref":"gh-2","labels":["e2e-testing"],"dependency_count":0,"dependent_count":0,"comment_count":0} +{"_type":"issue","id":"gh-toy-4ef","title":"Add --version flag to CLI","description":"The CLI tool should support a --version (or -v) flag that prints the current version string and exits with code 0. Acceptance: running ./tool --version prints fleet-e2e-toy v1.0.0, exit code 0, works alongside other flags.","status":"closed","priority":1,"issue_type":"feature","assignee":"E2E Tester","owner":"azure-pipeline@test.com","created_at":"2026-05-12T20:46:54Z","created_by":"Azure Pipeline","updated_at":"2026-05-22T13:18:47Z","started_at":"2026-05-22T13:18:29Z","closed_at":"2026-05-22T13:18:47Z","close_reason":"Closed","external_ref":"gh-1","labels":["e2e-testing"],"dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"gh-toy-24g","title":"Add config file support (~/.fleet-e2e-toy.yaml)","description":"Allow users to persist default flag values in a YAML config file at ~/.fleet-e2e-toy.yaml so they don't have to repeat common flags on every run. Acceptance: reads config on startup, CLI flags override config, warns on unknown keys, documents supported keys in README.","status":"open","priority":2,"issue_type":"feature","owner":"azure-pipeline@test.com","created_at":"2026-05-12T20:47:15Z","created_by":"Azure Pipeline","updated_at":"2026-05-12T20:47:15Z","external_ref":"gh-6","labels":["e2e-testing"],"dependency_count":0,"dependent_count":0,"comment_count":0} {"_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} diff --git a/.gitignore b/.gitignore index 52dc9c3..69a94e0 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,12 @@ coverage/ .dolt/ *.db .beads-credential-key + +# Agent context files +AGY.md +GEMINI.md +CLAUDE.md +AGENTS.md +COPILOT.md +.claude/ +.gemini/ diff --git a/plan.md b/plan.md deleted file mode 100644 index 682b4d7..0000000 --- a/plan.md +++ /dev/null @@ -1,32 +0,0 @@ -# Feature: NoteAPI v2 — Search, Pagination, and Archiving - -## Problem Statement -The API supports basic CRUD but lacks the query features users need for real use: filtering by tag, searching content, paginating large result sets, and archiving old notes without deleting them. - -## Approach -Add four features incrementally. Each feature is independent — no ordering dependencies. All use the existing in-memory store (no database changes). Each feature must have tests before it's considered done. - -## Phases - -### Phase 1: Tag Filtering -- [ ] GET /api/notes?tag=work returns only notes with that tag -- [ ] Tests: single tag, no match, multiple tags on same note -- Integration test: `curl localhost:3000/api/notes?tag=work` - -### Phase 2: Full-Text Search -- [ ] GET /api/notes?q=meeting searches title and content (case-insensitive) -- [ ] Tests: match in title, match in content, no match, empty query returns all -- Integration test: `curl localhost:3000/api/notes?q=meeting` - -### Phase 3: Pagination -- [ ] GET /api/notes?page=1&limit=10 returns paginated results -- [ ] Response format: `{ data: [...], total: N, page: N, limit: N }` -- [ ] Default: page 1, limit 20 -- Integration test: create 25 notes, verify page 1 has 20, page 2 has 5 - -### Phase 4: Note Archiving -- [ ] Add `archived: boolean` field to Note model (default: false) -- [ ] POST /api/notes/:id/archive and /api/notes/:id/unarchive endpoints -- [ ] GET /api/notes excludes archived by default -- [ ] GET /api/notes?include_archived=true includes them -- Integration test: archive a note, verify it's hidden, unarchive, verify it's back diff --git a/src/cli.ts b/src/cli.ts new file mode 100644 index 0000000..b8f8010 --- /dev/null +++ b/src/cli.ts @@ -0,0 +1,29 @@ +import * as process from "process"; + +function main() { + const args = process.argv.slice(2); + + for (const arg of args) { + if (arg.trim() === "") { + console.error("Error: Argument cannot be empty or whitespace-only."); + process.exit(1); + } + } + + if (args.includes("--version") || args.includes("-v")) { + console.log("fleet-e2e-toy v1.0.0"); + process.exit(0); + } + + if (args.includes("help") || args.includes("--help") || args.includes("-h")) { + console.log("Usage: fleet-e2e-toy [command] [options]\n\n" + + "Commands:\n" + + " help Show this help message\n\n" + + "Options:\n" + + " --version, -v Show version information\n" + + " --help, -h Show this help message"); + process.exit(0); + } +} + +main(); diff --git a/tests/cli.test.ts b/tests/cli.test.ts new file mode 100644 index 0000000..164242f --- /dev/null +++ b/tests/cli.test.ts @@ -0,0 +1,99 @@ +import { exec } from "child_process"; +import * as path from "path"; + +jest.setTimeout(20000); + +const cliScript = path.resolve(__dirname, "../src/cli.ts"); + +function runCli(args: string[]): Promise<{ code: number; stdout: string; stderr: string }> { + return new Promise((resolve) => { + // For executing in terminal, quote each argument. + // Replace double quotes inside the arg with escaped double quotes. + const escapedArgs = args.map(arg => { + const escaped = arg.replace(/"/g, '\\"'); + return `"${escaped}"`; + }).join(" "); + + const cmd = `npx ts-node "${cliScript}" ${escapedArgs}`; + + exec(cmd, (error, stdout, stderr) => { + resolve({ + code: error ? (error.code ?? 1) : 0, + stdout: stdout.trim(), + stderr: stderr.trim(), + }); + }); + }); +} + +describe("CLI Tool", () => { + describe("Version Flags", () => { + it("should print version and exit 0 for --version", async () => { + const result = await runCli(["--version"]); + expect(result.code).toBe(0); + expect(result.stdout).toBe("fleet-e2e-toy v1.0.0"); + expect(result.stderr).toBe(""); + }); + + it("should print version and exit 0 for -v", async () => { + const result = await runCli(["-v"]); + expect(result.code).toBe(0); + expect(result.stdout).toBe("fleet-e2e-toy v1.0.0"); + expect(result.stderr).toBe(""); + }); + }); + + describe("Help Commands", () => { + const expectedHelpText = + "Usage: fleet-e2e-toy [command] [options]\n\n" + + "Commands:\n" + + " help Show this help message\n\n" + + "Options:\n" + + " --version, -v Show version information\n" + + " --help, -h Show this help message"; + + it("should print help and exit 0 for help command", async () => { + const result = await runCli(["help"]); + expect(result.code).toBe(0); + expect(result.stdout).toBe(expectedHelpText); + expect(result.stderr).toBe(""); + }); + + it("should print help and exit 0 for --help flag", async () => { + const result = await runCli(["--help"]); + expect(result.code).toBe(0); + expect(result.stdout).toBe(expectedHelpText); + expect(result.stderr).toBe(""); + }); + + it("should print help and exit 0 for -h flag", async () => { + const result = await runCli(["-h"]); + expect(result.code).toBe(0); + expect(result.stdout).toBe(expectedHelpText); + expect(result.stderr).toBe(""); + }); + }); + + describe("Input Validation", () => { + it("should fail and exit 1 for empty string argument", async () => { + const result = await runCli([""]); + expect(result.code).toBe(1); + expect(result.stderr).toContain("Error: Argument cannot be empty or whitespace-only."); + expect(result.stdout).toBe(""); + }); + + it("should fail and exit 1 for whitespace-only argument", async () => { + const result = await runCli([" "]); + expect(result.code).toBe(1); + expect(result.stderr).toContain("Error: Argument cannot be empty or whitespace-only."); + expect(result.stdout).toBe(""); + }); + + it("should succeed and exit 0 for valid arguments", async () => { + const result = await runCli(["valid-arg"]); + expect(result.code).toBe(0); + expect(result.stdout).toBe(""); + expect(result.stderr).toBe(""); + }); + }); +}); diff --git a/tool b/tool new file mode 100755 index 0000000..b1b7a1b --- /dev/null +++ b/tool @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +# Determine the directory of this script to handle execution from other folders +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +npx ts-node "$DIR/src/cli.ts" "$@" diff --git a/tool.cmd b/tool.cmd new file mode 100644 index 0000000..d882860 --- /dev/null +++ b/tool.cmd @@ -0,0 +1,2 @@ +@echo off +npx ts-node "%~dp0src\cli.ts" %*