🔨 chore: track user last active time (#10733)

This commit is contained in:
YuTengjing 2025-12-11 17:14:25 +08:00 committed by GitHub
parent 542f4d97dd
commit 458cbf4d5c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 8595 additions and 92 deletions

View file

@ -1,2 +1,2 @@
npm run type-check
npm run typecheck
npx --no-install lint-staged

View file

@ -65,7 +65,7 @@ The project follows a well-organized monorepo structure:
### Type Checking
- Use `bun run type-check` to check for type errors
- Use `bun run typecheck` to check for type errors
### i18n

View file

@ -47,7 +47,7 @@ see @.cursor/rules/typescript.mdc
### Typecheck
- use `bun run type-check` to check type errors.
- use `bun run typecheck` to check type errors.
### i18n
@ -79,7 +79,7 @@ When working with Linear issues:
**Workflow for EACH individual issue:**
1. Complete the implementation for this specific issue
2. Run type check: `bun run type-check`
2. Run type check: `bun run typecheck`
3. Run related tests if applicable
4. Create PR if needed
5. **IMMEDIATELY** update issue status to **"In Review"** (NOT "Done"): `mcp__linear-server__update_issue`

View file

@ -46,7 +46,7 @@ see @.cursor/rules/typescript.mdc
### Typecheck
- use `bun run type-check` to check type errors.
- use `bun run typecheck` to check type errors.
### i18n

View file

@ -50,7 +50,7 @@
"@types/resolve": "^1.20.6",
"@types/semver": "^7.7.1",
"@types/set-cookie-parser": "^2.4.10",
"@typescript/native-preview": "7.0.0-dev.20250711.1",
"@typescript/native-preview": "7.0.0-dev.20251210.1",
"async-retry": "^1.3.3",
"consola": "^3.4.2",
"cookie": "^1.1.1",
@ -67,18 +67,18 @@
"happy-dom": "^20.0.11",
"http-proxy-agent": "^7.0.2",
"https-proxy-agent": "^7.0.6",
"i18next": "^25.6.3",
"i18next": "^25.7.2",
"just-diff": "^6.0.2",
"lodash": "^4.17.21",
"lodash-es": "^4.17.21",
"resolve": "^1.22.11",
"semver": "^7.7.3",
"set-cookie-parser": "^2.7.2",
"tsx": "^4.20.6",
"tsx": "^4.21.0",
"typescript": "^5.9.3",
"undici": "^7.16.0",
"uuid": "^13.0.0",
"vite": "^7.2.4",
"vite": "^7.2.7",
"vitest": "^3.2.4"
},
"pnpm": {

View file

@ -8,23 +8,14 @@
"esModuleInterop": true,
"emitDecoratorMetadata": true,
"composite": true,
"baseUrl": ".",
"experimentalDecorators": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"paths": {
"@/*": [
"src/main/*"
],
"~common/*": [
"src/common/*"
]
"@/*": ["./src/main/*"],
"~common/*": ["./src/common/*"]
}
},
"include": [
"src/main/**/*",
"src/preload/**/*",
"electron-builder.js"
]
}
"include": ["src/main/**/*", "src/preload/**/*", "src/common/**/*", "electron-builder.js"]
}

View file

