total-recall-brain 3.13.0 → 3.13.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +91 -108
- package/bin/antigravity.mjs +81 -52
- package/bin/total-recall.mjs +32 -57
- package/extension/background.js +1 -1
- package/extension/options/options.js +9 -3
- package/extension/sidepanel/sidepanel.js +0 -2
- package/frontend/index.html +4 -3
- package/frontend/package.json +1 -0
- package/frontend/src/App.tsx +109 -152
- package/frontend/src/api.ts +605 -23
- package/frontend/src/components/BrainSelector.tsx +211 -199
- package/frontend/src/components/DocumentEditorModal.tsx +57 -0
- package/frontend/src/components/DocumentTable.tsx +47 -0
- package/frontend/src/components/Graph3D.tsx +120 -71
- package/frontend/src/components/UsageChart.tsx +3 -3
- package/frontend/src/components/brand/BrandMark.tsx +95 -0
- package/frontend/src/index.css +286 -86
- package/frontend/src/pages/ApiKeysPage.tsx +1878 -215
- package/frontend/src/pages/ChatPage.tsx +103 -45
- package/frontend/src/pages/DesignDocsPage.tsx +5 -5
- package/frontend/src/pages/FilesPage.tsx +10 -6
- package/frontend/src/pages/GraphPage.tsx +5 -3
- package/frontend/src/pages/HelpPage.tsx +2 -2
- package/frontend/src/pages/InboxPage.tsx +82 -0
- package/frontend/src/pages/InstructionsPage.tsx +1 -1
- package/frontend/src/pages/IntegrationsPage.tsx +21 -10
- package/frontend/src/pages/LoginPage.tsx +95 -77
- package/frontend/src/pages/MemoryPage.tsx +31 -8
- package/frontend/src/pages/OnboardingPage.tsx +690 -0
- package/frontend/src/pages/OpenWikiPage.tsx +412 -323
- package/frontend/src/pages/SettingsPage.tsx +825 -922
- package/frontend/src/pages/SkillsPage.tsx +272 -147
- package/frontend/src/pages/UsagePage.tsx +23 -31
- package/frontend/src/pages/VaultPage.tsx +223 -0
- package/frontend/src/types.ts +13 -0
- package/package.json +4 -4
- package/scaffold/.agent/skills/total-recall/SKILL.md +43 -4
- package/scaffold/.agent/skills/total-recall/modules/README.md +10 -0
- package/scaffold/.agent/skills/total-recall/modules/agents/README.md +5 -0
- package/scaffold/.agent/skills/total-recall/{skills/tr-cli-agents → modules/agents}/agents.yml +8 -0
- package/scaffold/.agent/skills/total-recall/modules/skill-deploy/README.md +9 -0
- package/scaffold/.agent/skills/total-recall/openwiki/README.md +10 -0
- package/scaffold/.agent/skills/total-recall/openwiki/architecture.md +5 -0
- package/scaffold/.agent/skills/total-recall/openwiki/ide.md +10 -0
- package/scaffold/.agent/skills/total-recall/openwiki/memory.md +11 -0
- package/scaffold/.agent/skills/total-recall/openwiki/secrets.md +7 -0
- package/scaffold/.agent/skills/total-recall/openwiki/skills.md +7 -0
- package/src/cli/brain.mjs +63 -5
- package/src/cli/connect.mjs +11 -19
- package/src/cli/daemon.mjs +5 -4
- package/src/cli/deploy-ui.mjs +8 -3
- package/src/cli/deploy.mjs +84 -27
- package/src/cli/ingest/index.mjs +1 -1
- package/src/cli/init.mjs +63 -34
- package/src/cli/remember.mjs +3 -3
- package/src/cli/secret.mjs +812 -0
- package/src/cli/setup.mjs +34 -7
- package/src/cli/share.mjs +2 -2
- package/src/cli/skill.mjs +396 -35
- package/src/cli/sync.mjs +4 -4
- package/src/cli/task.mjs +287 -0
- package/src/core/clarity-rewriter.mjs +5 -5
- package/src/core/config.mjs +44 -2
- package/src/core/conflict-detector.mjs +5 -5
- package/src/core/crons.mjs +54 -0
- package/src/core/daemon-loop.mjs +87 -235
- package/src/core/dream.mjs +170 -10
- package/src/core/env-import.mjs +456 -0
- package/src/core/fact-seeker.mjs +14 -14
- package/src/core/import-rules.mjs +20 -7
- package/src/core/inference-engine.mjs +3 -3
- package/src/core/migrate.mjs +14 -8
- package/src/core/okf-adapter.mjs +2 -2
- package/src/core/operation-validator.mjs +39 -331
- package/src/core/optimizer.mjs +7 -0
- package/src/core/post-mortem.mjs +2 -2
- package/src/core/project-brain.mjs +349 -0
- package/src/core/provider-catalog.mjs +296 -0
- package/src/core/quick-capture.mjs +1 -1
- package/src/core/remote-vault-sync.mjs +194 -0
- package/src/core/research-queue.mjs +7 -11
- package/src/core/runtime.mjs +33 -9
- package/src/core/scheduler.mjs +59 -5
- package/src/core/schema.mjs +14 -0
- package/src/core/search.mjs +1 -1
- package/src/core/secrets-env-export.mjs +323 -0
- package/src/core/secrets-rotate.mjs +245 -0
- package/src/core/secrets-store.mjs +645 -0
- package/src/core/semantic-index.mjs +3 -3
- package/src/core/skills-registry.mjs +1062 -0
- package/src/core/source-adapters.mjs +4 -1
- package/src/core/ssss-host-extension.mjs +94 -0
- package/src/core/ssss-kernel-bridge.mjs +530 -0
- package/src/core/surface.mjs +14 -0
- package/src/core/task-envelope.mjs +298 -0
- package/src/core/task-executors.mjs +600 -0
- package/src/core/validated-write.mjs +124 -56
- package/src/core/vault.mjs +21 -34
- package/src/core/webauthn-store.mjs +272 -0
- package/src/server/api.mjs +26 -16
- package/src/server/auth.mjs +51 -0
- package/src/server/index.mjs +3 -3
- package/src/server/keys.mjs +30 -4
- package/src/server/rest.mjs +198 -25
- package/src/server/route-manifest.json +156 -0
- package/src/server/routes/_shared.mjs +40 -1
- package/src/server/routes/docs.mjs +260 -0
- package/src/server/routes/memory.mjs +145 -32
- package/src/server/routes/research.mjs +30 -0
- package/src/server/routes/secrets.mjs +470 -0
- package/src/server/routes/share.mjs +2 -2
- package/src/server/routes/skills.mjs +452 -54
- package/src/server/routes/sync.mjs +48 -0
- package/src/server/routes/webauthn.mjs +184 -0
- package/src/server/update-route-manifest.mjs +39 -0
- package/templates/onboarding-interview.md +32 -22
- package/templates/openwiki/README.md +10 -0
- package/templates/openwiki/architecture.md +5 -0
- package/templates/openwiki/ide.md +10 -0
- package/templates/openwiki/memory.md +11 -0
- package/templates/openwiki/secrets.md +7 -0
- package/templates/openwiki/skills.md +7 -0
- package/scaffold/.agent/skills/total-recall/skills/tr-cli-agents/SKILL.md +0 -121
- package/scaffold/.agent/skills/total-recall/skills/tr-cli-agents/references/antigravity.txt +0 -20
- package/scaffold/.agent/skills/total-recall/skills/tr-cli-agents/references/claude.txt +0 -58
- package/scaffold/.agent/skills/total-recall/skills/tr-cli-agents/references/codex.txt +0 -81
- package/scaffold/.agent/skills/total-recall/skills/tr-cli-agents/references/gemini.txt +0 -44
- package/scaffold/.agent/skills/total-recall/skills/tr-cli-agents/scripts/.gitkeep +0 -0
- package/scaffold/.agent/skills/total-recall/skills/tr-cli-agents/subagents/.gitkeep +0 -0
- package/scaffold/.agent/skills/total-recall/skills/tr-research/SKILL.md +0 -338
- package/scaffold/.agent/skills/total-recall/skills/tr-skill/SKILL.md +0 -282
- package/scaffold/.agent/skills/total-recall/skills/tr-skill/assets/skill-template/SKILL.md +0 -20
- package/scaffold/.agent/skills/total-recall/skills/tr-skill/evals/evals.json +0 -20
- package/scaffold/.agent/skills/total-recall/skills/tr-skill/hooks/on-skill-add.sh +0 -14
- package/scaffold/.agent/skills/total-recall/skills/tr-skill/hooks/on-skill-delete.sh +0 -14
- package/scaffold/.agent/skills/total-recall/skills/tr-skill/learnings/.gitkeep +0 -0
- package/scaffold/.agent/skills/total-recall/skills/tr-skill/references/.gitkeep +0 -0
- package/scaffold/.agent/skills/total-recall/skills/tr-skill/scripts/.gitkeep +0 -0
- package/scaffold/.agent/skills/total-recall/skills/tr-skill/scripts/create-skill.sh +0 -65
- package/scaffold/.agent/skills/total-recall/skills/tr-skill/scripts/enforce-skill-optimization.mjs +0 -135
- package/scaffold/.agent/skills/total-recall/skills/tr-skill/scripts/skill-watcher.mjs +0 -64
- package/scaffold/.agent/skills/total-recall/skills/tr-skill/subagents/audit-all-skills.md +0 -62
- package/scaffold/.agent/skills/total-recall/skills/tr-ssss/SKILL.md +0 -298
- package/scaffold/.agent/skills/total-recall/skills/tr-ssss/evals/evals.json +0 -17
- package/scaffold/.agent/skills/total-recall/skills/tr-ssss/references/CHANGELOG.md +0 -124
- package/scaffold/.agent/skills/total-recall/skills/tr-ssss/references/ssss-spec.md +0 -1325
- package/scaffold/.agent/skills/total-recall/skills/tr-ssss/scripts/validate-schema.mjs +0 -232
- package/scaffold/.agent/skills/total-recall/skills/tr-ssss/subagents/audit-ssss-node.md +0 -9
- /package/scaffold/.agent/skills/total-recall/{skills/tr-cli-agents/evals → modules/skill-deploy/scripts}/.gitkeep +0 -0
- /package/scaffold/.agent/skills/total-recall/{skills/tr-skill → modules/skill-deploy}/scripts/find-skills.mjs +0 -0
- /package/scaffold/.agent/skills/total-recall/{skills/tr-skill → modules/skill-deploy}/scripts/install-skill.mjs +0 -0
- /package/scaffold/.agent/skills/total-recall/{skills/tr-skill → modules/skill-deploy}/scripts/scan-skill.mjs +0 -0
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { useState, useEffect, useCallback } from 'react';
|
|
1
|
+
import { useState, useEffect, useCallback, useMemo } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
listSkills,
|
|
4
4
|
fetchSkill,
|
|
5
|
+
fetchSkillFiles,
|
|
5
6
|
saveSkill,
|
|
6
7
|
deleteSkill,
|
|
7
8
|
triggerRecompile,
|
|
@@ -14,11 +15,25 @@ import {
|
|
|
14
15
|
import type { ResearchItem } from '../types';
|
|
15
16
|
|
|
16
17
|
interface SkillItem {
|
|
18
|
+
id?: string;
|
|
17
19
|
name: string;
|
|
20
|
+
repo?: string;
|
|
21
|
+
project_path?: string;
|
|
22
|
+
source?: string;
|
|
18
23
|
size: number;
|
|
19
24
|
modified: string;
|
|
20
25
|
isDirectory: boolean;
|
|
21
26
|
subSkills: string[];
|
|
27
|
+
has_skill_md?: boolean;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
interface RepoSkillsGroup {
|
|
31
|
+
repo: string;
|
|
32
|
+
path: string | null;
|
|
33
|
+
skills: SkillItem[];
|
|
34
|
+
ok?: boolean;
|
|
35
|
+
count?: number;
|
|
36
|
+
error?: string;
|
|
22
37
|
}
|
|
23
38
|
|
|
24
39
|
interface RegistrySkill {
|
|
@@ -156,16 +171,14 @@ function generateSuggestions(skills: SkillItem[]): ImprovementSuggestion[] {
|
|
|
156
171
|
|
|
157
172
|
// ─── Helper: format file sizes ──────────────────────────────────────────────────
|
|
158
173
|
|
|
159
|
-
|
|
160
|
-
if (bytes < 1024) return `${bytes} B`;
|
|
161
|
-
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
162
|
-
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
163
|
-
}
|
|
174
|
+
|
|
164
175
|
|
|
165
176
|
export default function SkillsPage() {
|
|
166
177
|
const [skills, setSkills] = useState<SkillItem[]>([]);
|
|
178
|
+
const [repoGroups, setRepoGroups] = useState<RepoSkillsGroup[]>([]);
|
|
167
179
|
const [selectedSkill, setSelectedSkill] = useState<SkillItem | null>(null);
|
|
168
180
|
const [skillContent, setSkillContent] = useState('');
|
|
181
|
+
const [expandedRepos, setExpandedRepos] = useState<Set<string>>(new Set());
|
|
169
182
|
|
|
170
183
|
// Navigation tabs state
|
|
171
184
|
const [activeTab, setActiveTab] = useState<TabId>('active');
|
|
@@ -190,9 +203,9 @@ export default function SkillsPage() {
|
|
|
190
203
|
const [quarantineAlert, setQuarantineAlert] = useState<{ pkg: string; reason: string } | null>(null);
|
|
191
204
|
|
|
192
205
|
// ─── Lifecycle tab state ────────────────────────────────────────────────────
|
|
193
|
-
const
|
|
194
|
-
const
|
|
195
|
-
const
|
|
206
|
+
const lifecycleVersion = useMemo(() => selectedSkill && skillContent ? extractVersion(skillContent) : '0.0.0', [selectedSkill, skillContent]);
|
|
207
|
+
const changelogEntries = useMemo(() => selectedSkill && skillContent ? parseChangelog(skillContent) : [], [selectedSkill, skillContent]);
|
|
208
|
+
const healthChecks = useMemo(() => selectedSkill && skillContent ? computeHealthChecks(selectedSkill, skillContent) : [], [selectedSkill, skillContent]);
|
|
196
209
|
const [expandedSubDirs, setExpandedSubDirs] = useState<Record<string, boolean>>({});
|
|
197
210
|
const [subDirFiles, setSubDirFiles] = useState<Record<string, SubFileEntry[]>>({});
|
|
198
211
|
const [previewFile, setPreviewFile] = useState<{ name: string; content: string } | null>(null);
|
|
@@ -203,28 +216,65 @@ export default function SkillsPage() {
|
|
|
203
216
|
const [researchItems, setResearchItems] = useState<ResearchItem[]>([]);
|
|
204
217
|
const [researchLoading, setResearchLoading] = useState(false);
|
|
205
218
|
const [checkingUpdates, setCheckingUpdates] = useState(false);
|
|
206
|
-
const
|
|
219
|
+
const improvementSuggestions = useMemo(() => skills.length > 0 ? generateSuggestions(skills) : [], [skills]);
|
|
207
220
|
|
|
208
221
|
/**
|
|
209
|
-
* Syncs
|
|
222
|
+
* Syncs project skills by repo (excludes total-recall / tr-* system skills).
|
|
210
223
|
*/
|
|
211
|
-
const fetchSkillsList = async (selectName?: string) => {
|
|
224
|
+
const fetchSkillsList = async (selectName?: string, selectRepo?: string) => {
|
|
212
225
|
setLoading(true);
|
|
213
226
|
setError(null);
|
|
214
227
|
try {
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
228
|
+
const data = await listSkills();
|
|
229
|
+
let list: SkillItem[] = [];
|
|
230
|
+
let groups: RepoSkillsGroup[] = [];
|
|
231
|
+
|
|
232
|
+
if (Array.isArray(data)) {
|
|
233
|
+
list = data as SkillItem[];
|
|
234
|
+
const map = new Map<string, SkillItem[]>();
|
|
235
|
+
for (const s of list) {
|
|
236
|
+
const r = s.repo || 'unknown';
|
|
237
|
+
if (!map.has(r)) map.set(r, []);
|
|
238
|
+
map.get(r)!.push(s);
|
|
239
|
+
}
|
|
240
|
+
groups = [...map.entries()].map(([repo, skills]) => ({
|
|
241
|
+
repo,
|
|
242
|
+
path: skills[0]?.project_path || null,
|
|
243
|
+
skills,
|
|
244
|
+
count: skills.length,
|
|
245
|
+
ok: true,
|
|
246
|
+
}));
|
|
247
|
+
} else {
|
|
248
|
+
list = (data.skills || []) as SkillItem[];
|
|
249
|
+
groups = (data.repos || []) as RepoSkillsGroup[];
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
setSkills(list);
|
|
253
|
+
setRepoGroups(groups);
|
|
254
|
+
|
|
255
|
+
// Expand repos that have skills (first 5 by default to keep UI light)
|
|
256
|
+
const withSkills = groups.filter((g) => (g.skills?.length || 0) > 0).map((g) => g.repo);
|
|
257
|
+
setExpandedRepos(new Set(withSkills.slice(0, 6)));
|
|
258
|
+
|
|
259
|
+
if (list.length > 0) {
|
|
260
|
+
const target = selectName
|
|
261
|
+
? list.find(
|
|
262
|
+
(s) =>
|
|
263
|
+
s.name === selectName &&
|
|
264
|
+
(!selectRepo || s.repo === selectRepo),
|
|
265
|
+
)
|
|
266
|
+
: list[0];
|
|
220
267
|
if (target) {
|
|
221
268
|
setSelectedSkill(target);
|
|
222
|
-
const skillDetails = await fetchSkill(target.name);
|
|
269
|
+
const skillDetails = await fetchSkill(target.name, target.repo);
|
|
223
270
|
setSkillContent(skillDetails.content || '');
|
|
224
271
|
}
|
|
272
|
+
} else {
|
|
273
|
+
setSelectedSkill(null);
|
|
274
|
+
setSkillContent('');
|
|
225
275
|
}
|
|
226
276
|
} catch (err: unknown) {
|
|
227
|
-
setError((err as Error).message || 'Failed to fetch
|
|
277
|
+
setError((err as Error).message || 'Failed to fetch skills by repo.');
|
|
228
278
|
} finally {
|
|
229
279
|
setLoading(false);
|
|
230
280
|
}
|
|
@@ -235,21 +285,8 @@ export default function SkillsPage() {
|
|
|
235
285
|
void fetchSkillsList();
|
|
236
286
|
}, []);
|
|
237
287
|
|
|
238
|
-
// ─── Lifecycle: recompute when selectedSkill or content changes ─────────────
|
|
239
|
-
useEffect(() => {
|
|
240
|
-
if (selectedSkill && skillContent) {
|
|
241
|
-
setLifecycleVersion(extractVersion(skillContent));
|
|
242
|
-
setChangelogEntries(parseChangelog(skillContent));
|
|
243
|
-
setHealthChecks(computeHealthChecks(selectedSkill, skillContent));
|
|
244
|
-
}
|
|
245
|
-
}, [selectedSkill, skillContent]);
|
|
246
288
|
|
|
247
|
-
|
|
248
|
-
useEffect(() => {
|
|
249
|
-
if (skills.length > 0) {
|
|
250
|
-
setImprovementSuggestions(generateSuggestions(skills));
|
|
251
|
-
}
|
|
252
|
-
}, [skills]);
|
|
289
|
+
|
|
253
290
|
|
|
254
291
|
const handleSelectSkill = async (skill: SkillItem) => {
|
|
255
292
|
setSelectedSkill(skill);
|
|
@@ -259,7 +296,7 @@ export default function SkillsPage() {
|
|
|
259
296
|
setPreviewFile(null);
|
|
260
297
|
setExpandedSubDirs({});
|
|
261
298
|
try {
|
|
262
|
-
const details = await fetchSkill(skill.name);
|
|
299
|
+
const details = await fetchSkill(skill.name, skill.repo);
|
|
263
300
|
setSkillContent(details.content || '');
|
|
264
301
|
} catch (err: unknown) {
|
|
265
302
|
setError((err as Error).message || `Failed to fetch rules for skill "${skill.name}".`);
|
|
@@ -274,8 +311,10 @@ export default function SkillsPage() {
|
|
|
274
311
|
setError(null);
|
|
275
312
|
setSuccess(null);
|
|
276
313
|
try {
|
|
277
|
-
await saveSkill(selectedSkill.name, skillContent);
|
|
278
|
-
setSuccess(
|
|
314
|
+
await saveSkill(selectedSkill.name, skillContent, selectedSkill.repo);
|
|
315
|
+
setSuccess(
|
|
316
|
+
`Skill "${selectedSkill.name}"${selectedSkill.repo ? ` (${selectedSkill.repo})` : ''} saved successfully!`,
|
|
317
|
+
);
|
|
279
318
|
setTimeout(() => setSuccess(null), 5000);
|
|
280
319
|
} catch (err: unknown) {
|
|
281
320
|
setError((err as Error).message || 'Failed to save skill changes.');
|
|
@@ -300,11 +339,16 @@ export default function SkillsPage() {
|
|
|
300
339
|
};
|
|
301
340
|
|
|
302
341
|
const handleDeleteSkill = async (skill: SkillItem) => {
|
|
303
|
-
if (
|
|
342
|
+
if (
|
|
343
|
+
!window.confirm(
|
|
344
|
+
`Delete skill "${skill.name}"${skill.repo ? ` from ${skill.repo}` : ''} permanently?`,
|
|
345
|
+
)
|
|
346
|
+
)
|
|
347
|
+
return;
|
|
304
348
|
setError(null);
|
|
305
349
|
setSuccess(null);
|
|
306
350
|
try {
|
|
307
|
-
await deleteSkill(skill.name);
|
|
351
|
+
await deleteSkill(skill.name, skill.repo);
|
|
308
352
|
setSuccess(`Skill "${skill.name}" successfully deleted.`);
|
|
309
353
|
setTimeout(() => setSuccess(null), 5000);
|
|
310
354
|
void fetchSkillsList();
|
|
@@ -318,9 +362,23 @@ export default function SkillsPage() {
|
|
|
318
362
|
if (!newSkillName.trim()) return;
|
|
319
363
|
setError(null);
|
|
320
364
|
setSuccess(null);
|
|
365
|
+
|
|
366
|
+
// Prefer selected skill's repo, else first repo with a path
|
|
367
|
+
const targetRepo =
|
|
368
|
+
selectedSkill?.repo ||
|
|
369
|
+
repoGroups.find((g) => g.ok && g.path)?.repo ||
|
|
370
|
+
'';
|
|
371
|
+
if (!targetRepo) {
|
|
372
|
+
setError('No project repo available to create a skill in. Register a project first.');
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
321
375
|
|
|
322
376
|
// Normalize naming: alphanumeric and hyphens
|
|
323
377
|
const cleanName = newSkillName.toLowerCase().replace(/[^a-z0-9-]/g, '-');
|
|
378
|
+
if (cleanName.startsWith('tr-') || cleanName === 'total-recall') {
|
|
379
|
+
setError('Cannot create Total Recall system skills here.');
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
324
382
|
|
|
325
383
|
const initialContent = `---
|
|
326
384
|
type: skill
|
|
@@ -334,7 +392,7 @@ Configure triggers, options, and prompts inside this rules sheet to hot-recompil
|
|
|
334
392
|
`;
|
|
335
393
|
|
|
336
394
|
try {
|
|
337
|
-
await saveSkill(cleanName, initialContent);
|
|
395
|
+
await saveSkill(cleanName, initialContent, targetRepo);
|
|
338
396
|
setNewSkillName('');
|
|
339
397
|
setNewSkillDesc('');
|
|
340
398
|
setShowCreateForm(false);
|
|
@@ -402,22 +460,16 @@ Configure triggers, options, and prompts inside this rules sheet to hot-recompil
|
|
|
402
460
|
|
|
403
461
|
// ─── Lifecycle tab handlers ─────────────────────────────────────────────────
|
|
404
462
|
|
|
405
|
-
const handleToggleSubDir = (dir: string) => {
|
|
463
|
+
const handleToggleSubDir = async (dir: string) => {
|
|
406
464
|
setExpandedSubDirs(prev => ({ ...prev, [dir]: !prev[dir] }));
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
syntheticFiles.push({ name: 'ARCHITECTURE.md', size: '8.5 KB' });
|
|
415
|
-
} else if (dir === 'evals') {
|
|
416
|
-
syntheticFiles.push({ name: 'evals.json', size: '1.3 KB' });
|
|
417
|
-
} else if (dir === 'subagents') {
|
|
418
|
-
syntheticFiles.push({ name: 'agent-config.json', size: '0.9 KB' });
|
|
465
|
+
if (!subDirFiles[dir] && selectedSkill) {
|
|
466
|
+
try {
|
|
467
|
+
const files = await fetchSkillFiles(selectedSkill.name, dir, selectedSkill.repo);
|
|
468
|
+
setSubDirFiles(prev => ({ ...prev, [dir]: files }));
|
|
469
|
+
} catch (err) {
|
|
470
|
+
console.error('Failed to fetch skill files', err);
|
|
471
|
+
setSubDirFiles(prev => ({ ...prev, [dir]: [] }));
|
|
419
472
|
}
|
|
420
|
-
setSubDirFiles(prev => ({ ...prev, [dir]: syntheticFiles }));
|
|
421
473
|
}
|
|
422
474
|
};
|
|
423
475
|
|
|
@@ -482,48 +534,170 @@ Configure triggers, options, and prompts inside this rules sheet to hot-recompil
|
|
|
482
534
|
// ─── Tab configuration ────────────────────────────────────────────────────────
|
|
483
535
|
|
|
484
536
|
const tabs: { id: TabId; label: string; icon: string }[] = [
|
|
485
|
-
{ id: 'active', label: '
|
|
537
|
+
{ id: 'active', label: 'Skills by repo', icon: '📂' },
|
|
486
538
|
{ id: 'registry', label: 'skills.sh Registry Hub', icon: '✨' },
|
|
487
539
|
{ id: 'lifecycle', label: 'Lifecycle & Versioning', icon: '📋' },
|
|
488
540
|
{ id: 'network', label: 'P2P Skills Network', icon: '🌐' },
|
|
489
541
|
{ id: 'automation', label: 'Auto-Improvement', icon: '⚡' },
|
|
490
542
|
];
|
|
491
543
|
|
|
544
|
+
const skillKey = (s: SkillItem) => s.id || `${s.repo || '_'}::${s.name}`;
|
|
545
|
+
const isSkillSelected = (s: SkillItem) =>
|
|
546
|
+
selectedSkill?.name === s.name && (selectedSkill?.repo || '') === (s.repo || '');
|
|
547
|
+
|
|
548
|
+
const toggleRepo = (repo: string) => {
|
|
549
|
+
setExpandedRepos((prev) => {
|
|
550
|
+
const next = new Set(prev);
|
|
551
|
+
if (next.has(repo)) next.delete(repo);
|
|
552
|
+
else next.add(repo);
|
|
553
|
+
return next;
|
|
554
|
+
});
|
|
555
|
+
};
|
|
556
|
+
|
|
557
|
+
const renderRepoSkillList = (prefix: string) => (
|
|
558
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 8, flex: 1, overflowY: 'auto' }}>
|
|
559
|
+
{repoGroups.length === 0 && (
|
|
560
|
+
<p style={{ fontSize: 12, color: 'var(--text-tertiary)', padding: 8 }}>
|
|
561
|
+
No project repos with skills found.
|
|
562
|
+
</p>
|
|
563
|
+
)}
|
|
564
|
+
{repoGroups.map((group) => {
|
|
565
|
+
const open = expandedRepos.has(group.repo);
|
|
566
|
+
const count = group.skills?.length || 0;
|
|
567
|
+
return (
|
|
568
|
+
<div key={group.repo} style={{ borderBottom: '1px solid var(--border)' }}>
|
|
569
|
+
<button
|
|
570
|
+
type="button"
|
|
571
|
+
onClick={() => toggleRepo(group.repo)}
|
|
572
|
+
style={{
|
|
573
|
+
width: '100%',
|
|
574
|
+
display: 'flex',
|
|
575
|
+
justifyContent: 'space-between',
|
|
576
|
+
alignItems: 'center',
|
|
577
|
+
gap: 8,
|
|
578
|
+
padding: '10px 8px',
|
|
579
|
+
background: 'transparent',
|
|
580
|
+
border: 'none',
|
|
581
|
+
color: 'inherit',
|
|
582
|
+
cursor: 'pointer',
|
|
583
|
+
textAlign: 'left',
|
|
584
|
+
}}
|
|
585
|
+
>
|
|
586
|
+
<span style={{ fontSize: 12, fontWeight: 700 }}>
|
|
587
|
+
{open ? '▾' : '▸'} {group.repo}
|
|
588
|
+
</span>
|
|
589
|
+
<span
|
|
590
|
+
style={{
|
|
591
|
+
fontSize: 10,
|
|
592
|
+
fontWeight: 600,
|
|
593
|
+
padding: '2px 8px',
|
|
594
|
+
borderRadius: 99,
|
|
595
|
+
background: count ? 'rgba(59,130,246,0.15)' : 'rgba(148,163,184,0.1)',
|
|
596
|
+
color: count ? '#93c5fd' : 'var(--text-tertiary)',
|
|
597
|
+
}}
|
|
598
|
+
>
|
|
599
|
+
{count}
|
|
600
|
+
</span>
|
|
601
|
+
</button>
|
|
602
|
+
{open && (
|
|
603
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 6, padding: '0 4px 10px' }}>
|
|
604
|
+
{count === 0 ? (
|
|
605
|
+
<div style={{ fontSize: 11, color: 'var(--text-tertiary)', padding: '4px 8px' }}>
|
|
606
|
+
{group.error || 'No app skills (TR skills hidden)'}
|
|
607
|
+
</div>
|
|
608
|
+
) : (
|
|
609
|
+
group.skills.map((skill) => {
|
|
610
|
+
const active = isSkillSelected(skill);
|
|
611
|
+
return (
|
|
612
|
+
<div
|
|
613
|
+
key={skillKey(skill)}
|
|
614
|
+
id={`${prefix}-skill-${skill.repo}-${skill.name}`}
|
|
615
|
+
onClick={() => void handleSelectSkill(skill)}
|
|
616
|
+
style={{
|
|
617
|
+
background: active ? 'var(--accent-muted)' : 'var(--bg-tertiary)',
|
|
618
|
+
border: active ? '1px solid var(--accent)' : '1px solid var(--border)',
|
|
619
|
+
padding: '10px 12px',
|
|
620
|
+
borderRadius: 8,
|
|
621
|
+
cursor: 'pointer',
|
|
622
|
+
transition: 'all 0.15s ease',
|
|
623
|
+
}}
|
|
624
|
+
>
|
|
625
|
+
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
|
626
|
+
<span
|
|
627
|
+
style={{
|
|
628
|
+
fontSize: 13,
|
|
629
|
+
fontWeight: active ? 600 : 500,
|
|
630
|
+
color: active ? 'var(--accent)' : 'var(--text-primary)',
|
|
631
|
+
}}
|
|
632
|
+
>
|
|
633
|
+
{skill.name}
|
|
634
|
+
</span>
|
|
635
|
+
<button
|
|
636
|
+
type="button"
|
|
637
|
+
onClick={(e) => {
|
|
638
|
+
e.stopPropagation();
|
|
639
|
+
void handleDeleteSkill(skill);
|
|
640
|
+
}}
|
|
641
|
+
style={{
|
|
642
|
+
background: 'none',
|
|
643
|
+
border: 'none',
|
|
644
|
+
color: 'var(--text-tertiary)',
|
|
645
|
+
fontSize: 12,
|
|
646
|
+
cursor: 'pointer',
|
|
647
|
+
padding: '2px 6px',
|
|
648
|
+
}}
|
|
649
|
+
title="Delete skill"
|
|
650
|
+
>
|
|
651
|
+
✖
|
|
652
|
+
</button>
|
|
653
|
+
</div>
|
|
654
|
+
{skill.source && (
|
|
655
|
+
<div style={{ fontSize: 10, color: 'var(--text-tertiary)', marginTop: 4 }}>
|
|
656
|
+
{skill.source}
|
|
657
|
+
</div>
|
|
658
|
+
)}
|
|
659
|
+
{skill.subSkills && skill.subSkills.length > 0 && (
|
|
660
|
+
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 4, marginTop: 6 }}>
|
|
661
|
+
{skill.subSkills.slice(0, 6).map((sd) => (
|
|
662
|
+
<span
|
|
663
|
+
key={sd}
|
|
664
|
+
className="badge"
|
|
665
|
+
style={{
|
|
666
|
+
fontSize: 9,
|
|
667
|
+
padding: '1px 5px',
|
|
668
|
+
background: 'var(--bg-elevated)',
|
|
669
|
+
color: 'var(--text-secondary)',
|
|
670
|
+
border: '1px solid var(--border)',
|
|
671
|
+
}}
|
|
672
|
+
>
|
|
673
|
+
{sd}
|
|
674
|
+
</span>
|
|
675
|
+
))}
|
|
676
|
+
</div>
|
|
677
|
+
)}
|
|
678
|
+
</div>
|
|
679
|
+
);
|
|
680
|
+
})
|
|
681
|
+
)}
|
|
682
|
+
</div>
|
|
683
|
+
)}
|
|
684
|
+
</div>
|
|
685
|
+
);
|
|
686
|
+
})}
|
|
687
|
+
</div>
|
|
688
|
+
);
|
|
689
|
+
|
|
492
690
|
// ─── Render helpers ─────────────────────────────────────────────────────────
|
|
493
691
|
|
|
494
692
|
const renderLifecycleTab = () => (
|
|
495
693
|
<div style={{ display: 'flex', flex: 1, gap: 24, minHeight: 0, overflow: 'hidden', paddingBottom: 16 }}>
|
|
496
|
-
{/* Left: Skill selector
|
|
497
|
-
<div style={{ width:
|
|
694
|
+
{/* Left: Skill selector by repo */}
|
|
695
|
+
<div style={{ width: 280, display: 'flex', flexDirection: 'column', gap: 16, flexShrink: 0, overflowY: 'auto' }}>
|
|
498
696
|
<div className="card" style={{ padding: 16, background: 'rgba(18, 18, 26, 0.5)', border: '1px solid var(--border)', flex: 1, display: 'flex', flexDirection: 'column' }}>
|
|
499
697
|
<h3 style={{ fontSize: 12, fontWeight: 700, color: 'var(--text-tertiary)', textTransform: 'uppercase', letterSpacing: 0.5, borderBottom: '1px solid var(--border)', paddingBottom: 8, marginBottom: 12 }}>
|
|
500
|
-
|
|
698
|
+
Skills by repo
|
|
501
699
|
</h3>
|
|
502
|
-
|
|
503
|
-
{skills.map(skill => {
|
|
504
|
-
const isActive = selectedSkill?.name === skill.name;
|
|
505
|
-
return (
|
|
506
|
-
<div
|
|
507
|
-
key={skill.name}
|
|
508
|
-
id={`lifecycle-skill-${skill.name}`}
|
|
509
|
-
onClick={() => void handleSelectSkill(skill)}
|
|
510
|
-
style={{
|
|
511
|
-
background: isActive ? 'var(--accent-muted)' : 'var(--bg-tertiary)',
|
|
512
|
-
border: isActive ? '1px solid var(--accent)' : '1px solid var(--border)',
|
|
513
|
-
padding: '10px 12px',
|
|
514
|
-
borderRadius: 8,
|
|
515
|
-
cursor: 'pointer',
|
|
516
|
-
transition: 'all 0.15s ease',
|
|
517
|
-
fontSize: 13,
|
|
518
|
-
fontWeight: isActive ? 600 : 400,
|
|
519
|
-
color: isActive ? 'var(--accent)' : 'var(--text-primary)',
|
|
520
|
-
}}
|
|
521
|
-
>
|
|
522
|
-
{skill.name}
|
|
523
|
-
</div>
|
|
524
|
-
);
|
|
525
|
-
})}
|
|
526
|
-
</div>
|
|
700
|
+
{renderRepoSkillList('lifecycle')}
|
|
527
701
|
</div>
|
|
528
702
|
</div>
|
|
529
703
|
|
|
@@ -536,6 +710,11 @@ Configure triggers, options, and prompts inside this rules sheet to hot-recompil
|
|
|
536
710
|
<div style={{ display: 'flex', alignItems: 'center', gap: 16, flexWrap: 'wrap' }}>
|
|
537
711
|
<h3 id="lifecycle-version-title" style={{ fontSize: 16, fontWeight: 600, color: 'var(--text-primary)', margin: 0 }}>
|
|
538
712
|
{selectedSkill.name}
|
|
713
|
+
{selectedSkill.repo ? (
|
|
714
|
+
<span style={{ fontSize: 12, fontWeight: 500, color: 'var(--text-tertiary)', marginLeft: 8 }}>
|
|
715
|
+
· {selectedSkill.repo}
|
|
716
|
+
</span>
|
|
717
|
+
) : null}
|
|
539
718
|
</h3>
|
|
540
719
|
<span
|
|
541
720
|
id="lifecycle-version-badge"
|
|
@@ -1254,8 +1433,8 @@ Configure triggers, options, and prompts inside this rules sheet to hot-recompil
|
|
|
1254
1433
|
<div className="page" style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
|
|
1255
1434
|
<div className="page-header" style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: 16 }}>
|
|
1256
1435
|
<div>
|
|
1257
|
-
<h1>
|
|
1258
|
-
<p>
|
|
1436
|
+
<h1>Skills Manager</h1>
|
|
1437
|
+
<p>Track, edit, and deploy agent skills across any repo — registry-based, no hardcoded hosts</p>
|
|
1259
1438
|
</div>
|
|
1260
1439
|
|
|
1261
1440
|
<div style={{ display: 'flex', gap: 10 }}>
|
|
@@ -1304,7 +1483,7 @@ Configure triggers, options, and prompts inside this rules sheet to hot-recompil
|
|
|
1304
1483
|
{/* Creation Modal Form Overlay */}
|
|
1305
1484
|
{showCreateForm && (
|
|
1306
1485
|
<div className="card" style={{ marginBottom: 24, padding: 24, background: 'rgba(18, 18, 26, 0.9)', border: '1px solid var(--border)', backdropFilter: 'blur(10px)' }}>
|
|
1307
|
-
<h3 style={{ fontSize: 14, fontWeight: 600, borderBottom: '1px solid var(--border)', paddingBottom: 10, marginBottom: 14 }}>➕ Create New
|
|
1486
|
+
<h3 style={{ fontSize: 14, fontWeight: 600, borderBottom: '1px solid var(--border)', paddingBottom: 10, marginBottom: 14 }}>➕ Create New Skill</h3>
|
|
1308
1487
|
<form onSubmit={handleCreateSkill} style={{ display: 'flex', gap: 16, flexWrap: 'wrap', alignItems: 'flex-end' }}>
|
|
1309
1488
|
<div style={{ flex: '1 1 200px', display: 'flex', flexDirection: 'column', gap: 6 }}>
|
|
1310
1489
|
<label htmlFor="new_name" style={{ fontSize: 11, fontWeight: 500, color: 'var(--text-secondary)' }}>Skill Name (kebab-case)</label>
|
|
@@ -1368,70 +1547,16 @@ Configure triggers, options, and prompts inside this rules sheet to hot-recompil
|
|
|
1368
1547
|
/* Original split layout: Active skills editor */
|
|
1369
1548
|
<div style={{ display: 'flex', flex: 1, gap: 24, minHeight: 0, overflow: 'hidden', paddingBottom: 16 }}>
|
|
1370
1549
|
|
|
1371
|
-
{/* Left column: Skills
|
|
1372
|
-
<div style={{ width:
|
|
1550
|
+
{/* Left column: Skills by repo */}
|
|
1551
|
+
<div style={{ width: 300, display: 'flex', flexDirection: 'column', gap: 16, flexShrink: 0, overflowY: 'auto' }}>
|
|
1373
1552
|
<div className="card" style={{ padding: 16, background: 'rgba(18, 18, 26, 0.5)', border: '1px solid var(--border)', flex: 1, display: 'flex', flexDirection: 'column' }}>
|
|
1374
|
-
<h3 style={{ fontSize: 12, fontWeight: 700, color: 'var(--text-tertiary)', textTransform: 'uppercase', letterSpacing: 0.5, borderBottom: '1px solid var(--border)', paddingBottom: 8, marginBottom:
|
|
1375
|
-
|
|
1553
|
+
<h3 style={{ fontSize: 12, fontWeight: 700, color: 'var(--text-tertiary)', textTransform: 'uppercase', letterSpacing: 0.5, borderBottom: '1px solid var(--border)', paddingBottom: 8, marginBottom: 8 }}>
|
|
1554
|
+
Skills by repo
|
|
1376
1555
|
</h3>
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
return (
|
|
1382
|
-
<div
|
|
1383
|
-
key={skill.name}
|
|
1384
|
-
onClick={() => void handleSelectSkill(skill)}
|
|
1385
|
-
style={{
|
|
1386
|
-
background: isActive ? 'var(--accent-muted)' : 'var(--bg-tertiary)',
|
|
1387
|
-
border: isActive ? '1px solid var(--accent)' : '1px solid var(--border)',
|
|
1388
|
-
padding: '12px 14px',
|
|
1389
|
-
borderRadius: 8,
|
|
1390
|
-
cursor: 'pointer',
|
|
1391
|
-
transition: 'all 0.15s ease',
|
|
1392
|
-
position: 'relative'
|
|
1393
|
-
}}
|
|
1394
|
-
>
|
|
1395
|
-
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
|
1396
|
-
<span style={{ fontSize: 13, fontWeight: 600, color: isActive ? 'var(--accent)' : 'var(--text-primary)' }}>
|
|
1397
|
-
{skill.name}
|
|
1398
|
-
</span>
|
|
1399
|
-
{skill.name !== 'total-recall' && (
|
|
1400
|
-
<button
|
|
1401
|
-
onClick={(e) => {
|
|
1402
|
-
e.stopPropagation();
|
|
1403
|
-
void handleDeleteSkill(skill);
|
|
1404
|
-
}}
|
|
1405
|
-
style={{
|
|
1406
|
-
background: 'none',
|
|
1407
|
-
border: 'none',
|
|
1408
|
-
color: 'var(--text-tertiary)',
|
|
1409
|
-
fontSize: 12,
|
|
1410
|
-
cursor: 'pointer',
|
|
1411
|
-
padding: '2px 6px'
|
|
1412
|
-
}}
|
|
1413
|
-
onMouseEnter={(e) => (e.currentTarget.style.color = '#ff6b6b')}
|
|
1414
|
-
onMouseLeave={(e) => (e.currentTarget.style.color = 'var(--text-tertiary)')}
|
|
1415
|
-
>
|
|
1416
|
-
✖
|
|
1417
|
-
</button>
|
|
1418
|
-
)}
|
|
1419
|
-
</div>
|
|
1420
|
-
|
|
1421
|
-
{/* Sub Skills tags */}
|
|
1422
|
-
{skill.subSkills && skill.subSkills.length > 0 && (
|
|
1423
|
-
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 4, marginTop: 8 }}>
|
|
1424
|
-
{skill.subSkills.map(sd => (
|
|
1425
|
-
<span key={sd} className="badge" style={{ fontSize: 9, padding: '1px 5px', background: 'var(--bg-elevated)', color: 'var(--text-secondary)', border: '1px solid var(--border)' }}>
|
|
1426
|
-
🧩 {sd}
|
|
1427
|
-
</span>
|
|
1428
|
-
))}
|
|
1429
|
-
</div>
|
|
1430
|
-
)}
|
|
1431
|
-
</div>
|
|
1432
|
-
);
|
|
1433
|
-
})}
|
|
1434
|
-
</div>
|
|
1556
|
+
<p style={{ fontSize: 10, color: 'var(--text-tertiary)', margin: '0 0 10px', lineHeight: 1.4 }}>
|
|
1557
|
+
Project skills only · total-recall / tr-* hidden · {skills.length} skill{skills.length === 1 ? '' : 's'}
|
|
1558
|
+
</p>
|
|
1559
|
+
{renderRepoSkillList('active')}
|
|
1435
1560
|
</div>
|
|
1436
1561
|
</div>
|
|
1437
1562
|
|