@getbourdon/participants 0.2.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/LICENSE_FAQ.md +1 -1
- package/dist/base.d.ts +109 -0
- package/dist/base.js +88 -0
- package/dist/index.cjs +12 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +42 -1083
- package/dist/index.js +53 -6590
- package/dist/index.js.map +1 -1
- package/dist/network-base.d.ts +108 -0
- package/dist/network-base.js +248 -0
- package/dist/readers/cascade.d.ts +49 -0
- package/dist/readers/cascade.js +428 -0
- package/dist/readers/claude-code-automations.d.ts +62 -0
- package/dist/readers/claude-code-automations.js +693 -0
- package/dist/readers/claude-code.d.ts +50 -0
- package/dist/readers/claude-code.js +590 -0
- package/dist/readers/claude-desktop-code.d.ts +54 -0
- package/dist/readers/claude-desktop-code.js +526 -0
- package/dist/readers/claude-desktop-cowork.d.ts +47 -0
- package/dist/readers/claude-desktop-cowork.js +659 -0
- package/dist/readers/codex-automations.d.ts +55 -0
- package/dist/readers/codex-automations.js +519 -0
- package/dist/readers/codex-sessions.d.ts +88 -0
- package/dist/readers/codex-sessions.js +194 -0
- package/dist/readers/copilot-automations.d.ts +64 -0
- package/dist/readers/copilot-automations.js +530 -0
- package/dist/readers/copilot-cli.d.ts +45 -0
- package/dist/readers/copilot-cli.js +416 -0
- package/dist/readers/copilot-vscode.d.ts +54 -0
- package/dist/readers/copilot-vscode.js +553 -0
- package/dist/readers/copilot.d.ts +59 -0
- package/dist/readers/copilot.js +413 -0
- package/dist/readers/cursor-automations.d.ts +57 -0
- package/dist/readers/cursor-automations.js +685 -0
- package/dist/readers/cursor.d.ts +50 -0
- package/dist/readers/cursor.js +670 -0
- package/dist/readers/github-copilot.d.ts +34 -0
- package/dist/readers/github-copilot.js +242 -0
- package/dist/readers/hermes.d.ts +37 -0
- package/dist/readers/hermes.js +426 -0
- package/dist/readers/openclaw.d.ts +78 -0
- package/dist/readers/openclaw.js +386 -0
- package/dist/registry.d.ts +39 -0
- package/dist/registry.js +98 -0
- package/dist/sqlite-base.d.ts +65 -0
- package/dist/sqlite-base.js +163 -0
- package/package.json +6 -6
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub Copilot (local convention) participant — reads the convention-based
|
|
3
|
+
* memory file at `~/.copilot-bourdon/memory.md`. Port of `participants/copilot.py`.
|
|
4
|
+
*
|
|
5
|
+
* Copilot has no accessible local session index (its reasoning is cloud-side and
|
|
6
|
+
* its native memory is not exposed on disk), so — like the Codex pre-distillation
|
|
7
|
+
* fallback — this reader normalizes a user-maintained convention file into a
|
|
8
|
+
* Bourdon L5 manifest. The file is YAML front-matter (structured `entities:` /
|
|
9
|
+
* `sessions:`) followed by an optional freeform markdown body that Copilot Chat
|
|
10
|
+
* reads as plain context but this reader ignores.
|
|
11
|
+
*
|
|
12
|
+
* NOT the cloud GitHub-Copilot network adapter ({@link GitHubCopilotParticipant})
|
|
13
|
+
* and NOT the Copilot CLI SQLite reader.
|
|
14
|
+
*
|
|
15
|
+
* Paths checked (in order):
|
|
16
|
+
* 1. `COPILOT_BOURDON_HOME` env-var override
|
|
17
|
+
* 2. `~/.copilot-bourdon/` (default convention path)
|
|
18
|
+
*
|
|
19
|
+
* Defensive throughout: a missing convention directory raises
|
|
20
|
+
* {@link ParticipantDiscoveryError} from `discover()`; malformed YAML degrades to
|
|
21
|
+
* an empty manifest rather than raising; `healthCheck` never throws.
|
|
22
|
+
*/
|
|
23
|
+
import { mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
24
|
+
import { homedir, hostname } from "node:os";
|
|
25
|
+
import { join } from "node:path";
|
|
26
|
+
import { parse as yamlParse } from "yaml";
|
|
27
|
+
import { Visibility, filterForFederation, makeAgentInfo, makeEntity, makeManifest, makeSession, makeVisibilityPolicy, } from "@getbourdon/l5";
|
|
28
|
+
import { redactText } from "@getbourdon/redaction";
|
|
29
|
+
import { ParticipantDiscoveryError, SPEC_VERSION, } from "../base.js";
|
|
30
|
+
const AGENT_ID = "copilot";
|
|
31
|
+
const AGENT_TYPE = "code-assistant";
|
|
32
|
+
const DISPLAY_NAME = "GitHub Copilot";
|
|
33
|
+
const ROLE_NARRATIVE = "Inline completion and chat assistant present across every IDE the team uses. " +
|
|
34
|
+
"Works alongside the human at the keystroke level -- the ambient layer that " +
|
|
35
|
+
"recognises what is being typed before a full turn is even formed. Bourdon " +
|
|
36
|
+
"gives Copilot cross-session entity awareness it would otherwise lack entirely.";
|
|
37
|
+
// Convention directory and file names. Override dir with COPILOT_BOURDON_HOME.
|
|
38
|
+
const CONVENTION_DIR_NAME = ".copilot-bourdon";
|
|
39
|
+
const MEMORY_FILENAME = "memory.md";
|
|
40
|
+
// Front-matter delimiters (mirrors claude-code's parseFrontmatter conventions).
|
|
41
|
+
const FRONTMATTER_OPEN = "---\n";
|
|
42
|
+
const FRONTMATTER_CLOSE = "\n---\n";
|
|
43
|
+
const DEFAULT_POLICY = makeVisibilityPolicy({
|
|
44
|
+
default: Visibility.TEAM,
|
|
45
|
+
private_tags: ["personal", "financial", "credential", "health", "family", "legal"],
|
|
46
|
+
team_tags: ["copilot-memory", "workspace", "copilot"],
|
|
47
|
+
});
|
|
48
|
+
/** Minimal logger so a degraded read is visible without pulling in a dep
|
|
49
|
+
* (matches the registry's logging idiom). */
|
|
50
|
+
const logger = {
|
|
51
|
+
warn(msg) {
|
|
52
|
+
// eslint-disable-next-line no-console
|
|
53
|
+
console.warn(`[bourdon.participants] ${msg}`);
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
/** Starter template written by `bourdon copilot init`. */
|
|
57
|
+
const MEMORY_TEMPLATE = `---
|
|
58
|
+
# Copilot Bourdon Memory
|
|
59
|
+
# Edit this file to give Copilot cross-session entity awareness.
|
|
60
|
+
# The YAML front-matter is parsed by \`bourdon copilot export\`.
|
|
61
|
+
# The markdown body below the closing \`---\` is freeform context for Copilot Chat.
|
|
62
|
+
|
|
63
|
+
entities: []
|
|
64
|
+
|
|
65
|
+
sessions: []
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
# Copilot notes
|
|
69
|
+
|
|
70
|
+
Add project notes, preferences, or anything else you want Copilot Chat to
|
|
71
|
+
remember here. This section is freeform -- the participant only reads the YAML
|
|
72
|
+
front-matter above.
|
|
73
|
+
`;
|
|
74
|
+
// -- fs helpers ---------------------------------------------------------------
|
|
75
|
+
function homeDir() {
|
|
76
|
+
// os.homedir() can throw under some sandboxes; stay defensive so import +
|
|
77
|
+
// construction never crash even when HOME points at an unreadable path.
|
|
78
|
+
try {
|
|
79
|
+
return homedir();
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return process.env.HOME || process.env.USERPROFILE || ".";
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function isDir(p) {
|
|
86
|
+
try {
|
|
87
|
+
return statSync(p).isDirectory();
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function isFile(p) {
|
|
94
|
+
try {
|
|
95
|
+
return statSync(p).isFile();
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// -- Path helpers -------------------------------------------------------------
|
|
102
|
+
/**
|
|
103
|
+
* Return the conventional `~/.copilot-bourdon/` directory path. Respects the
|
|
104
|
+
* `COPILOT_BOURDON_HOME` env-var override (returned verbatim, even if absent;
|
|
105
|
+
* the directory is not created here).
|
|
106
|
+
*/
|
|
107
|
+
export function defaultCopilotBourdonDir() {
|
|
108
|
+
const env = process.env.COPILOT_BOURDON_HOME;
|
|
109
|
+
if (env)
|
|
110
|
+
return env;
|
|
111
|
+
return join(homeDir(), CONVENTION_DIR_NAME);
|
|
112
|
+
}
|
|
113
|
+
/** Return the path to the convention memory file. */
|
|
114
|
+
export function defaultCopilotMemoryPath(copilotDir) {
|
|
115
|
+
return join(copilotDir || defaultCopilotBourdonDir(), MEMORY_FILENAME);
|
|
116
|
+
}
|
|
117
|
+
// -- Coercion helpers (mirror Python str() / isinstance filtering) ------------
|
|
118
|
+
/** Mirror Python `str(value)` for the value types YAML yields here. The `yaml`
|
|
119
|
+
* lib parses timestamps into JS `Date`; Python's `yaml.safe_load` yields
|
|
120
|
+
* `date`/`datetime` whose `str()` is ISO-ish, so a Date is rendered as its ISO
|
|
121
|
+
* date (date-only callers slice to 10 anyway). */
|
|
122
|
+
function coerceStr(v) {
|
|
123
|
+
if (v instanceof Date)
|
|
124
|
+
return v.toISOString().slice(0, 10);
|
|
125
|
+
return String(v);
|
|
126
|
+
}
|
|
127
|
+
/** Mirror Python `str(raw.get(key) or "") or None`. */
|
|
128
|
+
function strOrUndef(v) {
|
|
129
|
+
const s = coerceStr(v || "");
|
|
130
|
+
return s || undefined;
|
|
131
|
+
}
|
|
132
|
+
/** Mirror `[str(x) for x in raw if isinstance(x, str) and x.strip()]`. */
|
|
133
|
+
function strList(v) {
|
|
134
|
+
if (!Array.isArray(v))
|
|
135
|
+
return [];
|
|
136
|
+
return v.filter((x) => typeof x === "string" && x.trim() !== "");
|
|
137
|
+
}
|
|
138
|
+
function isRecord(v) {
|
|
139
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
140
|
+
}
|
|
141
|
+
function asArray(v) {
|
|
142
|
+
return Array.isArray(v) ? v : [];
|
|
143
|
+
}
|
|
144
|
+
// -- YAML frontmatter ---------------------------------------------------------
|
|
145
|
+
/**
|
|
146
|
+
* Extract and parse the YAML front-matter block from memory file text. Returns
|
|
147
|
+
* an empty object when the file has no front-matter or when the YAML cannot be
|
|
148
|
+
* parsed (logged at WARNING in the oracle; never raises here either).
|
|
149
|
+
*/
|
|
150
|
+
function parseFrontmatter(text, source) {
|
|
151
|
+
if (!text.startsWith(FRONTMATTER_OPEN))
|
|
152
|
+
return {};
|
|
153
|
+
const end = text.indexOf(FRONTMATTER_CLOSE, FRONTMATTER_OPEN.length);
|
|
154
|
+
if (end === -1)
|
|
155
|
+
return {};
|
|
156
|
+
const fmText = text.slice(FRONTMATTER_OPEN.length, end);
|
|
157
|
+
let parsed;
|
|
158
|
+
try {
|
|
159
|
+
parsed = yamlParse(fmText);
|
|
160
|
+
}
|
|
161
|
+
catch (err) {
|
|
162
|
+
const where = source ? ` in ${source}` : "";
|
|
163
|
+
const detail = String(err).replace(/\n/g, " ").slice(0, 200);
|
|
164
|
+
logger.warn(`CopilotParticipant: malformed YAML frontmatter${where}; ` +
|
|
165
|
+
`treating as no-frontmatter (${detail})`);
|
|
166
|
+
return {};
|
|
167
|
+
}
|
|
168
|
+
return isRecord(parsed) ? parsed : {};
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Read and parse a copilot-bourdon memory file. Returns an empty object on any
|
|
172
|
+
* I/O or parse error — the participant degrades to an empty manifest.
|
|
173
|
+
*/
|
|
174
|
+
function readMemoryFile(path) {
|
|
175
|
+
if (!isFile(path))
|
|
176
|
+
return {};
|
|
177
|
+
let text;
|
|
178
|
+
try {
|
|
179
|
+
text = readFileSync(path, "utf8");
|
|
180
|
+
}
|
|
181
|
+
catch (err) {
|
|
182
|
+
logger.warn(`CopilotParticipant: cannot read ${path}: ${String(err)}`);
|
|
183
|
+
return {};
|
|
184
|
+
}
|
|
185
|
+
return parseFrontmatter(text, path);
|
|
186
|
+
}
|
|
187
|
+
// -- Entity / Session converters ----------------------------------------------
|
|
188
|
+
/** Convert a raw YAML entity record to a Bourdon Entity. Skips malformed
|
|
189
|
+
* entries (oracle logs at DEBUG). Never raises. */
|
|
190
|
+
function buildEntity(raw) {
|
|
191
|
+
if (!isRecord(raw))
|
|
192
|
+
return null;
|
|
193
|
+
const name = raw["name"];
|
|
194
|
+
if (typeof name !== "string" || name.trim() === "")
|
|
195
|
+
return null;
|
|
196
|
+
const summaryRaw = raw["summary"] || "";
|
|
197
|
+
const summary = summaryRaw ? redactText(coerceStr(summaryRaw)) : undefined;
|
|
198
|
+
return makeEntity({
|
|
199
|
+
name: name.trim(),
|
|
200
|
+
type: strOrUndef(raw["type"]),
|
|
201
|
+
aliases: strList(raw["aliases"]),
|
|
202
|
+
summary,
|
|
203
|
+
last_touched: strOrUndef(raw["last_touched"]),
|
|
204
|
+
tags: strList(raw["tags"]),
|
|
205
|
+
valid_from: strOrUndef(raw["valid_from"]),
|
|
206
|
+
valid_to: strOrUndef(raw["valid_to"]),
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
/** Convert a raw YAML session record to a Bourdon Session. Skips malformed
|
|
210
|
+
* entries (oracle logs at DEBUG). Never raises. */
|
|
211
|
+
function buildSession(raw) {
|
|
212
|
+
if (!isRecord(raw))
|
|
213
|
+
return null;
|
|
214
|
+
const dateVal = raw["date"];
|
|
215
|
+
if (!dateVal)
|
|
216
|
+
return null;
|
|
217
|
+
const dateStr = coerceStr(dateVal).slice(0, 10); // keep YYYY-MM-DD prefix only
|
|
218
|
+
return makeSession({
|
|
219
|
+
date: dateStr,
|
|
220
|
+
cwd: strOrUndef(raw["cwd"]),
|
|
221
|
+
project_focus: strList(raw["project_focus"]),
|
|
222
|
+
key_actions: strList(raw["key_actions"]),
|
|
223
|
+
files_touched: strList(raw["files_touched"]),
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
// -- Diagnostics --------------------------------------------------------------
|
|
227
|
+
/** Diagnostic summary of the copilot-bourdon memory file (`bourdon copilot
|
|
228
|
+
* doctor`). Never raises. */
|
|
229
|
+
export function inspectCopilotMemory(copilotDir) {
|
|
230
|
+
const memPath = defaultCopilotMemoryPath(copilotDir);
|
|
231
|
+
const report = {
|
|
232
|
+
path: memPath,
|
|
233
|
+
present: isFile(memPath),
|
|
234
|
+
readable: false,
|
|
235
|
+
frontmatter_valid: false,
|
|
236
|
+
entity_count: 0,
|
|
237
|
+
session_count: 0,
|
|
238
|
+
error: null,
|
|
239
|
+
};
|
|
240
|
+
if (!isFile(memPath)) {
|
|
241
|
+
report["error"] = "missing";
|
|
242
|
+
return report;
|
|
243
|
+
}
|
|
244
|
+
let text;
|
|
245
|
+
try {
|
|
246
|
+
text = readFileSync(memPath, "utf8");
|
|
247
|
+
report["readable"] = true;
|
|
248
|
+
}
|
|
249
|
+
catch (err) {
|
|
250
|
+
report["error"] = String(err);
|
|
251
|
+
return report;
|
|
252
|
+
}
|
|
253
|
+
const data = parseFrontmatter(text, memPath);
|
|
254
|
+
if (Object.keys(data).length > 0)
|
|
255
|
+
report["frontmatter_valid"] = true;
|
|
256
|
+
report["entity_count"] = asArray(data["entities"]).length;
|
|
257
|
+
report["session_count"] = asArray(data["sessions"]).length;
|
|
258
|
+
return report;
|
|
259
|
+
}
|
|
260
|
+
// -- Participant --------------------------------------------------------------
|
|
261
|
+
/**
|
|
262
|
+
* External participant for GitHub Copilot via `~/.copilot-bourdon/memory.md`.
|
|
263
|
+
* Implements {@link BourdonParticipant} structurally.
|
|
264
|
+
*/
|
|
265
|
+
export class CopilotParticipant {
|
|
266
|
+
agentId = AGENT_ID;
|
|
267
|
+
agentType = AGENT_TYPE;
|
|
268
|
+
displayName = DISPLAY_NAME;
|
|
269
|
+
nativePath;
|
|
270
|
+
/** Conventional `~/.copilot-bourdon` dir used by the setup wizard's detection. */
|
|
271
|
+
static defaultNativePath(home) {
|
|
272
|
+
if (home !== undefined)
|
|
273
|
+
return join(home, CONVENTION_DIR_NAME);
|
|
274
|
+
return defaultCopilotBourdonDir();
|
|
275
|
+
}
|
|
276
|
+
copilotDir;
|
|
277
|
+
constructor(copilotDir) {
|
|
278
|
+
this.copilotDir = copilotDir ?? null;
|
|
279
|
+
this.nativePath = this.copilotDir || defaultCopilotBourdonDir();
|
|
280
|
+
}
|
|
281
|
+
discover() {
|
|
282
|
+
const path = this.copilotDir || defaultCopilotBourdonDir();
|
|
283
|
+
if (!isDir(path)) {
|
|
284
|
+
throw new ParticipantDiscoveryError(`Copilot convention directory not found at '${path}'. ` +
|
|
285
|
+
"Run `bourdon copilot init` to create it with a starter template, " +
|
|
286
|
+
"or create ~/.copilot-bourdon/memory.md manually.");
|
|
287
|
+
}
|
|
288
|
+
const memPath = join(path, MEMORY_FILENAME);
|
|
289
|
+
return {
|
|
290
|
+
path,
|
|
291
|
+
version: "convention-v1",
|
|
292
|
+
metadata: {
|
|
293
|
+
memory_file: memPath,
|
|
294
|
+
memory_file_present: isFile(memPath),
|
|
295
|
+
},
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
exportSessions(since, limit = 100) {
|
|
299
|
+
const data = this.read();
|
|
300
|
+
const sinceIso = since ? since.toISOString().slice(0, 10) : null;
|
|
301
|
+
const sessions = [];
|
|
302
|
+
for (const raw of asArray(data["sessions"])) {
|
|
303
|
+
const session = buildSession(raw);
|
|
304
|
+
if (session === null)
|
|
305
|
+
continue;
|
|
306
|
+
if (sinceIso && session.date && session.date < sinceIso)
|
|
307
|
+
continue;
|
|
308
|
+
sessions.push(session);
|
|
309
|
+
if (sessions.length >= limit)
|
|
310
|
+
break;
|
|
311
|
+
}
|
|
312
|
+
return sessions;
|
|
313
|
+
}
|
|
314
|
+
exportL5(since) {
|
|
315
|
+
const data = this.read();
|
|
316
|
+
const entities = [];
|
|
317
|
+
for (const raw of asArray(data["entities"])) {
|
|
318
|
+
const entity = buildEntity(raw);
|
|
319
|
+
if (entity !== null)
|
|
320
|
+
entities.push(entity);
|
|
321
|
+
}
|
|
322
|
+
const visibleEntities = filterForFederation(entities, DEFAULT_POLICY);
|
|
323
|
+
const sinceIso = since ? since.toISOString().slice(0, 10) : null;
|
|
324
|
+
const sessions = [];
|
|
325
|
+
for (const raw of asArray(data["sessions"])) {
|
|
326
|
+
const session = buildSession(raw);
|
|
327
|
+
if (session === null)
|
|
328
|
+
continue;
|
|
329
|
+
if (sinceIso && session.date && session.date < sinceIso)
|
|
330
|
+
continue;
|
|
331
|
+
sessions.push(session);
|
|
332
|
+
}
|
|
333
|
+
return makeManifest({
|
|
334
|
+
spec_version: SPEC_VERSION,
|
|
335
|
+
agent: makeAgentInfo({
|
|
336
|
+
id: AGENT_ID,
|
|
337
|
+
type: AGENT_TYPE,
|
|
338
|
+
instance: hostname() || "unknown",
|
|
339
|
+
spec_version_compat: `>=${SPEC_VERSION}`,
|
|
340
|
+
role_narrative: ROLE_NARRATIVE,
|
|
341
|
+
}),
|
|
342
|
+
last_updated: new Date().toISOString(),
|
|
343
|
+
capabilities: ["inline-completion", "chat", "pr-review", "convention-memory"],
|
|
344
|
+
recent_sessions: sessions,
|
|
345
|
+
known_entities: visibleEntities,
|
|
346
|
+
visibility_policy: DEFAULT_POLICY,
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
healthCheck() {
|
|
350
|
+
const path = this.copilotDir || defaultCopilotBourdonDir();
|
|
351
|
+
if (!isDir(path)) {
|
|
352
|
+
return {
|
|
353
|
+
status: "blocked",
|
|
354
|
+
reason: `Convention directory ${path} not found. ` +
|
|
355
|
+
"Run `bourdon copilot init` to create it.",
|
|
356
|
+
details: { expected_path: path },
|
|
357
|
+
proposedFix: "Run `bourdon copilot init` to create the convention directory.",
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
const memPath = join(path, MEMORY_FILENAME);
|
|
361
|
+
if (!isFile(memPath)) {
|
|
362
|
+
return {
|
|
363
|
+
status: "degraded",
|
|
364
|
+
reason: `Memory file ${memPath} not found. ` +
|
|
365
|
+
"Run `bourdon copilot init` to write a starter template.",
|
|
366
|
+
details: { expected_memory_file: memPath },
|
|
367
|
+
proposedFix: "Run `bourdon copilot init` to write the starter memory.md template.",
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
let data;
|
|
371
|
+
try {
|
|
372
|
+
data = this.read();
|
|
373
|
+
}
|
|
374
|
+
catch (err) {
|
|
375
|
+
logger.warn(`CopilotParticipant health_check failed: ${String(err)}`);
|
|
376
|
+
return {
|
|
377
|
+
status: "degraded",
|
|
378
|
+
reason: "Memory file present but could not be parsed.",
|
|
379
|
+
details: { error: String(err) },
|
|
380
|
+
proposedFix: `Inspect ${memPath} for malformed YAML front-matter. ` +
|
|
381
|
+
"The opening and closing `---` fences must wrap a valid YAML " +
|
|
382
|
+
"block. See docs/agent-integration-status.md for the schema.",
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
return {
|
|
386
|
+
status: "ok",
|
|
387
|
+
details: {
|
|
388
|
+
memory_file: memPath,
|
|
389
|
+
entity_count: asArray(data["entities"]).length,
|
|
390
|
+
session_count: asArray(data["sessions"]).length,
|
|
391
|
+
},
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
// -- Internal ---------------------------------------------------------------
|
|
395
|
+
read() {
|
|
396
|
+
return readMemoryFile(defaultCopilotMemoryPath(this.copilotDir));
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
// -- Init helper --------------------------------------------------------------
|
|
400
|
+
/**
|
|
401
|
+
* Create `~/.copilot-bourdon/memory.md` with a starter template. Returns the
|
|
402
|
+
* written path. Throws when the file already exists unless `force` is true.
|
|
403
|
+
*/
|
|
404
|
+
export function initMemoryFile(copilotDir, force = false) {
|
|
405
|
+
const targetDir = copilotDir || defaultCopilotBourdonDir();
|
|
406
|
+
mkdirSync(targetDir, { recursive: true });
|
|
407
|
+
const memPath = join(targetDir, MEMORY_FILENAME);
|
|
408
|
+
if (isFile(memPath) && !force) {
|
|
409
|
+
throw new Error(`${memPath} already exists. Pass --force to overwrite.`);
|
|
410
|
+
}
|
|
411
|
+
writeFileSync(memPath, MEMORY_TEMPLATE, "utf8");
|
|
412
|
+
return memPath;
|
|
413
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cursor automations participant — publish background agent run memory as L5
|
|
3
|
+
* evidence. Port of `participants/cursor_automations.py`.
|
|
4
|
+
*
|
|
5
|
+
* Reads the `~/.cursor/automations/<id>/` CONVENTION (file-based, no SQLite):
|
|
6
|
+
*
|
|
7
|
+
* automation.toml — id, name, status, schedule (rrule), kind, cwds
|
|
8
|
+
* memory.md — dated bullet entries, one block per run
|
|
9
|
+
*
|
|
10
|
+
* Each `automation.toml` becomes a known Entity; each dated section of
|
|
11
|
+
* `memory.md` becomes a recent Session. This covers the federation gap the
|
|
12
|
+
* interactive-only `cursor` participant leaves behind: Cursor Cloud Agent
|
|
13
|
+
* background tasks whose work never touches the interactive SQLite state.
|
|
14
|
+
*
|
|
15
|
+
* Path resolution (default dir):
|
|
16
|
+
* 1. explicit `cursorHome` ctor arg → `<cursorHome>/automations`
|
|
17
|
+
* 2. `CURSOR_DIR` or `CURSOR_HOME` env → `<env>/automations`
|
|
18
|
+
* 3. `~/.cursor/automations`
|
|
19
|
+
*
|
|
20
|
+
* Redaction: every native action string passes through the redaction SSOT
|
|
21
|
+
* (`redactText(v, 180)` == Python `_safe_native_memory_text`). Visibility policy
|
|
22
|
+
* filters PRIVATE entities BEFORE emission. `healthCheck` never throws.
|
|
23
|
+
*/
|
|
24
|
+
import { type L5ManifestModel, type SessionModel } from "@getbourdon/l5";
|
|
25
|
+
import { type AgentStore, type BourdonParticipant, type HealthStatus } from "../base.js";
|
|
26
|
+
/** Summary of a {@link mergeAutomationTree} call. */
|
|
27
|
+
export interface MergeResult {
|
|
28
|
+
automationsSeen: number;
|
|
29
|
+
automationsCreated: number;
|
|
30
|
+
bulletsAdded: number;
|
|
31
|
+
sectionsCreated: number;
|
|
32
|
+
skipped: string[];
|
|
33
|
+
}
|
|
34
|
+
/** Return the default Cursor automations directory. */
|
|
35
|
+
export declare function defaultCursorAutomationsDir(cursorHome?: string | null): string;
|
|
36
|
+
/**
|
|
37
|
+
* Merge an automations tree from `sourceDir` into `destDir`. Idempotent —
|
|
38
|
+
* calling twice on the same source is a no-op. Throws when `sourceDir` is not a
|
|
39
|
+
* directory (Python `FileNotFoundError`).
|
|
40
|
+
*/
|
|
41
|
+
export declare function mergeAutomationTree(sourceDir: string, destDir: string, defaultKind?: string): MergeResult;
|
|
42
|
+
/** Create a starter automation directory with toml + memory.md. */
|
|
43
|
+
export declare function initAutomationsDir(automationsDir?: string | null, automationId?: string, force?: boolean): string;
|
|
44
|
+
/** External participant for Cursor Cloud Agent automation memory artifacts. */
|
|
45
|
+
export declare class CursorAutomationsParticipant implements BourdonParticipant {
|
|
46
|
+
agentId: string;
|
|
47
|
+
agentType: string;
|
|
48
|
+
private readonly automationsDir;
|
|
49
|
+
private readonly policy;
|
|
50
|
+
constructor(automationsDir?: string | null, cursorHome?: string | null);
|
|
51
|
+
get nativePath(): string;
|
|
52
|
+
discover(): AgentStore;
|
|
53
|
+
exportSessions(since?: Date, limit?: number): SessionModel[];
|
|
54
|
+
exportL5(since?: Date): L5ManifestModel;
|
|
55
|
+
healthCheck(): HealthStatus;
|
|
56
|
+
private runs;
|
|
57
|
+
}
|