@ectplsm/relic 0.5.1 → 0.5.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.
Files changed (36) hide show
  1. package/README.md +36 -4
  2. package/dist/adapters/shells/codex-hook.d.ts +3 -3
  3. package/dist/adapters/shells/codex-hook.js +12 -11
  4. package/dist/adapters/shells/codex-paths.d.ts +11 -0
  5. package/dist/adapters/shells/codex-paths.js +23 -0
  6. package/dist/adapters/shells/codex-shell.js +7 -5
  7. package/dist/adapters/shells/gemini-shell.js +38 -38
  8. package/dist/interfaces/cli/index.js +0 -0
  9. package/dist/interfaces/mcp/index.js +0 -0
  10. package/package.json +3 -2
  11. package/templates/engrams/commander/IDENTITY.md +4 -0
  12. package/templates/engrams/commander/SOUL.md +14 -1
  13. package/templates/engrams/rebel/IDENTITY.md +3 -1
  14. package/templates/engrams/rebel/SOUL.md +12 -0
  15. package/dist/adapters/shells/copilot-shell.d.ts +0 -14
  16. package/dist/adapters/shells/copilot-shell.js +0 -43
  17. package/dist/adapters/shells/trust-registrar.d.ts +0 -19
  18. package/dist/adapters/shells/trust-registrar.js +0 -141
  19. package/dist/core/usecases/inbox-search.d.ts +0 -20
  20. package/dist/core/usecases/inbox-search.js +0 -46
  21. package/dist/core/usecases/inbox-write.d.ts +0 -27
  22. package/dist/core/usecases/inbox-write.js +0 -72
  23. package/dist/core/usecases/memory-search.d.ts +0 -23
  24. package/dist/core/usecases/memory-search.js +0 -59
  25. package/dist/core/usecases/mikoshi-clone.d.ts +0 -29
  26. package/dist/core/usecases/mikoshi-clone.js +0 -94
  27. package/dist/interfaces/cli/commands/extract.d.ts +0 -2
  28. package/dist/interfaces/cli/commands/extract.js +0 -37
  29. package/dist/interfaces/cli/commands/inject.d.ts +0 -2
  30. package/dist/interfaces/cli/commands/inject.js +0 -34
  31. package/dist/interfaces/cli/commands/sync.d.ts +0 -2
  32. package/dist/interfaces/cli/commands/sync.js +0 -47
  33. package/dist/shared/memory-inbox.d.ts +0 -78
  34. package/dist/shared/memory-inbox.js +0 -168
  35. package/dist/shared/session-recorder.d.ts +0 -47
  36. package/dist/shared/session-recorder.js +0 -112
