From dde4ba62c680183ba2861d95227c15f42dcbb0c6 Mon Sep 17 00:00:00 2001 From: Aravind Date: Fri, 18 Sep 2026 15:32:14 +0530 Subject: [PATCH] =?UTF-8?q?refactor(ts-migration):=20Phase=2011=20batch=20?= =?UTF-8?q?6=20=E2=80=94=20agent=20and=20skill=20components?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The fifteen files under `src/components/agents/` (including `skills/`) and `src/components/skills/`. 15/15 erase byte-identically; bundle hash unchanged. Renaming raised 43 errors and 29 of them came from one thing: these files carry 24 `/** @param {any} props */` annotations, one per component, and JSDoc stops applying at the extension boundary. Without them TypeScript infers every destructured prop as required, so a call site passing seven of nine props is an error — which is how a file that declared its props `any` ended up with fourteen complaints about missing `className`. Restoring the author's own declaration as `: any` is not blanket typing; it is the annotation that was already there, in the only form that still works. `Workspace` in `AgentCanvas` was the one component in that file its author left without the hatch. It now matches its siblings. The rest were five separate things: - `React.isValidElement(children)` no longer narrows enough to read `children.props.id`: React 19 types `ReactElement`'s props as `unknown`. `isValidElement` says what the `cloneElement` call beneath it has always assumed. The migration plan predicted this site by name. - `useSkillSections(page, placement)` is called with one argument by `UiEditingProvider`, which its doc comment explicitly permits — "called with no placement it returns every section on the page". The parameter simply lacked its optional marker. The marker erases, so the emitted signature is unchanged. - `new Date(b.at) - new Date(a.at)` is valueOf coercion, which JavaScript performs and TypeScript refuses to describe. Cast rather than rewritten to `.getTime()`: that would change the emitted code, and this comparison orders the list. - `Object.values` on a tally, the same inference gap as earlier batches, which also fixed a `ReactNode` complaint downstream of it. All fifteen are linted under their new extensions, checked by name. Measured against `446df7b`: typecheck 6 errors, down from 9; no new error anywhere lint exit 0, 0 errors, 289 warnings, 15/15 linted by name npm test 1684/1691, the same 7 failures verbatim build exit 0, identical bundle hash 74d17e2d… type erasure 72/72 byte-identical across Phase 11 so far No baseline artifact touched. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01HBG1wnuRfJKCstGB8Fekr8 --- .../{AgentCanvas.jsx => AgentCanvas.tsx} | 34 +++++++------------ ...{AgentConfigure.jsx => AgentConfigure.tsx} | 3 +- ...sightsPanel.jsx => AgentInsightsPanel.tsx} | 6 ++-- ...{AgentTestPanel.jsx => AgentTestPanel.tsx} | 3 +- src/components/agents/{icons.js => icons.ts} | 0 ...lWorkspace.jsx => AgentSkillWorkspace.tsx} | 3 +- .../skills/{AgentTree.jsx => AgentTree.tsx} | 3 +- ...{BoardSkillList.jsx => BoardSkillList.tsx} | 3 +- .../skills/{SkillCard.jsx => SkillCard.tsx} | 4 +-- .../{SkillCatalog.jsx => SkillCatalog.tsx} | 6 ++-- .../{SkillDetails.jsx => SkillDetails.tsx} | 6 ++-- .../{SurfaceSelect.jsx => SurfaceSelect.tsx} | 3 +- .../agents/skills/{glyphs.js => glyphs.ts} | 0 .../{SkillSections.jsx => SkillSections.tsx} | 4 +-- .../{SkillSurface.jsx => SkillSurface.tsx} | 5 ++- 15 files changed, 30 insertions(+), 53 deletions(-) rename src/components/agents/{AgentCanvas.jsx => AgentCanvas.tsx} (96%) rename src/components/agents/{AgentConfigure.jsx => AgentConfigure.tsx} (99%) rename src/components/agents/{AgentInsightsPanel.jsx => AgentInsightsPanel.tsx} (98%) rename src/components/agents/{AgentTestPanel.jsx => AgentTestPanel.tsx} (99%) rename src/components/agents/{icons.js => icons.ts} (100%) rename src/components/agents/skills/{AgentSkillWorkspace.jsx => AgentSkillWorkspace.tsx} (99%) rename src/components/agents/skills/{AgentTree.jsx => AgentTree.tsx} (99%) rename src/components/agents/skills/{BoardSkillList.jsx => BoardSkillList.tsx} (99%) rename src/components/agents/skills/{SkillCard.jsx => SkillCard.tsx} (99%) rename src/components/agents/skills/{SkillCatalog.jsx => SkillCatalog.tsx} (97%) rename src/components/agents/skills/{SkillDetails.jsx => SkillDetails.tsx} (99%) rename src/components/agents/skills/{SurfaceSelect.jsx => SurfaceSelect.tsx} (97%) rename src/components/agents/skills/{glyphs.js => glyphs.ts} (100%) rename src/components/skills/{SkillSections.jsx => SkillSections.tsx} (99%) rename src/components/skills/{SkillSurface.jsx => SkillSurface.tsx} (99%) diff --git a/src/components/agents/AgentCanvas.jsx b/src/components/agents/AgentCanvas.tsx similarity index 96% rename from src/components/agents/AgentCanvas.jsx rename to src/components/agents/AgentCanvas.tsx index f9f3686..ff01e7c 100644 --- a/src/components/agents/AgentCanvas.jsx +++ b/src/components/agents/AgentCanvas.tsx @@ -44,7 +44,7 @@ import OwliverAvatar from '@/components/krow/OwliverAvatar'; * holds a ~380px track on the right of every Admin page, so below `xl` the * remaining width belongs to the editor and the rail becomes a strip above it. */ -export function Workspace({ rail, children, className }) { +export function Workspace({ rail, children, className }: any) { return (
@@ -64,8 +64,7 @@ export function Workspace({ rail, children, className }) { * height without measuring it in JavaScript. Content stays mounted, which is * what keeps a section addressable by the rail while it is closed. */ -/** @param {any} props */ -export function Collapse({ open, children, className }) { +export function Collapse({ open, children, className }: any) { return (
s.id === activeId); return ( @@ -181,11 +179,10 @@ export function Rail({ sections, activeId, onSelect }) { /** * One section of the document. */ -/** @param {any} props */ export function DocSection({ id, icon: Icon, title, description, meta, open, onToggle, emphasis = false, last = false, children, -}) { +}: any) { return (
{label} - {React.isValidElement(children) + {React.isValidElement(children) ? React.cloneElement(children, { id: children.props.id || controlId }) : children} {hint &&

{hint}

} @@ -284,8 +280,7 @@ export function DocField({ id, label, hint, children, className }) { * One notch quieter than a section title and one louder than a row, which is * exactly the level of the thing it names. */ -/** @param {any} props */ -export function GroupHead({ id, icon: Icon, title, count, action, className }) { +export function GroupHead({ id, icon: Icon, title, count, action, className }: any) { return (
{icons.map((icon) => { @@ -486,8 +477,7 @@ export function IconPicker({ icons, value, onChange }) { * same three things the switcher renders — icon, name, description — and * nothing it does not. */ -/** @param {any} props */ -export function AgentPreview({ name, description, icon }) { +export function AgentPreview({ name, description, icon }: any) { const Icon = agentIconFor(icon); return ( diff --git a/src/components/agents/AgentConfigure.jsx b/src/components/agents/AgentConfigure.tsx similarity index 99% rename from src/components/agents/AgentConfigure.jsx rename to src/components/agents/AgentConfigure.tsx index 8b39c04..c40f415 100644 --- a/src/components/agents/AgentConfigure.jsx +++ b/src/components/agents/AgentConfigure.tsx @@ -94,7 +94,6 @@ function useActiveSection(ids) { return [active, setActive]; } -/** @param {any} props */ export function AgentConfigure({ fields, agents, customSkills = [], onChange, onOpenSkills, scopeLocked = false, /* Attaching and detaching a skill goes through the caller's existing write @@ -107,7 +106,7 @@ export function AgentConfigure({ /* The tools this deployment registers, from GET /api/v1/tools. Passed in rather than fetched here so this component stays a form over `fields`. */ toolCatalogue = [], -}) { +}: any) { /* Sections are open by default: this is a document, and one that greets its author with four closed headers hides the thing they came to write. Closing is for focus, not for the initial reading. */ diff --git a/src/components/agents/AgentInsightsPanel.jsx b/src/components/agents/AgentInsightsPanel.tsx similarity index 98% rename from src/components/agents/AgentInsightsPanel.jsx rename to src/components/agents/AgentInsightsPanel.tsx index 94c07a0..5f2ca58 100644 --- a/src/components/agents/AgentInsightsPanel.jsx +++ b/src/components/agents/AgentInsightsPanel.tsx @@ -22,8 +22,7 @@ import { cn } from '@/lib/utils'; * Real-time usage analytics computed directly from the local telemetry log. */ -/** @param {any} props */ -function MetricCard({ icon: Icon, label, value, detail, highlight }) { +function MetricCard({ icon: Icon, label, value, detail, highlight }: any) { return (
@@ -45,8 +44,7 @@ function MetricCard({ icon: Icon, label, value, detail, highlight }) { ); } -/** @param {any} props */ -export function AgentInsightsPanel({ agentId, agentName }) { +export function AgentInsightsPanel({ agentId, agentName }: any) { const preferences = usePreferences(); const [records] = React.useState(() => readHistory()); diff --git a/src/components/agents/AgentTestPanel.jsx b/src/components/agents/AgentTestPanel.tsx similarity index 99% rename from src/components/agents/AgentTestPanel.jsx rename to src/components/agents/AgentTestPanel.tsx index 9c0867f..98b9a7d 100644 --- a/src/components/agents/AgentTestPanel.jsx +++ b/src/components/agents/AgentTestPanel.tsx @@ -46,8 +46,7 @@ const CLASSIFICATION_COPY = { combined: 'Requires both structured records and knowledge retrieval.', }; -/** @param {any} props */ -export function AgentTestPanel({ fields, dirty, customSkills = [], onRunLive }) { +export function AgentTestPanel({ fields, dirty, customSkills = [], onRunLive }: any) { const [contextId, setContextId] = React.useState( () => CONTEXT_OPTIONS.find((o) => fields.pages.includes(pageKeyForContext(o.contextId)))?.contextId || CONTEXT_OPTIONS[0].contextId diff --git a/src/components/agents/icons.js b/src/components/agents/icons.ts similarity index 100% rename from src/components/agents/icons.js rename to src/components/agents/icons.ts diff --git a/src/components/agents/skills/AgentSkillWorkspace.jsx b/src/components/agents/skills/AgentSkillWorkspace.tsx similarity index 99% rename from src/components/agents/skills/AgentSkillWorkspace.jsx rename to src/components/agents/skills/AgentSkillWorkspace.tsx index 55cf1e7..7cf791e 100644 --- a/src/components/agents/skills/AgentSkillWorkspace.jsx +++ b/src/components/agents/skills/AgentSkillWorkspace.tsx @@ -43,7 +43,6 @@ import { SkillDetails } from './SkillDetails'; const OWLIVER = 'owliver'; const BOARD = 'board'; -/** @param {any} props */ export function AgentSkillWorkspace({ fields, customSkills = [], @@ -55,7 +54,7 @@ export function AgentSkillWorkspace({ busy = false, pendingId = null, dirty = false, -}) { +}: any) { const [kind, setKind] = React.useState(OWLIVER); const [query, setQuery] = React.useState(''); const [boardQuery, setBoardQuery] = React.useState(''); diff --git a/src/components/agents/skills/AgentTree.jsx b/src/components/agents/skills/AgentTree.tsx similarity index 99% rename from src/components/agents/skills/AgentTree.jsx rename to src/components/agents/skills/AgentTree.tsx index 336ba38..5fc30da 100644 --- a/src/components/agents/skills/AgentTree.jsx +++ b/src/components/agents/skills/AgentTree.tsx @@ -174,7 +174,6 @@ function StaticLeaf({ icon: Icon, label, detail = null }) { ); } -/** @param {any} props */ export function AgentTree({ agentName, agentIcon, @@ -191,7 +190,7 @@ export function AgentTree({ onToggleBranch, busy = false, className, -}) { +}: any) { const Glyph = agentIconFor(agentIcon); return ( diff --git a/src/components/agents/skills/BoardSkillList.jsx b/src/components/agents/skills/BoardSkillList.tsx similarity index 99% rename from src/components/agents/skills/BoardSkillList.jsx rename to src/components/agents/skills/BoardSkillList.tsx index f4a6922..a40c604 100644 --- a/src/components/agents/skills/BoardSkillList.jsx +++ b/src/components/agents/skills/BoardSkillList.tsx @@ -159,11 +159,10 @@ function BoardCard({ entry, enabled, onToggle, onSurfaces, busy, attached = fals ); } -/** @param {any} props */ export function BoardSkillList({ entries, agentPages = [], disabledIds, onToggle, busy = false, query, onQueryChange, total, attachedIds = null, onAttach = null, -}) { +}: any) { const searchId = React.useId(); const onSurfaces = React.useMemo(() => new Set(agentPages), [agentPages]); diff --git a/src/components/agents/skills/SkillCard.jsx b/src/components/agents/skills/SkillCard.tsx similarity index 99% rename from src/components/agents/skills/SkillCard.jsx rename to src/components/agents/skills/SkillCard.tsx index 7b1711b..f8f7323 100644 --- a/src/components/agents/skills/SkillCard.jsx +++ b/src/components/agents/skills/SkillCard.tsx @@ -25,9 +25,9 @@ import { glyphForGroup } from './glyphs'; */ /** @type {any} */ -export const SkillCard = React.memo(/** @param {any} props */ ({ +export const SkillCard = React.memo(({ entry, enabled, onOpen, onAddAgent, onTest, canTest = false, busy = false, isPending = false, -}) => { +}: any) => { const Glyph = glyphForGroup(entry.group); const samplePrompt = entry.suggestions?.[0]?.label; diff --git a/src/components/agents/skills/SkillCatalog.jsx b/src/components/agents/skills/SkillCatalog.tsx similarity index 97% rename from src/components/agents/skills/SkillCatalog.jsx rename to src/components/agents/skills/SkillCatalog.tsx index ba04d21..33e139b 100644 --- a/src/components/agents/skills/SkillCatalog.jsx +++ b/src/components/agents/skills/SkillCatalog.tsx @@ -23,8 +23,7 @@ import { SkillCard } from './SkillCard'; * has, at every width, with no media query to get wrong. */ -/** @param {any} props */ -function CategoryFilter({ groups, value, onChange, total }) { +function CategoryFilter({ groups, value, onChange, total }: any) { return (
OPTIONS.filter((o) => o.supported(type)).map((o) => o.id); -/** @param {any} props */ -export function SurfaceSelect({ type, value, onChange, name }) { +export function SurfaceSelect({ type, value, onChange, name }: any) { return (
{OPTIONS.map((option) => { diff --git a/src/components/agents/skills/glyphs.js b/src/components/agents/skills/glyphs.ts similarity index 100% rename from src/components/agents/skills/glyphs.js rename to src/components/agents/skills/glyphs.ts diff --git a/src/components/skills/SkillSections.jsx b/src/components/skills/SkillSections.tsx similarity index 99% rename from src/components/skills/SkillSections.jsx rename to src/components/skills/SkillSections.tsx index b404587..2e8cdf3 100644 --- a/src/components/skills/SkillSections.jsx +++ b/src/components/skills/SkillSections.tsx @@ -133,7 +133,7 @@ function ListSection({ data }) { function TimelineSection({ data }) { const items = [...(data.items || [])] .filter((item) => item.at) - .sort((a, b) => new Date(b.at) - new Date(a.at)); + .sort((a, b) => (new Date(b.at) as any) - (new Date(a.at) as any)); if (data.empty || !items.length) return {data.emptyNote}; return ( @@ -308,7 +308,7 @@ function WeightsSection({ data, section, onApply, compact = false }) { if (data?.empty || !rows.length) return {data?.emptyNote}; const current = draft || Object.fromEntries(rows.map((r) => [r.id, Number(r.value) || 0])); - const total = Object.values(current).reduce((a, b) => a + b, 0); + const total = Object.values(current).reduce((a, b) => a + b, 0); const balanced = total === 100; const dirty = draft !== null && rows.some((r) => (Number(r.value) || 0) !== current[r.id]); diff --git a/src/components/skills/SkillSurface.jsx b/src/components/skills/SkillSurface.tsx similarity index 99% rename from src/components/skills/SkillSurface.jsx rename to src/components/skills/SkillSurface.tsx index 288b96a..8ab45a4 100644 --- a/src/components/skills/SkillSurface.jsx +++ b/src/components/skills/SkillSurface.tsx @@ -49,7 +49,7 @@ import { agentPermitsSkill } from '@/lib/agents/runtime'; * from one resolution, or the two would disagree about what a page carries. * Called with no placement it returns every section on the page. */ -export function useSkillSections(page, placement) { +export function useSkillSections(page, placement?) { const preferences = usePreferences(); /** * Who is answering here. @@ -217,7 +217,6 @@ function SkillSection({ skill, section, context }) { ); } -/** @param {any} props */ export function SkillSurface({ page = '', placement = '', @@ -231,7 +230,7 @@ export function SkillSurface({ _customSources = [], _email = '', _onOpenTraining = null, -}) { +}: any) { const sections = useSkillSections(page, placement); const resolved = useSkillDataContext(context);