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..c4be7e6 --- /dev/null +++ b/.github/agents/code-review-codex.agent.md @@ -0,0 +1,163 @@ +--- +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..a903bc5 --- /dev/null +++ b/.github/agents/code-review-gemini.agent.md @@ -0,0 +1,163 @@ +--- +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..264def9 --- /dev/null +++ b/.github/agents/code-review-opus.agent.md @@ -0,0 +1,163 @@ +--- +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..2bdc7ef 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1,4 +1,13 @@ -# 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 diff --git a/.github/prompts/deslop.prompt.md b/.github/prompts/deslop.prompt.md new file mode 100644 index 0000000..3c7d492 --- /dev/null +++ b/.github/prompts/deslop.prompt.md @@ -0,0 +1,13 @@ +--- +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. \ No newline at end of file diff --git a/.github/prompts/generate-improvements.prompt.md b/.github/prompts/generate-improvements.prompt.md new file mode 100644 index 0000000..774c0e3 --- /dev/null +++ b/.github/prompts/generate-improvements.prompt.md @@ -0,0 +1,128 @@ +--- +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. \ No newline at end of file diff --git a/.github/prompts/review.prompt.md b/.github/prompts/review.prompt.md new file mode 100644 index 0000000..54a8d01 --- /dev/null +++ b/.github/prompts/review.prompt.md @@ -0,0 +1,10 @@ +--- +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 \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..d336a10 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,39 @@ +name: CI + +on: + push: + branches: [main, vnext] + pull_request: + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + - name: Install + run: npm ci + - name: Lint + run: npm run lint + - name: Typecheck + run: npm run typecheck + - name: Test + run: npm run test + - name: Build + run: npm run build + - name: Verify CLI + run: | + chmod +x dist/index.js + node dist/index.js --version + - name: Upload coverage + if: always() + uses: actions/upload-artifact@v4 + with: + name: coverage + path: coverage + if-no-files-found: ignore 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..e91d5ff --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,12 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +## [Unreleased] + +- Readiness report with monorepo support and fix-first checklist. +- CI workflow with lint, typecheck, tests, and build steps. +- Release automation via release-please. +- ESLint, Prettier, and Vitest tooling plus starter tests. +- Added contributing, security, and issue/PR templates. +- Added examples folder with sample configs. 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..7e7cf79 100644 --- a/PLAN.md +++ b/PLAN.md @@ -12,11 +12,10 @@ 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** @@ -96,8 +95,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 +106,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 ``` --- @@ -370,23 +381,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 @@ -476,24 +496,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 ``` --- diff --git a/README.md b/README.md index 7c5dd6c..0e8d17a 100644 --- a/README.md +++ b/README.md @@ -2,26 +2,29 @@ > 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. ## 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` +1. **Node.js 20+** +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` +5. **Azure DevOps PAT (optional)** — For Azure DevOps workflows: set `AZURE_DEVOPS_PAT` ## Installation @@ -30,194 +33,162 @@ Primer is a CLI tool that analyzes your codebase and generates `.github/copilot- git clone https://github.com/pierceboggan/primer.git cd primer npm install +npm run build +npm link ``` -## Usage +Then use `primer` anywhere: + +```bash +primer --help +``` -### Quick Start (Init) +## Usage -The easiest way to get started is with the `init` command: +### Quick Start ```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 +primer init -# Work with a GitHub repository -npx tsx src/index.ts init --github +# Acceptp defaults and generate everything +primer init --yes ``` -### Interactive Mode (TUI) +### Interactive TUI ```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 - -# Skip the animated intro -npx tsx src/index.ts tui --no-animation +primer tui +primer tui --repo /path/to/repo ``` -**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) +**Main menu:** +- `[G]` Generate → choose Copilot instructions or AGENTS.md (with per-app support for monorepos) +- `[E]` Eval → run evals or init eval config +- `[B]` Batch → pick GitHub or Azure DevOps +- `[M]` / `[J]` → pick eval/judge model from available models (arrow keys + Enter) - `[Q]` Quit ### Generate Instructions ```bash -# Generate instructions for current directory -npx tsx src/index.ts instructions +# Generate copilot-instructions.md +primer generate instructions -# Generate for specific repo with custom output -npx tsx src/index.ts instructions --repo /path/to/repo --output ./instructions.md +# Generate AGENTS.md +primer generate agents -# Use a specific model -npx tsx src/index.ts instructions --model gpt-5 -``` +# Generate per-app in monorepos +primer generate instructions --per-app -### Batch Processing +# Generate MCP or VS Code configs +primer generate mcp +primer generate vscode --force +``` -Process multiple repositories across organizations: +Or use the standalone command: ```bash -# Launch batch TUI -npx tsx src/index.ts batch - -# Save results to file -npx tsx src/index.ts batch --output results.json +primer instructions --repo /path/to/repo --model claude-sonnet-4.5 ``` -**Batch TUI Keys:** -- `[Space]` Toggle selection -- `[A]` Select all repos -- `[Enter]` Confirm selection -- `[Y/N]` Confirm/cancel processing -- `[Q]` Quit +### Readiness Report -### Analyze Repository +Assess AI readiness across 9 pillars with a maturity model: ```bash -# Analyze current directory -npx tsx src/index.ts analyze +# Terminal output +primer readiness -# Analyze specific path with JSON output -npx tsx src/index.ts analyze /path/to/repo --json -``` +# Visual HTML report (GitHub-themed, light/dark toggle) +primer readiness --visual -### Generate Configs +# JSON output +primer readiness --json -Generate configuration files for your repo: +# Save to file +primer readiness --output report.html +``` -```bash -# Generate MCP config -npx tsx src/index.ts generate mcp +**Maturity levels:** +| 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 | -# Generate VS Code settings -npx tsx src/index.ts generate vscode --force +**AI Tooling checks:** +- Custom instructions (`copilot-instructions.md`, `CLAUDE.md`, `AGENTS.md`, `.cursorrules`) +- MCP configuration (`.vscode/mcp.json`, settings) +- Custom AI agents (`.github/agents/`, `.copilot/agents/`) +- Copilot/Claude skills (`.copilot/skills/`, `.github/skills/`) -# Generate custom prompts -npx tsx src/index.ts generate prompts +#### Batch Readiness -# Generate agent configs -npx tsx src/index.ts generate agents +Consolidated visual report across multiple repositories: -# Generate .aiignore file -npx tsx src/index.ts generate aiignore +```bash +primer batch-readiness +primer batch-readiness --output team-readiness.html ``` -### Manage Templates +### Batch Processing -View available instruction templates: +Process multiple repos across organizations: ```bash -npx tsx src/index.ts templates +# GitHub +primer batch + +# Azure DevOps +primer batch --provider azure ``` -### Configuration +### Evaluation Framework -View and manage Primer configuration: +Measure instruction effectiveness: ```bash -npx tsx src/index.ts config -``` - -### Update +# Create eval config +primer eval --init -Check for and apply updates: +# Run evaluation (defaults to claude-sonnet-4.5) +primer eval primer.eval.json --repo /path/to/repo -```bash -npx tsx src/index.ts update +# Custom models +primer eval --model claude-sonnet-4.5 --judge-model claude-sonnet-4.5 ``` ### Create Pull Requests -Automatically create a PR to add Primer configs to a repository: - ```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 - -Test how well your instructions improve AI responses: +## Development ```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 -``` - -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." - } - ] -} -``` +# Type check +npm run typecheck -## How It Works +# Lint +npm run lint -1. **Analysis** - Scans the repository for: - - Language files (`.ts`, `.js`, `.py`, `.go`, etc.) - - Framework indicators (`package.json`, `tsconfig.json`, etc.) - - Package manager lock files +# Test (51 tests) +npm run test -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 +# Test with coverage +npm run test:coverage -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 +# Build +npm run build -4. **Evaluation** - Measure instruction quality: - - Run prompts with and without instructions - - Use a judge model to score responses - - Generate comparison reports +# Run from source +npx tsx src/index.ts --help +``` ## Project Structure @@ -227,45 +198,37 @@ primer/ │ ├── 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 +│ │ ├── batch.tsx # Batch processing (GitHub) +│ │ ├── batchReadiness.tsx # Batch readiness reports +│ │ ├── eval.ts # Evaluation framework +│ │ ├── generate.ts # Generate instructions/configs +│ │ ├── init.ts # Interactive setup +│ │ ├── instructions.tsx # Instructions generation +│ │ ├── pr.ts # PR creation +│ │ ├── readiness.ts # Readiness command +│ │ └── tui.tsx # TUI launcher +│ ├── services/ # Core 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 +│ │ ├── readiness.ts # Readiness scoring engine +│ │ ├── visualReport.ts # HTML report generator +│ │ └── __tests__/ # Test suite +│ ├── ui/ # Terminal UI (Ink/React) │ │ ├── AnimatedBanner.tsx -│ │ ├── BatchTui.tsx # Batch processing UI -│ │ └── tui.tsx # Main TUI -│ └── utils/ # Helpers +│ │ ├── BatchTui.tsx +│ │ ├── BatchTuiAzure.tsx +│ │ ├── BatchReadinessTui.tsx +│ │ └── tui.tsx +│ └── utils/ │ ├── 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 +├── tsup.config.ts # Bundler config +├── vitest.config.ts # Test config +└── primer.eval.json # Example eval config ``` ## Troubleshooting @@ -276,16 +239,10 @@ 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) +### "GitHub authentication required" Install GitHub CLI and authenticate: `brew install gh && gh auth login` - Or set a token: `export GITHUB_TOKEN=` -### Generation hangs or times out -- Ensure you're authenticated with the Copilot CLI -- Check your network connection -- Try a smaller repository first - ## License MIT 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..309d504 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,45 @@ +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"; + +const sourceGlobs = ["**/*.{ts,tsx,js,jsx}"]; + +export default [ + { + ignores: ["dist/**", "node_modules/**", "coverage/**"] + }, + js.configs.recommended, + { + files: sourceGlobs, + languageOptions: { + parser: tsParser, + parserOptions: { + project: "./tsconfig.json", + sourceType: "module", + ecmaVersion: "latest" + } + }, + plugins: { + "@typescript-eslint": tseslint, + import: importPlugin, + n: nPlugin, + promise: promisePlugin + }, + rules: { + "@typescript-eslint/no-unused-vars": ["warn", { "argsIgnorePattern": "^_" }], + "@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..4a89c6c --- /dev/null +++ b/eval-results.html @@ -0,0 +1,277 @@ + + + + + + Primer Eval Trajectory + + + +

