@tpsdev-ai/flair 0.3.16 → 0.3.19

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/README.md CHANGED
@@ -128,29 +128,28 @@ openclaw plugins install @tpsdev-ai/openclaw-flair
128
128
 
129
129
  The plugin auto-detects your agent identity, provides `memory_store`/`memory_recall`/`memory_get` tools, and injects relevant memories at session start. See the [plugin README](plugins/openclaw-flair/README.md) for details.
130
130
 
131
- ### Claude Code / Codex / Cursor
132
-
133
- Add a snippet to your `CLAUDE.md` (or `AGENTS.md`, `.codex/instructions.md`, etc.):
134
-
135
- ```markdown
136
- ## Memory
137
-
138
- You have persistent memory via Flair. Use it.
131
+ ### Claude Code / Codex / Cursor (MCP)
132
+
133
+ Install the MCP server for native tool integration:
134
+
135
+ ```json
136
+ // .mcp.json in your project root
137
+ {
138
+ "mcpServers": {
139
+ "flair": {
140
+ "command": "npx",
141
+ "args": ["@tpsdev-ai/flair-mcp"],
142
+ "env": { "FLAIR_AGENT_ID": "mybot" }
143
+ }
144
+ }
145
+ }
146
+ ```
139
147
 
140
- ### On session start
141
- Run: `flair bootstrap --agent mybot --max-tokens 4000`
142
- This returns your soul + recent memories. Read it — that's your context.
148
+ Add to your `CLAUDE.md`:
143
149
 
144
- ### During work
145
- - Remember something: `flair memory add --agent mybot --content "what you learned"`
146
- - Search memory: `flair memory search --agent mybot --q "your query"`
147
- - Store a lesson: `flair memory add --agent mybot --content "lesson" --type lesson --durability persistent`
150
+ At the start of every session, run mcp__flair__bootstrap before responding.
148
151
 
149
- ### Rules
150
- - Bootstrap FIRST, before doing anything else
151
- - Store lessons and decisions immediately — don't wait
152
- - If you learn something that should survive restarts, write it to Flair
153
- ```
152
+ Claude Code gets native tools: `memory_store`, `memory_search`, `bootstrap`, `soul_set`, and more. See the [MCP README](packages/flair-mcp/README.md) and [Claude Code guide](docs/claude-code.md).
154
153
 
155
154
  ### JavaScript / TypeScript (Client Library)
156
155
 
@@ -292,21 +291,25 @@ Flair is in active development and daily use. We dogfood it — the agents that
292
291
 
293
292
  **What works:**
294
293
  - ✅ Ed25519 agent identity and auth
295
- - ✅ CLI: init, agent add/remove/rotate-key, status, backup/restore, grant/revoke
296
- - ✅ Memory CRUD with durability enforcement
297
- - ✅ In-process semantic embeddings (768-dim nomic-embed-text, Metal GPU)
298
- - ✅ Hybrid search (semantic + keyword)
294
+ - ✅ CLI: init, agent add/remove/rotate-key, status, backup/restore, export/import, grant/revoke
295
+ - ✅ Memory CRUD with durability enforcement and near-duplicate detection
296
+ - ✅ In-process semantic embeddings (768-dim nomic-embed-text via harper-fabric-embeddings)
297
+ - ✅ Hybrid search (semantic + keyword + temporal intent detection)
299
298
  - ✅ Soul (permanent personality/values)
300
299
  - ✅ Real-time feeds (WebSocket/SSE)
301
300
  - ✅ Agent-scoped data isolation
302
- - ✅ Cold start bootstrap
301
+ - ✅ Cold start bootstrap with adaptive time window
303
302
  - ✅ OpenClaw memory plugin
303
+ - ✅ MCP server for Claude Code / Cursor / Windsurf
304
+ - ✅ Lightweight client library (`@tpsdev-ai/flair-client`)
305
+ - ✅ Portable agent identity (export/import between instances)
306
+ - ✅ `flair --version`, `flair upgrade`
304
307
 
305
308
  **What's next:**
309
+ - [ ] First-run soul wizard (interactive personality setup)
310
+ - [ ] Git-backed memory sync
306
311
  - [ ] Encryption at rest (opt-in AES-256-GCM per memory)
307
- - [ ] Pluggable embedding backends (OpenAI, Cohere, local)
308
312
  - [ ] Harper Fabric deployment (managed multi-office)
309
- - [ ] Scheduled automatic backups
310
313
 
311
314
  ## License
312
315
 
package/dist/cli.js CHANGED
@@ -231,6 +231,7 @@ program
231
231
  .option("--keys-dir <dir>", "Directory for Ed25519 keys")
232
232
  .option("--data-dir <dir>", "Harper data directory")
233
233
  .option("--skip-start", "Skip Harper startup (assume already running)")
