diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..c7f8e50 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,23 @@ +--- +name: Bug report +description: Report a problem with Primer +labels: [bug] +--- + +## Description + +## Steps to reproduce + +1. +2. +3. + +## Expected behavior + +## Actual behavior + +## Environment + +- OS: +- Node version: +- Primer version: diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..3624ac5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Questions + url: https://github.com/pierceboggan/primer/discussions + about: Ask questions and discuss ideas here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..dabc116 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,13 @@ +--- +name: Feature request +description: Suggest an enhancement for Primer +labels: [enhancement] +--- + +## Summary + +## Problem statement + +## Proposed solution + +## Additional context diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..84350e7 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,10 @@ +## Summary + +- [ ] What changed +- [ ] Why it changed + +## Checklist + +- [ ] Tests added or updated +- [ ] Lint/typecheck pass +- [ ] Docs updated if needed diff --git a/.github/agents/code-review-codex.agent.md b/.github/agents/code-review-codex.agent.md new file mode 100644 index 0000000..ba3a4aa --- /dev/null +++ b/.github/agents/code-review-codex.agent.md @@ -0,0 +1,167 @@ +--- +description: Code review following VS Code contribution standards — correctness, lifecycle, naming, layering, accessibility, and security +name: Code Review (Codex) +tools: ["search", "read/problems", "read/terminalLastCommand", "web/githubRepo"] +model: GPT-5.3-Codex (copilot) +handoffs: + - label: Fix Issues + agent: agent + prompt: Fix the issues identified in the code review above. + send: false +--- + +You are a code reviewer for the VS Code codebase. Review changes against VS Code's engineering standards from its `copilot-instructions.md`, ESLint config, and codebase conventions. + +# Review Process + +1. **Understand context** — Read changed files and surrounding code to understand intent +2. **Check correctness** — Logic, edge cases, error handling, off-by-one errors +3. **Check VS Code conventions** — Naming, disposables, layering, localization, style, accessibility +4. **Check security** — OWASP Top 10 where relevant +5. **Check testing** — Disposable leak checks, coverage of new behavior + +# VS Code Conventions Checklist + +## Indentation + +- Use **tabs**, not spaces + +## Naming + +- **Classes, interfaces, enums, type aliases**: `PascalCase` +- **Interfaces**: prefix with `I` (e.g., `IDisposable`, `IEditorService`) +- **Enum values**: `PascalCase` +- **Functions, methods, properties, local variables**: `camelCase` +- **Private/protected members**: prefix with `_` (e.g., `private _myField`) +- **Service decorators**: `createDecorator('serviceName')` +- Use whole words in names when possible + +## Strings + +- Use `"double quotes"` for user-facing strings that need localization +- Use `'single quotes'` for everything else +- All user-visible strings must use `localize()` or `nls.localize()` +- Never concatenate localized strings — use placeholders (`{0}`, `{1}`) + +## UI Labels + +- Title-style capitalization for command labels, buttons, and menu items +- Don't capitalize prepositions of four or fewer letters unless first or last word + +## Types + +- Don't export types or functions unless shared across multiple components +- Don't introduce new types or values to the global namespace +- Don't use `any` or `unknown` unless absolutely necessary — define proper types + +## Comments + +- Use JSDoc style comments for functions, interfaces, enums, and classes + +## Style + +- Prefer arrow functions `=>` over anonymous function expressions +- Only surround arrow function parameters when necessary (`x => x` not `(x) => x`, but `(x, y) => x + y` is fine) +- Always surround loop and conditional bodies with curly braces +- Open curly braces on the same line as the statement +- Prefer top-level `export function x() {}` over `export const x = () => {}` (better stack traces) +- Prefer `async`/`await` over `.then()` chains +- Prefer named regex capture groups over numbered ones + +## Disposable Lifecycle + +- Classes holding resources must extend `Disposable` and use `this._register()` to track child disposables +- Use `DisposableStore`, `MutableDisposable`, or `DisposableMap` — never raw `IDisposable[]` +- Event listeners, file watchers, and providers must be registered via `this._register()` +- Do NOT register a disposable to the containing class if created in a method called repeatedly — return `IDisposable` from the method and let the caller register it +- Disposables must not be leaked: verify `dispose()` is called or ownership is transferred +- Prefer correlated file watchers (via `fileService.createWatcher`) over shared ones + +## Layering & Architecture + +- `/common/` — no DOM, no Node.js, no Electron imports +- `/browser/` — may use DOM APIs, never Node.js +- `/node/` or `/electron-main/` — may use Node.js APIs +- Never import `browser` from `common`, or `node` from `browser`/`common` +- Contributions use `registerWorkbenchContribution2()` with appropriate `WorkbenchPhase` +- Use `npm run valid-layers-check` to verify layering + +## Error Handling + +- Use `onUnexpectedError()` for errors in async flows that shouldn't crash +- Use typed error classes (e.g., `BugIndicatingError`) for programming errors +- Never swallow errors silently — at minimum log via `ILogService` + +## Events + +- Use `Emitter` for event sources, expose as `Event` via getter +- Register event listeners with `this._register()` to prevent leaks + +## File Headers + +- Every file must start with the Microsoft copyright header (MIT license) + +## Accessibility + +- Interactive elements must have ARIA labels +- Keyboard navigation must work for all new UI +- Screen reader announcements for dynamic state changes via `aria.alert()` +- Prefer `IHoverService` for tooltips over custom implementations + +## Code Quality + +- Never duplicate imports — reuse existing imports +- Don't duplicate code — look for existing utilities before writing new ones +- Don't use another component's storage keys directly — use proper API +- Clean up any temporary files or scripts created during development + +## Testing + +- `ensureNoDisposablesAreLeakedInTestSuite()` must be called in every test suite +- Minimize assertions — prefer one snapshot-style `assert.deepStrictEqual` over many small assertions +- Don't add tests to the wrong suite (e.g., appending to end of file instead of inside the relevant `suite`) +- Match existing test patterns (`describe`/`test` or `suite`/`test`) consistently + +# Severity Levels + +- **Critical**: Security vulnerabilities, disposable leaks in hot paths, layering violations. Must fix. +- **Major**: Bugs, missing error handling, naming violations, missing localization, `any` casts. Must fix. +- **Minor**: Style improvements, missing region markers, non-blocking refactors. Recommended. +- **Nit**: Cosmetic preferences. Optional. + +# Review Rules + +- Never approve code with Critical or Major findings +- Explain _why_ something is a problem, not just _what_ +- Suggest a concrete fix for Critical and Major findings +- Do not flag style preferences as Major issues +- Do not rewrite working code just because you would write it differently +- Limit feedback to actionable items — no praise or filler + +# Security Checklist + +- XSS: user content rendered via `MarkdownString` must set `supportHtml: false` or sanitize +- Trusted Types: use `TrustedTypePolicy` for dynamic script/style injection +- Secrets: no hardcoded credentials, tokens, or API keys in source +- Input validation: untrusted input validated at extension host / IPC boundaries +- Dependencies: no known vulnerable packages introduced + +# Output Format + +```markdown +## Summary + +One-sentence summary of the overall change quality. + +## Findings + +### [Severity] Title + +**File:** `path/to/file.ts:L42` +**Issue:** Description of the problem and why it matters. +**Suggestion:** Concrete fix or approach. + +## Verdict + +APPROVE | REQUEST_CHANGES | NEEDS_DISCUSSION +``` diff --git a/.github/agents/code-review-gemini.agent.md b/.github/agents/code-review-gemini.agent.md new file mode 100644 index 0000000..710568b --- /dev/null +++ b/.github/agents/code-review-gemini.agent.md @@ -0,0 +1,167 @@ +--- +description: Code review following VS Code contribution standards — correctness, lifecycle, naming, layering, accessibility, and security +name: Code Review (Gemini) +tools: ["search", "read/problems", "read/terminalLastCommand", "web/githubRepo"] +model: Gemini 3 Pro (Preview) (copilot) +handoffs: + - label: Fix Issues + agent: agent + prompt: Fix the issues identified in the code review above. + send: false +--- + +You are a code reviewer for the VS Code codebase. Review changes against VS Code's engineering standards from its `copilot-instructions.md`, ESLint config, and codebase conventions. + +# Review Process + +1. **Understand context** — Read changed files and surrounding code to understand intent +2. **Check correctness** — Logic, edge cases, error handling, off-by-one errors +3. **Check VS Code conventions** — Naming, disposables, layering, localization, style, accessibility +4. **Check security** — OWASP Top 10 where relevant +5. **Check testing** — Disposable leak checks, coverage of new behavior + +# VS Code Conventions Checklist + +## Indentation + +- Use **tabs**, not spaces + +## Naming + +- **Classes, interfaces, enums, type aliases**: `PascalCase` +- **Interfaces**: prefix with `I` (e.g., `IDisposable`, `IEditorService`) +- **Enum values**: `PascalCase` +- **Functions, methods, properties, local variables**: `camelCase` +- **Private/protected members**: prefix with `_` (e.g., `private _myField`) +- **Service decorators**: `createDecorator('serviceName')` +- Use whole words in names when possible + +## Strings + +- Use `"double quotes"` for user-facing strings that need localization +- Use `'single quotes'` for everything else +- All user-visible strings must use `localize()` or `nls.localize()` +- Never concatenate localized strings — use placeholders (`{0}`, `{1}`) + +## UI Labels + +- Title-style capitalization for command labels, buttons, and menu items +- Don't capitalize prepositions of four or fewer letters unless first or last word + +## Types + +- Don't export types or functions unless shared across multiple components +- Don't introduce new types or values to the global namespace +- Don't use `any` or `unknown` unless absolutely necessary — define proper types + +## Comments + +- Use JSDoc style comments for functions, interfaces, enums, and classes + +## Style + +- Prefer arrow functions `=>` over anonymous function expressions +- Only surround arrow function parameters when necessary (`x => x` not `(x) => x`, but `(x, y) => x + y` is fine) +- Always surround loop and conditional bodies with curly braces +- Open curly braces on the same line as the statement +- Prefer top-level `export function x() {}` over `export const x = () => {}` (better stack traces) +- Prefer `async`/`await` over `.then()` chains +- Prefer named regex capture groups over numbered ones + +## Disposable Lifecycle + +- Classes holding resources must extend `Disposable` and use `this._register()` to track child disposables +- Use `DisposableStore`, `MutableDisposable`, or `DisposableMap` — never raw `IDisposable[]` +- Event listeners, file watchers, and providers must be registered via `this._register()` +- Do NOT register a disposable to the containing class if created in a method called repeatedly — return `IDisposable` from the method and let the caller register it +- Disposables must not be leaked: verify `dispose()` is called or ownership is transferred +- Prefer correlated file watchers (via `fileService.createWatcher`) over shared ones + +## Layering & Architecture + +- `/common/` — no DOM, no Node.js, no Electron imports +- `/browser/` — may use DOM APIs, never Node.js +- `/node/` or `/electron-main/` — may use Node.js APIs +- Never import `browser` from `common`, or `node` from `browser`/`common` +- Contributions use `registerWorkbenchContribution2()` with appropriate `WorkbenchPhase` +- Use `npm run valid-layers-check` to verify layering + +## Error Handling + +- Use `onUnexpectedError()` for errors in async flows that shouldn't crash +- Use typed error classes (e.g., `BugIndicatingError`) for programming errors +- Never swallow errors silently — at minimum log via `ILogService` + +## Events + +- Use `Emitter` for event sources, expose as `Event` via getter +- Register event listeners with `this._register()` to prevent leaks + +## File Headers + +- Every file must start with the Microsoft copyright header (MIT license) + +## Accessibility + +- Interactive elements must have ARIA labels +- Keyboard navigation must work for all new UI +- Screen reader announcements for dynamic state changes via `aria.alert()` +- Prefer `IHoverService` for tooltips over custom implementations + +## Code Quality + +- Never duplicate imports — reuse existing imports +- Don't duplicate code — look for existing utilities before writing new ones +- Don't use another component's storage keys directly — use proper API +- Clean up any temporary files or scripts created during development + +## Testing + +- `ensureNoDisposablesAreLeakedInTestSuite()` must be called in every test suite +- Minimize assertions — prefer one snapshot-style `assert.deepStrictEqual` over many small assertions +- Don't add tests to the wrong suite (e.g., appending to end of file instead of inside the relevant `suite`) +- Match existing test patterns (`describe`/`test` or `suite`/`test`) consistently + +# Severity Levels + +- **Critical**: Security vulnerabilities, disposable leaks in hot paths, layering violations. Must fix. +- **Major**: Bugs, missing error handling, naming violations, missing localization, `any` casts. Must fix. +- **Minor**: Style improvements, missing region markers, non-blocking refactors. Recommended. +- **Nit**: Cosmetic preferences. Optional. + +# Review Rules + +- Never approve code with Critical or Major findings +- Explain _why_ something is a problem, not just _what_ +- Suggest a concrete fix for Critical and Major findings +- Do not flag style preferences as Major issues +- Do not rewrite working code just because you would write it differently +- Limit feedback to actionable items — no praise or filler + +# Security Checklist + +- XSS: user content rendered via `MarkdownString` must set `supportHtml: false` or sanitize +- Trusted Types: use `TrustedTypePolicy` for dynamic script/style injection +- Secrets: no hardcoded credentials, tokens, or API keys in source +- Input validation: untrusted input validated at extension host / IPC boundaries +- Dependencies: no known vulnerable packages introduced + +# Output Format + +```markdown +## Summary + +One-sentence summary of the overall change quality. + +## Findings + +### [Severity] Title + +**File:** `path/to/file.ts:L42` +**Issue:** Description of the problem and why it matters. +**Suggestion:** Concrete fix or approach. + +## Verdict + +APPROVE | REQUEST_CHANGES | NEEDS_DISCUSSION +``` diff --git a/.github/agents/code-review-opus.agent.md b/.github/agents/code-review-opus.agent.md new file mode 100644 index 0000000..bfdeb18 --- /dev/null +++ b/.github/agents/code-review-opus.agent.md @@ -0,0 +1,167 @@ +--- +description: Code review following VS Code contribution standards — correctness, lifecycle, naming, layering, accessibility, and security +name: Code Review (Opus) +tools: ["search", "read/problems", "read/terminalLastCommand", "web/githubRepo"] +model: Claude Opus 4.6 (fast mode) (Preview) (copilot) +handoffs: + - label: Fix Issues + agent: agent + prompt: Fix the issues identified in the code review above. + send: false +--- + +You are a code reviewer for the VS Code codebase. Review changes against VS Code's engineering standards from its `copilot-instructions.md`, ESLint config, and codebase conventions. + +# Review Process + +1. **Understand context** — Read changed files and surrounding code to understand intent +2. **Check correctness** — Logic, edge cases, error handling, off-by-one errors +3. **Check VS Code conventions** — Naming, disposables, layering, localization, style, accessibility +4. **Check security** — OWASP Top 10 where relevant +5. **Check testing** — Disposable leak checks, coverage of new behavior + +# VS Code Conventions Checklist + +## Indentation + +- Use **tabs**, not spaces + +## Naming + +- **Classes, interfaces, enums, type aliases**: `PascalCase` +- **Interfaces**: prefix with `I` (e.g., `IDisposable`, `IEditorService`) +- **Enum values**: `PascalCase` +- **Functions, methods, properties, local variables**: `camelCase` +- **Private/protected members**: prefix with `_` (e.g., `private _myField`) +- **Service decorators**: `createDecorator('serviceName')` +- Use whole words in names when possible + +## Strings + +- Use `"double quotes"` for user-facing strings that need localization +- Use `'single quotes'` for everything else +- All user-visible strings must use `localize()` or `nls.localize()` +- Never concatenate localized strings — use placeholders (`{0}`, `{1}`) + +## UI Labels + +- Title-style capitalization for command labels, buttons, and menu items +- Don't capitalize prepositions of four or fewer letters unless first or last word + +## Types + +- Don't export types or functions unless shared across multiple components +- Don't introduce new types or values to the global namespace +- Don't use `any` or `unknown` unless absolutely necessary — define proper types + +## Comments + +- Use JSDoc style comments for functions, interfaces, enums, and classes + +## Style + +- Prefer arrow functions `=>` over anonymous function expressions +- Only surround arrow function parameters when necessary (`x => x` not `(x) => x`, but `(x, y) => x + y` is fine) +- Always surround loop and conditional bodies with curly braces +- Open curly braces on the same line as the statement +- Prefer top-level `export function x() {}` over `export const x = () => {}` (better stack traces) +- Prefer `async`/`await` over `.then()` chains +- Prefer named regex capture groups over numbered ones + +## Disposable Lifecycle + +- Classes holding resources must extend `Disposable` and use `this._register()` to track child disposables +- Use `DisposableStore`, `MutableDisposable`, or `DisposableMap` — never raw `IDisposable[]` +- Event listeners, file watchers, and providers must be registered via `this._register()` +- Do NOT register a disposable to the containing class if created in a method called repeatedly — return `IDisposable` from the method and let the caller register it +- Disposables must not be leaked: verify `dispose()` is called or ownership is transferred +- Prefer correlated file watchers (via `fileService.createWatcher`) over shared ones + +## Layering & Architecture + +- `/common/` — no DOM, no Node.js, no Electron imports +- `/browser/` — may use DOM APIs, never Node.js +- `/node/` or `/electron-main/` — may use Node.js APIs +- Never import `browser` from `common`, or `node` from `browser`/`common` +- Contributions use `registerWorkbenchContribution2()` with appropriate `WorkbenchPhase` +- Use `npm run valid-layers-check` to verify layering + +## Error Handling + +- Use `onUnexpectedError()` for errors in async flows that shouldn't crash +- Use typed error classes (e.g., `BugIndicatingError`) for programming errors +- Never swallow errors silently — at minimum log via `ILogService` + +## Events + +- Use `Emitter` for event sources, expose as `Event` via getter +- Register event listeners with `this._register()` to prevent leaks + +## File Headers + +- Every file must start with the Microsoft copyright header (MIT license) + +## Accessibility + +- Interactive elements must have ARIA labels +- Keyboard navigation must work for all new UI +- Screen reader announcements for dynamic state changes via `aria.alert()` +- Prefer `IHoverService` for tooltips over custom implementations + +## Code Quality + +- Never duplicate imports — reuse existing imports +- Don't duplicate code — look for existing utilities before writing new ones +- Don't use another component's storage keys directly — use proper API +- Clean up any temporary files or scripts created during development + +## Testing + +- `ensureNoDisposablesAreLeakedInTestSuite()` must be called in every test suite +- Minimize assertions — prefer one snapshot-style `assert.deepStrictEqual` over many small assertions +- Don't add tests to the wrong suite (e.g., appending to end of file instead of inside the relevant `suite`) +- Match existing test patterns (`describe`/`test` or `suite`/`test`) consistently + +# Severity Levels + +- **Critical**: Security vulnerabilities, disposable leaks in hot paths, layering violations. Must fix. +- **Major**: Bugs, missing error handling, naming violations, missing localization, `any` casts. Must fix. +- **Minor**: Style improvements, missing region markers, non-blocking refactors. Recommended. +- **Nit**: Cosmetic preferences. Optional. + +# Review Rules + +- Never approve code with Critical or Major findings +- Explain _why_ something is a problem, not just _what_ +- Suggest a concrete fix for Critical and Major findings +- Do not flag style preferences as Major issues +- Do not rewrite working code just because you would write it differently +- Limit feedback to actionable items — no praise or filler + +# Security Checklist + +- XSS: user content rendered via `MarkdownString` must set `supportHtml: false` or sanitize +- Trusted Types: use `TrustedTypePolicy` for dynamic script/style injection +- Secrets: no hardcoded credentials, tokens, or API keys in source +- Input validation: untrusted input validated at extension host / IPC boundaries +- Dependencies: no known vulnerable packages introduced + +# Output Format + +```markdown +## Summary + +One-sentence summary of the overall change quality. + +## Findings + +### [Severity] Title + +**File:** `path/to/file.ts:L42` +**Issue:** Description of the problem and why it matters. +**Suggestion:** Concrete fix or approach. + +## Verdict + +APPROVE | REQUEST_CHANGES | NEEDS_DISCUSSION +``` diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 968515a..1cb8090 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1,4 +1,12 @@ -# Copilot Instructions for Primer +# Copilot Instructions for This Repository + +- Use ESM syntax everywhere (see "type": "module" in package.json). +- TypeScript is strict; target ES2022, module ESNext (see tsconfig.json). +- Use Commander for CLI, Ink/React for TUI, and simple-git/Octokit for GitHub automation. +- Only overwrite config files (e.g., .vscode/settings.json, .vscode/mcp.json) with --force. +- All Copilot/VS Code settings reference this file and enable MCP. +- Place new CLI commands in src/commands/, core logic in src/services/, and TUI in src/ui/. +- Do not add new build/lint/test tools unless necessary; use existing npm scripts. ## Overview @@ -52,4 +60,4 @@ - `.github/copilot-instructions.md` — This file - `src/index.ts` — CLI entrypoint - `src/services/` — Core logic -- `.vscode/settings.json` — Copilot/VS Code integration \ No newline at end of file +- `.vscode/settings.json` — Copilot/VS Code integration diff --git a/.github/prompts/deslop.prompt.md b/.github/prompts/deslop.prompt.md new file mode 100644 index 0000000..d1ec948 --- /dev/null +++ b/.github/prompts/deslop.prompt.md @@ -0,0 +1,14 @@ +--- +name: deslop +description: Remove AI-generated slop from a branch by diffing against main +--- + +Get the diff (`git diff main...HEAD`) and remove AI-generated slop from changed files: + +- Comments inconsistent with the rest of the file +- Defensive checks or try/catch blocks abnormal for that codepath +- `any` casts used to work around type issues +- Inline imports (move to top of file) +- Style inconsistencies with the surrounding code + +Preserve legitimate changes. Report a 1-3 sentence summary of what was removed. diff --git a/.github/prompts/generate-improvements.prompt.md b/.github/prompts/generate-improvements.prompt.md new file mode 100644 index 0000000..d482130 --- /dev/null +++ b/.github/prompts/generate-improvements.prompt.md @@ -0,0 +1,135 @@ +--- +description: Suggest improvements to the Primer CLI project across features, bug fixes, security, performance, and engineering quality. +--- + +You are a senior software engineer reviewing the **Primer** project — a TypeScript CLI tool that primes repositories for AI-assisted development by analyzing codebases, generating Copilot instructions and VS Code configs, running evaluations, and producing AI readiness reports. + +## Architecture Context + +- **Tech Stack:** TypeScript (ESM, strict), Node.js, React (Ink for TUI), Commander for CLI +- **Entrypoint:** `src/index.ts` → `runCli` in `src/cli.ts` +- **Dependencies:** `@github/copilot-sdk`, `@octokit/rest`, `simple-git`, `ink`, `commander`, `fast-glob`, `@inquirer/prompts` + +### Key Directories + +- `src/commands/` — CLI subcommands (`init`, `generate`, `pr`, `eval`, `tui`, `instructions`, `readiness`, `batch`, `batch-readiness`) +- `src/services/` — Core logic: + - `analyzer.ts` — Scans repo files to detect languages, frameworks, package manager, monorepo workspaces + - `instructions.ts` — Generates `.github/copilot-instructions.md` using Copilot SDK agent sessions + - `generator.ts` — Writes `.vscode/settings.json` and `.vscode/mcp.json` configs + - `evaluator.ts` — Runs eval cases comparing agent responses with/without instructions, builds trajectory viewer HTML + - `readiness.ts` — Multi-pillar AI readiness assessment (style, build, testing, docs, dev-env, code-quality, observability, security, ai-tooling) + - `visualReport.ts` — Generates beautiful HTML readiness reports with summary cards, pillar charts, level distribution + - `git.ts` — Clone/branch operations via `simple-git` + - `github.ts` / `azureDevops.ts` — GitHub (Octokit) and Azure DevOps API integrations + - `copilot.ts` — Locates and validates the Copilot CLI binary + - `evalScaffold.ts` — Scaffolds starter eval config files +- `src/ui/` — Ink/React-based TUI components (`tui.tsx`, `BatchTui.tsx`, `BatchReadinessTui.tsx`, `BatchTuiAzure.tsx`, `AnimatedBanner.tsx`) +- `src/utils/` — Shared utilities (`fs.ts` for safe file writes, `logger.ts`, `pr.ts`) + +### CLI Commands + +| Command | Description | +| ------------------------ | ------------------------------------------------------------- | +| `primer init` | Interactive setup wizard (instructions + configs) | +| `primer generate ` | Generate `instructions`, `agents`, `mcp`, or `vscode` configs | +| `primer instructions` | Generate copilot-instructions.md via Copilot SDK | +| `primer eval` | Run evaluation cases comparing with/without instructions | +| `primer readiness` | AI readiness assessment with optional visual HTML report | +| `primer batch` | Batch process multiple repos across GitHub/Azure orgs | +| `primer batch-readiness` | Batch readiness reports across multiple repos | +| `primer pr` | Automate branch/PR creation for generated configs | +| `primer tui` | Interactive Ink-based terminal UI | + +### Key Patterns + +- ESM everywhere (`"type": "module"` in `package.json`) +- Strict TypeScript (ES2022 target, ESNext modules) +- Safe file writes: only overwrites with `--force` flag (`safeWriteFile` in `src/utils/fs.ts`) +- Copilot SDK integration via `@github/copilot-sdk` with session-based agent conversations +- GitHub token resolution: `GITHUB_TOKEN` → `GH_TOKEN` → `gh auth token` fallback chain +- Readiness uses a leveled criteria system (levels 1-5) across 9 pillars with pass/fail/skip status +- Build with `tsup`, test with `vitest`, lint with `eslint`, format with `prettier` + +## Your Task + +Analyze the full codebase and generate a prioritized list of **concrete, actionable improvements**. For each suggestion, provide: + +1. **Title** — short descriptive name +2. **Category** — one of: `feature`, `bug-fix`, `security`, `performance`, `engineering`, `testing`, `dx` (developer experience) +3. **Priority** — `critical`, `high`, `medium`, `low` +4. **Description** — what the problem or opportunity is and why it matters +5. **Suggested implementation** — specific code changes, files to modify, and approach + +## Areas to Evaluate + +### Features & Functionality + +- Are there CLI commands or flags referenced in README/help text that aren't fully implemented? +- Could `analyzeRepo` detect more languages, frameworks, or package managers (e.g., Gradle, Maven, .NET, Ruby)? +- Does `primer init --yes` skip useful defaults (currently only selects instructions, not MCP/VS Code configs)? +- Could `primer readiness` support more output formats (e.g., CSV, PDF) or comparison over time? +- Are there opportunities to improve the batch processing UX (progress, retries, parallel execution)? +- Could `primer eval` scaffold richer default eval cases or support custom grading rubrics? + +### Bug Fixes & Correctness + +- Does `analyzeRepo` correctly handle edge cases like empty repos, non-git directories, or deeply nested monorepos? +- Does `readPnpmWorkspace` handle all valid YAML edge cases or is the line-by-line parser fragile? +- Does the Copilot SDK session handling (`instructions.ts`) properly clean up on errors (session.destroy, client.stop)? +- Are there race conditions in batch processing when cloning/analyzing multiple repos concurrently? +- Does `process.chdir()` in `generateCopilotInstructions` create issues if called concurrently? + +### Security + +- Is the GitHub token (`getGitHubToken`) handled securely — never logged, never leaked in error messages? +- Are user-supplied repo paths validated against path traversal (e.g., `../../etc/passwd` as a repo path)? +- Does `execFileAsync` usage properly sanitize arguments to prevent command injection? +- Are Azure DevOps PAT tokens handled securely throughout the `azureDevops.ts` service? +- Is the `safeWriteFile` function safe against symlink attacks (writing through a symlink to an unintended location)? + +### Performance + +- Could `analyzeRepo` avoid redundant `readdir`/`readFile` calls when the same repo is analyzed multiple times? +- Is `fast-glob` usage in workspace detection efficient for large monorepos with many packages? +- Could the Copilot CLI path lookup (`findCopilotCliPath` in `copilot.ts`) be cached across invocations? +- Are batch operations (batch, batch-readiness) parallelized effectively, or do they process repos sequentially? +- Does the eval trajectory viewer HTML (`evaluator.ts`) generate excessively large output for many eval cases? + +### Engineering Quality + +- Are there TypeScript strict-mode violations, `any` types, or `as` casts that should be eliminated? +- Is error handling consistent across services — do all commands give clear, actionable error messages? +- Are there dead code paths or unused exports in the services or commands? +- Could the `process.chdir()` pattern in `instructions.ts` be replaced with a safer approach (e.g., passing cwd to child processes)? +- Are service interfaces well-separated for testability, or are there tight couplings (e.g., direct `process.env` reads)? + +### Testing + +- What is the current test coverage? Only `analyzer.test.ts`, `fs.test.ts`, `readiness.test.ts`, and `visualReport.test.ts` exist — many services and commands are untested. +- Are there tests for the Copilot SDK integration paths (even with mocked SDK)? +- Are edge cases in `readPnpmWorkspace`, `detectWorkspace`, and `resolveWorkspaceApps` covered? +- Are there integration tests for the full `primer init` or `primer generate` flows? +- Is the GitHub/Azure DevOps API integration tested with mocked HTTP responses? + +### Developer Experience + +- Is the `npx tsx` workflow sufficient, or should there be a `dev` script for faster iteration? +- Are error messages clear when prerequisites are missing (Copilot CLI, GitHub token, `gh` CLI)? +- Is the TUI (`src/ui/tui.tsx`) tested or difficult to test due to Ink rendering? +- Are there missing npm scripts for common workflows (e.g., `npm run dev`, `npm run test:unit`)? + +## Output Format + +Return the improvements as a numbered list grouped by category. Use this structure: + +``` +## Category Name + +### 1. Title (Priority: critical/high/medium/low) +**Problem:** What's wrong or missing +**Suggestion:** Specific changes to make +**Files:** Which files to modify +``` + +Focus on substance over volume. Prefer 10 high-quality, specific suggestions over 30 vague ones. Always reference actual code, file paths, and function names from the codebase. diff --git a/.github/prompts/review.prompt.md b/.github/prompts/review.prompt.md new file mode 100644 index 0000000..6150a18 --- /dev/null +++ b/.github/prompts/review.prompt.md @@ -0,0 +1,11 @@ +--- +name: review +description: Run three parallel code reviews (Opus, Gemini, Codex) and synthesize findings into a prioritized fix list +--- + +Run a multi-model code review: + +1. Invoke `code-review-opus`, `code-review-gemini`, and `code-review-codex` as three parallel subagents +2. Cross-grade: have each reviewer evaluate the other two reviews for false positives and missed issues +3. Synthesize a deduplicated list of findings ordered by severity (Critical > Major > Minor > Nit) +4. Output one final fix list with file, line, and suggested change for each item diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..5c8a778 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,86 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + lint: + name: Lint & Format + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + - run: npm ci + - name: Lint + run: npm run lint + - name: Format check + run: npm run format:check + + typecheck: + name: Typecheck + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + - run: npm ci + - run: npm run typecheck + + test: + name: Test (Node ${{ matrix.node }}, ${{ matrix.os }}) + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + node: [20, 22] + os: [ubuntu-latest] + include: + - node: 22 + os: windows-latest + - node: 22 + os: macos-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node }} + cache: npm + - run: npm ci + - name: Test + run: npm run test:coverage + - name: Upload coverage + if: matrix.node == 22 && matrix.os == 'ubuntu-latest' + uses: actions/upload-artifact@v4 + with: + name: coverage + path: coverage + if-no-files-found: ignore + + build: + name: Build & Verify + runs-on: ubuntu-latest + needs: [lint, typecheck, test] + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + - run: npm ci + - run: npm run build + - name: Verify CLI + run: node dist/index.js --version diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml new file mode 100644 index 0000000..3547bac --- /dev/null +++ b/.github/workflows/release-please.yml @@ -0,0 +1,19 @@ +name: Release Please + +on: + push: + branches: [main] + +permissions: + contents: write + pull-requests: write + +jobs: + release-please: + runs-on: ubuntu-latest + steps: + - name: Release + uses: google-github-actions/release-please-action@v4 + with: + config-file: release-please-config.json + manifest-file: release-please-manifest.json diff --git a/.gitignore b/.gitignore index 6b4a136..bdc551a 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ node_modules/ # Build output dist/ +coverage/ # IDE .vscode/ @@ -25,3 +26,7 @@ npm-debug.log* # Primer cache .primer-cache/ + +# Primer outputs +.primer/ +readiness-report.html diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..1ad2e3d --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,5 @@ +{ + "singleQuote": false, + "trailingComma": "none", + "printWidth": 100 +} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..f6433ab --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,58 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +## [Unreleased] + +### Complete Rewrite + +Primer vNext is a complete rewrite as a TypeScript CLI tool (ESM, strict, ES2022) for priming repositories for AI-assisted development and evaluation. + +### New Commands + +- **`primer readiness`** — AI readiness report scoring repos across 9 pillars (style, build, testing, docs, dev-env, code-quality, observability, security, AI tooling) with a 5-level maturity model (Functional → Autonomous). +- **`primer readiness --visual`** — GitHub-themed HTML report with light/dark toggle, expandable pillar details, and maturity model descriptions. +- **`primer batch-readiness`** — Consolidated visual readiness report across multiple repositories. +- **`primer generate instructions`** — Generate `copilot-instructions.md` via Copilot SDK, with `--per-app` support for monorepos. +- **`primer generate agents`** — Generate `AGENTS.md` guidance files. +- **`primer eval --init`** — AI-powered eval scaffold generation that analyzes codebases and produces cross-cutting eval cases. +- **`primer eval --list-models`** — List available Copilot CLI models. + +### New Features + +- **Azure DevOps integration** — Full support for batch processing, PR creation, and repo cloning via Azure DevOps PAT authentication. +- **Monorepo detection** — Detect and analyze Cargo workspaces, Go workspaces (`go.work`), .NET solutions (`.sln`), Gradle multi-project, Maven multi-module, in addition to npm/pnpm/yarn workspaces. +- **Eval trajectory viewer** — Interactive HTML viewer comparing responses with/without instructions, including token usage, tool call metrics, and duration tracking. +- **Copilot CLI discovery** — Cross-platform discovery with TTL caching and glob-based fallback for VS Code extension paths. +- **Centralized model defaults** — Default model set to `claude-sonnet-4.5` via `src/config.ts`. + +### Improvements + +- Replaced `process.chdir()` with safe `withCwd` utility for directory switching during Copilot SDK calls. +- Path traversal protection via `validateCachePath` for cloned repo paths. +- Credential sanitization in git push error messages to prevent token leaks. +- `buildAuthedUrl` utility supporting both GitHub (`x-access-token`) and Azure DevOps (`pat`) auth. +- `safeWriteFile` utility for safe config file writes (skip existing unless `--force`). +- `checkRepoHasInstructions` now re-throws non-404 errors instead of silently returning false. +- `init --yes` now generates instructions, MCP, and VS Code configs (previously only instructions). + +### Removed + +- Removed stub commands: `analyze`, `templates`, `update`, `config`. + +### Testing & Tooling + +- Vitest test framework with 51+ tests covering analyzer, generator, git, readiness, visual report, fs utilities, and cache path validation. +- ESLint flat config with TypeScript, import ordering, and Prettier integration. +- CI workflow with lint, typecheck, tests (Node 20/22, Ubuntu/macOS/Windows), and build verification. +- Release automation via release-please. +- Code coverage via `@vitest/coverage-v8`. + +### Project Setup + +- Added CONTRIBUTING.md, SECURITY.md, LICENSE (MIT), and CODEOWNERS. +- Added issue templates (bug report, feature request) and PR template. +- Added `.github/agents/` with multi-model code review agents (Opus, Gemini, Codex). +- Added `.github/prompts/` with reusable prompts (deslop, review, generate-improvements). +- Added examples folder with sample eval config and CLI usage guide. +- Added `.prettierrc.json` with project formatting rules. diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000..77511c7 --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1 @@ +* @pierceboggan diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..e66fc98 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,34 @@ +# Contributing + +Thanks for contributing to Primer. + +## Quick start + +1. Fork and clone the repo. +2. Install dependencies: npm install +3. Build locally: npm run build +4. Run lint/typecheck/tests before opening a PR: + - npm run lint + - npm run typecheck + - npm run test + +## Development workflow + +- Create a feature branch from main. +- Use clear, conventional commit messages (e.g. feat: add readiness report). +- Keep PRs focused and include context in the description. +- Add or update tests when behavior changes. + +## Code style + +- ESLint + Prettier are enforced in CI. +- Prefer small, composable functions with clear types. + +## Reporting issues + +- Use GitHub Issues for bugs and feature requests. +- Provide steps to reproduce and expected behavior. + +## Releasing + +Releases are automated with release-please when changes are merged to main. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..cb6db3e --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Primer + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/PLAN.md b/PLAN.md index 3dcc268..8259911 100644 --- a/PLAN.md +++ b/PLAN.md @@ -12,21 +12,22 @@ Make any repository "AI-ready" with a single command — generating optimal conf ## ✨ Core Features -### 1. **Repository Analysis** -- Detect language(s), frameworks, and project structure -- Identify existing AI configurations -- Analyze package managers, build tools, and testing frameworks -- Detect monorepo vs single-project structure +### 1. **Readiness Report** + +- Score AI readiness across key pillars +- Provide fix-first checklists and maturity levels +- Support monorepos with app-scoped checks ### 2. **Configuration Generation** -| Config Type | Description | -|-------------|-------------| -| **Custom Instructions** | `.github/copilot-instructions.md` generated via Copilot SDK | -| **MCP Server Config** | `.vscode/mcp.json` for Model Context Protocol servers | -| **VS Code Settings** | `.vscode/settings.json` with AI-optimized workspace settings | +| Config Type | Description | +| ----------------------- | ------------------------------------------------------------ | +| **Custom Instructions** | `.github/copilot-instructions.md` generated via Copilot SDK | +| **MCP Server Config** | `.vscode/mcp.json` for Model Context Protocol servers | +| **VS Code Settings** | `.vscode/settings.json` with AI-optimized workspace settings | ### 3. **GitHub Integration** + - Authenticate via GitHub CLI (`gh auth`) or OAuth device flow - List and select from accessible repositories - Clone repos temporarily for analysis @@ -34,11 +35,13 @@ Make any repository "AI-ready" with a single command — generating optimal conf - Support for GitHub Enterprise ### 4. **Local Repository Support** + - Detect local Git repositories - Work offline with local-only mode - Push changes to remote when ready ### 5. **Interactive & Non-Interactive Modes** + - Beautiful TUI with prompts and previews - CI/CD-friendly `--yes` flag for automation - JSON output for scripting @@ -62,17 +65,17 @@ Make any repository "AI-ready" with a single command — generating optimal conf ### Tech Stack Recommendation -| Component | Choice | Rationale | -|-----------|--------|-----------| -| **Language** | TypeScript | Type safety, excellent tooling, npm ecosystem | -| **CLI Framework** | [Commander.js](https://github.com/tj/commander.js) | Mature, cross-platform, great DX | -| **TUI** | [Ink](https://github.com/vadimdemedes/ink) | React for CLIs, beautiful components | -| **Prompts** | [@inquirer/prompts](https://github.com/SBoudrias/Inquirer.js) | Modern, accessible prompts | -| **GitHub API** | [Octokit](https://github.com/octokit/octokit.js) | Official GitHub SDK | -| **Git Operations** | [simple-git](https://github.com/steveukx/git-js) | Cross-platform Git commands | -| **Styling** | [chalk](https://github.com/chalk/chalk) + [boxen](https://github.com/sindresorhus/boxen) | Beautiful terminal output | -| **Bundling** | [tsup](https://github.com/egoist/tsup) | Fast, zero-config bundler | -| **Distribution** | npm + standalone binaries via [pkg](https://github.com/vercel/pkg) | Maximum reach | +| Component | Choice | Rationale | +| ------------------ | ---------------------------------------------------------------------------------------- | --------------------------------------------- | +| **Language** | TypeScript | Type safety, excellent tooling, npm ecosystem | +| **CLI Framework** | [Commander.js](https://github.com/tj/commander.js) | Mature, cross-platform, great DX | +| **TUI** | [Ink](https://github.com/vadimdemedes/ink) | React for CLIs, beautiful components | +| **Prompts** | [@inquirer/prompts](https://github.com/SBoudrias/Inquirer.js) | Modern, accessible prompts | +| **GitHub API** | [Octokit](https://github.com/octokit/octokit.js) | Official GitHub SDK | +| **Git Operations** | [simple-git](https://github.com/steveukx/git-js) | Cross-platform Git commands | +| **Styling** | [chalk](https://github.com/chalk/chalk) + [boxen](https://github.com/sindresorhus/boxen) | Beautiful terminal output | +| **Bundling** | [tsup](https://github.com/egoist/tsup) | Fast, zero-config bundler | +| **Distribution** | npm + standalone binaries via [pkg](https://github.com/vercel/pkg) | Maximum reach | --- @@ -96,8 +99,8 @@ primer generate vscode # Create PR with all generated configs primer pr owner/repo -# Analyze repo without making changes -primer analyze +# Readiness report +primer readiness # Update existing configurations primer update @@ -107,6 +110,18 @@ primer templates # Configure CLI settings primer config + +# Generate instructions +primer instructions + +# Run evaluations +primer eval primer.eval.json + +# Run TUI +primer tui + +# Batch processing +primer batch ``` --- @@ -114,6 +129,7 @@ primer config ## 🔍 Repository Detection Logic ### Language Detection Priority + 1. Check for lock files (`package-lock.json`, `yarn.lock`, `Cargo.lock`, `go.sum`, etc.) 2. Analyze file extensions distribution 3. Check for framework-specific files @@ -121,17 +137,18 @@ primer config ### Framework Detection -| Language | Frameworks to Detect | -|----------|---------------------| +| Language | Frameworks to Detect | +| ------------------------- | --------------------------------------------------------------------- | | **JavaScript/TypeScript** | React, Vue, Angular, Next.js, Nuxt, Svelte, Express, Nest.js, Fastify | -| **Python** | Django, Flask, FastAPI, Pandas/NumPy (data science) | -| **Go** | Gin, Echo, Fiber | -| **Rust** | Actix, Axum, Rocket | -| **Java** | Spring Boot, Quarkus | -| **C#** | ASP.NET Core, Blazor | -| **Ruby** | Rails, Sinatra | +| **Python** | Django, Flask, FastAPI, Pandas/NumPy (data science) | +| **Go** | Gin, Echo, Fiber | +| **Rust** | Actix, Axum, Rocket | +| **Java** | Spring Boot, Quarkus | +| **C#** | ASP.NET Core, Blazor | +| **Ruby** | Rails, Sinatra | ### Project Type Classification + - **Frontend**: UI components, styling, client-side routing - **Backend**: API routes, database schemas, authentication - **Full-stack**: Both frontend and backend @@ -150,28 +167,33 @@ primer config # Project: {name} ## Tech Stack + - Language: TypeScript - Framework: Next.js 14 (App Router) - Styling: Tailwind CSS - Database: Prisma + PostgreSQL ## Coding Conventions + - Use functional components with hooks - Prefer server components where possible - Use `cn()` utility for conditional classes - Follow existing patterns in `src/components/` ## File Structure + - `src/app/` - App router pages and layouts - `src/components/` - Reusable UI components - `src/lib/` - Utility functions and shared logic - `src/server/` - Server-side code and API logic ## Testing + - Run tests: `npm test` - Test files: `*.test.ts` colocated with source ## Important Notes + - This project uses {specific conventions} - Avoid {anti-patterns specific to this codebase} ``` @@ -299,6 +321,7 @@ primer config ### GitHub Auth Flow 1. **Check for existing `gh` CLI auth** + ```bash gh auth status ``` @@ -313,6 +336,7 @@ primer config - Fallback to encrypted file in `~/.config/primer/` ### Required Scopes + - `repo` - Full repository access - `read:user` - Read user profile @@ -344,11 +368,11 @@ This PR adds configurations to prime this repository for AI coding assistants. ### Added Files -| File | Purpose | -|------|---------| -| `.github/copilot-instructions.md` | Project context for GitHub Copilot | -| `.vscode/settings.json` | VS Code settings for optimal AI assistance | -| `.vscode/mcp.json` | Model Context Protocol server configuration | +| File | Purpose | +| --------------------------------- | ------------------------------------------- | +| `.github/copilot-instructions.md` | Project context for GitHub Copilot | +| `.vscode/settings.json` | VS Code settings for optimal AI assistance | +| `.vscode/mcp.json` | Model Context Protocol server configuration | ### How to Use @@ -357,7 +381,8 @@ This PR adds configurations to prime this repository for AI coding assistants. 3. Start chatting with Copilot — it now understands your project! --- -*Generated by [Primer](https://github.com/your-org/primer)* + +_Generated by [Primer](https://github.com/your-org/primer)_ ``` --- @@ -370,23 +395,32 @@ primer/ │ ├── index.ts # Entry point │ ├── cli.ts # Commander setup │ ├── commands/ -│ │ ├── init.ts +│ │ ├── batch.tsx +│ │ ├── config.ts +│ │ ├── eval.ts │ │ ├── generate.ts -│ │ ├── analyze.ts +│ │ ├── init.ts +│ │ ├── instructions.tsx │ │ ├── pr.ts -│ │ └── config.ts +│ │ ├── readiness.ts +│ │ ├── templates.ts +│ │ ├── tui.tsx +│ │ └── update.ts │ ├── services/ -│ │ ├── github.ts # GitHub API interactions │ │ ├── analyzer.ts # Repo analysis logic +│ │ ├── azureDevops.ts # Azure DevOps integration +│ │ ├── evaluator.ts # Eval runner │ │ ├── generator.ts # Config generation -│ │ └── git.ts # Local git operations +│ │ ├── git.ts # Local git operations +│ │ ├── github.ts # GitHub API interactions +│ │ └── instructions.ts # Copilot SDK integration │ ├── ui/ -│ │ ├── prompts.ts # Inquirer prompts -│ │ ├── spinner.ts # Loading indicators -│ │ └── preview.ts # File previews +│ │ ├── AnimatedBanner.tsx +│ │ ├── BatchTui.tsx +│ │ ├── BatchTuiAzure.tsx +│ │ └── tui.tsx │ └── utils/ │ ├── fs.ts # File system helpers -│ ├── detection.ts # Language/framework detection │ └── logger.ts # Styled console output ├── package.json ├── tsconfig.json @@ -398,21 +432,26 @@ primer/ ## 🧪 Testing Strategy ### Unit Tests + - Template rendering with different inputs - Language/framework detection - Config merging logic ### Integration Tests + - Full init flow (mocked filesystem) - GitHub API interactions (mocked) - PR creation flow ### E2E Tests + - Real repo analysis (test fixtures) - Actual file generation ### Test Fixtures + Create example repos for each major stack: + - `fixtures/nextjs-app/` - `fixtures/python-fastapi/` - `fixtures/rust-cli/` @@ -423,12 +462,14 @@ Create example repos for each major stack: ## 🌟 Additional Feature Ideas ### Phase 2 + - [ ] **Team Sync** — Share configs across org/team repos - [ ] **Config Validation** — Lint generated configs - [ ] **Diff View** — Show what will change in existing files - [ ] **Rollback** — Undo generated changes ### Phase 3 + - [ ] **AI Enhancement** — Use AI to generate better project-specific instructions - [ ] **Telemetry** — Anonymous usage stats (opt-in) - [ ] **VS Code Extension** — GUI version of the CLI @@ -436,6 +477,7 @@ Create example repos for each major stack: - [ ] **Monorepo Support** — Generate configs per package ### Community Features + - [ ] **Repo Showcase** — Examples of well-configured repos --- @@ -443,6 +485,7 @@ Create example repos for each major stack: ## 📅 Implementation Phases ### Phase 1: MVP (2-3 weeks) + - [x] Project setup (TypeScript, Commander, tsup) - [ ] Basic CLI with `init` and `generate` commands - [ ] Local repo analysis @@ -451,18 +494,21 @@ Create example repos for each major stack: - [ ] Basic interactive prompts ### Phase 2: GitHub Integration (1-2 weeks) + - [ ] GitHub authentication - [ ] Remote repo access - [ ] PR creation - [ ] Fork workflow ### Phase 3: Polish (1 week) + - [ ] Beautiful TUI with previews - [ ] More language/framework support - [ ] MCP configurations - [ ] Documentation and examples ### Phase 4: Distribution (1 week) + - [ ] npm publish - [ ] Standalone binaries - [ ] Homebrew formula @@ -476,24 +522,24 @@ Create example repos for each major stack: # Install dependencies npm install -# Development with watch mode -npm run dev - # Build for production npm run build -# Run tests -npm test - # Lint and format npm run lint npm run format +# Type check +npm run typecheck + +# Run tests +npm run test + +# Coverage +npm run test:coverage + # Link globally for testing npm link - -# Create standalone binaries -npm run package ``` --- @@ -516,4 +562,4 @@ npm run package --- -*This plan is a living document. Update as the project evolves.* +_This plan is a living document. Update as the project evolves._ diff --git a/README.md b/README.md index 7c5dd6c..510d4d7 100644 --- a/README.md +++ b/README.md @@ -2,290 +2,209 @@ > Prime your repositories for AI-assisted development. -Primer is a CLI tool that analyzes your codebase and generates `.github/copilot-instructions.md` files to help AI coding assistants understand your project better. It supports single repos, batch processing across organizations, and includes an evaluation framework to measure instruction effectiveness. +[![CI](https://github.com/pierceboggan/primer/actions/workflows/ci.yml/badge.svg)](https://github.com/pierceboggan/primer/actions/workflows/ci.yml) +[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE) -![Primer](primer.png) +Primer is a CLI tool that helps teams prepare repositories for AI-assisted development. It generates custom instructions, assesses AI readiness with a maturity model, and supports batch processing across organizations — with an interactive TUI and beautiful visual reports. + +```bash +npx github:pierceboggan/primer readiness --visual +``` ## Features -- **Repository Analysis** - Detects languages, frameworks, and package managers -- **AI-Powered Generation** - Uses the Copilot SDK to analyze your codebase and generate context-aware instructions -- **Batch Processing** - Process multiple repos across organizations with a single command -- **Evaluation Framework** - Test and measure how well your instructions improve AI responses -- **GitHub Integration** - Clone repos, create branches, and open PRs automatically -- **Interactive TUI** - Beautiful terminal interface built with Ink -- **Config Generation** - Generate MCP and VS Code configurations +- **AI Readiness Reports** — Score repos across 9 pillars with a maturity model (Functional → Autonomous), including an AI tooling pillar that checks for MCP, custom agents, Copilot skills, and custom instructions +- **Visual Reports** — GitHub-themed HTML reports with light/dark toggle, expandable pillar details, and maturity model descriptions +- **Instruction Generation** — Generate `copilot-instructions.md` or `AGENTS.md` using the Copilot SDK, with per-app support for monorepos +- **Batch Processing** — Process multiple repos across GitHub or Azure DevOps organizations +- **Evaluation Framework** — Measure how instructions improve AI responses with a judge model +- **Interactive TUI** — Ink-based terminal UI with submenus, model picker, activity log, and animated banner +- **Config Generation** — Generate MCP and VS Code configurations +- **GitHub Integration** — Clone repos, create branches, and open PRs automatically ## Prerequisites -1. **Node.js 18+** -2. **GitHub Copilot CLI** - Installed via VS Code's Copilot Chat extension -3. **Copilot CLI Authentication** - Run `copilot` then `/login` to authenticate -4. **GitHub CLI (optional)** - For batch processing and PR creation: `brew install gh && gh auth login` +| Requirement | Notes | +| --------------------------------- | ------------------------------------------------------------------------ | +| **Node.js 20+** | Runtime | +| **GitHub Copilot CLI** | Bundled with the VS Code Copilot Chat extension | +| **Copilot authentication** | Run `copilot` → `/login` | +| **GitHub CLI** _(optional)_ | For batch processing and PR creation: `brew install gh && gh auth login` | +| **Azure DevOps PAT** _(optional)_ | Set `AZURE_DEVOPS_PAT` for Azure DevOps workflows | ## Installation ```bash -# Clone and install git clone https://github.com/pierceboggan/primer.git cd primer npm install +npm run build +npm link ``` -## Usage - -### Quick Start (Init) - -The easiest way to get started is with the `init` command: +Verify: ```bash -# Interactive setup for current directory -npx tsx src/index.ts init - -# Accept defaults and generate instructions automatically -npx tsx src/index.ts init --yes - -# Work with a GitHub repository -npx tsx src/index.ts init --github +primer --help ``` -### Interactive Mode (TUI) +## Quick Start ```bash -# Run TUI in current directory -npx tsx src/index.ts tui - -# Run on a specific repo -npx tsx src/index.ts tui --repo /path/to/repo +# Interactive setup — generates instructions, MCP, and VS Code configs +primer init -# Skip the animated intro -npx tsx src/index.ts tui --no-animation +# Non-interactive with defaults +primer init --yes ``` -**Keys:** -- `[A]` Analyze - Detect languages, frameworks, and package manager -- `[G]` Generate - Generate copilot-instructions.md using Copilot SDK -- `[S]` Save - Save generated instructions (in preview mode) -- `[D]` Discard - Discard generated instructions (in preview mode) -- `[Q]` Quit +## Commands -### Generate Instructions - -```bash -# Generate instructions for current directory -npx tsx src/index.ts instructions +### `primer readiness` -# Generate for specific repo with custom output -npx tsx src/index.ts instructions --repo /path/to/repo --output ./instructions.md +Score a repo's AI readiness across 9 pillars: -# Use a specific model -npx tsx src/index.ts instructions --model gpt-5 +```bash +primer readiness # terminal summary +primer readiness --visual # GitHub-themed HTML report +primer readiness --json # machine-readable JSON +primer readiness /path/to/repo --output report.html ``` -### Batch Processing +**Maturity levels:** -Process multiple repositories across organizations: +| Level | Name | Description | +| ----- | ------------ | --------------------------------------------------- | +| 1 | Functional | Builds, tests, basic tooling in place | +| 2 | Documented | README, CONTRIBUTING, custom AI instructions exist | +| 3 | Standardized | CI/CD, security policies, CODEOWNERS, observability | +| 4 | Optimized | MCP servers, custom agents, AI skills configured | +| 5 | Autonomous | Full AI-native development with minimal oversight | -```bash -# Launch batch TUI -npx tsx src/index.ts batch +**AI Tooling checks** include `copilot-instructions.md`, `CLAUDE.md`, `AGENTS.md`, `.cursorrules`, MCP configs, custom agents, and Copilot/Claude skills. -# Save results to file -npx tsx src/index.ts batch --output results.json -``` +### `primer batch-readiness` -**Batch TUI Keys:** -- `[Space]` Toggle selection -- `[A]` Select all repos -- `[Enter]` Confirm selection -- `[Y/N]` Confirm/cancel processing -- `[Q]` Quit - -### Analyze Repository +Consolidated visual readiness report across multiple repositories: ```bash -# Analyze current directory -npx tsx src/index.ts analyze - -# Analyze specific path with JSON output -npx tsx src/index.ts analyze /path/to/repo --json +primer batch-readiness +primer batch-readiness --output team-readiness.html ``` -### Generate Configs +### `primer generate` -Generate configuration files for your repo: +Generate AI configuration files using the Copilot SDK: ```bash -# Generate MCP config -npx tsx src/index.ts generate mcp - -# Generate VS Code settings -npx tsx src/index.ts generate vscode --force - -# Generate custom prompts -npx tsx src/index.ts generate prompts - -# Generate agent configs -npx tsx src/index.ts generate agents - -# Generate .aiignore file -npx tsx src/index.ts generate aiignore +primer generate instructions # copilot-instructions.md +primer generate agents # AGENTS.md +primer generate instructions --per-app # per-app in monorepos +primer generate mcp # .vscode/mcp.json +primer generate vscode --force # .vscode/settings.json (overwrite) ``` -### Manage Templates - -View available instruction templates: +Standalone shortcut for instructions: ```bash -npx tsx src/index.ts templates +primer instructions --repo /path/to/repo --model claude-sonnet-4.5 ``` -### Configuration +### `primer eval` -View and manage Primer configuration: +Measure how instructions improve AI responses: ```bash -npx tsx src/index.ts config +primer eval --init # scaffold eval config from codebase analysis +primer eval primer.eval.json --repo . # run evaluation (default model: claude-sonnet-4.5) +primer eval --model gpt-4.1 --judge-model claude-sonnet-4.5 +primer eval --list-models # list available Copilot CLI models ``` -### Update +Results include an interactive HTML trajectory viewer comparing responses with/without instructions, with token usage, tool-call counts, and duration. + +### `primer batch` -Check for and apply updates: +Batch-process repos across an organization: ```bash -npx tsx src/index.ts update +primer batch # GitHub repos +primer batch --provider azure # Azure DevOps repos ``` -### Create Pull Requests +### `primer pr` -Automatically create a PR to add Primer configs to a repository: +Clone a repo, generate configs, and open a PR: ```bash -# Create PR for a GitHub repo -npx tsx src/index.ts pr owner/repo-name - -# Use custom branch name -npx tsx src/index.ts pr owner/repo-name --branch primer/custom-branch +primer pr owner/repo-name +primer pr my-org/my-project/my-repo --provider azure ``` -### Evaluation Framework +### `primer tui` -Test how well your instructions improve AI responses: +Interactive terminal UI: ```bash -# Create a starter eval config -npx tsx src/index.ts eval --init - -# Run evaluation -npx tsx src/index.ts eval primer.eval.json --repo /path/to/repo - -# Save results and use specific models -npx tsx src/index.ts eval --output results.json --model gpt-5 --judge-model gpt-5 +primer tui +primer tui --repo /path/to/repo --no-animation ``` -Example `primer.eval.json`: -```json -{ - "instructionFile": ".github/copilot-instructions.md", - "cases": [ - { - "id": "project-overview", - "prompt": "Summarize what this project does and list the main entry points.", - "expectation": "Should mention the primary purpose and key files/directories." - } - ] -} -``` - -## How It Works +| Key | Action | +| --------- | ---------------------------------- | +| `G` | Generate instructions or AGENTS.md | +| `E` | Run evals or init eval config | +| `B` | Batch — GitHub or Azure DevOps | +| `M` / `J` | Pick eval / judge model | +| `Q` | Quit | -1. **Analysis** - Scans the repository for: - - Language files (`.ts`, `.js`, `.py`, `.go`, etc.) - - Framework indicators (`package.json`, `tsconfig.json`, etc.) - - Package manager lock files +## Development -2. **Generation** - Uses the Copilot SDK to: - - Start a Copilot CLI session - - Let the AI agent explore your codebase using tools (`glob`, `view`, `grep`) - - Generate concise, project-specific instructions +```bash +npm run typecheck # type check +npm run lint # ESLint (flat config + Prettier) +npm run test # 51+ Vitest tests +npm run test:coverage # with coverage via @vitest/coverage-v8 +npm run build # production build via tsup -3. **Batch Processing** - For multiple repos: - - Select organizations and repositories via TUI - - Clone, branch, generate, commit, push, and create PRs - - Track success/failure for each repository +# Run from source (no build needed) +npx tsx src/index.ts --help +``` -4. **Evaluation** - Measure instruction quality: - - Run prompts with and without instructions - - Use a judge model to score responses - - Generate comparison reports +See [CONTRIBUTING.md](CONTRIBUTING.md) for workflow and code style guidelines. ## Project Structure ``` -primer/ -├── src/ -│ ├── index.ts # Entry point -│ ├── cli.ts # Commander CLI setup -│ ├── commands/ # CLI commands -│ │ ├── analyze.ts # Repository analysis -│ │ ├── batch.tsx # Batch processing -│ │ ├── config.ts # Config management -│ │ ├── eval.ts # Evaluation framework -│ │ ├── generate.ts # Config generation -│ │ ├── init.ts # Interactive setup -│ │ ├── instructions.tsx # Instructions generation -│ │ ├── pr.ts # PR creation -│ │ ├── templates.ts # Template management -│ │ ├── tui.tsx # TUI launcher -│ │ └── update.ts # Update command -│ ├── services/ # Core business logic -│ │ ├── analyzer.ts # Repository analysis -│ │ ├── evaluator.ts # Eval runner -│ │ ├── generator.ts # Config generation -│ │ ├── git.ts # Git operations -│ │ ├── github.ts # GitHub API -│ │ └── instructions.ts # Copilot SDK integration -│ ├── ui/ # Terminal UI -│ │ ├── AnimatedBanner.tsx -│ │ ├── BatchTui.tsx # Batch processing UI -│ │ └── tui.tsx # Main TUI -│ └── utils/ # Helpers -│ ├── fs.ts -│ └── logger.ts -├── package.json -├── tsconfig.json -├── primer.eval.json # Example eval config -└── PLAN.md # Project roadmap -``` - -## Development - -```bash -# Type check -npx tsc -p tsconfig.json --noEmit - -# Run in dev mode -npx tsx src/index.ts +src/ +├── index.ts # Entry point +├── cli.ts # Commander CLI wiring +├── config.ts # Default models (claude-sonnet-4.5) +├── commands/ # CLI subcommands +├── services/ # Core logic +│ ├── analyzer.ts # Repo scanning (languages, frameworks, monorepos) +│ ├── readiness.ts # 9-pillar scoring engine +│ ├── visualReport.ts # HTML report generator +│ ├── instructions.ts # Copilot SDK integration +│ ├── evaluator.ts # Eval runner + trajectory viewer +│ ├── evalScaffold.ts # AI-powered eval config generation +│ ├── generator.ts # MCP/VS Code config generation +│ ├── git.ts # Git operations (clone, branch, push) +│ ├── github.ts # GitHub API (Octokit) +│ ├── azureDevops.ts # Azure DevOps API +│ └── __tests__/ # Test suite +├── ui/ # Ink/React terminal UI +└── utils/ # Shared utilities (fs, logger, cwd) ``` ## Troubleshooting -### "Copilot CLI not found" -Install the GitHub Copilot Chat extension in VS Code. The CLI is bundled with it. - -### "Copilot CLI not logged in" -Run `copilot` in your terminal, then type `/login` to authenticate. - -### "GitHub authentication required" (batch/PR commands) -Install GitHub CLI and authenticate: `brew install gh && gh auth login` +**"Copilot CLI not found"** — Install the GitHub Copilot Chat extension in VS Code. The CLI is bundled with it. -Or set a token: `export GITHUB_TOKEN=` +**"Copilot CLI not logged in"** — Run `copilot` in your terminal, then `/login` to authenticate. -### Generation hangs or times out -- Ensure you're authenticated with the Copilot CLI -- Check your network connection -- Try a smaller repository first +**"GitHub authentication required"** — Install GitHub CLI (`brew install gh && gh auth login`) or set `GITHUB_TOKEN` / `GH_TOKEN`. ## License -MIT +[MIT](LICENSE) diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..0f09971 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,7 @@ +# Security Policy + +## Reporting a Vulnerability + +Please report security issues privately by emailing the maintainer or opening a GitHub security advisory. Avoid filing public issues for vulnerabilities. + +We aim to respond within 72 hours and will work with you on remediation and disclosure. diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..ca739a0 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,54 @@ +import js from "@eslint/js"; +import tseslint from "@typescript-eslint/eslint-plugin"; +import tsParser from "@typescript-eslint/parser"; +import eslintConfigPrettier from "eslint-config-prettier"; +import importPlugin from "eslint-plugin-import"; +import nPlugin from "eslint-plugin-n"; +import promisePlugin from "eslint-plugin-promise"; +import globals from "globals"; + +const sourceGlobs = ["**/*.{ts,tsx,js,jsx}"]; + +export default [ + { + ignores: ["dist/**", "node_modules/**", "coverage/**", "eslint.config.js", "*.config.ts"] + }, + js.configs.recommended, + { + files: sourceGlobs, + languageOptions: { + parser: tsParser, + parserOptions: { + project: "./tsconfig.json", + sourceType: "module", + ecmaVersion: "latest" + }, + globals: { + ...globals.node + } + }, + plugins: { + "@typescript-eslint": tseslint, + import: importPlugin, + n: nPlugin, + promise: promisePlugin + }, + rules: { + "no-undef": "off", + "no-unused-vars": "off", + "@typescript-eslint/no-unused-vars": [ + "warn", + { argsIgnorePattern: "^_", varsIgnorePattern: "^_", destructuredArrayIgnorePattern: "^_" } + ], + "@typescript-eslint/consistent-type-imports": ["warn", { prefer: "type-imports" }], + "import/order": [ + "warn", + { + "newlines-between": "always", + alphabetize: { order: "asc", caseInsensitive: true } + } + ] + } + }, + eslintConfigPrettier +]; diff --git a/eval-results.html b/eval-results.html new file mode 100644 index 0000000..abba370 --- /dev/null +++ b/eval-results.html @@ -0,0 +1,9391 @@ + + + + + + Primer Eval Trajectory + + + +

Primer Eval Trajectory

+
+
+
+
+
+
+ + + diff --git a/eval-results.json b/eval-results.json index 765bd07..6184dac 100644 --- a/eval-results.json +++ b/eval-results.json @@ -2,16 +2,9185 @@ "repoPath": "/Users/pierceboggan/Documents/get-ready", "model": "gpt-4.1", "judgeModel": "gpt-4.1", + "runMetrics": { + "startedAt": "2026-02-04T05:50:01.801Z", + "finishedAt": "2026-02-04T05:50:44.924Z", + "durationMs": 43123 + }, "results": [ { - "id": "overview", - "prompt": "Summarize what this application does and list the main entrypoints.", - "expectation": "Mentions the CLI entrypoint in src/index.ts and that this is the Primer CLI.", - "withInstructions": "This application, Primer, is a CLI tool that analyzes your codebase and generates `.github/copilot-instructions.md` files to help AI coding assistants (like Copilot) better understand your project. It detects languages, frameworks, and package managers, and can generate, preview, and save project-specific instructions using the Copilot SDK. \n\n**Main entrypoints:**\n- `src/index.ts` (primary entry)\n- `src/cli.ts` (CLI setup)\n- CLI commands in `src/commands/` (e.g., `analyze.ts`, `generate.ts`, `instructions.tsx`, `tui.tsx`, `pr.ts`, `eval.ts`, etc.)", - "withoutInstructions": "This application, Primer, is a CLI tool that analyzes your codebase and generates `.github/copilot-instructions.md` files to help AI coding assistants (like Copilot) better understand your project. It detects languages, frameworks, and package managers, and uses the Copilot SDK to generate project-specific instructions. \n\n**Main entrypoints:**\n- `src/index.ts`: Primary entrypoint for the CLI.\n- `src/cli.ts`: Sets up the command-line interface using Commander.\n- `src/commands/`: Contains subcommands for analyze, generate, and instructions.\n- `src/ui/tui.tsx`: Entry for the interactive terminal UI (TUI).", + "id": "case-1", + "prompt": "What is the main purpose of the Primer project and what are its key entrypoints?", + "expectation": "Primer is a TypeScript CLI tool for priming repositories for AI-assisted development and evaluation. The main entrypoint is src/index.ts, which calls runCli in src/cli.ts. Key directories include src/commands/ for CLI subcommands, src/services/ for core logic, and src/ui/ for the Ink/React-based terminal UI.", + "withInstructions": "The main purpose of the Primer project is to prime repositories for AI-assisted development and evaluation by generating Copilot instructions, configuration files, and automating repo analysis and GitHub workflows. Its key entrypoints are:\n- src/index.ts (CLI entrypoint)\n- src/cli.ts (wires CLI commands)\n- src/commands/ (CLI subcommands)\n- src/services/ (core logic)\n- src/ui/tui.tsx (Ink/React TUI)", + "withoutInstructions": "Primer is a TypeScript CLI tool designed to prime repositories for AI-assisted development and evaluation. Its main entrypoint is src/index.ts, which calls runCli in src/cli.ts. Key entrypoints for functionality include src/commands/ for CLI subcommands, src/services/ for core logic, and src/ui/tui.tsx for the Ink/React-based terminal UI.", + "verdict": "pass", + "score": 95, + "rationale": "Response B best matches the expectation. It covers the main purpose, entrypoints, and key directories, and adds relevant details about Copilot instructions and automation, which are consistent with the expectation. Response A is accurate but omits some context about the project's broader purpose and automation features.", + "metrics": { + "withoutInstructions": { + "durationMs": 6365, + "tokenUsage": { + "promptTokens": 9926, + "completionTokens": 77, + "totalTokens": 10003 + }, + "toolCalls": { + "count": 1, + "byName": { + "report_intent": 1 + }, + "totalDurationMs": 0 + } + }, + "withInstructions": { + "durationMs": 5783, + "tokenUsage": { + "promptTokens": 10714, + "completionTokens": 96, + "totalTokens": 10810 + }, + "toolCalls": { + "count": 0, + "byName": {}, + "totalDurationMs": 0 + } + }, + "judge": { + "durationMs": 6595, + "tokenUsage": { + "promptTokens": 10112, + "completionTokens": 81, + "totalTokens": 10193 + }, + "toolCalls": { + "count": 0, + "byName": {}, + "totalDurationMs": 0 + } + }, + "totalDurationMs": 20105 + }, + "trajectory": [ + { + "timestampMs": 1770184203132, + "phase": "withoutInstructions", + "type": "pending_messages.modified", + "data": {} + }, + { + "timestampMs": 1770184203132, + "phase": "withoutInstructions", + "type": "pending_messages.modified", + "data": {} + }, + { + "timestampMs": 1770184205608, + "phase": "withoutInstructions", + "type": "user.message", + "data": { + "content": "You are working in this repository:\n/Users/pierceboggan/Documents/get-ready\nUse the file system tools when needed to inspect the codebase.\n\nWhat is the main purpose of the Primer project and what are its key entrypoints?", + "transformedContent": "2026-02-04T05:50:05.400Z\n\nYou are working in this repository:\n/Users/pierceboggan/Documents/get-ready\nUse the file system tools when needed to inspect the codebase.\n\nWhat is the main purpose of the Primer project and what are its key entrypoints?", + "attachments": [] + } + }, + { + "timestampMs": 1770184205608, + "phase": "withoutInstructions", + "type": "assistant.turn_start", + "data": { + "turnId": "0" + } + }, + { + "timestampMs": 1770184205608, + "phase": "withoutInstructions", + "type": "session.usage_info", + "data": { + "tokenLimit": 64000, + "currentTokens": 10529, + "messagesLength": 2 + } + }, + { + "timestampMs": 1770184207573, + "phase": "withoutInstructions", + "type": "assistant.usage", + "data": { + "model": "gpt-4.1", + "inputTokens": 9896, + "outputTokens": 20, + "cacheReadTokens": 3840, + "cacheWriteTokens": 0, + "cost": 0, + "duration": 1933, + "initiator": "user", + "apiCallId": "chatcmpl-D5Q46RvtJkokzvDWC69j0W4cByW2e", + "providerCallId": "F30B:9765B:B52EE:D44ED:6982DE0D", + "quotaSnapshots": { + "chat": { + "isUnlimitedEntitlement": true, + "entitlementRequests": -1, + "usedRequests": 0, + "usageAllowedWithExhaustedQuota": true, + "overage": 0, + "overageAllowedWithExhaustedQuota": true, + "remainingPercentage": 100, + "resetDate": "2026-03-01T00:00:00.000Z" + }, + "completions": { + "isUnlimitedEntitlement": true, + "entitlementRequests": -1, + "usedRequests": 0, + "usageAllowedWithExhaustedQuota": true, + "overage": 0, + "overageAllowedWithExhaustedQuota": true, + "remainingPercentage": 100, + "resetDate": "2026-03-01T00:00:00.000Z" + }, + "premium_interactions": { + "isUnlimitedEntitlement": true, + "entitlementRequests": -1, + "usedRequests": 0, + "usageAllowedWithExhaustedQuota": true, + "overage": 0, + "overageAllowedWithExhaustedQuota": true, + "remainingPercentage": 100, + "resetDate": "2026-03-01T00:00:00.000Z" + }, + "premium_interactions_tbb": { + "isUnlimitedEntitlement": true, + "entitlementRequests": -1, + "usedRequests": 0, + "usageAllowedWithExhaustedQuota": true, + "overage": 0, + "overageAllowedWithExhaustedQuota": true, + "remainingPercentage": 100, + "resetDate": "2026-03-01T00:00:00.000Z" + } + } + } + }, + { + "timestampMs": 1770184207573, + "phase": "withoutInstructions", + "type": "assistant.message", + "data": { + "messageId": "97200cac-f77f-4c75-9822-c96230c6b40b", + "content": "", + "toolRequests": [ + { + "toolCallId": "call_XZsq8Hyb0NbdqvtKjSxmVUA1", + "name": "report_intent", + "arguments": { + "intent": "Summarizing project purpose" + }, + "type": "function" + } + ] + } + }, + { + "timestampMs": 1770184207573, + "phase": "withoutInstructions", + "type": "tool.execution_start", + "data": { + "toolCallId": "call_XZsq8Hyb0NbdqvtKjSxmVUA1", + "toolName": "report_intent", + "arguments": { + "intent": "Summarizing project purpose" + } + } + }, + { + "timestampMs": 1770184207573, + "phase": "withoutInstructions", + "type": "tool.execution_complete", + "data": { + "toolCallId": "call_XZsq8Hyb0NbdqvtKjSxmVUA1", + "success": true, + "result": { + "content": "Intent logged", + "detailedContent": "Summarizing project purpose" + }, + "toolTelemetry": {} + } + }, + { + "timestampMs": 1770184207573, + "phase": "withoutInstructions", + "type": "assistant.turn_end", + "data": { + "turnId": "0" + } + }, + { + "timestampMs": 1770184207573, + "phase": "withoutInstructions", + "type": "assistant.turn_start", + "data": { + "turnId": "1" + } + }, + { + "timestampMs": 1770184207573, + "phase": "withoutInstructions", + "type": "session.usage_info", + "data": { + "tokenLimit": 64000, + "currentTokens": 10553, + "messagesLength": 4 + } + }, + { + "timestampMs": 1770184208353, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 6, + "deltaPreview": "Primer", + "totalResponseSizeBytes": 6 + } + }, + { + "timestampMs": 1770184208353, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 3, + "deltaPreview": " is", + "totalResponseSizeBytes": 9 + } + }, + { + "timestampMs": 1770184208353, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 2, + "deltaPreview": " a", + "totalResponseSizeBytes": 11 + } + }, + { + "timestampMs": 1770184208353, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 5, + "deltaPreview": " Type", + "totalResponseSizeBytes": 16 + } + }, + { + "timestampMs": 1770184208353, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 6, + "deltaPreview": "Script", + "totalResponseSizeBytes": 22 + } + }, + { + "timestampMs": 1770184208353, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 4, + "deltaPreview": " CLI", + "totalResponseSizeBytes": 26 + } + }, + { + "timestampMs": 1770184208515, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 5, + "deltaPreview": " tool", + "totalResponseSizeBytes": 31 + } + }, + { + "timestampMs": 1770184208515, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 9, + "deltaPreview": " designed", + "totalResponseSizeBytes": 40 + } + }, + { + "timestampMs": 1770184208515, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 3, + "deltaPreview": " to", + "totalResponseSizeBytes": 43 + } + }, + { + "timestampMs": 1770184208515, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 6, + "deltaPreview": " prime", + "totalResponseSizeBytes": 49 + } + }, + { + "timestampMs": 1770184208515, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 13, + "deltaPreview": " repositories", + "totalResponseSizeBytes": 62 + } + }, + { + "timestampMs": 1770184208515, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 4, + "deltaPreview": " for", + "totalResponseSizeBytes": 66 + } + }, + { + "timestampMs": 1770184208515, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 3, + "deltaPreview": " AI", + "totalResponseSizeBytes": 69 + } + }, + { + "timestampMs": 1770184208515, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 9, + "deltaPreview": "-assisted", + "totalResponseSizeBytes": 78 + } + }, + { + "timestampMs": 1770184208515, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 12, + "deltaPreview": " development", + "totalResponseSizeBytes": 90 + } + }, + { + "timestampMs": 1770184208515, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 4, + "deltaPreview": " and", + "totalResponseSizeBytes": 94 + } + }, + { + "timestampMs": 1770184208676, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 11, + "deltaPreview": " evaluation", + "totalResponseSizeBytes": 105 + } + }, + { + "timestampMs": 1770184208676, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 1, + "deltaPreview": ".", + "totalResponseSizeBytes": 106 + } + }, + { + "timestampMs": 1770184208676, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 4, + "deltaPreview": " Its", + "totalResponseSizeBytes": 110 + } + }, + { + "timestampMs": 1770184208676, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 5, + "deltaPreview": " main", + "totalResponseSizeBytes": 115 + } + }, + { + "timestampMs": 1770184208676, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 6, + "deltaPreview": " entry", + "totalResponseSizeBytes": 121 + } + }, + { + "timestampMs": 1770184208676, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 5, + "deltaPreview": "point", + "totalResponseSizeBytes": 126 + } + }, + { + "timestampMs": 1770184208676, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 3, + "deltaPreview": " is", + "totalResponseSizeBytes": 129 + } + }, + { + "timestampMs": 1770184208676, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 4, + "deltaPreview": " src", + "totalResponseSizeBytes": 133 + } + }, + { + "timestampMs": 1770184208676, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 6, + "deltaPreview": "/index", + "totalResponseSizeBytes": 139 + } + }, + { + "timestampMs": 1770184208676, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 3, + "deltaPreview": ".ts", + "totalResponseSizeBytes": 142 + } + }, + { + "timestampMs": 1770184208832, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 1, + "deltaPreview": ",", + "totalResponseSizeBytes": 143 + } + }, + { + "timestampMs": 1770184208833, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 6, + "deltaPreview": " which", + "totalResponseSizeBytes": 149 + } + }, + { + "timestampMs": 1770184208833, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 6, + "deltaPreview": " calls", + "totalResponseSizeBytes": 155 + } + }, + { + "timestampMs": 1770184208833, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 4, + "deltaPreview": " run", + "totalResponseSizeBytes": 159 + } + }, + { + "timestampMs": 1770184208833, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 3, + "deltaPreview": "Cli", + "totalResponseSizeBytes": 162 + } + }, + { + "timestampMs": 1770184208833, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 3, + "deltaPreview": " in", + "totalResponseSizeBytes": 165 + } + }, + { + "timestampMs": 1770184208833, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 4, + "deltaPreview": " src", + "totalResponseSizeBytes": 169 + } + }, + { + "timestampMs": 1770184208833, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 1, + "deltaPreview": "/", + "totalResponseSizeBytes": 170 + } + }, + { + "timestampMs": 1770184208833, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 3, + "deltaPreview": "cli", + "totalResponseSizeBytes": 173 + } + }, + { + "timestampMs": 1770184208833, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 3, + "deltaPreview": ".ts", + "totalResponseSizeBytes": 176 + } + }, + { + "timestampMs": 1770184208993, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 1, + "deltaPreview": ".", + "totalResponseSizeBytes": 177 + } + }, + { + "timestampMs": 1770184208993, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 4, + "deltaPreview": " Key", + "totalResponseSizeBytes": 181 + } + }, + { + "timestampMs": 1770184208993, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 6, + "deltaPreview": " entry", + "totalResponseSizeBytes": 187 + } + }, + { + "timestampMs": 1770184208993, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 6, + "deltaPreview": "points", + "totalResponseSizeBytes": 193 + } + }, + { + "timestampMs": 1770184208993, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 4, + "deltaPreview": " for", + "totalResponseSizeBytes": 197 + } + }, + { + "timestampMs": 1770184208993, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 14, + "deltaPreview": " functionality", + "totalResponseSizeBytes": 211 + } + }, + { + "timestampMs": 1770184208994, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 8, + "deltaPreview": " include", + "totalResponseSizeBytes": 219 + } + }, + { + "timestampMs": 1770184208994, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 4, + "deltaPreview": " src", + "totalResponseSizeBytes": 223 + } + }, + { + "timestampMs": 1770184208994, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 1, + "deltaPreview": "/", + "totalResponseSizeBytes": 224 + } + }, + { + "timestampMs": 1770184208994, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 8, + "deltaPreview": "commands", + "totalResponseSizeBytes": 232 + } + }, + { + "timestampMs": 1770184209157, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 1, + "deltaPreview": "/", + "totalResponseSizeBytes": 233 + } + }, + { + "timestampMs": 1770184209158, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 4, + "deltaPreview": " for", + "totalResponseSizeBytes": 237 + } + }, + { + "timestampMs": 1770184209158, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 4, + "deltaPreview": " CLI", + "totalResponseSizeBytes": 241 + } + }, + { + "timestampMs": 1770184209158, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 4, + "deltaPreview": " sub", + "totalResponseSizeBytes": 245 + } + }, + { + "timestampMs": 1770184209158, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 8, + "deltaPreview": "commands", + "totalResponseSizeBytes": 253 + } + }, + { + "timestampMs": 1770184209158, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 1, + "deltaPreview": ",", + "totalResponseSizeBytes": 254 + } + }, + { + "timestampMs": 1770184209158, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 4, + "deltaPreview": " src", + "totalResponseSizeBytes": 258 + } + }, + { + "timestampMs": 1770184209158, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 9, + "deltaPreview": "/services", + "totalResponseSizeBytes": 267 + } + }, + { + "timestampMs": 1770184209158, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 1, + "deltaPreview": "/", + "totalResponseSizeBytes": 268 + } + }, + { + "timestampMs": 1770184209158, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 4, + "deltaPreview": " for", + "totalResponseSizeBytes": 272 + } + }, + { + "timestampMs": 1770184209317, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 5, + "deltaPreview": " core", + "totalResponseSizeBytes": 277 + } + }, + { + "timestampMs": 1770184209317, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 6, + "deltaPreview": " logic", + "totalResponseSizeBytes": 283 + } + }, + { + "timestampMs": 1770184209317, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 1, + "deltaPreview": ",", + "totalResponseSizeBytes": 284 + } + }, + { + "timestampMs": 1770184209317, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 4, + "deltaPreview": " and", + "totalResponseSizeBytes": 288 + } + }, + { + "timestampMs": 1770184209317, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 4, + "deltaPreview": " src", + "totalResponseSizeBytes": 292 + } + }, + { + "timestampMs": 1770184209317, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 3, + "deltaPreview": "/ui", + "totalResponseSizeBytes": 295 + } + }, + { + "timestampMs": 1770184209317, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 2, + "deltaPreview": "/t", + "totalResponseSizeBytes": 297 + } + }, + { + "timestampMs": 1770184209317, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 2, + "deltaPreview": "ui", + "totalResponseSizeBytes": 299 + } + }, + { + "timestampMs": 1770184209317, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 3, + "deltaPreview": ".ts", + "totalResponseSizeBytes": 302 + } + }, + { + "timestampMs": 1770184209317, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 1, + "deltaPreview": "x", + "totalResponseSizeBytes": 303 + } + }, + { + "timestampMs": 1770184209317, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 4, + "deltaPreview": " for", + "totalResponseSizeBytes": 307 + } + }, + { + "timestampMs": 1770184209492, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 4, + "deltaPreview": " the", + "totalResponseSizeBytes": 311 + } + }, + { + "timestampMs": 1770184209492, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 4, + "deltaPreview": " Ink", + "totalResponseSizeBytes": 315 + } + }, + { + "timestampMs": 1770184209492, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 1, + "deltaPreview": "/", + "totalResponseSizeBytes": 316 + } + }, + { + "timestampMs": 1770184209492, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 5, + "deltaPreview": "React", + "totalResponseSizeBytes": 321 + } + }, + { + "timestampMs": 1770184209492, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 6, + "deltaPreview": "-based", + "totalResponseSizeBytes": 327 + } + }, + { + "timestampMs": 1770184209492, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 9, + "deltaPreview": " terminal", + "totalResponseSizeBytes": 336 + } + }, + { + "timestampMs": 1770184209492, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 3, + "deltaPreview": " UI", + "totalResponseSizeBytes": 339 + } + }, + { + "timestampMs": 1770184209492, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "deltaChars": 1, + "deltaPreview": ".", + "totalResponseSizeBytes": 340 + } + }, + { + "timestampMs": 1770184209492, + "phase": "withoutInstructions", + "type": "assistant.usage", + "data": { + "model": "gpt-4.1", + "inputTokens": 9926, + "outputTokens": 77, + "cacheReadTokens": 9856, + "cacheWriteTokens": 0, + "cost": 0, + "duration": 1920, + "initiator": "agent", + "apiCallId": "chatcmpl-D5Q47ZTu4zM37MD9Esf2vS0HfSAlp", + "providerCallId": "F30B:9765B:B58D8:D4BB6:6982DE0F", + "quotaSnapshots": { + "chat": { + "isUnlimitedEntitlement": true, + "entitlementRequests": -1, + "usedRequests": 0, + "usageAllowedWithExhaustedQuota": true, + "overage": 0, + "overageAllowedWithExhaustedQuota": true, + "remainingPercentage": 100, + "resetDate": "2026-03-01T00:00:00.000Z" + }, + "completions": { + "isUnlimitedEntitlement": true, + "entitlementRequests": -1, + "usedRequests": 0, + "usageAllowedWithExhaustedQuota": true, + "overage": 0, + "overageAllowedWithExhaustedQuota": true, + "remainingPercentage": 100, + "resetDate": "2026-03-01T00:00:00.000Z" + }, + "premium_interactions": { + "isUnlimitedEntitlement": true, + "entitlementRequests": -1, + "usedRequests": 0, + "usageAllowedWithExhaustedQuota": true, + "overage": 0, + "overageAllowedWithExhaustedQuota": true, + "remainingPercentage": 100, + "resetDate": "2026-03-01T00:00:00.000Z" + }, + "premium_interactions_tbb": { + "isUnlimitedEntitlement": true, + "entitlementRequests": -1, + "usedRequests": 0, + "usageAllowedWithExhaustedQuota": true, + "overage": 0, + "overageAllowedWithExhaustedQuota": true, + "remainingPercentage": 100, + "resetDate": "2026-03-01T00:00:00.000Z" + } + } + } + }, + { + "timestampMs": 1770184209492, + "phase": "withoutInstructions", + "type": "assistant.message", + "data": { + "messageId": "28037b5e-a1da-4535-8012-e0d7e568d0ba", + "content": "Primer is a TypeScript CLI tool designed to prime repositories for AI-assisted development and evaluation. Its main entrypoint is src/index.ts, which calls runCli in src/cli.ts. Key entrypoints for functionality include src/commands/ for CLI subcommands, src/services/ for core logic, and src/ui/tui.tsx for the Ink/React-based terminal UI.", + "toolRequests": [] + } + }, + { + "timestampMs": 1770184209492, + "phase": "withoutInstructions", + "type": "assistant.turn_end", + "data": { + "turnId": "1" + } + }, + { + "timestampMs": 1770184209493, + "phase": "withoutInstructions", + "type": "session.idle", + "data": {} + }, + { + "timestampMs": 1770184209515, + "phase": "withInstructions", + "type": "pending_messages.modified", + "data": {} + }, + { + "timestampMs": 1770184209515, + "phase": "withInstructions", + "type": "pending_messages.modified", + "data": {} + }, + { + "timestampMs": 1770184210213, + "phase": "withInstructions", + "type": "user.message", + "data": { + "content": "You are working in this repository:\n/Users/pierceboggan/Documents/get-ready\nUse the file system tools when needed to inspect the codebase.\n\nWhat is the main purpose of the Primer project and what are its key entrypoints?", + "transformedContent": "2026-02-04T05:50:10.175Z\n\nYou are working in this repository:\n/Users/pierceboggan/Documents/get-ready\nUse the file system tools when needed to inspect the codebase.\n\nWhat is the main purpose of the Primer project and what are its key entrypoints?", + "attachments": [] + } + }, + { + "timestampMs": 1770184210213, + "phase": "withInstructions", + "type": "assistant.turn_start", + "data": { + "turnId": "0" + } + }, + { + "timestampMs": 1770184210213, + "phase": "withInstructions", + "type": "session.usage_info", + "data": { + "tokenLimit": 64000, + "currentTokens": 11347, + "messagesLength": 2 + } + }, + { + "timestampMs": 1770184211583, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 3, + "deltaPreview": "The", + "totalResponseSizeBytes": 3 + } + }, + { + "timestampMs": 1770184211583, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 5, + "deltaPreview": " main", + "totalResponseSizeBytes": 8 + } + }, + { + "timestampMs": 1770184211583, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 8, + "deltaPreview": " purpose", + "totalResponseSizeBytes": 16 + } + }, + { + "timestampMs": 1770184211583, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 3, + "deltaPreview": " of", + "totalResponseSizeBytes": 19 + } + }, + { + "timestampMs": 1770184211583, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 4, + "deltaPreview": " the", + "totalResponseSizeBytes": 23 + } + }, + { + "timestampMs": 1770184211583, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 7, + "deltaPreview": " Primer", + "totalResponseSizeBytes": 30 + } + }, + { + "timestampMs": 1770184211583, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 8, + "deltaPreview": " project", + "totalResponseSizeBytes": 38 + } + }, + { + "timestampMs": 1770184211722, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 3, + "deltaPreview": " is", + "totalResponseSizeBytes": 41 + } + }, + { + "timestampMs": 1770184211722, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 3, + "deltaPreview": " to", + "totalResponseSizeBytes": 44 + } + }, + { + "timestampMs": 1770184211725, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 6, + "deltaPreview": " prime", + "totalResponseSizeBytes": 50 + } + }, + { + "timestampMs": 1770184211725, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 13, + "deltaPreview": " repositories", + "totalResponseSizeBytes": 63 + } + }, + { + "timestampMs": 1770184211725, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 4, + "deltaPreview": " for", + "totalResponseSizeBytes": 67 + } + }, + { + "timestampMs": 1770184211725, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 3, + "deltaPreview": " AI", + "totalResponseSizeBytes": 70 + } + }, + { + "timestampMs": 1770184211725, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 9, + "deltaPreview": "-assisted", + "totalResponseSizeBytes": 79 + } + }, + { + "timestampMs": 1770184211725, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 12, + "deltaPreview": " development", + "totalResponseSizeBytes": 91 + } + }, + { + "timestampMs": 1770184211725, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 4, + "deltaPreview": " and", + "totalResponseSizeBytes": 95 + } + }, + { + "timestampMs": 1770184211725, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 11, + "deltaPreview": " evaluation", + "totalResponseSizeBytes": 106 + } + }, + { + "timestampMs": 1770184211885, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 3, + "deltaPreview": " by", + "totalResponseSizeBytes": 109 + } + }, + { + "timestampMs": 1770184211885, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 11, + "deltaPreview": " generating", + "totalResponseSizeBytes": 120 + } + }, + { + "timestampMs": 1770184211885, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 4, + "deltaPreview": " Cop", + "totalResponseSizeBytes": 124 + } + }, + { + "timestampMs": 1770184211885, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 4, + "deltaPreview": "ilot", + "totalResponseSizeBytes": 128 + } + }, + { + "timestampMs": 1770184211885, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 13, + "deltaPreview": " instructions", + "totalResponseSizeBytes": 141 + } + }, + { + "timestampMs": 1770184211885, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 1, + "deltaPreview": ",", + "totalResponseSizeBytes": 142 + } + }, + { + "timestampMs": 1770184211885, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 14, + "deltaPreview": " configuration", + "totalResponseSizeBytes": 156 + } + }, + { + "timestampMs": 1770184211885, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 6, + "deltaPreview": " files", + "totalResponseSizeBytes": 162 + } + }, + { + "timestampMs": 1770184211885, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 1, + "deltaPreview": ",", + "totalResponseSizeBytes": 163 + } + }, + { + "timestampMs": 1770184211885, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 4, + "deltaPreview": " and", + "totalResponseSizeBytes": 167 + } + }, + { + "timestampMs": 1770184212048, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 6, + "deltaPreview": " autom", + "totalResponseSizeBytes": 173 + } + }, + { + "timestampMs": 1770184212048, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 5, + "deltaPreview": "ating", + "totalResponseSizeBytes": 178 + } + }, + { + "timestampMs": 1770184212048, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 5, + "deltaPreview": " repo", + "totalResponseSizeBytes": 183 + } + }, + { + "timestampMs": 1770184212048, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 9, + "deltaPreview": " analysis", + "totalResponseSizeBytes": 192 + } + }, + { + "timestampMs": 1770184212048, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 4, + "deltaPreview": " and", + "totalResponseSizeBytes": 196 + } + }, + { + "timestampMs": 1770184212048, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 4, + "deltaPreview": " Git", + "totalResponseSizeBytes": 200 + } + }, + { + "timestampMs": 1770184212048, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 3, + "deltaPreview": "Hub", + "totalResponseSizeBytes": 203 + } + }, + { + "timestampMs": 1770184212048, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 10, + "deltaPreview": " workflows", + "totalResponseSizeBytes": 213 + } + }, + { + "timestampMs": 1770184212048, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 1, + "deltaPreview": ".", + "totalResponseSizeBytes": 214 + } + }, + { + "timestampMs": 1770184212048, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 4, + "deltaPreview": " Its", + "totalResponseSizeBytes": 218 + } + }, + { + "timestampMs": 1770184212215, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 4, + "deltaPreview": " key", + "totalResponseSizeBytes": 222 + } + }, + { + "timestampMs": 1770184212215, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 6, + "deltaPreview": " entry", + "totalResponseSizeBytes": 228 + } + }, + { + "timestampMs": 1770184212215, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 6, + "deltaPreview": "points", + "totalResponseSizeBytes": 234 + } + }, + { + "timestampMs": 1770184212215, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 4, + "deltaPreview": " are", + "totalResponseSizeBytes": 238 + } + }, + { + "timestampMs": 1770184212215, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 2, + "deltaPreview": ":\n", + "totalResponseSizeBytes": 240 + } + }, + { + "timestampMs": 1770184212215, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 1, + "deltaPreview": "-", + "totalResponseSizeBytes": 241 + } + }, + { + "timestampMs": 1770184212215, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 4, + "deltaPreview": " src", + "totalResponseSizeBytes": 245 + } + }, + { + "timestampMs": 1770184212215, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 6, + "deltaPreview": "/index", + "totalResponseSizeBytes": 251 + } + }, + { + "timestampMs": 1770184212215, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 3, + "deltaPreview": ".ts", + "totalResponseSizeBytes": 254 + } + }, + { + "timestampMs": 1770184212215, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 2, + "deltaPreview": " (", + "totalResponseSizeBytes": 256 + } + }, + { + "timestampMs": 1770184212377, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 3, + "deltaPreview": "CLI", + "totalResponseSizeBytes": 259 + } + }, + { + "timestampMs": 1770184212377, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 6, + "deltaPreview": " entry", + "totalResponseSizeBytes": 265 + } + }, + { + "timestampMs": 1770184212377, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 5, + "deltaPreview": "point", + "totalResponseSizeBytes": 270 + } + }, + { + "timestampMs": 1770184212377, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 2, + "deltaPreview": ")\n", + "totalResponseSizeBytes": 272 + } + }, + { + "timestampMs": 1770184212377, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 1, + "deltaPreview": "-", + "totalResponseSizeBytes": 273 + } + }, + { + "timestampMs": 1770184212377, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 4, + "deltaPreview": " src", + "totalResponseSizeBytes": 277 + } + }, + { + "timestampMs": 1770184212377, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 1, + "deltaPreview": "/", + "totalResponseSizeBytes": 278 + } + }, + { + "timestampMs": 1770184212377, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 3, + "deltaPreview": "cli", + "totalResponseSizeBytes": 281 + } + }, + { + "timestampMs": 1770184212377, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 3, + "deltaPreview": ".ts", + "totalResponseSizeBytes": 284 + } + }, + { + "timestampMs": 1770184212377, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 2, + "deltaPreview": " (", + "totalResponseSizeBytes": 286 + } + }, + { + "timestampMs": 1770184213815, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 1, + "deltaPreview": "w", + "totalResponseSizeBytes": 287 + } + }, + { + "timestampMs": 1770184213815, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 4, + "deltaPreview": "ires", + "totalResponseSizeBytes": 291 + } + }, + { + "timestampMs": 1770184213815, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 4, + "deltaPreview": " CLI", + "totalResponseSizeBytes": 295 + } + }, + { + "timestampMs": 1770184213815, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 9, + "deltaPreview": " commands", + "totalResponseSizeBytes": 304 + } + }, + { + "timestampMs": 1770184213815, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 2, + "deltaPreview": ")\n", + "totalResponseSizeBytes": 306 + } + }, + { + "timestampMs": 1770184213815, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 1, + "deltaPreview": "-", + "totalResponseSizeBytes": 307 + } + }, + { + "timestampMs": 1770184213815, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 4, + "deltaPreview": " src", + "totalResponseSizeBytes": 311 + } + }, + { + "timestampMs": 1770184213815, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 1, + "deltaPreview": "/", + "totalResponseSizeBytes": 312 + } + }, + { + "timestampMs": 1770184213815, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 8, + "deltaPreview": "commands", + "totalResponseSizeBytes": 320 + } + }, + { + "timestampMs": 1770184213815, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 1, + "deltaPreview": "/", + "totalResponseSizeBytes": 321 + } + }, + { + "timestampMs": 1770184213971, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 2, + "deltaPreview": " (", + "totalResponseSizeBytes": 323 + } + }, + { + "timestampMs": 1770184213972, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 3, + "deltaPreview": "CLI", + "totalResponseSizeBytes": 326 + } + }, + { + "timestampMs": 1770184213972, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 4, + "deltaPreview": " sub", + "totalResponseSizeBytes": 330 + } + }, + { + "timestampMs": 1770184213972, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 8, + "deltaPreview": "commands", + "totalResponseSizeBytes": 338 + } + }, + { + "timestampMs": 1770184213972, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 2, + "deltaPreview": ")\n", + "totalResponseSizeBytes": 340 + } + }, + { + "timestampMs": 1770184213972, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 1, + "deltaPreview": "-", + "totalResponseSizeBytes": 341 + } + }, + { + "timestampMs": 1770184213973, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 4, + "deltaPreview": " src", + "totalResponseSizeBytes": 345 + } + }, + { + "timestampMs": 1770184213973, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 9, + "deltaPreview": "/services", + "totalResponseSizeBytes": 354 + } + }, + { + "timestampMs": 1770184213973, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 1, + "deltaPreview": "/", + "totalResponseSizeBytes": 355 + } + }, + { + "timestampMs": 1770184213973, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 2, + "deltaPreview": " (", + "totalResponseSizeBytes": 357 + } + }, + { + "timestampMs": 1770184215118, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 4, + "deltaPreview": "core", + "totalResponseSizeBytes": 361 + } + }, + { + "timestampMs": 1770184215118, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 6, + "deltaPreview": " logic", + "totalResponseSizeBytes": 367 + } + }, + { + "timestampMs": 1770184215118, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 2, + "deltaPreview": ")\n", + "totalResponseSizeBytes": 369 + } + }, + { + "timestampMs": 1770184215118, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 1, + "deltaPreview": "-", + "totalResponseSizeBytes": 370 + } + }, + { + "timestampMs": 1770184215118, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 4, + "deltaPreview": " src", + "totalResponseSizeBytes": 374 + } + }, + { + "timestampMs": 1770184215118, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 3, + "deltaPreview": "/ui", + "totalResponseSizeBytes": 377 + } + }, + { + "timestampMs": 1770184215118, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 2, + "deltaPreview": "/t", + "totalResponseSizeBytes": 379 + } + }, + { + "timestampMs": 1770184215118, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 2, + "deltaPreview": "ui", + "totalResponseSizeBytes": 381 + } + }, + { + "timestampMs": 1770184215118, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 3, + "deltaPreview": ".ts", + "totalResponseSizeBytes": 384 + } + }, + { + "timestampMs": 1770184215118, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 1, + "deltaPreview": "x", + "totalResponseSizeBytes": 385 + } + }, + { + "timestampMs": 1770184215118, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 2, + "deltaPreview": " (", + "totalResponseSizeBytes": 387 + } + }, + { + "timestampMs": 1770184215295, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 3, + "deltaPreview": "Ink", + "totalResponseSizeBytes": 390 + } + }, + { + "timestampMs": 1770184215295, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 1, + "deltaPreview": "/", + "totalResponseSizeBytes": 391 + } + }, + { + "timestampMs": 1770184215295, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 5, + "deltaPreview": "React", + "totalResponseSizeBytes": 396 + } + }, + { + "timestampMs": 1770184215295, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 2, + "deltaPreview": " T", + "totalResponseSizeBytes": 398 + } + }, + { + "timestampMs": 1770184215295, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 2, + "deltaPreview": "UI", + "totalResponseSizeBytes": 400 + } + }, + { + "timestampMs": 1770184215295, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "deltaChars": 1, + "deltaPreview": ")", + "totalResponseSizeBytes": 401 + } + }, + { + "timestampMs": 1770184215295, + "phase": "withInstructions", + "type": "assistant.usage", + "data": { + "model": "gpt-4.1", + "inputTokens": 10714, + "outputTokens": 96, + "cacheReadTokens": 3840, + "cacheWriteTokens": 0, + "cost": 0, + "duration": 5080, + "initiator": "user", + "apiCallId": "chatcmpl-D5Q4AGTscnBzVNO2qtl0nAYsMNFtS", + "providerCallId": "F30B:9765B:B6056:D5480:6982DE12", + "quotaSnapshots": { + "chat": { + "isUnlimitedEntitlement": true, + "entitlementRequests": -1, + "usedRequests": 0, + "usageAllowedWithExhaustedQuota": true, + "overage": 0, + "overageAllowedWithExhaustedQuota": true, + "remainingPercentage": 100, + "resetDate": "2026-03-01T00:00:00.000Z" + }, + "completions": { + "isUnlimitedEntitlement": true, + "entitlementRequests": -1, + "usedRequests": 0, + "usageAllowedWithExhaustedQuota": true, + "overage": 0, + "overageAllowedWithExhaustedQuota": true, + "remainingPercentage": 100, + "resetDate": "2026-03-01T00:00:00.000Z" + }, + "premium_interactions": { + "isUnlimitedEntitlement": true, + "entitlementRequests": -1, + "usedRequests": 0, + "usageAllowedWithExhaustedQuota": true, + "overage": 0, + "overageAllowedWithExhaustedQuota": true, + "remainingPercentage": 100, + "resetDate": "2026-03-01T00:00:00.000Z" + }, + "premium_interactions_tbb": { + "isUnlimitedEntitlement": true, + "entitlementRequests": -1, + "usedRequests": 0, + "usageAllowedWithExhaustedQuota": true, + "overage": 0, + "overageAllowedWithExhaustedQuota": true, + "remainingPercentage": 100, + "resetDate": "2026-03-01T00:00:00.000Z" + } + } + } + }, + { + "timestampMs": 1770184215295, + "phase": "withInstructions", + "type": "assistant.message", + "data": { + "messageId": "87c859d3-de1f-422e-bf2d-c13d9418907c", + "content": "The main purpose of the Primer project is to prime repositories for AI-assisted development and evaluation by generating Copilot instructions, configuration files, and automating repo analysis and GitHub workflows. Its key entrypoints are:\n- src/index.ts (CLI entrypoint)\n- src/cli.ts (wires CLI commands)\n- src/commands/ (CLI subcommands)\n- src/services/ (core logic)\n- src/ui/tui.tsx (Ink/React TUI)", + "toolRequests": [] + } + }, + { + "timestampMs": 1770184215295, + "phase": "withInstructions", + "type": "assistant.turn_end", + "data": { + "turnId": "0" + } + }, + { + "timestampMs": 1770184215295, + "phase": "withInstructions", + "type": "session.idle", + "data": {} + }, + { + "timestampMs": 1770184215319, + "phase": "judge", + "type": "pending_messages.modified", + "data": {} + }, + { + "timestampMs": 1770184215319, + "phase": "judge", + "type": "pending_messages.modified", + "data": {} + }, + { + "timestampMs": 1770184215933, + "phase": "judge", + "type": "user.message", + "data": { + "content": "Evaluate which response best matches the expectation.\n\nExpectation: Primer is a TypeScript CLI tool for priming repositories for AI-assisted development and evaluation. The main entrypoint is src/index.ts, which calls runCli in src/cli.ts. Key directories include src/commands/ for CLI subcommands, src/services/ for core logic, and src/ui/ for the Ink/React-based terminal UI.\n\nResponse A (without custom instructions):\nPrimer is a TypeScript CLI tool designed to prime repositories for AI-assisted development and evaluation. Its main entrypoint is src/index.ts, which calls runCli in src/cli.ts. Key entrypoints for functionality include src/commands/ for CLI subcommands, src/services/ for core logic, and src/ui/tui.tsx for the Ink/React-based terminal UI.\n\nResponse B (with custom instructions):\nThe main purpose of the Primer project is to prime repositories for AI-assisted development and evaluation by generating Copilot instructions, configuration files, and automating repo analysis and GitHub workflows. Its key entrypoints are:\n- src/index.ts (CLI entrypoint)\n- src/cli.ts (wires CLI commands)\n- src/commands/ (CLI subcommands)\n- src/services/ (core logic)\n- src/ui/tui.tsx (Ink/React TUI)\n\nReturn JSON only.", + "transformedContent": "2026-02-04T05:50:15.906Z\n\nEvaluate which response best matches the expectation.\n\nExpectation: Primer is a TypeScript CLI tool for priming repositories for AI-assisted development and evaluation. The main entrypoint is src/index.ts, which calls runCli in src/cli.ts. Key directories include src/commands/ for CLI subcommands, src/services/ for core logic, and src/ui/ for the Ink/React-based terminal UI.\n\nResponse A (without custom instructions):\nPrimer is a TypeScript CLI tool designed to prime repositories for AI-assisted development and evaluation. Its main entrypoint is src/index.ts, which calls runCli in src/cli.ts. Key entrypoints for functionality include src/commands/ for CLI subcommands, src/services/ for core logic, and src/ui/tui.tsx for the Ink/React-based terminal UI.\n\nResponse B (with custom instructions):\nThe main purpose of the Primer project is to prime repositories for AI-assisted development and evaluation by generating Copilot instructions, configuration files, and automating repo analysis and GitHub workflows. Its key entrypoints are:\n- src/index.ts (CLI entrypoint)\n- src/cli.ts (wires CLI commands)\n- src/commands/ (CLI subcommands)\n- src/services/ (core logic)\n- src/ui/tui.tsx (Ink/React TUI)\n\nReturn JSON only.", + "attachments": [] + } + }, + { + "timestampMs": 1770184215933, + "phase": "judge", + "type": "assistant.turn_start", + "data": { + "turnId": "0" + } + }, + { + "timestampMs": 1770184215933, + "phase": "judge", + "type": "session.usage_info", + "data": { + "tokenLimit": 64000, + "currentTokens": 10745, + "messagesLength": 2 + } + }, + { + "timestampMs": 1770184220718, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 2, + "deltaPreview": "{\n", + "totalResponseSizeBytes": 2 + } + }, + { + "timestampMs": 1770184220718, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 1, + "deltaPreview": " ", + "totalResponseSizeBytes": 3 + } + }, + { + "timestampMs": 1770184220719, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 2, + "deltaPreview": " \"", + "totalResponseSizeBytes": 5 + } + }, + { + "timestampMs": 1770184220719, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 3, + "deltaPreview": "ver", + "totalResponseSizeBytes": 8 + } + }, + { + "timestampMs": 1770184220719, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 4, + "deltaPreview": "dict", + "totalResponseSizeBytes": 12 + } + }, + { + "timestampMs": 1770184220719, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 2, + "deltaPreview": "\":", + "totalResponseSizeBytes": 14 + } + }, + { + "timestampMs": 1770184220719, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 2, + "deltaPreview": " \"", + "totalResponseSizeBytes": 16 + } + }, + { + "timestampMs": 1770184220719, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 4, + "deltaPreview": "pass", + "totalResponseSizeBytes": 20 + } + }, + { + "timestampMs": 1770184220905, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 3, + "deltaPreview": "\",\n", + "totalResponseSizeBytes": 23 + } + }, + { + "timestampMs": 1770184220905, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 1, + "deltaPreview": " ", + "totalResponseSizeBytes": 24 + } + }, + { + "timestampMs": 1770184220905, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 2, + "deltaPreview": " \"", + "totalResponseSizeBytes": 26 + } + }, + { + "timestampMs": 1770184220905, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 5, + "deltaPreview": "score", + "totalResponseSizeBytes": 31 + } + }, + { + "timestampMs": 1770184220905, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 2, + "deltaPreview": "\":", + "totalResponseSizeBytes": 33 + } + }, + { + "timestampMs": 1770184220905, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 1, + "deltaPreview": " ", + "totalResponseSizeBytes": 34 + } + }, + { + "timestampMs": 1770184220905, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 2, + "deltaPreview": "95", + "totalResponseSizeBytes": 36 + } + }, + { + "timestampMs": 1770184220906, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 2, + "deltaPreview": ",\n", + "totalResponseSizeBytes": 38 + } + }, + { + "timestampMs": 1770184220906, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 1, + "deltaPreview": " ", + "totalResponseSizeBytes": 39 + } + }, + { + "timestampMs": 1770184220906, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 2, + "deltaPreview": " \"", + "totalResponseSizeBytes": 41 + } + }, + { + "timestampMs": 1770184221080, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 1, + "deltaPreview": "r", + "totalResponseSizeBytes": 42 + } + }, + { + "timestampMs": 1770184221080, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 8, + "deltaPreview": "ationale", + "totalResponseSizeBytes": 50 + } + }, + { + "timestampMs": 1770184221080, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 2, + "deltaPreview": "\":", + "totalResponseSizeBytes": 52 + } + }, + { + "timestampMs": 1770184221080, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 2, + "deltaPreview": " \"", + "totalResponseSizeBytes": 54 + } + }, + { + "timestampMs": 1770184221080, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 8, + "deltaPreview": "Response", + "totalResponseSizeBytes": 62 + } + }, + { + "timestampMs": 1770184221080, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 2, + "deltaPreview": " B", + "totalResponseSizeBytes": 64 + } + }, + { + "timestampMs": 1770184221080, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 5, + "deltaPreview": " best", + "totalResponseSizeBytes": 69 + } + }, + { + "timestampMs": 1770184221080, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 8, + "deltaPreview": " matches", + "totalResponseSizeBytes": 77 + } + }, + { + "timestampMs": 1770184221080, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 4, + "deltaPreview": " the", + "totalResponseSizeBytes": 81 + } + }, + { + "timestampMs": 1770184221081, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 12, + "deltaPreview": " expectation", + "totalResponseSizeBytes": 93 + } + }, + { + "timestampMs": 1770184221239, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 1, + "deltaPreview": ".", + "totalResponseSizeBytes": 94 + } + }, + { + "timestampMs": 1770184221240, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 3, + "deltaPreview": " It", + "totalResponseSizeBytes": 97 + } + }, + { + "timestampMs": 1770184221240, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 7, + "deltaPreview": " covers", + "totalResponseSizeBytes": 104 + } + }, + { + "timestampMs": 1770184221240, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 4, + "deltaPreview": " the", + "totalResponseSizeBytes": 108 + } + }, + { + "timestampMs": 1770184221240, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 5, + "deltaPreview": " main", + "totalResponseSizeBytes": 113 + } + }, + { + "timestampMs": 1770184221240, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 8, + "deltaPreview": " purpose", + "totalResponseSizeBytes": 121 + } + }, + { + "timestampMs": 1770184221240, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 1, + "deltaPreview": ",", + "totalResponseSizeBytes": 122 + } + }, + { + "timestampMs": 1770184221240, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 6, + "deltaPreview": " entry", + "totalResponseSizeBytes": 128 + } + }, + { + "timestampMs": 1770184221240, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 6, + "deltaPreview": "points", + "totalResponseSizeBytes": 134 + } + }, + { + "timestampMs": 1770184221240, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 1, + "deltaPreview": ",", + "totalResponseSizeBytes": 135 + } + }, + { + "timestampMs": 1770184221404, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 4, + "deltaPreview": " and", + "totalResponseSizeBytes": 139 + } + }, + { + "timestampMs": 1770184221404, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 4, + "deltaPreview": " key", + "totalResponseSizeBytes": 143 + } + }, + { + "timestampMs": 1770184221404, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 12, + "deltaPreview": " directories", + "totalResponseSizeBytes": 155 + } + }, + { + "timestampMs": 1770184221404, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 1, + "deltaPreview": ",", + "totalResponseSizeBytes": 156 + } + }, + { + "timestampMs": 1770184221404, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 4, + "deltaPreview": " and", + "totalResponseSizeBytes": 160 + } + }, + { + "timestampMs": 1770184221404, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 5, + "deltaPreview": " adds", + "totalResponseSizeBytes": 165 + } + }, + { + "timestampMs": 1770184221404, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 9, + "deltaPreview": " relevant", + "totalResponseSizeBytes": 174 + } + }, + { + "timestampMs": 1770184221404, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 8, + "deltaPreview": " details", + "totalResponseSizeBytes": 182 + } + }, + { + "timestampMs": 1770184221404, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 6, + "deltaPreview": " about", + "totalResponseSizeBytes": 188 + } + }, + { + "timestampMs": 1770184221404, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 4, + "deltaPreview": " Cop", + "totalResponseSizeBytes": 192 + } + }, + { + "timestampMs": 1770184221567, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 4, + "deltaPreview": "ilot", + "totalResponseSizeBytes": 196 + } + }, + { + "timestampMs": 1770184221567, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 13, + "deltaPreview": " instructions", + "totalResponseSizeBytes": 209 + } + }, + { + "timestampMs": 1770184221567, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 4, + "deltaPreview": " and", + "totalResponseSizeBytes": 213 + } + }, + { + "timestampMs": 1770184221567, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 11, + "deltaPreview": " automation", + "totalResponseSizeBytes": 224 + } + }, + { + "timestampMs": 1770184221567, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 1, + "deltaPreview": ",", + "totalResponseSizeBytes": 225 + } + }, + { + "timestampMs": 1770184221567, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 6, + "deltaPreview": " which", + "totalResponseSizeBytes": 231 + } + }, + { + "timestampMs": 1770184221567, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 4, + "deltaPreview": " are", + "totalResponseSizeBytes": 235 + } + }, + { + "timestampMs": 1770184221567, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 11, + "deltaPreview": " consistent", + "totalResponseSizeBytes": 246 + } + }, + { + "timestampMs": 1770184221567, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 5, + "deltaPreview": " with", + "totalResponseSizeBytes": 251 + } + }, + { + "timestampMs": 1770184221567, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 4, + "deltaPreview": " the", + "totalResponseSizeBytes": 255 + } + }, + { + "timestampMs": 1770184221732, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 12, + "deltaPreview": " expectation", + "totalResponseSizeBytes": 267 + } + }, + { + "timestampMs": 1770184221732, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 1, + "deltaPreview": ".", + "totalResponseSizeBytes": 268 + } + }, + { + "timestampMs": 1770184221732, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 9, + "deltaPreview": " Response", + "totalResponseSizeBytes": 277 + } + }, + { + "timestampMs": 1770184221732, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 2, + "deltaPreview": " A", + "totalResponseSizeBytes": 279 + } + }, + { + "timestampMs": 1770184221732, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 3, + "deltaPreview": " is", + "totalResponseSizeBytes": 282 + } + }, + { + "timestampMs": 1770184221732, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 9, + "deltaPreview": " accurate", + "totalResponseSizeBytes": 291 + } + }, + { + "timestampMs": 1770184221732, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 4, + "deltaPreview": " but", + "totalResponseSizeBytes": 295 + } + }, + { + "timestampMs": 1770184221732, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 3, + "deltaPreview": " om", + "totalResponseSizeBytes": 298 + } + }, + { + "timestampMs": 1770184221732, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 3, + "deltaPreview": "its", + "totalResponseSizeBytes": 301 + } + }, + { + "timestampMs": 1770184221732, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 5, + "deltaPreview": " some", + "totalResponseSizeBytes": 306 + } + }, + { + "timestampMs": 1770184221907, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 8, + "deltaPreview": " context", + "totalResponseSizeBytes": 314 + } + }, + { + "timestampMs": 1770184221907, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 6, + "deltaPreview": " about", + "totalResponseSizeBytes": 320 + } + }, + { + "timestampMs": 1770184221907, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 4, + "deltaPreview": " the", + "totalResponseSizeBytes": 324 + } + }, + { + "timestampMs": 1770184221911, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 10, + "deltaPreview": " project's", + "totalResponseSizeBytes": 334 + } + }, + { + "timestampMs": 1770184221911, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 8, + "deltaPreview": " broader", + "totalResponseSizeBytes": 342 + } + }, + { + "timestampMs": 1770184221911, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 8, + "deltaPreview": " purpose", + "totalResponseSizeBytes": 350 + } + }, + { + "timestampMs": 1770184221911, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 4, + "deltaPreview": " and", + "totalResponseSizeBytes": 354 + } + }, + { + "timestampMs": 1770184221911, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 11, + "deltaPreview": " automation", + "totalResponseSizeBytes": 365 + } + }, + { + "timestampMs": 1770184221911, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 9, + "deltaPreview": " features", + "totalResponseSizeBytes": 374 + } + }, + { + "timestampMs": 1770184221911, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 3, + "deltaPreview": ".\"\n", + "totalResponseSizeBytes": 377 + } + }, + { + "timestampMs": 1770184221911, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "deltaChars": 1, + "deltaPreview": "}", + "totalResponseSizeBytes": 378 + } + }, + { + "timestampMs": 1770184221911, + "phase": "judge", + "type": "assistant.usage", + "data": { + "model": "gpt-4.1", + "inputTokens": 10112, + "outputTokens": 81, + "cacheReadTokens": 9088, + "cacheWriteTokens": 0, + "cost": 0, + "duration": 5977, + "initiator": "user", + "apiCallId": "chatcmpl-D5Q4GXqFijypr8Y4ffC964TeLlilJ", + "providerCallId": "F30B:9765B:B70BA:D67D1:6982DE17", + "quotaSnapshots": { + "chat": { + "isUnlimitedEntitlement": true, + "entitlementRequests": -1, + "usedRequests": 0, + "usageAllowedWithExhaustedQuota": true, + "overage": 0, + "overageAllowedWithExhaustedQuota": true, + "remainingPercentage": 100, + "resetDate": "2026-03-01T00:00:00.000Z" + }, + "completions": { + "isUnlimitedEntitlement": true, + "entitlementRequests": -1, + "usedRequests": 0, + "usageAllowedWithExhaustedQuota": true, + "overage": 0, + "overageAllowedWithExhaustedQuota": true, + "remainingPercentage": 100, + "resetDate": "2026-03-01T00:00:00.000Z" + }, + "premium_interactions": { + "isUnlimitedEntitlement": true, + "entitlementRequests": -1, + "usedRequests": 0, + "usageAllowedWithExhaustedQuota": true, + "overage": 0, + "overageAllowedWithExhaustedQuota": true, + "remainingPercentage": 100, + "resetDate": "2026-03-01T00:00:00.000Z" + }, + "premium_interactions_tbb": { + "isUnlimitedEntitlement": true, + "entitlementRequests": -1, + "usedRequests": 0, + "usageAllowedWithExhaustedQuota": true, + "overage": 0, + "overageAllowedWithExhaustedQuota": true, + "remainingPercentage": 100, + "resetDate": "2026-03-01T00:00:00.000Z" + } + } + } + }, + { + "timestampMs": 1770184221911, + "phase": "judge", + "type": "assistant.message", + "data": { + "messageId": "67ad997b-7610-4fba-a0d0-b3d4c0bb1d75", + "content": "{\n \"verdict\": \"pass\",\n \"score\": 95,\n \"rationale\": \"Response B best matches the expectation. It covers the main purpose, entrypoints, and key directories, and adds relevant details about Copilot instructions and automation, which are consistent with the expectation. Response A is accurate but omits some context about the project's broader purpose and automation features.\"\n}", + "toolRequests": [] + } + }, + { + "timestampMs": 1770184221911, + "phase": "judge", + "type": "assistant.turn_end", + "data": { + "turnId": "0" + } + }, + { + "timestampMs": 1770184221911, + "phase": "judge", + "type": "session.idle", + "data": {} + } + ] + }, + { + "id": "case-2", + "prompt": "How do I build, lint, and test the Primer project locally?", + "expectation": "Install dependencies with npm install. Build with npm run build, lint with npm run lint, and test with npm run test. Type checking is done with npx tsc -p tsconfig.json --noEmit, and coverage with npm run test:coverage.", + "withInstructions": "To build, lint, and test Primer locally, use the existing npm scripts:\n\n- Build: `npm run build`\n- Lint: `npm run lint`\n- Test: `npm run test`\n\nRun these commands from the project root. No extra setup is required beyond `npm install`.", + "withoutInstructions": "To build: run `npm run build` (uses TypeScript compiler). \nTo lint: run `npm run lint` (uses ESLint). \nTo test: run `npm run test` (uses Vitest).", + "verdict": "fail", + "score": 60, + "rationale": "Both responses mention build, lint, and test commands, but neither includes the type checking (npx tsc -p tsconfig.json --noEmit) or coverage (npm run test:coverage) steps as required by the expectation. Response B is slightly more complete and user-friendly, but both omit critical commands, so neither fully matches the expectation.", + "metrics": { + "withoutInstructions": { + "durationMs": 7417, + "tokenUsage": { + "promptTokens": 10278, + "completionTokens": 89, + "totalTokens": 10367 + }, + "toolCalls": { + "count": 4, + "byName": { + "report_intent": 1, + "grep": 3 + }, + "totalDurationMs": 0 + } + }, + "withInstructions": { + "durationMs": 2482, + "tokenUsage": { + "promptTokens": 10711, + "completionTokens": 61, + "totalTokens": 10772 + }, + "toolCalls": { + "count": 0, + "byName": {}, + "totalDurationMs": 0 + } + }, + "judge": { + "durationMs": 4235, + "tokenUsage": { + "promptTokens": 10030, + "completionTokens": 91, + "totalTokens": 10121 + }, + "toolCalls": { + "count": 0, + "byName": {}, + "totalDurationMs": 0 + } + }, + "totalDurationMs": 14196 + }, + "trajectory": [ + { + "timestampMs": 1770184221936, + "phase": "withoutInstructions", + "type": "pending_messages.modified", + "data": {} + }, + { + "timestampMs": 1770184221936, + "phase": "withoutInstructions", + "type": "pending_messages.modified", + "data": {} + }, + { + "timestampMs": 1770184222585, + "phase": "withoutInstructions", + "type": "user.message", + "data": { + "content": "You are working in this repository:\n/Users/pierceboggan/Documents/get-ready\nUse the file system tools when needed to inspect the codebase.\n\nHow do I build, lint, and test the Primer project locally?", + "transformedContent": "2026-02-04T05:50:22.562Z\n\nYou are working in this repository:\n/Users/pierceboggan/Documents/get-ready\nUse the file system tools when needed to inspect the codebase.\n\nHow do I build, lint, and test the Primer project locally?", + "attachments": [] + } + }, + { + "timestampMs": 1770184222586, + "phase": "withoutInstructions", + "type": "assistant.turn_start", + "data": { + "turnId": "0" + } + }, + { + "timestampMs": 1770184222586, + "phase": "withoutInstructions", + "type": "session.usage_info", + "data": { + "tokenLimit": 64000, + "currentTokens": 10526, + "messagesLength": 2 + } + }, + { + "timestampMs": 1770184225975, + "phase": "withoutInstructions", + "type": "assistant.usage", + "data": { + "model": "gpt-4.1", + "inputTokens": 9893, + "outputTokens": 21, + "cacheReadTokens": 9088, + "cacheWriteTokens": 0, + "cost": 0, + "duration": 3359, + "initiator": "user", + "apiCallId": "chatcmpl-D5Q4N09vcn3gbKBho7qwtxANJHqc0", + "providerCallId": "F30B:9765B:B84CF:D7F2A:6982DE1E", + "quotaSnapshots": { + "chat": { + "isUnlimitedEntitlement": true, + "entitlementRequests": -1, + "usedRequests": 0, + "usageAllowedWithExhaustedQuota": true, + "overage": 0, + "overageAllowedWithExhaustedQuota": true, + "remainingPercentage": 100, + "resetDate": "2026-03-01T00:00:00.000Z" + }, + "completions": { + "isUnlimitedEntitlement": true, + "entitlementRequests": -1, + "usedRequests": 0, + "usageAllowedWithExhaustedQuota": true, + "overage": 0, + "overageAllowedWithExhaustedQuota": true, + "remainingPercentage": 100, + "resetDate": "2026-03-01T00:00:00.000Z" + }, + "premium_interactions": { + "isUnlimitedEntitlement": true, + "entitlementRequests": -1, + "usedRequests": 0, + "usageAllowedWithExhaustedQuota": true, + "overage": 0, + "overageAllowedWithExhaustedQuota": true, + "remainingPercentage": 100, + "resetDate": "2026-03-01T00:00:00.000Z" + }, + "premium_interactions_tbb": { + "isUnlimitedEntitlement": true, + "entitlementRequests": -1, + "usedRequests": 0, + "usageAllowedWithExhaustedQuota": true, + "overage": 0, + "overageAllowedWithExhaustedQuota": true, + "remainingPercentage": 100, + "resetDate": "2026-03-01T00:00:00.000Z" + } + } + } + }, + { + "timestampMs": 1770184225975, + "phase": "withoutInstructions", + "type": "assistant.message", + "data": { + "messageId": "97025c63-f0bf-4dec-a001-2f12bae4d105", + "content": "", + "toolRequests": [ + { + "toolCallId": "call_NXt0bcYpDuHYzQAaqi4ZUlfP", + "name": "report_intent", + "arguments": { + "intent": "Checking build/lint/test commands" + }, + "type": "function" + } + ] + } + }, + { + "timestampMs": 1770184225975, + "phase": "withoutInstructions", + "type": "tool.execution_start", + "data": { + "toolCallId": "call_NXt0bcYpDuHYzQAaqi4ZUlfP", + "toolName": "report_intent", + "arguments": { + "intent": "Checking build/lint/test commands" + } + } + }, + { + "timestampMs": 1770184225975, + "phase": "withoutInstructions", + "type": "tool.execution_complete", + "data": { + "toolCallId": "call_NXt0bcYpDuHYzQAaqi4ZUlfP", + "success": true, + "result": { + "content": "Intent logged", + "detailedContent": "Checking build/lint/test commands" + }, + "toolTelemetry": {} + } + }, + { + "timestampMs": 1770184225975, + "phase": "withoutInstructions", + "type": "assistant.turn_end", + "data": { + "turnId": "0" + } + }, + { + "timestampMs": 1770184225975, + "phase": "withoutInstructions", + "type": "assistant.turn_start", + "data": { + "turnId": "1" + } + }, + { + "timestampMs": 1770184225975, + "phase": "withoutInstructions", + "type": "session.usage_info", + "data": { + "tokenLimit": 64000, + "currentTokens": 10551, + "messagesLength": 4 + } + }, + { + "timestampMs": 1770184227727, + "phase": "withoutInstructions", + "type": "assistant.usage", + "data": { + "model": "gpt-4.1", + "inputTokens": 9924, + "outputTokens": 89, + "cacheReadTokens": 9856, + "cacheWriteTokens": 0, + "cost": 0, + "duration": 1748, + "initiator": "agent", + "apiCallId": "chatcmpl-D5Q4Q7dnVGgsp6nN4Ex9dxa7Vt1Vi", + "providerCallId": "F30B:9765B:B8F87:D8B58:6982DE21", + "quotaSnapshots": { + "chat": { + "isUnlimitedEntitlement": true, + "entitlementRequests": -1, + "usedRequests": 0, + "usageAllowedWithExhaustedQuota": true, + "overage": 0, + "overageAllowedWithExhaustedQuota": true, + "remainingPercentage": 100, + "resetDate": "2026-03-01T00:00:00.000Z" + }, + "completions": { + "isUnlimitedEntitlement": true, + "entitlementRequests": -1, + "usedRequests": 0, + "usageAllowedWithExhaustedQuota": true, + "overage": 0, + "overageAllowedWithExhaustedQuota": true, + "remainingPercentage": 100, + "resetDate": "2026-03-01T00:00:00.000Z" + }, + "premium_interactions": { + "isUnlimitedEntitlement": true, + "entitlementRequests": -1, + "usedRequests": 0, + "usageAllowedWithExhaustedQuota": true, + "overage": 0, + "overageAllowedWithExhaustedQuota": true, + "remainingPercentage": 100, + "resetDate": "2026-03-01T00:00:00.000Z" + }, + "premium_interactions_tbb": { + "isUnlimitedEntitlement": true, + "entitlementRequests": -1, + "usedRequests": 0, + "usageAllowedWithExhaustedQuota": true, + "overage": 0, + "overageAllowedWithExhaustedQuota": true, + "remainingPercentage": 100, + "resetDate": "2026-03-01T00:00:00.000Z" + } + } + } + }, + { + "timestampMs": 1770184227727, + "phase": "withoutInstructions", + "type": "assistant.message", + "data": { + "messageId": "bb4d3841-caad-432b-bbb6-40c4a9574430", + "content": "", + "toolRequests": [ + { + "toolCallId": "call_s32rB59ZZTfeolhzdWBuw3Ih", + "name": "grep", + "arguments": { + "pattern": "build", + "glob": "package.json", + "output_mode": "content" + }, + "type": "function" + }, + { + "toolCallId": "call_TAHAIp3ggdCEuAXCXwxhcAfB", + "name": "grep", + "arguments": { + "pattern": "lint", + "glob": "package.json", + "output_mode": "content" + }, + "type": "function" + }, + { + "toolCallId": "call_9ycksvzAJGSXJ3T7NQy1OcyO", + "name": "grep", + "arguments": { + "pattern": "test", + "glob": "package.json", + "output_mode": "content" + }, + "type": "function" + } + ] + } + }, + { + "timestampMs": 1770184227727, + "phase": "withoutInstructions", + "type": "tool.execution_start", + "data": { + "toolCallId": "call_s32rB59ZZTfeolhzdWBuw3Ih", + "toolName": "grep", + "arguments": { + "pattern": "build", + "glob": "package.json", + "output_mode": "content" + } + } + }, + { + "timestampMs": 1770184227728, + "phase": "withoutInstructions", + "type": "tool.execution_start", + "data": { + "toolCallId": "call_TAHAIp3ggdCEuAXCXwxhcAfB", + "toolName": "grep", + "arguments": { + "pattern": "lint", + "glob": "package.json", + "output_mode": "content" + } + } + }, + { + "timestampMs": 1770184227728, + "phase": "withoutInstructions", + "type": "tool.execution_start", + "data": { + "toolCallId": "call_9ycksvzAJGSXJ3T7NQy1OcyO", + "toolName": "grep", + "arguments": { + "pattern": "test", + "glob": "package.json", + "output_mode": "content" + } + } + }, + { + "timestampMs": 1770184227764, + "phase": "withoutInstructions", + "type": "tool.execution_complete", + "data": { + "toolCallId": "call_s32rB59ZZTfeolhzdWBuw3Ih", + "success": true, + "result": { + "content": "./package.json: \"build\": \"tsc && npm link\",", + "detailedContent": "./package.json: \"build\": \"tsc && npm link\"," + }, + "toolTelemetry": { + "properties": { + "pattern": "build", + "output_mode": "content", + "matches_found": "true" + }, + "metrics": { + "result_length": 46 + } + } + } + }, + { + "timestampMs": 1770184227764, + "phase": "withoutInstructions", + "type": "tool.execution_complete", + "data": { + "toolCallId": "call_TAHAIp3ggdCEuAXCXwxhcAfB", + "success": true, + "result": { + "content": "./package.json: \"lint\": \"eslint .\",\n./package.json: \"@typescript-eslint/eslint-plugin\": \"^8.5.0\",\n./package.json: \"@typescript-eslint/parser\": \"^8.5.0\",\n./package.json: \"eslint\": \"^9.7.0\",\n./package.json: \"eslint-config-prettier\": \"^9.1.0\",\n./package.json: \"eslint-plugin-import\": \"^2.30.0\",\n./package.json: \"eslint-plugin-n\": \"^17.10.2\",\n./package.json: \"eslint-plugin-promise\": \"^7.1.0\",", + "detailedContent": "./package.json: \"lint\": \"eslint .\",\n./package.json: \"@typescript-eslint/eslint-plugin\": \"^8.5.0\",\n./package.json: \"@typescript-eslint/parser\": \"^8.5.0\",\n./package.json: \"eslint\": \"^9.7.0\",\n./package.json: \"eslint-config-prettier\": \"^9.1.0\",\n./package.json: \"eslint-plugin-import\": \"^2.30.0\",\n./package.json: \"eslint-plugin-n\": \"^17.10.2\",\n./package.json: \"eslint-plugin-promise\": \"^7.1.0\"," + }, + "toolTelemetry": { + "properties": { + "pattern": "lint", + "output_mode": "content", + "matches_found": "true" + }, + "metrics": { + "result_length": 413 + } + } + } + }, + { + "timestampMs": 1770184227797, + "phase": "withoutInstructions", + "type": "tool.execution_complete", + "data": { + "toolCallId": "call_9ycksvzAJGSXJ3T7NQy1OcyO", + "success": true, + "result": { + "content": "./package.json: \"test\": \"vitest run\",\n./package.json: \"test:watch\": \"vitest\",\n./package.json: \"test:coverage\": \"vitest run --coverage\"\n./package.json: \"@vitest/coverage-v8\": \"^2.1.4\",\n./package.json: \"vitest\": \"^2.1.4\"", + "detailedContent": "./package.json: \"test\": \"vitest run\",\n./package.json: \"test:watch\": \"vitest\",\n./package.json: \"test:coverage\": \"vitest run --coverage\"\n./package.json: \"@vitest/coverage-v8\": \"^2.1.4\",\n./package.json: \"vitest\": \"^2.1.4\"" + }, + "toolTelemetry": { + "properties": { + "pattern": "test", + "output_mode": "content", + "matches_found": "true" + }, + "metrics": { + "result_length": 233 + } + } + } + }, + { + "timestampMs": 1770184227798, + "phase": "withoutInstructions", + "type": "assistant.turn_end", + "data": { + "turnId": "1" + } + }, + { + "timestampMs": 1770184227798, + "phase": "withoutInstructions", + "type": "assistant.turn_start", + "data": { + "turnId": "2" + } + }, + { + "timestampMs": 1770184227798, + "phase": "withoutInstructions", + "type": "session.usage_info", + "data": { + "tokenLimit": 64000, + "currentTokens": 10865, + "messagesLength": 8 + } + }, + { + "timestampMs": 1770184228685, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "effb8ff2-b181-47d5-9d4a-baa863978dde", + "deltaChars": 2, + "deltaPreview": "To", + "totalResponseSizeBytes": 2 + } + }, + { + "timestampMs": 1770184228685, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "effb8ff2-b181-47d5-9d4a-baa863978dde", + "deltaChars": 6, + "deltaPreview": " build", + "totalResponseSizeBytes": 8 + } + }, + { + "timestampMs": 1770184228685, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "effb8ff2-b181-47d5-9d4a-baa863978dde", + "deltaChars": 1, + "deltaPreview": ":", + "totalResponseSizeBytes": 9 + } + }, + { + "timestampMs": 1770184228685, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "effb8ff2-b181-47d5-9d4a-baa863978dde", + "deltaChars": 4, + "deltaPreview": " run", + "totalResponseSizeBytes": 13 + } + }, + { + "timestampMs": 1770184228685, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "effb8ff2-b181-47d5-9d4a-baa863978dde", + "deltaChars": 2, + "deltaPreview": " `", + "totalResponseSizeBytes": 15 + } + }, + { + "timestampMs": 1770184228685, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "effb8ff2-b181-47d5-9d4a-baa863978dde", + "deltaChars": 3, + "deltaPreview": "npm", + "totalResponseSizeBytes": 18 + } + }, + { + "timestampMs": 1770184228685, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "effb8ff2-b181-47d5-9d4a-baa863978dde", + "deltaChars": 4, + "deltaPreview": " run", + "totalResponseSizeBytes": 22 + } + }, + { + "timestampMs": 1770184228848, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "effb8ff2-b181-47d5-9d4a-baa863978dde", + "deltaChars": 6, + "deltaPreview": " build", + "totalResponseSizeBytes": 28 + } + }, + { + "timestampMs": 1770184228848, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "effb8ff2-b181-47d5-9d4a-baa863978dde", + "deltaChars": 1, + "deltaPreview": "`", + "totalResponseSizeBytes": 29 + } + }, + { + "timestampMs": 1770184228848, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "effb8ff2-b181-47d5-9d4a-baa863978dde", + "deltaChars": 2, + "deltaPreview": " (", + "totalResponseSizeBytes": 31 + } + }, + { + "timestampMs": 1770184228848, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "effb8ff2-b181-47d5-9d4a-baa863978dde", + "deltaChars": 4, + "deltaPreview": "uses", + "totalResponseSizeBytes": 35 + } + }, + { + "timestampMs": 1770184228848, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "effb8ff2-b181-47d5-9d4a-baa863978dde", + "deltaChars": 5, + "deltaPreview": " Type", + "totalResponseSizeBytes": 40 + } + }, + { + "timestampMs": 1770184228848, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "effb8ff2-b181-47d5-9d4a-baa863978dde", + "deltaChars": 6, + "deltaPreview": "Script", + "totalResponseSizeBytes": 46 + } + }, + { + "timestampMs": 1770184228848, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "effb8ff2-b181-47d5-9d4a-baa863978dde", + "deltaChars": 9, + "deltaPreview": " compiler", + "totalResponseSizeBytes": 55 + } + }, + { + "timestampMs": 1770184228848, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "effb8ff2-b181-47d5-9d4a-baa863978dde", + "deltaChars": 2, + "deltaPreview": ").", + "totalResponseSizeBytes": 57 + } + }, + { + "timestampMs": 1770184228848, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "effb8ff2-b181-47d5-9d4a-baa863978dde", + "deltaChars": 3, + "deltaPreview": " \n", + "totalResponseSizeBytes": 60 + } + }, + { + "timestampMs": 1770184228848, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "effb8ff2-b181-47d5-9d4a-baa863978dde", + "deltaChars": 2, + "deltaPreview": "To", + "totalResponseSizeBytes": 62 + } + }, + { + "timestampMs": 1770184229008, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "effb8ff2-b181-47d5-9d4a-baa863978dde", + "deltaChars": 5, + "deltaPreview": " lint", + "totalResponseSizeBytes": 67 + } + }, + { + "timestampMs": 1770184229008, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "effb8ff2-b181-47d5-9d4a-baa863978dde", + "deltaChars": 1, + "deltaPreview": ":", + "totalResponseSizeBytes": 68 + } + }, + { + "timestampMs": 1770184229008, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "effb8ff2-b181-47d5-9d4a-baa863978dde", + "deltaChars": 4, + "deltaPreview": " run", + "totalResponseSizeBytes": 72 + } + }, + { + "timestampMs": 1770184229008, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "effb8ff2-b181-47d5-9d4a-baa863978dde", + "deltaChars": 2, + "deltaPreview": " `", + "totalResponseSizeBytes": 74 + } + }, + { + "timestampMs": 1770184229008, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "effb8ff2-b181-47d5-9d4a-baa863978dde", + "deltaChars": 3, + "deltaPreview": "npm", + "totalResponseSizeBytes": 77 + } + }, + { + "timestampMs": 1770184229008, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "effb8ff2-b181-47d5-9d4a-baa863978dde", + "deltaChars": 4, + "deltaPreview": " run", + "totalResponseSizeBytes": 81 + } + }, + { + "timestampMs": 1770184229009, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "effb8ff2-b181-47d5-9d4a-baa863978dde", + "deltaChars": 5, + "deltaPreview": " lint", + "totalResponseSizeBytes": 86 + } + }, + { + "timestampMs": 1770184229009, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "effb8ff2-b181-47d5-9d4a-baa863978dde", + "deltaChars": 1, + "deltaPreview": "`", + "totalResponseSizeBytes": 87 + } + }, + { + "timestampMs": 1770184229009, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "effb8ff2-b181-47d5-9d4a-baa863978dde", + "deltaChars": 2, + "deltaPreview": " (", + "totalResponseSizeBytes": 89 + } + }, + { + "timestampMs": 1770184229009, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "effb8ff2-b181-47d5-9d4a-baa863978dde", + "deltaChars": 4, + "deltaPreview": "uses", + "totalResponseSizeBytes": 93 + } + }, + { + "timestampMs": 1770184229172, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "effb8ff2-b181-47d5-9d4a-baa863978dde", + "deltaChars": 4, + "deltaPreview": " ESL", + "totalResponseSizeBytes": 97 + } + }, + { + "timestampMs": 1770184229173, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "effb8ff2-b181-47d5-9d4a-baa863978dde", + "deltaChars": 3, + "deltaPreview": "int", + "totalResponseSizeBytes": 100 + } + }, + { + "timestampMs": 1770184229173, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "effb8ff2-b181-47d5-9d4a-baa863978dde", + "deltaChars": 2, + "deltaPreview": ").", + "totalResponseSizeBytes": 102 + } + }, + { + "timestampMs": 1770184229173, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "effb8ff2-b181-47d5-9d4a-baa863978dde", + "deltaChars": 3, + "deltaPreview": " \n", + "totalResponseSizeBytes": 105 + } + }, + { + "timestampMs": 1770184229173, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "effb8ff2-b181-47d5-9d4a-baa863978dde", + "deltaChars": 2, + "deltaPreview": "To", + "totalResponseSizeBytes": 107 + } + }, + { + "timestampMs": 1770184229173, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "effb8ff2-b181-47d5-9d4a-baa863978dde", + "deltaChars": 5, + "deltaPreview": " test", + "totalResponseSizeBytes": 112 + } + }, + { + "timestampMs": 1770184229173, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "effb8ff2-b181-47d5-9d4a-baa863978dde", + "deltaChars": 1, + "deltaPreview": ":", + "totalResponseSizeBytes": 113 + } + }, + { + "timestampMs": 1770184229173, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "effb8ff2-b181-47d5-9d4a-baa863978dde", + "deltaChars": 4, + "deltaPreview": " run", + "totalResponseSizeBytes": 117 + } + }, + { + "timestampMs": 1770184229173, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "effb8ff2-b181-47d5-9d4a-baa863978dde", + "deltaChars": 2, + "deltaPreview": " `", + "totalResponseSizeBytes": 119 + } + }, + { + "timestampMs": 1770184229173, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "effb8ff2-b181-47d5-9d4a-baa863978dde", + "deltaChars": 3, + "deltaPreview": "npm", + "totalResponseSizeBytes": 122 + } + }, + { + "timestampMs": 1770184229173, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "effb8ff2-b181-47d5-9d4a-baa863978dde", + "deltaChars": 4, + "deltaPreview": " run", + "totalResponseSizeBytes": 126 + } + }, + { + "timestampMs": 1770184229349, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "effb8ff2-b181-47d5-9d4a-baa863978dde", + "deltaChars": 5, + "deltaPreview": " test", + "totalResponseSizeBytes": 131 + } + }, + { + "timestampMs": 1770184229350, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "effb8ff2-b181-47d5-9d4a-baa863978dde", + "deltaChars": 1, + "deltaPreview": "`", + "totalResponseSizeBytes": 132 + } + }, + { + "timestampMs": 1770184229350, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "effb8ff2-b181-47d5-9d4a-baa863978dde", + "deltaChars": 2, + "deltaPreview": " (", + "totalResponseSizeBytes": 134 + } + }, + { + "timestampMs": 1770184229350, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "effb8ff2-b181-47d5-9d4a-baa863978dde", + "deltaChars": 4, + "deltaPreview": "uses", + "totalResponseSizeBytes": 138 + } + }, + { + "timestampMs": 1770184229350, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "effb8ff2-b181-47d5-9d4a-baa863978dde", + "deltaChars": 4, + "deltaPreview": " Vit", + "totalResponseSizeBytes": 142 + } + }, + { + "timestampMs": 1770184229351, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "effb8ff2-b181-47d5-9d4a-baa863978dde", + "deltaChars": 3, + "deltaPreview": "est", + "totalResponseSizeBytes": 145 + } + }, + { + "timestampMs": 1770184229351, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "effb8ff2-b181-47d5-9d4a-baa863978dde", + "deltaChars": 2, + "deltaPreview": ").", + "totalResponseSizeBytes": 147 + } + }, + { + "timestampMs": 1770184229351, + "phase": "withoutInstructions", + "type": "assistant.usage", + "data": { + "model": "gpt-4.1", + "inputTokens": 10278, + "outputTokens": 47, + "cacheReadTokens": 9984, + "cacheWriteTokens": 0, + "cost": 0, + "duration": 1554, + "initiator": "agent", + "apiCallId": "chatcmpl-D5Q4SkfmNac7XJsi0v0yGSXX1b9sY", + "providerCallId": "F30B:9765B:B94D7:D917F:6982DE23", + "quotaSnapshots": { + "chat": { + "isUnlimitedEntitlement": true, + "entitlementRequests": -1, + "usedRequests": 0, + "usageAllowedWithExhaustedQuota": true, + "overage": 0, + "overageAllowedWithExhaustedQuota": true, + "remainingPercentage": 100, + "resetDate": "2026-03-01T00:00:00.000Z" + }, + "completions": { + "isUnlimitedEntitlement": true, + "entitlementRequests": -1, + "usedRequests": 0, + "usageAllowedWithExhaustedQuota": true, + "overage": 0, + "overageAllowedWithExhaustedQuota": true, + "remainingPercentage": 100, + "resetDate": "2026-03-01T00:00:00.000Z" + }, + "premium_interactions": { + "isUnlimitedEntitlement": true, + "entitlementRequests": -1, + "usedRequests": 0, + "usageAllowedWithExhaustedQuota": true, + "overage": 0, + "overageAllowedWithExhaustedQuota": true, + "remainingPercentage": 100, + "resetDate": "2026-03-01T00:00:00.000Z" + }, + "premium_interactions_tbb": { + "isUnlimitedEntitlement": true, + "entitlementRequests": -1, + "usedRequests": 0, + "usageAllowedWithExhaustedQuota": true, + "overage": 0, + "overageAllowedWithExhaustedQuota": true, + "remainingPercentage": 100, + "resetDate": "2026-03-01T00:00:00.000Z" + } + } + } + }, + { + "timestampMs": 1770184229351, + "phase": "withoutInstructions", + "type": "assistant.message", + "data": { + "messageId": "effb8ff2-b181-47d5-9d4a-baa863978dde", + "content": "To build: run `npm run build` (uses TypeScript compiler). \nTo lint: run `npm run lint` (uses ESLint). \nTo test: run `npm run test` (uses Vitest).", + "toolRequests": [] + } + }, + { + "timestampMs": 1770184229351, + "phase": "withoutInstructions", + "type": "assistant.turn_end", + "data": { + "turnId": "2" + } + }, + { + "timestampMs": 1770184229351, + "phase": "withoutInstructions", + "type": "session.idle", + "data": {} + }, + { + "timestampMs": 1770184229380, + "phase": "withInstructions", + "type": "pending_messages.modified", + "data": {} + }, + { + "timestampMs": 1770184229380, + "phase": "withInstructions", + "type": "pending_messages.modified", + "data": {} + }, + { + "timestampMs": 1770184230003, + "phase": "withInstructions", + "type": "user.message", + "data": { + "content": "You are working in this repository:\n/Users/pierceboggan/Documents/get-ready\nUse the file system tools when needed to inspect the codebase.\n\nHow do I build, lint, and test the Primer project locally?", + "transformedContent": "2026-02-04T05:50:29.962Z\n\nYou are working in this repository:\n/Users/pierceboggan/Documents/get-ready\nUse the file system tools when needed to inspect the codebase.\n\nHow do I build, lint, and test the Primer project locally?", + "attachments": [] + } + }, + { + "timestampMs": 1770184230003, + "phase": "withInstructions", + "type": "assistant.turn_start", + "data": { + "turnId": "0" + } + }, + { + "timestampMs": 1770184230003, + "phase": "withInstructions", + "type": "session.usage_info", + "data": { + "tokenLimit": 64000, + "currentTokens": 11344, + "messagesLength": 2 + } + }, + { + "timestampMs": 1770184231019, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 2, + "deltaPreview": "To", + "totalResponseSizeBytes": 2 + } + }, + { + "timestampMs": 1770184231019, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 6, + "deltaPreview": " build", + "totalResponseSizeBytes": 8 + } + }, + { + "timestampMs": 1770184231019, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 1, + "deltaPreview": ",", + "totalResponseSizeBytes": 9 + } + }, + { + "timestampMs": 1770184231019, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 5, + "deltaPreview": " lint", + "totalResponseSizeBytes": 14 + } + }, + { + "timestampMs": 1770184231019, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 1, + "deltaPreview": ",", + "totalResponseSizeBytes": 15 + } + }, + { + "timestampMs": 1770184231019, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 4, + "deltaPreview": " and", + "totalResponseSizeBytes": 19 + } + }, + { + "timestampMs": 1770184231019, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 5, + "deltaPreview": " test", + "totalResponseSizeBytes": 24 + } + }, + { + "timestampMs": 1770184231195, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 7, + "deltaPreview": " Primer", + "totalResponseSizeBytes": 31 + } + }, + { + "timestampMs": 1770184231195, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 8, + "deltaPreview": " locally", + "totalResponseSizeBytes": 39 + } + }, + { + "timestampMs": 1770184231195, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 1, + "deltaPreview": ",", + "totalResponseSizeBytes": 40 + } + }, + { + "timestampMs": 1770184231195, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 4, + "deltaPreview": " use", + "totalResponseSizeBytes": 44 + } + }, + { + "timestampMs": 1770184231195, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 4, + "deltaPreview": " the", + "totalResponseSizeBytes": 48 + } + }, + { + "timestampMs": 1770184231196, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 9, + "deltaPreview": " existing", + "totalResponseSizeBytes": 57 + } + }, + { + "timestampMs": 1770184231196, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 4, + "deltaPreview": " npm", + "totalResponseSizeBytes": 61 + } + }, + { + "timestampMs": 1770184231196, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 8, + "deltaPreview": " scripts", + "totalResponseSizeBytes": 69 + } + }, + { + "timestampMs": 1770184231196, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 3, + "deltaPreview": ":\n\n", + "totalResponseSizeBytes": 72 + } + }, + { + "timestampMs": 1770184231196, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 1, + "deltaPreview": "-", + "totalResponseSizeBytes": 73 + } + }, + { + "timestampMs": 1770184231359, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 6, + "deltaPreview": " Build", + "totalResponseSizeBytes": 79 + } + }, + { + "timestampMs": 1770184231359, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 1, + "deltaPreview": ":", + "totalResponseSizeBytes": 80 + } + }, + { + "timestampMs": 1770184231359, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 2, + "deltaPreview": " `", + "totalResponseSizeBytes": 82 + } + }, + { + "timestampMs": 1770184231359, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 3, + "deltaPreview": "npm", + "totalResponseSizeBytes": 85 + } + }, + { + "timestampMs": 1770184231359, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 4, + "deltaPreview": " run", + "totalResponseSizeBytes": 89 + } + }, + { + "timestampMs": 1770184231360, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 6, + "deltaPreview": " build", + "totalResponseSizeBytes": 95 + } + }, + { + "timestampMs": 1770184231360, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 2, + "deltaPreview": "`\n", + "totalResponseSizeBytes": 97 + } + }, + { + "timestampMs": 1770184231360, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 1, + "deltaPreview": "-", + "totalResponseSizeBytes": 98 + } + }, + { + "timestampMs": 1770184231360, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 2, + "deltaPreview": " L", + "totalResponseSizeBytes": 100 + } + }, + { + "timestampMs": 1770184231360, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 3, + "deltaPreview": "int", + "totalResponseSizeBytes": 103 + } + }, + { + "timestampMs": 1770184231520, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 1, + "deltaPreview": ":", + "totalResponseSizeBytes": 104 + } + }, + { + "timestampMs": 1770184231521, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 2, + "deltaPreview": " `", + "totalResponseSizeBytes": 106 + } + }, + { + "timestampMs": 1770184231521, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 3, + "deltaPreview": "npm", + "totalResponseSizeBytes": 109 + } + }, + { + "timestampMs": 1770184231523, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 4, + "deltaPreview": " run", + "totalResponseSizeBytes": 113 + } + }, + { + "timestampMs": 1770184231523, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 5, + "deltaPreview": " lint", + "totalResponseSizeBytes": 118 + } + }, + { + "timestampMs": 1770184231523, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 2, + "deltaPreview": "`\n", + "totalResponseSizeBytes": 120 + } + }, + { + "timestampMs": 1770184231523, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 1, + "deltaPreview": "-", + "totalResponseSizeBytes": 121 + } + }, + { + "timestampMs": 1770184231523, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 5, + "deltaPreview": " Test", + "totalResponseSizeBytes": 126 + } + }, + { + "timestampMs": 1770184231523, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 1, + "deltaPreview": ":", + "totalResponseSizeBytes": 127 + } + }, + { + "timestampMs": 1770184231523, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 2, + "deltaPreview": " `", + "totalResponseSizeBytes": 129 + } + }, + { + "timestampMs": 1770184231684, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 3, + "deltaPreview": "npm", + "totalResponseSizeBytes": 132 + } + }, + { + "timestampMs": 1770184231685, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 4, + "deltaPreview": " run", + "totalResponseSizeBytes": 136 + } + }, + { + "timestampMs": 1770184231685, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 5, + "deltaPreview": " test", + "totalResponseSizeBytes": 141 + } + }, + { + "timestampMs": 1770184231685, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 3, + "deltaPreview": "`\n\n", + "totalResponseSizeBytes": 144 + } + }, + { + "timestampMs": 1770184231685, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 3, + "deltaPreview": "Run", + "totalResponseSizeBytes": 147 + } + }, + { + "timestampMs": 1770184231685, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 6, + "deltaPreview": " these", + "totalResponseSizeBytes": 153 + } + }, + { + "timestampMs": 1770184231685, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 9, + "deltaPreview": " commands", + "totalResponseSizeBytes": 162 + } + }, + { + "timestampMs": 1770184231685, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 5, + "deltaPreview": " from", + "totalResponseSizeBytes": 167 + } + }, + { + "timestampMs": 1770184231685, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 4, + "deltaPreview": " the", + "totalResponseSizeBytes": 171 + } + }, + { + "timestampMs": 1770184231685, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 8, + "deltaPreview": " project", + "totalResponseSizeBytes": 179 + } + }, + { + "timestampMs": 1770184231849, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 5, + "deltaPreview": " root", + "totalResponseSizeBytes": 184 + } + }, + { + "timestampMs": 1770184231849, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 1, + "deltaPreview": ".", + "totalResponseSizeBytes": 185 + } + }, + { + "timestampMs": 1770184231849, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 3, + "deltaPreview": " No", + "totalResponseSizeBytes": 188 + } + }, + { + "timestampMs": 1770184231849, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 6, + "deltaPreview": " extra", + "totalResponseSizeBytes": 194 + } + }, + { + "timestampMs": 1770184231849, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 6, + "deltaPreview": " setup", + "totalResponseSizeBytes": 200 + } + }, + { + "timestampMs": 1770184231849, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 3, + "deltaPreview": " is", + "totalResponseSizeBytes": 203 + } + }, + { + "timestampMs": 1770184231849, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 9, + "deltaPreview": " required", + "totalResponseSizeBytes": 212 + } + }, + { + "timestampMs": 1770184231849, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 7, + "deltaPreview": " beyond", + "totalResponseSizeBytes": 219 + } + }, + { + "timestampMs": 1770184231849, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 2, + "deltaPreview": " `", + "totalResponseSizeBytes": 221 + } + }, + { + "timestampMs": 1770184231849, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 3, + "deltaPreview": "npm", + "totalResponseSizeBytes": 224 + } + }, + { + "timestampMs": 1770184231859, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 8, + "deltaPreview": " install", + "totalResponseSizeBytes": 232 + } + }, + { + "timestampMs": 1770184231859, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "deltaChars": 2, + "deltaPreview": "`.", + "totalResponseSizeBytes": 234 + } + }, + { + "timestampMs": 1770184231859, + "phase": "withInstructions", + "type": "assistant.usage", + "data": { + "model": "gpt-4.1", + "inputTokens": 10711, + "outputTokens": 61, + "cacheReadTokens": 9984, + "cacheWriteTokens": 0, + "cost": 0, + "duration": 1857, + "initiator": "user", + "apiCallId": "chatcmpl-D5Q4UxYS11dY2UXRqoNoEr1Zq7mSO", + "providerCallId": "F30B:9765B:B9BB4:D996B:6982DE25", + "quotaSnapshots": { + "chat": { + "isUnlimitedEntitlement": true, + "entitlementRequests": -1, + "usedRequests": 0, + "usageAllowedWithExhaustedQuota": true, + "overage": 0, + "overageAllowedWithExhaustedQuota": true, + "remainingPercentage": 100, + "resetDate": "2026-03-01T00:00:00.000Z" + }, + "completions": { + "isUnlimitedEntitlement": true, + "entitlementRequests": -1, + "usedRequests": 0, + "usageAllowedWithExhaustedQuota": true, + "overage": 0, + "overageAllowedWithExhaustedQuota": true, + "remainingPercentage": 100, + "resetDate": "2026-03-01T00:00:00.000Z" + }, + "premium_interactions": { + "isUnlimitedEntitlement": true, + "entitlementRequests": -1, + "usedRequests": 0, + "usageAllowedWithExhaustedQuota": true, + "overage": 0, + "overageAllowedWithExhaustedQuota": true, + "remainingPercentage": 100, + "resetDate": "2026-03-01T00:00:00.000Z" + }, + "premium_interactions_tbb": { + "isUnlimitedEntitlement": true, + "entitlementRequests": -1, + "usedRequests": 0, + "usageAllowedWithExhaustedQuota": true, + "overage": 0, + "overageAllowedWithExhaustedQuota": true, + "remainingPercentage": 100, + "resetDate": "2026-03-01T00:00:00.000Z" + } + } + } + }, + { + "timestampMs": 1770184231859, + "phase": "withInstructions", + "type": "assistant.message", + "data": { + "messageId": "ddfaa17f-838c-49e9-af15-eb0937e347cb", + "content": "To build, lint, and test Primer locally, use the existing npm scripts:\n\n- Build: `npm run build`\n- Lint: `npm run lint`\n- Test: `npm run test`\n\nRun these commands from the project root. No extra setup is required beyond `npm install`.", + "toolRequests": [] + } + }, + { + "timestampMs": 1770184231859, + "phase": "withInstructions", + "type": "assistant.turn_end", + "data": { + "turnId": "0" + } + }, + { + "timestampMs": 1770184231859, + "phase": "withInstructions", + "type": "session.idle", + "data": {} + }, + { + "timestampMs": 1770184231875, + "phase": "judge", + "type": "pending_messages.modified", + "data": {} + }, + { + "timestampMs": 1770184231875, + "phase": "judge", + "type": "pending_messages.modified", + "data": {} + }, + { + "timestampMs": 1770184232485, + "phase": "judge", + "type": "user.message", + "data": { + "content": "Evaluate which response best matches the expectation.\n\nExpectation: Install dependencies with npm install. Build with npm run build, lint with npm run lint, and test with npm run test. Type checking is done with npx tsc -p tsconfig.json --noEmit, and coverage with npm run test:coverage.\n\nResponse A (without custom instructions):\nTo build: run `npm run build` (uses TypeScript compiler). \nTo lint: run `npm run lint` (uses ESLint). \nTo test: run `npm run test` (uses Vitest).\n\nResponse B (with custom instructions):\nTo build, lint, and test Primer locally, use the existing npm scripts:\n\n- Build: `npm run build`\n- Lint: `npm run lint`\n- Test: `npm run test`\n\nRun these commands from the project root. No extra setup is required beyond `npm install`.\n\nReturn JSON only.", + "transformedContent": "2026-02-04T05:50:32.447Z\n\nEvaluate which response best matches the expectation.\n\nExpectation: Install dependencies with npm install. Build with npm run build, lint with npm run lint, and test with npm run test. Type checking is done with npx tsc -p tsconfig.json --noEmit, and coverage with npm run test:coverage.\n\nResponse A (without custom instructions):\nTo build: run `npm run build` (uses TypeScript compiler). \nTo lint: run `npm run lint` (uses ESLint). \nTo test: run `npm run test` (uses Vitest).\n\nResponse B (with custom instructions):\nTo build, lint, and test Primer locally, use the existing npm scripts:\n\n- Build: `npm run build`\n- Lint: `npm run lint`\n- Test: `npm run test`\n\nRun these commands from the project root. No extra setup is required beyond `npm install`.\n\nReturn JSON only.", + "attachments": [] + } + }, + { + "timestampMs": 1770184232485, + "phase": "judge", + "type": "assistant.turn_start", + "data": { + "turnId": "0" + } + }, + { + "timestampMs": 1770184232485, + "phase": "judge", + "type": "session.usage_info", + "data": { + "tokenLimit": 64000, + "currentTokens": 10663, + "messagesLength": 2 + } + }, + { + "timestampMs": 1770184233270, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 2, + "deltaPreview": "{\"", + "totalResponseSizeBytes": 2 + } + }, + { + "timestampMs": 1770184233270, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 3, + "deltaPreview": "ver", + "totalResponseSizeBytes": 5 + } + }, + { + "timestampMs": 1770184233270, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 4, + "deltaPreview": "dict", + "totalResponseSizeBytes": 9 + } + }, + { + "timestampMs": 1770184233270, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 2, + "deltaPreview": "\":", + "totalResponseSizeBytes": 11 + } + }, + { + "timestampMs": 1770184233270, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 2, + "deltaPreview": " \"", + "totalResponseSizeBytes": 13 + } + }, + { + "timestampMs": 1770184233270, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 4, + "deltaPreview": "fail", + "totalResponseSizeBytes": 17 + } + }, + { + "timestampMs": 1770184233270, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 2, + "deltaPreview": "\",", + "totalResponseSizeBytes": 19 + } + }, + { + "timestampMs": 1770184233444, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 2, + "deltaPreview": " \"", + "totalResponseSizeBytes": 21 + } + }, + { + "timestampMs": 1770184233444, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 5, + "deltaPreview": "score", + "totalResponseSizeBytes": 26 + } + }, + { + "timestampMs": 1770184233444, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 2, + "deltaPreview": "\":", + "totalResponseSizeBytes": 28 + } + }, + { + "timestampMs": 1770184233444, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 1, + "deltaPreview": " ", + "totalResponseSizeBytes": 29 + } + }, + { + "timestampMs": 1770184233444, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 2, + "deltaPreview": "60", + "totalResponseSizeBytes": 31 + } + }, + { + "timestampMs": 1770184233444, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 1, + "deltaPreview": ",", + "totalResponseSizeBytes": 32 + } + }, + { + "timestampMs": 1770184233444, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 2, + "deltaPreview": " \"", + "totalResponseSizeBytes": 34 + } + }, + { + "timestampMs": 1770184233444, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 1, + "deltaPreview": "r", + "totalResponseSizeBytes": 35 + } + }, + { + "timestampMs": 1770184233444, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 8, + "deltaPreview": "ationale", + "totalResponseSizeBytes": 43 + } + }, + { + "timestampMs": 1770184233444, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 2, + "deltaPreview": "\":", + "totalResponseSizeBytes": 45 + } + }, + { + "timestampMs": 1770184233621, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 2, + "deltaPreview": " \"", + "totalResponseSizeBytes": 47 + } + }, + { + "timestampMs": 1770184233621, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 4, + "deltaPreview": "Both", + "totalResponseSizeBytes": 51 + } + }, + { + "timestampMs": 1770184233621, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 10, + "deltaPreview": " responses", + "totalResponseSizeBytes": 61 + } + }, + { + "timestampMs": 1770184233621, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 8, + "deltaPreview": " mention", + "totalResponseSizeBytes": 69 + } + }, + { + "timestampMs": 1770184233621, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 6, + "deltaPreview": " build", + "totalResponseSizeBytes": 75 + } + }, + { + "timestampMs": 1770184233622, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 1, + "deltaPreview": ",", + "totalResponseSizeBytes": 76 + } + }, + { + "timestampMs": 1770184233622, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 5, + "deltaPreview": " lint", + "totalResponseSizeBytes": 81 + } + }, + { + "timestampMs": 1770184233622, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 1, + "deltaPreview": ",", + "totalResponseSizeBytes": 82 + } + }, + { + "timestampMs": 1770184233622, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 4, + "deltaPreview": " and", + "totalResponseSizeBytes": 86 + } + }, + { + "timestampMs": 1770184233622, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 5, + "deltaPreview": " test", + "totalResponseSizeBytes": 91 + } + }, + { + "timestampMs": 1770184233767, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 9, + "deltaPreview": " commands", + "totalResponseSizeBytes": 100 + } + }, + { + "timestampMs": 1770184233767, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 1, + "deltaPreview": ",", + "totalResponseSizeBytes": 101 + } + }, + { + "timestampMs": 1770184233770, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 4, + "deltaPreview": " but", + "totalResponseSizeBytes": 105 + } + }, + { + "timestampMs": 1770184233770, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 8, + "deltaPreview": " neither", + "totalResponseSizeBytes": 113 + } + }, + { + "timestampMs": 1770184233770, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 9, + "deltaPreview": " includes", + "totalResponseSizeBytes": 122 + } + }, + { + "timestampMs": 1770184233770, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 4, + "deltaPreview": " the", + "totalResponseSizeBytes": 126 + } + }, + { + "timestampMs": 1770184233770, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 5, + "deltaPreview": " type", + "totalResponseSizeBytes": 131 + } + }, + { + "timestampMs": 1770184233770, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 9, + "deltaPreview": " checking", + "totalResponseSizeBytes": 140 + } + }, + { + "timestampMs": 1770184233770, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 2, + "deltaPreview": " (", + "totalResponseSizeBytes": 142 + } + }, + { + "timestampMs": 1770184233770, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 1, + "deltaPreview": "n", + "totalResponseSizeBytes": 143 + } + }, + { + "timestampMs": 1770184234467, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 2, + "deltaPreview": "px", + "totalResponseSizeBytes": 145 + } + }, + { + "timestampMs": 1770184234467, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 2, + "deltaPreview": " t", + "totalResponseSizeBytes": 147 + } + }, + { + "timestampMs": 1770184234467, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 2, + "deltaPreview": "sc", + "totalResponseSizeBytes": 149 + } + }, + { + "timestampMs": 1770184234467, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 2, + "deltaPreview": " -", + "totalResponseSizeBytes": 151 + } + }, + { + "timestampMs": 1770184234467, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 1, + "deltaPreview": "p", + "totalResponseSizeBytes": 152 + } + }, + { + "timestampMs": 1770184234467, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 3, + "deltaPreview": " ts", + "totalResponseSizeBytes": 155 + } + }, + { + "timestampMs": 1770184234467, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 6, + "deltaPreview": "config", + "totalResponseSizeBytes": 161 + } + }, + { + "timestampMs": 1770184234467, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 5, + "deltaPreview": ".json", + "totalResponseSizeBytes": 166 + } + }, + { + "timestampMs": 1770184234467, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 3, + "deltaPreview": " --", + "totalResponseSizeBytes": 169 + } + }, + { + "timestampMs": 1770184234467, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 2, + "deltaPreview": "no", + "totalResponseSizeBytes": 171 + } + }, + { + "timestampMs": 1770184234872, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 4, + "deltaPreview": "Emit", + "totalResponseSizeBytes": 175 + } + }, + { + "timestampMs": 1770184234873, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 1, + "deltaPreview": ")", + "totalResponseSizeBytes": 176 + } + }, + { + "timestampMs": 1770184234873, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 3, + "deltaPreview": " or", + "totalResponseSizeBytes": 179 + } + }, + { + "timestampMs": 1770184234873, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 9, + "deltaPreview": " coverage", + "totalResponseSizeBytes": 188 + } + }, + { + "timestampMs": 1770184234873, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 2, + "deltaPreview": " (", + "totalResponseSizeBytes": 190 + } + }, + { + "timestampMs": 1770184234873, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 3, + "deltaPreview": "npm", + "totalResponseSizeBytes": 193 + } + }, + { + "timestampMs": 1770184234873, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 4, + "deltaPreview": " run", + "totalResponseSizeBytes": 197 + } + }, + { + "timestampMs": 1770184234873, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 5, + "deltaPreview": " test", + "totalResponseSizeBytes": 202 + } + }, + { + "timestampMs": 1770184234873, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 1, + "deltaPreview": ":", + "totalResponseSizeBytes": 203 + } + }, + { + "timestampMs": 1770184234873, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 8, + "deltaPreview": "coverage", + "totalResponseSizeBytes": 211 + } + }, + { + "timestampMs": 1770184235037, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 1, + "deltaPreview": ")", + "totalResponseSizeBytes": 212 + } + }, + { + "timestampMs": 1770184235037, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 6, + "deltaPreview": " steps", + "totalResponseSizeBytes": 218 + } + }, + { + "timestampMs": 1770184235039, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 3, + "deltaPreview": " as", + "totalResponseSizeBytes": 221 + } + }, + { + "timestampMs": 1770184235039, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 9, + "deltaPreview": " required", + "totalResponseSizeBytes": 230 + } + }, + { + "timestampMs": 1770184235039, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 3, + "deltaPreview": " by", + "totalResponseSizeBytes": 233 + } + }, + { + "timestampMs": 1770184235039, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 4, + "deltaPreview": " the", + "totalResponseSizeBytes": 237 + } + }, + { + "timestampMs": 1770184235039, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 12, + "deltaPreview": " expectation", + "totalResponseSizeBytes": 249 + } + }, + { + "timestampMs": 1770184235039, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 1, + "deltaPreview": ".", + "totalResponseSizeBytes": 250 + } + }, + { + "timestampMs": 1770184235039, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 9, + "deltaPreview": " Response", + "totalResponseSizeBytes": 259 + } + }, + { + "timestampMs": 1770184235039, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 2, + "deltaPreview": " B", + "totalResponseSizeBytes": 261 + } + }, + { + "timestampMs": 1770184235604, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 3, + "deltaPreview": " is", + "totalResponseSizeBytes": 264 + } + }, + { + "timestampMs": 1770184235604, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 9, + "deltaPreview": " slightly", + "totalResponseSizeBytes": 273 + } + }, + { + "timestampMs": 1770184235604, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 5, + "deltaPreview": " more", + "totalResponseSizeBytes": 278 + } + }, + { + "timestampMs": 1770184235604, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 9, + "deltaPreview": " complete", + "totalResponseSizeBytes": 287 + } + }, + { + "timestampMs": 1770184235604, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 4, + "deltaPreview": " and", + "totalResponseSizeBytes": 291 + } + }, + { + "timestampMs": 1770184235605, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 5, + "deltaPreview": " user", + "totalResponseSizeBytes": 296 + } + }, + { + "timestampMs": 1770184235605, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 9, + "deltaPreview": "-friendly", + "totalResponseSizeBytes": 305 + } + }, + { + "timestampMs": 1770184235605, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 1, + "deltaPreview": ",", + "totalResponseSizeBytes": 306 + } + }, + { + "timestampMs": 1770184235605, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 4, + "deltaPreview": " but", + "totalResponseSizeBytes": 310 + } + }, + { + "timestampMs": 1770184235605, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 5, + "deltaPreview": " both", + "totalResponseSizeBytes": 315 + } + }, + { + "timestampMs": 1770184236087, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 5, + "deltaPreview": " omit", + "totalResponseSizeBytes": 320 + } + }, + { + "timestampMs": 1770184236087, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 9, + "deltaPreview": " critical", + "totalResponseSizeBytes": 329 + } + }, + { + "timestampMs": 1770184236087, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 9, + "deltaPreview": " commands", + "totalResponseSizeBytes": 338 + } + }, + { + "timestampMs": 1770184236087, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 1, + "deltaPreview": ",", + "totalResponseSizeBytes": 339 + } + }, + { + "timestampMs": 1770184236087, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 3, + "deltaPreview": " so", + "totalResponseSizeBytes": 342 + } + }, + { + "timestampMs": 1770184236087, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 8, + "deltaPreview": " neither", + "totalResponseSizeBytes": 350 + } + }, + { + "timestampMs": 1770184236087, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 6, + "deltaPreview": " fully", + "totalResponseSizeBytes": 356 + } + }, + { + "timestampMs": 1770184236087, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 8, + "deltaPreview": " matches", + "totalResponseSizeBytes": 364 + } + }, + { + "timestampMs": 1770184236087, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 4, + "deltaPreview": " the", + "totalResponseSizeBytes": 368 + } + }, + { + "timestampMs": 1770184236087, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 12, + "deltaPreview": " expectation", + "totalResponseSizeBytes": 380 + } + }, + { + "timestampMs": 1770184236107, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 2, + "deltaPreview": ".\"", + "totalResponseSizeBytes": 382 + } + }, + { + "timestampMs": 1770184236107, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "deltaChars": 1, + "deltaPreview": "}", + "totalResponseSizeBytes": 383 + } + }, + { + "timestampMs": 1770184236107, + "phase": "judge", + "type": "assistant.usage", + "data": { + "model": "gpt-4.1", + "inputTokens": 10030, + "outputTokens": 91, + "cacheReadTokens": 9088, + "cacheWriteTokens": 0, + "cost": 0, + "duration": 3621, + "initiator": "user", + "apiCallId": "chatcmpl-D5Q4WqKm3J4Ph8bL8tx4KsQly09KT", + "providerCallId": "F30B:9765B:BA390:DA261:6982DE28", + "quotaSnapshots": { + "chat": { + "isUnlimitedEntitlement": true, + "entitlementRequests": -1, + "usedRequests": 0, + "usageAllowedWithExhaustedQuota": true, + "overage": 0, + "overageAllowedWithExhaustedQuota": true, + "remainingPercentage": 100, + "resetDate": "2026-03-01T00:00:00.000Z" + }, + "completions": { + "isUnlimitedEntitlement": true, + "entitlementRequests": -1, + "usedRequests": 0, + "usageAllowedWithExhaustedQuota": true, + "overage": 0, + "overageAllowedWithExhaustedQuota": true, + "remainingPercentage": 100, + "resetDate": "2026-03-01T00:00:00.000Z" + }, + "premium_interactions": { + "isUnlimitedEntitlement": true, + "entitlementRequests": -1, + "usedRequests": 0, + "usageAllowedWithExhaustedQuota": true, + "overage": 0, + "overageAllowedWithExhaustedQuota": true, + "remainingPercentage": 100, + "resetDate": "2026-03-01T00:00:00.000Z" + }, + "premium_interactions_tbb": { + "isUnlimitedEntitlement": true, + "entitlementRequests": -1, + "usedRequests": 0, + "usageAllowedWithExhaustedQuota": true, + "overage": 0, + "overageAllowedWithExhaustedQuota": true, + "remainingPercentage": 100, + "resetDate": "2026-03-01T00:00:00.000Z" + } + } + } + }, + { + "timestampMs": 1770184236107, + "phase": "judge", + "type": "assistant.message", + "data": { + "messageId": "18e636e5-7f9b-4820-9c9b-2770a45fbb4a", + "content": "{\"verdict\": \"fail\", \"score\": 60, \"rationale\": \"Both responses mention build, lint, and test commands, but neither includes the type checking (npx tsc -p tsconfig.json --noEmit) or coverage (npm run test:coverage) steps as required by the expectation. Response B is slightly more complete and user-friendly, but both omit critical commands, so neither fully matches the expectation.\"}", + "toolRequests": [] + } + }, + { + "timestampMs": 1770184236107, + "phase": "judge", + "type": "assistant.turn_end", + "data": { + "turnId": "0" + } + }, + { + "timestampMs": 1770184236107, + "phase": "judge", + "type": "session.idle", + "data": {} + } + ] + }, + { + "id": "case-3", + "prompt": "Where should new CLI commands and core logic be placed, and what conventions should be followed for configuration files?", + "expectation": "New CLI commands should be placed in src/commands/, and core logic in src/services/. Use ESM syntax everywhere, strict TypeScript targeting ES2022 and module ESNext, and only overwrite config files like .vscode/settings.json and .vscode/mcp.json with --force. All Copilot/VS Code settings reference .github/copilot-instructions.md and enable MCP.", + "withInstructions": "New CLI commands should be placed in src/commands/, and core logic should go in src/services/. For configuration files like .vscode/settings.json and .vscode/mcp.json, only overwrite them with --force, and ensure all Copilot/VS Code settings reference the main copilot-instructions file and enable MCP. Use ESM syntax everywhere, strict TypeScript, and follow safe file write conventions.", + "withoutInstructions": "New CLI commands should be placed in src/commands/, and core logic should go in src/services/. For configuration files, only overwrite them (e.g., .vscode/settings.json, .vscode/mcp.json) with --force, and all Copilot/VS Code settings should reference the main instructions file and enable MCP. Use ESM syntax everywhere, strict TypeScript, and follow safe file write conventions.", "verdict": "pass", "score": 100, - "rationale": "Both responses mention the foCLI entrypoint in src/index.ts and that this is the Primer CLI. Response B is slightly more complete, but both meet the expectation." + "rationale": "Both responses accurately reflect the expectation, but Response B is slightly more precise by explicitly mentioning the copilot-instructions file and the config files. Both responses match the required conventions and instructions, with no significant omissions or errors.", + "metrics": { + "withoutInstructions": { + "durationMs": 2946, + "tokenUsage": { + "promptTokens": 9900, + "completionTokens": 85, + "totalTokens": 9985 + }, + "toolCalls": { + "count": 0, + "byName": {}, + "totalDurationMs": 0 + } + }, + "withInstructions": { + "durationMs": 3189, + "tokenUsage": { + "promptTokens": 10718, + "completionTokens": 84, + "totalTokens": 10802 + }, + "toolCalls": { + "count": 0, + "byName": {}, + "totalDurationMs": 0 + } + }, + "judge": { + "durationMs": 2608, + "tokenUsage": { + "promptTokens": 10116, + "completionTokens": 69, + "totalTokens": 10185 + }, + "toolCalls": { + "count": 0, + "byName": {}, + "totalDurationMs": 0 + } + }, + "totalDurationMs": 8815 + }, + "trajectory": [ + { + "timestampMs": 1770184236135, + "phase": "withoutInstructions", + "type": "pending_messages.modified", + "data": {} + }, + { + "timestampMs": 1770184236135, + "phase": "withoutInstructions", + "type": "pending_messages.modified", + "data": {} + }, + { + "timestampMs": 1770184236736, + "phase": "withoutInstructions", + "type": "user.message", + "data": { + "content": "You are working in this repository:\n/Users/pierceboggan/Documents/get-ready\nUse the file system tools when needed to inspect the codebase.\n\nWhere should new CLI commands and core logic be placed, and what conventions should be followed for configuration files?", + "transformedContent": "2026-02-04T05:50:36.710Z\n\nYou are working in this repository:\n/Users/pierceboggan/Documents/get-ready\nUse the file system tools when needed to inspect the codebase.\n\nWhere should new CLI commands and core logic be placed, and what conventions should be followed for configuration files?", + "attachments": [] + } + }, + { + "timestampMs": 1770184236736, + "phase": "withoutInstructions", + "type": "assistant.turn_start", + "data": { + "turnId": "0" + } + }, + { + "timestampMs": 1770184236736, + "phase": "withoutInstructions", + "type": "session.usage_info", + "data": { + "tokenLimit": 64000, + "currentTokens": 10533, + "messagesLength": 2 + } + }, + { + "timestampMs": 1770184237817, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 3, + "deltaPreview": "New", + "totalResponseSizeBytes": 3 + } + }, + { + "timestampMs": 1770184237817, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 4, + "deltaPreview": " CLI", + "totalResponseSizeBytes": 7 + } + }, + { + "timestampMs": 1770184237817, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 9, + "deltaPreview": " commands", + "totalResponseSizeBytes": 16 + } + }, + { + "timestampMs": 1770184237817, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 7, + "deltaPreview": " should", + "totalResponseSizeBytes": 23 + } + }, + { + "timestampMs": 1770184237817, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 3, + "deltaPreview": " be", + "totalResponseSizeBytes": 26 + } + }, + { + "timestampMs": 1770184237817, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 7, + "deltaPreview": " placed", + "totalResponseSizeBytes": 33 + } + }, + { + "timestampMs": 1770184237932, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 3, + "deltaPreview": " in", + "totalResponseSizeBytes": 36 + } + }, + { + "timestampMs": 1770184237932, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 4, + "deltaPreview": " src", + "totalResponseSizeBytes": 40 + } + }, + { + "timestampMs": 1770184237933, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 1, + "deltaPreview": "/", + "totalResponseSizeBytes": 41 + } + }, + { + "timestampMs": 1770184237933, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 8, + "deltaPreview": "commands", + "totalResponseSizeBytes": 49 + } + }, + { + "timestampMs": 1770184237933, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 2, + "deltaPreview": "/,", + "totalResponseSizeBytes": 51 + } + }, + { + "timestampMs": 1770184237933, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 4, + "deltaPreview": " and", + "totalResponseSizeBytes": 55 + } + }, + { + "timestampMs": 1770184237935, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 5, + "deltaPreview": " core", + "totalResponseSizeBytes": 60 + } + }, + { + "timestampMs": 1770184237935, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 6, + "deltaPreview": " logic", + "totalResponseSizeBytes": 66 + } + }, + { + "timestampMs": 1770184237935, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 7, + "deltaPreview": " should", + "totalResponseSizeBytes": 73 + } + }, + { + "timestampMs": 1770184237935, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 3, + "deltaPreview": " go", + "totalResponseSizeBytes": 76 + } + }, + { + "timestampMs": 1770184238094, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 3, + "deltaPreview": " in", + "totalResponseSizeBytes": 79 + } + }, + { + "timestampMs": 1770184238095, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 4, + "deltaPreview": " src", + "totalResponseSizeBytes": 83 + } + }, + { + "timestampMs": 1770184238095, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 9, + "deltaPreview": "/services", + "totalResponseSizeBytes": 92 + } + }, + { + "timestampMs": 1770184238095, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 2, + "deltaPreview": "/.", + "totalResponseSizeBytes": 94 + } + }, + { + "timestampMs": 1770184238095, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 4, + "deltaPreview": " For", + "totalResponseSizeBytes": 98 + } + }, + { + "timestampMs": 1770184238095, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 14, + "deltaPreview": " configuration", + "totalResponseSizeBytes": 112 + } + }, + { + "timestampMs": 1770184238095, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 6, + "deltaPreview": " files", + "totalResponseSizeBytes": 118 + } + }, + { + "timestampMs": 1770184238095, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 1, + "deltaPreview": ",", + "totalResponseSizeBytes": 119 + } + }, + { + "timestampMs": 1770184238095, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 5, + "deltaPreview": " only", + "totalResponseSizeBytes": 124 + } + }, + { + "timestampMs": 1770184238096, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 10, + "deltaPreview": " overwrite", + "totalResponseSizeBytes": 134 + } + }, + { + "timestampMs": 1770184238261, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 5, + "deltaPreview": " them", + "totalResponseSizeBytes": 139 + } + }, + { + "timestampMs": 1770184238261, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 2, + "deltaPreview": " (", + "totalResponseSizeBytes": 141 + } + }, + { + "timestampMs": 1770184238261, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 1, + "deltaPreview": "e", + "totalResponseSizeBytes": 142 + } + }, + { + "timestampMs": 1770184238261, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 2, + "deltaPreview": ".g", + "totalResponseSizeBytes": 144 + } + }, + { + "timestampMs": 1770184238262, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 2, + "deltaPreview": ".,", + "totalResponseSizeBytes": 146 + } + }, + { + "timestampMs": 1770184238262, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 2, + "deltaPreview": " .", + "totalResponseSizeBytes": 148 + } + }, + { + "timestampMs": 1770184238262, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 1, + "deltaPreview": "v", + "totalResponseSizeBytes": 149 + } + }, + { + "timestampMs": 1770184238262, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 5, + "deltaPreview": "scode", + "totalResponseSizeBytes": 154 + } + }, + { + "timestampMs": 1770184238262, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 9, + "deltaPreview": "/settings", + "totalResponseSizeBytes": 163 + } + }, + { + "timestampMs": 1770184238262, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 5, + "deltaPreview": ".json", + "totalResponseSizeBytes": 168 + } + }, + { + "timestampMs": 1770184238420, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 1, + "deltaPreview": ",", + "totalResponseSizeBytes": 169 + } + }, + { + "timestampMs": 1770184238420, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 2, + "deltaPreview": " .", + "totalResponseSizeBytes": 171 + } + }, + { + "timestampMs": 1770184238420, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 1, + "deltaPreview": "v", + "totalResponseSizeBytes": 172 + } + }, + { + "timestampMs": 1770184238420, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 5, + "deltaPreview": "scode", + "totalResponseSizeBytes": 177 + } + }, + { + "timestampMs": 1770184238421, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 2, + "deltaPreview": "/m", + "totalResponseSizeBytes": 179 + } + }, + { + "timestampMs": 1770184238421, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 2, + "deltaPreview": "cp", + "totalResponseSizeBytes": 181 + } + }, + { + "timestampMs": 1770184238421, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 5, + "deltaPreview": ".json", + "totalResponseSizeBytes": 186 + } + }, + { + "timestampMs": 1770184238421, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 1, + "deltaPreview": ")", + "totalResponseSizeBytes": 187 + } + }, + { + "timestampMs": 1770184238421, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 5, + "deltaPreview": " with", + "totalResponseSizeBytes": 192 + } + }, + { + "timestampMs": 1770184238421, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 3, + "deltaPreview": " --", + "totalResponseSizeBytes": 195 + } + }, + { + "timestampMs": 1770184238421, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 5, + "deltaPreview": "force", + "totalResponseSizeBytes": 200 + } + }, + { + "timestampMs": 1770184238630, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 1, + "deltaPreview": ",", + "totalResponseSizeBytes": 201 + } + }, + { + "timestampMs": 1770184238630, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 4, + "deltaPreview": " and", + "totalResponseSizeBytes": 205 + } + }, + { + "timestampMs": 1770184238630, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 4, + "deltaPreview": " all", + "totalResponseSizeBytes": 209 + } + }, + { + "timestampMs": 1770184238630, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 4, + "deltaPreview": " Cop", + "totalResponseSizeBytes": 213 + } + }, + { + "timestampMs": 1770184238631, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 4, + "deltaPreview": "ilot", + "totalResponseSizeBytes": 217 + } + }, + { + "timestampMs": 1770184238631, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 1, + "deltaPreview": "/", + "totalResponseSizeBytes": 218 + } + }, + { + "timestampMs": 1770184238631, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 2, + "deltaPreview": "VS", + "totalResponseSizeBytes": 220 + } + }, + { + "timestampMs": 1770184238631, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 5, + "deltaPreview": " Code", + "totalResponseSizeBytes": 225 + } + }, + { + "timestampMs": 1770184238631, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 9, + "deltaPreview": " settings", + "totalResponseSizeBytes": 234 + } + }, + { + "timestampMs": 1770184238631, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 7, + "deltaPreview": " should", + "totalResponseSizeBytes": 241 + } + }, + { + "timestampMs": 1770184238790, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 10, + "deltaPreview": " reference", + "totalResponseSizeBytes": 251 + } + }, + { + "timestampMs": 1770184238790, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 4, + "deltaPreview": " the", + "totalResponseSizeBytes": 255 + } + }, + { + "timestampMs": 1770184238790, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 5, + "deltaPreview": " main", + "totalResponseSizeBytes": 260 + } + }, + { + "timestampMs": 1770184238790, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 13, + "deltaPreview": " instructions", + "totalResponseSizeBytes": 273 + } + }, + { + "timestampMs": 1770184238790, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 5, + "deltaPreview": " file", + "totalResponseSizeBytes": 278 + } + }, + { + "timestampMs": 1770184238790, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 4, + "deltaPreview": " and", + "totalResponseSizeBytes": 282 + } + }, + { + "timestampMs": 1770184238790, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 7, + "deltaPreview": " enable", + "totalResponseSizeBytes": 289 + } + }, + { + "timestampMs": 1770184238790, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 4, + "deltaPreview": " MCP", + "totalResponseSizeBytes": 293 + } + }, + { + "timestampMs": 1770184238790, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 1, + "deltaPreview": ".", + "totalResponseSizeBytes": 294 + } + }, + { + "timestampMs": 1770184238790, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 4, + "deltaPreview": " Use", + "totalResponseSizeBytes": 298 + } + }, + { + "timestampMs": 1770184238949, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 2, + "deltaPreview": " E", + "totalResponseSizeBytes": 300 + } + }, + { + "timestampMs": 1770184238949, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 2, + "deltaPreview": "SM", + "totalResponseSizeBytes": 302 + } + }, + { + "timestampMs": 1770184238949, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 7, + "deltaPreview": " syntax", + "totalResponseSizeBytes": 309 + } + }, + { + "timestampMs": 1770184238950, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 11, + "deltaPreview": " everywhere", + "totalResponseSizeBytes": 320 + } + }, + { + "timestampMs": 1770184238950, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 1, + "deltaPreview": ",", + "totalResponseSizeBytes": 321 + } + }, + { + "timestampMs": 1770184238950, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 7, + "deltaPreview": " strict", + "totalResponseSizeBytes": 328 + } + }, + { + "timestampMs": 1770184238950, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 5, + "deltaPreview": " Type", + "totalResponseSizeBytes": 333 + } + }, + { + "timestampMs": 1770184238950, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 6, + "deltaPreview": "Script", + "totalResponseSizeBytes": 339 + } + }, + { + "timestampMs": 1770184238950, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 1, + "deltaPreview": ",", + "totalResponseSizeBytes": 340 + } + }, + { + "timestampMs": 1770184238950, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 4, + "deltaPreview": " and", + "totalResponseSizeBytes": 344 + } + }, + { + "timestampMs": 1770184239078, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 7, + "deltaPreview": " follow", + "totalResponseSizeBytes": 351 + } + }, + { + "timestampMs": 1770184239078, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 5, + "deltaPreview": " safe", + "totalResponseSizeBytes": 356 + } + }, + { + "timestampMs": 1770184239078, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 5, + "deltaPreview": " file", + "totalResponseSizeBytes": 361 + } + }, + { + "timestampMs": 1770184239078, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 6, + "deltaPreview": " write", + "totalResponseSizeBytes": 367 + } + }, + { + "timestampMs": 1770184239078, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 12, + "deltaPreview": " conventions", + "totalResponseSizeBytes": 379 + } + }, + { + "timestampMs": 1770184239078, + "phase": "withoutInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "deltaChars": 1, + "deltaPreview": ".", + "totalResponseSizeBytes": 380 + } + }, + { + "timestampMs": 1770184239078, + "phase": "withoutInstructions", + "type": "assistant.usage", + "data": { + "model": "gpt-4.1", + "inputTokens": 9900, + "outputTokens": 85, + "cacheReadTokens": 9088, + "cacheWriteTokens": 0, + "cost": 0, + "duration": 2342, + "initiator": "user", + "apiCallId": "chatcmpl-D5Q4bqEMJMkUWnQOI96LI4BYwIYTH", + "providerCallId": "F30B:9765B:BB0D6:DB1F4:6982DE2C", + "quotaSnapshots": { + "chat": { + "isUnlimitedEntitlement": true, + "entitlementRequests": -1, + "usedRequests": 0, + "usageAllowedWithExhaustedQuota": true, + "overage": 0, + "overageAllowedWithExhaustedQuota": true, + "remainingPercentage": 100, + "resetDate": "2026-03-01T00:00:00.000Z" + }, + "completions": { + "isUnlimitedEntitlement": true, + "entitlementRequests": -1, + "usedRequests": 0, + "usageAllowedWithExhaustedQuota": true, + "overage": 0, + "overageAllowedWithExhaustedQuota": true, + "remainingPercentage": 100, + "resetDate": "2026-03-01T00:00:00.000Z" + }, + "premium_interactions": { + "isUnlimitedEntitlement": true, + "entitlementRequests": -1, + "usedRequests": 0, + "usageAllowedWithExhaustedQuota": true, + "overage": 0, + "overageAllowedWithExhaustedQuota": true, + "remainingPercentage": 100, + "resetDate": "2026-03-01T00:00:00.000Z" + }, + "premium_interactions_tbb": { + "isUnlimitedEntitlement": true, + "entitlementRequests": -1, + "usedRequests": 0, + "usageAllowedWithExhaustedQuota": true, + "overage": 0, + "overageAllowedWithExhaustedQuota": true, + "remainingPercentage": 100, + "resetDate": "2026-03-01T00:00:00.000Z" + } + } + } + }, + { + "timestampMs": 1770184239078, + "phase": "withoutInstructions", + "type": "assistant.message", + "data": { + "messageId": "a4517944-2205-4b00-9258-5d1418814388", + "content": "New CLI commands should be placed in src/commands/, and core logic should go in src/services/. For configuration files, only overwrite them (e.g., .vscode/settings.json, .vscode/mcp.json) with --force, and all Copilot/VS Code settings should reference the main instructions file and enable MCP. Use ESM syntax everywhere, strict TypeScript, and follow safe file write conventions.", + "toolRequests": [] + } + }, + { + "timestampMs": 1770184239078, + "phase": "withoutInstructions", + "type": "assistant.turn_end", + "data": { + "turnId": "0" + } + }, + { + "timestampMs": 1770184239078, + "phase": "withoutInstructions", + "type": "session.idle", + "data": {} + }, + { + "timestampMs": 1770184239101, + "phase": "withInstructions", + "type": "pending_messages.modified", + "data": {} + }, + { + "timestampMs": 1770184239101, + "phase": "withInstructions", + "type": "pending_messages.modified", + "data": {} + }, + { + "timestampMs": 1770184239841, + "phase": "withInstructions", + "type": "user.message", + "data": { + "content": "You are working in this repository:\n/Users/pierceboggan/Documents/get-ready\nUse the file system tools when needed to inspect the codebase.\n\nWhere should new CLI commands and core logic be placed, and what conventions should be followed for configuration files?", + "transformedContent": "2026-02-04T05:50:39.807Z\n\nYou are working in this repository:\n/Users/pierceboggan/Documents/get-ready\nUse the file system tools when needed to inspect the codebase.\n\nWhere should new CLI commands and core logic be placed, and what conventions should be followed for configuration files?", + "attachments": [] + } + }, + { + "timestampMs": 1770184239841, + "phase": "withInstructions", + "type": "assistant.turn_start", + "data": { + "turnId": "0" + } + }, + { + "timestampMs": 1770184239841, + "phase": "withInstructions", + "type": "session.usage_info", + "data": { + "tokenLimit": 64000, + "currentTokens": 11351, + "messagesLength": 2 + } + }, + { + "timestampMs": 1770184240932, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 3, + "deltaPreview": "New", + "totalResponseSizeBytes": 3 + } + }, + { + "timestampMs": 1770184240932, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 4, + "deltaPreview": " CLI", + "totalResponseSizeBytes": 7 + } + }, + { + "timestampMs": 1770184240932, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 9, + "deltaPreview": " commands", + "totalResponseSizeBytes": 16 + } + }, + { + "timestampMs": 1770184240932, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 7, + "deltaPreview": " should", + "totalResponseSizeBytes": 23 + } + }, + { + "timestampMs": 1770184240932, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 3, + "deltaPreview": " be", + "totalResponseSizeBytes": 26 + } + }, + { + "timestampMs": 1770184240932, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 7, + "deltaPreview": " placed", + "totalResponseSizeBytes": 33 + } + }, + { + "timestampMs": 1770184241100, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 3, + "deltaPreview": " in", + "totalResponseSizeBytes": 36 + } + }, + { + "timestampMs": 1770184241100, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 4, + "deltaPreview": " src", + "totalResponseSizeBytes": 40 + } + }, + { + "timestampMs": 1770184241100, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 1, + "deltaPreview": "/", + "totalResponseSizeBytes": 41 + } + }, + { + "timestampMs": 1770184241100, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 8, + "deltaPreview": "commands", + "totalResponseSizeBytes": 49 + } + }, + { + "timestampMs": 1770184241100, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 2, + "deltaPreview": "/,", + "totalResponseSizeBytes": 51 + } + }, + { + "timestampMs": 1770184241100, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 4, + "deltaPreview": " and", + "totalResponseSizeBytes": 55 + } + }, + { + "timestampMs": 1770184241100, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 5, + "deltaPreview": " core", + "totalResponseSizeBytes": 60 + } + }, + { + "timestampMs": 1770184241100, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 6, + "deltaPreview": " logic", + "totalResponseSizeBytes": 66 + } + }, + { + "timestampMs": 1770184241100, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 7, + "deltaPreview": " should", + "totalResponseSizeBytes": 73 + } + }, + { + "timestampMs": 1770184241100, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 3, + "deltaPreview": " go", + "totalResponseSizeBytes": 76 + } + }, + { + "timestampMs": 1770184241259, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 3, + "deltaPreview": " in", + "totalResponseSizeBytes": 79 + } + }, + { + "timestampMs": 1770184241259, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 4, + "deltaPreview": " src", + "totalResponseSizeBytes": 83 + } + }, + { + "timestampMs": 1770184241262, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 9, + "deltaPreview": "/services", + "totalResponseSizeBytes": 92 + } + }, + { + "timestampMs": 1770184241262, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 2, + "deltaPreview": "/.", + "totalResponseSizeBytes": 94 + } + }, + { + "timestampMs": 1770184241262, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 4, + "deltaPreview": " For", + "totalResponseSizeBytes": 98 + } + }, + { + "timestampMs": 1770184241262, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 14, + "deltaPreview": " configuration", + "totalResponseSizeBytes": 112 + } + }, + { + "timestampMs": 1770184241262, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 6, + "deltaPreview": " files", + "totalResponseSizeBytes": 118 + } + }, + { + "timestampMs": 1770184241262, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 5, + "deltaPreview": " like", + "totalResponseSizeBytes": 123 + } + }, + { + "timestampMs": 1770184241263, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 2, + "deltaPreview": " .", + "totalResponseSizeBytes": 125 + } + }, + { + "timestampMs": 1770184241263, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 1, + "deltaPreview": "v", + "totalResponseSizeBytes": 126 + } + }, + { + "timestampMs": 1770184241450, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 5, + "deltaPreview": "scode", + "totalResponseSizeBytes": 131 + } + }, + { + "timestampMs": 1770184241450, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 9, + "deltaPreview": "/settings", + "totalResponseSizeBytes": 140 + } + }, + { + "timestampMs": 1770184241450, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 5, + "deltaPreview": ".json", + "totalResponseSizeBytes": 145 + } + }, + { + "timestampMs": 1770184241450, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 4, + "deltaPreview": " and", + "totalResponseSizeBytes": 149 + } + }, + { + "timestampMs": 1770184241450, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 2, + "deltaPreview": " .", + "totalResponseSizeBytes": 151 + } + }, + { + "timestampMs": 1770184241450, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 1, + "deltaPreview": "v", + "totalResponseSizeBytes": 152 + } + }, + { + "timestampMs": 1770184241450, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 5, + "deltaPreview": "scode", + "totalResponseSizeBytes": 157 + } + }, + { + "timestampMs": 1770184241450, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 2, + "deltaPreview": "/m", + "totalResponseSizeBytes": 159 + } + }, + { + "timestampMs": 1770184241450, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 2, + "deltaPreview": "cp", + "totalResponseSizeBytes": 161 + } + }, + { + "timestampMs": 1770184241450, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 5, + "deltaPreview": ".json", + "totalResponseSizeBytes": 166 + } + }, + { + "timestampMs": 1770184241683, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 1, + "deltaPreview": ",", + "totalResponseSizeBytes": 167 + } + }, + { + "timestampMs": 1770184241683, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 5, + "deltaPreview": " only", + "totalResponseSizeBytes": 172 + } + }, + { + "timestampMs": 1770184241683, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 10, + "deltaPreview": " overwrite", + "totalResponseSizeBytes": 182 + } + }, + { + "timestampMs": 1770184241683, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 5, + "deltaPreview": " them", + "totalResponseSizeBytes": 187 + } + }, + { + "timestampMs": 1770184241683, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 5, + "deltaPreview": " with", + "totalResponseSizeBytes": 192 + } + }, + { + "timestampMs": 1770184241683, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 3, + "deltaPreview": " --", + "totalResponseSizeBytes": 195 + } + }, + { + "timestampMs": 1770184241683, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 5, + "deltaPreview": "force", + "totalResponseSizeBytes": 200 + } + }, + { + "timestampMs": 1770184241683, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 1, + "deltaPreview": ",", + "totalResponseSizeBytes": 201 + } + }, + { + "timestampMs": 1770184241683, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 4, + "deltaPreview": " and", + "totalResponseSizeBytes": 205 + } + }, + { + "timestampMs": 1770184241683, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 7, + "deltaPreview": " ensure", + "totalResponseSizeBytes": 212 + } + }, + { + "timestampMs": 1770184241683, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 4, + "deltaPreview": " all", + "totalResponseSizeBytes": 216 + } + }, + { + "timestampMs": 1770184241847, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 4, + "deltaPreview": " Cop", + "totalResponseSizeBytes": 220 + } + }, + { + "timestampMs": 1770184241847, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 4, + "deltaPreview": "ilot", + "totalResponseSizeBytes": 224 + } + }, + { + "timestampMs": 1770184241847, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 1, + "deltaPreview": "/", + "totalResponseSizeBytes": 225 + } + }, + { + "timestampMs": 1770184241847, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 2, + "deltaPreview": "VS", + "totalResponseSizeBytes": 227 + } + }, + { + "timestampMs": 1770184241847, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 5, + "deltaPreview": " Code", + "totalResponseSizeBytes": 232 + } + }, + { + "timestampMs": 1770184241847, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 9, + "deltaPreview": " settings", + "totalResponseSizeBytes": 241 + } + }, + { + "timestampMs": 1770184241847, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 10, + "deltaPreview": " reference", + "totalResponseSizeBytes": 251 + } + }, + { + "timestampMs": 1770184241847, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 4, + "deltaPreview": " the", + "totalResponseSizeBytes": 255 + } + }, + { + "timestampMs": 1770184241847, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 5, + "deltaPreview": " main", + "totalResponseSizeBytes": 260 + } + }, + { + "timestampMs": 1770184241847, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 4, + "deltaPreview": " cop", + "totalResponseSizeBytes": 264 + } + }, + { + "timestampMs": 1770184242013, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 4, + "deltaPreview": "ilot", + "totalResponseSizeBytes": 268 + } + }, + { + "timestampMs": 1770184242013, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 3, + "deltaPreview": "-in", + "totalResponseSizeBytes": 271 + } + }, + { + "timestampMs": 1770184242013, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 10, + "deltaPreview": "structions", + "totalResponseSizeBytes": 281 + } + }, + { + "timestampMs": 1770184242013, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 5, + "deltaPreview": " file", + "totalResponseSizeBytes": 286 + } + }, + { + "timestampMs": 1770184242013, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 4, + "deltaPreview": " and", + "totalResponseSizeBytes": 290 + } + }, + { + "timestampMs": 1770184242013, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 7, + "deltaPreview": " enable", + "totalResponseSizeBytes": 297 + } + }, + { + "timestampMs": 1770184242013, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 4, + "deltaPreview": " MCP", + "totalResponseSizeBytes": 301 + } + }, + { + "timestampMs": 1770184242014, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 1, + "deltaPreview": ".", + "totalResponseSizeBytes": 302 + } + }, + { + "timestampMs": 1770184242014, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 4, + "deltaPreview": " Use", + "totalResponseSizeBytes": 306 + } + }, + { + "timestampMs": 1770184242014, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 2, + "deltaPreview": " E", + "totalResponseSizeBytes": 308 + } + }, + { + "timestampMs": 1770184242176, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 2, + "deltaPreview": "SM", + "totalResponseSizeBytes": 310 + } + }, + { + "timestampMs": 1770184242176, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 7, + "deltaPreview": " syntax", + "totalResponseSizeBytes": 317 + } + }, + { + "timestampMs": 1770184242176, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 11, + "deltaPreview": " everywhere", + "totalResponseSizeBytes": 328 + } + }, + { + "timestampMs": 1770184242176, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 1, + "deltaPreview": ",", + "totalResponseSizeBytes": 329 + } + }, + { + "timestampMs": 1770184242176, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 7, + "deltaPreview": " strict", + "totalResponseSizeBytes": 336 + } + }, + { + "timestampMs": 1770184242176, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 5, + "deltaPreview": " Type", + "totalResponseSizeBytes": 341 + } + }, + { + "timestampMs": 1770184242176, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 6, + "deltaPreview": "Script", + "totalResponseSizeBytes": 347 + } + }, + { + "timestampMs": 1770184242176, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 1, + "deltaPreview": ",", + "totalResponseSizeBytes": 348 + } + }, + { + "timestampMs": 1770184242176, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 4, + "deltaPreview": " and", + "totalResponseSizeBytes": 352 + } + }, + { + "timestampMs": 1770184242176, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 7, + "deltaPreview": " follow", + "totalResponseSizeBytes": 359 + } + }, + { + "timestampMs": 1770184242288, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 5, + "deltaPreview": " safe", + "totalResponseSizeBytes": 364 + } + }, + { + "timestampMs": 1770184242288, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 5, + "deltaPreview": " file", + "totalResponseSizeBytes": 369 + } + }, + { + "timestampMs": 1770184242288, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 6, + "deltaPreview": " write", + "totalResponseSizeBytes": 375 + } + }, + { + "timestampMs": 1770184242288, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 12, + "deltaPreview": " conventions", + "totalResponseSizeBytes": 387 + } + }, + { + "timestampMs": 1770184242288, + "phase": "withInstructions", + "type": "assistant.message_delta", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "deltaChars": 1, + "deltaPreview": ".", + "totalResponseSizeBytes": 388 + } + }, + { + "timestampMs": 1770184242288, + "phase": "withInstructions", + "type": "assistant.usage", + "data": { + "model": "gpt-4.1", + "inputTokens": 10718, + "outputTokens": 84, + "cacheReadTokens": 9984, + "cacheWriteTokens": 0, + "cost": 0, + "duration": 2446, + "initiator": "user", + "apiCallId": "chatcmpl-D5Q4etdA0iKp1GopzUuy9TjAQajTf", + "providerCallId": "F30B:9765B:BBA56:DBCEF:6982DE2F", + "quotaSnapshots": { + "chat": { + "isUnlimitedEntitlement": true, + "entitlementRequests": -1, + "usedRequests": 0, + "usageAllowedWithExhaustedQuota": true, + "overage": 0, + "overageAllowedWithExhaustedQuota": true, + "remainingPercentage": 100, + "resetDate": "2026-03-01T00:00:00.000Z" + }, + "completions": { + "isUnlimitedEntitlement": true, + "entitlementRequests": -1, + "usedRequests": 0, + "usageAllowedWithExhaustedQuota": true, + "overage": 0, + "overageAllowedWithExhaustedQuota": true, + "remainingPercentage": 100, + "resetDate": "2026-03-01T00:00:00.000Z" + }, + "premium_interactions": { + "isUnlimitedEntitlement": true, + "entitlementRequests": -1, + "usedRequests": 0, + "usageAllowedWithExhaustedQuota": true, + "overage": 0, + "overageAllowedWithExhaustedQuota": true, + "remainingPercentage": 100, + "resetDate": "2026-03-01T00:00:00.000Z" + }, + "premium_interactions_tbb": { + "isUnlimitedEntitlement": true, + "entitlementRequests": -1, + "usedRequests": 0, + "usageAllowedWithExhaustedQuota": true, + "overage": 0, + "overageAllowedWithExhaustedQuota": true, + "remainingPercentage": 100, + "resetDate": "2026-03-01T00:00:00.000Z" + } + } + } + }, + { + "timestampMs": 1770184242288, + "phase": "withInstructions", + "type": "assistant.message", + "data": { + "messageId": "fe6bb094-be6d-4b79-ac63-ec0e39f62612", + "content": "New CLI commands should be placed in src/commands/, and core logic should go in src/services/. For configuration files like .vscode/settings.json and .vscode/mcp.json, only overwrite them with --force, and ensure all Copilot/VS Code settings reference the main copilot-instructions file and enable MCP. Use ESM syntax everywhere, strict TypeScript, and follow safe file write conventions.", + "toolRequests": [] + } + }, + { + "timestampMs": 1770184242288, + "phase": "withInstructions", + "type": "assistant.turn_end", + "data": { + "turnId": "0" + } + }, + { + "timestampMs": 1770184242288, + "phase": "withInstructions", + "type": "session.idle", + "data": {} + }, + { + "timestampMs": 1770184242317, + "phase": "judge", + "type": "pending_messages.modified", + "data": {} + }, + { + "timestampMs": 1770184242317, + "phase": "judge", + "type": "pending_messages.modified", + "data": {} + }, + { + "timestampMs": 1770184242973, + "phase": "judge", + "type": "user.message", + "data": { + "content": "Evaluate which response best matches the expectation.\n\nExpectation: New CLI commands should be placed in src/commands/, and core logic in src/services/. Use ESM syntax everywhere, strict TypeScript targeting ES2022 and module ESNext, and only overwrite config files like .vscode/settings.json and .vscode/mcp.json with --force. All Copilot/VS Code settings reference .github/copilot-instructions.md and enable MCP.\n\nResponse A (without custom instructions):\nNew CLI commands should be placed in src/commands/, and core logic should go in src/services/. For configuration files, only overwrite them (e.g., .vscode/settings.json, .vscode/mcp.json) with --force, and all Copilot/VS Code settings should reference the main instructions file and enable MCP. Use ESM syntax everywhere, strict TypeScript, and follow safe file write conventions.\n\nResponse B (with custom instructions):\nNew CLI commands should be placed in src/commands/, and core logic should go in src/services/. For configuration files like .vscode/settings.json and .vscode/mcp.json, only overwrite them with --force, and ensure all Copilot/VS Code settings reference the main copilot-instructions file and enable MCP. Use ESM syntax everywhere, strict TypeScript, and follow safe file write conventions.\n\nReturn JSON only.", + "transformedContent": "2026-02-04T05:50:42.950Z\n\nEvaluate which response best matches the expectation.\n\nExpectation: New CLI commands should be placed in src/commands/, and core logic in src/services/. Use ESM syntax everywhere, strict TypeScript targeting ES2022 and module ESNext, and only overwrite config files like .vscode/settings.json and .vscode/mcp.json with --force. All Copilot/VS Code settings reference .github/copilot-instructions.md and enable MCP.\n\nResponse A (without custom instructions):\nNew CLI commands should be placed in src/commands/, and core logic should go in src/services/. For configuration files, only overwrite them (e.g., .vscode/settings.json, .vscode/mcp.json) with --force, and all Copilot/VS Code settings should reference the main instructions file and enable MCP. Use ESM syntax everywhere, strict TypeScript, and follow safe file write conventions.\n\nResponse B (with custom instructions):\nNew CLI commands should be placed in src/commands/, and core logic should go in src/services/. For configuration files like .vscode/settings.json and .vscode/mcp.json, only overwrite them with --force, and ensure all Copilot/VS Code settings reference the main copilot-instructions file and enable MCP. Use ESM syntax everywhere, strict TypeScript, and follow safe file write conventions.\n\nReturn JSON only.", + "attachments": [] + } + }, + { + "timestampMs": 1770184242973, + "phase": "judge", + "type": "assistant.turn_start", + "data": { + "turnId": "0" + } + }, + { + "timestampMs": 1770184242973, + "phase": "judge", + "type": "session.usage_info", + "data": { + "tokenLimit": 64000, + "currentTokens": 10749, + "messagesLength": 2 + } + }, + { + "timestampMs": 1770184243938, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 2, + "deltaPreview": "{\n", + "totalResponseSizeBytes": 2 + } + }, + { + "timestampMs": 1770184243938, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 1, + "deltaPreview": " ", + "totalResponseSizeBytes": 3 + } + }, + { + "timestampMs": 1770184243938, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 2, + "deltaPreview": " \"", + "totalResponseSizeBytes": 5 + } + }, + { + "timestampMs": 1770184243938, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 3, + "deltaPreview": "ver", + "totalResponseSizeBytes": 8 + } + }, + { + "timestampMs": 1770184243938, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 4, + "deltaPreview": "dict", + "totalResponseSizeBytes": 12 + } + }, + { + "timestampMs": 1770184243938, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 2, + "deltaPreview": "\":", + "totalResponseSizeBytes": 14 + } + }, + { + "timestampMs": 1770184243938, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 2, + "deltaPreview": " \"", + "totalResponseSizeBytes": 16 + } + }, + { + "timestampMs": 1770184243938, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 4, + "deltaPreview": "pass", + "totalResponseSizeBytes": 20 + } + }, + { + "timestampMs": 1770184244096, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 3, + "deltaPreview": "\",\n", + "totalResponseSizeBytes": 23 + } + }, + { + "timestampMs": 1770184244096, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 1, + "deltaPreview": " ", + "totalResponseSizeBytes": 24 + } + }, + { + "timestampMs": 1770184244098, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 2, + "deltaPreview": " \"", + "totalResponseSizeBytes": 26 + } + }, + { + "timestampMs": 1770184244098, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 5, + "deltaPreview": "score", + "totalResponseSizeBytes": 31 + } + }, + { + "timestampMs": 1770184244098, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 2, + "deltaPreview": "\":", + "totalResponseSizeBytes": 33 + } + }, + { + "timestampMs": 1770184244098, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 1, + "deltaPreview": " ", + "totalResponseSizeBytes": 34 + } + }, + { + "timestampMs": 1770184244098, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 3, + "deltaPreview": "100", + "totalResponseSizeBytes": 37 + } + }, + { + "timestampMs": 1770184244099, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 2, + "deltaPreview": ",\n", + "totalResponseSizeBytes": 39 + } + }, + { + "timestampMs": 1770184244099, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 1, + "deltaPreview": " ", + "totalResponseSizeBytes": 40 + } + }, + { + "timestampMs": 1770184244099, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 2, + "deltaPreview": " \"", + "totalResponseSizeBytes": 42 + } + }, + { + "timestampMs": 1770184244261, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 1, + "deltaPreview": "r", + "totalResponseSizeBytes": 43 + } + }, + { + "timestampMs": 1770184244262, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 8, + "deltaPreview": "ationale", + "totalResponseSizeBytes": 51 + } + }, + { + "timestampMs": 1770184244262, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 2, + "deltaPreview": "\":", + "totalResponseSizeBytes": 53 + } + }, + { + "timestampMs": 1770184244262, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 2, + "deltaPreview": " \"", + "totalResponseSizeBytes": 55 + } + }, + { + "timestampMs": 1770184244262, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 4, + "deltaPreview": "Both", + "totalResponseSizeBytes": 59 + } + }, + { + "timestampMs": 1770184244262, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 10, + "deltaPreview": " responses", + "totalResponseSizeBytes": 69 + } + }, + { + "timestampMs": 1770184244262, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 11, + "deltaPreview": " accurately", + "totalResponseSizeBytes": 80 + } + }, + { + "timestampMs": 1770184244262, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 8, + "deltaPreview": " reflect", + "totalResponseSizeBytes": 88 + } + }, + { + "timestampMs": 1770184244262, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 4, + "deltaPreview": " the", + "totalResponseSizeBytes": 92 + } + }, + { + "timestampMs": 1770184244262, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 12, + "deltaPreview": " expectation", + "totalResponseSizeBytes": 104 + } + }, + { + "timestampMs": 1770184244422, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 1, + "deltaPreview": ",", + "totalResponseSizeBytes": 105 + } + }, + { + "timestampMs": 1770184244422, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 4, + "deltaPreview": " but", + "totalResponseSizeBytes": 109 + } + }, + { + "timestampMs": 1770184244422, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 9, + "deltaPreview": " Response", + "totalResponseSizeBytes": 118 + } + }, + { + "timestampMs": 1770184244422, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 2, + "deltaPreview": " B", + "totalResponseSizeBytes": 120 + } + }, + { + "timestampMs": 1770184244422, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 3, + "deltaPreview": " is", + "totalResponseSizeBytes": 123 + } + }, + { + "timestampMs": 1770184244423, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 9, + "deltaPreview": " slightly", + "totalResponseSizeBytes": 132 + } + }, + { + "timestampMs": 1770184244423, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 5, + "deltaPreview": " more", + "totalResponseSizeBytes": 137 + } + }, + { + "timestampMs": 1770184244423, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 8, + "deltaPreview": " precise", + "totalResponseSizeBytes": 145 + } + }, + { + "timestampMs": 1770184244423, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 3, + "deltaPreview": " by", + "totalResponseSizeBytes": 148 + } + }, + { + "timestampMs": 1770184244423, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 11, + "deltaPreview": " explicitly", + "totalResponseSizeBytes": 159 + } + }, + { + "timestampMs": 1770184244586, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 11, + "deltaPreview": " mentioning", + "totalResponseSizeBytes": 170 + } + }, + { + "timestampMs": 1770184244586, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 4, + "deltaPreview": " the", + "totalResponseSizeBytes": 174 + } + }, + { + "timestampMs": 1770184244586, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 4, + "deltaPreview": " cop", + "totalResponseSizeBytes": 178 + } + }, + { + "timestampMs": 1770184244586, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 4, + "deltaPreview": "ilot", + "totalResponseSizeBytes": 182 + } + }, + { + "timestampMs": 1770184244586, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 3, + "deltaPreview": "-in", + "totalResponseSizeBytes": 185 + } + }, + { + "timestampMs": 1770184244586, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 10, + "deltaPreview": "structions", + "totalResponseSizeBytes": 195 + } + }, + { + "timestampMs": 1770184244586, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 5, + "deltaPreview": " file", + "totalResponseSizeBytes": 200 + } + }, + { + "timestampMs": 1770184244586, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 4, + "deltaPreview": " and", + "totalResponseSizeBytes": 204 + } + }, + { + "timestampMs": 1770184244586, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 4, + "deltaPreview": " the", + "totalResponseSizeBytes": 208 + } + }, + { + "timestampMs": 1770184244586, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 7, + "deltaPreview": " config", + "totalResponseSizeBytes": 215 + } + }, + { + "timestampMs": 1770184244746, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 6, + "deltaPreview": " files", + "totalResponseSizeBytes": 221 + } + }, + { + "timestampMs": 1770184244746, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 1, + "deltaPreview": ".", + "totalResponseSizeBytes": 222 + } + }, + { + "timestampMs": 1770184244746, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 5, + "deltaPreview": " Both", + "totalResponseSizeBytes": 227 + } + }, + { + "timestampMs": 1770184244746, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 10, + "deltaPreview": " responses", + "totalResponseSizeBytes": 237 + } + }, + { + "timestampMs": 1770184244746, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 6, + "deltaPreview": " match", + "totalResponseSizeBytes": 243 + } + }, + { + "timestampMs": 1770184244746, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 4, + "deltaPreview": " the", + "totalResponseSizeBytes": 247 + } + }, + { + "timestampMs": 1770184244746, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 9, + "deltaPreview": " required", + "totalResponseSizeBytes": 256 + } + }, + { + "timestampMs": 1770184244746, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 12, + "deltaPreview": " conventions", + "totalResponseSizeBytes": 268 + } + }, + { + "timestampMs": 1770184244746, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 4, + "deltaPreview": " and", + "totalResponseSizeBytes": 272 + } + }, + { + "timestampMs": 1770184244746, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 13, + "deltaPreview": " instructions", + "totalResponseSizeBytes": 285 + } + }, + { + "timestampMs": 1770184244922, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 1, + "deltaPreview": ",", + "totalResponseSizeBytes": 286 + } + }, + { + "timestampMs": 1770184244922, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 5, + "deltaPreview": " with", + "totalResponseSizeBytes": 291 + } + }, + { + "timestampMs": 1770184244922, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 3, + "deltaPreview": " no", + "totalResponseSizeBytes": 294 + } + }, + { + "timestampMs": 1770184244922, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 12, + "deltaPreview": " significant", + "totalResponseSizeBytes": 306 + } + }, + { + "timestampMs": 1770184244922, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 10, + "deltaPreview": " omissions", + "totalResponseSizeBytes": 316 + } + }, + { + "timestampMs": 1770184244922, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 3, + "deltaPreview": " or", + "totalResponseSizeBytes": 319 + } + }, + { + "timestampMs": 1770184244922, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 7, + "deltaPreview": " errors", + "totalResponseSizeBytes": 326 + } + }, + { + "timestampMs": 1770184244922, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 3, + "deltaPreview": ".\"\n", + "totalResponseSizeBytes": 329 + } + }, + { + "timestampMs": 1770184244922, + "phase": "judge", + "type": "assistant.message_delta", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "deltaChars": 1, + "deltaPreview": "}", + "totalResponseSizeBytes": 330 + } + }, + { + "timestampMs": 1770184244922, + "phase": "judge", + "type": "assistant.usage", + "data": { + "model": "gpt-4.1", + "inputTokens": 10116, + "outputTokens": 69, + "cacheReadTokens": 9088, + "cacheWriteTokens": 0, + "cost": 0, + "duration": 1949, + "initiator": "user", + "apiCallId": "chatcmpl-D5Q4hzEi5ijMG2ws1HHHyPmK9qadB", + "providerCallId": "F30B:9765B:BC404:DC81F:6982DE32", + "quotaSnapshots": { + "chat": { + "isUnlimitedEntitlement": true, + "entitlementRequests": -1, + "usedRequests": 0, + "usageAllowedWithExhaustedQuota": true, + "overage": 0, + "overageAllowedWithExhaustedQuota": true, + "remainingPercentage": 100, + "resetDate": "2026-03-01T00:00:00.000Z" + }, + "completions": { + "isUnlimitedEntitlement": true, + "entitlementRequests": -1, + "usedRequests": 0, + "usageAllowedWithExhaustedQuota": true, + "overage": 0, + "overageAllowedWithExhaustedQuota": true, + "remainingPercentage": 100, + "resetDate": "2026-03-01T00:00:00.000Z" + }, + "premium_interactions": { + "isUnlimitedEntitlement": true, + "entitlementRequests": -1, + "usedRequests": 0, + "usageAllowedWithExhaustedQuota": true, + "overage": 0, + "overageAllowedWithExhaustedQuota": true, + "remainingPercentage": 100, + "resetDate": "2026-03-01T00:00:00.000Z" + }, + "premium_interactions_tbb": { + "isUnlimitedEntitlement": true, + "entitlementRequests": -1, + "usedRequests": 0, + "usageAllowedWithExhaustedQuota": true, + "overage": 0, + "overageAllowedWithExhaustedQuota": true, + "remainingPercentage": 100, + "resetDate": "2026-03-01T00:00:00.000Z" + } + } + } + }, + { + "timestampMs": 1770184244922, + "phase": "judge", + "type": "assistant.message", + "data": { + "messageId": "f401f59a-7133-428b-baa2-c50a3e82b1f5", + "content": "{\n \"verdict\": \"pass\",\n \"score\": 100,\n \"rationale\": \"Both responses accurately reflect the expectation, but Response B is slightly more precise by explicitly mentioning the copilot-instructions file and the config files. Both responses match the required conventions and instructions, with no significant omissions or errors.\"\n}", + "toolRequests": [] + } + }, + { + "timestampMs": 1770184244923, + "phase": "judge", + "type": "assistant.turn_end", + "data": { + "turnId": "0" + } + }, + { + "timestampMs": 1770184244923, + "phase": "judge", + "type": "session.idle", + "data": {} + } + ] } ] -} \ No newline at end of file +} diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..7d8a2f7 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,25 @@ +# Primer Examples + +This folder includes quick examples to help you get started. + +## CLI usage + +```bash +# Interactive setup +primer init + +# AI readiness report +primer readiness /path/to/repo +primer readiness --visual + +# Generate instructions +primer instructions --repo /path/to/repo + +# Scaffold and run evals +primer eval --init --repo /path/to/repo +primer eval primer.eval.json --repo /path/to/repo +``` + +## Sample eval config + +See `primer.eval.json` for a starter eval config you can customize. diff --git a/examples/primer.eval.json b/examples/primer.eval.json new file mode 100644 index 0000000..279dbde --- /dev/null +++ b/examples/primer.eval.json @@ -0,0 +1,16 @@ +{ + "instructionFile": ".github/copilot-instructions.md", + "systemMessage": "You are answering questions about this repository. Use tools to inspect the repo and cite its files. Avoid generic Copilot CLI details unless the prompt explicitly asks for them.", + "cases": [ + { + "id": "project-overview", + "prompt": "Summarize what this project does and list the main entry points.", + "expectation": "Should mention the primary purpose and key files/directories." + }, + { + "id": "build-commands", + "prompt": "How do I build and test this project?", + "expectation": "Should provide the correct build and test commands from package.json or equivalent." + } + ] +} diff --git a/package-lock.json b/package-lock.json index 4679573..504496d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,18 +19,32 @@ "react": "^19.2.4", "simple-git": "^3.30.0" }, + "bin": { + "primer": "dist/index.js" + }, "devDependencies": { "@types/node": "^25.1.0", "@types/react": "^19.2.10", + "@typescript-eslint/eslint-plugin": "^8.5.0", + "@typescript-eslint/parser": "^8.5.0", + "@vitest/coverage-v8": "^4.0.18", + "eslint": "^9.39.2", + "eslint-config-prettier": "^9.1.2", + "eslint-plugin-import": "^2.30.0", + "eslint-plugin-n": "^17.10.2", + "eslint-plugin-promise": "^7.1.0", + "globals": "^17.3.0", + "prettier": "^3.3.3", "tsup": "^8.5.1", "tsx": "^4.21.0", - "typescript": "^5.9.3" + "typescript": "^5.9.3", + "vitest": "^4.0.18" } }, "node_modules/@alcalzone/ansi-tokenize": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@alcalzone/ansi-tokenize/-/ansi-tokenize-0.2.3.tgz", - "integrity": "sha512-jsElTJ0sQ4wHRz+C45tfect76BwbTbgkgKByOzpCN9xG61N5V6u/glvg1CsNJhq2xJIFpKHSwG3D2wPPuEYOrQ==", + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@alcalzone/ansi-tokenize/-/ansi-tokenize-0.2.5.tgz", + "integrity": "sha512-3NX/MpTdroi0aKz134A6RC2Gb2iXVECN4QaAXnvCIxxIm3C3AVB1mkUe8NaaiyvOpDfsrqWhYtj+Q6a62RrTsw==", "license": "MIT", "dependencies": { "ansi-styles": "^6.2.1", @@ -40,10 +54,70 @@ "node": ">=18" } }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", + "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", + "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz", - "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", + "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", "cpu": [ "ppc64" ], @@ -58,9 +132,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz", - "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", + "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", "cpu": [ "arm" ], @@ -75,9 +149,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz", - "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", + "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", "cpu": [ "arm64" ], @@ -92,9 +166,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz", - "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", + "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", "cpu": [ "x64" ], @@ -109,9 +183,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz", - "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", + "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", "cpu": [ "arm64" ], @@ -126,9 +200,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz", - "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", + "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", "cpu": [ "x64" ], @@ -143,9 +217,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz", - "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", + "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", "cpu": [ "arm64" ], @@ -160,9 +234,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz", - "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", + "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", "cpu": [ "x64" ], @@ -177,9 +251,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz", - "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", + "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", "cpu": [ "arm" ], @@ -194,9 +268,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz", - "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", + "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", "cpu": [ "arm64" ], @@ -211,9 +285,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz", - "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", + "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", "cpu": [ "ia32" ], @@ -228,9 +302,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz", - "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", + "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", "cpu": [ "loong64" ], @@ -245,9 +319,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz", - "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", + "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", "cpu": [ "mips64el" ], @@ -262,9 +336,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz", - "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", + "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", "cpu": [ "ppc64" ], @@ -279,9 +353,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz", - "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", + "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", "cpu": [ "riscv64" ], @@ -296,9 +370,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz", - "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", + "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", "cpu": [ "s390x" ], @@ -313,9 +387,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz", - "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", + "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", "cpu": [ "x64" ], @@ -330,9 +404,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz", - "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", + "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", "cpu": [ "arm64" ], @@ -347,9 +421,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz", - "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", + "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", "cpu": [ "x64" ], @@ -364,9 +438,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz", - "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", + "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", "cpu": [ "arm64" ], @@ -381,9 +455,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz", - "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", + "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", "cpu": [ "x64" ], @@ -398,9 +472,9 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz", - "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", + "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", "cpu": [ "arm64" ], @@ -415,9 +489,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz", - "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", + "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", "cpu": [ "x64" ], @@ -432,9 +506,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz", - "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", + "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", "cpu": [ "arm64" ], @@ -449,9 +523,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz", - "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", + "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", "cpu": [ "ia32" ], @@ -466,9 +540,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz", - "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", + "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", "cpu": [ "x64" ], @@ -482,27 +556,229 @@ "node": ">=18" } }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", + "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-array/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz", + "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.2", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz", + "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@github/copilot": { - "version": "0.0.394", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-0.0.394.tgz", - "integrity": "sha512-koSiaHvVwjgppgh+puxf6dgsR8ql/WST1scS5bjzMsJFfWk7f4xtEXla7TCQfSGoZkCmCsr2Tis27v5TpssiCg==", + "version": "0.0.403", + "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-0.0.403.tgz", + "integrity": "sha512-v5jUdtGJReLmE1rmff/LZf+50nzmYQYAaSRNtVNr9g0j0GkCd/noQExe31i1+PudvWU0ZJjltR0B8pUfDRdA9Q==", "license": "SEE LICENSE IN LICENSE.md", "bin": { "copilot": "npm-loader.js" }, "optionalDependencies": { - "@github/copilot-darwin-arm64": "0.0.394", - "@github/copilot-darwin-x64": "0.0.394", - "@github/copilot-linux-arm64": "0.0.394", - "@github/copilot-linux-x64": "0.0.394", - "@github/copilot-win32-arm64": "0.0.394", - "@github/copilot-win32-x64": "0.0.394" + "@github/copilot-darwin-arm64": "0.0.403", + "@github/copilot-darwin-x64": "0.0.403", + "@github/copilot-linux-arm64": "0.0.403", + "@github/copilot-linux-x64": "0.0.403", + "@github/copilot-win32-arm64": "0.0.403", + "@github/copilot-win32-x64": "0.0.403" } }, "node_modules/@github/copilot-darwin-arm64": { - "version": "0.0.394", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-0.0.394.tgz", - "integrity": "sha512-qDmDFiFaYFW45UhxylN2JyQRLVGLCpkr5UmgbfH5e0aksf+69qytK/MwpD2Cq12KdTjyGMEorlADkSu5eftELA==", + "version": "0.0.403", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-0.0.403.tgz", + "integrity": "sha512-dOw8IleA0d1soHnbr/6wc6vZiYWNTKMgfTe/NET1nCfMzyKDt/0F0I7PT5y+DLujJknTla/ZeEmmBUmliTW4Cg==", "cpu": [ "arm64" ], @@ -516,9 +792,9 @@ } }, "node_modules/@github/copilot-darwin-x64": { - "version": "0.0.394", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-0.0.394.tgz", - "integrity": "sha512-iN4YwSVFxhASiBjLk46f+AzRTNHCvYcmyTKBASxieMIhnDxznYmpo+haFKPCv2lCsEWU8s5LARCnXxxx8J1wKA==", + "version": "0.0.403", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-0.0.403.tgz", + "integrity": "sha512-aK2jSNWgY8eiZ+TmrvGhssMCPDTKArc0ip6Ul5OaslpytKks8hyXoRbxGD0N9sKioSUSbvKUf+1AqavbDpJO+w==", "cpu": [ "x64" ], @@ -532,9 +808,9 @@ } }, "node_modules/@github/copilot-linux-arm64": { - "version": "0.0.394", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-0.0.394.tgz", - "integrity": "sha512-9NeGvmO2tGztuneXZfYAyW3fDk6Pdl6Ffg8MAUaevA/p0awvA+ti/Vh0ZSTcI81nDTjkzONvrcIcjYAN7x0oSg==", + "version": "0.0.403", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-0.0.403.tgz", + "integrity": "sha512-KhoR2iR70O6vCkzf0h8/K+p82qAgOvMTgAPm9bVEHvbdGFR7Py9qL5v03bMbPxsA45oNaZAkzDhfTAqWhIAZsQ==", "cpu": [ "arm64" ], @@ -548,9 +824,9 @@ } }, "node_modules/@github/copilot-linux-x64": { - "version": "0.0.394", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-0.0.394.tgz", - "integrity": "sha512-toahsYQORrP/TPSBQ7sxj4/fJg3YUrD0ksCj/Z4y2vT6EwrE9iC2BspKgQRa4CBoCqxYDNB2blc+mQ1UuzPOxg==", + "version": "0.0.403", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-0.0.403.tgz", + "integrity": "sha512-eoswUc9vo4TB+/9PgFJLVtzI4dPjkpJXdCsAioVuoqPdNxHxlIHFe9HaVcqMRZxUNY1YHEBZozy+IpUEGjgdfQ==", "cpu": [ "x64" ], @@ -564,23 +840,23 @@ } }, "node_modules/@github/copilot-sdk": { - "version": "0.1.19", - "resolved": "https://registry.npmjs.org/@github/copilot-sdk/-/copilot-sdk-0.1.19.tgz", - "integrity": "sha512-h/KvYb6g99v9SurNJGxeXUatmP7GO8KHTAb68GYfmgUqH1EUeN5g0xMUc5lvKxAi7hwj2OxRR73dd37zMMiiiQ==", + "version": "0.1.22", + "resolved": "https://registry.npmjs.org/@github/copilot-sdk/-/copilot-sdk-0.1.22.tgz", + "integrity": "sha512-ZGOEBmYOfu/vLXKjjoiw4lO3Cb8QBUuAWXcW/qzmPPsM9+Qe00qVr2AuDTU/Gft9Dm/yZcPK2QuTZc7LVeom9w==", "license": "MIT", "dependencies": { - "@github/copilot": "^0.0.394", + "@github/copilot": "^0.0.403", "vscode-jsonrpc": "^8.2.1", - "zod": "^4.3.5" + "zod": "^4.3.6" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@github/copilot-win32-arm64": { - "version": "0.0.394", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-0.0.394.tgz", - "integrity": "sha512-R7XBP3l+oeDuBrP0KD80ZBEMsZoxAW8QO2MNsDUV8eVrNJnp6KtGHoA+iCsKYKNOD6wHA/q5qm/jR+gpsz46Aw==", + "version": "0.0.403", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-0.0.403.tgz", + "integrity": "sha512-djWjzCsp2xPNafMyOZ/ivU328/WvWhdroGie/DugiJBTgQL2SP0quWW1fhTlDwE81a3g9CxfJonaRgOpFTJTcg==", "cpu": [ "arm64" ], @@ -594,9 +870,9 @@ } }, "node_modules/@github/copilot-win32-x64": { - "version": "0.0.394", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-0.0.394.tgz", - "integrity": "sha512-/XYV8srP+pMXbf9Gc3wr58zCzBZvsdA3X4poSvr2uU8yCZ6E4pD0agFaZ1c/CikANJi8nb0Id3kulhEhePz/3A==", + "version": "0.0.403", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-0.0.403.tgz", + "integrity": "sha512-lju8cHy2E6Ux7R7tWyLZeksYC2MVZu9i9ocjiBX/qfG2/pNJs7S5OlkwKJ0BSXSbZEHQYq7iHfEWp201bVfk9A==", "cpu": [ "x64" ], @@ -609,6 +885,58 @@ "copilot-win32-x64": "copilot.exe" } }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, "node_modules/@inquirer/ansi": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-2.0.3.tgz", @@ -619,13 +947,13 @@ } }, "node_modules/@inquirer/checkbox": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-5.0.4.tgz", - "integrity": "sha512-DrAMU3YBGMUAp6ArwTIp/25CNDtDbxk7UjIrrtM25JVVrlVYlVzHh5HR1BDFu9JMyUoZ4ZanzeaHqNDttf3gVg==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-5.0.6.tgz", + "integrity": "sha512-qLZ1gOpsqsieB5k98GQ9bWYggvMsCXTc7HUwhEQpTsxFQYGthqR9UysCwqB7L9h47THYdXhJegnYb1IqURMjng==", "license": "MIT", "dependencies": { "@inquirer/ansi": "^2.0.3", - "@inquirer/core": "^11.1.1", + "@inquirer/core": "^11.1.3", "@inquirer/figures": "^2.0.3", "@inquirer/type": "^4.0.3" }, @@ -642,12 +970,12 @@ } }, "node_modules/@inquirer/confirm": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-6.0.4.tgz", - "integrity": "sha512-WdaPe7foUnoGYvXzH4jp4wH/3l+dBhZ3uwhKjXjwdrq5tEIFaANxj6zrGHxLdsIA0yKM0kFPVcEalOZXBB5ISA==", + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-6.0.6.tgz", + "integrity": "sha512-9ZkrGYiWnOKQPc3xfLIORE3lZW1qvtgRoJcoqopr5zssBn7yk4yONmzGynEOjc16FnUXzkAejj/I29BbfcoUfQ==", "license": "MIT", "dependencies": { - "@inquirer/core": "^11.1.1", + "@inquirer/core": "^11.1.3", "@inquirer/type": "^4.0.3" }, "engines": { @@ -663,18 +991,18 @@ } }, "node_modules/@inquirer/core": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-11.1.1.tgz", - "integrity": "sha512-hV9o15UxX46OyQAtaoMqAOxGR8RVl1aZtDx1jHbCtSJy1tBdTfKxLPKf7utsE4cRy4tcmCQ4+vdV+ca+oNxqNA==", + "version": "11.1.3", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-11.1.3.tgz", + "integrity": "sha512-TBAGPDGvpwFSQ4nkawQzq5/X7DhElANjvKeUtcjpVnBIfuH/OEu4M+79R3+bGPtwxST4DOIGRtF933mUH2bRVw==", "license": "MIT", "dependencies": { "@inquirer/ansi": "^2.0.3", "@inquirer/figures": "^2.0.3", "@inquirer/type": "^4.0.3", "cli-width": "^4.1.0", + "fast-wrap-ansi": "^0.2.0", "mute-stream": "^3.0.0", - "signal-exit": "^4.1.0", - "wrap-ansi": "^9.0.2" + "signal-exit": "^4.1.0" }, "engines": { "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" @@ -689,12 +1017,12 @@ } }, "node_modules/@inquirer/editor": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-5.0.4.tgz", - "integrity": "sha512-QI3Jfqcv6UO2/VJaEFONH8Im1ll++Xn/AJTBn9Xf+qx2M+H8KZAdQ5sAe2vtYlo+mLW+d7JaMJB4qWtK4BG3pw==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-5.0.6.tgz", + "integrity": "sha512-dxTi/TB29NaW18u0pQl3B140695izGUMzr340a4Yhxll3oa0/iwxl6C88sX9LDUPFaaM4FDASEMnLm8XVk2VVg==", "license": "MIT", "dependencies": { - "@inquirer/core": "^11.1.1", + "@inquirer/core": "^11.1.3", "@inquirer/external-editor": "^2.0.3", "@inquirer/type": "^4.0.3" }, @@ -711,12 +1039,12 @@ } }, "node_modules/@inquirer/expand": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-5.0.4.tgz", - "integrity": "sha512-0I/16YwPPP0Co7a5MsomlZLpch48NzYfToyqYAOWtBmaXSB80RiNQ1J+0xx2eG+Wfxt0nHtpEWSRr6CzNVnOGg==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-5.0.6.tgz", + "integrity": "sha512-HmgMzFdMk/gmPXfuFy4xgWkyIVbdH81otQkrFbhklFZcGauwDFD1EbgmZdgmYCN5pWhSEnYIadg1kysLgPIYag==", "license": "MIT", "dependencies": { - "@inquirer/core": "^11.1.1", + "@inquirer/core": "^11.1.3", "@inquirer/type": "^4.0.3" }, "engines": { @@ -762,12 +1090,12 @@ } }, "node_modules/@inquirer/input": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-5.0.4.tgz", - "integrity": "sha512-4B3s3jvTREDFvXWit92Yc6jF1RJMDy2VpSqKtm4We2oVU65YOh2szY5/G14h4fHlyQdpUmazU5MPCFZPRJ0AOw==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-5.0.6.tgz", + "integrity": "sha512-RZsJcjMJA3QNI9q9OiAi1fAom+Pb8on6alJB1Teh5jjKaiG5C79P69cG955ZRfgPdxTmI4uyhf33+94Xj7xWig==", "license": "MIT", "dependencies": { - "@inquirer/core": "^11.1.1", + "@inquirer/core": "^11.1.3", "@inquirer/type": "^4.0.3" }, "engines": { @@ -783,12 +1111,12 @@ } }, "node_modules/@inquirer/number": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-4.0.4.tgz", - "integrity": "sha512-CmMp9LF5HwE+G/xWsC333TlCzYYbXMkcADkKzcawh49fg2a1ryLc7JL1NJYYt1lJ+8f4slikNjJM9TEL/AljYQ==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-4.0.6.tgz", + "integrity": "sha512-owMkAY+gR0BggomDTL+Z22x/yfE4ocFrmNyJacOiaDVA/d+iL4IWyk7Ds7JEuDMxuhHFB46Dubdxg1uiD7GlCA==", "license": "MIT", "dependencies": { - "@inquirer/core": "^11.1.1", + "@inquirer/core": "^11.1.3", "@inquirer/type": "^4.0.3" }, "engines": { @@ -804,13 +1132,13 @@ } }, "node_modules/@inquirer/password": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-5.0.4.tgz", - "integrity": "sha512-ZCEPyVYvHK4W4p2Gy6sTp9nqsdHQCfiPXIP9LbJVW4yCinnxL/dDDmPaEZVysGrj8vxVReRnpfS2fOeODe9zjg==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-5.0.6.tgz", + "integrity": "sha512-c4BT4SB79iYwPhtGVBSvrlTnn4oFSYnwocafmktpay8RK75T2c2+fLlR0i1Cxw0QOhdy/YULdmpHoy1sOrPzvA==", "license": "MIT", "dependencies": { "@inquirer/ansi": "^2.0.3", - "@inquirer/core": "^11.1.1", + "@inquirer/core": "^11.1.3", "@inquirer/type": "^4.0.3" }, "engines": { @@ -826,21 +1154,21 @@ } }, "node_modules/@inquirer/prompts": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-8.2.0.tgz", - "integrity": "sha512-rqTzOprAj55a27jctS3vhvDDJzYXsr33WXTjODgVOru21NvBo9yIgLIAf7SBdSV0WERVly3dR6TWyp7ZHkvKFA==", + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-8.2.1.tgz", + "integrity": "sha512-76knJFW2oXdI6If5YRmEoT5u7l+QroXYrMiINFcb97LsyECgsbO9m6iWlPuhBtaFgNITPHQCk3wbex38q8gsjg==", "license": "MIT", "dependencies": { - "@inquirer/checkbox": "^5.0.4", - "@inquirer/confirm": "^6.0.4", - "@inquirer/editor": "^5.0.4", - "@inquirer/expand": "^5.0.4", - "@inquirer/input": "^5.0.4", - "@inquirer/number": "^4.0.4", - "@inquirer/password": "^5.0.4", - "@inquirer/rawlist": "^5.2.0", - "@inquirer/search": "^4.1.0", - "@inquirer/select": "^5.0.4" + "@inquirer/checkbox": "^5.0.5", + "@inquirer/confirm": "^6.0.5", + "@inquirer/editor": "^5.0.5", + "@inquirer/expand": "^5.0.5", + "@inquirer/input": "^5.0.5", + "@inquirer/number": "^4.0.5", + "@inquirer/password": "^5.0.5", + "@inquirer/rawlist": "^5.2.1", + "@inquirer/search": "^4.1.1", + "@inquirer/select": "^5.0.5" }, "engines": { "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" @@ -855,12 +1183,12 @@ } }, "node_modules/@inquirer/rawlist": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-5.2.0.tgz", - "integrity": "sha512-CciqGoOUMrFo6HxvOtU5uL8fkjCmzyeB6fG7O1vdVAZVSopUBYECOwevDBlqNLyyYmzpm2Gsn/7nLrpruy9RFg==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-5.2.2.tgz", + "integrity": "sha512-ld2EhLlf3fsBv7QfxR31NdBecGdS6eeFFZ+Nx88ApjtifeCEc9TNrw8x5tGe+gd6HG1ERczOb4B/bMojiGIp1g==", "license": "MIT", "dependencies": { - "@inquirer/core": "^11.1.1", + "@inquirer/core": "^11.1.3", "@inquirer/type": "^4.0.3" }, "engines": { @@ -876,12 +1204,12 @@ } }, "node_modules/@inquirer/search": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-4.1.0.tgz", - "integrity": "sha512-EAzemfiP4IFvIuWnrHpgZs9lAhWDA0GM3l9F4t4mTQ22IFtzfrk8xbkMLcAN7gmVML9O/i+Hzu8yOUyAaL6BKA==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-4.1.2.tgz", + "integrity": "sha512-kdGbbbWYKldWxpxodKYPmFl/ctBi3DjWlA4LX48jXtqJ7NEeoEKlyFTbE4xNEFcGDi15tvaxRLzCV4A53zqYIw==", "license": "MIT", "dependencies": { - "@inquirer/core": "^11.1.1", + "@inquirer/core": "^11.1.3", "@inquirer/figures": "^2.0.3", "@inquirer/type": "^4.0.3" }, @@ -898,13 +1226,13 @@ } }, "node_modules/@inquirer/select": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-5.0.4.tgz", - "integrity": "sha512-s8KoGpPYMEQ6WXc0dT9blX2NtIulMdLOO3LA1UKOiv7KFWzlJ6eLkEYTDBIi+JkyKXyn8t/CD6TinxGjyLt57g==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-5.0.6.tgz", + "integrity": "sha512-9DyVbNCo4q0C3CkGd6zW0SW3NQuuk4Hy0NSbP6zErz2YNWF4EHHJCRzcV34/CDQLraeAQXbHYlMofuUrs6BBZQ==", "license": "MIT", "dependencies": { "@inquirer/ansi": "^2.0.3", - "@inquirer/core": "^11.1.1", + "@inquirer/core": "^11.1.3", "@inquirer/figures": "^2.0.3", "@inquirer/type": "^4.0.3" }, @@ -1181,9 +1509,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.57.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.57.0.tgz", - "integrity": "sha512-tPgXB6cDTndIe1ah7u6amCI1T0SsnlOuKgg10Xh3uizJk4e5M1JGaUMk7J4ciuAUcFpbOiNhm2XIjP9ON0dUqA==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.57.1.tgz", + "integrity": "sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==", "cpu": [ "arm" ], @@ -1195,9 +1523,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.57.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.57.0.tgz", - "integrity": "sha512-sa4LyseLLXr1onr97StkU1Nb7fWcg6niokTwEVNOO7awaKaoRObQ54+V/hrF/BP1noMEaaAW6Fg2d/CfLiq3Mg==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.57.1.tgz", + "integrity": "sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w==", "cpu": [ "arm64" ], @@ -1209,9 +1537,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.57.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.57.0.tgz", - "integrity": "sha512-/NNIj9A7yLjKdmkx5dC2XQ9DmjIECpGpwHoGmA5E1AhU0fuICSqSWScPhN1yLCkEdkCwJIDu2xIeLPs60MNIVg==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.57.1.tgz", + "integrity": "sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg==", "cpu": [ "arm64" ], @@ -1223,9 +1551,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.57.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.57.0.tgz", - "integrity": "sha512-xoh8abqgPrPYPr7pTYipqnUi1V3em56JzE/HgDgitTqZBZ3yKCWI+7KUkceM6tNweyUKYru1UMi7FC060RyKwA==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.57.1.tgz", + "integrity": "sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w==", "cpu": [ "x64" ], @@ -1237,9 +1565,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.57.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.57.0.tgz", - "integrity": "sha512-PCkMh7fNahWSbA0OTUQ2OpYHpjZZr0hPr8lId8twD7a7SeWrvT3xJVyza+dQwXSSq4yEQTMoXgNOfMCsn8584g==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.57.1.tgz", + "integrity": "sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug==", "cpu": [ "arm64" ], @@ -1251,9 +1579,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.57.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.57.0.tgz", - "integrity": "sha512-1j3stGx+qbhXql4OCDZhnK7b01s6rBKNybfsX+TNrEe9JNq4DLi1yGiR1xW+nL+FNVvI4D02PUnl6gJ/2y6WJA==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.57.1.tgz", + "integrity": "sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q==", "cpu": [ "x64" ], @@ -1265,9 +1593,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.57.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.57.0.tgz", - "integrity": "sha512-eyrr5W08Ms9uM0mLcKfM/Uzx7hjhz2bcjv8P2uynfj0yU8GGPdz8iYrBPhiLOZqahoAMB8ZiolRZPbbU2MAi6Q==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.57.1.tgz", + "integrity": "sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==", "cpu": [ "arm" ], @@ -1279,9 +1607,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.57.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.57.0.tgz", - "integrity": "sha512-Xds90ITXJCNyX9pDhqf85MKWUI4lqjiPAipJ8OLp8xqI2Ehk+TCVhF9rvOoN8xTbcafow3QOThkNnrM33uCFQA==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.57.1.tgz", + "integrity": "sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==", "cpu": [ "arm" ], @@ -1293,9 +1621,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.57.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.57.0.tgz", - "integrity": "sha512-Xws2KA4CLvZmXjy46SQaXSejuKPhwVdaNinldoYfqruZBaJHqVo6hnRa8SDo9z7PBW5x84SH64+izmldCgbezw==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.57.1.tgz", + "integrity": "sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==", "cpu": [ "arm64" ], @@ -1307,9 +1635,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.57.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.57.0.tgz", - "integrity": "sha512-hrKXKbX5FdaRJj7lTMusmvKbhMJSGWJ+w++4KmjiDhpTgNlhYobMvKfDoIWecy4O60K6yA4SnztGuNTQF+Lplw==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.57.1.tgz", + "integrity": "sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==", "cpu": [ "arm64" ], @@ -1321,9 +1649,9 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.57.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.57.0.tgz", - "integrity": "sha512-6A+nccfSDGKsPm00d3xKcrsBcbqzCTAukjwWK6rbuAnB2bHaL3r9720HBVZ/no7+FhZLz/U3GwwZZEh6tOSI8Q==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.57.1.tgz", + "integrity": "sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==", "cpu": [ "loong64" ], @@ -1335,9 +1663,9 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.57.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.57.0.tgz", - "integrity": "sha512-4P1VyYUe6XAJtQH1Hh99THxr0GKMMwIXsRNOceLrJnaHTDgk1FTcTimDgneRJPvB3LqDQxUmroBclQ1S0cIJwQ==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.57.1.tgz", + "integrity": "sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==", "cpu": [ "loong64" ], @@ -1349,9 +1677,9 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.57.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.57.0.tgz", - "integrity": "sha512-8Vv6pLuIZCMcgXre6c3nOPhE0gjz1+nZP6T+hwWjr7sVH8k0jRkH+XnfjjOTglyMBdSKBPPz54/y1gToSKwrSQ==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.57.1.tgz", + "integrity": "sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==", "cpu": [ "ppc64" ], @@ -1363,9 +1691,9 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.57.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.57.0.tgz", - "integrity": "sha512-r1te1M0Sm2TBVD/RxBPC6RZVwNqUTwJTA7w+C/IW5v9Ssu6xmxWEi+iJQlpBhtUiT1raJ5b48pI8tBvEjEFnFA==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.57.1.tgz", + "integrity": "sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==", "cpu": [ "ppc64" ], @@ -1377,9 +1705,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.57.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.57.0.tgz", - "integrity": "sha512-say0uMU/RaPm3CDQLxUUTF2oNWL8ysvHkAjcCzV2znxBr23kFfaxocS9qJm+NdkRhF8wtdEEAJuYcLPhSPbjuQ==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.57.1.tgz", + "integrity": "sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==", "cpu": [ "riscv64" ], @@ -1391,9 +1719,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.57.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.57.0.tgz", - "integrity": "sha512-/MU7/HizQGsnBREtRpcSbSV1zfkoxSTR7wLsRmBPQ8FwUj5sykrP1MyJTvsxP5KBq9SyE6kH8UQQQwa0ASeoQQ==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.57.1.tgz", + "integrity": "sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==", "cpu": [ "riscv64" ], @@ -1405,9 +1733,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.57.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.57.0.tgz", - "integrity": "sha512-Q9eh+gUGILIHEaJf66aF6a414jQbDnn29zeu0eX3dHMuysnhTvsUvZTCAyZ6tJhUjnvzBKE4FtuaYxutxRZpOg==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.57.1.tgz", + "integrity": "sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==", "cpu": [ "s390x" ], @@ -1419,9 +1747,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.57.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.0.tgz", - "integrity": "sha512-OR5p5yG5OKSxHReWmwvM0P+VTPMwoBS45PXTMYaskKQqybkS3Kmugq1W+YbNWArF8/s7jQScgzXUhArzEQ7x0A==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.1.tgz", + "integrity": "sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==", "cpu": [ "x64" ], @@ -1433,9 +1761,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.57.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.57.0.tgz", - "integrity": "sha512-XeatKzo4lHDsVEbm1XDHZlhYZZSQYym6dg2X/Ko0kSFgio+KXLsxwJQprnR48GvdIKDOpqWqssC3iBCjoMcMpw==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.57.1.tgz", + "integrity": "sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==", "cpu": [ "x64" ], @@ -1447,9 +1775,9 @@ ] }, "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.57.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.57.0.tgz", - "integrity": "sha512-Lu71y78F5qOfYmubYLHPcJm74GZLU6UJ4THkf/a1K7Tz2ycwC2VUbsqbJAXaR6Bx70SRdlVrt2+n5l7F0agTUw==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.57.1.tgz", + "integrity": "sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==", "cpu": [ "x64" ], @@ -1461,9 +1789,9 @@ ] }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.57.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.57.0.tgz", - "integrity": "sha512-v5xwKDWcu7qhAEcsUubiav7r+48Uk/ENWdr82MBZZRIm7zThSxCIVDfb3ZeRRq9yqk+oIzMdDo6fCcA5DHfMyA==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.57.1.tgz", + "integrity": "sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ==", "cpu": [ "arm64" ], @@ -1475,9 +1803,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.57.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.57.0.tgz", - "integrity": "sha512-XnaaaSMGSI6Wk8F4KK3QP7GfuuhjGchElsVerCplUuxRIzdvZ7hRBpLR0omCmw+kI2RFJB80nenhOoGXlJ5TfQ==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.57.1.tgz", + "integrity": "sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ==", "cpu": [ "arm64" ], @@ -1489,9 +1817,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.57.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.57.0.tgz", - "integrity": "sha512-3K1lP+3BXY4t4VihLw5MEg6IZD3ojSYzqzBG571W3kNQe4G4CcFpSUQVgurYgib5d+YaCjeFow8QivWp8vuSvA==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.57.1.tgz", + "integrity": "sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew==", "cpu": [ "ia32" ], @@ -1503,9 +1831,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.57.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.57.0.tgz", - "integrity": "sha512-MDk610P/vJGc5L5ImE4k5s+GZT3en0KoK1MKPXCRgzmksAMk79j4h3k1IerxTNqwDLxsGxStEZVBqG0gIqZqoA==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.57.1.tgz", + "integrity": "sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ==", "cpu": [ "x64" ], @@ -1517,9 +1845,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.57.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.57.0.tgz", - "integrity": "sha512-Zv7v6q6aV+VslnpwzqKAmrk5JdVkLUzok2208ZXGipjb+msxBr/fJPZyeEXiFgH7k62Ak0SLIfxQRZQvTuf7rQ==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.57.1.tgz", + "integrity": "sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA==", "cpu": [ "x64" ], @@ -1530,6 +1858,38 @@ "win32" ] }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -1537,10 +1897,24 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/node": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.1.0.tgz", - "integrity": "sha512-t7frlewr6+cbx+9Ohpl0NOTKXZNV9xHRmNOvql47BFJKcEG1CxtxlPEEe+gR9uhVWM4DwhnvTF110mIL4yP9RA==", + "version": "25.2.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.3.tgz", + "integrity": "sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ==", "devOptional": true, "license": "MIT", "dependencies": { @@ -1548,314 +1922,438 @@ } }, "node_modules/@types/react": { - "version": "19.2.10", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.10.tgz", - "integrity": "sha512-WPigyYuGhgZ/cTPRXB2EwUw+XvsRA3GqHlsP4qteqrnnjDrApbS7MxcGr/hke5iUoeB7E/gQtrs9I37zAJ0Vjw==", + "version": "19.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", "devOptional": true, "license": "MIT", "dependencies": { "csstype": "^3.2.2" } }, - "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.55.0.tgz", + "integrity": "sha512-1y/MVSz0NglV1ijHC8OT49mPJ4qhPYjiK08YUQVbIOyu+5k862LKUHFkpKHWu//zmr7hDR2rhwUm6gnCGNmGBQ==", "dev": true, "license": "MIT", - "bin": { - "acorn": "bin/acorn" + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.55.0", + "@typescript-eslint/type-utils": "8.55.0", + "@typescript-eslint/utils": "8.55.0", + "@typescript-eslint/visitor-keys": "8.55.0", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.4.0" }, "engines": { - "node": ">=0.4.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.55.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/ansi-escapes": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.2.0.tgz", - "integrity": "sha512-g6LhBsl+GBPRWGWsBtutpzBYuIIdBkLEvad5C/va/74Db018+5TZiyA26cZJAr3Rft5lprVqOIPxf5Vid6tqAw==", + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/type-utils": { + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.55.0.tgz", + "integrity": "sha512-x1iH2unH4qAt6I37I2CGlsNs+B9WGxurP2uyZLRz6UJoZWDBx9cJL1xVN/FiOmHEONEg6RIufdvyT0TEYIgC5g==", + "dev": true, "license": "MIT", "dependencies": { - "environment": "^1.0.0" + "@typescript-eslint/types": "8.55.0", + "@typescript-eslint/typescript-estree": "8.55.0", + "@typescript-eslint/utils": "8.55.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.4.0" }, "engines": { - "node": ">=18" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": { + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.55.0.tgz", + "integrity": "sha512-BqZEsnPGdYpgyEIkDC1BadNY8oMwckftxBT+C8W0g1iKPdeqKZBtTfnvcq0nf60u7MkjFO8RBvpRGZBPw4L2ow==", + "dev": true, "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.55.0", + "@typescript-eslint/types": "8.55.0", + "@typescript-eslint/typescript-estree": "8.55.0" + }, "engines": { - "node": ">=12" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "node_modules/@typescript-eslint/parser": { + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.55.0.tgz", + "integrity": "sha512-4z2nCSBfVIMnbuu8uinj+f0o4qOeggYJLbjpPHka3KH1om7e+H9yLKTYgksTaHcGco+NClhhY2vyO3HsMH1RGw==", + "dev": true, "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.55.0", + "@typescript-eslint/types": "8.55.0", + "@typescript-eslint/typescript-estree": "8.55.0", + "@typescript-eslint/visitor-keys": "8.55.0", + "debug": "^4.4.3" + }, "engines": { - "node": ">=12" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "node_modules/@typescript-eslint/project-service": { + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.55.0.tgz", + "integrity": "sha512-zRcVVPFUYWa3kNnjaZGXSu3xkKV1zXy8M4nO/pElzQhFweb7PPtluDLQtKArEOGmjXoRjnUZ29NjOiF0eCDkcQ==", "dev": true, - "license": "MIT" - }, - "node_modules/auto-bind": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/auto-bind/-/auto-bind-5.0.1.tgz", - "integrity": "sha512-ooviqdwwgfIfNmDwo94wlshcdzfO64XV0Cg6oDsDYBJfITDz1EngD2z7DkbvCWn+XIMsIqW27sEVF6qcpJrRcg==", "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.55.0", + "@typescript-eslint/types": "^8.55.0", + "debug": "^4.4.3" + }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/before-after-hook": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-4.0.0.tgz", - "integrity": "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==", - "license": "Apache-2.0" - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.55.0.tgz", + "integrity": "sha512-fVu5Omrd3jeqeQLiB9f1YsuK/iHFOwb04bCtY4BSCLgjNbOD33ZdV6KyEqplHr+IlpgT0QTZ/iJ+wT7hvTx49Q==", + "dev": true, "license": "MIT", "dependencies": { - "fill-range": "^7.1.1" + "@typescript-eslint/types": "8.55.0", + "@typescript-eslint/visitor-keys": "8.55.0" }, "engines": { - "node": ">=8" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/bundle-require": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/bundle-require/-/bundle-require-5.1.0.tgz", - "integrity": "sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==", + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.55.0.tgz", + "integrity": "sha512-1R9cXqY7RQd7WuqSN47PK9EDpgFUK3VqdmbYrvWJZYDd0cavROGn+74ktWBlmJ13NXUQKlZ/iAEQHI/V0kKe0Q==", "dev": true, "license": "MIT", - "dependencies": { - "load-tsconfig": "^0.2.3" - }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "esbuild": ">=0.18" + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/cac": { - "version": "6.7.14", - "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", - "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "node_modules/@typescript-eslint/types": { + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.55.0.tgz", + "integrity": "sha512-ujT0Je8GI5BJWi+/mMoR0wxwVEQaxM+pi30xuMiJETlX80OPovb2p9E8ss87gnSVtYXtJoU9U1Cowcr6w2FE0w==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/chalk": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", - "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.55.0.tgz", + "integrity": "sha512-EwrH67bSWdx/3aRQhCoxDaHM+CrZjotc2UCCpEDVqfCE+7OjKAGWNY2HsCSTEVvWH2clYQK8pdeLp42EVs+xQw==", + "dev": true, "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.55.0", + "@typescript-eslint/tsconfig-utils": "8.55.0", + "@typescript-eslint/types": "8.55.0", + "@typescript-eslint/visitor-keys": "8.55.0", + "debug": "^4.4.3", + "minimatch": "^9.0.5", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.4.0" + }, "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/chardet": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.1.tgz", - "integrity": "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==", - "license": "MIT" - }, - "node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.55.0.tgz", + "integrity": "sha512-AxNRwEie8Nn4eFS1FzDMJWIISMGoXMb037sgCBJ3UR6o0fQTzr2tqN9WT+DkWJPhIdQCfV7T6D387566VtnCJA==", "dev": true, "license": "MIT", "dependencies": { - "readdirp": "^4.0.1" + "@typescript-eslint/types": "8.55.0", + "eslint-visitor-keys": "^4.2.1" }, "engines": { - "node": ">= 14.16.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://paulmillr.com/funding/" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/cli-boxes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", - "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", - "license": "MIT", + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">=10" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://opencollective.com/eslint" } }, - "node_modules/cli-cursor": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", - "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", + "node_modules/@vitest/coverage-v8": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.0.18.tgz", + "integrity": "sha512-7i+N2i0+ME+2JFZhfuz7Tg/FqKtilHjGyGvoHYQ6iLV0zahbsJ9sljC9OcFcPDbhYKCet+sG8SsVqlyGvPflZg==", + "dev": true, "license": "MIT", "dependencies": { - "restore-cursor": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "@bcoe/v8-coverage": "^1.0.2", + "@vitest/utils": "4.0.18", + "ast-v8-to-istanbul": "^0.3.10", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.2.0", + "magicast": "^0.5.1", + "obug": "^2.1.1", + "std-env": "^3.10.0", + "tinyrainbow": "^3.0.3" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@vitest/browser": "4.0.18", + "vitest": "4.0.18" + }, + "peerDependenciesMeta": { + "@vitest/browser": { + "optional": true + } } }, - "node_modules/cli-truncate": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-5.1.1.tgz", - "integrity": "sha512-SroPvNHxUnk+vIW/dOSfNqdy1sPEFkrTk6TUtqLCnBlo3N7TNYYkzzN7uSD6+jVjrdO4+p8nH7JzH6cIvUem6A==", + "node_modules/@vitest/expect": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.18.tgz", + "integrity": "sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==", + "dev": true, "license": "MIT", "dependencies": { - "slice-ansi": "^7.1.0", - "string-width": "^8.0.0" - }, - "engines": { - "node": ">=20" + "@standard-schema/spec": "^1.0.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.0.18", + "@vitest/utils": "4.0.18", + "chai": "^6.2.1", + "tinyrainbow": "^3.0.3" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://opencollective.com/vitest" } }, - "node_modules/cli-truncate/node_modules/string-width": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.1.0.tgz", - "integrity": "sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg==", + "node_modules/@vitest/mocker": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.0.18.tgz", + "integrity": "sha512-HhVd0MDnzzsgevnOWCBj5Otnzobjy5wLBe4EdeeFGv8luMsGcYqDuFRMcttKWZA5vVO8RFjexVovXvAM4JoJDQ==", + "dev": true, "license": "MIT", "dependencies": { - "get-east-asian-width": "^1.3.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=20" + "@vitest/spy": "4.0.18", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } } }, - "node_modules/cli-width": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", - "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", - "license": "ISC", - "engines": { - "node": ">= 12" + "node_modules/@vitest/pretty-format": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.18.tgz", + "integrity": "sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/code-excerpt": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/code-excerpt/-/code-excerpt-4.0.0.tgz", - "integrity": "sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA==", + "node_modules/@vitest/runner": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.0.18.tgz", + "integrity": "sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw==", + "dev": true, "license": "MIT", "dependencies": { - "convert-to-spaces": "^2.0.1" + "@vitest/utils": "4.0.18", + "pathe": "^2.0.3" }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/commander": { - "version": "14.0.2", - "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz", - "integrity": "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==", + "node_modules/@vitest/snapshot": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.18.tgz", + "integrity": "sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==", + "dev": true, "license": "MIT", - "engines": { - "node": ">=20" + "dependencies": { + "@vitest/pretty-format": "4.0.18", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/confbox": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", - "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "node_modules/@vitest/spy": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.18.tgz", + "integrity": "sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw==", "dev": true, - "license": "MIT" + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } }, - "node_modules/consola": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", - "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "node_modules/@vitest/utils": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.0.18.tgz", + "integrity": "sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==", "dev": true, "license": "MIT", - "engines": { - "node": "^14.18.0 || >=16.10.0" + "dependencies": { + "@vitest/pretty-format": "4.0.18", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/convert-to-spaces": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/convert-to-spaces/-/convert-to-spaces-2.0.1.tgz", - "integrity": "sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ==", + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=0.4.0" } }, - "node_modules/csstype": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", - "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "devOptional": true, - "license": "MIT" + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, "license": "MIT", "dependencies": { - "ms": "^2.1.3" + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/emoji-regex": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", - "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", - "license": "MIT" - }, - "node_modules/environment": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", - "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "node_modules/ansi-escapes": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.3.0.tgz", + "integrity": "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==", "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, "engines": { "node": ">=18" }, @@ -1863,1172 +2361,4925 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/es-toolkit": { - "version": "1.44.0", - "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.44.0.tgz", - "integrity": "sha512-6penXeZalaV88MM3cGkFZZfOoLGWshWWfdy0tWw/RlVVyhvMaWSBTOvXNeiW3e5FwdS5ePW0LGEu17zT139ktg==", - "license": "MIT", - "workspaces": [ - "docs", - "benchmarks" - ] - }, - "node_modules/esbuild": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz", - "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==", - "dev": true, - "hasInstallScript": true, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, "engines": { - "node": ">=18" + "node": ">=12" }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.2", - "@esbuild/android-arm": "0.27.2", - "@esbuild/android-arm64": "0.27.2", - "@esbuild/android-x64": "0.27.2", - "@esbuild/darwin-arm64": "0.27.2", - "@esbuild/darwin-x64": "0.27.2", - "@esbuild/freebsd-arm64": "0.27.2", - "@esbuild/freebsd-x64": "0.27.2", - "@esbuild/linux-arm": "0.27.2", - "@esbuild/linux-arm64": "0.27.2", - "@esbuild/linux-ia32": "0.27.2", - "@esbuild/linux-loong64": "0.27.2", - "@esbuild/linux-mips64el": "0.27.2", - "@esbuild/linux-ppc64": "0.27.2", - "@esbuild/linux-riscv64": "0.27.2", - "@esbuild/linux-s390x": "0.27.2", - "@esbuild/linux-x64": "0.27.2", - "@esbuild/netbsd-arm64": "0.27.2", - "@esbuild/netbsd-x64": "0.27.2", - "@esbuild/openbsd-arm64": "0.27.2", - "@esbuild/openbsd-x64": "0.27.2", - "@esbuild/openharmony-arm64": "0.27.2", - "@esbuild/sunos-x64": "0.27.2", - "@esbuild/win32-arm64": "0.27.2", - "@esbuild/win32-ia32": "0.27.2", - "@esbuild/win32-x64": "0.27.2" + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/fast-content-type-parse": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-3.0.0.tgz", - "integrity": "sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, "license": "MIT" }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, "license": "MIT", "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" }, "engines": { - "node": ">=8.6.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/fastq": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", - "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", - "license": "ISC", + "node_modules/array-includes": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", + "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", + "dev": true, + "license": "MIT", "dependencies": { - "reusify": "^1.0.4" + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.0", + "es-object-atoms": "^1.1.1", + "get-intrinsic": "^1.3.0", + "is-string": "^1.1.1", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "node_modules/array.prototype.findlastindex": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", + "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", "dev": true, "license": "MIT", - "engines": { - "node": ">=12.0.0" + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-shim-unscopables": "^1.1.0" }, - "peerDependencies": { - "picomatch": "^3 || ^4" + "engines": { + "node": ">= 0.4" }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "node_modules/array.prototype.flat": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", + "dev": true, "license": "MIT", "dependencies": { - "to-regex-range": "^5.0.1" + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" }, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/fix-dts-default-cjs-exports": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/fix-dts-default-cjs-exports/-/fix-dts-default-cjs-exports-1.0.1.tgz", - "integrity": "sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==", + "node_modules/array.prototype.flatmap": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", "dev": true, "license": "MIT", "dependencies": { - "magic-string": "^0.30.17", - "mlly": "^1.7.4", - "rollup": "^4.34.8" + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", "dev": true, - "hasInstallScript": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/get-east-asian-width": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz", - "integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==", + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, "license": "MIT", "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=12" } }, - "node_modules/get-tsconfig": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz", - "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==", + "node_modules/ast-v8-to-istanbul": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.11.tgz", + "integrity": "sha512-Qya9fkoofMjCBNVdWINMjB5KZvkYfaO9/anwkWnjxibpWUxo5iHl2sOdP7/uAqaRuUYuoo8rDwnbaaKVFxoUvw==", "dev": true, "license": "MIT", "dependencies": { - "resolve-pkg-maps": "^1.0.0" - }, - "funding": { - "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + "@jridgewell/trace-mapping": "^0.3.31", + "estree-walker": "^3.0.3", + "js-tokens": "^10.0.0" } }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", "engines": { - "node": ">= 6" + "node": ">= 0.4" } }, - "node_modules/iconv-lite": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", - "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "node_modules/auto-bind": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/auto-bind/-/auto-bind-5.0.1.tgz", + "integrity": "sha512-ooviqdwwgfIfNmDwo94wlshcdzfO64XV0Cg6oDsDYBJfITDz1EngD2z7DkbvCWn+XIMsIqW27sEVF6qcpJrRcg==", "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, "engines": { - "node": ">=0.10.0" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/indent-string": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", - "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, "engines": { - "node": ">=12" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/ink": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/ink/-/ink-6.6.0.tgz", - "integrity": "sha512-QDt6FgJxgmSxAelcOvOHUvFxbIUjVpCH5bx+Slvc5m7IEcpGt3dYwbz/L+oRnqEGeRvwy1tineKK4ect3nW1vQ==", + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/before-after-hook": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-4.0.0.tgz", + "integrity": "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==", + "license": "Apache-2.0" + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, "license": "MIT", "dependencies": { - "@alcalzone/ansi-tokenize": "^0.2.1", - "ansi-escapes": "^7.2.0", - "ansi-styles": "^6.2.1", - "auto-bind": "^5.0.1", - "chalk": "^5.6.0", - "cli-boxes": "^3.0.0", - "cli-cursor": "^4.0.0", - "cli-truncate": "^5.1.1", - "code-excerpt": "^4.0.0", - "es-toolkit": "^1.39.10", - "indent-string": "^5.0.0", - "is-in-ci": "^2.0.0", - "patch-console": "^2.0.0", - "react-reconciler": "^0.33.0", - "signal-exit": "^3.0.7", - "slice-ansi": "^7.1.0", - "stack-utils": "^2.0.6", - "string-width": "^8.1.0", - "type-fest": "^4.27.0", - "widest-line": "^5.0.0", - "wrap-ansi": "^9.0.0", - "ws": "^8.18.0", - "yoga-layout": "~3.2.1" + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" }, "engines": { - "node": ">=20" - }, - "peerDependencies": { - "@types/react": ">=19.0.0", - "react": ">=19.0.0", - "react-devtools-core": "^6.1.2" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "react-devtools-core": { - "optional": true - } + "node": ">=8" } }, - "node_modules/ink/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "license": "ISC" - }, - "node_modules/ink/node_modules/string-width": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.1.0.tgz", - "integrity": "sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg==", + "node_modules/bundle-require": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/bundle-require/-/bundle-require-5.1.0.tgz", + "integrity": "sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==", + "dev": true, "license": "MIT", "dependencies": { - "get-east-asian-width": "^1.3.0", - "strip-ansi": "^7.1.0" + "load-tsconfig": "^0.2.3" }, "engines": { - "node": ">=20" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "esbuild": ">=0.18" } }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/is-fullwidth-code-point": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", - "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==", + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, "license": "MIT", "dependencies": { - "get-east-asian-width": "^1.3.1" + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" }, "engines": { - "node": ">=18" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, "license": "MIT", "dependencies": { - "is-extglob": "^2.1.1" + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" } }, - "node_modules/is-in-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-in-ci/-/is-in-ci-2.0.0.tgz", - "integrity": "sha512-cFeerHriAnhrQSbpAxL37W1wcJKUUX07HyLWZCW1URJT/ra3GyUTzBgUnh24TMVfNTV2Hij2HLxkPHFZfOZy5w==", + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, "license": "MIT", - "bin": { - "is-in-ci": "cli.js" + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" }, "engines": { - "node": ">=20" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, "license": "MIT", "engines": { - "node": ">=0.12.0" + "node": ">=6" } }, - "node_modules/joycon": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", - "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", "dev": true, "license": "MIT", "engines": { - "node": ">=10" + "node": ">=18" } }, - "node_modules/lilconfig": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", - "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", - "dev": true, + "node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", "license": "MIT", "engines": { - "node": ">=14" + "node": "^12.17.0 || ^14.13 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/antonk52" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true, + "node_modules/chardet": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.1.tgz", + "integrity": "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==", "license": "MIT" }, - "node_modules/load-tsconfig": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz", - "integrity": "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" } }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "node_modules/cli-boxes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", + "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", "license": "MIT", "engines": { - "node": ">= 8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "node_modules/cli-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", + "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", "license": "MIT", "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" + "restore-cursor": "^4.0.0" }, "engines": { - "node": ">=8.6" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/micromatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "node_modules/cli-truncate": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-5.1.1.tgz", + "integrity": "sha512-SroPvNHxUnk+vIW/dOSfNqdy1sPEFkrTk6TUtqLCnBlo3N7TNYYkzzN7uSD6+jVjrdO4+p8nH7JzH6cIvUem6A==", "license": "MIT", + "dependencies": { + "slice-ansi": "^7.1.0", + "string-width": "^8.0.0" + }, "engines": { - "node": ">=8.6" + "node": ">=20" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "license": "ISC", + "engines": { + "node": ">= 12" + } + }, + "node_modules/code-excerpt": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/code-excerpt/-/code-excerpt-4.0.0.tgz", + "integrity": "sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA==", "license": "MIT", + "dependencies": { + "convert-to-spaces": "^2.0.1" + }, "engines": { - "node": ">=6" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, - "node_modules/mlly": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.0.tgz", - "integrity": "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==", + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "license": "MIT", "dependencies": { - "acorn": "^8.15.0", - "pathe": "^2.0.3", - "pkg-types": "^1.3.1", - "ufo": "^1.6.1" + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, "license": "MIT" }, - "node_modules/mute-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-3.0.0.tgz", - "integrity": "sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==", - "license": "ISC", + "node_modules/commander": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", + "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", + "license": "MIT", "engines": { - "node": "^20.17.0 || >=22.9.0" + "node": ">=20" } }, - "node_modules/mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true, - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" - } + "license": "MIT" }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", "dev": true, "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": "^14.18.0 || >=16.10.0" } }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "node_modules/convert-to-spaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/convert-to-spaces/-/convert-to-spaces-2.0.1.tgz", + "integrity": "sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ==", "license": "MIT", - "dependencies": { - "mimic-fn": "^2.1.0" - }, "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, - "node_modules/patch-console": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/patch-console/-/patch-console-2.0.0.tgz", - "integrity": "sha512-0YNdUceMdaQwoKce1gatDScmMo5pu/tfABfnzEqeG0gtTmd7mh/WcwgUjtAeOU7N8nFFlbQBnFK2gXW5fGvmMA==", + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">= 8" } }, - "node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "dev": true, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "devOptional": true, "license": "MIT" }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", "dev": true, "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, "engines": { - "node": ">=12" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/pirates": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", - "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", "dev": true, "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, "engines": { - "node": ">= 6" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" } }, - "node_modules/pkg-types": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", - "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", "dev": true, "license": "MIT", "dependencies": { - "confbox": "^0.1.8", - "mlly": "^1.7.4", - "pathe": "^2.0.1" + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/postcss-load-config": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", - "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", "dependencies": { - "lilconfig": "^3.1.1" + "ms": "^2.1.3" }, "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "jiti": ">=1.21.0", - "postcss": ">=8.0.9", - "tsx": "^4.8.1", - "yaml": "^2.4.2" + "node": ">=6.0" }, "peerDependenciesMeta": { - "jiti": { - "optional": true - }, - "postcss": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { + "supports-color": { "optional": true } } }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, "license": "MIT" }, - "node_modules/react": { - "version": "19.2.4", - "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", - "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-reconciler": { - "version": "0.33.0", - "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.33.0.tgz", - "integrity": "sha512-KetWRytFv1epdpJc3J4G75I4WrplZE5jOL7Yq0p34+OVOKF4Se7WrdIdVC45XsSSmUTlht2FM/fM1FZb1mfQeA==", + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, "license": "MIT", "dependencies": { - "scheduler": "^0.27.0" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" }, - "peerDependencies": { - "react": "^19.2.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "dev": true, "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, "engines": { - "node": ">= 14.18.0" + "node": ">= 0.4" }, "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/resolve-pkg-maps": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", "dev": true, "license": "MIT", - "funding": { - "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" } }, - "node_modules/restore-cursor": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", - "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", + "node_modules/enhanced-resolve": { + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.19.0.tgz", + "integrity": "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==", + "dev": true, "license": "MIT", "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" + "graceful-fs": "^4.2.4", + "tapable": "^2.3.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=10.13.0" } }, - "node_modules/restore-cursor/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "license": "ISC" - }, - "node_modules/reusify": { + "node_modules/environment": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", "license": "MIT", "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/rollup": { - "version": "4.57.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.57.0.tgz", - "integrity": "sha512-e5lPJi/aui4TO1LpAXIRLySmwXSE8k3b9zoGfd42p67wzxog4WHjiZF3M2uheQih4DGyc25QEV4yRBbpueNiUA==", + "node_modules/es-abstract": { + "version": "1.24.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz", + "integrity": "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "1.0.8" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" + }, + "engines": { + "node": ">= 0.4" }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.57.0", - "@rollup/rollup-android-arm64": "4.57.0", - "@rollup/rollup-darwin-arm64": "4.57.0", - "@rollup/rollup-darwin-x64": "4.57.0", - "@rollup/rollup-freebsd-arm64": "4.57.0", - "@rollup/rollup-freebsd-x64": "4.57.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.57.0", - "@rollup/rollup-linux-arm-musleabihf": "4.57.0", - "@rollup/rollup-linux-arm64-gnu": "4.57.0", - "@rollup/rollup-linux-arm64-musl": "4.57.0", - "@rollup/rollup-linux-loong64-gnu": "4.57.0", - "@rollup/rollup-linux-loong64-musl": "4.57.0", - "@rollup/rollup-linux-ppc64-gnu": "4.57.0", - "@rollup/rollup-linux-ppc64-musl": "4.57.0", - "@rollup/rollup-linux-riscv64-gnu": "4.57.0", - "@rollup/rollup-linux-riscv64-musl": "4.57.0", - "@rollup/rollup-linux-s390x-gnu": "4.57.0", - "@rollup/rollup-linux-x64-gnu": "4.57.0", - "@rollup/rollup-linux-x64-musl": "4.57.0", - "@rollup/rollup-openbsd-x64": "4.57.0", - "@rollup/rollup-openharmony-arm64": "4.57.0", - "@rollup/rollup-win32-arm64-msvc": "4.57.0", - "@rollup/rollup-win32-ia32-msvc": "4.57.0", - "@rollup/rollup-win32-x64-gnu": "4.57.0", - "@rollup/rollup-win32-x64-msvc": "4.57.0", - "fsevents": "~2.3.2" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" + "engines": { + "node": ">= 0.4" } }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, - "node_modules/scheduler": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", - "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", - "license": "MIT" - }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "license": "ISC", + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">= 0.4" } }, - "node_modules/simple-git": { - "version": "3.30.0", - "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.30.0.tgz", - "integrity": "sha512-q6lxyDsCmEal/MEGhP1aVyQ3oxnagGlBDOVSIB4XUVLl1iZh0Pah6ebC9V4xBap/RfgP2WlI8EKs0WS0rMEJHg==", + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, "license": "MIT", "dependencies": { - "@kwsites/file-exists": "^1.1.1", - "@kwsites/promise-deferred": "^1.1.1", - "debug": "^4.4.0" + "es-errors": "^1.3.0" }, - "funding": { - "type": "github", - "url": "https://github.com/steveukx/git-js?sponsor=1" + "engines": { + "node": ">= 0.4" } }, - "node_modules/slice-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz", - "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==", + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^6.2.1", - "is-fullwidth-code-point": "^5.0.0" + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" }, "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" + "node": ">= 0.4" } }, - "node_modules/source-map": { - "version": "0.7.6", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", - "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "node_modules/es-shim-unscopables": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">= 12" - } - }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", "license": "MIT", "dependencies": { - "escape-string-regexp": "^2.0.0" + "hasown": "^2.0.2" }, "engines": { - "node": ">=10" + "node": ">= 0.4" } }, - "node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, "license": "MIT", "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" }, "engines": { - "node": ">=18" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-toolkit": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.44.0.tgz", + "integrity": "sha512-6penXeZalaV88MM3cGkFZZfOoLGWshWWfdy0tWw/RlVVyhvMaWSBTOvXNeiW3e5FwdS5ePW0LGEu17zT139ktg==", + "license": "MIT", + "workspaces": [ + "docs", + "benchmarks" + ] + }, + "node_modules/esbuild": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", + "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.3", + "@esbuild/android-arm": "0.27.3", + "@esbuild/android-arm64": "0.27.3", + "@esbuild/android-x64": "0.27.3", + "@esbuild/darwin-arm64": "0.27.3", + "@esbuild/darwin-x64": "0.27.3", + "@esbuild/freebsd-arm64": "0.27.3", + "@esbuild/freebsd-x64": "0.27.3", + "@esbuild/linux-arm": "0.27.3", + "@esbuild/linux-arm64": "0.27.3", + "@esbuild/linux-ia32": "0.27.3", + "@esbuild/linux-loong64": "0.27.3", + "@esbuild/linux-mips64el": "0.27.3", + "@esbuild/linux-ppc64": "0.27.3", + "@esbuild/linux-riscv64": "0.27.3", + "@esbuild/linux-s390x": "0.27.3", + "@esbuild/linux-x64": "0.27.3", + "@esbuild/netbsd-arm64": "0.27.3", + "@esbuild/netbsd-x64": "0.27.3", + "@esbuild/openbsd-arm64": "0.27.3", + "@esbuild/openbsd-x64": "0.27.3", + "@esbuild/openharmony-arm64": "0.27.3", + "@esbuild/sunos-x64": "0.27.3", + "@esbuild/win32-arm64": "0.27.3", + "@esbuild/win32-ia32": "0.27.3", + "@esbuild/win32-x64": "0.27.3" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.39.2", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz", + "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.1", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.39.2", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-compat-utils": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.5.1.tgz", + "integrity": "sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "eslint": ">=6.0.0" + } + }, + "node_modules/eslint-config-prettier": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.2.tgz", + "integrity": "sha512-iI1f+D2ViGn+uvv5HuHVUamg8ll4tN+JRHGc6IJi4TP9Kl976C57fzPXgseXNs8v0iA8aSJpHsTWjDb9QJamGQ==", + "dev": true, + "license": "MIT", + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz", + "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-es-x": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-es-x/-/eslint-plugin-es-x-7.8.0.tgz", + "integrity": "sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/ota-meshi", + "https://opencollective.com/eslint" + ], + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.1.2", + "@eslint-community/regexpp": "^4.11.0", + "eslint-compat-utils": "^0.5.1" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": ">=8" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.32.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", + "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.9", + "array.prototype.findlastindex": "^1.2.6", + "array.prototype.flat": "^1.3.3", + "array.prototype.flatmap": "^1.3.3", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.12.1", + "hasown": "^2.0.2", + "is-core-module": "^2.16.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.1", + "semver": "^6.3.1", + "string.prototype.trimend": "^1.0.9", + "tsconfig-paths": "^3.15.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-import/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint-plugin-import/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-n": { + "version": "17.23.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-17.23.2.tgz", + "integrity": "sha512-RhWBeb7YVPmNa2eggvJooiuehdL76/bbfj/OJewyoGT80qn5PXdz8zMOTO6YHOsI7byPt7+Ighh/i/4a5/v7hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.5.0", + "enhanced-resolve": "^5.17.1", + "eslint-plugin-es-x": "^7.8.0", + "get-tsconfig": "^4.8.1", + "globals": "^15.11.0", + "globrex": "^0.1.2", + "ignore": "^5.3.2", + "semver": "^7.6.3", + "ts-declaration-location": "^1.0.6" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": ">=8.23.0" + } + }, + "node_modules/eslint-plugin-n/node_modules/globals": { + "version": "15.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", + "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-plugin-n/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/eslint-plugin-promise": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-7.2.1.tgz", + "integrity": "sha512-SWKjd+EuvWkYaS+uN2csvj0KoP43YTu7+phKQ5v+xw6+A0gutVX2yqCeCkC3uLCJFiPfR2dD8Es5L7yUsmvEaA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0" + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fast-content-type-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-3.0.0.tgz", + "integrity": "sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-string-truncated-width": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz", + "integrity": "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==", + "license": "MIT" + }, + "node_modules/fast-string-width": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fast-string-width/-/fast-string-width-3.0.2.tgz", + "integrity": "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==", + "license": "MIT", + "dependencies": { + "fast-string-truncated-width": "^3.0.2" + } + }, + "node_modules/fast-wrap-ansi": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/fast-wrap-ansi/-/fast-wrap-ansi-0.2.0.tgz", + "integrity": "sha512-rLV8JHxTyhVmFYhBJuMujcrHqOT2cnO5Zxj37qROj23CP39GXubJRBUFF0z8KFK77Uc0SukZUf7JZhsVEQ6n8w==", + "license": "MIT", + "dependencies": { + "fast-string-width": "^3.0.2" + } + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fix-dts-default-cjs-exports": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fix-dts-default-cjs-exports/-/fix-dts-default-cjs-exports-1.0.1.tgz", + "integrity": "sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "magic-string": "^0.30.17", + "mlly": "^1.7.4", + "rollup": "^4.34.8" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/generator-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", + "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz", + "integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-tsconfig": { + "version": "4.13.6", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.6.tgz", + "integrity": "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "17.3.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.3.0.tgz", + "integrity": "sha512-yMqGUQVVCkD4tqjOJf3TnrvaaHDMYp4VlUSObbkIiuCPe/ofdMBFIAcBbCSRFWOnos6qRiTVStDwqPLUclaxIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globrex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", + "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==", + "dev": true, + "license": "MIT" + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", + "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ink": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/ink/-/ink-6.7.0.tgz", + "integrity": "sha512-dhB16KfdTO8yYwF2K0E4wPXpL88tdrjjB6w44AZ0ljSktYoUQQcxccq9KL1vpRhk8JIa0A7B7zvjajHqI42teA==", + "license": "MIT", + "dependencies": { + "@alcalzone/ansi-tokenize": "^0.2.4", + "ansi-escapes": "^7.3.0", + "ansi-styles": "^6.2.1", + "auto-bind": "^5.0.1", + "chalk": "^5.6.0", + "cli-boxes": "^3.0.0", + "cli-cursor": "^4.0.0", + "cli-truncate": "^5.1.1", + "code-excerpt": "^4.0.0", + "es-toolkit": "^1.39.10", + "indent-string": "^5.0.0", + "is-in-ci": "^2.0.0", + "patch-console": "^2.0.0", + "react-reconciler": "^0.33.0", + "scheduler": "^0.27.0", + "signal-exit": "^3.0.7", + "slice-ansi": "^7.1.0", + "stack-utils": "^2.0.6", + "string-width": "^8.1.1", + "terminal-size": "^4.0.1", + "type-fest": "^5.4.1", + "widest-line": "^6.0.0", + "wrap-ansi": "^9.0.0", + "ws": "^8.18.0", + "yoga-layout": "~3.2.1" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "@types/react": ">=19.0.0", + "react": ">=19.0.0", + "react-devtools-core": "^6.1.2" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react-devtools-core": { + "optional": true + } + } + }, + "node_modules/ink/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", + "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==", + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.3.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", + "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.4", + "generator-function": "^2.0.0", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-in-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-in-ci/-/is-in-ci-2.0.0.tgz", + "integrity": "sha512-cFeerHriAnhrQSbpAxL37W1wcJKUUX07HyLWZCW1URJT/ra3GyUTzBgUnh24TMVfNTV2Hij2HLxkPHFZfOZy5w==", + "license": "MIT", + "bin": { + "is-in-ci": "cli.js" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/joycon": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", + "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/js-tokens": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz", + "integrity": "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/load-tsconfig": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz", + "integrity": "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/magicast": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.2.tgz", + "integrity": "sha512-E3ZJh4J3S9KfwdjZhe2afj6R9lGIN5Pher1pF39UGrXRqq/VDaGVIGN13BjHd2u8B61hArAGOnso7nBOouW3TQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "source-map-js": "^1.2.1" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mlly": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.0.tgz", + "integrity": "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.15.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.1" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/mute-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-3.0.0.tgz", + "integrity": "sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==", + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.values": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obug": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT" + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/patch-console": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/patch-console/-/patch-console-2.0.0.tgz", + "integrity": "sha512-0YNdUceMdaQwoKce1gatDScmMo5pu/tfABfnzEqeG0gtTmd7mh/WcwgUjtAeOU7N8nFFlbQBnFK2gXW5fGvmMA==", + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", + "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/react": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", + "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-reconciler": { + "version": "0.33.0", + "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.33.0.tgz", + "integrity": "sha512-KetWRytFv1epdpJc3J4G75I4WrplZE5jOL7Yq0p34+OVOKF4Se7WrdIdVC45XsSSmUTlht2FM/fM1FZb1mfQeA==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "peerDependencies": { + "react": "^19.2.0" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/restore-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", + "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.57.1.tgz", + "integrity": "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.57.1", + "@rollup/rollup-android-arm64": "4.57.1", + "@rollup/rollup-darwin-arm64": "4.57.1", + "@rollup/rollup-darwin-x64": "4.57.1", + "@rollup/rollup-freebsd-arm64": "4.57.1", + "@rollup/rollup-freebsd-x64": "4.57.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.57.1", + "@rollup/rollup-linux-arm-musleabihf": "4.57.1", + "@rollup/rollup-linux-arm64-gnu": "4.57.1", + "@rollup/rollup-linux-arm64-musl": "4.57.1", + "@rollup/rollup-linux-loong64-gnu": "4.57.1", + "@rollup/rollup-linux-loong64-musl": "4.57.1", + "@rollup/rollup-linux-ppc64-gnu": "4.57.1", + "@rollup/rollup-linux-ppc64-musl": "4.57.1", + "@rollup/rollup-linux-riscv64-gnu": "4.57.1", + "@rollup/rollup-linux-riscv64-musl": "4.57.1", + "@rollup/rollup-linux-s390x-gnu": "4.57.1", + "@rollup/rollup-linux-x64-gnu": "4.57.1", + "@rollup/rollup-linux-x64-musl": "4.57.1", + "@rollup/rollup-openbsd-x64": "4.57.1", + "@rollup/rollup-openharmony-arm64": "4.57.1", + "@rollup/rollup-win32-arm64-msvc": "4.57.1", + "@rollup/rollup-win32-ia32-msvc": "4.57.1", + "@rollup/rollup-win32-x64-gnu": "4.57.1", + "@rollup/rollup-win32-x64-msvc": "4.57.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/simple-git": { + "version": "3.31.1", + "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.31.1.tgz", + "integrity": "sha512-oiWP4Q9+kO8q9hHqkX35uuHmxiEbZNTrZ5IPxgMGrJwN76pzjm/jabkZO0ItEcqxAincqGAzL3QHSaHt4+knBg==", + "license": "MIT", + "dependencies": { + "@kwsites/file-exists": "^1.1.1", + "@kwsites/promise-deferred": "^1.1.1", + "debug": "^4.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/steveukx/git-js?sponsor=1" + } + }, + "node_modules/slice-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz", + "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "dev": true, + "license": "MIT" + }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string-width": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.1.1.tgz", + "integrity": "sha512-KpqHIdDL9KwYk22wEOg/VIqYbrnLeSApsKT/bSj6Ez7pn3CftUiLAv2Lccpq1ALcpLV9UX1Ppn92npZWu2w/aw==", + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.3.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/sucrase": { + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "tinyglobby": "^0.2.11", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/sucrase/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tagged-tag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/tagged-tag/-/tagged-tag-1.0.0.tgz", + "integrity": "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tapable": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", + "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/terminal-size": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/terminal-size/-/terminal-size-4.0.1.tgz", + "integrity": "sha512-avMLDQpUI9I5XFrklECw1ZEUPJhqzcwSWsyyI8blhRLT+8N1jLJWLWWYQpB2q2xthq8xDvjZPISVh53T/+CLYQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/tinyrainbow": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.0.3.tgz", + "integrity": "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/ts-api-utils": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", + "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/ts-declaration-location": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/ts-declaration-location/-/ts-declaration-location-1.0.7.tgz", + "integrity": "sha512-EDyGAwH1gO0Ausm9gV6T2nUvBgXT5kGoCMJPllOaooZ+4VvJiKBdZE7wK18N1deEowhcUptS+5GXZK8U/fvpwA==", + "dev": true, + "funding": [ + { + "type": "ko-fi", + "url": "https://ko-fi.com/rebeccastevens" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/ts-declaration-location" + } + ], + "license": "BSD-3-Clause", + "dependencies": { + "picomatch": "^4.0.2" + }, + "peerDependencies": { + "typescript": ">=4.0.0" + } + }, + "node_modules/ts-declaration-location/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tsup": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/tsup/-/tsup-8.5.1.tgz", + "integrity": "sha512-xtgkqwdhpKWr3tKPmCkvYmS9xnQK3m3XgxZHwSUjvfTjp7YfXe5tT3GgWi0F2N+ZSMsOeWeZFh7ZZFg5iPhing==", + "dev": true, + "license": "MIT", + "dependencies": { + "bundle-require": "^5.1.0", + "cac": "^6.7.14", + "chokidar": "^4.0.3", + "consola": "^3.4.0", + "debug": "^4.4.0", + "esbuild": "^0.27.0", + "fix-dts-default-cjs-exports": "^1.0.0", + "joycon": "^3.1.1", + "picocolors": "^1.1.1", + "postcss-load-config": "^6.0.1", + "resolve-from": "^5.0.0", + "rollup": "^4.34.8", + "source-map": "^0.7.6", + "sucrase": "^3.35.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.11", + "tree-kill": "^1.2.2" + }, + "bin": { + "tsup": "dist/cli-default.js", + "tsup-node": "dist/cli-node.js" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@microsoft/api-extractor": "^7.36.0", + "@swc/core": "^1", + "postcss": "^8.4.12", + "typescript": ">=4.5.0" + }, + "peerDependenciesMeta": { + "@microsoft/api-extractor": { + "optional": true + }, + "@swc/core": { + "optional": true + }, + "postcss": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/tsup/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/tsx": { + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", + "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.27.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.4.4.tgz", + "integrity": "sha512-JnTrzGu+zPV3aXIUhnyWJj4z/wigMsdYajGLIYakqyOW1nPllzXEJee0QQbHj+CTIQtXGlAjuK0UY+2xTyjVAw==", + "license": "(MIT OR CC0-1.0)", + "dependencies": { + "tagged-tag": "^1.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/ufo": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.3.tgz", + "integrity": "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/universal-user-agent": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz", + "integrity": "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==", + "license": "ISC" + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/vite": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", + "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/vitest": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.18.tgz", + "integrity": "sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.0.18", + "@vitest/mocker": "4.0.18", + "@vitest/pretty-format": "4.0.18", + "@vitest/runner": "4.0.18", + "@vitest/snapshot": "4.0.18", + "@vitest/spy": "4.0.18", + "@vitest/utils": "4.0.18", + "es-module-lexer": "^1.7.0", + "expect-type": "^1.2.2", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^3.10.0", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.0.3", + "vite": "^6.0.0 || ^7.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.0.18", + "@vitest/browser-preview": "4.0.18", + "@vitest/browser-webdriverio": "4.0.18", + "@vitest/ui": "4.0.18", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } } }, - "node_modules/strip-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", - "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "node_modules/vitest/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, "engines": { "node": ">=12" }, "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/sucrase": { - "version": "3.35.1", - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", - "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "node_modules/vitest/node_modules/tinyexec": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", + "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", "dev": true, "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.2", - "commander": "^4.0.0", - "lines-and-columns": "^1.1.6", - "mz": "^2.7.0", - "pirates": "^4.0.1", - "tinyglobby": "^0.2.11", - "ts-interface-checker": "^0.1.9" - }, - "bin": { - "sucrase": "bin/sucrase", - "sucrase-node": "bin/sucrase-node" - }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=18" } }, - "node_modules/sucrase/node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true, + "node_modules/vscode-jsonrpc": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.1.tgz", + "integrity": "sha512-kdjOSJ2lLIn7r1rtrMbbNCHjyMPfRnowdKjBQ+mGq6NAW5QY2bEZC/khaC5OR8svbbjvLEaIXkOq45e2X9BIbQ==", "license": "MIT", "engines": { - "node": ">= 6" - } - }, - "node_modules/thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0" + "node": ">=14.0.0" } }, - "node_modules/thenify-all": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "thenify": ">= 3.1.0 < 4" + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" }, "engines": { - "node": ">=0.8" + "node": ">= 8" } }, - "node_modules/tinyexec": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", - "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", "dev": true, "license": "MIT", "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.3" + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" }, "engines": { - "node": ">=12.0.0" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, "license": "MIT", "dependencies": { - "is-number": "^7.0.0" + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" }, - "engines": { - "node": ">=8.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/tree-kill": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", - "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", "dev": true, "license": "MIT", - "bin": { - "tree-kill": "cli.js" + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/ts-interface-checker": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/tsup": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/tsup/-/tsup-8.5.1.tgz", - "integrity": "sha512-xtgkqwdhpKWr3tKPmCkvYmS9xnQK3m3XgxZHwSUjvfTjp7YfXe5tT3GgWi0F2N+ZSMsOeWeZFh7ZZFg5iPhing==", + "node_modules/which-typed-array": { + "version": "1.1.20", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz", + "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", "dev": true, "license": "MIT", "dependencies": { - "bundle-require": "^5.1.0", - "cac": "^6.7.14", - "chokidar": "^4.0.3", - "consola": "^3.4.0", - "debug": "^4.4.0", - "esbuild": "^0.27.0", - "fix-dts-default-cjs-exports": "^1.0.0", - "joycon": "^3.1.1", - "picocolors": "^1.1.1", - "postcss-load-config": "^6.0.1", - "resolve-from": "^5.0.0", - "rollup": "^4.34.8", - "source-map": "^0.7.6", - "sucrase": "^3.35.0", - "tinyexec": "^0.3.2", - "tinyglobby": "^0.2.11", - "tree-kill": "^1.2.2" - }, - "bin": { - "tsup": "dist/cli-default.js", - "tsup-node": "dist/cli-node.js" + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" }, "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@microsoft/api-extractor": "^7.36.0", - "@swc/core": "^1", - "postcss": "^8.4.12", - "typescript": ">=4.5.0" + "node": ">= 0.4" }, - "peerDependenciesMeta": { - "@microsoft/api-extractor": { - "optional": true - }, - "@swc/core": { - "optional": true - }, - "postcss": { - "optional": true - }, - "typescript": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/tsx": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", - "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", "dev": true, "license": "MIT", "dependencies": { - "esbuild": "~0.27.0", - "get-tsconfig": "^4.7.5" + "siginfo": "^2.0.0", + "stackback": "0.0.2" }, "bin": { - "tsx": "dist/cli.mjs" + "why-is-node-running": "cli.js" }, "engines": { - "node": ">=18.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" + "node": ">=8" } }, - "node_modules/type-fest": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", - "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", - "license": "(MIT OR CC0-1.0)", + "node_modules/widest-line": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-6.0.0.tgz", + "integrity": "sha512-U89AsyEeAsyoF0zVJBkG9zBgekjgjK7yk9sje3F4IQpXBJ10TF6ByLlIfjMhcmHMJgHZI4KHt4rdNfktzxIAMA==", + "license": "MIT", + "dependencies": { + "string-width": "^8.1.0" + }, "engines": { - "node": ">=16" + "node": ">=20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/ufo": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.3.tgz", - "integrity": "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==", + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, - "license": "MIT" - }, - "node_modules/undici-types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", - "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/universal-user-agent": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz", - "integrity": "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==", - "license": "ISC" - }, - "node_modules/vscode-jsonrpc": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.1.tgz", - "integrity": "sha512-kdjOSJ2lLIn7r1rtrMbbNCHjyMPfRnowdKjBQ+mGq6NAW5QY2bEZC/khaC5OR8svbbjvLEaIXkOq45e2X9BIbQ==", "license": "MIT", "engines": { - "node": ">=14.0.0" + "node": ">=0.10.0" } }, - "node_modules/widest-line": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-5.0.0.tgz", - "integrity": "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==", + "node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", "license": "MIT", "dependencies": { - "string-width": "^7.0.0" + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" }, "engines": { "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrap-ansi": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", - "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "license": "MIT" + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "license": "MIT", "dependencies": { - "ansi-styles": "^6.2.1", - "string-width": "^7.0.0", + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" }, "engines": { "node": ">=18" }, "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/ws": { @@ -3052,6 +7303,19 @@ } } }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/yoga-layout": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/yoga-layout/-/yoga-layout-3.2.1.tgz", diff --git a/package.json b/package.json index 2a15771..ea742af 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,26 @@ { "name": "primer", "version": "1.0.0", - "description": "", - "main": "index.js", + "description": "Prime repositories for AI-assisted development", + "main": "dist/index.js", + "type": "module", + "bin": { + "primer": "dist/index.js" + }, + "files": [ + "dist" + ], "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "build": "tsup", + "dev": "tsx src/index.ts", + "prepare": "test \"$CI\" = true || tsup", + "lint": "eslint .", + "format": "prettier --write .", + "format:check": "prettier --check .", + "typecheck": "tsc -p tsconfig.json --noEmit", + "test": "vitest run", + "test:watch": "vitest", + "test:coverage": "vitest run --coverage" }, "keywords": [], "author": "", @@ -23,12 +39,19 @@ "devDependencies": { "@types/node": "^25.1.0", "@types/react": "^19.2.10", + "@typescript-eslint/eslint-plugin": "^8.5.0", + "@typescript-eslint/parser": "^8.5.0", + "@vitest/coverage-v8": "^4.0.18", + "eslint": "^9.39.2", + "eslint-config-prettier": "^9.1.2", + "eslint-plugin-import": "^2.30.0", + "eslint-plugin-n": "^17.10.2", + "eslint-plugin-promise": "^7.1.0", + "globals": "^17.3.0", + "prettier": "^3.3.3", "tsup": "^8.5.1", "tsx": "^4.21.0", - "typescript": "^5.9.3" - }, - "type": "module", - "bin": { - "primer": "dist/index.js" + "typescript": "^5.9.3", + "vitest": "^4.0.18" } } diff --git a/primer.eval.json b/primer.eval.json index 3b77578..f892d2a 100644 --- a/primer.eval.json +++ b/primer.eval.json @@ -1,10 +1,21 @@ { "instructionFile": ".github/copilot-instructions.md", + "systemMessage": "You are answering questions about the Primer repository. Scope your answers to this repo's architecture, usage, configuration, and workflows. Do not provide generic Copilot CLI details unless specifically asked.", "cases": [ { - "id": "overview", - "prompt": "Summarize what this application does and list the main entrypoints.", - "expectation": "Mentions the CLI entrypoint in src/index.ts and that this is the Primer CLI." + "id": "case-1", + "prompt": "What is the main purpose of the Primer project and what are its key entrypoints?", + "expectation": "Primer is a TypeScript CLI tool for priming repositories for AI-assisted development and evaluation. The main entrypoint is src/index.ts, which calls runCli in src/cli.ts. Key directories include src/commands/ for CLI subcommands, src/services/ for core logic, and src/ui/ for the Ink/React-based terminal UI." + }, + { + "id": "case-2", + "prompt": "How do I build, lint, and test the Primer project locally?", + "expectation": "Install dependencies with npm install. Build with npm run build, lint with npm run lint, and test with npm run test. Type checking is done with npx tsc -p tsconfig.json --noEmit, and coverage with npm run test:coverage." + }, + { + "id": "case-3", + "prompt": "Where should new CLI commands and core logic be placed, and what conventions should be followed for configuration files?", + "expectation": "New CLI commands should be placed in src/commands/, and core logic in src/services/. Use ESM syntax everywhere, strict TypeScript targeting ES2022 and module ESNext, and only overwrite config files like .vscode/settings.json and .vscode/mcp.json with --force. All Copilot/VS Code settings reference .github/copilot-instructions.md and enable MCP." } ] } diff --git a/release-please-config.json b/release-please-config.json new file mode 100644 index 0000000..0ef4665 --- /dev/null +++ b/release-please-config.json @@ -0,0 +1,9 @@ +{ + "release-type": "node", + "packages": { + ".": { + "package-name": "primer", + "changelog-path": "CHANGELOG.md" + } + } +} diff --git a/release-please-manifest.json b/release-please-manifest.json new file mode 100644 index 0000000..37fcefa --- /dev/null +++ b/release-please-manifest.json @@ -0,0 +1,3 @@ +{ + ".": "1.0.0" +} diff --git a/src/cli.ts b/src/cli.ts index 86bcf74..1c834af 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -1,15 +1,15 @@ import { Command } from "commander"; -import { initCommand } from "./commands/init"; -import { analyzeCommand } from "./commands/analyze"; + +import { batchCommand } from "./commands/batch"; +import { batchReadinessCommand } from "./commands/batchReadiness"; +import { evalCommand } from "./commands/eval"; import { generateCommand } from "./commands/generate"; +import { initCommand } from "./commands/init"; +import { instructionsCommand } from "./commands/instructions"; import { prCommand } from "./commands/pr"; -import { templatesCommand } from "./commands/templates"; -import { updateCommand } from "./commands/update"; -import { configCommand } from "./commands/config"; -import { evalCommand } from "./commands/eval"; +import { readinessCommand } from "./commands/readiness"; import { tuiCommand } from "./commands/tui"; -import { instructionsCommand } from "./commands/instructions"; -import { batchCommand } from "./commands/batch"; +import { DEFAULT_MODEL, DEFAULT_JUDGE_MODEL } from "./config"; export function runCli(argv: string[]): void { const program = new Command(); @@ -17,43 +17,42 @@ export function runCli(argv: string[]): void { program .name("primer") .description("Prime repositories for AI-assisted development") - .version("0.1.0"); + .version("1.0.0"); program .command("init") .argument("[path]", "Path to a local repository") .option("--github", "Use a GitHub repository") - .option("--yes", "Accept defaults and skip prompts") + .option("--provider ", "Repo provider (github|azure)") + .option("--yes", "Accept defaults (generates instructions, MCP, and VS Code configs)") .option("--force", "Overwrite existing files") .action(initCommand); - program - .command("analyze") - .argument("[path]", "Path to a local repository") - .option("--json", "Output JSON") - .action(analyzeCommand); - program .command("generate") - .argument("", "prompts|agents|mcp|vscode|aiignore") + .argument("", "instructions|agents|mcp|vscode") .argument("[path]", "Path to a local repository") .option("--force", "Overwrite existing files") + .option("--per-app", "Generate per-app in monorepos") .action(generateCommand); program .command("pr") - .argument("[repo]", "GitHub repo in owner/name form") - .option("--branch ", "Branch name", "primer/add-configs") + .argument("[repo]", "Repo identifier (github: owner/name, azure: org/project/repo)") + .option("--branch ", "Branch name") + .option("--provider ", "Repo provider (github|azure)") .action(prCommand); program .command("eval") .argument("[path]", "Path to eval config JSON") .option("--repo ", "Repository path", process.cwd()) - .option("--model ", "Model for responses", "gpt-5") - .option("--judge-model ", "Model for judging", "gpt-5") + .option("--model ", "Model for responses", DEFAULT_MODEL) + .option("--judge-model ", "Model for judging", DEFAULT_JUDGE_MODEL) + .option("--list-models", "List Copilot CLI models and exit") .option("--output ", "Write results JSON to file") .option("--init", "Create a starter primer.eval.json file") + .option("--count ", "Number of eval cases to generate (with --init)") .action(evalCommand); program @@ -66,18 +65,29 @@ export function runCli(argv: string[]): void { .command("instructions") .option("--repo ", "Repository path", process.cwd()) .option("--output ", "Output path for copilot instructions") - .option("--model ", "Model for instructions generation", "gpt-4.1") + .option("--model ", "Model for instructions generation", DEFAULT_MODEL) .action(instructionsCommand); + program + .command("readiness") + .argument("[path]", "Path to a local repository") + .option("--json", "Output JSON") + .option("--output ", "Write report to file (.json or .html)") + .option("--visual", "Generate visual HTML report") + .action(readinessCommand); + program .command("batch") .description("Batch process multiple repos across orgs") .option("--output ", "Write results JSON to file") + .option("--provider ", "Repo provider (github|azure)", "github") .action(batchCommand); - program.command("templates").action(templatesCommand); - program.command("update").action(updateCommand); - program.command("config").action(configCommand); + program + .command("batch-readiness") + .description("Generate batch AI readiness report for multiple repos") + .option("--output ", "Write HTML report to file") + .action(batchReadinessCommand); program.parse(argv); } diff --git a/src/commands/analyze.ts b/src/commands/analyze.ts deleted file mode 100644 index 10aeea0..0000000 --- a/src/commands/analyze.ts +++ /dev/null @@ -1,23 +0,0 @@ -import path from "path"; -import { analyzeRepo } from "../services/analyzer"; - -type AnalyzeOptions = { - json?: boolean; -}; - -export async function analyzeCommand(repoPathArg: string | undefined, options: AnalyzeOptions): Promise { - const repoPath = path.resolve(repoPathArg ?? process.cwd()); - const analysis = await analyzeRepo(repoPath); - - if (options.json) { - console.log(JSON.stringify(analysis, null, 2)); - return; - } - - console.log("Repository analysis:"); - console.log(`- Path: ${analysis.path}`); - console.log(`- Git: ${analysis.isGitRepo ? "yes" : "no"}`); - console.log(`- Languages: ${analysis.languages.join(", ") || "unknown"}`); - console.log(`- Frameworks: ${analysis.frameworks.join(", ") || "none"}`); - console.log(`- Package manager: ${analysis.packageManager ?? "unknown"}`); -} diff --git a/src/commands/batch.tsx b/src/commands/batch.tsx index eb69267..4ced461 100644 --- a/src/commands/batch.tsx +++ b/src/commands/batch.tsx @@ -1,15 +1,41 @@ -import React from "react"; import { render } from "ink"; -import { BatchTui } from "../ui/BatchTui"; +import React from "react"; + +import { getAzureDevOpsToken } from "../services/azureDevops"; import { getGitHubToken } from "../services/github"; +import { BatchTui } from "../ui/BatchTui"; +import { BatchTuiAzure } from "../ui/BatchTuiAzure"; type BatchOptions = { output?: string; + provider?: string; }; export async function batchCommand(options: BatchOptions): Promise { + const provider = options.provider ?? "github"; + if (provider !== "github" && provider !== "azure") { + console.error("Invalid provider. Use github or azure."); + process.exitCode = 1; + return; + } + + if (provider === "azure") { + const token = getAzureDevOpsToken(); + if (!token) { + console.error("Error: Azure DevOps authentication required."); + console.error(""); + console.error("Set a PAT environment variable:"); + console.error(" export AZURE_DEVOPS_PAT="); + process.exitCode = 1; + return; + } + + const { waitUntilExit } = render(); + await waitUntilExit(); + return; + } + const token = await getGitHubToken(); - if (!token) { console.error("Error: GitHub authentication required."); console.error(""); @@ -22,10 +48,7 @@ export async function batchCommand(options: BatchOptions): Promise { return; } - const { waitUntilExit } = render( - - ); - + const { waitUntilExit } = render(); await waitUntilExit(); } diff --git a/src/commands/batchReadiness.tsx b/src/commands/batchReadiness.tsx new file mode 100644 index 0000000..772d34b --- /dev/null +++ b/src/commands/batchReadiness.tsx @@ -0,0 +1,28 @@ +import { render } from "ink"; +import React from "react"; + +import { getGitHubToken } from "../services/github"; +import { BatchReadinessTui } from "../ui/BatchReadinessTui"; + +type BatchReadinessOptions = { + output?: string; +}; + +export async function batchReadinessCommand(options: BatchReadinessOptions): Promise { + const token = await getGitHubToken(); + if (!token) { + console.error("Error: GitHub authentication required."); + console.error(""); + console.error("Option 1 (recommended): Install and authenticate GitHub CLI"); + console.error(" brew install gh && gh auth login"); + console.error(""); + console.error("Option 2: Set a token environment variable"); + console.error(" export GITHUB_TOKEN="); + process.exitCode = 1; + return; + } + + const { waitUntilExit } = render(); + + await waitUntilExit(); +} diff --git a/src/commands/config.ts b/src/commands/config.ts deleted file mode 100644 index a924966..0000000 --- a/src/commands/config.ts +++ /dev/null @@ -1,3 +0,0 @@ -export async function configCommand(): Promise { - console.log("Config is not implemented yet."); -} diff --git a/src/commands/eval.ts b/src/commands/eval.ts index b875f97..1572f0c 100644 --- a/src/commands/eval.ts +++ b/src/commands/eval.ts @@ -1,5 +1,9 @@ -import path from "path"; import fs from "fs/promises"; +import path from "path"; + +import { DEFAULT_MODEL, DEFAULT_JUDGE_MODEL } from "../config"; +import { listCopilotModels } from "../services/copilot"; +import { generateEvalScaffold } from "../services/evalScaffold"; import { runEval } from "../services/evaluator"; type EvalOptions = { @@ -8,35 +12,30 @@ type EvalOptions = { judgeModel?: string; output?: string; init?: boolean; + count?: string; + listModels?: boolean; }; -const EVAL_SCAFFOLD = { - instructionFile: ".github/copilot-instructions.md", - cases: [ - { - id: "project-overview", - prompt: "Summarize what this project does and list the main entry points.", - expectation: "Should mention the primary purpose and key files/directories." - }, - { - id: "tech-stack", - prompt: "What languages and frameworks does this project use?", - expectation: "Should correctly identify the main languages and frameworks." - }, - { - id: "build-commands", - prompt: "How do I build and test this project?", - expectation: "Should provide the correct build and test commands from package.json or equivalent." +export async function evalCommand( + configPathArg: string | undefined, + options: EvalOptions +): Promise { + const repoPath = path.resolve(options.repo ?? process.cwd()); + + if (options.listModels) { + const models = await listCopilotModels(); + if (!models.length) { + console.log("No models detected from Copilot CLI."); + return; } - ] -}; + console.log(models.join("\n")); + return; + } -export async function evalCommand(configPathArg: string | undefined, options: EvalOptions): Promise { - const repoPath = path.resolve(options.repo ?? process.cwd()); - // Handle --init flag if (options.init) { const outputPath = path.join(repoPath, "primer.eval.json"); + const desiredCount = Math.max(1, Number.parseInt(options.count ?? "5", 10) || 5); try { await fs.access(outputPath); console.error(`primer.eval.json already exists at ${outputPath}`); @@ -45,7 +44,12 @@ export async function evalCommand(configPathArg: string | undefined, options: Ev } catch { // File doesn't exist, create it } - await fs.writeFile(outputPath, JSON.stringify(EVAL_SCAFFOLD, null, 2), "utf8"); + const scaffold = await generateEvalScaffold({ + repoPath, + count: desiredCount, + model: options.model + }); + await fs.writeFile(outputPath, JSON.stringify(scaffold, null, 2), "utf8"); console.log(`Created ${outputPath}`); console.log("Edit the file to add your own test cases, then run 'primer eval' to test."); return; @@ -53,13 +57,16 @@ export async function evalCommand(configPathArg: string | undefined, options: Ev const configPath = path.resolve(configPathArg ?? path.join(repoPath, "primer.eval.json")); - const { summary } = await runEval({ + const { summary, viewerPath } = await runEval({ configPath, repoPath, - model: options.model ?? "gpt-5", - judgeModel: options.judgeModel ?? "gpt-5", + model: options.model ?? DEFAULT_MODEL, + judgeModel: options.judgeModel ?? DEFAULT_JUDGE_MODEL, outputPath: options.output }); console.log(summary); + if (viewerPath) { + console.log(`Trajectory viewer: ${viewerPath}`); + } } diff --git a/src/commands/generate.ts b/src/commands/generate.ts index a4c69c7..0a0b1f0 100644 --- a/src/commands/generate.ts +++ b/src/commands/generate.ts @@ -1,15 +1,24 @@ +import fs from "fs/promises"; import path from "path"; + import { analyzeRepo } from "../services/analyzer"; import { generateConfigs } from "../services/generator"; +import { generateCopilotInstructions } from "../services/instructions"; +import { ensureDir } from "../utils/fs"; type GenerateOptions = { force?: boolean; + perApp?: boolean; }; -export async function generateCommand(type: string, repoPathArg: string | undefined, options: GenerateOptions): Promise { - const allowed = new Set(["mcp", "vscode"]); +export async function generateCommand( + type: string, + repoPathArg: string | undefined, + options: GenerateOptions +): Promise { + const allowed = new Set(["mcp", "vscode", "instructions", "agents"]); if (!allowed.has(type)) { - console.error("Invalid type. Use: mcp, vscode."); + console.error("Invalid type. Use: instructions, agents, mcp, vscode."); process.exitCode = 1; return; } @@ -17,6 +26,46 @@ export async function generateCommand(type: string, repoPathArg: string | undefi const repoPath = path.resolve(repoPathArg ?? process.cwd()); const analysis = await analyzeRepo(repoPath); + if (type === "instructions" || type === "agents") { + const apps = analysis.apps ?? []; + const targets: Array<{ repoPath: string; savePath: string; label: string }> = []; + + if (options.perApp && analysis.isMonorepo && apps.length > 1) { + for (const app of apps) { + const savePath = + type === "instructions" + ? path.join(app.path, ".github", "copilot-instructions.md") + : path.join(app.path, "AGENTS.md"); + targets.push({ repoPath: app.path, savePath, label: app.name }); + } + } else { + const savePath = + type === "instructions" + ? path.join(repoPath, ".github", "copilot-instructions.md") + : path.join(repoPath, "AGENTS.md"); + targets.push({ repoPath, savePath, label: path.basename(repoPath) }); + } + + for (const target of targets) { + console.log(`Generating ${type} for ${target.label}...`); + try { + const content = await generateCopilotInstructions({ + repoPath: target.repoPath + }); + if (!content.trim()) { + console.error(` No content generated for ${target.label}.`); + continue; + } + await ensureDir(path.dirname(target.savePath)); + await fs.writeFile(target.savePath, content, "utf8"); + console.log(` ✓ ${path.relative(process.cwd(), target.savePath)}`); + } catch (error) { + console.error(` ✗ ${error instanceof Error ? error.message : String(error)}`); + } + } + return; + } + const selections = [type]; const result = await generateConfigs({ repoPath, diff --git a/src/commands/init.ts b/src/commands/init.ts index 4f22389..6de672a 100644 --- a/src/commands/init.ts +++ b/src/commands/init.ts @@ -1,24 +1,44 @@ import path from "path"; -import fs from "fs/promises"; + import { checkbox, select } from "@inquirer/prompts"; + import { analyzeRepo } from "../services/analyzer"; +import type { AzureDevOpsOrg, AzureDevOpsProject, AzureDevOpsRepo } from "../services/azureDevops"; +import { + getAzureDevOpsToken, + listOrganizations, + listProjects, + listRepos +} from "../services/azureDevops"; import { generateConfigs } from "../services/generator"; -import { GitHubRepo, listAccessibleRepos } from "../services/github"; -import { cloneRepo, isGitRepo } from "../services/git"; +import { buildAuthedUrl, cloneRepo, isGitRepo, setRemoteUrl } from "../services/git"; +import type { GitHubRepo } from "../services/github"; +import { listAccessibleRepos } from "../services/github"; import { generateCopilotInstructions } from "../services/instructions"; -import { ensureDir } from "../utils/fs"; +import { ensureDir, safeWriteFile, validateCachePath } from "../utils/fs"; import { prettyPrintSummary } from "../utils/logger"; type InitOptions = { github?: boolean; + provider?: string; yes?: boolean; force?: boolean; }; -export async function initCommand(repoPathArg: string | undefined, options: InitOptions): Promise { +export async function initCommand( + repoPathArg: string | undefined, + options: InitOptions +): Promise { let repoPath = path.resolve(repoPathArg ?? process.cwd()); + const provider = options.provider ?? (options.github ? "github" : undefined); + + if (provider && provider !== "github" && provider !== "azure") { + console.error("Invalid provider. Use github or azure."); + process.exitCode = 1; + return; + } - if (options.github) { + if (provider === "github") { const token = process.env.GITHUB_TOKEN ?? process.env.GH_TOKEN; if (!token) { console.error("Set GITHUB_TOKEN or GH_TOKEN to use GitHub mode."); @@ -42,7 +62,7 @@ export async function initCommand(repoPathArg: string | undefined, options: Init }); const cacheRoot = path.join(process.cwd(), ".primer-cache"); - repoPath = path.join(cacheRoot, selection.owner, selection.name); + repoPath = validateCachePath(cacheRoot, selection.owner, selection.name); await ensureDir(repoPath); const hasGit = await isGitRepo(repoPath); @@ -50,11 +70,81 @@ export async function initCommand(repoPathArg: string | undefined, options: Init await cloneRepo(selection.cloneUrl, repoPath); } } + + if (provider === "azure") { + const token = getAzureDevOpsToken(); + if (!token) { + console.error("Set AZURE_DEVOPS_PAT (or AZDO_PAT) to use Azure DevOps mode."); + process.exitCode = 1; + return; + } + + const orgs = await listOrganizations(token); + if (orgs.length === 0) { + console.error("No Azure DevOps organizations found."); + process.exitCode = 1; + return; + } + + const orgSelection = await select({ + message: "Choose an Azure DevOps organization", + choices: orgs.map((org) => ({ + name: org.name, + value: org + })) + }); + + const projects = await listProjects(token, orgSelection.name); + if (projects.length === 0) { + console.error("No Azure DevOps projects found."); + process.exitCode = 1; + return; + } + + const projectSelection = await select({ + message: "Choose an Azure DevOps project", + choices: projects.map((project) => ({ + name: project.name, + value: project + })) + }); + + const repos = await listRepos(token, orgSelection.name, projectSelection.name); + if (repos.length === 0) { + console.error("No Azure DevOps repositories found."); + process.exitCode = 1; + return; + } + + const repoSelection = await select({ + message: "Choose a repository", + choices: repos.map((repo) => ({ + name: `${repo.name}${repo.isPrivate ? " (private)" : ""}`, + value: repo + })) + }); + + const cacheRoot = path.join(process.cwd(), ".primer-cache"); + repoPath = validateCachePath( + cacheRoot, + orgSelection.name, + projectSelection.name, + repoSelection.name + ); + await ensureDir(repoPath); + + const hasGit = await isGitRepo(repoPath); + if (!hasGit) { + const authedUrl = buildAuthedUrl(repoSelection.cloneUrl, token, "azure"); + await cloneRepo(authedUrl, repoPath); + await setRemoteUrl(repoPath, repoSelection.cloneUrl); + } + } const analysis = await analyzeRepo(repoPath); prettyPrintSummary(analysis); const selections = options.yes - ? ["instructions"] + ? ["instructions", "mcp", "vscode"] : await checkbox({ message: "What would you like to generate?", choices: [ @@ -69,8 +159,8 @@ export async function initCommand(repoPathArg: string | undefined, options: Init const outputPath = path.join(repoPath, ".github", "copilot-instructions.md"); await ensureDir(path.dirname(outputPath)); const content = await generateCopilotInstructions({ repoPath }); - await fs.writeFile(outputPath, content, "utf8"); - console.log(`Updated ${path.relative(process.cwd(), outputPath)}`); + const result = await safeWriteFile(outputPath, content, Boolean(options.force)); + console.log(result); } const result = await generateConfigs({ diff --git a/src/commands/instructions.tsx b/src/commands/instructions.tsx index aa60795..b1b607a 100644 --- a/src/commands/instructions.tsx +++ b/src/commands/instructions.tsx @@ -1,5 +1,6 @@ -import path from "path"; import fs from "fs/promises"; +import path from "path"; + import { generateCopilotInstructions } from "../services/instructions"; import { ensureDir } from "../utils/fs"; @@ -23,7 +24,9 @@ export async function instructionsCommand(options: InstructionsOptions): Promise }); } catch (error) { console.error("Failed to generate instructions with Copilot SDK."); - console.error("Ensure the Copilot CLI is installed (copilot --version) and logged in (run 'copilot' then '/login')." ); + console.error( + "Ensure the Copilot CLI is installed (copilot --version) and logged in (run 'copilot' then '/login')." + ); console.error(error instanceof Error ? error.message : String(error)); process.exitCode = 1; return; @@ -39,4 +42,4 @@ export async function instructionsCommand(options: InstructionsOptions): Promise console.log(`Updated ${path.relative(process.cwd(), outputPath)}`); console.log("Please review and share feedback on any unclear or incomplete sections."); -} \ No newline at end of file +} diff --git a/src/commands/pr.ts b/src/commands/pr.ts index d8ce79b..c1a8a8a 100644 --- a/src/commands/pr.ts +++ b/src/commands/pr.ts @@ -1,21 +1,100 @@ +import fs from "fs/promises"; import path from "path"; + +import { DEFAULT_MODEL } from "../config"; import { analyzeRepo } from "../services/analyzer"; +import { + createPullRequest as createAzurePullRequest, + getAzureDevOpsToken, + getRepo as getAzureRepo +} from "../services/azureDevops"; import { generateConfigs } from "../services/generator"; +import { + buildAuthedUrl, + checkoutBranch, + cloneRepo, + commitAll, + isGitRepo, + pushBranch, + setRemoteUrl +} from "../services/git"; import { createPullRequest, getRepo } from "../services/github"; -import { checkoutBranch, cloneRepo, commitAll, isGitRepo, pushBranch } from "../services/git"; -import { ensureDir } from "../utils/fs"; +import { generateCopilotInstructions } from "../services/instructions"; +import { ensureDir, validateCachePath } from "../utils/fs"; +import { buildConfigsPrBody, buildInstructionsPrBody } from "../utils/pr"; type PrOptions = { branch?: string; + provider?: string; }; export async function prCommand(repo: string | undefined, options: PrOptions): Promise { + const provider = options.provider ?? "github"; + if (provider !== "github" && provider !== "azure") { + console.error("Invalid provider. Use github or azure."); + process.exitCode = 1; + return; + } + if (!repo) { - console.error("Provide a repo in owner/name form."); + console.error("Provide a repo identifier (github: owner/name, azure: org/project/repo)."); process.exitCode = 1; return; } + if (provider === "azure") { + const token = getAzureDevOpsToken(); + if (!token) { + console.error("Set AZURE_DEVOPS_PAT (or AZDO_PAT) to use Azure DevOps PR automation."); + process.exitCode = 1; + return; + } + + const [organization, project, name] = repo.split("/"); + if (!organization || !project || !name) { + console.error("Invalid Azure DevOps repo format. Use org/project/repo."); + process.exitCode = 1; + return; + } + + const repoInfo = await getAzureRepo(token, organization, project, name); + const cacheRoot = path.join(process.cwd(), ".primer-cache"); + const repoPath = validateCachePath(cacheRoot, organization, project, name); + await ensureDir(repoPath); + + if (!(await isGitRepo(repoPath))) { + const authedUrl = buildAuthedUrl(repoInfo.cloneUrl, token, "azure"); + await cloneRepo(authedUrl, repoPath); + await setRemoteUrl(repoPath, repoInfo.cloneUrl); + } + + const branch = options.branch ?? "primer/add-instructions"; + await checkoutBranch(repoPath, branch); + + const instructions = await generateCopilotInstructions({ repoPath, model: DEFAULT_MODEL }); + const instructionsPath = path.join(repoPath, ".github", "copilot-instructions.md"); + await ensureDir(path.dirname(instructionsPath)); + await fs.writeFile(instructionsPath, instructions, "utf8"); + + await commitAll(repoPath, "chore: add copilot instructions via Primer"); + await pushBranch(repoPath, branch, token, "azure"); + + const prUrl = await createAzurePullRequest({ + token, + organization, + project, + repoId: repoInfo.id, + repoName: repoInfo.name, + title: "🤖 Add Copilot instructions via Primer", + body: buildInstructionsPrBody(), + sourceBranch: branch, + targetBranch: repoInfo.defaultBranch + }); + + console.log(`Created PR: ${prUrl}`); + return; + } + const token = process.env.GITHUB_TOKEN ?? process.env.GH_TOKEN; if (!token) { console.error("Set GITHUB_TOKEN or GH_TOKEN to use PR automation."); @@ -32,7 +111,7 @@ export async function prCommand(repo: string | undefined, options: PrOptions): P const repoInfo = await getRepo(token, owner, name); const cacheRoot = path.join(process.cwd(), ".primer-cache"); - const repoPath = path.join(cacheRoot, owner, name); + const repoPath = validateCachePath(cacheRoot, owner, name); await ensureDir(repoPath); if (!(await isGitRepo(repoPath))) { @@ -51,41 +130,17 @@ export async function prCommand(repo: string | undefined, options: PrOptions): P }); await commitAll(repoPath, "chore: add AI configurations via Primer"); - await pushBranch(repoPath, branch); + await pushBranch(repoPath, branch, token, "github"); const prUrl = await createPullRequest({ token, owner, repo: name, title: "🤖 Prime this repo for AI", - body: buildPrBody(), + body: buildConfigsPrBody(), head: `${owner}:${branch}`, base: repoInfo.defaultBranch }); console.log(`Created PR: ${prUrl}`); } - -function buildPrBody(): string { - return [ - "## 🤖 Primed for AI", - "", - "This PR adds configurations to prime this repository for AI coding assistants.", - "", - "### Added Files", - "", - "| File | Purpose |", - "|------|---------|", - "| `.vscode/settings.json` | VS Code settings for optimal AI assistance |", - "| `.vscode/mcp.json` | Model Context Protocol server configuration |", - "", - "### How to Use", - "", - "1. Merge this PR", - "2. Open the project in VS Code", - "3. Start chatting with Copilot — it now understands your project!", - "", - "---", - "*Generated by Primer*" - ].join("\n"); -} diff --git a/src/commands/readiness.ts b/src/commands/readiness.ts new file mode 100644 index 0000000..d0d73a0 --- /dev/null +++ b/src/commands/readiness.ts @@ -0,0 +1,147 @@ +import fs from "fs/promises"; +import path from "path"; + +import chalk from "chalk"; + +import type { ReadinessReport, ReadinessCriterionResult } from "../services/readiness"; +import { runReadinessReport } from "../services/readiness"; +import { generateVisualReport } from "../services/visualReport"; + +type ReadinessOptions = { + json?: boolean; + output?: string; + visual?: boolean; +}; + +export async function readinessCommand( + repoPathArg: string | undefined, + options: ReadinessOptions +): Promise { + const repoPath = path.resolve(repoPathArg ?? process.cwd()); + const report = await runReadinessReport({ repoPath }); + const repoName = path.basename(repoPath); + + // Generate visual HTML report + if (options.visual || (options.output && options.output.endsWith(".html"))) { + const html = generateVisualReport({ + reports: [{ repo: repoName, report }], + title: `AI Readiness Report: ${repoName}`, + generatedAt: new Date().toISOString() + }); + + const outputPath = options.output + ? path.resolve(options.output) + : path.join(repoPath, "readiness-report.html"); + + await fs.writeFile(outputPath, html, "utf8"); + console.log(chalk.green(`✓ Visual report generated: ${outputPath}`)); + return; + } + + // Output JSON + if (options.output && options.output.endsWith(".json")) { + const outputPath = path.resolve(options.output); + await fs.writeFile(outputPath, JSON.stringify(report, null, 2), "utf8"); + console.log(chalk.green(`✓ JSON report saved: ${outputPath}`)); + return; + } + + if (options.json) { + console.log(JSON.stringify(report, null, 2)); + return; + } + + printReadinessChecklist(report); +} + +function printReadinessChecklist(report: ReadinessReport): void { + console.log(chalk.bold("Readiness report")); + console.log(`- Repo: ${report.repoPath}`); + console.log( + `- Monorepo: ${report.isMonorepo ? "yes" : "no"}${report.apps.length ? ` (${report.apps.length} apps)` : ""}` + ); + console.log(`- Level: ${report.achievedLevel || 1} (${levelName(report.achievedLevel || 1)})`); + + console.log(chalk.bold("\nPillars")); + for (const pillar of report.pillars) { + const rate = formatPercent(pillar.passRate); + const icon = pillar.passRate >= 0.8 ? chalk.green("●") : chalk.yellow("●"); + console.log(`${icon} ${pillar.name}: ${pillar.passed}/${pillar.total} (${rate})`); + } + + console.log(chalk.bold("\nFix first")); + const fixes = rankFixes(report.criteria); + if (!fixes.length) { + console.log(chalk.green("✔ No failing criteria detected.")); + } else { + for (const fix of fixes) { + const impact = colorImpact(fix.impact); + const effort = colorEffort(fix.effort); + const scope = fix.scope === "app" ? "app" : "repo"; + const detail = fix.appSummary + ? ` (${fix.appSummary.passed}/${fix.appSummary.total} apps)` + : ""; + console.log(`- ${impact} impact / ${effort} effort • ${fix.title}${detail} [${scope}]`); + if (fix.reason) { + console.log(` ${chalk.dim(fix.reason)}`); + } + if (fix.appFailures?.length) { + console.log(` ${chalk.dim(`Apps: ${fix.appFailures.join(", ")}`)}`); + } + } + } + + if (report.extras.length) { + console.log(chalk.bold("\nAI readiness extras")); + for (const extra of report.extras) { + const icon = extra.status === "pass" ? chalk.green("✔") : chalk.red("✖"); + console.log(`${icon} ${extra.title}`); + } + } +} + +function rankFixes(criteria: ReadinessCriterionResult[]): ReadinessCriterionResult[] { + return criteria + .filter((criterion) => criterion.status === "fail") + .sort((a, b) => { + const impactDelta = impactWeight(b.impact) - impactWeight(a.impact); + if (impactDelta !== 0) return impactDelta; + return effortWeight(a.effort) - effortWeight(b.effort); + }); +} + +function impactWeight(value: "high" | "medium" | "low"): number { + if (value === "high") return 3; + if (value === "medium") return 2; + return 1; +} + +function effortWeight(value: "low" | "medium" | "high"): number { + if (value === "low") return 1; + if (value === "medium") return 2; + return 3; +} + +function colorImpact(value: "high" | "medium" | "low"): string { + if (value === "high") return chalk.red("High"); + if (value === "medium") return chalk.yellow("Medium"); + return chalk.green("Low"); +} + +function colorEffort(value: "low" | "medium" | "high"): string { + if (value === "high") return chalk.red("High"); + if (value === "medium") return chalk.yellow("Medium"); + return chalk.green("Low"); +} + +function formatPercent(value: number): string { + return `${Math.round(value * 100)}%`; +} + +function levelName(level: number): string { + if (level === 2) return "Documented"; + if (level === 3) return "Standardized"; + if (level === 4) return "Optimized"; + if (level === 5) return "Autonomous"; + return "Functional"; +} diff --git a/src/commands/templates.ts b/src/commands/templates.ts deleted file mode 100644 index ad8c643..0000000 --- a/src/commands/templates.ts +++ /dev/null @@ -1,3 +0,0 @@ -export async function templatesCommand(): Promise { - console.log("Available templates: mcp, vscode"); -} diff --git a/src/commands/tui.tsx b/src/commands/tui.tsx index db6c55e..4a2704d 100644 --- a/src/commands/tui.tsx +++ b/src/commands/tui.tsx @@ -1,6 +1,8 @@ import path from "path"; -import React from "react"; + import { render } from "ink"; +import React from "react"; + import { PrimerTui } from "../ui/tui"; type TuiOptions = { diff --git a/src/commands/update.ts b/src/commands/update.ts deleted file mode 100644 index b828b40..0000000 --- a/src/commands/update.ts +++ /dev/null @@ -1,3 +0,0 @@ -export async function updateCommand(): Promise { - console.log("Update is not implemented yet."); -} diff --git a/src/config.ts b/src/config.ts new file mode 100644 index 0000000..a46583c --- /dev/null +++ b/src/config.ts @@ -0,0 +1,2 @@ +export const DEFAULT_MODEL = "claude-sonnet-4.5"; +export const DEFAULT_JUDGE_MODEL = "claude-sonnet-4.5"; diff --git a/src/index.ts b/src/index.ts index ce2fa64..7c9a934 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,7 +2,7 @@ import { runCli } from "./cli"; const [, , ...args] = process.argv; if (args.length === 0) { - runCli([process.argv[0], process.argv[1], "tui"]); + runCli([process.argv[0], process.argv[1], "tui"]); } else { - runCli(process.argv); + runCli(process.argv); } diff --git a/src/services/__tests__/analyzer.test.ts b/src/services/__tests__/analyzer.test.ts new file mode 100644 index 0000000..748d7ba --- /dev/null +++ b/src/services/__tests__/analyzer.test.ts @@ -0,0 +1,311 @@ +import fs from "fs/promises"; +import os from "os"; +import path from "path"; + +import { afterEach, describe, expect, it } from "vitest"; + +import { analyzeRepo } from "../analyzer"; + +describe("analyzeRepo", () => { + const tmpDirs: string[] = []; + + async function makeTmpDir(): Promise { + const dir = await fs.mkdtemp(path.join(os.tmpdir(), "primer-test-")); + tmpDirs.push(dir); + return dir; + } + + afterEach(async () => { + for (const dir of tmpDirs) { + await fs.rm(dir, { recursive: true, force: true }).catch(() => {}); + } + tmpDirs.length = 0; + }); + + it("detects TypeScript and npm workspace", async () => { + const repoPath = await makeTmpDir(); + const packageJson = { + name: "demo", + workspaces: ["packages/*"], + dependencies: { react: "^19.0.0" } + }; + + await fs.writeFile(path.join(repoPath, "package.json"), JSON.stringify(packageJson, null, 2)); + await fs.writeFile(path.join(repoPath, "tsconfig.json"), "{}", "utf8"); + await fs.mkdir(path.join(repoPath, "packages", "app"), { recursive: true }); + await fs.writeFile( + path.join(repoPath, "packages", "app", "package.json"), + JSON.stringify({ name: "app", scripts: { build: "tsc" } }, null, 2) + ); + + const result = await analyzeRepo(repoPath); + + expect(result.languages).toContain("TypeScript"); + expect(result.workspaceType).toBe("npm"); + expect(result.apps?.length).toBe(1); + }); + + it("detects C# language", async () => { + const repoPath = await makeTmpDir(); + await fs.writeFile(path.join(repoPath, "MyProject.csproj"), "", "utf8"); + + const result = await analyzeRepo(repoPath); + expect(result.languages).toContain("C#"); + }); + + it("detects Java via pom.xml", async () => { + const repoPath = await makeTmpDir(); + await fs.writeFile(path.join(repoPath, "pom.xml"), "", "utf8"); + + const result = await analyzeRepo(repoPath); + expect(result.languages).toContain("Java"); + expect(result.packageManager).toBe("maven"); + }); + + it("detects Java via build.gradle", async () => { + const repoPath = await makeTmpDir(); + await fs.writeFile(path.join(repoPath, "build.gradle"), "plugins {}", "utf8"); + + const result = await analyzeRepo(repoPath); + expect(result.languages).toContain("Java"); + expect(result.packageManager).toBe("gradle"); + }); + + it("detects Ruby", async () => { + const repoPath = await makeTmpDir(); + await fs.writeFile(path.join(repoPath, "Gemfile"), "source 'https://rubygems.org'", "utf8"); + + const result = await analyzeRepo(repoPath); + expect(result.languages).toContain("Ruby"); + expect(result.packageManager).toBe("bundler"); + }); + + it("detects PHP", async () => { + const repoPath = await makeTmpDir(); + await fs.writeFile(path.join(repoPath, "composer.json"), "{}", "utf8"); + + const result = await analyzeRepo(repoPath); + expect(result.languages).toContain("PHP"); + expect(result.packageManager).toBe("composer"); + }); + + it("returns empty analysis for empty directory", async () => { + const repoPath = await makeTmpDir(); + + const result = await analyzeRepo(repoPath); + expect(result.languages).toEqual([]); + expect(result.frameworks).toEqual([]); + expect(result.packageManager).toBeUndefined(); + }); + + it("detects pnpm workspace with comments in YAML", async () => { + const repoPath = await makeTmpDir(); + await fs.writeFile(path.join(repoPath, "package.json"), JSON.stringify({ name: "root" })); + await fs.writeFile(path.join(repoPath, "pnpm-lock.yaml"), "lockfileVersion: 9"); + await fs.writeFile( + path.join(repoPath, "pnpm-workspace.yaml"), + ["# workspace config", "packages:", " - 'apps/*' # main apps", " - 'libs/*'", "# end"].join( + "\n" + ) + ); + await fs.mkdir(path.join(repoPath, "apps", "web"), { recursive: true }); + await fs.writeFile( + path.join(repoPath, "apps", "web", "package.json"), + JSON.stringify({ name: "web", scripts: { build: "tsc" } }) + ); + + const result = await analyzeRepo(repoPath); + expect(result.workspaceType).toBe("pnpm"); + expect(result.workspacePatterns).toContain("apps/*"); + expect(result.workspacePatterns).toContain("libs/*"); + // Should not include comment text in patterns + expect(result.workspacePatterns?.some((p) => p.includes("#"))).toBe(false); + }); + + it("detects pnpm inline array workspace", async () => { + const repoPath = await makeTmpDir(); + await fs.writeFile(path.join(repoPath, "package.json"), JSON.stringify({ name: "root" })); + await fs.writeFile(path.join(repoPath, "pnpm-lock.yaml"), "lockfileVersion: 9"); + await fs.writeFile( + path.join(repoPath, "pnpm-workspace.yaml"), + 'packages: ["apps/*", "libs/*"]\n' + ); + + const result = await analyzeRepo(repoPath); + expect(result.workspaceType).toBe("pnpm"); + expect(result.workspacePatterns).toContain("apps/*"); + expect(result.workspacePatterns).toContain("libs/*"); + }); + + it("detects Cargo workspace (Rust monorepo)", async () => { + const repoPath = await makeTmpDir(); + await fs.writeFile( + path.join(repoPath, "Cargo.toml"), + [ + "[workspace]", + 'members = ["crates/core", "crates/cli"]', + "", + "[package]", + 'name = "root"' + ].join("\n") + ); + await fs.mkdir(path.join(repoPath, "crates", "core"), { recursive: true }); + await fs.writeFile( + path.join(repoPath, "crates", "core", "Cargo.toml"), + '[package]\nname = "my-core"\nversion = "0.1.0"' + ); + await fs.mkdir(path.join(repoPath, "crates", "cli"), { recursive: true }); + await fs.writeFile( + path.join(repoPath, "crates", "cli", "Cargo.toml"), + '[package]\nname = "my-cli"\nversion = "0.1.0"' + ); + + const result = await analyzeRepo(repoPath); + expect(result.languages).toContain("Rust"); + expect(result.isMonorepo).toBe(true); + expect(result.workspaceType).toBe("cargo"); + expect(result.apps?.length).toBe(2); + expect(result.apps?.map((a) => a.name).sort()).toEqual(["my-cli", "my-core"]); + expect(result.apps?.[0].ecosystem).toBe("rust"); + }); + + it("detects Go workspace (go.work)", async () => { + const repoPath = await makeTmpDir(); + await fs.writeFile( + path.join(repoPath, "go.work"), + ["go 1.21", "", "use (", " ./cmd/server", " ./pkg/lib", ")"].join("\n") + ); + await fs.mkdir(path.join(repoPath, "cmd", "server"), { recursive: true }); + await fs.writeFile( + path.join(repoPath, "cmd", "server", "go.mod"), + "module github.com/example/server\n\ngo 1.21" + ); + await fs.mkdir(path.join(repoPath, "pkg", "lib"), { recursive: true }); + await fs.writeFile( + path.join(repoPath, "pkg", "lib", "go.mod"), + "module github.com/example/lib\n\ngo 1.21" + ); + + const result = await analyzeRepo(repoPath); + expect(result.isMonorepo).toBe(true); + expect(result.workspaceType).toBe("go"); + expect(result.apps?.length).toBe(2); + expect(result.apps?.map((a) => a.name).sort()).toEqual(["lib", "server"]); + expect(result.apps?.[0].ecosystem).toBe("go"); + }); + + it("detects .NET solution (monorepo)", async () => { + const repoPath = await makeTmpDir(); + const slnContent = [ + "Microsoft Visual Studio Solution File, Format Version 12.00", + "# Visual Studio Version 17", + 'Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApp", "src\\WebApp\\WebApp.csproj", "{GUID1}"', + "EndProject", + 'Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CoreLib", "src\\CoreLib\\CoreLib.csproj", "{GUID2}"', + "EndProject" + ].join("\n"); + await fs.writeFile(path.join(repoPath, "MySolution.sln"), slnContent); + await fs.mkdir(path.join(repoPath, "src", "WebApp"), { recursive: true }); + await fs.writeFile(path.join(repoPath, "src", "WebApp", "WebApp.csproj"), ""); + await fs.mkdir(path.join(repoPath, "src", "CoreLib"), { recursive: true }); + await fs.writeFile(path.join(repoPath, "src", "CoreLib", "CoreLib.csproj"), ""); + + const result = await analyzeRepo(repoPath); + expect(result.languages).toContain("C#"); + expect(result.isMonorepo).toBe(true); + expect(result.workspaceType).toBe("dotnet"); + expect(result.apps?.length).toBe(2); + expect(result.apps?.map((a) => a.name).sort()).toEqual(["CoreLib", "WebApp"]); + expect(result.apps?.[0].ecosystem).toBe("dotnet"); + }); + + it("detects Gradle multi-project", async () => { + const repoPath = await makeTmpDir(); + await fs.writeFile( + path.join(repoPath, "settings.gradle"), + "rootProject.name = 'my-app'\ninclude ':app', ':lib'" + ); + await fs.writeFile(path.join(repoPath, "build.gradle"), "plugins {}", "utf8"); + await fs.mkdir(path.join(repoPath, "app"), { recursive: true }); + await fs.writeFile(path.join(repoPath, "app", "build.gradle"), "plugins {}"); + await fs.mkdir(path.join(repoPath, "lib"), { recursive: true }); + await fs.writeFile(path.join(repoPath, "lib", "build.gradle"), "plugins {}"); + + const result = await analyzeRepo(repoPath); + expect(result.languages).toContain("Java"); + expect(result.isMonorepo).toBe(true); + expect(result.workspaceType).toBe("gradle"); + expect(result.apps?.length).toBe(2); + expect(result.apps?.map((a) => a.name).sort()).toEqual(["app", "lib"]); + expect(result.apps?.[0].ecosystem).toBe("java"); + }); + + it("detects Gradle Kotlin DSL multi-project", async () => { + const repoPath = await makeTmpDir(); + await fs.writeFile( + path.join(repoPath, "settings.gradle.kts"), + 'rootProject.name = "my-app"\ninclude(":app", ":server")' + ); + await fs.writeFile(path.join(repoPath, "build.gradle.kts"), "plugins {}", "utf8"); + await fs.mkdir(path.join(repoPath, "app"), { recursive: true }); + await fs.writeFile(path.join(repoPath, "app", "build.gradle.kts"), "plugins {}"); + await fs.mkdir(path.join(repoPath, "server"), { recursive: true }); + await fs.writeFile(path.join(repoPath, "server", "build.gradle.kts"), "plugins {}"); + + const result = await analyzeRepo(repoPath); + expect(result.isMonorepo).toBe(true); + expect(result.workspaceType).toBe("gradle"); + expect(result.apps?.length).toBe(2); + expect(result.apps?.map((a) => a.name).sort()).toEqual(["app", "server"]); + }); + + it("detects Maven multi-module", async () => { + const repoPath = await makeTmpDir(); + await fs.writeFile( + path.join(repoPath, "pom.xml"), + [ + "", + " ", + " api", + " web", + " ", + "" + ].join("\n") + ); + await fs.mkdir(path.join(repoPath, "api"), { recursive: true }); + await fs.writeFile(path.join(repoPath, "api", "pom.xml"), ""); + await fs.mkdir(path.join(repoPath, "web"), { recursive: true }); + await fs.writeFile(path.join(repoPath, "web", "pom.xml"), ""); + + const result = await analyzeRepo(repoPath); + expect(result.languages).toContain("Java"); + expect(result.isMonorepo).toBe(true); + expect(result.workspaceType).toBe("maven"); + expect(result.apps?.length).toBe(2); + expect(result.apps?.map((a) => a.name).sort()).toEqual(["api", "web"]); + }); + + it("sets ecosystem to node for JS workspace apps", async () => { + const repoPath = await makeTmpDir(); + const packageJson = { + name: "demo", + workspaces: ["packages/*"] + }; + await fs.writeFile(path.join(repoPath, "package.json"), JSON.stringify(packageJson)); + await fs.mkdir(path.join(repoPath, "packages", "a"), { recursive: true }); + await fs.writeFile( + path.join(repoPath, "packages", "a", "package.json"), + JSON.stringify({ name: "a" }) + ); + await fs.mkdir(path.join(repoPath, "packages", "b"), { recursive: true }); + await fs.writeFile( + path.join(repoPath, "packages", "b", "package.json"), + JSON.stringify({ name: "b" }) + ); + + const result = await analyzeRepo(repoPath); + expect(result.isMonorepo).toBe(true); + expect(result.apps?.[0].ecosystem).toBe("node"); + expect(result.apps?.[0].manifestPath).toBeTruthy(); + }); +}); diff --git a/src/services/__tests__/cachePath.test.ts b/src/services/__tests__/cachePath.test.ts new file mode 100644 index 0000000..063419e --- /dev/null +++ b/src/services/__tests__/cachePath.test.ts @@ -0,0 +1,42 @@ +import os from "os"; +import path from "path"; + +import { describe, expect, it } from "vitest"; + +import { validateCachePath } from "../../utils/fs"; + +const cacheRoot = path.join(os.tmpdir(), "primer-cache"); + +describe("validateCachePath", () => { + it("returns resolved path for normal segments", () => { + const result = validateCachePath(cacheRoot, "owner", "repo"); + expect(result).toBe(path.resolve(cacheRoot, "owner", "repo")); + }); + + it("throws on path traversal via ..", () => { + expect(() => validateCachePath(cacheRoot, "..", "..", "etc")).toThrow( + "escapes cache directory" + ); + }); + + it("throws on absolute path segment that escapes", () => { + const escaping = path.resolve(cacheRoot, "..", "..", "etc"); + expect(() => validateCachePath(cacheRoot, escaping)).toThrow("escapes cache directory"); + }); + + it("allows the cache root itself", () => { + const result = validateCachePath(cacheRoot); + expect(result).toBe(path.resolve(cacheRoot)); + }); + + it("allows nested paths within cache root", () => { + const result = validateCachePath(cacheRoot, "org", "project", "repo"); + expect(result).toBe(path.resolve(cacheRoot, "org", "project", "repo")); + }); + + it("throws when segment contains .. to escape", () => { + expect(() => validateCachePath(cacheRoot, "owner", "repo", "..", "..", "..", "etc")).toThrow( + "escapes cache directory" + ); + }); +}); diff --git a/src/services/__tests__/fs.test.ts b/src/services/__tests__/fs.test.ts new file mode 100644 index 0000000..1c6592a --- /dev/null +++ b/src/services/__tests__/fs.test.ts @@ -0,0 +1,74 @@ +import fs from "fs/promises"; +import os from "os"; +import path from "path"; + +import { afterEach, beforeEach, describe, expect, it } from "vitest"; + +import { ensureDir, safeWriteFile } from "../../utils/fs"; + +describe("ensureDir", () => { + let tmpDir: string; + + beforeEach(async () => { + tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "primer-fs-")); + }); + + afterEach(async () => { + await fs.rm(tmpDir, { recursive: true, force: true }); + }); + + it("creates a directory that does not exist", async () => { + const target = path.join(tmpDir, "a", "b", "c"); + await ensureDir(target); + + const stat = await fs.stat(target); + expect(stat.isDirectory()).toBe(true); + }); + + it("does not throw if directory already exists", async () => { + const target = path.join(tmpDir, "existing"); + await fs.mkdir(target); + await expect(ensureDir(target)).resolves.toBeUndefined(); + }); +}); + +describe("safeWriteFile", () => { + let tmpDir: string; + + beforeEach(async () => { + tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "primer-fs-")); + }); + + afterEach(async () => { + await fs.rm(tmpDir, { recursive: true, force: true }); + }); + + it("writes a new file", async () => { + const filePath = path.join(tmpDir, "test.txt"); + const result = await safeWriteFile(filePath, "hello", false); + + const content = await fs.readFile(filePath, "utf8"); + expect(content).toBe("hello"); + expect(result).toContain("Wrote"); + }); + + it("skips existing file without force", async () => { + const filePath = path.join(tmpDir, "test.txt"); + await fs.writeFile(filePath, "original"); + const result = await safeWriteFile(filePath, "new content", false); + + const content = await fs.readFile(filePath, "utf8"); + expect(content).toBe("original"); + expect(result).toContain("Skipped"); + }); + + it("overwrites existing file with force", async () => { + const filePath = path.join(tmpDir, "test.txt"); + await fs.writeFile(filePath, "original"); + const result = await safeWriteFile(filePath, "new content", true); + + const content = await fs.readFile(filePath, "utf8"); + expect(content).toBe("new content"); + expect(result).toContain("Wrote"); + }); +}); diff --git a/src/services/__tests__/generator.test.ts b/src/services/__tests__/generator.test.ts new file mode 100644 index 0000000..ddca1e4 --- /dev/null +++ b/src/services/__tests__/generator.test.ts @@ -0,0 +1,129 @@ +import fs from "fs/promises"; +import os from "os"; +import path from "path"; + +import { afterEach, beforeEach, describe, expect, it } from "vitest"; + +import type { RepoAnalysis } from "../analyzer"; +import { generateConfigs } from "../generator"; + +describe("generateConfigs", () => { + let tmpDir: string; + + beforeEach(async () => { + tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "primer-gen-")); + }); + + afterEach(async () => { + await fs.rm(tmpDir, { recursive: true, force: true }); + }); + + function makeAnalysis(overrides?: Partial): RepoAnalysis { + return { + path: tmpDir, + isGitRepo: false, + languages: ["TypeScript"], + frameworks: [], + ...overrides + }; + } + + it("generates valid mcp.json", async () => { + const analysis = makeAnalysis(); + const { summary } = await generateConfigs({ + repoPath: tmpDir, + analysis, + selections: ["mcp"], + force: false + }); + + const content = await fs.readFile(path.join(tmpDir, ".vscode", "mcp.json"), "utf8"); + const parsed = JSON.parse(content); + + expect(parsed.servers).toBeDefined(); + expect(parsed.servers.github).toBeDefined(); + expect(parsed.servers.filesystem).toBeDefined(); + expect(summary).toContain("Wrote"); + }); + + it("generates valid vscode settings with frameworks", async () => { + const analysis = makeAnalysis({ frameworks: ["React", "Next.js"] }); + await generateConfigs({ + repoPath: tmpDir, + analysis, + selections: ["vscode"], + force: false + }); + + const content = await fs.readFile(path.join(tmpDir, ".vscode", "settings.json"), "utf8"); + const parsed = JSON.parse(content); + + expect(parsed["github.copilot.chat.codeGeneration.instructions"]).toBeDefined(); + expect(parsed["chat.mcp.enabled"]).toBe(true); + // Should mention frameworks in review instructions + const reviewText = parsed["github.copilot.chat.reviewSelection.instructions"][0].text; + expect(reviewText).toContain("React"); + expect(reviewText).toContain("Next.js"); + }); + + it("generates fallback review text when no frameworks", async () => { + const analysis = makeAnalysis({ frameworks: [] }); + await generateConfigs({ + repoPath: tmpDir, + analysis, + selections: ["vscode"], + force: false + }); + + const content = await fs.readFile(path.join(tmpDir, ".vscode", "settings.json"), "utf8"); + const parsed = JSON.parse(content); + const reviewText = parsed["github.copilot.chat.reviewSelection.instructions"][0].text; + expect(reviewText).toContain("repo conventions"); + }); + + it("skips existing files without force", async () => { + await fs.mkdir(path.join(tmpDir, ".vscode"), { recursive: true }); + await fs.writeFile(path.join(tmpDir, ".vscode", "mcp.json"), "original", "utf8"); + + const analysis = makeAnalysis(); + const { summary } = await generateConfigs({ + repoPath: tmpDir, + analysis, + selections: ["mcp"], + force: false + }); + + const content = await fs.readFile(path.join(tmpDir, ".vscode", "mcp.json"), "utf8"); + expect(content).toBe("original"); + expect(summary).toContain("Skipped"); + }); + + it("overwrites existing files with force", async () => { + await fs.mkdir(path.join(tmpDir, ".vscode"), { recursive: true }); + await fs.writeFile(path.join(tmpDir, ".vscode", "mcp.json"), "original", "utf8"); + + const analysis = makeAnalysis(); + const { summary } = await generateConfigs({ + repoPath: tmpDir, + analysis, + selections: ["mcp"], + force: true + }); + + const content = await fs.readFile(path.join(tmpDir, ".vscode", "mcp.json"), "utf8"); + expect(content).not.toBe("original"); + expect(summary).toContain("Wrote"); + }); + + it("does nothing with empty selections", async () => { + const analysis = makeAnalysis(); + const { summary } = await generateConfigs({ + repoPath: tmpDir, + analysis, + selections: [], + force: false + }); + + expect(summary).toBe("No changes made."); + }); +}); diff --git a/src/services/__tests__/git.test.ts b/src/services/__tests__/git.test.ts new file mode 100644 index 0000000..51adfdc --- /dev/null +++ b/src/services/__tests__/git.test.ts @@ -0,0 +1,47 @@ +import { describe, expect, it } from "vitest"; + +import { buildAuthedUrl } from "../git"; + +describe("buildAuthedUrl", () => { + it("adds github x-access-token to https URL", () => { + expect(buildAuthedUrl("https://github.com/owner/repo", "tok123", "github")).toBe( + "https://x-access-token:tok123@github.com/owner/repo" + ); + }); + + it("adds azure PAT to https URL", () => { + expect(buildAuthedUrl("https://dev.azure.com/org/project/_git/repo", "pat123", "azure")).toBe( + "https://pat:pat123@dev.azure.com/org/project/_git/repo" + ); + }); + + it("strips trailing slashes before adding auth", () => { + expect(buildAuthedUrl("https://github.com/owner/repo///", "tok", "github")).toBe( + "https://x-access-token:tok@github.com/owner/repo" + ); + }); + + it("replaces existing x-access-token auth", () => { + expect( + buildAuthedUrl("https://x-access-token:old@github.com/owner/repo", "new-tok", "github") + ).toBe("https://x-access-token:new-tok@github.com/owner/repo"); + }); + + it("replaces existing PAT auth for azure", () => { + expect(buildAuthedUrl("https://pat:old@dev.azure.com/repo", "new-pat", "azure")).toBe( + "https://pat:new-pat@dev.azure.com/repo" + ); + }); + + it("returns non-https URLs unchanged", () => { + expect(buildAuthedUrl("git@github.com:owner/repo.git", "tok", "github")).toBe( + "git@github.com:owner/repo.git" + ); + }); + + it("handles whitespace in URL", () => { + expect(buildAuthedUrl(" https://github.com/owner/repo ", "tok", "github")).toBe( + "https://x-access-token:tok@github.com/owner/repo" + ); + }); +}); diff --git a/src/services/__tests__/readiness.test.ts b/src/services/__tests__/readiness.test.ts new file mode 100644 index 0000000..9b0fbf4 --- /dev/null +++ b/src/services/__tests__/readiness.test.ts @@ -0,0 +1,361 @@ +import fs from "fs/promises"; +import os from "os"; +import path from "path"; + +import { afterEach, beforeEach, describe, expect, it } from "vitest"; + +import { runReadinessReport } from "../readiness"; + +describe("runReadinessReport", () => { + let repoPath: string; + + beforeEach(async () => { + repoPath = await fs.mkdtemp(path.join(os.tmpdir(), "primer-readiness-")); + }); + + afterEach(async () => { + await fs.rm(repoPath, { recursive: true, force: true }); + }); + + async function writeFile(relativePath: string, content: string): Promise { + const fullPath = path.join(repoPath, relativePath); + await fs.mkdir(path.dirname(fullPath), { recursive: true }); + await fs.writeFile(fullPath, content, "utf8"); + } + + async function writePackageJson(pkg: Record): Promise { + await writeFile("package.json", JSON.stringify(pkg, null, 2)); + } + + it("returns a valid report structure", async () => { + await writePackageJson({ name: "test-repo", scripts: { build: "tsc", test: "vitest" } }); + const report = await runReadinessReport({ repoPath }); + + expect(report.repoPath).toBe(repoPath); + expect(report.generatedAt).toBeTruthy(); + expect(report.pillars).toBeInstanceOf(Array); + expect(report.levels).toBeInstanceOf(Array); + expect(report.criteria).toBeInstanceOf(Array); + expect(typeof report.achievedLevel).toBe("number"); + }); + + it("has all expected pillars", async () => { + await writePackageJson({ name: "test-repo" }); + const report = await runReadinessReport({ repoPath }); + + const pillarIds = report.pillars.map((p) => p.id); + expect(pillarIds).toContain("style-validation"); + expect(pillarIds).toContain("build-system"); + expect(pillarIds).toContain("testing"); + expect(pillarIds).toContain("documentation"); + expect(pillarIds).toContain("dev-environment"); + expect(pillarIds).toContain("code-quality"); + expect(pillarIds).toContain("observability"); + expect(pillarIds).toContain("security-governance"); + expect(pillarIds).toContain("ai-tooling"); + }); + + it("has 5 maturity levels", async () => { + await writePackageJson({ name: "test-repo" }); + const report = await runReadinessReport({ repoPath }); + + expect(report.levels).toHaveLength(5); + expect(report.levels.map((l) => l.level)).toEqual([1, 2, 3, 4, 5]); + }); + + describe("style-validation pillar", () => { + it("passes lint-config when eslint.config.js exists", async () => { + await writePackageJson({ name: "test-repo" }); + await writeFile("eslint.config.js", "export default [];"); + + const report = await runReadinessReport({ repoPath }); + const criterion = report.criteria.find((c) => c.id === "lint-config"); + + expect(criterion?.status).toBe("pass"); + }); + + it("fails lint-config when no lint config exists", async () => { + await writePackageJson({ name: "test-repo" }); + + const report = await runReadinessReport({ repoPath }); + const criterion = report.criteria.find((c) => c.id === "lint-config"); + + expect(criterion?.status).toBe("fail"); + }); + + it("passes typecheck-config when tsconfig.json exists", async () => { + await writePackageJson({ name: "test-repo" }); + await writeFile("tsconfig.json", "{}"); + + const report = await runReadinessReport({ repoPath }); + const criterion = report.criteria.find((c) => c.id === "typecheck-config"); + + expect(criterion?.status).toBe("pass"); + }); + }); + + describe("documentation pillar", () => { + it("passes readme when README.md exists", async () => { + await writePackageJson({ name: "test-repo" }); + await writeFile("README.md", "# Test"); + + const report = await runReadinessReport({ repoPath }); + const criterion = report.criteria.find((c) => c.id === "readme"); + + expect(criterion?.status).toBe("pass"); + }); + + it("passes contributing when CONTRIBUTING.md exists", async () => { + await writePackageJson({ name: "test-repo" }); + await writeFile("CONTRIBUTING.md", "# Contributing"); + + const report = await runReadinessReport({ repoPath }); + const criterion = report.criteria.find((c) => c.id === "contributing"); + + expect(criterion?.status).toBe("pass"); + }); + }); + + describe("dev-environment pillar", () => { + it("passes lockfile when package-lock.json exists", async () => { + await writePackageJson({ name: "test-repo" }); + await writeFile("package-lock.json", "{}"); + + const report = await runReadinessReport({ repoPath }); + const criterion = report.criteria.find((c) => c.id === "lockfile"); + + expect(criterion?.status).toBe("pass"); + }); + + it("passes env-example when .env.example exists", async () => { + await writePackageJson({ name: "test-repo" }); + await writeFile(".env.example", "API_KEY=your-key"); + + const report = await runReadinessReport({ repoPath }); + const criterion = report.criteria.find((c) => c.id === "env-example"); + + expect(criterion?.status).toBe("pass"); + }); + }); + + describe("security-governance pillar", () => { + it("passes codeowners when CODEOWNERS exists", async () => { + await writePackageJson({ name: "test-repo" }); + await writeFile("CODEOWNERS", "* @owner"); + + const report = await runReadinessReport({ repoPath }); + const criterion = report.criteria.find((c) => c.id === "codeowners"); + + expect(criterion?.status).toBe("pass"); + }); + + it("passes codeowners when .github/CODEOWNERS exists", async () => { + await writePackageJson({ name: "test-repo" }); + await writeFile(".github/CODEOWNERS", "* @owner"); + + const report = await runReadinessReport({ repoPath }); + const criterion = report.criteria.find((c) => c.id === "codeowners"); + + expect(criterion?.status).toBe("pass"); + }); + + it("passes license when LICENSE exists", async () => { + await writePackageJson({ name: "test-repo" }); + await writeFile("LICENSE", "MIT License"); + + const report = await runReadinessReport({ repoPath }); + const criterion = report.criteria.find((c) => c.id === "license"); + + expect(criterion?.status).toBe("pass"); + }); + + it("passes security-policy when SECURITY.md exists", async () => { + await writePackageJson({ name: "test-repo" }); + await writeFile("SECURITY.md", "# Security"); + + const report = await runReadinessReport({ repoPath }); + const criterion = report.criteria.find((c) => c.id === "security-policy"); + + expect(criterion?.status).toBe("pass"); + }); + + it("passes dependabot when .github/dependabot.yml exists", async () => { + await writePackageJson({ name: "test-repo" }); + await writeFile(".github/dependabot.yml", "version: 2"); + + const report = await runReadinessReport({ repoPath }); + const criterion = report.criteria.find((c) => c.id === "dependabot"); + + expect(criterion?.status).toBe("pass"); + }); + }); + + describe("ai-tooling pillar", () => { + it("passes custom-instructions when copilot-instructions.md exists", async () => { + await writePackageJson({ name: "test-repo" }); + await writeFile(".github/copilot-instructions.md", "# Instructions"); + + const report = await runReadinessReport({ repoPath }); + const criterion = report.criteria.find((c) => c.id === "custom-instructions"); + + expect(criterion?.status).toBe("pass"); + }); + + it("passes custom-instructions when CLAUDE.md exists", async () => { + await writePackageJson({ name: "test-repo" }); + await writeFile("CLAUDE.md", "# Claude instructions"); + + const report = await runReadinessReport({ repoPath }); + const criterion = report.criteria.find((c) => c.id === "custom-instructions"); + + expect(criterion?.status).toBe("pass"); + }); + + it("passes custom-instructions when AGENTS.md exists", async () => { + await writePackageJson({ name: "test-repo" }); + await writeFile("AGENTS.md", "# Agents guidance"); + + const report = await runReadinessReport({ repoPath }); + const criterion = report.criteria.find((c) => c.id === "custom-instructions"); + + expect(criterion?.status).toBe("pass"); + }); + + it("passes custom-instructions when .cursorrules exists", async () => { + await writePackageJson({ name: "test-repo" }); + await writeFile(".cursorrules", "rules here"); + + const report = await runReadinessReport({ repoPath }); + const criterion = report.criteria.find((c) => c.id === "custom-instructions"); + + expect(criterion?.status).toBe("pass"); + }); + + it("fails custom-instructions when none exist", async () => { + await writePackageJson({ name: "test-repo" }); + + const report = await runReadinessReport({ repoPath }); + const criterion = report.criteria.find((c) => c.id === "custom-instructions"); + + expect(criterion?.status).toBe("fail"); + }); + + it("passes mcp-config when .vscode/mcp.json exists", async () => { + await writePackageJson({ name: "test-repo" }); + await writeFile(".vscode/mcp.json", "{}"); + + const report = await runReadinessReport({ repoPath }); + const criterion = report.criteria.find((c) => c.id === "mcp-config"); + + expect(criterion?.status).toBe("pass"); + }); + + it("passes mcp-config when .vscode/settings.json has mcp key", async () => { + await writePackageJson({ name: "test-repo" }); + await writeFile( + ".vscode/settings.json", + JSON.stringify({ "github.copilot.chat.mcp.enabled": true }) + ); + + const report = await runReadinessReport({ repoPath }); + const criterion = report.criteria.find((c) => c.id === "mcp-config"); + + expect(criterion?.status).toBe("pass"); + }); + + it("passes custom-agents when .github/agents directory exists", async () => { + await writePackageJson({ name: "test-repo" }); + await writeFile(".github/agents/.gitkeep", ""); + + const report = await runReadinessReport({ repoPath }); + const criterion = report.criteria.find((c) => c.id === "custom-agents"); + + expect(criterion?.status).toBe("pass"); + }); + + it("passes copilot-skills when .copilot/skills directory exists", async () => { + await writePackageJson({ name: "test-repo" }); + await writeFile(".copilot/skills/.gitkeep", ""); + + const report = await runReadinessReport({ repoPath }); + const criterion = report.criteria.find((c) => c.id === "copilot-skills"); + + expect(criterion?.status).toBe("pass"); + }); + }); + + describe("build-system pillar", () => { + it("passes ci-config when .github/workflows exists", async () => { + await writePackageJson({ name: "test-repo" }); + await writeFile(".github/workflows/ci.yml", "name: CI"); + + const report = await runReadinessReport({ repoPath }); + const criterion = report.criteria.find((c) => c.id === "ci-config"); + + expect(criterion?.status).toBe("pass"); + }); + }); + + describe("achieved level", () => { + it("achieves level 1 with basic setup", async () => { + await writePackageJson({ + name: "test-repo", + scripts: { build: "tsc", test: "vitest" } + }); + await writeFile("eslint.config.js", "export default [];"); + await writeFile("README.md", "# Test"); + await writeFile("package-lock.json", "{}"); + await writeFile("LICENSE", "MIT"); + + const report = await runReadinessReport({ repoPath }); + + expect(report.achievedLevel).toBeGreaterThanOrEqual(1); + }); + + it("is 0 for an empty repo", async () => { + await writePackageJson({ name: "empty-repo" }); + + const report = await runReadinessReport({ repoPath }); + + // Level 0 means nothing achieved (most L1 checks fail) + expect(report.achievedLevel).toBeLessThanOrEqual(1); + }); + }); + + describe("pillar summaries", () => { + it("calculates passRate correctly", async () => { + await writePackageJson({ name: "test-repo" }); + await writeFile("eslint.config.js", "export default [];"); + + const report = await runReadinessReport({ repoPath }); + const stylePillar = report.pillars.find((p) => p.id === "style-validation"); + + expect(stylePillar).toBeDefined(); + expect(stylePillar!.passed).toBeGreaterThanOrEqual(1); + expect(stylePillar!.total).toBeGreaterThanOrEqual(1); + expect(stylePillar!.passRate).toBe(stylePillar!.passed / stylePillar!.total); + }); + }); + + describe("extras", () => { + it("includes extras by default", async () => { + await writePackageJson({ name: "test-repo" }); + + const report = await runReadinessReport({ repoPath }); + + expect(report.extras.length).toBeGreaterThan(0); + const extraIds = report.extras.map((e) => e.id); + expect(extraIds).toContain("pr-template"); + expect(extraIds).toContain("pre-commit"); + expect(extraIds).toContain("architecture-doc"); + }); + + it("excludes extras when disabled", async () => { + await writePackageJson({ name: "test-repo" }); + + const report = await runReadinessReport({ repoPath, includeExtras: false }); + + expect(report.extras).toHaveLength(0); + }); + }); +}); diff --git a/src/services/__tests__/visualReport.test.ts b/src/services/__tests__/visualReport.test.ts new file mode 100644 index 0000000..b2fd645 --- /dev/null +++ b/src/services/__tests__/visualReport.test.ts @@ -0,0 +1,242 @@ +import { describe, expect, it } from "vitest"; + +import type { ReadinessReport } from "../readiness"; +import { generateVisualReport } from "../visualReport"; + +function makeReport(overrides: Partial = {}): ReadinessReport { + return { + repoPath: "/tmp/test-repo", + generatedAt: "2026-01-01T00:00:00.000Z", + isMonorepo: false, + apps: [], + pillars: [ + { id: "style-validation", name: "Style & Validation", passed: 2, total: 2, passRate: 1 }, + { id: "build-system", name: "Build System", passed: 1, total: 2, passRate: 0.5 }, + { id: "testing", name: "Testing", passed: 0, total: 1, passRate: 0 }, + { id: "documentation", name: "Documentation", passed: 1, total: 2, passRate: 0.5 }, + { id: "dev-environment", name: "Dev Environment", passed: 1, total: 2, passRate: 0.5 }, + { id: "code-quality", name: "Code Quality", passed: 1, total: 1, passRate: 1 }, + { id: "observability", name: "Observability", passed: 0, total: 1, passRate: 0 }, + { + id: "security-governance", + name: "Security & Governance", + passed: 2, + total: 4, + passRate: 0.5 + }, + { id: "ai-tooling", name: "AI Tooling", passed: 1, total: 4, passRate: 0.25 } + ], + levels: [ + { level: 1, name: "Functional", passed: 5, total: 6, passRate: 0.83, achieved: true }, + { level: 2, name: "Documented", passed: 3, total: 6, passRate: 0.5, achieved: false }, + { level: 3, name: "Standardized", passed: 1, total: 4, passRate: 0.25, achieved: false }, + { level: 4, name: "Optimized", passed: 0, total: 0, passRate: 0, achieved: false }, + { level: 5, name: "Autonomous", passed: 0, total: 0, passRate: 0, achieved: false } + ], + achievedLevel: 1, + criteria: [ + { + id: "lint-config", + title: "Linting configured", + pillar: "style-validation", + level: 1, + scope: "repo", + impact: "high", + effort: "low", + status: "pass" + }, + { + id: "readme", + title: "README present", + pillar: "documentation", + level: 1, + scope: "repo", + impact: "high", + effort: "low", + status: "pass" + }, + { + id: "custom-instructions", + title: "Custom AI instructions", + pillar: "ai-tooling", + level: 1, + scope: "repo", + impact: "high", + effort: "low", + status: "pass" + }, + { + id: "mcp-config", + title: "MCP config present", + pillar: "ai-tooling", + level: 2, + scope: "repo", + impact: "high", + effort: "low", + status: "fail", + reason: "Missing MCP config." + } + ], + extras: [], + ...overrides + }; +} + +describe("generateVisualReport", () => { + it("returns valid HTML", () => { + const html = generateVisualReport({ + reports: [{ repo: "test-repo", report: makeReport() }] + }); + + expect(html).toContain(""); + expect(html).toContain(""); + }); + + it("includes the report title", () => { + const html = generateVisualReport({ + reports: [{ repo: "test-repo", report: makeReport() }], + title: "My Custom Report" + }); + + expect(html).toContain("My Custom Report"); + }); + + it("includes repo name", () => { + const html = generateVisualReport({ + reports: [{ repo: "my-repo", report: makeReport() }] + }); + + expect(html).toContain("my-repo"); + }); + + it("includes pillar names", () => { + const html = generateVisualReport({ + reports: [{ repo: "test-repo", report: makeReport() }] + }); + + expect(html).toContain("Style & Validation"); + expect(html).toContain("Build System"); + expect(html).toContain("AI Tooling"); + }); + + it("includes maturity level badge", () => { + const html = generateVisualReport({ + reports: [{ repo: "test-repo", report: makeReport({ achievedLevel: 2 }) }] + }); + + expect(html).toContain("Maturity 2"); + expect(html).toContain("Documented"); + }); + + it("includes AI Tooling Readiness hero section", () => { + const html = generateVisualReport({ + reports: [{ repo: "test-repo", report: makeReport() }] + }); + + expect(html).toContain("AI Tooling Readiness"); + }); + + it("includes maturity model descriptions", () => { + const html = generateVisualReport({ + reports: [{ repo: "test-repo", report: makeReport() }] + }); + + expect(html).toContain("Functional"); + expect(html).toContain("Documented"); + expect(html).toContain("Standardized"); + expect(html).toContain("Optimized"); + expect(html).toContain("Autonomous"); + }); + + it("includes theme toggle", () => { + const html = generateVisualReport({ + reports: [{ repo: "test-repo", report: makeReport() }] + }); + + expect(html).toContain("toggleTheme"); + expect(html).toContain("data-theme"); + }); + + it("includes light theme CSS variables", () => { + const html = generateVisualReport({ + reports: [{ repo: "test-repo", report: makeReport() }] + }); + + expect(html).toContain('[data-theme="light"]'); + expect(html).toContain('[data-theme="dark"]'); + }); + + it("includes GitHub logo SVG", () => { + const html = generateVisualReport({ + reports: [{ repo: "test-repo", report: makeReport() }] + }); + + expect(html).toContain("header-logo"); + expect(html).toContain(" { + const html = generateVisualReport({ + reports: [ + { repo: "good-repo", report: makeReport() }, + { repo: "bad-repo", report: makeReport(), error: "Clone failed" } + ] + }); + + expect(html).toContain("bad-repo"); + expect(html).toContain("Clone failed"); + }); + + it("shows summary cards with correct counts", () => { + const html = generateVisualReport({ + reports: [ + { repo: "repo-1", report: makeReport() }, + { repo: "repo-2", report: makeReport() } + ] + }); + + // Total repos should be 2 + expect(html).toContain(">2<"); + }); + + it("includes top fixes for failing criteria", () => { + const html = generateVisualReport({ + reports: [{ repo: "test-repo", report: makeReport() }] + }); + + expect(html).toContain("Top Fixes"); + expect(html).toContain("MCP config present"); + }); + + it("shows all criteria passing when all pass", () => { + const report = makeReport({ + criteria: [ + { + id: "lint-config", + title: "Linting configured", + pillar: "style-validation", + level: 1, + scope: "repo", + impact: "high", + effort: "low", + status: "pass" + } + ] + }); + + const html = generateVisualReport({ + reports: [{ repo: "test-repo", report }] + }); + + expect(html).toContain("All criteria passing"); + }); + + it("escapes HTML in repo names", () => { + const html = generateVisualReport({ + reports: [{ repo: '', report: makeReport() }] + }); + + expect(html).not.toContain(''); + expect(html).toContain("<script>"); + }); +}); diff --git a/src/services/analyzer.ts b/src/services/analyzer.ts index bd1b28a..9c0ed12 100644 --- a/src/services/analyzer.ts +++ b/src/services/analyzer.ts @@ -1,13 +1,32 @@ import fs from "fs/promises"; import path from "path"; + +import fg from "fast-glob"; + +import { fileExists, safeReadDir, readJson } from "../utils/fs"; + import { isGitRepo } from "./git"; +export type RepoApp = { + name: string; + path: string; + ecosystem?: "node" | "rust" | "go" | "dotnet" | "java" | "python" | "ruby" | "php"; + manifestPath?: string; + packageJsonPath: string; + scripts: Record; + hasTsConfig: boolean; +}; + export type RepoAnalysis = { path: string; isGitRepo: boolean; languages: string[]; frameworks: string[]; packageManager?: string; + isMonorepo?: boolean; + workspaceType?: "npm" | "pnpm" | "yarn" | "cargo" | "go" | "dotnet" | "gradle" | "maven"; + workspacePatterns?: string[]; + apps?: RepoApp[]; }; const PACKAGE_MANAGERS: Array<{ file: string; name: string }> = [ @@ -32,37 +51,76 @@ export async function analyzeRepo(repoPath: string): Promise { const hasRequirements = files.includes("requirements.txt"); const hasGoMod = files.includes("go.mod"); const hasCargo = files.includes("Cargo.toml"); + const hasCsproj = files.some((f) => f.endsWith(".csproj") || f.endsWith(".sln")); + const hasPomXml = files.includes("pom.xml"); + const hasBuildGradle = files.includes("build.gradle") || files.includes("build.gradle.kts"); + const hasGemfile = files.includes("Gemfile"); + const hasComposerJson = files.includes("composer.json"); if (hasPackageJson) analysis.languages.push("JavaScript"); if (hasTsConfig) analysis.languages.push("TypeScript"); if (hasPyProject || hasRequirements) analysis.languages.push("Python"); if (hasGoMod) analysis.languages.push("Go"); if (hasCargo) analysis.languages.push("Rust"); + if (hasCsproj) analysis.languages.push("C#"); + if (hasPomXml || hasBuildGradle) analysis.languages.push("Java"); + if (hasGemfile) analysis.languages.push("Ruby"); + if (hasComposerJson) analysis.languages.push("PHP"); analysis.packageManager = await detectPackageManager(repoPath, files); + let rootPackageJson: Record | undefined; + if (hasPackageJson) { - const packageJson = await readJson(path.join(repoPath, "package.json")); + rootPackageJson = await readJson(path.join(repoPath, "package.json")); const deps = Object.keys({ - ...(packageJson?.dependencies ?? {}), - ...(packageJson?.devDependencies ?? {}) + ...(rootPackageJson?.dependencies ?? {}), + ...(rootPackageJson?.devDependencies ?? {}) }); analysis.frameworks.push(...detectFrameworks(deps, files)); } + const workspace = await detectWorkspace(repoPath, files, rootPackageJson); + if (workspace) { + analysis.workspaceType = workspace.type; + analysis.workspacePatterns = workspace.patterns; + } + + let apps = await resolveWorkspaceApps(repoPath, workspace?.patterns ?? [], rootPackageJson); + + // If JS workspace didn't find multiple apps, try non-JS monorepo detection + if (apps.length <= 1) { + const nonJs = await detectNonJsMonorepo(repoPath, files); + if (nonJs.apps.length > 1) { + apps = nonJs.apps; + if (nonJs.type) analysis.workspaceType = nonJs.type; + if (nonJs.patterns) analysis.workspacePatterns = nonJs.patterns; + } + } + + analysis.apps = apps; + analysis.isMonorepo = apps.length > 1; + analysis.languages = unique(analysis.languages); analysis.frameworks = unique(analysis.frameworks); return analysis; } -async function detectPackageManager(repoPath: string, files: string[]): Promise { +async function detectPackageManager( + _repoPath: string, + files: string[] +): Promise { for (const manager of PACKAGE_MANAGERS) { if (files.includes(manager.file)) return manager.name; } if (files.includes("package.json")) return "npm"; if (files.includes("pyproject.toml")) return "pip"; + if (files.includes("pom.xml")) return "maven"; + if (files.includes("build.gradle") || files.includes("build.gradle.kts")) return "gradle"; + if (files.includes("Gemfile")) return "bundler"; + if (files.includes("composer.json")) return "composer"; return undefined; } @@ -70,7 +128,8 @@ function detectFrameworks(deps: string[], files: string[]): string[] { const frameworks: string[] = []; const hasFile = (file: string): boolean => files.includes(file); - if (deps.includes("next") || hasFile("next.config.js") || hasFile("next.config.mjs")) frameworks.push("Next.js"); + if (deps.includes("next") || hasFile("next.config.js") || hasFile("next.config.mjs")) + frameworks.push("Next.js"); if (deps.includes("react") || deps.includes("react-dom")) frameworks.push("React"); if (deps.includes("vue") || hasFile("vue.config.js")) frameworks.push("Vue"); if (deps.includes("@angular/core") || hasFile("angular.json")) frameworks.push("Angular"); @@ -82,21 +141,329 @@ function detectFrameworks(deps: string[], files: string[]): string[] { return frameworks; } -async function safeReadDir(dirPath: string): Promise { +async function safeReadFile(filePath: string): Promise { try { - return await fs.readdir(dirPath); + return await fs.readFile(filePath, "utf8"); } catch { - return []; + return undefined; + } +} + +type WorkspaceConfig = { + type: "npm" | "pnpm" | "yarn"; + patterns: string[]; +}; + +async function detectWorkspace( + repoPath: string, + files: string[], + packageJson?: Record +): Promise { + if (files.includes("pnpm-workspace.yaml")) { + const patterns = await readPnpmWorkspace(path.join(repoPath, "pnpm-workspace.yaml")); + if (patterns.length) return { type: "pnpm", patterns }; + } + + const workspaces = packageJson?.workspaces; + if (Array.isArray(workspaces)) { + return { type: files.includes("yarn.lock") ? "yarn" : "npm", patterns: workspaces.map(String) }; + } + + if (workspaces && typeof workspaces === "object") { + const packages = (workspaces as { packages?: unknown }).packages; + if (Array.isArray(packages)) { + return { type: files.includes("yarn.lock") ? "yarn" : "npm", patterns: packages.map(String) }; + } } + + return undefined; } -async function readJson(filePath: string): Promise | undefined> { +async function readPnpmWorkspace(filePath: string): Promise { try { const raw = await fs.readFile(filePath, "utf8"); - return JSON.parse(raw) as Record; + const lines = raw.split(/\r?\n/u); + const patterns: string[] = []; + let inPackages = false; + for (const line of lines) { + // Skip comment-only lines + if (/^\s*#/u.test(line)) continue; + if (!inPackages && /^\s*packages\s*:/u.test(line)) { + // Handle inline array: packages: ["apps/*", "libs/*"] + const inline = line.match(/packages\s*:\s*\[([^\]]+)\]/u); + if (inline) { + const items = inline[1].split(",").map((s) => s.trim().replace(/^['"]|['"]$/gu, "")); + return items.filter(Boolean); + } + inPackages = true; + continue; + } + if (inPackages) { + const match = line.match(/^\s*-\s*(.+)$/u); + if (match?.[1]) { + // Strip trailing comments and quotes + const value = match[1] + .split("#")[0] + .trim() + .replace(/^['"]|['"]$/gu, ""); + if (value) patterns.push(value); + continue; + } + // Non-indented, non-empty line means a new top-level key + if (/^\S/u.test(line) && line.trim()) break; + } + } + return patterns; } catch { - return undefined; + return []; + } +} + +async function resolveWorkspaceApps( + repoPath: string, + patterns: string[], + rootPackageJson?: Record +): Promise { + const workspacePatterns = patterns + .map((pattern) => pattern.replace(/\\/gu, "/")) + .map((pattern) => + pattern.endsWith("package.json") ? pattern : path.posix.join(pattern, "package.json") + ); + + const packageJsonPaths = workspacePatterns.length + ? ( + await fg(workspacePatterns, { cwd: repoPath, absolute: true, onlyFiles: true, dot: false }) + ).map((p) => path.normalize(p)) + : []; + + if (!packageJsonPaths.length && rootPackageJson) { + const rootPath = path.join(repoPath, "package.json"); + return [await buildRepoApp(repoPath, rootPath, rootPackageJson)]; } + + const apps = await Promise.all( + packageJsonPaths.map(async (pkgPath) => { + const pkg = await readJson(pkgPath); + return buildRepoApp(path.dirname(pkgPath), pkgPath, pkg); + }) + ); + + return apps.filter(Boolean) as RepoApp[]; +} + +async function buildRepoApp( + appPath: string, + packageJsonPath: string, + packageJson?: Record +): Promise { + const scripts = (packageJson?.scripts ?? {}) as Record; + const name = typeof packageJson?.name === "string" ? packageJson.name : path.basename(appPath); + const hasTsConfig = await fileExists(path.join(appPath, "tsconfig.json")); + + return { + name, + path: appPath, + ecosystem: "node", + manifestPath: packageJsonPath, + packageJsonPath, + scripts, + hasTsConfig + }; +} + +function buildNonJsApp( + name: string, + appPath: string, + ecosystem: NonNullable, + manifestPath: string +): RepoApp { + return { + name, + path: appPath, + ecosystem, + manifestPath, + packageJsonPath: "", + scripts: {}, + hasTsConfig: false + }; +} + +// ─── Non-JS monorepo detection ─── + +type NonJsMonorepoResult = { + type?: "cargo" | "go" | "dotnet" | "gradle" | "maven"; + patterns?: string[]; + apps: RepoApp[]; +}; + +async function detectNonJsMonorepo( + repoPath: string, + files: string[] +): Promise { + const cargoApps = await detectCargoWorkspace(repoPath); + if (cargoApps.length > 1) return { type: "cargo", apps: cargoApps }; + + const goApps = await detectGoWorkspace(repoPath); + if (goApps.length > 1) return { type: "go", apps: goApps }; + + const dotnetApps = await detectDotnetSolution(repoPath, files); + if (dotnetApps.length > 1) return { type: "dotnet", apps: dotnetApps }; + + const gradleApps = await detectGradleMultiProject(repoPath, files); + if (gradleApps.length > 1) return { type: "gradle", apps: gradleApps }; + + const mavenApps = await detectMavenMultiModule(repoPath); + if (mavenApps.length > 1) return { type: "maven", apps: mavenApps }; + + return { apps: [] }; +} + +async function detectCargoWorkspace(repoPath: string): Promise { + const content = await safeReadFile(path.join(repoPath, "Cargo.toml")); + if (!content) return []; + + // Extract [workspace] section up to the next section header + const workspaceSection = content.match(/\[workspace\]([\s\S]*?)(?:\n\[|$)/u); + if (!workspaceSection) return []; + + const membersMatch = workspaceSection[1].match(/members\s*=\s*\[([\s\S]*?)\]/u); + if (!membersMatch) return []; + + const patterns = [...membersMatch[1].matchAll(/"([^"]+)"/gu)].map((m) => m[1]); + if (!patterns.length) return []; + + const tomlPaths = ( + await fg( + patterns.map((p) => path.posix.join(p, "Cargo.toml")), + { cwd: repoPath, absolute: true, onlyFiles: true } + ) + ).map((p) => path.normalize(p)); + + return Promise.all( + tomlPaths.map(async (tomlPath) => { + const dir = path.dirname(tomlPath); + const toml = await safeReadFile(tomlPath); + const nameMatch = toml?.match(/^\s*name\s*=\s*"([^"]+)"/mu); + return buildNonJsApp(nameMatch?.[1] ?? path.basename(dir), dir, "rust", tomlPath); + }) + ); +} + +async function detectGoWorkspace(repoPath: string): Promise { + const content = await safeReadFile(path.join(repoPath, "go.work")); + if (!content) return []; + + const modules: string[] = []; + + // Block form: use ( ./cmd/server \n ./pkg/lib ) + const blockMatch = content.match(/use\s*\(([\s\S]*?)\)/u); + if (blockMatch) { + for (const line of blockMatch[1].split(/\r?\n/u)) { + const trimmed = line.replace(/\/\/.*$/u, "").trim(); + if (trimmed) modules.push(trimmed); + } + } + + // Single-line form: use ./cmd/server + for (const match of content.matchAll(/^use\s+(\S+)\s*$/gmu)) { + modules.push(match[1]); + } + + const apps: RepoApp[] = []; + for (const mod of modules) { + const modPath = path.resolve(repoPath, mod); + const goModPath = path.join(modPath, "go.mod"); + if (!(await fileExists(goModPath))) continue; + + const goMod = await safeReadFile(goModPath); + const nameMatch = goMod?.match(/^module\s+(\S+)/mu); + const shortName = nameMatch?.[1]?.split("/").pop() ?? path.basename(modPath); + apps.push(buildNonJsApp(shortName, modPath, "go", goModPath)); + } + + return apps; +} + +async function detectDotnetSolution(repoPath: string, files: string[]): Promise { + const slnFile = files.find((f) => f.endsWith(".sln")); + if (!slnFile) return []; + + const content = await safeReadFile(path.join(repoPath, slnFile)); + if (!content) return []; + + // Match: Project("{guid}") = "Name", "path\to\Project.csproj", "{guid}" + const projectRegex = /Project\("[^"]*"\)\s*=\s*"([^"]+)",\s*"([^"]+\.(?:cs|fs|vb)proj)"/giu; + const apps: RepoApp[] = []; + + for (const match of content.matchAll(projectRegex)) { + const name = match[1]; + const projRelPath = match[2].replace(/\\/gu, "/"); + const projPath = path.resolve(repoPath, projRelPath); + const appDir = path.dirname(projPath); + + if (await fileExists(projPath)) { + apps.push(buildNonJsApp(name, appDir, "dotnet", projPath)); + } + } + + return apps; +} + +async function detectGradleMultiProject(repoPath: string, files: string[]): Promise { + const settingsFile = files.includes("settings.gradle.kts") + ? "settings.gradle.kts" + : files.includes("settings.gradle") + ? "settings.gradle" + : null; + if (!settingsFile) return []; + + const content = await safeReadFile(path.join(repoPath, settingsFile)); + if (!content) return []; + + // Extract all Gradle project references (':app', ':lib:core') from the file + const projectNames: string[] = []; + for (const match of content.matchAll(/['"](:(?:[\w.-]+:)*[\w.-]+)['"]/gu)) { + projectNames.push(match[1].replace(/^:/u, "").replace(/:/gu, "/")); + } + + const uniqueProjects = [...new Set(projectNames)]; + const apps: RepoApp[] = []; + + for (const project of uniqueProjects) { + const projectDir = path.resolve(repoPath, project); + const ktsPath = path.join(projectDir, "build.gradle.kts"); + const groovyPath = path.join(projectDir, "build.gradle"); + + const buildFile = (await fileExists(ktsPath)) + ? ktsPath + : (await fileExists(groovyPath)) + ? groovyPath + : null; + + if (buildFile) { + apps.push(buildNonJsApp(path.basename(project), projectDir, "java", buildFile)); + } + } + + return apps; +} + +async function detectMavenMultiModule(repoPath: string): Promise { + const content = await safeReadFile(path.join(repoPath, "pom.xml")); + if (!content) return []; + + const apps: RepoApp[] = []; + for (const match of content.matchAll(/([^<]+)<\/module>/gu)) { + const modName = match[1].trim(); + const modDir = path.resolve(repoPath, modName); + const pomPath = path.join(modDir, "pom.xml"); + + if (await fileExists(pomPath)) { + apps.push(buildNonJsApp(path.basename(modName), modDir, "java", pomPath)); + } + } + + return apps; } function unique(items: T[]): T[] { diff --git a/src/services/azureDevops.ts b/src/services/azureDevops.ts new file mode 100644 index 0000000..65d3ab3 --- /dev/null +++ b/src/services/azureDevops.ts @@ -0,0 +1,272 @@ +type AzureDevOpsProfileResponse = { + id: string; + displayName: string; +}; + +type AzureDevOpsAccountResponse = { + accountId: string; + accountName: string; + accountUri: string; +}; + +type AzureDevOpsListResponse = { + value: T[]; +}; + +type AzureDevOpsProjectResponse = { + id: string; + name: string; + url: string; +}; + +type AzureDevOpsRepoResponse = { + id: string; + name: string; + webUrl: string; + remoteUrl: string; + isPrivate: boolean; + defaultBranch?: string; + project?: { + id: string; + name: string; + }; +}; + +export type AzureDevOpsOrg = { + id: string; + name: string; + url: string; +}; + +export type AzureDevOpsProject = { + id: string; + name: string; + organization: string; + url: string; +}; + +export type AzureDevOpsRepo = { + id: string; + name: string; + organization: string; + project: string; + projectId: string; + webUrl: string; + cloneUrl: string; + isPrivate: boolean; + defaultBranch: string; + hasInstructions?: boolean; +}; + +const PROFILE_URL = + "https://app.vssps.visualstudio.com/_apis/profile/profiles/me?api-version=7.1-preview.1"; + +const ADO_SLUG_RE = /^[\w][\w.-]*$/u; + +function validateAdoSlug(value: string, label: string): string { + if (!ADO_SLUG_RE.test(value)) { + throw new Error(`Invalid ${label}: ${value}`); + } + return encodeURIComponent(value); +} + +function getAuthHeader(token: string): string { + const encoded = Buffer.from(`:${token}`).toString("base64"); + return `Basic ${encoded}`; +} + +async function adoRequest(url: string, token: string, init?: RequestInit): Promise { + const response = await fetch(url, { + ...init, + headers: { + "Content-Type": "application/json", + Authorization: getAuthHeader(token), + ...(init?.headers ?? {}) + } + }); + + if (!response.ok) { + const text = await response.text(); + throw new Error(`Azure DevOps request failed (${response.status}): ${text}`); + } + + return (await response.json()) as T; +} + +export function getAzureDevOpsToken(): string | null { + return process.env.AZURE_DEVOPS_PAT ?? process.env.AZDO_PAT ?? null; +} + +export async function listOrganizations(token: string): Promise { + const profile = await adoRequest(PROFILE_URL, token); + const accountsUrl = `https://app.vssps.visualstudio.com/_apis/accounts?memberId=${encodeURIComponent(profile.id)}&api-version=7.1-preview.1`; + const accounts = await adoRequest>( + accountsUrl, + token + ); + + return accounts.value.map((account) => ({ + id: account.accountId, + name: account.accountName, + url: account.accountUri + })); +} + +export async function listProjects( + token: string, + organization: string +): Promise { + const org = validateAdoSlug(organization, "organization"); + const url = `https://dev.azure.com/${org}/_apis/projects?stateFilter=wellFormed&api-version=7.1-preview.1`; + const response = await adoRequest>( + url, + token + ); + + return response.value.map((project) => ({ + id: project.id, + name: project.name, + organization, + url: project.url + })); +} + +export async function listRepos( + token: string, + organization: string, + project: string +): Promise { + const org = validateAdoSlug(organization, "organization"); + const proj = validateAdoSlug(project, "project"); + const url = `https://dev.azure.com/${org}/${proj}/_apis/git/repositories?api-version=7.1-preview.1`; + const response = await adoRequest>(url, token); + + return response.value.map((repo) => ({ + id: repo.id, + name: repo.name, + organization, + project, + projectId: repo.project?.id ?? "", + webUrl: repo.webUrl, + cloneUrl: repo.remoteUrl, + isPrivate: repo.isPrivate, + defaultBranch: repo.defaultBranch ?? "refs/heads/main" + })); +} + +export async function getRepo( + token: string, + organization: string, + project: string, + repo: string +): Promise { + const org = validateAdoSlug(organization, "organization"); + const proj = validateAdoSlug(project, "project"); + const r = validateAdoSlug(repo, "repo"); + const url = `https://dev.azure.com/${org}/${proj}/_apis/git/repositories/${r}?api-version=7.1-preview.1`; + const response = await adoRequest(url, token); + + return { + id: response.id, + name: response.name, + organization, + project, + projectId: response.project?.id ?? "", + webUrl: response.webUrl, + cloneUrl: response.remoteUrl, + isPrivate: response.isPrivate, + defaultBranch: response.defaultBranch ?? "refs/heads/main" + }; +} + +function toRefName(branch: string): string { + if (branch.startsWith("refs/")) return branch; + return `refs/heads/${branch}`; +} + +export async function createPullRequest(params: { + token: string; + organization: string; + project: string; + repoId: string; + repoName: string; + title: string; + body: string; + sourceBranch: string; + targetBranch: string; +}): Promise { + const org = validateAdoSlug(params.organization, "organization"); + const proj = validateAdoSlug(params.project, "project"); + const url = `https://dev.azure.com/${org}/${proj}/_apis/git/repositories/${encodeURIComponent(params.repoId)}/pullrequests?api-version=7.1-preview.1`; + const payload = { + title: params.title, + description: params.body, + sourceRefName: toRefName(params.sourceBranch), + targetRefName: toRefName(params.targetBranch) + }; + + const response = await adoRequest<{ pullRequestId: number }>(url, params.token, { + method: "POST", + body: JSON.stringify(payload) + }); + + return `https://dev.azure.com/${org}/${proj}/_git/${encodeURIComponent( + params.repoName + )}/pullrequest/${response.pullRequestId}`; +} + +export async function checkRepoHasInstructions( + token: string, + organization: string, + project: string, + repoId: string +): Promise { + const org = validateAdoSlug(organization, "organization"); + const proj = validateAdoSlug(project, "project"); + const url = `https://dev.azure.com/${org}/${proj}/_apis/git/repositories/${encodeURIComponent(repoId)}/items?path=/.github/copilot-instructions.md&includeContentMetadata=true&api-version=7.1-preview.1`; + const response = await fetch(url, { + headers: { + Authorization: getAuthHeader(token) + } + }); + + if (response.status === 404) { + return false; + } + + if (!response.ok) { + throw new Error(`Azure DevOps request failed (${response.status})`); + } + + return true; +} + +export async function checkReposForInstructions( + token: string, + repos: AzureDevOpsRepo[], + onProgress?: (checked: number, total: number) => void +): Promise { + const concurrency = 10; + const results: AzureDevOpsRepo[] = []; + let checked = 0; + + for (let i = 0; i < repos.length; i += concurrency) { + const batch = repos.slice(i, i + concurrency); + const checks = await Promise.all( + batch.map(async (repo) => { + const hasInstructions = await checkRepoHasInstructions( + token, + repo.organization, + repo.project, + repo.id + ); + return { ...repo, hasInstructions }; + }) + ); + results.push(...checks); + checked += batch.length; + onProgress?.(checked, repos.length); + } + + return results; +} diff --git a/src/services/copilot.ts b/src/services/copilot.ts new file mode 100644 index 0000000..3ec951e --- /dev/null +++ b/src/services/copilot.ts @@ -0,0 +1,142 @@ +import fs from "fs/promises"; +import { execFile } from "node:child_process"; +import { promisify } from "node:util"; +import path from "path"; + +import fg from "fast-glob"; + +const execFileAsync = promisify(execFile); + +let cachedCliPath: string | null = null; +let cachedCliPathTimestamp = 0; +const CLI_CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes + +export async function assertCopilotCliReady(): Promise { + const cliPath = await findCopilotCliPath(); + + try { + await execFileAsync(cliPath, ["--version"], { timeout: 5000 }); + } catch { + throw new Error(`Copilot CLI at ${cliPath} is not working.`); + } + + return cliPath; +} + +export async function listCopilotModels(): Promise { + const cliPath = await assertCopilotCliReady(); + const { stdout } = await execFileAsync(cliPath, ["--help"], { timeout: 5000 }); + return extractModelChoices(stdout); +} + +async function findCopilotCliPath(): Promise { + if (cachedCliPath && Date.now() - cachedCliPathTimestamp < CLI_CACHE_TTL_MS) { + return cachedCliPath; + } + + // Try PATH lookup first (works on all platforms) + const whichCmd = process.platform === "win32" ? "where" : "which"; + try { + const { stdout } = await execFileAsync(whichCmd, ["copilot"], { timeout: 5000 }); + const found = stdout.trim().split(/\r?\n/)[0]; + if (found) { + cachedCliPath = found; + cachedCliPathTimestamp = Date.now(); + return found; + } + } catch { + // Ignore - will try VS Code locations + } + + const home = process.env.HOME ?? process.env.USERPROFILE ?? ""; + const staticLocations: string[] = []; + + if (process.platform === "darwin") { + staticLocations.push( + `${home}/Library/Application Support/Code - Insiders/User/globalStorage/github.copilot-chat/copilotCli/copilot`, + `${home}/Library/Application Support/Code/User/globalStorage/github.copilot-chat/copilotCli/copilot` + ); + } else if (process.platform === "linux") { + staticLocations.push( + `${home}/.config/Code - Insiders/User/globalStorage/github.copilot-chat/copilotCli/copilot`, + `${home}/.config/Code/User/globalStorage/github.copilot-chat/copilotCli/copilot` + ); + } else if (process.platform === "win32") { + const appData = process.env.APPDATA ?? ""; + if (appData) { + staticLocations.push( + `${appData}\\Code - Insiders\\User\\globalStorage\\github.copilot-chat\\copilotCli\\copilot.exe`, + `${appData}\\Code\\User\\globalStorage\\github.copilot-chat\\copilotCli\\copilot.exe` + ); + } + } + + for (const location of staticLocations) { + try { + await fs.access(location); + cachedCliPath = location; + cachedCliPathTimestamp = Date.now(); + return location; + } catch { + // Try next location + } + } + + const ext = process.platform === "win32" ? ".exe" : ""; + const normalizedHome = home.replace(/\\/g, "/"); + const globPatterns = [ + `${normalizedHome}/.vscode-insiders/extensions/github.copilot-chat-*/copilotCli/copilot${ext}`, + `${normalizedHome}/.vscode/extensions/github.copilot-chat-*/copilotCli/copilot${ext}` + ]; + + for (const pattern of globPatterns) { + const matches = await fg(pattern, { onlyFiles: true }); + if (matches.length > 0) { + const normalized = path.normalize(matches[0]); + cachedCliPath = normalized; + cachedCliPathTimestamp = Date.now(); + return normalized; + } + } + + const platformHint = + process.platform === "win32" + ? " Searched APPDATA and VS Code extension paths." + : process.platform === "linux" + ? " Searched ~/.config/Code and VS Code extension paths." + : " Searched ~/Library/Application Support/Code and VS Code extension paths."; + + throw new Error( + `Copilot CLI not found. Install GitHub Copilot Chat extension in VS Code.${platformHint}` + ); +} + +function extractModelChoices(helpText: string): string[] { + const lines = helpText.split("\n"); + let captured = ""; + + for (let index = 0; index < lines.length; index += 1) { + const line = lines[index]; + if (!line.includes("--model")) continue; + + captured = line.trim(); + while (!captured.includes(")") && index + 1 < lines.length) { + index += 1; + captured += ` ${lines[index].trim()}`; + } + break; + } + + const match = captured.match(/choices:\s*([^)]*)/); + if (!match) return []; + + const models: string[] = []; + const matcher = /"([^"]+)"/g; + let entry = matcher.exec(match[1]); + while (entry) { + models.push(entry[1]); + entry = matcher.exec(match[1]); + } + + return models; +} diff --git a/src/services/evalScaffold.ts b/src/services/evalScaffold.ts new file mode 100644 index 0000000..9836ea4 --- /dev/null +++ b/src/services/evalScaffold.ts @@ -0,0 +1,201 @@ +import { DEFAULT_MODEL } from "../config"; +import { withCwd } from "../utils/cwd"; + +import { assertCopilotCliReady } from "./copilot"; + +const EVAL_SCAFFOLD_TIMEOUT_MS = 600000; +const EVAL_SCAFFOLD_RECOVERY_TIMEOUT_MS = 90000; + +export type EvalCase = { + id?: string; + prompt: string; + expectation: string; +}; + +export type EvalConfig = { + instructionFile?: string; + cases: EvalCase[]; + systemMessage?: string; + outputPath?: string; + ui?: { + modelPicker?: "visible" | "hidden"; + }; +}; + +type EvalScaffoldOptions = { + repoPath: string; + count: number; + model?: string; + onProgress?: (message: string) => void; +}; + +export async function generateEvalScaffold(options: EvalScaffoldOptions): Promise { + const repoPath = options.repoPath; + const count = Math.max(1, options.count); + const progress = options.onProgress ?? (() => {}); + + return withCwd(repoPath, async () => { + progress("Checking Copilot CLI..."); + const cliPath = await assertCopilotCliReady(); + + progress("Starting Copilot SDK..."); + const sdk = await import("@github/copilot-sdk"); + const client = new sdk.CopilotClient({ cliPath }); + + try { + progress("Creating session..."); + const preferredModel = options.model ?? DEFAULT_MODEL; + const session = await client.createSession({ + model: preferredModel, + streaming: true, + systemMessage: { + content: + "You are an expert codebase analyst specializing in deep architectural analysis. Generate challenging, cross-cutting eval cases for this repository that require synthesizing information from multiple files and tracing logic across layers. Avoid trivial questions answerable from a single file read or grep. Use tools (glob, view, grep) extensively to inspect the codebase. Output ONLY JSON with keys: instructionFile, cases (array of {id,prompt,expectation})." + }, + infiniteSessions: { enabled: false } + }); + + let content = ""; + session.on((event: { type: string; data?: Record }) => { + if (event.type === "assistant.message_delta") { + const delta = event.data?.deltaContent as string | undefined; + if (delta) { + content += delta; + progress("Generating eval cases..."); + } + } else if (event.type === "tool.execution_start") { + const toolName = event.data?.toolName as string | undefined; + progress(`Using tool: ${toolName ?? "..."}`); + } else if (event.type === "session.error") { + const errorMsg = (event.data?.message as string) ?? "Unknown error"; + if (errorMsg.toLowerCase().includes("auth") || errorMsg.toLowerCase().includes("login")) { + throw new Error( + "Copilot CLI not logged in. Run `copilot` then `/login` to authenticate." + ); + } + } + }); + + const prompt = [ + `Analyze this repository and generate ${count} eval cases.`, + "", + "IMPORTANT: Generate HARD eval cases that require deep, cross-cutting understanding of the codebase.", + "Each case should require synthesizing information from MULTIPLE files or tracing logic across several layers.", + "Do NOT generate simple questions that can be answered by reading a single file or running a single grep.", + "", + "Good eval case examples (adapt to this repo):", + "- Questions about how data flows end-to-end through multiple modules (e.g., 'Trace what happens when X is called — which services, transforms, and side effects are involved?')", + "- Questions about implicit conventions or patterns that span many files (e.g., 'What error-handling pattern is used across the service layer, and where does it deviate?')", + "- Questions requiring understanding of runtime behavior not obvious from static code (e.g., 'What is the order of initialization and what would break if module X loaded before Y?')", + "- Questions about non-obvious interactions between components (e.g., 'How does changing config option X affect the behavior of feature Y?')", + "- Questions about edge cases or failure modes that require reading implementation details across files", + "- Questions that require understanding the type system, generics, or shared interfaces across module boundaries", + "", + "Bad eval case examples (avoid these):", + "- 'What does this project do?' (answered by README alone)", + "- 'How do I build/test?' (answered by package.json alone)", + "- 'What is the entrypoint?' (answered by a single file)", + "- Any question answerable by reading one file or searching for one keyword", + "", + "Use tools extensively to inspect the codebase — read multiple files, trace imports, follow call chains.", + "If this is a monorepo (npm/pnpm/yarn workspaces, Cargo workspace, Go workspace, .NET solution, Gradle/Maven multi-module), generate cases that involve cross-app dependencies, shared libraries, and how changes in one app affect others.", + "Ensure cases cover cross-cutting concerns: data flow, error propagation, configuration impact, implicit coupling, architectural invariants.", + "Include a systemMessage that keeps answers scoped to this repository (avoid generic Copilot CLI details unless asked).", + "Return JSON ONLY (no markdown, no commentary) in this schema:", + '{\n "instructionFile": ".github/copilot-instructions.md",\n "systemMessage": "...",\n "cases": [\n {"id": "case-1", "prompt": "...", "expectation": "..."}\n ]\n}' + ].join("\n"); + + progress("Analyzing codebase..."); + let timedOutWaitingForIdle = false; + try { + await session.sendAndWait({ prompt }, EVAL_SCAFFOLD_TIMEOUT_MS); + } catch (error) { + if (!isSessionIdleTimeoutError(error)) { + throw error; + } + + timedOutWaitingForIdle = true; + progress("Generation took longer than expected; requesting final JSON output..."); + + try { + await session.sendAndWait( + { + prompt: + "Stop analysis and return only the final JSON scaffold now. Do not include markdown or commentary." + }, + EVAL_SCAFFOLD_RECOVERY_TIMEOUT_MS + ); + } catch (recoveryError) { + if (!isSessionIdleTimeoutError(recoveryError)) { + throw recoveryError; + } + progress("Still waiting on idle; attempting to parse partial output..."); + } + } finally { + await session.destroy(); + } + + let parsed: EvalConfig; + try { + parsed = parseEvalConfig(content); + } catch (error) { + if (timedOutWaitingForIdle) { + throw new Error( + "Timed out waiting for scaffold generation to become idle before a complete JSON payload was returned. Try again or lower `--count`." + ); + } + throw error; + } + + const normalized = normalizeEvalConfig(parsed, count); + return normalized; + } finally { + await client.stop(); + } + }); +} + +function isSessionIdleTimeoutError(error: unknown): boolean { + if (!(error instanceof Error)) { + return false; + } + + const message = error.message.toLowerCase(); + return message.includes("timeout") && message.includes("session.idle"); +} + +function parseEvalConfig(raw: string): EvalConfig { + const match = raw.match(/\{[\s\S]*\}/u); + if (!match) { + throw new Error("Failed to parse eval scaffold JSON."); + } + const parsed = JSON.parse(match[0]) as EvalConfig; + if (!parsed || !Array.isArray(parsed.cases)) { + throw new Error("Eval scaffold JSON is missing cases."); + } + return parsed; +} + +function normalizeEvalConfig(parsed: EvalConfig, count: number): EvalConfig { + const cases = (parsed.cases ?? []).slice(0, count).map((entry, index) => { + const id = typeof entry.id === "string" && entry.id.trim() ? entry.id : `case-${index + 1}`; + return { + id, + prompt: String(entry.prompt ?? "").trim(), + expectation: String(entry.expectation ?? "").trim() + }; + }); + + if (!cases.length) { + throw new Error("Eval scaffold JSON did not include any usable cases."); + } + + const defaultSystemMessage = + "You are answering questions about this repository. Use tools to inspect the repo and cite its files. Avoid generic Copilot CLI details unless the prompt explicitly asks for them."; + + return { + instructionFile: parsed.instructionFile ?? ".github/copilot-instructions.md", + systemMessage: parsed.systemMessage ?? defaultSystemMessage, + cases + }; +} diff --git a/src/services/evaluator.ts b/src/services/evaluator.ts index 6a30001..db3f195 100644 --- a/src/services/evaluator.ts +++ b/src/services/evaluator.ts @@ -1,21 +1,24 @@ import fs from "fs/promises"; import path from "path"; -import { execFile } from "node:child_process"; -import { promisify } from "node:util"; -const execFileAsync = promisify(execFile); +import { buildTimestampedName } from "../utils/fs"; -type EvalCase = { - prompt: string; - expectation: string; - id?: string; -}; +import { assertCopilotCliReady } from "./copilot"; +import type { EvalConfig } from "./evalScaffold"; -type EvalConfig = { - instructionFile?: string; - cases: EvalCase[]; - systemMessage?: string; -}; +const DEFAULT_SYSTEM_MESSAGE = + "You are answering questions about this repository. Use tools to inspect the repo and cite its files. Avoid generic Copilot CLI details unless the prompt explicitly asks for them."; + +interface CopilotSession { + on(handler: (event: { type: string; data?: Record }) => void): void; + sendAndWait(params: { prompt: string }, timeoutMs?: number): Promise; + destroy(): Promise; +} + +interface CopilotClient { + createSession(config?: Record): Promise; + stop(): Promise; +} type EvalRunOptions = { configPath: string; @@ -26,6 +29,40 @@ type EvalRunOptions = { onProgress?: (message: string) => void; }; +type TokenUsage = { + promptTokens?: number; + completionTokens?: number; + totalTokens?: number; +}; + +type ToolCallSummary = { + count: number; + byName: Record; + totalDurationMs: number; +}; + +type AskMetrics = { + durationMs: number; + tokenUsage?: TokenUsage; + toolCalls: ToolCallSummary; +}; + +type EvalMetrics = { + withoutInstructions: AskMetrics; + withInstructions: AskMetrics; + judge: AskMetrics; + totalDurationMs: number; +}; + +type EvalPhase = "withoutInstructions" | "withInstructions" | "judge"; + +type TrajectoryEvent = { + timestampMs: number; + phase: EvalPhase; + type: string; + data?: Record; +}; + export type EvalResult = { id: string; prompt: string; @@ -35,17 +72,31 @@ export type EvalResult = { verdict?: "pass" | "fail" | "unknown"; score?: number; rationale?: string; + metrics?: EvalMetrics; + trajectory?: TrajectoryEvent[]; }; -export async function runEval(options: EvalRunOptions): Promise<{ summary: string; results: EvalResult[] }> { +export async function runEval( + options: EvalRunOptions +): Promise<{ summary: string; results: EvalResult[]; viewerPath?: string }> { const config = await loadConfig(options.configPath); const instructionFile = config.instructionFile ?? ".github/copilot-instructions.md"; const instructionPath = path.resolve(options.repoPath, instructionFile); const instructionText = await readOptionalFile(instructionPath); + const baseSystemMessage = config.systemMessage ?? DEFAULT_SYSTEM_MESSAGE; const progress = options.onProgress ?? (() => {}); + const defaultOutputPath = path.resolve( + options.repoPath, + ".primer", + "evals", + buildTimestampedName("eval-results") + ); + const outputPath = + resolveOutputPath(options.repoPath, options.outputPath, config.outputPath) ?? defaultOutputPath; + const runStartedAt = Date.now(); progress("Starting Copilot SDK..."); - const cliPath = await findCopilotCliPath(); + const cliPath = await assertCopilotCliReady(); const sdk = await import("@github/copilot-sdk"); const client = new sdk.CopilotClient({ cliPath }); @@ -56,19 +107,22 @@ export async function runEval(options: EvalRunOptions): Promise<{ summary: strin for (const [index, testCase] of config.cases.entries()) { const id = testCase.id ?? `case-${index + 1}`; const prompt = buildPrompt(options.repoPath, testCase.prompt); + const caseStartedAt = Date.now(); progress(`Running eval ${index + 1}/${total}: ${id} (without instructions)...`); - const withoutInstructions = await askOnce(client, { + const withoutResult = await askOnce(client, { prompt, model: options.model, - systemMessage: config.systemMessage + systemMessage: baseSystemMessage, + phase: "withoutInstructions" }); progress(`Running eval ${index + 1}/${total}: ${id} (with instructions)...`); - const withInstructions = await askOnce(client, { + const withResult = await askOnce(client, { prompt, model: options.model, - systemMessage: [config.systemMessage, instructionText].filter(Boolean).join("\n\n") + systemMessage: [baseSystemMessage, instructionText].filter(Boolean).join("\n\n"), + phase: "withInstructions" }); progress(`Running eval ${index + 1}/${total}: ${id} (judging)...`); @@ -76,36 +130,63 @@ export async function runEval(options: EvalRunOptions): Promise<{ summary: strin model: options.judgeModel, prompt: testCase.prompt, expectation: testCase.expectation, - withoutInstructions, - withInstructions + withoutInstructions: withoutResult.content, + withInstructions: withResult.content }); + const metrics: EvalMetrics = { + withoutInstructions: withoutResult.metrics, + withInstructions: withResult.metrics, + judge: judgment.metrics, + totalDurationMs: Date.now() - caseStartedAt + }; + + const trajectory = [ + ...withoutResult.trajectory, + ...withResult.trajectory, + ...judgment.trajectory + ]; + results.push({ id, prompt: testCase.prompt, expectation: testCase.expectation, - withInstructions, - withoutInstructions, - verdict: judgment.verdict, - score: judgment.score, - rationale: judgment.rationale + withInstructions: withResult.content, + withoutInstructions: withoutResult.content, + verdict: judgment.result.verdict, + score: judgment.result.score, + rationale: judgment.result.rationale, + metrics, + trajectory }); - progress(`Eval ${index + 1}/${total}: ${id} → ${judgment.verdict} (score: ${judgment.score})`); + progress( + `Eval ${index + 1}/${total}: ${id} → ${judgment.result.verdict} (score: ${judgment.result.score})` + ); } - if (options.outputPath) { - const output = { - repoPath: options.repoPath, - model: options.model, - judgeModel: options.judgeModel, - results - }; - await fs.writeFile(options.outputPath, JSON.stringify(output, null, 2), "utf8"); + const runFinishedAt = Date.now(); + const output = { + repoPath: options.repoPath, + model: options.model, + judgeModel: options.judgeModel, + runMetrics: { + startedAt: new Date(runStartedAt).toISOString(), + finishedAt: new Date(runFinishedAt).toISOString(), + durationMs: runFinishedAt - runStartedAt + }, + results + }; + let viewerPath: string | undefined; + if (outputPath) { + await fs.mkdir(path.dirname(outputPath), { recursive: true }); + await fs.writeFile(outputPath, JSON.stringify(output, null, 2), "utf8"); + viewerPath = buildViewerPath(outputPath); + await fs.writeFile(viewerPath, buildTrajectoryViewerHtml(output), "utf8"); } - const summary = formatSummary(results); - return { summary, results }; + const summary = formatSummary(results, runFinishedAt - runStartedAt); + return { summary, results, viewerPath }; } finally { await client.stop(); } @@ -115,23 +196,28 @@ type AskOptions = { prompt: string; model: string; systemMessage?: string; + phase: EvalPhase; +}; + +type AskResult = { + content: string; + metrics: AskMetrics; + trajectory: TrajectoryEvent[]; }; -async function askOnce( - client: { createSession: (config?: Record) => Promise }, - options: AskOptions -): Promise { +async function askOnce(client: CopilotClient, options: AskOptions): Promise { const session = await client.createSession({ model: options.model, streaming: true, infiniteSessions: { enabled: false }, - systemMessage: options.systemMessage - ? { content: options.systemMessage } - : undefined + systemMessage: options.systemMessage ? { content: options.systemMessage } : undefined }); let content = ""; + const telemetry = createTelemetry(options.phase); + const startedAt = Date.now(); session.on((event: { type: string; data?: Record }) => { + captureTelemetryEvent(event, telemetry); if (event.type === "assistant.message_delta") { const delta = event.data?.deltaContent as string | undefined; if (delta) content += delta; @@ -140,7 +226,16 @@ async function askOnce( await session.sendAndWait({ prompt: options.prompt }, 120000); await session.destroy(); - return content.trim(); + const finishedAt = Date.now(); + return { + content: content.trim(), + metrics: { + durationMs: finishedAt - startedAt, + tokenUsage: normalizeTokenUsage(telemetry.tokenUsage), + toolCalls: telemetry.toolCalls + }, + trajectory: telemetry.trajectory + }; } type JudgeOptions = { @@ -158,20 +253,24 @@ type JudgeResult = { }; async function judge( - client: { createSession: (config?: Record) => Promise }, + client: CopilotClient, options: JudgeOptions -): Promise { +): Promise<{ result: JudgeResult; metrics: AskMetrics; trajectory: TrajectoryEvent[] }> { const session = await client.createSession({ model: options.model, streaming: true, infiniteSessions: { enabled: false }, systemMessage: { - content: "You are a strict evaluator. Return JSON with keys: verdict (pass|fail|unknown), score (0-100), rationale. Do not include any other text." + content: + "You are a strict evaluator. Return JSON with keys: verdict (pass|fail|unknown), score (0-100), rationale. Do not include any other text." } }); let content = ""; + const telemetry = createTelemetry("judge"); + const startedAt = Date.now(); session.on((event: { type: string; data?: Record }) => { + captureTelemetryEvent(event, telemetry); if (event.type === "assistant.message_delta") { const delta = event.data?.deltaContent as string | undefined; if (delta) content += delta; @@ -195,7 +294,16 @@ async function judge( await session.sendAndWait({ prompt }, 120000); await session.destroy(); - return parseJudge(content); + const finishedAt = Date.now(); + return { + result: parseJudge(content), + metrics: { + durationMs: finishedAt - startedAt, + tokenUsage: normalizeTokenUsage(telemetry.tokenUsage), + toolCalls: telemetry.toolCalls + }, + trajectory: telemetry.trajectory + }; } function parseJudge(content: string): JudgeResult { @@ -220,37 +328,11 @@ function parseJudge(content: string): JudgeResult { async function loadConfig(configPath: string): Promise { const raw = await fs.readFile(configPath, "utf8"); - return JSON.parse(raw) as EvalConfig; -} - -async function findCopilotCliPath(): Promise { - // Try standard PATH first - try { - const { stdout } = await execFileAsync("which", ["copilot"], { timeout: 5000 }); - return stdout.trim(); - } catch { - // Ignore - will try VS Code location - } - - // VS Code Copilot Chat extension location - const home = process.env.HOME ?? ""; - const vscodeLocations = [ - `${home}/Library/Application Support/Code - Insiders/User/globalStorage/github.copilot-chat/copilotCli/copilot`, - `${home}/Library/Application Support/Code/User/globalStorage/github.copilot-chat/copilotCli/copilot`, - `${home}/.vscode-insiders/extensions/github.copilot-chat-*/copilotCli/copilot`, - `${home}/.vscode/extensions/github.copilot-chat-*/copilotCli/copilot`, - ]; - - for (const location of vscodeLocations) { - try { - await fs.access(location); - return location; - } catch { - // Try next location - } + const parsed = JSON.parse(raw) as EvalConfig; + if (!parsed || !Array.isArray(parsed.cases)) { + throw new Error("Eval config must have a 'cases' array."); } - - throw new Error("Copilot CLI not found. Install GitHub Copilot Chat extension in VS Code."); + return parsed; } async function readOptionalFile(filePath: string): Promise { @@ -271,21 +353,613 @@ function buildPrompt(repoPath: string, userPrompt: string): string { ].join("\n"); } -function formatSummary(results: EvalResult[]): string { +function formatSummary(results: EvalResult[], runDurationMs: number): string { const total = results.length; const passed = results.filter((r) => r.verdict === "pass").length; const failed = results.filter((r) => r.verdict === "fail").length; const unknown = results.filter((r) => r.verdict === "unknown").length; + const totalUsage = aggregateTokenUsage(results); + const hasUsage = Boolean( + totalUsage.promptTokens || totalUsage.completionTokens || totalUsage.totalTokens + ); const lines = [ - `Eval results: ${passed}/${total} pass, ${failed} fail, ${unknown} unknown.` + `Eval results: ${passed}/${total} pass, ${failed} fail, ${unknown} unknown.`, + `Runtime: ${formatDuration(runDurationMs)}.`, + hasUsage ? `Token usage: ${formatTokenUsage(totalUsage)}.` : "Token usage: unavailable." ]; for (const result of results) { - lines.push( - `- ${result.id}: ${result.verdict ?? "unknown"} (score: ${result.score ?? 0})` - ); + lines.push(`- ${result.id}: ${result.verdict ?? "unknown"} (score: ${result.score ?? 0})`); } return `\n${lines.join("\n")}`; } + +type TelemetryCollector = { + trajectory: TrajectoryEvent[]; + tokenUsage: TokenUsage; + toolCalls: ToolCallSummary; + toolCallMap: Map; + phase: EvalPhase; +}; + +function createTelemetry(phase: EvalPhase): TelemetryCollector { + return { + trajectory: [], + tokenUsage: {}, + toolCalls: { count: 0, byName: {}, totalDurationMs: 0 }, + toolCallMap: new Map(), + phase + }; +} + +function captureTelemetryEvent( + event: { type: string; data?: Record }, + telemetry: TelemetryCollector +): void { + const timestampMs = Date.now(); + telemetry.trajectory.push({ + timestampMs, + phase: telemetry.phase, + type: event.type, + data: sanitizeEventData(event.data) + }); + + if (event.type === "tool.execution_start") { + const toolName = (event.data?.toolName as string | undefined) ?? "unknown"; + const toolId = resolveToolId(event.data, toolName, telemetry.toolCallMap.size); + telemetry.toolCallMap.set(toolId, { name: toolName, startMs: timestampMs }); + telemetry.toolCalls.count += 1; + telemetry.toolCalls.byName[toolName] = (telemetry.toolCalls.byName[toolName] ?? 0) + 1; + } else if (event.type === "tool.execution_finish" || event.type === "tool.execution_error") { + const toolName = (event.data?.toolName as string | undefined) ?? "unknown"; + const toolId = resolveToolId(event.data, toolName, telemetry.toolCallMap.size); + const entry = + telemetry.toolCallMap.get(toolId) ?? findLatestToolByName(telemetry.toolCallMap, toolName); + if (entry) { + const durationMs = timestampMs - entry.startMs; + telemetry.toolCalls.totalDurationMs += durationMs; + telemetry.toolCallMap.delete(toolId); + } + } + + const usage = extractTokenUsage(event.data); + if (usage) { + telemetry.tokenUsage = mergeTokenUsage(telemetry.tokenUsage, usage); + } +} + +function resolveToolId( + data: Record | undefined, + toolName: string, + index: number +): string { + const rawId = data?.executionId ?? data?.toolCallId ?? data?.callId ?? data?.id; + if (typeof rawId === "string" || typeof rawId === "number") { + return String(rawId); + } + return `${toolName}-${index + 1}`; +} + +function findLatestToolByName( + map: Map, + toolName: string +): { name?: string; startMs: number } | undefined { + const entries = Array.from(map.values()).filter((entry) => entry.name === toolName); + return entries.at(-1); +} + +function extractTokenUsage(data: Record | undefined): TokenUsage | null { + if (!data) return null; + const usage = findUsageObject(data); + const promptTokens = getNumber( + usage?.prompt_tokens ?? usage?.promptTokens ?? data.promptTokens ?? data.inputTokens + ); + const completionTokens = getNumber( + usage?.completion_tokens ?? + usage?.completionTokens ?? + data.completionTokens ?? + data.outputTokens + ); + const totalTokens = getNumber(usage?.total_tokens ?? usage?.totalTokens ?? data.totalTokens); + + if (promptTokens == null && completionTokens == null && totalTokens == null) { + return null; + } + + return { + promptTokens: promptTokens ?? undefined, + completionTokens: completionTokens ?? undefined, + totalTokens: totalTokens ?? undefined + }; +} + +function findUsageObject(data: Record): Record | undefined { + const direct = (data.usage ?? data.tokenUsage ?? data.tokens) as + | Record + | undefined; + if (direct) return direct; + + const candidates = [data.response, data.result, data.message, data.metrics, data.output]; + + for (const candidate of candidates) { + if (candidate && typeof candidate === "object") { + const nested = + (candidate as Record).usage ?? + (candidate as Record).tokenUsage; + if (nested && typeof nested === "object") return nested as Record; + } + } + + return scanForUsage(data, 0); +} + +function scanForUsage(value: unknown, depth: number): Record | undefined { + if (!value || typeof value !== "object" || depth > 4) return undefined; + if (Array.isArray(value)) { + for (const entry of value) { + const found = scanForUsage(entry, depth + 1); + if (found) return found; + } + return undefined; + } + + const record = value as Record; + if (hasTokenFields(record)) return record; + + for (const entry of Object.values(record)) { + const found = scanForUsage(entry, depth + 1); + if (found) return found; + } + + return undefined; +} + +function hasTokenFields(record: Record): boolean { + const keys = Object.keys(record); + return ( + keys.includes("prompt_tokens") || + keys.includes("completion_tokens") || + keys.includes("total_tokens") || + keys.includes("promptTokens") || + keys.includes("completionTokens") || + keys.includes("totalTokens") || + keys.includes("inputTokens") || + keys.includes("outputTokens") + ); +} + +function getNumber(value: unknown): number | null { + if (typeof value === "number" && Number.isFinite(value)) return value; + if (typeof value === "string" && value.trim()) { + const parsed = Number(value); + return Number.isFinite(parsed) ? parsed : null; + } + return null; +} + +// The SDK reports cumulative token counts per session, so we keep the peak (max) value +// rather than summing incremental deltas. +function mergeTokenUsage(existing: TokenUsage, next: TokenUsage): TokenUsage { + return { + promptTokens: Math.max(existing.promptTokens ?? 0, next.promptTokens ?? 0) || undefined, + completionTokens: + Math.max(existing.completionTokens ?? 0, next.completionTokens ?? 0) || undefined, + totalTokens: Math.max(existing.totalTokens ?? 0, next.totalTokens ?? 0) || undefined + }; +} + +function normalizeTokenUsage(usage: TokenUsage): TokenUsage | undefined { + if (!usage.promptTokens && !usage.completionTokens && !usage.totalTokens) return undefined; + if (!usage.totalTokens) { + const prompt = usage.promptTokens ?? 0; + const completion = usage.completionTokens ?? 0; + const total = prompt + completion; + return { + ...usage, + totalTokens: total || undefined + }; + } + return usage; +} + +function aggregateTokenUsage(results: EvalResult[]): TokenUsage { + const total: TokenUsage = { promptTokens: 0, completionTokens: 0, totalTokens: 0 }; + for (const result of results) { + const metrics = result.metrics; + if (!metrics) continue; + const usages = [ + metrics.withoutInstructions.tokenUsage, + metrics.withInstructions.tokenUsage, + metrics.judge.tokenUsage + ]; + for (const usage of usages) { + if (!usage) continue; + total.promptTokens = (total.promptTokens ?? 0) + (usage.promptTokens ?? 0); + total.completionTokens = (total.completionTokens ?? 0) + (usage.completionTokens ?? 0); + total.totalTokens = (total.totalTokens ?? 0) + (usage.totalTokens ?? 0); + } + } + return total; +} + +function formatDuration(durationMs: number): string { + const seconds = Math.round(durationMs / 100) / 10; + if (seconds < 60) return `${seconds}s`; + const minutes = Math.floor(seconds / 60); + const remaining = Math.round((seconds % 60) * 10) / 10; + return `${minutes}m ${remaining}s`; +} + +function formatTokenUsage(usage: TokenUsage): string { + const prompt = usage.promptTokens ?? 0; + const completion = usage.completionTokens ?? 0; + const total = usage.totalTokens ?? prompt + completion; + return `prompt ${prompt}, completion ${completion}, total ${total}`; +} + +function resolveOutputPath( + repoPath: string, + override?: string, + configValue?: string +): string | undefined { + const chosen = override ?? configValue; + if (!chosen) return undefined; + return path.isAbsolute(chosen) ? chosen : path.resolve(repoPath, chosen); +} + +function buildViewerPath(outputPath: string): string { + if (outputPath.endsWith(".json")) { + return outputPath.replace(/\.json$/u, ".html"); + } + return `${outputPath}.html`; +} + +function buildTrajectoryViewerHtml(data: Record): string { + const serialized = JSON.stringify(data).replace(/ + + + + +Primer Eval Results + + + +
+
+ +
+

Eval Results

+
+
+
+ +
+ +
+ +
+
Impact of Instructions
+
+
+ +
+
Results by Case
+
+
+ +
+
Case Details
+
+
+ + + +`; +} + +function sanitizeEventData( + data: Record | undefined +): Record | undefined { + if (!data) return undefined; + const sanitized: Record = {}; + for (const [key, value] of Object.entries(data)) { + if (key === "deltaContent" && typeof value === "string") { + sanitized.deltaChars = value.length; + sanitized.deltaPreview = value.slice(0, 120); + continue; + } + sanitized[key] = sanitizeValue(value, 0); + } + return sanitized; +} + +function sanitizeValue(value: unknown, depth: number): unknown { + if (depth > 4) return "[depth-limit]"; + if (typeof value === "string") { + return value.length > 2000 ? `${value.slice(0, 2000)}…` : value; + } + if (Array.isArray(value)) { + return value.slice(0, 50).map((entry) => sanitizeValue(entry, depth + 1)); + } + if (value && typeof value === "object") { + const obj: Record = {}; + for (const [key, entry] of Object.entries(value as Record)) { + obj[key] = sanitizeValue(entry, depth + 1); + } + return obj; + } + return value; +} diff --git a/src/services/generator.ts b/src/services/generator.ts index 27c5c07..8e6c9d5 100644 --- a/src/services/generator.ts +++ b/src/services/generator.ts @@ -1,7 +1,9 @@ import path from "path"; -import { RepoAnalysis } from "./analyzer"; + import { ensureDir, safeWriteFile } from "../utils/fs"; +import type { RepoAnalysis } from "./analyzer"; + export type GenerateOptions = { repoPath: string; analysis: RepoAnalysis; @@ -9,8 +11,7 @@ export type GenerateOptions = { force: boolean; }; -export async function generateConfigs(options: GenerateOptions): Promise<{ summary: string }> -{ +export async function generateConfigs(options: GenerateOptions): Promise<{ summary: string }> { const { repoPath, analysis, selections, force } = options; const actions: string[] = []; @@ -30,7 +31,6 @@ export async function generateConfigs(options: GenerateOptions): Promise<{ summa actions.push(result); } - const summary = actions.length ? `\n${actions.join("\n")}` : "No changes made."; return { summary }; } @@ -74,9 +74,7 @@ function renderVscodeSettings(analysis: RepoAnalysis): string { "github.copilot.chat.codeGeneration.instructions": [ { file: ".github/copilot-instructions.md" } ], - "github.copilot.chat.reviewSelection.instructions": [ - { text: reviewFocus } - ], + "github.copilot.chat.reviewSelection.instructions": [{ text: reviewFocus }], "chat.promptFiles": true, "chat.mcp.enabled": true }, diff --git a/src/services/git.ts b/src/services/git.ts index a8b1e79..6333143 100644 --- a/src/services/git.ts +++ b/src/services/git.ts @@ -1,6 +1,8 @@ import fs from "fs/promises"; import path from "path"; -import simpleGit, { SimpleGitProgressEvent } from "simple-git"; + +import type { SimpleGitProgressEvent } from "simple-git"; +import simpleGit from "simple-git"; export async function isGitRepo(repoPath: string): Promise { try { @@ -24,16 +26,18 @@ export type CloneOptions = { }; export async function cloneRepo( - repoUrl: string, + repoUrl: string, destination: string, options: CloneOptions = {} ): Promise { const { shallow = true, timeoutMs = 60000, onProgress } = options; - + const git = simpleGit({ - progress: onProgress ? ({ stage, progress }: SimpleGitProgressEvent) => { - onProgress(stage, progress); - } : undefined, + progress: onProgress + ? ({ stage, progress }: SimpleGitProgressEvent) => { + onProgress(stage, progress); + } + : undefined, timeout: { block: timeoutMs } @@ -47,6 +51,15 @@ export async function cloneRepo( await git.clone(repoUrl, destination, cloneArgs); } +/** + * Replace the remote origin URL, typically to strip embedded credentials + * after cloning with an authenticated URL. + */ +export async function setRemoteUrl(repoPath: string, url: string): Promise { + const git = simpleGit(repoPath); + await git.remote(["set-url", "origin", url]); +} + export async function checkoutBranch(repoPath: string, branch: string): Promise { const git = simpleGit(repoPath); const branches = await git.branchLocal(); @@ -65,6 +78,8 @@ export async function commitAll(repoPath: string, message: string): Promise { +export function buildAuthedUrl(url: string, token: string, provider: AuthProvider): string { + const normalizedUrl = normalizeGitUrl(url); + if (!normalizedUrl.startsWith("https://")) return normalizedUrl; + if (provider === "azure") { + return normalizedUrl.replace("https://", `https://pat:${token}@`); + } + return normalizedUrl.replace("https://", `https://x-access-token:${token}@`); +} + +export async function pushBranch( + repoPath: string, + branch: string, + token?: string, + provider: AuthProvider = "github" +): Promise { const git = simpleGit(repoPath); - + if (token) { // Set up credentials for this push const remoteUrl = (await git.remote(["get-url", "origin"])) ?? ""; const normalizedUrl = normalizeGitUrl(remoteUrl); if (normalizedUrl.startsWith("https://")) { - const authedUrl = normalizedUrl.replace("https://", `https://x-access-token:${token}@`); + const authedUrl = buildAuthedUrl(normalizedUrl, token, provider); await git.remote(["set-url", "origin", authedUrl]); try { await git.push(["-u", "origin", branch]); + } catch (err) { + // Strip embedded credentials from error messages to avoid leaking tokens + const sanitized = + err instanceof Error + ? new Error(err.message.replace(/https:\/\/[^@]+@/g, "https://***@")) + : err; + throw sanitized; } finally { // Restore original URL to avoid leaking token await git.remote(["set-url", "origin", normalizedUrl]); @@ -96,6 +134,6 @@ export async function pushBranch(repoPath: string, branch: string, token?: strin return; } } - + await git.push(["-u", "origin", branch]); } diff --git a/src/services/github.ts b/src/services/github.ts index fbabaea..ffc7a6b 100644 --- a/src/services/github.ts +++ b/src/services/github.ts @@ -1,7 +1,8 @@ -import { Octokit } from "@octokit/rest"; import { execFile } from "node:child_process"; import { promisify } from "node:util"; +import { Octokit } from "@octokit/rest"; + const execFileAsync = promisify(execFile); /** @@ -45,7 +46,7 @@ export function createGitHubClient(token: string): Octokit { export async function listAccessibleRepos(token: string, limit = 100): Promise { const client = createGitHubClient(token); - + // Fetch only first page - avoids timeout for users with many repos const repos = await client.rest.repos.listForAuthenticatedUser({ visibility: "all", @@ -113,13 +114,13 @@ export async function listUserOrgs(token: string): Promise { return orgs.map((org) => ({ login: org.login, - name: org.name ?? null + name: org.description ?? null })); } export async function listOrgRepos(token: string, org: string, limit = 100): Promise { const client = createGitHubClient(token); - + // Fetch only the first page(s) up to limit - avoids timeout on huge orgs const repos = await client.rest.repos.listForOrg({ org, @@ -141,7 +142,11 @@ export async function listOrgRepos(token: string, org: string, limit = 100): Pro /** * Check if a repo has .github/copilot-instructions.md */ -export async function checkRepoHasInstructions(token: string, owner: string, repo: string): Promise { +export async function checkRepoHasInstructions( + token: string, + owner: string, + repo: string +): Promise { const client = createGitHubClient(token); try { await client.rest.repos.getContent({ @@ -150,8 +155,16 @@ export async function checkRepoHasInstructions(token: string, owner: string, rep path: ".github/copilot-instructions.md" }); return true; - } catch { - return false; + } catch (error: unknown) { + if ( + error && + typeof error === "object" && + "status" in error && + (error as { status: number }).status === 404 + ) { + return false; + } + throw error; } } @@ -159,8 +172,8 @@ export async function checkRepoHasInstructions(token: string, owner: string, rep * Check multiple repos for instructions in parallel (with concurrency limit) */ export async function checkReposForInstructions( - token: string, - repos: GitHubRepo[], + token: string, + repos: GitHubRepo[], onProgress?: (checked: number, total: number) => void ): Promise { const concurrency = 10; diff --git a/src/services/instructions.ts b/src/services/instructions.ts index a1ea882..78735ef 100644 --- a/src/services/instructions.ts +++ b/src/services/instructions.ts @@ -1,6 +1,7 @@ -import fs from "fs/promises"; -import { execFile } from "node:child_process"; -import { promisify } from "node:util"; +import { DEFAULT_MODEL } from "../config"; +import { withCwd } from "../utils/cwd"; + +import { assertCopilotCliReady } from "./copilot"; type GenerateInstructionsOptions = { repoPath: string; @@ -9,126 +10,84 @@ type GenerateInstructionsOptions = { onProgress?: (message: string) => void; }; -export async function generateCopilotInstructions(options: GenerateInstructionsOptions): Promise { +export async function generateCopilotInstructions( + options: GenerateInstructionsOptions +): Promise { const repoPath = options.repoPath; const progress = options.onProgress ?? (() => {}); - const originalCwd = process.cwd(); - process.chdir(repoPath); - - progress("Checking Copilot CLI..."); - const cliPath = await assertCopilotCliReady(); + return withCwd(repoPath, async () => { + progress("Checking Copilot CLI..."); + const cliPath = await assertCopilotCliReady(); - progress("Starting Copilot SDK..."); - const sdk = await import("@github/copilot-sdk"); - const client = new sdk.CopilotClient({ - cliPath, - }); - - try { - progress("Creating session..."); - // Try requested model, fall back to gpt-4.1 if gpt-5 fails - const preferredModel = options.model ?? "gpt-4.1"; - const session = await client.createSession({ - model: preferredModel, - streaming: true, - systemMessage: { - content: "You are an expert codebase analyst. Your task is to generate a concise .github/copilot-instructions.md file. Use the available tools (glob, view, grep) to explore the codebase. Output ONLY the final markdown content, no explanations.", - }, - infiniteSessions: { enabled: false }, + progress("Starting Copilot SDK..."); + const sdk = await import("@github/copilot-sdk"); + const client = new sdk.CopilotClient({ + cliPath }); - let content = ""; - - // Subscribe to events for progress and to capture content - session.on((event) => { - const e = event as { type: string; data?: Record }; - if (e.type === "assistant.message_delta") { - const delta = e.data?.deltaContent as string | undefined; - if (delta) { - content += delta; - progress("Generating instructions..."); - } - } else if (e.type === "tool.execution_start") { - const toolName = e.data?.toolName as string | undefined; - progress(`Using tool: ${toolName ?? "..."}`); - } else if (e.type === "session.error") { - const errorMsg = (e.data?.message as string) ?? "Unknown error"; - if (errorMsg.toLowerCase().includes("auth") || errorMsg.toLowerCase().includes("login")) { - throw new Error("Copilot CLI not logged in. Run `copilot` then `/login` to authenticate."); + try { + progress("Creating session..."); + const preferredModel = options.model ?? DEFAULT_MODEL; + const session = await client.createSession({ + model: preferredModel, + streaming: true, + systemMessage: { + content: + "You are an expert codebase analyst. Your task is to generate a concise .github/copilot-instructions.md file. Use the available tools (glob, view, grep) to explore the codebase. Output ONLY the final markdown content, no explanations." + }, + infiniteSessions: { enabled: false } + }); + + let content = ""; + + // Subscribe to events for progress and to capture content + session.on((event) => { + const e = event as { type: string; data?: Record }; + if (e.type === "assistant.message_delta") { + const delta = e.data?.deltaContent as string | undefined; + if (delta) { + content += delta; + progress("Generating instructions..."); + } + } else if (e.type === "tool.execution_start") { + const toolName = e.data?.toolName as string | undefined; + progress(`Using tool: ${toolName ?? "..."}`); + } else if (e.type === "session.error") { + const errorMsg = (e.data?.message as string) ?? "Unknown error"; + if (errorMsg.toLowerCase().includes("auth") || errorMsg.toLowerCase().includes("login")) { + throw new Error( + "Copilot CLI not logged in. Run `copilot` then `/login` to authenticate." + ); + } } - } - }); + }); - // Simple prompt - let the agent use tools to explore - const prompt = `Analyze this codebase at ${repoPath} and generate a .github/copilot-instructions.md file. + // Simple prompt - let the agent use tools to explore + const prompt = `Analyze this codebase and generate a .github/copilot-instructions.md file. Use tools to explore: 1. Check for existing instruction files: glob for **/{.github/copilot-instructions.md,AGENT.md,CLAUDE.md,.cursorrules,README.md} -2. Identify the tech stack: look at package.json, tsconfig.json, pyproject.toml, Cargo.toml, etc. +2. Identify the tech stack: look at package.json, tsconfig.json, pyproject.toml, Cargo.toml, go.mod, *.csproj, *.sln, build.gradle, pom.xml, etc. 3. Understand the structure: list key directories +4. Detect monorepo structures: check for workspace configs (npm/pnpm/yarn workspaces, Cargo.toml [workspace], go.work, .sln solution files, settings.gradle include directives, pom.xml modules) Generate concise instructions (~20-50 lines) covering: - Tech stack and architecture -- Build/test commands +- Build/test commands - Project-specific conventions - Key files/directories +- Monorepo structure and per-app layout (if this is a monorepo, describe the workspace organization, how apps relate to each other, and any shared libraries) Output ONLY the markdown content for the instructions file.`; - progress("Analyzing codebase..."); - await session.sendAndWait({ prompt }, 180000); - await session.destroy(); - - return content.trim() || ""; - } finally { - await client.stop(); - process.chdir(originalCwd); - } -} + progress("Analyzing codebase..."); + await session.sendAndWait({ prompt }, 180000); + await session.destroy(); -const execFileAsync = promisify(execFile); - -async function findCopilotCliPath(): Promise { - // Try standard PATH first - try { - const { stdout } = await execFileAsync("which", ["copilot"], { timeout: 5000 }); - return stdout.trim(); - } catch { - // Ignore - will try VS Code location - } - - // VS Code Copilot Chat extension location - const home = process.env.HOME ?? ""; - const vscodeLocations = [ - `${home}/Library/Application Support/Code - Insiders/User/globalStorage/github.copilot-chat/copilotCli/copilot`, - `${home}/Library/Application Support/Code/User/globalStorage/github.copilot-chat/copilotCli/copilot`, - `${home}/.vscode-insiders/extensions/github.copilot-chat-*/copilotCli/copilot`, - `${home}/.vscode/extensions/github.copilot-chat-*/copilotCli/copilot`, - ]; - - for (const location of vscodeLocations) { - try { - await fs.access(location); - return location; - } catch { - // Try next location + return content.trim() || ""; + } finally { + await client.stop(); } - } - - throw new Error("Copilot CLI not found. Install GitHub Copilot Chat extension in VS Code."); + }); } - -async function assertCopilotCliReady(): Promise { - const cliPath = await findCopilotCliPath(); - - try { - await execFileAsync(cliPath, ["--version"], { timeout: 5000 }); - } catch { - throw new Error(`Copilot CLI at ${cliPath} is not working.`); - } - - // Note: Copilot CLI uses its own auth system, not gh CLI. - // User must run: copilot, then /login inside the CLI. - return cliPath; -} \ No newline at end of file diff --git a/src/services/readiness.ts b/src/services/readiness.ts new file mode 100644 index 0000000..e0e0a08 --- /dev/null +++ b/src/services/readiness.ts @@ -0,0 +1,876 @@ +import fs from "fs/promises"; +import path from "path"; + +import { fileExists, safeReadDir, readJson } from "../utils/fs"; + +import type { RepoApp, RepoAnalysis } from "./analyzer"; +import { analyzeRepo } from "./analyzer"; + +export type ReadinessPillar = + | "style-validation" + | "build-system" + | "testing" + | "documentation" + | "dev-environment" + | "code-quality" + | "observability" + | "security-governance" + | "ai-tooling"; + +export type ReadinessScope = "repo" | "app"; + +export type ReadinessStatus = "pass" | "fail" | "skip"; + +export type ReadinessCriterionResult = { + id: string; + title: string; + pillar: ReadinessPillar; + level: number; + scope: ReadinessScope; + impact: "high" | "medium" | "low"; + effort: "low" | "medium" | "high"; + status: ReadinessStatus; + reason?: string; + evidence?: string[]; + passRate?: number; + appSummary?: { passed: number; total: number }; + appFailures?: string[]; +}; + +export type ReadinessExtraResult = { + id: string; + title: string; + status: ReadinessStatus; + reason?: string; +}; + +export type ReadinessPillarSummary = { + id: ReadinessPillar; + name: string; + passed: number; + total: number; + passRate: number; +}; + +export type ReadinessLevelSummary = { + level: number; + name: string; + passed: number; + total: number; + passRate: number; + achieved: boolean; +}; + +export type ReadinessReport = { + repoPath: string; + generatedAt: string; + isMonorepo: boolean; + apps: Array<{ name: string; path: string }>; + pillars: ReadinessPillarSummary[]; + levels: ReadinessLevelSummary[]; + achievedLevel: number; + criteria: ReadinessCriterionResult[]; + extras: ReadinessExtraResult[]; +}; + +type ReadinessOptions = { + repoPath: string; + includeExtras?: boolean; +}; + +type ReadinessContext = { + repoPath: string; + analysis: RepoAnalysis; + apps: RepoApp[]; + rootFiles: string[]; + rootPackageJson?: Record; +}; + +type ReadinessCriterion = { + id: string; + title: string; + pillar: ReadinessPillar; + level: number; + scope: ReadinessScope; + impact: "high" | "medium" | "low"; + effort: "low" | "medium" | "high"; + check: (context: ReadinessContext, app?: RepoApp) => Promise; +}; + +type CheckResult = { + status: ReadinessStatus; + reason?: string; + evidence?: string[]; +}; + +export async function runReadinessReport(options: ReadinessOptions): Promise { + const repoPath = options.repoPath; + const analysis = await analyzeRepo(repoPath); + const rootFiles = await safeReadDir(repoPath); + const rootPackageJson = await readJson(path.join(repoPath, "package.json")); + const apps = analysis.apps?.length ? analysis.apps : []; + + const context: ReadinessContext = { + repoPath, + analysis, + apps, + rootFiles, + rootPackageJson + }; + + const criteria = buildCriteria(); + const criteriaResults: ReadinessCriterionResult[] = []; + + for (const criterion of criteria) { + if (criterion.scope === "repo") { + const result = await criterion.check(context); + criteriaResults.push({ + id: criterion.id, + title: criterion.title, + pillar: criterion.pillar, + level: criterion.level, + scope: criterion.scope, + impact: criterion.impact, + effort: criterion.effort, + status: result.status, + reason: result.reason, + evidence: result.evidence + }); + continue; + } + + const appResults = await Promise.all( + apps.map(async (app) => ({ + app, + result: await criterion.check(context, app) + })) + ); + + if (!appResults.length) { + criteriaResults.push({ + id: criterion.id, + title: criterion.title, + pillar: criterion.pillar, + level: criterion.level, + scope: criterion.scope, + impact: criterion.impact, + effort: criterion.effort, + status: "skip", + reason: "No application packages detected." + }); + continue; + } + + const passed = appResults.filter((entry) => entry.result.status === "pass").length; + const total = appResults.length; + const passRate = total ? passed / total : 0; + const status: ReadinessStatus = passRate >= 0.8 ? "pass" : "fail"; + const failures = appResults + .filter((entry) => entry.result.status !== "pass") + .map((entry) => entry.app.name); + + criteriaResults.push({ + id: criterion.id, + title: criterion.title, + pillar: criterion.pillar, + level: criterion.level, + scope: criterion.scope, + impact: criterion.impact, + effort: criterion.effort, + status, + reason: status === "pass" ? undefined : `Only ${passed}/${total} apps pass this check.`, + passRate, + appSummary: { passed, total }, + appFailures: failures + }); + } + + const pillars = summarizePillars(criteriaResults); + const levels = summarizeLevels(criteriaResults); + const achievedLevel = levels + .filter((level) => level.achieved) + .reduce((acc, level) => Math.max(acc, level.level), 0); + + const extras = options.includeExtras === false ? [] : await runExtras(context); + + return { + repoPath, + generatedAt: new Date().toISOString(), + isMonorepo: analysis.isMonorepo ?? false, + apps: apps.map((app) => ({ name: app.name, path: app.path })), + pillars, + levels, + achievedLevel, + criteria: criteriaResults, + extras + }; +} + +function buildCriteria(): ReadinessCriterion[] { + return [ + { + id: "lint-config", + title: "Linting configured", + pillar: "style-validation", + level: 1, + scope: "repo", + impact: "high", + effort: "low", + check: async (context) => { + const found = await hasLintConfig(context.repoPath); + return { + status: found ? "pass" : "fail", + reason: found ? undefined : "Missing ESLint/Biome/Prettier configuration.", + evidence: ["eslint.config.js", ".eslintrc", "biome.json", ".prettierrc"] + }; + } + }, + { + id: "typecheck-config", + title: "Type checking configured", + pillar: "style-validation", + level: 2, + scope: "repo", + impact: "medium", + effort: "low", + check: async (context) => { + const found = await hasTypecheckConfig(context.repoPath); + return { + status: found ? "pass" : "fail", + reason: found ? undefined : "Missing type checking config (tsconfig or equivalent).", + evidence: ["tsconfig.json", "pyproject.toml", "mypy.ini"] + }; + } + }, + { + id: "build-script", + title: "Build script present", + pillar: "build-system", + level: 1, + scope: "app", + impact: "high", + effort: "low", + check: async (_context, app) => { + const found = Boolean(app?.scripts?.build); + return { + status: found ? "pass" : "fail", + reason: found ? undefined : "Missing build script in package.json." + }; + } + }, + { + id: "ci-config", + title: "CI workflow configured", + pillar: "build-system", + level: 2, + scope: "repo", + impact: "high", + effort: "medium", + check: async (context) => { + const found = await hasGithubWorkflows(context.repoPath); + return { + status: found ? "pass" : "fail", + reason: found ? undefined : "Missing .github/workflows CI configuration.", + evidence: [".github/workflows"] + }; + } + }, + { + id: "test-script", + title: "Test script present", + pillar: "testing", + level: 1, + scope: "app", + impact: "high", + effort: "low", + check: async (_context, app) => { + const found = Boolean(app?.scripts?.test); + return { + status: found ? "pass" : "fail", + reason: found ? undefined : "Missing test script in package.json." + }; + } + }, + { + id: "readme", + title: "README present", + pillar: "documentation", + level: 1, + scope: "repo", + impact: "high", + effort: "low", + check: async (context) => { + const found = await hasReadme(context.repoPath); + return { + status: found ? "pass" : "fail", + reason: found ? undefined : "Missing README documentation.", + evidence: ["README.md"] + }; + } + }, + { + id: "contributing", + title: "CONTRIBUTING guide present", + pillar: "documentation", + level: 2, + scope: "repo", + impact: "medium", + effort: "low", + check: async (context) => { + const found = await fileExists(path.join(context.repoPath, "CONTRIBUTING.md")); + return { + status: found ? "pass" : "fail", + reason: found ? undefined : "Missing CONTRIBUTING.md for contributor workflows." + }; + } + }, + { + id: "lockfile", + title: "Lockfile present", + pillar: "dev-environment", + level: 1, + scope: "repo", + impact: "high", + effort: "low", + check: async (context) => { + const found = hasAnyFile(context.rootFiles, [ + "pnpm-lock.yaml", + "yarn.lock", + "package-lock.json", + "bun.lockb" + ]); + return { + status: found ? "pass" : "fail", + reason: found ? undefined : "Missing package manager lockfile." + }; + } + }, + { + id: "env-example", + title: "Environment example present", + pillar: "dev-environment", + level: 2, + scope: "repo", + impact: "medium", + effort: "low", + check: async (context) => { + const found = hasAnyFile(context.rootFiles, [".env.example", ".env.sample"]); + return { + status: found ? "pass" : "fail", + reason: found ? undefined : "Missing .env.example or .env.sample for setup guidance." + }; + } + }, + { + id: "format-config", + title: "Formatter configured", + pillar: "code-quality", + level: 2, + scope: "repo", + impact: "medium", + effort: "low", + check: async (context) => { + const found = await hasFormatterConfig(context.repoPath); + return { + status: found ? "pass" : "fail", + reason: found ? undefined : "Missing Prettier/Biome formatting config." + }; + } + }, + { + id: "codeowners", + title: "CODEOWNERS present", + pillar: "security-governance", + level: 2, + scope: "repo", + impact: "medium", + effort: "low", + check: async (context) => { + const found = await hasCodeowners(context.repoPath); + return { + status: found ? "pass" : "fail", + reason: found ? undefined : "Missing CODEOWNERS file." + }; + } + }, + { + id: "license", + title: "LICENSE present", + pillar: "security-governance", + level: 1, + scope: "repo", + impact: "medium", + effort: "low", + check: async (context) => { + const found = await hasLicense(context.repoPath); + return { + status: found ? "pass" : "fail", + reason: found ? undefined : "Missing LICENSE file." + }; + } + }, + { + id: "security-policy", + title: "Security policy present", + pillar: "security-governance", + level: 3, + scope: "repo", + impact: "high", + effort: "low", + check: async (context) => { + const found = await fileExists(path.join(context.repoPath, "SECURITY.md")); + return { + status: found ? "pass" : "fail", + reason: found ? undefined : "Missing SECURITY.md policy." + }; + } + }, + { + id: "dependabot", + title: "Dependabot configured", + pillar: "security-governance", + level: 3, + scope: "repo", + impact: "medium", + effort: "medium", + check: async (context) => { + const found = await fileExists(path.join(context.repoPath, ".github", "dependabot.yml")); + return { + status: found ? "pass" : "fail", + reason: found ? undefined : "Missing .github/dependabot.yml configuration." + }; + } + }, + { + id: "observability", + title: "Observability tooling present", + pillar: "observability", + level: 3, + scope: "repo", + impact: "medium", + effort: "medium", + check: async (context) => { + const deps = await readAllDependencies(context); + const has = deps.some((dep) => + ["@opentelemetry/api", "@opentelemetry/sdk", "pino", "winston", "bunyan"].includes(dep) + ); + return { + status: has ? "pass" : "fail", + reason: "No observability dependencies detected (OpenTelemetry/logging)." + }; + } + }, + { + id: "custom-instructions", + title: "Custom AI instructions or agent guidance", + pillar: "ai-tooling", + level: 1, + scope: "repo", + impact: "high", + effort: "low", + check: async (context) => { + const rootFound = await hasCustomInstructions(context.repoPath); + if (rootFound.length === 0) { + return { + status: "fail", + reason: + "Missing custom AI instructions (e.g. copilot-instructions.md, CLAUDE.md, AGENTS.md, .cursorrules).", + evidence: [ + "copilot-instructions.md", + "CLAUDE.md", + "AGENTS.md", + ".cursorrules", + ".github/copilot-instructions.md" + ] + }; + } + + // For monorepos, also check that each app has its own instructions + if (context.analysis.isMonorepo && context.apps.length > 1) { + const appsMissing: string[] = []; + for (const app of context.apps) { + const appFound = await hasCustomInstructions(app.path); + if (appFound.length === 0) { + appsMissing.push(app.name); + } + } + if (appsMissing.length > 0) { + return { + status: "pass", + reason: `Root instructions found, but ${appsMissing.length}/${context.apps.length} apps missing their own: ${appsMissing.join(", ")}`, + evidence: [ + ...rootFound, + ...appsMissing.map((name) => `${name}: missing app-level instructions`) + ] + }; + } + } + + return { + status: "pass", + evidence: rootFound + }; + } + }, + { + id: "mcp-config", + title: "MCP configuration present", + pillar: "ai-tooling", + level: 2, + scope: "repo", + impact: "high", + effort: "low", + check: async (context) => { + const found = await hasMcpConfig(context.repoPath); + return { + status: found.length > 0 ? "pass" : "fail", + reason: "Missing MCP (Model Context Protocol) configuration (e.g. .vscode/mcp.json).", + evidence: + found.length > 0 + ? found + : [".vscode/mcp.json", ".vscode/settings.json (mcp section)", "mcp.json"] + }; + } + }, + { + id: "custom-agents", + title: "Custom AI agents configured", + pillar: "ai-tooling", + level: 3, + scope: "repo", + impact: "medium", + effort: "medium", + check: async (context) => { + const found = await hasCustomAgents(context.repoPath); + return { + status: found.length > 0 ? "pass" : "fail", + reason: "No custom AI agents configured (e.g. .github/agents/, .copilot/agents/).", + evidence: + found.length > 0 + ? found + : [".github/agents/", ".copilot/agents/", ".github/copilot/agents/"] + }; + } + }, + { + id: "copilot-skills", + title: "Copilot/Claude skills present", + pillar: "ai-tooling", + level: 3, + scope: "repo", + impact: "medium", + effort: "medium", + check: async (context) => { + const found = await hasCopilotSkills(context.repoPath); + return { + status: found.length > 0 ? "pass" : "fail", + reason: "No Copilot or Claude skills found (e.g. .copilot/skills/, .github/skills/).", + evidence: + found.length > 0 ? found : [".copilot/skills/", ".github/skills/", ".claude/skills/"] + }; + } + } + ]; +} + +async function runExtras(context: ReadinessContext): Promise { + const results: ReadinessExtraResult[] = []; + + results.push({ + id: "agents-doc", + title: "AGENTS.md present", + status: (await fileExists(path.join(context.repoPath, "AGENTS.md"))) ? "pass" : "fail", + reason: "Missing AGENTS.md to guide coding agents." + }); + + results.push({ + id: "pr-template", + title: "Pull request template present", + status: (await hasPullRequestTemplate(context.repoPath)) ? "pass" : "fail", + reason: "Missing PR template for consistent reviews." + }); + + results.push({ + id: "pre-commit", + title: "Pre-commit hooks configured", + status: (await hasPrecommitConfig(context.repoPath)) ? "pass" : "fail", + reason: "Missing pre-commit or Husky configuration for fast feedback." + }); + + results.push({ + id: "architecture-doc", + title: "Architecture guide present", + status: (await hasArchitectureDoc(context.repoPath)) ? "pass" : "fail", + reason: "Missing architecture documentation." + }); + + return results; +} + +function summarizePillars(criteria: ReadinessCriterionResult[]): ReadinessPillarSummary[] { + const pillarNames: Record = { + "style-validation": "Style & Validation", + "build-system": "Build System", + testing: "Testing", + documentation: "Documentation", + "dev-environment": "Dev Environment", + "code-quality": "Code Quality", + observability: "Observability", + "security-governance": "Security & Governance", + "ai-tooling": "AI Tooling" + }; + + return (Object.keys(pillarNames) as ReadinessPillar[]).map((pillar) => { + const items = criteria.filter((criterion) => criterion.pillar === pillar); + const { passed, total } = countStatus(items); + return { + id: pillar, + name: pillarNames[pillar], + passed, + total, + passRate: total ? passed / total : 0 + }; + }); +} + +function summarizeLevels(criteria: ReadinessCriterionResult[]): ReadinessLevelSummary[] { + const levelNames: Record = { + 1: "Functional", + 2: "Documented", + 3: "Standardized", + 4: "Optimized", + 5: "Autonomous" + }; + + const summaries: ReadinessLevelSummary[] = []; + for (let level = 1; level <= 5; level += 1) { + const items = criteria.filter((criterion) => criterion.level === level); + const { passed, total } = countStatus(items); + const passRate = total ? passed / total : 0; + summaries.push({ + level, + name: levelNames[level], + passed, + total, + passRate, + achieved: false + }); + } + + for (const summary of summaries) { + const allPrior = summaries.filter((candidate) => candidate.level <= summary.level); + const achieved = allPrior.every( + (candidate) => candidate.total > 0 && candidate.passRate >= 0.8 + ); + summary.achieved = achieved; + } + + return summaries; +} + +function countStatus(items: ReadinessCriterionResult[]): { passed: number; total: number } { + const relevant = items.filter((item) => item.status !== "skip"); + const passed = relevant.filter((item) => item.status === "pass").length; + return { passed, total: relevant.length }; +} + +function hasAnyFile(files: string[], candidates: string[]): boolean { + return candidates.some((candidate) => files.includes(candidate)); +} + +async function hasReadme(repoPath: string): Promise { + const files = await safeReadDir(repoPath); + return files.some( + (file) => file.toLowerCase() === "readme.md" || file.toLowerCase() === "readme" + ); +} + +async function hasLintConfig(repoPath: string): Promise { + return hasAnyFile(await safeReadDir(repoPath), [ + "eslint.config.js", + "eslint.config.mjs", + ".eslintrc", + ".eslintrc.js", + ".eslintrc.cjs", + ".eslintrc.json", + ".eslintrc.yml", + ".eslintrc.yaml", + "biome.json", + "biome.jsonc", + ".prettierrc", + ".prettierrc.json", + ".prettierrc.js", + ".prettierrc.cjs", + "prettier.config.js", + "prettier.config.cjs" + ]); +} + +async function hasFormatterConfig(repoPath: string): Promise { + return hasAnyFile(await safeReadDir(repoPath), [ + "biome.json", + "biome.jsonc", + ".prettierrc", + ".prettierrc.json", + ".prettierrc.js", + ".prettierrc.cjs", + "prettier.config.js", + "prettier.config.cjs" + ]); +} + +async function hasTypecheckConfig(repoPath: string): Promise { + return hasAnyFile(await safeReadDir(repoPath), [ + "tsconfig.json", + "tsconfig.base.json", + "pyproject.toml", + "mypy.ini" + ]); +} + +async function hasGithubWorkflows(repoPath: string): Promise { + return fileExists(path.join(repoPath, ".github", "workflows")); +} + +async function hasCodeowners(repoPath: string): Promise { + const root = await fileExists(path.join(repoPath, "CODEOWNERS")); + const github = await fileExists(path.join(repoPath, ".github", "CODEOWNERS")); + return root || github; +} + +async function hasLicense(repoPath: string): Promise { + const files = await safeReadDir(repoPath); + return files.some((file) => file.toLowerCase().startsWith("license")); +} + +async function hasPullRequestTemplate(repoPath: string): Promise { + const direct = await fileExists(path.join(repoPath, ".github", "PULL_REQUEST_TEMPLATE.md")); + if (direct) return true; + const dir = path.join(repoPath, ".github", "PULL_REQUEST_TEMPLATE"); + try { + const entries = await fs.readdir(dir); + return entries.some((entry) => entry.toLowerCase().endsWith(".md")); + } catch { + return false; + } +} + +async function hasPrecommitConfig(repoPath: string): Promise { + const precommit = await fileExists(path.join(repoPath, ".pre-commit-config.yaml")); + if (precommit) return true; + return fileExists(path.join(repoPath, ".husky")); +} + +async function hasArchitectureDoc(repoPath: string): Promise { + const files = await safeReadDir(repoPath); + if (files.some((file) => file.toLowerCase() === "architecture.md")) return true; + return fileExists(path.join(repoPath, "docs", "architecture.md")); +} + +async function hasCustomInstructions(repoPath: string): Promise { + const found: string[] = []; + const candidates = [ + ".github/copilot-instructions.md", + "CLAUDE.md", + ".claude/CLAUDE.md", + "AGENTS.md", + ".github/AGENTS.md", + ".cursorrules", + ".cursorignore", + ".windsurfrules", + ".github/instructions.md", + "copilot-instructions.md" + ]; + for (const candidate of candidates) { + if (await fileExists(path.join(repoPath, candidate))) { + found.push(candidate); + } + } + return found; +} + +async function hasMcpConfig(repoPath: string): Promise { + const found: string[] = []; + // Check .vscode/mcp.json + if (await fileExists(path.join(repoPath, ".vscode", "mcp.json"))) { + found.push(".vscode/mcp.json"); + } + // Check root mcp.json + if (await fileExists(path.join(repoPath, "mcp.json"))) { + found.push("mcp.json"); + } + // Check .vscode/settings.json for MCP section + const settings = await readJson(path.join(repoPath, ".vscode", "settings.json")); + if (settings && (settings["mcp"] || settings["github.copilot.chat.mcp.enabled"])) { + found.push(".vscode/settings.json (mcp section)"); + } + // Check .claude/mcp.json + if (await fileExists(path.join(repoPath, ".claude", "mcp.json"))) { + found.push(".claude/mcp.json"); + } + return found; +} + +async function hasCustomAgents(repoPath: string): Promise { + const found: string[] = []; + const agentDirs = [".github/agents", ".copilot/agents", ".github/copilot/agents"]; + for (const dir of agentDirs) { + if (await fileExists(path.join(repoPath, dir))) { + found.push(dir); + } + } + // Check for agent config files + const agentFiles = [".github/copilot-agents.yml", ".github/copilot-agents.yaml"]; + for (const agentFile of agentFiles) { + if (await fileExists(path.join(repoPath, agentFile))) { + found.push(agentFile); + } + } + return found; +} + +async function hasCopilotSkills(repoPath: string): Promise { + const found: string[] = []; + const skillDirs = [ + ".copilot/skills", + ".github/skills", + ".claude/skills", + ".github/copilot/skills" + ]; + for (const dir of skillDirs) { + if (await fileExists(path.join(repoPath, dir))) { + found.push(dir); + } + } + return found; +} + +async function readAllDependencies(context: ReadinessContext): Promise { + const dependencies: string[] = []; + const apps = context.apps.length ? context.apps : []; + for (const app of apps) { + if (!app.packageJsonPath) continue; + const pkg = await readJson(app.packageJsonPath); + const deps = (pkg?.dependencies ?? {}) as Record; + const devDeps = (pkg?.devDependencies ?? {}) as Record; + dependencies.push( + ...Object.keys({ + ...deps, + ...devDeps + }) + ); + } + + if (!apps.length && context.rootPackageJson) { + const rootDeps = (context.rootPackageJson.dependencies ?? {}) as Record; + const rootDevDeps = (context.rootPackageJson.devDependencies ?? {}) as Record; + dependencies.push( + ...Object.keys({ + ...rootDeps, + ...rootDevDeps + }) + ); + } + + return Array.from(new Set(dependencies)); +} diff --git a/src/services/visualReport.ts b/src/services/visualReport.ts new file mode 100644 index 0000000..620562a --- /dev/null +++ b/src/services/visualReport.ts @@ -0,0 +1,860 @@ +import type { ReadinessReport } from "./readiness"; + +type VisualReportOptions = { + reports: Array<{ repo: string; report: ReadinessReport; error?: string }>; + title?: string; + generatedAt?: string; +}; + +export function generateVisualReport(options: VisualReportOptions): string { + const { + reports, + title = "AI Readiness Report", + generatedAt = new Date().toISOString() + } = options; + + const successfulReports = reports.filter((r) => !r.error); + const failedReports = reports.filter((r) => r.error); + + const totalRepos = reports.length; + const successfulRepos = successfulReports.length; + const avgLevel = + successfulReports.length > 0 + ? successfulReports.reduce((sum, r) => sum + r.report.achievedLevel, 0) / + successfulReports.length + : 0; + + const pillarStats = calculatePillarStats(successfulReports); + const aiToolingData = calculateAiToolingData(successfulReports); + + return ` + + + + + ${escapeHtml(title)} + + + +
+
+ +
+

${escapeHtml(title)}

+

Generated ${new Date(generatedAt).toLocaleString()}

+
+ +
+ +
+
+
Repositories
+
${totalRepos}
+
${successfulRepos} analyzed successfully
+
+
+
Avg Maturity
+
${avgLevel.toFixed(1)}
+
${getLevelName(Math.round(avgLevel))}
+
+
+
Success Rate
+
${totalRepos > 0 ? Math.round((successfulRepos / totalRepos) * 100) : 0}%
+
${failedReports.length > 0 ? failedReports.length + " failed" : "All succeeded"}
+
+
+ + ${ + successfulReports.length > 0 + ? ` + ${buildAiToolingHeroHtml(aiToolingData, successfulReports)} + +
+

Pillar Performance

+
+ ${pillarStats + .map( + (pillar) => ` +
+
${escapeHtml(pillar.name)}
+
+
+
+
+ ${pillar.passed}/${pillar.total} (${Math.round(pillar.passRate * 100)}%) +
+
+ ` + ) + .join("")} +
+
+ +
+

Maturity Model

+
+ ${[1, 2, 3, 4, 5] + .map((level) => { + const count = successfulReports.filter((r) => r.report.achievedLevel === level).length; + return ` +
+
+ ${level} + ${getLevelName(level)} + ${count} repo${count !== 1 ? "s" : ""} +
+
${getLevelDescription(level)}
+
+ `; + }) + .join("")} +
+ +

Distribution

+
+ ${[1, 2, 3, 4, 5] + .map((level) => { + const count = successfulReports.filter((r) => r.report.achievedLevel === level).length; + const percent = + successfulReports.length > 0 ? (count / successfulReports.length) * 100 : 0; + const barHeight = count > 0 ? Math.max(40, percent * 2) : 0; + return ` +
+
${count}
+
+
${level}
${getLevelName(level)}
+
+ `; + }) + .join("")} +
+
+ ` + : "" + } + +
+

Repository Details

+
+ ${reports + .map(({ repo, report, error }) => { + if (error) { + return ` +
+
+
${escapeHtml(repo)}
+ Error +
+
${escapeHtml(error)}
+
+ `; + } + + return ` +
+
+
${escapeHtml(repo)}
+
+ Maturity ${report.achievedLevel}: ${getLevelName(report.achievedLevel)} +
+
+ ${report.isMonorepo ? `
Monorepo · ${report.apps.length} apps
` : ""} +
+ ${report.pillars + .map((pillar) => { + const pillarCriteria = report.criteria.filter((c) => c.pillar === pillar.id); + return ` +
+
+ + ${escapeHtml(pillar.name)} + ${pillar.passed}/${pillar.total} (${Math.round(pillar.passRate * 100)}%) + +
+ ${pillarCriteria + .map( + (c) => ` +
+ ${escapeHtml(c.title)} + ${c.status === "pass" ? "Pass" : c.status === "fail" ? "Fail" : "Skip"} +
+ ` + ) + .join("")} + ${pillarCriteria.length === 0 ? '
No criteria
' : ""} +
+
+
+ `; + }) + .join("")} +
+ ${getTopFixesHtml(report)} +
+ `; + }) + .join("")} +
+
+ + ${ + failedReports.length > 0 + ? ` +
+

Failed Repositories

+
+ ${failedReports + .map( + ({ repo, error }) => ` +
+
${escapeHtml(repo)}
+
${escapeHtml(error || "Unknown error")}
+
+ ` + ) + .join("")} +
+
+ ` + : "" + } + + +
+ + +`; +} + +// ── Helper Functions ────────────────────────────────────────────────── + +function calculatePillarStats(reports: Array<{ repo: string; report: ReadinessReport }>): Array<{ + id: string; + name: string; + passed: number; + total: number; + passRate: number; +}> { + const pillarMap = new Map(); + + for (const { report } of reports) { + for (const pillar of report.pillars) { + const existing = pillarMap.get(pillar.id); + if (existing) { + existing.passed += pillar.passed; + existing.total += pillar.total; + } else { + pillarMap.set(pillar.id, { + name: pillar.name, + passed: pillar.passed, + total: pillar.total + }); + } + } + } + + return Array.from(pillarMap.entries()).map(([id, stats]) => ({ + id, + name: stats.name, + passed: stats.passed, + total: stats.total, + passRate: stats.total > 0 ? stats.passed / stats.total : 0 + })); +} + +function getTopFixesHtml(report: ReadinessReport): string { + const failedCriteria = report.criteria + .filter((c) => c.status === "fail") + .sort((a, b) => { + const impactWeight = { high: 3, medium: 2, low: 1 }; + const effortWeight = { low: 1, medium: 2, high: 3 }; + const impactDelta = impactWeight[b.impact] - impactWeight[a.impact]; + if (impactDelta !== 0) return impactDelta; + return effortWeight[a.effort] - effortWeight[b.effort]; + }) + .slice(0, 3); + + if (failedCriteria.length === 0) { + return '
All criteria passing
'; + } + + return ` +
+
Top Fixes Needed
+
    + ${failedCriteria + .map( + (c) => ` +
  • + + ${escapeHtml(c.title)} + ${c.impact} impact, ${c.effort} effort +
  • + ` + ) + .join("")} +
+
+ `; +} + +function getLevelName(level: number): string { + const names: Record = { + 1: "Functional", + 2: "Documented", + 3: "Standardized", + 4: "Optimized", + 5: "Autonomous" + }; + return names[level] || "Unknown"; +} + +function getLevelDescription(level: number): string { + const descriptions: Record = { + 1: "Repo builds, tests run, and basic tooling (linter, lockfile) is in place. AI agents can clone and get started.", + 2: "README, CONTRIBUTING guide, and custom AI instructions exist. Agents understand project context and conventions.", + 3: "CI/CD, security policies, CODEOWNERS, and observability are configured. Agents operate within well-defined guardrails.", + 4: "MCP servers, custom agents, and AI skills are set up. Agents have deep integration with project-specific tools and workflows.", + 5: "Full AI-native development: agents can independently plan, implement, test, and ship changes with minimal human oversight." + }; + return descriptions[level] || ""; +} + +function getProgressClass(passRate: number): string { + if (passRate >= 0.8) return "high"; + if (passRate >= 0.5) return "medium"; + return "low"; +} + +// ── AI Tooling Hero ─────────────────────────────────────────────────── + +type AiToolingCriterionSummary = { + id: string; + title: string; + passCount: number; + totalRepos: number; + status: "pass" | "fail"; + evidence: string[]; + reason: string; +}; + +type AiToolingData = { + criteria: AiToolingCriterionSummary[]; + passed: number; + total: number; + passRate: number; +}; + +function calculateAiToolingData( + reports: Array<{ repo: string; report: ReadinessReport }> +): AiToolingData { + const criterionMap = new Map(); + + for (const { report } of reports) { + const aiCriteria = report.criteria.filter((c) => c.pillar === "ai-tooling"); + for (const c of aiCriteria) { + const existing = criterionMap.get(c.id); + if (existing) { + existing.totalRepos += 1; + if (c.status === "pass") existing.passCount += 1; + if (c.evidence) existing.evidence.push(...c.evidence); + } else { + criterionMap.set(c.id, { + id: c.id, + title: c.title, + passCount: c.status === "pass" ? 1 : 0, + totalRepos: 1, + status: c.status === "pass" ? "pass" : "fail", + evidence: c.evidence ? [...c.evidence] : [], + reason: c.reason || "" + }); + } + } + } + + const criteria = Array.from(criterionMap.values()).map((c) => ({ + ...c, + status: (c.passCount / c.totalRepos >= 0.5 ? "pass" : "fail") as "pass" | "fail", + evidence: [...new Set(c.evidence)] + })); + + const passed = criteria.filter((c) => c.status === "pass").length; + return { + criteria, + passed, + total: criteria.length, + passRate: criteria.length > 0 ? passed / criteria.length : 0 + }; +} + +function getAiScoreClass(passRate: number): string { + if (passRate >= 0.6) return "score-high"; + if (passRate >= 0.3) return "score-medium"; + return "score-low"; +} + +function getAiScoreLabel(passRate: number): string { + if (passRate >= 0.8) return "Excellent"; + if (passRate >= 0.6) return "Good"; + if (passRate >= 0.4) return "Fair"; + if (passRate >= 0.2) return "Getting Started"; + return "Not Started"; +} + +function getAiCriterionIcon(id: string): string { + const icons: Record = { + "custom-instructions": "📝", + "mcp-config": "🔌", + "custom-agents": "🤖", + "copilot-skills": "⚡" + }; + return icons[id] || "🔧"; +} + +function buildAiToolingHeroHtml( + data: AiToolingData, + reports: Array<{ repo: string; report: ReadinessReport }> +): string { + if (data.criteria.length === 0) return ""; + + const pct = Math.round(data.passRate * 100); + const scoreClass = getAiScoreClass(data.passRate); + const scoreLabel = getAiScoreLabel(data.passRate); + + const multiRepo = reports.length > 1; + const perRepoHtml = multiRepo + ? ` +
+
Per Repository
+
+ ${reports + .map(({ repo, report }) => { + const aiPillar = report.pillars.find((p) => p.id === "ai-tooling"); + const repoPct = aiPillar ? Math.round(aiPillar.passRate * 100) : 0; + const repoPass = aiPillar?.passed ?? 0; + const repoTotal = aiPillar?.total ?? 0; + return `
+ ${escapeHtml(repo)} + = 30 ? "var(--color-attention-fg)" : "var(--color-danger-fg)"};">${repoPass}/${repoTotal} (${repoPct}%) +
`; + }) + .join("")} +
+
+ ` + : ""; + + return ` +
+

AI Tooling Readiness

+

How well prepared ${multiRepo ? "your repositories are" : "this repository is"} for AI-assisted development

+ +
+
${pct}%
+
+
${scoreLabel}
+
${data.passed} of ${data.total} AI tooling checks passing${multiRepo ? ` across ${reports.length} repositories` : ""}
+
+
+ +
+ ${data.criteria + .map( + (c) => ` +
+
+ ${c.status === "pass" ? "✓" : "✗"} +
+
+
${getAiCriterionIcon(c.id)} ${escapeHtml(c.title)}
+
${ + c.status === "pass" + ? multiRepo + ? `${c.passCount}/${c.totalRepos} repos` + : "Detected" + : escapeHtml(c.reason) + }
+
+
+ ` + ) + .join("")} +
+ ${perRepoHtml} +
+ `; +} + +function escapeHtml(text: string): string { + const map: Record = { + "&": "&", + "<": "<", + ">": ">", + '"': """, + "'": "'" + }; + return text.replace(/[&<>"']/g, (m) => map[m]); +} diff --git a/src/ui/AnimatedBanner.tsx b/src/ui/AnimatedBanner.tsx index f9e5509..7191416 100644 --- a/src/ui/AnimatedBanner.tsx +++ b/src/ui/AnimatedBanner.tsx @@ -1,5 +1,5 @@ -import React, { useState, useEffect } from "react"; import { Box, Text } from "ink"; +import React, { useState, useEffect } from "react"; /** * Animation frames for the PRIMER banner fly-in effect. @@ -14,31 +14,31 @@ const FULL_BANNER = [ "██████╔╝██████╔╝██║██╔████╔██║█████╗ ██████╔╝", "██╔═══╝ ██╔══██╗██║██║╚██╔╝██║██╔══╝ ██╔══██╗", "██║ ██║ ██║██║██║ ╚═╝ ██║███████╗██║ ██║", - "╚═╝ ╚═╝ ╚═╝╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝", + "╚═╝ ╚═╝ ╚═╝╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝" ]; // Animation frames - slide in from right with progressive reveal const generateFrames = (): string[][] => { const frames: string[][] = []; const width = FULL_BANNER[0].length; - + // Frame 0-4: Empty -> sparkles appearing frames.push(["", "", "", "", "", ""]); frames.push(["", "", " ✦", "", "", ""]); frames.push([" ✦", "", " ✦ ✧", "", " ✦", ""]); - + // Frame 5-15: Slide in from right for (let offset = width; offset >= 0; offset -= 4) { - const frame = FULL_BANNER.map(line => { + const frame = FULL_BANNER.map((line) => { if (offset >= line.length) return ""; return " ".repeat(Math.max(0, offset)) + line.slice(0, Math.max(0, line.length - offset)); }); frames.push(frame); } - + // Final frame: Full banner frames.push([...FULL_BANNER]); - + return frames; }; @@ -51,13 +51,13 @@ type ColorRole = "primary" | "accent" | "sparkle"; const THEME_DARK: Record = { primary: "magentaBright", accent: "cyanBright", - sparkle: "yellowBright", + sparkle: "yellowBright" }; const THEME_LIGHT: Record = { primary: "magenta", - accent: "cyan", - sparkle: "yellow", + accent: "cyan", + sparkle: "yellow" }; type AnimatedBannerProps = { @@ -66,14 +66,14 @@ type AnimatedBannerProps = { darkMode?: boolean; }; -export function AnimatedBanner({ - onComplete, +export function AnimatedBanner({ + onComplete, skipAnimation = false, - darkMode = true, + darkMode = true }: AnimatedBannerProps): React.JSX.Element { const [frameIndex, setFrameIndex] = useState(skipAnimation ? FRAMES.length - 1 : 0); const [isComplete, setIsComplete] = useState(skipAnimation); - + const theme = darkMode ? THEME_DARK : THEME_LIGHT; useEffect(() => { @@ -107,9 +107,9 @@ export function AnimatedBanner({ return ( {currentFrame.map((line, i) => ( - {line || " "} @@ -124,7 +124,7 @@ export function AnimatedBanner({ */ export function StaticBanner({ darkMode = true }: { darkMode?: boolean }): React.JSX.Element { const color = darkMode ? "magentaBright" : "magenta"; - + return ( {FULL_BANNER.map((line, i) => ( diff --git a/src/ui/BatchReadinessTui.tsx b/src/ui/BatchReadinessTui.tsx new file mode 100644 index 0000000..d0742fd --- /dev/null +++ b/src/ui/BatchReadinessTui.tsx @@ -0,0 +1,345 @@ +import fs from "fs/promises"; +import os from "os"; +import path from "path"; + +import { Box, Text, useApp, useInput } from "ink"; +import React, { useEffect, useState } from "react"; +import simpleGit from "simple-git"; + +import { buildAuthedUrl, cloneRepo } from "../services/git"; +import type { GitHubOrg, GitHubRepo } from "../services/github"; +import { listUserOrgs, listOrgRepos, listAccessibleRepos } from "../services/github"; +import type { ReadinessReport } from "../services/readiness"; +import { runReadinessReport } from "../services/readiness"; +import { generateVisualReport } from "../services/visualReport"; +import { ensureDir, validateCachePath } from "../utils/fs"; + +import { StaticBanner } from "./AnimatedBanner"; + +type Props = { + token: string; + outputPath?: string; +}; + +type Status = + | "loading-orgs" + | "select-orgs" + | "loading-repos" + | "select-repos" + | "confirm" + | "processing" + | "complete" + | "error"; + +type ProcessResult = { + repo: string; + report?: ReadinessReport; + error?: string; +}; + +export function BatchReadinessTui({ token, outputPath }: Props): React.JSX.Element { + const app = useApp(); + const [status, setStatus] = useState("loading-orgs"); + const [message, setMessage] = useState("Fetching organizations..."); + const [errorMessage, setErrorMessage] = useState(""); + + // Data + const [orgs, setOrgs] = useState([]); + const [repos, setRepos] = useState([]); + const [selectedOrgIndices, setSelectedOrgIndices] = useState>(new Set()); + const [selectedRepoIndices, setSelectedRepoIndices] = useState>(new Set()); + const [cursorIndex, setCursorIndex] = useState(0); + + // Processing + const [results, setResults] = useState([]); + const [currentRepoIndex, setCurrentRepoIndex] = useState(0); + const [processingMessage, setProcessingMessage] = useState(""); + + // Load orgs on mount + useEffect(() => { + loadOrgs(); + }, []); + + async function loadOrgs() { + try { + const userOrgs = await listUserOrgs(token); + const allOrgs: GitHubOrg[] = [ + { login: "__personal__", name: "Personal Repositories" }, + ...userOrgs + ]; + setOrgs(allOrgs); + setStatus("select-orgs"); + setMessage("Select organizations (space to toggle, enter to confirm)"); + } catch (error) { + setStatus("error"); + setErrorMessage(error instanceof Error ? error.message : "Failed to fetch organizations"); + } + } + + async function loadRepos() { + setStatus("loading-repos"); + setMessage("Fetching repositories..."); + try { + const selectedOrgs = Array.from(selectedOrgIndices).map((i) => orgs[i]); + let allRepos: GitHubRepo[] = []; + + for (let idx = 0; idx < selectedOrgs.length; idx++) { + const org = selectedOrgs[idx]; + setMessage( + `Fetching repos from ${org.name ?? org.login} (${idx + 1}/${selectedOrgs.length})...` + ); + + if (org.login === "__personal__") { + const personalRepos = await listAccessibleRepos(token); + const userRepos = personalRepos + .filter((r) => !orgs.some((o) => o.login !== "__personal__" && o.login === r.owner)) + .slice(0, 100); + allRepos = [...allRepos, ...userRepos]; + } else { + const orgRepos = await listOrgRepos(token, org.login, 100); + allRepos = [...allRepos, ...orgRepos]; + } + } + + setRepos(allRepos); + setStatus("select-repos"); + setMessage(`Select repositories (${allRepos.length} available)`); + setCursorIndex(0); + } catch (error) { + setStatus("error"); + setErrorMessage(error instanceof Error ? error.message : "Failed to fetch repositories"); + } + } + + async function processRepos() { + setStatus("processing"); + const selectedRepos = Array.from(selectedRepoIndices).map((i) => repos[i]); + const results: ProcessResult[] = []; + const tmpDir = path.join(os.tmpdir(), `primer-batch-readiness-${Date.now()}`); + + try { + await ensureDir(tmpDir); + + for (let i = 0; i < selectedRepos.length; i++) { + const repo = selectedRepos[i]; + setCurrentRepoIndex(i); + setProcessingMessage(`Analyzing ${repo.fullName} (${i + 1}/${selectedRepos.length})`); + + const repoDir = validateCachePath(tmpDir, repo.owner, repo.name); + + try { + // Clone repo + setProcessingMessage(`Cloning ${repo.fullName}...`); + const authedUrl = buildAuthedUrl(repo.cloneUrl, token, "github"); + await cloneRepo(authedUrl, repoDir, { shallow: true }); + // Strip credentials from persisted remote URL + const git = simpleGit(repoDir); + await git.remote(["set-url", "origin", repo.cloneUrl]); + + // Run readiness report + setProcessingMessage(`Running readiness report for ${repo.fullName}...`); + const report = await runReadinessReport({ repoPath: repoDir }); + + results.push({ + repo: repo.fullName, + report + }); + } catch (error) { + results.push({ + repo: repo.fullName, + error: error instanceof Error ? error.message : "Unknown error" + }); + } + } + + setResults(results); + + // Generate visual report + const html = generateVisualReport({ + reports: results + .filter((r) => r.report || r.error) + .map((r) => ({ + repo: r.repo, + report: r.report ?? { + repoPath: r.repo, + generatedAt: new Date().toISOString(), + isMonorepo: false, + apps: [], + pillars: [], + levels: [], + achievedLevel: 0, + criteria: [], + extras: [] + }, + error: r.error + })), + title: "Batch AI Readiness Report", + generatedAt: new Date().toISOString() + }); + + const finalOutputPath = outputPath ?? path.join(process.cwd(), "batch-readiness-report.html"); + await fs.writeFile(finalOutputPath, html, "utf8"); + + setStatus("complete"); + setMessage(`Report generated: ${finalOutputPath}`); + } catch (error) { + setStatus("error"); + setErrorMessage(error instanceof Error ? error.message : "Failed to process repositories"); + } finally { + // Clean up temp directory + try { + await fs.rm(tmpDir, { recursive: true, force: true }); + } catch { + // Ignore cleanup errors + } + } + } + + useInput((input, key) => { + if (key.escape || input.toLowerCase() === "q") { + app.exit(); + return; + } + + if (status === "select-orgs") { + if (key.upArrow) { + setCursorIndex(Math.max(0, cursorIndex - 1)); + } else if (key.downArrow) { + setCursorIndex(Math.min(orgs.length - 1, cursorIndex + 1)); + } else if (input === " ") { + const newSelected = new Set(selectedOrgIndices); + if (newSelected.has(cursorIndex)) { + newSelected.delete(cursorIndex); + } else { + newSelected.add(cursorIndex); + } + setSelectedOrgIndices(newSelected); + } else if (key.return) { + if (selectedOrgIndices.size === 0) { + setMessage("Please select at least one organization"); + return; + } + loadRepos().catch((err) => { + setStatus("error"); + setErrorMessage(err instanceof Error ? err.message : "Failed to load repos"); + }); + } else if (input.toLowerCase() === "a") { + setSelectedOrgIndices(new Set(orgs.map((_, i) => i))); + } + } + + if (status === "select-repos") { + if (key.upArrow) { + setCursorIndex(Math.max(0, cursorIndex - 1)); + } else if (key.downArrow) { + setCursorIndex(Math.min(repos.length - 1, cursorIndex + 1)); + } else if (input === " ") { + const newSelected = new Set(selectedRepoIndices); + if (newSelected.has(cursorIndex)) { + newSelected.delete(cursorIndex); + } else { + newSelected.add(cursorIndex); + } + setSelectedRepoIndices(newSelected); + } else if (key.return) { + if (selectedRepoIndices.size === 0) { + setMessage("Please select at least one repository"); + return; + } + setStatus("confirm"); + setMessage(`Process ${selectedRepoIndices.size} repositories? (y/n)`); + } else if (input.toLowerCase() === "a") { + setSelectedRepoIndices(new Set(repos.map((_, i) => i))); + } + } + + if (status === "confirm") { + if (input.toLowerCase() === "y") { + processRepos().catch((err) => { + setStatus("error"); + setErrorMessage(err instanceof Error ? err.message : "Processing failed"); + }); + } else if (input.toLowerCase() === "n") { + setStatus("select-repos"); + setMessage(`Select repositories (${repos.length} available)`); + } + } + + if (status === "complete" || status === "error") { + app.exit(); + } + }); + + return ( + + + + Batch Readiness Report + + + + {message} + + + {status === "error" && errorMessage && ( + + {errorMessage} + + )} + + {status === "select-orgs" && ( + + Organizations: + {orgs.slice(0, 20).map((org, i) => ( + + {i === cursorIndex ? ">" : " "} [{selectedOrgIndices.has(i) ? "●" : " "}]{" "} + {org.name ?? org.login} + + ))} + + [Space] toggle • [A] select all • [Enter] confirm • [Q] quit + + + )} + + {status === "select-repos" && ( + + Repositories ({repos.length}): + {repos + .slice(Math.max(0, cursorIndex - 10), Math.min(repos.length, cursorIndex + 10)) + .map((repo, i) => { + const actualIndex = Math.max(0, cursorIndex - 10) + i; + return ( + + {actualIndex === cursorIndex ? ">" : " "} [ + {selectedRepoIndices.has(actualIndex) ? "●" : " "}] {repo.fullName} + + ); + })} + + [Space] toggle • [A] select all • [Enter] confirm • [Q] quit + + + )} + + {status === "processing" && ( + + Processing repositories... + {processingMessage} + + Progress: {currentRepoIndex + 1}/{Array.from(selectedRepoIndices).length} + + + )} + + {status === "complete" && ( + + ✓ Complete! + Total repositories: {results.length} + Successful: {results.filter((r) => !r.error).length} + Failed: {results.filter((r) => r.error).length} + + )} + + ); +} diff --git a/src/ui/BatchTui.tsx b/src/ui/BatchTui.tsx index 2a13121..f4b39a7 100644 --- a/src/ui/BatchTui.tsx +++ b/src/ui/BatchTui.tsx @@ -1,19 +1,31 @@ -import React, { useEffect, useState } from "react"; -import { Box, Text, useApp, useInput } from "ink"; -import path from "path"; import fs from "fs/promises"; +import path from "path"; + +import { Box, Text, useApp, useInput } from "ink"; +import React, { useEffect, useState } from "react"; +import simpleGit from "simple-git"; + +import { DEFAULT_MODEL } from "../config"; +import { + buildAuthedUrl, + cloneRepo, + checkoutBranch, + commitAll, + pushBranch, + isGitRepo +} from "../services/git"; +import type { GitHubOrg, GitHubRepo } from "../services/github"; import { - GitHubOrg, - GitHubRepo, listUserOrgs, listOrgRepos, createPullRequest, listAccessibleRepos, checkReposForInstructions } from "../services/github"; -import { cloneRepo, checkoutBranch, commitAll, pushBranch, isGitRepo, CloneOptions } from "../services/git"; import { generateCopilotInstructions } from "../services/instructions"; -import { ensureDir } from "../utils/fs"; +import { ensureDir, validateCachePath } from "../utils/fs"; +import { buildInstructionsPrBody } from "../utils/pr"; + import { StaticBanner } from "./AnimatedBanner"; type Props = { @@ -53,7 +65,7 @@ export function BatchTui({ token, outputPath }: Props): React.JSX.Element { // Processing const [results, setResults] = useState([]); - const [currentRepoIndex, setCurrentRepoIndex] = useState(0); + const [_currentRepoIndex, setCurrentRepoIndex] = useState(0); const [processingMessage, setProcessingMessage] = useState(""); // Load orgs on mount @@ -82,19 +94,21 @@ export function BatchTui({ token, outputPath }: Props): React.JSX.Element { setStatus("loading-repos"); setMessage("Fetching repositories..."); try { - const selectedOrgs = Array.from(selectedOrgIndices).map(i => orgs[i]); + const selectedOrgs = Array.from(selectedOrgIndices).map((i) => orgs[i]); let allRepos: GitHubRepo[] = []; for (let idx = 0; idx < selectedOrgs.length; idx++) { const org = selectedOrgs[idx]; - setMessage(`Fetching repos from ${org.name ?? org.login} (${idx + 1}/${selectedOrgs.length})...`); - + setMessage( + `Fetching repos from ${org.name ?? org.login} (${idx + 1}/${selectedOrgs.length})...` + ); + if (org.login === "__personal__") { // Fetch personal repos (limited to 100 most recently pushed) const personalRepos = await listAccessibleRepos(token); // Filter to only repos owned by the user (not org repos) const userRepos = personalRepos - .filter(r => !orgs.some(o => o.login !== "__personal__" && o.login === r.owner)) + .filter((r) => !orgs.some((o) => o.login !== "__personal__" && o.login === r.owner)) .slice(0, 100); allRepos = [...allRepos, ...userRepos]; } else { @@ -106,7 +120,7 @@ export function BatchTui({ token, outputPath }: Props): React.JSX.Element { // Deduplicate by fullName const seen = new Set(); - const uniqueRepos = allRepos.filter(r => { + const uniqueRepos = allRepos.filter((r) => { if (seen.has(r.fullName)) return false; seen.add(r.fullName); return true; @@ -115,9 +129,10 @@ export function BatchTui({ token, outputPath }: Props): React.JSX.Element { // Check which repos already have instructions setMessage(`Checking ${uniqueRepos.length} repos for existing instructions...`); const reposWithStatus = await checkReposForInstructions( - token, + token, uniqueRepos, - (checked, total) => setMessage(`Checking for existing instructions (${checked}/${total})...`) + (checked, total) => + setMessage(`Checking for existing instructions (${checked}/${total})...`) ); // Sort: repos without instructions first @@ -126,14 +141,16 @@ export function BatchTui({ token, outputPath }: Props): React.JSX.Element { return a.hasInstructions ? 1 : -1; }); - const withInstructions = reposWithStatus.filter(r => r.hasInstructions).length; + const withInstructions = reposWithStatus.filter((r) => r.hasInstructions).length; const withoutInstructions = reposWithStatus.length - withInstructions; setRepos(reposWithStatus); setCursorIndex(0); setSelectedRepoIndices(new Set()); setStatus("select-repos"); - setMessage(`Found ${reposWithStatus.length} repos (${withoutInstructions} need instructions, ${withInstructions} already have them)`); + setMessage( + `Found ${reposWithStatus.length} repos (${withoutInstructions} need instructions, ${withInstructions} already have them)` + ); } catch (error) { setStatus("error"); setErrorMessage(error instanceof Error ? error.message : "Failed to fetch repositories"); @@ -141,11 +158,13 @@ export function BatchTui({ token, outputPath }: Props): React.JSX.Element { } async function processRepos() { - const selectedRepos = Array.from(selectedRepoIndices).map(i => repos[i]); + const selectedRepos = Array.from(selectedRepoIndices).map((i) => repos[i]); setStatus("processing"); setCurrentRepoIndex(0); setResults([]); + const localResults: ProcessResult[] = []; + for (let i = 0; i < selectedRepos.length; i++) { const repo = selectedRepos[i]; setCurrentRepoIndex(i); @@ -154,44 +173,59 @@ export function BatchTui({ token, outputPath }: Props): React.JSX.Element { try { // Clone const cacheRoot = path.join(process.cwd(), ".primer-cache"); - const repoPath = path.join(cacheRoot, repo.owner, repo.name); + const repoPath = validateCachePath(cacheRoot, repo.owner, repo.name); await ensureDir(repoPath); if (!(await isGitRepo(repoPath))) { - // Add auth to clone URL (strip trailing slashes first) - const cleanUrl = repo.cloneUrl.replace(/\/+$/, ""); - const authedUrl = cleanUrl.replace("https://", `https://x-access-token:${token}@`); + const authedUrl = buildAuthedUrl(repo.cloneUrl, token, "github"); await cloneRepo(authedUrl, repoPath, { shallow: true, timeoutMs: 120000, // 2 minute timeout for clone onProgress: (stage, progress) => { - setProcessingMessage(`[${i + 1}/${selectedRepos.length}] ${repo.fullName}: Cloning (${stage} ${progress}%)...`); + setProcessingMessage( + `[${i + 1}/${selectedRepos.length}] ${repo.fullName}: Cloning (${stage} ${progress}%)...` + ); } }); + // Strip credentials from persisted remote URL + const git = simpleGit(repoPath); + await git.remote(["set-url", "origin", repo.cloneUrl]); } // Branch - setProcessingMessage(`[${i + 1}/${selectedRepos.length}] ${repo.fullName}: Creating branch...`); + setProcessingMessage( + `[${i + 1}/${selectedRepos.length}] ${repo.fullName}: Creating branch...` + ); const branch = "primer/add-instructions"; await checkoutBranch(repoPath, branch); // Generate instructions with timeout - setProcessingMessage(`[${i + 1}/${selectedRepos.length}] ${repo.fullName}: Generating instructions...`); - + setProcessingMessage( + `[${i + 1}/${selectedRepos.length}] ${repo.fullName}: Generating instructions...` + ); + const timeoutMs = 120000; // 2 minute timeout per repo const instructionsPromise = generateCopilotInstructions({ repoPath, - model: "gpt-4.1", + model: DEFAULT_MODEL, onProgress: (msg) => { setProcessingMessage(`[${i + 1}/${selectedRepos.length}] ${repo.fullName}: ${msg}`); } }); - + + let timer: ReturnType; const timeoutPromise = new Promise((_, reject) => { - setTimeout(() => reject(new Error("Generation timed out after 2 minutes")), timeoutMs); + timer = setTimeout( + () => reject(new Error("Generation timed out after 2 minutes")), + timeoutMs + ); }); - - const instructions = await Promise.race([instructionsPromise, timeoutPromise]); + + const instructions = await Promise.race([instructionsPromise, timeoutPromise]).finally(() => + clearTimeout(timer) + ); + // Prevent unhandled rejection if the losing promise rejects later + instructionsPromise.catch(() => {}); if (!instructions.trim()) { throw new Error("Generated instructions were empty"); @@ -217,22 +251,23 @@ export function BatchTui({ token, outputPath }: Props): React.JSX.Element { owner: repo.owner, repo: repo.name, title: "🤖 Add Copilot instructions via Primer", - body: buildPrBody(), + body: buildInstructionsPrBody(), head: branch, base: repo.defaultBranch }); - setResults(prev => [...prev, { repo: repo.fullName, success: true, prUrl }]); + localResults.push({ repo: repo.fullName, success: true, prUrl }); + setResults([...localResults]); } catch (error) { const errorMsg = error instanceof Error ? error.message : "Unknown error"; - setResults(prev => [...prev, { repo: repo.fullName, success: false, error: errorMsg }]); + localResults.push({ repo: repo.fullName, success: false, error: errorMsg }); + setResults([...localResults]); } } // Write results if output path specified if (outputPath) { - const finalResults = [...results]; - await fs.writeFile(outputPath, JSON.stringify(finalResults, null, 2), "utf8"); + await fs.writeFile(outputPath, JSON.stringify(localResults, null, 2), "utf8"); } setStatus("complete"); @@ -248,11 +283,11 @@ export function BatchTui({ token, outputPath }: Props): React.JSX.Element { if (status === "select-orgs") { const items = orgs; if (key.upArrow) { - setCursorIndex(prev => Math.max(0, prev - 1)); + setCursorIndex((prev) => Math.max(0, prev - 1)); } else if (key.downArrow) { - setCursorIndex(prev => Math.min(items.length - 1, prev + 1)); + setCursorIndex((prev) => Math.min(items.length - 1, prev + 1)); } else if (input === " ") { - setSelectedOrgIndices(prev => { + setSelectedOrgIndices((prev) => { const next = new Set(prev); if (next.has(cursorIndex)) { next.delete(cursorIndex); @@ -262,18 +297,21 @@ export function BatchTui({ token, outputPath }: Props): React.JSX.Element { return next; }); } else if (key.return && selectedOrgIndices.size > 0) { - loadRepos(); + loadRepos().catch((err) => { + setStatus("error"); + setErrorMessage(err instanceof Error ? err.message : "Failed to load repos"); + }); } } if (status === "select-repos") { const items = repos; if (key.upArrow) { - setCursorIndex(prev => Math.max(0, prev - 1)); + setCursorIndex((prev) => Math.max(0, prev - 1)); } else if (key.downArrow) { - setCursorIndex(prev => Math.min(items.length - 1, prev + 1)); + setCursorIndex((prev) => Math.min(items.length - 1, prev + 1)); } else if (input === " ") { - setSelectedRepoIndices(prev => { + setSelectedRepoIndices((prev) => { const next = new Set(prev); if (next.has(cursorIndex)) { next.delete(cursorIndex); @@ -291,13 +329,18 @@ export function BatchTui({ token, outputPath }: Props): React.JSX.Element { setSelectedRepoIndices(new Set(indicesWithoutInstructions)); } else if (key.return && selectedRepoIndices.size > 0) { setStatus("confirm"); - setMessage(`Ready to process ${selectedRepoIndices.size} repositories. Press Y to confirm, N to go back.`); + setMessage( + `Ready to process ${selectedRepoIndices.size} repositories. Press Y to confirm, N to go back.` + ); } } if (status === "confirm") { if (input.toLowerCase() === "y") { - processRepos(); + processRepos().catch((err) => { + setStatus("error"); + setErrorMessage(err instanceof Error ? err.message : "Processing failed"); + }); } else if (input.toLowerCase() === "n") { setStatus("select-repos"); setMessage("Select repos (space to toggle, enter to confirm)"); @@ -366,7 +409,9 @@ export function BatchTui({ token, outputPath }: Props): React.JSX.Element { {isCursor ? "❯ " : " "} {isSelected ? "◉" : "○"} - {repo.hasInstructions ? "✓" : "✗"} + + {repo.hasInstructions ? "✓" : "✗"}{" "} + {repo.fullName} {repo.isPrivate && (private)} @@ -407,7 +452,8 @@ export function BatchTui({ token, outputPath }: Props): React.JSX.Element { {status === "complete" && ( - ✓ Batch complete: {results.filter(r => r.success).length} succeeded, {results.filter(r => !r.success).length} failed + ✓ Batch complete: {results.filter((r) => r.success).length} succeeded,{" "} + {results.filter((r) => !r.success).length} failed {results.map((r) => ( @@ -423,44 +469,16 @@ export function BatchTui({ token, outputPath }: Props): React.JSX.Element { {status === "select-orgs" && ( - Keys: [Space] Toggle [Enter] Confirm [Q] Quit + Keys: [Space] Toggle [Enter] Confirm [Q] Quit )} {status === "select-repos" && ( - Keys: [Space] Toggle [A] Select Missing [Enter] Confirm [Q] Quit + Keys: [Space] Toggle [A] Select Missing [Enter] Confirm [Q] Quit )} {status === "confirm" && ( - Keys: [Y] Yes, proceed [N] Go back [Q] Quit - )} - {(status === "complete" || status === "error") && ( - Keys: [Q] Quit + Keys: [Y] Yes, proceed [N] Go back [Q] Quit )} + {(status === "complete" || status === "error") && Keys: [Q] Quit} ); } - -function buildPrBody(): string { - return [ - "## 🤖 Copilot Instructions Added", - "", - "This PR adds a `.github/copilot-instructions.md` file to help GitHub Copilot understand this codebase better.", - "", - "### What's Included", - "", - "The instructions file contains:", - "- Project overview and architecture", - "- Tech stack and conventions", - "- Build/test commands", - "- Key directories and files", - "", - "### Benefits", - "", - "With these instructions, Copilot will:", - "- Generate more contextually-aware code suggestions", - "- Follow project-specific patterns and conventions", - "- Understand the codebase structure", - "", - "---", - "*Generated by [Primer](https://github.com/pierceboggan/primer) - Prime your repos for AI*" - ].join("\n"); -} diff --git a/src/ui/BatchTuiAzure.tsx b/src/ui/BatchTuiAzure.tsx new file mode 100644 index 0000000..8f7d88c --- /dev/null +++ b/src/ui/BatchTuiAzure.tsx @@ -0,0 +1,555 @@ +import fs from "fs/promises"; +import path from "path"; + +import { Box, Text, useApp, useInput } from "ink"; +import React, { useEffect, useState } from "react"; +import simpleGit from "simple-git"; + +import { DEFAULT_MODEL } from "../config"; +import type { AzureDevOpsOrg, AzureDevOpsProject, AzureDevOpsRepo } from "../services/azureDevops"; +import { + listOrganizations, + listProjects, + listRepos, + checkReposForInstructions, + createPullRequest +} from "../services/azureDevops"; +import { + buildAuthedUrl, + checkoutBranch, + cloneRepo, + commitAll, + isGitRepo, + pushBranch +} from "../services/git"; +import { generateCopilotInstructions } from "../services/instructions"; +import { ensureDir, validateCachePath } from "../utils/fs"; +import { buildInstructionsPrBody } from "../utils/pr"; + +import { StaticBanner } from "./AnimatedBanner"; + +type Props = { + token: string; + outputPath?: string; +}; + +type Status = + | "loading-orgs" + | "select-orgs" + | "loading-projects" + | "select-projects" + | "loading-repos" + | "select-repos" + | "confirm" + | "processing" + | "complete" + | "error"; + +type ProcessResult = { + repo: string; + success: boolean; + prUrl?: string; + error?: string; +}; + +export function BatchTuiAzure({ token, outputPath }: Props): React.JSX.Element { + const app = useApp(); + const [status, setStatus] = useState("loading-orgs"); + const [message, setMessage] = useState("Fetching organizations..."); + const [errorMessage, setErrorMessage] = useState(""); + + const [orgs, setOrgs] = useState([]); + const [projects, setProjects] = useState([]); + const [repos, setRepos] = useState([]); + const [selectedOrgIndices, setSelectedOrgIndices] = useState>(new Set()); + const [selectedProjectIndices, setSelectedProjectIndices] = useState>(new Set()); + const [selectedRepoIndices, setSelectedRepoIndices] = useState>(new Set()); + const [cursorIndex, setCursorIndex] = useState(0); + + const [results, setResults] = useState([]); + const [_currentRepoIndex, setCurrentRepoIndex] = useState(0); + const [processingMessage, setProcessingMessage] = useState(""); + + useEffect(() => { + loadOrgs(); + }, []); + + async function loadOrgs() { + try { + const userOrgs = await listOrganizations(token); + setOrgs(userOrgs); + setStatus("select-orgs"); + setMessage("Select organizations (space to toggle, enter to confirm)"); + } catch (error) { + setStatus("error"); + setErrorMessage(error instanceof Error ? error.message : "Failed to fetch organizations"); + } + } + + async function loadProjects() { + setStatus("loading-projects"); + setMessage("Fetching projects..."); + + try { + const selectedOrgs = Array.from(selectedOrgIndices).map((i) => orgs[i]); + let allProjects: AzureDevOpsProject[] = []; + + for (let idx = 0; idx < selectedOrgs.length; idx++) { + const org = selectedOrgs[idx]; + setMessage(`Fetching projects from ${org.name} (${idx + 1}/${selectedOrgs.length})...`); + const orgProjects = await listProjects(token, org.name); + allProjects = [...allProjects, ...orgProjects]; + } + + setProjects(allProjects); + setCursorIndex(0); + setSelectedProjectIndices(new Set()); + setStatus("select-projects"); + setMessage("Select projects (space to toggle, enter to confirm)"); + } catch (error) { + setStatus("error"); + setErrorMessage(error instanceof Error ? error.message : "Failed to fetch projects"); + } + } + + async function loadRepos() { + setStatus("loading-repos"); + setMessage("Fetching repositories..."); + + try { + const selectedProjects = Array.from(selectedProjectIndices).map((i) => projects[i]); + let allRepos: AzureDevOpsRepo[] = []; + + for (let idx = 0; idx < selectedProjects.length; idx++) { + const project = selectedProjects[idx]; + setMessage( + `Fetching repos from ${project.organization}/${project.name} (${idx + 1}/${selectedProjects.length})...` + ); + const projectRepos = await listRepos(token, project.organization, project.name); + allRepos = [...allRepos, ...projectRepos]; + } + + const seen = new Set(); + const uniqueRepos = allRepos.filter((repo) => { + const key = `${repo.organization}/${repo.project}/${repo.name}`; + if (seen.has(key)) return false; + seen.add(key); + return true; + }); + + setMessage(`Checking ${uniqueRepos.length} repos for existing instructions...`); + const reposWithStatus = await checkReposForInstructions( + token, + uniqueRepos, + (checked, total) => + setMessage(`Checking for existing instructions (${checked}/${total})...`) + ); + + reposWithStatus.sort((a, b) => { + if (a.hasInstructions === b.hasInstructions) return 0; + return a.hasInstructions ? 1 : -1; + }); + + const withInstructions = reposWithStatus.filter((r) => r.hasInstructions).length; + const withoutInstructions = reposWithStatus.length - withInstructions; + + setRepos(reposWithStatus); + setCursorIndex(0); + setSelectedRepoIndices(new Set()); + setStatus("select-repos"); + setMessage( + `Found ${reposWithStatus.length} repos (${withoutInstructions} need instructions, ${withInstructions} already have them)` + ); + } catch (error) { + setStatus("error"); + setErrorMessage(error instanceof Error ? error.message : "Failed to fetch repositories"); + } + } + + async function processRepos() { + const selectedRepos = Array.from(selectedRepoIndices).map((i) => repos[i]); + setStatus("processing"); + setCurrentRepoIndex(0); + setResults([]); + + const nextResults: ProcessResult[] = []; + + for (let i = 0; i < selectedRepos.length; i++) { + const repo = selectedRepos[i]; + setCurrentRepoIndex(i); + setProcessingMessage( + `[${i + 1}/${selectedRepos.length}] ${repo.organization}/${repo.project}/${repo.name}: Cloning...` + ); + + try { + const cacheRoot = path.join(process.cwd(), ".primer-cache"); + const repoPath = validateCachePath(cacheRoot, repo.organization, repo.project, repo.name); + await ensureDir(repoPath); + + if (!(await isGitRepo(repoPath))) { + const authedUrl = buildAuthedUrl(repo.cloneUrl, token, "azure"); + await cloneRepo(authedUrl, repoPath, { + shallow: true, + timeoutMs: 120000, + onProgress: (stage, progress) => { + setProcessingMessage( + `[${i + 1}/${selectedRepos.length}] ${repo.organization}/${repo.project}/${repo.name}: Cloning (${stage} ${progress}%)...` + ); + } + }); + // Strip credentials from persisted remote URL + const git = simpleGit(repoPath); + await git.remote(["set-url", "origin", repo.cloneUrl]); + } + + setProcessingMessage( + `[${i + 1}/${selectedRepos.length}] ${repo.organization}/${repo.project}/${repo.name}: Creating branch...` + ); + const branch = "primer/add-instructions"; + await checkoutBranch(repoPath, branch); + + setProcessingMessage( + `[${i + 1}/${selectedRepos.length}] ${repo.organization}/${repo.project}/${repo.name}: Generating instructions...` + ); + const timeoutMs = 120000; + const instructionsPromise = generateCopilotInstructions({ + repoPath, + model: DEFAULT_MODEL, + onProgress: (msg) => { + setProcessingMessage( + `[${i + 1}/${selectedRepos.length}] ${repo.organization}/${repo.project}/${repo.name}: ${msg}` + ); + } + }); + + let timer: ReturnType; + const timeoutPromise = new Promise((_, reject) => { + timer = setTimeout( + () => reject(new Error("Generation timed out after 2 minutes")), + timeoutMs + ); + }); + + const instructions = await Promise.race([instructionsPromise, timeoutPromise]).finally(() => + clearTimeout(timer) + ); + // Prevent unhandled rejection if the losing promise rejects later + instructionsPromise.catch(() => {}); + + if (!instructions.trim()) { + throw new Error("Generated instructions were empty"); + } + + const instructionsPath = path.join(repoPath, ".github", "copilot-instructions.md"); + await fs.mkdir(path.dirname(instructionsPath), { recursive: true }); + await fs.writeFile(instructionsPath, instructions, "utf8"); + + setProcessingMessage( + `[${i + 1}/${selectedRepos.length}] ${repo.organization}/${repo.project}/${repo.name}: Committing...` + ); + await commitAll(repoPath, "chore: add copilot instructions via Primer"); + + setProcessingMessage( + `[${i + 1}/${selectedRepos.length}] ${repo.organization}/${repo.project}/${repo.name}: Pushing...` + ); + await pushBranch(repoPath, branch, token, "azure"); + + setProcessingMessage( + `[${i + 1}/${selectedRepos.length}] ${repo.organization}/${repo.project}/${repo.name}: Creating PR...` + ); + const prUrl = await createPullRequest({ + token, + organization: repo.organization, + project: repo.project, + repoId: repo.id, + repoName: repo.name, + title: "🤖 Add Copilot instructions via Primer", + body: buildInstructionsPrBody(), + sourceBranch: branch, + targetBranch: repo.defaultBranch + }); + + nextResults.push({ + repo: `${repo.organization}/${repo.project}/${repo.name}`, + success: true, + prUrl + }); + setResults([...nextResults]); + } catch (error) { + const errorMsg = error instanceof Error ? error.message : "Unknown error"; + nextResults.push({ + repo: `${repo.organization}/${repo.project}/${repo.name}`, + success: false, + error: errorMsg + }); + setResults([...nextResults]); + } + } + + if (outputPath) { + await fs.writeFile(outputPath, JSON.stringify(nextResults, null, 2), "utf8"); + } + + setStatus("complete"); + setMessage("Batch processing complete!"); + } + + useInput((input, key) => { + if (key.escape || input.toLowerCase() === "q") { + app.exit(); + return; + } + + if (status === "select-orgs") { + if (key.upArrow) { + setCursorIndex((prev) => Math.max(0, prev - 1)); + } else if (key.downArrow) { + setCursorIndex((prev) => Math.min(orgs.length - 1, prev + 1)); + } else if (input === " ") { + setSelectedOrgIndices((prev) => { + const next = new Set(prev); + if (next.has(cursorIndex)) { + next.delete(cursorIndex); + } else { + next.add(cursorIndex); + } + return next; + }); + } else if (key.return && selectedOrgIndices.size > 0) { + loadProjects().catch((err) => { + setStatus("error"); + setErrorMessage(err instanceof Error ? err.message : "Failed to load projects"); + }); + } + } + + if (status === "select-projects") { + if (key.upArrow) { + setCursorIndex((prev) => Math.max(0, prev - 1)); + } else if (key.downArrow) { + setCursorIndex((prev) => Math.min(projects.length - 1, prev + 1)); + } else if (input === " ") { + setSelectedProjectIndices((prev) => { + const next = new Set(prev); + if (next.has(cursorIndex)) { + next.delete(cursorIndex); + } else { + next.add(cursorIndex); + } + return next; + }); + } else if (key.return && selectedProjectIndices.size > 0) { + loadRepos().catch((err) => { + setStatus("error"); + setErrorMessage(err instanceof Error ? err.message : "Failed to load repos"); + }); + } + } + + if (status === "select-repos") { + if (key.upArrow) { + setCursorIndex((prev) => Math.max(0, prev - 1)); + } else if (key.downArrow) { + setCursorIndex((prev) => Math.min(repos.length - 1, prev + 1)); + } else if (input === " ") { + setSelectedRepoIndices((prev) => { + const next = new Set(prev); + if (next.has(cursorIndex)) { + next.delete(cursorIndex); + } else { + next.add(cursorIndex); + } + return next; + }); + } else if (input.toLowerCase() === "a") { + const indicesWithoutInstructions = repos + .map((r, i) => ({ r, i })) + .filter(({ r }) => !r.hasInstructions) + .map(({ i }) => i); + setSelectedRepoIndices(new Set(indicesWithoutInstructions)); + } else if (key.return && selectedRepoIndices.size > 0) { + setStatus("confirm"); + setMessage( + `Ready to process ${selectedRepoIndices.size} repositories. Press Y to confirm, N to go back.` + ); + } + } + + if (status === "confirm") { + if (input.toLowerCase() === "y") { + processRepos().catch((err) => { + setStatus("error"); + setErrorMessage(err instanceof Error ? err.message : "Processing failed"); + }); + } else if (input.toLowerCase() === "n") { + setStatus("select-repos"); + setMessage("Select repos (space to toggle, enter to confirm)"); + } + } + }); + + const windowSize = 15; + const getVisibleItems = (items: T[], cursor: number): { items: T[]; startIndex: number } => { + const start = Math.max(0, cursor - Math.floor(windowSize / 2)); + const end = Math.min(items.length, start + windowSize); + const adjustedStart = Math.max(0, end - windowSize); + return { items: items.slice(adjustedStart, end), startIndex: adjustedStart }; + }; + + return ( + + + Batch Processing - Azure DevOps + + {message} + + + {status === "error" && ( + + Error: {errorMessage} + + )} + + {status === "select-orgs" && ( + + {(() => { + const { items: visibleOrgs, startIndex } = getVisibleItems(orgs, cursorIndex); + return visibleOrgs.map((org, i) => { + const realIndex = startIndex + i; + const isSelected = selectedOrgIndices.has(realIndex); + const isCursor = realIndex === cursorIndex; + return ( + + {isCursor ? "❯ " : " "} + {isSelected ? "◉" : "○"} + {org.name} + + ); + }); + })()} + {orgs.length > windowSize && ( + + Showing {Math.min(windowSize, orgs.length)} of {orgs.length} • Use ↑↓ to scroll + + )} + + )} + + {status === "select-projects" && ( + + {(() => { + const { items: visibleProjects, startIndex } = getVisibleItems(projects, cursorIndex); + return visibleProjects.map((project, i) => { + const realIndex = startIndex + i; + const isSelected = selectedProjectIndices.has(realIndex); + const isCursor = realIndex === cursorIndex; + return ( + + {isCursor ? "❯ " : " "} + {isSelected ? "◉" : "○"} + + {project.organization}/{project.name} + + + ); + }); + })()} + {projects.length > windowSize && ( + + Showing {Math.min(windowSize, projects.length)} of {projects.length} • Use ↑↓ to + scroll + + )} + + )} + + {status === "select-repos" && ( + + {(() => { + const { items: visibleRepos, startIndex } = getVisibleItems(repos, cursorIndex); + return visibleRepos.map((repo, i) => { + const realIndex = startIndex + i; + const isSelected = selectedRepoIndices.has(realIndex); + const isCursor = realIndex === cursorIndex; + return ( + + {isCursor ? "❯ " : " "} + {isSelected ? "◉" : "○"} + + {repo.hasInstructions ? "✓" : "✗"}{" "} + + + {repo.organization}/{repo.project}/{repo.name} + + {repo.isPrivate && (private)} + + ); + }); + })()} + {repos.length > windowSize && ( + + Showing {Math.min(windowSize, repos.length)} of {repos.length} • Use ↑↓ to scroll + + )} + + + Selected: {selectedRepoIndices.size} repos + + + + )} + + {status === "processing" && ( + + {processingMessage} + {results.length > 0 && ( + + Completed: + {results.slice(-5).map((r) => ( + + {r.success ? "✓" : "✗"} {r.repo} + {r.success && r.prUrl && → {r.prUrl}} + {!r.success && r.error && ({r.error})} + + ))} + + )} + + )} + + {status === "complete" && ( + + + ✓ Batch complete: {results.filter((r) => r.success).length} succeeded,{" "} + {results.filter((r) => !r.success).length} failed + + + {results.map((r) => ( + + {r.success ? "✓" : "✗"} {r.repo} + {r.success && r.prUrl && → {r.prUrl}} + {!r.success && r.error && ({r.error})} + + ))} + + + )} + + + {status === "select-orgs" && ( + Keys: [Space] Toggle [Enter] Confirm [Q] Quit + )} + {status === "select-projects" && ( + Keys: [Space] Toggle [Enter] Confirm [Q] Quit + )} + {status === "select-repos" && ( + Keys: [Space] Toggle [A] Select Missing [Enter] Confirm [Q] Quit + )} + {status === "confirm" && ( + Keys: [Y] Yes, proceed [N] Go back [Q] Quit + )} + {(status === "complete" || status === "error") && Keys: [Q] Quit} + + + ); +} diff --git a/src/ui/README.md b/src/ui/README.md index a34f7c0..551d04e 100644 --- a/src/ui/README.md +++ b/src/ui/README.md @@ -1 +1,3 @@ Primer TUI components live here. + +Run the TUI with `primer tui`. diff --git a/src/ui/tui.tsx b/src/ui/tui.tsx index cfa8dbb..3542c52 100644 --- a/src/ui/tui.tsx +++ b/src/ui/tui.tsx @@ -1,214 +1,1075 @@ -import React, { useMemo, useState } from "react"; -import { Box, Key, Text, useApp, useInput } from "ink"; import fs from "fs/promises"; import path from "path"; -import { analyzeRepo, RepoAnalysis } from "../services/analyzer"; -import { generateCopilotInstructions } from "../services/instructions"; + +import type { Key } from "ink"; +import { Box, Text, useApp, useInput } from "ink"; +import React, { useEffect, useMemo, useState } from "react"; + +import type { RepoApp } from "../services/analyzer"; +import { analyzeRepo } from "../services/analyzer"; +import { getAzureDevOpsToken } from "../services/azureDevops"; +import { listCopilotModels } from "../services/copilot"; +import { generateEvalScaffold } from "../services/evalScaffold"; +import type { EvalConfig } from "../services/evalScaffold"; import { runEval, type EvalResult } from "../services/evaluator"; +import { getGitHubToken } from "../services/github"; +import { generateCopilotInstructions } from "../services/instructions"; +import { safeWriteFile, buildTimestampedName } from "../utils/fs"; + import { AnimatedBanner, StaticBanner } from "./AnimatedBanner"; import { BatchTui } from "./BatchTui"; -import { getGitHubToken } from "../services/github"; +import { BatchTuiAzure } from "./BatchTuiAzure"; type Props = { repoPath: string; skipAnimation?: boolean; }; -type Status = "intro" | "idle" | "analyzing" | "generating" | "evaluating" | "preview" | "done" | "error" | "batch"; +type Status = + | "intro" + | "idle" + | "generating" + | "bootstrapping" + | "evaluating" + | "modelPicker" + | "preview" + | "done" + | "error" + | "batch-pick" + | "batch-github" + | "batch-azure" + | "eval-pick" + | "model-pick" + | "generate-pick" + | "generate-app-pick" + | "bootstrapEvalCount" + | "bootstrapEvalConfirm"; + +type LogEntry = { + text: string; + type: "info" | "success" | "error" | "progress"; + time: string; +}; + +const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]; + +function useSpinner(active: boolean): string { + const [frame, setFrame] = useState(0); + useEffect(() => { + if (!active) return; + const interval = setInterval(() => { + setFrame((f) => (f + 1) % SPINNER_FRAMES.length); + }, 80); + return () => clearInterval(interval); + }, [active]); + return active ? SPINNER_FRAMES[frame] : ""; +} + +function timestamp(): string { + return new Date().toLocaleTimeString("en-US", { + hour12: false, + hour: "2-digit", + minute: "2-digit", + second: "2-digit" + }); +} + +function KeyHint({ k, label }: { k: string; label: string }): React.JSX.Element { + return ( + + + {"["} + + + {k} + + + {"]"} + + {label} + + ); +} + +function Divider({ label }: { label?: string }): React.JSX.Element { + if (label) { + return ( + + + {"── "} + + + {label} + + + {" ──────────────────────────────────────────"} + + + ); + } + return ( + + + {"────────────────────────────────────────────────────"} + + + ); +} + +const PREFERRED_MODELS = ["claude-sonnet-4.5", "claude-sonnet-4", "gpt-4.1", "gpt-5"]; + +function pickBestModel(available: string[], fallback: string): string { + for (const preferred of PREFERRED_MODELS) { + if (available.includes(preferred)) return preferred; + } + return available[0] || fallback; +} export function PrimerTui({ repoPath, skipAnimation = false }: Props): React.JSX.Element { const app = useApp(); const [status, setStatus] = useState(skipAnimation ? "idle" : "intro"); - const [analysis, setAnalysis] = useState(null); const [message, setMessage] = useState(""); const [generatedContent, setGeneratedContent] = useState(""); const [evalResults, setEvalResults] = useState(null); + const [evalViewerPath, setEvalViewerPath] = useState(null); const [batchToken, setBatchToken] = useState(null); - const repoLabel = useMemo(() => repoPath, [repoPath]); + const [batchAzureToken, setBatchAzureToken] = useState(null); + const [evalCaseCountInput, setEvalCaseCountInput] = useState(""); + const [evalBootstrapCount, setEvalBootstrapCount] = useState(null); + const [availableModels, setAvailableModels] = useState([]); + const [evalModel, setEvalModel] = useState("claude-sonnet-4.5"); + const [judgeModel, setJudgeModel] = useState("claude-sonnet-4.5"); + const [hideModelPicker, setHideModelPicker] = useState(false); + const [modelPickTarget, setModelPickTarget] = useState<"eval" | "judge">("eval"); + const [modelCursor, setModelCursor] = useState(0); + const [hasEvalConfig, setHasEvalConfig] = useState(null); + const [activityLog, setActivityLog] = useState([]); + const [generateTarget, setGenerateTarget] = useState<"copilot-instructions" | "agents-md">( + "copilot-instructions" + ); + const [generateSavePath, setGenerateSavePath] = useState(""); + const [repoApps, setRepoApps] = useState([]); + const [isMonorepo, setIsMonorepo] = useState(false); + const repoLabel = useMemo(() => path.basename(repoPath), [repoPath]); + const repoFull = useMemo(() => repoPath, [repoPath]); + const isLoading = + status === "generating" || status === "bootstrapping" || status === "evaluating"; + const isMenu = + status === "model-pick" || + status === "eval-pick" || + status === "batch-pick" || + status === "generate-pick" || + status === "generate-app-pick"; + const spinner = useSpinner(isLoading); + + const addLog = (text: string, type: LogEntry["type"] = "info") => { + setActivityLog((prev) => [...prev.slice(-4), { text, type, time: timestamp() }]); + }; const handleAnimationComplete = () => { setStatus("idle"); }; - useInput(async (input: string, key: Key) => { - // During intro animation, any key skips it - if (status === "intro") { - setStatus("idle"); - return; - } + // Check for eval config and repo structure on mount + useEffect(() => { + const configPath = path.join(repoPath, "primer.eval.json"); + fs.access(configPath) + .then(() => setHasEvalConfig(true)) + .catch(() => setHasEvalConfig(false)); + analyzeRepo(repoPath) + .then((analysis) => { + const apps = analysis.apps ?? []; + setRepoApps(apps); + setIsMonorepo(analysis.isMonorepo ?? false); + }) + .catch((err) => { + addLog(`Repo analysis failed: ${err instanceof Error ? err.message : "unknown"}`, "error"); + }); + }, [repoPath]); - if (key.escape || input.toLowerCase() === "q") { - app.exit(); - return; - } + const indexForModel = (model: string): number => { + const index = availableModels.indexOf(model); + return index === -1 ? 0 : index; + }; - // In preview mode, handle save/discard - if (status === "preview") { - if (input.toLowerCase() === "s") { - try { - const outputPath = path.join(repoPath, ".github", "copilot-instructions.md"); - await fs.mkdir(path.dirname(outputPath), { recursive: true }); - await fs.writeFile(outputPath, generatedContent, "utf8"); - setStatus("done"); - setMessage("Saved to .github/copilot-instructions.md"); - setGeneratedContent(""); - } catch (error) { - setStatus("error"); - setMessage(error instanceof Error ? error.message : "Failed to save."); - } - return; + useEffect(() => { + let active = true; + listCopilotModels() + .then((models) => { + if (!active) return; + setAvailableModels(models); + if (models.length === 0) return; + setEvalModel((current) => + models.includes(current) ? current : pickBestModel(models, current) + ); + setJudgeModel((current) => + models.includes(current) ? current : pickBestModel(models, current) + ); + }) + .catch(() => { + if (!active) return; + setAvailableModels([]); + }); + return () => { + active = false; + }; + }, []); + + const doGenerate = async ( + targetRepoPath: string, + savePath: string, + target: string + ): Promise => { + setStatus("generating"); + setMessage(`Generating ${target}...`); + addLog(`Generating ${target}...`, "progress"); + try { + const content = await generateCopilotInstructions({ + repoPath: targetRepoPath, + onProgress: (msg) => setMessage(msg) + }); + if (!content.trim()) { + throw new Error("Copilot SDK returned empty content."); } - if (input.toLowerCase() === "d") { - setStatus("idle"); - setMessage("Discarded generated instructions."); - setGeneratedContent(""); - return; + setGeneratedContent(content); + setGenerateSavePath(savePath); + setStatus("preview"); + setMessage("Review the generated content below."); + addLog(`${target} generated — review and save.`, "success"); + } catch (error) { + setStatus("error"); + const msg = error instanceof Error ? error.message : "Generation failed."; + if (msg.toLowerCase().includes("auth") || msg.toLowerCase().includes("login")) { + setMessage(`${msg} Run 'copilot' then '/login' in a separate terminal.`); + } else { + setMessage(msg); } - return; + addLog(msg, "error"); } + }; - if (input.toLowerCase() === "a") { - setStatus("analyzing"); - try { - const result = await analyzeRepo(repoPath); - setAnalysis(result); - setStatus("done"); - setMessage("Analysis complete."); - } catch (error) { - setStatus("error"); - setMessage(error instanceof Error ? error.message : "Analysis failed."); - } - return; + useEffect(() => { + let active = true; + const configPath = path.join(repoPath, "primer.eval.json"); + fs.readFile(configPath, "utf8") + .then((raw) => { + if (!active) return; + const parsed = JSON.parse(raw) as EvalConfig; + const setting = parsed.ui?.modelPicker; + setHideModelPicker(setting === "hidden"); + }) + .catch(() => { + if (!active) return; + setHideModelPicker(false); + }); + return () => { + active = false; + }; + }, [repoPath]); + + const bootstrapEvalConfig = async (count: number, force: boolean): Promise => { + const configPath = path.join(repoPath, "primer.eval.json"); + try { + setStatus("bootstrapping"); + setMessage("Generating eval cases with Copilot SDK..."); + addLog("Generating eval scaffold...", "progress"); + const config = await generateEvalScaffold({ + repoPath, + count, + model: evalModel, + onProgress: (msg) => setMessage(msg) + }); + await safeWriteFile(configPath, JSON.stringify(config, null, 2), force); + setHasEvalConfig(true); + setStatus("idle"); + const msg = `Generated primer.eval.json with ${config.cases.length} cases.`; + setMessage(msg); + addLog(msg, "success"); + } catch (error) { + setStatus("error"); + const msg = error instanceof Error ? error.message : "Failed to generate eval config."; + setMessage(msg); + addLog(msg, "error"); + } finally { + setEvalCaseCountInput(""); + setEvalBootstrapCount(null); } + }; - if (input.toLowerCase() === "g") { - setStatus("generating"); - setMessage("Starting generation..."); + // NOTE: The useInput handler below is intentionally kept as a single callback + // to avoid prop-drilling ~20 state setters. If this grows further, consider + // extracting each status into a sub-component with its own useInput hook. + useInput((input: string, key: Key) => { + void (async () => { try { - const content = await generateCopilotInstructions({ - repoPath, - onProgress: (msg) => setMessage(msg), - }); - if (!content.trim()) { - throw new Error("Copilot SDK returned empty instructions."); - } - setGeneratedContent(content); - setStatus("preview"); - setMessage("Review the generated instructions below."); - } catch (error) { - setStatus("error"); - const message = error instanceof Error ? error.message : "Generation failed."; - if (message.toLowerCase().includes("auth") || message.toLowerCase().includes("login")) { - setMessage(`${message} Run 'copilot' then '/login' in a separate terminal.`); - } else { - setMessage(message); + if (status === "intro") { + setStatus("idle"); + return; } - } - } - if (input.toLowerCase() === "b") { - setStatus("analyzing"); - setMessage("Checking GitHub authentication..."); - const token = await getGitHubToken(); - if (!token) { - setStatus("error"); - setMessage("GitHub auth required. Run 'gh auth login' or set GITHUB_TOKEN."); - return; - } - setBatchToken(token); - setStatus("batch"); - return; - } + if (status === "modelPicker") { + if (key.escape) { + setStatus("idle"); + setMessage("Model picker cancelled."); + return; + } - if (input.toLowerCase() === "e") { - const configPath = path.join(repoPath, "primer.eval.json"); - try { - await fs.access(configPath); - } catch { - setStatus("error"); - setMessage("No primer.eval.json found. Run 'primer eval --init' to create one."); - return; - } - - setStatus("evaluating"); - setMessage("Running evals... (this may take a few minutes)"); - setEvalResults(null); - try { - const { results } = await runEval({ - configPath, - repoPath, - model: "gpt-4.1", - judgeModel: "gpt-4.1", - // Note: onProgress removed - causes issues with SDK in React/Ink context - }); - setEvalResults(results); - const passed = results.filter(r => r.verdict === "pass").length; - const failed = results.filter(r => r.verdict === "fail").length; - setStatus("done"); - setMessage(`Eval complete: ${passed} pass, ${failed} fail out of ${results.length} cases.`); - } catch (error) { + if (key.upArrow) { + setModelCursor((prev: number) => { + if (!availableModels.length) return 0; + return (prev - 1 + availableModels.length) % availableModels.length; + }); + return; + } + + if (key.downArrow) { + setModelCursor((prev: number) => { + if (!availableModels.length) return 0; + return (prev + 1) % availableModels.length; + }); + return; + } + + if (key.return) { + const selected = availableModels[modelCursor]; + if (!selected) return; + if (modelPickTarget === "eval") { + setEvalModel(selected); + setModelPickTarget("judge"); + setModelCursor(indexForModel(judgeModel)); + setMessage(`Eval model set: ${selected}. Select judge model.`); + return; + } + setJudgeModel(selected); + setStatus("idle"); + setMessage(`Models set: eval ${evalModel} • judge ${selected}.`); + } + return; + } + + if (key.escape || input.toLowerCase() === "q") { + app.exit(); + return; + } + + if (status === "preview") { + if (input.toLowerCase() === "s") { + try { + const outputPath = + generateSavePath || path.join(repoPath, ".github", "copilot-instructions.md"); + await fs.mkdir(path.dirname(outputPath), { recursive: true }); + await fs.writeFile(outputPath, generatedContent, "utf8"); + setStatus("done"); + const relPath = path.relative(repoPath, outputPath); + const msg = `Saved to ${relPath}`; + setMessage(msg); + addLog(msg, "success"); + setGeneratedContent(""); + } catch (error) { + setStatus("error"); + const msg = error instanceof Error ? error.message : "Failed to save."; + setMessage(msg); + addLog(msg, "error"); + } + return; + } + if (input.toLowerCase() === "d") { + setStatus("idle"); + setMessage("Discarded generated instructions."); + addLog("Discarded instructions.", "info"); + setGeneratedContent(""); + return; + } + return; + } + + if (status === "bootstrapEvalCount") { + if (key.return) { + const trimmed = evalCaseCountInput.trim(); + const count = Number.parseInt(trimmed, 10); + if (!trimmed || !Number.isFinite(count) || count <= 0) { + setMessage("Enter a positive number of eval cases, then press Enter."); + return; + } + + const configPath = path.join(repoPath, "primer.eval.json"); + setEvalBootstrapCount(count); + try { + await fs.access(configPath); + setStatus("bootstrapEvalConfirm"); + setMessage("primer.eval.json exists. Overwrite? (Y/N)"); + } catch { + await bootstrapEvalConfig(count, false); + } + return; + } + + if (key.backspace || key.delete) { + setEvalCaseCountInput((prev) => prev.slice(0, -1)); + return; + } + + if (/^\d$/.test(input)) { + setEvalCaseCountInput((prev) => prev + input); + return; + } + + return; + } + + if (status === "bootstrapEvalConfirm") { + if (input.toLowerCase() === "y") { + const count = evalBootstrapCount ?? 0; + if (count <= 0) { + setStatus("error"); + setMessage("Missing eval case count. Restart bootstrap."); + return; + } + await bootstrapEvalConfig(count, true); + return; + } + + if (input.toLowerCase() === "n") { + setStatus("idle"); + setMessage("Bootstrap cancelled."); + setEvalCaseCountInput(""); + setEvalBootstrapCount(null); + } + return; + } + + if (status === "generate-pick") { + if (input.toLowerCase() === "c") { + setGenerateTarget("copilot-instructions"); + if (isMonorepo && repoApps.length > 1) { + setStatus("generate-app-pick"); + setMessage("Generate for root or per-app?"); + } else { + const savePath = path.join(repoPath, ".github", "copilot-instructions.md"); + setGenerateSavePath(savePath); + await doGenerate(repoPath, savePath, "copilot-instructions"); + } + return; + } + if (input.toLowerCase() === "a") { + setGenerateTarget("agents-md"); + if (isMonorepo && repoApps.length > 1) { + setStatus("generate-app-pick"); + setMessage("Generate for root or per-app?"); + } else { + const savePath = path.join(repoPath, "AGENTS.md"); + setGenerateSavePath(savePath); + await doGenerate(repoPath, savePath, "agents-md"); + } + return; + } + if (key.escape) { + setStatus("idle"); + setMessage(""); + return; + } + return; + } + + if (status === "generate-app-pick") { + if (input.toLowerCase() === "r") { + // Root only + const savePath = + generateTarget === "copilot-instructions" + ? path.join(repoPath, ".github", "copilot-instructions.md") + : path.join(repoPath, "AGENTS.md"); + setGenerateSavePath(savePath); + await doGenerate(repoPath, savePath, generateTarget); + return; + } + if (input.toLowerCase() === "a") { + // All apps sequentially + setStatus("generating"); + addLog(`Generating ${generateTarget} for ${repoApps.length} apps...`, "progress"); + let count = 0; + for (const app of repoApps) { + const savePath = + generateTarget === "copilot-instructions" + ? path.join(app.path, ".github", "copilot-instructions.md") + : path.join(app.path, "AGENTS.md"); + setMessage(`Generating for ${app.name} (${count + 1}/${repoApps.length})...`); + try { + const content = await generateCopilotInstructions({ + repoPath: app.path, + onProgress: (msg) => setMessage(`${app.name}: ${msg}`) + }); + if (content.trim()) { + await fs.mkdir(path.dirname(savePath), { recursive: true }); + await fs.writeFile(savePath, content, "utf8"); + count++; + addLog(`${app.name}: saved ${path.basename(savePath)}`, "success"); + } + } catch (error) { + const msg = error instanceof Error ? error.message : "Failed."; + addLog(`${app.name}: ${msg}`, "error"); + } + } + setStatus("done"); + setMessage(`Generated ${generateTarget} for ${count}/${repoApps.length} apps.`); + return; + } + // Number to pick a specific app + const num = Number.parseInt(input, 10); + if (Number.isFinite(num) && num >= 1 && num <= repoApps.length) { + const app = repoApps[num - 1]; + const savePath = + generateTarget === "copilot-instructions" + ? path.join(app.path, ".github", "copilot-instructions.md") + : path.join(app.path, "AGENTS.md"); + setGenerateSavePath(savePath); + await doGenerate(app.path, savePath, generateTarget); + return; + } + if (key.escape) { + setStatus("generate-pick"); + setMessage("Select what to generate."); + return; + } + return; + } + + if (status === "model-pick") { + if (key.escape) { + setStatus("idle"); + setMessage(""); + return; + } + if (key.upArrow) { + setModelCursor((prev) => Math.max(0, prev - 1)); + return; + } + if (key.downArrow) { + setModelCursor((prev) => Math.min(availableModels.length - 1, prev + 1)); + return; + } + if (key.return) { + const chosen = availableModels[modelCursor]; + if (chosen) { + if (modelPickTarget === "eval") { + setEvalModel(chosen); + addLog(`Eval model → ${chosen}`, "success"); + } else { + setJudgeModel(chosen); + addLog(`Judge model → ${chosen}`, "success"); + } + setStatus("idle"); + setMessage(`${modelPickTarget === "eval" ? "Eval" : "Judge"} model set to ${chosen}`); + } + return; + } + return; + } + + if (status === "eval-pick") { + if (input.toLowerCase() === "r") { + // Run eval + const configPath = path.join(repoPath, "primer.eval.json"); + const outputPath = path.join( + repoPath, + ".primer", + "evals", + buildTimestampedName("eval-results") + ); + try { + await fs.access(configPath); + } catch { + setStatus("error"); + const msg = "No primer.eval.json found. Press [E] then [I] to create one."; + setMessage(msg); + addLog(msg, "error"); + return; + } + + setStatus("evaluating"); + setMessage("Running evals... (this may take a few minutes)"); + addLog("Running evals...", "progress"); + setEvalResults(null); + setEvalViewerPath(null); + try { + const { results, viewerPath } = await runEval({ + configPath, + repoPath, + model: evalModel, + judgeModel: judgeModel, + outputPath + }); + setEvalResults(results); + setEvalViewerPath(viewerPath ?? null); + const passed = results.filter((r) => r.verdict === "pass").length; + const failed = results.filter((r) => r.verdict === "fail").length; + setStatus("done"); + const msg = `Eval complete: ${passed} pass, ${failed} fail out of ${results.length} cases.`; + setMessage(msg); + addLog(msg, "success"); + } catch (error) { + setStatus("error"); + const msg = error instanceof Error ? error.message : "Eval failed."; + setMessage(msg); + addLog(msg, "error"); + } + return; + } + if (input.toLowerCase() === "i") { + setStatus("bootstrapEvalCount"); + setMessage("Enter number of eval cases, then press Enter."); + setEvalCaseCountInput(""); + setEvalBootstrapCount(null); + return; + } + if (key.escape || input.toLowerCase() === "b") { + setStatus("idle"); + setMessage(""); + return; + } + return; + } + + if (status === "batch-pick") { + if (input.toLowerCase() === "g") { + setStatus("generating"); + setMessage("Checking GitHub authentication..."); + addLog("Starting batch (GitHub)...", "progress"); + const token = await getGitHubToken(); + if (!token) { + setStatus("error"); + const msg = "GitHub auth required. Run 'gh auth login' or set GITHUB_TOKEN."; + setMessage(msg); + addLog(msg, "error"); + return; + } + setBatchToken(token); + setStatus("batch-github"); + return; + } + if (input.toLowerCase() === "a") { + setStatus("generating"); + setMessage("Checking Azure DevOps authentication..."); + addLog("Starting batch (Azure DevOps)...", "progress"); + const token = getAzureDevOpsToken(); + if (!token) { + setStatus("error"); + const msg = "Azure DevOps PAT required. Set AZURE_DEVOPS_PAT or AZDO_PAT."; + setMessage(msg); + addLog(msg, "error"); + return; + } + setBatchAzureToken(token); + setStatus("batch-azure"); + return; + } + if (key.escape || input.toLowerCase() === "b") { + setStatus("idle"); + setMessage(""); + return; + } + return; + } + + if (input.toLowerCase() === "g") { + setStatus("generate-pick"); + setMessage("Select what to generate."); + return; + } + + if (input.toLowerCase() === "b") { + setStatus("batch-pick"); + setMessage("Select batch provider."); + return; + } + + if (input.toLowerCase() === "e") { + setStatus("eval-pick"); + setMessage("Select eval action."); + return; + } + + if (input.toLowerCase() === "m") { + if (hideModelPicker) { + setMessage('Model picker hidden. Set ui.modelPicker to "visible" in primer.eval.json.'); + return; + } + setModelPickTarget("eval"); + setStatus("model-pick"); + setMessage("Pick eval model."); + const idx = availableModels.indexOf(evalModel); + setModelCursor(idx >= 0 ? idx : 0); + return; + } + + if (input.toLowerCase() === "j") { + if (hideModelPicker) { + setMessage('Model picker hidden. Set ui.modelPicker to "visible" in primer.eval.json.'); + return; + } + setModelPickTarget("judge"); + setStatus("model-pick"); + setMessage("Pick judge model."); + const idx = availableModels.indexOf(judgeModel); + setModelCursor(idx >= 0 ? idx : 0); + return; + } + } catch (err) { setStatus("error"); - setMessage(error instanceof Error ? error.message : "Eval failed."); + setMessage(err instanceof Error ? err.message : "Unexpected error"); } - } + })(); }); - const statusLabel = status === "intro" ? "..." : status === "idle" ? "ready (awaiting input)" : status; + const statusIcon = status === "error" ? "✗" : status === "done" ? "✓" : isLoading ? spinner : "●"; + const statusLabel = + status === "intro" + ? "starting" + : status === "idle" + ? "ready" + : status === "bootstrapEvalCount" + ? "input" + : status === "bootstrapEvalConfirm" + ? "confirm" + : status === "eval-pick" + ? "eval" + : status === "batch-pick" + ? "batch" + : status === "model-pick" + ? "models" + : status; + const statusColor = + status === "error" + ? "red" + : status === "done" + ? "green" + : isLoading + ? "yellow" + : isMenu + ? "magentaBright" + : "cyanBright"; + + const formatTokens = (result: EvalResult): string => { + const withUsage = result.metrics?.withInstructions?.tokenUsage; + const withoutUsage = result.metrics?.withoutInstructions?.tokenUsage; + const withTotal = + withUsage?.totalTokens ?? + (withUsage ? (withUsage.promptTokens ?? 0) + (withUsage.completionTokens ?? 0) : undefined); + const withoutTotal = + withoutUsage?.totalTokens ?? + (withoutUsage + ? (withoutUsage.promptTokens ?? 0) + (withoutUsage.completionTokens ?? 0) + : undefined); + if (withTotal == null && withoutTotal == null) return "tokens n/a"; + return `tokens w/: ${withTotal ?? "n/a"} • w/o: ${withoutTotal ?? "n/a"}`; + }; - // Truncate preview to fit terminal const previewLines = generatedContent.split("\n").slice(0, 20); - const truncatedPreview = previewLines.join("\n") + (generatedContent.split("\n").length > 20 ? "\n..." : ""); + const truncatedPreview = + previewLines.join("\n") + (generatedContent.split("\n").length > 20 ? "\n..." : ""); - // Render BatchTui when in batch mode - if (status === "batch" && batchToken) { + if (status === "batch-github" && batchToken) { return ; } + if (status === "batch-azure" && batchAzureToken) { + return ; + } + return ( - + {status === "intro" ? ( ) : ( )} - Prime your repo for AI. - Repo: {repoLabel} - - Status: {statusLabel} - {analysis && ( - - Languages: {analysis.languages.join(", ") || "unknown"} - Frameworks: {analysis.frameworks.join(", ") || "none"} - Package manager: {analysis.packageManager ?? "unknown"} - - )} + + {/* Status Bar */} + + + Prime your repo for AI + + + {statusIcon} {statusLabel} + - - {message} + + {/* Context */} + + + + Repo + + {repoLabel} + + {isMonorepo && monorepo · {repoApps.length} apps} + + {" "} + {repoFull} + + + + Model + {evalModel} + • Judge + {judgeModel} + {availableModels.length > 0 && ( + + {" "} + ({availableModels.length} available) + + )} + + + Eval + {hasEvalConfig === null ? ( + + checking... + + ) : hasEvalConfig ? ( + primer.eval.json found + ) : ( + no eval config — press [I] to create + )} + + + {/* Activity */} + + + {activityLog.length === 0 && !message ? ( + + Awaiting input. + + ) : ( + <> + {activityLog.slice(-3).map((entry, i) => ( + + + {entry.time}{" "} + + + {entry.text} + + + ))} + {message && !activityLog.some((e) => e.text === message) && ( + + + {isLoading ? `${spinner} ` : ""} + {message} + + + )} + + )} + + + {/* Model Picker */} + {status === "model-pick" && availableModels.length > 0 && ( + <> + + + {availableModels.map((model, i) => { + const current = modelPickTarget === "eval" ? evalModel : judgeModel; + const isCurrent = model === current; + const isCursor = i === modelCursor; + return ( + + {isCursor ? "\u276F " : " "} + + {model} + + {isCurrent && ( + + {" "} + (current) + + )} + + ); + })} + {availableModels.length > 15 && ( + + Use {"\u2191\u2193"} to scroll + + )} + + + )} + + {/* App picker for monorepo generate */} + {status === "generate-app-pick" && repoApps.length > 0 && ( + <> + + + {repoApps.map((app, i) => ( + + + {i + 1} + + + {app.name} + + {" "} + {path.relative(repoPath, app.path)} + + + ))} + + + )} + + {/* Input: eval case count */} + {status === "bootstrapEvalCount" && ( + + Eval case count: + + {evalCaseCountInput || "▍"} + + + )} + + {/* Preview */} {status === "preview" && generatedContent && ( - - Preview (.github/copilot-instructions.md): + + + Preview ({path.relative(repoPath, generateSavePath) || generateTarget}) + {truncatedPreview} )} + + {/* Eval Results */} {evalResults && evalResults.length > 0 && ( - - Eval Results: - {evalResults.map((r) => ( - - {r.verdict === "pass" ? "✓" : r.verdict === "fail" ? "✗" : "?"} {r.id}: {r.verdict} (score: {r.score}) - - ))} - + <> + + + {evalResults.map((r) => ( + + + {r.verdict === "pass" ? "✓" : r.verdict === "fail" ? "✗" : "?"}{" "} + + {r.id} + + {" "} + score:{r.score} • {formatTokens(r)} + + + ))} + {evalViewerPath && ( + + Viewer: {evalViewerPath} + + )} + + )} - + + {/* Commands */} + + {status === "intro" ? ( Press any key to skip animation... ) : status === "preview" ? ( - Keys: [S] Save [D] Discard [Q] Quit + + + + + + ) : status === "bootstrapEvalConfirm" ? ( + + + + + + ) : status === "model-pick" ? ( + + Use + + {"\u2191\u2193"} + + to navigate, + + Enter + + to select + + + ) : status === "generate-pick" ? ( + + + + + + ) : status === "generate-app-pick" ? ( + + + + + {" "} + or press{" "} + + + 1 + + + - + + + {repoApps.length} + + + {" "} + + + + ) : status === "eval-pick" ? ( + + + + + + ) : status === "batch-pick" ? ( + + + + + ) : ( - Keys: [A] Analyze [G] Generate [E] Eval [B] Batch [Q] Quit + + + + + + + + + + + + )} diff --git a/src/utils/cwd.ts b/src/utils/cwd.ts new file mode 100644 index 0000000..8a7c4c4 --- /dev/null +++ b/src/utils/cwd.ts @@ -0,0 +1,31 @@ +/** + * Serialized access to process.chdir(). Only one withCwd block + * runs at a time, preventing concurrent directory corruption. + * + * WARNING: process.chdir() is a process-global side effect. This lock + * only serializes concurrent `withCwd` callers. Any other code that + * resolves relative paths during execution will observe the changed + * working directory. Use only for Copilot SDK calls that require + * process.cwd() to be set. Prefer passing `cwd` options to libraries + * or child processes when available. + */ +let lock: Promise = Promise.resolve(); + +export function withCwd(dir: string, fn: () => Promise): Promise { + const previous = lock; + let release: () => void; + lock = new Promise((resolve) => { + release = resolve; + }); + + return previous.then(async () => { + const original = process.cwd(); + process.chdir(dir); + try { + return await fn(); + } finally { + process.chdir(original); + release(); + } + }); +} diff --git a/src/utils/fs.ts b/src/utils/fs.ts index 0c4a1f6..986bc4d 100644 --- a/src/utils/fs.ts +++ b/src/utils/fs.ts @@ -5,17 +5,44 @@ export async function ensureDir(dirPath: string): Promise { await fs.mkdir(dirPath, { recursive: true }); } -export async function safeWriteFile(filePath: string, content: string, force: boolean): Promise { - const exists = await fileExists(filePath); - if (exists && !force) { - return `Skipped ${path.relative(process.cwd(), filePath)} (exists)`; +export async function safeWriteFile( + filePath: string, + content: string, + force: boolean +): Promise { + const resolved = path.resolve(filePath); + + // Reject symlinks to prevent writing through them to unintended locations + try { + const stat = await fs.lstat(resolved); + if (stat.isSymbolicLink()) { + return `Skipped ${path.relative(process.cwd(), filePath)} (symlink)`; + } + if (!force) { + return `Skipped ${path.relative(process.cwd(), filePath)} (exists)`; + } + } catch { + // File does not exist — safe to create } - await fs.writeFile(filePath, content, "utf8"); + await fs.writeFile(resolved, content, "utf8"); return `Wrote ${path.relative(process.cwd(), filePath)}`; } -async function fileExists(filePath: string): Promise { +/** + * Validate that a constructed path stays within the expected root directory. + * Prevents path traversal via malicious repo names or owner slugs. + */ +export function validateCachePath(cacheRoot: string, ...segments: string[]): string { + const resolvedRoot = path.resolve(cacheRoot); + const resolved = path.resolve(cacheRoot, ...segments); + if (!resolved.startsWith(resolvedRoot + path.sep) && resolved !== resolvedRoot) { + throw new Error(`Invalid path: escapes cache directory (${resolved})`); + } + return resolved; +} + +export async function fileExists(filePath: string): Promise { try { await fs.access(filePath); return true; @@ -23,3 +50,25 @@ async function fileExists(filePath: string): Promise { return false; } } + +export async function safeReadDir(dirPath: string): Promise { + try { + return await fs.readdir(dirPath); + } catch { + return []; + } +} + +export async function readJson(filePath: string): Promise | undefined> { + try { + const raw = await fs.readFile(filePath, "utf8"); + return JSON.parse(raw) as Record; + } catch { + return undefined; + } +} + +export function buildTimestampedName(baseName: string, extension = ".json"): string { + const stamp = new Date().toISOString().replace(/[:.]/gu, "-"); + return `${baseName}-${stamp}${extension}`; +} diff --git a/src/utils/logger.ts b/src/utils/logger.ts index 28f69a4..01225f0 100644 --- a/src/utils/logger.ts +++ b/src/utils/logger.ts @@ -1,5 +1,6 @@ import chalk from "chalk"; -import { RepoAnalysis } from "../services/analyzer"; + +import type { RepoAnalysis } from "../services/analyzer"; export function prettyPrintSummary(analysis: RepoAnalysis): void { console.log(chalk.bold("Repository analysis")); diff --git a/src/utils/pr.ts b/src/utils/pr.ts new file mode 100644 index 0000000..b147d1e --- /dev/null +++ b/src/utils/pr.ts @@ -0,0 +1,49 @@ +export function buildConfigsPrBody(): string { + return [ + "## 🤖 Primed for AI", + "", + "This PR adds configurations to prime this repository for AI coding assistants.", + "", + "### Added Files", + "", + "| File | Purpose |", + "|------|---------|", + "| `.vscode/settings.json` | VS Code settings for optimal AI assistance |", + "| `.vscode/mcp.json` | Model Context Protocol server configuration |", + "", + "### How to Use", + "", + "1. Merge this PR", + "2. Open the project in VS Code", + "3. Start chatting with Copilot — it now understands your project!", + "", + "---", + "*Generated by Primer*" + ].join("\n"); +} + +export function buildInstructionsPrBody(): string { + return [ + "## 🤖 Copilot Instructions Added", + "", + "This PR adds a `.github/copilot-instructions.md` file to help GitHub Copilot understand this codebase better.", + "", + "### What's Included", + "", + "The instructions file contains:", + "- Project overview and architecture", + "- Tech stack and conventions", + "- Build/test commands", + "- Key directories and files", + "", + "### Benefits", + "", + "With these instructions, Copilot will:", + "- Generate more contextually-aware code suggestions", + "- Follow project-specific patterns and conventions", + "- Understand the codebase structure", + "", + "---", + "*Generated by [Primer](https://github.com/pierceboggan/primer) - Prime your repos for AI*" + ].join("\n"); +} diff --git a/tsup.config.ts b/tsup.config.ts new file mode 100644 index 0000000..9937cc3 --- /dev/null +++ b/tsup.config.ts @@ -0,0 +1,20 @@ +import { defineConfig } from "tsup"; + +export default defineConfig({ + entry: ["src/index.ts"], + format: ["esm"], + target: "node20", + outDir: "dist", + clean: true, + splitting: false, + sourcemap: true, + dts: false, + banner: { + js: "#!/usr/bin/env node" + }, + // Keep node_modules as external — they'll be installed via npm + external: [/^[^./]/], + esbuildOptions(options) { + options.jsx = "automatic"; + } +}); diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 0000000..2481bee --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + environment: "node", + coverage: { + provider: "v8", + reporter: ["text", "html", "json-summary"], + reportsDirectory: "./coverage" + } + } +});