Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion apps/daemon/src/artifacts/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import path from 'node:path';

const MANIFEST_VERSION = 1;
const MAX_TITLE_LENGTH = 200;
const MAX_ENTRY_LENGTH = 260;
const MAX_SOURCE_SKILL_ID_LENGTH = 128;
const MAX_DESIGN_SYSTEM_ID_LENGTH = 128;
const MAX_SUPPORTING_FILE_LENGTH = 260;
Expand Down
8 changes: 0 additions & 8 deletions apps/daemon/src/artifacts/text-suppression.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,10 +144,6 @@ export function emitWithTextSuppressor(
return true;
}

function possibleDsmlArtifactOpenStart(text: string): number {
return possibleTagStart(text, isPossibleDsmlArtifactOpen);
}

function possibleTagStart(text: string, predicate: (tail: string) => boolean): number {
const min = Math.max(0, text.length - MAX_CANDIDATE_LENGTH);
let index = text.lastIndexOf('<');
Expand All @@ -170,10 +166,6 @@ function isPossibleDsmlArtifactOpen(text: string): boolean {
compact.startsWith(ARTIFACT_OPEN_CANONICAL);
}

function possibleArtifactCloseStart(text: string): number {
return possibleTagStart(text, isPossibleArtifactClose);
}

function isPossibleArtifactClose(text: string): boolean {
if (!text.startsWith('<') || text.includes('>')) return false;
const compact = text.toLowerCase().replace(/[<|/\s]/g, '');
Expand Down
1 change: 0 additions & 1 deletion apps/daemon/src/byok-tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,6 @@ export function isAIHubMixSpeechModel(value: unknown): value is string {
}
export const BYOK_AIHUBMIX_DEFAULT_SPEECH_MODEL = 'aihubmix-tts-1';

const AIHUBMIX_DEFAULT_TTS_MODEL = 'tts-1';
const AIHUBMIX_DEFAULT_TTS_VOICE = 'alloy';

// AIHubMix video knobs for the chat `generate_video` tool. The wire shape
Expand Down
4 changes: 2 additions & 2 deletions apps/daemon/src/connectors/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
type ConnectorToolSafety,
type ConnectorStatus,
} from './catalog.js';
import { composioConnectorProvider, getStaticComposioCatalogDefinitions, type ComposioAuthConfigPrepareResult, type ComposioConnectionStart } from './composio.js';
import { composioConnectorProvider, type ComposioAuthConfigPrepareResult, type ComposioConnectionStart } from './composio.js';

export interface ConnectorExecuteRequest {
connectorId: string;
Expand Down Expand Up @@ -607,7 +607,7 @@ export class ConnectorService {
return this.statusService.getCredential(connectorId);
}

async listConnectors(signal?: AbortSignal): Promise<ConnectorDetail[]> {
async listConnectors(_signal?: AbortSignal): Promise<ConnectorDetail[]> {
return this.listFastDefinitions().map((definition) => this.toDetail(definition));
}

Expand Down
2 changes: 1 addition & 1 deletion apps/daemon/src/critique/scoreboard.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { CritiqueConfig, PanelEvent, PanelistRole, RoundDecision } from '@open-design/contracts/critique';
import type { CritiqueConfig, PanelistRole, RoundDecision } from '@open-design/contracts/critique';

/**
* Per-round scores indexed by panelist role. Absent roles are undefined.
Expand Down
1 change: 0 additions & 1 deletion apps/daemon/src/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import { migratePlugins } from './plugins/persistence.js';

type SqliteDb = Database.Database;
type DbRow = Record<string, any>;
type JsonObject = Record<string, unknown>;
type ChatSessionMode = 'design' | 'chat' | 'plan';

let dbInstance: SqliteDb | null = null;
Expand Down
2 changes: 1 addition & 1 deletion apps/daemon/src/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1591,7 +1591,7 @@ function rewriteHtmlAttributes(rawAttrs: string, tagName: string, attrs: Map<str
const shouldRewriteHref = shouldCollectHref(tagName, attrs);
return String(rawAttrs).replace(
/([^\s"'<>/=]+)(\s*=\s*)("([^"]*)"|'([^']*)'|([^\s"'=<>`]+))/g,
(full, rawName, equals, rawValue, doubleQuoted, singleQuoted, unquoted) => {
(full, rawName, equals, _rawValue, doubleQuoted, singleQuoted, unquoted) => {
const name = String(rawName).toLowerCase();
if (
name !== 'src' &&
Expand Down
13 changes: 0 additions & 13 deletions apps/daemon/src/design-systems/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2855,19 +2855,6 @@ function sanitizeRevisionId(raw: string | undefined): string | null {
return /^[a-zA-Z0-9-]+$/.test(value) ? value : null;
}

async function uniqueSlug(root: string, base: string): Promise<string> {
let candidate = base || 'design-system';
let index = 2;
for (;;) {
try {
await stat(path.join(root, candidate));
candidate = `${base}-${index++}`;
} catch {
return candidate;
}
}
}

async function reserveUniqueSlugDirectory(root: string, base: string): Promise<{ dirId: string; dir: string }> {
await mkdir(root, { recursive: true });
let candidate = base || 'design-system';
Expand Down
2 changes: 0 additions & 2 deletions apps/daemon/src/design-systems/token-contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,6 @@ type RoleHint = {

const DEFAULT_BODY_FONT =
'Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif';
const DEFAULT_MONO_FONT =
'ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Monaco, Consolas, monospace';

const DEFAULT_TOKEN_VALUES: TokenDefaults = {
'--bg': '#f8fafc',
Expand Down
1 change: 0 additions & 1 deletion apps/daemon/src/import-export-routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import type { Express, Response } from 'express';
import { PROJECT_EXPORT_MANIFEST_SCHEMA, isExportFormat } from '@open-design/contracts';
import nodePath from 'node:path';
import os from 'node:os';
import { readFile, rm } from 'node:fs/promises';
import { isBlocked as isBlockedSystemDir } from './linked-dirs.js';
import type { RouteDeps } from './server-context.js';
import {
Expand Down
1 change: 0 additions & 1 deletion apps/daemon/src/lint-artifact.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,6 @@ export function lintArtifact(rawHtml: unknown): LintFinding[] {
// pedagogical examples ("paste a `<section class="slide">` here") that
// would otherwise fire false positives for the section / slide checks.
const html = rawHtml.replace(/<!--[\s\S]*?-->/g, '');
const lower = html.toLowerCase();

// ── P0-1: purple gradient backgrounds ─────────────────────────────
for (const hex of PURPLE_HEXES) {
Expand Down
48 changes: 0 additions & 48 deletions apps/daemon/src/live-artifacts/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,6 @@ const MAX_TITLE_LENGTH = 200;
const MAX_SLUG_LENGTH = 128;
const MAX_PATH_LENGTH = 260;
const MAX_SHORT_TEXT_LENGTH = 1_024;
const MAX_LONG_TEXT_LENGTH = 16 * 1024;
const MAX_PROVENANCE_SOURCES = 50;
const MAX_MAPPING_PATHS = 100;
const MAX_REFRESH_STEP_LENGTH = 128;
const MAX_REFRESH_ERROR_CODE_LENGTH = 128;
Expand Down Expand Up @@ -216,16 +214,6 @@ const REFRESH_PERMISSIONS = new Set<LiveArtifactSource['refreshPermission']>([
'manual_refresh_granted_for_read_only',
]);
const OUTPUT_TRANSFORMS = new Set<LiveArtifactOutputTransform>(['identity', 'compact_table', 'metric_summary']);
const PROVENANCE_GENERATORS = new Set<LiveArtifactProvenance['generatedBy']>([
'agent',
'refresh_runner',
]);
const PROVENANCE_SOURCE_TYPES = new Set<LiveArtifactProvenanceSource['type']>([
'connector',
'local_file',
'user_input',
'derived',
]);
const REFRESH_STEP_STATUSES = new Set<LiveArtifactRefreshStepStatus>([
'running',
'succeeded',
Expand Down Expand Up @@ -694,42 +682,6 @@ function validateRefreshErrorRecord(value: unknown, path: string, issues: LiveAr
return record;
}

function validateProvenance(value: unknown, path: string, issues: LiveArtifactValidationIssue[]): LiveArtifactProvenance | undefined {
if (!isPlainObject(value)) {
issues.push({ path, message: `${path} must be an object` });
return undefined;
}
const generatedAt = validateIsoDate(value.generatedAt, `${path}.generatedAt`, issues);
const generatedBy = validateEnum(value.generatedBy, PROVENANCE_GENERATORS, `${path}.generatedBy`, issues);
const notes = asOptionalString(value.notes, `${path}.notes`, issues, MAX_LONG_TEXT_LENGTH);
let sources: LiveArtifactProvenanceSource[] | undefined;
if (!Array.isArray(value.sources) || value.sources.length > MAX_PROVENANCE_SOURCES) {
issues.push({ path: `${path}.sources`, message: `${path}.sources must be a bounded array` });
} else {
sources = [];
value.sources.forEach((source, index) => {
const sourcePath = `${path}.sources.${index}`;
if (!isPlainObject(source)) {
issues.push({ path: sourcePath, message: `${sourcePath} must be an object` });
return;
}
const label = asString(source.label, `${sourcePath}.label`, issues, MAX_SHORT_TEXT_LENGTH);
const type = validateEnum(source.type, PROVENANCE_SOURCE_TYPES, `${sourcePath}.type`, issues);
const ref = asOptionalString(source.ref, `${sourcePath}.ref`, issues, MAX_PATH_LENGTH);
if (ref !== undefined) validateRelativePath(ref, `${sourcePath}.ref`, issues);
if (label !== undefined && type !== undefined) {
const provenanceSource: LiveArtifactProvenanceSource = { label, type };
if (ref !== undefined) provenanceSource.ref = ref;
sources?.push(provenanceSource);
}
});
}
if (generatedAt === undefined || generatedBy === undefined || sources === undefined) return undefined;
const provenance: LiveArtifactProvenance = { generatedAt, generatedBy, sources };
if (notes !== undefined) provenance.notes = notes;
return provenance;
}

function validateOptionalInteger(value: unknown, path: string, issues: LiveArtifactValidationIssue[], min: number, max: number): number | undefined {
if (value === undefined) return undefined;
if (typeof value !== 'number' || !Number.isInteger(value) || value < min || value > max) {
Expand Down
1 change: 0 additions & 1 deletion apps/daemon/src/live-artifacts/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ export const LIVE_ARTIFACT_SNAPSHOTS_DIR = 'snapshots' as const;
const SAFE_LIVE_ARTIFACT_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
const LIVE_ARTIFACT_ID_PREFIX = 'la';
const LIVE_ARTIFACT_ID_RANDOM_BYTES = 6;
const LIVE_ARTIFACT_ID_RANDOM_SUFFIX_LENGTH = LIVE_ARTIFACT_ID_RANDOM_BYTES * 2;
const MAX_LIVE_ARTIFACT_STORAGE_ID_LENGTH = 128;
const MAX_LIVE_ARTIFACT_SLUG_LENGTH = 128;
const FALLBACK_LIVE_ARTIFACT_SLUG = 'live-artifact';
Expand Down
11 changes: 5 additions & 6 deletions apps/daemon/src/mcp-routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,18 @@ import fs from 'node:fs';
import { SIDECAR_ENV } from '@open-design/sidecar-proto';
import { buildMcpInstallPayload, type McpInstallPayload } from './mcp-install-info.js';
import { installCodexMcp, probeCodexInstall, uninstallCodexMcp } from './codex-cli.js';
import { MCP_TEMPLATES, buildAcpMcpServers, buildClaudeMcpJson, isManagedProjectCwd, readMcpConfig, writeMcpConfig } from './mcp-config.js';
import { beginAuth, exchangeCodeForToken, refreshAccessToken } from './mcp-oauth.js';
import { clearToken, getToken, isTokenExpired, readAllTokens, setToken } from './mcp-tokens.js';
import { MCP_TEMPLATES, readMcpConfig, writeMcpConfig } from './mcp-config.js';
import { beginAuth, exchangeCodeForToken } from './mcp-oauth.js';
import { clearToken, getToken, setToken } from './mcp-tokens.js';
import type { RouteDeps } from './server-context.js';

export interface RegisterMcpRoutesDeps extends RouteDeps<'http' | 'paths' | 'mcp'> {}

export function registerMcpRoutes(app: Express, ctx: RegisterMcpRoutesDeps) {
const { isLocalSameOrigin, resolvedPortRef, sendApiError } = ctx.http;
const { OD_BIN, RUNTIME_DATA_DIR, PROJECTS_DIR } = ctx.paths;
const { pendingAuth, daemonUrlRef } = ctx.mcp;
const { OD_BIN, RUNTIME_DATA_DIR } = ctx.paths;
const { pendingAuth } = ctx.mcp;
const getResolvedPort = () => resolvedPortRef.current;
const getDaemonUrl = () => daemonUrlRef.current;
// Surfaces the absolute paths to the daemon's Node-compatible runtime and
// CLI entry so the Settings → MCP server panel can render snippets that work
// even when `od` isn't on the user's PATH (the common case for source clones
Expand Down
1 change: 0 additions & 1 deletion apps/daemon/src/mcp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ type JsonObject = Record<string, unknown>;
interface RunMcpOptions { daemonUrl: string | URL }
interface CatalogItem { id: string; name?: string; title?: string; description?: string; summary?: string }
interface SkillsPayload { skills?: CatalogItem[] }
interface PluginsPayload { plugins?: CatalogItem[] }
interface DesignSystemsPayload { designSystems?: CatalogItem[] }
interface ResourcePayload { skill?: { body?: string; content?: string }; designSystem?: { body?: string; content?: string }; body?: string; content?: string }
interface ProjectSummary { id: string; name: string; metadata?: JsonObject }
Expand Down
5 changes: 2 additions & 3 deletions apps/daemon/src/media/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,10 @@
// placeholder as the final result.

import { mkdir, mkdtemp, readdir, readFile, rm, stat, writeFile } from 'node:fs/promises';
import { execFile as execFileCb, spawn } from 'node:child_process';
import { spawn } from 'node:child_process';
import os from 'node:os';
import path from 'node:path';
import { promisify } from 'node:util';

import { Agent as UndiciAgent } from 'undici';
import {
AUDIO_DURATIONS_SEC,
Expand Down Expand Up @@ -96,7 +96,6 @@ import {
classifyAIHubMixModel,
} from '../integrations/aihubmix.js';

const execFile = promisify(execFileCb);
const DEFAULT_OPENROUTER_VIDEO_POLL_INTERVAL_MS = 8000;
type ProviderConfig = { apiKey?: string; baseUrl?: string; model?: string };
type ProgressFn = (message: string) => void;
Expand Down
4 changes: 0 additions & 4 deletions apps/daemon/src/orbit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,10 +200,6 @@ function summaryFile(dataDir: string): string {
return path.join(orbitDir(dataDir), SUMMARY_FILE);
}

async function readLastSummary(dataDir: string): Promise<OrbitActivitySummary | null> {
return (await readSummaryStore(dataDir)).lastRun;
}

function isOrbitRunSummary(value: unknown): value is OrbitActivitySummary {
if (!value || typeof value !== 'object') return false;
const obj = value as Partial<OrbitActivitySummary>;
Expand Down
2 changes: 0 additions & 2 deletions apps/daemon/src/plugins/apply.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ import {
type InstalledPluginRecord,
type McpServerSpec,
type PluginAssetRef,
type PluginConnectorBinding,
type PluginConnectorRef,
type PluginManifest,
type PluginProjectMetadataPatch,
type TrustTier,
Expand Down
2 changes: 1 addition & 1 deletion apps/daemon/src/plugins/pack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ export async function packPlugin(input: PackPluginInput): Promise<PackPluginResu
// an author from packing a symlink and only finding out at
// install. The walker also pre-filters them; this is a
// belt-and-suspenders pass.
filter: (entryPath, stat) => {
filter: (_entryPath, stat) => {
const candidate = stat as { isSymbolicLink?: () => boolean };
if (typeof candidate.isSymbolicLink === 'function' && candidate.isSymbolicLink()) return false;
return true;
Expand Down
1 change: 0 additions & 1 deletion apps/daemon/src/plugins/resolve-snapshot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import type Database from 'better-sqlite3';
import type {
AppliedPluginSnapshot,
ApplyResult,
InstalledPluginRecord,
PluginConnectorBinding,
} from '@open-design/contracts';
import {
Expand Down
1 change: 0 additions & 1 deletion apps/daemon/src/qa/cta-hierarchy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import { type CheerioAPI, load } from 'cheerio';
// daemon's declared dependency boundary, so we derive a generic "node
// collection" type from the API surface we actually use.
type CheerioCollection = ReturnType<CheerioAPI>;
type CheerioNode = CheerioCollection extends ArrayLike<infer N> ? N : never;

export interface CtaHierarchyIssue {
/** Category of the finding; the UI may surface different copy per kind. */
Expand Down
3 changes: 1 addition & 2 deletions apps/daemon/src/routes/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ import {
classifyAIHubMixModel,
} from '../integrations/aihubmix.js';
import { isSafeId as isSafeProjectId } from '../projects.js';
import { projectKindToTracking } from '@open-design/contracts/analytics';
import { proxyDispatcherRequestInit, validateUserProviderBaseUrl } from '../connectionTest.js';
import { resolveModelForServiceTier } from '../runtimes/models.js';
import { googleStreamGenerateContentUrl } from '../integrations/google-models.js';
Expand Down Expand Up @@ -59,7 +58,7 @@ const FEEDBACK_REASON_ALLOWLIST: ReadonlySet<string> = new Set([
export interface RegisterChatRoutesDeps extends RouteDeps<'db' | 'design' | 'http' | 'chat' | 'agents' | 'critique' | 'validation' | 'lifecycle' | 'paths' | 'telemetry' | 'appConfig'> {}

export function registerChatRoutes(app: Express, ctx: RegisterChatRoutesDeps) {
const { db, design } = ctx;
const { db } = ctx;
const { sendApiError, createSseResponse } = ctx.http;
const { readAppConfig } = ctx.appConfig;
const { testProviderConnection, testAgentConnection, getAgentDef, isKnownModel, isKnownServiceTier, sanitizeCustomModel, listProviderModels } = ctx.agents;
Expand Down
3 changes: 1 addition & 2 deletions apps/daemon/src/routes/media.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export function resolveLegacyMediaRouteGrant(input: {

export function registerMediaRoutes(app: Express, ctx: RegisterMediaRoutesDeps) {
const { db, design } = ctx;
const { sendApiError, requireLocalDaemonRequest, isLocalSameOrigin, resolvedPortRef } = ctx.http;
const { sendApiError, isLocalSameOrigin, resolvedPortRef } = ctx.http;
const { PROJECT_ROOT, PROJECTS_DIR, RUNTIME_DATA_DIR } = ctx.paths;
const { authorizeToolRequest, optionalToolGrantFromRequest, requestProjectOverride } = ctx.auth;
const { randomUUID } = ctx.ids;
Expand All @@ -85,7 +85,6 @@ export function registerMediaRoutes(app: Express, ctx: RegisterMediaRoutesDeps)
const { orbitService } = ctx.orbit;
const { openBrowser, openNativeFolderDialog } = ctx.nativeDialogs;
const { getProject } = ctx.projectStore;
const { insertConversation, upsertMessage } = ctx.conversations;
const { searchResearch, ResearchError } = ctx.research;
const getResolvedPort = () => resolvedPortRef.current;

Expand Down
4 changes: 2 additions & 2 deletions apps/daemon/src/routes/plugins/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Express, NextFunction, Request, RequestHandler, Response } from 'express';
import type { Express, Request, RequestHandler, Response } from 'express';
import type {
InstalledPluginRecord,
PluginDuplicateProjectRequest,
Expand Down Expand Up @@ -278,7 +278,7 @@ export function registerPluginRoutes(app: Express, deps: RegisterPluginRoutesDep
}

export function registerProjectPluginRoutes(app: Express, deps: RegisterPluginRoutesDeps): void {
const { db, paths, plugins, helpers } = deps;
const { db, plugins, helpers } = deps;
app.post('/api/projects/:id/plugins/install-folder', async (req, res) => helpers.handleProjectInstallFolder(req, res));
app.post('/api/projects/:id/plugins/publish-github', async (req, res) => helpers.handleProjectPluginCli(req, res, 'publish-github'));
app.get('/api/projects/:id/plugin-candidates', (req, res) => { try { const project = helpers.getProject(db, req.params.id); if (!project) return helpers.sendApiError(res, 404, 'PROJECT_NOT_FOUND', 'project not found'); const includeDismissed = req.query.includeDismissed === 'true'; res.json({ candidates: plugins.listSkillPluginCandidates(db, req.params.id, includeDismissed) }); } catch (err: unknown) { res.status(400).json({ error: err instanceof Error ? err.message : String(err) }); } });
Expand Down
6 changes: 0 additions & 6 deletions apps/daemon/src/routes/static-resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import type { DesignSystemTokenContractRebuildJobResponse } from '@open-design/c
import { detectAgents, detectAgentsStream } from '../agents.js';
import {
SkillImportError,
deleteUserSkill,
findSkillById,
importUserSkill,
listSkillFiles,
Expand All @@ -15,15 +14,12 @@ import {
} from '../skills.js';
import { listCodexPets, readCodexPetSpritesheet } from '../codex-pets.js';
import { syncCommunityPets } from '../community-pets-sync.js';
import { readDesignSystem } from '../design-systems/index.js';
import {
LocalDesignSystemImportError,
importLocalDesignSystemProject,
} from '../design-systems/import.js';
import { importGitHubDesignSystemProject } from '../design-systems/github-import.js';
import { importShadcnDesignSystemProject } from '../design-systems/shadcn-import.js';
import { renderDesignSystemPreview } from '../design-systems/preview.js';
import { renderDesignSystemShowcase } from '../design-systems/showcase.js';
import { listPromptTemplates, readPromptTemplate } from '../media/prompt-templates.js';
import { readAppConfig } from '../app-config.js';
import { installFromTarget, uninstallById } from '../library-install.js';
Expand Down Expand Up @@ -75,8 +71,6 @@ export function registerStaticResourceRoutes(app: Express, ctx: RegisterStaticRe
PROJECT_ROOT,
DESIGN_SYSTEMS_DIR,
USER_DESIGN_SYSTEMS_DIR,
DESIGN_TEMPLATES_DIR,
USER_DESIGN_TEMPLATES_DIR,
SKILLS_DIR,
USER_SKILLS_DIR,
PROMPT_TEMPLATES_DIR,
Expand Down
Loading
Loading