@ -1063,6 +1063,7 @@ table users {
ban_expires "timestamp with time zone"
two_factor_enabled boolean [default: false]
phone_number_verified boolean
last_active_at "timestamp with time zone" [not null, default: `now()`]
accessed_at "timestamp with time zone" [not null, default: `now()`]
created_at "timestamp with time zone" [not null, default: `now()`]
updated_at "timestamp with time zone" [not null, default: `now()`]

View file

@ -60,7 +60,7 @@
"e2e:install": "playwright install",
"e2e:ui": "playwright test --ui",
"i18n": "npm run workflow:i18n && lobe-i18n && prettier -c --write \"locales/**\"",
"lint": "npm run lint:ts && npm run lint:style && npm run type-check && npm run lint:circular",
"lint": "npm run lint:ts && npm run lint:style && npm run typecheck && npm run lint:circular",
"lint:circular": "npm run lint:circular:main && npm run lint:circular:packages",
"lint:circular:main": "dpdm src/**/*.ts --no-warning --no-tree --exit-code circular:1 --no-progress -T true --skip-dynamic-imports circular",
"lint:circular:packages": "dpdm packages/**/src/**/*.ts --no-warning --no-tree --exit-code circular:1 --no-progress -T true --skip-dynamic-imports circular",
@ -85,7 +85,7 @@
"test:e2e": "pnpm --filter @lobechat/e2e-tests test",
"test:e2e:smoke": "pnpm --filter @lobechat/e2e-tests test:smoke",
"test:update": "vitest -u",
"type-check": "tsgo --noEmit",
"typecheck": "tsgo --noEmit",
"webhook:ngrok": "ngrok http http://localhost:3011",
"workflow:cdn": "tsx ./scripts/cdnWorkflow/index.ts",
"workflow:changelog": "tsx ./scripts/changelogWorkflow/index.ts",
@ -137,20 +137,20 @@
"@azure-rest/ai-inference": "1.0.0-beta.5",
"@azure/core-auth": "^1.10.1",
"@cfworker/json-schema": "^4.1.1",
"@clerk/localizations": "^3.28.5",
"@clerk/nextjs": "^6.35.5",
"@clerk/themes": "^2.4.40",
"@clerk/localizations": "^3.30.1",
"@clerk/nextjs": "^6.36.2",
"@clerk/themes": "^2.4.43",
"@codesandbox/sandpack-react": "^2.20.0",
"@cyntler/react-doc-viewer": "^1.17.1",
"@electric-sql/pglite": "0.2.17",
"@emotion/react": "^11.14.0",
"@fal-ai/client": "^1.7.2",
"@formkit/auto-animate": "^0.9.0",
"@google/genai": "^1.30.0",
"@huggingface/inference": "^4.13.4",
"@google/genai": "^1.33.0",
"@huggingface/inference": "^4.13.5",
"@icons-pack/react-simple-icons": "^13.8.0",
"@khmyznikov/pwa-install": "0.3.9",
"@langchain/community": "^0.3.57",
"@langchain/community": "^0.3.58",
"@lobechat/agent-runtime": "workspace:*",
"@lobechat/const": "workspace:*",
"@lobechat/context-engine": "workspace:*",
@ -171,14 +171,14 @@
"@lobehub/chat-plugin-sdk": "^1.32.4",
"@lobehub/chat-plugins-gateway": "^1.9.0",
"@lobehub/desktop-ipc-typings": "workspace:*",
"@lobehub/editor": "^1.23.1",
"@lobehub/icons": "^2.43.1",
"@lobehub/market-sdk": "^0.23.2",
"@lobehub/editor": "^1.31.2",
"@lobehub/icons": "^2.45.0",
"@lobehub/market-sdk": "^0.23.7",
"@lobehub/tts": "^2.0.1",
"@lobehub/ui": "^2.18.3",
"@modelcontextprotocol/sdk": "^1.24.0",
"@lobehub/ui": "^2.22.0",
"@modelcontextprotocol/sdk": "^1.24.3",
"@neondatabase/serverless": "^1.0.2",
"@next/third-parties": "^16.0.5",
"@next/third-parties": "^16.0.8",
"@opentelemetry/exporter-jaeger": "^2.2.0",
"@opentelemetry/winston-transport": "^0.19.0",
"@react-pdf/renderer": "^4.3.1",
@ -186,16 +186,16 @@
"@saintno/comfyui-sdk": "^0.2.49",
"@serwist/next": "^9.2.3",
"@t3-oss/env-nextjs": "^0.13.8",
"@tanstack/react-query": "^5.90.11",
"@tanstack/react-query": "^5.90.12",
"@trpc/client": "^11.7.2",
"@trpc/next": "^11.7.2",
"@trpc/react-query": "^11.7.2",
"@trpc/server": "^11.7.2",
"@upstash/redis": "^1.35.7",
"@vercel/analytics": "^1.5.0",
"@vercel/analytics": "^1.6.1",
"@vercel/edge-config": "^1.4.3",
"@vercel/functions": "^3.3.4",
"@vercel/speed-insights": "^1.2.0",
"@vercel/speed-insights": "^1.3.1",
"@virtuoso.dev/masonry": "^1.3.5",
"@xterm/xterm": "^5.5.0",
"@zumer/snapdom": "^1.9.14",
@ -203,7 +203,7 @@
"antd": "^5.29.1",
"antd-style": "^3.7.1",
"async-retry": "^1.3.3",
"better-auth": "^1.4.3",
"better-auth": "^1.4.6",
"better-auth-harmony": "^1.2.5",
"brotli-wasm": "^3.0.1",
"chroma-js": "^3.2.0",
@ -220,10 +220,10 @@
"fast-deep-equal": "^3.1.3",
"fflate": "^0.8.2",
"file-type": "^21.1.1",
"framer-motion": "^12.23.24",
"framer-motion": "^12.23.26",
"gray-matter": "^4.0.3",
"html-to-text": "^9.0.5",
"i18next": "^25.6.3",
"i18next": "^25.7.2",
"i18next-browser-languagedetector": "^8.2.0",
"i18next-resources-to-backend": "^1.2.1",
"immer": "^10.2.0",
@ -231,7 +231,7 @@
"jose": "^5.10.0",
"js-sha256": "^0.11.1",
"jsonl-parse-stringify": "^1.0.3",
"klavis": "^2.13.2",
"klavis": "^2.15.0",
"langchain": "^0.3.36",
"langfuse": "^3.38.6",
"langfuse-core": "^3.38.6",
@ -243,16 +243,16 @@
"mdast-util-to-markdown": "^2.1.2",
"model-bank": "workspace:*",
"nanoid": "^5.1.6",
"next": "^16.0.7",
"next": "^16.0.8",
"next-auth": "5.0.0-beta.30",
"next-mdx-remote": "^5.0.0",
"nextjs-toploader": "^3.9.17",
"node-machine-id": "^1.1.12",
"nodemailer": "^7.0.11",
"numeral": "^2.0.6",
"nuqs": "^2.8.1",
"nuqs": "^2.8.5",
"officeparser": "5.1.1",
"oidc-provider": "^9.5.2",
"oidc-provider": "^9.6.0",
"ollama": "^0.6.3",
"openai": "^4.104.0",
"openapi-fetch": "^0.14.1",
@ -283,23 +283,23 @@
"react-pdf": "^9.2.1",
"react-responsive": "^10.0.1",
"react-rnd": "^10.5.2",
"react-router-dom": "^7.9.6",
"react-router-dom": "^7.10.1",
"react-scan": "^0.4.3",
"react-virtuoso": "^4.15.0",
"react-virtuoso": "^4.17.0",
"react-wrap-balancer": "^1.1.1",
"remark": "^15.0.1",
"remark-gfm": "^4.0.1",
"remark-html": "^16.0.1",
"resend": "^6.5.2",
"resend": "^6.6.0",
"resolve-accept-language": "^3.1.15",
"rtl-detect": "^1.1.2",
"semver": "^7.7.3",
"sharp": "^0.34.5",
"shiki": "^3.17.0",
"shiki": "^3.19.0",
"ssrf-safe-fetch": "workspace:*",
"stripe": "^17.7.0",
"superjson": "^2.2.6",
"svix": "^1.81.0",
"svix": "^1.82.0",
"swr": "^2.3.7",
"systemjs": "^6.15.1",
"tokenx": "^1.2.1",
@ -309,7 +309,7 @@
"url-join": "^5.0.0",
"use-merge-value": "^1.2.0",
"uuid": "^13.0.0",
"virtua": "^0.47.0",
"virtua": "^0.47.2",
"word-extractor": "^1.0.4",
"ws": "^8.18.3",
"yaml": "^2.8.2",
@ -320,14 +320,14 @@
"devDependencies": {
"@commitlint/cli": "^19.8.1",
"@edge-runtime/vm": "^5.0.0",
"@huggingface/tasks": "^0.19.63",
"@huggingface/tasks": "^0.19.69",
"@lobechat/types": "workspace:*",
"@lobehub/i18n-cli": "^1.25.1",
"@lobehub/lint": "^1.26.3",
"@lobehub/market-types": "^1.11.4",
"@lobehub/market-types": "^1.11.5",
"@lobehub/seo-cli": "^1.7.0",
"@next/bundle-analyzer": "^16.0.5",
"@next/eslint-plugin-next": "^15.5.6",
"@next/bundle-analyzer": "^16.0.8",
"@next/eslint-plugin-next": "^15.5.7",
"@peculiar/webcrypto": "^1.5.0",
"@playwright/test": "^1.57.0",
"@prettier/sync": "^0.6.1",
@ -344,12 +344,12 @@
"@types/json-schema": "^7.0.15",
"@types/lodash": "^4.17.21",
"@types/lodash-es": "^4.17.12",
"@types/node": "^24.10.1",
"@types/node": "^24.10.3",
"@types/nodemailer": "^7.0.4",
"@types/numeral": "^2.0.5",
"@types/oidc-provider": "^9.5.0",
"@types/pdfkit": "^0.17.4",
"@types/pg": "^8.15.6",
"@types/pg": "^8.16.0",
"@types/react": "19.2.2",
"@types/react-dom": "19.2.2",
"@types/rtl-detect": "^1.0.3",
@ -358,7 +358,7 @@
"@types/ua-parser-js": "^0.7.39",
"@types/unist": "^3.0.3",
"@types/ws": "^8.18.1",
"@typescript/native-preview": "7.0.0-dev.20251102.1",
"@typescript/native-preview": "7.0.0-dev.20251210.1",
"@vitest/coverage-v8": "^3.2.4",
"ajv-keywords": "^5.1.0",
"code-inspector-plugin": "1.3.0",
@ -371,7 +371,7 @@
"dotenv-expand": "^12.0.3",
"dpdm-fast": "^1.0.14",
"drizzle-dbml-generator": "^0.10.0",
"drizzle-kit": "^0.31.6",
"drizzle-kit": "^0.31.8",
"eslint": "^8.57.1",
"eslint-plugin-mdx": "^3.6.2",
"fake-indexeddb": "^6.2.5",
@ -390,7 +390,7 @@
"node-gyp": "^11.5.0",
"openapi-typescript": "^7.10.1",
"p-map": "^7.0.4",
"prettier": "^3.7.3",
"prettier": "^3.7.4",
"remark-cli": "^12.0.1",
"remark-frontmatter": "^5.0.0",
"remark-mdx": "^3.1.1",
@ -399,12 +399,12 @@
"semantic-release": "^21.1.2",
"serwist": "^9.2.3",
"stylelint": "^15.11.0",
"tsx": "^4.20.6",
"type-fest": "^5.2.0",
"tsx": "^4.21.0",
"type-fest": "^5.3.1",
"typescript": "^5.9.3",
"unified": "^11.0.5",
"unist-util-visit": "^5.0.0",
"vite": "^7.2.4",
"vite": "^7.2.7",
"vitest": "^3.2.4"
},
"packageManager": "pnpm@10.20.0",

View file

@ -0,0 +1 @@
ALTER TABLE "users" ADD COLUMN IF NOT EXISTS "last_active_at" timestamp with time zone DEFAULT now() NOT NULL;

File diff suppressed because it is too large Load diff

View file

@ -420,7 +420,14 @@
"when": 1764858574403,
"tag": "0059_add_normalized_email_indexes",
"breakpoints": true
},
{
"idx": 60,
"version": "7",
"when": 1765437218969,
"tag": "0060_add_user_last_active_at",
"breakpoints": true
}
],
"version": "6"
}
}

