chat-recall 0.0.1 → 0.4.8
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/LICENSE +93 -0
- package/dist/auto-update.d.ts +52 -0
- package/dist/auto-update.d.ts.map +1 -0
- package/dist/auto-update.js +157 -0
- package/dist/auto-update.js.map +1 -0
- package/dist/cli.d.ts +6 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +26102 -0
- package/dist/cli.js.map +1 -0
- package/dist/client-events.d.ts +11 -0
- package/dist/client-events.d.ts.map +1 -0
- package/dist/client-events.js +77 -0
- package/dist/client-events.js.map +1 -0
- package/dist/collector-migrate.d.ts +27 -0
- package/dist/collector-migrate.d.ts.map +1 -0
- package/dist/collector-migrate.js +60 -0
- package/dist/collector-migrate.js.map +1 -0
- package/dist/device-auth.d.ts +37 -0
- package/dist/device-auth.d.ts.map +1 -0
- package/dist/device-auth.js +161 -0
- package/dist/device-auth.js.map +1 -0
- package/dist/escalate.d.ts +99 -0
- package/dist/escalate.d.ts.map +1 -0
- package/dist/escalate.js +219 -0
- package/dist/escalate.js.map +1 -0
- package/dist/http.d.ts +14 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +49 -0
- package/dist/http.js.map +1 -0
- package/dist/install-skills.d.ts +51 -0
- package/dist/install-skills.d.ts.map +1 -0
- package/dist/install-skills.js +223 -0
- package/dist/install-skills.js.map +1 -0
- package/dist/intent-drain.d.ts +25 -0
- package/dist/intent-drain.d.ts.map +1 -0
- package/dist/intent-drain.js +189 -0
- package/dist/intent-drain.js.map +1 -0
- package/dist/keyring.d.ts +28 -0
- package/dist/keyring.d.ts.map +1 -0
- package/dist/keyring.js +163 -0
- package/dist/keyring.js.map +1 -0
- package/dist/mcp.d.ts +8 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +23665 -0
- package/dist/mcp.js.map +1 -0
- package/dist/project-tasks.d.ts +54 -0
- package/dist/project-tasks.d.ts.map +1 -0
- package/dist/project-tasks.js +204 -0
- package/dist/project-tasks.js.map +1 -0
- package/dist/repair.d.ts +73 -0
- package/dist/repair.d.ts.map +1 -0
- package/dist/repair.js +267 -0
- package/dist/repair.js.map +1 -0
- package/dist/self-restart.d.ts +91 -0
- package/dist/self-restart.d.ts.map +1 -0
- package/dist/self-restart.js +137 -0
- package/dist/self-restart.js.map +1 -0
- package/dist/service-installer.d.ts +32 -0
- package/dist/service-installer.d.ts.map +1 -0
- package/dist/service-installer.js +231 -0
- package/dist/service-installer.js.map +1 -0
- package/dist/sync-client.d.ts +234 -0
- package/dist/sync-client.d.ts.map +1 -0
- package/dist/sync-client.js +1985 -0
- package/dist/sync-client.js.map +1 -0
- package/dist/sync-ledger.d.ts +131 -0
- package/dist/sync-ledger.d.ts.map +1 -0
- package/dist/sync-ledger.js +366 -0
- package/dist/sync-ledger.js.map +1 -0
- package/dist/update-notice.d.ts +16 -0
- package/dist/update-notice.d.ts.map +1 -0
- package/dist/update-notice.js +107 -0
- package/dist/update-notice.js.map +1 -0
- package/dist/verify-deep.d.ts +79 -0
- package/dist/verify-deep.d.ts.map +1 -0
- package/dist/verify-deep.js +97 -0
- package/dist/verify-deep.js.map +1 -0
- package/dist/verify-repair.d.ts +21 -0
- package/dist/verify-repair.d.ts.map +1 -0
- package/dist/verify-repair.js +58 -0
- package/dist/verify-repair.js.map +1 -0
- package/dist/watch.js +21550 -0
- package/hooks/chat_recall_escalate_hook.sh +51 -0
- package/hooks/chat_recall_resume_hook.sh +69 -0
- package/hooks/chat_recall_save_hook.sh +254 -0
- package/hooks/chat_recall_wakeup_hook.sh +39 -0
- package/package.json +38 -4
- package/skills/chat-recall/SKILL.md +76 -0
- package/skills/chat-recall-changes/SKILL.md +50 -0
- package/skills/chat-recall-find/SKILL.md +64 -0
- package/skills/chat-recall-memory/SKILL.md +55 -0
- package/skills/chat-recall-project/SKILL.md +75 -0
- package/skills/chat-recall-resume/SKILL.md +62 -0
- package/README.md +0 -7
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Device Authorization Grant (RFC 8628) for `chat-recall login` — two flavors,
|
|
3
|
+
* picked by the target server's /api/capabilities `authProvider`:
|
|
4
|
+
*
|
|
5
|
+
* better-auth (betterAuthDeviceLogin) — the flow runs against the SERVER
|
|
6
|
+
* ITSELF (/api/auth/device/*). The token it returns is a
|
|
7
|
+
* better-auth session token; the server accepts it as a Bearer
|
|
8
|
+
* at /api/me, /api/teams* via its bearer plugin.
|
|
9
|
+
* keycloak (deviceLogin, legacy) — the flow runs against the realm the
|
|
10
|
+
* server advertises as `oidcIssuer`. Returns a realm access
|
|
11
|
+
* token, verified server-side via JWKS.
|
|
12
|
+
*
|
|
13
|
+
* Either way the CLI can't host a redirect URI, so: mint a user_code, tell the
|
|
14
|
+
* human to approve it in a browser, poll the token endpoint until they do.
|
|
15
|
+
* There is NO baked-in issuer or server: everything is discovered from the
|
|
16
|
+
* server being logged into. Self-host with AUTH_PROVIDER=none skips this flow
|
|
17
|
+
* entirely (token / no-auth login).
|
|
18
|
+
*
|
|
19
|
+
* The Keycloak chat-recall-web client enforces PKCE (S256), so that flavor
|
|
20
|
+
* carries a code_challenge on init and the matching code_verifier on the poll.
|
|
21
|
+
*/
|
|
22
|
+
import { createHash, randomBytes } from 'node:crypto';
|
|
23
|
+
const b64url = (b) => b.toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
24
|
+
/** Optional env fallback for the OIDC issuer. No hardcoded default — the value
|
|
25
|
+
* comes from the target server's /api/capabilities, `--issuer`, or this var. */
|
|
26
|
+
export const DEFAULT_ISSUER = process.env.CHAT_RECALL_OIDC_ISSUER || '';
|
|
27
|
+
export const DEFAULT_CLIENT_ID = 'chat-recall-web';
|
|
28
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
29
|
+
/**
|
|
30
|
+
* Run the full device flow. `onPrompt` receives the human-facing instructions
|
|
31
|
+
* (verification URL + code) so the caller controls presentation.
|
|
32
|
+
*/
|
|
33
|
+
export async function deviceLogin(opts, onPrompt) {
|
|
34
|
+
const issuer = (opts.issuer || DEFAULT_ISSUER).replace(/\/+$/, '');
|
|
35
|
+
if (!issuer) {
|
|
36
|
+
throw new Error('No OIDC issuer configured for SSO login. Pass --issuer <url>, set '
|
|
37
|
+
+ 'CHAT_RECALL_OIDC_ISSUER, or log in with a token instead: '
|
|
38
|
+
+ '`chat-recall login <server-url> --token <token>` '
|
|
39
|
+
+ '(self-host with AUTH_PROVIDER=none needs no token at all).');
|
|
40
|
+
}
|
|
41
|
+
const clientId = opts.clientId || DEFAULT_CLIENT_ID;
|
|
42
|
+
const scope = opts.scope || 'openid email profile';
|
|
43
|
+
// PKCE: the client requires S256. Verifier is held until the token poll.
|
|
44
|
+
const codeVerifier = b64url(randomBytes(32));
|
|
45
|
+
const codeChallenge = b64url(createHash('sha256').update(codeVerifier).digest());
|
|
46
|
+
const startRes = await fetch(`${issuer}/protocol/openid-connect/auth/device`, {
|
|
47
|
+
method: 'POST',
|
|
48
|
+
headers: { 'content-type': 'application/x-www-form-urlencoded' },
|
|
49
|
+
body: new URLSearchParams({ client_id: clientId, scope, code_challenge: codeChallenge, code_challenge_method: 'S256' }),
|
|
50
|
+
});
|
|
51
|
+
if (!startRes.ok) {
|
|
52
|
+
throw new Error(`device-auth init failed: HTTP ${startRes.status} ${await startRes.text().catch(() => '')}`);
|
|
53
|
+
}
|
|
54
|
+
const start = (await startRes.json());
|
|
55
|
+
onPrompt({
|
|
56
|
+
url: start.verification_uri_complete || start.verification_uri,
|
|
57
|
+
userCode: start.user_code,
|
|
58
|
+
verificationUri: start.verification_uri,
|
|
59
|
+
});
|
|
60
|
+
const tokenUrl = `${issuer}/protocol/openid-connect/token`;
|
|
61
|
+
const deadline = Date.now() + start.expires_in * 1000;
|
|
62
|
+
let interval = (start.interval ?? 5) * 1000;
|
|
63
|
+
while (Date.now() < deadline) {
|
|
64
|
+
await sleep(interval);
|
|
65
|
+
const res = await fetch(tokenUrl, {
|
|
66
|
+
method: 'POST',
|
|
67
|
+
headers: { 'content-type': 'application/x-www-form-urlencoded' },
|
|
68
|
+
body: new URLSearchParams({
|
|
69
|
+
// RFC 8628 §3.4 — the exact URN matters: 'grant-type' is part of it.
|
|
70
|
+
// The truncated 'urn:ietf:params:oauth:device_code' made Keycloak
|
|
71
|
+
// reject every poll with unsupported_grant_type (found by the first
|
|
72
|
+
// real new-user onboarding test, 2026-07-03).
|
|
73
|
+
grant_type: 'urn:ietf:params:oauth:grant-type:device_code',
|
|
74
|
+
device_code: start.device_code,
|
|
75
|
+
client_id: clientId,
|
|
76
|
+
code_verifier: codeVerifier,
|
|
77
|
+
}),
|
|
78
|
+
});
|
|
79
|
+
const body = (await res.json().catch(() => ({})));
|
|
80
|
+
if (res.ok && typeof body.access_token === 'string') {
|
|
81
|
+
return {
|
|
82
|
+
accessToken: body.access_token,
|
|
83
|
+
refreshToken: typeof body.refresh_token === 'string' ? body.refresh_token : undefined,
|
|
84
|
+
expiresIn: typeof body.expires_in === 'number' ? body.expires_in : 0,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
// RFC 8628 §3.5: keep polling on these, back off on slow_down, fail otherwise.
|
|
88
|
+
const err = body.error;
|
|
89
|
+
if (err === 'authorization_pending')
|
|
90
|
+
continue;
|
|
91
|
+
if (err === 'slow_down') {
|
|
92
|
+
interval += 5000;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (err === 'expired_token')
|
|
96
|
+
break;
|
|
97
|
+
throw new Error(`device-auth failed: ${String(err || `HTTP ${res.status}`)}`);
|
|
98
|
+
}
|
|
99
|
+
throw new Error('device-auth timed out — the code expired before approval.');
|
|
100
|
+
}
|
|
101
|
+
/** Client id the CLI presents to the server's embedded provider. A public
|
|
102
|
+
* identifier (not a secret) — the server pins it via validateClient. */
|
|
103
|
+
export const BETTER_AUTH_CLI_CLIENT_ID = 'chat-recall-cli';
|
|
104
|
+
/**
|
|
105
|
+
* Device flow against the server's EMBEDDED better-auth provider
|
|
106
|
+
* (AUTH_PROVIDER=better-auth): POST /api/auth/device/code, send the human to
|
|
107
|
+
* the server's own /device page, poll /api/auth/device/token. Same RFC 8628
|
|
108
|
+
* poll semantics as the Keycloak flavor; JSON bodies instead of form-encoded.
|
|
109
|
+
*/
|
|
110
|
+
export async function betterAuthDeviceLogin(serverUrl, onPrompt) {
|
|
111
|
+
const base = serverUrl.replace(/\/+$/, '');
|
|
112
|
+
const startRes = await fetch(`${base}/api/auth/device/code`, {
|
|
113
|
+
method: 'POST',
|
|
114
|
+
headers: { 'content-type': 'application/json' },
|
|
115
|
+
body: JSON.stringify({ client_id: BETTER_AUTH_CLI_CLIENT_ID }),
|
|
116
|
+
});
|
|
117
|
+
if (!startRes.ok) {
|
|
118
|
+
throw new Error(`device-auth init failed: HTTP ${startRes.status} ${await startRes.text().catch(() => '')}`);
|
|
119
|
+
}
|
|
120
|
+
const start = (await startRes.json());
|
|
121
|
+
onPrompt({
|
|
122
|
+
url: start.verification_uri_complete || start.verification_uri,
|
|
123
|
+
userCode: start.user_code,
|
|
124
|
+
verificationUri: start.verification_uri,
|
|
125
|
+
});
|
|
126
|
+
const deadline = Date.now() + start.expires_in * 1000;
|
|
127
|
+
let interval = (start.interval ?? 5) * 1000;
|
|
128
|
+
while (Date.now() < deadline) {
|
|
129
|
+
await sleep(interval);
|
|
130
|
+
const res = await fetch(`${base}/api/auth/device/token`, {
|
|
131
|
+
method: 'POST',
|
|
132
|
+
headers: { 'content-type': 'application/json' },
|
|
133
|
+
body: JSON.stringify({
|
|
134
|
+
grant_type: 'urn:ietf:params:oauth:grant-type:device_code',
|
|
135
|
+
device_code: start.device_code,
|
|
136
|
+
client_id: BETTER_AUTH_CLI_CLIENT_ID,
|
|
137
|
+
}),
|
|
138
|
+
});
|
|
139
|
+
const body = (await res.json().catch(() => ({})));
|
|
140
|
+
if (res.ok && typeof body.access_token === 'string') {
|
|
141
|
+
return {
|
|
142
|
+
accessToken: body.access_token,
|
|
143
|
+
expiresIn: typeof body.expires_in === 'number' ? body.expires_in : 0,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
// RFC 8628 §3.5 — same contract as the Keycloak flavor.
|
|
147
|
+
const err = body.error;
|
|
148
|
+
if (err === 'authorization_pending')
|
|
149
|
+
continue;
|
|
150
|
+
if (err === 'slow_down') {
|
|
151
|
+
interval += 5000;
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
if (err === 'expired_token' || err === 'access_denied') {
|
|
155
|
+
throw new Error(err === 'access_denied' ? 'device-auth denied in the browser.' : 'device-auth timed out — the code expired before approval.');
|
|
156
|
+
}
|
|
157
|
+
throw new Error(`device-auth failed: ${String(err || `HTTP ${res.status}`)}`);
|
|
158
|
+
}
|
|
159
|
+
throw new Error('device-auth timed out — the code expired before approval.');
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=device-auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device-auth.js","sourceRoot":"","sources":["../src/device-auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `chat-recall escalate` — the WRITE half of the Context Engineering loop.
|
|
3
|
+
*
|
|
4
|
+
* The read half already exists: `chat-recall memory wake-up` (SessionStart
|
|
5
|
+
* hook) injects prior knowledge into a new session. This module closes the
|
|
6
|
+
* loop at the other end: at SessionEnd it extracts a finished session's
|
|
7
|
+
* learnings and asserts them into the temporal knowledge graph, so the NEXT
|
|
8
|
+
* wake-up already knows them. Three kinds of learning, three predicates:
|
|
9
|
+
*
|
|
10
|
+
* - decisions the agent announced → <project> → decided → <text>
|
|
11
|
+
* - corrections the user gave → <project> → user_corrected → <text>
|
|
12
|
+
* - how the session ended → <project> → session_outcome → <status: reason>
|
|
13
|
+
*
|
|
14
|
+
* Sources: decisions + outcome come from the server's own outcome analyzer
|
|
15
|
+
* (`GET /api/conversations/:id/outcome` — the same data recall_summary shows);
|
|
16
|
+
* corrections are extracted client-side from the synced transcript
|
|
17
|
+
* (`GET /api/conversations/:id?limit=0`) with the marker heuristics below.
|
|
18
|
+
* Writes go through `POST /api/kg/add` — the same endpoint recall_kg_add and
|
|
19
|
+
* recall_decision_record use — with `supersede: false` (learnings are
|
|
20
|
+
* multi-valued: a project accumulates many decisions) and `origin: 'asserted'`
|
|
21
|
+
* stamped server-side.
|
|
22
|
+
*
|
|
23
|
+
* Hook safety: this runs from a SessionEnd hook, so every "cannot proceed"
|
|
24
|
+
* condition (not logged in, server down, session not synced yet) is a dim
|
|
25
|
+
* note + a clean return — the CLI command exits 0. A hook must never break
|
|
26
|
+
* session end.
|
|
27
|
+
*/
|
|
28
|
+
export interface ConvoMessage {
|
|
29
|
+
line?: number;
|
|
30
|
+
role: string;
|
|
31
|
+
content: string;
|
|
32
|
+
}
|
|
33
|
+
export interface SessionOutcome {
|
|
34
|
+
status?: string;
|
|
35
|
+
reason?: string;
|
|
36
|
+
decisions?: Array<{
|
|
37
|
+
text: string;
|
|
38
|
+
}>;
|
|
39
|
+
}
|
|
40
|
+
export interface Learning {
|
|
41
|
+
kind: 'decision' | 'correction' | 'outcome';
|
|
42
|
+
subject: string;
|
|
43
|
+
predicate: string;
|
|
44
|
+
object: string;
|
|
45
|
+
confidence: number;
|
|
46
|
+
}
|
|
47
|
+
/** Minimal server access the runner needs — cli.ts passes its own helpers. */
|
|
48
|
+
export interface EscalateDeps {
|
|
49
|
+
get<T>(path: string): Promise<T>;
|
|
50
|
+
getSoft<T>(path: string): Promise<{
|
|
51
|
+
status: number;
|
|
52
|
+
data: T | null;
|
|
53
|
+
message?: string;
|
|
54
|
+
}>;
|
|
55
|
+
post<T>(path: string, body: unknown): Promise<T>;
|
|
56
|
+
/** Informational output (goes to stdout via the CLI's own styling). */
|
|
57
|
+
log(line: string): void;
|
|
58
|
+
}
|
|
59
|
+
export interface EscalateOptions {
|
|
60
|
+
/** Explicit session id; wins over `latest`. */
|
|
61
|
+
sessionId?: string;
|
|
62
|
+
/** Resolve the most recent synced session of the cwd's project. */
|
|
63
|
+
latest?: boolean;
|
|
64
|
+
/** KG entity name for the facts. Default: basename of the session's project path, then of cwd. */
|
|
65
|
+
project?: string;
|
|
66
|
+
/** Extract and print without writing. */
|
|
67
|
+
dryRun?: boolean;
|
|
68
|
+
cwd: string;
|
|
69
|
+
}
|
|
70
|
+
/** Collapse whitespace and cut at a word boundary. */
|
|
71
|
+
export declare function clip(text: string, max?: number): string;
|
|
72
|
+
/**
|
|
73
|
+
* Corrections the user gave, in transcript order. The FIRST user message is
|
|
74
|
+
* skipped (the task statement cannot correct anything), noise carriers are
|
|
75
|
+
* skipped, and the result is deduplicated and capped.
|
|
76
|
+
*/
|
|
77
|
+
export declare function extractCorrections(messages: ConvoMessage[], cap?: number): string[];
|
|
78
|
+
/**
|
|
79
|
+
* Assemble the full learning set for one session. Confidence 0.8 marks these
|
|
80
|
+
* as auto-extracted — below a deliberate recall_decision_record (1.0), above
|
|
81
|
+
* the KG's auto-mined guesses (0.5).
|
|
82
|
+
*/
|
|
83
|
+
export declare function extractLearnings(input: {
|
|
84
|
+
project: string;
|
|
85
|
+
messages: ConvoMessage[];
|
|
86
|
+
outcome: SessionOutcome | null;
|
|
87
|
+
}): Learning[];
|
|
88
|
+
export interface EscalateResult {
|
|
89
|
+
sessionId: string;
|
|
90
|
+
written: number;
|
|
91
|
+
skippedExisting: number;
|
|
92
|
+
dryRun: boolean;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Resolve → extract → dedup → write. Returns null when there is nothing to do
|
|
96
|
+
* (not synced, no learnings) — the caller exits 0 either way.
|
|
97
|
+
*/
|
|
98
|
+
export declare function runEscalate(deps: EscalateDeps, opts: EscalateOptions): Promise<EscalateResult | null>;
|
|
99
|
+
//# sourceMappingURL=escalate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"escalate.d.ts","sourceRoot":"","sources":["../src/escalate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAMH,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACrC;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,UAAU,GAAG,YAAY,GAAG,SAAS,CAAC;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,8EAA8E;AAC9E,MAAM,WAAW,YAAY;IAC3B,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACjC,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACxF,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACjD,uEAAuE;IACvE,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,MAAM,WAAW,eAAe;IAC9B,+CAA+C;IAC/C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mEAAmE;IACnE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,kGAAkG;IAClG,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yCAAyC;IACzC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;CACb;AAyCD,sDAAsD;AACtD,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,SAAM,GAAG,MAAM,CAMpD;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,YAAY,EAAE,EAAE,GAAG,SAAI,GAAG,MAAM,EAAE,CAsB9E;AAKD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,OAAO,EAAE,cAAc,GAAG,IAAI,CAAC;CAChC,GAAG,QAAQ,EAAE,CAqBb;AAgBD,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,OAAO,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAoF3G"}
|
package/dist/escalate.js
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `chat-recall escalate` — the WRITE half of the Context Engineering loop.
|
|
3
|
+
*
|
|
4
|
+
* The read half already exists: `chat-recall memory wake-up` (SessionStart
|
|
5
|
+
* hook) injects prior knowledge into a new session. This module closes the
|
|
6
|
+
* loop at the other end: at SessionEnd it extracts a finished session's
|
|
7
|
+
* learnings and asserts them into the temporal knowledge graph, so the NEXT
|
|
8
|
+
* wake-up already knows them. Three kinds of learning, three predicates:
|
|
9
|
+
*
|
|
10
|
+
* - decisions the agent announced → <project> → decided → <text>
|
|
11
|
+
* - corrections the user gave → <project> → user_corrected → <text>
|
|
12
|
+
* - how the session ended → <project> → session_outcome → <status: reason>
|
|
13
|
+
*
|
|
14
|
+
* Sources: decisions + outcome come from the server's own outcome analyzer
|
|
15
|
+
* (`GET /api/conversations/:id/outcome` — the same data recall_summary shows);
|
|
16
|
+
* corrections are extracted client-side from the synced transcript
|
|
17
|
+
* (`GET /api/conversations/:id?limit=0`) with the marker heuristics below.
|
|
18
|
+
* Writes go through `POST /api/kg/add` — the same endpoint recall_kg_add and
|
|
19
|
+
* recall_decision_record use — with `supersede: false` (learnings are
|
|
20
|
+
* multi-valued: a project accumulates many decisions) and `origin: 'asserted'`
|
|
21
|
+
* stamped server-side.
|
|
22
|
+
*
|
|
23
|
+
* Hook safety: this runs from a SessionEnd hook, so every "cannot proceed"
|
|
24
|
+
* condition (not logged in, server down, session not synced yet) is a dim
|
|
25
|
+
* note + a clean return — the CLI command exits 0. A hook must never break
|
|
26
|
+
* session end.
|
|
27
|
+
*/
|
|
28
|
+
import { basename } from 'node:path';
|
|
29
|
+
// ── Extraction heuristics ────────────────────────────────────────────────────
|
|
30
|
+
/**
|
|
31
|
+
* A user message counts as a correction when it opens with a rejection /
|
|
32
|
+
* redirection marker, or contains an explicit "you got it wrong" phrase.
|
|
33
|
+
* Anchored-at-start patterns keep false positives low ("no" mid-sentence is
|
|
34
|
+
* usually not a correction; "No, use the pooler" is).
|
|
35
|
+
*/
|
|
36
|
+
const CORRECTION_STARTS = [
|
|
37
|
+
/^no+\b[,.! ]/i,
|
|
38
|
+
/^(nope|wrong|incorrect|not quite|not right)\b/i,
|
|
39
|
+
/^that'?s (wrong|not right|not it|not what)/i,
|
|
40
|
+
/^(don'?t|do not|stop|undo|revert)\b/i,
|
|
41
|
+
/^(actually|instead)[, ]/i,
|
|
42
|
+
/^(why did you|you didn'?t|you did not|you forgot|you missed|you broke)\b/i,
|
|
43
|
+
];
|
|
44
|
+
const CORRECTION_CONTAINS = [
|
|
45
|
+
/\bthat'?s not what i (asked|meant|wanted|said)\b/i,
|
|
46
|
+
/\byou (misunderstood|misread|ignored my|shouldn'?t have)\b/i,
|
|
47
|
+
/\bi (said|asked for|told you)\b.*\bnot\b/i,
|
|
48
|
+
];
|
|
49
|
+
/**
|
|
50
|
+
* Transcript noise that must never be mined for corrections: slash-command
|
|
51
|
+
* carriers, caveat banners, interrupt markers, and giant pastes.
|
|
52
|
+
*/
|
|
53
|
+
function isNoiseUserMessage(content) {
|
|
54
|
+
const c = content.trimStart();
|
|
55
|
+
return (c.length === 0 ||
|
|
56
|
+
c.length > 4000 ||
|
|
57
|
+
c.startsWith('Caveat:') ||
|
|
58
|
+
c.startsWith('[Request interrupted') ||
|
|
59
|
+
c.includes('<command-name>') ||
|
|
60
|
+
c.includes('<local-command') ||
|
|
61
|
+
c.startsWith('<system-reminder>'));
|
|
62
|
+
}
|
|
63
|
+
/** Collapse whitespace and cut at a word boundary. */
|
|
64
|
+
export function clip(text, max = 200) {
|
|
65
|
+
const flat = text.replace(/\s+/g, ' ').trim();
|
|
66
|
+
if (flat.length <= max)
|
|
67
|
+
return flat;
|
|
68
|
+
const cut = flat.slice(0, max);
|
|
69
|
+
const lastSpace = cut.lastIndexOf(' ');
|
|
70
|
+
return (lastSpace > max * 0.6 ? cut.slice(0, lastSpace) : cut) + '…';
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Corrections the user gave, in transcript order. The FIRST user message is
|
|
74
|
+
* skipped (the task statement cannot correct anything), noise carriers are
|
|
75
|
+
* skipped, and the result is deduplicated and capped.
|
|
76
|
+
*/
|
|
77
|
+
export function extractCorrections(messages, cap = 5) {
|
|
78
|
+
const out = [];
|
|
79
|
+
const seen = new Set();
|
|
80
|
+
let userTurns = 0;
|
|
81
|
+
for (const msg of messages) {
|
|
82
|
+
if (msg.role !== 'user')
|
|
83
|
+
continue;
|
|
84
|
+
userTurns++;
|
|
85
|
+
if (userTurns === 1)
|
|
86
|
+
continue; // the initial prompt is the task, not feedback
|
|
87
|
+
if (isNoiseUserMessage(msg.content))
|
|
88
|
+
continue;
|
|
89
|
+
const text = msg.content.trim();
|
|
90
|
+
const hit = CORRECTION_STARTS.some((re) => re.test(text)) ||
|
|
91
|
+
CORRECTION_CONTAINS.some((re) => re.test(text));
|
|
92
|
+
if (!hit)
|
|
93
|
+
continue;
|
|
94
|
+
const clipped = clip(text);
|
|
95
|
+
const key = clipped.toLowerCase();
|
|
96
|
+
if (seen.has(key))
|
|
97
|
+
continue;
|
|
98
|
+
seen.add(key);
|
|
99
|
+
out.push(clipped);
|
|
100
|
+
if (out.length >= cap)
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
return out;
|
|
104
|
+
}
|
|
105
|
+
/** Outcome statuses worth remembering. Transient/unknown states are not learnings. */
|
|
106
|
+
const TERMINAL_STATUSES = new Set(['shipped', 'interrupted', 'abandoned']);
|
|
107
|
+
/**
|
|
108
|
+
* Assemble the full learning set for one session. Confidence 0.8 marks these
|
|
109
|
+
* as auto-extracted — below a deliberate recall_decision_record (1.0), above
|
|
110
|
+
* the KG's auto-mined guesses (0.5).
|
|
111
|
+
*/
|
|
112
|
+
export function extractLearnings(input) {
|
|
113
|
+
const learnings = [];
|
|
114
|
+
const subject = input.project;
|
|
115
|
+
for (const d of (input.outcome?.decisions ?? []).slice(0, 8)) {
|
|
116
|
+
const text = clip(d.text);
|
|
117
|
+
if (!text)
|
|
118
|
+
continue;
|
|
119
|
+
learnings.push({ kind: 'decision', subject, predicate: 'decided', object: text, confidence: 0.8 });
|
|
120
|
+
}
|
|
121
|
+
for (const c of extractCorrections(input.messages)) {
|
|
122
|
+
learnings.push({ kind: 'correction', subject, predicate: 'user_corrected', object: c, confidence: 0.8 });
|
|
123
|
+
}
|
|
124
|
+
const status = input.outcome?.status;
|
|
125
|
+
if (status && TERMINAL_STATUSES.has(status)) {
|
|
126
|
+
const reason = input.outcome?.reason ? `: ${clip(input.outcome.reason, 140)}` : '';
|
|
127
|
+
learnings.push({ kind: 'outcome', subject, predicate: 'session_outcome', object: `${status}${reason}`, confidence: 0.8 });
|
|
128
|
+
}
|
|
129
|
+
return learnings;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Resolve → extract → dedup → write. Returns null when there is nothing to do
|
|
133
|
+
* (not synced, no learnings) — the caller exits 0 either way.
|
|
134
|
+
*/
|
|
135
|
+
export async function runEscalate(deps, opts) {
|
|
136
|
+
// 1. Resolve the session id (+ the project entity name).
|
|
137
|
+
let sessionId = opts.sessionId;
|
|
138
|
+
let projectName = opts.project;
|
|
139
|
+
if (!sessionId && opts.latest) {
|
|
140
|
+
// The recent endpoint filters by LOGICAL project id (git:… / ws:…), the
|
|
141
|
+
// same resolution every other cwd-scoped command uses.
|
|
142
|
+
const { resolveProjectId } = await import('@chat-recall/engine/core/project-resolver.js');
|
|
143
|
+
const resolved = resolveProjectId(opts.cwd);
|
|
144
|
+
const pid = resolved && resolved.source !== 'ignored' ? resolved.id : '';
|
|
145
|
+
const qs = new URLSearchParams({ limit: '1' });
|
|
146
|
+
if (pid)
|
|
147
|
+
qs.set('project', pid);
|
|
148
|
+
const recent = await deps.get(`/api/conversations/recent?${qs.toString()}`);
|
|
149
|
+
const row = recent.sessions?.[0];
|
|
150
|
+
if (!row) {
|
|
151
|
+
deps.log(`escalate: no synced session found for this project${pid ? ` (${pid})` : ''} — nothing to do.`);
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
sessionId = row.sessionId;
|
|
155
|
+
if (!projectName && row.projectPath)
|
|
156
|
+
projectName = basename(row.projectPath);
|
|
157
|
+
}
|
|
158
|
+
if (!sessionId)
|
|
159
|
+
throw new Error('pass a session id or --latest');
|
|
160
|
+
if (!projectName)
|
|
161
|
+
projectName = basename(opts.cwd);
|
|
162
|
+
// 2. Fetch the transcript + the server-side outcome analysis.
|
|
163
|
+
const convo = await deps.getSoft(`/api/conversations/${encodeURIComponent(sessionId)}?limit=0`);
|
|
164
|
+
if (!convo.data || convo.data.messages.length === 0) {
|
|
165
|
+
deps.log(`escalate: session ${sessionId} not synced yet${convo.message ? ` (${convo.message})` : ''} — nothing to do.`);
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
const outcomeSoft = await deps.getSoft(`/api/conversations/${encodeURIComponent(sessionId)}/outcome`);
|
|
169
|
+
const outcome = outcomeSoft.data;
|
|
170
|
+
// 3. Extract.
|
|
171
|
+
const learnings = extractLearnings({ project: projectName, messages: convo.data.messages, outcome });
|
|
172
|
+
if (learnings.length === 0) {
|
|
173
|
+
deps.log(`escalate: no learnings found in session ${sessionId.slice(0, 8)}… — nothing to escalate.`);
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
// 4. Dedup against facts already in the graph, so a re-run (or a hook firing
|
|
177
|
+
// twice) does not double-write. The timeline lookup is best-effort: if it
|
|
178
|
+
// fails we still write (supersede:false tolerates rare duplicates).
|
|
179
|
+
const existing = new Set();
|
|
180
|
+
try {
|
|
181
|
+
const tl = await deps.get(`/api/kg/timeline?entity=${encodeURIComponent(projectName)}&limit=500`);
|
|
182
|
+
for (const e of tl.entries ?? []) {
|
|
183
|
+
if (e.current)
|
|
184
|
+
existing.add(`${e.predicate}${e.object}`.toLowerCase());
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
catch { /* best-effort */ }
|
|
188
|
+
const fresh = learnings.filter((l) => !existing.has(`${l.predicate}${l.object}`.toLowerCase()));
|
|
189
|
+
const skippedExisting = learnings.length - fresh.length;
|
|
190
|
+
// 5. Print (and in dry-run: stop here).
|
|
191
|
+
for (const l of fresh) {
|
|
192
|
+
deps.log(` [${l.kind}] ${l.subject} → ${l.predicate} → ${l.object}`);
|
|
193
|
+
}
|
|
194
|
+
if (skippedExisting > 0)
|
|
195
|
+
deps.log(` (${skippedExisting} already in the knowledge graph — skipped)`);
|
|
196
|
+
if (opts.dryRun) {
|
|
197
|
+
deps.log(`escalate (dry-run): ${fresh.length} learning(s) extracted from ${sessionId.slice(0, 8)}… — nothing written.`);
|
|
198
|
+
return { sessionId, written: 0, skippedExisting, dryRun: true };
|
|
199
|
+
}
|
|
200
|
+
// 6. Write each fresh learning as an asserted, session-attributed triple.
|
|
201
|
+
let written = 0;
|
|
202
|
+
for (const l of fresh) {
|
|
203
|
+
await deps.post('/api/kg/add', {
|
|
204
|
+
subject: l.subject,
|
|
205
|
+
predicate: l.predicate,
|
|
206
|
+
object: l.object,
|
|
207
|
+
confidence: l.confidence,
|
|
208
|
+
source_session: sessionId,
|
|
209
|
+
// Learnings are multi-valued: a project accumulates decisions and
|
|
210
|
+
// corrections. Without this, each write would supersede the previous
|
|
211
|
+
// fact with the same (subject, predicate).
|
|
212
|
+
supersede: false,
|
|
213
|
+
});
|
|
214
|
+
written++;
|
|
215
|
+
}
|
|
216
|
+
deps.log(`escalate: wrote ${written} learning(s) from session ${sessionId.slice(0, 8)}… to the knowledge graph.`);
|
|
217
|
+
return { sessionId, written, skippedExisting, dryRun: false };
|
|
218
|
+
}
|
|
219
|
+
//# sourceMappingURL=escalate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"escalate.js","sourceRoot":"","sources":["../src/escalate.ts"],"names":[],"mappings":"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"}
|
package/dist/http.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** This CLI's own version — the one the server compares against. */
|
|
2
|
+
export declare function cliVersion(): string;
|
|
3
|
+
/**
|
|
4
|
+
* Identity headers on every request this CLI makes to its server.
|
|
5
|
+
*
|
|
6
|
+
* The server stamps them onto the device row (see the auth middleware's device
|
|
7
|
+
* heartbeat), which is what makes "this machine last synced 6 days ago and runs
|
|
8
|
+
* 0.3.1" answerable at all. Before this the server knew a device token existed
|
|
9
|
+
* and nothing else — so a machine on a CLI too old to self-update stayed stale
|
|
10
|
+
* indefinitely and no UI could say so. Two short, non-identifying values.
|
|
11
|
+
*/
|
|
12
|
+
export declare function clientHeaders(): Record<string, string>;
|
|
13
|
+
export declare function fetchWithTimeout(url: string, init?: RequestInit, ms?: number): Promise<Response>;
|
|
14
|
+
//# sourceMappingURL=http.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAwBA,oEAAoE;AACpE,wBAAgB,UAAU,IAAI,MAAM,CAAwB;AAE5D;;;;;;;;GAQG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAKtD;AAED,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,WAAgB,EACtB,EAAE,SAAS,GACV,OAAO,CAAC,QAAQ,CAAC,CAMnB"}
|
package/dist/http.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fetch() with a hard timeout.
|
|
3
|
+
*
|
|
4
|
+
* Node's global `fetch` (undici) has NO caller-facing default timeout — a
|
|
5
|
+
* connected-but-silent peer (half-open TCP, a hung reverse proxy, a wedged
|
|
6
|
+
* server) makes the promise hang for undici's internal ~300s, or effectively
|
|
7
|
+
* forever from the caller's view. The collector's upload path already guards
|
|
8
|
+
* against this with an AbortController; every OTHER request the daemon makes
|
|
9
|
+
* (capabilities handshake, tenant-config pulls, intent drain, code-index push,
|
|
10
|
+
* auto-update checks) must too — otherwise ONE stalled endpoint wedges the whole
|
|
11
|
+
* serialized sync tick. This is the single helper they all use.
|
|
12
|
+
*/
|
|
13
|
+
import { readFileSync } from 'node:fs';
|
|
14
|
+
let CLI_VERSION = '0.0.0';
|
|
15
|
+
try {
|
|
16
|
+
CLI_VERSION = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf-8')).version || CLI_VERSION;
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
try {
|
|
20
|
+
if (typeof __CLI_VERSION__ === 'string')
|
|
21
|
+
CLI_VERSION = __CLI_VERSION__;
|
|
22
|
+
}
|
|
23
|
+
catch { /* keep default */ }
|
|
24
|
+
}
|
|
25
|
+
/** This CLI's own version — the one the server compares against. */
|
|
26
|
+
export function cliVersion() { return CLI_VERSION; }
|
|
27
|
+
/**
|
|
28
|
+
* Identity headers on every request this CLI makes to its server.
|
|
29
|
+
*
|
|
30
|
+
* The server stamps them onto the device row (see the auth middleware's device
|
|
31
|
+
* heartbeat), which is what makes "this machine last synced 6 days ago and runs
|
|
32
|
+
* 0.3.1" answerable at all. Before this the server knew a device token existed
|
|
33
|
+
* and nothing else — so a machine on a CLI too old to self-update stayed stale
|
|
34
|
+
* indefinitely and no UI could say so. Two short, non-identifying values.
|
|
35
|
+
*/
|
|
36
|
+
export function clientHeaders() {
|
|
37
|
+
return {
|
|
38
|
+
'x-chat-recall-cli': CLI_VERSION,
|
|
39
|
+
'x-chat-recall-os': `${process.platform}-${process.arch}`,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export function fetchWithTimeout(url, init = {}, ms = 15_000) {
|
|
43
|
+
// Headers first so an explicit caller value always wins.
|
|
44
|
+
const headers = new Headers(clientHeaders());
|
|
45
|
+
new Headers(init.headers ?? {}).forEach((v, k) => headers.set(k, v));
|
|
46
|
+
// Respect an explicit caller signal if given; otherwise arm a timeout.
|
|
47
|
+
return fetch(url, { ...init, headers, signal: init.signal ?? AbortSignal.timeout(ms) });
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=http.js.map
|
package/dist/http.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAKvC,IAAI,WAAW,GAAG,OAAO,CAAC;AAC1B,IAAI,CAAC;IACH,WAAW,GAAI,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,GAAG,CAAC,iBAAiB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,CAA0B,CAAC,OAAO,IAAI,WAAW,CAAC;AAChJ,CAAC;AAAC,MAAM,CAAC;IACP,IAAI,CAAC;QAAC,IAAI,OAAO,eAAe,KAAK,QAAQ;YAAE,WAAW,GAAG,eAAe,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC,CAAC,kBAAkB,CAAC,CAAC;AAC9G,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,UAAU,KAAa,OAAO,WAAW,CAAC,CAAC,CAAC;AAE5D;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa;IAC3B,OAAO;QACL,mBAAmB,EAAE,WAAW;QAChC,kBAAkB,EAAE,GAAG,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,IAAI,EAAE;KAC1D,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC9B,GAAW,EACX,OAAoB,EAAE,EACtB,EAAE,GAAG,MAAM;IAEX,yDAAyD;IACzD,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;IAC7C,IAAI,OAAO,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACrE,uEAAuE;IACvE,OAAO,KAAK,CAAC,GAAG,EAAE,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;AAC1F,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/** Bundled skills source dir: <package-root>/skills (dist/install-skills.js → ..). */
|
|
2
|
+
export declare function skillsSourceDir(): string;
|
|
3
|
+
/** The skill directory names we ship (any dir under skills/ containing SKILL.md). */
|
|
4
|
+
export declare function bundledSkillNames(): string[];
|
|
5
|
+
export interface SkillTarget {
|
|
6
|
+
id: string;
|
|
7
|
+
label: string;
|
|
8
|
+
dir: string;
|
|
9
|
+
available: boolean;
|
|
10
|
+
}
|
|
11
|
+
/** Per-tool skills dir. agy keeps its own (~/.gemini/antigravity-cli/skills) AND
|
|
12
|
+
* we also cover ~/.agents/skills (tool-neutral) so any future consumer sees them. */
|
|
13
|
+
export declare function skillTargets(): SkillTarget[];
|
|
14
|
+
export interface InstallResult {
|
|
15
|
+
version: string;
|
|
16
|
+
perTarget: Array<{
|
|
17
|
+
id: string;
|
|
18
|
+
label: string;
|
|
19
|
+
dir: string;
|
|
20
|
+
installed: string[];
|
|
21
|
+
skippedUserOwned: string[];
|
|
22
|
+
available: boolean;
|
|
23
|
+
}>;
|
|
24
|
+
}
|
|
25
|
+
/** Install (or update) the bundled skills into every target tool.
|
|
26
|
+
* @param onlyAvailable install only to tools detected on this machine (default true). */
|
|
27
|
+
export declare function installSkills(opts?: {
|
|
28
|
+
onlyAvailable?: boolean;
|
|
29
|
+
}): InstallResult;
|
|
30
|
+
export interface UninstallResult {
|
|
31
|
+
perTarget: Array<{
|
|
32
|
+
id: string;
|
|
33
|
+
label: string;
|
|
34
|
+
removed: string[];
|
|
35
|
+
}>;
|
|
36
|
+
}
|
|
37
|
+
/** Remove only chat-recall-managed skills (identified by the marker). */
|
|
38
|
+
export declare function uninstallSkills(): UninstallResult;
|
|
39
|
+
/** True if any AVAILABLE tool is missing a bundled skill or has an out-of-date
|
|
40
|
+
* version. The MCP boot uses this to refresh only when needed (cheap: stat a
|
|
41
|
+
* marker per skill), so it doesn't churn the disk on every tool launch. */
|
|
42
|
+
export declare function skillsNeedRefresh(): boolean;
|
|
43
|
+
/** For `doctor`: which tools have the current skill version installed. */
|
|
44
|
+
export declare function skillStatus(): Array<{
|
|
45
|
+
id: string;
|
|
46
|
+
label: string;
|
|
47
|
+
available: boolean;
|
|
48
|
+
installed: number;
|
|
49
|
+
version: string | null;
|
|
50
|
+
}>;
|
|
51
|
+
//# sourceMappingURL=install-skills.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install-skills.d.ts","sourceRoot":"","sources":["../src/install-skills.ts"],"names":[],"mappings":"AA6BA,sFAAsF;AACtF,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAQD,qFAAqF;AACrF,wBAAgB,iBAAiB,IAAI,MAAM,EAAE,CAO5C;AAED,MAAM,WAAW,WAAW;IAAG,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAC;CAAE;AA+C5F;sFACsF;AACtF,wBAAgB,YAAY,IAAI,WAAW,EAAE,CAa5C;AAOD,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,EAAE,CAAC;QAAC,gBAAgB,EAAE,MAAM,EAAE,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CACnI;AAED;0FAC0F;AAC1F,wBAAgB,aAAa,CAAC,IAAI,GAAE;IAAE,aAAa,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,aAAa,CAwBnF;AAED,MAAM,WAAW,eAAe;IAAG,SAAS,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;CAAE;AAExG,yEAAyE;AACzE,wBAAgB,eAAe,IAAI,eAAe,CAkBjD;AAED;;4EAE4E;AAC5E,wBAAgB,iBAAiB,IAAI,OAAO,CAa3C;AAED,0EAA0E;AAC1E,wBAAgB,WAAW,IAAI,KAAK,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAcjI"}
|