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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ coverage/
.dolt/
*.db
.beads-credential-key
AGY.md
49 changes: 49 additions & 0 deletions feedback.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# e2e-s8.1-26313491849/sprint - Code Review

**Reviewer:** reviewer
**Date:** 2026-05-22 18:00:00-04:00
**Verdict:** APPROVED

> See the recent git history of this file to understand the context of this review.

---

## Code Review Findings

### CLI Interface & Version Flag (Task 1) - PASS
- Checked the implementation of `src/cli.ts` and the main entry point logic.
- The `--version` and `-v` flags correctly print `fleet-e2e-toy v1.0.0` and exit with code 0.
- Verified that `tool` (bash script) and `tool.cmd` (cmd batch script) are created correctly in the root folder, and correctly forward arguments to `npx ts-node src/cli.ts`.

### Argument Validation (Task 2) - PASS
- Verified that empty and whitespace-only arguments (e.g. `""`, `" "`) are rejected.
- An error message `Error: Argument cannot be empty or whitespace-only.` is printed to stderr, and the command exits with code 1.

### Help Subcommand & Flags (Task 3) - PASS
- Verified that `help`, `--help`, and `-h` commands are supported.
- They correctly print the CLI usage documentation to stdout and exit with code 0.
- Unknown commands and flags (e.g., `unknown-command`, `--unknown-flag`) are correctly handled by printing `Unknown command or flag: <arg>` to stderr and exiting with code 1.

### CLI Unit Tests (Task 4) - PASS
- A comprehensive test suite has been created at `tests/cli.test.ts` wrapping command execution in `exec` block.
- Tests cover version flags, help commands/flags, blank/whitespace validation, and unknown flags/commands.
- All tests pass successfully (26s total execution time).

### Verification Checkpoint (Task 5) - PASS
- Build command `npm run build` succeeds without compilation errors.
- Linter `npm run lint` succeeds with no warnings or errors.
- Unit and integration tests for validation, notes, and CLI pass successfully.

---

## File Hygiene & Repository Status

- Checked the names of modified and added files via `git diff --name-only`.
- The files added/modified are: `.gitignore`, `plan.md`, `progress.json`, `src/cli.ts`, `tests/cli.test.ts`, `tool`, `tool.cmd`.
- There are no stale/unwanted files or agent context files (`AGY.md`, `CLAUDE.md`, etc.) tracked in git. `AGY.md` is correctly ignored in `.gitignore`.

---

## Summary

All required functionality has been fully implemented, verified, and clean tests have been added. No regressions detected in other API endpoints or validation logic. The codebase compiles and lints successfully.
93 changes: 61 additions & 32 deletions plan.md
Original file line number Diff line number Diff line change
@@ -1,32 +1,61 @@
# 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
# fleet-e2e-toy — Implementation Plan

> Add basic CLI behaviors to fleet-e2e-toy including version flag, input validation, and a help system with stubs.

---

## Tasks

### Phase 1: CLI Interface

#### Task 1: Add --version / -v flag to CLI and create tool launcher scripts
- **Change:** Create `src/cli.ts` as the CLI entry point. Check process arguments for `--version` or `-v` flags. If found, print `fleet-e2e-toy v1.0.0` to stdout and exit with code 0. Create root launcher scripts `tool` (bash) and `tool.cmd` (batch/Windows command script) to execute `npx ts-node src/cli.ts` forwarding arguments.
- **Files:** `src/cli.ts`, `tool`, `tool.cmd`
- **Tier:** cheap
- **Done when:** Running `./tool --version` and `./tool -v` print `fleet-e2e-toy v1.0.0` to stdout and exit with code 0.
- **Blockers:** None

#### Task 2: Implement validation for empty or blank strings
- **Change:** In `src/cli.ts`, validate that none of the arguments passed to the tool are empty or contain only whitespace. If any argument is invalid, print a clear, user-friendly error message to stderr (e.g. `Error: Arguments cannot be empty or blank strings.`) and exit with a non-zero code (e.g. `1`).
- **Files:** `src/cli.ts`
- **Tier:** cheap
- **Done when:** Running `./tool ""` and `./tool " "` print a clear error message to stderr and exit with a non-zero code.
- **Blockers:** None

#### Task 3: Implement help command and help flags
- **Change:** In `src/cli.ts`, handle the `help` subcommand and the `--help` and `-h` flags. When triggered, print CLI usage information listing available subcommands and flags to stdout, and exit with code 0. If an unknown command or flag is provided, print an error message to stderr (e.g., `Unknown command or flag: <arg>`) and exit with code 1.
- **Files:** `src/cli.ts`
- **Tier:** standard
- **Done when:** Running `./tool help`, `./tool --help`, and `./tool -h` print CLI usage information to stdout and exit with code 0.
- **Blockers:** None

