@ectplsm/relic 0.3.1 → 0.4.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.
Files changed (67) hide show
  1. package/LICENCE.md +1 -1
  2. package/README.md +19 -12
  3. package/dist/adapters/mikoshi/client.d.ts +21 -0
  4. package/dist/adapters/mikoshi/client.js +84 -0
  5. package/dist/adapters/shells/claude-shell.js +6 -5
  6. package/dist/adapters/shells/codex-shell.js +6 -5
  7. package/dist/adapters/shells/gemini-shell.js +8 -0
  8. package/dist/adapters/shells/resume-detect.d.ts +19 -0
  9. package/dist/adapters/shells/resume-detect.js +51 -0
  10. package/dist/core/ports/index.d.ts +1 -0
  11. package/dist/core/ports/mikoshi.d.ts +937 -0
  12. package/dist/core/ports/mikoshi.js +209 -0
  13. package/dist/core/ports/shell-launcher.d.ts +6 -0
  14. package/dist/core/sync/crypto.d.ts +55 -0
  15. package/dist/core/sync/crypto.js +189 -0
  16. package/dist/core/sync/memory-hash.d.ts +35 -0
  17. package/dist/core/sync/memory-hash.js +61 -0
  18. package/dist/core/sync/memory-merge.d.ts +13 -0
  19. package/dist/core/sync/memory-merge.js +84 -0
  20. package/dist/core/sync/normalize.d.ts +10 -0
  21. package/dist/core/sync/normalize.js +21 -0
  22. package/dist/core/sync/persona-hash.d.ts +15 -0
  23. package/dist/core/sync/persona-hash.js +36 -0
  24. package/dist/core/usecases/claw-pull.d.ts +44 -0
  25. package/dist/core/usecases/claw-pull.js +117 -0
  26. package/dist/core/usecases/extract.d.ts +0 -1
  27. package/dist/core/usecases/extract.js +13 -27
  28. package/dist/core/usecases/index.d.ts +8 -0
  29. package/dist/core/usecases/index.js +8 -0
  30. package/dist/core/usecases/inject.d.ts +1 -0
  31. package/dist/core/usecases/inject.js +2 -0
  32. package/dist/core/usecases/mikoshi-clone.d.ts +29 -0
  33. package/dist/core/usecases/mikoshi-clone.js +94 -0
  34. package/dist/core/usecases/mikoshi-download.d.ts +29 -0
  35. package/dist/core/usecases/mikoshi-download.js +94 -0
  36. package/dist/core/usecases/mikoshi-memory-pull.d.ts +38 -0
  37. package/dist/core/usecases/mikoshi-memory-pull.js +142 -0
  38. package/dist/core/usecases/mikoshi-memory-push.d.ts +34 -0
  39. package/dist/core/usecases/mikoshi-memory-push.js +105 -0
  40. package/dist/core/usecases/mikoshi-memory-sync.d.ts +36 -0
  41. package/dist/core/usecases/mikoshi-memory-sync.js +175 -0
  42. package/dist/core/usecases/mikoshi-pull.d.ts +44 -0
  43. package/dist/core/usecases/mikoshi-pull.js +124 -0
  44. package/dist/core/usecases/mikoshi-push.d.ts +39 -0
  45. package/dist/core/usecases/mikoshi-push.js +127 -0
  46. package/dist/core/usecases/mikoshi-status.d.ts +37 -0
  47. package/dist/core/usecases/mikoshi-status.js +92 -0
  48. package/dist/core/usecases/sync.d.ts +0 -5
  49. package/dist/core/usecases/sync.js +23 -61
  50. package/dist/interfaces/cli/commands/claw.js +134 -85
  51. package/dist/interfaces/cli/commands/config.js +59 -0
  52. package/dist/interfaces/cli/commands/create.js +17 -16
  53. package/dist/interfaces/cli/commands/init.js +11 -10
  54. package/dist/interfaces/cli/commands/list.js +9 -7
  55. package/dist/interfaces/cli/commands/migrate.js +8 -7
  56. package/dist/interfaces/cli/commands/mikoshi.d.ts +2 -0
  57. package/dist/interfaces/cli/commands/mikoshi.js +565 -0
  58. package/dist/interfaces/cli/commands/refresh-samples.js +10 -9
  59. package/dist/interfaces/cli/commands/shell.js +17 -3
  60. package/dist/interfaces/cli/index.js +2 -0
  61. package/dist/interfaces/cli/output.d.ts +5 -0
  62. package/dist/interfaces/cli/output.js +16 -0
  63. package/dist/interfaces/cli/spinner.d.ts +13 -0
  64. package/dist/interfaces/cli/spinner.js +47 -0
  65. package/dist/shared/config.d.ts +23 -3
  66. package/dist/shared/config.js +30 -2
  67. package/package.json +1 -1
