Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .beads/issues.jsonl
Original file line number Diff line number Diff line change
Expand Up @@ -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}
55 changes: 35 additions & 20 deletions feature_list.json
Original file line number Diff line number Diff line change
@@ -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"
}
]
32 changes: 32 additions & 0 deletions feedback.md
Original file line number Diff line number Diff line change
@@ -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.
13 changes: 13 additions & 0 deletions progress.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
31 changes: 31 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
57 changes: 57 additions & 0 deletions tests/cli.test.ts
Original file line number Diff line number Diff line change
@@ -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();
});
});
2 changes: 2 additions & 0 deletions tool
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/bin/bash
./node_modules/.bin/ts-node src/index.ts "\$@"
2 changes: 2 additions & 0 deletions tool.cmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@echo off
node_modules\.bin\ts-node src\index.ts %*
Loading