#### Task 4: Add CLI unit tests
- **Change:** Create `tests/cli.test.ts` to programmatically run `npx ts-node src/cli.ts` (using `execSync`) and assert the output and exit status for `--version`, `-v`, `--help`, `-h`, `help`, empty/blank string inputs, and unknown commands.
- **Files:** `tests/cli.test.ts`
- **Tier:** standard
- **Done when:** `npm test` runs successfully, including the new `tests/cli.test.ts` suite, with all tests passing.
- **Blockers:** None

#### VERIFY: CLI Interface
- Run `npm run build` to verify compilation succeeds without TypeScript errors
- Run `npm test` to verify all tests (existing note/validation tests + new CLI tests) pass
- Run manual validation of version, help, validation, and exit codes
- Report: tests passing, any regressions, any issues found
- Push: `git push origin e2e-s8.1-26311371724/sprint`

---

## Risk Register

| Risk | Impact | Mitigation |
|------|--------|------------|
| Launcher scripts (`tool` and `tool.cmd`) are not executable on target environment | med | Use cross-platform node runner (`npx ts-node`) in launcher scripts and test on both bash and cmd environments |
| Empty string arguments are stripped by shell before being processed | low | Use wrapper test executions in Jest to ensure exact arguments are passed and validated correctly |
| Adding CLI code breaks existing web API server | low | Keep CLI code completely separated in `src/cli.ts` and `tests/cli.test.ts` with no changes to the Express app or router |
| `process.exit()` during Jest tests exits the test process | high | Use `execSync` to run `src/cli.ts` in a separate process in Jest tests to isolate execution, avoiding `process.exit` in the main test process |

## Notes
- Each task should result in a git commit
- Verify tasks are checkpoints — stop and report after each one
- Base branch: main
- Implementation branch: e2e-s8.1-26313491849/sprint
51 changes: 51 additions & 0 deletions progress.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{
"project": "fleet-e2e-toy",
"plan_file": "PLAN.md",
"created": "2026-05-22T21:10:00Z",
"tasks": [
{
"id": 1,
"step": "Add --version / -v flag to CLI and create tool launcher scripts",
"type": "work",
"status": "completed",
"tier": "cheap",
"commit": "af448d7633c8ef2825c21a800e99b4eaef72d85b",
"notes": "Version flags and launcher scripts are implemented and verified."
},
{
"id": 2,
"step": "Implement validation for empty or blank strings",
"type": "work",
"status": "completed",
"tier": "cheap",
"commit": "87a23031b03fdf24cabdab8f72dd57a74b6f7afc",
"notes": "Argument validation for empty and whitespace-only strings is implemented and verified."
},
{
"id": 3,
"step": "Implement help command and help flags",
"type": "work",
"status": "completed",
"tier": "standard",
"commit": "ce35313ca012603b3485f91aad8575e0536edc0f",
"notes": "Help command and help flags (--help, -h) are implemented and verified."
},
{
"id": 4,
"step": "Add CLI unit tests",
"type": "work",
"status": "completed",
"tier": "standard",
"commit": "93b3b3a1b696d16173c79e7406acb7931c4606ac",
"notes": "CLI unit tests have been added to tests/cli.test.ts and verify version, help, and validation behaviors."
},
{
"id": 5,
"step": "VERIFY: CLI Interface",
"type": "verify",
"status": "completed",
"commit": "PENDING",
"notes": "Verified build (npm run build), linting (npm run lint), and test suite (npm test) successfully pass. Manual validation of version, help, and validation checks completed."
}
]
}
33 changes: 33 additions & 0 deletions src/cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
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.length > 0) {
const firstArg = args[0];
if (firstArg === "--version" || firstArg === "-v") {
console.log("fleet-e2e-toy v1.0.0");
process.exit(0);
} else if (firstArg === "help" || firstArg === "--help" || firstArg === "-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);
} else {
console.error(`Unknown command or flag: ${firstArg}`);
process.exit(1);
}
}
}

main();
106 changes: 106 additions & 0 deletions tests/cli.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
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 fail and exit 1 for unknown command", async () => {
const result = await runCli(["unknown-command"]);
expect(result.code).toBe(1);
expect(result.stderr).toContain("Unknown command or flag: unknown-command");
expect(result.stdout).toBe("");
});

it("should fail and exit 1 for unknown flag", async () => {
const result = await runCli(["--unknown-flag"]);
expect(result.code).toBe(1);
expect(result.stderr).toContain("Unknown command or flag: --unknown-flag");
expect(result.stdout).toBe("");
});
});
});
4 changes: 4 additions & 0 deletions tool
Original file line number Diff line number Diff line change
@@ -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" "$@"
2 changes: 2 additions & 0 deletions tool.cmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@echo off
npx ts-node "%~dp0src\cli.ts" %*
Loading