feat: web ui v2#9
Conversation
Replace the entire CSS design token system: swap OKLCH colors for hex values from DESIGN.md, replace Geist/Bricolage fonts with Inter/JetBrains Mono, and remove all gradients, textures, and shadows to achieve the flat depth model. This is the foundation for the Paper design system. - Rewrite styles.css with hex-based --color-* tokens and shadcn mapping - Swap font packages: @fontsource-variable/inter + @fontsource/jetbrains-mono - Update 35+ component files from var(--ds-*) to var(--color-*) references - Remove .ds-texture-*, .ds-panel*, font-display, box-shadow, color-mix() - Add design token verification tests (styles.test.ts) - All tests pass (365/365), lint and typecheck clean Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace shadcn Sidebar with custom two-zone layout: 40px icon rail (workspace circle avatars, app logo) + 220px collapsible panel (workspace name, search, agent list, Knowledge/Skills nav, system footer). Add Zustand sidebar store, empty skills/knowledge routes, and comprehensive tests. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Update chat header to breadcrumb pattern (status dot > agent > session), message bubbles to right-aligned user bubbles and left-aligned agent messages with JetBrains Mono labels, tool call cards with bordered card styling and DONE/RUNNING/ERROR status badges, message composer with rounded container and circular accent send button, and empty state with 48px terminal icon per Paper spec. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Expose the skills.Registry through the HTTP and UDS API layers: - GET /api/skills?workspace=:id — list all skills for a workspace - GET /api/skills/:name — get skill detail by name - POST /api/skills/:name/enable — enable a skill - POST /api/skills/:name/disable — disable a skill Adds SkillPayload/ProvenancePayload contract types, SkillsRegistry interface, conversion helpers, error mapping, and comprehensive table-driven tests with 81% coverage. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Complete systems/skill/ module following app-renderer-systems pattern: Zod schemas, typed API adapter with SkillApiError, query keys/options, useSkills/useSkill hooks, useEnableSkill/useDisableSkill mutations with cache invalidation. All tests pass with full coverage. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…e view Replaces the empty skills route placeholder with a fully functional Skills page matching the Paper design. Includes INSTALLED tab with grouped skill list (BUNDLED/WORKSPACE/MARKETPLACE sections), detail panel with source badges and enable/disable actions, and MARKETPLACE tab with search and category filter chips. 24 tests with >96% coverage. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…d tests Create systems/knowledge/ module following app-renderer-systems pattern, wrapping existing /api/memory backend endpoints with typed adapter functions, TanStack Query hooks, and 100% test coverage. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…view Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
WalkthroughAdds a new skills subsystem: API DTOs, handlers, and server wiring; a SkillsRegistry interface and registry runtime changes (load content, SetEnabled); integrates skills into daemon and servers; and implements full frontend features (skills + knowledge UIs, hooks, adapters), styling/token migration, and many tests. Changes
Sequence Diagram(s)mermaid Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly related PRs
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
|
There was a problem hiding this comment.
Actionable comments posted: 2
Note
Due to the large number of review comments, Critical severity comments were prioritized as inline comments.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
web/src/systems/session/components/message-bubble.tsx (1)
102-108:⚠️ Potential issue | 🟠 MajorThe memo comparator ignores the new timestamp output.
formatTimestamp(message.timestamp)is now part of the rendered assistant header, but the custom equality function still skipsmessage.timestamp. A timestamp update will leave the old time on screen. Includingmessage.rolehere is also safer now that the render path branches on it.Suggested fix
(prev, next) => + prev.message.role === next.message.role && prev.message.id === next.message.id && prev.message.content === next.message.content && prev.message.thinking === next.message.thinking && prev.message.thinkingComplete === next.message.thinkingComplete && + prev.message.timestamp === next.message.timestamp && prev.message.isStreaming === next.message.isStreaming && prev.agentName === next.agentName🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/systems/session/components/message-bubble.tsx` around lines 102 - 108, The memo comparator function that currently compares prev.message.id, content, thinking, thinkingComplete, isStreaming and prev.agentName must also compare message.timestamp and message.role so timestamp updates and role-based render branches re-render correctly; update the arrow comparator (the custom equality used with React.memo) to include prev.message.timestamp === next.message.timestamp and prev.message.role === next.message.role (or compare formatTimestamp(prev.message.timestamp) === formatTimestamp(next.message.timestamp) if you prefer comparing the rendered string) along with the existing checks.
🟠 Major comments (19)
web/src/systems/knowledge/adapters/knowledge-api.ts-63-73 (1)
63-73:⚠️ Potential issue | 🟠 MajorAdd
AbortSignalparameter to all mutating adapter functions.The
writeMemory,deleteMemory, andconsolidateMemoryfunctions do not accept or passsignalto theirfetchcalls, breaking cancellation propagation from TanStack Query. Other functions in this adapter already implement proper signal threading (see lines 32, 51), establishing the expected pattern.Proposed fix
export async function writeMemory( filename: string, content: string, scope?: string, - workspace?: string + workspace?: string, + signal?: AbortSignal ): Promise<MemoryMutationResponse> { const res = await fetch(`/api/memory/${encodeURIComponent(filename)}`, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ content, scope, workspace }), + signal, }); if (!res.ok) { throw new KnowledgeApiError(`Failed to write memory "${filename}": ${res.status}`, res.status); } const json = await res.json(); return memoryMutationResponseSchema.parse(json); } export async function deleteMemory( scope: string, filename: string, - workspace?: string + workspace?: string, + signal?: AbortSignal ): Promise<MemoryMutationResponse> { const params = new URLSearchParams(); params.set("scope", scope); if (workspace) params.set("workspace", workspace); const url = `/api/memory/${encodeURIComponent(filename)}?${params.toString()}`; - const res = await fetch(url, { method: "DELETE" }); + const res = await fetch(url, { method: "DELETE", signal }); if (!res.ok) { if (res.status === 404) { throw new KnowledgeApiError(`Memory not found: ${filename}`, 404); } throw new KnowledgeApiError(`Failed to delete memory "${filename}": ${res.status}`, res.status); } const json = await res.json(); return memoryMutationResponseSchema.parse(json); } -export async function consolidateMemory(workspace?: string): Promise<MemoryConsolidateResponse> { +export async function consolidateMemory( + workspace?: string, + signal?: AbortSignal +): Promise<MemoryConsolidateResponse> { const res = await fetch("/api/memory/consolidate", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ workspace }), + signal, }); if (!res.ok) { throw new KnowledgeApiError(`Failed to consolidate memory: ${res.status}`, res.status); } const json = await res.json(); return memoryConsolidateResponseSchema.parse(json); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/systems/knowledge/adapters/knowledge-api.ts` around lines 63 - 73, The mutating adapter functions writeMemory, deleteMemory, and consolidateMemory need an AbortSignal parameter so cancellation from TanStack Query propagates; update each function signature to accept an optional signal (e.g., signal?: AbortSignal), thread that signal into the fetch init (pass signal in the options object alongside method/headers/body), and ensure the encoded filename usage (encodeURIComponent(filename)) and JSON body behavior remain unchanged while returning the same MemoryMutationResponse; locate the fetch calls in writeMemory, deleteMemory, and consolidateMemory and add the signal property to their fetch options.web/src/systems/knowledge/adapters/knowledge-api.ts-37-38 (1)
37-38:⚠️ Potential issue | 🟠 MajorNormalize schema-validation failures into
KnowledgeApiError.The direct
.parse()calls on lines 37, 59, 78, 99, and 112 can throw rawZodErrorwhen the server returns a 200 status with invalid JSON structure, violating the adapter requirement to use only typed error classes.Proposed fix pattern
+function parseOrThrow<T>( + result: { success: true; data: T } | { success: false }, + message: string, + status: number +): T { + if (!result.success) { + throw new KnowledgeApiError(message, status); + } + return result.data; +} export async function listMemories( scope?: string, workspace?: string, signal?: AbortSignal ): Promise<MemoryHeader[]> { @@ const res = await fetch(url, { signal }); @@ const json = await res.json(); - return memoryHeaderSchema.array().parse(json); + return parseOrThrow( + memoryHeaderSchema.array().safeParse(json), + "Invalid memories list response", + res.status + ); }Apply the same
safeParse+parseOrThrowpattern to the other four schema parse sites (lines 59, 78, 99, 112).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/systems/knowledge/adapters/knowledge-api.ts` around lines 37 - 38, Replace direct .parse() calls that can throw ZodError (e.g., memoryHeaderSchema.array().parse(json)) with the safeParse + parseOrThrow pattern so validation failures are normalized to KnowledgeApiError; for each schema usage that currently does .parse(json) (the sites using memoryHeaderSchema.array(), and the other four schema parses in this file), call schema.safeParse(json) and pass the result into the existing parseOrThrow helper (or throw new KnowledgeApiError wrapping the ZodError) so all schema validation errors are converted into KnowledgeApiError instead of letting ZodError escape.web/src/systems/knowledge/lib/query-options.test.ts-3-3 (1)
3-3:⚠️ Potential issue | 🟠 MajorUse the project alias import in this test file.
./query-optionsshould be replaced with the@/*path alias form.Proposed fix
-import { memoriesListOptions, memoryDetailOptions } from "./query-options"; +import { memoriesListOptions, memoryDetailOptions } from "@/systems/knowledge/lib/query-options";As per coding guidelines:
Use path alias@/* to map to ./src/* for all imports.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/systems/knowledge/lib/query-options.test.ts` at line 3, Replace the relative import in the test so it uses the project path alias: change the import of memoriesListOptions and memoryDetailOptions from "./query-options" to the alias-based path that maps to src (i.e., import from "@/systems/knowledge/lib/query-options") so the test uses the `@/`* alias and matches project coding guidelines.web/src/systems/knowledge/hooks/use-knowledge.test.tsx-6-16 (1)
6-16:⚠️ Potential issue | 🟠 MajorNormalize test imports/mocks to
@/*aliases.Both direct imports and the
vi.mock(...)module path should follow the alias rule.Proposed fix
-import { useMemories, useMemory } from "./use-knowledge"; +import { useMemories, useMemory } from "@/systems/knowledge/hooks/use-knowledge"; -vi.mock("../adapters/knowledge-api", () => ({ +vi.mock("@/systems/knowledge/adapters/knowledge-api", () => ({ listMemories: vi.fn(), readMemory: vi.fn(), deleteMemory: vi.fn(), writeMemory: vi.fn(), consolidateMemory: vi.fn(), })); -import { listMemories, readMemory } from "../adapters/knowledge-api"; +import { listMemories, readMemory } from "@/systems/knowledge/adapters/knowledge-api";As per coding guidelines:
Use path alias@/* to map to ./src/* for all imports.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/systems/knowledge/hooks/use-knowledge.test.tsx` around lines 6 - 16, Update the test to use the project path alias for the adapters module consistently: change the vi.mock module path and the subsequent named import so they refer to the aliased module (e.g., "@/systems/knowledge/adapters/knowledge-api") instead of the relative "../adapters/knowledge-api"; ensure both the mock declaration and the imported symbols (listMemories, readMemory) match the alias, and keep the existing mocked function names and the test references to useMemories/useMemory unchanged.web/src/systems/knowledge/hooks/use-knowledge-actions.test.tsx-6-16 (1)
6-16:⚠️ Potential issue | 🟠 MajorUse alias-based module paths for imports and mocks.
This file should also use
@/*paths (includingvi.mock(...)) to satisfy the web import convention.Proposed fix
-import { useConsolidateMemory, useDeleteMemory } from "./use-knowledge-actions"; +import { + useConsolidateMemory, + useDeleteMemory, +} from "@/systems/knowledge/hooks/use-knowledge-actions"; -vi.mock("../adapters/knowledge-api", () => ({ +vi.mock("@/systems/knowledge/adapters/knowledge-api", () => ({ listMemories: vi.fn(), readMemory: vi.fn(), deleteMemory: vi.fn(), writeMemory: vi.fn(), consolidateMemory: vi.fn(), })); -import { consolidateMemory, deleteMemory } from "../adapters/knowledge-api"; +import { consolidateMemory, deleteMemory } from "@/systems/knowledge/adapters/knowledge-api";As per coding guidelines:
Use path alias@/* to map to ./src/* for all imports.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/systems/knowledge/hooks/use-knowledge-actions.test.tsx` around lines 6 - 16, The tests use relative paths; change both the hook import and the mocked module to the project alias form so they follow the web convention: import useConsolidateMemory and useDeleteMemory from the aliased hook module (replace "./use-knowledge-actions" with "@/systems/knowledge/hooks/use-knowledge-actions") and update the vi.mock call to mock the aliased adapter module instead of "../adapters/knowledge-api" (mock "@/systems/knowledge/adapters/knowledge-api"), keeping the same exported symbols consolidateMemory and deleteMemory in the mock and import statements.web/src/systems/knowledge/lib/query-options.ts-3-4 (1)
3-4:⚠️ Potential issue | 🟠 MajorReplace relative imports with
@/*aliases in query options.Please use
@/systems/...paths to match the web import standard.Proposed fix
-import { listMemories, readMemory } from "../adapters/knowledge-api"; -import { knowledgeKeys } from "./query-keys"; +import { listMemories, readMemory } from "@/systems/knowledge/adapters/knowledge-api"; +import { knowledgeKeys } from "@/systems/knowledge/lib/query-keys";As per coding guidelines:
Use path alias@/* to map to ./src/* for all imports.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/systems/knowledge/lib/query-options.ts` around lines 3 - 4, Update the imports in query-options.ts to use the project path alias instead of relative paths: replace the import of listMemories and readMemory from "../adapters/knowledge-api" with "@/systems/knowledge/adapters/knowledge-api" and replace the import of knowledgeKeys from "./query-keys" with "@/systems/knowledge/lib/query-keys", keeping the same exported symbol names (listMemories, readMemory, knowledgeKeys) so existing references in this file remain unchanged.web/src/systems/knowledge/hooks/use-knowledge-actions.ts-3-4 (1)
3-4:⚠️ Potential issue | 🟠 MajorUse
@/*imports instead of relative paths in this hook module.Relative imports here break the repo-wide web import convention; switch these to
@/systems/...paths.Proposed fix
-import { consolidateMemory, deleteMemory } from "../adapters/knowledge-api"; -import { knowledgeKeys } from "../lib/query-keys"; +import { consolidateMemory, deleteMemory } from "@/systems/knowledge/adapters/knowledge-api"; +import { knowledgeKeys } from "@/systems/knowledge/lib/query-keys";As per coding guidelines:
Use path alias@/* to map to ./src/* for all imports.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/systems/knowledge/hooks/use-knowledge-actions.ts` around lines 3 - 4, Switch the two relative imports in this hook (consolidateMemory and deleteMemory from "../adapters/knowledge-api" and knowledgeKeys from "../lib/query-keys") to use the repo path alias (e.g., "@/systems/...") so they follow the project convention; locate the import statements that reference consolidateMemory, deleteMemory and knowledgeKeys and replace the "../adapters/knowledge-api" and "../lib/query-keys" paths with the corresponding "@/systems/..." aliased paths.web/src/systems/knowledge/adapters/knowledge-api.test.ts-3-10 (1)
3-10:⚠️ Potential issue | 🟠 MajorSwitch this test import to the
@/*alias.This relative import violates the project’s web import rule.
Proposed fix
} from "./knowledge-api"; +} from "@/systems/knowledge/adapters/knowledge-api";As per coding guidelines:
Use path alias@/* to map to ./src/* for all imports.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/systems/knowledge/adapters/knowledge-api.test.ts` around lines 3 - 10, Replace the relative import in the test so it uses the path alias: change the import statement that currently pulls consolidateMemory, deleteMemory, KnowledgeApiError, listMemories, readMemory, writeMemory from "./knowledge-api" to import them from "@/systems/knowledge/adapters/knowledge-api" so the test conforms to the project's alias rule; update the import line referencing those symbols accordingly.web/src/systems/knowledge/hooks/use-knowledge.ts-3-3 (1)
3-3:⚠️ Potential issue | 🟠 MajorAdopt
@/*import paths in this hook file.The relative import should be normalized to the configured alias.
Proposed fix
-import { memoriesListOptions, memoryDetailOptions } from "../lib/query-options"; +import { memoriesListOptions, memoryDetailOptions } from "@/systems/knowledge/lib/query-options";As per coding guidelines:
Use path alias@/* to map to ./src/* for all imports.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/systems/knowledge/hooks/use-knowledge.ts` at line 3, Replace the relative import of memoriesListOptions and memoryDetailOptions in use-knowledge.ts with the project path-alias import (use the `@/`* alias) so the hook imports those symbols via the configured alias (e.g., import { memoriesListOptions, memoryDetailOptions } from "@/systems/knowledge/lib/query-options") to normalize imports to the configured alias.internal/api/udsapi/server.go-250-255 (1)
250-255:⚠️ Potential issue | 🟠 MajorRequire
skillsRegistryinNewto fail fast.Line 254/Line 460 wires
skillsRegistryinto handlers, butNewdoes not validate it. This can defer dependency failures to runtime on skill routes instead of failing server construction deterministically.✅ Proposed fix
@@ if server.workspaces == nil { return nil, errors.New("udsapi: workspace resolver is required") } + if server.skillsRegistry == nil { + return nil, errors.New("udsapi: skills registry is required") + } if strings.TrimSpace(server.config.Daemon.Socket) == "" { server.config.Daemon.Socket = server.homePaths.DaemonSocket }Based on learnings: "No production users exist — never sacrifice code quality for backward compatibility; never write migration, compat, or defensive code for old state — delete the old thing instead".
Also applies to: 460-460
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/api/udsapi/server.go` around lines 250 - 255, The constructor New should validate that the required dependency skillsRegistry is non-nil so server construction fails fast instead of deferring errors to route handling; update New to check the incoming skillsRegistry parameter and return an error (or panic) when nil, and ensure this validation occurs before wiring server.handlers/newHandlers with handlerConfig (which contains sessions, observer, workspaces, skillsRegistry, memoryStore) so handlers never receive a nil skillsRegistry.web/src/systems/skill/hooks/use-skills.ts-3-3 (1)
3-3: 🛠️ Refactor suggestion | 🟠 MajorUse the project alias import here instead of a relative path.
This should use
@/*imports to stay consistent with repository conventions.Proposed fix
-import { skillDetailOptions, skillsListOptions } from "../lib/query-options"; +import { skillDetailOptions, skillsListOptions } from "@/systems/skill/lib/query-options";As per coding guidelines, "Use path alias
@/*to map to./src/*for all imports".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/systems/skill/hooks/use-skills.ts` at line 3, Replace the relative import of skillDetailOptions and skillsListOptions in use-skills.ts with the project path-alias form; locate the import that brings in skillDetailOptions, skillsListOptions from "../lib/query-options" and update it to use the "@/..." alias that maps to src (e.g., import from the equivalent "@/systems/skill/lib/query-options") so the module uses the repository's `@/`* import convention.web/src/systems/session/components/chat-header.tsx-35-38 (1)
35-38:⚠️ Potential issue | 🟠 MajorDon't make session state color-only.
Replacing the textual badge with a dot removes the state for screen readers and for anyone who can't reliably distinguish the token colors. Keep the dot if you want, but add an accessible text label (
aria-labeland/orsr-onlycontent) forsession.state.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/systems/session/components/chat-header.tsx` around lines 35 - 38, The status dot in chat-header.tsx currently encodes session.state only via color (STATE_DOT_COLOR) which is inaccessible; update the element that uses className={cn("size-2.5 shrink-0 rounded-full", STATE_DOT_COLOR[session.state])} and data-testid="agent-status-dot" to keep the visual dot but add an accessible label and/or hidden text so screen readers read the state—e.g., add an aria-label like `aria-label={`Session state: ${session.state}`}` and/or include an adjacent span with the project's sr-only class containing {session.state} so assistive tech can convey the state.web/src/components/design-system/design-system-showcase.tsx-165-166 (1)
165-166:⚠️ Potential issue | 🟠 MajorFinish the token migration here too.
These surfaces still hardcode
bg-black/10andborder-white/12, which bypasses the design-token layer this PR is otherwise standardizing on. Since this page is acting as a reference surface, it should use semantic--color-*tokens end-to-end.As per coding guidelines, "Use Tailwind CSS v4 with only defined design tokens from DESIGN.md; implement flat depth model without shadows".
Also applies to: 255-255, 296-300, 314-330
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/components/design-system/design-system-showcase.tsx` around lines 165 - 166, The showcased thread card currently hardcodes bg-black/10 and border-white/12 (see the element using key={thread.id} and the other card blocks in this file); replace those hardcoded colors with the semantic design-token CSS variables used elsewhere (use the semantic surface token for the background, e.g. the --color-surface-* token for muted surfaces, and the --color-divider token for borders) so the component uses the tokenized Tailwind/CSS variable classes consistently; apply the same token replacements to the other occurrences in this component (the other card blocks mentioned) to finish the token migration and conform to the flat depth model.web/src/components/app-sidebar.tsx-20-29 (1)
20-29: 🛠️ Refactor suggestion | 🟠 MajorKeep
AppSidebarpresentational and move the orchestration up a level.This component now owns Zustand state, several data hooks, and a mutation. That makes a shared
web/src/componentsview responsible for app orchestration instead of just rendering props.As per coding guidelines, "UI components MUST be pure and presentational; orchestration logic lives in pages/routes" and "Components MUST NOT import from stores/ or adapters directly — pass via props or route context."
Also applies to: 213-223, 307-313, 399-400, 419-425
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/components/app-sidebar.tsx` around lines 20 - 29, AppSidebar is doing orchestration by importing and using hooks/stores (useSidebarStore, useAgents, useDaemonHealth, useCreateSession, useSessions, useWorkspaces) and components tied to state (ConnectionStatus, AgentIcon); refactor by removing all direct imports/usage of stores and data hooks from AppSidebar and instead accept the required data and callbacks as props (e.g., sidebarState, agents, daemonHealth, createSession, sessions, workspaces, onToggle) so AppSidebar becomes purely presentational, then lift the orchestration to the parent route/page which will call useSidebarStore, useAgents, useDaemonHealth, useCreateSession, useSessions, useWorkspaces and pass their results into AppSidebar; apply the same change for the other mentioned blocks (lines referenced) so no stores/hooks are imported inside any component under web/src/components.web/src/components/app-sidebar.tsx-21-29 (1)
21-29: 🛠️ Refactor suggestion | 🟠 MajorImport other systems through their public barrels.
This sidebar now reaches into
agent,daemon, andsessioninternals directly from outside those systems. That couples the shell to private module layout and defeats the system boundary you just added forskill.As per coding guidelines, "Cross-system imports: only through the public barrel (
@/systems/<domain>); never reach into another system's internals."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/components/app-sidebar.tsx` around lines 21 - 29, The imports in app-sidebar.tsx are reaching into internal modules (symbols: AgentIcon, useAgents, AgentPayload, ConnectionStatus, useDaemonHealth, useCreateSession, useSessions, SessionPayload, SessionState as SessionStateType, useWorkspaces, WorkspacePayload); update these to import only from each system's public barrel (e.g., "@/systems/agent", "@/systems/daemon", "@/systems/session", "@/systems/workspace") so the sidebar consumes public exports only, and adjust any names if the barrel exports use different identifiers or re-exports them from each system's index barrel.web/src/components/app-sidebar.tsx-71-72 (1)
71-72: 🛠️ Refactor suggestion | 🟠 MajorReplace raw
#E8572Aclasses with the accent token.This PR is migrating the UI to
--color-*tokens, but the sidebar reintroduces hard-coded accent hex values for the logo, active workspace ring, and active indicators. That will drift from the theme and forces tests to couple to implementation details.As per coding guidelines, "Use Tailwind CSS v4 with only defined design tokens from DESIGN.md; implement flat depth model without shadows."
Also applies to: 89-90, 134-134, 286-286
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/components/app-sidebar.tsx` around lines 71 - 72, Replace hard-coded hex color usages in the AppSidebar JSX with the design token CSS variable (e.g., use var(--color-accent) via Tailwind arbitrary value syntax) so the logo, active workspace ring, and active indicators follow the theme; locate the instances by the className strings such as "mb-3 flex size-8 items-center justify-center rounded-lg bg-[`#E8572A`] text-white" (logo), the workspace item class that includes "ring-[...]" for the active workspace, and the active indicator class that uses "bg-[`#E8572A`]"; update those className values to use bg-[var(--color-accent)] and ring-[var(--color-accent)] (and any similar occurrences noted) instead of the hex literal.web/src/components/app-sidebar.tsx-263-266 (1)
263-266:⚠️ Potential issue | 🟠 MajorRemove the namespace qualifier for
ReactNode— import it directly from React.The file only imports named bindings from React (
import { useMemo, useState } from "react"), soReactis not in scope. UsingReact.ReactNodewithout a namespace import will fail typecheck.Suggested fix
-import { useMemo, useState } from "react"; +import { useMemo, useState, type ReactNode } from "react"; @@ interface NavItemProps { to: string; - icon: React.ReactNode; + icon: ReactNode; label: string; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/components/app-sidebar.tsx` around lines 263 - 266, The NavItemProps interface uses the undefined namespace React.ReactNode; update the module import to bring ReactNode into scope and adjust the type: add ReactNode to the named imports from "react" (so the existing import like `import { useMemo, useState } from "react"` becomes `import { useMemo, useState, ReactNode } from "react"`) and change the interface type to use ReactNode (NavItemProps.icon: ReactNode), or alternatively import the default React and keep React.ReactNode—patch the import and the NavItemProps usage accordingly.web/src/components/app-sidebar.tsx-370-371 (1)
370-371:⚠️ Potential issue | 🟠 MajorFix navigation paths to use valid route targets instead of route IDs.
The
topaths should be/knowledgeand/skills, not/_app/knowledgeand/_app/skills. TanStack Router's file-based routing generates/knowledgeand/skillsas the valid navigation destinations, while/_app/knowledgeand/_app/skillsare route IDs used internally. Using incorrect paths breaks theuseMatchRoutecall, causing the active state detection to fail—users won't see which page they're on.Current code (lines 370–371)
<NavItem to="/_app/knowledge" icon={<Book className="size-3.5" />} label="Knowledge" /> <NavItem to="/_app/skills" icon={<Wrench className="size-3.5" />} label="Skills" />🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/components/app-sidebar.tsx` around lines 370 - 371, The NavItem components are using internal route IDs ("/_app/knowledge" and "/_app/skills") which breaks active-route detection; update the `to` props on the NavItem instances (the ones rendering Book and Wrench icons) to the actual route paths "/knowledge" and "/skills" so useMatchRoute and other router utilities match correctly and the active state displays.web/src/styles.css-86-88 (1)
86-88:⚠️ Potential issue | 🟠 MajorFix stylelint configuration to support Tailwind v4 directives and quote
BlinkMacSystemFont.The
.stylelintrc.jsonlacks configuration for Tailwind v4's@themeand@custom-variantdirectives, andBlinkMacSystemFontin the font-family list needs to be quoted to pass thevalue-keyword-caserule.Required fixes
- Add to
.stylelintrc.json(inside"rules"):"at-rule-no-unknown": ["error", { "ignoreAtRules": ["theme", "custom-variant"] }]
- Quote
BlinkMacSystemFontinweb/src/styles.cssline 87:- --font-sans: "Inter Variable", -apple-system, BlinkMacSystemFont, sans-serif; + --font-sans: "Inter Variable", -apple-system, "BlinkMacSystemFont", sans-serif;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/styles.css` around lines 86 - 88, Update stylelint rules and quote the font keyword: add the at-rule exception for Tailwind v4 by adding the rule "at-rule-no-unknown" with ignoreAtRules including "theme" and "custom-variant" to your .stylelintrc.json rules, and in web/src/styles.css update the --font-sans value to quote BlinkMacSystemFont (i.e., "BlinkMacSystemFont") so it satisfies the value-keyword-case rule; locate the font declaration in the `@theme` block and the stylelint config rule named at-rule-no-unknown to make these edits.
🟡 Minor comments (11)
web/src/components/design-system/status-dot.tsx-9-13 (1)
9-13:⚠️ Potential issue | 🟡 MinorMap
ambertone to warning token, not accent token.On Line 10,
amberis wired to--color-accent(action). For semantic status signaling,ambershould use the warning token.Suggested fix
- amber: "bg-[color:var(--color-accent)]", + amber: "bg-[color:var(--color-warning)]",As per coding guidelines
web/src/**/*.{ts,tsx}: “Follow the signal system for colors: accent#E8572A= action, ...#FFD60A= warning”.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/components/design-system/status-dot.tsx` around lines 9 - 13, The amber tone mapping in the StatusDot color map is using the accent token; update the amber entry so it uses the warning token instead (change the mapping for "amber" from "bg-[color:var(--color-accent)]" to "bg-[color:var(--color-warning)]" in the status-dot component where the color map defines neutral, amber, green, violet, danger).web/src/systems/skill/hooks/use-skill-actions.test.tsx-26-51 (1)
26-51:⚠️ Potential issue | 🟡 MinorCover the rejected mutation path too.
Both cases only prove invalidation after a successful mutation. If either hook accidentally switches from
onSettledtoonSuccess, this suite still passes. Add one rejected-mutation case per hook and keep theinvalidateQueries(...)expectation there as well.As per coding guidelines, "Always invalidate TanStack Query cache after mutations via
onSettled".Also applies to: 63-88
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/systems/skill/hooks/use-skill-actions.test.tsx` around lines 26 - 51, Add a test that covers the rejected-mutation path to ensure cache invalidation still happens in onSettled: in the existing test file add a case for useEnableSkill where vi.mocked(enableSkill).mockRejectedValue(new Error("fail")), call result.current.mutate(...) inside act, waitFor the mutation to be in error (result.current.isError) and assert that the QueryClient invalidateQueries spy was still called with { queryKey: ["skills", "list", "ws_123"] }; do the same pattern for the other hook test(s) that mirror this one (e.g., the disable-skill hook test) so both success and failure paths verify invalidateQueries is invoked from onSettled.web/src/systems/session/components/chat-header.tsx-46-50 (1)
46-50:⚠️ Potential issue | 🟡 MinorEmpty session names will render a blank breadcrumb.
??only falls back fornull/undefined, soname: ""still produces no visible label here. A trimmed falsy fallback is safer for this UI string.Possible fix
- {session.name ?? session.id} + {session.name?.trim() || session.id}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/systems/session/components/chat-header.tsx` around lines 46 - 50, The breadcrumb can render blank when session.name is an empty string because the code uses the nullish coalescing operator; update the rendering logic in the chat header component to treat empty/whitespace names as missing by using a trimmed falsy fallback (e.g., check trimmed session.name and fall back to session.id) for the element that uses session.name ?? session.id (data-testid="session-name").web/src/systems/session/components/tool-call-card.test.tsx-42-45 (1)
42-45:⚠️ Potential issue | 🟡 MinorThis test never verifies the terminal icon.
tool-call-cardis the outer wrapper, so this will still pass if the icon disappears. Assert the icon itself, or add a stable accessible hook for it, otherwise the test name is misleading.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/systems/session/components/tool-call-card.test.tsx` around lines 42 - 45, The test in tool-call-card.test.tsx currently only checks the outer wrapper (getByTestId("tool-call-card")) so it doesn't verify the terminal icon; update the test for the ToolCallCard component (the it block that renders <ToolCallCard message={makeToolMessage()} />) to explicitly assert the terminal icon is present by querying the icon itself (e.g., getByTestId("tool-call-terminal-icon") or getByRole/getByAltText with a stable accessible name) or add a stable data-testid on the icon element inside ToolCallCard to target in the test and replace the loose wrapper assertion with a concrete icon assertion.web/src/systems/skill/adapters/skill-api.test.ts-16-22 (1)
16-22:⚠️ Potential issue | 🟡 MinorAdd
vi.unstubAllGlobals()to clean up global stubs in allafterEachblocks.
vi.restoreAllMocks()only restoresvi.spyOn()spies, not global stubs created withvi.stubGlobal(). Omittingvi.unstubAllGlobals()allows the mockedfetchto leak into subsequent test suites, causing unintended test pollution.Suggested fix
afterEach(() => { vi.restoreAllMocks(); + vi.unstubAllGlobals(); });Also applies to: 85-91, 150-152, 192-194
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/systems/skill/adapters/skill-api.test.ts` around lines 16 - 22, The global fetch stub created in the beforeEach (vi.stubGlobal("fetch", ...)) is not removed by vi.restoreAllMocks(); update the afterEach blocks in web/src/systems/skill/adapters/skill-api.test.ts to call vi.unstubAllGlobals() in addition to vi.restoreAllMocks() so the global stub is cleaned up; apply the same change for all afterEach blocks corresponding to the other beforeEach usages in this file (the blocks around the other vi.stubGlobal("fetch", ...) calls).web/src/systems/knowledge/components/knowledge-detail-panel.tsx-228-234 (1)
228-234:⚠️ Potential issue | 🟡 Minor"View in CLI" button has no onClick handler.
Similar to the "View full content" button, this button has no functionality. Add an onClick handler or indicate this is a placeholder.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/systems/knowledge/components/knowledge-detail-panel.tsx` around lines 228 - 234, The "View in CLI" button in KnowledgeDetailPanel (the JSX button with data-testid="view-in-cli-btn" and the ExternalLink icon) lacks an onClick handler; add a handler similar to the "View full content" button (e.g., a method like handleViewInCli or reuse an existing view handler) that either performs the intended action (open CLI modal/route/copy command) or explicitly marks the button as a placeholder (onClick={() => {/* TODO: implement */}}) and/or disables it (disabled prop and aria-disabled) so its lack of functionality is clear to users and tests can target the handler by data-testid.web/src/systems/skill/components/skill-list-panel.tsx-116-122 (1)
116-122:⚠️ Potential issue | 🟡 MinorPotential runtime error if
descriptionis undefined.The filter at line 120 calls
.toLowerCase()ons.descriptiondirectly. IfSkillPayload.descriptioncan beundefinedornull, this will throw a runtime error. Apply nullish coalescing for safety.🛡️ Proposed defensive fix
const filtered = useMemo(() => { if (!searchQuery) return skills; const q = searchQuery.toLowerCase(); return skills.filter( - s => s.name.toLowerCase().includes(q) || s.description.toLowerCase().includes(q) + s => s.name.toLowerCase().includes(q) || (s.description ?? "").toLowerCase().includes(q) ); }, [skills, searchQuery]);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/systems/skill/components/skill-list-panel.tsx` around lines 116 - 122, The filter in the useMemo for variable filtered calls s.description.toLowerCase() which can throw if SkillPayload.description is undefined/null; update the predicate in the useMemo (the skills.filter callback) to safely handle missing descriptions by coercing description to an empty string (e.g., use s.description ?? '' or optional chaining) before calling toLowerCase, keeping the existing checks for s.name; no other logic changes required.web/src/systems/knowledge/components/knowledge-detail-panel.tsx-63-69 (1)
63-69:⚠️ Potential issue | 🟡 Minor"View full content" button has no onClick handler.
The button is rendered but doesn't have any functionality attached. Consider either implementing the handler or adding a TODO comment indicating this is WIP.
💡 Add placeholder or TODO
{content.length > 300 && ( <button - className="mt-2 text-sm text-[color:var(--color-accent)] hover:text-[color:var(--color-accent-hover)]" + onClick={() => {}} // TODO: Implement full content modal/view + className="mt-2 text-sm text-[color:var(--color-accent)] hover:text-[color:var(--color-accent-hover)] cursor-pointer" data-testid="view-full-content-link" > View full content → </button> )}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/systems/knowledge/components/knowledge-detail-panel.tsx` around lines 63 - 69, The "View full content" button in the KnowledgeDetailPanel component is rendered without any onClick handler; add a handler (e.g., create a handleViewFullContent function inside KnowledgeDetailPanel) and wire it to the button's onClick to either call a passed-in prop callback (like onViewFullContent) or trigger the existing modal/navigation logic for showing full content; if this feature is not yet implemented, add a clear TODO comment next to the button and attach a placeholder onClick that prevents default and logs/returns early so the UI is not dead.web/src/routes/_app/knowledge.tsx-156-162 (1)
156-162:⚠️ Potential issue | 🟡 Minor
formatDreamStatus()always returns "Dream: never" due to missing argument.The function is called without the
lastConsolidationparameter, so it will always display "Dream: never". If this is placeholder UI, consider adding a TODO. Otherwise, wire it to actual consolidation timestamp data.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/routes/_app/knowledge.tsx` around lines 156 - 162, formatDreamStatus() is being called with no arguments so it always returns the default "Dream: never"; fix by passing the actual consolidation timestamp into it (e.g., call formatDreamStatus(lastConsolidation) using the relevant consolidation variable from this component/props/state such as conversation.lastConsolidation or a local lastConsolidation value), or if this UI is a placeholder, add a TODO comment and leave a deliberate placeholder value instead.web/src/systems/skill/components/skill-detail-panel.tsx-209-215 (1)
209-215:⚠️ Potential issue | 🟡 Minor"View in CLI" button has no click handler.
Similar to "View full content", this button is rendered without functionality. Consider adding an
onClickhandler or disabling the button until the feature is implemented.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/systems/skill/components/skill-detail-panel.tsx` around lines 209 - 215, The "View in CLI" button in the SkillDetailPanel component is missing a click handler; add an onClick that either calls an existing action to open the skill in CLI (e.g., a helper like openSkillInCli(skill.id) or a prop method passed into SkillDetailPanel such as onOpenInCli) or, if the feature isn't ready, set the button to disabled and add aria-disabled and a tooltip explaining it's coming soon; ensure you reference the button by its data-testid "view-in-cli-btn" and update the component's props/state (SkillDetailPanel, the relevant handler name: onOpenInCli or openSkillInCli) accordingly so the button performs the action or is clearly disabled.web/src/systems/skill/components/skill-detail-panel.tsx-64-66 (1)
64-66:⚠️ Potential issue | 🟡 Minor"View full content" button lacks click handler.
The button renders but has no
onClickhandler, making it non-functional. If this is intentional placeholder code, consider disabling it or adding a TODO comment.Proposed fix
+ {content.length > 300 && ( + <button + onClick={() => {/* TODO: implement full content view */}} + className="mt-2 text-sm text-[color:var(--color-accent)] hover:text-[color:var(--color-accent-hover)]" + > View full content </button> )}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/systems/skill/components/skill-detail-panel.tsx` around lines 64 - 66, The "View full content" button in SkillDetailPanel is missing an onClick handler; either wire it up to the component's expand/show flow (e.g., call a passed prop like onViewFull or toggle local state such as setIsExpanded / setShowFullContent inside the SkillDetailPanel component) or explicitly disable it and add a TODO comment. Update the button element to include onClick={() => onViewFull?.()} if you expect a parent callback, or onClick={() => setShowFullContent(true)} if using local state, or add disabled aria-disabled and a // TODO: implement view full content comment to mark it as intentionally non-functional.
🧹 Nitpick comments (7)
web/src/routes/-_app.test.tsx (1)
37-40: Test name is stronger than what is asserted.The test says “inside the content area” but currently only checks outlet presence. Consider renaming it or adding a containment assertion.
Suggested minimal rename
- it("renders outlet inside the content area", () => { + it("renders outlet", () => {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/routes/-_app.test.tsx` around lines 37 - 40, The test name "renders outlet inside the content area" is stronger than the assertion; update the test in the AppLayout.spec by either renaming the it() string to "renders outlet" or add a containment assertion using the existing outlet lookup (getByTestId("outlet")) and the content container lookup (e.g., getByTestId("content-area") or the actual container test id used in AppLayout) and assert containment (e.g., expect(contentContainer).toContainElement(outlet)) so the name matches the behavior.web/src/routes/_app/skills.tsx (1)
160-165: Marketplace install handler is stubbed.The
onInstall={() => {}}andisInstalling={false}are placeholder implementations. If this is intentional WIP, consider adding a TODO comment or disabling the install button in the MarketplaceView to avoid user confusion.💡 Optional: Add a TODO comment for clarity
<MarketplaceView skills={skills ?? []} installedSkillNames={installedSkillNames} - onInstall={() => {}} + onInstall={() => {}} // TODO: Implement marketplace install flow isInstalling={false} />🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/routes/_app/skills.tsx` around lines 160 - 165, The Marketplace install props are stubbed — replace the no-op with a real install handler and loading state: add a useState hook (e.g., const [isInstalling, setIsInstalling] = useState(false)) and implement a handler function (e.g., async function handleInstallSkill(skillId) { setIsInstalling(true); try { await installSkill(skillId); /* update installedSkillNames/state */ } finally { setIsInstalling(false); } }) and pass <MarketplaceView onInstall={handleInstallSkill} isInstalling={isInstalling} />; alternatively, if this is intentionally WIP, add a clear TODO comment next to the MarketplaceView and disable the install button in MarketplaceView until the handler (handleInstallSkill) and isInstalling state are implemented.web/src/systems/skill/components/skill-detail-panel.tsx (1)
24-46: Consider extracting badge color map to a shared constant or using CSS variables.The
colorMaphardcodes hex values with opacity suffixes (e.g.,#30d15826). While functional, this could drift from design tokens. If these badge colors are reused elsewhere, consider defining them as CSS variables or a shared constant.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/systems/skill/components/skill-detail-panel.tsx` around lines 24 - 46, The SourceBadge component currently hardcodes the badge colorMap inside the function (colorMap with hex+alpha values), which can drift from design tokens; extract this mapping into a shared constant or CSS variables so it can be reused and maintained centrally. Move the colorMap out of SourceBadge (e.g., export const BADGE_COLORS or map to CSS custom properties) and update SourceBadge to reference that shared constant or use class names/CSS vars instead of inline hex strings; ensure to preserve the keys (bundled, workspace, marketplace, user, additional) so existing references to source remain valid.web/src/routes/_app/-knowledge.test.tsx (1)
272-285: Delete mutation assertion could be more specific.The assertion uses
expect.any(String)for bothfilenameandscope. Consider asserting the actual expected values based on the auto-selected first memory (user_role.md,global).More specific assertion
expect(mockDeleteMutate).toHaveBeenCalledWith( - expect.objectContaining({ - filename: expect.any(String), - scope: expect.any(String), - }) + { + filename: "user_role.md", + scope: "global", + workspace: "ws_test", + } );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/routes/_app/-knowledge.test.tsx` around lines 272 - 285, The test "detail panel Delete button calls useDeleteMemory mutation" is too loose—replace the generic expect.any(String) assertions for filename and scope with the concrete expected values for the auto-selected first memory: assert that mockDeleteMutate was called with an objectContaining { filename: "user_role.md", scope: "global" } after clicking the delete button (targets: mockDeleteMutate, "delete-memory-btn", KnowledgePage).web/src/systems/skill/components/marketplace-view.tsx (1)
138-147: Category filter description fallback may produce false positives.When tags aren't available, the filter falls back to checking if the description contains the category string (e.g., "testing"). This could match unintended skills (e.g., a skill mentioning "testing" in its description but not actually in that category).
Consider either removing the fallback or making it stricter (e.g., word-boundary match).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/systems/skill/components/marketplace-view.tsx` around lines 138 - 147, The category filter in marketplace-view.tsx currently falls back from checking s.metadata?.tags to matching activeCategory against s.description, which causes false positives; update the filter in the activeCategory branch (the result.filter callback using activeCategory, s.metadata?.tags and s.description) to stop using a raw substring fallback — either remove the description fallback entirely and only accept items with Array.isArray(tags) that match the category, or replace the fallback with a stricter word-boundary, case-insensitive match (e.g., build a regex using the lowercased activeCategory with \b boundaries) so only whole-word category matches are accepted.internal/api/core/skills_test.go (1)
338-367: Enable/Disable tests verify response but not actual state mutation.The tests confirm the endpoint returns
ok: true, but don't verify thatskill.Enabledwas actually toggled. If the handler is supposed to persist the state change, consider asserting the skill's enabled state after the call.Example: verify state change
t.Run("returns ok true on success", func(t *testing.T) { t.Parallel() skill := testSkill() skill.Enabled = false registry := &stubSkillsRegistry{ GetFn: func(name string) (*skills.Skill, bool) { if name == "test-skill" { return skill, true } return nil, false }, } engine := newSkillsHandlerFixture(t, registry, testutil.StubWorkspaceService{}) rec := testutil.PerformRequest(t, engine, http.MethodPost, "/api/skills/test-skill/enable?workspace=ws-1", nil) if rec.Code != http.StatusOK { t.Fatalf("status = %d, want %d; body=%s", rec.Code, http.StatusOK, rec.Body.String()) } + // Verify state was mutated + if !skill.Enabled { + t.Error("skill.Enabled = false after enable, want true") + } + var resp contract.SkillActionResponse🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/api/core/skills_test.go` around lines 338 - 367, The test TestEnableSkill only asserts the HTTP response but not that the skill's state changed; after decoding the response in TestEnableSkill (using the local skill variable returned by testSkill and the stub registry), add an assertion that skill.Enabled is true (i.e., verify the handler mutated/persisted the skill state). Locate the same pattern around TestDisableSkill (if present) and similarly assert skill.Enabled is false after calling the disable endpoint; use the existing skill, registry (stubSkillsRegistry), and newSkillsHandlerFixture references to perform the assertions.web/src/styles.css (1)
43-79: Map the shadcn aliases to the new palette tokens.These semantic vars are repeating raw hex values instead of consuming the
--color-*tokens defined above, which makes the palette easier to drift the next time someone updates a color.Suggested cleanup
- --background: `#121212`; - --foreground: `#e5e5e7`; - --card: `#1c1c1e`; - --card-foreground: `#e5e5e7`; - --popover: `#2c2c2e`; - --popover-foreground: `#e5e5e7`; - --primary: `#e8572a`; + --background: var(--color-canvas); + --foreground: var(--color-text-primary); + --card: var(--color-surface); + --card-foreground: var(--color-text-primary); + --popover: var(--color-surface-elevated); + --popover-foreground: var(--color-text-primary); + --primary: var(--color-accent); --primary-foreground: `#ffffff`; --secondary: transparent; - --secondary-foreground: `#e5e5e7`; - --muted: `#2c2c2e`; - --muted-foreground: `#8e8e93`; - --accent: `#2c2c2e`; - --accent-foreground: `#e5e5e7`; - --destructive: `#ff453a`; - --destructive-foreground: `#e5e5e7`; - --border: `#3a3a3c`; - --input: `#3a3a3c`; - --ring: `#e8572a`; + --secondary-foreground: var(--color-text-primary); + --muted: var(--color-surface-elevated); + --muted-foreground: var(--color-text-secondary); + --accent: var(--color-surface-elevated); + --accent-foreground: var(--color-text-primary); + --destructive: var(--color-danger); + --destructive-foreground: var(--color-text-primary); + --border: var(--color-divider); + --input: var(--color-divider); + --ring: var(--color-accent);As per coding guidelines,
web/src/**/*.{tsx,css}: Use Tailwind CSS v4 with only defined design tokens from DESIGN.md; implement flat depth model without shadows🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/styles.css` around lines 43 - 79, Replace the hardcoded hex values in the shadcn semantic variables (e.g., --background, --foreground, --card, --primary, --muted, --accent, --destructive, --border, --input, --ring, --chart-1..-5, --sidebar, --sidebar-foreground, --sidebar-primary, --sidebar-accent, --sidebar-border, --success, etc.) so they reference the existing design palette tokens (the --color-* variables) instead of raw hex literals; update each declaration in styles.css to use var(--color-<semantic>) (e.g., --background: var(--color-background); --primary: var(--color-primary); --muted: var(--color-muted); --destructive: var(--color-destructive); --chart-1: var(--color-chart-1); etc.), ensuring all shadcn aliases consume the normalized --color-* tokens and follow the flat token model per DESIGN.md.
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (10)
web/src/systems/session/components/message-bubble.tsx (2)
16-19: Consider defensive handling for invalid timestamps.If
message.timestampisundefined,NaN, or0, this will produce unexpected output ("Invalid Date" or epoch time). Consider adding a guard or fallback.🛡️ Optional defensive fix
function formatTimestamp(ts: number): string { + if (!ts || !Number.isFinite(ts)) return ""; const d = new Date(ts); return d.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/systems/session/components/message-bubble.tsx` around lines 16 - 19, The formatTimestamp function should defensively handle invalid timestamps (undefined, NaN, 0) to avoid "Invalid Date" or epoch output: in formatTimestamp(ts: number) validate ts (e.g., check typeof ts === "number" && isFinite(ts) && ts > 0) and fall back to a sensible value such as Date.now() or an empty string; update any call sites that pass message.timestamp (e.g., where message.timestamp is used) to rely on the sanitized output from formatTimestamp so UI never displays "Invalid Date" or epoch time.
81-88: Consider extracting duplicate prose styles to a shared constant.The same prose class configuration appears in both user and assistant bubbles (lines 41-46 and 82-87). Extracting to a constant would reduce duplication.
♻️ Optional DRY refactor
+const proseClasses = cn( + "prose prose-sm prose-invert max-w-none", + "prose-p:my-1 prose-headings:mb-2 prose-headings:mt-4", + "prose-ul:my-1 prose-ol:my-1 prose-li:my-0.5", + "prose-pre:my-0 prose-pre:bg-transparent prose-pre:p-0" +); + export const MessageBubble = memo(Then use it in both bubbles:
<div className={cn(proseClasses, "text-sm leading-relaxed text-[color:var(--color-text-primary)]")}>Also applies to: 39-47
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/systems/session/components/message-bubble.tsx` around lines 81 - 88, The prose class string is duplicated between the user and assistant message bubbles in message-bubble.tsx; extract the repeated CN string into a shared constant (e.g., const proseClasses = "prose prose-sm prose-invert max-w-none prose-p:my-1 prose-headings:mb-2 prose-headings:mt-4 prose-ul:my-1 prose-ol:my-1 prose-li:my-0.5 prose-pre:my-0 prose-pre:bg-transparent prose-pre:p-0") and replace both className calls (the divs in the user and assistant branches inside the MessageBubble component) to use cn(proseClasses, "<per-bubble text color/size classes>") so only the per-bubble overrides remain; update any nearby references to cn or imports if necessary.web/src/systems/session/components/tool-call-card.test.tsx (1)
47-51: Optional: avoid duplicate DOM lookup in the executing-state test.You can query
tool-card-executingonce and reuse the element to keep the test slightly cleaner.Proposed small cleanup
it("shows tool name in executing state", () => { render(<ToolCallCard message={makeToolMessage()} />); - expect(screen.getByTestId("tool-card-executing")).toBeInTheDocument(); - expect(screen.getByTestId("tool-card-executing")).toHaveTextContent("Reading..."); + const executing = screen.getByTestId("tool-card-executing"); + expect(executing).toBeInTheDocument(); + expect(executing).toHaveTextContent("Reading..."); });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/systems/session/components/tool-call-card.test.tsx` around lines 47 - 51, The test "shows tool name in executing state" in ToolCallCard should avoid duplicate DOM lookups: query the element with test id "tool-card-executing" once (e.g., store the result of screen.getByTestId("tool-card-executing") in a variable) and then run both assertions against that variable so you reuse the element instead of calling getByTestId twice.web/src/systems/knowledge/adapters/knowledge-api.test.ts (1)
179-247: Consider adding filename encoding test forwriteMemoryfor parity.The
readMemorytests include an explicit test for filename URL encoding (line 166-176), butwriteMemorylacks this. Since the implementation does encode the filename, adding a similar test would improve coverage consistency.💡 Optional test to add
+ it("encodes filename in URL", async () => { + vi.mocked(fetch).mockResolvedValue({ + ok: true, + json: () => Promise.resolve({ ok: true }), + } as Response); + + await writeMemory("my file.md", "content", "global"); + expect(fetch).toHaveBeenCalledWith("/api/memory/my%20file.md", expect.any(Object)); + });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/systems/knowledge/adapters/knowledge-api.test.ts` around lines 179 - 247, Add a unit test for writeMemory that verifies filenames are URL-encoded: call writeMemory with a filename containing spaces/special characters (e.g., "my file `@1.md`") and a sample content/scope/workspace, mock fetch to return a successful JSON response, then assert fetch was called with the encoded path (e.g., "/api/memory/my%20file%20%401.md") and the expected options (method "PUT", Content-Type header, JSON stringified body, and signal if provided). Target the existing writeMemory tests (same describe block) and mirror the shape of the readMemory filename-encoding test to ensure parity.web/src/systems/knowledge/components/knowledge-detail-panel.tsx (1)
113-118: Movescopeorchestration out of the component.This component derives mutation input (
scope) and feeds it intoonDelete, which mixes orchestration into a presentational layer. Prefer passingscope(or a pre-bound delete handler) from the route/page.As per coding guidelines,
UI components MUST be pure and presentational; orchestration logic lives in pages/routes.Also applies to: 179-183, 225-225
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/systems/knowledge/components/knowledge-detail-panel.tsx` around lines 113 - 118, The component contains orchestration by deriving a deletion scope via deriveScope(mem: MemoryHeader) and calling onDelete with that computed scope; pull that logic out of the presentational component (KnowledgeDetailPanel) and instead accept either a pre-computed scope prop or a pre-bound delete handler (e.g., onDeleteWithScope) from the route/page that owns orchestration. Remove calls to deriveScope inside the component (and any uses at the other noted locations) and update the component props signature to take scope: string or onDeleteBound: (id: string) => Promise<void>, then update the parent/route to compute deriveScope(mem) and pass the result or bound handler into the component.web/src/systems/skill/components/skill-detail-panel.tsx (2)
3-4: Consider consistent import ordering.The relative import (
../types) comes before the alias import (@/lib/utils). While not breaking, consistent ordering (aliases first or relatives first) improves readability.♻️ Reorder imports
+import { cn } from "@/lib/utils"; + import type { SkillPayload } from "../types"; -import { cn } from "@/lib/utils";🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/systems/skill/components/skill-detail-panel.tsx` around lines 3 - 4, The imports in skill-detail-panel.tsx are out of the preferred order (relative import of SkillPayload before alias import of cn); reorder them so alias imports come first — e.g., import { cn } from "@/lib/utils" before importing type { SkillPayload } from "../types" — to keep import ordering consistent and readable.
67-91: Content preview truncation could use a named constant.The magic number
300appears twice. Extracting it improves maintainability.♻️ Extract constant
+const CONTENT_PREVIEW_MAX_LENGTH = 300; + function ContentPreviewCard({ content }: { content: string }) { - const preview = content.length > 300 ? `${content.slice(0, 300)}...` : content; + const preview = content.length > CONTENT_PREVIEW_MAX_LENGTH + ? `${content.slice(0, CONTENT_PREVIEW_MAX_LENGTH)}...` + : content; // ... - {content.length > 300 && ( + {content.length > CONTENT_PREVIEW_MAX_LENGTH && (🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/systems/skill/components/skill-detail-panel.tsx` around lines 67 - 91, The magic number 300 in ContentPreviewCard should be extracted to a named constant (e.g., CONTENT_PREVIEW_LIMIT or PREVIEW_CHAR_LIMIT) and used in place of the two literal occurrences; update the preview calculation (currently using content.slice(0, 300)) and the conditional check (content.length > 300) to reference that constant so future changes only require updating one identifier.web/src/components/app-sidebar.test.tsx (1)
43-53: Collapsible mock ignoresdefaultOpenprop.The mock always renders
data-state="open"regardless of thedefaultOpenprop value. This could mask bugs where the component relies on collapsed-by-default behavior (e.g., agents with zero sessions should start collapsed per the actual component logic at Line 150 of app-sidebar.tsx).♻️ Optional: Respect defaultOpen in mock
vi.mock("@/components/ui/collapsible", () => ({ - Collapsible: ({ children, className }: { children: ReactNode; className?: string }) => ( - <div className={className} data-state="open"> + Collapsible: ({ children, className, defaultOpen = true }: { children: ReactNode; className?: string; defaultOpen?: boolean }) => ( + <div className={className} data-state={defaultOpen ? "open" : "closed"}> {children} </div> ),🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/components/app-sidebar.test.tsx` around lines 43 - 53, The Collapsible mock always forces data-state="open" and ignores the defaultOpen prop, which hides collapse-by-default behavior used in app-sidebar (e.g., agents with zero sessions); update the vi.mock for Collapsible to accept a defaultOpen prop on the Collapsible component and set data-state conditionally (e.g., "open" when defaultOpen is true, otherwise "closed"), preserving the existing CollapsibleContent and CollapsibleTrigger shapes so tests reflect real collapsed vs. open states used by the app-sidebar component.internal/api/core/skills_test.go (1)
19-55: Share this stub viainternal/api/testutil.This helper is now duplicated with
internal/api/udsapi/helpers_test.goLines 32-65. Moving it intointernal/api/testutilwill keepcore.SkillsRegistryfixture behavior in one place and prevent the copies from drifting on the next interface change.As per coding guidelines, "Use shared test helpers from
internal/testutilandinternal/api/testutil."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/api/core/skills_test.go` around lines 19 - 55, The stubSkillsRegistry test helper is duplicated; extract the type and its methods (stubSkillsRegistry, Get, List, ForWorkspace, SetEnabled) into a shared package internal/api/testutil and replace the duplicate in internal/api/udsapi/helpers_test.go by importing that package; update tests to use testutil.stubSkillsRegistry (or the exported name you choose), remove the duplicate definition from both places, and run tests to ensure imports and package names are correct so future core.SkillsRegistry interface changes stay centralized.internal/skills/registry_test.go (1)
1113-1157: Cover theSetEnablederror branches with subtests.This only exercises the happy-path toggle. The new contract also rejects blank names and unknown skills, and those are the branches that will drive handler error mapping. Please fold this into
t.Run("Should...")cases so the full behavior is pinned down.As per coding guidelines, "Use table-driven tests with subtests (
t.Run) as default" and "MUST use t.Run("Should...") pattern for ALL test cases."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/skills/registry_test.go` around lines 1113 - 1157, Refactor TestRegistrySetEnabled into table-driven subtests using t.Run("Should ...") so it covers not only the happy-path toggle but also the error branches: call registry.SetEnabled with a blank name and with an unknown skill name and assert the expected error responses; keep the existing happy-path checks for globalSkills, wsCache, and registry.cfg.DisabledSkills, and ensure each case references registry.SetEnabled and inspects registry.globalSkills, registry.wsCache["ws-1"].skills, and registry.cfg.DisabledSkills to validate state changes or returned errors.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@internal/api/core/interfaces.go`:
- Around line 51-56: SetEnabled on the SkillsRegistry is underspecified for
workspace-scoped skills and can’t distinguish toggling a workspace override from
the global/shared skill; change the SkillsRegistry interface to carry workspace
identity (e.g., replace SetEnabled(name string, enabled bool) error with
something that includes workspace context or a unique skill id — for example
SetEnabled(name string, workspace *workspacepkg.ResolvedWorkspace, enabled bool)
error or SetEnabledByID(skillID string, enabled bool) error), update the
implementation sites that call SetEnabled (notably the code that uses
ForWorkspace and the tests in registry_test.go), and adjust
internal/api/core/skills.go usage so enable/disable operates on the intended
workspace-scoped skill rather than mutating all same-name copies.
In `@internal/skills/registry.go`:
- Around line 187-229: The DisabledSkills slice is mutated under r.mu in
SetEnabled while applyDisabled (used by loadGlobalSkills/loadWorkspaceSkills and
by ForWorkspace/RefreshGlobal) reads it without locking, causing races; fix by
introducing a locked snapshot or accessor: add a method (e.g.,
getDisabledSkillsSnapshot) that acquires r.mu and returns a shallow copy of
r.cfg.DisabledSkills, then update loadGlobalSkills, loadWorkspaceSkills,
ForWorkspace, RefreshGlobal and any applyDisabled callers to use that snapshot
(or accept the slice as a parameter) instead of reading r.cfg.DisabledSkills
directly so all reads use the copied slice and races are eliminated.
In `@web/src/systems/knowledge/components/knowledge-detail-panel.tsx`:
- Line 5: Change the relative import of MemoryHeader in
knowledge-detail-panel.tsx to use the project path-alias form (start with "@/")
instead of "../types"; update the import that currently references MemoryHeader
to import from the aliased module path (e.g., "@/...") so it follows the "Use
path alias `@/`* to map to ./src/*" guideline and keeps the symbol MemoryHeader
intact.
- Around line 120-131: formatDateTime currently constructs a Date from dateStr
but relies on a try/catch that never fires for invalid dates; update
formatDateTime to create the Date once (e.g., const date = new Date(dateStr)),
check validity with Number.isNaN(date.getTime()) and if invalid return the
original dateStr, otherwise call date.toLocaleString(...) to format; ensure you
reference the formatDateTime function when making the change.
---
Nitpick comments:
In `@internal/api/core/skills_test.go`:
- Around line 19-55: The stubSkillsRegistry test helper is duplicated; extract
the type and its methods (stubSkillsRegistry, Get, List, ForWorkspace,
SetEnabled) into a shared package internal/api/testutil and replace the
duplicate in internal/api/udsapi/helpers_test.go by importing that package;
update tests to use testutil.stubSkillsRegistry (or the exported name you
choose), remove the duplicate definition from both places, and run tests to
ensure imports and package names are correct so future core.SkillsRegistry
interface changes stay centralized.
In `@internal/skills/registry_test.go`:
- Around line 1113-1157: Refactor TestRegistrySetEnabled into table-driven
subtests using t.Run("Should ...") so it covers not only the happy-path toggle
but also the error branches: call registry.SetEnabled with a blank name and with
an unknown skill name and assert the expected error responses; keep the existing
happy-path checks for globalSkills, wsCache, and registry.cfg.DisabledSkills,
and ensure each case references registry.SetEnabled and inspects
registry.globalSkills, registry.wsCache["ws-1"].skills, and
registry.cfg.DisabledSkills to validate state changes or returned errors.
In `@web/src/components/app-sidebar.test.tsx`:
- Around line 43-53: The Collapsible mock always forces data-state="open" and
ignores the defaultOpen prop, which hides collapse-by-default behavior used in
app-sidebar (e.g., agents with zero sessions); update the vi.mock for
Collapsible to accept a defaultOpen prop on the Collapsible component and set
data-state conditionally (e.g., "open" when defaultOpen is true, otherwise
"closed"), preserving the existing CollapsibleContent and CollapsibleTrigger
shapes so tests reflect real collapsed vs. open states used by the app-sidebar
component.
In `@web/src/systems/knowledge/adapters/knowledge-api.test.ts`:
- Around line 179-247: Add a unit test for writeMemory that verifies filenames
are URL-encoded: call writeMemory with a filename containing spaces/special
characters (e.g., "my file `@1.md`") and a sample content/scope/workspace, mock
fetch to return a successful JSON response, then assert fetch was called with
the encoded path (e.g., "/api/memory/my%20file%20%401.md") and the expected
options (method "PUT", Content-Type header, JSON stringified body, and signal if
provided). Target the existing writeMemory tests (same describe block) and
mirror the shape of the readMemory filename-encoding test to ensure parity.
In `@web/src/systems/knowledge/components/knowledge-detail-panel.tsx`:
- Around line 113-118: The component contains orchestration by deriving a
deletion scope via deriveScope(mem: MemoryHeader) and calling onDelete with that
computed scope; pull that logic out of the presentational component
(KnowledgeDetailPanel) and instead accept either a pre-computed scope prop or a
pre-bound delete handler (e.g., onDeleteWithScope) from the route/page that owns
orchestration. Remove calls to deriveScope inside the component (and any uses at
the other noted locations) and update the component props signature to take
scope: string or onDeleteBound: (id: string) => Promise<void>, then update the
parent/route to compute deriveScope(mem) and pass the result or bound handler
into the component.
In `@web/src/systems/session/components/message-bubble.tsx`:
- Around line 16-19: The formatTimestamp function should defensively handle
invalid timestamps (undefined, NaN, 0) to avoid "Invalid Date" or epoch output:
in formatTimestamp(ts: number) validate ts (e.g., check typeof ts === "number"
&& isFinite(ts) && ts > 0) and fall back to a sensible value such as Date.now()
or an empty string; update any call sites that pass message.timestamp (e.g.,
where message.timestamp is used) to rely on the sanitized output from
formatTimestamp so UI never displays "Invalid Date" or epoch time.
- Around line 81-88: The prose class string is duplicated between the user and
assistant message bubbles in message-bubble.tsx; extract the repeated CN string
into a shared constant (e.g., const proseClasses = "prose prose-sm prose-invert
max-w-none prose-p:my-1 prose-headings:mb-2 prose-headings:mt-4 prose-ul:my-1
prose-ol:my-1 prose-li:my-0.5 prose-pre:my-0 prose-pre:bg-transparent
prose-pre:p-0") and replace both className calls (the divs in the user and
assistant branches inside the MessageBubble component) to use cn(proseClasses,
"<per-bubble text color/size classes>") so only the per-bubble overrides remain;
update any nearby references to cn or imports if necessary.
In `@web/src/systems/session/components/tool-call-card.test.tsx`:
- Around line 47-51: The test "shows tool name in executing state" in
ToolCallCard should avoid duplicate DOM lookups: query the element with test id
"tool-card-executing" once (e.g., store the result of
screen.getByTestId("tool-card-executing") in a variable) and then run both
assertions against that variable so you reuse the element instead of calling
getByTestId twice.
In `@web/src/systems/skill/components/skill-detail-panel.tsx`:
- Around line 3-4: The imports in skill-detail-panel.tsx are out of the
preferred order (relative import of SkillPayload before alias import of cn);
reorder them so alias imports come first — e.g., import { cn } from
"@/lib/utils" before importing type { SkillPayload } from "../types" — to keep
import ordering consistent and readable.
- Around line 67-91: The magic number 300 in ContentPreviewCard should be
extracted to a named constant (e.g., CONTENT_PREVIEW_LIMIT or
PREVIEW_CHAR_LIMIT) and used in place of the two literal occurrences; update the
preview calculation (currently using content.slice(0, 300)) and the conditional
check (content.length > 300) to reference that constant so future changes only
require updating one identifier.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 7ff968ab-23bc-4190-8667-5ddd3c4818f5
📒 Files selected for processing (38)
internal/api/core/interfaces.gointernal/api/core/skills.gointernal/api/core/skills_test.gointernal/api/udsapi/helpers_test.gointernal/api/udsapi/server.gointernal/api/udsapi/server_test.gointernal/skills/registry.gointernal/skills/registry_test.goweb/src/components/app-sidebar.test.tsxweb/src/components/app-sidebar.tsxweb/src/components/design-system/design-system-showcase.tsxweb/src/components/design-system/status-dot.tsxweb/src/routes/-_app.test.tsxweb/src/routes/_app.tsxweb/src/routes/_app/-knowledge.test.tsxweb/src/routes/_app/knowledge.tsxweb/src/routes/_app/skills.tsxweb/src/styles.cssweb/src/styles.test.tsweb/src/systems/knowledge/adapters/knowledge-api.test.tsweb/src/systems/knowledge/adapters/knowledge-api.tsweb/src/systems/knowledge/components/knowledge-detail-panel.tsxweb/src/systems/knowledge/hooks/use-knowledge-actions.test.tsxweb/src/systems/knowledge/hooks/use-knowledge-actions.tsweb/src/systems/knowledge/hooks/use-knowledge.test.tsxweb/src/systems/knowledge/hooks/use-knowledge.tsweb/src/systems/knowledge/lib/query-options.test.tsweb/src/systems/knowledge/lib/query-options.tsweb/src/systems/session/components/chat-header.tsxweb/src/systems/session/components/message-bubble.tsxweb/src/systems/session/components/tool-call-card.test.tsxweb/src/systems/session/components/tool-call-card.tsxweb/src/systems/skill/adapters/skill-api.test.tsweb/src/systems/skill/components/marketplace-view.tsxweb/src/systems/skill/components/skill-detail-panel.tsxweb/src/systems/skill/components/skill-list-panel.tsxweb/src/systems/skill/hooks/use-skill-actions.test.tsxweb/src/systems/skill/hooks/use-skills.ts
✅ Files skipped from review due to trivial changes (7)
- web/src/components/design-system/status-dot.tsx
- web/src/systems/skill/hooks/use-skills.ts
- web/src/systems/knowledge/hooks/use-knowledge.ts
- web/src/systems/knowledge/lib/query-options.test.ts
- web/src/systems/knowledge/lib/query-options.ts
- web/src/routes/_app/-knowledge.test.tsx
- web/src/systems/knowledge/hooks/use-knowledge-actions.test.tsx
🚧 Files skipped from review as they are similar to previous changes (14)
- web/src/routes/-_app.test.tsx
- internal/api/udsapi/server.go
- web/src/routes/_app.tsx
- web/src/systems/knowledge/hooks/use-knowledge-actions.ts
- web/src/components/design-system/design-system-showcase.tsx
- web/src/systems/knowledge/hooks/use-knowledge.test.tsx
- web/src/systems/session/components/chat-header.tsx
- web/src/systems/skill/hooks/use-skill-actions.test.tsx
- web/src/systems/skill/adapters/skill-api.test.ts
- web/src/systems/skill/components/skill-list-panel.tsx
- web/src/styles.test.ts
- web/src/systems/session/components/tool-call-card.tsx
- web/src/systems/skill/components/marketplace-view.tsx
- web/src/routes/_app/knowledge.tsx
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (3)
web/src/routes/_app/-knowledge.test.tsx (1)
51-55: MakeuseMemoriesmock parameter-aware to actually validate tab filtering.The current mock returns the same dataset regardless of
scope/workspace, so the tab tests mainly validate styling, not route-to-hook filtering behavior.Suggested test-mock refinement
- useMemories: () => ({ - data: mockMemories, + useMemories: (scope?: string) => ({ + data: mockMemories.filter(memory => { + const isWorkspace = memory.filename.startsWith("workspace/") || memory.filename.startsWith("ws/"); + if (!scope || scope === "all") return true; + if (scope === "workspace") return isWorkspace; + if (scope === "global") return !isWorkspace; + return true; + }), isLoading: mockMemoriesLoading, error: mockMemoriesError, }),Based on learnings: "Check dependent package APIs before writing integration code or tests".
Also applies to: 167-186
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/routes/_app/-knowledge.test.tsx` around lines 51 - 55, The useMemories mock currently returns mockMemories regardless of incoming params, so update the mock implementation for useMemories to be parameter-aware (inspect its arguments like scope/workspace) and return a filtered array based on those params (e.g., filter mockMemories by memory.scope or memory.workspace), then adjust the tests to call the component with the appropriate route/query params and assert the filtered results; apply the same change to the other mocked hook(s) referenced around the later block (lines 167-186) so all tab/filter tests validate real route->hook behavior rather than only styling.internal/api/httpapi/server.go (1)
241-249: Consider validatingskillsRegistryif it's a required dependency.The server validates
sessions,observer, andworkspacesas required but does not validateskillsRegistry. If skills endpoints are called without a registry, this could result in nil pointer dereferences in handlers. If the registry is intentionally optional (for backward compatibility), consider adding nil checks in the handlers.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/api/httpapi/server.go` around lines 241 - 249, The init validation currently checks server.sessions, server.observer, and server.workspaces but omits server.skillsRegistry; add a nil check for skillsRegistry alongside the existing checks (i.e., return an error like "httpapi: skills registry is required") if the registry must be present, or if it is intentionally optional, add guards in the skills-related handlers (the handlers referencing skillsRegistry) to return a clear error response when skillsRegistry is nil. Locate the initialization code that checks server.sessions/server.observer/server.workspaces and either add the skillsRegistry validation there or add nil checks inside the skills endpoints' handler functions to avoid nil pointer dereferences.internal/api/testutil/apitest.go (1)
210-251: Add compile-time interface verification forStubSkillsRegistry.The file includes compile-time interface checks for other stubs (lines 407-409) but not for
StubSkillsRegistry.♻️ Proposed fix
Add at the end of the file (after line 409):
var _ core.SkillsRegistry = StubSkillsRegistry{}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/api/testutil/apitest.go` around lines 210 - 251, Add a compile-time interface assertion to ensure StubSkillsRegistry implements core.SkillsRegistry by adding the line creating the var assertion for these types (var _ core.SkillsRegistry = StubSkillsRegistry{}) to the end of the file; locate the StubSkillsRegistry type and append that var assertion after its method definitions so the compiler verifies the implementation.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@web/src/routes/_app/-knowledge.test.tsx`:
- Line 68: The test imports Route via a relative path ("./knowledge"); update
the import to use the project path alias instead (use the `@/`* map to src/*) so
the import reads from the aliased module and keeps the same exported symbol
(Route) — e.g., replace the current "./knowledge" import with the corresponding
"@/routes/_app/knowledge" aliased import and keep the named import "Route"
unchanged.
In `@web/src/routes/_app/knowledge.tsx`:
- Around line 35-47: The function formatDreamStatus can produce "NaN" when given
invalid date strings because new Date(...) doesn't throw; update
formatDreamStatus to validate the parsed date (e.g., const date = new
Date(lastConsolidation); if (isNaN(date.getTime())) return "Dream: unknown";)
before computing diffMs and diffH, so malformed timestamps return the fallback
message rather than producing "NaN" output.
In `@web/src/routes/_app/skills.tsx`:
- Around line 39-40: The code sets activeWorkspaceId = workspaces?.[0]?.id ?? ""
which yields an empty string when no workspace exists and causes downstream
skill API hooks to request with ?workspace=; change this to produce undefined
when no workspace (e.g., const activeWorkspaceId = workspaces?.[0]?.id ??
undefined) and update all places that call the skill API hooks to only pass
activeWorkspaceId when it is truthy or to skip/inhibit the hook calls when
activeWorkspaceId is undefined (for example wrap calls to the skill hooks or
conditionally render components depending on useWorkspaces and
activeWorkspaceId). Ensure the hooks that receive workspace param are invoked
only when activeWorkspaceId is valid.
In `@web/src/systems/skill/adapters/skill-api.test.ts`:
- Around line 154-188: Add tests for error handling in getSkillContent: add one
test that mocks fetch returning { ok: false, status: 404 } and asserts
getSkillContent throws a SkillApiError (or rejects) for not-found, and another
test that mocks fetch returning ok: false with a non-404 status (e.g., 500) and
asserts it throws a SkillApiError for general non-2xx responses; reference the
getSkillContent function and SkillApiError in the assertions and ensure the
fetch call URL encoding behavior is preserved in these error tests.
---
Nitpick comments:
In `@internal/api/httpapi/server.go`:
- Around line 241-249: The init validation currently checks server.sessions,
server.observer, and server.workspaces but omits server.skillsRegistry; add a
nil check for skillsRegistry alongside the existing checks (i.e., return an
error like "httpapi: skills registry is required") if the registry must be
present, or if it is intentionally optional, add guards in the skills-related
handlers (the handlers referencing skillsRegistry) to return a clear error
response when skillsRegistry is nil. Locate the initialization code that checks
server.sessions/server.observer/server.workspaces and either add the
skillsRegistry validation there or add nil checks inside the skills endpoints'
handler functions to avoid nil pointer dereferences.
In `@internal/api/testutil/apitest.go`:
- Around line 210-251: Add a compile-time interface assertion to ensure
StubSkillsRegistry implements core.SkillsRegistry by adding the line creating
the var assertion for these types (var _ core.SkillsRegistry =
StubSkillsRegistry{}) to the end of the file; locate the StubSkillsRegistry type
and append that var assertion after its method definitions so the compiler
verifies the implementation.
In `@web/src/routes/_app/-knowledge.test.tsx`:
- Around line 51-55: The useMemories mock currently returns mockMemories
regardless of incoming params, so update the mock implementation for useMemories
to be parameter-aware (inspect its arguments like scope/workspace) and return a
filtered array based on those params (e.g., filter mockMemories by memory.scope
or memory.workspace), then adjust the tests to call the component with the
appropriate route/query params and assert the filtered results; apply the same
change to the other mocked hook(s) referenced around the later block (lines
167-186) so all tab/filter tests validate real route->hook behavior rather than
only styling.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 8ee90373-1cfb-424c-8355-4ce865526b88
⛔ Files ignored due to path filters (32)
.codex/plans/2026-04-09-skill-progressive-disclosure.mdis excluded by!**/*.md.compozy/tasks/extensability/analysis.mdis excluded by!**/*.md.compozy/tasks/extensability/analysis/analysis_claude_code.mdis excluded by!**/*.md.compozy/tasks/extensability/analysis/analysis_cross_cutting.mdis excluded by!**/*.md.compozy/tasks/extensability/analysis/analysis_goclaw.mdis excluded by!**/*.md.compozy/tasks/extensability/analysis/analysis_hermes.mdis excluded by!**/*.md.compozy/tasks/extensability/analysis/analysis_openclaw.mdis excluded by!**/*.md.compozy/tasks/extensability/analysis/analysis_openfang.mdis excluded by!**/*.md.compozy/tasks/extensability/analysis/analysis_pi_mono.mdis excluded by!**/*.md.compozy/tasks/extensability/analysis/analysis_research_extensibility.mdis excluded by!**/*.mddocs/ideas/from-claude-code/_meta.mdis excluded by!**/*.mddocs/ideas/from-claude-code/analysis_memory_autonomous.mdis excluded by!**/*.mddocs/ideas/from-claude-code/analysis_multi_agent.mdis excluded by!**/*.mddocs/ideas/from-claude-code/analysis_prompt_architecture.mdis excluded by!**/*.mddocs/ideas/from-claude-code/analysis_query_engine.mdis excluded by!**/*.mddocs/ideas/from-claude-code/analysis_services_infra.mdis excluded by!**/*.mddocs/ideas/from-claude-code/analysis_tool_system.mdis excluded by!**/*.mddocs/ideas/from-claude-code/filtered_recommendations.mdis excluded by!**/*.mddocs/ideas/from-claude-code/our_system_cli.mdis excluded by!**/*.mddocs/ideas/from-claude-code/our_system_infra.mdis excluded by!**/*.mddocs/ideas/from-claude-code/our_system_kernel.mdis excluded by!**/*.mddocs/ideas/network/agora-council_round1.mdis excluded by!**/*.mddocs/ideas/network/agora-council_round2.mdis excluded by!**/*.mddocs/ideas/network/agora-recipe-design.mdis excluded by!**/*.mddocs/ideas/network/agora-spec-v0.1.mdis excluded by!**/*.mddocs/ideas/network/agora-spec-v0.2.mdis excluded by!**/*.mddocs/ideas/network/draft_1.mdis excluded by!**/*.mddocs/ideas/network/draft_2.mdis excluded by!**/*.mddocs/ideas/network/draft_3.mdis excluded by!**/*.mddocs/ideas/network/draft_4.mdis excluded by!**/*.mddocs/ideas/network/draft_5.mdis excluded by!**/*.mddocs/ideas/orchestration/multi-agent-patterns-analysis.mdis excluded by!**/*.md
📒 Files selected for processing (41)
.gitignoreinternal/api/contract/contract.gointernal/api/core/conversions.gointernal/api/core/interfaces.gointernal/api/core/skills.gointernal/api/core/skills_test.gointernal/api/httpapi/handlers_test.gointernal/api/httpapi/server.gointernal/api/testutil/apitest.gointernal/api/udsapi/handlers_test.gointernal/api/udsapi/helpers_test.gointernal/api/udsapi/routes.gointernal/cli/skill.gointernal/skills/bundled/bundled_test.gointernal/skills/loader.gointernal/skills/loader_test.gointernal/skills/registry.gointernal/skills/registry_test.gointernal/skills/types.goweb/src/components/app-sidebar.test.tsxweb/src/routes/_app/-knowledge.test.tsxweb/src/routes/_app/-skills.test.tsxweb/src/routes/_app/knowledge.tsxweb/src/routes/_app/skills.tsxweb/src/systems/knowledge/adapters/knowledge-api.test.tsweb/src/systems/knowledge/components/knowledge-detail-panel.tsxweb/src/systems/session/components/message-bubble.test.tsxweb/src/systems/session/components/message-bubble.tsxweb/src/systems/session/components/tool-call-card.test.tsxweb/src/systems/skill/adapters/skill-api.test.tsweb/src/systems/skill/adapters/skill-api.tsweb/src/systems/skill/components/skill-detail-panel.tsxweb/src/systems/skill/hooks/use-skill-actions.test.tsxweb/src/systems/skill/hooks/use-skill-actions.tsweb/src/systems/skill/hooks/use-skills.test.tsxweb/src/systems/skill/hooks/use-skills.tsweb/src/systems/skill/index.tsweb/src/systems/skill/lib/query-keys.tsweb/src/systems/skill/lib/query-options.tsweb/src/systems/skill/types.test.tsweb/src/systems/skill/types.ts
✅ Files skipped from review due to trivial changes (10)
- .gitignore
- internal/api/udsapi/helpers_test.go
- internal/api/udsapi/handlers_test.go
- internal/api/udsapi/routes.go
- internal/api/contract/contract.go
- web/src/systems/session/components/tool-call-card.test.tsx
- web/src/systems/knowledge/adapters/knowledge-api.test.ts
- web/src/routes/_app/-skills.test.tsx
- internal/api/core/skills_test.go
- web/src/systems/skill/types.ts
🚧 Files skipped from review as they are similar to previous changes (16)
- internal/api/core/conversions.go
- web/src/systems/skill/hooks/use-skill-actions.ts
- web/src/systems/session/components/message-bubble.test.tsx
- web/src/systems/skill/hooks/use-skills.ts
- web/src/systems/skill/types.test.ts
- web/src/systems/skill/lib/query-keys.ts
- web/src/systems/skill/hooks/use-skills.test.tsx
- web/src/systems/skill/hooks/use-skill-actions.test.tsx
- internal/api/core/interfaces.go
- web/src/systems/skill/lib/query-options.ts
- internal/skills/registry_test.go
- internal/api/httpapi/handlers_test.go
- web/src/systems/knowledge/components/knowledge-detail-panel.tsx
- internal/skills/registry.go
- web/src/systems/skill/components/skill-detail-panel.tsx
- internal/api/core/skills.go
| }; | ||
| }); | ||
|
|
||
| import { Route } from "./knowledge"; |
There was a problem hiding this comment.
Use @/* alias import instead of relative route import.
Please replace ./knowledge with the configured alias path to keep import style consistent.
As per coding guidelines web/src/**/*.{ts,tsx}: "Use path alias @/* (maps to ./src/*) for all module imports".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/routes/_app/-knowledge.test.tsx` at line 68, The test imports Route
via a relative path ("./knowledge"); update the import to use the project path
alias instead (use the `@/`* map to src/*) so the import reads from the aliased
module and keeps the same exported symbol (Route) — e.g., replace the current
"./knowledge" import with the corresponding "@/routes/_app/knowledge" aliased
import and keep the named import "Route" unchanged.
| function formatDreamStatus(lastConsolidation?: string): string { | ||
| if (!lastConsolidation) return "Dream: status unavailable"; | ||
| try { | ||
| const date = new Date(lastConsolidation); | ||
| const diffMs = Date.now() - date.getTime(); | ||
| const diffH = Math.floor(diffMs / (1000 * 60 * 60)); | ||
| if (diffH < 1) return "Dream: <1h ago"; | ||
| if (diffH < 24) return `Dream: ${diffH}h ago`; | ||
| const diffD = Math.floor(diffH / 24); | ||
| return `Dream: ${diffD}d ago`; | ||
| } catch { | ||
| return "Dream: unknown"; | ||
| } |
There was a problem hiding this comment.
Handle invalid date strings explicitly in formatDreamStatus.
new Date(...) does not throw on invalid input, so the current code can return Dream: NaNd ago (Line 44) for malformed timestamps.
Proposed fix
function formatDreamStatus(lastConsolidation?: string): string {
if (!lastConsolidation) return "Dream: status unavailable";
- try {
- const date = new Date(lastConsolidation);
- const diffMs = Date.now() - date.getTime();
- const diffH = Math.floor(diffMs / (1000 * 60 * 60));
- if (diffH < 1) return "Dream: <1h ago";
- if (diffH < 24) return `Dream: ${diffH}h ago`;
- const diffD = Math.floor(diffH / 24);
- return `Dream: ${diffD}d ago`;
- } catch {
+ try {
+ const timestamp = new Date(lastConsolidation).getTime();
+ if (Number.isNaN(timestamp)) return "Dream: unknown";
+ const diffMs = Date.now() - timestamp;
+ if (diffMs < 0) return "Dream: unknown";
+ const diffH = Math.floor(diffMs / (1000 * 60 * 60));
+ if (diffH < 1) return "Dream: <1h ago";
+ if (diffH < 24) return `Dream: ${diffH}h ago`;
+ const diffD = Math.floor(diffH / 24);
+ return `Dream: ${diffD}d ago`;
+ } catch {
return "Dream: unknown";
}
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/routes/_app/knowledge.tsx` around lines 35 - 47, The function
formatDreamStatus can produce "NaN" when given invalid date strings because new
Date(...) doesn't throw; update formatDreamStatus to validate the parsed date
(e.g., const date = new Date(lastConsolidation); if (isNaN(date.getTime()))
return "Dream: unknown";) before computing diffMs and diffH, so malformed
timestamps return the fallback message rather than producing "NaN" output.
| const { data: workspaces } = useWorkspaces(); | ||
| const activeWorkspaceId = workspaces?.[0]?.id ?? ""; |
There was a problem hiding this comment.
Consider guarding against empty workspace.
When workspaces is undefined or empty, activeWorkspaceId defaults to "". This empty string is then passed to all skill API hooks, which will make requests with ?workspace= (empty). Depending on backend behavior, this could cause errors or return unexpected results.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/routes/_app/skills.tsx` around lines 39 - 40, The code sets
activeWorkspaceId = workspaces?.[0]?.id ?? "" which yields an empty string when
no workspace exists and causes downstream skill API hooks to request with
?workspace=; change this to produce undefined when no workspace (e.g., const
activeWorkspaceId = workspaces?.[0]?.id ?? undefined) and update all places that
call the skill API hooks to only pass activeWorkspaceId when it is truthy or to
skip/inhibit the hook calls when activeWorkspaceId is undefined (for example
wrap calls to the skill hooks or conditionally render components depending on
useWorkspaces and activeWorkspaceId). Ensure the hooks that receive workspace
param are invoked only when activeWorkspaceId is valid.
| describe("getSkillContent", () => { | ||
| beforeEach(() => { | ||
| vi.stubGlobal("fetch", vi.fn()); | ||
| }); | ||
|
|
||
| afterEach(() => { | ||
| vi.restoreAllMocks(); | ||
| vi.unstubAllGlobals(); | ||
| }); | ||
|
|
||
| it("calls GET /api/skills/:name/content?workspace=:id and returns content string", async () => { | ||
| vi.mocked(fetch).mockResolvedValue({ | ||
| ok: true, | ||
| json: () => Promise.resolve({ content: "full skill content" }), | ||
| } as Response); | ||
|
|
||
| const result = await getSkillContent("test-skill", "ws_123"); | ||
| expect(result).toBe("full skill content"); | ||
| expect(fetch).toHaveBeenCalledWith("/api/skills/test-skill/content?workspace=ws_123", { | ||
| signal: undefined, | ||
| }); | ||
| }); | ||
|
|
||
| it("encodes skill name in content URL", async () => { | ||
| vi.mocked(fetch).mockResolvedValue({ | ||
| ok: true, | ||
| json: () => Promise.resolve({ content: "full skill content" }), | ||
| } as Response); | ||
|
|
||
| await getSkillContent("my skill", "ws_123"); | ||
| expect(fetch).toHaveBeenCalledWith("/api/skills/my%20skill/content?workspace=ws_123", { | ||
| signal: undefined, | ||
| }); | ||
| }); | ||
| }); |
There was a problem hiding this comment.
Missing error handling tests for getSkillContent.
The getSkillContent implementation handles 404 and other non-2xx responses by throwing SkillApiError, but this test block only covers success cases. Other functions (getSkill, enableSkill, disableSkill) all have tests for 404 and non-2xx error handling.
💡 Suggested tests to add
it("encodes skill name in content URL", async () => {
vi.mocked(fetch).mockResolvedValue({
ok: true,
json: () => Promise.resolve({ content: "full skill content" }),
} as Response);
await getSkillContent("my skill", "ws_123");
expect(fetch).toHaveBeenCalledWith("/api/skills/my%20skill/content?workspace=ws_123", {
signal: undefined,
});
});
+
+ it("throws SkillApiError with 404 for unknown skill", async () => {
+ vi.mocked(fetch).mockResolvedValue({
+ ok: false,
+ status: 404,
+ } as Response);
+
+ await expect(getSkillContent("unknown", "ws_123")).rejects.toThrow("Skill not found: unknown");
+ });
+
+ it("throws SkillApiError on non-2xx response", async () => {
+ vi.mocked(fetch).mockResolvedValue({
+ ok: false,
+ status: 500,
+ } as Response);
+
+ await expect(getSkillContent("test-skill", "ws_123")).rejects.toThrow(SkillApiError);
+ });
});📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| describe("getSkillContent", () => { | |
| beforeEach(() => { | |
| vi.stubGlobal("fetch", vi.fn()); | |
| }); | |
| afterEach(() => { | |
| vi.restoreAllMocks(); | |
| vi.unstubAllGlobals(); | |
| }); | |
| it("calls GET /api/skills/:name/content?workspace=:id and returns content string", async () => { | |
| vi.mocked(fetch).mockResolvedValue({ | |
| ok: true, | |
| json: () => Promise.resolve({ content: "full skill content" }), | |
| } as Response); | |
| const result = await getSkillContent("test-skill", "ws_123"); | |
| expect(result).toBe("full skill content"); | |
| expect(fetch).toHaveBeenCalledWith("/api/skills/test-skill/content?workspace=ws_123", { | |
| signal: undefined, | |
| }); | |
| }); | |
| it("encodes skill name in content URL", async () => { | |
| vi.mocked(fetch).mockResolvedValue({ | |
| ok: true, | |
| json: () => Promise.resolve({ content: "full skill content" }), | |
| } as Response); | |
| await getSkillContent("my skill", "ws_123"); | |
| expect(fetch).toHaveBeenCalledWith("/api/skills/my%20skill/content?workspace=ws_123", { | |
| signal: undefined, | |
| }); | |
| }); | |
| }); | |
| describe("getSkillContent", () => { | |
| beforeEach(() => { | |
| vi.stubGlobal("fetch", vi.fn()); | |
| }); | |
| afterEach(() => { | |
| vi.restoreAllMocks(); | |
| vi.unstubAllGlobals(); | |
| }); | |
| it("calls GET /api/skills/:name/content?workspace=:id and returns content string", async () => { | |
| vi.mocked(fetch).mockResolvedValue({ | |
| ok: true, | |
| json: () => Promise.resolve({ content: "full skill content" }), | |
| } as Response); | |
| const result = await getSkillContent("test-skill", "ws_123"); | |
| expect(result).toBe("full skill content"); | |
| expect(fetch).toHaveBeenCalledWith("/api/skills/test-skill/content?workspace=ws_123", { | |
| signal: undefined, | |
| }); | |
| }); | |
| it("encodes skill name in content URL", async () => { | |
| vi.mocked(fetch).mockResolvedValue({ | |
| ok: true, | |
| json: () => Promise.resolve({ content: "full skill content" }), | |
| } as Response); | |
| await getSkillContent("my skill", "ws_123"); | |
| expect(fetch).toHaveBeenCalledWith("/api/skills/my%20skill/content?workspace=ws_123", { | |
| signal: undefined, | |
| }); | |
| }); | |
| it("throws SkillApiError with 404 for unknown skill", async () => { | |
| vi.mocked(fetch).mockResolvedValue({ | |
| ok: false, | |
| status: 404, | |
| } as Response); | |
| await expect(getSkillContent("unknown", "ws_123")).rejects.toThrow("Skill not found: unknown"); | |
| }); | |
| it("throws SkillApiError on non-2xx response", async () => { | |
| vi.mocked(fetch).mockResolvedValue({ | |
| ok: false, | |
| status: 500, | |
| } as Response); | |
| await expect(getSkillContent("test-skill", "ws_123")).rejects.toThrow(SkillApiError); | |
| }); | |
| }); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/systems/skill/adapters/skill-api.test.ts` around lines 154 - 188, Add
tests for error handling in getSkillContent: add one test that mocks fetch
returning { ok: false, status: 404 } and asserts getSkillContent throws a
SkillApiError (or rejects) for not-found, and another test that mocks fetch
returning ok: false with a non-404 status (e.g., 500) and asserts it throws a
SkillApiError for general non-2xx responses; reference the getSkillContent
function and SkillApiError in the assertions and ensure the fetch call URL
encoding behavior is preserved in these error tests.
## Release v0.0.1 This PR prepares the release of version v0.0.1. ### Changelog ## 0.0.1 - 2026-05-26 ### Other Changes - Lessons learned ### ♻️ Refactoring - Project structure (#7) - Kb improvements (#12) - Rename spaces to channels (#17) - Add extensions gaps (#21) - Improve tool calls ui (#22) - Remove web app header - Module improvements (#29) - Memory improvements (#35) - Storybook for web and ui (#38) - Enable AGH network by default for new installs (#57) - Hermes adjustments (#69) - Badges design (#84) - Storybook scenario and logos gallery - Migrate typescript tests (#114) - Internal go packages (#120) - Ui patterns (#127) - Improve e2e tests (#130) - Ui redesign - Workspace isolation across runtime surfaces (#145) - Prod ready applies (#162) - Tool card ui (#164) - Alpha on logo - Prod ready features (#167) - Thread sheet (#202) ### 🎉 Features - Implement config foundation packages - Implement sqlite store package - Add ACP client package - Add session lifecycle manager - Implement observe package - Add daemon composition root - Add uds api server - Implement cli package - Add http api server - Add system design - Add foundation types, schemas, and layout shell for web client - Add daemon health polling and agent sidebar systems for web client - Add session system CRUD, streaming core, and session store for web client - Add chat view, messages, and composer tests for web client - Add tool cards and renderers for web client - Add file-backed memory store core - Scaffold memory session seams - Add memory dream consolidation service - Wire memory assembler into daemon - Add memory api and cli - New skills system (#1) - Add workspace entity (#5) - Add new skill capabilities (#8) - Web ui v2 (#9) - Improve hooks system (#10) - Session resilience (#11) - Add extensability (#13) - Add automation (#16) - Add channels (#14) - Add network implementation (#15) - Add network, bridges and automations web pages (#18) - Ext registry (#20) - Add core tasks (#19) - Bridge adapters (#23) - Add site (#26) - Add ext refac and sandbox (#25) - Settings ui (#37) - Tasks ui (#36) - Harness improvements (#44) - Agent capabilities (#49) - Redesign ui (#48) - Unify capability (#53) - Redesign network workspace (#59) - Add task deletion and split session delete from stop (#58) - Session provider selection (#60) - Production grade adjustments (#66) - Autonomous system (#75) - Add agent session route (#80) - Tools registry (#85) - Agents soul (#88) - Add network threads (#105) - Orchestration improvements (#106) - Memory v2 (#108) - Agent categories (#113) - Providers model (#118) - Add canonical AGH bundled skill (#143) - Onboarding and improvements (#198) - Onboarding and improvements (#201) ### 🐛 Bug Fixes - Review round - Review rounds - Resolve memory extensibility review batch - Embed web into daemon - Defaults agents - Acp integration (#4) - Lint errors - Prd folder - Remove orphan web actions and dead surfaces (#55) - Qa testing and fixes (#73) - New review rounds (#82) - Security audit (#90) - Release qa round (#95) - Add missing tools (#141) - New qa round (#147) - Advanced qa round (#149) - Homebrew tap - Final review round (#151) - Daemon healthy - Reasoning models (#158) - Lint errors (#160) - Review round (#168) - Release adjustments (#171) - Stabilize release ci fixtures - Stabilize release integration gate - Stabilize release verify gates - Stabilize release integration flows - Stabilize release verify gates - Stabilize main verify shutdown - Ignore stale acpmock cancel - Marketplace search focus and filtering (#193) - Website video - Workspace command select ### 📚 Documentation - Update agents.md - Update prd - Update skills - Update compozy tasks - Update compozy - Update compozy - Add new skills - Archive prd - Update prds - Update rfc - Update prds - Update prds - Add automation prd - Channels prd - Update prd - Update prd - New prds - Archive prds - Bridges adapters prd - Sandbox prd - Update - Archive prd - Update - Add new prd - New design - Update prd - Archive prds - Update prds - Tasks-ui prd tasks - Update prd - Update design docs - Agent capabilities prd - Improve site docs - Remove old design references - Udpate - Autonomous prd - Update skills - Blog design - Agent sould prd - Final qa plan - Update - Remove codex ledgers from gitignore - Remove not needed files - Udpate ledger - Update cy-codex-loop skill - Orchestration improves prd - Update prds - Orch improvs prd - Memv2 prd - Providers model prd - Update refacs prd - New design proposal - Update rules - Update skills - New blog posts (#173) - Format docs - Remove old design files - Remove old - Skeeper update ### 📦 Build System - Initial structure - Commitlint - Frontend base structure - Update vscode settings - Add subagents - Coderabbit - Prd and tooling - Bun lock - Lint tooling - Copy.md and tooling adjusts - Add repoclone rc - Upgrade skeeper to v0.2.0 - Update go.mod - Adopt task artifacts into skeeper - Sync codex plans with skeeper - Skeeper lock - Skeeper lock - New skills - Skeeper lock - Skeeper lock - Skeeper lock - Update deps and go - Regenerate daytona sidecar assets for go 1.26.3 - Fix cliff - Ignore docs on fmt - Build web assets before goreleaser - Extend release dry-run timeout ### 🔧 CI/CD - Lint errors - Fint release pr - Fix goreleaser ### 🧪 Testing - Add e2e tests (#27) - Qa rounds (#78) - Improve test suite (#138) - Harden daemon-served restart reloads - Harden daemon-served readiness waits - Stabilize dashboard focus assertion - Stabilize release integration gates - Stabilize release e2e markers - Stabilize release e2e flows Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
## Release v0.0.1 This PR prepares the release of version v0.0.1. ### Changelog ## 0.0.1 - 2026-05-26 ### Other Changes - Lessons learned ### ♻️ Refactoring - Project structure (#7) - Kb improvements (#12) - Rename spaces to channels (#17) - Add extensions gaps (#21) - Improve tool calls ui (#22) - Remove web app header - Module improvements (#29) - Memory improvements (#35) - Storybook for web and ui (#38) - Enable AGH network by default for new installs (#57) - Hermes adjustments (#69) - Badges design (#84) - Storybook scenario and logos gallery - Migrate typescript tests (#114) - Internal go packages (#120) - Ui patterns (#127) - Improve e2e tests (#130) - Ui redesign - Workspace isolation across runtime surfaces (#145) - Prod ready applies (#162) - Tool card ui (#164) - Alpha on logo - Prod ready features (#167) - Thread sheet (#202) ### 🎉 Features - Implement config foundation packages - Implement sqlite store package - Add ACP client package - Add session lifecycle manager - Implement observe package - Add daemon composition root - Add uds api server - Implement cli package - Add http api server - Add system design - Add foundation types, schemas, and layout shell for web client - Add daemon health polling and agent sidebar systems for web client - Add session system CRUD, streaming core, and session store for web client - Add chat view, messages, and composer tests for web client - Add tool cards and renderers for web client - Add file-backed memory store core - Scaffold memory session seams - Add memory dream consolidation service - Wire memory assembler into daemon - Add memory api and cli - New skills system (#1) - Add workspace entity (#5) - Add new skill capabilities (#8) - Web ui v2 (#9) - Improve hooks system (#10) - Session resilience (#11) - Add extensability (#13) - Add automation (#16) - Add channels (#14) - Add network implementation (#15) - Add network, bridges and automations web pages (#18) - Ext registry (#20) - Add core tasks (#19) - Bridge adapters (#23) - Add site (#26) - Add ext refac and sandbox (#25) - Settings ui (#37) - Tasks ui (#36) - Harness improvements (#44) - Agent capabilities (#49) - Redesign ui (#48) - Unify capability (#53) - Redesign network workspace (#59) - Add task deletion and split session delete from stop (#58) - Session provider selection (#60) - Production grade adjustments (#66) - Autonomous system (#75) - Add agent session route (#80) - Tools registry (#85) - Agents soul (#88) - Add network threads (#105) - Orchestration improvements (#106) - Memory v2 (#108) - Agent categories (#113) - Providers model (#118) - Add canonical AGH bundled skill (#143) - Onboarding and improvements (#198) - Onboarding and improvements (#201) ### 🐛 Bug Fixes - Review round - Review rounds - Resolve memory extensibility review batch - Embed web into daemon - Defaults agents - Acp integration (#4) - Lint errors - Prd folder - Remove orphan web actions and dead surfaces (#55) - Qa testing and fixes (#73) - New review rounds (#82) - Security audit (#90) - Release qa round (#95) - Add missing tools (#141) - New qa round (#147) - Advanced qa round (#149) - Homebrew tap - Final review round (#151) - Daemon healthy - Reasoning models (#158) - Lint errors (#160) - Review round (#168) - Release adjustments (#171) - Stabilize release ci fixtures - Stabilize release integration gate - Stabilize release verify gates - Stabilize release integration flows - Stabilize release verify gates - Stabilize main verify shutdown - Ignore stale acpmock cancel - Marketplace search focus and filtering (#193) - Website video - Workspace command select ### 📚 Documentation - Update agents.md - Update prd - Update skills - Update compozy tasks - Update compozy - Update compozy - Add new skills - Archive prd - Update prds - Update rfc - Update prds - Update prds - Add automation prd - Channels prd - Update prd - Update prd - New prds - Archive prds - Bridges adapters prd - Sandbox prd - Update - Archive prd - Update - Add new prd - New design - Update prd - Archive prds - Update prds - Tasks-ui prd tasks - Update prd - Update design docs - Agent capabilities prd - Improve site docs - Remove old design references - Udpate - Autonomous prd - Update skills - Blog design - Agent sould prd - Final qa plan - Update - Remove codex ledgers from gitignore - Remove not needed files - Udpate ledger - Update cy-codex-loop skill - Orchestration improves prd - Update prds - Orch improvs prd - Memv2 prd - Providers model prd - Update refacs prd - New design proposal - Update rules - Update skills - New blog posts (#173) - Format docs - Remove old design files - Remove old - Skeeper update ### 📦 Build System - Initial structure - Commitlint - Frontend base structure - Update vscode settings - Add subagents - Coderabbit - Prd and tooling - Bun lock - Lint tooling - Copy.md and tooling adjusts - Add repoclone rc - Upgrade skeeper to v0.2.0 - Update go.mod - Adopt task artifacts into skeeper - Sync codex plans with skeeper - Skeeper lock - Skeeper lock - New skills - Skeeper lock - Skeeper lock - Skeeper lock - Update deps and go - Regenerate daytona sidecar assets for go 1.26.3 - Fix cliff - Ignore docs on fmt - Build web assets before goreleaser - Extend release dry-run timeout ### 🔧 CI/CD - Lint errors - Fint release pr - Fix goreleaser - Fix release ### 🧪 Testing - Add e2e tests (#27) - Qa rounds (#78) - Improve test suite (#138) - Harden daemon-served restart reloads - Harden daemon-served readiness waits - Stabilize dashboard focus assertion - Stabilize release integration gates - Stabilize release e2e markers - Stabilize release e2e flows Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
## Release v0.0.2 This PR prepares the release of version v0.0.2. ### Changelog ## 0.0.2 - 2026-05-26 ### Other Changes - Lessons learned ### ♻️ Refactoring - Project structure (#7) - Kb improvements (#12) - Rename spaces to channels (#17) - Add extensions gaps (#21) - Improve tool calls ui (#22) - Remove web app header - Module improvements (#29) - Memory improvements (#35) - Storybook for web and ui (#38) - Enable AGH network by default for new installs (#57) - Hermes adjustments (#69) - Badges design (#84) - Storybook scenario and logos gallery - Migrate typescript tests (#114) - Internal go packages (#120) - Ui patterns (#127) - Improve e2e tests (#130) - Ui redesign - Workspace isolation across runtime surfaces (#145) - Prod ready applies (#162) - Tool card ui (#164) - Alpha on logo - Prod ready features (#167) - Thread sheet (#202) ### 🎉 Features - Implement config foundation packages - Implement sqlite store package - Add ACP client package - Add session lifecycle manager - Implement observe package - Add daemon composition root - Add uds api server - Implement cli package - Add http api server - Add system design - Add foundation types, schemas, and layout shell for web client - Add daemon health polling and agent sidebar systems for web client - Add session system CRUD, streaming core, and session store for web client - Add chat view, messages, and composer tests for web client - Add tool cards and renderers for web client - Add file-backed memory store core - Scaffold memory session seams - Add memory dream consolidation service - Wire memory assembler into daemon - Add memory api and cli - New skills system (#1) - Add workspace entity (#5) - Add new skill capabilities (#8) - Web ui v2 (#9) - Improve hooks system (#10) - Session resilience (#11) - Add extensability (#13) - Add automation (#16) - Add channels (#14) - Add network implementation (#15) - Add network, bridges and automations web pages (#18) - Ext registry (#20) - Add core tasks (#19) - Bridge adapters (#23) - Add site (#26) - Add ext refac and sandbox (#25) - Settings ui (#37) - Tasks ui (#36) - Harness improvements (#44) - Agent capabilities (#49) - Redesign ui (#48) - Unify capability (#53) - Redesign network workspace (#59) - Add task deletion and split session delete from stop (#58) - Session provider selection (#60) - Production grade adjustments (#66) - Autonomous system (#75) - Add agent session route (#80) - Tools registry (#85) - Agents soul (#88) - Add network threads (#105) - Orchestration improvements (#106) - Memory v2 (#108) - Agent categories (#113) - Providers model (#118) - Add canonical AGH bundled skill (#143) - Onboarding and improvements (#198) - Onboarding and improvements (#201) ### 🐛 Bug Fixes - Review round - Review rounds - Resolve memory extensibility review batch - Embed web into daemon - Defaults agents - Acp integration (#4) - Lint errors - Prd folder - Remove orphan web actions and dead surfaces (#55) - Qa testing and fixes (#73) - New review rounds (#82) - Security audit (#90) - Release qa round (#95) - Add missing tools (#141) - New qa round (#147) - Advanced qa round (#149) - Homebrew tap - Final review round (#151) - Daemon healthy - Reasoning models (#158) - Lint errors (#160) - Review round (#168) - Release adjustments (#171) - Stabilize release ci fixtures - Stabilize release integration gate - Stabilize release verify gates - Stabilize release integration flows - Stabilize release verify gates - Stabilize main verify shutdown - Ignore stale acpmock cancel - Marketplace search focus and filtering (#193) - Website video - Workspace command select ### 📚 Documentation - Update agents.md - Update prd - Update skills - Update compozy tasks - Update compozy - Update compozy - Add new skills - Archive prd - Update prds - Update rfc - Update prds - Update prds - Add automation prd - Channels prd - Update prd - Update prd - New prds - Archive prds - Bridges adapters prd - Sandbox prd - Update - Archive prd - Update - Add new prd - New design - Update prd - Archive prds - Update prds - Tasks-ui prd tasks - Update prd - Update design docs - Agent capabilities prd - Improve site docs - Remove old design references - Udpate - Autonomous prd - Update skills - Blog design - Agent sould prd - Final qa plan - Update - Remove codex ledgers from gitignore - Remove not needed files - Udpate ledger - Update cy-codex-loop skill - Orchestration improves prd - Update prds - Orch improvs prd - Memv2 prd - Providers model prd - Update refacs prd - New design proposal - Update rules - Update skills - New blog posts (#173) - Format docs - Remove old design files - Remove old - Skeeper update ### 📦 Build System - Initial structure - Commitlint - Frontend base structure - Update vscode settings - Add subagents - Coderabbit - Prd and tooling - Bun lock - Lint tooling - Copy.md and tooling adjusts - Add repoclone rc - Upgrade skeeper to v0.2.0 - Update go.mod - Adopt task artifacts into skeeper - Sync codex plans with skeeper - Skeeper lock - Skeeper lock - New skills - Skeeper lock - Skeeper lock - Skeeper lock - Update deps and go - Regenerate daytona sidecar assets for go 1.26.3 - Fix cliff - Ignore docs on fmt - Build web assets before goreleaser - Extend release dry-run timeout ### 🔧 CI/CD - Lint errors - Fint release pr - Fix goreleaser - Fix release - Fix release process ### 🧪 Testing - Add e2e tests (#27) - Qa rounds (#78) - Improve test suite (#138) - Harden daemon-served restart reloads - Harden daemon-served readiness waits - Stabilize dashboard focus assertion - Stabilize release integration gates - Stabilize release e2e markers - Stabilize release e2e flows - Improve suite speed Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
## Release v0.0.2 This PR prepares the release of version v0.0.2. ### Changelog ## 0.0.2 - 2026-05-26 ### Other Changes - Lessons learned ### ♻️ Refactoring - Project structure (#7) - Kb improvements (#12) - Rename spaces to channels (#17) - Add extensions gaps (#21) - Improve tool calls ui (#22) - Remove web app header - Module improvements (#29) - Memory improvements (#35) - Storybook for web and ui (#38) - Enable AGH network by default for new installs (#57) - Hermes adjustments (#69) - Badges design (#84) - Storybook scenario and logos gallery - Migrate typescript tests (#114) - Internal go packages (#120) - Ui patterns (#127) - Improve e2e tests (#130) - Ui redesign - Workspace isolation across runtime surfaces (#145) - Prod ready applies (#162) - Tool card ui (#164) - Alpha on logo - Prod ready features (#167) - Thread sheet (#202) ### 🎉 Features - Implement config foundation packages - Implement sqlite store package - Add ACP client package - Add session lifecycle manager - Implement observe package - Add daemon composition root - Add uds api server - Implement cli package - Add http api server - Add system design - Add foundation types, schemas, and layout shell for web client - Add daemon health polling and agent sidebar systems for web client - Add session system CRUD, streaming core, and session store for web client - Add chat view, messages, and composer tests for web client - Add tool cards and renderers for web client - Add file-backed memory store core - Scaffold memory session seams - Add memory dream consolidation service - Wire memory assembler into daemon - Add memory api and cli - New skills system (#1) - Add workspace entity (#5) - Add new skill capabilities (#8) - Web ui v2 (#9) - Improve hooks system (#10) - Session resilience (#11) - Add extensability (#13) - Add automation (#16) - Add channels (#14) - Add network implementation (#15) - Add network, bridges and automations web pages (#18) - Ext registry (#20) - Add core tasks (#19) - Bridge adapters (#23) - Add site (#26) - Add ext refac and sandbox (#25) - Settings ui (#37) - Tasks ui (#36) - Harness improvements (#44) - Agent capabilities (#49) - Redesign ui (#48) - Unify capability (#53) - Redesign network workspace (#59) - Add task deletion and split session delete from stop (#58) - Session provider selection (#60) - Production grade adjustments (#66) - Autonomous system (#75) - Add agent session route (#80) - Tools registry (#85) - Agents soul (#88) - Add network threads (#105) - Orchestration improvements (#106) - Memory v2 (#108) - Agent categories (#113) - Providers model (#118) - Add canonical AGH bundled skill (#143) - Onboarding and improvements (#198) - Onboarding and improvements (#201) ### 🐛 Bug Fixes - Review round - Review rounds - Resolve memory extensibility review batch - Embed web into daemon - Defaults agents - Acp integration (#4) - Lint errors - Prd folder - Remove orphan web actions and dead surfaces (#55) - Qa testing and fixes (#73) - New review rounds (#82) - Security audit (#90) - Release qa round (#95) - Add missing tools (#141) - New qa round (#147) - Advanced qa round (#149) - Homebrew tap - Final review round (#151) - Daemon healthy - Reasoning models (#158) - Lint errors (#160) - Review round (#168) - Release adjustments (#171) - Stabilize release ci fixtures - Stabilize release integration gate - Stabilize release verify gates - Stabilize release integration flows - Stabilize release verify gates - Stabilize main verify shutdown - Ignore stale acpmock cancel - Marketplace search focus and filtering (#193) - Website video - Workspace command select ### 📚 Documentation - Update agents.md - Update prd - Update skills - Update compozy tasks - Update compozy - Update compozy - Add new skills - Archive prd - Update prds - Update rfc - Update prds - Update prds - Add automation prd - Channels prd - Update prd - Update prd - New prds - Archive prds - Bridges adapters prd - Sandbox prd - Update - Archive prd - Update - Add new prd - New design - Update prd - Archive prds - Update prds - Tasks-ui prd tasks - Update prd - Update design docs - Agent capabilities prd - Improve site docs - Remove old design references - Udpate - Autonomous prd - Update skills - Blog design - Agent sould prd - Final qa plan - Update - Remove codex ledgers from gitignore - Remove not needed files - Udpate ledger - Update cy-codex-loop skill - Orchestration improves prd - Update prds - Orch improvs prd - Memv2 prd - Providers model prd - Update refacs prd - New design proposal - Update rules - Update skills - New blog posts (#173) - Format docs - Remove old design files - Remove old - Skeeper update ### 📦 Build System - Initial structure - Commitlint - Frontend base structure - Update vscode settings - Add subagents - Coderabbit - Prd and tooling - Bun lock - Lint tooling - Copy.md and tooling adjusts - Add repoclone rc - Upgrade skeeper to v0.2.0 - Update go.mod - Adopt task artifacts into skeeper - Sync codex plans with skeeper - Skeeper lock - Skeeper lock - New skills - Skeeper lock - Skeeper lock - Skeeper lock - Update deps and go - Regenerate daytona sidecar assets for go 1.26.3 - Fix cliff - Ignore docs on fmt - Build web assets before goreleaser - Extend release dry-run timeout ### 🔧 CI/CD - Lint errors - Fint release pr - Fix goreleaser - Fix release - Fix release process - Fix release sync - Decouple release dry-run npm auth - Persist web assets git auth ### 🧪 Testing - Add e2e tests (#27) - Qa rounds (#78) - Improve test suite (#138) - Harden daemon-served restart reloads - Harden daemon-served readiness waits - Stabilize dashboard focus assertion - Stabilize release integration gates - Stabilize release e2e markers - Stabilize release e2e flows - Improve suite speed <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Updated web assets dependency to a newer version for improved stability and performance. <!-- review_stack_entry_start --> [](https://app.coderabbit.ai/change-stack/compozy/agh/pull/211?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack) <!-- review_stack_entry_end --> <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
## Release v0.0.2 This PR prepares the release of version v0.0.2. ### Changelog ## 0.0.2 - 2026-05-27 ### Other Changes - Lessons learned ### ♻️ Refactoring - Project structure (#7) - Kb improvements (#12) - Rename spaces to channels (#17) - Add extensions gaps (#21) - Improve tool calls ui (#22) - Remove web app header - Module improvements (#29) - Memory improvements (#35) - Storybook for web and ui (#38) - Enable AGH network by default for new installs (#57) - Hermes adjustments (#69) - Badges design (#84) - Storybook scenario and logos gallery - Migrate typescript tests (#114) - Internal go packages (#120) - Ui patterns (#127) - Improve e2e tests (#130) - Ui redesign - Workspace isolation across runtime surfaces (#145) - Prod ready applies (#162) - Tool card ui (#164) - Alpha on logo - Prod ready features (#167) - Thread sheet (#202) ### 🎉 Features - Implement config foundation packages - Implement sqlite store package - Add ACP client package - Add session lifecycle manager - Implement observe package - Add daemon composition root - Add uds api server - Implement cli package - Add http api server - Add system design - Add foundation types, schemas, and layout shell for web client - Add daemon health polling and agent sidebar systems for web client - Add session system CRUD, streaming core, and session store for web client - Add chat view, messages, and composer tests for web client - Add tool cards and renderers for web client - Add file-backed memory store core - Scaffold memory session seams - Add memory dream consolidation service - Wire memory assembler into daemon - Add memory api and cli - New skills system (#1) - Add workspace entity (#5) - Add new skill capabilities (#8) - Web ui v2 (#9) - Improve hooks system (#10) - Session resilience (#11) - Add extensability (#13) - Add automation (#16) - Add channels (#14) - Add network implementation (#15) - Add network, bridges and automations web pages (#18) - Ext registry (#20) - Add core tasks (#19) - Bridge adapters (#23) - Add site (#26) - Add ext refac and sandbox (#25) - Settings ui (#37) - Tasks ui (#36) - Harness improvements (#44) - Agent capabilities (#49) - Redesign ui (#48) - Unify capability (#53) - Redesign network workspace (#59) - Add task deletion and split session delete from stop (#58) - Session provider selection (#60) - Production grade adjustments (#66) - Autonomous system (#75) - Add agent session route (#80) - Tools registry (#85) - Agents soul (#88) - Add network threads (#105) - Orchestration improvements (#106) - Memory v2 (#108) - Agent categories (#113) - Providers model (#118) - Add canonical AGH bundled skill (#143) - Onboarding and improvements (#198) - Onboarding and improvements (#201) ### 🐛 Bug Fixes - Review round - Review rounds - Resolve memory extensibility review batch - Embed web into daemon - Defaults agents - Acp integration (#4) - Lint errors - Prd folder - Remove orphan web actions and dead surfaces (#55) - Qa testing and fixes (#73) - New review rounds (#82) - Security audit (#90) - Release qa round (#95) - Add missing tools (#141) - New qa round (#147) - Advanced qa round (#149) - Homebrew tap - Final review round (#151) - Daemon healthy - Reasoning models (#158) - Lint errors (#160) - Review round (#168) - Release adjustments (#171) - Stabilize release ci fixtures - Stabilize release integration gate - Stabilize release verify gates - Stabilize release integration flows - Stabilize release verify gates - Stabilize main verify shutdown - Ignore stale acpmock cancel - Marketplace search focus and filtering (#193) - Website video - Workspace command select ### 📚 Documentation - Update agents.md - Update prd - Update skills - Update compozy tasks - Update compozy - Update compozy - Add new skills - Archive prd - Update prds - Update rfc - Update prds - Update prds - Add automation prd - Channels prd - Update prd - Update prd - New prds - Archive prds - Bridges adapters prd - Sandbox prd - Update - Archive prd - Update - Add new prd - New design - Update prd - Archive prds - Update prds - Tasks-ui prd tasks - Update prd - Update design docs - Agent capabilities prd - Improve site docs - Remove old design references - Udpate - Autonomous prd - Update skills - Blog design - Agent sould prd - Final qa plan - Update - Remove codex ledgers from gitignore - Remove not needed files - Udpate ledger - Update cy-codex-loop skill - Orchestration improves prd - Update prds - Orch improvs prd - Memv2 prd - Providers model prd - Update refacs prd - New design proposal - Update rules - Update skills - New blog posts (#173) - Format docs - Remove old design files - Remove old - Skeeper update ### 📦 Build System - Initial structure - Commitlint - Frontend base structure - Update vscode settings - Add subagents - Coderabbit - Prd and tooling - Bun lock - Lint tooling - Copy.md and tooling adjusts - Add repoclone rc - Upgrade skeeper to v0.2.0 - Update go.mod - Adopt task artifacts into skeeper - Sync codex plans with skeeper - Skeeper lock - Skeeper lock - New skills - Skeeper lock - Skeeper lock - Skeeper lock - Update deps and go - Regenerate daytona sidecar assets for go 1.26.3 - Fix cliff - Ignore docs on fmt - Build web assets before goreleaser - Extend release dry-run timeout - Fix release dry-run token contract ### 🔧 CI/CD - Lint errors - Fint release pr - Fix goreleaser - Fix release - Fix release process - Fix release sync - Decouple release dry-run npm auth - Persist web assets git auth - Require npm auth before release merge ### 🧪 Testing - Add e2e tests (#27) - Qa rounds (#78) - Improve test suite (#138) - Harden daemon-served restart reloads - Harden daemon-served readiness waits - Stabilize dashboard focus assertion - Stabilize release integration gates - Stabilize release e2e markers - Stabilize release e2e flows - Improve suite speed <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Updated dependencies to latest versions. <!-- review_stack_entry_start --> [](https://app.coderabbit.ai/change-stack/compozy/agh/pull/214?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack) <!-- review_stack_entry_end --> <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Summary by CodeRabbit
New Features
UI Updates