@ectplsm/relic 0.2.1 → 0.2.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 +54 -31
  2. package/dist/adapters/local/local-engram-repository.d.ts +5 -1
  3. package/dist/adapters/local/local-engram-repository.js +53 -19
  4. package/dist/core/entities/engram.d.ts +56 -14
  5. package/dist/core/entities/engram.js +15 -6
  6. package/dist/core/entities/index.d.ts +1 -1
  7. package/dist/core/entities/index.js +1 -1
  8. package/dist/core/usecases/create-engram.d.ts +26 -0
  9. package/dist/core/usecases/create-engram.js +54 -0
  10. package/dist/core/usecases/delete-engram.d.ts +24 -0
  11. package/dist/core/usecases/delete-engram.js +46 -0
  12. package/dist/core/usecases/index.d.ts +2 -0
  13. package/dist/core/usecases/index.js +2 -0
  14. package/dist/core/usecases/migrate-engrams.d.ts +18 -0
  15. package/dist/core/usecases/migrate-engrams.js +49 -0
  16. package/dist/core/usecases/refresh-samples.d.ts +21 -0
  17. package/dist/core/usecases/refresh-samples.js +60 -0
  18. package/dist/interfaces/cli/commands/create.d.ts +2 -0
  19. package/dist/interfaces/cli/commands/create.js +143 -0
  20. package/dist/interfaces/cli/commands/delete.d.ts +2 -0
  21. package/dist/interfaces/cli/commands/delete.js +113 -0
  22. package/dist/interfaces/cli/commands/migrate.d.ts +2 -0
  23. package/dist/interfaces/cli/commands/migrate.js +31 -0
  24. package/dist/interfaces/cli/commands/refresh-samples.d.ts +2 -0
  25. package/dist/interfaces/cli/commands/refresh-samples.js +24 -0
  26. package/dist/interfaces/cli/index.js +8 -0
  27. package/dist/interfaces/mcp/index.js +68 -0
  28. package/dist/shared/config.d.ts +2 -1
  29. package/dist/shared/config.js +13 -7
  30. package/dist/shared/templates.d.ts +6 -0
  31. package/dist/shared/templates.js +65 -0
  32. package/package.json +1 -1
  33. package/templates/engrams/johnny/IDENTITY.md +10 -0
  34. package/templates/engrams/johnny/SOUL.md +41 -0
  35. package/templates/engrams/motoko/IDENTITY.md +19 -1
  36. package/templates/engrams/motoko/SOUL.md +58 -1
package/README.md CHANGED
@@ -2,6 +2,7 @@
2
2
  |:---:|:---:|
3
3
 
4
4
  # PROJECT RELIC