View file

@ -938,5 +938,13 @@
"bps": true,
"folderMillis": 1764858574403,
"hash": "7838f9938b370867470e5e11807855253d23b11c2ac6aa9e90687844a356c949"
},
{
"sql": [
"ALTER TABLE \"users\" ADD COLUMN IF NOT EXISTS \"last_active_at\" timestamp with time zone DEFAULT now() NOT NULL;"
],
"bps": true,
"folderMillis": 1765437218969,
"hash": "004923916a27bc61294eeb20738f2884f1ca43978a03aa9f0216fc849c98465b"
}
]
]

View file

@ -42,6 +42,7 @@ export const users = pgTable(
// better-auth phone number
phoneNumberVerified: boolean('phone_number_verified'),
lastActiveAt: timestamptz('last_active_at').notNull().defaultNow(),
...timestamps,
},

View file

@ -51,6 +51,7 @@
"./ppio": "./src/aiModels/ppio.ts",
"./qiniu": "./src/aiModels/qiniu.ts",
"./qwen": "./src/aiModels/qwen.ts",
"./replicate": "./src/aiModels/replicate.ts",
"./sambanova": "./src/aiModels/sambanova.ts",
"./search1api": "./src/aiModels/search1api.ts",
"./sensenova": "./src/aiModels/sensenova.ts",

