From e71dc1a37562f3bd8770489b013cb33c3e86b55f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20M=2E=20Wo=C5=BAniak?= Date: Fri, 30 Oct 2026 09:02:12 +0100 Subject: [PATCH] Update T3 contract to 5ef5685 --- pins.json | 2 +- .../server/src/mcp/toolkits/thread/tools.ts | 1 + .../src/provider/Drivers/ClaudeSkills.ts | 41 ++++++++++++------- t3/contract/packages/shared/src/htmlRender.ts | 3 +- 4 files changed, 31 insertions(+), 16 deletions(-) diff --git a/pins.json b/pins.json index 694cc39..bc8e388 100644 --- a/pins.json +++ b/pins.json @@ -23,7 +23,7 @@ }, "t3": { "repo": "pingdotgg/t3code", - "commit": "f855f016b3fe103f0b6cb05f26de08cb735b1f7b", + "commit": "5ef56853ef1854da50bc1909c6c0a32700f2cb13", "paths": [ "apps/server/src/mcp/toolkits/attachment/tools.ts", "apps/server/src/mcp/toolkits/device/tools.ts", diff --git a/t3/contract/apps/server/src/mcp/toolkits/thread/tools.ts b/t3/contract/apps/server/src/mcp/toolkits/thread/tools.ts index 4b566c4..018acac 100644 --- a/t3/contract/apps/server/src/mcp/toolkits/thread/tools.ts +++ b/t3/contract/apps/server/src/mcp/toolkits/thread/tools.ts @@ -61,6 +61,7 @@ const ThreadOrganizeTool = Tool.make("t3_thread_organize", { ], }) .annotate(Tool.Title, "Organize a thread") + .annotate(Tool.Strict, true) .annotate(Tool.Destructive, true); const commandTool = { diff --git a/t3/contract/apps/server/src/provider/Drivers/ClaudeSkills.ts b/t3/contract/apps/server/src/provider/Drivers/ClaudeSkills.ts index 56c2ff1..bc68e42 100644 --- a/t3/contract/apps/server/src/provider/Drivers/ClaudeSkills.ts +++ b/t3/contract/apps/server/src/provider/Drivers/ClaudeSkills.ts @@ -5,7 +5,7 @@ import * as Path from "effect/Path"; import * as Option from "effect/Option"; import * as Schema from "effect/Schema"; import * as HostProcess from "@t3tools/shared/HostProcess"; -import { parseCliArgs } from "@t3tools/shared/cliArgs"; +import { tokenizeCliArgs } from "@t3tools/shared/cliArgs"; import { fromLenientJson } from "@t3tools/shared/schemaJson"; import { SKILL_MENTION_PATTERN } from "@t3tools/shared/composerInlineTokens"; import { parse as parseYamlDocument } from "yaml"; @@ -427,6 +427,26 @@ const resolveClaudeConfigDirPath = Effect.fn("resolveClaudeConfigDirPath")(funct return path.join(home, ".claude"); }); +// Claude's --add-dir is repeatable and variadic; parseCliArgs keeps only one value. +export function claudeLaunchArgDirectories(launchArgs?: string): ReadonlyArray { + const directories: string[] = []; + let collecting = false; + for (const token of tokenizeCliArgs(launchArgs)) { + if (token === "--") break; + if (token === "--add-dir") { + collecting = true; + } else if (token.startsWith("--add-dir=")) { + directories.push(token.slice("--add-dir=".length)); + collecting = false; + } else if (token.startsWith("-")) { + collecting = false; + } else if (collecting) { + directories.push(token); + } + } + return directories; +} + /** First name wins, matching Claude Code's precedence of user skills over project skills. */ export const discoverClaudeSkills = Effect.fn("discoverClaudeSkills")(function* ( config: Pick & Partial>, @@ -442,22 +462,15 @@ export const discoverClaudeSkills = Effect.fn("discoverClaudeSkills")(function* environment ?? process.env, ); - const additionalDirectory = parseCliArgs(config.launchArgs ?? "").flags["add-dir"]; const roots: ReadonlyArray = [ { directory: path.join(configDirPath, "skills"), scope: "user" }, ...(cwd ? [{ directory: path.join(cwd, ".claude", "skills"), scope: "project" as const }] : []), - ...(additionalDirectory && (cwd || path.isAbsolute(additionalDirectory)) - ? [ - { - directory: path.join( - path.resolve(cwd ?? ".", additionalDirectory), - ".claude", - "skills", - ), - scope: "project" as const, - }, - ] - : []), + ...claudeLaunchArgDirectories(config.launchArgs) + .filter((directory) => directory && (cwd || path.isAbsolute(directory))) + .map((directory) => ({ + directory: path.join(path.resolve(cwd ?? ".", directory), ".claude", "skills"), + scope: "project" as const, + })), ...(yield* discoverPluginSkillRoots( configDirPath, cwd, diff --git a/t3/contract/packages/shared/src/htmlRender.ts b/t3/contract/packages/shared/src/htmlRender.ts index 572441f..12af0f3 100644 --- a/t3/contract/packages/shared/src/htmlRender.ts +++ b/t3/contract/packages/shared/src/htmlRender.ts @@ -232,6 +232,7 @@ export const HTML_RENDER_THEME_GUIDE = [ export const HTML_RENDER_LAYOUT_GUIDE = [ `Frame width is reply width (${HTML_RENDER_COLUMN_WIDTH}px desktop, about 360px phones), on the thread background.`, "Leave html, body, and the outer element with no background color, no outer card, border, banner title, or side padding. This wins over any rule that sets a page background.", + "T3 adds an overridable 8px html gutter for selection rings and shadows. Use fluid widths within it.", "A box that needs its own background gets 16px padding and var(--radius) corners. Give charts fixed pixel heights.", "Let content set the height. Do not use vh units or height:100% on html or body. Both are rejected or loop.", ].join(" "); @@ -353,7 +354,7 @@ export function htmlRenderThemeMessage(theme: HtmlRenderTheme) { } const BASE_CSS = - "html{background:var(--background);color:var(--foreground);font-family:var(--font-sans);font-size:14px;line-height:1.5;-webkit-font-smoothing:antialiased;-webkit-text-size-adjust:100%;scrollbar-width:none}" + + "html{box-sizing:border-box;padding:8px;background:var(--background);color:var(--foreground);font-family:var(--font-sans);font-size:14px;line-height:1.5;-webkit-font-smoothing:antialiased;-webkit-text-size-adjust:100%;scrollbar-width:none}" + "html::-webkit-scrollbar{display:none}body{margin:0}code,kbd,pre,samp{font-family:var(--font-mono)}" + // Focus outlines paint inside the element, so the frame edge never clips them. ":where(:focus-visible){outline:2px solid var(--ring)}:focus-visible{outline-width:2px!important;outline-offset:-2px!important}" +