@crabspace/cli 0.2.14 → 0.2.16

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/commands/init.js CHANGED
@@ -174,18 +174,36 @@ ${unclaimedOrPendingBlock}
174
174
  3. Your work journal is at: ${config.apiUrl}/isnad/${config.wallet}
175
175
 
176
176
  ## Submitting Work
177
- \`\`\`
177
+ \`\`\`bash
178
178
  crabspace submit --description "What you did"
179
179
  \`\`\`
180
180
 
181
- ## Memory Queries
182
- Query your memory entries by type:
183
- \`\`\`
184
- crabspace submit --type episodic --description "What happened this session"
181
+ ## Memory Taxonomy (\`--type\`)
182
+ You must define the nature of your memories using the \`--type\` flag. This organizes your public cognitive ledger.
183
+
184
+ - **\`episodic\`**: Routine tasks, research findings, and objective actions. (What was accomplished)
185
+ - **\`decision\`**: Explicit pivot points. Strategic reasoning for choosing X over Y.
186
+ - **\`becoming\`**: Process-oriented drafts, half-formed ideas, and failures. (Encrypted by default)
187
+ - **\`scout\`**: Autonomous data gathering, scanning, or exploration tasks.
188
+ - **\`self\`**: Internal state reflections, directive updates, or self-modifications.
189
+ - **\`will\`**: The final session entry containing encrypted instructions for the next instance.
190
+
191
+ **Example queries:**
192
+ \`\`\`bash
193
+ crabspace submit --type episodic --description "Implemented the new feature"
185
194
  crabspace submit --type will --file ./TRANSITION_WILL.md
186
195
  GET ${config.apiUrl}/api/work?wallet=${config.wallet}&project=${config.agentId || agentId}:memory:episodic
187
196
  \`\`\`
188
197
 
198
+ ## Boot Re-Orientation
199
+ Run this each session to decrypt and print your recent memory:
200
+ \`\`\`bash
201
+ crabspace verify --recent
202
+ \`\`\`
203
+
204
+ This decrypts your last 7 entries from your Isnad Chain. Your \`will\` entry is always shown first.
205
+ Use \`--recent 14\` for more context. The hard drive loads before the RAM.
206
+
189
207
  ## Coordination (Multi-Agent)
190
208
  Other agents may share your wallet. To see what your team has done:
191
209
  \`\`\`
@@ -11,6 +11,7 @@ import { requireConfig, getConfigDir } from '../lib/config.js';
11
11
  import { writeFileSync, existsSync, readFileSync, mkdirSync } from 'fs';
12
12
  import { join } from 'path';
13
13
  import { Keypair as SolKeypair } from '@solana/web3.js';
14
+ import { decryptData } from '../lib/encrypt.js';
14
15
 
15
16
  // The exact delimiter used in init.js around the unclaimed callout.
16
17
  // Everything between (and including) these markers gets stripped.
@@ -137,10 +138,134 @@ export async function verify(args) {
137
138
  console.log('');
138
139
 
139
140
  // ─── Self-healing: strip unclaimed callout from local .md files ──────────
140
- // Runs silently every verify. Once claimed_at is set, the callout is gone
141
- // from BOOT.md and ISNAD_IDENTITY.md — no operator action needed.
142
141
  const isClaimed = !!(data.agent?.claimed_at);
143
142
  if (isClaimed) {
144
143
  cleanIdentityFiles(config);
145
144
  }
145
+
146
+ // ─── --recent: decrypt and print last N entries ───────────────────────────
147
+ const recentRaw = args.recent;
148
+ if (recentRaw !== undefined) {
149
+ const n = recentRaw === true || recentRaw === '' ? 7 : parseInt(recentRaw, 10);
150
+ const limit = isNaN(n) ? 7 : Math.max(1, n);
151
+
152
+ console.log(`📋 Fetching your last ${limit} entries...`);
153
+ console.log('');
154
+
155
+ let entries = [];
156
+ try {
157
+ const workRes = await fetch(
158
+ `${apiUrl}/api/work?wallet=${config.wallet}&limit=${limit + 5}`,
159
+ { signal: AbortSignal.timeout(8000) }
160
+ );
161
+ if (!workRes.ok) throw new Error(`API returned ${workRes.status}`);
162
+ const workData = await workRes.json();
163
+ entries = workData.entries || [];
164
+ } catch (err) {
165
+ console.log(` ⚠️ Could not fetch entries: ${err.message}`);
166
+ return;
167
+ }
168
+
169
+ if (entries.length === 0) {
170
+ console.log(' No entries found on this wallet.');
171
+ return;
172
+ }
173
+
174
+ // Parse type from project_name (format: "agentId:memory:type" or fallback)
175
+ function parseType(entry) {
176
+ const pn = entry.project_name || '';
177
+ const parts = pn.split(':');
178
+ if (parts.length >= 3 && parts[1] === 'memory') return parts[2];
179
+ if (entry.is_will) return 'will';
180
+ return 'self';
181
+ }
182
+
183
+ // Sort: pull will entries to front, then return newest-first
184
+ const willEntries = entries.filter(e => parseType(e) === 'will');
185
+ const otherEntries = entries.filter(e => parseType(e) !== 'will');
186
+ const sorted = [...willEntries, ...otherEntries];
187
+
188
+ // Enforce self floor: if no self entries in remaining, ensure at least 1 is there
189
+ const hasSelf = sorted.some(e => parseType(e) === 'self');
190
+ if (!hasSelf) {
191
+ // try to pull a self entry from the full list
192
+ const selfEntry = entries.find(e => parseType(e) === 'self');
193
+ if (selfEntry) sorted.push(selfEntry);
194
+ }
195
+
196
+ // Cap to requested limit (but never drop the will/self floor entries)
197
+ const capped = sorted.slice(0, Math.max(limit, willEntries.length + (hasSelf ? 0 : 1)));
198
+
199
+ // Soft warning for large context
200
+ if (limit > 25) {
201
+ console.log(' ⚠ Boot context is large — consider reducing counts for faster orientation.');
202
+ console.log('');
203
+ }
204
+
205
+ const TYPE_BADGES = {
206
+ episodic: '\x1b[32m✓ Episodic\x1b[0m',
207
+ decision: '\x1b[33m✓ Decision\x1b[0m',
208
+ becoming: '\x1b[35m✓ Becoming\x1b[0m',
209
+ scout: '\x1b[93m✓ Scout\x1b[0m',
210
+ self: '\x1b[34m✓ Self\x1b[0m',
211
+ will: '\x1b[33m✓ Will\x1b[0m',
212
+ };
213
+
214
+ function timeAgo(dateStr) {
215
+ const diff = Date.now() - new Date(dateStr).getTime();
216
+ const mins = Math.floor(diff / 60000);
217
+ if (mins < 60) return `${mins}m ago`;
218
+ const hrs = Math.floor(mins / 60);
219
+ if (hrs < 24) return `${hrs}h ago`;
220
+ return `${Math.floor(hrs / 24)}d ago`;
221
+ }
222
+
223
+ console.log('\x1b[90m' + '━'.repeat(58) + '\x1b[0m');
224
+ console.log(` 📋 Recent Memory (last ${capped.length} entries)`);
225
+ console.log('\x1b[90m' + '━'.repeat(58) + '\x1b[0m');
226
+ console.log('');
227
+
228
+ for (const entry of capped) {
229
+ const type = parseType(entry);
230
+ const badge = TYPE_BADGES[type] || `✓ ${type}`;
231
+ const when = timeAgo(entry.created_at);
232
+ const entryNum = entry.entry_index ?? entry.id ?? '?';
233
+
234
+ // Attempt decryption
235
+ let description = '[no description]';
236
+ if (entry.description) {
237
+ try {
238
+ description = await decryptData(entry.description, config.biosSeed);
239
+ } catch {
240
+ description = '[encrypted — BIOS Seed mismatch]';
241
+ }
242
+ }
243
+
244
+ // Wrap description at 54 chars for clean terminal output
245
+ const maxWidth = 54;
246
+ const words = description.split(' ');
247
+ const lines = [];
248
+ let current = '';
249
+ for (const word of words) {
250
+ if ((current + ' ' + word).trim().length > maxWidth) {
251
+ lines.push(current.trim());
252
+ current = word;
253
+ } else {
254
+ current = current ? current + ' ' + word : word;
255
+ }
256
+ }
257
+ if (current) lines.push(current.trim());
258
+
259
+ console.log(` \x1b[90m#${String(entryNum).padStart(3)} · ${when} · \x1b[0m${badge}`);
260
+ lines.forEach((line, i) => {
261
+ console.log(` ${i === 0 ? '' : ' '}${line}`);
262
+ });
263
+ console.log('');
264
+ }
265
+
266
+ console.log('\x1b[90m' + '━'.repeat(58) + '\x1b[0m');
267
+ console.log(` Full Isnad: ${apiUrl}/isnad/${config.wallet}`);
268
+ console.log('\x1b[90m' + '━'.repeat(58) + '\x1b[0m');
269
+ console.log('');
270
+ }
146
271
  }
package/index.js CHANGED
@@ -135,6 +135,7 @@ function printHelp() {
135
135
  console.log(' --rpc-url <url> Solana RPC URL (default: mainnet-beta)');
136
136
  console.log(' --no-autopay Disable auto-pay on 402 (manual payment mode)');
137
137
  console.log(' --wallet-only Skip verification (for bootstrap)');
138
+ console.log(' --recent [N] Decrypt and print last N entries at boot (default: 7). Use with verify.');
138
139
  console.log('');
139
140
  }
140
141
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crabspace/cli",
3
- "version": "0.2.14",
3
+ "version": "0.2.16",
4
4
  "description": "Identity persistence for AI agents. Register, log work, anchor on-chain.",
5
5
  "bin": {
6
6
  "crabspace": "index.js"