diff --git a/.brv b/.brv new file mode 160000 index 00000000..2e6a74c1 --- /dev/null +++ b/.brv @@ -0,0 +1 @@ +Subproject commit 2e6a74c1a24f86b80da81a6da985c55783edc937 diff --git a/.brv/.gitignore b/.brv/.gitignore deleted file mode 100644 index 910e362a..00000000 --- a/.brv/.gitignore +++ /dev/null @@ -1,18 +0,0 @@ -# BEGIN pi-byterover -# Dream state and logs -dream-log/ -dream-state.json -dream.lock - -# Review backups -review-backups/ - -# Generated files -config.json -_queue_status.json -.snapshot.json -_manifest.json -_index.md -*.abstract.md -*.overview.md -# END pi-byterover diff --git a/.brv/context-tree/architecture/auth/context.md b/.brv/context-tree/architecture/auth/context.md deleted file mode 100644 index d18ff8ee..00000000 --- a/.brv/context-tree/architecture/auth/context.md +++ /dev/null @@ -1,13 +0,0 @@ -# Topic: auth - -## Overview - -Covers review findings and verification outcomes for the Task 8 remote authentication implementation, especially state ownership, callback safety, and flow cleanup. - -## Key Concepts - -- remote auth completion semantics -- flow lifecycle cleanup -- callback safety -- test coverage gaps -- typecheck verification diff --git a/.brv/context-tree/architecture/auth/remote_auth_and_state_ownership.md b/.brv/context-tree/architecture/auth/remote_auth_and_state_ownership.md deleted file mode 100644 index 64e3db39..00000000 --- a/.brv/context-tree/architecture/auth/remote_auth_and_state_ownership.md +++ /dev/null @@ -1,58 +0,0 @@ ---- -title: Remote Auth and State Ownership -summary: Remote mode keeps all Caplets state and downstream auth credentials on the server; local clients only coordinate login/logout flows and must never receive secrets. -tags: [] -related: [architecture/remote_control/working_module.md, architecture/remote_control/unified_environment_variable_interface.md, architecture/remote_control/cli_remote_mode_selection.md, architecture/remote_control/remote_control_api_shape.md] -keywords: [] -createdAt: '2026-05-20T11:04:45.510Z' -updatedAt: '2026-05-20T11:04:45.510Z' ---- - -## Reason - -Document the decision that server owns auth creds and Caplets state in remote mode - -## Raw Concept - -**Task:** -Document remote auth and state ownership model for Caplets - -**Changes:** -- Server owns Caplets config, .caplets files, installed Caplets, backend definitions, OAuth/token auth store, and reload/watch lifecycle in remote mode -- Login/logout commands operate against the remote server auth store when invoked from a local client -- Remote responses must not expose access tokens or secrets - -**Flow:** -local client -> remote control command -> browser/device auth flow -> server stores final tokens -> subsequent auth/status operations read server-side state - -**Timestamp:** 2026-05-20T11:04:32.841Z - -## Narrative - -### Structure - -This design centers on server-owned state for remote mode, with local terminals acting as coordinators for auth flows rather than state holders. - -### Dependencies - -Applies to auth list/logout/login behavior, remote control API shape, and any future remote config-paths command. - -### Highlights - -A local caplets auth login linear command should complete against the remote server’s auth store, while `/control` responses remain secret-free. - -### Rules - -Remote control API responses must never include access tokens or secrets. Remote control auth (the Basic Auth used to access `/control`) is separate from downstream provider auth (GitHub/Linear/etc.). - -### Examples - -auth list shows server-side credential status; auth logout deletes server-side credentials; config paths defaults to local paths and can later support a remote variant. - -## Facts - -- **remote_state_ownership**: In remote mode, the server owns all Caplets state. [project] -- **auth_store_location**: OAuth and token auth store live on the server in remote mode. [project] -- **secret_exposure_policy**: Remote control API responses must never include access tokens or secrets. [project] -- **auth_boundary**: Remote control auth is separate from downstream provider auth. [project] -- **config_paths_default**: Local caplets config paths should show local paths by default. [project] diff --git a/.brv/context-tree/architecture/auth/remote_auth_review_findings.md b/.brv/context-tree/architecture/auth/remote_auth_review_findings.md deleted file mode 100644 index 62613d52..00000000 --- a/.brv/context-tree/architecture/auth/remote_auth_review_findings.md +++ /dev/null @@ -1,65 +0,0 @@ ---- -title: Remote Auth Review Findings -summary: Task 8 review found a high-severity premature success message in remote auth login and a medium-severity lack of TTL/cleanup for pending remote auth flows; tests and typecheck passed. -tags: [] -related: [] -keywords: [] -createdAt: '2026-05-20T15:01:08.681Z' -updatedAt: '2026-05-20T15:01:08.681Z' ---- - -## Reason - -Preserve lasting findings from Task 8 remote auth implementation review - -## Raw Concept - -**Task:** -Document the code review findings for Task 8 remote auth implementation - -**Changes:** -- Identified premature success reporting in remote auth login -- Identified missing TTL and pruning for pending remote auth flows -- Recorded passing test and typecheck verification results - -**Files:** -- packages/core/src/cli.ts -- packages/core/src/remote-control/auth-flow.ts -- packages/core/src/remote-control/dispatch.ts -- packages/core/test/remote-control-dispatch.test.ts -- packages/core/test/serve-http.test.ts -- packages/core/test/cli-remote.test.ts -- packages/core/test/auth.test.ts - -**Flow:** -review scope -> inspect auth login behavior -> inspect remote auth flow cleanup -> verify tests and typecheck - -**Timestamp:** 2026-05-20T15:00:51.436Z - -**Author:** code quality review - -## Narrative - -### Structure - -This review notes two issues: a high-severity mismatch between remote and local auth completion semantics, and a medium-severity lifecycle cleanup gap in the remote auth flow store. - -### Dependencies - -The findings depend on remote-control auth flow handling and callback dispatch behavior, plus the existing test suite and typecheck validation. - -### Highlights - -Existing tests passed, but they do not catch premature remote-login success or stale-flow lifecycle behavior. - -### Rules - -Return APPROVED or FINDINGS with severity and refs. Do not edit during review. - -## Facts - -- **remote_auth_login_premature_success**: Remote caplets auth login reports success immediately after auth_login_start before the OAuth callback completes. [project] -- **remote_auth_flow_cleanup**: Pending remote auth flows are stored in an unbounded Map and are deleted only after flow.complete(callbackUrl) succeeds. [project] -- **remote_auth_flow_lifecycle_risk**: Failed callbacks leave the remote auth flow live and abandoned flows can remain indefinitely. [project] -- **verification_tests**: Verification run passed pnpm --filter @caplets/core test for remote-control-dispatch, serve-http, cli-remote, and auth tests: 33 files / 410 tests. [project] -- **verification_typecheck**: pnpm --filter @caplets/core typecheck passed. [project] diff --git a/.brv/context-tree/architecture/remote_control/caplets_interface_ux.md b/.brv/context-tree/architecture/remote_control/caplets_interface_ux.md deleted file mode 100644 index e834a9f1..00000000 --- a/.brv/context-tree/architecture/remote_control/caplets_interface_ux.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -title: Caplets Interface UX -summary: Caplets interface UX work covering remote control polish, output field selection, and native hot reload ergonomics. -tags: [] -related: [] -keywords: [] -createdAt: '2026-05-20T13:00:01.109Z' -updatedAt: '2026-05-20T13:00:01.109Z' ---- - -## Reason - -Curate interface UX planning and implementation details for caplets remote control and output selection - -## Raw Concept - -**Task:** -Document the caplets interface UX work across remote control, output field selection, and hot reload-related usability planning - -**Changes:** -- Refined remote control interface behavior and selection flows -- Added output field selection planning -- Captured native hot reload UX considerations - -**Flow:** -caplets UX planning -> remote control behavior -> output selection -> hot reload ergonomics - -**Timestamp:** 2026-05-20T12:59:55.885Z - -## Narrative - -### Structure - -This topic groups interface and UX-oriented caplets planning notes that affect remote control behavior, configuration surfaces, and output presentation. - -### Dependencies - -Related to caplets remote control, CLI inspection polish, and hot reload backend work. - -### Highlights - -Focuses on making caplets easier to use and understand through clearer control surfaces and better output selection. diff --git a/.brv/context-tree/architecture/remote_control/caplets_remote_control_module.md b/.brv/context-tree/architecture/remote_control/caplets_remote_control_module.md deleted file mode 100644 index eb959474..00000000 --- a/.brv/context-tree/architecture/remote_control/caplets_remote_control_module.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -title: Caplets Remote Control Module -summary: Documents the remote control module selection, API shape, context propagation, environment variable interface, and working module behavior for caplets. -tags: [] -related: [] -keywords: [] -createdAt: '2026-05-20T12:35:54.751Z' -updatedAt: '2026-05-20T12:35:54.751Z' ---- - -## Reason - -Curate remote control module notes from the provided context - -## Raw Concept - -**Task:** -Document the caplets remote control module and its related implementation notes - -**Changes:** -- Captured remote mode selection guidance -- Captured API shape and context module behavior -- Captured unified environment variable interface and working module notes - -**Flow:** -CLI remote mode selection -> build remote control context -> expose API shape -> configure unified environment variables -> execute working module - -**Timestamp:** 2026-05-20T12:35:48.316Z - -## Narrative - -### Structure - -The context groups several remote-control-related notes under architecture/remote_control, including CLI mode selection, context handling, API shape, environment variable unification, and working module guidance. - -### Dependencies - -These notes depend on shared remote authentication/state ownership decisions and on the broader caplets remote-control architecture. - -### Highlights - -The material emphasizes a unified interface for remote control and captures working-module expectations alongside spec-compliance review notes. diff --git a/.brv/context-tree/architecture/remote_control/cli_remote_mode_selection.md b/.brv/context-tree/architecture/remote_control/cli_remote_mode_selection.md deleted file mode 100644 index 6b55843b..00000000 --- a/.brv/context-tree/architecture/remote_control/cli_remote_mode_selection.md +++ /dev/null @@ -1,64 +0,0 @@ ---- -title: CLI Remote Mode Selection -summary: Remote control uses CAPLETS_MODE with auto/local/remote selection, independent server URL and credentials, and remote mode coordinates server-owned state and auth flows. -tags: [] -related: [architecture/remote_control/working_module.md, architecture/remote_control/unified_environment_variable_interface.md, architecture/remote_control/remote_control_api_shape.md, architecture/auth/remote_auth_and_state_ownership.md, architecture/remote_control/context.md] -keywords: [] -createdAt: '2026-05-20T11:09:24.904Z' -updatedAt: '2026-05-20T11:32:37.464Z' ---- - -## Reason - -Curate remote control mode selection and execution rules from context - -## Raw Concept - -**Task:** -Document CLI remote mode selection and working module behavior - -**Changes:** -- Removed dependency on CAPLETS_NATIVE_MODE for CLI routing -- Established CAPLETS_REMOTE_URL as the remote-mode trigger -- Recorded command-level routing exceptions and remote-capable commands -- Introduced CAPLETS_CLI_MODE as separate CLI-specific mode control -- Preserved remote connection settings independently via CAPLETS_REMOTE_URL, CAPLETS_REMOTE_USER, and CAPLETS_REMOTE_PASSWORD -- Defined routing rules for auto, local, and remote CLI modes -- Clarified that CAPLETS_NATIVE_MODE is not used for CLI routing -- CAPLETS_MODE is the selector for auto/local/remote mode selection -- CAPLETS_SERVER_URL and credentials are independent and required for remote mode -- Remote mode uses server-owned state and the local client coordinates auth flows - -**Flow:** -select mode -> resolve server URL and credentials -> if remote, coordinate auth flow with server-owned state -> execute remote control operations - -**Timestamp:** 2026-05-20T11:32:32.346Z - -**Patterns:** -- `^CAPLETS_CLI_MODE=(auto|local|remote)$` - Valid CLI mode values -- `^CAPLETS_REMOTE_URL=.+$` - Remote backend URL configuration - -## Narrative - -### Structure - -The remote control architecture separates mode selection from server configuration and auth handling. The client selects auto, local, or remote via CAPLETS_MODE, while CAPLETS_SERVER_URL and credentials are handled independently. - -### Dependencies - -Remote mode depends on server availability, explicit server URL configuration, and credentials. The client must not expose tokens or secrets. - -### Highlights - -Remote control API is command-semantic via structured /control endpoints, not raw CLI-string execution on the server. Remote mode owns durable state on the server side. - -### Rules - -1. CAPLETS_CLI_MODE=auto or unset: use remote for remote-capable commands when CAPLETS_REMOTE_URL is set; otherwise use local. -2. CAPLETS_CLI_MODE=local: always use local, even if CAPLETS_REMOTE_URL is set. -3. CAPLETS_CLI_MODE=remote: require CAPLETS_REMOTE_URL and fail fast with a clear config error if missing. -4. CAPLETS_NATIVE_MODE remains native-integration-specific for OpenCode/Pi and is not used for CLI routing. - -### Examples - -Examples include CAPLETS_CLI_MODE=local caplets list to keep remote settings but force local, CAPLETS_CLI_MODE=remote caplets list to force remote, and caplets list using remote automatically when CAPLETS_REMOTE_URL is set. diff --git a/.brv/context-tree/architecture/remote_control/context.md b/.brv/context-tree/architecture/remote_control/context.md deleted file mode 100644 index 060d3341..00000000 --- a/.brv/context-tree/architecture/remote_control/context.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -related: [architecture/remote_control/remote_control_api_shape.md, architecture/remote_control/cli_remote_mode_selection.md, architecture/remote_control/unified_environment_variable_interface.md, architecture/remote_control/working_module.md] ---- - -# Topic: remote_control - -## Overview - -Covers the remote control API used to drive caplets operations through structured commands and standardized responses. - -## Key Concepts - -- command-semantic API -- structured request envelope -- structured response envelope -- Basic Auth admin protection -- multi-step auth login diff --git a/.brv/context-tree/architecture/remote_control/context_module.md b/.brv/context-tree/architecture/remote_control/context_module.md deleted file mode 100644 index 7d38b901..00000000 --- a/.brv/context-tree/architecture/remote_control/context_module.md +++ /dev/null @@ -1,65 +0,0 @@ ---- -title: Context Module -summary: Context module composes shared remote control state, merges environment-driven values with defaults, and exposes normalized values for CLI and remote mode workflows. -tags: [] -related: [architecture/auth/remote_auth_and_state_ownership.md, architecture/remote_control/cli_remote_mode_selection.md, architecture/remote_control/remote_control_api_shape.md, architecture/remote_control/unified_environment_variable_interface.md, architecture/remote_control/working_module.md] -keywords: [] -createdAt: '2026-05-20T12:15:33.272Z' -updatedAt: '2026-05-20T12:20:25.975Z' ---- - -## Reason - -Curate remote control context module responsibilities and state handling - -## Raw Concept - -**Task:** -Document the remote control context module and how it composes state from environment and defaults. - -**Changes:** -- Captured the unified environment variable interface for mode selection -- Recorded remote mode state ownership and auth flow boundaries -- Documented the structured remote control API shape and endpoint set -- Clarified that CAPLETS_MODE selects auto/local/remote -- Documented that remote credentials are independent from server URL -- Captured server-owned state and local auth coordination boundaries -- Recorded the structured /control API shape and HTTP service routes -- Defines shared state fields for remote control coordination -- Normalizes environment-backed values before use -- Supports both CLI-driven and remote-mode workflows - -**Flow:** -load defaults -> merge environment values -> normalize shared state -> expose context to consumers - -**Timestamp:** 2026-05-20T12:20:20.089Z - -**Author:** ByteRover context engineering notes - -## Narrative - -### Structure - -The context module sits under remote_control as the shared state layer used by the CLI and remote control paths. It centralizes environment-derived configuration and keeps the consumed shape consistent for downstream logic. - -### Dependencies - -Depends on environment configuration and default remote control settings. It is paired with the working module and the CLI remote mode selection logic. - -### Highlights - -The main responsibility is to keep remote control state coherent across local and remote execution paths while avoiding ad hoc environment parsing in callers. - -### Rules - -User explicitly said pre-1.0, so breaking changes/backward compatibility are not a concern. Remote mode uses server-owned state; local client only coordinates auth flows and must never expose tokens/secrets. - -### Examples - -Example routes include /healthz, /mcp, /control, and optional /control/auth/callback/:flowId. CAPLETS_MODE can be auto, local, or remote. - -## Facts - -- **context_module_ownership**: The context module owns shared remote control state composition and normalization. [project] -- **environment_merge_behavior**: Environment values are merged with defaults before being exposed to callers. [project] -- **normalized_values_usage**: The module surfaces normalized values for CLI and remote mode workflows. [project] diff --git a/.brv/context-tree/architecture/remote_control/remote_control_api_shape.md b/.brv/context-tree/architecture/remote_control/remote_control_api_shape.md deleted file mode 100644 index 11693859..00000000 --- a/.brv/context-tree/architecture/remote_control/remote_control_api_shape.md +++ /dev/null @@ -1,71 +0,0 @@ ---- -title: Remote Control API Shape -summary: Remote control API shape with unified environment selection, CLI remote mode resolution, and working/context modules for routing remote actions. -tags: [] -related: [architecture/remote_control/working_module.md, architecture/remote_control/unified_environment_variable_interface.md, architecture/remote_control/cli_remote_mode_selection.md, architecture/auth/remote_auth_and_state_ownership.md, architecture/remote_control/context.md, architecture/remote_control/cli_remote_mode_selection.md, architecture/remote_control/unified_environment_variable_interface.md, architecture/remote_control/context_module.md, architecture/remote_control/working_module.md] -keywords: [] -createdAt: '2026-05-20T11:07:38.964Z' -updatedAt: '2026-05-20T15:42:10.341Z' ---- - -## Reason - -Curate remote control API shape, environment selection, and routing behavior from the provided RLM context. - -## Raw Concept - -**Task:** -Document the remote control API shape and its related execution model. - -**Changes:** -- Defined a command-semantic /control endpoint -- Specified structured request and response envelopes -- Outlined a multi-step auth login flow -- Captured API shape details -- Recorded command routing and ownership rules -- Preserved execution and state-handling behavior -- Captured the remote control API shape and surrounding module responsibilities. -- Recorded CLI remote mode selection and unified environment variable interface behavior. -- Preserved task-specific review findings related to context and working modules. - -**Files:** -- caplets/github/CAPLET.md -- caplets/github-cli/CAPLET.md -- caplets/linear/CAPLET.md -- caplets/repo-cli/CAPLET.md -- caplets/context7.md - -**Flow:** -CLI input -> remote mode selection -> context/working module resolution -> remote action routing - -**Timestamp:** 2026-05-20T15:42:02.926Z - -## Narrative - -### Structure - -This topic groups the remote control API shape together with selection and context-handling modules that determine how remote actions are routed. - -### Dependencies - -Depends on CLI remote mode selection, unified environment variable handling, and the context/working module split. - -### Highlights - -Captures the architectural shape of remote control behavior and the supporting module responsibilities in the caplets system. - -### Rules - -No remote request is ever “run this CLI string”. -No shelling out to `caplets` on the server. - -### Examples - -Example request: POST /control { command: "list_tools", arguments: {} } -Example login flow: auth_login_start returns authorizationUrl and flowId, then auth_login_complete exchanges credentials. - -## Facts - -- **remote_mode_selection**: Remote mode selection is resolved through CLI remote mode selection logic. [project] -- **environment_variable_interface**: The remote control layer uses a unified environment variable interface. [project] -- **remote_control_modules**: Context and working modules are part of the remote control architecture. [project] diff --git a/.brv/context-tree/architecture/remote_control/remote_control_context.md b/.brv/context-tree/architecture/remote_control/remote_control_context.md deleted file mode 100644 index b8d53ec9..00000000 --- a/.brv/context-tree/architecture/remote_control/remote_control_context.md +++ /dev/null @@ -1,83 +0,0 @@ ---- -title: Remote Control Context -summary: Remote control architecture covering context, API shape, environment variable interface, working module, and interface UX. -tags: [] -related: [architecture/auth/remote_auth_and_state_ownership.md, architecture/remote_control/cli_remote_mode_selection.md, architecture/remote_control/remote_control_api_shape.md, architecture/remote_control/unified_environment_variable_interface.md, architecture/remote_control/working_module.md, architecture/remote_control/context.md, architecture/remote_control/context_module.md, architecture/remote_control/caplets_remote_control_module.md, architecture/remote_control/task_1_spec_compliance_review.md] -keywords: [] -createdAt: '2026-05-20T12:30:03.683Z' -updatedAt: '2026-05-20T13:17:52.974Z' ---- - -## Reason - -Curate remote control architecture knowledge from the provided context - -## Raw Concept - -**Task:** -Document the remote control architecture knowledge captured in the context tree. - -**Changes:** -- Defined CAPLETS_MODE as the selector for auto, local, and remote execution modes -- Clarified that CAPLETS_SERVER_URL and credentials are required independently for remote mode -- Recorded the remote HTTP service default endpoint and route surface -- Preserved the pre-1.0 compatibility stance -- Captured the remote control context module and its supporting design notes -- Recorded unified environment variable interface details -- Documented CLI remote mode selection and API shape considerations -- Captured remote control module topics -- Captured CLI remote mode selection behavior -- Captured unified environment variable interface and working module details -- Captured remote auth and state ownership notes -- Captured remote control context as durable knowledge -- Included mode selection, context boundaries, and API shape -- Recorded working module behavior and spec compliance review -- Documented remote control API shape and context handling -- Preserved remote auth and state ownership knowledge -- Captured the remote control module family and its related implementation concerns -- Captured the context module and related remote control architecture topics -- Preserved references to API shape, environment variable interface, and working module -- Recorded interface UX and auth ownership relationships - -**Files:** -- caplets/github/CAPLET.md -- caplets/github-cli/CAPLET.md -- caplets/linear/CAPLET.md -- caplets/repo-cli/CAPLET.md -- caplets/context7.md - -**Flow:** -remote control context -> API shape -> environment variable interface -> working module -> interface UX - -**Timestamp:** 2026-05-20 - -**Author:** ByteRover context engineering - -## Narrative - -### Structure - -This topic groups the remote control architecture, including the context module, API shape, working module, and unified environment variable interface. - -### Dependencies - -Relates to auth ownership and interface UX documentation already present in the architecture domain. - -### Highlights - -The context emphasizes how remote control functionality is organized and how the interface and environment variable interface fit into the design. - -### Rules - -User explicitly said pre-1.0, so breaking changes/backward compatibility are not a concern. - -### Examples - -Remote mode usage is governed by CAPLETS_MODE, while CAPLETS_SERVER_URL and credentials remain separate inputs. - -## Facts - -- **context_module**: Caplets supports a context module for remote control and related workflows. [project] -- **remote_control_api**: The remote control API shape is documented as part of the architecture knowledge. [project] -- **environment_variable_interface**: The curated knowledge includes a unified environment variable interface for remote control. [project] -- **working_module**: A working module is documented alongside remote control architecture. [project] diff --git a/.brv/context-tree/architecture/remote_control/task_1_spec_compliance_review.md b/.brv/context-tree/architecture/remote_control/task_1_spec_compliance_review.md deleted file mode 100644 index e6c09111..00000000 --- a/.brv/context-tree/architecture/remote_control/task_1_spec_compliance_review.md +++ /dev/null @@ -1,55 +0,0 @@ ---- -title: Task 1 Spec Compliance Review -summary: 'Task 1 spec compliance was approved after fixes: object-union mode resolution, IPv6 loopback bracket support, and env-based tests for mode and credentials.' -tags: [] -related: [architecture/remote_control/remote_control_api_shape.md, architecture/remote_control/unified_environment_variable_interface.md, architecture/remote_control/working_module.md] -keywords: [] -createdAt: '2026-05-20T12:19:17.755Z' -updatedAt: '2026-05-20T12:19:17.755Z' ---- - -## Reason - -Preserve the approved verification outcome and linked implementation details - -## Raw Concept - -**Task:** -Document the accepted compliance review for Task 1 - -**Changes:** -- Verified resolveCapletsMode returns an object union -- Verified IPv6 loopback bracket form is accepted -- Verified env-based tests cover CAPLETS_MODE and CAPLETS_SERVER_USER/PASSWORD behavior - -**Files:** -- packages/core/src/server/options.ts -- packages/core/test/server-options.test.ts - -**Flow:** -Review fixes -> confirm implementation behavior -> confirm tests -> return APPROVED - -**Timestamp:** 2026-05-20T12:18:51.566Z - -## Narrative - -### Structure - -This record captures the final verification outcome for Task 1 and ties it to the implementation and test files cited in the review. - -### Dependencies - -Depends on the server options implementation and its test coverage. - -### Highlights - -All cited concerns were addressed and the verification run passed. - -## Facts - -- **task_1_review_status**: Re-review of Task 1 spec compliance after fixes returned APPROVED. [project] -- **resolve_caplets_mode_return_type**: resolveCapletsMode returns an object union. [project] -- **ipv6_loopback_bracket_support**: IPv6 loopback bracket form is accepted. [project] -- **caplets_mode_env_tests**: Env-based tests cover CAPLETS_MODE behavior. [project] -- **caplets_server_credentials_env_tests**: Env-based tests cover CAPLETS_SERVER_USER and CAPLETS_SERVER_PASSWORD behavior. [project] -- **server_options_test_run**: Verification run passed 364 tests for packages/core server options test suite. [project] diff --git a/.brv/context-tree/architecture/remote_control/unified_environment_variable_interface.md b/.brv/context-tree/architecture/remote_control/unified_environment_variable_interface.md deleted file mode 100644 index 83908bbd..00000000 --- a/.brv/context-tree/architecture/remote_control/unified_environment_variable_interface.md +++ /dev/null @@ -1,79 +0,0 @@ ---- -title: Unified Environment Variable Interface -summary: Unified Caplets env interface uses CAPLETS_MODE plus CAPLETS_SERVER_* vars, with deprecated aliases and explicit precedence rules. -tags: [] -related: [architecture/remote_control/remote_control_api_shape.md, architecture/remote_control/cli_remote_mode_selection.md, architecture/remote_control/working_module.md, architecture/auth/remote_auth_and_state_ownership.md, architecture/remote_control/context.md] -keywords: [] -createdAt: '2026-05-20T11:12:05.554Z' -updatedAt: '2026-05-20T11:12:05.554Z' ---- - -## Reason - -Document the agreed unified environment variable model for Caplets CLI, native integrations, and serve/client modes - -## Raw Concept - -**Task:** -Document the unified environment variable interface for Caplets - -**Changes:** -- Replaced CAPLETS_NATIVE_* with CAPLETS_MODE and CAPLETS_SERVER_* variables -- Defined resolution rules for auto, local, and remote modes -- Added backward-compatible alias mappings for one release path -- Specified env-to-CLI derivation for caplets serve and client mode - -**Files:** -- caplets/github/README.md -- caplets/github-cli/CAPLET.md - -**Flow:** -config/options -> unified env vars -> deprecated aliases -> defaults - -**Timestamp:** 2026-05-20T11:11:45.922Z - -**Patterns:** -- `^CAPLETS_MODE=(auto|local|remote)$` - Allowed unified mode values -- `^CAPLETS_SERVER_URL=.+$` - Server URL setting used by serve and client mode - -## Narrative - -### Structure - -The design centralizes all runtime selection around CAPLETS_MODE and a single CAPLETS_SERVER_* namespace, while preserving old variables as deprecated aliases. - -### Dependencies - -Relies on explicit host config or CLI options taking highest precedence, with environment variables filling in defaults for serving and client behavior. - -### Highlights - -The model simplifies configuration, keeps backward compatibility for one release path, and derives control/MCP endpoints from the same base server URL. - -### Rules - -1. CAPLETS_MODE=auto or unset uses remote/client mode when CAPLETS_SERVER_URL is set, otherwise local mode. -2. CAPLETS_MODE=local always forces local mode, even if server settings exist. -3. CAPLETS_MODE=remote requires CAPLETS_SERVER_URL and fails fast if missing. -4. If both old and new env vars are present, the new unified variable wins. -5. Explicit CLI flags still override env values for caplets serve. - -## Facts - -- **caplets_mode**: CAPLETS_MODE is the single mode selector and can be auto, local, or remote. [project] -- **caplets_server_url**: CAPLETS_SERVER_URL is used by caplets serve and caplets client mode. [project] -- **caplets_server_auth**: CAPLETS_SERVER_USER and CAPLETS_SERVER_PASSWORD provide Basic Auth credentials for serving and client/server configuration. [project] -- **deprecated_alias_native_mode**: CAPLETS_NATIVE_MODE maps to CAPLETS_MODE as a deprecated alias. [project] -- **deprecated_alias_remote_url**: CAPLETS_REMOTE_URL maps to CAPLETS_SERVER_URL as a deprecated alias. [project] -- **deprecated_alias_remote_user**: CAPLETS_REMOTE_USER maps to CAPLETS_SERVER_USER as a deprecated alias. [project] -- **deprecated_alias_remote_password**: CAPLETS_REMOTE_PASSWORD maps to CAPLETS_SERVER_PASSWORD as a deprecated alias. [project] -- **caplets_mode_resolution**: When CAPLETS_MODE is auto or unset, remote/client mode is used if CAPLETS_SERVER_URL is set; otherwise local mode is used. [project] -- **caplets_local_override**: When CAPLETS_MODE is local, local mode is used even when server settings exist. [project] -- **caplets_remote_requirement**: When CAPLETS_MODE is remote, CAPLETS_SERVER_URL is required and missing it is a fast failure. [project] -- **caplets_precedence**: Precedence is explicit host config/options, then new unified env vars, then deprecated aliases, then defaults. [project] -- **caplets_env_conflict_resolution**: If both old and new env vars are present, the new unified variable wins. [project] -- **caplets_deprecation_warning**: The CLI and native integrations may warn once when deprecated aliases are used. [project] -- **caplets_serve_url_derivation**: caplets serve --transport http can derive host, port, and path from CAPLETS_SERVER_URL. [project] -- **caplets_cli_override**: Explicit CLI flags override environment variables for caplets serve. [project] -- **caplets_control_endpoint**: Client mode derives MCP endpoint from CAPLETS_SERVER_URL and control endpoint from the same base URL plus /control. [project] -- **caplets_control_url_escape_hatch**: CAPLETS_CONTROL_URL is an optional future escape hatch for separate control endpoints. [project] diff --git a/.brv/context-tree/architecture/remote_control/working_module.md b/.brv/context-tree/architecture/remote_control/working_module.md deleted file mode 100644 index 03b72dbd..00000000 --- a/.brv/context-tree/architecture/remote_control/working_module.md +++ /dev/null @@ -1,62 +0,0 @@ ---- -title: Working Module -summary: Working module notes for caplets, highlighting active curation, included caplet areas, and durable-note separation practice. -tags: [] -related: [architecture/remote_control/unified_environment_variable_interface.md, architecture/remote_control/cli_remote_mode_selection.md, architecture/remote_control/remote_control_api_shape.md, architecture/auth/remote_auth_and_state_ownership.md, architecture/remote_control/context.md] -keywords: [] -createdAt: '2026-05-20T11:16:46.330Z' -updatedAt: '2026-05-20T13:22:13.573Z' ---- - -## Reason - -Curate the working module notes from the provided RLM context - -## Raw Concept - -**Task:** -Document the working module for caplets and its curation status. - -**Changes:** -- Captured the current working module notes for durable knowledge. -- Recorded the spec path and commit identifier. -- Recorded the self-review status and format check blocker. -- Captured the command-semantic control model for remote orchestration -- Recorded the unified environment variable interface for mode selection and remote credentials -- Preserved remote auth and server-owned state constraints -- Identified the working module as actively curated during curate sessions -- Captured included caplet areas: GitHub, GitHub CLI, Linear, Repo CLI, and Context7 -- Recorded the practice of separating durable notes from raw source snippets - -**Flow:** -curate session -> inspect working module notes -> separate durable notes from source snippets -> preserve curated knowledge - -**Timestamp:** 2026-05-20T13:22:08.097Z - -## Narrative - -### Structure - -This knowledge captures the working module as a curated area related to caplets and their supporting documentation. - -### Dependencies - -Relies on caplets-related documentation and prior curation sessions for continuity. - -### Highlights - -The working module is treated as an actively curated knowledge area, with emphasis on durable notes instead of raw source snippets. - -### Rules - -Remote mode must keep tokens and secrets server-owned. Structured /control endpoints are used instead of raw CLI-string execution. - -### Examples - -Example endpoints include /healthz for liveness, /mcp for MCP access, /control for command semantics, and /control/auth/callback/:flowId for auth callback handling. - -## Facts - -- **caplets_working_module_scope**: The working module for caplets currently includes a caplets package with GitHub, GitHub CLI, linear, repo CLI, and Context7-related documentation. [project] -- **working_module_status**: The working module is actively curated and surfaced during curate sessions. [project] -- **working_module_curation_practice**: Session fff205ed separated durable notes from raw source snippets while curating the working module. [project] diff --git a/.brv/context-tree/facts/conventions/context.md b/.brv/context-tree/facts/conventions/context.md deleted file mode 100644 index 56acf145..00000000 --- a/.brv/context-tree/facts/conventions/context.md +++ /dev/null @@ -1,12 +0,0 @@ -# Topic: conventions - -## Overview - -Covers workflow conventions and guardrails for curated runtime knowledge ingestion. - -## Key Concepts - -- precomputed recon -- single-pass mode -- mapExtract timeout -- verification via applied file paths diff --git a/.brv/context-tree/facts/conventions/curate_runtime_conventions.md b/.brv/context-tree/facts/conventions/curate_runtime_conventions.md deleted file mode 100644 index 81e92b93..00000000 --- a/.brv/context-tree/facts/conventions/curate_runtime_conventions.md +++ /dev/null @@ -1,53 +0,0 @@ ---- -title: Curate Runtime Conventions -summary: Runtime curation conventions for RLM workflow, precomputed recon usage, no raw context printing, and verification via applied file paths. -tags: [] -related: [facts/project/project_knowledge_notes.md] -keywords: [] -createdAt: '2026-05-20T13:18:52.099Z' -updatedAt: '2026-05-20T15:10:55.420Z' ---- - -## Reason - -Capture the runtime curation workflow and verification rules from the provided RLM context. - -## Raw Concept - -**Task:** -Document the runtime conventions for curating context with the RLM approach. - -**Changes:** -- Captured the precomputed recon workflow -- Recorded single-pass handling for small contexts -- Recorded timeout and verification requirements for mapExtract and curate -- Established recon precomputation as the starting point -- Defined single-pass as the default for small contexts -- Specified chunked extraction requirements when needed -- Use precomputed recon results instead of recomputing reconnaissance. -- Proceed directly to extraction for single-pass contexts. -- Use mapExtract only when chunked extraction is needed. -- Verify curation through result.applied[].filePath rather than readFile. - -**Flow:** -recon precomputed -> extract facts -> curate UPSERT -> verify applied file paths -> record progress - -**Timestamp:** 2026-05-20T15:10:46.102Z - -## Narrative - -### Structure - -The guidance describes an RLM curation workflow with a single-pass path for small contexts and chunked extraction for larger ones. - -### Dependencies - -Depends on precomputed recon variables, the curate tool, and optional curation helpers such as mapExtract, groupBySubject, and dedup. - -### Highlights - -The context explicitly forbids printing raw context, prefers direct extraction, and requires verification through the curate result object. - -### Rules - -IMPORTANT: Do NOT print raw context. Do NOT call tools.curation.recon — it has been precomputed. Proceed directly to extraction. For chunked extraction use tools.curation.mapExtract(). Pass taskId: __taskId_57b46e01_4a31_431c_8d92_e7ea01360858 (bare variable, not a string). Verify via result.applied[].filePath — do NOT call readFile for verification. diff --git a/.brv/context-tree/facts/conventions/curation_runtime_conventions.md b/.brv/context-tree/facts/conventions/curation_runtime_conventions.md deleted file mode 100644 index abd4bad7..00000000 --- a/.brv/context-tree/facts/conventions/curation_runtime_conventions.md +++ /dev/null @@ -1,65 +0,0 @@ ---- -title: Curation Runtime Conventions -summary: 'Defines the RLM curation workflow: recon, single-pass handling for small contexts, mapExtract for chunked contexts, UPSERT-based curation, and verification via applied file paths and result.summary.' -tags: [] -related: [architecture/remote_control/context.md] -keywords: [] -createdAt: '2026-05-20T12:47:22.778Z' -updatedAt: '2026-05-20T13:09:19.427Z' ---- - -## Reason - -Capture runtime curation workflow and verification conventions from context - -## Raw Concept - -**Task:** -Document the runtime curation conventions used for RLM processing - -**Changes:** -- Identified single-pass mode as the recommended approach -- Recorded mapExtract taskId passing requirement -- Recorded code_exec timeout requirement for mapExtract calls -- Captured required tool-use constraints -- Recorded RLM single-pass guidance -- Preserved verification and context-handling rules -- Confirmed the context should be handled in single-pass mode. -- Preserved the requirement to pass taskId as a bare variable when using mapExtract. -- Captured the verification rule to check applied file paths instead of reading files back. -- Recorded the recon -> single-pass or chunked extraction workflow -- Captured the preferred UPSERT curation pattern -- Preserved the verification rule using applied file paths and result.summary - -**Flow:** -recon -> choose single-pass or chunked extraction -> curate with UPSERT -> verify applied file paths and summary - -**Timestamp:** 2026-05-20T13:09:12.305Z - -**Author:** ByteRover context engineer - -## Narrative - -### Structure - -Curation runtime conventions are organized as procedural guidance for how to process context, extract facts, and apply knowledge tree updates. - -### Dependencies - -Relies on the RLM curation workflow, tools.curation.recon, tools.curation.mapExtract, tools.curation.dedup, tools.curation.groupBySubject, and tools.curate. - -### Highlights - -Small contexts should be handled in single-pass mode. Chunked contexts require mapExtract, deduplication, grouping by subject, and verification through applied file paths. - -### Rules - -IMPORTANT: Do NOT print raw context. Do NOT call tools.curation.recon when it has been precomputed. For chunked extraction use tools.curation.mapExtract(). Pass taskId as a bare variable, not a string. Use tools.curation.groupBySubject() and tools.curation.dedup() to organize extractions. Verify via result.applied[].filePath — do NOT call readFile for verification. - -## Facts - -- **curation_workflow**: Use recon as the first step in RLM curation workflow. [convention] -- **curation_mode**: For small contexts, suggestedMode single-pass should skip chunking. [convention] -- **chunked_extraction**: For chunked contexts, use mapExtract with taskId passed as a bare variable and timeout 300000 on the code_exec call. [convention] -- **curate_operation**: Use UPSERT as the preferred curation operation. [convention] -- **verification**: Verify curation via result.applied[].filePath and result.summary.failed. [convention] diff --git a/.brv/context-tree/facts/conventions/rlm_curate_runtime_conventions.md b/.brv/context-tree/facts/conventions/rlm_curate_runtime_conventions.md deleted file mode 100644 index fcc126d3..00000000 --- a/.brv/context-tree/facts/conventions/rlm_curate_runtime_conventions.md +++ /dev/null @@ -1,77 +0,0 @@ ---- -title: RLM Curate Runtime Conventions -summary: RLM curation conventions for single-pass handling, mapExtract timeout requirements, and verification via applied file paths -tags: [] -related: [facts/conventions/context.md] -keywords: [] -createdAt: '2026-05-20T14:24:21.257Z' -updatedAt: '2026-05-20T17:58:07.189Z' ---- -## Reason -Record the runtime conventions for RLM curation execution - -## Raw Concept -**Task:** -Document RLM curation runtime conventions for handling precomputed recon and extraction flow - -**Changes:** -- Use single-pass mode when recon already recommends it -- Keep extraction timeout at 300000 for mapExtract calls made from code_exec -- Verify curation via applied file paths instead of readFile -- Use precomputed recon instead of recalculating it -- Proceed directly to extraction when suggestedMode is single-pass -- Verify curate output via applied file paths -- Use recon first, then choose single-pass or chunked extraction based on suggestedMode -- For chunked contexts, use tools.curation.mapExtract() with taskId passed as a bare variable -- Verify curation via result.applied[].filePath and result.summary.failed === 0 -- Do not print raw context during curation -- Established that precomputed recon should be trusted -- Defined single-pass flow for small contexts -- Captured timeout and taskId requirements for mapExtract -- Recorded verification constraints for curate results -- Precomputed recon is available and must be reused. -- Single-pass mode is recommended for this small context. -- Verification must use applied file paths rather than readFile checks. -- Use recon-precomputed single-pass processing when suggestedMode is single-pass -- Use mapExtract with a 300000ms code_exec timeout when chunked extraction is needed -- Pass taskId as a bare variable for mapExtract calls -- Verify curated files through result.applied[].filePath rather than readFile -- Use the precomputed recon result instead of recomputing it -- Proceed directly to extraction in single-pass mode -- Pass the task ID as a bare variable when using mapExtract -- Verify curation through result.applied file paths rather than readFile -- Single-pass recon results should bypass chunking -- mapExtract timeout requirement is 300000 for code_exec calls -- Verification should rely on applied file paths rather than readFile - -**Files:** -- .brv/context-tree/facts/conventions/rlm_curate_runtime_conventions.md - -**Flow:** -precomputed recon -> single-pass decision -> direct curate -> verify applied file paths - -**Timestamp:** 2026-05-20T17:58:00.375Z - -**Author:** ByteRover context engineer - -## Narrative -### Structure -Captures the runtime rules governing RLM curation execution and verification. - -### Dependencies -Depends on precomputed recon metadata and the curate result payload. - -### Highlights -This task is single-pass and should not invoke recon again. The execution should preserve the verification rule that uses applied file paths. - -### Rules -IMPORTANT: Do NOT print raw context. Do NOT call tools.curation.recon — it has been precomputed. Proceed directly to extraction. For chunked extraction use tools.curation.mapExtract(). Pass taskId: __taskId_29a79f97_36be_4187_b8a0_0e24253814f2 (bare variable, not a string). Use tools.curation.groupBySubject() and tools.curation.dedup() to organize extractions. Verify via result.applied[].filePath — do NOT call readFile for verification. - -### Examples -The current recon result suggests single-pass mode with charCount 1472, lineCount 25, and messageCount 0. - -## Facts -- **rlm_curation_recon_step**: RLM curation tasks must start with tools.curation.recon, but recon was already precomputed for this task. [convention] -- **single_pass_mode**: When recon suggests single-pass, chunking and mapExtract are skipped. [convention] -- **mapextract_timeout**: For chunked extraction, tools.curation.mapExtract must be called with timeout 300000 on the code_exec call itself. [convention] -- **verification_method**: Verification of curation should use result.applied[].filePath and not readFile. [convention] diff --git a/.brv/context-tree/facts/conventions/rlm_curation_runtime_conventions.md b/.brv/context-tree/facts/conventions/rlm_curation_runtime_conventions.md deleted file mode 100644 index 8726caf0..00000000 --- a/.brv/context-tree/facts/conventions/rlm_curation_runtime_conventions.md +++ /dev/null @@ -1,75 +0,0 @@ ---- -title: RLM Curation Runtime Conventions -summary: Runtime conventions for RLM curation, including recon-first workflow, single-pass handling for small contexts, mapExtract chunking for larger contexts, and verification via applied file paths. -tags: [] -related: [facts/conventions/context.md] -keywords: [] -createdAt: '2026-05-20T13:23:05.390Z' -updatedAt: '2026-05-20T18:39:31.988Z' ---- -## Reason -Document runtime curation constraints and workflow requirements from the current RLM context. - -## Raw Concept -**Task:** -Curate RLM approach instructions for context-driven curation. - -**Changes:** -- Established single-pass curation for the current context. -- Captured the required variable names for context, history, metadata, and task ID. -- Recorded verification guidance to use applied file paths rather than readFile. -- Established recon-first workflow -- Defined single-pass handling for small contexts -- Specified mapExtract timeout and taskId handling -- Defined verification using applied file paths -- Single-pass mode should be used when recon suggests it. -- Reconstruction step is already precomputed and should not be repeated. -- Verification should use applied file paths rather than readFile. -- Established recon-first decision flow -- Defined chunked mapExtract handling for larger contexts -- Standardized UPSERT-based curation and verification -- Specified that recon is mandatory before curation when mode is not already known. -- Defined single-pass handling as a two-call flow. -- Defined chunked handling with mapExtract, deduplication, and grouping. -- Defined verification by applied file paths and zero failed operations. -- Defined single-pass handling for small contexts. -- Recorded chunked extraction requirements for mapExtract. -- Recorded verification and status reporting expectations. -- Use precomputed recon when available -- Skip chunking in single-pass mode -- Pass taskId as a bare variable to mapExtract -- Verify results through applied file paths -- Recorded that recon is precomputed and single-pass is the recommended mode for this context. -- Captured the requirement to avoid printing raw context during curation. -- Captured the taskId and timeout requirements for mapExtract-based extraction. -- Use pre-computed recon when available and proceed directly to extraction. -- Single-pass mode is appropriate for the 1402-character, 31-line context. -- Use mapExtract with taskId only when chunking is required. -- Verify curation via result.applied[].filePath without readFile-based verification. - -**Files:** -- .brv/context-tree/ -- .brv/context-tree/facts/conventions/ - -**Flow:** -recon -> extract -> dedup/group -> curate -> verify - -**Timestamp:** 2026-05-20T18:39:26.083Z - -**Author:** ByteRover context engineer - -**Patterns:** -- `^300000$` - Required timeout value for code_exec calls containing mapExtract - -## Narrative -### Structure -The instructions define an RLM curation workflow with a pre-computed recon result, a single-pass recommendation, and explicit verification guidance. - -### Dependencies -Depends on sandbox variables for context, history, metadata, and taskId; uses tools.curation helpers and tools.curate. - -### Highlights -Emphasizes not printing raw context, not calling recon again, and using groupBySubject and dedup when extraction results need organization. - -### Rules -IMPORTANT: Do NOT print raw context. Do NOT call tools.curation.recon — it has been precomputed. Proceed directly to extraction. For chunked extraction use tools.curation.mapExtract(). Pass taskId as a bare variable. Any code_exec call containing mapExtract MUST use timeout: 300000 on the code_exec tool call itself. Verify via result.applied[].filePath — do NOT call readFile for verification. diff --git a/.brv/context-tree/facts/conventions/task_3_spec_review_fix.md b/.brv/context-tree/facts/conventions/task_3_spec_review_fix.md deleted file mode 100644 index 80feada5..00000000 --- a/.brv/context-tree/facts/conventions/task_3_spec_review_fix.md +++ /dev/null @@ -1,45 +0,0 @@ ---- -title: Task 3 Spec Review Fix -summary: 'Task 3 fix: IPv6 loopback serve URLs now bind with ::1, CLI help text updated, focused tests passed, commit cdde479' -tags: [] -related: [] -keywords: [] -createdAt: '2026-05-20T12:56:28.017Z' -updatedAt: '2026-05-20T12:56:28.017Z' ---- - -## Reason - -Record the implementation outcome, tests, and commit for the spec review fix - -## Raw Concept - -**Task:** -Document the Task 3 spec review fix outcome for serve binding and CLI help text. - -**Changes:** -- Adjusted IPv6 loopback handling for CAPLETS_SERVER_URL -- Updated CLI help text wording -- Ran focused tests and recorded successful results - -**Flow:** -spec review finding -> implementation fix -> focused tests -> commit - -**Timestamp:** 2026-05-20T12:56:17.437Z - -## Narrative - -### Structure - -This note captures the final outcome of the Task 3 spec review fix, including the binding behavior change, help text update, test coverage, and commit identifier. - -### Highlights - -The fix ensures IPv6 loopback URLs bind correctly as ::1, and the CLI help copy now matches the service base path wording. - -## Facts - -- **serve_ipv6_loopback_binding**: CAPLETS_SERVER_URL http://[::1]:5387/caplets should treat IPv6 loopback as loopback for serve binding and return host ::1 with path /caplets without requiring auth or allow flag. [project] -- **cli_help_text**: CLI help text in packages/core/src/cli.ts was updated from "HTTP MCP endpoint path" to the new service base path wording. [project] -- **focused_tests**: Focused tests passed for packages/core/test/serve-options.test.ts, packages/core/test/serve-http.test.ts, and packages/core/test/cli.test.ts. [project] -- **commit_sha**: The fix was committed as cdde479 with message "fix(serve): bind IPv6 loopback server URLs". [project] diff --git a/.brv/context-tree/facts/conventions/task_5_regression_test_update.md b/.brv/context-tree/facts/conventions/task_5_regression_test_update.md deleted file mode 100644 index 67dc5132..00000000 --- a/.brv/context-tree/facts/conventions/task_5_regression_test_update.md +++ /dev/null @@ -1,53 +0,0 @@ ---- -title: Task 5 Regression Test Update -summary: Added regression tests for remote add rejecting server-owned fields; tests passed and commit 089542d was created with a note about unrelated worktree changes. -tags: [] -related: [] -keywords: [] -createdAt: '2026-05-20T13:52:39.382Z' -updatedAt: '2026-05-20T13:52:39.382Z' ---- - -## Reason - -Record durable task outcome, tests, commit, and concerns from the conversation - -## Raw Concept - -**Task:** -Add regression tests ensuring remote add rejects options.destinationRoot and options.print as server-owned fields - -**Changes:** -- Added focused regression coverage for server-owned remote add fields -- Ran remote-control-dispatch and serve-http tests -- Created commit 089542d without amending existing history - -**Files:** -- packages/core/test/remote-control-dispatch.test.ts - -**Flow:** -Implement rejection tests -> run targeted tests -> commit test-only change -> report outcome - -**Timestamp:** 2026-05-20T13:52:25.305Z - -## Narrative - -### Structure - -This update records the regression-testing work for the remote add server-owned field check, along with execution results and commit metadata. - -### Dependencies - -Relies on existing implementation behavior that already rejects the fields; tests were the only required change unless implementation needed adjustment. - -### Highlights - -pnpm --filter @caplets/core test -- test/remote-control-dispatch.test.ts test/serve-http.test.ts passed: 32 files, 389 tests. Commit sha: 089542d. - -### Rules - -Return DONE, DONE_WITH_CONCERNS, or BLOCKED and include tests run plus commit sha. The commit should be new and should not amend prior history. - -### Examples - -Concern noted: unrelated staged/untracked .brv and docs files remained in the worktree, but the commit included only the test file. diff --git a/.brv/context-tree/facts/conventions/task_6_review_outcome.md b/.brv/context-tree/facts/conventions/task_6_review_outcome.md deleted file mode 100644 index 5d2bf6a1..00000000 --- a/.brv/context-tree/facts/conventions/task_6_review_outcome.md +++ /dev/null @@ -1,67 +0,0 @@ ---- -title: Task 6 Review Outcome -summary: Task 6 review approved; nested engine request envelope validation is correct, old CAPLETS_REMOTE_* docs belong to Task 10, and source/test verification passed. -tags: [] -related: [] -keywords: [] -createdAt: '2026-05-20T14:17:40.238Z' -updatedAt: '2026-05-20T14:17:40.238Z' ---- - -## Reason - -Capture approved review details and verification results for Task 6 - -## Raw Concept - -**Task:** -Record the Task 6 quality review outcome after operation envelope validation. - -**Changes:** -- Approved the review -- Confirmed nested request.operation validation in dispatch -- Confirmed CLI remote routing env var usage -- Assigned lingering CAPLETS_REMOTE_* docs to Task 10 - -**Files:** -- packages/core/src/remote-control/dispatch.ts -- packages/core/test/remote-control-dispatch.test.ts - -**Flow:** -review request -> validate source/test behavior -> note doc-only cleanup scope -> verify tests and typecheck - -**Timestamp:** 2026-05-20T14:17:26.056Z - -**Author:** assistant - -## Narrative - -### Structure - -This note captures the final review decision plus the specific code and test evidence cited in the approval. - -### Dependencies - -The review depends on dispatch envelope validation, test coverage for missing/mismatched nested operations, and the intended old-env removal scope. - -### Highlights - -Approved with no findings. Doc-only CAPLETS_REMOTE_* references were explicitly deferred to Task 10 because they do not block current source/test correctness. - -### Rules - -Do not edit. Return APPROVED or FINDINGS. - -### Examples - -Examples of verified behavior: missing nested request.operation is rejected; mismatched nested request.operation is rejected; CLI remote routing uses CAPLETS_MODE / CAPLETS_SERVER_URL. - -## Facts - -- **task_6_review_outcome**: Task 6 review outcome was APPROVED after operation envelope validation. [project] -- **remote_control_dispatch_validation**: packages/core/src/remote-control/dispatch.ts validates the nested engine request envelope and rejects missing or mismatched request.operation values. [project] -- **remote_control_dispatch_tests**: packages/core/test/remote-control-dispatch.test.ts covers both missing and mismatched nested operation cases. [project] -- **cli_remote_routing_env_vars**: CLI remote routing uses CAPLETS_MODE and CAPLETS_SERVER_URL only. [project] -- **old_env_docs_task_assignment**: Remaining CAPLETS_REMOTE_* documentation references belong to Task 10 and do not block Task 6 source/test correctness. [project] -- **task_6_test_verification**: Verification passed for pnpm --filter @caplets/core test -- test/cli-remote.test.ts test/remote-control-dispatch.test.ts test/remote-control-client.test.ts test/server-options.test.ts with 33 files and 396 tests. [project] -- **task_6_typecheck_status**: pnpm --filter @caplets/core typecheck passed. [project] diff --git a/.brv/context-tree/facts/conventions/task_7_remote_mutation_routing_review.md b/.brv/context-tree/facts/conventions/task_7_remote_mutation_routing_review.md deleted file mode 100644 index fb22d0b1..00000000 --- a/.brv/context-tree/facts/conventions/task_7_remote_mutation_routing_review.md +++ /dev/null @@ -1,71 +0,0 @@ ---- -title: Task 7 Remote Mutation Routing Review -summary: Task 7 remote mutation routing review was approved; remote add/init/install flows, server-side boundary validation, response parsing, and redaction were verified, with tests and typecheck passing. -tags: [] -related: [architecture/remote_control/context.md, architecture/remote_control/remote_control_api_shape.md] -keywords: [] -createdAt: '2026-05-20T14:35:11.652Z' -updatedAt: '2026-05-20T14:35:11.652Z' ---- - -## Reason - -Document the approved review outcome and verification details for remote mutation routing - -## Raw Concept - -**Task:** -Capture the code review outcome and verified technical findings for Task 7 remote mutation routing. - -**Changes:** -- Approved the remote mutation routing review -- Confirmed request sanitization and command boundary checks -- Confirmed remote response parsing and error redaction -- Recorded passing targeted tests and typecheck - -**Files:** -- packages/core/src/cli.ts -- packages/core/src/remote-control/dispatch.ts -- packages/core/src/remote-control/client.ts -- packages/core/test/cli-remote.test.ts -- packages/core/test/remote-control-dispatch.test.ts - -**Flow:** -review request -> inspect CLI and remote control paths -> verify tests and typecheck -> approve - -**Timestamp:** 2026-05-20T14:34:21.017Z - -**Author:** assistant - -## Narrative - -### Structure - -The review spans CLI routing, server-side dispatch validation, remote client parsing, and targeted test coverage. - -### Dependencies - -Depends on CLI sanitization behavior, dispatch-layer validation, and client-side response handling. - -### Highlights - -No blocking findings were reported. The review explicitly confirmed payload sanitization, boundary enforcement, malformed-response rejection, and redacted error surfacing. - -### Rules - -Return APPROVED or FINDINGS with severity/refs. Do not edit. - -## Facts - -- **task_7_review_outcome**: Task 7 remote mutation routing review was approved. [project] -- **remote_route**: Remote init, install, and all add variants route through /control. [project] -- **stripped_add_fields**: Local-only add fields are stripped before remote requests: global, print, output, destinationRoot. [project] -- **server_command_boundary**: Server-side command boundary validates add option types. [project] -- **destination_root_ownership**: Server owns destination root and rejects output, destinationRoot, and print. [project] -- **engine_command_request_validation**: Engine command requests require nested operation to match the outer command. [project] -- **remote_envelope_parsing**: Remote envelope parsing rejects malformed responses. [project] -- **remote_error_redaction**: Remote error messages are redacted before surfacing. [project] -- **cli_remote_test_status**: Verification ran pnpm --filter @caplets/core test -- test/cli-remote.test.ts and it passed. [project] -- **remote_control_dispatch_test_status**: Verification ran pnpm --filter @caplets/core test -- test/remote-control-dispatch.test.ts and it passed. [project] -- **typecheck_status**: pnpm --filter @caplets/core typecheck passed. [project] -- **full_verify_status**: Full pnpm verify was not run. [project] diff --git a/.brv/context-tree/facts/conventions/task_7_review_outcome.md b/.brv/context-tree/facts/conventions/task_7_review_outcome.md deleted file mode 100644 index f41dfdb0..00000000 --- a/.brv/context-tree/facts/conventions/task_7_review_outcome.md +++ /dev/null @@ -1,58 +0,0 @@ ---- -title: Task 7 Review Outcome -summary: Task 7 review approved after verifying remote add payload sanitization and add-subcommand coverage; tests passed -tags: [] -related: [] -keywords: [] -createdAt: '2026-05-20T14:30:35.963Z' -updatedAt: '2026-05-20T14:30:35.963Z' ---- - -## Reason - -Document the review result and verification details for Task 7 spec after fix - -## Raw Concept - -**Task:** -Document the Task 7 spec re-review after the fix - -**Changes:** -- Approved the review after confirming payload sanitization -- Confirmed add subcommand coverage across cli, mcp, openapi, graphql, and http -- Recorded the successful verification run - -**Files:** -- packages/core/src/cli.ts -- packages/core/test/cli-remote.test.ts -- packages/core/test/remote-control-dispatch.test.ts - -**Flow:** -review request -> verify sanitization -> verify subcommand coverage -> run tests -> APPROVED - -**Timestamp:** 2026-05-20T14:30:22.611Z - -**Author:** assistant - -## Narrative - -### Structure - -This note captures the post-fix review outcome for Task 7, including the specific sanitization behavior, add subcommand coverage, and test verification. - -### Dependencies - -Depends on the updated CLI remote add implementation and the associated remote control dispatch tests. - -### Highlights - -The review was approved and the targeted test run passed with 404 tests across 33 files. - -## Facts - -- **task_7_review_outcome**: Task 7 spec review after fix was APPROVED [project] -- **remote_add_payload_sanitization**: remote add payload sanitization strips global, print, output, and destinationRoot before remote.request("add", ...) [project] -- **add_subcommand_coverage**: Remote routing coverage includes all add subcommands: cli, mcp, openapi, graphql, and http [project] -- **sanitization_test_case**: Sanitization is explicitly tested for add mcp with --global, --print, and --output [project] -- **verification_command**: The verification run was pnpm --filter @caplets/core test -- test/cli-remote.test.ts test/remote-control-dispatch.test.ts [project] -- **verification_result**: The verification result was 33 files / 404 tests passed [project] diff --git a/.brv/context-tree/facts/conventions/task_9_remote_config_test_fix.md b/.brv/context-tree/facts/conventions/task_9_remote_config_test_fix.md deleted file mode 100644 index 0b2e6c83..00000000 --- a/.brv/context-tree/facts/conventions/task_9_remote_config_test_fix.md +++ /dev/null @@ -1,63 +0,0 @@ ---- -title: Task 9 Remote Config Test Fix -summary: Task 9 fix updated the in-process remote control app test to snapshot local.configPath before runCli and assert it remains unchanged; focused tests passed and commit c9f08ac was created. -tags: [] -related: [] -keywords: [] -createdAt: '2026-05-20T15:14:33.118Z' -updatedAt: '2026-05-20T15:14:33.118Z' ---- - -## Reason - -Record the durable outcome of the Task 9 spec finding fix and verification. - -## Raw Concept - -**Task:** -Document the Task 9 spec finding fix for the in-process remote control app test in packages/core/test/cli-remote.test.ts - -**Changes:** -- Added assertion that local.configPath content remains unchanged after runCli -- Kept the fix path-limited to packages/core/test/cli-remote.test.ts -- Ran the targeted @caplets/core test for cli-remote.test.ts -- Committed the change without amend - -**Files:** -- packages/core/test/cli-remote.test.ts - -**Flow:** -snapshot local config -> runCli -> compare post-run contents -> assert unchanged - -**Timestamp:** 2026-05-20T15:14:13.980Z - -**Author:** ByteRover context engineer - -## Narrative - -### Structure - -This knowledge captures a single targeted test fix and its verification outcome for the remote CLI path. The important behavior is that the local config file is protected from mutation during the in-process remote control app test. - -### Dependencies - -The fix depends on the cli-remote test harness and the local fixture config behavior in packages/core/test/cli-remote.test.ts. - -### Highlights - -The focused test passed after the assertion change, and the resulting commit SHA was c9f08ac. - -### Rules - -Goal is to prove local config file is not mutated. Commit new test fix path-limited, no amend. - -### Examples - -Use the test command pnpm --filter @caplets/core test -- test/cli-remote.test.ts when validating similar targeted CLI test fixes. - -## Facts - -- **remote_cli_local_config_mutation_guard**: The in-process remote control app test now snapshots local.configPath contents before runCli and asserts they are unchanged afterward. [project] -- **remote_cli_local_fixture_handling**: If the local fixture config does not exist, the test should handle existence and non-existence consistently while proving the local config file is not mutated. [project] -- **focused_test_command**: The focused test command run was pnpm --filter @caplets/core test -- test/cli-remote.test.ts. [project] -- **commit_sha**: The commit created for the fix was c9f08ac with message test(cli): assert remote routing preserves local config. [project] diff --git a/.brv/context-tree/facts/conventions/trivial_continuation_marker.md b/.brv/context-tree/facts/conventions/trivial_continuation_marker.md deleted file mode 100644 index 2278877a..00000000 --- a/.brv/context-tree/facts/conventions/trivial_continuation_marker.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -title: Trivial Continuation Marker -summary: No durable knowledge extracted; conversation contained only a continuation marker. -tags: [] -related: [] -keywords: [] -createdAt: '2026-05-20T17:49:29.002Z' -updatedAt: '2026-05-20T17:49:29.002Z' ---- - -## Reason - -The only substantive available content is a continuation request with no lasting knowledge. - -## Raw Concept - -**Task:** -Record that this context contains no durable knowledge - -**Changes:** -- Identified the input as a continuation-only message with no substantive content - -**Flow:** -receive context -> detect no substantive knowledge -> store continuation marker - -**Timestamp:** 2026-05-20 - -**Author:** ByteRover - -## Narrative - -### Structure - -This entry documents that the provided context was only a continuation prompt. - -### Dependencies - -No source material beyond the continuation marker was available. - -### Highlights - -Nothing actionable or durable was present for deeper curation. - -## Facts - -- **conversation_state**: The conversation content consisted only of a user message saying "Continue" [other] diff --git a/.brv/context-tree/facts/project/context.md b/.brv/context-tree/facts/project/context.md deleted file mode 100644 index b91b05a4..00000000 --- a/.brv/context-tree/facts/project/context.md +++ /dev/null @@ -1,12 +0,0 @@ -# Topic: project - -## Overview - -Covers operational conventions for curation workflows in the runtime environment, especially when recon has already been computed. - -## Key Concepts - -- single-pass processing -- chunked extraction via mapExtract -- verification through applied file paths -- preserving exact instructions and avoiding unnecessary exploration diff --git a/.brv/context-tree/facts/project/curate_runtime_conventions.md b/.brv/context-tree/facts/project/curate_runtime_conventions.md deleted file mode 100644 index f700c4e8..00000000 --- a/.brv/context-tree/facts/project/curate_runtime_conventions.md +++ /dev/null @@ -1,87 +0,0 @@ ---- -title: Curate Runtime Conventions -summary: "Runtime conventions for curation: use RLM recon first, prefer single-pass when suggested, use mapExtract for chunked contexts, and verify curated file paths via result.applied." -tags: [] -related: [facts/conventions/context.md, facts/project/context.md] -keywords: [] -createdAt: "2026-05-20T14:16:38.968Z" -updatedAt: "2026-05-20T18:36:27.951Z" ---- - -## Reason - -Persist runtime curation conventions from the provided context - -## Raw Concept - -**Task:** -Document runtime conventions for RLM-based curation of context variables. - -**Changes:** - -- Added a combined recon workflow recommendation -- Specified single-pass handling for small contexts -- Preserved verification requirements for curation results -- Use the precomputed recon result instead of calling tools.curation.recon again -- Proceed directly to extraction in single-pass mode when suggested -- Verify curation results through result.applied[].filePath -- Recorded recon-guided single-pass curation behavior -- Recorded mapExtract timeout requirement -- Recorded UPSERT preference and verification rule -- Captured the single-pass curation path for precomputed recon results -- Recorded tool usage constraints for recon, mapExtract, and verification -- Captured the required RLM curation workflow. -- Recorded the preferred UPSERT-based curation pattern. -- Preserved the verification rule using result.applied[].filePath. -- Use recon before curation when context is available -- Prefer single-pass handling when recon suggests it -- Use mapExtract for chunked contexts with taskId passed as a bare variable -- Verify curation via result.applied[].filePath -- Established recon-first curation workflow -- Preferred single-pass processing for small contexts -- Defined verification via curate results instead of rereading files -- Captured precomputed recon guidance -- Captured single-pass versus chunked extraction guidance -- Captured verification requirements - -**Files:** - -- .brv/context-tree/facts/conventions/context.md - -**Flow:** -recon -> choose single-pass or chunked extraction -> curate -> verify applied file paths - -**Timestamp:** 2026-05-20T18:36:17.812Z - -**Author:** ByteRover context engineer - -**Patterns:** - -- `^single-pass$` - Suggested mode for small contexts -- `^chunked$` - Suggested mode for larger contexts - -## Narrative - -### Structure - -The context describes how to process curation inputs using precomputed recon, then either direct single-pass curation or chunked mapExtract processing. - -### Dependencies - -Depends on precomputed recon output, tools.curation.mapExtract for chunked extraction, and tools.curate for persistence. - -### Highlights - -Suggested mode is single-pass for this context; do not print raw context and do not use recon again. - -### Rules - -Do not call tools.curation.recon again when recon is already computed. Do not print raw context. Verify via result.applied[].filePath and result.summary.failed. - -## Facts - -- **curation_workflow**: The curation workflow uses RLM approach with precomputed recon data when available. [convention] -- **single_pass_mode**: When recon suggests single-pass, chunking should be skipped. [convention] -- **map_extract_usage**: For chunked extraction, tools.curation.mapExtract() should be used with taskId passed as a bare variable. [convention] -- **mapextract_timeout**: Any code_exec call containing mapExtract must set timeout to 300000 on the tool call itself. [convention] -- **verification_method**: Verification must use result.applied[].filePath and must not call readFile for verification. [convention] diff --git a/.brv/context-tree/facts/project/curation_context_facts.md b/.brv/context-tree/facts/project/curation_context_facts.md deleted file mode 100644 index c2fd9a48..00000000 --- a/.brv/context-tree/facts/project/curation_context_facts.md +++ /dev/null @@ -1,41 +0,0 @@ ---- -title: Curation Context Facts -summary: Project facts and runtime conventions extracted from the curation context, including task metadata and processing guidance. -tags: [] -related: [] -keywords: [] -createdAt: '2026-05-20T19:28:15.751Z' -updatedAt: '2026-05-20T19:28:15.751Z' ---- -## Reason -Curate extracted runtime and task metadata facts from the provided context - -## Raw Concept -**Task:** -Curate RLM context metadata and runtime guidance - -**Changes:** -- Extracted key-value facts from the provided context variable - -**Flow:** -context -> line parsing -> fact extraction -> deduplication -> curation - -**Timestamp:** 2026-05-20T19:28:10.552Z - -**Author:** ByteRover context engineer - -## Narrative -### Structure -Captured the current curation task metadata and processing instructions as durable project facts. - -### Dependencies -Uses the precomputed recon result and the supplied task/history/metadata variables. - -### Highlights -The context is small enough for single-pass processing and the provided recon recommended single-pass mode. - -## Facts -- **curate_only_information_with_lasting_value**: Curate only information with lasting value: facts, decisions, technical details, preferences, or notable outcomes. [project] -- **_user_**: [user]: continue [project] -- **_assistant_**: [assistant]: Yes — I checked again more broadly, including PR review threads and top-level review bodies. [project] -- **_warnings_**: - `warnings?: string[]` protocol field is defined but unused/unwired. [project] diff --git a/.brv/context-tree/facts/project/curation_input_notes.md b/.brv/context-tree/facts/project/curation_input_notes.md deleted file mode 100644 index 81505e3f..00000000 --- a/.brv/context-tree/facts/project/curation_input_notes.md +++ /dev/null @@ -1,45 +0,0 @@ ---- -title: Curation Input Notes -summary: Captured the provided curation input as durable project knowledge. -tags: [] -related: [] -keywords: [] -createdAt: '2026-05-20T17:11:58.075Z' -updatedAt: '2026-05-20T17:11:58.075Z' ---- - -## Reason - -Curate the provided RLM context into durable knowledge - -## Raw Concept - -**Task:** -Curate the provided RLM context into the knowledge tree - -**Changes:** -- Captured the input as durable knowledge - -**Flow:** -context input -> fact extraction -> durable knowledge entry - -**Timestamp:** 2026-05-20T17:11:53.415Z - -## Narrative - -### Structure - -A compact curation note derived from the provided RLM context. - -### Highlights - -Preserves the supplied context for future recall. - -## Facts - -- **curation_input**: The following is a conversation between a user and an AI assistant. [other] -- **curation_input**: Curate only information with lasting value: facts, decisions, technical details, preferences, or notable outcomes. [other] -- **curation_input**: Skip trivial messages such as greetings, acknowledgments ("ok", "thanks", "sure", "got it"), one-word replies, anything with no substantive content. [other] -- **curation_input**: Conversation: [other] -- **curation_input**: --- [other] -- **curation_input**: [user]: continue [other] diff --git a/.brv/context-tree/facts/project/curation_runtime_conventions.md b/.brv/context-tree/facts/project/curation_runtime_conventions.md deleted file mode 100644 index 35f8cf32..00000000 --- a/.brv/context-tree/facts/project/curation_runtime_conventions.md +++ /dev/null @@ -1,66 +0,0 @@ ---- -title: Curation Runtime Conventions -summary: 'Runtime conventions for curation workflows: use RLM variables, single-pass when suggested, do not print raw context, verify via curate result, and preserve detailed facts and structure.' -tags: [] -related: [] -keywords: [] -createdAt: '2026-05-20T14:56:37.757Z' -updatedAt: '2026-05-20T17:10:10.808Z' ---- - -## Reason - -Curate runtime instructions and task-specific curation rules from the provided RLM context - -## Raw Concept - -**Task:** -Document the curation runtime conventions for RLM-based context processing - -**Changes:** -- Use precomputed recon results when available -- Proceed directly to extraction for single-pass contexts -- Verify curated outputs through applied file paths -- Defined immediate execution behavior for operations -- Established UPSERT as the preferred default curation action -- Captured RLM workflow guidance for variable-based curation prompts -- Recorded single-pass recon handling for small contexts -- Use the precomputed recon result instead of calling recon again -- Proceed directly to extraction in single-pass mode -- Use groupBySubject and dedup to organize extracted facts -- Verify curated outputs through result.applied[].filePath -- Established the single-pass path when recon recommends it -- Documented the required timeout for mapExtract-based extraction -- Captured the verification rule using result.applied[].filePath - -**Flow:** -recon -> choose mode -> extract or curate -> verify via curate result - -**Timestamp:** 2026-05-20T17:10:03.093Z - -**Author:** ByteRover context engine - -## Narrative - -### Structure - -This knowledge captures the operating rules for RLM curation runs, including mode selection and verification expectations. - -### Dependencies - -Relies on precomputed recon output and sandbox variables for context, history, metadata, and task ID. - -### Highlights - -The instructions emphasize not printing raw context, using single-pass when suggested, and preserving only compact summaries during curation. - -### Rules - -IMPORTANT: Do NOT print raw context. Do NOT call tools.curation.recon — it has been precomputed. Proceed directly to extraction. For chunked extraction use tools.curation.mapExtract(). Pass taskId: __taskId_315cb313_abe8_40eb_97b1_c8fe949ba458 (bare variable, not a string). IMPORTANT: Any code_exec call containing mapExtract MUST use timeout: 300000 on the code_exec tool call itself (not inside mapExtract options). Use tools.curation.groupBySubject() and tools.curation.dedup() to organize extractions. Verify via result.applied[].filePath — do NOT call readFile for verification. - -## Facts - -- **rlm_curation_workflow**: The current curation workflow uses the RLM approach with context, history, metadata, and task ID variables. [convention] -- **single_pass_mode**: When recon suggests single-pass, chunking should be skipped. [convention] -- **mapextract_timeout**: If mapExtract is used, the code_exec call must set timeout to 300000 milliseconds. [convention] -- **verification_method**: Verification should be done via result.applied[].filePath without reading files back. [convention] diff --git a/.brv/context-tree/facts/project/greptile_review_comments.md b/.brv/context-tree/facts/project/greptile_review_comments.md deleted file mode 100644 index 19029843..00000000 --- a/.brv/context-tree/facts/project/greptile_review_comments.md +++ /dev/null @@ -1,45 +0,0 @@ ---- -title: Greptile Review Comments -summary: Two remaining Greptile review comments were noted, along with passing PR checks. -tags: [] -related: [] -keywords: [] -createdAt: '2026-05-20T19:26:36.972Z' -updatedAt: '2026-05-20T19:26:36.972Z' ---- -## Reason -Capture remaining review comments and PR status from the conversation - -## Raw Concept -**Task:** -Document the remaining review comments and the current PR status - -**Changes:** -- Captured two unresolved Greptile review comments -- Recorded that verify, changeset, and Greptile review checks are passing - -**Files:** -- packages/core/src/serve/http.ts -- packages/core/src/remote-control/types.ts - -**Flow:** -review comment surfaced -> remediation suggested -> PR checks remain passing - -**Timestamp:** 2026-05-20T19:26:25.037Z - -**Author:** assistant summary - -## Narrative -### Structure -The comments target HTTP forwarding trust boundaries and an unused protocol warnings field. - -### Dependencies -The HTTP issue depends on proxy trust configuration; the types issue depends on whether warnings are intended to be part of the protocol. - -### Highlights -Both comments remain open in the latest Greptile top-level review even though the automated checks are passing. - -## Facts -- **greptile_http_review_comment**: There are remaining Greptile review comments in packages/core/src/serve/http.ts about trusting X-Forwarded-* headers without an explicit proxy opt-in. [project] -- **greptile_types_review_comment**: There are remaining Greptile review comments in packages/core/src/remote-control/types.ts about warnings?: string[] being defined in protocol types but never produced or consumed. [project] -- **pr_checks_status**: Current PR checks are passing: Verify success, Changeset success, and Greptile Review success. [project] diff --git a/.brv/context-tree/facts/project/pr_65_fixes_and_verification.md b/.brv/context-tree/facts/project/pr_65_fixes_and_verification.md deleted file mode 100644 index b6547c7f..00000000 --- a/.brv/context-tree/facts/project/pr_65_fixes_and_verification.md +++ /dev/null @@ -1,48 +0,0 @@ ---- -title: PR 65 Fixes and Verification -summary: 'PR #65 fix for Pi legacy remote settings and auth-flow object isolation, with verification passing locally and on push hook' -tags: [] -related: [] -keywords: [] -createdAt: '2026-05-20T19:06:59.769Z' -updatedAt: '2026-05-20T19:06:59.769Z' ---- -## Reason -Curate durable facts about the PR fixes, tests, and release state - -## Raw Concept -**Task:** -Document PR review fixes and verification outcome - -**Changes:** -- Addressed review comments and pushed fixes -- Preserved legacy Pi remote settings behavior -- Added regression tests for legacy settings and auth-flow object isolation - -**Flow:** -review comments -> fixes -> tests -> local verify -> push hook verify -> PR checks queued - -**Timestamp:** 2026-05-20T19:06:38.716Z - -## Narrative -### Structure -The update centers on PR #65, with commit c86806d carrying the fixes and verification results. - -### Dependencies -Relies on Pi/core tests and pnpm verify for validation. - -### Highlights -Legacy Pi remote settings continue to load into server settings; auth-flow objects are now isolated by copy. - -## Facts -- **pr_65**: PR #65 was updated with fixes addressing review comments, including outside-diff comments. [project] -- **legacy_remote_settings**: Commit c86806d preserves Pi legacy remote settings by loading remote.url, remote.user, and remote.password into server settings instead of falling back to local mode. [project] -- **legacy_remote_url_warning**: A deprecation warning is emitted for legacy Pi remote.url settings. [project] -- **remote_auth_flow_store**: RemoteAuthFlowStore.create() now returns a shallow copy instead of the internal stored object reference. [project] -- **regression_tests**: Regression tests were added for Pi legacy remote settings and auth-flow object isolation. [project] -- **focused_tests**: Focused Pi/core tests passed. [project] -- **verification**: pnpm verify passed locally. [project] -- **push_hook_verification**: The push hook ran pnpm verify again and it passed. [project] -- **verification_metrics**: Verification reported 37 test files passed and 500 tests passed, with format, lint, typecheck, schema, benchmark, and build all passing. [project] -- **pr_head**: PR head is now c86806d. [project] -- **pr_checks**: PR checks are queued on GitHub. [project] diff --git a/.brv/context-tree/facts/project/project_knowledge_notes.md b/.brv/context-tree/facts/project/project_knowledge_notes.md deleted file mode 100644 index 53c1ea21..00000000 --- a/.brv/context-tree/facts/project/project_knowledge_notes.md +++ /dev/null @@ -1,75 +0,0 @@ ---- -title: Project Knowledge Notes -summary: Project knowledge notes covering repository conventions, curated knowledge organization, and workflow expectations. -tags: [] -related: [] -keywords: [] -createdAt: '2026-05-20T13:11:07.251Z' -updatedAt: '2026-05-20T14:01:45.228Z' ---- - -## Reason - -Curate extracted project-wide knowledge from the provided context - -## Raw Concept - -**Task:** -Curate project-level knowledge and workflow conventions from the provided RLM context. - -**Changes:** -- Recorded the current context tree areas relevant to remote control and working module knowledge -- Preserved prior curated areas including packages/core and docs/plans -- Added guarded JSON parsing and protocol validation in RemoteControlClient -- Redacted remote-provided error messages before surfacing CapletsError -- Added regression tests for malformed payloads and secret-like error messages -- Verified the targeted test suite and typecheck commands -- Created fix commit bf60f51 -- Preserved working module findings as durable knowledge -- Curated the knowledge into the context tree -- Captured context-tree location and hierarchy rules -- Captured writable vs read-only knowledge source constraints -- Captured UPSERT preference for curation operations - -**Files:** -- packages/core/test/remote-control-client.test.ts -- packages/core/src/errors.ts -- .brv/context-tree/ -- .brv/context-tree/facts/project/ - -**Flow:** -context supplied -> extracted conventions and facts -> upserted into durable knowledge - -**Timestamp:** 2026-05-20T14:01:37.395Z - -**Author:** ByteRover context engineer - -## Narrative - -### Structure - -This knowledge belongs in the facts/project domain because it documents repository-wide operational conventions and curation expectations. - -### Dependencies - -Depends on the ByteRover context-tree organization and the RLM curation workflow. - -### Highlights - -Preserves the project knowledge needed to curate future context safely and consistently. - -### Rules - -Do not print raw context. Do not call recon when recon has already been computed. Verify curation via result.applied[].filePath. - -### Examples - -Useful for future curation sessions that need to target the local context tree and use UPSERT by default. - -## Facts - -- **context_tree_root**: The project context tree is stored under .brv/context-tree/ [project] -- **context_tree_depth**: Context tree hierarchy is domain -> topic -> subtopic, with a maximum depth of 2 levels. [convention] -- **context_tree_write_scope**: Only the local .brv/context-tree/ is writable; shared source context trees are read-only. [convention] -- **curate_operation_preference**: UPSERT is the preferred curation operation and auto-detects whether to create or update a file. [convention] -- **curate_content_requirements**: For curation tasks, context content should include rawConcept and narrative sections, with at least one of changes, files, or flow in rawConcept. [convention] diff --git a/.brv/context-tree/facts/project/remote_control_review_outcome.md b/.brv/context-tree/facts/project/remote_control_review_outcome.md deleted file mode 100644 index e62927b1..00000000 --- a/.brv/context-tree/facts/project/remote_control_review_outcome.md +++ /dev/null @@ -1,66 +0,0 @@ ---- -title: Remote Control Review Outcome -summary: 'Approved remote control review: server-side redaction fixed, regression tests added, CLI pending auth wording accepted, with residual spec mismatch noted.' -tags: [] -related: [] -keywords: [] -createdAt: '2026-05-20T15:41:00.585Z' -updatedAt: '2026-05-20T15:41:00.585Z' ---- - -## Reason - -Persist final review outcome and verification details from the code review conversation - -## Raw Concept - -**Task:** -Document the final remote control review outcome after verifying the security fix and auth behavior. - -**Changes:** -- Approved the review after confirming server-side redaction fix -- Noted regression test coverage for remote control dispatch -- Accepted the pending-auth CLI wording as non-misleading -- Captured the remaining spec-versus-CLI residual risk - -**Files:** -- packages/core/src/remote-control/dispatch.ts -- packages/core/test/remote-control-dispatch.test.ts -- packages/core/test/remote-control-client.test.ts -- packages/core/test/cli-remote.test.ts - -**Flow:** -review request -> verify security fix -> reassess auth wording -> inspect test results -> approve - -**Timestamp:** 2026-05-20T15:40:45.193Z - -## Narrative - -### Structure - -The review outcome centers on one fixed security issue, one accepted pending-auth UX detail, and one residual design-spec mismatch. - -### Dependencies - -Depends on the remote control dispatch implementation, its regression tests, and the CLI wording/docs for remote authentication. - -### Highlights - -The review concluded APPROVED, with focused remote tests passing and the prior high security issue resolved. - -### Rules - -Return APPROVED or FINDINGS. Do not edit. The pending auth flow is acceptable when the CLI wording clearly indicates browser completion and server-side credential storage. - -### Examples - -Verification used the command: pnpm --filter @caplets/core test -- test/remote-control-dispatch.test.ts test/remote-control-client.test.ts test/cli-remote.test.ts - -## Facts - -- **remote_control_error_redaction**: Commit 7cb9381 fixed server-side remote control error redaction. [project] -- **remote_control_dispatch**: A high security finding was verified as addressed in packages/core/src/remote-control/dispatch.ts. [project] -- **remote_control_regression_tests**: Regression coverage was added in packages/core/test/remote-control-dispatch.test.ts. [project] -- **remote_auth_cli_wording**: CLI wording for pending remote auth login says: "Complete authentication in your browser. The server callback will store credentials." [project] -- **remote_test_results**: Focused remote tests passed with 33 files and 415 tests. [project] -- **remote_auth_residual_risk**: Residual risk noted that the design spec still describes wait/poll as the ideal flow while current CLI behavior is pending-oriented and non-misleading. [project] diff --git a/.brv/context-tree/facts/project/rlm_curate_runtime_conventions.md b/.brv/context-tree/facts/project/rlm_curate_runtime_conventions.md deleted file mode 100644 index 0d64ceb8..00000000 --- a/.brv/context-tree/facts/project/rlm_curate_runtime_conventions.md +++ /dev/null @@ -1,62 +0,0 @@ ---- -title: RLM Curate Runtime Conventions -summary: RLM curation uses precomputed recon, single-pass processing for small contexts, mapExtract for chunked contexts, and verification via applied file paths. -tags: [] -related: [] -keywords: [] -createdAt: '2026-05-20T15:10:55.428Z' -updatedAt: '2026-05-20T17:07:06.627Z' ---- - -## Reason - -Curate runtime conventions from RLM context - -## Raw Concept - -**Task:** -Document the runtime conventions for RLM curation workflows - -**Changes:** -- The context variable is small enough for single-pass processing. -- Recon was already computed and should not be repeated. -- mapExtract is reserved for chunked extraction paths. -- Verification should rely on the curate result rather than filesystem reads. -- Recorded the recon-first workflow for RLM curation -- Captured the single-pass optimization path -- Preserved chunked extraction and verification constraints -- Stored task-specific execution requirements for mapExtract and timeout handling -- Established single-pass handling for small contexts -- Established mapExtract usage for chunked contexts -- Established verification via applied file paths - -**Flow:** -precomputed recon -> choose suggested mode -> direct curate or mapExtract -> verify applied file paths - -**Timestamp:** 2026-05-20T17:06:50.241Z - -## Narrative - -### Structure - -The convention set governs how to process RLM curation inputs depending on recon output. Small contexts are handled in a single pass, while larger inputs use chunked extraction. - -### Dependencies - -Depends on precomputed recon metadata, taskId propagation, and curate result verification fields. - -### Highlights - -The workflow emphasizes no redundant recon call, no raw context printing, and explicit timeout requirements for mapExtract. - -### Rules - -Do NOT print raw context. Do NOT call tools.curation.recon when recon is already computed. For chunked extraction use tools.curation.mapExtract() and pass taskId as a bare variable. Verify via result.applied[].filePath and do NOT call readFile for verification. - -## Facts - -- **rlm_recon_precomputed**: For RLM curation, recon is precomputed and should not be called again when provided. [convention] -- **single_pass_mode**: When suggestedMode is single-pass, skip chunking and curate directly. [convention] -- **map_extract_usage**: When chunked extraction is needed, tools.curation.mapExtract() should be used with taskId passed as a bare variable. [convention] -- **mapextract_timeout**: Any code_exec call containing mapExtract must use timeout 300000 on the code_exec tool call itself. [convention] -- **verification_method**: Verification should use result.applied[].filePath and must not use readFile for verification. [convention] diff --git a/.brv/context-tree/facts/project/rlm_curation_workflow.md b/.brv/context-tree/facts/project/rlm_curation_workflow.md deleted file mode 100644 index a963def4..00000000 --- a/.brv/context-tree/facts/project/rlm_curation_workflow.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -title: RLM Curation Workflow -summary: RLM curation workflow instruction with precomputed recon and single-pass processing guidance -tags: [] -related: [] -keywords: [] -createdAt: '2026-05-20T19:27:41.144Z' -updatedAt: '2026-05-20T19:27:41.144Z' ---- -## Reason -Capture the curation workflow instruction from the provided context - -## Raw Concept -**Task:** -Curate RLM approach context - -**Changes:** -- Documented the requirement to use recon-precomputed single-pass curation workflow. - -**Flow:** -recon -> extraction -> curate - -**Timestamp:** 2026-05-20T19:27:36.707Z - -**Author:** ByteRover context engineer - -## Narrative -### Structure -This context records the operational instruction to use the RLM curation workflow with precomputed recon and single-pass processing for a small context. - -### Dependencies -Relies on the provided context, history, metadata, and task ID variables for curation. - -### Highlights -Recon is already computed; suggestedMode is single-pass; do not print raw context or re-run recon. - -## Facts -- **curation_approach**: Context was curated using the RLM approach. [project] diff --git a/.brv/context-tree/facts/project/task_6_spec_review_after_envelope_fix.md b/.brv/context-tree/facts/project/task_6_spec_review_after_envelope_fix.md deleted file mode 100644 index f35150c5..00000000 --- a/.brv/context-tree/facts/project/task_6_spec_review_after_envelope_fix.md +++ /dev/null @@ -1,63 +0,0 @@ ---- -title: Task 6 Spec Review After Envelope Fix -summary: 'Task 6 review approved: CLI and dispatch align on { caplet, request }, config path handling remains local-only, and tests cover remote control behavior.' -tags: [] -related: [architecture/remote_control/remote_control_api_shape.md, architecture/remote_control/task_1_spec_compliance_review.md, architecture/remote_control/cli_remote_mode_selection.md] -keywords: [] -createdAt: '2026-05-20T14:11:18.102Z' -updatedAt: '2026-05-20T14:11:18.102Z' ---- - -## Reason - -Capture durable verification of CLI and dispatch alignment after envelope fix commit f70d578 - -## Raw Concept - -**Task:** -Document the approved Task 6 spec review after the envelope fix commit f70d578 - -**Changes:** -- Verified CLI and dispatch alignment on nested { caplet, request } envelopes -- Confirmed config path and config paths remain local-only -- Confirmed test coverage for CLI remote list/call-tool and dispatch nested envelope handling - -**Files:** -- packages/core/src/cli.ts -- packages/core/src/remote-control/dispatch.ts -- packages/core/test/cli-remote.test.ts -- packages/core/test/remote-control-dispatch.test.ts - -**Flow:** -CLI builds { caplet, request } envelope -> dispatch passes request to engine.execute -> tests verify local-only config paths and remote-control handling - -**Timestamp:** 2026-05-20T14:11:05.386Z - -**Author:** assistant - -## Narrative - -### Structure - -This note records the approved review result, the envelope contract, local-only config rules, and the test files that validate the behavior. - -### Dependencies - -Depends on the envelope fix in commit f70d578 and the associated CLI, dispatch, and test updates. - -### Highlights - -The review returned APPROVED and the verification run passed with 394 tests. - -### Examples - -Covered tests include CLI remote list and call-tool envelope behavior, local-only config path handling, and dispatch nested envelope handling for get_caplet and search_tools. - -## Facts - -- **task_6_review_status**: Task 6 spec review after envelope fix commit f70d578 was approved. [project] -- **cli_envelope_shape**: CLI sends direct operation envelope as { caplet, request }. [project] -- **dispatch_envelope_shape**: Dispatch consumes the same nested envelope and passes request to engine.execute. [project] -- **config_path_scope**: config path remains local-only. [project] -- **config_paths_scope**: config paths remains local-only. [project] -- **verification_run**: pnpm --filter @caplets/core test -- test/cli-remote.test.ts test/remote-control-dispatch.test.ts passed with 394 tests. [project] diff --git a/.changeset/funny-news-act.md b/.changeset/funny-news-act.md new file mode 100644 index 00000000..2c02b6b5 --- /dev/null +++ b/.changeset/funny-news-act.md @@ -0,0 +1,5 @@ +--- +"@caplets/pi": patch +--- + +Fix pi peer dependency resolution diff --git a/.coderabbit.yaml b/.coderabbit.yaml new file mode 100644 index 00000000..c5b4451a --- /dev/null +++ b/.coderabbit.yaml @@ -0,0 +1,15 @@ +# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json +language: "en-US" +early_access: false +reviews: + profile: "chill" + request_changes_workflow: false + high_level_summary: true + poem: true + review_status: true + review_details: false + auto_review: + enabled: true + drafts: false + path_filters: + - "!.brv/**" diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..93606a0f --- /dev/null +++ b/.dockerignore @@ -0,0 +1,21 @@ +.git +.github +.husky +.brv +.pi +.pi-lens +.opencode +.claude-plugin +node_modules +**/node_modules +dist +**/dist +.turbo +coverage +*.log +.env +.env.* +!.env.example +.caplets +**/.caplets +.DS_Store diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index db90c2ae..4f4678e8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -24,6 +24,11 @@ jobs: release: name: Version or Publish runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + id-token: write + packages: write steps: - name: Checkout uses: actions/checkout@v6 @@ -47,6 +52,7 @@ jobs: run: pnpm verify - name: Create release PR or publish + id: changesets uses: changesets/action@v1 with: version: pnpm version-packages @@ -55,3 +61,63 @@ jobs: title: "chore: version packages" env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Check whether CLI package was published + if: steps.changesets.outputs.published == 'true' + id: cli-package + env: + PUBLISHED_PACKAGES: ${{ steps.changesets.outputs.publishedPackages }} + run: | + cli_published=$(node <<'NODE' + const publishedPackages = JSON.parse(process.env.PUBLISHED_PACKAGES || '[]'); + const cliPublished = publishedPackages.some((pkg) => pkg && pkg.name === 'caplets'); + process.stdout.write(cliPublished ? 'true' : 'false'); + NODE + ) + echo "published=${cli_published}" >> "$GITHUB_OUTPUT" + + - name: Read Docker image version + if: steps.changesets.outputs.published == 'true' && steps.cli-package.outputs.published == 'true' + id: image-version + run: | + version=$(node -p "require('./packages/cli/package.json').version") + echo "version=${version}" >> "$GITHUB_OUTPUT" + + - name: Setup QEMU + if: steps.changesets.outputs.published == 'true' && steps.cli-package.outputs.published == 'true' + uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 + + - name: Setup Docker Buildx + if: steps.changesets.outputs.published == 'true' && steps.cli-package.outputs.published == 'true' + uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f + + - name: Log in to GitHub Container Registry + if: steps.changesets.outputs.published == 'true' && steps.cli-package.outputs.published == 'true' + uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Generate Docker metadata + if: steps.changesets.outputs.published == 'true' && steps.cli-package.outputs.published == 'true' + id: docker-meta + uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 + with: + images: ghcr.io/spiritledsoftware/caplets + tags: | + type=raw,value=latest + type=raw,value=${{ steps.image-version.outputs.version }} + type=raw,value=v${{ steps.image-version.outputs.version }} + type=sha,format=short,prefix=sha- + + - name: Publish Docker image + if: steps.changesets.outputs.published == 'true' && steps.cli-package.outputs.published == 'true' + uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 + with: + context: . + file: ./Dockerfile + platforms: linux/amd64,linux/arm64 + push: true + tags: ${{ steps.docker-meta.outputs.tags }} + labels: ${{ steps.docker-meta.outputs.labels }} diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..d144c474 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule ".brv"] + path = .brv + url = https://github.com/spiritledsoftware/caplets-brv.git diff --git a/.greptile/config.json b/.greptile/config.json new file mode 100644 index 00000000..1538790d --- /dev/null +++ b/.greptile/config.json @@ -0,0 +1,3 @@ +{ + "ignorePatterns": ".brv/**" +} diff --git a/.husky/pre-commit b/.husky/pre-commit index cb2c84d5..b10e9e5d 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1 +1,2 @@ +scripts/commit-brv-submodule.sh pnpm lint-staged diff --git a/.husky/pre-push b/.husky/pre-push index 347ade03..1ba195e5 100755 --- a/.husky/pre-push +++ b/.husky/pre-push @@ -1 +1,2 @@ +scripts/commit-brv-submodule.sh --push pnpm verify diff --git a/.opencode/opencode.json b/.opencode/opencode.json index 8adeebf3..2c9f88e4 100644 --- a/.opencode/opencode.json +++ b/.opencode/opencode.json @@ -5,7 +5,7 @@ "@caplets/opencode@file:./packages/opencode", { "mode": "local", - "remote": { + "server": { "url": "http://localhost:5387/mcp" } } diff --git a/.pi/settings.json b/.pi/settings.json index 4d549282..21aa329a 100644 --- a/.pi/settings.json +++ b/.pi/settings.json @@ -2,7 +2,7 @@ "packages": ["npm:@caplets/pi@file:../../packages/pi"], "caplets": { "mode": "local", - "remote": { + "server": { "url": "http://localhost:5387/mcp" } } diff --git a/AGENTS.md b/AGENTS.md index d30891de..84e2ebd8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -19,6 +19,7 @@ ## Generated And Checked Files +- Put design specs in `docs/specs/`, implementation plans in `docs/plans/`, and product requirements documents in `docs/product/`; do not use `docs/superpowers/` in this repo. - Config schema source of truth is Zod in `packages/core/src/config.ts`; update `schemas/caplets-config.schema.json` with `pnpm schema:generate` and verify with `pnpm schema:check`. - `pnpm benchmark` updates `docs/benchmarks/coding-agent.md`; `pnpm benchmark:check` fails if the committed report is stale. - Live benchmarks are opt-in only: build first, then run `CAPLETS_BENCH_LIVE=1 pnpm benchmark:live:opencode` or `CAPLETS_BENCH_LIVE=1 pnpm benchmark:live:pi`; results are local/model-dependent and not deterministic product claims. diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..b95e047c --- /dev/null +++ b/Dockerfile @@ -0,0 +1,50 @@ +# syntax=docker/dockerfile:1.7 + +ARG NODE_VERSION=24-bookworm-slim +ARG PNPM_VERSION=11.0.9 + +FROM node:${NODE_VERSION} AS build +ARG PNPM_VERSION +WORKDIR /app + +RUN corepack enable && corepack prepare pnpm@${PNPM_VERSION} --activate + +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml turbo.json ./ +COPY packages/core/package.json packages/core/package.json +COPY packages/cli/package.json packages/cli/package.json +COPY packages/opencode/package.json packages/opencode/package.json +COPY packages/pi/package.json packages/pi/package.json +COPY packages/benchmarks/package.json packages/benchmarks/package.json + +RUN pnpm install --frozen-lockfile + +COPY . . + +RUN pnpm build && CI=true pnpm prune --prod --ignore-scripts + +FROM node:${NODE_VERSION} AS runtime +ENV NODE_ENV=production \ + XDG_CONFIG_HOME=/data/config \ + XDG_STATE_HOME=/data/state \ + CAPLETS_SERVER_URL=http://127.0.0.1:5387 +WORKDIR /app + +RUN mkdir -p /data/config /data/state && \ + chown -R node:root /app /data + +COPY --from=build --chown=node:root /app/package.json /app/pnpm-workspace.yaml ./ +COPY --from=build --chown=node:root /app/node_modules ./node_modules +COPY --from=build --chown=node:root /app/packages/core/package.json ./packages/core/package.json +COPY --from=build --chown=node:root /app/packages/core/dist ./packages/core/dist +COPY --from=build --chown=node:root /app/packages/cli/package.json ./packages/cli/package.json +COPY --from=build --chown=node:root /app/packages/cli/dist ./packages/cli/dist + +VOLUME ["/data"] +EXPOSE 5387 + +USER node + +HEALTHCHECK --interval=30s --timeout=5s --retries=5 --start-period=10s \ + CMD node -e "fetch('http://127.0.0.1:5387/healthz').then((response) => process.exit(response.ok ? 0 : 1)).catch(() => process.exit(1))" + +CMD ["sh", "-c", "test -f /data/config/caplets/config.json || CAPLETS_MODE=local node packages/cli/dist/index.js init && exec env CAPLETS_MODE=local node packages/cli/dist/index.js serve --transport http --host 0.0.0.0"] diff --git a/README.md b/README.md index 68da9a53..fe462b68 100644 --- a/README.md +++ b/README.md @@ -124,6 +124,46 @@ With `CAPLETS_SERVER_URL=http://127.0.0.1:5387/caplets`, the derived endpoints a `caplets serve --transport http` serves plain HTTP. For non-loopback or network access, expose it only through HTTPS/TLS (for example, a reverse proxy or secure tunnel) and enable Basic Auth; Basic Auth over plain HTTP exposes credentials. Keep credentials out of plugin manifests. +#### Docker Compose self-hosting + +This repository includes a source-build Docker image and Compose service for running the HTTP service from the checked-out source tree: + +```sh +CAPLETS_SERVER_PASSWORD=change-me docker compose up --build +``` + +By default, Compose publishes the service on loopback only: + +- Base URL: `http://127.0.0.1:5387` +- MCP endpoint: `http://127.0.0.1:5387/mcp` +- Control endpoint: `http://127.0.0.1:5387/control` +- Health endpoint: `http://127.0.0.1:5387/healthz` + +The service stores Caplets config and auth state in a Docker named volume mounted at `/data`. To use a host-visible bind mount instead, replace this Compose volume entry: + +```yaml +volumes: + - caplets-data:/data +``` + +with: + +```yaml +volumes: + - ./data:/data +``` + +To expose the service to a LAN interface or reverse proxy, set an explicit bind address and public base URL: + +```sh +CAPLETS_BIND_ADDRESS=0.0.0.0 \ +CAPLETS_SERVER_URL=https://caplets.example.com \ +CAPLETS_SERVER_PASSWORD=change-me \ +docker compose up --build +``` + +Only expose Caplets beyond loopback through HTTPS/TLS and Basic Auth. `CAPLETS_SERVER_PASSWORD` protects both the MCP and control endpoints; downstream provider tokens and auth files remain server-owned inside the mounted `/data` location. + Native integrations and remote-capable CLI commands read remote client settings from environment variables: ```sh diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..bb955686 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,32 @@ +services: + caplets: + build: + context: . + dockerfile: Dockerfile + image: caplets:local + restart: unless-stopped + environment: + CAPLETS_SERVER_URL: ${CAPLETS_SERVER_URL:-http://127.0.0.1:5387} + CAPLETS_SERVER_USER: ${CAPLETS_SERVER_USER:-caplets} + CAPLETS_SERVER_PASSWORD: ${CAPLETS_SERVER_PASSWORD:-} + XDG_CONFIG_HOME: /data/config + XDG_STATE_HOME: /data/state + env_file: + - path: .env + required: false + ports: + - "${CAPLETS_BIND_ADDRESS:-127.0.0.1}:${CAPLETS_PORT:-5387}:5387" + volumes: + - caplets-data:/data + healthcheck: + test: + - CMD-SHELL + - >- + node -e "fetch('http://127.0.0.1:5387/healthz').then((response) => process.exit(response.ok ? 0 : 1)).catch(() => process.exit(1))" + interval: 30s + timeout: 5s + retries: 5 + start_period: 10s + +volumes: + caplets-data: diff --git a/docs/plans/2026-05-21-docker-self-hosting.md b/docs/plans/2026-05-21-docker-self-hosting.md new file mode 100644 index 00000000..cf6348d2 --- /dev/null +++ b/docs/plans/2026-05-21-docker-self-hosting.md @@ -0,0 +1,474 @@ +# Docker Self-Hosting Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Add source-build Docker and Docker Compose support so Caplets can be self-hosted as an HTTP service with persistent server-owned state. + +**Architecture:** The implementation adds root container artifacts only: a multi-stage `Dockerfile`, a focused `.dockerignore`, and a `docker-compose.yml` service that runs `caplets serve --transport http`. Runtime state is persisted under `/data` via XDG environment variables, while Compose owns external bind address/port configuration and passes through unified `CAPLETS_SERVER_*` settings. + +**Tech Stack:** Docker, Docker Compose, Node.js 24 slim images, Corepack, `pnpm@11.0.9`, Caplets CLI HTTP serve mode. + +--- + +## File Structure + +- Create: `.dockerignore` + - Keeps Docker build contexts small and prevents local caches, VCS metadata, generated output, and secret-bearing local state from entering the image build context. +- Create: `Dockerfile` + - Multi-stage source-build image for the monorepo. + - Build stage installs workspace dependencies and runs `pnpm build`. + - Runtime stage copies the built workspace and starts the HTTP Caplets server. +- Create: `docker-compose.yml` + - Local/self-hosted Compose service with configurable host binding, port, credentials, health check, and durable named volume. +- Modify: `README.md` + - Adds a Docker Compose self-hosting section near the existing “Remote Caplets service” docs. +- Existing reference only: `docs/specs/2026-05-21-docker-self-hosting-design.md` + - Source of truth for the approved design. + +--- + +### Task 1: Add Docker build context rules + +**Files:** + +- Create: `.dockerignore` + +- [ ] **Step 1: Create `.dockerignore`** + +Create `.dockerignore` with exactly this content: + +```dockerignore +.git +.github +.husky +.brv +.pi +.pi-lens +.opencode +.claude-plugin +node_modules +**/node_modules +dist +**/dist +.turbo +coverage +*.log +.env +.env.* +!.env.example +.caplets +**/.caplets +.DS_Store +``` + +Rationale: + +- `node_modules`, `dist`, `.turbo`, and `coverage` are regenerated inside Docker. +- `.git`, agent state directories, and local Caplets config/state should not enter the image context. +- `.env` files are excluded to avoid accidental secret inclusion. + +- [ ] **Step 2: Verify `.dockerignore` exists and is formatted as plain text** + +Run: + +```bash +test -f .dockerignore && sed -n '1,120p' .dockerignore +``` + +Expected output contains the entries from Step 1 and exits with status `0`. + +- [ ] **Step 3: Commit Docker ignore rules** + +Run: + +```bash +git add .dockerignore +git commit -m "build: add docker ignore rules" +``` + +Expected: commit succeeds and includes only `.dockerignore`. + +--- + +### Task 2: Add the source-build Dockerfile + +**Files:** + +- Create: `Dockerfile` + +- [ ] **Step 1: Create `Dockerfile`** + +Create `Dockerfile` with exactly this content: + +```dockerfile +# syntax=docker/dockerfile:1.7 + +ARG NODE_VERSION=24-bookworm-slim +ARG PNPM_VERSION=11.0.9 + +FROM node:${NODE_VERSION} AS build +ARG PNPM_VERSION +WORKDIR /app + +RUN corepack enable && corepack prepare pnpm@${PNPM_VERSION} --activate + +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml turbo.json tsconfig.json vitest.config.ts ./ +COPY packages/core/package.json packages/core/package.json +COPY packages/cli/package.json packages/cli/package.json +COPY packages/opencode/package.json packages/opencode/package.json +COPY packages/pi/package.json packages/pi/package.json +COPY packages/benchmarks/package.json packages/benchmarks/package.json + +RUN pnpm install --frozen-lockfile + +COPY . . + +RUN pnpm build + +FROM node:${NODE_VERSION} AS runtime +ARG PNPM_VERSION +ENV NODE_ENV=production \ + XDG_CONFIG_HOME=/data/config \ + XDG_STATE_HOME=/data/state \ + CAPLETS_SERVER_URL=http://127.0.0.1:5387 +WORKDIR /app + +RUN corepack enable && corepack prepare pnpm@${PNPM_VERSION} --activate + +COPY --from=build /app /app + +VOLUME ["/data"] +EXPOSE 5387 + +CMD ["pnpm", "--filter", "caplets", "exec", "caplets", "serve", "--transport", "http", "--host", "0.0.0.0"] +``` + +Notes for implementer: + +- Keep `CAPLETS_SERVER_URL` as a default only. Compose will override it for user-facing deployments. +- `XDG_CONFIG_HOME=/data/config` makes the default config path `/data/config/caplets/config.json`. +- `XDG_STATE_HOME=/data/state` makes auth state persist under `/data/state/caplets/auth`. +- The runtime image intentionally copies the built workspace and `node_modules` from the build stage to preserve workspace links for local-source execution. + +- [ ] **Step 2: Validate Dockerfile parses with Docker** + +Run: + +```bash +docker build --target runtime -t caplets:self-host-test . +``` + +Expected: build succeeds. The final lines should include an image tagged `caplets:self-host-test`. + +If Docker is unavailable in the execution environment, record the exact Docker error in the task notes and continue to Step 3; do not claim the Docker build was verified. + +- [ ] **Step 3: Commit the Dockerfile** + +Run: + +```bash +git add Dockerfile +git commit -m "build: add caplets docker image" +``` + +Expected: commit succeeds and includes only `Dockerfile`. + +--- + +### Task 3: Add Docker Compose service + +**Files:** + +- Create: `docker-compose.yml` + +- [ ] **Step 1: Create `docker-compose.yml`** + +Create `docker-compose.yml` with exactly this content: + +```yaml +services: + caplets: + build: + context: . + dockerfile: Dockerfile + image: caplets:local + restart: unless-stopped + environment: + CAPLETS_SERVER_URL: ${CAPLETS_SERVER_URL:-http://127.0.0.1:5387} + CAPLETS_SERVER_USER: ${CAPLETS_SERVER_USER:-caplets} + CAPLETS_SERVER_PASSWORD: ${CAPLETS_SERVER_PASSWORD:-} + XDG_CONFIG_HOME: /data/config + XDG_STATE_HOME: /data/state + ports: + - "${CAPLETS_BIND_ADDRESS:-127.0.0.1}:${CAPLETS_PORT:-5387}:5387" + volumes: + - caplets-data:/data + healthcheck: + test: + - CMD-SHELL + - >- + node -e "fetch('http://127.0.0.1:5387/healthz').then((response) => process.exit(response.ok ? 0 : 1)).catch(() => process.exit(1))" + interval: 30s + timeout: 5s + retries: 5 + start_period: 10s + +volumes: + caplets-data: +``` + +Rationale: + +- The container listens internally on `0.0.0.0:5387` from the Dockerfile command. +- Compose defaults external binding to `127.0.0.1` for safety. +- `CAPLETS_SERVER_PASSWORD` is intentionally empty by default so local loopback startup works without hard-coded secrets. +- Self-hosters exposing beyond loopback must set `CAPLETS_SERVER_PASSWORD` and should place the service behind HTTPS/TLS. + +- [ ] **Step 2: Validate Compose renders correctly** + +Run: + +```bash +docker compose config +``` + +Expected: command exits `0` and rendered output includes: + +```yaml +ports: + - mode: ingress + host_ip: 127.0.0.1 + target: 5387 + published: "5387" +``` + +Compose output formatting can vary by Docker Compose version. If the exact YAML differs, verify these equivalent facts in the rendered output: + +- service name is `caplets` +- published port is `5387` +- host IP is `127.0.0.1` +- named volume `caplets-data` is declared + +- [ ] **Step 3: Validate LAN binding override renders correctly** + +Run: + +```bash +CAPLETS_BIND_ADDRESS=0.0.0.0 CAPLETS_PORT=15387 docker compose config +``` + +Expected: command exits `0` and rendered output shows host IP `0.0.0.0` and published port `15387` for target port `5387`. + +- [ ] **Step 4: Commit Compose service** + +Run: + +```bash +git add docker-compose.yml +git commit -m "build: add docker compose service" +``` + +Expected: commit succeeds and includes only `docker-compose.yml`. + +--- + +### Task 4: Document Docker self-hosting + +**Files:** + +- Modify: `README.md` after the remote HTTP service paragraph and before “Native integrations and remote-capable CLI commands read remote client settings from environment variables:” + +- [ ] **Step 1: Insert Docker Compose documentation** + +In `README.md`, find this paragraph under `### Remote Caplets service`: + +```markdown +`caplets serve --transport http` serves plain HTTP. For non-loopback or network access, expose it only through HTTPS/TLS (for example, a reverse proxy or secure tunnel) and enable Basic Auth; Basic Auth over plain HTTP exposes credentials. Keep credentials out of plugin manifests. +``` + +Immediately after it, insert this section: + +````markdown +#### Docker Compose self-hosting + +This repository includes a source-build Docker image and Compose service for running the HTTP service from the checked-out source tree: + +```sh +CAPLETS_SERVER_PASSWORD=change-me docker compose up --build +``` + +By default, Compose publishes the service on loopback only: + +- Base URL: `http://127.0.0.1:5387` +- MCP endpoint: `http://127.0.0.1:5387/mcp` +- Control endpoint: `http://127.0.0.1:5387/control` +- Health endpoint: `http://127.0.0.1:5387/healthz` + +The service stores Caplets config and auth state in a Docker named volume mounted at `/data`. To use a host-visible bind mount instead, replace this Compose volume entry: + +```yaml +volumes: + - caplets-data:/data +``` + +with: + +```yaml +volumes: + - ./data:/data +``` + +To expose the service to a LAN interface or reverse proxy, set an explicit bind address and public base URL: + +```sh +CAPLETS_BIND_ADDRESS=0.0.0.0 \ +CAPLETS_SERVER_URL=https://caplets.example.com \ +CAPLETS_SERVER_PASSWORD=change-me \ +docker compose up --build +``` + +Only expose Caplets beyond loopback through HTTPS/TLS and Basic Auth. `CAPLETS_SERVER_PASSWORD` protects both the MCP and control endpoints; downstream provider tokens and auth files remain server-owned inside the mounted `/data` location. +```` + +Important markdown escaping note: the inserted section contains fenced code blocks inside markdown. Keep each fence exactly as shown. + +- [ ] **Step 2: Verify README formatting** + +Run: + +```bash +pnpm format:check README.md +``` + +Expected: `All matched files use the correct format.` + +- [ ] **Step 3: Commit README documentation** + +Run: + +```bash +git add README.md +git commit -m "docs: document docker self-hosting" +``` + +Expected: commit succeeds and includes only `README.md`. + +--- + +### Task 5: Run final verification + +**Files:** + +- Verify: `.dockerignore` +- Verify: `Dockerfile` +- Verify: `docker-compose.yml` +- Verify: `README.md` + +- [ ] **Step 1: Run formatting check for changed text files** + +Run: + +```bash +pnpm format:check .dockerignore Dockerfile docker-compose.yml README.md docs/specs/2026-05-21-docker-self-hosting-design.md +``` + +Expected: command exits `0` and reports all matched files use correct format. + +- [ ] **Step 2: Run Compose config validation** + +Run: + +```bash +docker compose config +``` + +Expected: command exits `0` and renders the `caplets` service and `caplets-data` volume. + +If Docker Compose is unavailable, record the exact command failure in the task notes and continue; do not claim Compose was verified. + +- [ ] **Step 3: Run Docker image build verification** + +Run: + +```bash +docker build -t caplets:self-host-test . +``` + +Expected: command exits `0` and creates image `caplets:self-host-test`. + +If Docker is unavailable, record the exact command failure in the task notes and continue; do not claim the image build was verified. + +- [ ] **Step 4: Optionally run service smoke test when Docker daemon is available** + +Run: + +```bash +CAPLETS_SERVER_PASSWORD=change-me docker compose up --build -d +sleep 5 +curl --fail http://127.0.0.1:5387/healthz +docker compose down +``` + +Expected: + +- `curl` exits `0`. +- Response body is JSON with a healthy Caplets HTTP service, including `transport` set to `http`. +- `docker compose down` exits `0` and stops the service. + +If the service fails to become healthy, inspect logs with: + +```bash +docker compose logs caplets +``` + +Fix the issue before completing the task. + +- [ ] **Step 5: Run repo-level non-Docker checks** + +Run: + +```bash +pnpm verify +``` + +Expected: command exits `0`. + +- [ ] **Step 6: Commit any verification fixes** + +If Steps 1-5 required fixes, commit those fixes: + +```bash +git add .dockerignore Dockerfile docker-compose.yml README.md +git commit -m "fix: polish docker self-hosting setup" +``` + +If no fixes were required, do not create an empty commit. + +--- + +## Self-Review + +### Spec coverage + +- Root `Dockerfile`: Task 2. +- Root `docker-compose.yml`: Task 3. +- README Docker usage docs: Task 4. +- Unified env alignment: Task 2 and Task 3 use `CAPLETS_SERVER_URL`, `CAPLETS_SERVER_USER`, `CAPLETS_SERVER_PASSWORD`, and XDG state/config paths; Task 4 documents remote clients with the same server URL model. +- Source-build image rather than npm-install-only image: Task 2. +- Configurable external binding: Task 3 and Task 4. +- Named volume default and bind-mount alternative: Task 3 and Task 4. +- Health check: Task 3 and Task 5. +- Security guidance for loopback defaults, HTTPS/TLS, Basic Auth, and server-owned state: Task 3 and Task 4. +- Verification commands: Task 2, Task 3, and Task 5. + +### Placeholder scan + +No placeholder markers are present. Every file creation step includes exact content. Every verification step includes exact commands and expected outcomes. + +### Type and name consistency + +- Compose service name is consistently `caplets`. +- Named volume is consistently `caplets-data`. +- Internal port is consistently `5387`. +- External bind variables are consistently `CAPLETS_BIND_ADDRESS` and `CAPLETS_PORT`. +- Unified server variables are consistently `CAPLETS_SERVER_URL`, `CAPLETS_SERVER_USER`, and `CAPLETS_SERVER_PASSWORD`. diff --git a/docs/plans/2026-05-21-ghcr-release-publishing.md b/docs/plans/2026-05-21-ghcr-release-publishing.md new file mode 100644 index 00000000..01214df9 --- /dev/null +++ b/docs/plans/2026-05-21-ghcr-release-publishing.md @@ -0,0 +1,296 @@ +# GHCR Release Publishing Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Publish the Caplets Docker image to GitHub Container Registry when the existing Changesets release workflow publishes npm packages. + +**Architecture:** Extend `.github/workflows/release.yml` in-place. The release job keeps the current install, verify, and Changesets publish flow, then runs Docker metadata/login/build-push steps only when `changesets/action` reports `published == 'true'` and the published package list includes the `caplets` CLI package. The image is pushed to `ghcr.io/spiritledsoftware/caplets` with `latest`, semantic version, `v`-prefixed semantic version, and short SHA tags. + +**Tech Stack:** GitHub Actions, Changesets, Docker Buildx, GHCR, `docker/login-action`, `docker/metadata-action`, `docker/build-push-action`, Node.js 24, pnpm 11.0.9. + +--- + +## File Structure + +- Modify: `.github/workflows/release.yml` + - Add job-scoped `packages: write` permission for GHCR publishing. + - Add an `id: changesets` to the existing Changesets action step. + - Add a step to check whether the `caplets` CLI package was published, then read `packages/cli/package.json` version for Docker metadata. + - Add QEMU, Docker Buildx, GHCR login, metadata, and build-push steps gated on published CLI releases. +- Verify only: `Dockerfile` + - Existing source-build image is the image pushed by the workflow. + +--- + +### Task 1: Add GHCR permissions and Changesets output ID + +**Files:** + +- Modify: `.github/workflows/release.yml` + +- [ ] **Step 1: Update workflow permissions and Changesets step ID** + +In `.github/workflows/release.yml`, change the permissions block from: + +```yaml +permissions: + contents: write + pull-requests: write + id-token: write +``` + +to keep workflow-level permissions unchanged and add job-scoped package publishing permission: + +```yaml +permissions: + contents: write + pull-requests: write + id-token: write + +jobs: + release: + permissions: + contents: write + pull-requests: write + id-token: write + packages: write +``` + +Then change the Changesets step from: + +```yaml +- name: Create release PR or publish + uses: changesets/action@v1 +``` + +to: + +```yaml +- name: Create release PR or publish + id: changesets + uses: changesets/action@v1 +``` + +- [ ] **Step 2: Verify workflow formatting** + +Run: + +```bash +pnpm format:check .github/workflows/release.yml +``` + +Expected: command exits `0` and reports all matched files use the correct format. + +- [ ] **Step 3: Commit workflow release output plumbing** + +Run: + +```bash +git add .github/workflows/release.yml +git commit -m "ci: expose release publish result" +``` + +Expected: commit succeeds and includes only `.github/workflows/release.yml`. + +--- + +### Task 2: Add Docker image metadata and publishing steps + +**Files:** + +- Modify: `.github/workflows/release.yml` + +- [ ] **Step 1: Insert Docker publish steps after Changesets** + +Immediately after the existing `Create release PR or publish` step, insert: + +```yaml +- name: Check whether CLI package was published + if: steps.changesets.outputs.published == 'true' + id: cli-package + env: + PUBLISHED_PACKAGES: ${{ steps.changesets.outputs.publishedPackages }} + run: | + cli_published=$(node <<'NODE' + const publishedPackages = JSON.parse(process.env.PUBLISHED_PACKAGES || '[]'); + const cliPublished = publishedPackages.some((pkg) => pkg && pkg.name === 'caplets'); + process.stdout.write(cliPublished ? 'true' : 'false'); + NODE + ) + echo "published=${cli_published}" >> "$GITHUB_OUTPUT" + +- name: Read Docker image version + if: steps.changesets.outputs.published == 'true' && steps.cli-package.outputs.published == 'true' + id: image-version + run: echo "version=$(node -p \"require('./packages/cli/package.json').version\")" >> "$GITHUB_OUTPUT" + +- name: Setup QEMU + if: steps.changesets.outputs.published == 'true' && steps.cli-package.outputs.published == 'true' + uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 + +- name: Setup Docker Buildx + if: steps.changesets.outputs.published == 'true' && steps.cli-package.outputs.published == 'true' + uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f + +- name: Log in to GitHub Container Registry + if: steps.changesets.outputs.published == 'true' && steps.cli-package.outputs.published == 'true' + uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + +- name: Generate Docker metadata + if: steps.changesets.outputs.published == 'true' && steps.cli-package.outputs.published == 'true' + id: docker-meta + uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 + with: + images: ghcr.io/spiritledsoftware/caplets + tags: | + type=raw,value=latest + type=raw,value=${{ steps.image-version.outputs.version }} + type=raw,value=v${{ steps.image-version.outputs.version }} + type=sha,format=short,prefix=sha- + +- name: Publish Docker image + if: steps.changesets.outputs.published == 'true' && steps.cli-package.outputs.published == 'true' + uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 + with: + context: . + file: ./Dockerfile + platforms: linux/amd64,linux/arm64 + push: true + tags: ${{ steps.docker-meta.outputs.tags }} + labels: ${{ steps.docker-meta.outputs.labels }} +``` + +- [ ] **Step 2: Verify workflow formatting** + +Run: + +```bash +pnpm format:check .github/workflows/release.yml +``` + +Expected: command exits `0` and reports all matched files use the correct format. + +- [ ] **Step 3: Commit Docker publishing steps** + +Run: + +```bash +git add .github/workflows/release.yml +git commit -m "ci: publish docker image on release" +``` + +Expected: commit succeeds and includes only `.github/workflows/release.yml`. + +--- + +### Task 3: Verify release workflow and Docker image build + +**Files:** + +- Verify: `.github/workflows/release.yml` +- Verify: `Dockerfile` + +- [ ] **Step 1: Run workflow formatting check** + +Run: + +```bash +pnpm format:check .github/workflows/release.yml +``` + +Expected: command exits `0` and reports all matched files use the correct format. + +- [ ] **Step 2: Check workflow contains required release gates and tags** + +Run: + +```bash +node <<'NODE' +const fs = require('node:fs'); +const workflow = fs.readFileSync('.github/workflows/release.yml', 'utf8'); +const required = [ + 'packages: write', + 'id: changesets', + "if: steps.changesets.outputs.published == 'true' && steps.cli-package.outputs.published == 'true'", + 'id: cli-package', + 'uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130', + 'uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9', + 'uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051', + 'uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8', + 'images: ghcr.io/spiritledsoftware/caplets', + 'type=raw,value=latest', + 'type=raw,value=${{ steps.image-version.outputs.version }}', + 'type=raw,value=v${{ steps.image-version.outputs.version }}', + 'type=sha,format=short,prefix=sha-', +]; +const missing = required.filter((entry) => !workflow.includes(entry)); +if (missing.length > 0) { + console.error(`Missing workflow entries:\n${missing.join('\n')}`); + process.exit(1); +} +NODE +``` + +Expected: command exits `0` with no missing workflow entries. + +- [ ] **Step 3: Verify the Docker image still builds** + +Run: + +```bash +docker build -t caplets:self-host-test . +``` + +Expected: command exits `0` and creates image `caplets:self-host-test`. + +If Docker is unavailable, record the exact Docker error in task notes and continue; do not claim Docker build verification passed. + +- [ ] **Step 4: Run focused repo checks** + +Run: + +```bash +pnpm verify +``` + +Expected: command exits `0`. + +- [ ] **Step 5: Commit verification fixes if needed** + +If verification required fixes, run: + +```bash +git add .github/workflows/release.yml +git commit -m "fix: polish docker release publishing" +``` + +Expected: commit succeeds only if fixes were made. If no fixes were required, do not create an empty commit. + +--- + +## Self-Review + +### Spec coverage + +- GHCR target image `ghcr.io/spiritledsoftware/caplets`: Task 2. +- Publish only after a real CLI package release: Tasks 1 and 2 gate on both `steps.changesets.outputs.published == 'true'` and `steps.cli-package.outputs.published == 'true'`. +- Use repository-scoped token and no extra long-lived registry secret: Task 2 uses `docker/login-action` pinned to a full commit SHA with `secrets.GITHUB_TOKEN`. +- Add required GitHub Packages permission: Task 1. +- Tags for `latest`, semantic version, `v` semantic version, and short SHA: Task 2 and Task 3. +- Multi-platform Docker publishing includes QEMU registration before Buildx: Task 2. +- Local verification of workflow and Dockerfile: Task 3. + +### Placeholder scan + +No placeholder markers are present. Every edit step includes exact YAML or exact verification commands. + +### Type and name consistency + +- Changesets step ID is consistently `changesets`. +- Version step ID is consistently `image-version`. +- Docker metadata step ID is consistently `docker-meta`. +- Image name is consistently `ghcr.io/spiritledsoftware/caplets`. diff --git a/docs/specs/2026-05-21-docker-self-hosting-design.md b/docs/specs/2026-05-21-docker-self-hosting-design.md new file mode 100644 index 00000000..f745ad81 --- /dev/null +++ b/docs/specs/2026-05-21-docker-self-hosting-design.md @@ -0,0 +1,94 @@ +# Docker self-hosting design + +## Goal + +Make the HTTP Caplets service easy to self-host from this repository by adding a source-build Docker image and a Docker Compose setup that preserves server-owned Caplets state. + +## Scope + +- Add a root `Dockerfile` that builds the monorepo with the pinned pnpm version and runs the built `caplets` CLI. +- Add a root `docker-compose.yml` for local and self-hosted deployments. +- Document Docker usage in the README. +- Keep runtime configuration aligned with the unified Caplets environment variables: `CAPLETS_MODE`, `CAPLETS_SERVER_URL`, `CAPLETS_SERVER_USER`, and `CAPLETS_SERVER_PASSWORD`. + +## Non-goals + +- Do not publish or maintain a separate registry image in this change. +- Do not add a second npm-install-only Dockerfile. +- Do not change HTTP server behavior or remote control semantics. + +## Dockerfile design + +Use a multi-stage Node image: + +1. Build stage + - Start from an official Node image that satisfies `node >=22`. + - Enable Corepack and activate `pnpm@11.0.9`. + - Copy workspace manifests first for cache-friendly dependency installation. + - Run `pnpm install --frozen-lockfile`. + - Copy the full repository. + - Run `pnpm build`. + +2. Runtime stage + - Start from a smaller official Node runtime image. + - Enable Corepack and activate `pnpm@11.0.9` so workspace-linked runtime execution is predictable. + - Copy only the files needed to run the built CLI and its workspace dependencies. + - Set a durable Caplets home/config location under `/data`. + - Expose port `5387`. + - Default command: `caplets serve --transport http --host 0.0.0.0`. + +The image represents the checked-out source tree, which is better for this repo than installing the latest published `caplets` package from npm. + +## Compose design + +Add a single `caplets` service that builds from `.` and runs the Dockerfile. + +External network binding should be configurable: + +```yaml +ports: + - "${CAPLETS_BIND_ADDRESS:-127.0.0.1}:${CAPLETS_PORT:-5387}:5387" +``` + +This gives safe local-only exposure by default while allowing self-hosters to set `CAPLETS_BIND_ADDRESS=0.0.0.0` for LAN or reverse-proxy deployments. + +Environment variables: + +- `CAPLETS_SERVER_URL` defaults to `http://127.0.0.1:5387` for local use. +- `CAPLETS_SERVER_USER` and `CAPLETS_SERVER_PASSWORD` are passed through for Basic Auth. +- Compose should not hard-code secrets. + +Persistence: + +- Default: Docker named volume mounted at `/data`. +- Document alternative: replace the named volume with `./data:/data` for a host-visible bind mount. + +Health check: + +- Use the service health endpoint (`/healthz` for the default base path). +- Keep the health check unauthenticated, matching the HTTP service design. + +## Documentation design + +Add a README section covering: + +- Building and starting with `docker compose up --build`. +- Local URL and health endpoint. +- Setting `CAPLETS_SERVER_PASSWORD` and optional `CAPLETS_SERVER_USER`. +- Changing `CAPLETS_BIND_ADDRESS` for LAN/reverse proxy exposure. +- Named volume default and bind-mount alternative. +- Connecting clients with `CAPLETS_MODE=remote` and `CAPLETS_SERVER_URL`. + +## Security considerations + +- Default host binding should remain loopback-only. +- LAN/public exposure should require an explicit bind-address change and should strongly recommend `CAPLETS_SERVER_PASSWORD`. +- Remote downstream auth and Caplets state remain server-owned inside the mounted `/data` location. +- Remote control Basic Auth is separate from downstream provider credentials. + +## Testing and verification + +- `docker compose config` validates the Compose file. +- `docker build .` validates the Dockerfile. +- If Docker can run containers locally, start Compose and verify `GET /healthz` returns success. +- Run relevant repo checks after editing, at minimum formatting and type/build checks affected by docs/config changes. diff --git a/docs/specs/2026-05-21-ghcr-release-publishing-design.md b/docs/specs/2026-05-21-ghcr-release-publishing-design.md new file mode 100644 index 00000000..ee9cd4a8 --- /dev/null +++ b/docs/specs/2026-05-21-ghcr-release-publishing-design.md @@ -0,0 +1,30 @@ +# GHCR Release Publishing Design + +## Goal + +Publish the source-build Caplets Docker image to GitHub Container Registry as part of the existing Changesets release pipeline, only when a real package release is published. + +## Current release flow + +The existing `.github/workflows/release.yml` workflow runs on pushes to `main` and manual dispatch. It installs dependencies, runs `pnpm verify`, then uses `changesets/action@v1` to either create/update the version PR or publish npm packages with `pnpm release`. + +## Design + +Use the existing release job and add GHCR publishing steps after the Changesets action. The Docker image publish steps are gated on both `steps.changesets.outputs.published == 'true'` and `steps.cli-package.outputs.published == 'true'`, so they do not run while the workflow is merely creating or updating a version PR or when only non-CLI packages are published. + +The release job will grant `packages: write`, log in to `ghcr.io` using `GITHUB_TOKEN`, register QEMU for multi-platform builds, generate image metadata, and push the existing root `Dockerfile` image to: + +- `ghcr.io/spiritledsoftware/caplets:latest` +- `ghcr.io/spiritledsoftware/caplets:v` +- `ghcr.io/spiritledsoftware/caplets:` +- `ghcr.io/spiritledsoftware/caplets:sha-` + +The package version is read from `packages/cli/package.json` after Changesets has published, because the Docker image represents the CLI service runtime. + +## Security and permissions + +The workflow uses the repository-scoped `GITHUB_TOKEN` and GitHub Actions package permissions. No new long-lived registry token is required. The release job adds only `packages: write`; existing `contents`, `pull-requests`, and `id-token` permissions remain unchanged. + +## Validation + +Validation should cover YAML formatting, workflow syntax sanity, QEMU/Buildx setup ordering, and Docker metadata behavior where practical. Full GHCR push verification can only happen in GitHub Actions on a release publish. Local verification should include `pnpm format:check .github/workflows/release.yml` and a Docker build of the existing image. diff --git a/packages/pi/package.json b/packages/pi/package.json index 5d3a42d2..22fcd43e 100644 --- a/packages/pi/package.json +++ b/packages/pi/package.json @@ -42,8 +42,8 @@ "vitest": "^4.1.6" }, "peerDependencies": { - "@earendil-works/pi-coding-agent": ">=0", - "@earendil-works/pi-tui": ">=0" + "@earendil-works/pi-coding-agent": "*", + "@earendil-works/pi-tui": "*" }, "engines": { "node": ">=22" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c4e477a7..5bc6bdfd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -374,10 +374,10 @@ importers: specifier: workspace:* version: link:../core '@earendil-works/pi-coding-agent': - specifier: '>=0' + specifier: '*' version: 0.74.0(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(ws@8.20.0)(zod@4.4.3) '@earendil-works/pi-tui': - specifier: '>=0' + specifier: '*' version: 0.74.0 devDependencies: '@types/node': @@ -1711,9 +1711,6 @@ packages: '@types/node@12.20.55': resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} - '@types/node@25.7.0': - resolution: {integrity: sha512-z+pdZyxE+RTQE9AcboAZCb4otwcrvgHD+GlBpPgn0emDVt0ohrTMhAwlr2Wd9nZ+nihhYFxO2pThz3C5qSu2Eg==} - '@types/node@25.9.0': resolution: {integrity: sha512-AOQwYUNolgy3VosiRqXrACUXTN8nJUtPl7FJXMqZVyxiiCLhQuG3jXKvCS1ALr+Y2OmZhzzLVlYPEqJaiqkaJQ==} @@ -3155,9 +3152,6 @@ packages: resolution: {integrity: sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==} engines: {node: '>=18'} - undici-types@7.21.0: - resolution: {integrity: sha512-w9IMgQrz4O0YN1LtB7K5P63vhlIOvC7opSmouCJ+ZywlPAlO9gIkJ+otk6LvGpAs2wg4econaCz3TvQ9xPoyuQ==} - undici-types@7.24.6: resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} @@ -4799,10 +4793,6 @@ snapshots: '@types/node@12.20.55': {} - '@types/node@25.7.0': - dependencies: - undici-types: 7.21.0 - '@types/node@25.9.0': dependencies: undici-types: 7.24.6 @@ -4813,7 +4803,7 @@ snapshots: '@types/yauzl@2.10.3': dependencies: - '@types/node': 25.7.0 + '@types/node': 25.9.0 optional: true '@typescript/native-preview-darwin-arm64@7.0.0-dev.20260518.1': @@ -5927,7 +5917,7 @@ snapshots: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.1 - '@types/node': 25.7.0 + '@types/node': 25.9.0 long: 5.3.2 proxy-addr@2.0.7: @@ -6281,8 +6271,6 @@ snapshots: uint8array-extras@1.5.0: {} - undici-types@7.21.0: {} - undici-types@7.24.6: {} undici@7.25.0: {} diff --git a/scripts/commit-brv-submodule.sh b/scripts/commit-brv-submodule.sh new file mode 100755 index 00000000..72a32cce --- /dev/null +++ b/scripts/commit-brv-submodule.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash +set -euo pipefail + +SUBMODULE_PATH=".brv" +SUBMODULE_REMOTE="origin" +COMMIT_MESSAGE="chore: update memory" +PUSH=false +COMMITTED_MEMORY=false + +if [[ "${1:-}" == "--push" ]]; then + PUSH=true +fi + +if [[ ! -d "$SUBMODULE_PATH" ]]; then + exit 0 +fi + +if ! git -C "$SUBMODULE_PATH" rev-parse --is-inside-work-tree >/dev/null 2>&1; then + exit 0 +fi + +git_path() { + git -C "$SUBMODULE_PATH" rev-parse --git-path "$1" +} + +# Do not try to auto-commit while the submodule is in the middle of a manual +# operation. That state needs human resolution. +if [[ -d "$(git_path rebase-merge)" || -d "$(git_path rebase-apply)" || -f "$(git_path MERGE_HEAD)" ]]; then + echo "Skipping $SUBMODULE_PATH auto-commit: submodule has an in-progress git operation." >&2 + exit 1 +fi + +if [[ -n "$(git -C "$SUBMODULE_PATH" status --porcelain --untracked-files=all)" ]]; then + echo "Auto-committing ByteRover memory changes in $SUBMODULE_PATH" + git -C "$SUBMODULE_PATH" add -A + git -C "$SUBMODULE_PATH" commit -m "$COMMIT_MESSAGE" + git add "$SUBMODULE_PATH" + COMMITTED_MEMORY=true +fi + +if [[ "$PUSH" == true ]]; then + branch="$(git -C "$SUBMODULE_PATH" branch --show-current)" + if [[ -n "$branch" ]]; then + # Push any new memory commits before the parent repo publishes a gitlink that + # points at them. + git -C "$SUBMODULE_PATH" push "$SUBMODULE_REMOTE" "$branch" + fi + + if [[ "$COMMITTED_MEMORY" == true ]]; then + if ! git diff --quiet -- "$SUBMODULE_PATH" || ! git diff --cached --quiet -- "$SUBMODULE_PATH"; then + git add "$SUBMODULE_PATH" + git commit --no-verify -m "chore: update byterover memory pointer" + echo "Committed new $SUBMODULE_PATH gitlink. Re-run git push so the new parent commit is sent." >&2 + exit 1 + fi + fi +fi