total-recall-brain 3.9.0 → 3.11.0
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/bin/antigravity.mjs +62 -183
- package/package.json +6 -5
- package/scaffold/.agent/skills/total-recall/SKILL.md +1 -0
- package/scaffold/.agent/skills/total-recall/skills/{cli-agents → tr-cli-agents}/SKILL.md +3 -2
- package/scaffold/.agent/skills/total-recall/skills/{research → tr-research}/SKILL.md +2 -1
- package/scaffold/.agent/skills/total-recall/skills/{skill → tr-skill}/SKILL.md +3 -2
- package/scaffold/.agent/skills/total-recall/skills/{skill → tr-skill}/evals/evals.json +3 -3
- package/scaffold/.agent/skills/total-recall/skills/{skill → tr-skill}/hooks/on-skill-add.sh +1 -1
- package/scaffold/.agent/skills/total-recall/skills/{skill → tr-skill}/hooks/on-skill-delete.sh +1 -1
- package/scaffold/.agent/skills/total-recall/skills/{skill → tr-skill}/scripts/create-skill.sh +3 -3
- package/scaffold/.agent/skills/total-recall/skills/{skill → tr-skill}/scripts/find-skills.mjs +25 -1
- package/scaffold/.agent/skills/total-recall/skills/tr-skill/scripts/install-skill.mjs +117 -0
- package/scaffold/.agent/skills/total-recall/skills/{skill → tr-skill}/scripts/scan-skill.mjs +20 -0
- package/scaffold/.agent/skills/total-recall/skills/{ssss → tr-ssss}/SKILL.md +120 -19
- package/scaffold/.agent/skills/total-recall/skills/tr-ssss/references/CHANGELOG.md +124 -0
- package/scaffold/.agent/skills/total-recall/skills/{ssss → tr-ssss}/references/ssss-spec.md +474 -43
- package/src/cli/connect.mjs +6 -59
- package/src/cli/ingest-openwiki.mjs +122 -0
- package/src/cli/ingest.mjs +9 -0
- package/src/cli/init.mjs +42 -2
- package/src/cli/skill-projection.mjs +214 -0
- package/src/cli/skill.mjs +44 -18
- package/src/core/daemon-loop.mjs +13 -2
- package/src/core/operation-validator.mjs +85 -9
- package/src/core/research.mjs +47 -9
- package/src/core/runtime.mjs +14 -7
- package/src/core/schema.mjs +85 -13
- package/src/server/api.mjs +2 -2
- package/src/server/rest.mjs +3 -3
- package/src/server/routes/skills.mjs +25 -4
- package/scaffold/.agent/skills/total-recall/skills/ssss/references/CHANGELOG.md +0 -9
- /package/scaffold/.agent/skills/total-recall/skills/{cli-agents → tr-cli-agents}/agents.yml +0 -0
- /package/scaffold/.agent/skills/total-recall/skills/{cli-agents → tr-cli-agents}/evals/.gitkeep +0 -0
- /package/scaffold/.agent/skills/total-recall/skills/{cli-agents → tr-cli-agents}/references/antigravity.txt +0 -0
- /package/scaffold/.agent/skills/total-recall/skills/{cli-agents → tr-cli-agents}/references/claude.txt +0 -0
- /package/scaffold/.agent/skills/total-recall/skills/{cli-agents → tr-cli-agents}/references/codex.txt +0 -0
- /package/scaffold/.agent/skills/total-recall/skills/{cli-agents → tr-cli-agents}/references/gemini.txt +0 -0
- /package/scaffold/.agent/skills/total-recall/skills/{cli-agents → tr-cli-agents}/scripts/.gitkeep +0 -0
- /package/scaffold/.agent/skills/total-recall/skills/{cli-agents → tr-cli-agents}/subagents/.gitkeep +0 -0
- /package/scaffold/.agent/skills/total-recall/skills/{skill → tr-skill}/assets/skill-template/SKILL.md +0 -0
- /package/scaffold/.agent/skills/total-recall/skills/{skill → tr-skill}/learnings/.gitkeep +0 -0
- /package/scaffold/.agent/skills/total-recall/skills/{skill → tr-skill}/references/.gitkeep +0 -0
- /package/scaffold/.agent/skills/total-recall/skills/{skill → tr-skill}/scripts/.gitkeep +0 -0
- /package/scaffold/.agent/skills/total-recall/skills/{skill → tr-skill}/scripts/enforce-skill-optimization.mjs +0 -0
- /package/scaffold/.agent/skills/total-recall/skills/{skill → tr-skill}/scripts/skill-watcher.mjs +0 -0
- /package/scaffold/.agent/skills/total-recall/skills/{skill → tr-skill}/subagents/audit-all-skills.md +0 -0
- /package/scaffold/.agent/skills/total-recall/skills/{ssss → tr-ssss}/evals/evals.json +0 -0
- /package/scaffold/.agent/skills/total-recall/skills/{ssss → tr-ssss}/scripts/validate-schema.mjs +0 -0
- /package/scaffold/.agent/skills/total-recall/skills/{ssss → tr-ssss}/subagents/audit-ssss-node.md +0 -0
package/bin/antigravity.mjs
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
// Antigravity CLI Agent Wrapper — routes through the running Antigravity IDE
|
|
3
|
+
// via agentapi, using the user's logged-in Google account.
|
|
2
4
|
|
|
3
5
|
import fs from 'node:fs';
|
|
4
6
|
import path from 'node:path';
|
|
5
7
|
import os from 'node:os';
|
|
8
|
+
import { spawnSync } from 'node:child_process';
|
|
6
9
|
|
|
7
10
|
const args = process.argv.slice(2);
|
|
8
11
|
let prompt = '';
|
|
9
12
|
let outputJson = false;
|
|
10
|
-
let requestedModel = process.env.TR_CLI_MODEL || '
|
|
13
|
+
let requestedModel = process.env.TR_CLI_MODEL || 'flash';
|
|
11
14
|
|
|
12
15
|
if (args.includes('-h') || args.includes('--help')) {
|
|
13
16
|
console.log(`
|
|
14
|
-
Antigravity CLI Agent Wrapper — Headless
|
|
17
|
+
Antigravity CLI Agent Wrapper — Headless via agentapi (logged-in account)
|
|
15
18
|
|
|
16
19
|
Usage:
|
|
17
20
|
antigravity [options] [prompt]
|
|
@@ -19,39 +22,30 @@ Usage:
|
|
|
19
22
|
|
|
20
23
|
Options:
|
|
21
24
|
-p, --prompt The text prompt to generate content for
|
|
22
|
-
-m, --model
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
- pro / gemini-pro / 3.1-pro -> dynamically resolves best Pro model
|
|
26
|
-
-o, --json Output dynamic JSON response payload (choices: stream/json/default)
|
|
27
|
-
--json Output dynamic JSON response payload
|
|
25
|
+
-m, --model Model: flash_lite, flash, pro (default: flash)
|
|
26
|
+
-o json Output JSON response payload
|
|
27
|
+
--json Output JSON response payload
|
|
28
28
|
-h, --help Show this help information
|
|
29
|
-
|
|
30
|
-
Examples:
|
|
31
|
-
antigravity "Say hello"
|
|
32
|
-
antigravity -p "Write an essay about AI" -m pro
|
|
33
|
-
antigravity -p "Get system stats" --json
|
|
34
29
|
`);
|
|
35
30
|
process.exit(0);
|
|
36
31
|
}
|
|
37
32
|
|
|
33
|
+
// Ignored flags for compatibility with tr-cli-agents dispatch
|
|
34
|
+
const IGNORED = new Set(['--sandbox=false', '--yolo']);
|
|
35
|
+
|
|
38
36
|
for (let i = 0; i < args.length; i++) {
|
|
39
|
-
if (args[i]
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
i++;
|
|
45
|
-
} else if (args[i] === '--json') {
|
|
46
|
-
outputJson = true;
|
|
47
|
-
} else if (args[i] === '-m' || args[i] === '--model') {
|
|
48
|
-
requestedModel = args[i + 1] || requestedModel;
|
|
49
|
-
i++;
|
|
50
|
-
}
|
|
37
|
+
if (IGNORED.has(args[i])) continue;
|
|
38
|
+
if (args[i] === '-p' || args[i] === '--prompt') { prompt = args[i + 1] || ''; i++; }
|
|
39
|
+
else if (args[i] === '-o' && args[i + 1] === 'json') { outputJson = true; i++; }
|
|
40
|
+
else if (args[i] === '--json') { outputJson = true; }
|
|
41
|
+
else if (args[i] === '-m' || args[i] === '--model') { requestedModel = args[i + 1] || requestedModel; i++; }
|
|
51
42
|
}
|
|
52
43
|
|
|
53
44
|
if (!prompt && args.length > 0) {
|
|
54
|
-
|
|
45
|
+
// Last non-flag arg is the prompt
|
|
46
|
+
for (let i = args.length - 1; i >= 0; i--) {
|
|
47
|
+
if (!args[i].startsWith('-') && !IGNORED.has(args[i])) { prompt = args[i]; break; }
|
|
48
|
+
}
|
|
55
49
|
}
|
|
56
50
|
|
|
57
51
|
if (!prompt) {
|
|
@@ -59,165 +53,50 @@ if (!prompt) {
|
|
|
59
53
|
process.exit(1);
|
|
60
54
|
}
|
|
61
55
|
|
|
62
|
-
//
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
56
|
+
// Map model aliases
|
|
57
|
+
const modelMap = {
|
|
58
|
+
default: 'flash', gemini: 'flash', 'gemini-flash': 'flash',
|
|
59
|
+
'gemini-3.5-flash': 'flash', '3.5-flash': 'flash',
|
|
60
|
+
pro: 'pro', 'gemini-pro': 'pro', '3.1-pro': 'pro',
|
|
61
|
+
lite: 'flash_lite', 'flash-lite': 'flash_lite',
|
|
62
|
+
};
|
|
63
|
+
const model = modelMap[requestedModel] || requestedModel;
|
|
64
|
+
|
|
65
|
+
// Spawn agentapi new-conversation and capture the response
|
|
66
|
+
const result = spawnSync('agentapi', [
|
|
67
|
+
'new-conversation',
|
|
68
|
+
`--model=${model}`,
|
|
69
|
+
'--title=antigravity-cli',
|
|
70
|
+
prompt,
|
|
71
|
+
], {
|
|
72
|
+
encoding: 'utf8',
|
|
73
|
+
timeout: 300_000,
|
|
74
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
75
|
+
env: process.env,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
if (result.status !== 0) {
|
|
79
|
+
const err = result.stderr || result.stdout || 'agentapi failed';
|
|
80
|
+
console.error('Error:', err);
|
|
81
|
+
process.exit(1);
|
|
84
82
|
}
|
|
85
83
|
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
84
|
+
const raw = result.stdout?.trim() || '';
|
|
85
|
+
|
|
86
|
+
// Parse agentapi response
|
|
87
|
+
let text = raw;
|
|
88
|
+
try {
|
|
89
|
+
const parsed = JSON.parse(raw);
|
|
90
|
+
// agentapi returns { response: { conversationId, content } }
|
|
91
|
+
if (parsed.response?.content) text = parsed.response.content;
|
|
92
|
+
else if (parsed.content) text = parsed.content;
|
|
93
|
+
else if (parsed.response) text = typeof parsed.response === 'string' ? parsed.response : JSON.stringify(parsed.response);
|
|
94
|
+
} catch {
|
|
95
|
+
// Not JSON, use raw output
|
|
89
96
|
}
|
|
90
97
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
default: 'flash',
|
|
96
|
-
flash: 'flash',
|
|
97
|
-
'gemini-flash': 'flash',
|
|
98
|
-
'3.5-flash': 'flash',
|
|
99
|
-
pro: 'pro',
|
|
100
|
-
'gemini-pro': 'pro',
|
|
101
|
-
'3.1-pro': 'pro'
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
const category = aliases[selectedModel];
|
|
105
|
-
if (!category) {
|
|
106
|
-
// If it's a full model string, use it directly
|
|
107
|
-
return selectedModel;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
try {
|
|
111
|
-
const keyParam = apiKey ? `key=${apiKey}` : '';
|
|
112
|
-
const headers = {};
|
|
113
|
-
if (!apiKey && process.env.GCLOUD_TOKEN) {
|
|
114
|
-
headers['Authorization'] = `Bearer ${process.env.GCLOUD_TOKEN}`;
|
|
115
|
-
}
|
|
116
|
-
const res = await fetch(`https://generativelanguage.googleapis.com/v1beta/models?${keyParam}`, {
|
|
117
|
-
headers,
|
|
118
|
-
signal: AbortSignal.timeout(5000)
|
|
119
|
-
});
|
|
120
|
-
if (res.ok) {
|
|
121
|
-
const data = await res.json();
|
|
122
|
-
const models = data.models || [];
|
|
123
|
-
|
|
124
|
-
if (category === 'pro') {
|
|
125
|
-
const proPreferences = [
|
|
126
|
-
'gemini-3.1-pro-preview',
|
|
127
|
-
'gemini-2.5-pro',
|
|
128
|
-
'gemini-1.5-pro',
|
|
129
|
-
'gemini-pro-latest'
|
|
130
|
-
];
|
|
131
|
-
for (const p of proPreferences) {
|
|
132
|
-
if (models.some(m => m.name === `models/${p}`)) {
|
|
133
|
-
return p;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
} else {
|
|
137
|
-
const flashPreferences = [
|
|
138
|
-
'gemini-3.5-flash',
|
|
139
|
-
'gemini-3.1-flash-lite',
|
|
140
|
-
'gemini-2.5-flash',
|
|
141
|
-
'gemini-2.0-flash-lite',
|
|
142
|
-
'gemini-flash-latest'
|
|
143
|
-
];
|
|
144
|
-
for (const p of flashPreferences) {
|
|
145
|
-
if (models.some(m => m.name === `models/${p}`)) {
|
|
146
|
-
return p;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
} catch {
|
|
152
|
-
// Silent failover to hardcoded default targets
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// Ultimate fallback
|
|
156
|
-
return category === 'pro' ? 'gemini-3.1-pro-preview' : 'gemini-3.5-flash';
|
|
98
|
+
if (outputJson) {
|
|
99
|
+
console.log(JSON.stringify({ response: text }));
|
|
100
|
+
} else {
|
|
101
|
+
console.log(text);
|
|
157
102
|
}
|
|
158
|
-
|
|
159
|
-
async function main() {
|
|
160
|
-
try {
|
|
161
|
-
const resolvedModel = await resolveGenerativeModel(requestedModel);
|
|
162
|
-
const keyParam = apiKey ? `?key=${apiKey}` : '';
|
|
163
|
-
const url = `https://generativelanguage.googleapis.com/v1beta/models/${resolvedModel}:generateContent${keyParam}`;
|
|
164
|
-
|
|
165
|
-
const headers = {
|
|
166
|
-
'Content-Type': 'application/json',
|
|
167
|
-
};
|
|
168
|
-
if (!apiKey && process.env.GCLOUD_TOKEN) {
|
|
169
|
-
headers['Authorization'] = `Bearer ${process.env.GCLOUD_TOKEN}`;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const res = await fetch(url, {
|
|
173
|
-
method: 'POST',
|
|
174
|
-
headers,
|
|
175
|
-
body: JSON.stringify({
|
|
176
|
-
contents: [{
|
|
177
|
-
parts: [{ text: prompt }]
|
|
178
|
-
}]
|
|
179
|
-
})
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
if (!res.ok) {
|
|
183
|
-
const errText = await res.text();
|
|
184
|
-
throw new Error(`Gemini API returned status ${res.status}: ${errText}`);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
const data = await res.json();
|
|
188
|
-
const text = data.candidates?.[0]?.content?.parts?.[0]?.text || '';
|
|
189
|
-
|
|
190
|
-
// Save usage data for token monitoring/budget safety
|
|
191
|
-
try {
|
|
192
|
-
const promptTokens = data.usageMetadata?.promptTokenCount || 0;
|
|
193
|
-
const candidatesTokens = data.usageMetadata?.candidatesTokenCount || 0;
|
|
194
|
-
if (promptTokens > 0 || candidatesTokens > 0) {
|
|
195
|
-
const tmpDir = path.join(os.homedir(), '.gemini', 'tmp', 'antigravity', 'chats');
|
|
196
|
-
if (!fs.existsSync(tmpDir)) {
|
|
197
|
-
fs.mkdirSync(tmpDir, { recursive: true });
|
|
198
|
-
}
|
|
199
|
-
const usageLine = JSON.stringify({
|
|
200
|
-
timestamp: new Date().toISOString(),
|
|
201
|
-
tokens: {
|
|
202
|
-
input: promptTokens,
|
|
203
|
-
output: candidatesTokens
|
|
204
|
-
}
|
|
205
|
-
});
|
|
206
|
-
fs.appendFileSync(path.join(tmpDir, 'usage.jsonl'), usageLine + '\n', 'utf8');
|
|
207
|
-
}
|
|
208
|
-
} catch {
|
|
209
|
-
// Silent ignore
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
if (outputJson) {
|
|
213
|
-
console.log(JSON.stringify({ response: text }));
|
|
214
|
-
} else {
|
|
215
|
-
console.log(text);
|
|
216
|
-
}
|
|
217
|
-
} catch (err) {
|
|
218
|
-
console.error('Error calling Gemini API:', err.message);
|
|
219
|
-
process.exit(1);
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
main();
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "total-recall-brain",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.11.0",
|
|
4
4
|
"description": "Sovereign AI system — database-free, SSSS-powered personal intelligence engine deployable to any POSIX host.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"total-recall": "
|
|
8
|
-
"antigravity": "
|
|
7
|
+
"total-recall": "bin/total-recall.mjs",
|
|
8
|
+
"antigravity": "bin/antigravity.mjs"
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
11
|
"bin/",
|
|
@@ -56,6 +56,7 @@
|
|
|
56
56
|
},
|
|
57
57
|
"homepage": "https://github.com/gregiteen/total-recall#readme",
|
|
58
58
|
"dependencies": {
|
|
59
|
+
"@ssss/cli": "github:gregiteen/ssss#v0.7.0",
|
|
59
60
|
"bcrypt": "^6.0.0",
|
|
60
61
|
"cookie-parser": "^1.4.7",
|
|
61
62
|
"cors": "^2.8.6",
|
|
@@ -64,9 +65,9 @@
|
|
|
64
65
|
"gray-matter": "^4.0.3",
|
|
65
66
|
"jsonwebtoken": "^9.0.3",
|
|
66
67
|
"natural": "^8.1.1",
|
|
68
|
+
"ws": "^8.18.0",
|
|
67
69
|
"yaml": "^2.9.0",
|
|
68
|
-
"zod": "^4.4.3"
|
|
69
|
-
"ws": "^8.18.0"
|
|
70
|
+
"zod": "^4.4.3"
|
|
70
71
|
},
|
|
71
72
|
"devDependencies": {
|
|
72
73
|
"skills": "^1.5.7",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: total-recall
|
|
3
|
+
provenance: total-recall
|
|
3
4
|
description: "Use this skill as the master guide to understand the entire Total Recall Sovereign AI OS setup, VFS topologies, SSSS protocol, CLI parameter reference, troubleshooting, and automated upstream repository sync. MANDATORY: Read this file before attempting major setup modifications or diagnoses."
|
|
4
5
|
---
|
|
5
6
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: cli-agents
|
|
2
|
+
name: tr-cli-agents
|
|
3
|
+
provenance: total-recall
|
|
3
4
|
description: "Orchestrate headlessly spawned CLI agents from the central registry."
|
|
4
5
|
---
|
|
5
6
|
|
|
@@ -12,7 +13,7 @@ This system skill governs the discovery, configuration, and execution of headles
|
|
|
12
13
|
## 🎯 SYSTEM OVERVIEW
|
|
13
14
|
|
|
14
15
|
The CLI Agent Dispatch Engine parses agent profiles from the central registry file:
|
|
15
|
-
`.agent/skills/total-recall/skills/cli-agents/agents.yml`
|
|
16
|
+
`.agent/skills/total-recall/skills/tr-cli-agents/agents.yml`
|
|
16
17
|
|
|
17
18
|
```mermaid
|
|
18
19
|
flowchart TD
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: skill
|
|
2
|
+
name: tr-skill
|
|
3
|
+
provenance: total-recall
|
|
3
4
|
description: "Use this skill when creating, auditing, or modifying any skill in the .agent/skills/ ecosystem. MANDATORY: You MUST read the full SKILL.md file before executing."
|
|
4
5
|
---
|
|
5
6
|
|
|
@@ -186,7 +187,7 @@ Contains standalone prompt files for subtasks that can be delegated to parallel
|
|
|
186
187
|
|
|
187
188
|
### Quick method (script):
|
|
188
189
|
```bash
|
|
189
|
-
bash .agent/skills/skill/scripts/create-skill.sh my-new-skill
|
|
190
|
+
bash .agent/skills/tr-skill/scripts/create-skill.sh my-new-skill
|
|
190
191
|
```
|
|
191
192
|
|
|
192
193
|
### Manual method:
|
|
@@ -2,19 +2,19 @@
|
|
|
2
2
|
{
|
|
3
3
|
"id": "skill-eval-1",
|
|
4
4
|
"description": "SKILL.md exists",
|
|
5
|
-
"command": "test -f .agent/skills/skill/SKILL.md",
|
|
5
|
+
"command": "test -f .agent/skills/tr-skill/SKILL.md",
|
|
6
6
|
"expected_exit_code": 0
|
|
7
7
|
},
|
|
8
8
|
{
|
|
9
9
|
"id": "skill-eval-2",
|
|
10
10
|
"description": "Evals validation",
|
|
11
|
-
"command": "test -f .agent/skills/skill/scripts/run-evals.mjs",
|
|
11
|
+
"command": "test -f .agent/skills/tr-skill/scripts/run-evals.mjs",
|
|
12
12
|
"expected_exit_code": 0
|
|
13
13
|
},
|
|
14
14
|
{
|
|
15
15
|
"id": "skill-eval-3",
|
|
16
16
|
"description": "Optimization enforcement",
|
|
17
|
-
"command": "test -f .agent/skills/skill/scripts/enforce-skill-optimization.mjs",
|
|
17
|
+
"command": "test -f .agent/skills/tr-skill/scripts/enforce-skill-optimization.mjs",
|
|
18
18
|
"expected_exit_code": 0
|
|
19
19
|
}
|
|
20
20
|
]
|
|
@@ -10,5 +10,5 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
|
10
10
|
ROOT="$(cd "$SCRIPT_DIR/../../../.." && pwd)"
|
|
11
11
|
|
|
12
12
|
echo "🔄 OnSkillAdd hook triggered — regenerating skill index..."
|
|
13
|
-
node "$ROOT/.agent/skills/skill/scripts/update-skill-index.mjs"
|
|
13
|
+
node "$ROOT/.agent/skills/tr-skill/scripts/update-skill-index.mjs"
|
|
14
14
|
echo "✅ Routing table and INSTRUCTIONS.md updated."
|
package/scaffold/.agent/skills/total-recall/skills/{skill → tr-skill}/hooks/on-skill-delete.sh
RENAMED
|
@@ -10,5 +10,5 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
|
10
10
|
ROOT="$(cd "$SCRIPT_DIR/../../../.." && pwd)"
|
|
11
11
|
|
|
12
12
|
echo "🧹 OnSkillDelete hook triggered — cleaning skill index..."
|
|
13
|
-
node "$ROOT/.agent/skills/skill/scripts/update-skill-index.mjs"
|
|
13
|
+
node "$ROOT/.agent/skills/tr-skill/scripts/update-skill-index.mjs"
|
|
14
14
|
echo "✅ Routing table and INSTRUCTIONS.md cleaned."
|
package/scaffold/.agent/skills/total-recall/skills/{skill → tr-skill}/scripts/create-skill.sh
RENAMED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
# create-skill.sh — Scaffold a new Dev Skill from the template
|
|
3
3
|
#
|
|
4
|
-
# Usage: bash .agent/skills/skill/scripts/create-skill.sh <skill-name>
|
|
5
|
-
# Example: bash .agent/skills/skill/scripts/create-skill.sh my-new-skill
|
|
4
|
+
# Usage: bash .agent/skills/tr-skill/scripts/create-skill.sh <skill-name>
|
|
5
|
+
# Example: bash .agent/skills/tr-skill/scripts/create-skill.sh my-new-skill
|
|
6
6
|
|
|
7
7
|
set -euo pipefail
|
|
8
8
|
|
|
@@ -60,6 +60,6 @@ echo ""
|
|
|
60
60
|
echo "📝 Next steps:"
|
|
61
61
|
echo " 1. Edit $TARGET/SKILL.md with real instructions"
|
|
62
62
|
echo " 2. Write a trigger-optimized description in the frontmatter"
|
|
63
|
-
echo " 3. Run: node .agent/skills/skill/scripts/update-skill-index.mjs"
|
|
63
|
+
echo " 3. Run: node .agent/skills/tr-skill/scripts/update-skill-index.mjs"
|
|
64
64
|
echo ""
|
|
65
65
|
echo "🏁 Done."
|
package/scaffold/.agent/skills/total-recall/skills/{skill → tr-skill}/scripts/find-skills.mjs
RENAMED
|
@@ -77,6 +77,7 @@ export function parseFindOutput(rawOutput) {
|
|
|
77
77
|
|
|
78
78
|
const name = skillMatch[1];
|
|
79
79
|
const installs = parseInstalls(skillMatch[2], skillMatch[3] || '');
|
|
80
|
+
const installsStr = `${skillMatch[2]}${skillMatch[3] || ''}`;
|
|
80
81
|
|
|
81
82
|
// Next non-empty line should be the URL (prefixed with └ or similar box-drawing chars)
|
|
82
83
|
let url = '';
|
|
@@ -92,12 +93,19 @@ export function parseFindOutput(rawOutput) {
|
|
|
92
93
|
break; // only check the very next non-empty line
|
|
93
94
|
}
|
|
94
95
|
|
|
95
|
-
results.push({ name, installs, url });
|
|
96
|
+
results.push({ name, installs, installsStr, url });
|
|
96
97
|
}
|
|
97
98
|
|
|
98
99
|
return results;
|
|
99
100
|
}
|
|
100
101
|
|
|
102
|
+
function formatInstalls(installs) {
|
|
103
|
+
if (installs >= 1_000_000_000) return `${(installs / 1_000_000_000).toFixed(1).replace(/\.0$/, '')}B`;
|
|
104
|
+
if (installs >= 1_000_000) return `${(installs / 1_000_000).toFixed(1).replace(/\.0$/, '')}M`;
|
|
105
|
+
if (installs >= 1_000) return `${(installs / 1_000).toFixed(1).replace(/\.0$/, '')}K`;
|
|
106
|
+
return String(installs);
|
|
107
|
+
}
|
|
108
|
+
|
|
101
109
|
// ─── Live Registry Search ───────────────────────────────────────────────────
|
|
102
110
|
|
|
103
111
|
/**
|
|
@@ -122,3 +130,19 @@ export function searchRegistry(query, { timeoutMs = 15_000 } = {}) {
|
|
|
122
130
|
return [];
|
|
123
131
|
}
|
|
124
132
|
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Search skills.sh and sort by absolute install count, highest first.
|
|
136
|
+
*
|
|
137
|
+
* @param {string} query
|
|
138
|
+
* @param {object} [options]
|
|
139
|
+
* @returns {Array<{ name: string, installs: number, installsStr: string, url: string }>}
|
|
140
|
+
*/
|
|
141
|
+
export function searchAndSort(query, options = {}) {
|
|
142
|
+
return searchRegistry(query, options)
|
|
143
|
+
.map(result => ({
|
|
144
|
+
...result,
|
|
145
|
+
installsStr: result.installsStr || formatInstalls(result.installs || 0),
|
|
146
|
+
}))
|
|
147
|
+
.sort((a, b) => (b.installs || 0) - (a.installs || 0));
|
|
148
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* install-skill.mjs — safe wrapper around the Skills CLI installer.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import fs from 'node:fs';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import { spawnSync } from 'node:child_process';
|
|
8
|
+
import { scanDirectory, formatReport } from './scan-skill.mjs';
|
|
9
|
+
|
|
10
|
+
const BLOCKED_SOURCE_CHARS = /[\0\r\n;&|<>`$]/;
|
|
11
|
+
|
|
12
|
+
export function validateSkillSource(source) {
|
|
13
|
+
if (typeof source !== 'string') return false;
|
|
14
|
+
const trimmed = source.trim();
|
|
15
|
+
if (!trimmed || trimmed.length > 500) return false;
|
|
16
|
+
return !BLOCKED_SOURCE_CHARS.test(trimmed);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function inferSkillName(source) {
|
|
20
|
+
const trimmed = String(source || '').trim().replace(/\/+$/, '');
|
|
21
|
+
if (!trimmed) return null;
|
|
22
|
+
if (!trimmed.startsWith('git@') && trimmed.includes('@')) {
|
|
23
|
+
return trimmed.split('@').pop() || null;
|
|
24
|
+
}
|
|
25
|
+
const last = trimmed.split('/').filter(Boolean).pop();
|
|
26
|
+
return last ? last.replace(/\.git$/i, '') : null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function uniqueExistingDirs(paths) {
|
|
30
|
+
const seen = new Set();
|
|
31
|
+
const dirs = [];
|
|
32
|
+
for (const candidate of paths) {
|
|
33
|
+
if (!candidate) continue;
|
|
34
|
+
const resolved = path.resolve(candidate);
|
|
35
|
+
if (seen.has(resolved) || !fs.existsSync(resolved)) continue;
|
|
36
|
+
seen.add(resolved);
|
|
37
|
+
dirs.push(resolved);
|
|
38
|
+
}
|
|
39
|
+
return dirs;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function installedSkillCandidates(source, { cwd = process.cwd(), agentDir = path.join(cwd, '.agent') } = {}) {
|
|
43
|
+
const skillName = inferSkillName(source);
|
|
44
|
+
if (!skillName) return [];
|
|
45
|
+
return [
|
|
46
|
+
path.join(agentDir, 'skills', skillName),
|
|
47
|
+
path.join(cwd, '.agent', 'skills', skillName),
|
|
48
|
+
path.join(cwd, '.agents', 'skills', skillName),
|
|
49
|
+
path.join(cwd, '.claude', 'skills', skillName),
|
|
50
|
+
];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Install a skill with the Skills CLI, then scan installed script files.
|
|
55
|
+
*
|
|
56
|
+
* @param {string} source skills add source, e.g. owner/repo@skill or URL
|
|
57
|
+
* @param {object} [options]
|
|
58
|
+
* @param {string} [options.cwd]
|
|
59
|
+
* @param {string} [options.agentDir]
|
|
60
|
+
* @param {boolean} [options.dryRun=false]
|
|
61
|
+
* @returns {{ success: boolean, source: string, command?: string[], findings?: Array, error?: string }}
|
|
62
|
+
*/
|
|
63
|
+
export function installSkill(source, options = {}) {
|
|
64
|
+
const cwd = options.cwd || process.cwd();
|
|
65
|
+
const agentDir = options.agentDir || path.join(cwd, '.agent');
|
|
66
|
+
const normalized = String(source || '').trim();
|
|
67
|
+
|
|
68
|
+
if (!validateSkillSource(normalized)) {
|
|
69
|
+
const error = 'Invalid skill source. Use a repository, URL, git source, local path, or owner/repo@skill without shell metacharacters.';
|
|
70
|
+
console.error(`❌ ${error}`);
|
|
71
|
+
return { success: false, source: normalized, error };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const command = ['npx', '-y', 'skills', 'add', normalized, '-y'];
|
|
75
|
+
if (options.dryRun) {
|
|
76
|
+
return { success: true, source: normalized, command };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
console.log(`📦 Installing skill: ${normalized}`);
|
|
80
|
+
const child = spawnSync(command[0], command.slice(1), {
|
|
81
|
+
cwd,
|
|
82
|
+
encoding: 'utf8',
|
|
83
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
if (child.stdout) process.stdout.write(child.stdout);
|
|
87
|
+
if (child.stderr) process.stderr.write(child.stderr);
|
|
88
|
+
|
|
89
|
+
if (child.error || child.status !== 0) {
|
|
90
|
+
const error = child.error?.message || `skills CLI exited with status ${child.status}`;
|
|
91
|
+
console.error(`❌ Skill installation failed: ${error}`);
|
|
92
|
+
return { success: false, source: normalized, command, error };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const installedDirs = uniqueExistingDirs(installedSkillCandidates(normalized, { cwd, agentDir }));
|
|
96
|
+
const findings = installedDirs.flatMap(dir => scanDirectory(dir));
|
|
97
|
+
const report = formatReport(findings);
|
|
98
|
+
console.log(report.trimEnd());
|
|
99
|
+
|
|
100
|
+
if (findings.some(f => f.severity === 'CRITICAL')) {
|
|
101
|
+
const error = 'Installed skill contains critical scanner findings.';
|
|
102
|
+
console.error(`❌ ${error}`);
|
|
103
|
+
return { success: false, source: normalized, command, findings, error };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const compile = spawnSync('npx', ['total-recall', 'compile'], {
|
|
107
|
+
cwd,
|
|
108
|
+
encoding: 'utf8',
|
|
109
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
110
|
+
env: { ...process.env, AGENT_DIR: agentDir },
|
|
111
|
+
});
|
|
112
|
+
if (compile.status !== 0) {
|
|
113
|
+
console.warn('⚠️ Skill installed, but surface recompilation returned a warning.');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return { success: true, source: normalized, command, findings };
|
|
117
|
+
}
|
package/scaffold/.agent/skills/total-recall/skills/{skill → tr-skill}/scripts/scan-skill.mjs
RENAMED
|
@@ -205,3 +205,23 @@ export function formatReport(findings) {
|
|
|
205
205
|
|
|
206
206
|
return report;
|
|
207
207
|
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* CLI-friendly scanner entrypoint.
|
|
211
|
+
*
|
|
212
|
+
* @param {string} targetDir
|
|
213
|
+
* @returns {{ success: boolean, findings: Array, report: string }}
|
|
214
|
+
*/
|
|
215
|
+
export function runScan(targetDir) {
|
|
216
|
+
if (!targetDir || !fs.existsSync(targetDir)) {
|
|
217
|
+
const report = `Skill directory not found: ${targetDir}\n`;
|
|
218
|
+
console.error(report.trim());
|
|
219
|
+
return { success: false, findings: [], report };
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const findings = scanDirectory(targetDir);
|
|
223
|
+
const report = formatReport(findings);
|
|
224
|
+
const blocked = findings.some(f => f.severity === 'CRITICAL');
|
|
225
|
+
console.log(report.trimEnd());
|
|
226
|
+
return { success: !blocked, findings, report };
|
|
227
|
+
}
|