@@ -1,141 +0,0 @@
1
- import { readFile, writeFile } from "node:fs/promises";
2
- import { join } from "node:path";
3
- import { homedir } from "node:os";
4
- import { existsSync } from "node:fs";
5
- export async function registerTrustedFolders(relicEngramsPath) {
6
- const result = { registered: [], skipped: [] };
7
- await Promise.all([
8
- registerClaude(relicEngramsPath, result),
9
- registerCodex(relicEngramsPath, result),
10
- registerGemini(relicEngramsPath, result),
11
- ]);
12
- return result;
13
- }
14
- // ---------------------------------------------------------------------------
15
- // Claude Code — ~/.claude/settings.json
16
- // { "sandbox": { "filesystem": { "allowWrite": ["~/.relic/engrams"] } } }
17
- // ---------------------------------------------------------------------------
18
- async function registerClaude(engramsPath, result) {
19
- const claudeDir = join(homedir(), ".claude");
20
- const configPath = join(claudeDir, "settings.json");
21
- if (!existsSync(claudeDir)) {
22
- return;
23
- }
24
- try {
25
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
26
- let settings = {};
27
- if (existsSync(configPath)) {
28
- const raw = await readFile(configPath, "utf-8");
29
- settings = JSON.parse(raw);
30
- }
31
- // sandbox.filesystem.allowWrite を確保
32
- settings.sandbox ??= {};
33
- settings.sandbox.filesystem ??= {};
34
- settings.sandbox.filesystem.allowWrite ??= [];
35
- const allowWrite = settings.sandbox.filesystem.allowWrite;
36
- // permissions.allow — ツール承認の自動化
37
- settings.permissions ??= {};
38
- settings.permissions.allow ??= [];
39
- const allow = settings.permissions.allow;
40
- const tildeForm = engramsPath.replace(homedir(), "~");
41
- // sandbox: allowWrite
42
- const sandboxNeeded = !allowWrite.includes(engramsPath) && !allowWrite.includes(tildeForm);
43
- if (sandboxNeeded) {
44
- allowWrite.push(tildeForm);
45
- }
46
- // permissions: Edit allow
47
- const editRule = `Edit(${tildeForm}/**)`;
48
- const permNeeded = !allow.includes(editRule);
49
- if (permNeeded) {
50
- allow.push(editRule);
51
- }
52
- if (!sandboxNeeded && !permNeeded) {
53
- result.skipped.push("Claude Code");
54
- return;
55
- }
56
- await writeFile(configPath, JSON.stringify(settings, null, 2), "utf-8");
57
- result.registered.push("Claude Code");
58
- }
59
- catch {
60
- // best effort
61
- }
62
- }
63
- // ---------------------------------------------------------------------------
64
- // Codex CLI — ~/.codex/config.toml
65
- // [sandbox]
66
- // writable_roots = ["~/.relic/engrams"]
67
- // ---------------------------------------------------------------------------
68
- async function registerCodex(engramsPath, result) {
69
- const codexDir = join(homedir(), ".codex");
70
- const configPath = join(codexDir, "config.toml");
71
- if (!existsSync(codexDir)) {
72
- return;
73
- }
74
- try {
75
- let content = "";
76
- if (existsSync(configPath)) {
77
- content = await readFile(configPath, "utf-8");
78
- }
79
- const tildeForm = engramsPath.replace(homedir(), "~");
80
- // 既に writable_roots に含まれているかチェック
81
- if (content.includes(engramsPath) || content.includes(tildeForm)) {
82
- result.skipped.push("Codex CLI");
83
- return;
84
- }
85
- // [sandbox] セクションが存在するか
86
- const sandboxMatch = content.match(/^\[sandbox\]\s*$/m);
87
- if (sandboxMatch) {
88
- // [sandbox] セクション内の writable_roots を探す
89
- const writableMatch = content.match(/^(writable_roots\s*=\s*\[)(.*?)(\])/m);
90
- if (writableMatch) {
91
- // 既存の writable_roots に追加
92
- const existing = writableMatch[2].trim();
93
- const newValue = existing
94
- ? `${existing}, "${tildeForm}"`
95
- : `"${tildeForm}"`;
96
- content = content.replace(/^(writable_roots\s*=\s*\[)(.*?)(\])/m, `$1${newValue}$3`);
97
- }
98
- else {
99
- // [sandbox] セクションに writable_roots を追加
100
- content = content.replace(/^\[sandbox\]\s*$/m, `[sandbox]\nwritable_roots = ["${tildeForm}"]`);
101
- }
102
- }
103
- else {
104
- // [sandbox] セクションごと追加
105
- content =
106
- content.trimEnd() + `\n\n[sandbox]\nwritable_roots = ["${tildeForm}"]\n`;
107
- }
108
- await writeFile(configPath, content, "utf-8");
109
- result.registered.push("Codex CLI");
110
- }
111
- catch {
112
- // best effort
113
- }
114
- }
115
- // ---------------------------------------------------------------------------
116
- // Gemini CLI — ~/.gemini/trustedFolders.json
117
- // { "/path/to/engrams": "TRUST_FOLDER" }
118
- // ---------------------------------------------------------------------------
119
- async function registerGemini(engramsPath, result) {
120
- const configPath = join(homedir(), ".gemini", "trustedFolders.json");
121
- if (!existsSync(join(homedir(), ".gemini"))) {
122
- return;
123
- }
124
- try {
125
- let folders = {};
126
- if (existsSync(configPath)) {
127
- const raw = await readFile(configPath, "utf-8");
128
- folders = JSON.parse(raw);
129
- }
130
- if (folders[engramsPath]) {
131
- result.skipped.push("Gemini CLI");
132
- return;
133
- }
134
- folders[engramsPath] = "TRUST_FOLDER";
135
- await writeFile(configPath, JSON.stringify(folders, null, 2), "utf-8");
136
- result.registered.push("Gemini CLI");
137
- }
138
- catch {
139
- // best effort
140
- }
141
- }
@@ -1,20 +0,0 @@
1
- export interface InboxSearchResult {
2
- /** マッチしたエントリの内容 */
3
- entry: string;
4
- /** エントリのインデックス(新しい順: 0が最新) */
5
- index: number;
6
- }
7
- /**
8
- * InboxSearch — inbox.md をキーワード検索する
9
- *
10
- * inbox.md は全セッションログ + [memory] エントリを含む生データ。
11
- * memory/*.md(蒸留済み)より情報量が多いため、検索先として優れている。
12
- */
13
- export declare class InboxSearch {
14
- private readonly engramsPath;
15
- constructor(engramsPath: string);
16
- search(engramId: string, query: string, limit?: number): Promise<InboxSearchResult[]>;
17
- }
18
- export declare class InboxSearchEngramNotFoundError extends Error {
19
- constructor(id: string);
20
- }
@@ -1,46 +0,0 @@
1
- import { join } from "node:path";
2
- import { existsSync } from "node:fs";
3
- import { readFile } from "node:fs/promises";
4
- const ENTRY_SEPARATOR = /\n---\n/;
5
- /**
6
- * InboxSearch — inbox.md をキーワード検索する
7
- *
8
- * inbox.md は全セッションログ + [memory] エントリを含む生データ。
9
- * memory/*.md(蒸留済み)より情報量が多いため、検索先として優れている。
10
- */
11
- export class InboxSearch {
12
- engramsPath;
13
- constructor(engramsPath) {
14
- this.engramsPath = engramsPath;
15
- }
16
- async search(engramId, query, limit = 5) {
17
- const inboxPath = join(this.engramsPath, engramId, "inbox.md");
18
- if (!existsSync(inboxPath)) {
19
- throw new InboxSearchEngramNotFoundError(engramId);
20
- }
21
- const raw = await readFile(inboxPath, "utf-8");
22
- if (!raw.trim())
23
- return [];
24
- const entries = raw
25
- .split(ENTRY_SEPARATOR)
26
- .map((e) => e.trim())
27
- .filter((e) => e.length > 0)
28
- .reverse(); // 新しい順
29
- const queryLower = query.toLowerCase();
30
- const results = [];
31
- for (let i = 0; i < entries.length; i++) {
32
- if (entries[i].toLowerCase().includes(queryLower)) {
33
- results.push({ entry: entries[i], index: i });
34
- }
35
- if (results.length >= limit)
36
- break;
37
- }
38
- return results;
39
- }
40
- }
41
- export class InboxSearchEngramNotFoundError extends Error {
42
- constructor(id) {
43
- super(`Engram "${id}" inbox not found`);
44
- this.name = "InboxSearchEngramNotFoundError";
45
- }
46
- }
@@ -1,27 +0,0 @@
1
- export interface InboxWriteResult {
2
- engramId: string;
3
- /** inbox.md に追記されたエントリ数 */
4
- totalEntries: number;
5
- /** memory/*.md に永続化されたメモリ数 */
6
- memoriesSaved: number;
7
- /** ログとして記録されたエントリ数 */
8
- logsRecorded: number;
9
- }
10
- /**
11
- * InboxWrite — MCP経由で inbox.md にエントリを追記する
12
- *
13
- * CLI の MemoryInbox (fs.watch) と同じフォーマットで inbox.md に書き込む。
14
- * `[memory]` タグ付きエントリは即座に memory/*.md にも永続化する。
15
- *
16
- * これにより CLI Shell と MCP Desktop で同一の inbox.md フォーマットを共有し、
17
- * セッションログ + メモリ永続化の二重の役割を果たす。
18
- */
19
- export declare class InboxWrite {
20
- private readonly engramsPath;
21
- constructor(engramsPath: string);
22
- execute(engramId: string, content: string): Promise<InboxWriteResult>;
23
- private appendToInbox;
24
- }
25
- export declare class InboxWriteEngramNotFoundError extends Error {
26
- constructor(id: string);
27
- }
@@ -1,72 +0,0 @@
1
- import { join } from "node:path";
2
- import { existsSync } from "node:fs";
3
- import { readFile, writeFile, mkdir } from "node:fs/promises";
4
- import { MemoryWrite } from "./memory-write.js";
5
- const ENTRY_SEPARATOR = /\n---\n/;
6
- const MEMORY_TAG = /^\[memory\]\s*/i;
7
- /**
8
- * InboxWrite — MCP経由で inbox.md にエントリを追記する
9
- *
10
- * CLI の MemoryInbox (fs.watch) と同じフォーマットで inbox.md に書き込む。
11
- * `[memory]` タグ付きエントリは即座に memory/*.md にも永続化する。
12
- *
13
- * これにより CLI Shell と MCP Desktop で同一の inbox.md フォーマットを共有し、
14
- * セッションログ + メモリ永続化の二重の役割を果たす。
15
- */
16
- export class InboxWrite {
17
- engramsPath;
18
- constructor(engramsPath) {
19
- this.engramsPath = engramsPath;
20
- }
21
- async execute(engramId, content) {
22
- const engramDir = join(this.engramsPath, engramId);
23
- if (!existsSync(engramDir)) {
24
- throw new InboxWriteEngramNotFoundError(engramId);
25
- }
26
- // inbox.md に追記
27
- const inboxPath = join(engramDir, "inbox.md");
28
- await this.appendToInbox(inboxPath, content);
29
- // エントリをパースして [memory] タグ付きを永続化
30
- const entries = content
31
- .split(ENTRY_SEPARATOR)
32
- .map((e) => e.trim())
33
- .filter((e) => e.length > 0);
34
- const writer = new MemoryWrite(this.engramsPath);
35
- let memoriesSaved = 0;
36
- let logsRecorded = 0;
37
- for (const entry of entries) {
38
- if (MEMORY_TAG.test(entry)) {
39
- const memContent = entry.replace(MEMORY_TAG, "").trim();
40
- await writer.execute(engramId, memContent);
41
- memoriesSaved++;
42
- }
43
- else {
44
- logsRecorded++;
45
- }
46
- }
47
- return {
48
- engramId,
49
- totalEntries: entries.length,
50
- memoriesSaved,
51
- logsRecorded,
52
- };
53
- }
54
- async appendToInbox(inboxPath, content) {
55
- await mkdir(join(inboxPath, ".."), { recursive: true });
56
- let existing = "";
57
- if (existsSync(inboxPath)) {
58
- existing = await readFile(inboxPath, "utf-8");
59
- }
60
- // 既存内容があれば --- で区切って追記
61
- const separator = existing.length > 0 && !existing.endsWith("\n---\n")
62
- ? "\n---\n"
63
- : "";
64
- await writeFile(inboxPath, existing + separator + content + "\n", "utf-8");
65
- }
66
- }
67
- export class InboxWriteEngramNotFoundError extends Error {
68
- constructor(id) {
69
- super(`Engram "${id}" not found`);
70
- this.name = "InboxWriteEngramNotFoundError";
71
- }
72
- }
@@ -1,23 +0,0 @@
1
- import type { EngramRepository } from "../ports/engram-repository.js";
2
- export interface MemorySearchResult {
3
- date: string;
4
- content: string;
5
- /** Matched lines (for keyword search) */
6
- matchedLines: string[];
7
- }
8
- /**
9
- * MemorySearch — Engramのメモリエントリをキーワード検索する
10
- */
11
- export declare class MemorySearch {
12
- private readonly repository;
13
- constructor(repository: EngramRepository);
14
- search(engramId: string, query: string, limit?: number): Promise<MemorySearchResult[]>;
15
- get(engramId: string, date: string): Promise<{
16
- date: string;
17
- content: string;
18
- } | null>;
19
- listDates(engramId: string): Promise<string[]>;
20
- }
21
- export declare class MemoryEngramNotFoundError extends Error {
22
- constructor(id: string);
23
- }
@@ -1,59 +0,0 @@
1
- /**
2
- * MemorySearch — Engramのメモリエントリをキーワード検索する
3
- */
4
- export class MemorySearch {
5
- repository;
6
- constructor(repository) {
7
- this.repository = repository;
8
- }
9
- async search(engramId, query, limit = 5) {
10
- const engram = await this.repository.get(engramId);
11
- if (!engram) {
12
- throw new MemoryEngramNotFoundError(engramId);
13
- }
14
- if (!engram.files.memoryEntries) {
15
- return [];
16
- }
17
- const queryLower = query.toLowerCase();
18
- const results = [];
19
- // Search entries from newest to oldest
20
- const entries = Object.entries(engram.files.memoryEntries).sort(([a], [b]) => b.localeCompare(a));
21
- for (const [date, content] of entries) {
22
- const lines = content.split("\n");
23
- const matchedLines = lines.filter((line) => line.toLowerCase().includes(queryLower));
24
- if (matchedLines.length > 0) {
25
- results.push({ date, content, matchedLines });
26
- }
27
- if (results.length >= limit)
28
- break;
29
- }
30
- return results;
31
- }
32
- async get(engramId, date) {
33
- const engram = await this.repository.get(engramId);
34
- if (!engram) {
35
- throw new MemoryEngramNotFoundError(engramId);
36
- }
37
- const content = engram.files.memoryEntries?.[date];
38
- if (!content) {
39
- return null;
40
- }
41
- return { date, content };
42
- }
43
- async listDates(engramId) {
44
- const engram = await this.repository.get(engramId);
45
- if (!engram) {
46
- throw new MemoryEngramNotFoundError(engramId);
47
- }
48
- if (!engram.files.memoryEntries) {
49
- return [];
50
- }
51
- return Object.keys(engram.files.memoryEntries).sort((a, b) => b.localeCompare(a));
52
- }
53
- }
54
- export class MemoryEngramNotFoundError extends Error {
55
- constructor(id) {
56
- super(`Engram "${id}" not found`);
57
- this.name = "MemoryEngramNotFoundError";
58
- }
59
- }
@@ -1,29 +0,0 @@
1
- import type { EngramRepository } from "../ports/engram-repository.js";
2
- import type { MikoshiClient } from "../ports/mikoshi.js";
3
- export interface MikoshiCloneResult {
4
- engramId: string;
5
- engramName: string;
6
- cloudEngramId: string;
7
- }
8
- export declare class MikoshiCloneAlreadyExistsError extends Error {
9
- readonly engramId: string;
10
- constructor(engramId: string);
11
- }
12
- export declare class MikoshiCloneCloudNotFoundError extends Error {
13
- readonly sourceEngramId: string;
14
- constructor(sourceEngramId: string);
15
- }
16
- export declare class MikoshiClonePersonaMissingError extends Error {
17
- readonly cloudEngramId: string;
18
- constructor(cloudEngramId: string);
19
- }
20
- export declare class MikoshiClone {
21
- private readonly localRepo;
22
- private readonly mikoshi;
23
- constructor(localRepo: EngramRepository, mikoshi: MikoshiClient);
24
- /**
25
- * Clone a remote Engram from Mikoshi to local.
26
- * Fails if the local Engram already exists.
27
- */
28
- execute(engramId: string): Promise<MikoshiCloneResult>;
29
- }
@@ -1,94 +0,0 @@
1
- // ---------------------------------------------------------------------------
2
- // Errors
3
- // ---------------------------------------------------------------------------
4
- export class MikoshiCloneAlreadyExistsError extends Error {
5
- engramId;
6
- constructor(engramId) {
7
- super(`Engram "${engramId}" already exists locally. Use "relic mikoshi pull" to update it.`);
8
- this.engramId = engramId;
9
- this.name = "MikoshiCloneAlreadyExistsError";
10
- }
11
- }
12
- export class MikoshiCloneCloudNotFoundError extends Error {
13
- sourceEngramId;
14
- constructor(sourceEngramId) {
15
- super(`Engram "${sourceEngramId}" not found on Mikoshi`);
16
- this.sourceEngramId = sourceEngramId;
17
- this.name = "MikoshiCloneCloudNotFoundError";
18
- }
19
- }
20
- export class MikoshiClonePersonaMissingError extends Error {
21
- cloudEngramId;
22
- constructor(cloudEngramId) {
23
- super(`Remote Engram "${cloudEngramId}" is missing SOUL.md or IDENTITY.md`);
24
- this.cloudEngramId = cloudEngramId;
25
- this.name = "MikoshiClonePersonaMissingError";
26
- }
27
- }
28
- // ---------------------------------------------------------------------------
29
- // Usecase
30
- // ---------------------------------------------------------------------------
31
- export class MikoshiClone {
32
- localRepo;
33
- mikoshi;
34
- constructor(localRepo, mikoshi) {
35
- this.localRepo = localRepo;
36
- this.mikoshi = mikoshi;
37
- }
38
- /**
39
- * Clone a remote Engram from Mikoshi to local.
40
- * Fails if the local Engram already exists.
41
- */
42
- async execute(engramId) {
43
- // 1. ローカルに既に存在 → エラー
44
- const existing = await this.localRepo.get(engramId);
45
- if (existing) {
46
- throw new MikoshiCloneAlreadyExistsError(engramId);
47
- }
48
- // 2. クラウド検索
49
- const cloudEngram = await this.mikoshi.getEngramBySourceId(engramId);
50
- if (!cloudEngram)
51
- throw new MikoshiCloneCloudNotFoundError(engramId);
52
- // 3. Detail 取得 (persona content 込み)
53
- const detail = await this.mikoshi.getEngram(cloudEngram.id);
54
- const { soul, identity } = extractPersona(detail);
55
- if (!soul || !identity) {
56
- throw new MikoshiClonePersonaMissingError(cloudEngram.id);
57
- }
58
- // 4. ローカルに新規作成
59
- const now = new Date().toISOString();
60
- await this.localRepo.save({
61
- meta: {
62
- id: engramId,
63
- name: detail.name,
64
- description: detail.description ?? undefined,
65
- tags: detail.tags,
66
- createdAt: now,
67
- updatedAt: now,
68
- },
69
- files: {
70
- soul,
71
- identity,
72
- },
73
- });
74
- return {
75
- engramId,
76
- engramName: detail.name,
77
- cloudEngramId: cloudEngram.id,
78
- };
79
- }
80
- }
81
- // ---------------------------------------------------------------------------
82
- // Helpers
83
- // ---------------------------------------------------------------------------
84
- function extractPersona(detail) {
85
- let soul;
86
- let identity;
87
- for (const f of detail.personaFiles) {
88
- if (f.fileType === "SOUL")
89
- soul = f.content;
90
- if (f.fileType === "IDENTITY")
91
- identity = f.content;
92
- }
93
- return { soul, identity };
94
- }
@@ -1,2 +0,0 @@
1
- import type { Command } from "commander";
2
- export declare function registerExtractCommand(program: Command): void;
@@ -1,37 +0,0 @@
1
- import { LocalEngramRepository } from "../../../adapters/local/index.js";
2
- import { Extract, WorkspaceNotFoundError, WorkspaceEmptyError, AlreadyExtractedError, } from "../../../core/usecases/index.js";
3
- import { resolveEngramsPath, resolveOpenclawPath } from "../../../shared/config.js";
4
- export function registerExtractCommand(program) {
5
- program
6
- .command("extract")
7
- .description("Create a new Engram from an OpenClaw agent workspace")
8
- .option("-a, --agent <name>", "OpenClaw agent name to extract from (default: main)")
9
- .option("--name <name>", "Engram display name (defaults to agent name)")
10
- .option("--openclaw <dir>", "Override OpenClaw directory path (default: ~/.openclaw)")
11
- .option("-p, --path <dir>", "Override engrams directory path")
12
- .action(async (opts) => {
13
- const engramsPath = await resolveEngramsPath(opts.path);
14
- const openclawDir = await resolveOpenclawPath(opts.openclaw);
15
- const repo = new LocalEngramRepository(engramsPath);
16
- const extract = new Extract(repo);
17
- try {
18
- const agentName = opts.agent ?? "main";
19
- const result = await extract.execute(agentName, {
20
- name: opts.name,
21
- openclawDir,
22
- });
23
- console.log(`Extracted "${result.engramName}" from ${result.sourcePath}`);
24
- console.log(` Files read: ${result.filesRead.join(", ")}`);
25
- console.log(` Saved as Engram: ${result.engramId}`);
26
- }
27
- catch (err) {
28
- if (err instanceof WorkspaceNotFoundError ||
29
- err instanceof WorkspaceEmptyError ||
30
- err instanceof AlreadyExtractedError) {
31
- console.error(`Error: ${err.message}`);
32
- process.exit(1);
33
- }
34
- throw err;
35
- }
36
- });
37
- }
@@ -1,2 +0,0 @@
1
- import type { Command } from "commander";
2
- export declare function registerInjectCommand(program: Command): void;
@@ -1,34 +0,0 @@
1
- import { LocalEngramRepository } from "../../../adapters/local/index.js";
2
- import { Inject, InjectEngramNotFoundError, InjectWorkspaceNotFoundError, } from "../../../core/usecases/index.js";
3
- import { resolveEngramsPath, resolveOpenclawPath } from "../../../shared/config.js";
4
- export function registerInjectCommand(program) {
5
- program
6
- .command("inject")
7
- .description("Inject an Engram into an OpenClaw workspace")
8
- .requiredOption("-e, --engram <id>", "Engram ID to inject (= agent name)")
9
- .option("--to <agent>", "Inject into a different agent name")
10
- .option("--openclaw <dir>", "Override OpenClaw directory path (default: ~/.openclaw)")
11
- .option("-p, --path <dir>", "Override engrams directory path")
12
- .action(async (opts) => {
13
- const engramsPath = await resolveEngramsPath(opts.path);
14
- const openclawDir = await resolveOpenclawPath(opts.openclaw);
15
- const repo = new LocalEngramRepository(engramsPath);
16
- const inject = new Inject(repo);
17
- try {
18
- const result = await inject.execute(opts.engram, {
19
- to: opts.to,
20
- openclawDir,
21
- });
22
- console.log(`Injected "${result.engramName}" into ${result.targetPath}`);
23
- console.log(` Files written: ${result.filesWritten.join(", ")}`);
24
- }
25
- catch (err) {
26
- if (err instanceof InjectEngramNotFoundError ||
27
- err instanceof InjectWorkspaceNotFoundError) {
28
- console.error(`Error: ${err.message}`);
29
- process.exit(1);
30
- }
31
- throw err;
32
- }
33
- });
34
- }
@@ -1,2 +0,0 @@
1
- import type { Command } from "commander";
2
- export declare function registerSyncCommand(program: Command): void;
@@ -1,47 +0,0 @@
1
- import { LocalEngramRepository } from "../../../adapters/local/index.js";
2
- import { Sync, SyncOpenclawDirNotFoundError, } from "../../../core/usecases/index.js";
3
- import { resolveEngramsPath } from "../../../shared/config.js";
4
- export function registerSyncCommand(program) {
5
- program
6
- .command("sync")
7
- .description("Bidirectional memory sync between Relic Engrams and OpenClaw workspaces")
8
- .option("--openclaw <dir>", "Override OpenClaw directory path (default: ~/.openclaw)")
9
- .option("-p, --path <dir>", "Override engrams directory path")
10
- .action(async (opts) => {
11
- const engramsPath = await resolveEngramsPath(opts.path);
12
- const repo = new LocalEngramRepository(engramsPath);
13
- const sync = new Sync(repo, engramsPath);
14
- try {
15
- const result = await sync.execute(opts.openclaw);
16
- if (result.synced.length === 0 && result.skipped.length === 0) {
17
- console.log("No OpenClaw workspaces found.");
18
- return;
19
- }
20
- for (const s of result.synced) {
21
- const details = [];
22
- if (s.memoryFilesMerged > 0) {
23
- details.push(`${s.memoryFilesMerged} memory file(s)`);
24
- }
25
- if (s.memoryIndexMerged) {
26
- details.push("MEMORY.md");
27
- }
28
- if (details.length > 0) {
29
- console.log(` ${s.engramId}: merged ${details.join(", ")}`);
30
- }
31
- else {
32
- console.log(` ${s.engramId}: already in sync`);
33
- }
34
- }
35
- if (result.skipped.length > 0) {
36
- console.log(` Skipped (no matching Engram): ${result.skipped.join(", ")}`);
37
- }
38
- }
39
- catch (err) {
40
- if (err instanceof SyncOpenclawDirNotFoundError) {
41
- console.error(`Error: ${err.message}`);
42
- process.exit(1);
43
- }
44
- throw err;
45
- }
46
- });
47
- }