View file

@ -390,7 +390,7 @@ bunx vitest run --silent='passed-only' 'src/providers/{provider}/index.test.ts'
```bash
# Check TypeScript types (from project root)
cd ../../../ && bun run type-check
cd ../../../ && bun run typecheck
# Or run typecheck for model-runtime only
bunx tsc --noEmit
@ -475,7 +475,7 @@ Based on your development summary, update the following sections:
bunx vitest run --silent='passed-only' 'src/providers/{provider}/index.test.ts'
# Verify type check still passes
cd ../../../ && bun run type-check
cd ../../../ && bun run typecheck
```
#### Complete Workflow Example
@ -486,7 +486,7 @@ cd ../../../ && bun run type-check
bunx vitest run --silent='passed-only' 'src/providers/example/index.test.ts'
# 2. Type/Lint Phase (REQUIRED)
cd ../../../ && bun run type-check # Must pass!
cd ../../../ && bun run typecheck # Must pass!
bunx eslint src/providers/example/ --fix
# 3. Coverage Phase
@ -501,7 +501,7 @@ bunx vitest run --coverage --silent='passed-only'
# 6. Final Verification
bunx vitest run --silent='passed-only' 'src/providers/example/index.test.ts'
cd ../../../ && bun run type-check
cd ../../../ && bun run typecheck
# 7. Commit
git add .
@ -539,7 +539,7 @@ bunx vitest watch 'src/providers/{provider}/index.test.ts'
```bash
# Type check entire project (from project root)
cd ../../../ && bun run type-check
cd ../../../ && bun run typecheck
# Type check model-runtime only
bunx tsc --noEmit

View file

@ -144,6 +144,7 @@ export const MODEL_OWNER_DETECTION_CONFIG = {
moonshot: ['moonshot', 'kimi'],
openai: ['o1', 'o3', 'o4', 'gpt-'],
qwen: ['qwen', 'qwq', 'qvq'],
replicate: [],
v0: ['v0'],
volcengine: ['doubao'],
wenxin: ['ernie', 'qianfan'],

View file

@ -98,6 +98,7 @@ describe('userRouter', () => {
() =>
({
getUserState: vi.fn().mockResolvedValue(mockState),
updateUser: vi.fn().mockResolvedValue({ rowCount: 1 }),
}) as any,
);
@ -163,6 +164,7 @@ describe('userRouter', () => {
preference: { telemetry: null },
settings: {},
}),
updateUser: vi.fn().mockResolvedValue({ rowCount: 1 }),
}) as any,
);

View file

@ -4,6 +4,7 @@ import { serialize } from 'cookie';
import debug from 'debug';
import { z } from 'zod';
import { ToolCallContent } from '@/libs/mcp';
import { authedProcedure, publicProcedure, router } from '@/libs/trpc/lambda';
import { serverDatabase } from '@/libs/trpc/lambda/middleware';
import { DiscoverService } from '@/server/services/discover';
@ -80,13 +81,15 @@ export const marketRouter = router({
toolName: input.toolName,
userAccessToken,
});
const cloudResultContent = (cloudResult?.content ?? []) as ToolCallContent[];
// Format the cloud result to MCPToolCallResult format
// Process content blocks (upload images, etc.)
const newContent =
cloudResult?.isError || !ctx.fileService
? cloudResult?.content
: await processContentBlocks(cloudResult?.content, ctx.fileService);
? cloudResultContent
: // FIXME: the type assertion here is a temporary solution, need to remove it after refactoring
await processContentBlocks(cloudResultContent, ctx.fileService);
// Convert content blocks to string
const content = contentBlocksToString(newContent);

View file

@ -55,6 +55,11 @@ export const userRouter = router({
}),
getUserState: userProcedure.query(async ({ ctx }): Promise<UserInitializationState> => {
// don't block following process
ctx.userModel.updateUser({ lastActiveAt: new Date() }).catch((err) => {
console.error('update lastActiveAt failed, error:', err);
});
let state: Awaited<ReturnType<UserModel['getUserState']>> | undefined;
// get or create first-time user

View file

@ -102,6 +102,7 @@ class MCPService {
// Call cloud gateway via lambda market endpoint
// Server will automatically get user access token from database
// and format the result to MCPToolCallResult
// @ts-ignore tsgo 误报错误
result = await lambdaClient.market.callCloudMcpEndpoint.mutate({
apiParams,
identifier,

View file

@ -56,7 +56,7 @@ Key principles:
**Subagent workflow**:
1. **One subagent per action file** - Each subagent focuses on testing ONE action file completely
2. **Independent verification** - Each subagent runs its own type-check, lint, and test verification
2. **Independent verification** - Each subagent runs its own typecheck, lint, and test verification
3. **No commits from subagents** - Only the parent agent creates the final commit after all subagents complete
4. **Parallel execution** - Launch all subagents in a single message using multiple Task tool calls
5. **Consolidate results** - Parent agent reviews all results, runs final verification, updates docs, and commits
@ -129,12 +129,12 @@ For files with multiple action files to test, use the Task tool to create subage
2. **Launch one subagent per action file** using the Task tool
3. **Each subagent independently**:
- Writes tests for ONE action file only
- Runs type-check and lint
- Runs typecheck and lint
- Verifies tests pass
- Reports results back
- **DOES NOT commit** (parent agent handles commits)
4. **After all subagents complete**, review all results
5. **Run final verification** (type-check, lint, tests)
5. **Run final verification** (typecheck, lint, tests)
6. **Update test-coverage.md** with combined results
7. **Create single commit** with all new tests
@ -146,7 +146,7 @@ Write comprehensive tests for src/store/discover/slices/plugin/action.ts followi
Requirements:
1. Write tests covering all actions in the file
2. Follow SWR hooks testing pattern (if applicable)
3. Run type-check and lint to verify
3. Run typecheck and lint to verify
4. Run tests to ensure they pass
5. Report back with:
- Number of tests written
@ -163,7 +163,7 @@ DO NOT:
- ✅ Parallel execution - multiple action files tested simultaneously
- ✅ Focused scope - each subagent handles one file completely
- ✅ Independent verification - each file gets type-check/lint/test verification
- ✅ Independent verification - each file gets typecheck/lint/test verification
- ✅ Clean commits - single commit after all work is done
- ✅ Better organization - clear separation of concerns
@ -196,7 +196,7 @@ bunx vitest run --silent='passed-only' 'src/store/[domain]/slices/[slice]/action
```bash
# Check TypeScript types (from project root)
bun run type-check
bun run typecheck
# Fix any linting issues
bunx eslint src/store/[domain]/ --fix
@ -270,7 +270,7 @@ Based on your development summary, update the following sections:
bunx vitest run 'src/store'
# Verify type check still passes
bun run type-check
bun run typecheck
```
### Complete Workflow Example (Single File)
@ -281,7 +281,7 @@ bun run type-check
bunx vitest run --silent='passed-only' 'src/store/tool/slices/mcpStore/action.test.ts'
# 2. Type/Lint Phase (REQUIRED)
bun run type-check # Must pass!
bun run typecheck # Must pass!
bunx eslint src/store/tool/ --fix
# 3. Coverage Phase
@ -295,7 +295,7 @@ bunx vitest run --coverage 'src/store'
# 6. Final Verification
bunx vitest run 'src/store'
bun run type-check
bun run typecheck
# 7. Commit
git add .
@ -320,7 +320,7 @@ Task({
Requirements:
1. Write tests covering all actions (usePluginCategories, usePluginDetail, usePluginList, usePluginIdentifiers)
2. Follow SWR hooks testing pattern
3. Run type-check and lint to verify
3. Run typecheck and lint to verify
4. Run tests to ensure they pass
5. Report back with number of tests written and coverage areas
@ -335,7 +335,7 @@ Task({
Requirements:
1. Write tests covering all actions (useFetchMcpDetail, useFetchMcpList, useMcpCategories)
2. Follow SWR hooks testing pattern
3. Run type-check and lint to verify
3. Run typecheck and lint to verify
4. Run tests to ensure they pass
5. Report back with number of tests written and coverage areas
@ -350,7 +350,7 @@ DO NOT commit changes or update test-coverage.md.`,
Each subagent will:
- Write tests
- Run type-check and lint
- Run typecheck and lint
- Verify tests pass
- Report results
@ -365,8 +365,8 @@ After all subagents complete:
**Step 4: Final Verification**
```bash
# Run type-check on entire project
bun run type-check
# Run typecheck on entire project
bun run typecheck
# Run lint on all new test files
bunx eslint src/store/discover/ --fix
@ -396,7 +396,7 @@ git commit -m "✅ test(store): add comprehensive tests for discover store
- Add tests for plugin, mcp, assistant, model, provider slices
- Coverage: X% → Y% (+Z tests, 5 new test files)
- All tests pass type-check and lint
- All tests pass typecheck and lint
🤖 Generated with [Claude Code](https://claude.com/claude-code)
@ -444,7 +444,7 @@ bunx vitest watch 'src/store/[domain]/slices/[slice]/action.test.ts'
```bash
# Type check entire project (from project root)
bun run type-check
bun run typecheck
# Watch mode
bunx tsc --noEmit --watch
@ -494,7 +494,7 @@ bunx eslint src/store/[domain]/
- Semantic search and RAG integration testing
- File upload with progress callbacks
- **Development Method**: Used parallel subagents (9 subagents running simultaneously)
- **Type Safety**: All tests pass type-check ✅
- **Type Safety**: All tests pass typecheck ✅
- **Lint**: All tests pass lint ✅
- **Action Files Coverage**: 31/40 → 40/40 tested (100%, +9 files)
- **🎉 MILESTONE**: All 40 action files now have comprehensive test coverage!
@ -523,7 +523,7 @@ bunx eslint src/store/[domain]/
- AbortController management testing
- Mock return types matching actual services
- **Development Method**: Used parallel subagents (2 subagents, one per file)
- **Type Safety**: All tests pass type-check ✅
- **Type Safety**: All tests pass typecheck ✅
- **Lint**: All tests pass lint ✅
- **Action Files Coverage**: 31/40 tested (77.5%, +2 files)
- **Milestone**: 🏆 All high priority files (>200 LOC) now have comprehensive tests!
@ -544,7 +544,7 @@ bunx eslint src/store/[domain]/
- Successfully adapted zustand testing patterns for SWR hooks
- Mock strategy: Synchronously return data from mock useSWR
- Type safety: Used `as any` for test mock data where needed
- **Type Safety**: All tests pass type-check
- **Type Safety**: All tests pass typecheck
- **Action Files Coverage**: 29/40 tested (72.5%, +2 files)
**Session (2024-10-14)**: 📋 Store Testing Documentation Created