5
+ ![NPM Downloads](https://img.shields.io/npm/dt/%40ectplsm%2Frelic)
5
6
 
6
7
  ```
7
8
  ____ ________ ____________
@@ -17,6 +18,7 @@ Relic manages AI **Engrams** (memory + personality) and injects them into coding
17
18
 
18
19
  ## Table of Contents
19
20
 
21
+ - [Requirements](#requirements)
20
22
  - [Install](#install)
21
23
  - [Quick Start](#quick-start)
22
24
  - [What `relic init` Creates](#what-relic-init-creates)
@@ -29,9 +31,14 @@ Relic manages AI **Engrams** (memory + personality) and injects them into coding
29
31
  - [Memory Management](#memory-management)
30
32
  - [Configuration](#configuration)
31
33
  - [Creating Your Own Engram](#creating-your-own-engram)
34
+ - [Deleting an Engram](#deleting-an-engram)
32
35
  - [Domain Glossary](#domain-glossary)
33
36
  - [Roadmap](#roadmap)
34
37
 
38
+ ## Requirements
39
+
40
+ - Node.js 18 or later
41
+
35
42
  ## Install
36
43
 
37
44
  <img alt="version badge" src="https://img.shields.io/github/v/release/ectplsm/relic?filter=*.*.*">
@@ -98,12 +105,14 @@ Running `relic init` creates `~/.relic/`, writes `config.json`, and seeds two sa
98
105
  └── engrams/
99
106
  ├── johnny/
100
107
  │ ├── engram.json
108
+ │ ├── manifest.json
101
109
  │ ├── SOUL.md
102
110
  │ ├── IDENTITY.md
103
111
  │ └── memory/
104
112
  │ └── YYYY-MM-DD.md
105
113
  └── motoko/
106
114
  ├── engram.json
115
+ ├── manifest.json
107
116
  ├── SOUL.md
108
117
  ├── IDENTITY.md
109
118
  └── memory/
@@ -112,7 +121,8 @@ Running `relic init` creates `~/.relic/`, writes `config.json`, and seeds two sa
112
121
 
113
122
  - `config.json` stores global Relic settings such as `engramsPath`, `defaultEngram`, `clawPath`, and `memoryWindowSize`.
114
123
  - `engrams/<id>/` is one Engram workspace. This is where persona files and memory for that Engram live.
115
- - `engram.json` stores metadata like the Engram's ID, display name, description, and tags.
124
+ - `engram.json` stores editable profile fields like display name, description, and tags.
125
+ - `manifest.json` stores system-managed fields like the Engram ID and timestamps.
116
126
  - `SOUL.md` and `IDENTITY.md` define the persona itself.
117
127
  - `memory/YYYY-MM-DD.md` stores dated distilled memory entries. `relic init` seeds an initial memory file for each sample Engram.
118
128
 
@@ -123,11 +133,20 @@ As you keep using an Engram, more files are added to the same workspace:
123
133
  - `USER.md` is created or updated during memory distillation to record user preferences, tendencies, and work style.
124
134
  - `~/.relic/hooks/` and `~/.relic/gemini-system-default.md` are created later on first shell launch when hook registration or Gemini prompt caching is needed.
125
135
 
136
+ ### Migration
137
+
138
+ If you want to manually update parts of an existing local setup that changed over time, use:
139
+
140
+ ```bash
141
+ relic migrate engrams # migrate legacy engram.json metadata to manifest.json
142
+ relic refresh-samples # refresh bundled sample personas like johnny and motoko
143
+ ```
144
+
126
145
  ## Sample Engrams
127
146
 
128
147
  `relic init` seeds two ready-to-use Engrams. Their SOUL.md and IDENTITY.md follow the [OpenClaw](https://github.com/openclaw/openclaw) format.
129
148
 
130
- > **Existing users:** The latest templates are always available in [`templates/engrams/`](templates/engrams/). Copy them over your `~/.relic/engrams/` files to update.
149
+ > **Existing users:** Run `relic refresh-samples` to update bundled sample personas from the latest templates.
131
150
 
132
151
  ### Johnny Silverhand (`johnny`)
133
152
 
@@ -266,6 +285,7 @@ Session logs and memory entries are written automatically by a **background hook
266
285
 
267
286
  | Tool | Description |
268
287
  |------|-------------|
288
+ | `relic_engram_create` | Create a new Engram with optional LLM-generated SOUL.md and IDENTITY.md |
269
289
  | `relic_archive_search` | Search the Engram's raw archive by keyword (newest-first) |
270
290
  | `relic_archive_pending` | Get un-distilled archive entries since the last distillation (up to 30) |
271
291
  | `relic_memory_write` | Write distilled memory to `memory/*.md`, optionally append to `MEMORY.md`, optionally update `USER.md`, and advance the archive cursor |
@@ -287,6 +307,7 @@ To suppress confirmation dialogs and auto-approve Relic tools across all project
287
307
  "permissions": {
288
308
  "allow": [
289
309
  "Edit(~/.relic/engrams/**)",
310
+ "mcp__relic__relic_engram_create",
290
311
  "mcp__relic__relic_archive_search",
291
312
  "mcp__relic__relic_archive_pending",
292
313
  "mcp__relic__relic_memory_write"
@@ -306,6 +327,9 @@ codex mcp add relic -- relic-mcp
306
327
  To suppress confirmation dialogs and auto-approve Relic tools, add the following to `~/.codex/config.toml`:
307
328
 
308
329
  ```toml
330
+ [mcp_servers.relic.tools.relic_engram_create]
331
+ approval_mode = "approve"
332
+
309
333
  [mcp_servers.relic.tools.relic_archive_search]
310
334
  approval_mode = "approve"
311
335
 
@@ -378,7 +402,7 @@ relic claw inject --engram motoko --yes
378
402
  Creates a new Engram from an existing Claw agent workspace.
379
403
 
380
404
  What `extract` writes locally:
381
- - New extract: `engram.json`, `SOUL.md`, `IDENTITY.md`, `USER.md`, `MEMORY.md`, `memory/*.md`
405
+ - New extract: `engram.json`, `manifest.json`, `SOUL.md`, `IDENTITY.md`, `USER.md`, `MEMORY.md`, `memory/*.md`
382
406
  - `extract --force`: only `SOUL.md` and `IDENTITY.md`
383
407
  - `extract --force --name`: `SOUL.md`, `IDENTITY.md`, and `engram.json.name`
384
408
 
@@ -509,34 +533,29 @@ CLI flags always take precedence over config values.
509
533
 
510
534
  ## Creating Your Own Engram
511
535
 
512
- Create a directory under `~/.relic/engrams/` with the following structure:
536
+ The easiest way to create a new Engram is with `relic create`:
513
537
 
514
- ```
515
- ~/.relic/engrams/your-persona/
516
- ├── engram.json # Metadata (id, name, description, tags)
517
- ├── SOUL.md # Core directive — how the persona thinks and acts
518
- ├── IDENTITY.md # Name, tone, background, personality
519
- ├── AGENTS.md # (optional) Tool usage policies
520
- ├── USER.md # (optional) User context
521
- ├── MEMORY.md # (optional) Memory index
522
- ├── HEARTBEAT.md # (optional) Periodic reflection
523
- └── memory/ # (optional) Dated memory entries
524
- └── 2026-03-21.md
538
+ ```bash
539
+ # Fully interactive — prompts for everything
540
+ relic create
541
+
542
+ # Pre-supply some fields
543
+ relic create --id my-agent --name "My Agent" --description "A helpful assistant" --tags "custom,dev"
525
544
  ```
526
545
 
527
- **engram.json:**
528
- ```json
529
- {
530
- "id": "your-persona",
531
- "name": "Display Name",
532
- "description": "A short description",
533
- "createdAt": "2026-03-21T00:00:00Z",
534
- "updatedAt": "2026-03-21T00:00:00Z",
535
- "tags": ["custom"]
536
- }
546
+ This creates the directory structure, writes `engram.json` / `manifest.json`, and seeds `SOUL.md` / `IDENTITY.md` with OpenClaw-compatible default templates. Customize the persona files, then launch a shell:
547
+
548
+ ```bash
549
+ relic claude my-agent
537
550
  ```
538
551
 
539
- **SOUL.md** — The most important file. Defines how the persona behaves. Follows the [OpenClaw](https://github.com/openclaw/openclaw) format:
552
+ You can also create Engrams via the `relic_engram_create` MCP tool — LLMs can ask about the desired personality and generate `SOUL.md` / `IDENTITY.md` content through conversation, then call the tool with the results.
553
+
554
+ ### Customizing the Persona
555
+
556
+ After running `relic create`, edit `SOUL.md` and `IDENTITY.md` in the Engram directory. These follow the [OpenClaw](https://github.com/openclaw/openclaw) format:
557
+
558
+ **SOUL.md** — The most important file. Defines how the persona behaves:
540
559
  ```markdown
541
560
  # SOUL.md - Who You Are
542
561
 
@@ -574,11 +593,14 @@ Each session, you wake up fresh. These files _are_ your memory. Read them. Updat
574
593
 
575
594
  See [`templates/engrams/`](templates/engrams/) for full working examples.
576
595
 
577
- After creating the directory, set it as your default Engram:
596
+ ## Deleting an Engram
597
+
578
598
  ```bash
579
- relic config default-engram your-persona
599
+ relic delete my-agent
580
600
  ```
581
601
 
602
+ If the Engram has memory data (`MEMORY.md`, `USER.md`, `memory/*.md`, `archive.md`), you'll need to type the Engram ID to confirm deletion. Use `--force` to skip all prompts.
603
+
582
604
  ## Domain Glossary
583
605
 
584
606
  | Term | Role | Description |
@@ -597,10 +619,11 @@ relic config default-engram your-persona
597
619
  - [x] Claw integration (inject / extract / sync)
598
620
  - [x] `relic claw sync` — bidirectional memory sync with Claw workspaces
599
621
  - [x] `relic config` — manage default Engram, Claw path, memory window
600
- - [ ] `relic login` — authenticate with Mikoshi (OAuth Device Flow)
601
- - [ ] `relic push` / `relic pull` — sync Engrams with Mikoshi
622
+ - [x] `relic create` — interactive Engram creation wizard + MCP tool
623
+ - [x] `relic delete` — safe Engram deletion with memory-aware confirmation
602
624
  - [ ] Mikoshi cloud backend (`mikoshi.ectplsm.com`)
603
- - [ ] `relic create` — interactive Engram creation wizard
625
+ - [ ] `relic mikoshi login` — authenticate with Mikoshi (OAuth Device Flow)
626
+ - [ ] `relic mikoshi upload` / `relic mikoshi download` / `relic mikoshi sync` — sync Engrams with Mikoshi
604
627
 
605
628
  ## License
606
629
 
@@ -6,7 +6,8 @@ import type { EngramRepository } from "../../core/ports/engram-repository.js";
6
6
  *
7
7
  * ディレクトリ構造:
8
8
  * {basePath}/{engramId}/
9
- * ├── engram.json (メタデータ)
9
+ * ├── engram.json (ユーザー編集可能なプロフィール)
10
+ * ├── manifest.json (システム管理の識別子・タイムスタンプ)
10
11
  * ├── SOUL.md
11
12
  * ├── IDENTITY.md
12
13
  * ├── AGENTS.md (optional)
@@ -24,5 +25,8 @@ export declare class LocalEngramRepository implements EngramRepository {
24
25
  save(engram: Engram): Promise<void>;
25
26
  delete(id: string): Promise<void>;
26
27
  private readMeta;
28
+ private toProfile;
29
+ private toManifest;
30
+ private writeMetaFiles;
27
31
  private readFiles;
28
32
  }
@@ -1,7 +1,7 @@
1
1
  import { readdir, readFile, writeFile, mkdir, rm } from "node:fs/promises";
2
2
  import { join } from "node:path";
3
3
  import { existsSync } from "node:fs";
4
- import { EngramMetaSchema } from "../../core/entities/engram.js";
4
+ import { EngramManifestSchema, EngramMetaSchema, EngramProfileSchema, } from "../../core/entities/engram.js";
5
5
  /**
6
6
  * OpenClaw互換のファイル名マッピング
7
7
  * EngramFiles のキー → 実ファイル名
@@ -14,7 +14,8 @@ const FILE_MAP = {
14
14
  memory: "MEMORY.md",
15
15
  heartbeat: "HEARTBEAT.md",
16
16
  };
17
- const META_FILE = "engram.json";
17
+ const PROFILE_FILE = "engram.json";
18
+ const MANIFEST_FILE = "manifest.json";
18
19
  const MEMORY_DIR = "memory";
19
20
  /**
20
21
  * LocalEngramRepository — ローカルファイルシステム上の
@@ -22,7 +23,8 @@ const MEMORY_DIR = "memory";
22
23
  *
23
24
  * ディレクトリ構造:
24
25
  * {basePath}/{engramId}/
25
- * ├── engram.json (メタデータ)
26
+ * ├── engram.json (ユーザー編集可能なプロフィール)
27
+ * ├── manifest.json (システム管理の識別子・タイムスタンプ)
26
28
  * ├── SOUL.md
27
29
  * ├── IDENTITY.md
28
30
  * ├── AGENTS.md (optional)
@@ -45,13 +47,9 @@ export class LocalEngramRepository {
45
47
  const dirs = entries.filter((e) => e.isDirectory());
46
48
  const metas = [];
47
49
  for (const dir of dirs) {
48
- const metaPath = join(this.basePath, dir.name, META_FILE);
49
- if (!existsSync(metaPath))
50
- continue;
51
- const raw = await readFile(metaPath, "utf-8");
52
- const parsed = EngramMetaSchema.safeParse(JSON.parse(raw));
53
- if (parsed.success) {
54
- metas.push(parsed.data);
50
+ const meta = await this.readMeta(join(this.basePath, dir.name));
51
+ if (meta) {
52
+ metas.push(meta);
55
53
  }
56
54
  }
57
55
  return metas;
@@ -61,7 +59,7 @@ export class LocalEngramRepository {
61
59
  if (!existsSync(engramDir)) {
62
60
  return null;
63
61
  }
64
- const meta = await this.readMeta(engramDir);
62
+ const meta = await this.readMeta(engramDir, { migrateLegacy: true });
65
63
  if (!meta)
66
64
  return null;
67
65
  const files = await this.readFiles(engramDir);
@@ -70,8 +68,7 @@ export class LocalEngramRepository {
70
68
  async save(engram) {
71
69
  const engramDir = join(this.basePath, engram.meta.id);
72
70
  await mkdir(engramDir, { recursive: true });
73
- // メタデータ書き込み
74
- await writeFile(join(engramDir, META_FILE), JSON.stringify(engram.meta, null, 2), "utf-8");
71
+ await this.writeMetaFiles(engramDir, engram.meta);
75
72
  // 必須ファイル書き込み
76
73
  for (const [key, filename] of Object.entries(FILE_MAP)) {
77
74
  const content = engram.files[key];
@@ -95,13 +92,50 @@ export class LocalEngramRepository {
95
92
  }
96
93
  }
97
94
  // --- private ---
98
- async readMeta(engramDir) {
99
- const metaPath = join(engramDir, META_FILE);
100
- if (!existsSync(metaPath))
95
+ async readMeta(engramDir, options) {
96
+ const profilePath = join(engramDir, PROFILE_FILE);
97
+ if (!existsSync(profilePath))
101
98
  return null;
102
- const raw = await readFile(metaPath, "utf-8");
103
- const parsed = EngramMetaSchema.safeParse(JSON.parse(raw));
104
- return parsed.success ? parsed.data : null;
99
+ const profileRaw = JSON.parse(await readFile(profilePath, "utf-8"));
100
+ const manifestPath = join(engramDir, MANIFEST_FILE);
101
+ if (existsSync(manifestPath)) {
102
+ const profile = EngramProfileSchema.safeParse(profileRaw);
103
+ if (!profile.success)
104
+ return null;
105
+ const manifest = EngramManifestSchema.safeParse(JSON.parse(await readFile(manifestPath, "utf-8")));
106
+ if (!manifest.success)
107
+ return null;
108
+ return {
109
+ ...profile.data,
110
+ ...manifest.data,
111
+ };
112
+ }
113
+ // 後方互換: 旧形式では engram.json に profile + manifest が同居していた
114
+ const legacy = EngramMetaSchema.safeParse(profileRaw);
115
+ if (!legacy.success)
116
+ return null;
117
+ if (options?.migrateLegacy) {
118
+ await this.writeMetaFiles(engramDir, legacy.data);
119
+ }
120
+ return legacy.data;
121
+ }
122
+ toProfile(meta) {
123
+ return {
124
+ name: meta.name,
125
+ description: meta.description,
126
+ tags: meta.tags,
127
+ };
128
+ }
129
+ toManifest(meta) {
130
+ return {
131
+ id: meta.id,
132
+ createdAt: meta.createdAt,
133
+ updatedAt: meta.updatedAt,
134
+ };
135
+ }
136
+ async writeMetaFiles(engramDir, meta) {
137
+ await writeFile(join(engramDir, PROFILE_FILE), JSON.stringify(this.toProfile(meta), null, 2), "utf-8");
138
+ await writeFile(join(engramDir, MANIFEST_FILE), JSON.stringify(this.toManifest(meta), null, 2), "utf-8");
105
139
  }
106
140
  async readFiles(engramDir) {
107
141
  const files = {};
@@ -37,31 +37,72 @@ export declare const EngramFileSchema: z.ZodObject<{
37
37
  }>;
38
38
  export type EngramFiles = z.infer<typeof EngramFileSchema>;
39
39
  /**
40
- * Engramメタデータ — Mikoshiでの管理情報
40
+ * Engramプロフィール — ユーザーが編集可能な表示用メタデータ
41
41
  */
42
- export declare const EngramMetaSchema: z.ZodObject<{
43
- /** 一意識別子 (例: "ghost-in-the-shell") */
44
- id: z.ZodString;
42
+ export declare const EngramProfileSchema: z.ZodObject<{
45
43
  /** 表示名 (例: "攻殻機動隊の少佐") */
46
44
  name: z.ZodString;
47
45
  /** 説明 */
48
46
  description: z.ZodOptional<z.ZodString>;
47
+ /** タグ */
48
+ tags: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
49
+ }, "strip", z.ZodTypeAny, {
50
+ name: string;
51
+ description?: string | undefined;
52
+ tags?: string[] | undefined;
53
+ }, {
54
+ name: string;
55
+ description?: string | undefined;
56
+ tags?: string[] | undefined;
57
+ }>;
58
+ export type EngramProfile = z.infer<typeof EngramProfileSchema>;
59
+ /**
60
+ * Engramマニフェスト — システム管理の不変識別子と監査情報
61
+ */
62
+ export declare const EngramManifestSchema: z.ZodObject<{
63
+ /** 一意識別子 (例: "ghost-in-the-shell") */
64
+ id: z.ZodString;
49
65
  /** 作成日時 */
50
66
  createdAt: z.ZodString;
51
67
  /** 最終更新日時 */
52
68
  updatedAt: z.ZodString;
69
+ }, "strip", z.ZodTypeAny, {
70
+ id: string;
71
+ createdAt: string;
72
+ updatedAt: string;
73
+ }, {
74
+ id: string;
75
+ createdAt: string;
76
+ updatedAt: string;
77
+ }>;
78
+ export type EngramManifest = z.infer<typeof EngramManifestSchema>;
79
+ /**
80
+ * Engramメタデータ — プロフィールとマニフェストを結合した利用時ビュー
81
+ */
82
+ export declare const EngramMetaSchema: z.ZodObject<{
83
+ /** 表示名 (例: "攻殻機動隊の少佐") */
84
+ name: z.ZodString;
85
+ /** 説明 */
86
+ description: z.ZodOptional<z.ZodString>;
53
87
  /** タグ */
54
88
  tags: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
89
+ } & {
90
+ /** 一意識別子 (例: "ghost-in-the-shell") */
91
+ id: z.ZodString;
92
+ /** 作成日時 */
93
+ createdAt: z.ZodString;
94
+ /** 最終更新日時 */
95
+ updatedAt: z.ZodString;
55
96
  }, "strip", z.ZodTypeAny, {
56
- id: string;
57
97
  name: string;
98
+ id: string;
58
99
  createdAt: string;
59
100
  updatedAt: string;
60
101
  description?: string | undefined;
61
102
  tags?: string[] | undefined;
62
103
  }, {
63
- id: string;
64
104
  name: string;
105
+ id: string;
65
106
  createdAt: string;
66
107
  updatedAt: string;
67
108
  description?: string | undefined;
@@ -74,28 +115,29 @@ export type EngramMeta = z.infer<typeof EngramMetaSchema>;
74
115
  */
75
116
  export declare const EngramSchema: z.ZodObject<{
76
117
  meta: z.ZodObject<{
77
- /** 一意識別子 (例: "ghost-in-the-shell") */
78
- id: z.ZodString;
79
118
  /** 表示名 (例: "攻殻機動隊の少佐") */
80
119
  name: z.ZodString;
81
120
  /** 説明 */
82
121
  description: z.ZodOptional<z.ZodString>;
122
+ /** タグ */
123
+ tags: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
124
+ } & {
125
+ /** 一意識別子 (例: "ghost-in-the-shell") */
126
+ id: z.ZodString;
83
127
  /** 作成日時 */
84
128
  createdAt: z.ZodString;
85
129
  /** 最終更新日時 */
86
130
  updatedAt: z.ZodString;
87
- /** タグ */
88
- tags: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
89
131
  }, "strip", z.ZodTypeAny, {
90
- id: string;
91
132
  name: string;
133
+ id: string;
92
134
  createdAt: string;
93
135
  updatedAt: string;
94
136
  description?: string | undefined;
95
137
  tags?: string[] | undefined;
96
138
  }, {
97
- id: string;
98
139
  name: string;
140
+ id: string;
99
141
  createdAt: string;
100
142
  updatedAt: string;
101
143
  description?: string | undefined;
@@ -135,8 +177,8 @@ export declare const EngramSchema: z.ZodObject<{
135
177
  }>;
136
178
  }, "strip", z.ZodTypeAny, {
137
179
  meta: {
138
- id: string;
139
180
  name: string;
181
+ id: string;
140
182
  createdAt: string;
141
183
  updatedAt: string;
142
184
  description?: string | undefined;
@@ -153,8 +195,8 @@ export declare const EngramSchema: z.ZodObject<{
153
195
  };
154
196
  }, {
155
197
  meta: {
156
- id: string;
157
198
  name: string;
199
+ id: string;
158
200
  createdAt: string;
159
201
  updatedAt: string;
160
202
  description?: string | undefined;
@@ -23,22 +23,31 @@ export const EngramFileSchema = z.object({
23
23
  memoryEntries: z.record(z.string(), z.string()).optional(),
24
24
  });
25
25
  /**
26
- * Engramメタデータ — Mikoshiでの管理情報
26
+ * Engramプロフィール — ユーザーが編集可能な表示用メタデータ
27
27
  */
28
- export const EngramMetaSchema = z.object({
29
- /** 一意識別子 (例: "ghost-in-the-shell") */
30
- id: z.string(),
28
+ export const EngramProfileSchema = z.object({
31
29
  /** 表示名 (例: "攻殻機動隊の少佐") */
32
30
  name: z.string(),
33
31
  /** 説明 */
34
32
  description: z.string().optional(),
33
+ /** タグ */
34
+ tags: z.array(z.string()).optional(),
35
+ });
36
+ /**
37
+ * Engramマニフェスト — システム管理の不変識別子と監査情報
38
+ */
39
+ export const EngramManifestSchema = z.object({
40
+ /** 一意識別子 (例: "ghost-in-the-shell") */
41
+ id: z.string(),
35
42
  /** 作成日時 */
36
43
  createdAt: z.string().datetime(),
37
44
  /** 最終更新日時 */
38
45
  updatedAt: z.string().datetime(),
39
- /** タグ */
40
- tags: z.array(z.string()).optional(),
41
46
  });
47
+ /**
48
+ * Engramメタデータ — プロフィールとマニフェストを結合した利用時ビュー
49
+ */
50
+ export const EngramMetaSchema = EngramProfileSchema.merge(EngramManifestSchema);
42
51
  /**
43
52
  * Engram — 完全な人格データセット
44
53
  * メタデータとファイル群の統合体
@@ -1 +1 @@
1
- export { EngramSchema, EngramFileSchema, EngramMetaSchema, ConstructStatusSchema, ShellTypeSchema, type Engram, type EngramFiles, type EngramMeta, type ConstructStatus, type ShellType, } from "./engram.js";
1
+ export { EngramSchema, EngramFileSchema, EngramProfileSchema, EngramManifestSchema, EngramMetaSchema, ConstructStatusSchema, ShellTypeSchema, type Engram, type EngramFiles, type EngramProfile, type EngramManifest, type EngramMeta, type ConstructStatus, type ShellType, } from "./engram.js";
@@ -1 +1 @@
1
- export { EngramSchema, EngramFileSchema, EngramMetaSchema, ConstructStatusSchema, ShellTypeSchema, } from "./engram.js";
1
+ export { EngramSchema, EngramFileSchema, EngramProfileSchema, EngramManifestSchema, EngramMetaSchema, ConstructStatusSchema, ShellTypeSchema, } from "./engram.js";
@@ -0,0 +1,26 @@
1
+ import type { Engram } from "../entities/engram.js";
2
+ import type { EngramRepository } from "../ports/engram-repository.js";
3
+ export declare class EngramAlreadyExistsError extends Error {
4
+ constructor(id: string);
5
+ }
6
+ export declare class InvalidEngramIdError extends Error {
7
+ constructor(id: string);
8
+ }
9
+ export interface CreateEngramParams {
10
+ id: string;
11
+ name: string;
12
+ description?: string;
13
+ tags?: string[];
14
+ soul: string;
15
+ identity: string;
16
+ }
17
+ export interface CreateEngramResult {
18
+ engram: Engram;
19
+ }
20
+ /** ID format: lowercase alphanumeric + hyphens, no leading/trailing hyphen */
21
+ export declare const ENGRAM_ID_PATTERN: RegExp;
22
+ export declare class CreateEngram {
23
+ private readonly repository;
24
+ constructor(repository: EngramRepository);
25
+ execute(params: CreateEngramParams): Promise<CreateEngramResult>;
26
+ }
@@ -0,0 +1,54 @@
1
+ // ============================================================
2
+ // Errors
3
+ // ============================================================
4
+ export class EngramAlreadyExistsError extends Error {
5
+ constructor(id) {
6
+ super(`Engram "${id}" already exists. Delete it first or choose a different ID.`);
7
+ this.name = "EngramAlreadyExistsError";
8
+ }
9
+ }
10
+ export class InvalidEngramIdError extends Error {
11
+ constructor(id) {
12
+ super(`Invalid Engram ID "${id}". Use lowercase letters, numbers, and hyphens only (e.g. "my-agent").`);
13
+ this.name = "InvalidEngramIdError";
14
+ }
15
+ }
16
+ // ============================================================
17
+ // Usecase
18
+ // ============================================================
19
+ /** ID format: lowercase alphanumeric + hyphens, no leading/trailing hyphen */
20
+ export const ENGRAM_ID_PATTERN = /^[a-z0-9]+(-[a-z0-9]+)*$/;
21
+ export class CreateEngram {
22
+ repository;
23
+ constructor(repository) {
24
+ this.repository = repository;
25
+ }
26
+ async execute(params) {
27
+ // Validate ID format
28
+ if (!ENGRAM_ID_PATTERN.test(params.id)) {
29
+ throw new InvalidEngramIdError(params.id);
30
+ }
31
+ // Check for duplicates
32
+ const existing = await this.repository.get(params.id);
33
+ if (existing) {
34
+ throw new EngramAlreadyExistsError(params.id);
35
+ }
36
+ const now = new Date().toISOString();
37
+ const engram = {
38
+ meta: {
39
+ id: params.id,
40
+ name: params.name,
41
+ description: params.description,
42
+ tags: params.tags,
43
+ createdAt: now,
44
+ updatedAt: now,
45
+ },
46
+ files: {
47
+ soul: params.soul,
48
+ identity: params.identity,
49
+ },
50
+ };
51
+ await this.repository.save(engram);
52
+ return { engram };
53
+ }
54
+ }
@@ -0,0 +1,24 @@
1
+ import type { Engram } from "../entities/engram.js";
2
+ import type { EngramRepository } from "../ports/engram-repository.js";
3
+ export declare class DeleteEngramNotFoundError extends Error {
4
+ constructor(id: string);
5
+ }
6
+ export interface DeleteEngramInfo {
7
+ engram: Engram;
8
+ hasMemory: boolean;
9
+ hasUser: boolean;
10
+ hasArchive: boolean;
11
+ memoryEntryCount: number;
12
+ }
13
+ export declare class DeleteEngram {
14
+ private readonly repository;
15
+ constructor(repository: EngramRepository);
16
+ /**
17
+ * Inspect an Engram before deletion — returns info for confirmation UI.
18
+ */
19
+ inspect(id: string): Promise<DeleteEngramInfo>;
20
+ /**
21
+ * Actually delete the Engram. Call inspect() first for confirmation.
22
+ */
23
+ execute(id: string): Promise<void>;
24
+ }
@@ -0,0 +1,46 @@
1
+ // ============================================================
2
+ // Errors
3
+ // ============================================================
4
+ export class DeleteEngramNotFoundError extends Error {
5
+ constructor(id) {
6
+ super(`Engram "${id}" not found.`);
7
+ this.name = "DeleteEngramNotFoundError";
8
+ }
9
+ }
10
+ // ============================================================
11
+ // Usecase
12
+ // ============================================================
13
+ export class DeleteEngram {
14
+ repository;
15
+ constructor(repository) {
16
+ this.repository = repository;
17
+ }
18
+ /**
19
+ * Inspect an Engram before deletion — returns info for confirmation UI.
20
+ */
21
+ async inspect(id) {
22
+ const engram = await this.repository.get(id);
23
+ if (!engram) {
24
+ throw new DeleteEngramNotFoundError(id);
25
+ }
26
+ return {
27
+ engram,
28
+ hasMemory: !!engram.files.memory,
29
+ hasUser: !!engram.files.user,
30
+ hasArchive: false, // archive.md is not loaded via EngramFiles; CLI checks separately
31
+ memoryEntryCount: engram.files.memoryEntries
32
+ ? Object.keys(engram.files.memoryEntries).length
33
+ : 0,
34
+ };
35
+ }
36
+ /**
37
+ * Actually delete the Engram. Call inspect() first for confirmation.
38
+ */
39
+ async execute(id) {
40
+ const existing = await this.repository.get(id);
41
+ if (!existing) {
42
+ throw new DeleteEngramNotFoundError(id);
43
+ }
44
+ await this.repository.delete(id);
45
+ }
46
+ }