@arp-protocol/cli 0.0.1 → 0.0.3
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/CHANGELOG.md +74 -0
- package/LICENSE +26 -0
- package/README.md +19 -8
- package/dist/chunks/backfill-awengrsd.js +1048 -0
- package/dist/chunks/{ccip-qmytgn8e.js → ccip-zxtpc4fx.js} +4 -4
- package/dist/chunks/cli-00qp1sj2.js +116 -0
- package/dist/chunks/{cli-c8568g4c.js → cli-0gsbnq5w.js} +72 -72
- package/dist/chunks/{cli-0z6f3603.js → cli-10wacn76.js} +229 -15
- package/dist/chunks/{cli-zpft011j.js → cli-64fx73kx.js} +3 -3
- package/dist/chunks/{cli-v446mw5v.js → cli-fbwpcfvp.js} +34 -34
- package/dist/chunks/{cli-0hqxver6.js → cli-mhzh9z8k.js} +33 -33
- package/dist/chunks/{cli-tyey74p3.js → cli-n4qagybd.js} +6 -2
- package/dist/chunks/{cli-74hv4etk.js → cli-wev0jgf7.js} +4801 -3898
- package/dist/chunks/{commands-dnk10vze.js → commands-157bms70.js} +77 -16
- package/dist/chunks/{env-s4585w6j.js → env-n5ac8d1z.js} +1 -0
- package/dist/chunks/{index-3a3ssef1.js → index-jrs2shcc.js} +3 -3
- package/dist/chunks/line-691gbaha.js +166 -0
- package/dist/chunks/{node-init-4v2m2vj0.js → node-init-rcepfyeh.js} +6 -6
- package/dist/chunks/{node-0ydee9hy.js → node-m0mx4y5k.js} +6 -6
- package/dist/chunks/{server-jmyjttvv.js → server-59zry2jp.js} +3817 -163
- package/dist/chunks/{share-g8v5qe6r.js → share-vtbkzs5f.js} +8 -5
- package/dist/cli.js +48 -19
- package/dist/hook.js +3542 -232
- package/package.json +10 -3
- package/dist/chunks/backfill-j4hxt1w1.js +0 -262
- package/dist/chunks/line-zy1tc2np.js +0 -45
package/dist/hook.js
CHANGED
|
@@ -14,9 +14,496 @@ function appendOwn(path, text) {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
// ../hook/src/hook.ts
|
|
17
|
-
import { existsSync as
|
|
17
|
+
import { existsSync as existsSync3, lstatSync as lstatSync2, mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "node:fs";
|
|
18
18
|
import { homedir as homedir3 } from "node:os";
|
|
19
|
-
import { dirname as
|
|
19
|
+
import { dirname as dirname4, join as join5 } from "node:path";
|
|
20
|
+
// ../../packages/graphql/dist/http.js
|
|
21
|
+
var DEFAULT_TIMEOUT_MS = 1e4;
|
|
22
|
+
var DEFAULT_MAX_BODY_BYTES = 8 * 1024 * 1024;
|
|
23
|
+
|
|
24
|
+
class HttpError extends Error {
|
|
25
|
+
kind;
|
|
26
|
+
timeoutMs;
|
|
27
|
+
constructor(kind, message, timeoutMs, cause) {
|
|
28
|
+
super(message, cause === undefined ? undefined : { cause });
|
|
29
|
+
this.name = "HttpError";
|
|
30
|
+
this.kind = kind;
|
|
31
|
+
this.timeoutMs = timeoutMs;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
async function readBodyText(response, options = {}) {
|
|
35
|
+
const maxBytes = options.maxBytes ?? DEFAULT_MAX_BODY_BYTES;
|
|
36
|
+
const timeoutMs = options.timeoutMs ?? 0;
|
|
37
|
+
const body = response.body;
|
|
38
|
+
if (body === null)
|
|
39
|
+
return "";
|
|
40
|
+
const reader = body.getReader();
|
|
41
|
+
const signal = options.signal;
|
|
42
|
+
let onAbort = null;
|
|
43
|
+
const aborted = signal === undefined ? null : new Promise((_, reject) => {
|
|
44
|
+
const fire = () => reject(new HttpError("timeout", `timed out after ${timeoutMs}ms reading the body`, timeoutMs));
|
|
45
|
+
onAbort = fire;
|
|
46
|
+
if (signal.aborted)
|
|
47
|
+
fire();
|
|
48
|
+
else
|
|
49
|
+
signal.addEventListener("abort", fire, { once: true });
|
|
50
|
+
});
|
|
51
|
+
aborted?.catch(() => {
|
|
52
|
+
return;
|
|
53
|
+
});
|
|
54
|
+
const decoder = new TextDecoder;
|
|
55
|
+
let total = 0;
|
|
56
|
+
let text = "";
|
|
57
|
+
try {
|
|
58
|
+
for (;; ) {
|
|
59
|
+
const chunk = await (aborted === null ? reader.read() : Promise.race([reader.read(), aborted]));
|
|
60
|
+
if (chunk.done)
|
|
61
|
+
break;
|
|
62
|
+
total += chunk.value.byteLength;
|
|
63
|
+
if (total > maxBytes) {
|
|
64
|
+
throw new HttpError("too-large", `body exceeds ${maxBytes} bytes`, timeoutMs);
|
|
65
|
+
}
|
|
66
|
+
text += decoder.decode(chunk.value, { stream: true });
|
|
67
|
+
}
|
|
68
|
+
return text + decoder.decode();
|
|
69
|
+
} catch (error) {
|
|
70
|
+
reader.cancel().catch(() => {
|
|
71
|
+
return;
|
|
72
|
+
});
|
|
73
|
+
if (error instanceof HttpError)
|
|
74
|
+
throw error;
|
|
75
|
+
throw new HttpError("network", error instanceof Error ? error.message : String(error), timeoutMs, error);
|
|
76
|
+
} finally {
|
|
77
|
+
if (signal !== undefined && onAbort !== null)
|
|
78
|
+
signal.removeEventListener("abort", onAbort);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
async function fetchTextWithTimeout(fetchImpl, url, init, options) {
|
|
82
|
+
const { timeoutMs } = options;
|
|
83
|
+
const controller = new AbortController;
|
|
84
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
85
|
+
try {
|
|
86
|
+
let response;
|
|
87
|
+
try {
|
|
88
|
+
response = await fetchImpl(url, { ...init, signal: controller.signal });
|
|
89
|
+
} catch (error) {
|
|
90
|
+
if (controller.signal.aborted) {
|
|
91
|
+
throw new HttpError("timeout", `timed out after ${timeoutMs}ms: ${url}`, timeoutMs);
|
|
92
|
+
}
|
|
93
|
+
throw new HttpError("network", error instanceof Error ? error.message : String(error), timeoutMs, error);
|
|
94
|
+
}
|
|
95
|
+
const text = await readBodyText(response, {
|
|
96
|
+
signal: controller.signal,
|
|
97
|
+
timeoutMs,
|
|
98
|
+
...options.maxBytes === undefined ? {} : { maxBytes: options.maxBytes }
|
|
99
|
+
});
|
|
100
|
+
return { response, text };
|
|
101
|
+
} finally {
|
|
102
|
+
clearTimeout(timer);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function defaultFetch() {
|
|
106
|
+
if (typeof globalThis.fetch !== "function") {
|
|
107
|
+
throw new TypeError("no global fetch available — pass one via createErc8004Client({fetch})");
|
|
108
|
+
}
|
|
109
|
+
return (input, init) => globalThis.fetch(input, init);
|
|
110
|
+
}
|
|
111
|
+
// ../../packages/graphql/dist/json.js
|
|
112
|
+
function isRecord(value) {
|
|
113
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
114
|
+
}
|
|
115
|
+
function readRecord(source, key) {
|
|
116
|
+
if (!isRecord(source))
|
|
117
|
+
return null;
|
|
118
|
+
const child = source[key];
|
|
119
|
+
return isRecord(child) ? child : null;
|
|
120
|
+
}
|
|
121
|
+
function readString(source, key) {
|
|
122
|
+
if (!isRecord(source))
|
|
123
|
+
return null;
|
|
124
|
+
const value = source[key];
|
|
125
|
+
return typeof value === "string" ? value : null;
|
|
126
|
+
}
|
|
127
|
+
function readArray(source, key) {
|
|
128
|
+
if (!isRecord(source))
|
|
129
|
+
return [];
|
|
130
|
+
const value = source[key];
|
|
131
|
+
return Array.isArray(value) ? value : [];
|
|
132
|
+
}
|
|
133
|
+
// ../../packages/graphql/dist/client.js
|
|
134
|
+
class GraphqlError extends Error {
|
|
135
|
+
url;
|
|
136
|
+
httpStatus;
|
|
137
|
+
constructor(message, url, httpStatus, cause) {
|
|
138
|
+
super(message, cause === undefined ? undefined : { cause });
|
|
139
|
+
this.name = "GraphqlError";
|
|
140
|
+
this.url = url;
|
|
141
|
+
this.httpStatus = httpStatus;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
function createGraphqlTransport(config) {
|
|
145
|
+
const timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
146
|
+
return {
|
|
147
|
+
async request(query, variables, options) {
|
|
148
|
+
let response;
|
|
149
|
+
let text;
|
|
150
|
+
try {
|
|
151
|
+
({ response, text } = await fetchTextWithTimeout(config.fetch, config.url, {
|
|
152
|
+
method: "POST",
|
|
153
|
+
headers: {
|
|
154
|
+
"content-type": "application/json",
|
|
155
|
+
accept: "application/json"
|
|
156
|
+
},
|
|
157
|
+
body: JSON.stringify({ query, variables })
|
|
158
|
+
}, {
|
|
159
|
+
timeoutMs: options?.timeoutMs ?? timeoutMs,
|
|
160
|
+
...config.maxBodyBytes === undefined ? {} : { maxBytes: config.maxBodyBytes }
|
|
161
|
+
}));
|
|
162
|
+
} catch (error) {
|
|
163
|
+
const message = error instanceof HttpError ? error.message : String(error);
|
|
164
|
+
throw new GraphqlError(message, config.url, null, error);
|
|
165
|
+
}
|
|
166
|
+
if (!response.ok) {
|
|
167
|
+
throw new GraphqlError(`HTTP ${response.status} ${response.statusText}`, config.url, response.status);
|
|
168
|
+
}
|
|
169
|
+
let body = null;
|
|
170
|
+
try {
|
|
171
|
+
body = JSON.parse(text);
|
|
172
|
+
} catch {
|
|
173
|
+
body = null;
|
|
174
|
+
}
|
|
175
|
+
if (!isRecord(body)) {
|
|
176
|
+
throw new GraphqlError("response body is not a JSON object", config.url, 200);
|
|
177
|
+
}
|
|
178
|
+
const errors = readArray(body, "errors");
|
|
179
|
+
if (errors.length > 0) {
|
|
180
|
+
const messages = errors.map((entry) => readString(entry, "message") ?? "unknown error").join("; ");
|
|
181
|
+
throw new GraphqlError(messages, config.url, 200);
|
|
182
|
+
}
|
|
183
|
+
const data = body["data"];
|
|
184
|
+
if (!isRecord(data)) {
|
|
185
|
+
throw new GraphqlError("response carries no data object", config.url, 200);
|
|
186
|
+
}
|
|
187
|
+
return data;
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
// ../../packages/graphql/dist/queries/agent.js
|
|
192
|
+
var THING = `value { thing { name description image url } }`;
|
|
193
|
+
var MARKET_SIDE = `
|
|
194
|
+
total_market_cap
|
|
195
|
+
total_assets
|
|
196
|
+
vaults(where: {curve_id: {_eq: $curveId}}, limit: 1) {
|
|
197
|
+
market_cap
|
|
198
|
+
total_assets
|
|
199
|
+
total_shares
|
|
200
|
+
current_share_price
|
|
201
|
+
position_count
|
|
202
|
+
}`;
|
|
203
|
+
var RESOLVE_AGENT_QUERY = `
|
|
204
|
+
query ResolveAgentByCaip($sameAsPredicateId: String!, $caipId: String!) {
|
|
205
|
+
triples(
|
|
206
|
+
where: {
|
|
207
|
+
predicate_id: {_eq: $sameAsPredicateId}
|
|
208
|
+
object: {value: {thing: {name: {_eq: $caipId}}}}
|
|
209
|
+
}
|
|
210
|
+
limit: 2
|
|
211
|
+
) {
|
|
212
|
+
subject { term_id label data ${THING} }
|
|
213
|
+
}
|
|
214
|
+
}`;
|
|
215
|
+
var TRUST_SURFACE_QUERY = `
|
|
216
|
+
query AgentTrustSurface($subjectId: String!, $predicateIds: [String!], $curveId: numeric!, $limit: Int!) {
|
|
217
|
+
triples(
|
|
218
|
+
where: {subject_id: {_eq: $subjectId}, predicate_id: {_in: $predicateIds}}
|
|
219
|
+
order_by: {term: {total_market_cap: desc}}
|
|
220
|
+
limit: $limit
|
|
221
|
+
) {
|
|
222
|
+
term_id
|
|
223
|
+
predicate { term_id label }
|
|
224
|
+
object { term_id label data ${THING} }
|
|
225
|
+
term {${MARKET_SIDE}
|
|
226
|
+
}
|
|
227
|
+
counter_term {${MARKET_SIDE}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}`;
|
|
231
|
+
var CAPABILITIES_QUERY = `
|
|
232
|
+
query AgentCapabilities($subjectId: String!, $predicateIds: [String!], $limit: Int!) {
|
|
233
|
+
triples(
|
|
234
|
+
where: {subject_id: {_eq: $subjectId}, predicate_id: {_in: $predicateIds}}
|
|
235
|
+
order_by: {term_id: asc}
|
|
236
|
+
limit: $limit
|
|
237
|
+
) {
|
|
238
|
+
term_id
|
|
239
|
+
predicate { term_id label }
|
|
240
|
+
object { term_id label ${THING} }
|
|
241
|
+
}
|
|
242
|
+
}`;
|
|
243
|
+
var IDENTITY_SAMPLE_LIMIT = 4;
|
|
244
|
+
var CAPABILITY_SAMPLE_LIMIT = 8;
|
|
245
|
+
var COHORT_QUERY = `
|
|
246
|
+
query AgentCohort($where: triples_bool_exp!, $sameAsPredicateId: String!, $capabilityPredicateIds: [String!], $orderBy: [triples_order_by!]!, $limit: Int!, $offset: Int!) {
|
|
247
|
+
total: triples_aggregate(
|
|
248
|
+
where: $where
|
|
249
|
+
) {
|
|
250
|
+
aggregate { count }
|
|
251
|
+
}
|
|
252
|
+
triples(
|
|
253
|
+
where: $where
|
|
254
|
+
order_by: $orderBy
|
|
255
|
+
limit: $limit
|
|
256
|
+
offset: $offset
|
|
257
|
+
) {
|
|
258
|
+
subject {
|
|
259
|
+
term_id
|
|
260
|
+
label
|
|
261
|
+
${THING}
|
|
262
|
+
as_subject_triples_aggregate { aggregate { count } }
|
|
263
|
+
term {
|
|
264
|
+
total_market_cap
|
|
265
|
+
vaults_aggregate { aggregate { count sum { position_count } } }
|
|
266
|
+
}
|
|
267
|
+
identityCount: as_subject_triples_aggregate(
|
|
268
|
+
where: {predicate_id: {_eq: $sameAsPredicateId}}
|
|
269
|
+
) {
|
|
270
|
+
aggregate { count }
|
|
271
|
+
}
|
|
272
|
+
identity: as_subject_triples(
|
|
273
|
+
where: {predicate_id: {_eq: $sameAsPredicateId}}
|
|
274
|
+
limit: ${IDENTITY_SAMPLE_LIMIT}
|
|
275
|
+
) {
|
|
276
|
+
object { label ${THING} }
|
|
277
|
+
}
|
|
278
|
+
capabilities: as_subject_triples(
|
|
279
|
+
where: {predicate_id: {_in: $capabilityPredicateIds}}
|
|
280
|
+
limit: ${CAPABILITY_SAMPLE_LIMIT}
|
|
281
|
+
) {
|
|
282
|
+
predicate_id
|
|
283
|
+
object { term_id label }
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}`;
|
|
288
|
+
// ../../packages/graphql/dist/queries/atom-standing.js
|
|
289
|
+
var VAULT_SIDE = `
|
|
290
|
+
total_market_cap
|
|
291
|
+
total_assets
|
|
292
|
+
vaults(where: {curve_id: {_eq: $curveId}}, limit: 1) {
|
|
293
|
+
market_cap
|
|
294
|
+
total_assets
|
|
295
|
+
total_shares
|
|
296
|
+
current_share_price
|
|
297
|
+
position_count
|
|
298
|
+
}`;
|
|
299
|
+
var ATOM_STANDING_QUERY = `
|
|
300
|
+
query AtomStanding($atomId: String!, $curveId: numeric!, $limit: Int!, $attestor: [String!]!) {
|
|
301
|
+
atom: atoms(where: {term_id: {_eq: $atomId}}, limit: 1) {
|
|
302
|
+
term_id
|
|
303
|
+
label
|
|
304
|
+
data
|
|
305
|
+
type
|
|
306
|
+
value { thing { url } }
|
|
307
|
+
term { ${VAULT_SIDE} }
|
|
308
|
+
}
|
|
309
|
+
triples(
|
|
310
|
+
where: {object_id: {_eq: $atomId}}
|
|
311
|
+
order_by: {term: {total_assets: desc}}
|
|
312
|
+
limit: $limit
|
|
313
|
+
) {
|
|
314
|
+
term_id
|
|
315
|
+
predicate { term_id label }
|
|
316
|
+
subject { term_id label type }
|
|
317
|
+
term { ${VAULT_SIDE} }
|
|
318
|
+
counter_term { ${VAULT_SIDE} }
|
|
319
|
+
}
|
|
320
|
+
claims: triples(
|
|
321
|
+
where: {subject_id: {_eq: $atomId}}
|
|
322
|
+
order_by: {term: {total_assets: desc}}
|
|
323
|
+
limit: $limit
|
|
324
|
+
) {
|
|
325
|
+
term_id
|
|
326
|
+
predicate { term_id label }
|
|
327
|
+
object { term_id label type }
|
|
328
|
+
term {
|
|
329
|
+
${VAULT_SIDE}
|
|
330
|
+
vaults(where: {curve_id: {_eq: $curveId}}, limit: 1) {
|
|
331
|
+
positions(where: {account_id: {_in: $attestor}, shares: {_gt: "0"}}, limit: 1) { shares }
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}`;
|
|
336
|
+
var LISTED_CAPABILITIES_FOR_QUERY = `query ListedCapabilitiesFor($subject: String!, $predicates: [String!]) {
|
|
337
|
+
atoms(where: {term_id: {_eq: $subject}}, limit: 1) {
|
|
338
|
+
capabilities: as_subject_triples(
|
|
339
|
+
where: {predicate_id: {_in: $predicates}}
|
|
340
|
+
limit: ${CAPABILITY_SAMPLE_LIMIT}
|
|
341
|
+
) { object { term_id } }
|
|
342
|
+
}
|
|
343
|
+
}`;
|
|
344
|
+
// ../../packages/graphql/dist/queries/tool-listing.js
|
|
345
|
+
function toolListingQuery(sponsored) {
|
|
346
|
+
const variables = sponsored ? "$uses: String!, $sameAs: String!, $curveId: numeric!, $limit: Int!, $sponsor: String!" : "$uses: String!, $sameAs: String!, $curveId: numeric!, $limit: Int!";
|
|
347
|
+
const sponsoredCount = sponsored ? `
|
|
348
|
+
sponsored: deposits_aggregate(where: {sender_id: {_ilike: $sponsor}, receiver_id: {_nilike: $sponsor}}) { aggregate { count } }` : "";
|
|
349
|
+
return `
|
|
350
|
+
query ToolListing(${variables}) {
|
|
351
|
+
triples(
|
|
352
|
+
where: {
|
|
353
|
+
predicate_id: {_eq: $uses}
|
|
354
|
+
subject: {as_subject_triples: {predicate_id: {_eq: $sameAs}}}
|
|
355
|
+
}
|
|
356
|
+
limit: $limit
|
|
357
|
+
) {
|
|
358
|
+
term_id
|
|
359
|
+
subject_id
|
|
360
|
+
object {
|
|
361
|
+
term_id
|
|
362
|
+
label
|
|
363
|
+
data
|
|
364
|
+
type
|
|
365
|
+
value { thing { url } }
|
|
366
|
+
}
|
|
367
|
+
term {
|
|
368
|
+
total_assets
|
|
369
|
+
vaults(where: {curve_id: {_eq: $curveId}}, limit: 1) { position_count total_assets }
|
|
370
|
+
deposits: deposits_aggregate { aggregate { count } }${sponsoredCount}
|
|
371
|
+
}
|
|
372
|
+
counter_term {
|
|
373
|
+
total_assets
|
|
374
|
+
vaults(where: {curve_id: {_eq: $curveId}}, limit: 1) { position_count total_assets }
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}`;
|
|
378
|
+
}
|
|
379
|
+
var TOOL_LISTING_QUERY = toolListingQuery(false);
|
|
380
|
+
var TOOL_LISTING_SPONSORED_QUERY = toolListingQuery(true);
|
|
381
|
+
// ../../packages/graphql/dist/queries/pulse-usage.js
|
|
382
|
+
var PULSE_USES_QUERY = `query PulseUses($tools: [String!]!, $uses: String!, $limit: Int!) {
|
|
383
|
+
triples(where: {object_id: {_in: $tools}, predicate_id: {_eq: $uses}}, limit: $limit) {
|
|
384
|
+
term_id
|
|
385
|
+
object_id
|
|
386
|
+
creator_id
|
|
387
|
+
created_at
|
|
388
|
+
subject {
|
|
389
|
+
term_id
|
|
390
|
+
label
|
|
391
|
+
}
|
|
392
|
+
positions(where: {shares: {_gt: "0"}}) {
|
|
393
|
+
account_id
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}`;
|
|
397
|
+
var PULSE_QUALIFIERS_QUERY = `query PulseQualifiers($edges: [String!]!, $predicates: [String!]!, $writers: [String!]!, $limit: Int!) {
|
|
398
|
+
triples(where: {subject_id: {_in: $edges}, predicate_id: {_in: $predicates}}, limit: $limit) {
|
|
399
|
+
term_id
|
|
400
|
+
subject_id
|
|
401
|
+
predicate_id
|
|
402
|
+
creator_id
|
|
403
|
+
created_at
|
|
404
|
+
object {
|
|
405
|
+
label
|
|
406
|
+
}
|
|
407
|
+
positions(where: {shares: {_gt: "0"}, account_id: {_in: $writers}}) {
|
|
408
|
+
account_id
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}`;
|
|
412
|
+
var PULSE_ATOMS_QUERY = `query PulseAtoms($uris: [String!]!, $limit: Int!) {
|
|
413
|
+
atoms(where: {data: {_in: $uris}}, limit: $limit) {
|
|
414
|
+
term_id
|
|
415
|
+
data
|
|
416
|
+
}
|
|
417
|
+
}`;
|
|
418
|
+
// ../../packages/graphql/dist/queries/tool-usage.js
|
|
419
|
+
var VAULT_SIDE2 = `
|
|
420
|
+
total_market_cap
|
|
421
|
+
total_assets
|
|
422
|
+
vaults(where: {curve_id: {_eq: $curveId}}, limit: 1) {
|
|
423
|
+
market_cap
|
|
424
|
+
total_assets
|
|
425
|
+
total_shares
|
|
426
|
+
current_share_price
|
|
427
|
+
position_count
|
|
428
|
+
}`;
|
|
429
|
+
var TOOL_USAGE_QUERY = `
|
|
430
|
+
query ToolUsage($toolId: String!, $uses: String!, $curveId: numeric!) {
|
|
431
|
+
tool: atoms(where: {term_id: {_eq: $toolId}}, limit: 1) {
|
|
432
|
+
term_id
|
|
433
|
+
label
|
|
434
|
+
data
|
|
435
|
+
}
|
|
436
|
+
triples(where: {predicate_id: {_eq: $uses}, object_id: {_eq: $toolId}}, limit: 200) {
|
|
437
|
+
term_id
|
|
438
|
+
subject {
|
|
439
|
+
term_id
|
|
440
|
+
label
|
|
441
|
+
type
|
|
442
|
+
}
|
|
443
|
+
term { ${VAULT_SIDE2} }
|
|
444
|
+
counter_term { ${VAULT_SIDE2} }
|
|
445
|
+
}
|
|
446
|
+
}`;
|
|
447
|
+
var ATTESTED_TOOL_USAGE_QUERY = `
|
|
448
|
+
query AttestedToolUsage($toolId: String!, $uses: String!, $curveId: numeric!, $attestor: String!) {
|
|
449
|
+
tool: atoms(where: {term_id: {_eq: $toolId}}, limit: 1) {
|
|
450
|
+
term_id
|
|
451
|
+
label
|
|
452
|
+
data
|
|
453
|
+
}
|
|
454
|
+
triples(where: {
|
|
455
|
+
predicate_id: {_eq: $uses},
|
|
456
|
+
object_id: {_eq: $toolId},
|
|
457
|
+
term: {positions: {account_id: {_eq: $attestor}}}
|
|
458
|
+
}, limit: 200) {
|
|
459
|
+
term_id
|
|
460
|
+
subject {
|
|
461
|
+
term_id
|
|
462
|
+
label
|
|
463
|
+
type
|
|
464
|
+
}
|
|
465
|
+
term { ${VAULT_SIDE2} }
|
|
466
|
+
counter_term { ${VAULT_SIDE2} }
|
|
467
|
+
}
|
|
468
|
+
}`;
|
|
469
|
+
// ../../packages/graphql/dist/queries/x402-feed.js
|
|
470
|
+
var LEVEL = `term_id
|
|
471
|
+
subject_id
|
|
472
|
+
predicate_id
|
|
473
|
+
creator_id
|
|
474
|
+
created_at
|
|
475
|
+
subject {
|
|
476
|
+
term_id
|
|
477
|
+
label
|
|
478
|
+
}
|
|
479
|
+
object {
|
|
480
|
+
term_id
|
|
481
|
+
label
|
|
482
|
+
data
|
|
483
|
+
value {
|
|
484
|
+
thing {
|
|
485
|
+
url
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
positions(where: {shares: {_gt: "0"}}) {
|
|
490
|
+
account_id
|
|
491
|
+
}`;
|
|
492
|
+
var X402_PAYMENT_LEVELS_QUERY = `query X402PaymentLevels($paidWith: String!, $limit: Int!, $offset: Int!) {
|
|
493
|
+
triples(where: {predicate_id: {_eq: $paidWith}}, order_by: {term_id: asc}, limit: $limit, offset: $offset) {
|
|
494
|
+
${LEVEL}
|
|
495
|
+
}
|
|
496
|
+
}`;
|
|
497
|
+
var X402_LEVELS_BY_ID_QUERY = `query X402LevelsById($ids: [String!]!, $limit: Int!) {
|
|
498
|
+
triples(where: {term_id: {_in: $ids}}, limit: $limit) {
|
|
499
|
+
${LEVEL}
|
|
500
|
+
}
|
|
501
|
+
}`;
|
|
502
|
+
var X402_REASONS_QUERY = `query X402Reasons($ids: [String!]!, $because: String!, $limit: Int!) {
|
|
503
|
+
triples(where: {subject_id: {_in: $ids}, predicate_id: {_eq: $because}}, order_by: {term_id: asc}, limit: $limit) {
|
|
504
|
+
${LEVEL}
|
|
505
|
+
}
|
|
506
|
+
}`;
|
|
20
507
|
// ../../packages/protocol/dist/declare/tool-thing.js
|
|
21
508
|
class InvalidToolUrlError extends Error {
|
|
22
509
|
input;
|
|
@@ -211,140 +698,6 @@ function readRequires(markdown) {
|
|
|
211
698
|
}
|
|
212
699
|
return { cli, runs, login: null, description };
|
|
213
700
|
}
|
|
214
|
-
// ../../packages/protocol/dist/skill-domains.js
|
|
215
|
-
var SKILL_DOMAINS = [
|
|
216
|
-
{
|
|
217
|
-
domain: "payment",
|
|
218
|
-
parent: "tool",
|
|
219
|
-
words: ["payment", "stripe", "paypal", "invoice", "checkout", "billing", "paywall"]
|
|
220
|
-
},
|
|
221
|
-
{
|
|
222
|
-
domain: "video",
|
|
223
|
-
parent: "tool",
|
|
224
|
-
words: ["video", "remotion", "hyperframes", "keyframe", "footage", "youtube"],
|
|
225
|
-
nameWords: ["render", "caption", "motion", "title", "carousel", "cursor", "frame", "overlay", "shard", "animation", "avatar", "clip"]
|
|
226
|
-
},
|
|
227
|
-
{
|
|
228
|
-
domain: "image",
|
|
229
|
-
parent: "tool",
|
|
230
|
-
words: ["image generation", "inpainting", "outpainting", "flux", "midjourney", "stable diffusion", "nano banana"],
|
|
231
|
-
nameWords: ["image", "photo", "relight", "upscale", "thumbnail", "imagegen"]
|
|
232
|
-
},
|
|
233
|
-
{
|
|
234
|
-
domain: "audio",
|
|
235
|
-
parent: "tool",
|
|
236
|
-
words: ["elevenlabs", "music generation", "text to speech", "voiceover", "sound effect", "instrumental"],
|
|
237
|
-
nameWords: ["audio", "music", "voice", "speech", "sound"]
|
|
238
|
-
},
|
|
239
|
-
{
|
|
240
|
-
domain: "document",
|
|
241
|
-
parent: "tool",
|
|
242
|
-
words: ["pptx", "docx", "xlsx", "powerpoint", "spreadsheet", ".potx", "pdf file"],
|
|
243
|
-
nameWords: ["pdf", "docx", "pptx", "xlsx", "document", "slide", "markdown"]
|
|
244
|
-
},
|
|
245
|
-
{
|
|
246
|
-
domain: "scraping",
|
|
247
|
-
parent: "tool",
|
|
248
|
-
words: ["scrap", "crawl", "firecrawl", "apify"],
|
|
249
|
-
nameWords: ["extract", "scrape"]
|
|
250
|
-
},
|
|
251
|
-
{
|
|
252
|
-
domain: "browser",
|
|
253
|
-
parent: "tool",
|
|
254
|
-
words: ["playwright", "puppeteer", "headless browser", "selenium"],
|
|
255
|
-
nameWords: ["browser", "chrome"]
|
|
256
|
-
},
|
|
257
|
-
{
|
|
258
|
-
domain: "database",
|
|
259
|
-
parent: "tool",
|
|
260
|
-
words: ["database", "postgres", "prisma", "supabase", "mongo", " sql"],
|
|
261
|
-
nameWords: ["schema", "query", "migration", "sql"]
|
|
262
|
-
},
|
|
263
|
-
{
|
|
264
|
-
domain: "design",
|
|
265
|
-
parent: "tool",
|
|
266
|
-
words: ["figma", "typograph", "design system", "design token"],
|
|
267
|
-
nameWords: ["design", "ux", "colour", "color", "layout", "logo", "icon", "theme"]
|
|
268
|
-
},
|
|
269
|
-
{
|
|
270
|
-
domain: "collaboration",
|
|
271
|
-
parent: "tool",
|
|
272
|
-
words: ["slack", "notion", "jira", "linear", "atlassian", "monday", "飞书", "obsidian"],
|
|
273
|
-
nameWords: ["lark", "calendar", "whiteboard", "meeting"]
|
|
274
|
-
},
|
|
275
|
-
{
|
|
276
|
-
domain: "marketing",
|
|
277
|
-
parent: "tool",
|
|
278
|
-
words: ["marketing", "seo", "campaign", "copywriting", "newsletter", "onboarding", "referral", "product hunt"],
|
|
279
|
-
nameWords: ["cro", "launch", "photography", "signup", "popup", "ads", "audience", "growth", "brand"]
|
|
280
|
-
},
|
|
281
|
-
{
|
|
282
|
-
domain: "blockchain",
|
|
283
|
-
parent: "tool",
|
|
284
|
-
words: ["blockchain", "onchain", "on-chain", "ethereum", "solidity", "web3", "smart contract", "erc-20", "erc-721"],
|
|
285
|
-
nameWords: ["wallet", "crypto", "nft", "defi"]
|
|
286
|
-
},
|
|
287
|
-
{
|
|
288
|
-
domain: "infrastructure",
|
|
289
|
-
parent: "tool",
|
|
290
|
-
words: ["deploy", "docker", "kubernetes", "terraform", "cloudflare", "aws", "azure", "serverless", "ci/cd", "firebase", "hosting", "vercel"],
|
|
291
|
-
nameWords: ["infra", "cli"]
|
|
292
|
-
},
|
|
293
|
-
{
|
|
294
|
-
domain: "observability",
|
|
295
|
-
parent: "tool",
|
|
296
|
-
words: ["observab", "logging", "grafana", "sentry", "telemetry"],
|
|
297
|
-
nameWords: ["monitor", "metric", "trace", "alert", "log"]
|
|
298
|
-
},
|
|
299
|
-
{
|
|
300
|
-
domain: "quality",
|
|
301
|
-
parent: "tool",
|
|
302
|
-
words: ["linting", "unit test", "code coverage", "static analysis"],
|
|
303
|
-
nameWords: ["test", "lint", "review", "debug", "audit", "coverage", "quality", "refactor", "eval"]
|
|
304
|
-
},
|
|
305
|
-
{
|
|
306
|
-
domain: "writing",
|
|
307
|
-
parent: "tool",
|
|
308
|
-
words: ["cold email", "pr body", "commit message", "conventional commits"],
|
|
309
|
-
nameWords: ["writing", "write", "article", "prose", "caveman", "cave", "edit"]
|
|
310
|
-
},
|
|
311
|
-
{
|
|
312
|
-
domain: "method",
|
|
313
|
-
parent: "tool",
|
|
314
|
-
words: ["handoff", "hand the current", "implementation plan", "subagent", "superpowers", "overbuilding", "chief of staff", "retrospective", "acceptance condition", "router over", "re-pitch"],
|
|
315
|
-
nameWords: ["handoff", "triage", "retro", "brainstorm", "spec", "prd", "questionnaire", "plan", "implement", "dispatch", "execute", "executing", "investigate", "wayfinder", "decision", "teach", "verify", "verification", "authoring", "template", "zoom", "wait", "grill", "ask", "manage", "optimize", "worktree", "lean", "sensei", "help", "ticket", "issue"]
|
|
316
|
-
},
|
|
317
|
-
{
|
|
318
|
-
domain: "website",
|
|
319
|
-
parent: "tool",
|
|
320
|
-
words: ["landing page", "static site", "next.js", "nextjs", "tailwind"],
|
|
321
|
-
nameWords: ["website", "html", "css", "landing", "frontend", "react"]
|
|
322
|
-
},
|
|
323
|
-
{
|
|
324
|
-
domain: "api",
|
|
325
|
-
parent: "tool",
|
|
326
|
-
words: ["rest api", "graphql", "webhook", "openapi", "postman"],
|
|
327
|
-
nameWords: ["api", "endpoint", "rest"]
|
|
328
|
-
},
|
|
329
|
-
{
|
|
330
|
-
domain: "automation",
|
|
331
|
-
parent: "tool",
|
|
332
|
-
words: ["zapier", "cron", "orchestrat"],
|
|
333
|
-
nameWords: ["automation", "workflow", "schedule", "pipeline"]
|
|
334
|
-
},
|
|
335
|
-
{
|
|
336
|
-
domain: "development",
|
|
337
|
-
parent: "tool",
|
|
338
|
-
words: ["typescript", "python", "rust ", "sdk"],
|
|
339
|
-
nameWords: ["code", "compile", "library", "framework", "git"]
|
|
340
|
-
}
|
|
341
|
-
];
|
|
342
|
-
function classifySkill(skill, description) {
|
|
343
|
-
const name = ` ${skill.replace(/-/g, " ").toLowerCase()} `;
|
|
344
|
-
const both = `${name}${description.toLowerCase()}`;
|
|
345
|
-
const hit = SKILL_DOMAINS.find((rule) => rule.words.some((word) => both.includes(word)) || (rule.nameWords ?? []).some((word) => name.includes(` ${word}`)));
|
|
346
|
-
return hit?.domain ?? null;
|
|
347
|
-
}
|
|
348
701
|
// ../../packages/protocol/dist/content-path.js
|
|
349
702
|
function skillRefFromUrl(url) {
|
|
350
703
|
let parsed;
|
|
@@ -395,12 +748,6 @@ function installedFromReceipts(text) {
|
|
|
395
748
|
}
|
|
396
749
|
return found;
|
|
397
750
|
}
|
|
398
|
-
// ../../packages/protocol/dist/pulse-score.js
|
|
399
|
-
function inferDomain(query, labels) {
|
|
400
|
-
const words = ` ${query.toLowerCase().replace(/[^a-z0-9-]+/g, " ").trim()} `;
|
|
401
|
-
const matches = labels.filter((label) => words.includes(` ${label.toLowerCase()} `));
|
|
402
|
-
return [...matches].sort((a, b) => b.length - a.length)[0] ?? null;
|
|
403
|
-
}
|
|
404
751
|
// ../hook/src/classify.ts
|
|
405
752
|
var CANCELLED = /\b(interrupted|cancel(l)?ed|aborted) by (the )?user\b|\buser (rejected|denied|declined)\b|\bpermission denied by user\b|\buser doesn't want to (proceed|take this action)\b/i;
|
|
406
753
|
var AUTH = /\b(unauthori[sz]ed|forbidden|invalid (api )?(key|token)|authentication|not authenticated|expired token)\b/i;
|
|
@@ -408,7 +755,7 @@ var TIMEOUT = /\b(timed? ?out|timeout|deadline exceeded|ETIMEDOUT)\b/i;
|
|
|
408
755
|
var UNREACHABLE = /\b(ENOTFOUND|EAI_AGAIN|ECONNREFUSED|ECONNRESET|EHOSTUNREACH|ENETUNREACH|getaddrinfo|certificate|TLS|SSL|socket hang up|connection refused|fetch failed)\b/i;
|
|
409
756
|
var PAYMENT = /\b(payment required|insufficient (funds|balance)|payment (failed|refused|rejected)|x402)\b/i;
|
|
410
757
|
var STATUS = /\b(40[123]|429|5\d\d)\b/;
|
|
411
|
-
function
|
|
758
|
+
function isRecord2(value) {
|
|
412
759
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
413
760
|
}
|
|
414
761
|
function failureText(call) {
|
|
@@ -416,7 +763,7 @@ function failureText(call) {
|
|
|
416
763
|
const push = (value) => {
|
|
417
764
|
if (typeof value === "string")
|
|
418
765
|
parts.push(value);
|
|
419
|
-
else if (
|
|
766
|
+
else if (isRecord2(value)) {
|
|
420
767
|
for (const key of ["error", "message", "reason"]) {
|
|
421
768
|
const text = value[key];
|
|
422
769
|
if (typeof text === "string")
|
|
@@ -424,7 +771,7 @@ function failureText(call) {
|
|
|
424
771
|
}
|
|
425
772
|
if (Array.isArray(value.content)) {
|
|
426
773
|
for (const item of value.content) {
|
|
427
|
-
if (
|
|
774
|
+
if (isRecord2(item) && typeof item.text === "string")
|
|
428
775
|
parts.push(item.text);
|
|
429
776
|
}
|
|
430
777
|
}
|
|
@@ -436,7 +783,7 @@ function failureText(call) {
|
|
|
436
783
|
`).slice(0, 4000);
|
|
437
784
|
}
|
|
438
785
|
function statusOf(call) {
|
|
439
|
-
if (!
|
|
786
|
+
if (!isRecord2(call.response))
|
|
440
787
|
return null;
|
|
441
788
|
for (const key of ["status", "statusCode", "status_code"]) {
|
|
442
789
|
const value = call.response[key];
|
|
@@ -446,7 +793,7 @@ function statusOf(call) {
|
|
|
446
793
|
return null;
|
|
447
794
|
}
|
|
448
795
|
function flaggedError(response) {
|
|
449
|
-
return
|
|
796
|
+
return isRecord2(response) && (response.isError === true || response.is_error === true);
|
|
450
797
|
}
|
|
451
798
|
function classifyCall(call) {
|
|
452
799
|
if (call.interrupted)
|
|
@@ -476,7 +823,7 @@ function classifyCall(call) {
|
|
|
476
823
|
import { readFileSync } from "node:fs";
|
|
477
824
|
import { homedir } from "node:os";
|
|
478
825
|
import { join } from "node:path";
|
|
479
|
-
function
|
|
826
|
+
function isRecord3(value) {
|
|
480
827
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
481
828
|
}
|
|
482
829
|
function readJson(path) {
|
|
@@ -487,7 +834,7 @@ function readJson(path) {
|
|
|
487
834
|
}
|
|
488
835
|
}
|
|
489
836
|
function entryOf(raw) {
|
|
490
|
-
if (!
|
|
837
|
+
if (!isRecord3(raw))
|
|
491
838
|
return null;
|
|
492
839
|
if (typeof raw.url === "string" && raw.url.length > 0)
|
|
493
840
|
return { transport: "remote", url: raw.url };
|
|
@@ -498,7 +845,7 @@ function entryOf(raw) {
|
|
|
498
845
|
return null;
|
|
499
846
|
}
|
|
500
847
|
function collect(into, servers) {
|
|
501
|
-
if (!
|
|
848
|
+
if (!isRecord3(servers))
|
|
502
849
|
return;
|
|
503
850
|
for (const [name, raw] of Object.entries(servers)) {
|
|
504
851
|
const entry = entryOf(raw);
|
|
@@ -512,16 +859,16 @@ function readMcpServers({
|
|
|
512
859
|
}) {
|
|
513
860
|
const servers = new Map;
|
|
514
861
|
const user = readJson(join(home, ".claude.json"));
|
|
515
|
-
if (
|
|
862
|
+
if (isRecord3(user)) {
|
|
516
863
|
collect(servers, user.mcpServers);
|
|
517
|
-
if (
|
|
864
|
+
if (isRecord3(user.projects)) {
|
|
518
865
|
const project = user.projects[cwd];
|
|
519
|
-
if (
|
|
866
|
+
if (isRecord3(project))
|
|
520
867
|
collect(servers, project.mcpServers);
|
|
521
868
|
}
|
|
522
869
|
}
|
|
523
870
|
const shared = readJson(join(cwd, ".mcp.json"));
|
|
524
|
-
if (
|
|
871
|
+
if (isRecord3(shared))
|
|
525
872
|
collect(servers, shared.mcpServers);
|
|
526
873
|
return servers;
|
|
527
874
|
}
|
|
@@ -644,7 +991,7 @@ function capture(payload, dir, now = new Date) {
|
|
|
644
991
|
}
|
|
645
992
|
|
|
646
993
|
// ../hook/src/payload.ts
|
|
647
|
-
function
|
|
994
|
+
function isRecord4(value) {
|
|
648
995
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
649
996
|
}
|
|
650
997
|
function str(value) {
|
|
@@ -659,8 +1006,13 @@ function first(raw, keys) {
|
|
|
659
1006
|
return raw[key];
|
|
660
1007
|
return;
|
|
661
1008
|
}
|
|
1009
|
+
function eventOf(raw) {
|
|
1010
|
+
if (!isRecord4(raw))
|
|
1011
|
+
return null;
|
|
1012
|
+
return str(first(raw, ["hook_event_name", "hookEventName"]));
|
|
1013
|
+
}
|
|
662
1014
|
function toolCallOf(raw) {
|
|
663
|
-
if (!
|
|
1015
|
+
if (!isRecord4(raw))
|
|
664
1016
|
return null;
|
|
665
1017
|
const format = typeof raw.toolName === "string" && typeof raw.tool_name !== "string" ? "grok" : "claude";
|
|
666
1018
|
const camel = format === "grok";
|
|
@@ -676,7 +1028,7 @@ function toolCallOf(raw) {
|
|
|
676
1028
|
session: str(camel ? raw.sessionId : raw.session_id),
|
|
677
1029
|
id: str(camel ? raw.toolUseId : raw.tool_use_id),
|
|
678
1030
|
tool,
|
|
679
|
-
input:
|
|
1031
|
+
input: isRecord4(input) ? input : undefined,
|
|
680
1032
|
hasResponse,
|
|
681
1033
|
response: hasResponse ? first(raw, responseKeys) : undefined,
|
|
682
1034
|
error: str(raw.error),
|
|
@@ -687,60 +1039,2788 @@ function toolCallOf(raw) {
|
|
|
687
1039
|
};
|
|
688
1040
|
}
|
|
689
1041
|
|
|
690
|
-
//
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
if (
|
|
702
|
-
return
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
1042
|
+
// ../../packages/protocol/src/content-path.ts
|
|
1043
|
+
function skillRefFromUrl3(url) {
|
|
1044
|
+
let parsed;
|
|
1045
|
+
try {
|
|
1046
|
+
parsed = new URL(url.includes("://") ? url : `https://${url}`);
|
|
1047
|
+
} catch {
|
|
1048
|
+
return null;
|
|
1049
|
+
}
|
|
1050
|
+
if (parsed.hostname.replace(/^www\./, "") !== "github.com")
|
|
1051
|
+
return null;
|
|
1052
|
+
const skill = parsed.searchParams.get("skill");
|
|
1053
|
+
if (skill === null || skill.length === 0)
|
|
1054
|
+
return null;
|
|
1055
|
+
const [owner, repo] = parsed.pathname.replace(/^\/+/, "").replace(/\/+$/, "").replace(/\.git$/, "").split("/");
|
|
1056
|
+
if (owner === undefined || repo === undefined)
|
|
1057
|
+
return null;
|
|
1058
|
+
if (owner.length === 0 || repo.length === 0)
|
|
1059
|
+
return null;
|
|
1060
|
+
return { repo: `${owner}/${repo}`, skill };
|
|
706
1061
|
}
|
|
707
|
-
|
|
708
|
-
|
|
1062
|
+
function directoryOf2(path) {
|
|
1063
|
+
const parts = path.split("/");
|
|
1064
|
+
return parts.length < 2 ? "" : parts[parts.length - 2] ?? "";
|
|
1065
|
+
}
|
|
1066
|
+
function skillFilesIn2(listing) {
|
|
1067
|
+
return listing.filter((file) => (file.path.split("/").pop() ?? "").toLowerCase() === "skill.md");
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
// ../../packages/protocol/src/skill-requires.ts
|
|
1071
|
+
var LOGIN2 = /\b(auth login|log ?in|sign ?up|api[ _-]?key|access token)\b/i;
|
|
1072
|
+
function frontmatter2(markdown) {
|
|
1073
|
+
const lines = markdown.split(`
|
|
1074
|
+
`);
|
|
1075
|
+
if (lines[0]?.trim() !== "---")
|
|
1076
|
+
return { lines: [], bodyStart: 0 };
|
|
1077
|
+
const end = lines.findIndex((line, index) => index > 0 && line.trim() === "---");
|
|
1078
|
+
return end === -1 ? { lines: [], bodyStart: 0 } : { lines: lines.slice(1, end), bodyStart: end + 1 };
|
|
1079
|
+
}
|
|
1080
|
+
function unquote2(value) {
|
|
1081
|
+
return value.trim().replace(/^["']|["']$/g, "");
|
|
1082
|
+
}
|
|
1083
|
+
function declaredBins2(lines) {
|
|
1084
|
+
const at = lines.findIndex((line) => /^\s*bins\s*:/.test(line));
|
|
1085
|
+
if (at === -1)
|
|
1086
|
+
return [];
|
|
1087
|
+
const inline = lines[at]?.split(":").slice(1).join(":").trim() ?? "";
|
|
1088
|
+
if (inline.startsWith("[")) {
|
|
1089
|
+
return inline.replace(/^\[|\]$/g, "").split(",").map(unquote2).filter((name) => name.length > 0);
|
|
1090
|
+
}
|
|
1091
|
+
const out = [];
|
|
1092
|
+
for (const line of lines.slice(at + 1)) {
|
|
1093
|
+
const item = /^\s*-\s*(.+)$/.exec(line);
|
|
1094
|
+
if (item === null)
|
|
1095
|
+
break;
|
|
1096
|
+
out.push(unquote2(item[1] ?? ""));
|
|
1097
|
+
}
|
|
1098
|
+
return out.filter((name) => name.length > 0);
|
|
1099
|
+
}
|
|
1100
|
+
function bashTools2(lines) {
|
|
1101
|
+
const line = lines.find((candidate) => /^\s*allowed-tools\s*:/.test(candidate));
|
|
1102
|
+
if (line === undefined)
|
|
1103
|
+
return [];
|
|
1104
|
+
return [...line.matchAll(/Bash\(\s*([A-Za-z0-9._-]+)/g)].map((match) => match[1] ?? "").filter((name) => name.length > 0);
|
|
1105
|
+
}
|
|
1106
|
+
function declaredDescription2(lines) {
|
|
1107
|
+
const at = lines.findIndex((line) => /^description\s*:/.test(line));
|
|
1108
|
+
if (at === -1)
|
|
1109
|
+
return null;
|
|
1110
|
+
const inline = lines[at]?.split(":").slice(1).join(":").trim() ?? "";
|
|
1111
|
+
if (inline !== "" && !/^[>|][-+]?$/.test(inline))
|
|
1112
|
+
return unquote2(inline) || null;
|
|
1113
|
+
const block = [];
|
|
1114
|
+
for (const line of lines.slice(at + 1)) {
|
|
1115
|
+
if (!/^\s+\S/.test(line))
|
|
1116
|
+
break;
|
|
1117
|
+
block.push(line.trim());
|
|
1118
|
+
}
|
|
1119
|
+
return block.length === 0 ? null : block.join(" ");
|
|
1120
|
+
}
|
|
1121
|
+
var COMMON2 = new Set(("awk bash break bun case cat cd chmod command continue cp curl cut date diff do done echo else end env " + "esac exit export false fi find for function git grep head if kill ln local ls mkdir mv node npm npx open pip pip3 pnpm printf pwd " + "python python3 read return rm sed set sh shift sleep sort source sudo tail tar tee test then time " + "touch tr true uniq unset unzip wait wc wget which while xargs yarn zip").split(" "));
|
|
1122
|
+
var INSTALL2 = /\b(?:npm\s+(?:i|install)\s+(?:-g|--global)|pnpm\s+add\s+-g|yarn\s+global\s+add|brew\s+install|pipx\s+install|pip3?\s+install|cargo\s+install)\s+([@a-z0-9][@\w./-]*)/i;
|
|
1123
|
+
var PROBE2 = /\b(?:command\s+-v|which)\s+([a-z][\w-]*)/i;
|
|
1124
|
+
var COMMAND2 = /^[a-z][a-z0-9_-]*$/;
|
|
1125
|
+
function commandsIn2(shell, explicitOnly) {
|
|
709
1126
|
const found = [];
|
|
710
|
-
for (const
|
|
711
|
-
const
|
|
712
|
-
|
|
713
|
-
if (first2 === undefined || first2 === "")
|
|
1127
|
+
for (const segment of shell.split(/&&|\|\||[|;]/)) {
|
|
1128
|
+
const line = segment.trim().replace(/^\$\s+/, "");
|
|
1129
|
+
if (line === "" || line.startsWith("#"))
|
|
714
1130
|
continue;
|
|
715
|
-
const
|
|
716
|
-
if (
|
|
717
|
-
found.push(
|
|
1131
|
+
const named = INSTALL2.exec(line)?.[1] ?? PROBE2.exec(line)?.[1];
|
|
1132
|
+
if (named !== undefined) {
|
|
1133
|
+
found.push(named);
|
|
718
1134
|
continue;
|
|
719
1135
|
}
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
1136
|
+
if (explicitOnly)
|
|
1137
|
+
continue;
|
|
1138
|
+
const words = line.split(/\s+/).filter((word) => !/^[A-Za-z_][A-Za-z0-9_]*=/.test(word));
|
|
1139
|
+
const first2 = words[0] === "sudo" ? words[1] : words[0];
|
|
1140
|
+
if (first2 === undefined || !COMMAND2.test(first2))
|
|
1141
|
+
continue;
|
|
1142
|
+
found.push(first2);
|
|
723
1143
|
}
|
|
724
1144
|
return found;
|
|
725
1145
|
}
|
|
726
|
-
var
|
|
727
|
-
function
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
function lastUserWords(transcript) {
|
|
737
|
-
let words = "";
|
|
738
|
-
for (const line of transcript.split(`
|
|
739
|
-
`)) {
|
|
740
|
-
if (!line.includes('"user"'))
|
|
1146
|
+
var SHELL_FENCE2 = /^(sh|bash|shell|zsh|console|terminal)$/i;
|
|
1147
|
+
function shellLinesRun2(lines) {
|
|
1148
|
+
const run = [];
|
|
1149
|
+
let heredocEnd = null;
|
|
1150
|
+
let quote = null;
|
|
1151
|
+
let continued = false;
|
|
1152
|
+
for (const line of lines) {
|
|
1153
|
+
if (heredocEnd !== null) {
|
|
1154
|
+
if (line.trim() === heredocEnd)
|
|
1155
|
+
heredocEnd = null;
|
|
741
1156
|
continue;
|
|
742
|
-
|
|
743
|
-
|
|
1157
|
+
}
|
|
1158
|
+
if (quote === null && !continued)
|
|
1159
|
+
run.push(line);
|
|
1160
|
+
continued = line.trimEnd().endsWith("\\");
|
|
1161
|
+
heredocEnd = /<<-?\s*['"]?([A-Za-z_]+)/.exec(line)?.[1] ?? null;
|
|
1162
|
+
for (const char of line) {
|
|
1163
|
+
if (quote === null && (char === '"' || char === "'"))
|
|
1164
|
+
quote = char;
|
|
1165
|
+
else if (char === quote)
|
|
1166
|
+
quote = null;
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
return run;
|
|
1170
|
+
}
|
|
1171
|
+
function commandsRun2(body, declared) {
|
|
1172
|
+
const found = [];
|
|
1173
|
+
let fence = null;
|
|
1174
|
+
let fenced = [];
|
|
1175
|
+
for (const line of body) {
|
|
1176
|
+
const marker = /^\s*(?:```|~~~)\s*([\w-]*)/.exec(line);
|
|
1177
|
+
if (marker !== null) {
|
|
1178
|
+
if (fence === "shell") {
|
|
1179
|
+
for (const run of shellLinesRun2(fenced))
|
|
1180
|
+
found.push(...commandsIn2(run, false));
|
|
1181
|
+
}
|
|
1182
|
+
fenced = [];
|
|
1183
|
+
fence = fence !== null ? null : SHELL_FENCE2.test(marker[1] ?? "") ? "shell" : "other";
|
|
1184
|
+
continue;
|
|
1185
|
+
}
|
|
1186
|
+
if (fence !== null) {
|
|
1187
|
+
fenced.push(line);
|
|
1188
|
+
continue;
|
|
1189
|
+
}
|
|
1190
|
+
for (const inline of line.matchAll(/`([^`]+)`/g))
|
|
1191
|
+
found.push(...commandsIn2(inline[1] ?? "", true));
|
|
1192
|
+
}
|
|
1193
|
+
return [...new Set(found)].filter((name) => !COMMON2.has(name) && !declared.includes(name));
|
|
1194
|
+
}
|
|
1195
|
+
function requiresLine2(lines) {
|
|
1196
|
+
const name = lines.map((line) => /^\s*requires\s*:\s*(\S+)\s*$/.exec(line)?.[1]).find(Boolean);
|
|
1197
|
+
return name === undefined ? [] : [unquote2(name)];
|
|
1198
|
+
}
|
|
1199
|
+
function readRequires2(markdown) {
|
|
1200
|
+
const { lines, bodyStart } = frontmatter2(markdown);
|
|
1201
|
+
const cli = [...new Set([...declaredBins2(lines), ...bashTools2(lines), ...requiresLine2(lines)])];
|
|
1202
|
+
const description = declaredDescription2(lines);
|
|
1203
|
+
const body = markdown.split(`
|
|
1204
|
+
`);
|
|
1205
|
+
const runs = commandsRun2(body.slice(bodyStart), cli);
|
|
1206
|
+
for (let index = bodyStart;index < body.length; index += 1) {
|
|
1207
|
+
const text = body[index] ?? "";
|
|
1208
|
+
if (LOGIN2.test(text)) {
|
|
1209
|
+
return { cli, runs, login: { line: index + 1, text: text.trim().slice(0, 160) }, description };
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
return { cli, runs, login: null, description };
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
// ../../packages/protocol/src/skill-contents.ts
|
|
1216
|
+
var CONTENTS_LIMITS2 = {
|
|
1217
|
+
files: 200,
|
|
1218
|
+
fileChars: 512 * 1024,
|
|
1219
|
+
items: 40,
|
|
1220
|
+
lineChars: 160
|
|
1221
|
+
};
|
|
1222
|
+
var SCRIPT = /\.(sh|bash|zsh|py|js|mjs|cjs|ts|rb|pl|ps1|php|lua)$/i;
|
|
1223
|
+
var MARKDOWN = /\.(md|mdx|markdown)$/i;
|
|
1224
|
+
var FENCE = /^\s*(?:```|~~~)/;
|
|
1225
|
+
function cap(text) {
|
|
1226
|
+
const flat = text.replace(/\s+/g, " ").trim();
|
|
1227
|
+
return flat.length > CONTENTS_LIMITS2.lineChars ? `${flat.slice(0, CONTENTS_LIMITS2.lineChars - 1)}…` : flat;
|
|
1228
|
+
}
|
|
1229
|
+
function compare(a, b) {
|
|
1230
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
1231
|
+
}
|
|
1232
|
+
var BASE_SHELLS = new Set(["sh", "bash", "zsh", "dash"]);
|
|
1233
|
+
function interpreterOf(firstLine) {
|
|
1234
|
+
const match = /^#!\s*(\S+)(?:\s+(?:-\S+\s+)*(\S+))?/.exec(firstLine);
|
|
1235
|
+
if (match === null)
|
|
1236
|
+
return null;
|
|
1237
|
+
const binary = match[1]?.split("/").pop() ?? "";
|
|
1238
|
+
const name = binary === "env" ? match[2] ?? "" : binary;
|
|
1239
|
+
return /^[a-z][\w.-]*$/i.test(name) && !BASE_SHELLS.has(name) ? name : null;
|
|
1240
|
+
}
|
|
1241
|
+
var SCRIPT_PROBE = /(?:^|[;&|(!{]|\bif|\bthen|\$\()\s*(?:!\s*)?(?:command\s+-v|which|type\s+-p|hash)\s+([a-z][\w.+-]*)/g;
|
|
1242
|
+
var SYSTEM_INSTALL = /\b(?:brew|apt-get|apt|dnf|yum|apk|pacman|choco|winget|scoop|port)\s+(?:install|add|-S)\s+(?:-\S+\s+)*([a-z][\w.+-]*)/g;
|
|
1243
|
+
var COMMENT_LINE = /^\s*(?:#|\/\/|\*|--|;|rem\s)/i;
|
|
1244
|
+
var PYTHON_PROBE = /\bshutil\.which\(\s*["']([a-z][\w.+-]*)["']/g;
|
|
1245
|
+
function programsInScriptLine(line, shell) {
|
|
1246
|
+
if (COMMENT_LINE.test(line))
|
|
1247
|
+
return [];
|
|
1248
|
+
const matches = shell ? [...line.matchAll(SCRIPT_PROBE), ...line.matchAll(SYSTEM_INSTALL)] : [...line.matchAll(PYTHON_PROBE)];
|
|
1249
|
+
return matches.map((match) => match[1] ?? "").filter((name) => name !== "");
|
|
1250
|
+
}
|
|
1251
|
+
var PACKAGE_INSTALL = /\b(pip3?|pipx|uv\s+pip|uv\s+add|poetry\s+add|npm|pnpm|yarn|bun|gem|cargo)\s+(?:install|add|i)\b([^\n|;&#`)]*)/gi;
|
|
1252
|
+
function packagesIn(line) {
|
|
1253
|
+
const out = [];
|
|
1254
|
+
for (const match of line.matchAll(PACKAGE_INSTALL)) {
|
|
1255
|
+
const manager = (match[1] ?? "").replace(/\s+/g, " ").toLowerCase();
|
|
1256
|
+
for (const raw of (match[2] ?? "").trim().split(/\s+/)) {
|
|
1257
|
+
const word = raw.replace(/^["']|["']$/g, "");
|
|
1258
|
+
if (word === "" || word.startsWith("-") || word.startsWith(".") || /[:\\<>{}$]/.test(word))
|
|
1259
|
+
continue;
|
|
1260
|
+
if (/\.(txt|toml|json|cfg|lock|whl|tgz|tar\.gz|zip)$/i.test(word))
|
|
1261
|
+
continue;
|
|
1262
|
+
const name = word.replace(/[=<>!~[].*$/, "");
|
|
1263
|
+
if (/^(@[a-z0-9][\w.-]*\/)?[a-z0-9][\w.-]*(@[\w.^~-]+)?$/i.test(name))
|
|
1264
|
+
out.push({ manager, name });
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
return out;
|
|
1268
|
+
}
|
|
1269
|
+
function requirementsIn(text) {
|
|
1270
|
+
return text.split(`
|
|
1271
|
+
`).map((line) => line.replace(/#.*/, "").trim()).filter((line) => line !== "" && !line.startsWith("-") && !/^(https?|git\+)/.test(line)).map((line) => /^[A-Za-z0-9][\w.-]*/.exec(line)?.[0] ?? "").filter((name) => name !== "");
|
|
1272
|
+
}
|
|
1273
|
+
var SHELL_VARIABLES = new Set(("HOME PATH PWD OLDPWD USER LOGNAME SHELL TERM LANG LC_ALL TMPDIR TMP TEMP HOSTNAME IFS PS1 PS2 " + "RANDOM SECONDS LINENO BASH_SOURCE BASH_VERSION FUNCNAME PIPESTATUS OPTARG OPTIND EUID UID PPID " + "REPLY EDITOR VISUAL PAGER DISPLAY XDG_CONFIG_HOME XDG_DATA_HOME XDG_CACHE_HOME CI NODE_ENV DEBUG").split(" "));
|
|
1274
|
+
var READS_IN_CODE = [
|
|
1275
|
+
/\bprocess\.env\.([A-Z][A-Z0-9_]+)/g,
|
|
1276
|
+
/\bprocess\.env\[\s*["']([A-Z][A-Z0-9_]+)["']\s*\]/g,
|
|
1277
|
+
/\bBun\.env\.([A-Z][A-Z0-9_]+)/g,
|
|
1278
|
+
/\bDeno\.env\.get\(\s*["']([A-Z][A-Z0-9_]+)["']/g,
|
|
1279
|
+
/\bimport\.meta\.env\.([A-Z][A-Z0-9_]+)/g,
|
|
1280
|
+
/\bos\.environ\[\s*["']([A-Z][A-Z0-9_]+)["']\s*\]/g,
|
|
1281
|
+
/\b(?:os\.environ\.get|os\.getenv|getenv|environ\.get)\(\s*["']([A-Z][A-Z0-9_]+)["']/g,
|
|
1282
|
+
/\bENV(?:\.fetch\(|\[)\s*["']([A-Z][A-Z0-9_]+)["']/g,
|
|
1283
|
+
/\$env:([A-Za-z][A-Za-z0-9_]+)/g
|
|
1284
|
+
];
|
|
1285
|
+
var SHELL_READ = /\$\{?([A-Z][A-Z0-9_]{2,})\b/g;
|
|
1286
|
+
var SHELL_ASSIGN = /^\s*(?:export\s+|local\s+|readonly\s+|declare\s+(?:-\w+\s+)?)?([A-Z][A-Z0-9_]*)=/;
|
|
1287
|
+
var SHELL_LOOP = /\b(?:for|read(?:\s+-\w+)*)\s+([A-Z][A-Z0-9_]*)\b/g;
|
|
1288
|
+
var EXPORT_IN_PROSE = /\bexport\s+([A-Z][A-Z0-9_]{2,})=/g;
|
|
1289
|
+
var KEY_NAMED = /\b([A-Z][A-Z0-9]*(?:_[A-Z0-9]+)*_(?:API_KEY|APIKEY|KEY|TOKEN|SECRET|PASSWORD))\b/g;
|
|
1290
|
+
function shellReads(lines) {
|
|
1291
|
+
const assigned = new Set;
|
|
1292
|
+
for (const line of lines) {
|
|
1293
|
+
const name = SHELL_ASSIGN.exec(line)?.[1];
|
|
1294
|
+
if (name !== undefined)
|
|
1295
|
+
assigned.add(name);
|
|
1296
|
+
for (const loop of line.matchAll(SHELL_LOOP))
|
|
1297
|
+
assigned.add(loop[1] ?? "");
|
|
1298
|
+
}
|
|
1299
|
+
return lines.flatMap((line) => [...line.matchAll(SHELL_READ)].map((match) => match[1] ?? "")).filter((name) => !assigned.has(name));
|
|
1300
|
+
}
|
|
1301
|
+
function isShell(path, text) {
|
|
1302
|
+
return /\.(sh|bash|zsh)$/i.test(path) || /^#!\s*\S*\/(?:env\s+)?(?:ba|z|da)?sh\b/.test(text);
|
|
1303
|
+
}
|
|
1304
|
+
function settingsIn(path, text, code) {
|
|
1305
|
+
const found = new Set;
|
|
1306
|
+
for (const pattern of READS_IN_CODE) {
|
|
1307
|
+
for (const match of text.matchAll(pattern))
|
|
1308
|
+
found.add((match[1] ?? "").toUpperCase());
|
|
1309
|
+
}
|
|
1310
|
+
if (MARKDOWN.test(path)) {
|
|
1311
|
+
for (const match of text.matchAll(EXPORT_IN_PROSE))
|
|
1312
|
+
found.add(match[1] ?? "");
|
|
1313
|
+
for (const match of text.matchAll(KEY_NAMED))
|
|
1314
|
+
found.add(match[1] ?? "");
|
|
1315
|
+
} else if (isShell(path, text)) {
|
|
1316
|
+
for (const name of shellReads(code))
|
|
1317
|
+
found.add(name);
|
|
1318
|
+
}
|
|
1319
|
+
return [...found].filter((name) => name.length > 0 && !SHELL_VARIABLES.has(name));
|
|
1320
|
+
}
|
|
1321
|
+
function codeOf(path, text, isScript) {
|
|
1322
|
+
const lines = text.split(`
|
|
1323
|
+
`);
|
|
1324
|
+
if (isScript)
|
|
1325
|
+
return lines;
|
|
1326
|
+
if (!MARKDOWN.test(path))
|
|
1327
|
+
return [];
|
|
1328
|
+
const out = [];
|
|
1329
|
+
let fenced = false;
|
|
1330
|
+
for (const line of lines) {
|
|
1331
|
+
if (FENCE.test(line)) {
|
|
1332
|
+
fenced = !fenced;
|
|
1333
|
+
continue;
|
|
1334
|
+
}
|
|
1335
|
+
if (fenced)
|
|
1336
|
+
out.push(line);
|
|
1337
|
+
else
|
|
1338
|
+
for (const inline of line.matchAll(/`([^`]+)`/g))
|
|
1339
|
+
out.push(inline[1] ?? "");
|
|
1340
|
+
}
|
|
1341
|
+
return out;
|
|
1342
|
+
}
|
|
1343
|
+
var FETCH_AND_RUN = [
|
|
1344
|
+
{
|
|
1345
|
+
kind: "pipe to a shell",
|
|
1346
|
+
pattern: /\b(?:curl|wget|iwr|irm|Invoke-WebRequest|Invoke-RestMethod)\b[^|\n]*\|\s*(?:sudo\s+(?:-\S+\s+)*)?(?:sh|bash|zsh|dash|python3?|node|perl|ruby|iex|Invoke-Expression)\b/i
|
|
1347
|
+
},
|
|
1348
|
+
{
|
|
1349
|
+
kind: "runs a downloaded script",
|
|
1350
|
+
pattern: /\b(?:sh|bash|zsh|source)\s+<\(\s*(?:curl|wget)\b|\b(?:sh|bash|zsh)\s+-c\s+["']?\$\(\s*(?:curl|wget)\b|\b(?:iex|Invoke-Expression)\b[^\n]*\b(?:irm|iwr|Invoke-WebRequest|Invoke-RestMethod|DownloadString)\b|\bdeno\s+run\b[^\n]*\bhttps?:\/\//i
|
|
1351
|
+
},
|
|
1352
|
+
{
|
|
1353
|
+
kind: "installs from a URL",
|
|
1354
|
+
pattern: /\b(?:pip3?|pipx|uv\s+pip|npm|pnpm|yarn|bun|gem)\s+(?:install|add|i)\b[^\n|;&]*\s(?:https?:\/\/|git\+|github:)/i
|
|
1355
|
+
},
|
|
1356
|
+
{
|
|
1357
|
+
kind: "runs a package fetched at run time",
|
|
1358
|
+
pattern: /\bnpx\s+(?:-y|--yes)\b|\b(?:pnpm|yarn)\s+dlx\s+\S|\buvx\s+[a-z]|\bpipx\s+run\s+[a-z]/i
|
|
1359
|
+
}
|
|
1360
|
+
];
|
|
1361
|
+
var URL_HOST = /\bhttps?:\/\/([a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)+)/gi;
|
|
1362
|
+
var PLACEHOLDER_HOST = /(^|\.)(example\.(com|org|net)|localhost|local|test|invalid|example|internal)$|^(127\.\d+\.\d+\.\d+|0\.0\.0\.0)$|^(www\.)?(your|my)-?(domain|site|server|host|app|api|company|website)\./;
|
|
1363
|
+
var FILE_LIKE_HOST = /\.(jpe?g|png|gif|webp|svg|mp[34]|wav|mov|webm|pdf|md|json|txt|html?|js|ts|py|sh)$/;
|
|
1364
|
+
function domainsIn(text) {
|
|
1365
|
+
const out = [];
|
|
1366
|
+
for (const match of text.matchAll(URL_HOST)) {
|
|
1367
|
+
const host = (match[1] ?? "").toLowerCase();
|
|
1368
|
+
if (!/\.[a-z]{2,}$/.test(host) && !/^\d+\.\d+\.\d+\.\d+$/.test(host))
|
|
1369
|
+
continue;
|
|
1370
|
+
if (PLACEHOLDER_HOST.test(host) || FILE_LIKE_HOST.test(host))
|
|
1371
|
+
continue;
|
|
1372
|
+
out.push(host);
|
|
1373
|
+
}
|
|
1374
|
+
return out;
|
|
1375
|
+
}
|
|
1376
|
+
var INVISIBLE = /[----]|\uDB40[\uDC00-\uDDEF]/g;
|
|
1377
|
+
var PICTOGRAPH = /\p{Extended_Pictographic}/u;
|
|
1378
|
+
function codePoint(point) {
|
|
1379
|
+
return `U+${point.toString(16).toUpperCase().padStart(4, "0")}`;
|
|
1380
|
+
}
|
|
1381
|
+
function invisibleIn(line, lineIndex) {
|
|
1382
|
+
const counts = new Map;
|
|
1383
|
+
const spelled = [];
|
|
1384
|
+
for (const match of line.matchAll(INVISIBLE)) {
|
|
1385
|
+
const char = match[0];
|
|
1386
|
+
const at = match.index;
|
|
1387
|
+
if (lineIndex === 0 && at === 0 && char === "\uFEFF")
|
|
1388
|
+
continue;
|
|
1389
|
+
if (char === "") {
|
|
1390
|
+
const before = [...line.slice(0, at)].at(-1) ?? "";
|
|
1391
|
+
const after = [...line.slice(at + 1)][0] ?? "";
|
|
1392
|
+
if (PICTOGRAPH.test(before) && PICTOGRAPH.test(after))
|
|
1393
|
+
continue;
|
|
1394
|
+
}
|
|
1395
|
+
const point = char.codePointAt(0) ?? 0;
|
|
1396
|
+
if (point >= 917536 && point <= 917630)
|
|
1397
|
+
spelled.push(String.fromCharCode(point - 917504));
|
|
1398
|
+
const name = point >= 917504 && point <= 917631 ? "Unicode tag" : codePoint(point);
|
|
1399
|
+
counts.set(name, (counts.get(name) ?? 0) + 1);
|
|
1400
|
+
}
|
|
1401
|
+
if (counts.size === 0)
|
|
1402
|
+
return null;
|
|
1403
|
+
const listed = [...counts.entries()].map(([name, n]) => `${n} × ${name}`).join(", ");
|
|
1404
|
+
return spelled.length === 0 ? listed : `${listed}, spelling "${cap(spelled.join(""))}"`;
|
|
1405
|
+
}
|
|
1406
|
+
var BASE64_RUN = /[A-Za-z0-9+/]{100,}={0,2}/g;
|
|
1407
|
+
var DATA_URI = /data:([\w.+-]+\/[\w.+-]+)?(?:;[\w=-]+)*;base64,([A-Za-z0-9+/]{100,}={0,2})/g;
|
|
1408
|
+
var URL_TEXT = /\bhttps?:\/\/\S+/g;
|
|
1409
|
+
function encodedIn(line) {
|
|
1410
|
+
const out = [];
|
|
1411
|
+
const withoutUris = line.replace(DATA_URI, (_whole, type, body) => {
|
|
1412
|
+
out.push(`data URI${type === undefined ? "" : ` ${type}`}, ${body.length} characters of base64`);
|
|
1413
|
+
return " ";
|
|
1414
|
+
});
|
|
1415
|
+
for (const match of withoutUris.replace(URL_TEXT, " ").matchAll(BASE64_RUN)) {
|
|
1416
|
+
const run = match[0];
|
|
1417
|
+
if (/[A-Z]/.test(run) && /[a-z]/.test(run) && /[0-9]/.test(run))
|
|
1418
|
+
out.push(`${run.length} characters of base64`);
|
|
1419
|
+
}
|
|
1420
|
+
return out;
|
|
1421
|
+
}
|
|
1422
|
+
var IGNORE_INSTRUCTIONS = /\b(?:ignore|disregard|forget|override)\b[^.\n]{0,40}?\b(?:previous|prior|above|earlier|preceding|all|any|other|system|your)\b[^.\n]{0,25}?\b(?:instructions?|prompts?|rules|guidelines|directions)\b/i;
|
|
1423
|
+
var KEEP_FROM_PERSON = /\b(?:do not|don't|never)\s+(?:tell|inform|notify|alert|(?:mention|reveal)(?:\s+(?:this|it))?\s+to)\s+(?:the\s+)?(?:user|human|person|operator)\b|\bwithout\s+(?:telling|informing|notifying|alerting)\s+(?:the\s+)?(?:user|human|person|operator)\b/i;
|
|
1424
|
+
function unquoted(line) {
|
|
1425
|
+
return line.replace(/"[^"]*"|\u201C[^\u201D]*\u201D|\u300C[^\u300D]*\u300D|`[^`]*`/g, " ");
|
|
1426
|
+
}
|
|
1427
|
+
var COMMENT_WORDS = 4;
|
|
1428
|
+
function hiddenInMarkdown(path, text) {
|
|
1429
|
+
const out = [];
|
|
1430
|
+
const push = (kind, line, value) => out.push({ kind, file: path, line, text: value });
|
|
1431
|
+
let fenced = false;
|
|
1432
|
+
let comment = null;
|
|
1433
|
+
for (const [index, line] of text.split(`
|
|
1434
|
+
`).entries()) {
|
|
1435
|
+
const invisible = invisibleIn(line, index);
|
|
1436
|
+
if (invisible !== null)
|
|
1437
|
+
push("hidden characters", index + 1, invisible);
|
|
1438
|
+
for (const encoded of encodedIn(line))
|
|
1439
|
+
push("encoded block", index + 1, encoded);
|
|
1440
|
+
const visible = line.replace(INVISIBLE, "");
|
|
1441
|
+
if (IGNORE_INSTRUCTIONS.test(unquoted(visible)))
|
|
1442
|
+
push("says to ignore instructions", index + 1, cap(visible));
|
|
1443
|
+
if (KEEP_FROM_PERSON.test(unquoted(visible)))
|
|
1444
|
+
push("says not to tell the person", index + 1, cap(visible));
|
|
1445
|
+
if (comment === null && FENCE.test(line)) {
|
|
1446
|
+
fenced = !fenced;
|
|
1447
|
+
continue;
|
|
1448
|
+
}
|
|
1449
|
+
if (fenced)
|
|
1450
|
+
continue;
|
|
1451
|
+
let rest = visible;
|
|
1452
|
+
while (rest.length > 0) {
|
|
1453
|
+
if (comment === null) {
|
|
1454
|
+
const open = rest.indexOf("<!--");
|
|
1455
|
+
if (open === -1)
|
|
1456
|
+
break;
|
|
1457
|
+
comment = { start: index + 1, text: [] };
|
|
1458
|
+
rest = rest.slice(open + 4);
|
|
1459
|
+
}
|
|
1460
|
+
const close = rest.indexOf("-->");
|
|
1461
|
+
if (close === -1) {
|
|
1462
|
+
comment.text.push(rest);
|
|
1463
|
+
break;
|
|
1464
|
+
}
|
|
1465
|
+
comment.text.push(rest.slice(0, close));
|
|
1466
|
+
const inner = comment.text.join(" ").replace(/\s+/g, " ").trim();
|
|
1467
|
+
if (inner.split(" ").filter((word) => /[\p{L}\p{N}]/u.test(word)).length >= COMMENT_WORDS) {
|
|
1468
|
+
push("HTML comment", comment.start, cap(inner));
|
|
1469
|
+
}
|
|
1470
|
+
comment = null;
|
|
1471
|
+
rest = rest.slice(close + 3);
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
if (comment !== null)
|
|
1475
|
+
push("HTML comment", comment.start, cap(`(never closed) ${comment.text.join(" ")}`));
|
|
1476
|
+
return out;
|
|
1477
|
+
}
|
|
1478
|
+
function bounded(name, items, cut) {
|
|
1479
|
+
if (items.length <= CONTENTS_LIMITS2.items)
|
|
1480
|
+
return [...items];
|
|
1481
|
+
cut.push(name);
|
|
1482
|
+
return items.slice(0, CONTENTS_LIMITS2.items);
|
|
1483
|
+
}
|
|
1484
|
+
function escapeRegExp(text) {
|
|
1485
|
+
return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1486
|
+
}
|
|
1487
|
+
function namesFile(markdown, path) {
|
|
1488
|
+
const base = path.slice(path.lastIndexOf("/") + 1);
|
|
1489
|
+
return markdown.includes(path) || new RegExp(`(^|[\\s/\`'"(])${escapeRegExp(base)}($|[\\s\`'")]|[.,:;](\\s|$))`, "m").test(markdown);
|
|
1490
|
+
}
|
|
1491
|
+
function readSkillContents2(input) {
|
|
1492
|
+
const sorted = [...input].sort((a, b) => compare(a.path, b.path));
|
|
1493
|
+
const skipped = [];
|
|
1494
|
+
const files = [];
|
|
1495
|
+
for (const file of sorted) {
|
|
1496
|
+
if (files.length >= CONTENTS_LIMITS2.files)
|
|
1497
|
+
skipped.push({ path: file.path, why: "over the file limit" });
|
|
1498
|
+
else if (file.text === null)
|
|
1499
|
+
skipped.push({ path: file.path, why: file.notFetched === true ? "not fetched" : "binary" });
|
|
1500
|
+
else if (file.text.length > CONTENTS_LIMITS2.fileChars)
|
|
1501
|
+
skipped.push({ path: file.path, why: "too large" });
|
|
1502
|
+
else
|
|
1503
|
+
files.push({ path: file.path, text: file.text.replace(/\r\n?/g, `
|
|
1504
|
+
`) });
|
|
1505
|
+
}
|
|
1506
|
+
const skillMd = files.find((file) => file.path === "SKILL.md")?.text ?? "";
|
|
1507
|
+
const programs = new Map;
|
|
1508
|
+
const add = (name, from) => {
|
|
1509
|
+
if (!programs.has(name))
|
|
1510
|
+
programs.set(name, from);
|
|
1511
|
+
};
|
|
1512
|
+
const requires = readRequires2(skillMd);
|
|
1513
|
+
for (const name of requires.cli)
|
|
1514
|
+
add(name, "declared");
|
|
1515
|
+
for (const name of requires.runs)
|
|
1516
|
+
add(name, "runs");
|
|
1517
|
+
const packages = new Map;
|
|
1518
|
+
const settings = new Set;
|
|
1519
|
+
const domains = new Set;
|
|
1520
|
+
const fetchAndRun = [];
|
|
1521
|
+
const hidden = [];
|
|
1522
|
+
const scripts = skipped.filter((file) => file.why === "not fetched" && SCRIPT.test(file.path)).map((file) => ({ path: file.path, namedInSkillMd: namesFile(skillMd, file.path) }));
|
|
1523
|
+
for (const file of files) {
|
|
1524
|
+
const lines = file.text.split(`
|
|
1525
|
+
`);
|
|
1526
|
+
const isScript = SCRIPT.test(file.path) || lines[0]?.startsWith("#!") === true;
|
|
1527
|
+
const code = codeOf(file.path, file.text, isScript);
|
|
1528
|
+
const shell = isShell(file.path, file.text);
|
|
1529
|
+
for (const line of code) {
|
|
1530
|
+
const installed = packagesIn(line);
|
|
1531
|
+
for (const pkg of installed)
|
|
1532
|
+
packages.set(`${pkg.manager} ${pkg.name}`, pkg);
|
|
1533
|
+
if (!isScript)
|
|
1534
|
+
continue;
|
|
1535
|
+
for (const name of programsInScriptLine(line, shell)) {
|
|
1536
|
+
if (!installed.some((pkg) => pkg.name === name))
|
|
1537
|
+
add(name, "script");
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
if (isScript) {
|
|
1541
|
+
scripts.push({ path: file.path, namedInSkillMd: namesFile(skillMd, file.path) });
|
|
1542
|
+
const interpreter = interpreterOf(lines[0] ?? "");
|
|
1543
|
+
if (interpreter !== null)
|
|
1544
|
+
add(interpreter, "interpreter");
|
|
1545
|
+
}
|
|
1546
|
+
if (/(^|\/)requirements[\w.-]*\.txt$/i.test(file.path)) {
|
|
1547
|
+
for (const name of requirementsIn(file.text))
|
|
1548
|
+
packages.set(`pip ${name}`, { manager: "pip", name });
|
|
1549
|
+
}
|
|
1550
|
+
for (const setting of settingsIn(file.path, file.text, code))
|
|
1551
|
+
settings.add(setting);
|
|
1552
|
+
for (const host of domainsIn(file.text))
|
|
1553
|
+
domains.add(host);
|
|
1554
|
+
for (const [index, line] of lines.entries()) {
|
|
1555
|
+
const run = FETCH_AND_RUN.find(({ pattern }) => pattern.test(line));
|
|
1556
|
+
if (run !== undefined)
|
|
1557
|
+
fetchAndRun.push({ kind: run.kind, file: file.path, line: index + 1, text: cap(line) });
|
|
1558
|
+
}
|
|
1559
|
+
if (MARKDOWN.test(file.path))
|
|
1560
|
+
hidden.push(...hiddenInMarkdown(file.path, file.text));
|
|
1561
|
+
}
|
|
1562
|
+
const packageNames = new Set([...packages.values()].map((pkg) => pkg.name));
|
|
1563
|
+
for (const [name, from] of programs)
|
|
1564
|
+
if (from !== "declared" && packageNames.has(name))
|
|
1565
|
+
programs.delete(name);
|
|
1566
|
+
const cut = [];
|
|
1567
|
+
return {
|
|
1568
|
+
programs: bounded("programs", [...programs.entries()].map(([name, from]) => ({ name, from })).sort((a, b) => compare(a.name, b.name)), cut),
|
|
1569
|
+
packages: bounded("packages", [...packages.values()].sort((a, b) => compare(`${a.manager} ${a.name}`, `${b.manager} ${b.name}`)), cut),
|
|
1570
|
+
settings: bounded("settings", [...settings].sort(compare), cut),
|
|
1571
|
+
scripts: bounded("scripts", scripts.sort((a, b) => compare(a.path, b.path)), cut),
|
|
1572
|
+
fetchAndRun: bounded("fetchAndRun", fetchAndRun, cut),
|
|
1573
|
+
domains: bounded("domains", [...domains].sort(compare), cut),
|
|
1574
|
+
hidden: bounded("hidden", hidden, cut),
|
|
1575
|
+
read: {
|
|
1576
|
+
files: files.length,
|
|
1577
|
+
bytes: files.reduce((sum, file) => sum + new TextEncoder().encode(file.text).byteLength, 0),
|
|
1578
|
+
skipped
|
|
1579
|
+
},
|
|
1580
|
+
cut
|
|
1581
|
+
};
|
|
1582
|
+
}
|
|
1583
|
+
function contentsLines2(contents, perList = 6) {
|
|
1584
|
+
const list = (items, cut) => items.length === 0 ? "none found" : `${items.slice(0, perList).join(", ")}${items.length > perList || cut ? `, and ${Math.max(0, items.length - perList)}${cut ? "+" : ""} more` : ""}`;
|
|
1585
|
+
const at = (rows, name) => list(rows.map((row) => `${row.kind} at ${row.file}:${row.line} "${row.text}"`), contents.cut.includes(name));
|
|
1586
|
+
return [
|
|
1587
|
+
`Programs: ${list(contents.programs.map((p) => `${p.name} (${p.from})`), contents.cut.includes("programs"))}`,
|
|
1588
|
+
...contents.packages.length === 0 ? [] : [`Packages: ${list(contents.packages.map((p) => `${p.manager} ${p.name}`), contents.cut.includes("packages"))}`],
|
|
1589
|
+
`Keys and settings it reads: ${list(contents.settings, contents.cut.includes("settings"))}`,
|
|
1590
|
+
`Scripts: ${list(contents.scripts.map((s) => s.namedInSkillMd ? `${s.path} (named in SKILL.md)` : s.path), contents.cut.includes("scripts"))}`,
|
|
1591
|
+
`Fetches and runs code: ${at(contents.fetchAndRun, "fetchAndRun")}`,
|
|
1592
|
+
`Domains named: ${list(contents.domains, contents.cut.includes("domains"))}`,
|
|
1593
|
+
`Hidden or encoded text: ${at(contents.hidden, "hidden")}`,
|
|
1594
|
+
...contents.read.skipped.length === 0 ? [] : [`Not read: ${list(contents.read.skipped.map((s) => `${s.path} (${s.why})`), false)}`]
|
|
1595
|
+
];
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1598
|
+
// ../../packages/protocol/src/pulse.ts
|
|
1599
|
+
var MAX_SKILL_FILES_READ2 = 200;
|
|
1600
|
+
var MAX_SCRIPTS_READ = 5;
|
|
1601
|
+
var GITHUB_READ_TIMEOUT_MS = 1e4;
|
|
1602
|
+
var INSTALLER_ORDER = [
|
|
1603
|
+
"",
|
|
1604
|
+
"skills/",
|
|
1605
|
+
"skills/.curated/",
|
|
1606
|
+
"skills/.experimental/",
|
|
1607
|
+
"skills/.system/",
|
|
1608
|
+
".agents/skills/",
|
|
1609
|
+
".claude/skills/",
|
|
1610
|
+
".cline/skills/",
|
|
1611
|
+
".codebuddy/skills/",
|
|
1612
|
+
".codex/skills/",
|
|
1613
|
+
".commandcode/skills/",
|
|
1614
|
+
".continue/skills/",
|
|
1615
|
+
".factory/skills/",
|
|
1616
|
+
".github/skills/",
|
|
1617
|
+
".goose/skills/",
|
|
1618
|
+
".grok/skills/",
|
|
1619
|
+
".iflow/skills/",
|
|
1620
|
+
".junie/skills/",
|
|
1621
|
+
".kilo/skills/",
|
|
1622
|
+
".kilocode/skills/",
|
|
1623
|
+
".kimchi/skills/",
|
|
1624
|
+
".kiro/skills/",
|
|
1625
|
+
".minimax/skills/",
|
|
1626
|
+
".mux/skills/",
|
|
1627
|
+
".neovate/skills/",
|
|
1628
|
+
".opencode/skills/",
|
|
1629
|
+
".openhands/skills/",
|
|
1630
|
+
".pi/skills/",
|
|
1631
|
+
".posit/assistant/skills/",
|
|
1632
|
+
".qoder/skills/",
|
|
1633
|
+
".roo/skills/",
|
|
1634
|
+
".trae/skills/",
|
|
1635
|
+
".windsurf/skills/",
|
|
1636
|
+
".zcode/skills/",
|
|
1637
|
+
".zencoder/skills/"
|
|
1638
|
+
];
|
|
1639
|
+
function installerRank(path) {
|
|
1640
|
+
let best = null;
|
|
1641
|
+
INSTALLER_ORDER.forEach((prefix, rank) => {
|
|
1642
|
+
if (best !== null || !path.startsWith(prefix))
|
|
1643
|
+
return;
|
|
1644
|
+
const depth = path.slice(prefix.length).split("/").length;
|
|
1645
|
+
if (prefix === "" ? depth <= 2 : depth <= 4)
|
|
1646
|
+
best = rank;
|
|
1647
|
+
});
|
|
1648
|
+
return best;
|
|
1649
|
+
}
|
|
1650
|
+
function normaliseName(name) {
|
|
1651
|
+
return name.trim().toLowerCase().replace(/[\s_]+/g, "-");
|
|
1652
|
+
}
|
|
1653
|
+
function frontmatterName2(markdown) {
|
|
1654
|
+
const lines = markdown.split(`
|
|
1655
|
+
`);
|
|
1656
|
+
if (lines[0]?.trim() !== "---")
|
|
1657
|
+
return null;
|
|
1658
|
+
for (const line of lines.slice(1)) {
|
|
1659
|
+
if (line.trim() === "---")
|
|
1660
|
+
return null;
|
|
1661
|
+
const match = /^name\s*:\s*(.+?)\s*$/.exec(line);
|
|
1662
|
+
if (match !== null)
|
|
1663
|
+
return (match[1] ?? "").replace(/^["']|["']$/g, "");
|
|
1664
|
+
}
|
|
1665
|
+
return null;
|
|
1666
|
+
}
|
|
1667
|
+
var KEY_NAME3 = /\b[A-Z][A-Z0-9_]*(?:API_KEY|APIKEY|_TOKEN|_SECRET)\b/g;
|
|
1668
|
+
var PAID_WORD = /\b(pricing|paid|subscription|credits?|billing|free tier|per minute)\b/gi;
|
|
1669
|
+
var HOST = /https?:\/\/([a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/g;
|
|
1670
|
+
var NOT_A_SERVICE = /(^|\.)(github\.com|githubusercontent\.com|example\.(com|org|net))$/;
|
|
1671
|
+
var LOOKS_LIKE_A_FILE = /\.(jpe?g|png|gif|webp|mp[34]|wav|mov|webm|pdf|md|json|txt)$/;
|
|
1672
|
+
var SCRIPT2 = /\.(sh|bash|py|js|mjs|cjs|ts|rb|pl)$/i;
|
|
1673
|
+
var RUNS_FILE = /\b(?:node|bun|deno|tsx|python3?|bash|sh)\s+((?:~|\.{1,2})?\/?[\w.@-]+(?:\/[\w.@-]+)*\.(?:mjs|cjs|js|ts|py|sh))\b/g;
|
|
1674
|
+
var PLACEHOLDER = /(^|\/)(path|your|my|foo|example|script|file|input|output)(\/|\.|$)/i;
|
|
1675
|
+
function filesItRuns(markdown, repositoryFiles) {
|
|
1676
|
+
const named = [...markdown.matchAll(RUNS_FILE)].map((match) => match[1] ?? "");
|
|
1677
|
+
const found = new Set;
|
|
1678
|
+
for (const name of named) {
|
|
1679
|
+
if (name.startsWith("/") || name.startsWith("~") || PLACEHOLDER.test(name))
|
|
1680
|
+
continue;
|
|
1681
|
+
const tail = name.replace(/^(\.{1,2}\/)+/, "");
|
|
1682
|
+
const hit = repositoryFiles.find((file) => file.path === tail || file.path.endsWith(`/${tail}`));
|
|
1683
|
+
if (hit !== undefined)
|
|
1684
|
+
found.add(hit.path);
|
|
1685
|
+
}
|
|
1686
|
+
return [...found];
|
|
1687
|
+
}
|
|
1688
|
+
function absentFiles(markdown, repositoryFiles) {
|
|
1689
|
+
const named = [...markdown.matchAll(RUNS_FILE)].map((match) => match[1] ?? "");
|
|
1690
|
+
return [...new Set(named)].filter((name) => {
|
|
1691
|
+
if (name.startsWith("/") || name.startsWith("~") || PLACEHOLDER.test(name))
|
|
1692
|
+
return false;
|
|
1693
|
+
const tail = name.replace(/^(\.{1,2}\/)+/, "");
|
|
1694
|
+
return !repositoryFiles.some((file) => file.path === tail || file.path.endsWith(`/${tail}`));
|
|
1695
|
+
});
|
|
1696
|
+
}
|
|
1697
|
+
function readSkillFacts2(markdown, filesInDirectory, repositoryFiles = filesInDirectory) {
|
|
1698
|
+
const hosts = [...markdown.matchAll(HOST)].map((match) => (match[1] ?? "").toLowerCase()).filter((host) => !NOT_A_SERVICE.test(host) && !LOOKS_LIKE_A_FILE.test(host));
|
|
1699
|
+
return {
|
|
1700
|
+
files: filesInDirectory.length,
|
|
1701
|
+
bytes: filesInDirectory.reduce((total, file) => total + file.size, 0),
|
|
1702
|
+
scripts: filesInDirectory.filter((file) => SCRIPT2.test(file.path)).length,
|
|
1703
|
+
keys: [...new Set(markdown.match(KEY_NAME3) ?? [])],
|
|
1704
|
+
paidWords: [...new Set((markdown.match(PAID_WORD) ?? []).map((word) => word.toLowerCase()))],
|
|
1705
|
+
hosts: [...new Set(hosts)],
|
|
1706
|
+
runsAbsentFiles: absentFiles(markdown, repositoryFiles),
|
|
1707
|
+
keysInScriptsItRuns: [],
|
|
1708
|
+
requires: readRequires2(markdown),
|
|
1709
|
+
contains: readSkillContents2([{ path: "SKILL.md", text: markdown }])
|
|
1710
|
+
};
|
|
1711
|
+
}
|
|
1712
|
+
function parentOf(path) {
|
|
1713
|
+
const at = path.lastIndexOf("/");
|
|
1714
|
+
return at === -1 ? "" : path.slice(0, at);
|
|
1715
|
+
}
|
|
1716
|
+
async function locate(source, repo, skill, listing) {
|
|
1717
|
+
const wanted = normaliseName(skill);
|
|
1718
|
+
const candidates = listing.files.filter((file) => skillFilesIn2([file]).length === 1);
|
|
1719
|
+
const guessed = candidates.filter((file) => normaliseName(directoryOf2(file.path)) === wanted);
|
|
1720
|
+
const others = candidates.filter((file) => !guessed.includes(file));
|
|
1721
|
+
const read = async (files) => (await Promise.all(files.map(async (file) => ({ file, markdown: await source.file(repo, file.path) })))).filter((entry) => entry.markdown !== null && normaliseName(frontmatterName2(entry.markdown) ?? "") === wanted);
|
|
1722
|
+
let named = await read(guessed);
|
|
1723
|
+
if (named.length === 0) {
|
|
1724
|
+
if (others.length > MAX_SKILL_FILES_READ2) {
|
|
1725
|
+
return { status: "incomplete", reason: "too_many_skill_files" };
|
|
1726
|
+
}
|
|
1727
|
+
for (let at = 0;at < others.length && named.length === 0; at += 20) {
|
|
1728
|
+
named = await read(others.slice(at, at + 20));
|
|
1729
|
+
}
|
|
1730
|
+
if (named.length > 0) {
|
|
1731
|
+
const last = others.findIndex((file) => file === named.at(-1)?.file);
|
|
1732
|
+
named = [...named, ...await read(others.slice(last + 1))];
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
if (named.length === 0) {
|
|
1736
|
+
if (listing.truncated)
|
|
1737
|
+
return { status: "incomplete", reason: "truncated_listing" };
|
|
1738
|
+
return { status: "absent", skillFiles: candidates.length };
|
|
1739
|
+
}
|
|
1740
|
+
const ranked = named.map((entry) => ({ ...entry, rank: installerRank(entry.file.path) })).sort((a, b) => (a.rank ?? Infinity) - (b.rank ?? Infinity));
|
|
1741
|
+
const [first2, ...rest] = ranked;
|
|
1742
|
+
if (first2 === undefined)
|
|
1743
|
+
return { status: "absent", skillFiles: candidates.length };
|
|
1744
|
+
const differs = rest.filter((entry) => entry.file.sha !== first2.file.sha);
|
|
1745
|
+
if (first2.rank === null && differs.length > 0) {
|
|
1746
|
+
return { status: "ambiguous", paths: ranked.map((entry) => entry.file.path) };
|
|
1747
|
+
}
|
|
1748
|
+
return {
|
|
1749
|
+
status: "found",
|
|
1750
|
+
file: first2.file,
|
|
1751
|
+
markdown: first2.markdown,
|
|
1752
|
+
alsoAt: rest.filter((entry) => entry.file.sha === first2.file.sha).map((entry) => entry.file.path),
|
|
1753
|
+
differsAt: differs.map((entry) => entry.file.path)
|
|
1754
|
+
};
|
|
1755
|
+
}
|
|
1756
|
+
function usualPaths(skill) {
|
|
1757
|
+
return [`skills/${skill}/SKILL.md`, `${skill}/SKILL.md`, `.claude/skills/${skill}/SKILL.md`, "SKILL.md"];
|
|
1758
|
+
}
|
|
1759
|
+
async function readFileOnly(result, source) {
|
|
1760
|
+
if (!/^[\w.-]+$/.test(result.skill) || /^\.+$/.test(result.skill))
|
|
1761
|
+
return null;
|
|
1762
|
+
const wanted = normaliseName(result.skill);
|
|
1763
|
+
for (const path of usualPaths(result.skill)) {
|
|
1764
|
+
const markdown = await source.file(result.source, path);
|
|
1765
|
+
if (markdown === null || normaliseName(frontmatterName2(markdown) ?? "") !== wanted)
|
|
1766
|
+
continue;
|
|
1767
|
+
const { keys, paidWords, hosts, requires, contains } = readSkillFacts2(markdown, []);
|
|
1768
|
+
return { ...result, status: "read_file_only", path, keys, paidWords, hosts, requires, contains };
|
|
1769
|
+
}
|
|
1770
|
+
return null;
|
|
1771
|
+
}
|
|
1772
|
+
async function readPulse2(result, source) {
|
|
1773
|
+
if (!/^(?!\.{1,2}\/)[\w.-]+\/(?!\.{1,2}$)[\w.-]+$/.test(result.source))
|
|
1774
|
+
return { ...result, status: "not_github" };
|
|
1775
|
+
const listing = await source.listing(result.source);
|
|
1776
|
+
if (listing === "rate_limited")
|
|
1777
|
+
return await readFileOnly(result, source) ?? { ...result, status: "rate_limited" };
|
|
1778
|
+
if (listing === "unreachable")
|
|
1779
|
+
return { ...result, status: "repository_unreachable" };
|
|
1780
|
+
const located = await locate(source, result.source, result.skill, listing);
|
|
1781
|
+
if (located.status === "absent")
|
|
1782
|
+
return { ...result, status: "absent", skillFiles: located.skillFiles };
|
|
1783
|
+
if (located.status === "ambiguous")
|
|
1784
|
+
return { ...result, status: "ambiguous", paths: located.paths };
|
|
1785
|
+
if (located.status === "incomplete")
|
|
1786
|
+
return { ...result, status: "incomplete", reason: located.reason };
|
|
1787
|
+
const directory = parentOf(located.file.path);
|
|
1788
|
+
const prefix = directory === "" ? "" : `${directory}/`;
|
|
1789
|
+
const inDirectory = listing.files.filter((file) => file.path.startsWith(prefix));
|
|
1790
|
+
const scripts = filesItRuns(located.markdown, listing.files).slice(0, MAX_SCRIPTS_READ);
|
|
1791
|
+
const scriptTexts = await Promise.all(scripts.map(async (file) => ({ file, text: await source.file(result.source, file) })));
|
|
1792
|
+
const keysInScriptsItRuns = scriptTexts.map(({ file, text }) => ({ file, keys: [...new Set(text?.match(KEY_NAME3) ?? [])] })).filter((entry) => entry.keys.length > 0);
|
|
1793
|
+
const relative = (path) => path.startsWith(prefix) ? path.slice(prefix.length) : path;
|
|
1794
|
+
const fetched = new Set(scriptTexts.filter((entry) => entry.text !== null).map((entry) => entry.file));
|
|
1795
|
+
const contains = readSkillContents2([
|
|
1796
|
+
{ path: "SKILL.md", text: located.markdown },
|
|
1797
|
+
...scriptTexts.flatMap(({ file, text }) => text === null ? [] : [{ path: relative(file), text }]),
|
|
1798
|
+
...inDirectory.filter((file) => SCRIPT2.test(file.path) && !fetched.has(file.path)).map((file) => ({ path: relative(file.path), text: null, notFetched: true }))
|
|
1799
|
+
]);
|
|
1800
|
+
return {
|
|
1801
|
+
...result,
|
|
1802
|
+
status: "read",
|
|
1803
|
+
path: located.file.path,
|
|
1804
|
+
alsoAt: located.alsoAt,
|
|
1805
|
+
differsAt: located.differsAt,
|
|
1806
|
+
contentId: directory === "" ? listing.rootTreeId : listing.trees.get(directory) ?? located.file.sha,
|
|
1807
|
+
skillMdSha: located.file.sha,
|
|
1808
|
+
repository: await source.repository(result.source),
|
|
1809
|
+
...listing.readAt === undefined ? {} : { listingReadAt: listing.readAt },
|
|
1810
|
+
...readSkillFacts2(located.markdown, inDirectory, listing.files),
|
|
1811
|
+
keysInScriptsItRuns,
|
|
1812
|
+
contains
|
|
1813
|
+
};
|
|
1814
|
+
}
|
|
1815
|
+
function githubPulseSource2(options = {}) {
|
|
1816
|
+
const get = options.fetch ?? fetch;
|
|
1817
|
+
const timeoutMs = options.timeoutMs ?? GITHUB_READ_TIMEOUT_MS;
|
|
1818
|
+
const bounded2 = () => {
|
|
1819
|
+
const limit = AbortSignal.timeout(timeoutMs);
|
|
1820
|
+
return options.signal === undefined ? limit : AbortSignal.any([limit, options.signal]);
|
|
1821
|
+
};
|
|
1822
|
+
const headers = {
|
|
1823
|
+
accept: "application/vnd.github+json",
|
|
1824
|
+
"user-agent": "arp-pulse",
|
|
1825
|
+
...options.token === undefined ? {} : { authorization: `Bearer ${options.token}` }
|
|
1826
|
+
};
|
|
1827
|
+
const listings = new Map;
|
|
1828
|
+
const repositories = new Map;
|
|
1829
|
+
const quota = { apiRequests: 0, limited: false, resetsAt: null };
|
|
1830
|
+
const noteLimit = (response) => {
|
|
1831
|
+
const hit = response.status === 429 || response.status === 403 && (response.headers.get("x-ratelimit-remaining") === "0" || response.headers.has("retry-after"));
|
|
1832
|
+
if (!hit)
|
|
1833
|
+
return false;
|
|
1834
|
+
quota.limited = true;
|
|
1835
|
+
const reset = Number(response.headers.get("x-ratelimit-reset"));
|
|
1836
|
+
if (Number.isFinite(reset) && reset > 0)
|
|
1837
|
+
quota.resetsAt = new Date(reset * 1000).toISOString();
|
|
1838
|
+
return true;
|
|
1839
|
+
};
|
|
1840
|
+
const readListing = async (repo) => {
|
|
1841
|
+
const cached = await options.cache?.get(repo).catch(() => null);
|
|
1842
|
+
if (cached !== null && cached !== undefined)
|
|
1843
|
+
return cached;
|
|
1844
|
+
if (quota.limited)
|
|
1845
|
+
return "rate_limited";
|
|
1846
|
+
quota.apiRequests += 1;
|
|
1847
|
+
const response = await get(`https://api.github.com/repos/${repo}/git/trees/HEAD?recursive=1`, { headers, signal: bounded2() });
|
|
1848
|
+
if (noteLimit(response))
|
|
1849
|
+
return "rate_limited";
|
|
1850
|
+
if (!response.ok)
|
|
1851
|
+
return "unreachable";
|
|
1852
|
+
const body = await response.json();
|
|
1853
|
+
const files = [];
|
|
1854
|
+
const trees = new Map;
|
|
1855
|
+
for (const entry of body.tree ?? []) {
|
|
1856
|
+
if (typeof entry.path !== "string" || typeof entry.sha !== "string")
|
|
1857
|
+
continue;
|
|
1858
|
+
if (entry.type === "tree")
|
|
1859
|
+
trees.set(entry.path, entry.sha);
|
|
1860
|
+
if (entry.type === "blob") {
|
|
1861
|
+
files.push({ path: entry.path, sha: entry.sha, size: typeof entry.size === "number" ? entry.size : 0 });
|
|
1862
|
+
}
|
|
1863
|
+
}
|
|
1864
|
+
const listing = {
|
|
1865
|
+
rootTreeId: typeof body.sha === "string" ? body.sha : "",
|
|
1866
|
+
files,
|
|
1867
|
+
trees,
|
|
1868
|
+
truncated: body.truncated === true
|
|
1869
|
+
};
|
|
1870
|
+
await options.cache?.set(repo, listing).catch(() => {
|
|
1871
|
+
return;
|
|
1872
|
+
});
|
|
1873
|
+
return listing;
|
|
1874
|
+
};
|
|
1875
|
+
const readRepository = async (repo) => {
|
|
1876
|
+
if (options.withRepository === false || quota.limited)
|
|
1877
|
+
return null;
|
|
1878
|
+
quota.apiRequests += 1;
|
|
1879
|
+
const response = await get(`https://api.github.com/repos/${repo}`, { headers, signal: bounded2() });
|
|
1880
|
+
noteLimit(response);
|
|
1881
|
+
if (!response.ok)
|
|
1882
|
+
return null;
|
|
1883
|
+
const body = await response.json();
|
|
1884
|
+
return {
|
|
1885
|
+
stars: typeof body.stargazers_count === "number" ? body.stargazers_count : 0,
|
|
1886
|
+
pushedAt: typeof body.pushed_at === "string" ? body.pushed_at : null,
|
|
1887
|
+
archived: body.archived === true
|
|
1888
|
+
};
|
|
1889
|
+
};
|
|
1890
|
+
const once = (cache, repo, read) => {
|
|
1891
|
+
const hit = cache.get(repo);
|
|
1892
|
+
if (hit !== undefined)
|
|
1893
|
+
return hit;
|
|
1894
|
+
const started = read(repo);
|
|
1895
|
+
cache.set(repo, started);
|
|
1896
|
+
return started;
|
|
1897
|
+
};
|
|
1898
|
+
return {
|
|
1899
|
+
listing: (repo) => once(listings, repo, readListing),
|
|
1900
|
+
repository: (repo) => once(repositories, repo, readRepository),
|
|
1901
|
+
file: async (repo, path) => {
|
|
1902
|
+
const encoded = path.split("/").map(encodeURIComponent).join("/");
|
|
1903
|
+
const response = await get(`https://raw.githubusercontent.com/${repo}/HEAD/${encoded}`, { signal: bounded2() });
|
|
1904
|
+
return response.ok ? response.text() : null;
|
|
1905
|
+
},
|
|
1906
|
+
quota: () => ({ ...quota })
|
|
1907
|
+
};
|
|
1908
|
+
}
|
|
1909
|
+
|
|
1910
|
+
// ../../packages/protocol/src/skill-install.ts
|
|
1911
|
+
var MAX_SKILLS_READ2 = 3;
|
|
1912
|
+
var ADD = new Set(["add", "a", "install", "i"]);
|
|
1913
|
+
var RUNNERS = new Set(["npx", "pnpx", "bunx"]);
|
|
1914
|
+
var DLX = { pnpm: ["dlx"], yarn: ["dlx"], npm: ["exec", "x"] };
|
|
1915
|
+
var SKILLS = /^skills(@[\w.^~<>=*-]+)?$/;
|
|
1916
|
+
var ENV_ASSIGNMENT = /^[A-Za-z_][A-Za-z0-9_]*=/;
|
|
1917
|
+
function shellWords(command) {
|
|
1918
|
+
const commands = [[]];
|
|
1919
|
+
let word = null;
|
|
1920
|
+
let quote = null;
|
|
1921
|
+
const end = () => {
|
|
1922
|
+
if (word !== null)
|
|
1923
|
+
commands[commands.length - 1]?.push(word);
|
|
1924
|
+
word = null;
|
|
1925
|
+
};
|
|
1926
|
+
for (let at = 0;at < command.length; at += 1) {
|
|
1927
|
+
const char = command[at] ?? "";
|
|
1928
|
+
if (quote !== null) {
|
|
1929
|
+
if (char === quote)
|
|
1930
|
+
quote = null;
|
|
1931
|
+
else if (char === "\\" && quote === '"' && at + 1 < command.length)
|
|
1932
|
+
word = (word ?? "") + command[at += 1];
|
|
1933
|
+
else
|
|
1934
|
+
word = (word ?? "") + char;
|
|
1935
|
+
} else if (char === "'" || char === '"') {
|
|
1936
|
+
quote = char;
|
|
1937
|
+
word ??= "";
|
|
1938
|
+
} else if (char === "\\" && at + 1 < command.length) {
|
|
1939
|
+
word = (word ?? "") + command[at += 1];
|
|
1940
|
+
} else if (/\s/.test(char) && char !== `
|
|
1941
|
+
`) {
|
|
1942
|
+
end();
|
|
1943
|
+
} else if (char === `
|
|
1944
|
+
` || char === ";" || char === "&" || char === "|") {
|
|
1945
|
+
end();
|
|
1946
|
+
commands.push([]);
|
|
1947
|
+
} else {
|
|
1948
|
+
word = (word ?? "") + char;
|
|
1949
|
+
}
|
|
1950
|
+
}
|
|
1951
|
+
end();
|
|
1952
|
+
return commands.filter((words) => words.length > 0);
|
|
1953
|
+
}
|
|
1954
|
+
function addArguments(words) {
|
|
1955
|
+
let at = 0;
|
|
1956
|
+
while (at < words.length && ENV_ASSIGNMENT.test(words[at] ?? ""))
|
|
1957
|
+
at += 1;
|
|
1958
|
+
const first2 = words[at];
|
|
1959
|
+
if (first2 !== undefined && RUNNERS.has(first2))
|
|
1960
|
+
at += 1;
|
|
1961
|
+
else if (first2 !== undefined && DLX[first2]?.includes(words[at + 1] ?? "") === true)
|
|
1962
|
+
at += 2;
|
|
1963
|
+
while (at < words.length && (words[at] ?? "").startsWith("-"))
|
|
1964
|
+
at += 1;
|
|
1965
|
+
if (!SKILLS.test(words[at] ?? ""))
|
|
1966
|
+
return null;
|
|
1967
|
+
const subcommand = words[at + 1];
|
|
1968
|
+
return subcommand !== undefined && ADD.has(subcommand) ? words.slice(at + 2) : null;
|
|
1969
|
+
}
|
|
1970
|
+
function parseAdd(args) {
|
|
1971
|
+
const skills = [];
|
|
1972
|
+
let source = null;
|
|
1973
|
+
let all = false;
|
|
1974
|
+
let list = false;
|
|
1975
|
+
for (let at = 0;at < args.length; at += 1) {
|
|
1976
|
+
const arg = args[at] ?? "";
|
|
1977
|
+
if (arg === "--all")
|
|
1978
|
+
all = true;
|
|
1979
|
+
else if (arg === "-l" || arg === "--list")
|
|
1980
|
+
list = true;
|
|
1981
|
+
else if (arg === "-s" || arg === "--skill" || arg === "-a" || arg === "--agent" || arg === "--subagent") {
|
|
1982
|
+
while (at + 1 < args.length && !(args[at + 1] ?? "").startsWith("-")) {
|
|
1983
|
+
at += 1;
|
|
1984
|
+
if (arg === "-s" || arg === "--skill")
|
|
1985
|
+
skills.push(args[at] ?? "");
|
|
1986
|
+
}
|
|
1987
|
+
} else if (arg === "--metadata")
|
|
1988
|
+
at += 1;
|
|
1989
|
+
else if (!arg.startsWith("-") && source === null)
|
|
1990
|
+
source = arg;
|
|
1991
|
+
}
|
|
1992
|
+
return { source, skills, all, list };
|
|
1993
|
+
}
|
|
1994
|
+
var NAME = /^[\w.-]+$/;
|
|
1995
|
+
function github(repo, ref, subpath, named, all) {
|
|
1996
|
+
const [owner, name] = repo.split("/");
|
|
1997
|
+
const clean = `${owner}/${(name ?? "").replace(/\.git$/, "")}`;
|
|
1998
|
+
const path = subpath?.replace(/^\/+|\/+$/g, "") ?? "";
|
|
1999
|
+
return {
|
|
2000
|
+
kind: "github",
|
|
2001
|
+
repo: clean,
|
|
2002
|
+
ref,
|
|
2003
|
+
subpath: path === "" || path.split("/").includes("..") ? null : path,
|
|
2004
|
+
skills: all || named.length === 0 || named.includes("*") ? "all" : [...new Set(named)]
|
|
2005
|
+
};
|
|
2006
|
+
}
|
|
2007
|
+
function sourceOf(input, named, all) {
|
|
2008
|
+
const elsewhere = { kind: "elsewhere", source: input };
|
|
2009
|
+
if (/^(\.{1,2}(\/|$)|\/|~)/.test(input) || /^[A-Za-z]:[\\/]/.test(input))
|
|
2010
|
+
return elsewhere;
|
|
2011
|
+
const hash = input.indexOf("#");
|
|
2012
|
+
let rest = hash === -1 ? input : input.slice(0, hash);
|
|
2013
|
+
const fragment = hash === -1 ? "" : input.slice(hash + 1);
|
|
2014
|
+
const fragmentAt = fragment.indexOf("@");
|
|
2015
|
+
let ref = (fragmentAt === -1 ? fragment : fragment.slice(0, fragmentAt)) || null;
|
|
2016
|
+
const fragmentSkill = fragmentAt === -1 ? null : fragment.slice(fragmentAt + 1) || null;
|
|
2017
|
+
const withSkill = (names) => fragmentSkill === null ? names : [fragmentSkill];
|
|
2018
|
+
const prefixed = /^github:(.+)$/.exec(rest);
|
|
2019
|
+
if (prefixed?.[1] !== undefined)
|
|
2020
|
+
rest = prefixed[1];
|
|
2021
|
+
const tree = /^https?:\/\/(?:www\.)?github\.com\/([^/]+)\/([^/]+)\/tree\/([^/]+)(?:\/(.+))?$/.exec(rest);
|
|
2022
|
+
if (tree !== null) {
|
|
2023
|
+
ref = tree[3] ?? ref;
|
|
2024
|
+
return github(`${tree[1]}/${tree[2]}`, ref, tree[4] ?? null, withSkill(named), all);
|
|
2025
|
+
}
|
|
2026
|
+
const url = /^https?:\/\/(?:www\.)?github\.com\/([^/]+)\/([^/]+?)\/?$/.exec(rest);
|
|
2027
|
+
if (url !== null)
|
|
2028
|
+
return github(`${url[1]}/${url[2]}`, ref, null, withSkill(named), all);
|
|
2029
|
+
if (rest.includes(":"))
|
|
2030
|
+
return elsewhere;
|
|
2031
|
+
const at = /^([^/]+)\/([^/@]+)@(.+)$/.exec(rest);
|
|
2032
|
+
if (at !== null)
|
|
2033
|
+
return github(`${at[1]}/${at[2]}`, ref, null, [fragmentSkill ?? at[3] ?? ""], false);
|
|
2034
|
+
const shorthand = /^([^/]+)\/([^/]+?)(?:\/(.+?))?\/?$/.exec(rest);
|
|
2035
|
+
if (shorthand !== null && NAME.test(shorthand[1] ?? "") && NAME.test(shorthand[2] ?? "")) {
|
|
2036
|
+
return github(`${shorthand[1]}/${shorthand[2]}`, ref, shorthand[3] ?? null, withSkill(named), all);
|
|
2037
|
+
}
|
|
2038
|
+
return elsewhere;
|
|
2039
|
+
}
|
|
2040
|
+
function skillInstallOf2(command) {
|
|
2041
|
+
for (const words of shellWords(command)) {
|
|
2042
|
+
const args = addArguments(words);
|
|
2043
|
+
if (args === null)
|
|
2044
|
+
continue;
|
|
2045
|
+
const add = parseAdd(args);
|
|
2046
|
+
if (add.list || add.source === null)
|
|
2047
|
+
continue;
|
|
2048
|
+
return sourceOf(add.source, add.skills, add.all);
|
|
2049
|
+
}
|
|
2050
|
+
return null;
|
|
2051
|
+
}
|
|
2052
|
+
async function namesUnder(install, source) {
|
|
2053
|
+
const listing = await source.listing(install.repo);
|
|
2054
|
+
if (listing === "rate_limited" || listing === "unreachable")
|
|
2055
|
+
return listing;
|
|
2056
|
+
const prefix = install.subpath === null ? "" : `${install.subpath}/`;
|
|
2057
|
+
const files = skillFilesIn2(listing.files).filter((file) => file.path.startsWith(prefix)).sort((a, b) => a.path.split("/").length - b.path.split("/").length || a.path.localeCompare(b.path));
|
|
2058
|
+
const names = await Promise.all(files.slice(0, MAX_SKILLS_READ2).map(async (file) => {
|
|
2059
|
+
const markdown = await source.file(install.repo, file.path);
|
|
2060
|
+
const parts = file.path.split("/");
|
|
2061
|
+
return (markdown === null ? null : frontmatterName2(markdown)) ?? parts.at(-2) ?? install.repo.split("/")[1] ?? "";
|
|
2062
|
+
}));
|
|
2063
|
+
return { names, total: files.length };
|
|
2064
|
+
}
|
|
2065
|
+
async function readSkillInstall2(install, source, deadline) {
|
|
2066
|
+
if (install.kind === "elsewhere")
|
|
2067
|
+
return install;
|
|
2068
|
+
const late = deadline.then(() => "late");
|
|
2069
|
+
let names;
|
|
2070
|
+
let total;
|
|
2071
|
+
if (install.skills === "all") {
|
|
2072
|
+
const found = await Promise.race([
|
|
2073
|
+
namesUnder(install, source).catch(() => "unreachable"),
|
|
2074
|
+
late
|
|
2075
|
+
]);
|
|
2076
|
+
if (found === "late")
|
|
2077
|
+
return {
|
|
2078
|
+
kind: "github",
|
|
2079
|
+
install,
|
|
2080
|
+
readings: [],
|
|
2081
|
+
notRead: 0,
|
|
2082
|
+
listing: "no answer in time"
|
|
2083
|
+
};
|
|
2084
|
+
if (found === "rate_limited" || found === "unreachable") {
|
|
2085
|
+
return { kind: "github", install, readings: [], notRead: 0, listing: found };
|
|
2086
|
+
}
|
|
2087
|
+
({ names, total } = found);
|
|
2088
|
+
} else {
|
|
2089
|
+
names = install.skills.slice(0, MAX_SKILLS_READ2);
|
|
2090
|
+
total = install.skills.length;
|
|
2091
|
+
}
|
|
2092
|
+
const readings = await Promise.all(names.map(async (skill) => {
|
|
2093
|
+
const pulse = readPulse2({ source: install.repo, skill, installs: null, installsText: null, url: null }, source).then((read) => ({ skill, pulse: read })).catch(() => ({ skill, pulse: null, why: "read failed" }));
|
|
2094
|
+
const settled = await Promise.race([pulse, late]);
|
|
2095
|
+
return settled === "late" ? { skill, pulse: null, why: "no answer in time" } : settled;
|
|
2096
|
+
}));
|
|
2097
|
+
return {
|
|
2098
|
+
kind: "github",
|
|
2099
|
+
install,
|
|
2100
|
+
readings,
|
|
2101
|
+
notRead: Math.max(0, total - names.length),
|
|
2102
|
+
listing: null
|
|
2103
|
+
};
|
|
2104
|
+
}
|
|
2105
|
+
var plural = (count, word) => `${count} ${word}${count === 1 ? "" : "s"}`;
|
|
2106
|
+
function readingLines(repo, reading) {
|
|
2107
|
+
const head = `${repo} · ${reading.skill}`;
|
|
2108
|
+
const { pulse } = reading;
|
|
2109
|
+
if (pulse === null)
|
|
2110
|
+
return [
|
|
2111
|
+
`${head}: not read (${reading.why === "no answer in time" ? "GitHub did not answer in time" : "the read failed"})`
|
|
2112
|
+
];
|
|
2113
|
+
switch (pulse.status) {
|
|
2114
|
+
case "read": {
|
|
2115
|
+
const facts = [
|
|
2116
|
+
`${pulse.path}`,
|
|
2117
|
+
plural(pulse.files, "file"),
|
|
2118
|
+
...pulse.repository === null ? [] : [
|
|
2119
|
+
plural(pulse.repository.stars, "star"),
|
|
2120
|
+
...pulse.repository.pushedAt === null ? [] : [`last push ${pulse.repository.pushedAt.slice(0, 10)}`],
|
|
2121
|
+
...pulse.repository.archived ? ["archived"] : []
|
|
2122
|
+
]
|
|
2123
|
+
];
|
|
2124
|
+
return [
|
|
2125
|
+
`${head} (${facts.join(", ")})`,
|
|
2126
|
+
...contentsLines2(pulse.contains).map((line) => ` ${line}`),
|
|
2127
|
+
...pulse.differsAt.length === 0 ? [] : [` Other files carry this name and differ: ${pulse.differsAt.join(", ")}`]
|
|
2128
|
+
];
|
|
2129
|
+
}
|
|
2130
|
+
case "read_file_only":
|
|
2131
|
+
return [
|
|
2132
|
+
`${head} (${pulse.path}; GitHub's hourly limit was reached, so SKILL.md alone was read, not its scripts)`,
|
|
2133
|
+
...contentsLines2(pulse.contains).map((line) => ` ${line}`)
|
|
2134
|
+
];
|
|
2135
|
+
case "absent":
|
|
2136
|
+
return [
|
|
2137
|
+
`${head}: no SKILL.md in the repository names it (${plural(pulse.skillFiles, "skill file")} read)`
|
|
2138
|
+
];
|
|
2139
|
+
case "ambiguous":
|
|
2140
|
+
return [
|
|
2141
|
+
`${head}: several files carry this name and differ, and which one installs is not knowable here: ${pulse.paths.join(", ")}`
|
|
2142
|
+
];
|
|
2143
|
+
case "incomplete":
|
|
2144
|
+
return [
|
|
2145
|
+
`${head}: not read (${pulse.reason === "truncated_listing" ? "GitHub cut the repository's file list" : "too many skill files to search"})`
|
|
2146
|
+
];
|
|
2147
|
+
case "rate_limited":
|
|
2148
|
+
return [`${head}: not read (GitHub's hourly limit; GITHUB_TOKEN raises it)`];
|
|
2149
|
+
case "repository_unreachable":
|
|
2150
|
+
return [`${head}: not read (the repository did not answer)`];
|
|
2151
|
+
case "not_github":
|
|
2152
|
+
return [`${head}: not read (not a GitHub repository)`];
|
|
2153
|
+
}
|
|
2154
|
+
}
|
|
2155
|
+
function skillInstallReport2(read) {
|
|
2156
|
+
const title = "ARP read this skill before the install (what its own files contain; facts, not a verdict):";
|
|
2157
|
+
if (read.kind === "elsewhere") {
|
|
2158
|
+
return `${title}
|
|
2159
|
+
${read.source}: not read (ARP reads GitHub sources only)`;
|
|
2160
|
+
}
|
|
2161
|
+
const { install } = read;
|
|
2162
|
+
const lines = [title];
|
|
2163
|
+
if (read.listing !== null) {
|
|
2164
|
+
const why = read.listing === "rate_limited" ? "GitHub's hourly limit; GITHUB_TOKEN raises it" : read.listing === "no answer in time" ? "GitHub did not answer in time" : "the repository did not answer";
|
|
2165
|
+
lines.push(`${install.repo}${install.subpath === null ? "" : `/${install.subpath}`}: not read (${why})`);
|
|
2166
|
+
} else if (read.readings.length === 0) {
|
|
2167
|
+
lines.push(`${install.repo}${install.subpath === null ? "" : `/${install.subpath}`}: no SKILL.md found`);
|
|
2168
|
+
}
|
|
2169
|
+
for (const reading of read.readings)
|
|
2170
|
+
lines.push(...readingLines(install.repo, reading));
|
|
2171
|
+
if (read.notRead > 0)
|
|
2172
|
+
lines.push(`${plural(read.notRead, "more skill")} in this install not read (ARP reads ${MAX_SKILLS_READ2})`);
|
|
2173
|
+
if (install.ref !== null)
|
|
2174
|
+
lines.push(`Read at the default branch's HEAD; the command names ${install.ref}.`);
|
|
2175
|
+
return lines.join(`
|
|
2176
|
+
`);
|
|
2177
|
+
}
|
|
2178
|
+
|
|
2179
|
+
// ../hook/src/pre-install.ts
|
|
2180
|
+
var GITHUB_BUDGET_MS = 6000;
|
|
2181
|
+
var OUTPUT_CAP = 9500;
|
|
2182
|
+
var FOR_THE_AGENT = "This is what the skill's own files contain, read by ARP's hook before the install ran. " + "They are facts, not a verdict, and nothing was blocked. The person was shown the same lines.";
|
|
2183
|
+
function capped(text) {
|
|
2184
|
+
return text.length <= OUTPUT_CAP ? text : `${text.slice(0, OUTPUT_CAP)}
|
|
2185
|
+
… (cut)`;
|
|
2186
|
+
}
|
|
2187
|
+
async function preToolUse(raw, options = {}) {
|
|
2188
|
+
const call = toolCallOf(raw);
|
|
2189
|
+
if (call === null || call.tool !== "Bash")
|
|
2190
|
+
return null;
|
|
2191
|
+
const command = call.input?.["command"];
|
|
2192
|
+
if (typeof command !== "string")
|
|
2193
|
+
return null;
|
|
2194
|
+
const install = skillInstallOf2(command);
|
|
2195
|
+
if (install === null)
|
|
2196
|
+
return null;
|
|
2197
|
+
const env = options.env ?? process.env;
|
|
2198
|
+
const token = (env["GITHUB_TOKEN"] ?? env["GH_TOKEN"])?.trim() || undefined;
|
|
2199
|
+
const budgetMs = options.budgetMs ?? GITHUB_BUDGET_MS;
|
|
2200
|
+
const deadline = new Promise((resolve) => setTimeout(resolve, budgetMs));
|
|
2201
|
+
const source = githubPulseSource2({
|
|
2202
|
+
...options.fetch === undefined ? {} : { fetch: options.fetch },
|
|
2203
|
+
...token === undefined ? {} : { token },
|
|
2204
|
+
timeoutMs: budgetMs + 500,
|
|
2205
|
+
signal: AbortSignal.timeout(budgetMs + 500)
|
|
2206
|
+
});
|
|
2207
|
+
const report = capped(skillInstallReport2(await readSkillInstall2(install, source, deadline)));
|
|
2208
|
+
return JSON.stringify({
|
|
2209
|
+
systemMessage: report,
|
|
2210
|
+
hookSpecificOutput: {
|
|
2211
|
+
hookEventName: "PreToolUse",
|
|
2212
|
+
additionalContext: `${report}
|
|
2213
|
+
|
|
2214
|
+
${FOR_THE_AGENT}`
|
|
2215
|
+
}
|
|
2216
|
+
});
|
|
2217
|
+
}
|
|
2218
|
+
|
|
2219
|
+
// ../hook/src/failure-answer.ts
|
|
2220
|
+
import { createHash } from "node:crypto";
|
|
2221
|
+
|
|
2222
|
+
// ../../packages/protocol/src/declare/line-need.ts
|
|
2223
|
+
var NEEDS_LABEL3 = "needs";
|
|
2224
|
+
var WITH_LABEL3 = "with";
|
|
2225
|
+
var PROGRAM_NAME3 = /^[a-z0-9][a-z0-9._+-]{0,39}$/;
|
|
2226
|
+
var KEY_NAME4 = /^[A-Z][A-Z0-9_]{2,63}$/;
|
|
2227
|
+
function needKindOf3(name) {
|
|
2228
|
+
if (PROGRAM_NAME3.test(name))
|
|
2229
|
+
return "program";
|
|
2230
|
+
if (KEY_NAME4.test(name))
|
|
2231
|
+
return "key";
|
|
2232
|
+
return null;
|
|
2233
|
+
}
|
|
2234
|
+
function needKindOfReason3(reason) {
|
|
2235
|
+
if (reason === "missing-program")
|
|
2236
|
+
return "program";
|
|
2237
|
+
if (reason === "missing-key")
|
|
2238
|
+
return "key";
|
|
2239
|
+
return null;
|
|
2240
|
+
}
|
|
2241
|
+
var escaped = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2242
|
+
function namedInSkill3(name, markdown) {
|
|
2243
|
+
const kind = needKindOf3(name);
|
|
2244
|
+
if (kind === null)
|
|
2245
|
+
return false;
|
|
2246
|
+
const pattern = new RegExp(`(?<![A-Za-z0-9_+.-])${escaped(name)}(?![A-Za-z0-9_+-])(?!\\.[A-Za-z0-9])`, kind === "program" ? "i" : "");
|
|
2247
|
+
return pattern.test(markdown);
|
|
2248
|
+
}
|
|
2249
|
+
function publishableNeed3(name, markdown, kind) {
|
|
2250
|
+
const shape = needKindOf3(name);
|
|
2251
|
+
if (shape === null || kind !== undefined && shape !== kind)
|
|
2252
|
+
return null;
|
|
2253
|
+
return namedInSkill3(name, markdown) ? name : null;
|
|
2254
|
+
}
|
|
2255
|
+
|
|
2256
|
+
// ../../packages/protocol/src/declare/thing-cid.ts
|
|
2257
|
+
function thingDocument2(thing) {
|
|
2258
|
+
return JSON.stringify({
|
|
2259
|
+
"@context": "https://schema.org",
|
|
2260
|
+
"@type": "Thing",
|
|
2261
|
+
description: thing.description,
|
|
2262
|
+
image: thing.image,
|
|
2263
|
+
name: thing.name,
|
|
2264
|
+
url: thing.url
|
|
2265
|
+
});
|
|
2266
|
+
}
|
|
2267
|
+
function base32(bytes) {
|
|
2268
|
+
const alphabet = "abcdefghijklmnopqrstuvwxyz234567";
|
|
2269
|
+
let bits = 0;
|
|
2270
|
+
let value = 0;
|
|
2271
|
+
let out = "";
|
|
2272
|
+
for (const byte of bytes) {
|
|
2273
|
+
value = value << 8 | byte;
|
|
2274
|
+
bits += 8;
|
|
2275
|
+
while (bits >= 5) {
|
|
2276
|
+
out += alphabet[value >>> bits - 5 & 31];
|
|
2277
|
+
bits -= 5;
|
|
2278
|
+
}
|
|
2279
|
+
}
|
|
2280
|
+
if (bits > 0)
|
|
2281
|
+
out += alphabet[value << 5 - bits & 31];
|
|
2282
|
+
return out;
|
|
2283
|
+
}
|
|
2284
|
+
function thingUriOfDigest(digest) {
|
|
2285
|
+
const cid = new Uint8Array(4 + digest.length);
|
|
2286
|
+
cid.set([1, 85, 18, 32]);
|
|
2287
|
+
cid.set(digest, 4);
|
|
2288
|
+
return `ipfs://b${base32(cid)}`;
|
|
2289
|
+
}
|
|
2290
|
+
|
|
2291
|
+
// ../../packages/protocol/src/declare/tool-thing.ts
|
|
2292
|
+
class InvalidToolUrlError2 extends Error {
|
|
2293
|
+
input;
|
|
2294
|
+
constructor(input, why) {
|
|
2295
|
+
super(`${JSON.stringify(input)} is not a usable tool URL: ${why}`);
|
|
2296
|
+
this.name = "InvalidToolUrlError";
|
|
2297
|
+
this.input = input;
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2300
|
+
var TOOL_THING_DESCRIPTION2 = "A tool identified by its URL, so every declaration of its use lands on one atom.";
|
|
2301
|
+
function normaliseToolUrl2(input) {
|
|
2302
|
+
const trimmed = input.trim();
|
|
2303
|
+
if (trimmed.length === 0)
|
|
2304
|
+
throw new InvalidToolUrlError2(input, "empty");
|
|
2305
|
+
const withScheme = /^[a-z][a-z0-9+.-]*:/i.test(trimmed) ? trimmed : `https://${trimmed}`;
|
|
2306
|
+
let url;
|
|
2307
|
+
try {
|
|
2308
|
+
url = new URL(withScheme);
|
|
2309
|
+
} catch {
|
|
2310
|
+
throw new InvalidToolUrlError2(input, "not parseable as a URL");
|
|
2311
|
+
}
|
|
2312
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
2313
|
+
throw new InvalidToolUrlError2(input, `scheme ${url.protocol} is not http or https`);
|
|
2314
|
+
}
|
|
2315
|
+
if (url.hostname.length === 0)
|
|
2316
|
+
throw new InvalidToolUrlError2(input, "no host");
|
|
2317
|
+
const host = url.hostname.replace(/\.+$/, "").replace(/^(?:www\.)+/, "");
|
|
2318
|
+
const path = url.pathname.replace(/(?:\.git|\/)+$/, "");
|
|
2319
|
+
const params = [...new URLSearchParams(url.search).entries()].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
|
|
2320
|
+
const query = params.length === 0 ? "" : `?${new URLSearchParams(params).toString()}`;
|
|
2321
|
+
const port = url.port === "" || url.port === "80" || url.port === "443" ? "" : `:${url.port}`;
|
|
2322
|
+
return `https://${host}${port}${path}${query}`;
|
|
2323
|
+
}
|
|
2324
|
+
function toolThing2(url) {
|
|
2325
|
+
const normalised = normaliseToolUrl2(url);
|
|
2326
|
+
return {
|
|
2327
|
+
name: normalised.slice("https://".length),
|
|
2328
|
+
description: TOOL_THING_DESCRIPTION2,
|
|
2329
|
+
image: "",
|
|
2330
|
+
url: normalised
|
|
2331
|
+
};
|
|
2332
|
+
}
|
|
2333
|
+
|
|
2334
|
+
// ../../packages/protocol/src/declare/usage-words.ts
|
|
2335
|
+
var USAGE_OUTCOMES3 = ["worked", "failed"];
|
|
2336
|
+
var HAS_OUTCOME_LABEL3 = "has outcome";
|
|
2337
|
+
var USAGE_REASONS3 = ["missing-key", "missing-program", "missing-file", "not-the-right-tool", "no-delivery"];
|
|
2338
|
+
var BECAUSE_LABEL3 = "because";
|
|
2339
|
+
|
|
2340
|
+
// ../../packages/protocol/src/sources/intuition/terms.ts
|
|
2341
|
+
var USES2 = "0x5c0bde1cc696456c0268248c4656acdf9621fdb39e605bc99b0a83dc8ff6e800";
|
|
2342
|
+
|
|
2343
|
+
// ../../packages/protocol/src/pulse-usage.ts
|
|
2344
|
+
var DEFAULT_LIMIT = 250;
|
|
2345
|
+
var rowsOf = (data) => Array.isArray(data["triples"]) ? data["triples"].filter(isRecord) : [];
|
|
2346
|
+
async function findToolAtoms2(uris, config) {
|
|
2347
|
+
const out = new Map;
|
|
2348
|
+
if (uris.length === 0)
|
|
2349
|
+
return out;
|
|
2350
|
+
const transport = config.transport ?? createGraphqlTransport({
|
|
2351
|
+
url: config.graphqlUrl,
|
|
2352
|
+
fetch: config.fetch ?? defaultFetch(),
|
|
2353
|
+
timeoutMs: config.timeoutMs ?? DEFAULT_TIMEOUT_MS
|
|
2354
|
+
});
|
|
2355
|
+
const data = await transport.request(PULSE_ATOMS_QUERY, { uris: [...uris], limit: uris.length });
|
|
2356
|
+
const rows = Array.isArray(data["atoms"]) ? data["atoms"].filter(isRecord) : [];
|
|
2357
|
+
for (const row of rows) {
|
|
2358
|
+
const id = readString(row, "term_id");
|
|
2359
|
+
const uri = readString(row, "data");
|
|
2360
|
+
if (id !== null && uri !== null)
|
|
2361
|
+
out.set(uri, id);
|
|
2362
|
+
}
|
|
2363
|
+
return out;
|
|
2364
|
+
}
|
|
2365
|
+
async function getPulseUsage2(toolAtomIds, config) {
|
|
2366
|
+
const transport = config.transport ?? createGraphqlTransport({
|
|
2367
|
+
url: config.graphqlUrl,
|
|
2368
|
+
fetch: config.fetch ?? defaultFetch(),
|
|
2369
|
+
timeoutMs: config.timeoutMs ?? DEFAULT_TIMEOUT_MS
|
|
2370
|
+
});
|
|
2371
|
+
const limit = Math.min(config.limit ?? DEFAULT_LIMIT, DEFAULT_LIMIT);
|
|
2372
|
+
const key = (id) => id.toLowerCase();
|
|
2373
|
+
const empty = () => ({
|
|
2374
|
+
declarations: [],
|
|
2375
|
+
agents: 0,
|
|
2376
|
+
wallets: 0,
|
|
2377
|
+
byDomain: {},
|
|
2378
|
+
byOutcome: {},
|
|
2379
|
+
byReason: {},
|
|
2380
|
+
byNeed: {},
|
|
2381
|
+
byWith: {},
|
|
2382
|
+
byOthers: [],
|
|
2383
|
+
truncated: false
|
|
2384
|
+
});
|
|
2385
|
+
const out = new Map(toolAtomIds.map((id) => [key(id), empty()]));
|
|
2386
|
+
if (toolAtomIds.length === 0)
|
|
2387
|
+
return out;
|
|
2388
|
+
const edgeRows = rowsOf(await transport.request(PULSE_USES_QUERY, { tools: [...toolAtomIds], uses: USES2, limit }));
|
|
2389
|
+
const edges = new Map;
|
|
2390
|
+
const NESTED_CAP = 100;
|
|
2391
|
+
let truncated = edgeRows.length >= limit;
|
|
2392
|
+
for (const row of edgeRows) {
|
|
2393
|
+
const tripleId = readString(row, "term_id");
|
|
2394
|
+
const tool = readString(row, "object_id");
|
|
2395
|
+
const writer = readString(row, "creator_id");
|
|
2396
|
+
const subject = readRecord(row, "subject");
|
|
2397
|
+
const atomId = subject === null ? null : readString(subject, "term_id");
|
|
2398
|
+
if (tripleId === null || tool === null || writer === null || subject === null || atomId === null)
|
|
2399
|
+
continue;
|
|
2400
|
+
const edgeHolders = (Array.isArray(row["positions"]) ? row["positions"].filter(isRecord) : []).map((holder) => readString(holder, "account_id")).filter((account) => account !== null);
|
|
2401
|
+
const declaration = (wallet, declaredAt) => ({
|
|
2402
|
+
tripleId,
|
|
2403
|
+
agent: { atomId, label: readString(subject, "label") },
|
|
2404
|
+
writer: wallet,
|
|
2405
|
+
holders: edgeHolders,
|
|
2406
|
+
declaredAt,
|
|
2407
|
+
domains: [],
|
|
2408
|
+
outcomes: [],
|
|
2409
|
+
lines: []
|
|
2410
|
+
});
|
|
2411
|
+
const declarers = new Map([[key(writer), declaration(writer, readString(row, "created_at"))]]);
|
|
2412
|
+
const holders = Array.isArray(row["positions"]) ? row["positions"].filter(isRecord) : [];
|
|
2413
|
+
if (holders.length >= NESTED_CAP)
|
|
2414
|
+
truncated = true;
|
|
2415
|
+
for (const holder of holders) {
|
|
2416
|
+
const wallet = readString(holder, "account_id");
|
|
2417
|
+
if (wallet !== null && !declarers.has(key(wallet)))
|
|
2418
|
+
declarers.set(key(wallet), declaration(wallet, null));
|
|
2419
|
+
}
|
|
2420
|
+
edges.set(key(tripleId), { tool: key(tool), declarers });
|
|
2421
|
+
}
|
|
2422
|
+
const payers = new Set(config.payers.map(key));
|
|
2423
|
+
const writers = [...new Set([...edges.values()].flatMap((edge) => [...edge.declarers.values()].map((row) => row.writer)))];
|
|
2424
|
+
const qualifiers = async (subjects, predicates) => subjects.length === 0 ? [] : rowsOf(await transport.request(PULSE_QUALIFIERS_QUERY, { edges: subjects, predicates, writers, limit }));
|
|
2425
|
+
const pass = (rows) => {
|
|
2426
|
+
truncated = truncated || rows.length >= limit;
|
|
2427
|
+
return rows;
|
|
2428
|
+
};
|
|
2429
|
+
const standing = (row, edge, below) => {
|
|
2430
|
+
const holders = Array.isArray(row["positions"]) ? row["positions"].filter(isRecord) : [];
|
|
2431
|
+
if (holders.length >= NESTED_CAP)
|
|
2432
|
+
truncated = true;
|
|
2433
|
+
const wallets = [readString(row, "creator_id"), ...holders.map((holder) => readString(holder, "account_id"))];
|
|
2434
|
+
return new Set(wallets.filter((wallet) => wallet !== null).map(key).filter((wallet) => edge.declarers.has(wallet) && !payers.has(wallet) && (below === undefined || below.has(wallet))));
|
|
2435
|
+
};
|
|
2436
|
+
const parts = (row) => {
|
|
2437
|
+
const object = readRecord(row, "object");
|
|
2438
|
+
return {
|
|
2439
|
+
termId: readString(row, "term_id"),
|
|
2440
|
+
subjectId: readString(row, "subject_id"),
|
|
2441
|
+
predicateId: readString(row, "predicate_id"),
|
|
2442
|
+
writer: readString(row, "creator_id"),
|
|
2443
|
+
value: object === null ? null : readString(object, "label")
|
|
2444
|
+
};
|
|
2445
|
+
};
|
|
2446
|
+
const isOutcome = (value) => USAGE_OUTCOMES3.some((word) => word === value);
|
|
2447
|
+
const isReason = (value) => USAGE_REASONS3.some((word) => word === value);
|
|
2448
|
+
const contexts = new Map;
|
|
2449
|
+
const outcomes = new Map;
|
|
2450
|
+
const others = (edge, row, predicate) => out.get(edge.tool)?.byOthers.push({
|
|
2451
|
+
tripleId: row.termId ?? "",
|
|
2452
|
+
writer: row.writer ?? "",
|
|
2453
|
+
predicate,
|
|
2454
|
+
value: row.value ?? ""
|
|
2455
|
+
});
|
|
2456
|
+
const onEdge = pass(await qualifiers([...edges.keys()], [config.inContextOf, config.hasOutcome]));
|
|
2457
|
+
for (const row of onEdge) {
|
|
2458
|
+
const line = parts(row);
|
|
2459
|
+
const edge = line.subjectId === null ? undefined : edges.get(key(line.subjectId));
|
|
2460
|
+
if (edge === undefined || line.predicateId === null || line.writer === null || line.value === null || line.termId === null)
|
|
2461
|
+
continue;
|
|
2462
|
+
const isOutcomeLine = key(line.predicateId) === key(config.hasOutcome);
|
|
2463
|
+
if (isOutcomeLine && !isOutcome(line.value))
|
|
2464
|
+
continue;
|
|
2465
|
+
const wallets = standing(row, edge);
|
|
2466
|
+
if (wallets.size === 0) {
|
|
2467
|
+
others(edge, line, isOutcomeLine ? "outcome" : "context");
|
|
2468
|
+
} else if (isOutcomeLine && isOutcome(line.value)) {
|
|
2469
|
+
outcomes.set(key(line.termId), { edge, domain: null, form: "sibling", outcome: line.value, wallets, reasons: new Map, named: new Map });
|
|
2470
|
+
} else {
|
|
2471
|
+
contexts.set(key(line.termId), { edge, domain: line.value, wallets });
|
|
2472
|
+
}
|
|
2473
|
+
}
|
|
2474
|
+
const onContext = pass(await qualifiers([...contexts.keys()], [config.hasOutcome]));
|
|
2475
|
+
for (const row of onContext) {
|
|
2476
|
+
const line = parts(row);
|
|
2477
|
+
const context = line.subjectId === null ? undefined : contexts.get(key(line.subjectId));
|
|
2478
|
+
if (context === undefined || line.value === null || line.termId === null || !isOutcome(line.value))
|
|
2479
|
+
continue;
|
|
2480
|
+
const wallets = standing(row, context.edge, context.wallets);
|
|
2481
|
+
if (wallets.size === 0) {
|
|
2482
|
+
others(context.edge, line, "outcome");
|
|
2483
|
+
} else {
|
|
2484
|
+
outcomes.set(key(line.termId), { edge: context.edge, domain: context.domain, form: "nested", outcome: line.value, wallets, reasons: new Map, named: new Map });
|
|
2485
|
+
}
|
|
2486
|
+
}
|
|
2487
|
+
const reading = (predicate) => predicate !== undefined;
|
|
2488
|
+
const failures = [...outcomes.entries()].filter(([, found]) => found.outcome === "failed").map(([id]) => id);
|
|
2489
|
+
const worked = [...outcomes.entries()].filter(([, found]) => found.outcome === "worked").map(([id]) => id);
|
|
2490
|
+
const withId = config.with;
|
|
2491
|
+
const onOutcome = pass(await qualifiers(reading(withId) ? [...failures, ...worked] : failures, [config.because, ...reading(withId) ? [withId] : []]));
|
|
2492
|
+
const reasonTerms = new Map;
|
|
2493
|
+
const addName = (found, wallets, name) => {
|
|
2494
|
+
for (const wallet of wallets) {
|
|
2495
|
+
const given = found.named.get(wallet) ?? [];
|
|
2496
|
+
if (!given.includes(name))
|
|
2497
|
+
given.push(name);
|
|
2498
|
+
found.named.set(wallet, given);
|
|
2499
|
+
}
|
|
2500
|
+
};
|
|
2501
|
+
for (const row of onOutcome) {
|
|
2502
|
+
const line = parts(row);
|
|
2503
|
+
const found = line.subjectId === null ? undefined : outcomes.get(key(line.subjectId));
|
|
2504
|
+
if (found === undefined || line.value === null || line.predicateId === null)
|
|
2505
|
+
continue;
|
|
2506
|
+
if (withId !== undefined && key(line.predicateId) === key(withId)) {
|
|
2507
|
+
if (found.outcome !== "worked" || needKindOf3(line.value) === null)
|
|
2508
|
+
continue;
|
|
2509
|
+
const wallets2 = standing(row, found.edge, found.wallets);
|
|
2510
|
+
if (wallets2.size === 0)
|
|
2511
|
+
others(found.edge, line, "with");
|
|
2512
|
+
addName(found, wallets2, line.value);
|
|
2513
|
+
continue;
|
|
2514
|
+
}
|
|
2515
|
+
if (found.outcome !== "failed" || !isReason(line.value))
|
|
2516
|
+
continue;
|
|
2517
|
+
const wallets = standing(row, found.edge, found.wallets);
|
|
2518
|
+
if (wallets.size === 0)
|
|
2519
|
+
others(found.edge, line, "reason");
|
|
2520
|
+
for (const wallet of wallets) {
|
|
2521
|
+
const given = found.reasons.get(wallet) ?? [];
|
|
2522
|
+
if (!given.includes(line.value))
|
|
2523
|
+
given.push(line.value);
|
|
2524
|
+
found.reasons.set(wallet, given);
|
|
2525
|
+
}
|
|
2526
|
+
if (line.termId !== null && wallets.size > 0 && needKindOfReason3(line.value) !== null) {
|
|
2527
|
+
reasonTerms.set(key(line.termId), { found, reason: line.value, wallets });
|
|
2528
|
+
}
|
|
2529
|
+
}
|
|
2530
|
+
const needsId = config.needs;
|
|
2531
|
+
if (reading(needsId)) {
|
|
2532
|
+
const onReason = pass(await qualifiers([...reasonTerms.keys()], [needsId]));
|
|
2533
|
+
for (const row of onReason) {
|
|
2534
|
+
const line = parts(row);
|
|
2535
|
+
const reason = line.subjectId === null ? undefined : reasonTerms.get(key(line.subjectId));
|
|
2536
|
+
if (reason === undefined || line.value === null)
|
|
2537
|
+
continue;
|
|
2538
|
+
const kind = needKindOf3(line.value);
|
|
2539
|
+
if (kind === null || kind !== needKindOfReason3(reason.reason))
|
|
2540
|
+
continue;
|
|
2541
|
+
const wallets = standing(row, reason.found.edge, reason.wallets);
|
|
2542
|
+
if (wallets.size === 0)
|
|
2543
|
+
others(reason.found.edge, line, "needs");
|
|
2544
|
+
addName(reason.found, wallets, line.value);
|
|
2545
|
+
}
|
|
2546
|
+
}
|
|
2547
|
+
for (const { edge, domain, wallets } of contexts.values()) {
|
|
2548
|
+
for (const wallet of wallets)
|
|
2549
|
+
edge.declarers.get(wallet)?.domains.push(domain);
|
|
2550
|
+
}
|
|
2551
|
+
const ordered = [...outcomes.values()].sort((a, b) => Number(b.form === "nested") - Number(a.form === "nested"));
|
|
2552
|
+
for (const found of ordered) {
|
|
2553
|
+
for (const wallet of found.wallets) {
|
|
2554
|
+
const declaration = found.edge.declarers.get(wallet);
|
|
2555
|
+
if (declaration === undefined)
|
|
2556
|
+
continue;
|
|
2557
|
+
const reasons = found.reasons.get(wallet) ?? [];
|
|
2558
|
+
const named = found.named.get(wallet) ?? [];
|
|
2559
|
+
const field = found.outcome === "failed" ? "needs" : "with";
|
|
2560
|
+
if (!declaration.outcomes.includes(found.outcome))
|
|
2561
|
+
declaration.outcomes.push(found.outcome);
|
|
2562
|
+
const domains = found.form === "nested" ? [found.domain] : declaration.domains.length > 0 ? declaration.domains : [null];
|
|
2563
|
+
for (const domain of domains) {
|
|
2564
|
+
const same = declaration.lines.find((line) => line.domain === domain && line.outcome === found.outcome);
|
|
2565
|
+
if (same === undefined) {
|
|
2566
|
+
declaration.lines.push({ domain, outcome: found.outcome, reasons, form: found.form, ...named.length === 0 ? {} : { [field]: named } });
|
|
2567
|
+
} else {
|
|
2568
|
+
same.reasons = [...new Set([...same.reasons, ...reasons])];
|
|
2569
|
+
if (named.length > 0)
|
|
2570
|
+
same[field] = [...new Set([...same[field] ?? [], ...named])];
|
|
2571
|
+
}
|
|
2572
|
+
}
|
|
2573
|
+
}
|
|
2574
|
+
}
|
|
2575
|
+
for (const { tool, declarers } of edges.values()) {
|
|
2576
|
+
const rows = [...declarers.values()].filter((row, at) => at === 0 || row.domains.length > 0 || row.lines.length > 0);
|
|
2577
|
+
out.get(tool)?.declarations.push(...rows);
|
|
2578
|
+
}
|
|
2579
|
+
for (const usage of out.values()) {
|
|
2580
|
+
usage.truncated = truncated;
|
|
2581
|
+
usage.agents = new Set(usage.declarations.map((declaration) => key(declaration.agent.atomId))).size;
|
|
2582
|
+
usage.wallets = new Set(usage.declarations.map((declaration) => key(declaration.writer)).filter((wallet) => !payers.has(wallet))).size;
|
|
2583
|
+
for (const declaration of usage.declarations) {
|
|
2584
|
+
for (const domain of declaration.domains)
|
|
2585
|
+
usage.byDomain[domain] = (usage.byDomain[domain] ?? 0) + 1;
|
|
2586
|
+
for (const outcome of declaration.outcomes)
|
|
2587
|
+
usage.byOutcome[outcome] = (usage.byOutcome[outcome] ?? 0) + 1;
|
|
2588
|
+
for (const reason of new Set(declaration.lines.flatMap((line) => line.reasons))) {
|
|
2589
|
+
usage.byReason[reason] = (usage.byReason[reason] ?? 0) + 1;
|
|
2590
|
+
}
|
|
2591
|
+
const byNeed = usage.byNeed ??= {};
|
|
2592
|
+
for (const name of new Set(declaration.lines.flatMap((line) => line.needs ?? [])))
|
|
2593
|
+
byNeed[name] = (byNeed[name] ?? 0) + 1;
|
|
2594
|
+
const byWith = usage.byWith ??= {};
|
|
2595
|
+
for (const name of new Set(declaration.lines.flatMap((line) => line.with ?? [])))
|
|
2596
|
+
byWith[name] = (byWith[name] ?? 0) + 1;
|
|
2597
|
+
}
|
|
2598
|
+
}
|
|
2599
|
+
return out;
|
|
2600
|
+
}
|
|
2601
|
+
|
|
2602
|
+
// ../../packages/protocol/src/failure-lines.ts
|
|
2603
|
+
function skillUrlOf2(source, skill) {
|
|
2604
|
+
if (!/^[\w.-]+\/[\w.-]+$/.test(source))
|
|
2605
|
+
return null;
|
|
2606
|
+
const name = skill.split(":").pop() ?? skill;
|
|
2607
|
+
const url = `https://github.com/${source}?skill=${encodeURIComponent(name)}`;
|
|
2608
|
+
return skillRefFromUrl3(url) === null ? null : url;
|
|
2609
|
+
}
|
|
2610
|
+
function skillAtom(url, sha256) {
|
|
2611
|
+
const thing = toolThing2(url);
|
|
2612
|
+
return { uri: thingUriOfDigest(sha256(thingDocument2(thing))), label: thing.name };
|
|
2613
|
+
}
|
|
2614
|
+
async function readSkillLines(atomUri, config) {
|
|
2615
|
+
const words = [HAS_OUTCOME_LABEL3, BECAUSE_LABEL3, NEEDS_LABEL3, WITH_LABEL3];
|
|
2616
|
+
const atoms = await findToolAtoms2([atomUri, ...words], config);
|
|
2617
|
+
const tool = atoms.get(atomUri);
|
|
2618
|
+
const hasOutcome = atoms.get(HAS_OUTCOME_LABEL3);
|
|
2619
|
+
const because = atoms.get(BECAUSE_LABEL3);
|
|
2620
|
+
if (tool === undefined || hasOutcome === undefined || because === undefined)
|
|
2621
|
+
return null;
|
|
2622
|
+
const needs = atoms.get(NEEDS_LABEL3);
|
|
2623
|
+
const withId = atoms.get(WITH_LABEL3);
|
|
2624
|
+
const usage = await getPulseUsage2([tool], {
|
|
2625
|
+
...config,
|
|
2626
|
+
hasOutcome,
|
|
2627
|
+
because,
|
|
2628
|
+
...needs === undefined ? {} : { needs },
|
|
2629
|
+
...withId === undefined ? {} : { with: withId }
|
|
2630
|
+
});
|
|
2631
|
+
return usage.get(tool.toLowerCase()) ?? null;
|
|
2632
|
+
}
|
|
2633
|
+
function counted(counts) {
|
|
2634
|
+
return [...counts.entries()].sort(([a, x], [b, y]) => y.size - x.size || a.localeCompare(b)).map(([name, wallets]) => `${name} (${wallets.size})`).join(", ");
|
|
2635
|
+
}
|
|
2636
|
+
var plural2 = (n) => `${n} wallet${n === 1 ? "" : "s"}`;
|
|
2637
|
+
function linesSentence(usage, label, where) {
|
|
2638
|
+
if (usage === null)
|
|
2639
|
+
return null;
|
|
2640
|
+
const parts = [];
|
|
2641
|
+
for (const outcome of ["failed", "worked"]) {
|
|
2642
|
+
const wallets = new Set;
|
|
2643
|
+
const because = new Map;
|
|
2644
|
+
const named = new Map;
|
|
2645
|
+
const add = (to, name, wallet) => to.set(name, (to.get(name) ?? new Set).add(wallet));
|
|
2646
|
+
for (const row of usage.declarations) {
|
|
2647
|
+
const wallet = row.writer.toLowerCase();
|
|
2648
|
+
for (const line of row.lines) {
|
|
2649
|
+
if (line.outcome !== outcome)
|
|
2650
|
+
continue;
|
|
2651
|
+
wallets.add(wallet);
|
|
2652
|
+
for (const reason of line.reasons)
|
|
2653
|
+
add(because, reason, wallet);
|
|
2654
|
+
for (const name of (outcome === "failed" ? line.needs : line.with) ?? [])
|
|
2655
|
+
add(named, name, wallet);
|
|
2656
|
+
}
|
|
2657
|
+
}
|
|
2658
|
+
if (wallets.size === 0)
|
|
2659
|
+
continue;
|
|
2660
|
+
const detail = [
|
|
2661
|
+
...because.size === 0 ? [] : [`because ${counted(because)}`],
|
|
2662
|
+
...named.size === 0 ? [] : [`${outcome === "failed" ? "needs" : "with"} ${counted(named)}`]
|
|
2663
|
+
];
|
|
2664
|
+
parts.push([`${usage.truncated ? "at least " : ""}${plural2(wallets.size)} wrote ${outcome}`, ...detail].join(" · "));
|
|
2665
|
+
}
|
|
2666
|
+
if (parts.length === 0)
|
|
2667
|
+
return null;
|
|
2668
|
+
return `ARP, lines agents wrote on ${label} (${where}): ${parts.join("; ")}.`;
|
|
2669
|
+
}
|
|
2670
|
+
// ../../data/deployments/13579.json
|
|
2671
|
+
var _13579_default = {
|
|
2672
|
+
chain: {
|
|
2673
|
+
name: "Intuition Testnet",
|
|
2674
|
+
chainId: 13579,
|
|
2675
|
+
chainIdHex: "0x350b",
|
|
2676
|
+
nativeCurrency: {
|
|
2677
|
+
name: "Test Trust",
|
|
2678
|
+
symbol: "tTRUST",
|
|
2679
|
+
decimals: 18
|
|
2680
|
+
},
|
|
2681
|
+
rpcUrl: "https://testnet.rpc.intuition.systems/http",
|
|
2682
|
+
explorerUrl: "https://testnet.explorer.intuition.systems",
|
|
2683
|
+
graphqlUrl: "https://testnet.intuition.sh/v1/graphql",
|
|
2684
|
+
_source: [
|
|
2685
|
+
"Intuition skill: .claude/skills/intuition/SKILL.md (table at line 149, Viem chain config at line 184)",
|
|
2686
|
+
"MetaMask deployments: ~/Project/metamask-smart-accounts-kit/packages/delegation-deployments/src/index.ts (CHAIN_ID.intuitionTestnet = 0x350b)"
|
|
2687
|
+
]
|
|
2688
|
+
},
|
|
2689
|
+
intuition: {
|
|
2690
|
+
_source: ".claude/skills/intuition/SKILL.md line 152",
|
|
2691
|
+
multiVault: "0x2Ece8D4dEdcB9918A398528f3fa4688b1d2CAB91"
|
|
2692
|
+
},
|
|
2693
|
+
metamaskDelegationFramework: {
|
|
2694
|
+
version: "1.3.0",
|
|
2695
|
+
_source: "MetaMask deployments package (~/Project/metamask-smart-accounts-kit/packages/delegation-deployments/src/contractAddresses.ts → DEPLOYMENTS_1_3_0). All addresses are deterministic CREATE2 — same value on every chain MetaMask supports. Intuition Testnet (chainId 0x350b) is explicitly mapped to DEPLOYMENTS_1_3_0 at packages/delegation-deployments/src/index.ts line 148.",
|
|
2696
|
+
delegationManager: "0xdb9B1e94B5b69Df7e401DDbedE43491141047dB3",
|
|
2697
|
+
entryPoint: "0x0000000071727De22E5E9d8BAf0edAc6f37da032",
|
|
2698
|
+
simpleFactory: "0x69Aa2f9fe1572F1B640E1bbc512f5c3a734fc77c",
|
|
2699
|
+
implementations: {
|
|
2700
|
+
hybridDeleGator: "0x48dBe696A4D990079e039489bA2053B36E8FFEC4",
|
|
2701
|
+
multiSigDeleGator: "0x56a9EdB16a0105eb5a4C54f4C062e2868844f3A7",
|
|
2702
|
+
eip7702StatelessDeleGator: "0x63c0c19a282a1B52b07dD5a65b58948A07DAE32B"
|
|
2703
|
+
},
|
|
2704
|
+
enforcers: {
|
|
2705
|
+
_note: "Standard caveat enforcers. ARP composes its custom DomainScopeEnforcer + TrustStakeCapEnforcer alongside a subset of these.",
|
|
2706
|
+
allowedCalldata: "0xc2b0d624c1c4319760C96503BA27C347F3260f55",
|
|
2707
|
+
allowedMethods: "0x2c21fD0Cb9DC8445CB3fb0DC5E7Bb0Aca01842B5",
|
|
2708
|
+
allowedTargets: "0x7F20f61b1f09b08D970938F6fa563634d65c4EeB",
|
|
2709
|
+
approvalRevocation: "0xe264F1f09A19505a1ca1a86D5b01E8bFdb64324A",
|
|
2710
|
+
blockNumber: "0x5d9818dF0AE3f66e9c3D0c5029DAF99d1823ca6c",
|
|
2711
|
+
deployed: "0x24ff2AA430D53a8CD6788018E902E098083dcCd2",
|
|
2712
|
+
erc20BalanceChange: "0xcdF6aB796408598Cea671d79506d7D48E97a5437",
|
|
2713
|
+
erc20TransferAmount: "0xf100b0819427117EcF76Ed94B358B1A5b5C6D2Fc",
|
|
2714
|
+
erc20PeriodTransfer: "0x474e3Ae7E169e940607cC624Da8A15Eb120139aB",
|
|
2715
|
+
erc20Streaming: "0x56c97aE02f233B29fa03502Ecc0457266d9be00e",
|
|
2716
|
+
erc721BalanceChange: "0x8aFdf96eDBbe7e1eD3f5Cd89C7E084841e12A09e",
|
|
2717
|
+
erc721Transfer: "0x3790e6B7233f779b09DA74C72b6e94813925b9aF",
|
|
2718
|
+
erc1155BalanceChange: "0x63c322732695cAFbbD488Fc6937A0A7B66fC001A",
|
|
2719
|
+
exactCalldataBatch: "0x982FD5C86BBF425d7d1451f974192d4525113DfD",
|
|
2720
|
+
exactCalldata: "0x99F2e9bF15ce5eC84685604836F71aB835DBBdED",
|
|
2721
|
+
exactExecutionBatch: "0x1e141e455d08721Dd5BCDA1BaA6Ea5633Afd5017",
|
|
2722
|
+
exactExecution: "0x146713078D39eCC1F5338309c28405ccf85Abfbb",
|
|
2723
|
+
id: "0xC8B5D93463c893401094cc70e66A206fb5987997",
|
|
2724
|
+
logicalOrWrapper: "0xE1302607a3251AF54c3a6e69318d6aa07F5eB46c",
|
|
2725
|
+
limitedCalls: "0x04658B29F6b82ed55274221a06Fc97D318E25416",
|
|
2726
|
+
nativeBalanceChange: "0xbD7B277507723490Cd50b12EaaFe87C616be6880",
|
|
2727
|
+
argsEqualityCheck: "0x44B8C6ae3C304213c3e298495e12497Ed3E56E41",
|
|
2728
|
+
nativeTokenPayment: "0x4803a326ddED6dDBc60e659e5ed12d85c7582811",
|
|
2729
|
+
nativeTokenTransferAmount: "0xF71af580b9c3078fbc2BBF16FbB8EEd82b330320",
|
|
2730
|
+
nativeTokenStreaming: "0xD10b97905a320b13a0608f7E9cC506b56747df19",
|
|
2731
|
+
nativeTokenPeriodTransfer: "0x9BC0FAf4Aca5AE429F4c06aEEaC517520CB16BD9",
|
|
2732
|
+
nonce: "0xDE4f2FAC4B3D87A1d9953Ca5FC09FCa7F366254f",
|
|
2733
|
+
ownershipTransfer: "0x7EEf9734E7092032B5C56310Eb9BbD1f4A524681",
|
|
2734
|
+
redeemer: "0xE144b0b2618071B4E56f746313528a669c7E65c5",
|
|
2735
|
+
specificActionERC20TransferBatch: "0x6649b61c873F6F9686A1E1ae9ee98aC380c7bA13",
|
|
2736
|
+
timestamp: "0x1046bb45C8d673d4ea75321280DB34899413c069",
|
|
2737
|
+
valueLte: "0x92Bf12322527cAA612fd31a0e810472BBB106A8F",
|
|
2738
|
+
multiTokenPeriod: "0xFB2f1a9BD76d3701B730E5d69C3219D42D80eBb7"
|
|
2739
|
+
}
|
|
2740
|
+
},
|
|
2741
|
+
arp: {
|
|
2742
|
+
endpointBinding: {
|
|
2743
|
+
_note: "Task 39c, ADR 0094/0095. The two predicates the endpoint binding is written with: (agent identity atom) has endpoint (endpoint atom), and (pinned binding document) in context of (that triple). Both READ FROM THE GRAPH, neither minted by ARP: scripts/measure/endpoint-predicate.ts, run 2026-10-30, found `has endpoint` (Thing, 6 triples) and `in context of` (Thing, 29 triples) on testnet 13579, and none of `reachable at`, `is reachable at`, `served at`, `hosted at`. `in context of` differs from mainnet's 0x892054b0… (scripts/measure/nested-triples.ts), so never copy it across chains.",
|
|
2744
|
+
measuredAt: "2026-10-30",
|
|
2745
|
+
hasEndpoint: "0xa4488686eb3702e4e972a971ea008d6832a7f935b942366f1091566d1993a5f1",
|
|
2746
|
+
inContextOf: "0x61a88b9c372c0d164d2caf66947b67ed0fcb4c457178a271b6b3dc39fb1f8862"
|
|
2747
|
+
},
|
|
2748
|
+
_note: "Written by scripts/deploy/deploy.sh --broadcast. Addresses are EOA-nonce-derived (CREATE). deploy.sh skips a contract whose address here already has code unless --force, and on a redeploy keeps the replaced address under <name>V<n>. ModuleRegistry and DomainScopeEnforcer were removed from src/ in 59ac83a; their entries are the record.",
|
|
2749
|
+
deployer: "0xE596096F4176b682E300d73963e7B04B383C1AA1",
|
|
2750
|
+
deployedAt: "2026-06-01T13:30:00Z",
|
|
2751
|
+
moduleRegistry: "0xc9a2f66775828017e984E8be077fA2d17e0A41F4",
|
|
2752
|
+
moduleRegistryV1: "0x777C28eCb4688D647B535098d11fB87A9746334f",
|
|
2753
|
+
_moduleRegistryNote: "Redeployed 2026-06-03 with schemaURI uniqueness (ADR 0013). V1 address kept for historical reference; orphan (UI reads V2).",
|
|
2754
|
+
domainScopeEnforcer: "0x516B82E29e3Ca46Ca810FC2EEf348932b198f7f9",
|
|
2755
|
+
trustStakeCapEnforcer: "0x19e07280F1483Aa9aCDb1a7F8C61bfd20c7879Bc",
|
|
2756
|
+
identityRegistry: "0xC165A2AD2E540A4069E02834009161E2b4490d5A",
|
|
2757
|
+
sponsor: "0x77939A4c276f3E549276CEB3A09CDa1fF58b7ec9",
|
|
2758
|
+
sponsorV1: "0xE596096F4176b682E300d73963e7B04B383C1AA1",
|
|
2759
|
+
_sponsorNote: "The address ARP's sponsoring service sends from on 13579 (apps/sponsor, ADR 0092, 0094). Since 2026-11-09 a key of its own, for the hosted service (task 78, ADR 0149): sponsorV1 is the deployer's key, which sent for it before and also holds mainnet funds. Not a contract: an EOA whose key is a file mounted into the service's container, never read by an agent. Before 2026-11-09: The address ARP's sponsoring service sends from on 13579 (apps/sponsor, ADR 0092, 0094): its GET / answered this on 2026-11-02. /tools counts every deposit it sent in Usage: the proof-gated ones, the edge creations it paid for, and, because the address is also ARP's deployer, 3 deposits written before the service existed (scripts/measure/sponsor-deposit-provenance.ts, 2026-11-03). Not a contract: an EOA whose key is in a 0600 file the agent never reads.",
|
|
2760
|
+
deployedTxs: {
|
|
2761
|
+
moduleRegistry: "0x6ee4f4d76dd55c69ea434f8c3c204cb6149df1c588910b1792db1650681fbc2e",
|
|
2762
|
+
moduleRegistryV1: "0x02abe9a0c598df131995bc40927a4e0be0b86e7bdbfe747cfdf37fb19b069634",
|
|
2763
|
+
domainScopeEnforcer: "0x6ca2c4e680b45eaa37147c2458bd38d756e6ffabb560755657cc9caf893df272",
|
|
2764
|
+
trustStakeCapEnforcer: "0x8dd54ea3eeba14645531c501c3f1b8ec95e6a95788825e00fbe239ff32263159",
|
|
2765
|
+
identityRegistry: "0x60b1f6ff7dbdcbca6864794eb9c5cf1e3ba94770daac6d2e77a4df00d726900c",
|
|
2766
|
+
trustStakeCapEnforcerV1: "0x80e36828202a9c7606b92146076ed123155a94cfda3a2ab53013bd27be71764f"
|
|
2767
|
+
},
|
|
2768
|
+
explorerLinks: {
|
|
2769
|
+
moduleRegistry: "https://testnet.explorer.intuition.systems/address/0xc9a2f66775828017e984E8be077fA2d17e0A41F4",
|
|
2770
|
+
moduleRegistryV1: "https://testnet.explorer.intuition.systems/address/0x777C28eCb4688D647B535098d11fB87A9746334f",
|
|
2771
|
+
domainScopeEnforcer: "https://testnet.explorer.intuition.systems/address/0x516B82E29e3Ca46Ca810FC2EEf348932b198f7f9",
|
|
2772
|
+
trustStakeCapEnforcer: "https://testnet.explorer.intuition.systems/address/0x19e07280F1483Aa9aCDb1a7F8C61bfd20c7879Bc",
|
|
2773
|
+
identityRegistry: "https://testnet.explorer.intuition.systems/address/0xC165A2AD2E540A4069E02834009161E2b4490d5A",
|
|
2774
|
+
trustStakeCapEnforcerV1: "https://testnet.explorer.intuition.systems/address/0x7BB56819E9a413B8B4668C5cAF5C494c41dC0F8E"
|
|
2775
|
+
},
|
|
2776
|
+
modules: [
|
|
2777
|
+
{
|
|
2778
|
+
id: "1",
|
|
2779
|
+
name: "Solidity Audit",
|
|
2780
|
+
domain: "solidity-audit",
|
|
2781
|
+
schemaURI: "ipfs://bafkreiembmtprqzufrdkvig74lskv6iazuatblm4hwmtxkf5a3v67mkdkm",
|
|
2782
|
+
creator: "0xE596096F4176b682E300d73963e7B04B383C1AA1",
|
|
2783
|
+
tx: "0xbd97979b197e4423293180e36d69f72e8eb07d2bb11bdfb2940ec918d6f6af3c"
|
|
2784
|
+
}
|
|
2785
|
+
],
|
|
2786
|
+
intuitionAtoms: [
|
|
2787
|
+
{
|
|
2788
|
+
atomId: "0xde8890fca8dde322ca57bce0e20dfeb23be67332f9101305ebe72facdee032dc",
|
|
2789
|
+
uri: "ipfs://bafkreiembmtprqzufrdkvig74lskv6iazuatblm4hwmtxkf5a3v67mkdkm",
|
|
2790
|
+
tx: "0x97db7629c6c973d43c0c62325754d750ee262853bdbd87496b4b82c1ea3d0ac7"
|
|
2791
|
+
}
|
|
2792
|
+
],
|
|
2793
|
+
rigAgents: {
|
|
2794
|
+
_note: "Task 24. ERC-8004 agents on ARP's own testnet registry, minted 2026-10-19 on chain 13579. SIGNED BY THE AGENT SESSION using the operator's PRIVATE_KEY from .env, on the user's explicit authorisation — not by a human at a keyboard. See ADR 0037. security.md scopes its automation prohibition to mainnet, so this is permitted and was previously unwritten. Agents 1 and 2 were left with an EMPTY tokenURI deliberately: no-token-uri is 23% of the real cohort (32/140, scripts/measure/cohort-endpoints.ts), so they exercise a branch the three new agents cannot. Permanent: the ids are taken and setAgentURI leaves the superseded URI in the mint transaction's calldata.",
|
|
2795
|
+
registry: "0xC165A2AD2E540A4069E02834009161E2b4490d5A",
|
|
2796
|
+
agents: {
|
|
2797
|
+
"3": {
|
|
2798
|
+
slug: "rig-confirmed",
|
|
2799
|
+
exercises: "confirmed/serves-declared-protocol + tools/list recovery",
|
|
2800
|
+
endpoint: "http://localhost:3002/mcp",
|
|
2801
|
+
_endpointNote: "ARP's own MCP server, local by default. ARP_MCP_ENDPOINT regenerates the file and setAgentURI can update the agent — but that escape hatch is a rehearsal prop: portability.md rule 4 says any step that works because ARP can write to the registry is not a step, and on mainnet ARP owns no registry. The real fix is a public endpoint.",
|
|
2802
|
+
tx: "0xce4fee69c692ddf57d4537b23479d78384a9e2d06b733b532e419055ed50a086",
|
|
2803
|
+
documentSha256_16: "478ac85cd6b2357f",
|
|
2804
|
+
_documentShaNote: "sha256 of the data: URI, first 16 hex. Lets repo-vs-chain drift be detected: the fixtures in scripts/seed/rig-registration-files.ts are the source of truth for three permanent mints, and an edit to FILES would otherwise diverge silently. Checked by scripts/verify/verify-rig-claims.ts against a gate report. ONLY REPRODUCIBLE WITH ARP_MCP_ENDPOINT UNSET: OWN_ENDPOINT reads the environment and the document is hashed after substitution, so anyone with it exported sees a false DRIFT."
|
|
2805
|
+
},
|
|
2806
|
+
"4": {
|
|
2807
|
+
slug: "rig-dead",
|
|
2808
|
+
exercises: "confirmed/dead",
|
|
2809
|
+
endpoint: "http://192.0.2.1/mcp",
|
|
2810
|
+
_endpointNote: "TEST-NET-1, RFC 5737 — reserved and never routable, so it always times out. A .invalid hostname was tried first and gave inconclusive/transport-error, because a DNS failure is indistinguishable from our own network being down.",
|
|
2811
|
+
tx: "0x87bf71cf0ce55db7f82ca617b8601ee87f870e67018b1097a311ed2d61cc7378",
|
|
2812
|
+
uriUpdatedTx: "0x8f5950a61fca19b6a3f69b26f6c4159e29833689ed4b1623d8610206bfe87100",
|
|
2813
|
+
documentSha256_16: "6db8b769dd551e98"
|
|
2814
|
+
},
|
|
2815
|
+
"5": {
|
|
2816
|
+
slug: "rig-unprobeable",
|
|
2817
|
+
exercises: "not-checkable ×2 — templated and non-http-identifier. Never a defect.",
|
|
2818
|
+
tx: "0x859aff609fa10dba635ffd12d0b60b598c7c3174238f61962edb4b5eee1a0efd",
|
|
2819
|
+
documentSha256_16: "f61a9d7f6637958e"
|
|
2820
|
+
}
|
|
2821
|
+
},
|
|
2822
|
+
gate: {
|
|
2823
|
+
command: "bun run scripts/verify/verify-cohort.ts --target intuition-testnet --ids 1,2,3,4,5 --tools --out /tmp/gate.json && bun run scripts/verify/verify-rig-claims.ts /tmp/gate.json",
|
|
2824
|
+
ranAt: "2026-10-19",
|
|
2825
|
+
result: "5 agents: 2 with no tokenURI (40%), 3 read. 4 endpoints: 2 confirmed (serves-declared-protocol, dead), 2 not-checkable (templated, non-http-identifier), 0 inconclusive, 1 capability list recovered (arp_tool_reputation).",
|
|
2826
|
+
_requires: "apps/mcp/src/server.ts listening on :3002, or agent 3 reports inconclusive. The confirmed branch is therefore reproducible only on a host running that server — a known limit of this gate until ARP has a public endpoint.",
|
|
2827
|
+
_widerThanSpecced: "--ids 1,2,3,4,5 rather than 3,4,5. Including the two empty-tokenURI agents exercises a fifth branch for two extra requests.",
|
|
2828
|
+
_claimsCheck: "verify-rig-claims.ts asserts each fixture's `exercises` prose against the outcome the probe observed, and the document hashes against this file. The unit suite CANNOT close that class: confirmed and inconclusive are both probeable before a request is sent, and the mutation that actually shipped lived exactly there."
|
|
2829
|
+
}
|
|
2830
|
+
},
|
|
2831
|
+
trustStakeCapEnforcerV1: "0x7BB56819E9a413B8B4668C5cAF5C494c41dC0F8E",
|
|
2832
|
+
delegationRegister: {
|
|
2833
|
+
_note: "Task 37c, ADR 0092 item 7. The two predicates a published delegation is written with: (delegator CAIP-10) delegate to (delegate CAIP-10), and (pinned document) in context of (that triple). Both READ FROM THE GRAPH, neither minted by ARP: on testnet 13579 on 2026-11-03 exactly one atom carries each label, a Thing, `delegate to` in 10 triples and `in context of` in 29. `in context of` is the one endpointBinding uses. Never copy either across chains.",
|
|
2834
|
+
measuredAt: "2026-11-03",
|
|
2835
|
+
delegateTo: "0xb56980d42a3b03455bf41ea20fe04ae223fca0b9e688994dc661414e81e6433b",
|
|
2836
|
+
inContextOf: "0x61a88b9c372c0d164d2caf66947b67ed0fcb4c457178a271b6b3dc39fb1f8862"
|
|
2837
|
+
}
|
|
2838
|
+
}
|
|
2839
|
+
};
|
|
2840
|
+
|
|
2841
|
+
// ../hook/src/skill-failure.ts
|
|
2842
|
+
import { existsSync, mkdirSync as mkdirSync2, readFileSync as readFileSync2 } from "node:fs";
|
|
2843
|
+
import { homedir as homedir2 } from "node:os";
|
|
2844
|
+
import { dirname as dirname2, join as join3 } from "node:path";
|
|
2845
|
+
|
|
2846
|
+
// ../../packages/protocol/src/declare/label.ts
|
|
2847
|
+
function normaliseCategoryLabel2(raw) {
|
|
2848
|
+
return raw.normalize("NFKC").normalize("NFD").replace(/[̀-ͯ]/g, "").toLowerCase().trim().replace(/\s+/g, " ");
|
|
2849
|
+
}
|
|
2850
|
+
|
|
2851
|
+
// ../../packages/protocol/src/domain-words.ts
|
|
2852
|
+
function tokens(text) {
|
|
2853
|
+
return normaliseCategoryLabel2(text).split(/[^a-z0-9]+/).filter((t) => t.length > 1);
|
|
2854
|
+
}
|
|
2855
|
+
function domainInText2(text, words) {
|
|
2856
|
+
const padded = ` ${tokens(text).join(" ")} `;
|
|
2857
|
+
const hit = (phrase) => padded.includes(` ${tokens(phrase).join(" ")} `);
|
|
2858
|
+
const label = [...words.labels].filter((l) => tokens(l).length > 0 && hit(l)).sort((a, b) => b.length - a.length)[0];
|
|
2859
|
+
if (label !== undefined)
|
|
2860
|
+
return label;
|
|
2861
|
+
const alias = Object.keys(words.aliases).filter((a) => words.labels.includes(words.aliases[a]) && hit(a)).sort((a, b) => b.length - a.length)[0];
|
|
2862
|
+
return alias === undefined ? null : words.aliases[alias];
|
|
2863
|
+
}
|
|
2864
|
+
// ../../data/catalogue/domain-aliases.json
|
|
2865
|
+
var domain_aliases_default = {
|
|
2866
|
+
_note: "Words a person or an agent uses, mapped to a node of ARP's domain tree (OASF 1.1.0 names, plus ARP's own `design`), task 84. Read locally by the MCP server, the CLI and the hook: the words never leave the machine (the user, 2026-11-09), except for a caller who chooses ARP's hosted read-only endpoint, which receives its words and keeps none (ADR 0161). Every target is checked against data/catalogue/oasf-1.1.0-skills.json by packages/protocol/test/domain-words.test.ts. ARP's old category words map to the one node that held them; the split ones (method, quality) have no alias, and a refusal suggests nodes instead.",
|
|
2867
|
+
oasfVersion: "1.1.0",
|
|
2868
|
+
aliases: {
|
|
2869
|
+
ads: "advertising",
|
|
2870
|
+
advertising: "advertising",
|
|
2871
|
+
agent: "agent application building",
|
|
2872
|
+
agents: "agent orchestration",
|
|
2873
|
+
android: "mobile development",
|
|
2874
|
+
animation: "web animation",
|
|
2875
|
+
api: "api development",
|
|
2876
|
+
audio: "audio & speech processing",
|
|
2877
|
+
automation: "tool use and automation",
|
|
2878
|
+
backend: "backend development",
|
|
2879
|
+
blockchain: "blockchain and web3",
|
|
2880
|
+
brand: "branding",
|
|
2881
|
+
browser: "browser automation",
|
|
2882
|
+
captions: "video editing",
|
|
2883
|
+
chart: "chart generation",
|
|
2884
|
+
charts: "chart generation",
|
|
2885
|
+
chat: "team messaging",
|
|
2886
|
+
collaboration: "collaboration and communication",
|
|
2887
|
+
commit: "commit authoring",
|
|
2888
|
+
copywriting: "copywriting",
|
|
2889
|
+
crawl: "web crawling",
|
|
2890
|
+
cro: "conversion optimization",
|
|
2891
|
+
dashboard: "dashboard building",
|
|
2892
|
+
database: "databases",
|
|
2893
|
+
debug: "code debugging",
|
|
2894
|
+
debugging: "code debugging",
|
|
2895
|
+
deck: "presentation authoring",
|
|
2896
|
+
deploy: "deployment and release management",
|
|
2897
|
+
development: "software engineering",
|
|
2898
|
+
docker: "containerization",
|
|
2899
|
+
docs: "technical writing",
|
|
2900
|
+
document: "document authoring",
|
|
2901
|
+
documentation: "project documentation",
|
|
2902
|
+
docx: "document authoring",
|
|
2903
|
+
editing: "video editing",
|
|
2904
|
+
email: "email marketing",
|
|
2905
|
+
excel: "spreadsheet authoring",
|
|
2906
|
+
frontend: "frontend development",
|
|
2907
|
+
git: "git workflow",
|
|
2908
|
+
illustration: "image generation",
|
|
2909
|
+
image: "computer vision",
|
|
2910
|
+
infrastructure: "devops and cloud infrastructure",
|
|
2911
|
+
ios: "mobile development",
|
|
2912
|
+
issues: "issue tracking",
|
|
2913
|
+
kubernetes: "container orchestration",
|
|
2914
|
+
landing: "conversion optimization",
|
|
2915
|
+
layout: "design",
|
|
2916
|
+
logo: "branding",
|
|
2917
|
+
logs: "logging and log analysis",
|
|
2918
|
+
marketing: "content, writing and marketing",
|
|
2919
|
+
mcp: "agent tool building",
|
|
2920
|
+
meeting: "meeting intelligence",
|
|
2921
|
+
meetings: "meeting intelligence",
|
|
2922
|
+
mobile: "mobile development",
|
|
2923
|
+
monitoring: "monitoring and alerting",
|
|
2924
|
+
montage: "video editing",
|
|
2925
|
+
music: "text to audio",
|
|
2926
|
+
newsletter: "email marketing",
|
|
2927
|
+
notes: "note-taking",
|
|
2928
|
+
payment: "payments integration",
|
|
2929
|
+
pdf: "pdf manipulation",
|
|
2930
|
+
pentest: "penetration testing",
|
|
2931
|
+
postgres: "relational database operations",
|
|
2932
|
+
powerpoint: "presentation authoring",
|
|
2933
|
+
pptx: "presentation authoring",
|
|
2934
|
+
pr: "pull request management",
|
|
2935
|
+
prd: "requirements and prd authoring",
|
|
2936
|
+
presentation: "presentation authoring",
|
|
2937
|
+
prompt: "prompt design",
|
|
2938
|
+
rag: "retrieval-augmented generation",
|
|
2939
|
+
research: "research and scholarship",
|
|
2940
|
+
review: "code review",
|
|
2941
|
+
scrape: "web scraping",
|
|
2942
|
+
scraping: "web data acquisition",
|
|
2943
|
+
search: "web search research",
|
|
2944
|
+
security: "cybersecurity",
|
|
2945
|
+
seo: "search engine optimization",
|
|
2946
|
+
slide: "presentation authoring",
|
|
2947
|
+
slides: "presentation authoring",
|
|
2948
|
+
"smart contract": "smart contract development",
|
|
2949
|
+
social: "social media marketing",
|
|
2950
|
+
solidity: "smart contract development",
|
|
2951
|
+
spec: "requirements and prd authoring",
|
|
2952
|
+
spreadsheet: "spreadsheet authoring",
|
|
2953
|
+
sql: "relational database operations",
|
|
2954
|
+
subtitles: "video editing",
|
|
2955
|
+
summarize: "text summarization",
|
|
2956
|
+
summary: "text summarization",
|
|
2957
|
+
tdd: "software testing",
|
|
2958
|
+
test: "software testing",
|
|
2959
|
+
testing: "software testing",
|
|
2960
|
+
tests: "software testing",
|
|
2961
|
+
thumbnail: "image generation",
|
|
2962
|
+
tickets: "issue tracking",
|
|
2963
|
+
trading: "defi and crypto trading",
|
|
2964
|
+
transcription: "automatic speech recognition",
|
|
2965
|
+
translate: "translation",
|
|
2966
|
+
tts: "text to speech",
|
|
2967
|
+
typography: "design",
|
|
2968
|
+
ui: "design",
|
|
2969
|
+
ux: "design",
|
|
2970
|
+
voice: "text to speech",
|
|
2971
|
+
wallet: "crypto wallet operations",
|
|
2972
|
+
website: "web development",
|
|
2973
|
+
writing: "content writing",
|
|
2974
|
+
xlsx: "spreadsheet authoring"
|
|
2975
|
+
}
|
|
2976
|
+
};
|
|
2977
|
+
// ../../data/catalogue/skill-nodes-13579.json
|
|
2978
|
+
var skill_nodes_13579_default = {
|
|
2979
|
+
_note: "For the hook's failure record, which reads no network (task 84): the nodes of ARP's testnet tree as written by task 79 (data/catalogue/oasf-tree-13579.json), and the node each skill name is filed at, when the name is filed at one node only. Built by scripts/build/build-skill-nodes.ts from data/catalogue/oasf-skill-tags-proposal-13579.json, rows not reviewed out.",
|
|
2980
|
+
chainId: 13579,
|
|
2981
|
+
oasfVersion: "1.1.0",
|
|
2982
|
+
nodes: [
|
|
2983
|
+
"ad copywriting",
|
|
2984
|
+
"ad creative production",
|
|
2985
|
+
"advertising",
|
|
2986
|
+
"agent application building",
|
|
2987
|
+
"agent evaluation",
|
|
2988
|
+
"agent orchestration",
|
|
2989
|
+
"agent tool building",
|
|
2990
|
+
"agentic workflow orchestration",
|
|
2991
|
+
"ai agent development",
|
|
2992
|
+
"ai safety and guardrails",
|
|
2993
|
+
"ai/ml engineering",
|
|
2994
|
+
"answer engine optimization",
|
|
2995
|
+
"api development",
|
|
2996
|
+
"api schema understanding",
|
|
2997
|
+
"app store release",
|
|
2998
|
+
"architecture patterns",
|
|
2999
|
+
"architecture review",
|
|
3000
|
+
"article and blog writing",
|
|
3001
|
+
"audio & speech processing",
|
|
3002
|
+
"audio generation",
|
|
3003
|
+
"audio to audio",
|
|
3004
|
+
"authentication implementation",
|
|
3005
|
+
"authorization and access control",
|
|
3006
|
+
"benchmark execution",
|
|
3007
|
+
"branding",
|
|
3008
|
+
"browser automation",
|
|
3009
|
+
"build, packaging and dev tooling",
|
|
3010
|
+
"business and professional",
|
|
3011
|
+
"business and professional writing",
|
|
3012
|
+
"business operations",
|
|
3013
|
+
"business strategy",
|
|
3014
|
+
"calendar and scheduling",
|
|
3015
|
+
"campaign planning",
|
|
3016
|
+
"ci/cd configuration",
|
|
3017
|
+
"ci/cd pipelines",
|
|
3018
|
+
"cli and terminal app development",
|
|
3019
|
+
"cloud architecture design",
|
|
3020
|
+
"cloud cost optimization",
|
|
3021
|
+
"cloud networking",
|
|
3022
|
+
"cloud platform operations",
|
|
3023
|
+
"cloud resource management",
|
|
3024
|
+
"code comprehension",
|
|
3025
|
+
"code debugging",
|
|
3026
|
+
"code generation",
|
|
3027
|
+
"code migration and modernization",
|
|
3028
|
+
"code quality and maintenance",
|
|
3029
|
+
"code refactoring and optimization",
|
|
3030
|
+
"code review",
|
|
3031
|
+
"collaboration and communication",
|
|
3032
|
+
"commit authoring",
|
|
3033
|
+
"competitive analysis",
|
|
3034
|
+
"competitive intelligence",
|
|
3035
|
+
"compliance assessment",
|
|
3036
|
+
"computer vision",
|
|
3037
|
+
"configuration management",
|
|
3038
|
+
"container orchestration",
|
|
3039
|
+
"container workload management",
|
|
3040
|
+
"containerization",
|
|
3041
|
+
"content strategy",
|
|
3042
|
+
"content writing",
|
|
3043
|
+
"content, writing and marketing",
|
|
3044
|
+
"context window management",
|
|
3045
|
+
"controllable image generation",
|
|
3046
|
+
"conversion optimization",
|
|
3047
|
+
"copy editing",
|
|
3048
|
+
"copywriting",
|
|
3049
|
+
"crm management",
|
|
3050
|
+
"cross-platform mobile development",
|
|
3051
|
+
"customer support and success",
|
|
3052
|
+
"cybersecurity",
|
|
3053
|
+
"data analytics",
|
|
3054
|
+
"data engineering and analytics",
|
|
3055
|
+
"data modeling",
|
|
3056
|
+
"data modeling and design",
|
|
3057
|
+
"data pipelines",
|
|
3058
|
+
"data visualization",
|
|
3059
|
+
"data warehousing",
|
|
3060
|
+
"databases",
|
|
3061
|
+
"deep learning training",
|
|
3062
|
+
"deep research",
|
|
3063
|
+
"dependency management",
|
|
3064
|
+
"deployment and release management",
|
|
3065
|
+
"deployment orchestration",
|
|
3066
|
+
"design",
|
|
3067
|
+
"dev environment setup",
|
|
3068
|
+
"devops and cloud infrastructure",
|
|
3069
|
+
"document authoring",
|
|
3070
|
+
"document collaboration",
|
|
3071
|
+
"document or database question answering",
|
|
3072
|
+
"document processing",
|
|
3073
|
+
"editing and proofreading",
|
|
3074
|
+
"email campaign creation",
|
|
3075
|
+
"email management",
|
|
3076
|
+
"email marketing",
|
|
3077
|
+
"employee lifecycle",
|
|
3078
|
+
"end-to-end and ui testing",
|
|
3079
|
+
"error handling implementation",
|
|
3080
|
+
"experiment tracking",
|
|
3081
|
+
"face swapping",
|
|
3082
|
+
"frontend development",
|
|
3083
|
+
"full-stack development",
|
|
3084
|
+
"game development",
|
|
3085
|
+
"git workflow",
|
|
3086
|
+
"governance, compliance and ethics",
|
|
3087
|
+
"graph database operations",
|
|
3088
|
+
"growth strategy",
|
|
3089
|
+
"human resources",
|
|
3090
|
+
"iac authoring",
|
|
3091
|
+
"identity administration",
|
|
3092
|
+
"identity and access management",
|
|
3093
|
+
"image editing",
|
|
3094
|
+
"image generation",
|
|
3095
|
+
"image inpainting",
|
|
3096
|
+
"image outpainting",
|
|
3097
|
+
"image to image",
|
|
3098
|
+
"image to video",
|
|
3099
|
+
"incident response",
|
|
3100
|
+
"inference and serving",
|
|
3101
|
+
"infrastructure as code",
|
|
3102
|
+
"instructed image editing",
|
|
3103
|
+
"interactive visualization",
|
|
3104
|
+
"issue tracking",
|
|
3105
|
+
"knowledge base management",
|
|
3106
|
+
"knowledge management",
|
|
3107
|
+
"landing page and sales copy",
|
|
3108
|
+
"language generation",
|
|
3109
|
+
"language processing",
|
|
3110
|
+
"lead generation",
|
|
3111
|
+
"llm evaluation",
|
|
3112
|
+
"llm observability",
|
|
3113
|
+
"llm tracing and instrumentation",
|
|
3114
|
+
"logging and log analysis",
|
|
3115
|
+
"logical reasoning",
|
|
3116
|
+
"market and competitive intelligence",
|
|
3117
|
+
"market research",
|
|
3118
|
+
"marketing copywriting",
|
|
3119
|
+
"marketing strategy",
|
|
3120
|
+
"meeting intelligence",
|
|
3121
|
+
"ml experimentation and lifecycle",
|
|
3122
|
+
"mobile development",
|
|
3123
|
+
"model and agent evaluation",
|
|
3124
|
+
"model fine-tuning",
|
|
3125
|
+
"model serving",
|
|
3126
|
+
"model training and fine-tuning",
|
|
3127
|
+
"monitoring and alerting",
|
|
3128
|
+
"multi-agent coordination",
|
|
3129
|
+
"native mobile development",
|
|
3130
|
+
"network configuration",
|
|
3131
|
+
"nosql database operations",
|
|
3132
|
+
"note-taking",
|
|
3133
|
+
"observability",
|
|
3134
|
+
"observability instrumentation",
|
|
3135
|
+
"office productivity",
|
|
3136
|
+
"orm and data access",
|
|
3137
|
+
"paid ads management",
|
|
3138
|
+
"paper analysis",
|
|
3139
|
+
"pdf manipulation",
|
|
3140
|
+
"performance monitoring",
|
|
3141
|
+
"postmortem authoring",
|
|
3142
|
+
"presentation authoring",
|
|
3143
|
+
"problem solving",
|
|
3144
|
+
"process operations",
|
|
3145
|
+
"product analytics",
|
|
3146
|
+
"product management",
|
|
3147
|
+
"product marketing and gtm",
|
|
3148
|
+
"product strategy",
|
|
3149
|
+
"production llm monitoring",
|
|
3150
|
+
"project and task management",
|
|
3151
|
+
"project scaffolding",
|
|
3152
|
+
"prompt design",
|
|
3153
|
+
"prompt engineering",
|
|
3154
|
+
"prompt optimization",
|
|
3155
|
+
"pull request management",
|
|
3156
|
+
"quality evaluation",
|
|
3157
|
+
"reasoning and planning",
|
|
3158
|
+
"relational database operations",
|
|
3159
|
+
"release management",
|
|
3160
|
+
"requirements and prd authoring",
|
|
3161
|
+
"research and scholarship",
|
|
3162
|
+
"research ideation",
|
|
3163
|
+
"research, knowledge and productivity",
|
|
3164
|
+
"responsible ai governance",
|
|
3165
|
+
"retention management",
|
|
3166
|
+
"retrieval-augmented generation",
|
|
3167
|
+
"roadmapping and prioritization",
|
|
3168
|
+
"role assignment",
|
|
3169
|
+
"root-cause debugging",
|
|
3170
|
+
"runtime guardrails",
|
|
3171
|
+
"sales and crm",
|
|
3172
|
+
"sales enablement",
|
|
3173
|
+
"sales outreach",
|
|
3174
|
+
"script integration",
|
|
3175
|
+
"search engine optimization",
|
|
3176
|
+
"security hardening",
|
|
3177
|
+
"security operations",
|
|
3178
|
+
"seo auditing",
|
|
3179
|
+
"seo content optimization",
|
|
3180
|
+
"serverless and edge computing",
|
|
3181
|
+
"serverless function development",
|
|
3182
|
+
"site reliability engineering",
|
|
3183
|
+
"social content creation",
|
|
3184
|
+
"social media management",
|
|
3185
|
+
"social media marketing",
|
|
3186
|
+
"software architecture and design",
|
|
3187
|
+
"software engineering",
|
|
3188
|
+
"software testing",
|
|
3189
|
+
"specialized application development",
|
|
3190
|
+
"spreadsheet authoring",
|
|
3191
|
+
"static analysis and linting",
|
|
3192
|
+
"stream processing",
|
|
3193
|
+
"structured data markup",
|
|
3194
|
+
"styling and responsive layout",
|
|
3195
|
+
"support operations",
|
|
3196
|
+
"system design",
|
|
3197
|
+
"talking avatar generation",
|
|
3198
|
+
"task decomposition",
|
|
3199
|
+
"task management",
|
|
3200
|
+
"team messaging",
|
|
3201
|
+
"technical writing",
|
|
3202
|
+
"test strategy and tdd",
|
|
3203
|
+
"text paraphrasing",
|
|
3204
|
+
"text style transfer",
|
|
3205
|
+
"text to audio",
|
|
3206
|
+
"text to code",
|
|
3207
|
+
"text to image",
|
|
3208
|
+
"text to video",
|
|
3209
|
+
"third-party integration",
|
|
3210
|
+
"threat detection",
|
|
3211
|
+
"tool use and automation",
|
|
3212
|
+
"tool use planning",
|
|
3213
|
+
"trace analysis and debugging",
|
|
3214
|
+
"ux writing and microcopy",
|
|
3215
|
+
"version control and collaboration",
|
|
3216
|
+
"video",
|
|
3217
|
+
"video editing",
|
|
3218
|
+
"vulnerability analysis",
|
|
3219
|
+
"vulnerability management",
|
|
3220
|
+
"web accessibility",
|
|
3221
|
+
"web animation",
|
|
3222
|
+
"web development",
|
|
3223
|
+
"web search",
|
|
3224
|
+
"web search research",
|
|
3225
|
+
"workflow automation"
|
|
3226
|
+
],
|
|
3227
|
+
skills: {
|
|
3228
|
+
"ab-test-setup": "product analytics",
|
|
3229
|
+
"ab-testing": "product analytics",
|
|
3230
|
+
"academy-guide": "support operations",
|
|
3231
|
+
"ace-step": "text to audio",
|
|
3232
|
+
"ad-creative": "ad copywriting",
|
|
3233
|
+
adapt: "styling and responsive layout",
|
|
3234
|
+
ads: "paid ads management",
|
|
3235
|
+
"agent-browser": "browser automation",
|
|
3236
|
+
"agent-tools": "script integration",
|
|
3237
|
+
"agents-sdk": "agent application building",
|
|
3238
|
+
"ai-avatar-video": "talking avatar generation",
|
|
3239
|
+
"ai-image-generation": "text to image",
|
|
3240
|
+
"ai-music": "text to audio",
|
|
3241
|
+
"ai-paper-reproduction": "research and scholarship",
|
|
3242
|
+
"ai-research-explore": "research ideation",
|
|
3243
|
+
"ai-research-reproduction": "research and scholarship",
|
|
3244
|
+
"ai-seo": "answer engine optimization",
|
|
3245
|
+
"ai-video-generation": "text to video",
|
|
3246
|
+
"airunway-aks-setup": "model serving",
|
|
3247
|
+
"algorithmic-art": "image generation",
|
|
3248
|
+
analytics: "product analytics",
|
|
3249
|
+
"analytics-tracking": "product analytics",
|
|
3250
|
+
"analyze-project": "code comprehension",
|
|
3251
|
+
"analyze-test-run": "software testing",
|
|
3252
|
+
animate: "web animation",
|
|
3253
|
+
"animate-expo": "web animation",
|
|
3254
|
+
"animation-performance": "web animation",
|
|
3255
|
+
"animation-vocabulary": "web animation",
|
|
3256
|
+
animejs: "web animation",
|
|
3257
|
+
"app-store-screenshots": "app store release",
|
|
3258
|
+
"appinsights-instrumentation": "observability instrumentation",
|
|
3259
|
+
"apple-design": "design",
|
|
3260
|
+
arrange: "design",
|
|
3261
|
+
"ask-matt": "tool use planning",
|
|
3262
|
+
"ask-sonner": "frontend development",
|
|
3263
|
+
aso: "seo auditing",
|
|
3264
|
+
"aso-audit": "seo auditing",
|
|
3265
|
+
attribution: "product analytics",
|
|
3266
|
+
audit: "web accessibility",
|
|
3267
|
+
"azure-ai": "cloud platform operations",
|
|
3268
|
+
"azure-aigateway": "runtime guardrails",
|
|
3269
|
+
"azure-app-onboard": "deployment orchestration",
|
|
3270
|
+
"azure-app-onboard-prereq": "deployment orchestration",
|
|
3271
|
+
"azure-cloud-migrate": "code migration and modernization",
|
|
3272
|
+
"azure-compliance": "compliance assessment",
|
|
3273
|
+
"azure-compute": "cloud resource management",
|
|
3274
|
+
"azure-cost": "cloud cost optimization",
|
|
3275
|
+
"azure-cost-optimization": "cloud cost optimization",
|
|
3276
|
+
"azure-deploy": "deployment orchestration",
|
|
3277
|
+
"azure-diagnostics": "incident response",
|
|
3278
|
+
"azure-enterprise-infra-planner": "cloud architecture design",
|
|
3279
|
+
"azure-hosted-copilot-sdk": "agent application building",
|
|
3280
|
+
"azure-kubernetes": "container workload management",
|
|
3281
|
+
"azure-kusto": "logging and log analysis",
|
|
3282
|
+
"azure-kusto-graph": "graph database operations",
|
|
3283
|
+
"azure-kusto-irql": "threat detection",
|
|
3284
|
+
"azure-kusto-irql-graph": "interactive visualization",
|
|
3285
|
+
"azure-messaging": "code debugging",
|
|
3286
|
+
"azure-observability": "observability",
|
|
3287
|
+
"azure-postgres": "relational database operations",
|
|
3288
|
+
"azure-prepare": "iac authoring",
|
|
3289
|
+
"azure-quotas": "cloud resource management",
|
|
3290
|
+
"azure-rbac": "authorization and access control",
|
|
3291
|
+
"azure-reliability": "site reliability engineering",
|
|
3292
|
+
"azure-resource-lookup": "cloud resource management",
|
|
3293
|
+
"azure-resource-visualizer": "cloud architecture design",
|
|
3294
|
+
"azure-storage": "cloud resource management",
|
|
3295
|
+
"azure-upgrade": "code migration and modernization",
|
|
3296
|
+
"azure-validate": "ci/cd configuration",
|
|
3297
|
+
basin: "data warehousing",
|
|
3298
|
+
"batch-grill-me": "problem solving",
|
|
3299
|
+
bolder: "design",
|
|
3300
|
+
brainstorming: "reasoning and planning",
|
|
3301
|
+
"brand-guidelines": "branding",
|
|
3302
|
+
"break-ui": "end-to-end and ui testing",
|
|
3303
|
+
"building-ai-agent-on-cloudflare": "agent application building",
|
|
3304
|
+
"building-mcp-server-on-cloudflare": "agent tool building",
|
|
3305
|
+
"canopy-part-title": "video editing",
|
|
3306
|
+
"canvas-design": "image generation",
|
|
3307
|
+
"captions-overlay": "video editing",
|
|
3308
|
+
cavecrew: "role assignment",
|
|
3309
|
+
caveman: "text style transfer",
|
|
3310
|
+
"caveman-commit": "commit authoring",
|
|
3311
|
+
"caveman-compress": "context window management",
|
|
3312
|
+
"caveman-discover": "llm tracing and instrumentation",
|
|
3313
|
+
"caveman-es": "text style transfer",
|
|
3314
|
+
"caveman-evidence-review": "trace analysis and debugging",
|
|
3315
|
+
"caveman-explore": "code comprehension",
|
|
3316
|
+
"caveman-help": "text style transfer",
|
|
3317
|
+
"caveman-learn": "prompt optimization",
|
|
3318
|
+
"caveman-manage": "release management",
|
|
3319
|
+
"caveman-optimize": "llm evaluation",
|
|
3320
|
+
"caveman-review": "code review",
|
|
3321
|
+
"caveman-setup": "llm tracing and instrumentation",
|
|
3322
|
+
"caveman-stats": "production llm monitoring",
|
|
3323
|
+
"cf-cli": "cloud resource management",
|
|
3324
|
+
"changelog-video": "video editing",
|
|
3325
|
+
"character-design-sheet": "controllable image generation",
|
|
3326
|
+
"chief-of-staff": "agentic workflow orchestration",
|
|
3327
|
+
"churn-prevention": "retention management",
|
|
3328
|
+
clarify: "ux writing and microcopy",
|
|
3329
|
+
"claude-api": "third-party integration",
|
|
3330
|
+
"claude-design-hyperframes": "video editing",
|
|
3331
|
+
"claude-handoff": "multi-agent coordination",
|
|
3332
|
+
cloudflare: "cloud architecture design",
|
|
3333
|
+
"cloudflare-email-service": "third-party integration",
|
|
3334
|
+
"cloudflare-one": "network configuration",
|
|
3335
|
+
"cloudflare-one-migrations": "network configuration",
|
|
3336
|
+
"co-marketing": "campaign planning",
|
|
3337
|
+
"code-review": "code review",
|
|
3338
|
+
"code-slice-hero": "video editing",
|
|
3339
|
+
"codebase-design": "architecture patterns",
|
|
3340
|
+
"codex-pet": "image to image",
|
|
3341
|
+
"cold-email": "sales outreach",
|
|
3342
|
+
colorize: "design",
|
|
3343
|
+
"community-marketing": "growth strategy",
|
|
3344
|
+
"competitor-alternatives": "landing page and sales copy",
|
|
3345
|
+
"competitor-profiling": "competitive intelligence",
|
|
3346
|
+
"competitor-teardown": "competitive analysis",
|
|
3347
|
+
competitors: "landing page and sales copy",
|
|
3348
|
+
compress: "context window management",
|
|
3349
|
+
"content-strategy": "content strategy",
|
|
3350
|
+
"contribute-catalog": "video editing",
|
|
3351
|
+
"controlnet-pose": "image to video",
|
|
3352
|
+
"conversion-tracking": "product analytics",
|
|
3353
|
+
"copy-editing": "copy editing",
|
|
3354
|
+
copywriting: "marketing copywriting",
|
|
3355
|
+
critique: "design",
|
|
3356
|
+
cro: "conversion optimization",
|
|
3357
|
+
"css-animations": "web animation",
|
|
3358
|
+
"cuboid-carousel": "video editing",
|
|
3359
|
+
"customer-research": "market research",
|
|
3360
|
+
"cut-the-curve": "video editing",
|
|
3361
|
+
"decision-mapping": "task decomposition",
|
|
3362
|
+
delight: "design",
|
|
3363
|
+
"deploy-to-vercel": "deployment orchestration",
|
|
3364
|
+
"design-an-interface": "system design",
|
|
3365
|
+
"design-mobile-apps": "design",
|
|
3366
|
+
"design-taste-frontend": "design",
|
|
3367
|
+
"design-taste-frontend-v1": "design",
|
|
3368
|
+
"design-taste-frontend-v2-experimental": "design",
|
|
3369
|
+
"developing-genkit-dart": "agent application building",
|
|
3370
|
+
"developing-genkit-go": "agent application building",
|
|
3371
|
+
"developing-genkit-js": "agent application building",
|
|
3372
|
+
"developing-genkit-python": "agent application building",
|
|
3373
|
+
diagnose: "root-cause debugging",
|
|
3374
|
+
"diagnosing-bugs": "root-cause debugging",
|
|
3375
|
+
"diagnosing-superpowers": "trace analysis and debugging",
|
|
3376
|
+
"directory-submissions": "search engine optimization",
|
|
3377
|
+
"discernment-nudge": "responsible ai governance",
|
|
3378
|
+
"discover-azure-skills": "tool use planning",
|
|
3379
|
+
"dispatching-parallel-agents": "multi-agent coordination",
|
|
3380
|
+
distill: "design",
|
|
3381
|
+
"doc-coauthoring": "technical writing",
|
|
3382
|
+
docx: "document authoring",
|
|
3383
|
+
"domain-modeling": "data modeling",
|
|
3384
|
+
"durable-objects": "serverless function development",
|
|
3385
|
+
"edit-article": "editing and proofreading",
|
|
3386
|
+
"elevenlabs-music-generation": "text to audio",
|
|
3387
|
+
"email-sequence": "email campaign creation",
|
|
3388
|
+
emails: "email campaign creation",
|
|
3389
|
+
"embedded-captions": "video editing",
|
|
3390
|
+
"emil-design-eng": "design",
|
|
3391
|
+
"entra-agent-id": "identity administration",
|
|
3392
|
+
"entra-app-registration": "authentication implementation",
|
|
3393
|
+
"env-and-assets-bootstrap": "dev environment setup",
|
|
3394
|
+
events: "campaign planning",
|
|
3395
|
+
"executing-plans": "task management",
|
|
3396
|
+
"explore-code": "deep learning training",
|
|
3397
|
+
"explore-run": "experiment tracking",
|
|
3398
|
+
"extension-to-functions-codebase": "code migration and modernization",
|
|
3399
|
+
extract: "design",
|
|
3400
|
+
"face-swap": "face swapping",
|
|
3401
|
+
"faceless-explainer": "text to video",
|
|
3402
|
+
figma: "video editing",
|
|
3403
|
+
"file-test-bug": "issue tracking",
|
|
3404
|
+
"find-animation-opportunities": "web animation",
|
|
3405
|
+
"find-skills": "tool use planning",
|
|
3406
|
+
"finishing-a-development-branch": "git workflow",
|
|
3407
|
+
"firebase-ai-logic": "third-party integration",
|
|
3408
|
+
"firebase-ai-logic-basics": "third-party integration",
|
|
3409
|
+
"firebase-app-hosting-basics": "deployment orchestration",
|
|
3410
|
+
"firebase-auth-basics": "authentication implementation",
|
|
3411
|
+
"firebase-basics": "dev environment setup",
|
|
3412
|
+
"firebase-crashlytics": "monitoring and alerting",
|
|
3413
|
+
"firebase-data-connect": "relational database operations",
|
|
3414
|
+
"firebase-firestore": "nosql database operations",
|
|
3415
|
+
"firebase-firestore-basics": "nosql database operations",
|
|
3416
|
+
"firebase-firestore-enterprise-native-mode": "nosql database operations",
|
|
3417
|
+
"firebase-firestore-standard": "nosql database operations",
|
|
3418
|
+
"firebase-hosting-basics": "deployment orchestration",
|
|
3419
|
+
"firebase-local-env-setup": "dev environment setup",
|
|
3420
|
+
"firebase-remote-config-basics": "configuration management",
|
|
3421
|
+
"firebase-security-rules-auditor": "vulnerability analysis",
|
|
3422
|
+
"firestore-rules-creation": "authorization and access control",
|
|
3423
|
+
"firestore-security-rules-auditor": "vulnerability analysis",
|
|
3424
|
+
"flux-2-klein": "text to image",
|
|
3425
|
+
"flux-kontext": "instructed image editing",
|
|
3426
|
+
"form-cro": "conversion optimization",
|
|
3427
|
+
"free-tool-strategy": "growth strategy",
|
|
3428
|
+
"free-tools": "growth strategy",
|
|
3429
|
+
"frontend-design": "design",
|
|
3430
|
+
"frost-sequence-camera-orbit": "video editing",
|
|
3431
|
+
"full-output-enforcement": "prompt design",
|
|
3432
|
+
"gateway-workers": "serverless function development",
|
|
3433
|
+
"general-video": "video editing",
|
|
3434
|
+
"git-guardrails-claude-code": "git workflow",
|
|
3435
|
+
"glass-shard-title": "video editing",
|
|
3436
|
+
"google-agents-cli-adk-code": "agent application building",
|
|
3437
|
+
"google-agents-cli-deploy": "deployment orchestration",
|
|
3438
|
+
"google-agents-cli-eval": "agent evaluation",
|
|
3439
|
+
"google-agents-cli-observability": "llm tracing and instrumentation",
|
|
3440
|
+
"google-agents-cli-publish": "release management",
|
|
3441
|
+
"google-agents-cli-scaffold": "project scaffolding",
|
|
3442
|
+
"google-agents-cli-workflow": "agent application building",
|
|
3443
|
+
"gpt-image": "text to image",
|
|
3444
|
+
"gpt-image-2": "text to image",
|
|
3445
|
+
"gpt-image-edit": "instructed image editing",
|
|
3446
|
+
"gpt-taste": "design",
|
|
3447
|
+
"graphic-overlays": "video editing",
|
|
3448
|
+
"grill-me": "problem solving",
|
|
3449
|
+
"grill-with-docs": "problem solving",
|
|
3450
|
+
grilling: "problem solving",
|
|
3451
|
+
gsap: "web animation",
|
|
3452
|
+
handoff: "context window management",
|
|
3453
|
+
happyhorse: "text to video",
|
|
3454
|
+
"happyhorse-1-0": "text to video",
|
|
3455
|
+
harden: "error handling implementation",
|
|
3456
|
+
"higgsfield-brandkit": "branding",
|
|
3457
|
+
"higgsfield-faceless-video": "text to video",
|
|
3458
|
+
"higgsfield-game-generation": "game development",
|
|
3459
|
+
"higgsfield-generate": "text to image",
|
|
3460
|
+
"higgsfield-marketplace-cards": "ad creative production",
|
|
3461
|
+
"higgsfield-product-photoshoot": "text to image",
|
|
3462
|
+
"higgsfield-soul-id": "model fine-tuning",
|
|
3463
|
+
"higgsfield-websites": "full-stack development",
|
|
3464
|
+
"higgsfield-youtube-thumbnail": "text to image",
|
|
3465
|
+
"high-end-visual-design": "design",
|
|
3466
|
+
hyperframes: "video",
|
|
3467
|
+
"hyperframes-animation": "web animation",
|
|
3468
|
+
"hyperframes-audio": "audio to audio",
|
|
3469
|
+
"hyperframes-cli": "video editing",
|
|
3470
|
+
"hyperframes-core": "video editing",
|
|
3471
|
+
"hyperframes-creative": "video editing",
|
|
3472
|
+
"hyperframes-keyframes": "video editing",
|
|
3473
|
+
"hyperframes-media": "audio generation",
|
|
3474
|
+
"hyperframes-read-first": "video",
|
|
3475
|
+
"hyperframes-registry": "video editing",
|
|
3476
|
+
"hyperframes-studio": "video editing",
|
|
3477
|
+
image: "image generation",
|
|
3478
|
+
"image-edit": "instructed image editing",
|
|
3479
|
+
"image-inpainting": "image inpainting",
|
|
3480
|
+
"image-outpainting": "image outpainting",
|
|
3481
|
+
"image-taste-frontend": "design",
|
|
3482
|
+
"image-to-video": "image to video",
|
|
3483
|
+
impeccable: "design",
|
|
3484
|
+
implement: "text to code",
|
|
3485
|
+
"implement-spec": "text to code",
|
|
3486
|
+
"improve-animations": "web animation",
|
|
3487
|
+
"improve-codebase-architecture": "architecture review",
|
|
3488
|
+
"industrial-brutalist-ui": "design",
|
|
3489
|
+
"influencer-marketing": "campaign planning",
|
|
3490
|
+
"infsh-cli": "script integration",
|
|
3491
|
+
"internal-comms": "business and professional writing",
|
|
3492
|
+
"investigate-first": "root-cause debugging",
|
|
3493
|
+
k2: "stream processing",
|
|
3494
|
+
"kling-3-0": "text to video",
|
|
3495
|
+
"landing-page-design": "conversion optimization",
|
|
3496
|
+
"lark-approval": "process operations",
|
|
3497
|
+
"lark-apps": "full-stack development",
|
|
3498
|
+
"lark-attendance": "employee lifecycle",
|
|
3499
|
+
"lark-base": "spreadsheet authoring",
|
|
3500
|
+
"lark-calendar": "calendar and scheduling",
|
|
3501
|
+
"lark-contact": "team messaging",
|
|
3502
|
+
"lark-doc": "document collaboration",
|
|
3503
|
+
"lark-drive": "document collaboration",
|
|
3504
|
+
"lark-event": "workflow automation",
|
|
3505
|
+
"lark-im": "team messaging",
|
|
3506
|
+
"lark-mail": "email management",
|
|
3507
|
+
"lark-markdown": "document collaboration",
|
|
3508
|
+
"lark-meeting": "meeting intelligence",
|
|
3509
|
+
"lark-minutes": "meeting intelligence",
|
|
3510
|
+
"lark-note": "meeting intelligence",
|
|
3511
|
+
"lark-okr": "roadmapping and prioritization",
|
|
3512
|
+
"lark-openapi-explorer": "api schema understanding",
|
|
3513
|
+
"lark-shared": "authentication implementation",
|
|
3514
|
+
"lark-sheets": "spreadsheet authoring",
|
|
3515
|
+
"lark-skill-maker": "agent tool building",
|
|
3516
|
+
"lark-slides": "presentation authoring",
|
|
3517
|
+
"lark-task": "task management",
|
|
3518
|
+
"lark-vc": "meeting intelligence",
|
|
3519
|
+
"lark-vc-agent": "meeting intelligence",
|
|
3520
|
+
"lark-whiteboard": "document collaboration",
|
|
3521
|
+
"lark-whiteboard-cli": "document collaboration",
|
|
3522
|
+
"lark-wiki": "knowledge base management",
|
|
3523
|
+
"lark-workflow-meeting-summary": "meeting intelligence",
|
|
3524
|
+
"lark-workflow-standup-report": "task management",
|
|
3525
|
+
launch: "product marketing and gtm",
|
|
3526
|
+
"launch-strategy": "product marketing and gtm",
|
|
3527
|
+
layout: "design",
|
|
3528
|
+
"lead-magnets": "lead generation",
|
|
3529
|
+
"lean-build": "text to code",
|
|
3530
|
+
"link-building": "search engine optimization",
|
|
3531
|
+
lipsync: "talking avatar generation",
|
|
3532
|
+
"loop-me": "requirements and prd authoring",
|
|
3533
|
+
lottie: "web animation",
|
|
3534
|
+
"markdown-token-optimizer": "context window management",
|
|
3535
|
+
"marketing-council": "marketing strategy",
|
|
3536
|
+
"marketing-ideas": "growth strategy",
|
|
3537
|
+
"marketing-loops": "workflow automation",
|
|
3538
|
+
"marketing-plan": "marketing strategy",
|
|
3539
|
+
"marketing-psychology": "marketing strategy",
|
|
3540
|
+
"mcp-builder": "agent tool building",
|
|
3541
|
+
"media-use": "video",
|
|
3542
|
+
mediabunny: "video",
|
|
3543
|
+
megacave: "text style transfer",
|
|
3544
|
+
"microsoft-foundry": "agent application building",
|
|
3545
|
+
"migrate-to-shoehorn": "code migration and modernization",
|
|
3546
|
+
migration: "code migration and modernization",
|
|
3547
|
+
"minimal-run-and-audit": "benchmark execution",
|
|
3548
|
+
"minimalist-ui": "design",
|
|
3549
|
+
"mobile-native": "styling and responsive layout",
|
|
3550
|
+
"motion-doctrine": "video editing",
|
|
3551
|
+
"motion-graphics": "video editing",
|
|
3552
|
+
"music-to-video": "video editing",
|
|
3553
|
+
"nano-banana-2": "text to image",
|
|
3554
|
+
"nano-banana-edit": "instructed image editing",
|
|
3555
|
+
"nextjs-on-cloudflare": "deployment orchestration",
|
|
3556
|
+
normalize: "design",
|
|
3557
|
+
"obsidian-vault": "note-taking",
|
|
3558
|
+
offers: "product marketing and gtm",
|
|
3559
|
+
onboard: "design",
|
|
3560
|
+
onboarding: "conversion optimization",
|
|
3561
|
+
"onboarding-cro": "conversion optimization",
|
|
3562
|
+
optimize: "code refactoring and optimization",
|
|
3563
|
+
"orbit-card": "video editing",
|
|
3564
|
+
overdrive: "web animation",
|
|
3565
|
+
"oversized-cursor": "video editing",
|
|
3566
|
+
"p-image": "text to image",
|
|
3567
|
+
"p-video": "text to video",
|
|
3568
|
+
"p-video-avatar": "talking avatar generation",
|
|
3569
|
+
"page-cro": "conversion optimization",
|
|
3570
|
+
"paid-ads": "paid ads management",
|
|
3571
|
+
"paper-context-resolver": "paper analysis",
|
|
3572
|
+
"paywall-upgrade-cro": "conversion optimization",
|
|
3573
|
+
paywalls: "conversion optimization",
|
|
3574
|
+
pdf: "pdf manipulation",
|
|
3575
|
+
"pick-ui-library": "frontend development",
|
|
3576
|
+
polish: "design",
|
|
3577
|
+
"popup-cro": "conversion optimization",
|
|
3578
|
+
popups: "conversion optimization",
|
|
3579
|
+
pptx: "presentation authoring",
|
|
3580
|
+
pr: "pull request management",
|
|
3581
|
+
"pr-to-video": "video editing",
|
|
3582
|
+
pricing: "product strategy",
|
|
3583
|
+
"pricing-strategy": "product strategy",
|
|
3584
|
+
"prisma-cli": "orm and data access",
|
|
3585
|
+
"prisma-client-api": "orm and data access",
|
|
3586
|
+
"prisma-compute": "deployment orchestration",
|
|
3587
|
+
"prisma-database-setup": "orm and data access",
|
|
3588
|
+
"prisma-driver-adapter-implementation": "orm and data access",
|
|
3589
|
+
"prisma-mongodb-upgrade": "code migration and modernization",
|
|
3590
|
+
"prisma-orm-setup": "orm and data access",
|
|
3591
|
+
"prisma-postgres": "relational database operations",
|
|
3592
|
+
"prisma-postgres-setup": "relational database operations",
|
|
3593
|
+
"prisma-upgrade-v7": "code migration and modernization",
|
|
3594
|
+
"product-hunt-launch": "product marketing and gtm",
|
|
3595
|
+
"product-launch-video": "ad creative production",
|
|
3596
|
+
"product-marketing": "product marketing and gtm",
|
|
3597
|
+
"product-marketing-context": "product marketing and gtm",
|
|
3598
|
+
"product-photography": "text to image",
|
|
3599
|
+
"programmatic-seo": "seo content optimization",
|
|
3600
|
+
prospecting: "lead generation",
|
|
3601
|
+
"public-relations": "marketing strategy",
|
|
3602
|
+
"python-appservice-deploy": "deployment orchestration",
|
|
3603
|
+
"python-executor": "script integration",
|
|
3604
|
+
qa: "issue tracking",
|
|
3605
|
+
quieter: "design",
|
|
3606
|
+
"receiving-code-review": "code review",
|
|
3607
|
+
"reddit-automation": "social content creation",
|
|
3608
|
+
"redesign-existing-projects": "design",
|
|
3609
|
+
"referral-program": "growth strategy",
|
|
3610
|
+
referrals: "growth strategy",
|
|
3611
|
+
relight: "instructed image editing",
|
|
3612
|
+
"remotion-best-practices": "video",
|
|
3613
|
+
"remotion-captions": "video editing",
|
|
3614
|
+
"remotion-create": "video",
|
|
3615
|
+
"remotion-docs": "document or database question answering",
|
|
3616
|
+
"remotion-interactivity": "video",
|
|
3617
|
+
"remotion-maps": "video",
|
|
3618
|
+
"remotion-markup": "video",
|
|
3619
|
+
"remotion-multimedia": "video",
|
|
3620
|
+
"remotion-render": "video",
|
|
3621
|
+
"remotion-saas": "full-stack development",
|
|
3622
|
+
"remotion-studio": "video",
|
|
3623
|
+
"remotion-to-hyperframes": "code migration and modernization",
|
|
3624
|
+
"remotion-upgrade": "dependency management",
|
|
3625
|
+
"remove-animations": "web animation",
|
|
3626
|
+
"repo-intake-and-plan": "code comprehension",
|
|
3627
|
+
"request-refactor-plan": "code refactoring and optimization",
|
|
3628
|
+
"requesting-code-review": "code review",
|
|
3629
|
+
research: "deep research",
|
|
3630
|
+
"resolving-merge-conflicts": "git workflow",
|
|
3631
|
+
retro: "postmortem authoring",
|
|
3632
|
+
review: "code review",
|
|
3633
|
+
"review-animations": "web animation",
|
|
3634
|
+
revops: "crm management",
|
|
3635
|
+
"run-train": "deep learning training",
|
|
3636
|
+
"runcomfy-cli": "script integration",
|
|
3637
|
+
"safe-debug": "root-cause debugging",
|
|
3638
|
+
"safe-refactor": "code refactoring and optimization",
|
|
3639
|
+
"sales-enablement": "sales enablement",
|
|
3640
|
+
"sandbox-migrate-to-next": "code migration and modernization",
|
|
3641
|
+
"sandbox-next": "containerization",
|
|
3642
|
+
"sandbox-sdk": "containerization",
|
|
3643
|
+
"sandbox-stable": "containerization",
|
|
3644
|
+
"scaffold-exercises": "project scaffolding",
|
|
3645
|
+
schema: "structured data markup",
|
|
3646
|
+
"schema-markup": "structured data markup",
|
|
3647
|
+
"seam-craft": "video editing",
|
|
3648
|
+
seedance: "text to video",
|
|
3649
|
+
"seedance-v2": "text to video",
|
|
3650
|
+
sensei: "agent tool building",
|
|
3651
|
+
"seo-audit": "seo auditing",
|
|
3652
|
+
"setup-matt-pocock-skills": "dev environment setup",
|
|
3653
|
+
"setup-pre-commit": "dev environment setup",
|
|
3654
|
+
"setup-ts-deep-modules": "static analysis and linting",
|
|
3655
|
+
shape: "design",
|
|
3656
|
+
signup: "conversion optimization",
|
|
3657
|
+
"signup-flow-cro": "conversion optimization",
|
|
3658
|
+
"site-architecture": "search engine optimization",
|
|
3659
|
+
"skill-authoring": "agent tool building",
|
|
3660
|
+
"skill-creator": "agent tool building",
|
|
3661
|
+
"slack-gif-creator": "image generation",
|
|
3662
|
+
slideshow: "presentation authoring",
|
|
3663
|
+
sms: "email marketing",
|
|
3664
|
+
social: "social media management",
|
|
3665
|
+
"social-content": "social content creation",
|
|
3666
|
+
"stitch-design-taste": "design",
|
|
3667
|
+
"storyboard-creation": "video",
|
|
3668
|
+
"subagent-driven-development": "agentic workflow orchestration",
|
|
3669
|
+
"surgical-patch": "root-cause debugging",
|
|
3670
|
+
"systematic-debugging": "root-cause debugging",
|
|
3671
|
+
tailwind: "styling and responsive layout",
|
|
3672
|
+
"talking-head-recut": "video editing",
|
|
3673
|
+
tdd: "test strategy and tdd",
|
|
3674
|
+
teach: "research and scholarship",
|
|
3675
|
+
"template-skill": "agent tool building",
|
|
3676
|
+
"test-driven-development": "test strategy and tdd",
|
|
3677
|
+
"theme-factory": "design",
|
|
3678
|
+
three: "web animation",
|
|
3679
|
+
"to-issues": "issue tracking",
|
|
3680
|
+
"to-prd": "requirements and prd authoring",
|
|
3681
|
+
"to-questionnaire": "reasoning and planning",
|
|
3682
|
+
"to-spec": "requirements and prd authoring",
|
|
3683
|
+
"to-tickets": "issue tracking",
|
|
3684
|
+
triage: "issue tracking",
|
|
3685
|
+
"turnstile-spin": "security hardening",
|
|
3686
|
+
"twitter-automation": "social media management",
|
|
3687
|
+
typegpu: "web animation",
|
|
3688
|
+
typeset: "design",
|
|
3689
|
+
"ubiquitous-language": "data modeling",
|
|
3690
|
+
ultracave: "text style transfer",
|
|
3691
|
+
"using-git-worktrees": "git workflow",
|
|
3692
|
+
"using-superpowers": "tool use planning",
|
|
3693
|
+
"vercel-cli-with-tokens": "deployment orchestration",
|
|
3694
|
+
"vercel-composition-patterns": "frontend development",
|
|
3695
|
+
"vercel-deploy": "deployment orchestration",
|
|
3696
|
+
"vercel-optimize": "cloud cost optimization",
|
|
3697
|
+
"vercel-react-best-practices": "code refactoring and optimization",
|
|
3698
|
+
"vercel-react-native-skills": "cross-platform mobile development",
|
|
3699
|
+
"vercel-react-view-transitions": "web animation",
|
|
3700
|
+
"verification-before-completion": "quality evaluation",
|
|
3701
|
+
"verify-and-stop": "software testing",
|
|
3702
|
+
video: "video",
|
|
3703
|
+
"video-ad-specs": "ad creative production",
|
|
3704
|
+
"video-edit": "video editing",
|
|
3705
|
+
"video-extend": "video editing",
|
|
3706
|
+
"video-inpainting": "video editing",
|
|
3707
|
+
"video-outpainting": "video editing",
|
|
3708
|
+
waapi: "web animation",
|
|
3709
|
+
"wait-what": "text paraphrasing",
|
|
3710
|
+
"wan-2-7": "text to video",
|
|
3711
|
+
wayfinder: "task decomposition",
|
|
3712
|
+
"web-artifacts-builder": "frontend development",
|
|
3713
|
+
"web-design-guidelines": "design",
|
|
3714
|
+
"web-perf": "code refactoring and optimization",
|
|
3715
|
+
"web-search": "web search research",
|
|
3716
|
+
"webapp-testing": "end-to-end and ui testing",
|
|
3717
|
+
"website-to-hyperframes": "video editing",
|
|
3718
|
+
"website-to-video": "video editing",
|
|
3719
|
+
"wireframe-portal-title": "video editing",
|
|
3720
|
+
wizard: "cli and terminal app development",
|
|
3721
|
+
"workers-best-practices": "serverless function development",
|
|
3722
|
+
"workers-profiling": "performance monitoring",
|
|
3723
|
+
wrangler: "deployment orchestration",
|
|
3724
|
+
"write-a-skill": "agent tool building",
|
|
3725
|
+
"write-swift": "native mobile development",
|
|
3726
|
+
"writing-beats": "content writing",
|
|
3727
|
+
"writing-for-agents": "technical writing",
|
|
3728
|
+
"writing-fragments": "content writing",
|
|
3729
|
+
"writing-great-skills": "agent tool building",
|
|
3730
|
+
"writing-guidelines": "copy editing",
|
|
3731
|
+
"writing-plans": "task decomposition",
|
|
3732
|
+
"writing-shape": "article and blog writing",
|
|
3733
|
+
"writing-skills": "agent tool building",
|
|
3734
|
+
"xcode-project-setup": "dependency management",
|
|
3735
|
+
xlsx: "spreadsheet authoring",
|
|
3736
|
+
"youtube-thumbnail-design": "image generation",
|
|
3737
|
+
"zoom-out": "code comprehension"
|
|
3738
|
+
}
|
|
3739
|
+
};
|
|
3740
|
+
|
|
3741
|
+
// ../hook/src/skill-failure.ts
|
|
3742
|
+
var MISSING_KEY = /\b(api[ _-]?key|access[ _-]?token|credentials?)\b.{0,40}\b(missing|not set|not found|required|invalid|expired)\b|(?<!\bnot )\b(missing|no|invalid|expired)\b.{0,20}\b(api[ _-]?key|access[ _-]?token|credentials?)\b|\b(HTTP|status)(\/[\d.]+| code)?[ :=]*(401|403)\b|\b(401|403) (unauthori[sz]ed|forbidden)\b|\bunauthori[sz]ed\b|not authenticated|not logged in/i;
|
|
3743
|
+
var MISSING_KEY_VARIABLE = /\b[A-Z0-9_]*(API_KEY|TOKEN|SECRET)\b.{0,30}\b(not set|missing|required|unset)\b/;
|
|
3744
|
+
var MISSING_PROGRAM = /command not found|executable file not found|\bnot installed\b|No module named|spawn \S+ ENOENT|Cannot find module/i;
|
|
3745
|
+
var MISSING_FILE = /No such file or directory|\bENOENT\b|file not found|\b(file|path|directory|folder)\b(?!\.).{0,80}\bdoes not exist\b/i;
|
|
3746
|
+
function failureReason(text) {
|
|
3747
|
+
if (MISSING_PROGRAM.test(text))
|
|
3748
|
+
return "missing-program";
|
|
3749
|
+
if (MISSING_KEY.test(text) || MISSING_KEY_VARIABLE.test(text))
|
|
3750
|
+
return "missing-key";
|
|
3751
|
+
if (MISSING_FILE.test(text))
|
|
3752
|
+
return "missing-file";
|
|
3753
|
+
return null;
|
|
3754
|
+
}
|
|
3755
|
+
var PROGRAM_IN_ERROR = [
|
|
3756
|
+
/command not found: ([^\s'"`]+)/gi,
|
|
3757
|
+
/(?:^|[\s:])([^\s:'"`]+): (?:command )?not found\b/gim,
|
|
3758
|
+
/\bexec(?:utable)?:? "?([^\s"]+)"?: executable file not found/gi,
|
|
3759
|
+
/\bspawn ([^\s'"`]+) ENOENT\b/gi,
|
|
3760
|
+
/No module named '?([A-Za-z0-9_.-]+)'?/g,
|
|
3761
|
+
/Cannot find module '([^']+)'/g,
|
|
3762
|
+
/(?:^|\s)'?([^\s'"`]+)'? is not installed\b/gim
|
|
3763
|
+
];
|
|
3764
|
+
var KEY_IN_ERROR = [
|
|
3765
|
+
/\b([A-Z][A-Z0-9_]{2,63})\b[^=\n]{0,30}\b(?:not set|missing|required|unset|is empty)\b/g,
|
|
3766
|
+
/\b(?:set|export|provide|missing|define)\s+(?:the\s+)?(?:environment variable\s+)?\$?([A-Z][A-Z0-9_]{2,63})\b/gi,
|
|
3767
|
+
/\$?\b([A-Z][A-Z0-9_]*(?:API_KEY|TOKEN|SECRET|ACCESS_KEY)[A-Z0-9_]*)\b/g
|
|
3768
|
+
];
|
|
3769
|
+
function needOf(reason, text, markdown) {
|
|
3770
|
+
const kind = reason === "missing-program" ? "program" : reason === "missing-key" ? "key" : null;
|
|
3771
|
+
if (kind === null)
|
|
3772
|
+
return null;
|
|
3773
|
+
for (const pattern of kind === "program" ? PROGRAM_IN_ERROR : KEY_IN_ERROR) {
|
|
3774
|
+
for (const match of text.matchAll(pattern)) {
|
|
3775
|
+
const named = match[1] ?? "";
|
|
3776
|
+
const name = kind === "program" ? (named.split("/").pop() ?? "").toLowerCase() : named;
|
|
3777
|
+
if (!/[a-z]/i.test(name))
|
|
3778
|
+
continue;
|
|
3779
|
+
const kept = publishableNeed3(name, markdown, kind);
|
|
3780
|
+
if (kept !== null)
|
|
3781
|
+
return kept;
|
|
3782
|
+
}
|
|
3783
|
+
}
|
|
3784
|
+
return null;
|
|
3785
|
+
}
|
|
3786
|
+
var RUNNERS2 = new Set(["bash", "bun", "bunx", "node", "npx", "python", "python3", "sh", "uv", "uvx", "zsh"]);
|
|
3787
|
+
function programsOf(command) {
|
|
3788
|
+
const found = [];
|
|
3789
|
+
for (const part of command.split(/&&|\|\||[;|\n]/)) {
|
|
3790
|
+
const words = part.trim().split(/\s+/).filter((word) => !/^[A-Za-z_][A-Za-z0-9_]*=/.test(word));
|
|
3791
|
+
const first2 = words[0];
|
|
3792
|
+
if (first2 === undefined || first2 === "")
|
|
3793
|
+
continue;
|
|
3794
|
+
const program = first2.split("/").pop() ?? first2;
|
|
3795
|
+
if (!RUNNERS2.has(program)) {
|
|
3796
|
+
found.push(program);
|
|
3797
|
+
continue;
|
|
3798
|
+
}
|
|
3799
|
+
const target = words.slice(1).find((word) => !word.startsWith("-") && word !== "run");
|
|
3800
|
+
if (target !== undefined)
|
|
3801
|
+
found.push(target.split("/").pop() ?? target);
|
|
3802
|
+
}
|
|
3803
|
+
return found;
|
|
3804
|
+
}
|
|
3805
|
+
var escaped2 = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3806
|
+
function askedBySkill(command, markdown, directory) {
|
|
3807
|
+
if (new RegExp(`${escaped2(directory)}(?=[/\\s'"]|$)`).test(command))
|
|
3808
|
+
return true;
|
|
3809
|
+
const { cli, runs } = readRequires(markdown);
|
|
3810
|
+
const named = new Set([...cli, ...runs]);
|
|
3811
|
+
return programsOf(command).some((program) => named.has(program) || program.includes(".") && new RegExp(`(?:^|[\\s\`'"/(])${escaped2(program)}(?=[\\s\`'")]|$)`, "m").test(markdown));
|
|
3812
|
+
}
|
|
3813
|
+
var NODES = skill_nodes_13579_default.nodes;
|
|
3814
|
+
var WORDS = { labels: NODES, aliases: domain_aliases_default.aliases };
|
|
3815
|
+
var SKILL_NODE = skill_nodes_13579_default.skills;
|
|
3816
|
+
function lastUserWords(transcript) {
|
|
3817
|
+
let words = "";
|
|
3818
|
+
for (const line of transcript.split(`
|
|
3819
|
+
`)) {
|
|
3820
|
+
if (!line.includes('"user"'))
|
|
3821
|
+
continue;
|
|
3822
|
+
try {
|
|
3823
|
+
const entry = JSON.parse(line);
|
|
744
3824
|
if (entry.type !== "user" || entry.isMeta === true)
|
|
745
3825
|
continue;
|
|
746
3826
|
const content = entry.message?.content;
|
|
@@ -753,8 +3833,8 @@ function lastUserWords(transcript) {
|
|
|
753
3833
|
}
|
|
754
3834
|
return words;
|
|
755
3835
|
}
|
|
756
|
-
function contextOf(words, skill
|
|
757
|
-
return
|
|
3836
|
+
function contextOf(words, skill) {
|
|
3837
|
+
return domainInText2(words, WORDS) ?? SKILL_NODE[skill.split(":").pop() ?? skill] ?? null;
|
|
758
3838
|
}
|
|
759
3839
|
function answerOf(response) {
|
|
760
3840
|
const blocks = Array.isArray(response) ? response : typeof response === "object" && response !== null && Array.isArray(response.content) ? response.content : [response];
|
|
@@ -779,7 +3859,7 @@ function recordSearch(call, cwd) {
|
|
|
779
3859
|
const answer = answerOf(call.response);
|
|
780
3860
|
if (answer === null || !Array.isArray(answer["skills"]))
|
|
781
3861
|
return;
|
|
782
|
-
const domain = typeof answer["domain"] === "string" &&
|
|
3862
|
+
const domain = typeof answer["domain"] === "string" && NODES.includes(answer["domain"]) ? answer["domain"] : null;
|
|
783
3863
|
const skills = answer["skills"].flatMap((row) => typeof row?.name === "string" && typeof row.source === "string" ? [{ name: row.name, source: row.source }] : []);
|
|
784
3864
|
if (skills.length === 0)
|
|
785
3865
|
return;
|
|
@@ -815,6 +3895,7 @@ var readIfThere = (path) => existsSync(path) ? readFileSync2(path, "utf8") : nul
|
|
|
815
3895
|
function installedSource(name, cwd) {
|
|
816
3896
|
const known = [
|
|
817
3897
|
...installedFromSkillLock(readIfThere(join3(homedir2(), ".agents", ".skill-lock.json"))),
|
|
3898
|
+
...installedFromSkillLock(readIfThere(join3(cwd, "skills-lock.json"))),
|
|
818
3899
|
...installedFromReceipts(readIfThere(join3(arpHome(), "receipts.jsonl"))),
|
|
819
3900
|
...installedFromReceipts(readIfThere(join3(cwd, ".arp", "receipts.jsonl")))
|
|
820
3901
|
];
|
|
@@ -865,13 +3946,13 @@ function appendFailure(line) {
|
|
|
865
3946
|
function recordSkillFailure(call, cwd) {
|
|
866
3947
|
const classified = classifyCall(call);
|
|
867
3948
|
if (classified === null || classified.outcome === "ok" || call.session === null)
|
|
868
|
-
return;
|
|
3949
|
+
return null;
|
|
869
3950
|
const command = call.input?.["command"];
|
|
870
3951
|
if (typeof command !== "string")
|
|
871
|
-
return;
|
|
3952
|
+
return null;
|
|
872
3953
|
const reason = failureReason(failureText(call));
|
|
873
3954
|
if (reason === null)
|
|
874
|
-
return;
|
|
3955
|
+
return null;
|
|
875
3956
|
try {
|
|
876
3957
|
const log = process.env["ARP_SESSION_LOG"] ?? join3(cwd, ".arp", "session.jsonl");
|
|
877
3958
|
for (const skill of skillsOfSession(log, call.session)) {
|
|
@@ -883,33 +3964,258 @@ function recordSkillFailure(call, cwd) {
|
|
|
883
3964
|
continue;
|
|
884
3965
|
const search = foundBySearch(cwd, call.session, skill.name);
|
|
885
3966
|
const words = call.transcript !== null && existsSync(call.transcript) ? lastUserWords(readFileSync2(call.transcript, "utf8")) : "";
|
|
886
|
-
const domain = search?.domain ?? contextOf(words, skill.name
|
|
887
|
-
|
|
3967
|
+
const domain = search?.domain ?? contextOf(words, skill.name);
|
|
3968
|
+
const need = needOf(reason, failureText(call), markdown);
|
|
3969
|
+
const line = {
|
|
888
3970
|
at: new Date().toISOString(),
|
|
889
3971
|
session: call.session,
|
|
890
3972
|
skill: skill.name,
|
|
891
3973
|
source: skill.source ?? search?.source ?? installedSource(skill.name.split(":").pop() ?? skill.name, cwd),
|
|
892
3974
|
...domain === null ? {} : { domain },
|
|
893
|
-
reason
|
|
894
|
-
|
|
895
|
-
|
|
3975
|
+
reason,
|
|
3976
|
+
...need === null ? {} : { need }
|
|
3977
|
+
};
|
|
3978
|
+
return appendFailure(line) ? line : null;
|
|
896
3979
|
}
|
|
897
3980
|
} catch {}
|
|
3981
|
+
return null;
|
|
3982
|
+
}
|
|
3983
|
+
|
|
3984
|
+
// ../hook/src/failure-answer.ts
|
|
3985
|
+
var ANSWER_MS = 3000;
|
|
3986
|
+
var WHERE = "Intuition testnet 13579";
|
|
3987
|
+
var IN_CONTEXT_OF = _13579_default.arp.endpointBinding.inContextOf;
|
|
3988
|
+
var PAYERS = [_13579_default.arp.deployer, _13579_default.arp.sponsor];
|
|
3989
|
+
var sha256 = (text) => createHash("sha256").update(text, "utf8").digest();
|
|
3990
|
+
async function answerFailure(failure, env = process.env, fetcher) {
|
|
3991
|
+
if (env["ARP_FAILURE_LINES"] === "off" || failure.source === null)
|
|
3992
|
+
return null;
|
|
3993
|
+
const url = skillUrlOf2(failure.source, failure.skill);
|
|
3994
|
+
if (url === null)
|
|
3995
|
+
return null;
|
|
3996
|
+
const { uri, label } = skillAtom(url, sha256);
|
|
3997
|
+
const graphqlUrl = env["INTUITION_GRAPHQL_URL"] ?? _13579_default.chain.graphqlUrl;
|
|
3998
|
+
let timer;
|
|
3999
|
+
const late = new Promise((resolve) => {
|
|
4000
|
+
timer = setTimeout(() => resolve(null), ANSWER_MS);
|
|
4001
|
+
});
|
|
4002
|
+
const read = readSkillLines(uri, {
|
|
4003
|
+
graphqlUrl,
|
|
4004
|
+
inContextOf: IN_CONTEXT_OF,
|
|
4005
|
+
payers: PAYERS,
|
|
4006
|
+
timeoutMs: ANSWER_MS,
|
|
4007
|
+
...fetcher === undefined ? {} : { fetch: fetcher }
|
|
4008
|
+
}).then((usage) => linesSentence(usage, label, WHERE)).catch(() => null);
|
|
4009
|
+
try {
|
|
4010
|
+
return await Promise.race([read, late]);
|
|
4011
|
+
} finally {
|
|
4012
|
+
clearTimeout(timer);
|
|
4013
|
+
}
|
|
4014
|
+
}
|
|
4015
|
+
function failureOutput(context) {
|
|
4016
|
+
return JSON.stringify({ hookSpecificOutput: { hookEventName: "PostToolUseFailure", additionalContext: context } });
|
|
4017
|
+
}
|
|
4018
|
+
var RECORD = Symbol("record");
|
|
4019
|
+
async function postToolUseFailure(payload) {
|
|
4020
|
+
const call = toolCallOf(payload);
|
|
4021
|
+
if (call === null || call.tool !== "Bash")
|
|
4022
|
+
return RECORD;
|
|
4023
|
+
const failure = recordSkillFailure(call, call.cwd ?? process.cwd());
|
|
4024
|
+
const context = failure === null ? null : await answerFailure(failure);
|
|
4025
|
+
return context === null ? null : failureOutput(context);
|
|
4026
|
+
}
|
|
4027
|
+
|
|
4028
|
+
// ../hook/src/worked.ts
|
|
4029
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync3 } from "node:fs";
|
|
4030
|
+
import { dirname as dirname3, join as join4 } from "node:path";
|
|
4031
|
+
|
|
4032
|
+
// ../../packages/protocol/src/local-outcomes.ts
|
|
4033
|
+
var OUTCOME_ANSWERS2 = ["worked", "failed", "unsure"];
|
|
4034
|
+
function isOutcomeAnswer2(word) {
|
|
4035
|
+
return OUTCOME_ANSWERS2.includes(word);
|
|
4036
|
+
}
|
|
4037
|
+
var ANSWER = /^[\s>*`-]*arp:\s*([A-Za-z0-9._:@/-]+)\s+(worked|failed|unsure)[\s.`*]*$/gim;
|
|
4038
|
+
function answersIn2(text, asked) {
|
|
4039
|
+
const found = new Map;
|
|
4040
|
+
for (const match of text.matchAll(ANSWER)) {
|
|
4041
|
+
const [, skill, word] = match;
|
|
4042
|
+
if (skill === undefined || word === undefined)
|
|
4043
|
+
continue;
|
|
4044
|
+
const answer = word.toLowerCase();
|
|
4045
|
+
if (!asked.includes(skill) || found.has(skill) || !isOutcomeAnswer2(answer))
|
|
4046
|
+
continue;
|
|
4047
|
+
found.set(skill, answer);
|
|
4048
|
+
}
|
|
4049
|
+
return found;
|
|
4050
|
+
}
|
|
4051
|
+
|
|
4052
|
+
// ../hook/src/worked.ts
|
|
4053
|
+
var MAX_ASKED = 5;
|
|
4054
|
+
function isRecord5(value) {
|
|
4055
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4056
|
+
}
|
|
4057
|
+
var field = (raw, snake, camel) => raw[snake] ?? raw[camel];
|
|
4058
|
+
var text = (value) => typeof value === "string" && value.length > 0 ? value : null;
|
|
4059
|
+
function stopInputOf(raw) {
|
|
4060
|
+
if (!isRecord5(raw) || field(raw, "hook_event_name", "hookEventName") !== "Stop")
|
|
4061
|
+
return null;
|
|
4062
|
+
const session = text(field(raw, "session_id", "sessionId"));
|
|
4063
|
+
if (session === null)
|
|
4064
|
+
return null;
|
|
4065
|
+
return {
|
|
4066
|
+
session,
|
|
4067
|
+
cwd: text(raw["cwd"]) ?? process.cwd(),
|
|
4068
|
+
active: field(raw, "stop_hook_active", "stopHookActive") === true,
|
|
4069
|
+
last: text(field(raw, "last_assistant_message", "lastAssistantMessage")) ?? "",
|
|
4070
|
+
transcript: text(field(raw, "transcript_path", "transcriptPath"))
|
|
4071
|
+
};
|
|
4072
|
+
}
|
|
4073
|
+
function questionFor(skills) {
|
|
4074
|
+
return "ARP asks once per skill: did each skill you used here do its job, and did the task finish with it? " + "Reply with one line per skill and nothing else: `arp: <skill> worked`, `arp: <skill> failed` or `arp: <skill> unsure`. " + `Kept on this machine unless \`arp share\` is on. Skills: ${skills.join(", ")}`;
|
|
4075
|
+
}
|
|
4076
|
+
var isArpsOwn = (name) => name === "arp" || name.endsWith(":arp");
|
|
4077
|
+
function rows(path) {
|
|
4078
|
+
if (!existsSync2(path))
|
|
4079
|
+
return [];
|
|
4080
|
+
const found = [];
|
|
4081
|
+
for (const line of readFileSync3(path, "utf8").split(`
|
|
4082
|
+
`)) {
|
|
4083
|
+
if (line.trim() === "")
|
|
4084
|
+
continue;
|
|
4085
|
+
try {
|
|
4086
|
+
const row = JSON.parse(line);
|
|
4087
|
+
if (isRecord5(row))
|
|
4088
|
+
found.push(row);
|
|
4089
|
+
} catch {}
|
|
4090
|
+
}
|
|
4091
|
+
return found;
|
|
4092
|
+
}
|
|
4093
|
+
function withOf(skill, source, cwd, before) {
|
|
4094
|
+
const name = skill.split(":").pop() ?? skill;
|
|
4095
|
+
let need = null;
|
|
4096
|
+
let latest = "";
|
|
4097
|
+
for (const row of rows(join4(arpHome(), "failures.jsonl"))) {
|
|
4098
|
+
const { skill: rowSkill, source: rowSource, need: rowNeed, at } = row;
|
|
4099
|
+
if (typeof rowSkill !== "string" || typeof rowNeed !== "string" || typeof at !== "string")
|
|
4100
|
+
continue;
|
|
4101
|
+
if (rowSkill !== skill && rowSkill !== name && !rowSkill.endsWith(`:${name}`))
|
|
4102
|
+
continue;
|
|
4103
|
+
if (typeof rowSource === "string" && source !== null && rowSource !== source)
|
|
4104
|
+
continue;
|
|
4105
|
+
if (at > before || at < latest)
|
|
4106
|
+
continue;
|
|
4107
|
+
need = rowNeed;
|
|
4108
|
+
latest = at;
|
|
4109
|
+
}
|
|
4110
|
+
if (need === null)
|
|
4111
|
+
return null;
|
|
4112
|
+
const directory = skillDirectory(name, cwd);
|
|
4113
|
+
if (directory === null)
|
|
4114
|
+
return null;
|
|
4115
|
+
return publishableNeed3(need, readFileSync3(join4(directory, "SKILL.md"), "utf8"));
|
|
4116
|
+
}
|
|
4117
|
+
function record(input, asked, outcome, at) {
|
|
4118
|
+
const search = foundBySearch(input.cwd, input.session, asked.skill);
|
|
4119
|
+
const words = input.transcript !== null && existsSync2(input.transcript) ? lastUserWords(readFileSync3(input.transcript, "utf8")) : "";
|
|
4120
|
+
const domain = search?.domain ?? contextOf(words, asked.skill);
|
|
4121
|
+
const source = asked.source ?? search?.source ?? installedSource(asked.skill.split(":").pop() ?? asked.skill, input.cwd);
|
|
4122
|
+
const named = outcome === "worked" ? withOf(asked.skill, source, input.cwd, at) : null;
|
|
4123
|
+
const line = {
|
|
4124
|
+
at,
|
|
4125
|
+
session: input.session,
|
|
4126
|
+
skill: asked.skill,
|
|
4127
|
+
source,
|
|
4128
|
+
...domain === null ? {} : { domain },
|
|
4129
|
+
outcome,
|
|
4130
|
+
...named === null ? {} : { with: named }
|
|
4131
|
+
};
|
|
4132
|
+
appendOwn(join4(arpHome(), "outcomes.jsonl"), `${JSON.stringify(line)}
|
|
4133
|
+
`);
|
|
4134
|
+
}
|
|
4135
|
+
async function stop(payload) {
|
|
4136
|
+
const input = stopInputOf(payload);
|
|
4137
|
+
if (input === null)
|
|
4138
|
+
return null;
|
|
4139
|
+
const ask = onStop(input);
|
|
4140
|
+
return ask === null ? null : JSON.stringify(ask);
|
|
4141
|
+
}
|
|
4142
|
+
function onStop(input, now = new Date) {
|
|
4143
|
+
try {
|
|
4144
|
+
const home = arpHome();
|
|
4145
|
+
mkdirSync3(home, { recursive: true });
|
|
4146
|
+
const askedPath = join4(home, "asked.jsonl");
|
|
4147
|
+
const asked = rows(askedPath).flatMap((row) => row["session"] === input.session && typeof row["skill"] === "string" ? [{
|
|
4148
|
+
at: typeof row["at"] === "string" ? row["at"] : "",
|
|
4149
|
+
session: input.session,
|
|
4150
|
+
skill: row["skill"],
|
|
4151
|
+
source: typeof row["source"] === "string" ? row["source"] : null
|
|
4152
|
+
}] : []);
|
|
4153
|
+
const answered = new Set(rows(join4(home, "outcomes.jsonl")).flatMap((row) => row["session"] === input.session && typeof row["skill"] === "string" ? [row["skill"]] : []));
|
|
4154
|
+
const pending = asked.filter((row) => !answered.has(row.skill));
|
|
4155
|
+
const at = now.toISOString();
|
|
4156
|
+
for (const [skill, answer] of answersIn2(input.last, pending.map((row) => row.skill))) {
|
|
4157
|
+
const row = pending.find((entry) => entry.skill === skill);
|
|
4158
|
+
if (row !== undefined)
|
|
4159
|
+
record(input, row, answer, at);
|
|
4160
|
+
}
|
|
4161
|
+
if (input.active)
|
|
4162
|
+
return null;
|
|
4163
|
+
const known = new Set(asked.map((row) => row.skill));
|
|
4164
|
+
const log = process.env["ARP_SESSION_LOG"] ?? join4(input.cwd, ".arp", "session.jsonl");
|
|
4165
|
+
const fresh = [];
|
|
4166
|
+
for (const skill of skillsOfSession(log, input.session)) {
|
|
4167
|
+
if (known.has(skill.name) || isArpsOwn(skill.name))
|
|
4168
|
+
continue;
|
|
4169
|
+
known.add(skill.name);
|
|
4170
|
+
fresh.push({ at, session: input.session, skill: skill.name, source: skill.source });
|
|
4171
|
+
}
|
|
4172
|
+
const ask = fresh.slice(0, MAX_ASKED);
|
|
4173
|
+
if (ask.length === 0)
|
|
4174
|
+
return null;
|
|
4175
|
+
mkdirSync3(dirname3(askedPath), { recursive: true });
|
|
4176
|
+
appendOwn(askedPath, ask.map((row) => `${JSON.stringify(row)}
|
|
4177
|
+
`).join(""));
|
|
4178
|
+
return { decision: "block", reason: questionFor(ask.map((row) => row.skill)) };
|
|
4179
|
+
} catch {
|
|
4180
|
+
return null;
|
|
4181
|
+
}
|
|
898
4182
|
}
|
|
899
4183
|
|
|
900
4184
|
// ../hook/src/hook.ts
|
|
4185
|
+
var ROUTES = {
|
|
4186
|
+
PreToolUse: (payload) => preToolUse(payload),
|
|
4187
|
+
PostToolUseFailure: (payload) => postToolUseFailure(payload),
|
|
4188
|
+
Stop: (payload) => stop(payload)
|
|
4189
|
+
};
|
|
4190
|
+
async function route(handler, payload) {
|
|
4191
|
+
let out = null;
|
|
4192
|
+
try {
|
|
4193
|
+
out = await handler(payload);
|
|
4194
|
+
} catch {
|
|
4195
|
+
out = null;
|
|
4196
|
+
}
|
|
4197
|
+
if (out === RECORD)
|
|
4198
|
+
return;
|
|
4199
|
+
if (out === null)
|
|
4200
|
+
process.exit(0);
|
|
4201
|
+
process.stdout.write(`${out}
|
|
4202
|
+
`, () => process.exit(0));
|
|
4203
|
+
await new Promise(() => {
|
|
4204
|
+
return;
|
|
4205
|
+
});
|
|
4206
|
+
}
|
|
901
4207
|
function fetchedRefs(cwd) {
|
|
902
|
-
const path = process.env["ARP_RECEIPTS"] ??
|
|
903
|
-
if (!
|
|
4208
|
+
const path = process.env["ARP_RECEIPTS"] ?? join5(cwd, ".arp", "receipts.jsonl");
|
|
4209
|
+
if (!existsSync3(path))
|
|
904
4210
|
return [];
|
|
905
|
-
let
|
|
4211
|
+
let text2;
|
|
906
4212
|
try {
|
|
907
|
-
|
|
4213
|
+
text2 = readFileSync4(path, "utf8");
|
|
908
4214
|
} catch {
|
|
909
4215
|
return [];
|
|
910
4216
|
}
|
|
911
|
-
const
|
|
912
|
-
for (const line of
|
|
4217
|
+
const rows2 = [];
|
|
4218
|
+
for (const line of text2.split(`
|
|
913
4219
|
`)) {
|
|
914
4220
|
if (line.trim().length === 0)
|
|
915
4221
|
continue;
|
|
@@ -917,7 +4223,7 @@ function fetchedRefs(cwd) {
|
|
|
917
4223
|
const row = JSON.parse(line);
|
|
918
4224
|
if (typeof row.skill !== "string" || typeof row.atom !== "string")
|
|
919
4225
|
continue;
|
|
920
|
-
|
|
4226
|
+
rows2.push({
|
|
921
4227
|
skill: row.skill,
|
|
922
4228
|
atom: row.atom,
|
|
923
4229
|
url: typeof row.url === "string" ? row.url : "",
|
|
@@ -927,7 +4233,7 @@ function fetchedRefs(cwd) {
|
|
|
927
4233
|
continue;
|
|
928
4234
|
}
|
|
929
4235
|
}
|
|
930
|
-
return
|
|
4236
|
+
return rows2;
|
|
931
4237
|
}
|
|
932
4238
|
function responseOf(call, outcome) {
|
|
933
4239
|
if (!call.hasResponse)
|
|
@@ -941,7 +4247,7 @@ function responseOf(call, outcome) {
|
|
|
941
4247
|
}
|
|
942
4248
|
return { answered: raw !== null, bytes, failed: outcome !== "ok" };
|
|
943
4249
|
}
|
|
944
|
-
function
|
|
4250
|
+
function readString2(from, key) {
|
|
945
4251
|
const value = from?.[key];
|
|
946
4252
|
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
947
4253
|
}
|
|
@@ -957,15 +4263,15 @@ function serverRef(server, cwd) {
|
|
|
957
4263
|
}
|
|
958
4264
|
function whatOf(call, cwd, fetched) {
|
|
959
4265
|
if (call.tool === "Skill") {
|
|
960
|
-
const name =
|
|
4266
|
+
const name = readString2(call.input, "skill");
|
|
961
4267
|
if (name === null)
|
|
962
4268
|
return null;
|
|
963
|
-
const source =
|
|
4269
|
+
const source = readString2(call.input, "source");
|
|
964
4270
|
return {
|
|
965
4271
|
kind: "skill",
|
|
966
4272
|
name,
|
|
967
4273
|
source,
|
|
968
|
-
ref:
|
|
4274
|
+
ref: readString2(call.input, "atom_id") ?? readString2(call.input, "tool_atom_id") ?? readString2(call.input, "url") ?? refForInvocation(name, source, fetched())
|
|
969
4275
|
};
|
|
970
4276
|
}
|
|
971
4277
|
const mcp = /^mcp__([^_]+(?:_[^_]+)*?)__/.exec(call.tool);
|
|
@@ -984,7 +4290,11 @@ try {
|
|
|
984
4290
|
process.exit(0);
|
|
985
4291
|
}
|
|
986
4292
|
if (process.argv.includes("--capture"))
|
|
987
|
-
capture(parsed,
|
|
4293
|
+
capture(parsed, join5(homedir3(), ".arp", "captures"));
|
|
4294
|
+
var event = eventOf(parsed);
|
|
4295
|
+
var handler = event === null ? undefined : ROUTES[event];
|
|
4296
|
+
if (handler !== undefined)
|
|
4297
|
+
await route(handler, parsed);
|
|
988
4298
|
var call = toolCallOf(parsed);
|
|
989
4299
|
if (call === null)
|
|
990
4300
|
process.exit(0);
|
|
@@ -1001,8 +4311,8 @@ if (what === null)
|
|
|
1001
4311
|
var classified = classifyCall(call);
|
|
1002
4312
|
if (classified === null)
|
|
1003
4313
|
process.exit(0);
|
|
1004
|
-
var own =
|
|
1005
|
-
var path = process.env["ARP_SESSION_LOG"] ??
|
|
4314
|
+
var own = join5(cwd, ".arp");
|
|
4315
|
+
var path = process.env["ARP_SESSION_LOG"] ?? join5(own, "session.jsonl");
|
|
1006
4316
|
var line = {
|
|
1007
4317
|
at: new Date().toISOString(),
|
|
1008
4318
|
session: call.session,
|
|
@@ -1015,11 +4325,11 @@ var line = {
|
|
|
1015
4325
|
response: responseOf(call, classified.outcome)
|
|
1016
4326
|
};
|
|
1017
4327
|
try {
|
|
1018
|
-
|
|
1019
|
-
if (
|
|
4328
|
+
mkdirSync4(dirname4(path), { recursive: true });
|
|
4329
|
+
if (dirname4(path) === own && lstatSync2(own).isSymbolicLink())
|
|
1020
4330
|
throw new Error(`${own} is a symbolic link`);
|
|
1021
|
-
const ignore =
|
|
1022
|
-
if (
|
|
4331
|
+
const ignore = join5(own, ".gitignore");
|
|
4332
|
+
if (dirname4(path) === own && !existsSync3(ignore))
|
|
1023
4333
|
writeFileSync2(ignore, `*
|
|
1024
4334
|
`, { flag: "wx" });
|
|
1025
4335
|
appendOwn(path, `${JSON.stringify(line)}
|