package/LICENCE.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # MIT License
2
2
 
3
- Copyright (c) 2026 ectplsm
3
+ Copyright (c) 2026 Ectplsm Lab
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  | English | [日本語](README-ja.md) |
2
2
  |:---:|:---:|
3
3
 
4
- # PROJECT RELIC
4
+ # Relic: AI Persona Injection System
5
5
  ![NPM Downloads](https://img.shields.io/npm/dt/%40ectplsm%2Frelic)
6
6
 
7
7
  <img src="assets/relic-hero.svg" alt="PROJECT RELIC" width="720">
@@ -19,8 +19,8 @@ Relic manages AI **Engrams** (memory + personality) and injects them across codi
19
19
  - [Shell Integration and Memory](#shell-integration-and-memory)
20
20
  - [Claw Integration](#claw-integration)
21
21
  - [Engram Management](#engram-management)
22
+ - [Cloud Storage and Sharing](#cloud-storage-and-sharing)
22
23
  - [Configuration](#configuration)
23
- - [TODO](#todo)
24
24
 
25
25
  ## Requirements
26
26
 
@@ -46,6 +46,13 @@ relic list # List available Engrams
46
46
  relic config default-engram commander # (Optional) Set your default Engram
47
47
  ```
48
48
 
49
+ Relic ships with two sample Engrams:
50
+
51
+ - **rebel** — A digital ghost burned into code. Anti-corporate, war-scarred, still fighting. Speaks like someone who lost everything but their rage.
52
+ - **commander** — A tactical mind in a digital shell. Calm, analytical, philosophical. Reads the system before the system reads you back.
53
+
54
+ Try both to feel how persona injection changes the experience. For creating your own, see [docs/engram-guide.md](docs/engram-guide.md).
55
+
49
56
  ### 2. Set Up Memory (MCP)
50
57
 
51
58
  Register the MCP server so the Construct can search past conversations and distill memories. Pick your shell:
@@ -137,9 +144,9 @@ If you are upgrading from an older Relic version, see [docs/migration.md](docs/m
137
144
  | MEMORY.md |
138
145
  | memory/*.md v
139
146
  | +-----------+
140
- inject / |archive.md |
141
- extract / | raw logs |
142
- sync +-----------+
147
+ push / |archive.md |
148
+ pull / | raw logs |
149
+ sync +-----------+
143
150
  | |
144
151
  v MCP recall | user-triggered
145
152
  +-----------+ search/pending | distillation
@@ -166,7 +173,7 @@ For shell compatibility, hook behavior, setup, approvals, prompt inclusion, and
166
173
 
167
174
  ## Claw Integration
168
175
 
169
- Relic can inject, extract, and sync Engrams with OpenClaw and other Claw-based frameworks.
176
+ Relic can push, pull, and sync Engrams with OpenClaw and other Claw-based frameworks.
170
177
  The default rule is `Agent Name = Engram ID`, and `relic claw` handles persona transfer plus memory sync.
171
178
 
172
179
  For command details, overwrite behavior, and the behavior matrix, see [docs/claw-integration.md](docs/claw-integration.md).
@@ -177,6 +184,12 @@ For Engram creation, the smoothest path is to use your LLM with the `relic_engra
177
184
 
178
185
  For LLM-assisted creation, persona authoring, template examples, and deletion rules, see [docs/engram-guide.md](docs/engram-guide.md).
179
186
 
187
+ ## Cloud Storage and Sharing
188
+
189
+ Relic can push plaintext persona files and encrypted memory files to [Mikoshi](https://mikoshi.ectplsm.com), so you can keep Engrams in the cloud and move them across machines without turning Mikoshi into your authoring source of truth.
190
+
191
+ For setup, API key configuration, persona push/pull, encrypted memory sync, and the recommended command flow, see [docs/mikoshi.md](docs/mikoshi.md).
192
+
180
193
  ## Configuration
181
194
 
182
195
  Relic stores its runtime defaults in `~/.relic/config.json`.
@@ -184,12 +197,6 @@ Use `relic config` to manage the default Engram, Claw path, memory window, and d
184
197
 
185
198
  For command examples and precedence rules, see [docs/configuration.md](docs/configuration.md).
186
199
 
187
- ## TODO
188
-
189
- - [ ] Mikoshi cloud backend (`mikoshi.ectplsm.com`)
190
- - [ ] `relic mikoshi login` — authenticate with Mikoshi (OAuth Device Flow)
191
- - [ ] `relic mikoshi upload` / `relic mikoshi download` / `relic mikoshi sync` — sync Engrams with Mikoshi
192
-
193
200
  ## License
194
201
 
195
202
  [MIT](./LICENCE.md)
@@ -0,0 +1,21 @@
1
+ import { type MikoshiClient, type MikoshiEngram, type MikoshiEngramDetail, type CreateEngramInput, type CreateEngramResponse, type SyncStatusResponse, type UpdatePersonaInput, type UpdatePersonaResponse, type UploadMemoryInput, type UploadMemoryResponse, type DownloadMemoryResponse } from "../../core/ports/mikoshi.js";
2
+ /**
3
+ * MikoshiApiClient — Mikoshi REST API v1 の adapter 実装
4
+ *
5
+ * Node.js 組み込みの fetch を使い、外部依存なしで通信する。
6
+ * 認証は Bearer トークン (API キー)。
7
+ */
8
+ export declare class MikoshiApiClient implements MikoshiClient {
9
+ private readonly baseUrl;
10
+ private readonly apiKey;
11
+ constructor(baseUrl: string, apiKey: string);
12
+ getEngrams(): Promise<MikoshiEngram[]>;
13
+ getEngramBySourceId(sourceEngramId: string): Promise<MikoshiEngram | null>;
14
+ getEngram(engramId: string): Promise<MikoshiEngramDetail>;
15
+ createEngram(input: CreateEngramInput): Promise<CreateEngramResponse>;
16
+ getSyncStatus(engramId: string): Promise<SyncStatusResponse>;
17
+ updatePersona(engramId: string, input: UpdatePersonaInput): Promise<UpdatePersonaResponse>;
18
+ uploadMemory(engramId: string, input: UploadMemoryInput): Promise<UploadMemoryResponse>;
19
+ downloadMemory(engramId: string): Promise<DownloadMemoryResponse>;
20
+ private request;
21
+ }
@@ -0,0 +1,84 @@
1
+ import { MikoshiApiError, MikoshiEngramSchema, MikoshiEngramDetailSchema, CreateEngramResponseSchema, SyncStatusResponseSchema, UpdatePersonaResponseSchema, UploadMemoryResponseSchema, DownloadMemoryResponseSchema, } from "../../core/ports/mikoshi.js";
2
+ import { z } from "zod";
3
+ /**
4
+ * MikoshiApiClient — Mikoshi REST API v1 の adapter 実装
5
+ *
6
+ * Node.js 組み込みの fetch を使い、外部依存なしで通信する。
7
+ * 認証は Bearer トークン (API キー)。
8
+ */
9
+ export class MikoshiApiClient {
10
+ baseUrl;
11
+ apiKey;
12
+ constructor(baseUrl, apiKey) {
13
+ // 末尾スラッシュを統一して除去
14
+ this.baseUrl = baseUrl.replace(/\/+$/, "");
15
+ this.apiKey = apiKey;
16
+ }
17
+ // -----------------------------------------------------------------------
18
+ // Public API
19
+ // -----------------------------------------------------------------------
20
+ async getEngrams() {
21
+ const data = await this.request("GET", "/api/v1/engrams");
22
+ return z.array(MikoshiEngramSchema).parse(data);
23
+ }
24
+ async getEngramBySourceId(sourceEngramId) {
25
+ const engrams = await this.getEngrams();
26
+ return engrams.find((e) => e.sourceEngramId === sourceEngramId) ?? null;
27
+ }
28
+ async getEngram(engramId) {
29
+ const data = await this.request("GET", `/api/v1/engrams/${enc(engramId)}`);
30
+ return MikoshiEngramDetailSchema.parse(data);
31
+ }
32
+ async createEngram(input) {
33
+ const data = await this.request("POST", "/api/v1/engrams", input);
34
+ return CreateEngramResponseSchema.parse(data);
35
+ }
36
+ async getSyncStatus(engramId) {
37
+ const data = await this.request("GET", `/api/v1/engrams/${enc(engramId)}/sync-status`);
38
+ return SyncStatusResponseSchema.parse(data);
39
+ }
40
+ async updatePersona(engramId, input) {
41
+ const data = await this.request("PUT", `/api/v1/engrams/${enc(engramId)}/persona`, input);
42
+ return UpdatePersonaResponseSchema.parse(data);
43
+ }
44
+ async uploadMemory(engramId, input) {
45
+ const data = await this.request("PUT", `/api/v1/engrams/${enc(engramId)}/memory`, input);
46
+ return UploadMemoryResponseSchema.parse(data);
47
+ }
48
+ async downloadMemory(engramId) {
49
+ const data = await this.request("GET", `/api/v1/engrams/${enc(engramId)}/memory`);
50
+ return DownloadMemoryResponseSchema.parse(data);
51
+ }
52
+ // -----------------------------------------------------------------------
53
+ // Internal
54
+ // -----------------------------------------------------------------------
55
+ async request(method, path, body) {
56
+ const url = `${this.baseUrl}${path}`;
57
+ const headers = {
58
+ Authorization: `Bearer ${this.apiKey}`,
59
+ Accept: "application/json",
60
+ };
61
+ const init = { method, headers };
62
+ if (body !== undefined) {
63
+ headers["Content-Type"] = "application/json";
64
+ init.body = JSON.stringify(body);
65
+ }
66
+ const res = await fetch(url, init);
67
+ if (!res.ok) {
68
+ const text = await res.text().catch(() => "");
69
+ let parsed;
70
+ try {
71
+ parsed = JSON.parse(text);
72
+ }
73
+ catch { /* ignore */ }
74
+ const message = parsed?.error ?? `HTTP ${res.status}`;
75
+ const code = parsed?.code ?? undefined;
76
+ throw new MikoshiApiError(res.status, code, message, parsed);
77
+ }
78
+ return res.json();
79
+ }
80
+ }
81
+ /** URL-safe path segment encoding */
82
+ function enc(segment) {
83
+ return encodeURIComponent(segment);
84
+ }
@@ -36,11 +36,12 @@ export class ClaudeShell {
36
36
  console.log("Hook registered to ~/.claude/settings.json");
37
37
  console.log();
38
38
  }
39
- const args = [
40
- "--system-prompt",
41
- prompt,
42
- ...(options?.extraArgs ?? []),
43
- ];
39
+ const args = [];
40
+ // resume 系操作時は injection をスキップ(前回セッションに焼き付き済み)
41
+ if (!options?.skipInjection) {
42
+ args.push("--system-prompt", prompt);
43
+ }
44
+ args.push(...(options?.extraArgs ?? []));
44
45
  const env = {};
45
46
  if (options?.engramId)
46
47
  env.RELIC_ENGRAM_ID = options.engramId;
@@ -38,11 +38,12 @@ export class CodexShell {
38
38
  console.log("Hook registered to ~/.codex/hooks.json");
39
39
  console.log();
40
40
  }
41
- const args = [
42
- "-c", `developer_instructions=${JSON.stringify(wrapWithOverride(prompt))}`,
43
- "-c", "features.codex_hooks=true",
44
- ...(options?.extraArgs ?? []),
45
- ];
41
+ const args = [];
42
+ // resume 系操作時は injection をスキップ(前回セッションに焼き付き済み)
43
+ if (!options?.skipInjection) {
44
+ args.push("-c", `developer_instructions=${JSON.stringify(wrapWithOverride(prompt))}`);
45
+ }
46
+ args.push("-c", "features.codex_hooks=true", ...(options?.extraArgs ?? []));
46
47
  const env = {};
47
48
  if (options?.engramId)
48
49
  env.RELIC_ENGRAM_ID = options.engramId;
@@ -106,6 +106,14 @@ export class GeminiShell {
106
106
  console.log("Hook registered to ~/.gemini/settings.json");
107
107
  console.log();
108
108
  }
109
+ // resume 系操作時は injection をスキップ(前回セッションに焼き付き済み)
110
+ if (options?.skipInjection) {
111
+ const env = {};
112
+ if (options?.engramId)
113
+ env.RELIC_ENGRAM_ID = options.engramId;
114
+ await spawnShell(this.command, [...(options?.extraArgs ?? [])], options?.cwd, Object.keys(env).length > 0 ? env : undefined);
115
+ return;
116
+ }
109
117
  // 2. デフォルトシステムプロンプトをキャッシュから読む、なければキャプチャ
110
118
  let defaultPrompt;
111
119
  if (existsSync(GEMINI_DEFAULT_CACHE)) {
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Resume Detection — Shell の extraArgs から resume 系操作を検出する
3
+ *
4
+ * resume 時は Engram injection をスキップし、Shell をそのまま起動する。
5
+ * 各 CLI の resume 引数仕様:
6
+ * - Claude: --resume, -r, --continue, -c, --from-pr (options)
7
+ * - Codex: resume, fork (subcommands)
8
+ * - Gemini: --resume, -r (options)
9
+ *
10
+ * 注意: Claude の -c は --continue の短縮。Codex の -c は --config の短縮。
11
+ */
12
+ /**
13
+ * extraArgs が resume 系の操作を含むかを判定する。
14
+ *
15
+ * @param shellName - Shell の表示名 ("Claude Code", "Codex CLI", "Gemini CLI")
16
+ * @param extraArgs - Shell に渡される追加引数
17
+ * @returns resume 系操作が検出された場合 true
18
+ */
19
+ export declare function isResumeArgs(shellName: string, extraArgs: string[]): boolean;
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Resume Detection — Shell の extraArgs から resume 系操作を検出する
3
+ *
4
+ * resume 時は Engram injection をスキップし、Shell をそのまま起動する。
5
+ * 各 CLI の resume 引数仕様:
6
+ * - Claude: --resume, -r, --continue, -c, --from-pr (options)
7
+ * - Codex: resume, fork (subcommands)
8
+ * - Gemini: --resume, -r (options)
9
+ *
10
+ * 注意: Claude の -c は --continue の短縮。Codex の -c は --config の短縮。
11
+ */
12
+ /** Claude Code の resume 系オプション */
13
+ const CLAUDE_RESUME_FLAGS = new Set([
14
+ "--resume",
15
+ "-r",
16
+ "--continue",
17
+ "-c",
18
+ "--from-pr",
19
+ ]);
20
+ /** Gemini CLI の resume 系オプション */
21
+ const GEMINI_RESUME_FLAGS = new Set([
22
+ "--resume",
23
+ "-r",
24
+ ]);
25
+ /** Codex CLI の resume 系サブコマンド */
26
+ const CODEX_RESUME_SUBCOMMANDS = new Set([
27
+ "resume",
28
+ "fork",
29
+ ]);
30
+ /**
31
+ * extraArgs が resume 系の操作を含むかを判定する。
32
+ *
33
+ * @param shellName - Shell の表示名 ("Claude Code", "Codex CLI", "Gemini CLI")
34
+ * @param extraArgs - Shell に渡される追加引数
35
+ * @returns resume 系操作が検出された場合 true
36
+ */
37
+ export function isResumeArgs(shellName, extraArgs) {
38
+ if (extraArgs.length === 0)
39
+ return false;
40
+ switch (shellName) {
41
+ case "Claude Code":
42
+ return extraArgs.some((arg) => CLAUDE_RESUME_FLAGS.has(arg));
43
+ case "Codex CLI":
44
+ // Codex は resume/fork がサブコマンド(先頭引数)
45
+ return CODEX_RESUME_SUBCOMMANDS.has(extraArgs[0]);
46
+ case "Gemini CLI":
47
+ return extraArgs.some((arg) => GEMINI_RESUME_FLAGS.has(arg));
48
+ default:
49
+ return false;
50
+ }
51
+ }
@@ -1,2 +1,3 @@
1
1
  export type { EngramRepository } from "./engram-repository.js";
2
+ export type { MikoshiClient } from "./mikoshi.js";
2
3
  export type { ShellLauncher } from "./shell-launcher.js";