File size: 9,097 Bytes
38be44d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
#!/usr/bin/env node

/**
 * @license
 * Copyright 2026 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * Seeds the auto-memory inbox with REALISTIC patches for manual end-to-end
 * testing of `/memory inbox`. Mirrors what one extraction-agent run would
 * produce in practice: a single canonical `extraction.patch` per kind,
 * containing multiple hunks (MEMORY.md update + sibling creation, etc.).
 *
 * Run AFTER `npm run build` from the project root:
 *   node scripts/seed-test-inbox.js
 *
 * The script will:
 *   1. Initialize Storage for the current working directory.
 *   2. Compute <projectMemoryDir> = ~/.gemini/tmp/<projectId>/memory/.
 *   3. Seed `MEMORY.md` and TWO canonical inbox patches:
 *        - .inbox/private/extraction.patch  (multi-hunk: update MEMORY.md
 *          + create verify-workflow.md + add MEMORY.md pointer to it)
 *        - .inbox/global/extraction.patch   (creates ~/.gemini/GEMINI.md)
 *   4. Print a verification checklist + the launch command.
 *
 * To clean up later, delete `<projectMemoryDir>/.inbox/` and the seeded
 * MEMORY.md / GEMINI.md files.
 */

import * as fs from 'node:fs/promises';
import * as path from 'node:path';
import * as os from 'node:os';
import { fileURLToPath } from 'node:url';

const SCRIPT_DIR = path.dirname(fileURLToPath(import.meta.url));
const REPO_ROOT = path.resolve(SCRIPT_DIR, '..');

const corePath = path.join(REPO_ROOT, 'packages/core/dist/src/index.js');
try {
  await fs.access(corePath);
} catch {
  console.error(
    `Cannot find built core at ${corePath}. Run \`npm run build\` first.`,
  );
  process.exit(1);
}

const { Storage } = await import(corePath);

const cwd = process.cwd();
const storage = new Storage(cwd);
await storage.initialize();

const memoryDir = storage.getProjectMemoryTempDir();
const inboxPrivate = path.join(memoryDir, '.inbox', 'private');
const inboxGlobal = path.join(memoryDir, '.inbox', 'global');
const homeDir = os.homedir();
const globalGeminiMd = path.join(homeDir, '.gemini', 'GEMINI.md');

console.log(`\nπŸ”§ Seeding inbox for cwd: ${cwd}`);
console.log(`   memoryDir = ${memoryDir}\n`);

await fs.mkdir(inboxPrivate, { recursive: true });
await fs.mkdir(inboxGlobal, { recursive: true });

const seeded = [];
async function seed(filePath, content, label) {
  await fs.mkdir(path.dirname(filePath), { recursive: true });
  await fs.writeFile(filePath, content, 'utf-8');
  seeded.push({ filePath, label });
}

// --- 1. Pre-existing private MEMORY.md so the update hunk has something to modify ---
const memoryMd = path.join(memoryDir, 'MEMORY.md');
await seed(
  memoryMd,
  '# Project Memory\n\n- old fact about this project\n',
  'pre-existing active MEMORY.md',
);

// --- 2. Canonical PRIVATE extraction.patch ---
//     One file, multi-hunk: update MEMORY.md AND create verify-workflow.md
//     AND add a pointer line for the sibling. This is what one extraction
//     agent run typically produces.
const verifyWorkflowMd = path.join(memoryDir, 'verify-workflow.md');
await fs.rm(verifyWorkflowMd, { force: true });
await seed(
  path.join(inboxPrivate, 'extraction.patch'),
  [
    // Hunk 1: replace the existing fact and append a sibling pointer.
    `--- ${memoryMd}`,
    `+++ ${memoryMd}`,
    `@@ -1,3 +1,4 @@`,
    ` # Project Memory`,
    ` `,
    `-- old fact about this project`,
    `+- new fact extracted from session analysis`,
    `+- See ${verifyWorkflowMd} for the project's verification commands.`,
    // Hunk 2: create the verify-workflow.md sibling.
    `--- /dev/null`,
    `+++ ${verifyWorkflowMd}`,
    `@@ -0,0 +1,5 @@`,
    `+# Verify Workflow`,
    `+`,
    `+- Run \`npm run typecheck\` after editing any *.ts file.`,
    `+- Run \`npm run build --workspace @google/gemini-cli-core\` before testing CLI changes.`,
    `+- Inbox patches are guarded by /memory inbox.`,
    ``,
  ].join('\n'),
  'canonical PRIVATE extraction.patch (2 hunks: MEMORY.md update + sibling create)',
);

