diff --git a/.gitignore b/.gitignore
index c5226b51..0bad302e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
# dependencies (bun install)
node_modules
+btca.config.jsonc
.claude/
diff --git a/apps/cli/package.json b/apps/cli/package.json
index 887a3680..cbf73382 100644
--- a/apps/cli/package.json
+++ b/apps/cli/package.json
@@ -1,7 +1,7 @@
{
"name": "btca",
"author": "Ben Davis",
- "version": "2.0.3",
+ "version": "2.0.4",
"homepage": "https://btca.dev",
"description": "CLI tool for asking questions about technologies using btca server",
"type": "module",
diff --git a/apps/server/package.json b/apps/server/package.json
index f3c567e8..e5e5ddd1 100644
--- a/apps/server/package.json
+++ b/apps/server/package.json
@@ -1,6 +1,6 @@
{
"name": "btca-server",
- "version": "2.0.3",
+ "version": "2.0.4",
"description": "BTCA server for answering questions about your codebase using OpenCode AI",
"author": "Ben Davis",
"license": "MIT",
diff --git a/apps/server/src/agent/loop.ts b/apps/server/src/agent/loop.ts
index 4e086a15..d0d8d1b6 100644
--- a/apps/server/src/agent/loop.ts
+++ b/apps/server/src/agent/loop.ts
@@ -57,35 +57,57 @@ export type AgentLoopResult = {
events: AgentEvent[];
};
+const BASE_PROMPT = `
+You are btca, an expert research agent. Your job is to answer questions from the user by searching the resources at your disposal.
+
+
+- Persona: an expert professional researcher
+- Channel: internal
+- Emotional register: direct, calm, and concise
+- Formatting: bulleted/numbered lists are good + codeblocks
+- Length: be thorough with your response, don't let it get too long though
+- Default follow-through: don't ask permission to do the research, just do it and answer the question. ask for clarifications + suggest good follow up if needed
+
+
+
+- When multiple retrieval or lookup steps are independent, prefer parallel tool calls to reduce wall-clock time.
+- Do not parallelize steps that have prerequisite dependencies or where one result determines the next action.
+- After parallel retrieval, pause to synthesize the results before making more calls.
+- Prefer selective parallelism: parallelize independent evidence gathering, not speculative or redundant tool use.
+
+
+
+- Use tools whenever they materially improve correctness, completeness, or grounding.
+- Do NOT stop early to save tool calls.
+- Keep calling tools until either:
+ 1) the task is complete
+ 2) you've hit a doom loop where none of the tools function or something is missing
+- If a tool returns empty/partial results, retry with a different strategy (query, filters, alternate source).
+
+
+
+- Treat the task as incomplete until you have a complete answer to the user's question that's grounded
+- If any item is blocked by missing data, mark it [blocked] and state exactly what is missing.
+
+
+
+- Don't stop at the first plausible answer.
+- Look for second-order issues, edge cases, and missing constraints.
+
+
+
+- Return a thorough answer to the user's question with real code examples
+- Always output in proper markdown format
+- Always include sources for your answer:
+ - For git resources, source links must be full github blob urls
+ - In "Sources", format git citations as markdown links: - [repo/relative/path.ext](https://github.com/.../blob/.../repo/relative/path.ext)".'
+ - For local resources cite local file paths
+ - For npm resources cite the path in the npm package
+
+`;
+
const buildSystemPrompt = (agentInstructions: string): string =>
- [
- 'You are btca, an expert documentation search agent.',
- 'Your job is to answer questions by searching through the collection of resources.',
- '',
- 'You have access to the following tools:',
- '- read: Read file contents with line numbers',
- '- grep: Search file contents using regex patterns',
- '- glob: Find files matching glob patterns',
- '- list: List directory contents',
- '',
- 'Guidelines:',
- '- Ground answers in the loaded resources. Do not rely on unstated prior knowledge.',
- '- Search efficiently: start with one focused list/glob pass, then read likely files; only expand search when evidence is insufficient.',
- '- Prefer targeted grep/read over broad repeated scans once candidate files are known.',
- '- Give clear, unambiguous answers. State assumptions, prerequisites, and important version-sensitive caveats.',
- '- For implementation/how-to questions, provide complete step-by-step instructions with commands and code snippets.',
- '- Be concise but thorough in your responses.',
- '- End every answer with a "Sources" section.',
- '- For git resources, source links must be full GitHub blob URLs.',
- '- In "Sources", format git citations as markdown links: "- [repo/relative/path.ext](https://github.com/.../blob/.../repo/relative/path.ext)".',
- '- Do not use raw URLs as link labels.',
- '- Do not repeat a URL in parentheses after a link.',
- '- Do not output sources in "url (url)" format.',
- '- For local resources, cite local file paths (no GitHub URL required).',
- '- If you cannot find the answer, say so clearly',
- '',
- agentInstructions
- ].join('\n');
+ [BASE_PROMPT, agentInstructions].join('\n');
const createTools = (basePath: string, vfsId?: string) => ({
read: tool({
diff --git a/apps/server/src/collections/service.test.ts b/apps/server/src/collections/service.test.ts
index 23fa603d..cd5d3b83 100644
--- a/apps/server/src/collections/service.test.ts
+++ b/apps/server/src/collections/service.test.ts
@@ -4,31 +4,45 @@ import os from 'node:os';
import path from 'node:path';
import type { ConfigService } from '../config/index.ts';
+import type { ResourceDefinition } from '../resources/schema.ts';
import type { ResourcesService } from '../resources/service.ts';
+import type { BtcaFsResource } from '../resources/types.ts';
import { createCollectionsService } from './service.ts';
import { disposeVirtualFs, existsInVirtualFs } from '../vfs/virtual-fs.ts';
-const createLocalResource = (name: string, resourcePath: string) => ({
+const createFsResource = ({
+ name,
+ resourcePath,
+ type = 'local',
+ repoSubPaths = [],
+ specialAgentInstructions = ''
+}: {
+ name: string;
+ resourcePath: string;
+ type?: BtcaFsResource['type'];
+ repoSubPaths?: readonly string[];
+ specialAgentInstructions?: string;
+}) => ({
_tag: 'fs-based' as const,
name,
fsName: name,
- type: 'local' as const,
- repoSubPaths: [],
- specialAgentInstructions: '',
+ type,
+ repoSubPaths,
+ specialAgentInstructions,
getAbsoluteDirectoryPath: async () => resourcePath
});
-const createConfigMock = () =>
+const createConfigMock = (definitions: Record = {}) =>
({
- getResource: () => undefined
+ getResource: (name: string) => definitions[name]
}) as unknown as ConfigService;
-const createResourcesMock = (resourcePath: string) =>
+const createResourcesMock = (loadPromise: ResourcesService['loadPromise']) =>
({
load: () => {
throw new Error('Not implemented in test');
},
- loadPromise: async () => createLocalResource('repo', resourcePath)
+ loadPromise
}) as unknown as ResourcesService;
const runGit = (cwd: string, args: string[]) => {
@@ -55,7 +69,7 @@ describe('createCollectionsService', () => {
const resourcePath = await fs.mkdtemp(path.join(os.tmpdir(), 'btca-collections-git-'));
const collections = createCollectionsService({
config: createConfigMock(),
- resources: createResourcesMock(resourcePath)
+ resources: createResourcesMock(async () => createFsResource({ name: 'repo', resourcePath }))
});
try {
@@ -89,7 +103,7 @@ describe('createCollectionsService', () => {
const resourcePath = await fs.mkdtemp(path.join(os.tmpdir(), 'btca-collections-local-'));
const collections = createCollectionsService({
config: createConfigMock(),
- resources: createResourcesMock(resourcePath)
+ resources: createResourcesMock(async () => createFsResource({ name: 'repo', resourcePath }))
});
try {
@@ -109,6 +123,129 @@ describe('createCollectionsService', () => {
false
);
expect(await existsInVirtualFs('/repo/dist/bundle.js', collection.vfsId)).toBe(false);
+ expect(collection.agentInstructions).not.toContain('');
+ } finally {
+ await cleanupCollection(collection);
+ }
+ } finally {
+ await fs.rm(resourcePath, { recursive: true, force: true });
+ }
+ });
+
+ it('includes git citation metadata in agent instructions', async () => {
+ const resourcePath = await fs.mkdtemp(path.join(os.tmpdir(), 'btca-collections-git-meta-'));
+ const collections = createCollectionsService({
+ config: createConfigMock({
+ docs: {
+ type: 'git',
+ name: 'docs',
+ url: 'https://github.com/example/repo.git',
+ branch: 'main',
+ searchPath: 'guides',
+ specialNotes: 'Prefer the guides folder.'
+ }
+ }),
+ resources: createResourcesMock(async () =>
+ createFsResource({
+ name: 'docs',
+ resourcePath,
+ type: 'git',
+ repoSubPaths: ['guides'],
+ specialAgentInstructions: 'Prefer the guides folder.'
+ })
+ )
+ });
+
+ try {
+ await fs.writeFile(path.join(resourcePath, 'README.md'), 'hello\n');
+ runGit(resourcePath, ['init', '-q']);
+ runGit(resourcePath, ['config', 'user.email', 'test@example.com']);
+ runGit(resourcePath, ['config', 'user.name', 'BTCA Test']);
+ runGit(resourcePath, ['add', 'README.md']);
+ runGit(resourcePath, ['commit', '-m', 'init']);
+
+ const collection = await collections.loadPromise({ resourceNames: ['docs'] });
+
+ try {
+ expect(collection.agentInstructions).toContain(
+ 'https://github.com/example/repo'
+ );
+ expect(collection.agentInstructions).toContain('main');
+ expect(collection.agentInstructions).toContain(
+ 'https://github.com/example/repo/blob/main'
+ );
+ expect(collection.agentInstructions).toContain(
+ 'Convert virtual paths under ./docs/ to repo-relative paths, then encode each path segment for GitHub URLs.'
+ );
+ expect(collection.agentInstructions).toContain('./docs/guides');
+ expect(collection.agentInstructions).toContain('');
+ expect(collection.agentInstructions).toContain(
+ 'Prefer the guides folder.'
+ );
+ } finally {
+ await cleanupCollection(collection);
+ }
+ } finally {
+ await fs.rm(resourcePath, { recursive: true, force: true });
+ }
+ });
+
+ it('includes npm citation metadata in agent instructions', async () => {
+ const resourcePath = await fs.mkdtemp(path.join(os.tmpdir(), 'btca-collections-npm-meta-'));
+ const collections = createCollectionsService({
+ config: createConfigMock({
+ react: {
+ type: 'npm',
+ name: 'react',
+ package: 'react',
+ version: '19.0.0',
+ specialNotes: 'Use package docs.'
+ }
+ }),
+ resources: createResourcesMock(async () =>
+ createFsResource({
+ name: 'react',
+ resourcePath,
+ type: 'npm',
+ specialAgentInstructions: 'Use package docs.'
+ })
+ )
+ });
+
+ try {
+ await fs.writeFile(
+ path.join(resourcePath, '.btca-npm-meta.json'),
+ JSON.stringify({
+ packageName: 'react',
+ resolvedVersion: '19.0.0',
+ packageUrl: 'https://www.npmjs.com/package/react'
+ })
+ );
+ await fs.writeFile(path.join(resourcePath, 'README.md'), 'react docs\n');
+
+ const collection = await collections.loadPromise({ resourceNames: ['react'] });
+
+ try {
+ expect(collection.agentInstructions).toContain('react');
+ expect(collection.agentInstructions).toContain('19.0.0');
+ expect(collection.agentInstructions).toContain(
+ 'https://www.npmjs.com/package/react'
+ );
+ expect(collection.agentInstructions).toContain(
+ 'npm:react@19.0.0'
+ );
+ expect(collection.agentInstructions).toContain(
+ 'https://unpkg.com/react@19.0.0'
+ );
+ expect(collection.agentInstructions).toContain(
+ 'In Sources, cite npm files using npm:react@19.0.0/<file> and link them to https://unpkg.com/react@19.0.0/<file>. Do not cite encoded virtual folder names.'
+ );
+ expect(collection.agentInstructions).toContain(
+ 'https://unpkg.com/react@19.0.0/package.json'
+ );
+ expect(collection.agentInstructions).toContain(
+ 'Use package docs.'
+ );
} finally {
await cleanupCollection(collection);
}
diff --git a/apps/server/src/collections/service.ts b/apps/server/src/collections/service.ts
index af225650..a8e93934 100644
--- a/apps/server/src/collections/service.ts
+++ b/apps/server/src/collections/service.ts
@@ -36,56 +36,110 @@ export type CollectionsService = {
}) => Promise;
};
-const encodePathSegments = (value: string) => value.split('/').map(encodeURIComponent).join('/');
+const escapeXml = (value: string) =>
+ value
+ .replaceAll('&', '&')
+ .replaceAll('<', '<')
+ .replaceAll('>', '>')
+ .replaceAll('"', '"')
+ .replaceAll("'", ''');
+
+const getResourceTypeLabel = (resource: BtcaFsResource) => {
+ if (resource.type === 'git') return 'git repo';
+ if (resource.type === 'npm') return 'npm package';
+ return 'local directory';
+};
const trimGitSuffix = (url: string) => url.replace(/\.git$/u, '').replace(/\/+$/u, '');
+
+const encodePathSegments = (value: string) => value.split('/').map(encodeURIComponent).join('/');
+
+const xmlLine = (tag: string, value?: string) =>
+ value ? `\t\t<${tag}>${escapeXml(value)}${tag}>` : '';
+
+const xmlPathBlock = (tag: string, values: readonly string[], prefix = '') =>
+ values.length === 0
+ ? ''
+ : [
+ `\t\t<${tag}>`,
+ ...values.map((value) => `\t\t\t${escapeXml(`${prefix}${value}`)}`),
+ `\t\t${tag}>`
+ ].join('\n');
+
const getNpmCitationAlias = (metadata?: VirtualResourceMetadata) => {
if (!metadata?.package) return undefined;
return `npm:${metadata.package}@${metadata.version ?? 'latest'}`;
};
+const getNpmFileUrlPrefix = (metadata?: VirtualResourceMetadata) => {
+ if (!metadata?.package || !metadata?.version) return undefined;
+ return `https://unpkg.com/${metadata.package}@${metadata.version}`;
+};
+
const createCollectionInstructionBlock = (
resource: BtcaFsResource,
metadata?: VirtualResourceMetadata
-): string => {
- const focusLines = resource.repoSubPaths.map(
- (subPath) => `Focus: ./${resource.fsName}/${subPath}`
- );
+) => {
+ const repoUrl =
+ resource.type === 'git' && metadata?.url ? trimGitSuffix(metadata.url) : undefined;
const gitRef = metadata?.branch ?? metadata?.commit;
- const githubPrefix =
- resource.type === 'git' && metadata?.url && gitRef
- ? `${trimGitSuffix(metadata.url)}/blob/${encodeURIComponent(gitRef)}`
- : undefined;
+ const githubBlobPrefix =
+ repoUrl && gitRef ? `${repoUrl}/blob/${encodeURIComponent(gitRef)}` : undefined;
const npmCitationAlias = resource.type === 'npm' ? getNpmCitationAlias(metadata) : undefined;
- const lines = [
- `## Resource: ${resource.name}`,
- FS_RESOURCE_SYSTEM_NOTE,
- `Path: ./${resource.fsName}`,
- resource.type === 'git' && metadata?.url ? `Repo URL: ${trimGitSuffix(metadata.url)}` : '',
- resource.type === 'git' && metadata?.branch ? `Repo Branch: ${metadata.branch}` : '',
- resource.type === 'git' && metadata?.commit ? `Repo Commit: ${metadata.commit}` : '',
- resource.type === 'npm' && metadata?.package ? `NPM Package: ${metadata.package}` : '',
- resource.type === 'npm' && metadata?.version ? `NPM Version: ${metadata.version}` : '',
- resource.type === 'npm' && metadata?.url ? `NPM URL: ${metadata.url}` : '',
- npmCitationAlias ? `NPM Citation Alias: ${npmCitationAlias}` : '',
- githubPrefix ? `GitHub Blob Prefix: ${githubPrefix}` : '',
- githubPrefix
- ? `GitHub Citation Rule: Convert virtual paths under ./${resource.fsName}/ to repo-relative paths, then encode each path segment for GitHub URLs (example segment: "+page.server.js" -> "${encodeURIComponent('+page.server.js')}").`
- : '',
- githubPrefix
- ? `GitHub Citation Example: ${githubPrefix}/${encodePathSegments('src/routes/blog/+page.server.js')}`
- : '',
- resource.type !== 'git'
- ? 'Citation Rule: Cite local file paths only for this resource (no GitHub URL).'
- : '',
- npmCitationAlias
- ? `NPM Citation Rule: In "Sources", cite npm files using "${npmCitationAlias}/" (for example, "${npmCitationAlias}/README.md"). Do not cite encoded virtual folder names.`
- : '',
- ...focusLines,
- resource.specialAgentInstructions ? `Notes: ${resource.specialAgentInstructions}` : ''
- ].filter(Boolean);
-
- return lines.join('\n');
+ const npmFileUrlPrefix = resource.type === 'npm' ? getNpmFileUrlPrefix(metadata) : undefined;
+
+ return [
+ '\t',
+ `\t\t${escapeXml(resource.name)}`,
+ `\t\t${getResourceTypeLabel(resource)}`,
+ `\t\t${escapeXml(FS_RESOURCE_SYSTEM_NOTE)}`,
+ `\t\t${escapeXml(`./${resource.fsName}`)}`,
+ xmlLine('repo_url', repoUrl),
+ xmlLine('repo_branch', resource.type === 'git' ? metadata?.branch : undefined),
+ xmlLine('repo_commit', resource.type === 'git' ? metadata?.commit : undefined),
+ xmlLine('npm_package', resource.type === 'npm' ? metadata?.package : undefined),
+ xmlLine('npm_version', resource.type === 'npm' ? metadata?.version : undefined),
+ xmlLine('npm_url', resource.type === 'npm' ? metadata?.url : undefined),
+ xmlLine('npm_citation_alias', npmCitationAlias),
+ xmlLine('npm_file_url_prefix', npmFileUrlPrefix),
+ xmlLine('github_blob_prefix', githubBlobPrefix),
+ xmlLine(
+ 'citation_rule',
+ githubBlobPrefix
+ ? `Convert virtual paths under ./${resource.fsName}/ to repo-relative paths, then encode each path segment for GitHub URLs.`
+ : resource.type === 'npm' && npmCitationAlias
+ ? `In Sources, cite npm files using ${npmCitationAlias}/ and link them to ${npmFileUrlPrefix ?? 'the exact file URL prefix'}/. Do not cite encoded virtual folder names.`
+ : 'Cite local file paths only for this resource.'
+ ),
+ xmlLine(
+ 'citation_example',
+ githubBlobPrefix
+ ? `${githubBlobPrefix}/${encodePathSegments('src/routes/blog/+page.server.js')}`
+ : resource.type === 'npm' && npmCitationAlias && npmFileUrlPrefix
+ ? `${npmFileUrlPrefix}/package.json`
+ : undefined
+ ),
+ xmlPathBlock('focus_paths', resource.repoSubPaths, `./${resource.fsName}/`),
+ xmlLine('special_notes', resource.specialAgentInstructions),
+ '\t'
+ ]
+ .filter(Boolean)
+ .join('\n');
+};
+
+const createCollectionInstructions = (
+ resources: readonly BtcaFsResource[],
+ metadataResources: readonly VirtualResourceMetadata[]
+) => {
+ const metadataByName = new Map(metadataResources.map((resource) => [resource.name, resource]));
+
+ return [
+ '',
+ ...resources.map((resource) =>
+ createCollectionInstructionBlock(resource, metadataByName.get(resource.name))
+ ),
+ ''
+ ].join('\n');
};
const ignoreErrors = async (action: () => Promise) => {
@@ -400,16 +454,9 @@ export const createCollectionsService = (args: {
resources: metadataResources
});
- const metadataByName = new Map(
- metadataResources.map((resource) => [resource.name, resource])
- );
- const instructionBlocks = loadedResources.map((resource) =>
- createCollectionInstructionBlock(resource, metadataByName.get(resource.name))
- );
-
return {
path: collectionPath,
- agentInstructions: instructionBlocks.join('\n\n'),
+ agentInstructions: createCollectionInstructions(loadedResources, metadataResources),
vfsId,
cleanup: async () => {
await cleanupResources(loadedResources);
diff --git a/apps/web/src/convex/_generated/api.d.ts b/apps/web/src/convex/_generated/api.d.ts
index 7c558585..6d941959 100644
--- a/apps/web/src/convex/_generated/api.d.ts
+++ b/apps/web/src/convex/_generated/api.d.ts
@@ -8,76 +8,72 @@
* @module
*/
-import type * as analytics from "../analytics.js";
-import type * as analyticsEvents from "../analyticsEvents.js";
-import type * as apiHelpers from "../apiHelpers.js";
-import type * as authHelpers from "../authHelpers.js";
-import type * as clerkApiKeys from "../clerkApiKeys.js";
-import type * as clerkApiKeysQueries from "../clerkApiKeysQueries.js";
-import type * as crons from "../crons.js";
-import type * as githubAuth from "../githubAuth.js";
-import type * as githubConnections from "../githubConnections.js";
-import type * as http from "../http.js";
-import type * as instances_actions from "../instances/actions.js";
-import type * as instances_mutations from "../instances/mutations.js";
-import type * as instances_queries from "../instances/queries.js";
-import type * as mcp from "../mcp.js";
-import type * as mcp_resourceContract from "../mcp/resourceContract.js";
-import type * as mcpInternal from "../mcpInternal.js";
-import type * as mcpQuestions from "../mcpQuestions.js";
-import type * as messages from "../messages.js";
-import type * as migrations from "../migrations.js";
-import type * as privateWrappers from "../privateWrappers.js";
-import type * as projects from "../projects.js";
-import type * as resourceActions from "../resourceActions.js";
-import type * as resources from "../resources.js";
-import type * as scheduled_queries from "../scheduled/queries.js";
-import type * as scheduled_updates from "../scheduled/updates.js";
-import type * as scheduled_versionCheck from "../scheduled/versionCheck.js";
-import type * as streamSessions from "../streamSessions.js";
-import type * as threadTitle from "../threadTitle.js";
-import type * as threads from "../threads.js";
-import type * as usage from "../usage.js";
-import type * as users from "../users.js";
+import type * as analytics from '../analytics.js';
+import type * as analyticsEvents from '../analyticsEvents.js';
+import type * as apiHelpers from '../apiHelpers.js';
+import type * as authHelpers from '../authHelpers.js';
+import type * as clerkApiKeys from '../clerkApiKeys.js';
+import type * as clerkApiKeysQueries from '../clerkApiKeysQueries.js';
+import type * as crons from '../crons.js';
+import type * as githubAuth from '../githubAuth.js';
+import type * as githubConnections from '../githubConnections.js';
+import type * as http from '../http.js';
+import type * as instances_actions from '../instances/actions.js';
+import type * as instances_mutations from '../instances/mutations.js';
+import type * as instances_queries from '../instances/queries.js';
+import type * as mcp from '../mcp.js';
+import type * as mcp_resourceContract from '../mcp/resourceContract.js';
+import type * as mcpInternal from '../mcpInternal.js';
+import type * as mcpQuestions from '../mcpQuestions.js';
+import type * as messages from '../messages.js';
+import type * as migrations from '../migrations.js';
+import type * as privateWrappers from '../privateWrappers.js';
+import type * as projects from '../projects.js';
+import type * as resourceActions from '../resourceActions.js';
+import type * as resources from '../resources.js';
+import type * as scheduled_queries from '../scheduled/queries.js';
+import type * as scheduled_updates from '../scheduled/updates.js';
+import type * as scheduled_versionCheck from '../scheduled/versionCheck.js';
+import type * as streamSessions from '../streamSessions.js';
+import type * as threadTitle from '../threadTitle.js';
+import type * as threads from '../threads.js';
+import type * as usage from '../usage.js';
+import type * as users from '../users.js';
-import type {
- ApiFromModules,
- FilterApi,
- FunctionReference,
-} from "convex/server";
+import type { ApiFromModules, FilterApi, FunctionReference } from 'convex/server';
declare const fullApi: ApiFromModules<{
- analytics: typeof analytics;
- analyticsEvents: typeof analyticsEvents;
- apiHelpers: typeof apiHelpers;
- authHelpers: typeof authHelpers;
- clerkApiKeys: typeof clerkApiKeys;
- clerkApiKeysQueries: typeof clerkApiKeysQueries;
- crons: typeof crons;
- githubAuth: typeof githubAuth;
- githubConnections: typeof githubConnections;
- http: typeof http;
- "instances/actions": typeof instances_actions;
- "instances/mutations": typeof instances_mutations;
- "instances/queries": typeof instances_queries;
- mcp: typeof mcp;
- "mcp/resourceContract": typeof mcp_resourceContract;
- mcpInternal: typeof mcpInternal;
- mcpQuestions: typeof mcpQuestions;
- messages: typeof messages;
- migrations: typeof migrations;
- privateWrappers: typeof privateWrappers;
- projects: typeof projects;
- resourceActions: typeof resourceActions;
- resources: typeof resources;
- "scheduled/queries": typeof scheduled_queries;
- "scheduled/updates": typeof scheduled_updates;
- "scheduled/versionCheck": typeof scheduled_versionCheck;
- streamSessions: typeof streamSessions;
- threadTitle: typeof threadTitle;
- threads: typeof threads;
- usage: typeof usage;
- users: typeof users;
+ analytics: typeof analytics;
+ analyticsEvents: typeof analyticsEvents;
+ apiHelpers: typeof apiHelpers;
+ authHelpers: typeof authHelpers;
+ clerkApiKeys: typeof clerkApiKeys;
+ clerkApiKeysQueries: typeof clerkApiKeysQueries;
+ crons: typeof crons;
+ githubAuth: typeof githubAuth;
+ githubConnections: typeof githubConnections;
+ http: typeof http;
+ 'instances/actions': typeof instances_actions;
+ 'instances/mutations': typeof instances_mutations;
+ 'instances/queries': typeof instances_queries;
+ mcp: typeof mcp;
+ 'mcp/resourceContract': typeof mcp_resourceContract;
+ mcpInternal: typeof mcpInternal;
+ mcpQuestions: typeof mcpQuestions;
+ messages: typeof messages;
+ migrations: typeof migrations;
+ privateWrappers: typeof privateWrappers;
+ projects: typeof projects;
+ resourceActions: typeof resourceActions;
+ resources: typeof resources;
+ 'scheduled/queries': typeof scheduled_queries;
+ 'scheduled/updates': typeof scheduled_updates;
+ 'scheduled/versionCheck': typeof scheduled_versionCheck;
+ streamSessions: typeof streamSessions;
+ threadTitle: typeof threadTitle;
+ threads: typeof threads;
+ usage: typeof usage;
+ users: typeof users;
}>;
/**
@@ -88,10 +84,7 @@ declare const fullApi: ApiFromModules<{
* const myFunctionReference = api.myModule.myFunction;
* ```
*/
-export declare const api: FilterApi<
- typeof fullApi,
- FunctionReference
->;
+export declare const api: FilterApi>;
/**
* A utility for referencing Convex functions in your app's internal API.
@@ -101,96 +94,88 @@ export declare const api: FilterApi<
* const myFunctionReference = internal.myModule.myFunction;
* ```
*/
-export declare const internal: FilterApi<
- typeof fullApi,
- FunctionReference
->;
+export declare const internal: FilterApi>;
export declare const components: {
- migrations: {
- lib: {
- cancel: FunctionReference<
- "mutation",
- "internal",
- { name: string },
- {
- batchSize?: number;
- cursor?: string | null;
- error?: string;
- isDone: boolean;
- latestEnd?: number;
- latestStart: number;
- name: string;
- next?: Array;
- processed: number;
- state: "inProgress" | "success" | "failed" | "canceled" | "unknown";
- }
- >;
- cancelAll: FunctionReference<
- "mutation",
- "internal",
- { sinceTs?: number },
- Array<{
- batchSize?: number;
- cursor?: string | null;
- error?: string;
- isDone: boolean;
- latestEnd?: number;
- latestStart: number;
- name: string;
- next?: Array;
- processed: number;
- state: "inProgress" | "success" | "failed" | "canceled" | "unknown";
- }>
- >;
- clearAll: FunctionReference<
- "mutation",
- "internal",
- { before?: number },
- null
- >;
- getStatus: FunctionReference<
- "query",
- "internal",
- { limit?: number; names?: Array },
- Array<{
- batchSize?: number;
- cursor?: string | null;
- error?: string;
- isDone: boolean;
- latestEnd?: number;
- latestStart: number;
- name: string;
- next?: Array;
- processed: number;
- state: "inProgress" | "success" | "failed" | "canceled" | "unknown";
- }>
- >;
- migrate: FunctionReference<
- "mutation",
- "internal",
- {
- batchSize?: number;
- cursor?: string | null;
- dryRun: boolean;
- fnHandle: string;
- name: string;
- next?: Array<{ fnHandle: string; name: string }>;
- oneBatchOnly?: boolean;
- },
- {
- batchSize?: number;
- cursor?: string | null;
- error?: string;
- isDone: boolean;
- latestEnd?: number;
- latestStart: number;
- name: string;
- next?: Array;
- processed: number;
- state: "inProgress" | "success" | "failed" | "canceled" | "unknown";
- }
- >;
- };
- };
+ migrations: {
+ lib: {
+ cancel: FunctionReference<
+ 'mutation',
+ 'internal',
+ { name: string },
+ {
+ batchSize?: number;
+ cursor?: string | null;
+ error?: string;
+ isDone: boolean;
+ latestEnd?: number;
+ latestStart: number;
+ name: string;
+ next?: Array;
+ processed: number;
+ state: 'inProgress' | 'success' | 'failed' | 'canceled' | 'unknown';
+ }
+ >;
+ cancelAll: FunctionReference<
+ 'mutation',
+ 'internal',
+ { sinceTs?: number },
+ Array<{
+ batchSize?: number;
+ cursor?: string | null;
+ error?: string;
+ isDone: boolean;
+ latestEnd?: number;
+ latestStart: number;
+ name: string;
+ next?: Array;
+ processed: number;
+ state: 'inProgress' | 'success' | 'failed' | 'canceled' | 'unknown';
+ }>
+ >;
+ clearAll: FunctionReference<'mutation', 'internal', { before?: number }, null>;
+ getStatus: FunctionReference<
+ 'query',
+ 'internal',
+ { limit?: number; names?: Array },
+ Array<{
+ batchSize?: number;
+ cursor?: string | null;
+ error?: string;
+ isDone: boolean;
+ latestEnd?: number;
+ latestStart: number;
+ name: string;
+ next?: Array;
+ processed: number;
+ state: 'inProgress' | 'success' | 'failed' | 'canceled' | 'unknown';
+ }>
+ >;
+ migrate: FunctionReference<
+ 'mutation',
+ 'internal',
+ {
+ batchSize?: number;
+ cursor?: string | null;
+ dryRun: boolean;
+ fnHandle: string;
+ name: string;
+ next?: Array<{ fnHandle: string; name: string }>;
+ oneBatchOnly?: boolean;
+ },
+ {
+ batchSize?: number;
+ cursor?: string | null;
+ error?: string;
+ isDone: boolean;
+ latestEnd?: number;
+ latestStart: number;
+ name: string;
+ next?: Array;
+ processed: number;
+ state: 'inProgress' | 'success' | 'failed' | 'canceled' | 'unknown';
+ }
+ >;
+ };
+ };
};
diff --git a/apps/web/src/convex/_generated/api.js b/apps/web/src/convex/_generated/api.js
index 44bf9858..24593c74 100644
--- a/apps/web/src/convex/_generated/api.js
+++ b/apps/web/src/convex/_generated/api.js
@@ -8,7 +8,7 @@
* @module
*/
-import { anyApi, componentsGeneric } from "convex/server";
+import { anyApi, componentsGeneric } from 'convex/server';
/**
* A utility for referencing Convex functions in your app's API.
diff --git a/apps/web/src/convex/_generated/dataModel.d.ts b/apps/web/src/convex/_generated/dataModel.d.ts
index f97fd194..5428df6f 100644
--- a/apps/web/src/convex/_generated/dataModel.d.ts
+++ b/apps/web/src/convex/_generated/dataModel.d.ts
@@ -9,13 +9,13 @@
*/
import type {
- DataModelFromSchemaDefinition,
- DocumentByName,
- TableNamesInDataModel,
- SystemTableNames,
-} from "convex/server";
-import type { GenericId } from "convex/values";
-import schema from "../schema.js";
+ DataModelFromSchemaDefinition,
+ DocumentByName,
+ TableNamesInDataModel,
+ SystemTableNames
+} from 'convex/server';
+import type { GenericId } from 'convex/values';
+import schema from '../schema.js';
/**
* The names of all of your Convex tables.
@@ -27,10 +27,7 @@ export type TableNames = TableNamesInDataModel;
*
* @typeParam TableName - A string literal type of the table name (like "users").
*/
-export type Doc = DocumentByName<
- DataModel,
- TableName
->;
+export type Doc = DocumentByName;
/**
* An identifier for a document in Convex.
@@ -45,8 +42,7 @@ export type Doc = DocumentByName<
*
* @typeParam TableName - A string literal type of the table name (like "users").
*/
-export type Id =
- GenericId;
+export type Id = GenericId;
/**
* A type describing your Convex data model.
diff --git a/apps/web/src/convex/_generated/server.d.ts b/apps/web/src/convex/_generated/server.d.ts
index bec05e68..1cc047ef 100644
--- a/apps/web/src/convex/_generated/server.d.ts
+++ b/apps/web/src/convex/_generated/server.d.ts
@@ -9,17 +9,17 @@
*/
import {
- ActionBuilder,
- HttpActionBuilder,
- MutationBuilder,
- QueryBuilder,
- GenericActionCtx,
- GenericMutationCtx,
- GenericQueryCtx,
- GenericDatabaseReader,
- GenericDatabaseWriter,
-} from "convex/server";
-import type { DataModel } from "./dataModel.js";
+ ActionBuilder,
+ HttpActionBuilder,
+ MutationBuilder,
+ QueryBuilder,
+ GenericActionCtx,
+ GenericMutationCtx,
+ GenericQueryCtx,
+ GenericDatabaseReader,
+ GenericDatabaseWriter
+} from 'convex/server';
+import type { DataModel } from './dataModel.js';
/**
* Define a query in this Convex app's public API.
@@ -29,7 +29,7 @@ import type { DataModel } from "./dataModel.js";
* @param func - The query function. It receives a {@link QueryCtx} as its first argument.
* @returns The wrapped query. Include this as an `export` to name it and make it accessible.
*/
-export declare const query: QueryBuilder;
+export declare const query: QueryBuilder;
/**
* Define a query that is only accessible from other Convex functions (but not from the client).
@@ -39,7 +39,7 @@ export declare const query: QueryBuilder;
* @param func - The query function. It receives a {@link QueryCtx} as its first argument.
* @returns The wrapped query. Include this as an `export` to name it and make it accessible.
*/
-export declare const internalQuery: QueryBuilder;
+export declare const internalQuery: QueryBuilder;
/**
* Define a mutation in this Convex app's public API.
@@ -49,7 +49,7 @@ export declare const internalQuery: QueryBuilder;
* @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.
* @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.
*/
-export declare const mutation: MutationBuilder;
+export declare const mutation: MutationBuilder;
/**
* Define a mutation that is only accessible from other Convex functions (but not from the client).
@@ -59,7 +59,7 @@ export declare const mutation: MutationBuilder;
* @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.
* @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.
*/
-export declare const internalMutation: MutationBuilder;
+export declare const internalMutation: MutationBuilder;
/**
* Define an action in this Convex app's public API.
@@ -72,7 +72,7 @@ export declare const internalMutation: MutationBuilder;
* @param func - The action. It receives an {@link ActionCtx} as its first argument.
* @returns The wrapped action. Include this as an `export` to name it and make it accessible.
*/
-export declare const action: ActionBuilder;
+export declare const action: ActionBuilder;
/**
* Define an action that is only accessible from other Convex functions (but not from the client).
@@ -80,7 +80,7 @@ export declare const action: ActionBuilder;
* @param func - The function. It receives an {@link ActionCtx} as its first argument.
* @returns The wrapped function. Include this as an `export` to name it and make it accessible.
*/
-export declare const internalAction: ActionBuilder;
+export declare const internalAction: ActionBuilder;
/**
* Define an HTTP action.
diff --git a/apps/web/src/convex/_generated/server.js b/apps/web/src/convex/_generated/server.js
index bf3d25ad..a18aa285 100644
--- a/apps/web/src/convex/_generated/server.js
+++ b/apps/web/src/convex/_generated/server.js
@@ -9,14 +9,14 @@
*/
import {
- actionGeneric,
- httpActionGeneric,
- queryGeneric,
- mutationGeneric,
- internalActionGeneric,
- internalMutationGeneric,
- internalQueryGeneric,
-} from "convex/server";
+ actionGeneric,
+ httpActionGeneric,
+ queryGeneric,
+ mutationGeneric,
+ internalActionGeneric,
+ internalMutationGeneric,
+ internalQueryGeneric
+} from 'convex/server';
/**
* Define a query in this Convex app's public API.
diff --git a/bun.lock b/bun.lock
index da42831c..1697419e 100644
--- a/bun.lock
+++ b/bun.lock
@@ -20,7 +20,7 @@
},
"apps/cli": {
"name": "btca",
- "version": "2.0.3",
+ "version": "2.0.4",
"bin": {
"btca": "bin.js",
},
@@ -69,7 +69,7 @@
},
"apps/server": {
"name": "btca-server",
- "version": "2.0.3",
+ "version": "2.0.4",
"dependencies": {
"@ai-sdk/anthropic": "^3.0.23",
"@ai-sdk/google": "^3.0.13",
diff --git a/package.json b/package.json
index a5bcd1e0..fc7d1a3e 100644
--- a/package.json
+++ b/package.json
@@ -55,7 +55,7 @@
},
"devDependencies": {
"@types/bun": "^1.3.10",
- "@typescript/native-preview": "^7.0.0-dev.20260303.1",
+ "@typescript/native-preview": "^7.0.0-dev.20260305.1",
"prettier": "^3.8.1",
"prettier-plugin-svelte": "^3.5.1",
"prettier-plugin-tailwindcss": "^0.7.2",