(query: string, limit: number, owner?: string)
| 1912 | } |
| 1913 | |
| 1914 | taskFtsSearch(query: string, limit: number, owner?: string): Array<{ taskId: string; score: number }> { |
| 1915 | const sanitized = sanitizeFtsQuery(query); |
| 1916 | if (!sanitized) return []; |
| 1917 | try { |
| 1918 | let sql = ` |
| 1919 | SELECT t.id as task_id, rank |
| 1920 | FROM tasks_fts f |
| 1921 | JOIN tasks t ON t.rowid = f.rowid |
| 1922 | WHERE tasks_fts MATCH ?`; |
| 1923 | const params: any[] = [sanitized]; |
| 1924 | if (owner) { |
| 1925 | sql += ` AND (t.owner = ? OR t.owner = 'public')`; |
| 1926 | params.push(owner); |
| 1927 | } |
| 1928 | sql += ` ORDER BY rank LIMIT ?`; |
| 1929 | params.push(limit); |
| 1930 | const rows = this.db.prepare(sql).all(...params) as Array<{ task_id: string; rank: number }>; |
| 1931 | if (rows.length === 0) return []; |
| 1932 | const maxAbsRank = Math.max(...rows.map((r) => Math.abs(r.rank))); |
| 1933 | return rows.map((r) => ({ |
| 1934 | taskId: r.task_id, |
| 1935 | score: maxAbsRank > 0 ? Math.abs(r.rank) / maxAbsRank : 0, |
| 1936 | })); |
| 1937 | } catch { |
| 1938 | this.log.warn(`Task FTS query failed for: "${sanitized}", returning empty`); |
| 1939 | return []; |
| 1940 | } |
| 1941 | } |
| 1942 | |
| 1943 | listPublicSkills(): Skill[] { |
| 1944 | const rows = this.db.prepare("SELECT * FROM skills WHERE visibility = 'public' AND status = 'active' ORDER BY updated_at DESC").all() as SkillRow[]; |
no test coverage detected