@lorekit/cli 1.4.0 → 1.5.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.
package/README.md CHANGED
@@ -1,26 +1,33 @@
1
1
  # @lorekit/cli
2
2
 
3
- Install the **LoreKit shared-memory skill** into a project and run health
4
- checks against your LoreKit MCP server — a small, zero-dependency Node CLI.
3
+ > The fastest way to give your AI coding agent a memory that survives the session.
5
4
 
6
- LoreKit gives coding agents a shared, persistent memory: lessons one agent
7
- learns are stored centrally and read by every other agent, in every session,
8
- CI included. This CLI wires an agent up to it in two commands.
5
+ [![npm](https://img.shields.io/npm/v/@lorekit/cli.svg)](https://www.npmjs.com/package/@lorekit/cli)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](../../LICENSE)
9
7
 
10
- ## Install
8
+ [LoreKit](https://github.com/mthines/lorekit) gives coding agents a **shared,
9
+ persistent memory**: a lesson one agent learns — a migration gotcha, a
10
+ flaky-test fix, a costly wrong assumption — is stored once and recalled by every
11
+ other agent, in every session, on every machine, CI included.
11
12
 
12
- Install it globally recommended. You get one pinned version, the `lorekit`
13
- command stays on your `PATH`, and the plugin hooks (which call `lorekit hook`
14
- on every lifecycle event) fire instantly instead of paying an `npx` resolution
15
- cost each time:
13
+ This is the CLI that connects your agent to it. One command scaffolds
14
+ everything the memory skill, the MCP connection, and the lifecycle hooks and
15
+ it's a zero-dependency Node binary that also runs fully offline against local
16
+ markdown files if you never want to sign up for anything.
16
17
 
17
18
  ```bash
18
19
  npm install -g @lorekit/cli
19
- lorekit install
20
- lorekit doctor
20
+ lorekit install # wires up the skill, MCP server, and hooks
21
+ lorekit doctor # verify it's all green
21
22
  ```
22
23
 
23
- Upgrade later with `npm install -g @lorekit/cli@latest`.
24
+ ## Install
25
+
26
+ The quickstart above uses the **global install — recommended.** You get one
27
+ pinned version, the `lorekit` command stays on your `PATH`, and the plugin hooks
28
+ (which call `lorekit hook` on every lifecycle event) fire instantly instead of
29
+ paying an `npx` resolution cost each time. Upgrade later with
30
+ `npm install -g @lorekit/cli@latest`.
24
31
 
25
32
  Prefer not to install anything? Run it on demand with `npx` — same commands,
26
33
  always the latest version, fetched and cached per use:
package/bin/lorekit.mjs CHANGED
@@ -10,6 +10,7 @@ import { hook } from '../src/hook.mjs';
10
10
  import { migrate } from '../src/migrate.mjs';
11
11
  import { mcpServer } from '../src/mcp-server.mjs';
12
12
  import { traceCommand } from '../src/telemetry.mjs';
13
+ import { loadDotEnv } from '../src/dotenv.mjs';
13
14
 
14
15
  // Read the version from package.json so it always matches the published
15
16
  // package — release-please bumps package.json, and this tracks it for free.
@@ -76,6 +77,9 @@ ${c.bold('Environment')}
76
77
  LOREKIT_TELEMETRY / DO_NOT_TRACK set to 0/off (or DO_NOT_TRACK=1) to opt
77
78
  out of anonymous command-usage telemetry
78
79
 
80
+ A ${c.cyan('.env')} file in the current directory is loaded automatically. Real
81
+ environment variables take precedence, so the file is a fallback.
82
+
79
83
  ${c.bold('Examples')}
80
84
  npx @lorekit/cli install --endpoint https://ref.supabase.co/functions/v1/mcp --token lk_rw_xxx
81
85
  npx @lorekit/cli install --global # set up memory for every project (~/.claude)
@@ -214,6 +218,12 @@ const KNOWN_FLAGS = [
214
218
  const HUMAN_COMMANDS = new Set(['install', 'uninstall', 'doctor', 'migrate']);
215
219
 
216
220
  async function main() {
221
+ // Load a `.env` from the current directory (if any) before anything reads the
222
+ // environment — so telemetry config, tokens, and endpoints can come from a
223
+ // file. Best-effort and non-overriding: real env vars still win, a missing
224
+ // file is a silent no-op, and it never prints (safe for hook/mcp stdout).
225
+ loadDotEnv();
226
+
217
227
  const argv = process.argv.slice(2);
218
228
  const args = parseArgs(argv, {
219
229
  aliases: { d: 'dir', e: 'endpoint', t: 'token', y: 'yes', h: 'help', v: 'version' },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lorekit/cli",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "Install the LoreKit shared-memory skill and run health checks for the LoreKit MCP server.",
5
5
  "license": "MIT",
6
6
  "repository": {
package/src/dotenv.mjs ADDED
@@ -0,0 +1,106 @@
1
+ // LoreKit CLI — zero-dependency `.env` loader.
2
+ //
3
+ // The CLI is strictly zero-dependency (see package.json), and `engines.node`
4
+ // is `>=18`, so we can rely on neither the `dotenv` package nor Node's built-in
5
+ // `process.loadEnvFile()` (added in 20.6). This hand-rolled loader fills the gap
6
+ // with a small, faithful parser and a best-effort file read.
7
+ //
8
+ // Semantics (chosen to match the dominant `dotenv` mental model, NOT Node's
9
+ // `--env-file`):
10
+ // • A `.env` in the current working directory is loaded automatically if it
11
+ // exists. A missing file is a silent no-op — the default for real users.
12
+ // • Existing real environment variables WIN. A value already present in
13
+ // `process.env` (an explicit `export`, a CI var, an inline `FOO=bar cmd`)
14
+ // is never overwritten by the file. So a committed `.env` is a fallback,
15
+ // and a shell opt-out like `LOREKIT_TELEMETRY=0` can't be silently undone.
16
+ // • Loading never throws — a malformed line is skipped, an unreadable file is
17
+ // ignored. Nothing is printed, so machine-facing `hook` / `mcp` stdout is
18
+ // never touched.
19
+
20
+ import { readFileSync } from 'node:fs';
21
+ import path from 'node:path';
22
+ import process from 'node:process';
23
+
24
+ const KEY_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
25
+
26
+ /**
27
+ * Parse the contents of a `.env` file into a flat key→value object. Pure and
28
+ * unit-tested. Supports: blank lines, `#` comments, an optional `export `
29
+ * prefix, single/double-quoted values (double quotes unescape \n \r \t \\ \"),
30
+ * and trailing inline comments after unquoted values. Invalid keys are skipped.
31
+ */
32
+ export function parseDotEnv(text) {
33
+ const out = {};
34
+ if (!text) return out;
35
+ for (const rawLine of String(text).split(/\r?\n/)) {
36
+ const line = rawLine.trim();
37
+ if (!line || line.startsWith('#')) continue;
38
+
39
+ // Strip an optional `export ` prefix (a common shell-sourceable convention).
40
+ const withoutExport = line.startsWith('export ') ? line.slice(7).trimStart() : line;
41
+
42
+ const eq = withoutExport.indexOf('=');
43
+ if (eq <= 0) continue; // no key, or `=value` with empty key → skip
44
+
45
+ const key = withoutExport.slice(0, eq).trim();
46
+ if (!KEY_RE.test(key)) continue;
47
+
48
+ out[key] = parseValue(withoutExport.slice(eq + 1).trim());
49
+ }
50
+ return out;
51
+ }
52
+
53
+ /**
54
+ * Resolve one raw right-hand-side into its final string value: unwrap matching
55
+ * quotes (unescaping only inside double quotes), or, for a bare value, drop a
56
+ * trailing ` # comment`.
57
+ */
58
+ function parseValue(raw) {
59
+ if (!raw) return '';
60
+ const q = raw[0];
61
+ if ((q === '"' || q === "'") && raw.length >= 2 && raw[raw.length - 1] === q) {
62
+ const inner = raw.slice(1, -1);
63
+ return q === '"'
64
+ ? inner.replace(/\\([nrt"\\])/g, (_, ch) =>
65
+ ch === 'n' ? '\n' : ch === 'r' ? '\r' : ch === 't' ? '\t' : ch,
66
+ )
67
+ : inner;
68
+ }
69
+ // Unquoted: a whitespace-preceded `#` starts an inline comment.
70
+ const hash = raw.search(/\s#/);
71
+ return (hash === -1 ? raw : raw.slice(0, hash)).trim();
72
+ }
73
+
74
+ /**
75
+ * Load a `.env` from `cwd` into `env`, without overriding keys already set.
76
+ * Best-effort: returns the list of keys it applied (empty if the file is absent
77
+ * or unreadable). Never throws, never prints.
78
+ *
79
+ * @param {object} [opts]
80
+ * @param {string} [opts.cwd] directory to look for `.env` in (default cwd)
81
+ * @param {object} [opts.env] target env object to mutate (default process.env)
82
+ * @returns {string[]} keys newly set from the file
83
+ */
84
+ export function loadDotEnv({ cwd = process.cwd(), env = process.env } = {}) {
85
+ let text;
86
+ try {
87
+ text = readFileSync(path.join(cwd, '.env'), 'utf8');
88
+ } catch {
89
+ return []; // no file (ENOENT) or unreadable → nothing to do
90
+ }
91
+
92
+ const applied = [];
93
+ try {
94
+ const parsed = parseDotEnv(text);
95
+ for (const [key, value] of Object.entries(parsed)) {
96
+ // Real env wins: only fill keys that aren't already set.
97
+ if (env[key] === undefined) {
98
+ env[key] = value;
99
+ applied.push(key);
100
+ }
101
+ }
102
+ } catch {
103
+ // A malformed file must never break the CLI — leave whatever was applied.
104
+ }
105
+ return applied;
106
+ }
@@ -14,4 +14,4 @@
14
14
  // At runtime this is only the lowest-priority source: an explicit
15
15
  // LOREKIT_TELEMETRY_TOKEN env var or OTEL_EXPORTER_OTLP_HEADERS still win (see
16
16
  // resolveTelemetryConfig in telemetry.mjs).
17
- export const TELEMETRY_TOKEN = '';
17
+ export const TELEMETRY_TOKEN = "auth_XFLXatTBqqgvhZ12Tn9sfttClUkBPodp";
package/src/telemetry.mjs CHANGED
@@ -28,7 +28,7 @@ import { TELEMETRY_TOKEN } from './telemetry-token.mjs';
28
28
  // ── Baked-in defaults (public by design) ──────────────────────────────────────
29
29
  // The endpoint is a committed default; the token is injected at publish time
30
30
  // (empty in the source tree, so default export stays off until built/injected).
31
- const DEFAULT_ENDPOINT = 'https://ingress.us-east-1.aws.dash0.com';
31
+ const DEFAULT_ENDPOINT = 'https://ingress.europe-west4.gcp.dash0-dev.com';
32
32
  const DEFAULT_TOKEN = TELEMETRY_TOKEN; // injected from LOREKIT_TELEMETRY_TOKEN at publish
33
33
  const DEFAULT_DATASET = 'lorekit-cli';
34
34