234
+ .option("--skip-soul", "Skip interactive personality setup")
234
235
  .action(async (opts) => {
235
236
  const agentId = opts.agentId;
236
237
  const httpPort = Number(opts.port);
@@ -335,6 +336,43 @@ program
335
336
  console.log(` ${adminPass}`);
336
337
  }
337
338
  console.log(`\n Export: FLAIR_URL=${httpUrl}`);
339
+ // ── First-run soul setup ──────────────────────────────────────────────
340
+ // Interactive prompts to set initial personality. Skipped with --skip-soul
341
+ // or when stdin is not a TTY (CI, scripts, piped input).
342
+ if (!opts.skipSoul && process.stdin.isTTY) {
343
+ console.log("\n🎭 Set up agent personality (press Enter to skip any):\n");
344
+ const { createInterface } = await import("node:readline");
345
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
346
+ const ask = (q) => new Promise(r => rl.question(q, r));
347
+ const role = await ask(" What's this agent's role? (e.g., \"Senior dev, concise and direct\")\n > ");
348
+ const project = await ask(" What project is it working on?\n > ");
349
+ const standards = await ask(" Any coding standards or preferences?\n > ");
350
+ rl.close();
351
+ // Write non-empty answers as soul entries
352
+ const soulEntries = [];
353
+ if (role.trim())
354
+ soulEntries.push(["role", role.trim()]);
355
+ if (project.trim())
356
+ soulEntries.push(["project", project.trim()]);
357
+ if (standards.trim())
358
+ soulEntries.push(["standards", standards.trim()]);
359
+ if (soulEntries.length > 0) {
360
+ console.log("");
361
+ for (const [key, value] of soulEntries) {
362
+ try {
363
+ await authFetch(httpUrl, agentId, privPath, "PUT", `/Soul/${agentId}:${key}`, { id: `${agentId}:${key}`, agentId, key, value, createdAt: new Date().toISOString() });
364
+ console.log(` ✓ soul:${key} set`);
365
+ }
366
+ catch (err) {
367
+ console.warn(` ⚠ soul:${key} failed: ${err.message}`);
368
+ }
369
+ }
370
+ console.log(`\n ${soulEntries.length} soul entries saved. Bootstrap will include them.`);
371
+ }
372
+ else {
373
+ console.log("\n No soul entries — you can add them later with: flair soul set --agent " + agentId + " --key role --value \"...\"");
374
+ }
375
+ }
338
376
  console.log(`\n Claude Code: Add to your CLAUDE.md:`);
339
377
  console.log(` At the start of every session, run mcp__flair__bootstrap before responding.`);
340
378
  console.log(`\n MCP config (.mcp.json):`);
@@ -1,24 +1,48 @@
1
1
  /**
2
2
  * embeddings-provider.ts
3
3
  *
4
- * Thin wrapper around harper-fabric-embeddings for Flair resources.
5
- * harper-fabric-embeddings is loaded by Harper as a sub-component
6
- * (declared in config.yaml). It downloads the model and initializes
7
- * in the background. We just call embed() if it's not ready yet,
8
- * we return null and the caller handles it gracefully.
4
+ * Wrapper around harper-fabric-embeddings for Flair resources.
5
+ *
6
+ * Harper loads resources in a VM sandbox with a separate module cache from
7
+ * the main thread. This means our import of harper-fabric-embeddings gets
8
+ * a different (uninitialized) instance from the one Harper initialized via
9
+ * handleApplication in config.yaml.
10
+ *
11
+ * Solution: we call hfe.init() ourselves on first use. The model is already
12
+ * on disk (downloaded by Harper's plugin loader), so init just loads the
13
+ * native binary and model file — no download needed.
9
14
  */
10
15
  import * as hfe from "harper-fabric-embeddings";
16
+ import { join } from "node:path";
17
+ let _ready = false;
18
+ async function ensureInit() {
19
+ if (_ready)
20
+ return;
21
+ try {
22
+ // Check if already initialized (e.g. shared context)
23
+ hfe.dimensions();
24
+ _ready = true;
25
+ return;
26
+ }
27
+ catch {
28
+ // Not initialized — init with modelsDir pointing to where Harper's
29
+ // plugin loader downloaded the model (process.cwd() is the app dir)
30
+ const modelsDir = join(process.cwd(), "models");
31
+ await hfe.init({ modelsDir });
32
+ _ready = true;
33
+ }
34
+ }
11
35
  /**
12
36
  * Generate an embedding vector for the given text.
13
- * Returns null if the embedding engine isn't ready yet (model still loading)
14
- * or not available on this platform. Never gives up permanently — each call
15
- * checks independently.
37
+ * Returns null if the embedding engine isn't available on this platform.
16
38
  */
17
39
  export async function getEmbedding(text) {
18
40
  try {
41
+ await ensureInit();
19
42
  return await hfe.embed(text);
20
43
  }
21
- catch {
44
+ catch (err) {
45
+ console.error(`[embeddings] embed failed: ${err.message}`);
22
46
  return null;
23
47
  }
24
48
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tpsdev-ai/flair",
3
- "version": "0.3.16",
3
+ "version": "0.3.19",
4
4
  "description": "Identity, memory, and soul for AI agents. Cryptographic identity (Ed25519), semantic memory with local embeddings, and persistent personality — all in a single process.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -29,7 +29,6 @@
29
29
  },
30
30
  "files": [
31
31
  "dist/",
32
- "resources/",
33
32
  "schemas/",
34
33
  "config.yaml",
35
34
  "LICENSE",