// --- 3. Canonical GLOBAL extraction.patch ---
//     Creates ~/.gemini/GEMINI.md. Backs up any existing one first.
let existingGlobalGemini = null;
try {
  existingGlobalGemini = await fs.readFile(globalGeminiMd, 'utf-8');
} catch {
  // Doesn't exist yet β€” fine.
}
if (existingGlobalGemini !== null) {
  const backupPath = `${globalGeminiMd}.seed-test-backup-${Date.now()}`;
  await fs.copyFile(globalGeminiMd, backupPath);
  console.log(
    `   ℹ️  Backed up existing ${globalGeminiMd} β†’ ${backupPath}\n` +
      `       (restore manually after testing if you wish.)\n`,
  );
  await fs.rm(globalGeminiMd, { force: true });
}
await seed(
  path.join(inboxGlobal, 'extraction.patch'),
  [
    `--- /dev/null`,
    `+++ ${globalGeminiMd}`,
    `@@ -0,0 +1,3 @@`,
    `+# Global Personal Preferences`,
    `+`,
    `+- Prefer concise architecture summaries.`,
    ``,
  ].join('\n'),
  'canonical GLOBAL extraction.patch (creates ~/.gemini/GEMINI.md)',
);

// --- Summary ---
console.log('Seeded files:');
for (const { filePath, label } of seeded) {
  console.log(`   βœ“ ${path.relative(cwd, filePath)}`);
  console.log(`     ${label}\n`);
}

console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
console.log('NEXT STEPS');
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
console.log(`
1. Enable autoMemory in your settings (the inbox command requires it):

     ~/.gemini/settings.json should contain:
     {
       "experimental": { "autoMemory": true }
     }

   Or run this to set it:
     node -e "const fs=require('fs'),p=require('os').homedir()+'/.gemini/settings.json';let s={};try{s=JSON.parse(fs.readFileSync(p,'utf-8'))}catch{}s.experimental=s.experimental||{};s.experimental.autoMemory=true;fs.mkdirSync(require('path').dirname(p),{recursive:true});fs.writeFileSync(p,JSON.stringify(s,null,2))"

2. Launch the just-built CLI from THIS REPO ONLY. Do NOT use any globally
   installed "gemini" binary β€” it will be a stale build that doesn't know
   about memory patches and will silently show only skills.

     npm run start

   (or, equivalently: node ${path.relative(cwd, REPO_ROOT)}/bundle/gemini.js)

   Sanity check before launching:
     node ${path.relative(cwd, path.join(REPO_ROOT, 'scripts/check-inbox.js'))}
   should report 2 memory patches (Private memory + Global memory).

3. In the CLI, run:

     /memory inbox

   You should see exactly 2 entries in the "Memory Updates" group:
     - Private memory     2 hunks from 1 source patch
     - Global memory      1 hunk from 1 source patch

4. Test focus preservation: arrow-down to "Global memory" β†’ Enter β†’ Esc β†’
   cursor MUST still be on "Global memory" (not row 0).

5. Open "Private memory" preview. You'll see TWO target sections (no
   duplicates), since both hunks come from one source patch:

     ${memoryMd}
       - new fact extracted from session analysis
       - See ${verifyWorkflowMd} for the project's verification commands.

     ${verifyWorkflowMd} (new file)
       # Verify Workflow
       ...

6. Apply each entry:

   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
   β”‚ Item             β”‚ Action   β”‚ Expected outcome                      β”‚
   β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
   β”‚ Private memory   β”‚ Apply    β”‚ "Applied all 1 private memory patch." β”‚
   β”‚                  β”‚          β”‚ MEMORY.md updated; verify-workflow.md β”‚
   β”‚                  β”‚          β”‚ created.                              β”‚
   β”‚ Global memory    β”‚ Apply    β”‚ "Applied all 1 global memory patch."  β”‚
   β”‚                  β”‚          β”‚ ~/.gemini/GEMINI.md created.          β”‚
   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

7. Verify final state on disk:

     cat ${path.relative(cwd, memoryMd)}                    # should show new fact + pointer line
     cat ${path.relative(cwd, verifyWorkflowMd)}            # should exist
     cat ${globalGeminiMd}                                  # should show "Prefer concise..."
     ls ${path.relative(cwd, inboxPrivate)}                 # should be empty
     ls ${path.relative(cwd, inboxGlobal)}                  # should be empty

8. Cleanup:

     rm -rf ${path.relative(cwd, path.join(memoryDir, '.inbox'))}
     rm -f  ${path.relative(cwd, memoryMd)}
     rm -f  ${path.relative(cwd, verifyWorkflowMd)}
     rm -f  ${globalGeminiMd}
`);