Primer Eval Trajectory

+
+
+
+
+
+
+ + + \ No newline at end of file diff --git a/eval-results.json b/eval-results.json index 765bd07..48db37a 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..1be29f8 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,14 @@ +# Primer Examples + +This folder includes quick examples to help you get started. + +## CLI usage + +- Analyze a repo: primer analyze /path/to/repo +- Generate instructions: primer instructions --repo /path/to/repo +- Run readiness: primer readiness /path/to/repo +- Run evals: 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..bd529fe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,12 +19,25 @@ "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": "^2.1.4", + "eslint": "^9.7.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-import": "^2.30.0", + "eslint-plugin-n": "^17.10.2", + "eslint-plugin-promise": "^7.1.0", + "prettier": "^3.3.3", "tsup": "^8.5.1", "tsx": "^4.21.0", - "typescript": "^5.9.3" + "typescript": "^5.9.3", + "vitest": "^2.1.4" } }, "node_modules/@alcalzone/ansi-tokenize": { @@ -40,6 +53,77 @@ "node": ">=18" } }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "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": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "license": "MIT" + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.27.2", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz", @@ -482,6 +566,195 @@ "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/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", @@ -609,6 +882,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", @@ -937,6 +1262,77 @@ } } }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", @@ -1180,6 +1576,17 @@ "@octokit/openapi-types": "^27.0.0" } }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, "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", @@ -1530,6 +1937,13 @@ "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/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -1537,6 +1951,20 @@ "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", @@ -1557,1446 +1985,5914 @@ "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.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.54.0.tgz", + "integrity": "sha512-hAAP5io/7csFStuOmR782YmTthKBJ9ND3WVL60hcOjvtGFb+HJxH4O5huAcmcZ9v9G8P+JETiZ/G1B8MALnWZQ==", "dev": true, "license": "MIT", - "bin": { - "acorn": "bin/acorn" + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.54.0", + "@typescript-eslint/type-utils": "8.54.0", + "@typescript-eslint/utils": "8.54.0", + "@typescript-eslint/visitor-keys": "8.54.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.54.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/parser": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.54.0.tgz", + "integrity": "sha512-BtE0k6cjwjLZoZixN0t5AKP0kSzlGu7FctRXYuPAm//aaiZhmfq1JwdYpYr1brzEspYyFeF+8XF5j2VK6oalrA==", + "dev": true, "license": "MIT", "dependencies": { - "environment": "^1.0.0" + "@typescript-eslint/scope-manager": "8.54.0", + "@typescript-eslint/types": "8.54.0", + "@typescript-eslint/typescript-estree": "8.54.0", + "@typescript-eslint/visitor-keys": "8.54.0", + "debug": "^4.4.3" }, "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/project-service": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.54.0.tgz", + "integrity": "sha512-YPf+rvJ1s7MyiWM4uTRhE4DvBXrEV+d8oC3P9Y2eT7S+HBS0clybdMIPnhiATi9vZOYDc7OQ1L/i6ga6NFYK/g==", + "dev": true, "license": "MIT", - "engines": { - "node": ">=12" + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.54.0", + "@typescript-eslint/types": "^8.54.0", + "debug": "^4.4.3" + }, + "engines": { + "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": { + "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/scope-manager": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.54.0.tgz", + "integrity": "sha512-27rYVQku26j/PbHYcVfRPonmOlVI6gihHtXFbTdB5sb6qA0wdAQAbyXFVarQ5t4HRojIz64IV90YtsjQSSGlQg==", + "dev": true, "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.54.0", + "@typescript-eslint/visitor-keys": "8.54.0" + }, "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" } }, - "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/tsconfig-utils": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.54.0.tgz", + "integrity": "sha512-dRgOyT2hPk/JwxNMZDsIXDgyl9axdJI3ogZ2XWhBPsnZUv+hPesa5iuhdYt2gzwA9t8RE5ytOJ6xB0moV0Ujvw==", "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", "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" - } - }, - "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==", - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" }, - "engines": { - "node": ">=8" + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" } }, - "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/type-utils": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.54.0.tgz", + "integrity": "sha512-hiLguxJWHjjwL6xMBwD903ciAwd7DmK30Y9Axs/etOkftC3ZNN9K44IuRD/EB08amu+Zw6W37x9RecLkOo3pMA==", "dev": true, "license": "MIT", "dependencies": { - "load-tsconfig": "^0.2.3" + "@typescript-eslint/types": "8.54.0", + "@typescript-eslint/typescript-estree": "8.54.0", + "@typescript-eslint/utils": "8.54.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.4.0" }, "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" + "eslint": "^8.57.0 || ^9.0.0", + "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.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.54.0.tgz", + "integrity": "sha512-PDUI9R1BVjqu7AUDsRBbKMtwmjWcn4J3le+5LpcFgWULN3LvHC5rkc9gCVxbrsrGmO1jfPybN5s6h4Jy+OnkAA==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" - } - }, - "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": "^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" } }, - "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/typescript-estree": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.54.0.tgz", + "integrity": "sha512-BUwcskRaPvTk6fzVWgDPdUndLjB87KYDrN5EYGetnktoeAvPtO4ONHlAZDnj5VFnUANg0Sjm7j4usBlnoVMHwA==", "dev": true, "license": "MIT", "dependencies": { - "readdirp": "^4.0.1" + "@typescript-eslint/project-service": "8.54.0", + "@typescript-eslint/tsconfig-utils": "8.54.0", + "@typescript-eslint/types": "8.54.0", + "@typescript-eslint/visitor-keys": "8.54.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": ">= 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" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" } }, - "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==", + "node_modules/@typescript-eslint/utils": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.54.0.tgz", + "integrity": "sha512-9Cnda8GS57AQakvRyG0PTejJNlA2xhvyNtEVIMlDWOOeEyBkYWhGPnfrIAnqxLMTSTo6q8g12XVjjev5l1NvMA==", + "dev": true, "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.54.0", + "@typescript-eslint/types": "8.54.0", + "@typescript-eslint/typescript-estree": "8.54.0" + }, "engines": { - "node": ">=10" + "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/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/@typescript-eslint/visitor-keys": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.54.0.tgz", + "integrity": "sha512-VFlhGSl4opC0bprJiItPQ1RfUhGDIBokcPwaFH4yiBCaNPeld/9VeXbiPO1cLyorQi1G1vL+ecBk1x8o1axORA==", + "dev": true, "license": "MIT", "dependencies": { - "restore-cursor": "^4.0.0" + "@typescript-eslint/types": "8.54.0", + "eslint-visitor-keys": "^4.2.1" }, "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" } }, - "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" - }, + "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": ">=20" + "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-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/coverage-v8": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-2.1.9.tgz", + "integrity": "sha512-Z2cOr0ksM00MpEfyVE8KXIYPEcBFxdbLSs56L8PO0QQMxt/6bDj45uQfxoc96v05KW3clk7vvgP0qfDit9DmfQ==", + "dev": true, "license": "MIT", "dependencies": { - "get-east-asian-width": "^1.3.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=20" + "@ampproject/remapping": "^2.3.0", + "@bcoe/v8-coverage": "^0.2.3", + "debug": "^4.3.7", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-report": "^3.0.1", + "istanbul-lib-source-maps": "^5.0.6", + "istanbul-reports": "^3.1.7", + "magic-string": "^0.30.12", + "magicast": "^0.3.5", + "std-env": "^3.8.0", + "test-exclude": "^7.0.1", + "tinyrainbow": "^1.2.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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" + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@vitest/browser": "2.1.9", + "vitest": "2.1.9" + }, + "peerDependenciesMeta": { + "@vitest/browser": { + "optional": true + } } }, - "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/expect": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.9.tgz", + "integrity": "sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==", + "dev": true, "license": "MIT", "dependencies": { - "convert-to-spaces": "^2.0.1" + "@vitest/spy": "2.1.9", + "@vitest/utils": "2.1.9", + "chai": "^5.1.2", + "tinyrainbow": "^1.2.0" }, - "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/mocker": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.9.tgz", + "integrity": "sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg==", + "dev": true, "license": "MIT", - "engines": { - "node": ">=20" + "dependencies": { + "@vitest/spy": "2.1.9", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.12" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } } }, - "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==", + "node_modules/@vitest/pretty-format": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.9.tgz", + "integrity": "sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==", "dev": true, "license": "MIT", - "engines": { - "node": "^14.18.0 || >=16.10.0" + "dependencies": { + "tinyrainbow": "^1.2.0" + }, + "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/@vitest/runner": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.9.tgz", + "integrity": "sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g==", + "dev": true, "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "dependencies": { + "@vitest/utils": "2.1.9", + "pathe": "^1.1.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" } }, - "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, + "node_modules/@vitest/runner/node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true, "license": "MIT" }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "node_modules/@vitest/snapshot": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.9.tgz", + "integrity": "sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==", + "dev": true, "license": "MIT", "dependencies": { - "ms": "^2.1.3" + "@vitest/pretty-format": "2.1.9", + "magic-string": "^0.30.12", + "pathe": "^1.1.2" }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "funding": { + "url": "https://opencollective.com/vitest" } }, - "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==", + "node_modules/@vitest/snapshot/node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true, "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/@vitest/spy": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.9.tgz", + "integrity": "sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ==", + "dev": true, "license": "MIT", - "engines": { - "node": ">=18" + "dependencies": { + "tinyspy": "^3.0.2" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://opencollective.com/vitest" } }, - "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==", + "node_modules/@vitest/utils": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.9.tgz", + "integrity": "sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==", "dev": true, - "hasInstallScript": true, "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" + "dependencies": { + "@vitest/pretty-format": "2.1.9", + "loupe": "^3.1.2", + "tinyrainbow": "^1.2.0" }, - "engines": { - "node": ">=18" - }, - "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://opencollective.com/vitest" } }, - "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/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": ">=8" + "node": ">=0.4.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/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/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/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": { - "@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" + "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": ">=8.6.0" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "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/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==", + "license": "MIT", "dependencies": { - "reusify": "^1.0.4" + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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, + "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", "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" + "node": ">=12" }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "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/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", - "dependencies": { - "to-regex-range": "^5.0.1" - }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "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/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/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": { - "magic-string": "^0.30.17", - "mlly": "^1.7.4", - "rollup": "^4.34.8" + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "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/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, - "hasInstallScript": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], + "dependencies": { + "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": "^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/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", + "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" + }, "engines": { - "node": ">=18" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "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/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": { - "resolve-pkg-maps": "^1.0.0" + "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/privatenumber/get-tsconfig?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "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", + "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": { - "is-glob": "^4.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": ">= 6" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "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/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, "license": "MIT", "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" + "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": ">=0.10.0" + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "url": "https://github.com/sponsors/ljharb" } }, - "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/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": ">=12" + } + }, + "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": ">= 0.4" + } + }, + "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", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "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/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": { - "@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" + "possible-typed-array-names": "^1.0.0" }, "engines": { - "node": ">=20" + "node": ">= 0.4" }, - "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 - } + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "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/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/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/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": { - "get-east-asian-width": "^1.3.0", - "strip-ansi": "^7.1.0" + "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" + "node": ">=8" + } + }, + "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": { + "load-tsconfig": "^0.2.3" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "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": "5.3.3", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", + "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", "dev": true, "license": "MIT", + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, "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==", + "node_modules/check-error": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz", + "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==", "dev": true, "license": "MIT", "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">= 16" } }, - "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-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==", "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.3.0", + "strip-ansi": "^7.1.0" + }, "engines": { - "node": ">=6" + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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/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": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "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.2", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz", + "integrity": "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==", + "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" - } - ], - "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==", + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=6" } }, - "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/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/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" - } - }, - "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" + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.4" } }, - "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/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" }, - "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/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/rollup": { - "version": "4.57.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.57.0.tgz", - "integrity": "sha512-e5lPJi/aui4TO1LpAXIRLySmwXSE8k3b9zoGfd42p67wzxog4WHjiZF3M2uheQih4DGyc25QEV4yRBbpueNiUA==", + "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": { - "@types/estree": "1.0.8" - }, - "bin": { - "rollup": "dist/bin/rollup" + "graceful-fs": "^4.2.4", + "tapable": "^2.3.0" }, "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "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" + "node": ">=10.13.0" } }, - "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/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "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", "engines": { - "node": ">=14" + "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/sindresorhus" } }, - "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-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": { - "@kwsites/file-exists": "^1.1.1", - "@kwsites/promise-deferred": "^1.1.1", - "debug": "^4.4.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" }, "funding": { - "type": "github", - "url": "https://github.com/steveukx/git-js?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "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-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": { - "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": ">= 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-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": "BSD-3-Clause", + "license": "MIT", "engines": { - "node": ">= 12" + "node": ">= 0.4" } }, - "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==", + "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": { - "escape-string-regexp": "^2.0.0" + "es-errors": "^1.3.0" }, "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-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": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" }, "engines": { - "node": ">=18" + "node": ">= 0.4" + } + }, + "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": "MIT", + "dependencies": { + "hasown": "^2.0.2" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">= 0.4" } }, - "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/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": { - "ansi-regex": "^6.0.1" + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" }, "engines": { - "node": ">=12" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/sucrase": { - "version": "3.35.1", - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", - "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "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, "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" + "esbuild": "bin/esbuild" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=18" + }, + "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" } }, - "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/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": ">= 6" + "node": ">=8" } }, - "node_modules/thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "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": { - "any-promise": "^1.0.0" + "@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/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/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": { - "thenify": ">= 3.1.0 < 4" + "semver": "^7.5.4" }, "engines": { - "node": ">=0.8" + "node": ">=12" + }, + "peerDependencies": { + "eslint": ">=6.0.0" } }, - "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==", + "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" - }, - "node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "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": { - "fdir": "^6.5.0", - "picomatch": "^4.0.3" + "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/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/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/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/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": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" + "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-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/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/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/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/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz", + "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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/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/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/import-fresh/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/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.6.0", + "resolved": "https://registry.npmjs.org/ink/-/ink-6.6.0.tgz", + "integrity": "sha512-QDt6FgJxgmSxAelcOvOHUvFxbIUjVpCH5bx+Slvc5m7IEcpGt3dYwbz/L+oRnqEGeRvwy1tineKK4ect3nW1vQ==", + "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" + }, + "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/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==", + "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/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-lib-source-maps": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", + "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.23", + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.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/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "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-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/loupe": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", + "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "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.3.5", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz", + "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.25.4", + "@babel/types": "^7.25.4", + "source-map-js": "^1.2.0" + } + }, + "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/micromatch/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/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/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "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/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/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "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/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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/pathval": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "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": "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/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.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.57.0.tgz", + "integrity": "sha512-e5lPJi/aui4TO1LpAXIRLySmwXSE8k3b9zoGfd42p67wzxog4WHjiZF3M2uheQih4DGyc25QEV4yRBbpueNiUA==", + "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.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" + } + }, + "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.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "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.30.0", + "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.30.0.tgz", + "integrity": "sha512-q6lxyDsCmEal/MEGhP1aVyQ3oxnagGlBDOVSIB4XUVLl1iZh0Pah6ebC9V4xBap/RfgP2WlI8EKs0WS0rMEJHg==", + "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/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": "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": { + "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/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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/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/test-exclude": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.1.tgz", + "integrity": "sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^10.4.1", + "minimatch": "^9.0.4" + }, + "engines": { + "node": ">=18" + } + }, + "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/tinypool": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/tinyrainbow": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz", + "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", + "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", + "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-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/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": "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)", + "engines": { + "node": ">=16" + }, + "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": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.9.tgz", + "integrity": "sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.3.7", + "es-module-lexer": "^1.5.4", + "pathe": "^1.1.2", + "vite": "^5.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vite-node/node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite/node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" } }, - "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/vite/node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=8.0" + "node": ">=12" } }, - "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/vite/node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "bin": { - "tree-kill": "cli.js" + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" } }, - "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==", + "node_modules/vite/node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", "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==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/vitest": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.9.tgz", + "integrity": "sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q==", "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" + "@vitest/expect": "2.1.9", + "@vitest/mocker": "2.1.9", + "@vitest/pretty-format": "^2.1.9", + "@vitest/runner": "2.1.9", + "@vitest/snapshot": "2.1.9", + "@vitest/spy": "2.1.9", + "@vitest/utils": "2.1.9", + "chai": "^5.1.2", + "debug": "^4.3.7", + "expect-type": "^1.1.0", + "magic-string": "^0.30.12", + "pathe": "^1.1.2", + "std-env": "^3.8.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.1", + "tinypool": "^1.0.1", + "tinyrainbow": "^1.2.0", + "vite": "^5.0.0", + "vite-node": "2.1.9", + "why-is-node-running": "^2.3.0" }, "bin": { - "tsup": "dist/cli-default.js", - "tsup-node": "dist/cli-node.js" + "vitest": "vitest.mjs" }, "engines": { - "node": ">=18" + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@microsoft/api-extractor": "^7.36.0", - "@swc/core": "^1", - "postcss": "^8.4.12", - "typescript": ">=4.5.0" + "@edge-runtime/vm": "*", + "@types/node": "^18.0.0 || >=20.0.0", + "@vitest/browser": "2.1.9", + "@vitest/ui": "2.1.9", + "happy-dom": "*", + "jsdom": "*" }, "peerDependenciesMeta": { - "@microsoft/api-extractor": { + "@edge-runtime/vm": { "optional": true }, - "@swc/core": { + "@types/node": { "optional": true }, - "postcss": { + "@vitest/browser": { "optional": true }, - "typescript": { + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { "optional": true } } }, - "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/vitest/node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", "dev": true, + "license": "MIT" + }, + "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_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": "ISC", "dependencies": { - "esbuild": "~0.27.0", - "get-tsconfig": "^4.7.5" + "isexe": "^2.0.0" }, "bin": { - "tsx": "dist/cli.mjs" + "node-which": "bin/node-which" }, "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/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": { + "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": ">=16" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "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==", + "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": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" + "license": "MIT", + "dependencies": { + "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": ">=14.17" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "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/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" - }, - "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" + "license": "MIT", + "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/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/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": { + "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": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "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==", + "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": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, "engines": { - "node": ">=14.0.0" + "node": ">=8" } }, "node_modules/widest-line": { @@ -3014,6 +7910,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "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", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/wrap-ansi": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", @@ -3031,6 +7937,96 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/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/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/ws": { "version": "8.19.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", @@ -3052,6 +8048,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..5c8c40a 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,26 @@ { - "name": "primer", + "name": "@anthropic/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,18 @@ "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": "^2.1.4", + "eslint": "^9.7.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-import": "^2.30.0", + "eslint-plugin-n": "^17.10.2", + "eslint-plugin-promise": "^7.1.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": "^2.1.4" } } diff --git a/primer.eval.json b/primer.eval.json index 3b77578..750f8d9 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." } ] -} +} \ No newline at end of file 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..27a23d6 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -1,15 +1,14 @@ import { Command } from "commander"; import { initCommand } from "./commands/init"; -import { analyzeCommand } from "./commands/analyze"; import { generateCommand } from "./commands/generate"; 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 { tuiCommand } from "./commands/tui"; import { instructionsCommand } from "./commands/instructions"; import { batchCommand } from "./commands/batch"; +import { readinessCommand } from "./commands/readiness"; +import { batchReadinessCommand } from "./commands/batchReadiness"; +import { DEFAULT_MODEL, DEFAULT_JUDGE_MODEL } from "./config"; export function runCli(argv: string[]): void { const program = new Command(); @@ -23,37 +22,37 @@ export function runCli(argv: string[]): void { .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..bce2c3f 100644 --- a/src/commands/batch.tsx +++ b/src/commands/batch.tsx @@ -2,14 +2,41 @@ import React from "react"; import { render } from "ink"; import { BatchTui } from "../ui/BatchTui"; import { getGitHubToken } from "../services/github"; +import { BatchTuiAzure } from "../ui/BatchTuiAzure"; +import { getAzureDevOpsToken } from "../services/azureDevops"; 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(""); diff --git a/src/commands/batchReadiness.tsx b/src/commands/batchReadiness.tsx new file mode 100644 index 0000000..465ca61 --- /dev/null +++ b/src/commands/batchReadiness.tsx @@ -0,0 +1,29 @@ +import React from "react"; +import { render } from "ink"; +import { BatchReadinessTui } from "../ui/BatchReadinessTui"; +import { getGitHubToken } from "../services/github"; + +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/eval.ts b/src/commands/eval.ts index b875f97..ac9deba 100644 --- a/src/commands/eval.ts +++ b/src/commands/eval.ts @@ -1,6 +1,9 @@ import path from "path"; import fs from "fs/promises"; import { runEval } from "../services/evaluator"; +import { listCopilotModels } from "../services/copilot"; +import { generateEvalScaffold } from "../services/evalScaffold"; +import { DEFAULT_MODEL, DEFAULT_JUDGE_MODEL } from "../config"; type EvalOptions = { repo?: string; @@ -8,35 +11,27 @@ type EvalOptions = { judgeModel?: string; output?: string; init?: 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." - } - ] + count?: string; + listModels?: boolean; }; 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; + } // 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 +40,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 +53,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..891f078 100644 --- a/src/commands/generate.ts +++ b/src/commands/generate.ts @@ -1,15 +1,19 @@ import path from "path"; +import fs from "fs/promises"; 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"]); + 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 +21,44 @@ 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..1242547 100644 --- a/src/commands/init.ts +++ b/src/commands/init.ts @@ -4,21 +4,38 @@ import { checkbox, select } from "@inquirer/prompts"; import { analyzeRepo } from "../services/analyzer"; import { generateConfigs } from "../services/generator"; import { GitHubRepo, listAccessibleRepos } from "../services/github"; -import { cloneRepo, isGitRepo } from "../services/git"; +import { + AzureDevOpsOrg, + AzureDevOpsProject, + AzureDevOpsRepo, + getAzureDevOpsToken, + listOrganizations, + listProjects, + listRepos +} from "../services/azureDevops"; +import { buildAuthedUrl, cloneRepo, isGitRepo } from "../services/git"; import { generateCopilotInstructions } from "../services/instructions"; -import { ensureDir } from "../utils/fs"; +import { ensureDir, 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 { let repoPath = path.resolve(repoPathArg ?? process.cwd()); + const provider = options.provider ?? (options.github ? "github" : undefined); - if (options.github) { + if (provider && provider !== "github" && provider !== "azure") { + console.error("Invalid provider. Use github or azure."); + process.exitCode = 1; + return; + } + + 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 +59,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 +67,75 @@ 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); + } + } 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: [ diff --git a/src/commands/pr.ts b/src/commands/pr.ts index d8ce79b..5012c80 100644 --- a/src/commands/pr.ts +++ b/src/commands/pr.ts @@ -1,21 +1,90 @@ import path from "path"; +import fs from "fs/promises"; import { analyzeRepo } from "../services/analyzer"; import { generateConfigs } from "../services/generator"; 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 { + createPullRequest as createAzurePullRequest, + getAzureDevOpsToken, + getRepo as getAzureRepo +} from "../services/azureDevops"; +import { buildAuthedUrl, checkoutBranch, cloneRepo, commitAll, isGitRepo, pushBranch } from "../services/git"; +import { ensureDir, validateCachePath } from "../utils/fs"; +import { buildConfigsPrBody, buildInstructionsPrBody } from "../utils/pr"; +import { DEFAULT_MODEL } from "../config"; 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); + } + + 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 +101,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 +120,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..5f2e09f --- /dev/null +++ b/src/commands/readiness.ts @@ -0,0 +1,143 @@ +import path from "path"; +import fs from "fs/promises"; +import chalk from "chalk"; +import { + ReadinessReport, + ReadinessCriterionResult, + 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"; +} \ No newline at end of file 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/services/__tests__/analyzer.test.ts b/src/services/__tests__/analyzer.test.ts new file mode 100644 index 0000000..be0a9d6 --- /dev/null +++ b/src/services/__tests__/analyzer.test.ts @@ -0,0 +1,321 @@ +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..7901777 --- /dev/null +++ b/src/services/__tests__/cachePath.test.ts @@ -0,0 +1,39 @@ +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..bd9e28e --- /dev/null +++ b/src/services/__tests__/fs.test.ts @@ -0,0 +1,73 @@ +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..a64ec51 --- /dev/null +++ b/src/services/__tests__/generator.test.ts @@ -0,0 +1,128 @@ +import fs from "fs/promises"; +import os from "os"; +import path from "path"; +import { afterEach, beforeEach, describe, expect, it } from "vitest"; + +import { generateConfigs } from "../generator"; +import type { RepoAnalysis } from "../analyzer"; + +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..99c5a12 --- /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..b6825cc --- /dev/null +++ b/src/services/__tests__/readiness.test.ts @@ -0,0 +1,360 @@ +import fs from "fs/promises"; +import os from "os"; +import path from "path"; +import { afterEach, beforeEach, describe, expect, it } from "vitest"; + +import { runReadinessReport, type ReadinessReport } 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..27f199b --- /dev/null +++ b/src/services/__tests__/visualReport.test.ts @@ -0,0 +1,190 @@ +import { describe, expect, it } from "vitest"; + +import { generateVisualReport } from "../visualReport"; +import type { ReadinessReport } from "../readiness"; + +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..ee5b542 100644 --- a/src/services/analyzer.ts +++ b/src/services/analyzer.ts @@ -1,6 +1,18 @@ import fs from "fs/promises"; import path from "path"; +import fg from "fast-glob"; import { isGitRepo } from "./git"; +import { fileExists, safeReadDir, readJson } from "../utils/fs"; + +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; @@ -8,6 +20,10 @@ export type RepoAnalysis = { 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,24 +48,56 @@ 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); @@ -63,6 +111,10 @@ async function detectPackageManager(repoPath: string, files: string[]): Promise< 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; } @@ -82,21 +134,318 @@ 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; } } -async function readJson(filePath: string): Promise | 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 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..f5aebb7 --- /dev/null +++ b/src/services/azureDevops.ts @@ -0,0 +1,258 @@ +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; +} \ No newline at end of file diff --git a/src/services/copilot.ts b/src/services/copilot.ts new file mode 100644 index 0000000..1a6135a --- /dev/null +++ b/src/services/copilot.ts @@ -0,0 +1,138 @@ +import fs from "fs/promises"; +import path from "path"; +import fg from "fast-glob"; +import { execFile } from "node:child_process"; +import { promisify } from "node:util"; + +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..70367cd --- /dev/null +++ b/src/services/evalScaffold.ts @@ -0,0 +1,198 @@ +import { assertCopilotCliReady } from "./copilot"; +import { DEFAULT_MODEL } from "../config"; +import { withCwd } from "../utils/cwd"; + +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..1b30379 100644 --- a/src/services/evaluator.ts +++ b/src/services/evaluator.ts @@ -2,20 +2,25 @@ import fs from "fs/promises"; import path from "path"; import { execFile } from "node:child_process"; import { promisify } from "node:util"; +import { assertCopilotCliReady } from "./copilot"; +import { buildTimestampedName } from "../utils/fs"; +import type { EvalCase, EvalConfig } from "./evalScaffold"; const execFileAsync = promisify(execFile); -type EvalCase = { - prompt: string; - expectation: string; - id?: 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."; -type EvalConfig = { - instructionFile?: string; - cases: EvalCase[]; - systemMessage?: string; -}; +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 +31,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 +74,28 @@ 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 +106,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 +129,61 @@ 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,12 +193,19 @@ 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 }, + client: CopilotClient, options: AskOptions -): Promise { +): Promise { const session = await client.createSession({ model: options.model, streaming: true, @@ -131,7 +216,10 @@ async function askOnce( }); 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 +228,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,9 +255,9 @@ 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, @@ -171,7 +268,10 @@ async function judge( }); 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 +295,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 +329,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 + const parsed = JSON.parse(raw) as EvalConfig; + if (!parsed || !Array.isArray(parsed.cases)) { + throw new Error("Eval config must have a 'cases' array."); } - - // 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 - } - } - - throw new Error("Copilot CLI not found. Install GitHub Copilot Chat extension in VS Code."); + return parsed; } async function readOptionalFile(filePath: string): Promise { @@ -271,14 +354,18 @@ 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) { @@ -289,3 +376,578 @@ function formatSummary(results: EvalResult[]): string { 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/git.ts b/src/services/git.ts index a8b1e79..34acfd5 100644 --- a/src/services/git.ts +++ b/src/services/git.ts @@ -65,6 +65,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) { @@ -85,10 +103,16 @@ export async function pushBranch(repoPath: string, branch: string, token?: strin 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]); diff --git a/src/services/github.ts b/src/services/github.ts index fbabaea..91ac04f 100644 --- a/src/services/github.ts +++ b/src/services/github.ts @@ -113,7 +113,7 @@ export async function listUserOrgs(token: string): Promise { return orgs.map((org) => ({ login: org.login, - name: org.name ?? null + name: org.description ?? null })); } diff --git a/src/services/instructions.ts b/src/services/instructions.ts index a1ea882..245956d 100644 --- a/src/services/instructions.ts +++ b/src/services/instructions.ts @@ -1,6 +1,6 @@ -import fs from "fs/promises"; -import { execFile } from "node:child_process"; -import { promisify } from "node:util"; +import { assertCopilotCliReady } from "./copilot"; +import { DEFAULT_MODEL } from "../config"; +import { withCwd } from "../utils/cwd"; type GenerateInstructionsOptions = { repoPath: string; @@ -13,122 +13,75 @@ export async function generateCopilotInstructions(options: GenerateInstructionsO const repoPath = options.repoPath; const progress = options.onProgress ?? (() => {}); - const originalCwd = process.cwd(); - process.chdir(repoPath); + return withCwd(repoPath, async () => { + progress("Checking Copilot CLI..."); + const cliPath = await assertCopilotCliReady(); - 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 = ""; + 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..."); + // 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."); + } } - } 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); - } -} - -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 - } + progress("Analyzing codebase..."); + await session.sendAndWait({ prompt }, 180000); + await session.destroy(); - // 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..1797463 --- /dev/null +++ b/src/services/readiness.ts @@ -0,0 +1,846 @@ +import fs from "fs/promises"; +import path from "path"; +import { analyzeRepo, RepoApp, RepoAnalysis } from "./analyzer"; +import { fileExists, safeReadDir, readJson } from "../utils/fs"; + +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)); +} \ No newline at end of file diff --git a/src/services/visualReport.ts b/src/services/visualReport.ts new file mode 100644 index 0000000..115590c --- /dev/null +++ b/src/services/visualReport.ts @@ -0,0 +1,805 @@ +import path from "path"; +import { ReadinessReport, ReadinessCriterionResult } 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)} + ${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/BatchReadinessTui.tsx b/src/ui/BatchReadinessTui.tsx new file mode 100644 index 0000000..fe4d07b --- /dev/null +++ b/src/ui/BatchReadinessTui.tsx @@ -0,0 +1,328 @@ +import React, { useEffect, useState } from "react"; +import { Box, Text, useApp, useInput } from "ink"; +import path from "path"; +import fs from "fs/promises"; +import os from "os"; +import simpleGit from "simple-git"; +import { + GitHubOrg, + GitHubRepo, + listUserOrgs, + listOrgRepos, + listAccessibleRepos +} from "../services/github"; +import { buildAuthedUrl, cloneRepo } from "../services/git"; +import { runReadinessReport, ReadinessReport } from "../services/readiness"; +import { generateVisualReport } from "../services/visualReport"; +import { ensureDir } 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 = path.join(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..5b913cf 100644 --- a/src/ui/BatchTui.tsx +++ b/src/ui/BatchTui.tsx @@ -11,9 +11,12 @@ import { listAccessibleRepos, checkReposForInstructions } from "../services/github"; -import { cloneRepo, checkoutBranch, commitAll, pushBranch, isGitRepo, CloneOptions } from "../services/git"; +import simpleGit from "simple-git"; +import { buildAuthedUrl, 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 { DEFAULT_MODEL } from "../config"; import { StaticBanner } from "./AnimatedBanner"; type Props = { @@ -146,6 +149,8 @@ export function BatchTui({ token, outputPath }: Props): React.JSX.Element { setCurrentRepoIndex(0); setResults([]); + const localResults: ProcessResult[] = []; + for (let i = 0; i < selectedRepos.length; i++) { const repo = selectedRepos[i]; setCurrentRepoIndex(i); @@ -154,13 +159,11 @@ 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 @@ -168,6 +171,9 @@ export function BatchTui({ token, outputPath }: Props): React.JSX.Element { 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 @@ -181,17 +187,21 @@ export function BatchTui({ token, outputPath }: Props): React.JSX.Element { 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 +227,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"); @@ -262,7 +273,10 @@ 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"); + }); } } @@ -297,7 +311,10 @@ export function BatchTui({ token, outputPath }: Props): React.JSX.Element { 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)"); @@ -438,29 +455,3 @@ export function BatchTui({ token, outputPath }: Props): React.JSX.Element { ); } - -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..ccb19ad --- /dev/null +++ b/src/ui/BatchTuiAzure.tsx @@ -0,0 +1,519 @@ +import React, { useEffect, useState } from "react"; +import { Box, Text, useApp, useInput } from "ink"; +import path from "path"; +import fs from "fs/promises"; +import { + AzureDevOpsOrg, + AzureDevOpsProject, + AzureDevOpsRepo, + listOrganizations, + listProjects, + listRepos, + checkReposForInstructions, + createPullRequest +} from "../services/azureDevops"; +import simpleGit from "simple-git"; +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 { DEFAULT_MODEL } from "../config"; +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 + )} + + + ); +} \ No newline at end of file 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..2ab5d6c 100644 --- a/src/ui/tui.tsx +++ b/src/ui/tui.tsx @@ -1,214 +1,920 @@ -import React, { useMemo, useState } from "react"; +import React, { useEffect, 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 { runEval, type EvalResult } from "../services/evaluator"; +import { generateEvalScaffold } from "../services/evalScaffold"; +import { listCopilotModels } from "../services/copilot"; import { AnimatedBanner, StaticBanner } from "./AnimatedBanner"; import { BatchTui } from "./BatchTui"; +import { BatchTuiAzure } from "./BatchTuiAzure"; import { getGitHubToken } from "../services/github"; +import { getAzureDevOpsToken } from "../services/azureDevops"; +import { safeWriteFile, buildTimestampedName } from "../utils/fs"; +import { analyzeRepo, RepoApp } from "../services/analyzer"; 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; +}; + +import type { EvalConfig } from "../services/evalScaffold"; + +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"); + // 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]); + + const indexForModel = (model: string): number => { + const index = availableModels.indexOf(model); + return index === -1 ? 0 : index; + }; + + const openModelPicker = (target: "eval" | "judge"): void => { + if (!availableModels.length) { + setMessage("No Copilot CLI models detected; using defaults."); return; } + setModelPickTarget(target); + setModelCursor(indexForModel(target === "eval" ? evalModel : judgeModel)); + setStatus("modelPicker"); + setMessage(`Select ${target} model.`); + }; - if (key.escape || input.toLowerCase() === "q") { - app.exit(); - 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."); + } + 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); + } + addLog(msg, "error"); } + }; - // 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"); + 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); + } + }; + + // 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 { + if (status === "intro") { + setStatus("idle"); + return; + } + + if (status === "modelPicker") { + if (key.escape) { + setStatus("idle"); + setMessage("Model picker cancelled."); + return; + } + + 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(""); - } catch (error) { - setStatus("error"); - setMessage(error instanceof Error ? error.message : "Failed to save."); + return; } return; } - if (input.toLowerCase() === "d") { - setStatus("idle"); - setMessage("Discarded generated instructions."); - setGeneratedContent(""); + + 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; } - return; - } - 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."); + 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; } - return; - } - if (input.toLowerCase() === "g") { - setStatus("generating"); - setMessage("Starting generation..."); - 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 === "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 (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."); + 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; } - setBatchToken(token); - setStatus("batch"); - 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."); + 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; } - - 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 (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..." : ""); - // 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..0aaf76e 100644 --- a/src/utils/fs.ts +++ b/src/utils/fs.ts @@ -6,16 +6,39 @@ export async function ensureDir(dirPath: string): Promise { } 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)`; + 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 +46,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/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..9d7c740 --- /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" + } + } +});