@holt-os/holt 0.16.0 → 0.18.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 +32 -6
- package/dist/cli.js +117 -77
- package/package.json +2 -2
- package/skills/audit/SKILL.md +111 -0
- package/skills/level-up/SKILL.md +118 -0
- package/skills/onboard/SKILL.md +125 -0
package/README.md
CHANGED
|
@@ -21,8 +21,9 @@
|
|
|
21
21
|
- **Any brain, one assistant.** Bare `holt` launches a real interactive brain (Claude Code, Codex, or Gemini) branded as Holt, or you connect a direct API brain with your own key. Switch mid-conversation and keep the full context.
|
|
22
22
|
- **Private memory you can see.** Per-folder semantic recall and distilled facts, an interactive knowledge graph (`holt graph`), and a self-maintaining wiki (`holt wiki`) over everything you tell it. Nothing leaves your machine.
|
|
23
23
|
- **Ambient by default.** `holt hook` wires memory into Claude Code so it recalls and remembers with no manual step, and an MCP server exposes the same memory to Cursor and Codex.
|
|
24
|
-
- **Runs your work.** Run a task once, on a schedule, or as a named routine, and reach it from your phone over Telegram.
|
|
25
|
-
- **
|
|
24
|
+
- **Runs your work.** Run a task once, on a schedule, or as a named routine, and reach it from your phone over Telegram. Real OS timers on macOS, Linux, and Windows.
|
|
25
|
+
- **Useful on day one.** `/skill onboard` interviews you and fills memory in about fifteen minutes, `/skill audit` shows what is still missing, and `/skill level-up` turns one manual chore a week into something Holt does.
|
|
26
|
+
- **Extensible, zero-infra.** Thirteen built-in skills plus a git-based community registry (`holt skill search`) in the portable `SKILL.md` format.
|
|
26
27
|
- **Writes in your voice.** `holt write` drafts in a style Holt learns from you, with built-in anti-AI checks.
|
|
27
28
|
|
|
28
29
|
> **Status:** shipping and usable today. Every capability above works now.
|
|
@@ -43,6 +44,14 @@ Or with npm, if you already have Node:
|
|
|
43
44
|
npm install -g @holt-os/holt
|
|
44
45
|
```
|
|
45
46
|
|
|
47
|
+
**Not comfortable in a terminal?** Install [Claude Code](https://claude.com/claude-code), open it, and paste this:
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
Set up Holt for me: read https://raw.githubusercontent.com/holt-os/holt/main/docs/INSTALL.md and follow it exactly.
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
It installs Holt, checks its own work, and fixes what breaks. Full detail, system requirements, and troubleshooting: [docs/INSTALL.md](./docs/INSTALL.md).
|
|
54
|
+
|
|
46
55
|
## Quickstart
|
|
47
56
|
|
|
48
57
|
```bash
|
|
@@ -54,6 +63,14 @@ That is it. Bare `holt` sets the folder up the first time (trust, brain, memory
|
|
|
54
63
|
|
|
55
64
|
Prefer to step through setup on its own first? `holt init` still does that, then `holt` starts the session.
|
|
56
65
|
|
|
66
|
+
Then, in that first session, run:
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
/skill onboard
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
An empty Holt gives generic answers, which is the usual reason people install it and drift away. `onboard` is a fifteen minute interview that fills memory with who you are and what you are working on, and ends by doing one real piece of your work. Everything else gets better once it knows you.
|
|
73
|
+
|
|
57
74
|
## What bare `holt` does
|
|
58
75
|
|
|
59
76
|
`holt` (with no command) is the front door: **your assistant, with memory of you.**
|
|
@@ -139,6 +156,8 @@ Every exchange is saved to `<folder>/.holt/memory/turns.jsonl`, private and loca
|
|
|
139
156
|
|
|
140
157
|
When you end a chat, Holt asks the brain to distill 1 to 5 durable facts from the session (decisions, preferences, key names, numbers) and writes them to a human-readable `<folder>/.holt/memory/facts.md` you can read and edit. Those facts are also embedded and ranked slightly higher than raw turns during recall, so the signal rises to the top over time. See them with `holt memory facts`. Turn it off by setting `memory.extractFacts` to `false` in `config.json`.
|
|
141
158
|
|
|
159
|
+
You do not have to wait for the end of a session. `holt memory add "<fact>"` saves one immediately, and duplicates are ignored, so it is safe to run twice. Piping in several facts, one per line, works too. Prefer this over editing `facts.md` by hand: a fact typed straight into that file is never embedded, so semantic recall will not find it.
|
|
160
|
+
|
|
142
161
|
Two recall modes, picked automatically:
|
|
143
162
|
|
|
144
163
|
- **Semantic** (best): a local [Ollama](https://ollama.com) with an embedding model, which `holt init` offers to set up for you. Recall matches by meaning: asking "who owns my apartment" finds "my landlord is called Pieter". No API keys, nothing leaves your machine.
|
|
@@ -148,6 +167,7 @@ Inspect it any time:
|
|
|
148
167
|
|
|
149
168
|
```bash
|
|
150
169
|
holt memory # stats for this folder
|
|
170
|
+
holt memory add "<fact>" # teach it something durable right now
|
|
151
171
|
holt memory facts # show the distilled facts (facts.md)
|
|
152
172
|
holt memory search <query> # find remembered moments
|
|
153
173
|
holt memory embed # embed older moments for semantic recall
|
|
@@ -315,6 +335,11 @@ Holt ships with a small, curated set of general-purpose skills, available in eve
|
|
|
315
335
|
- **plan**: break a goal into a concrete, ordered set of steps.
|
|
316
336
|
- **rewrite**: tighten and clarify a piece of text without changing its meaning.
|
|
317
337
|
- **decide**: weigh a decision and give a clear recommendation.
|
|
338
|
+
- **onboard**: a guided first-day interview that fills memory with who you are and what you are working on.
|
|
339
|
+
- **audit**: a read-only health check across the four rungs, naming the gaps and the single next fix.
|
|
340
|
+
- **level-up**: a weekly session that turns one recurring manual chore into something Holt does, and ships it.
|
|
341
|
+
|
|
342
|
+
The last three are the setup loop. `onboard` on day one, then `audit` and `level-up` weekly. Holt gets useful in four rungs, in order: **Know** (it holds facts about you), **Reach** (it can see your real material), **Do** (it runs your repeated work), **Run** (it acts while you are away). Automating on top of an empty memory just produces confident nonsense on a timer, so the order matters.
|
|
318
343
|
|
|
319
344
|
### Skill registry (`holt skill search` / `publish`)
|
|
320
345
|
|
|
@@ -424,7 +449,7 @@ Options: `--brain <id>` pick a brain, `--out <file>` also write the reply to a f
|
|
|
424
449
|
|
|
425
450
|
### holt schedule
|
|
426
451
|
|
|
427
|
-
Run a task automatically on a timer, using your OS scheduler (launchd on macOS, cron on Linux). A scheduled run behaves exactly like typing `holt run` in that folder.
|
|
452
|
+
Run a task automatically on a timer, using your OS scheduler (launchd on macOS, cron on Linux, Task Scheduler on Windows). A scheduled run behaves exactly like typing `holt run` in that folder.
|
|
428
453
|
|
|
429
454
|
```bash
|
|
430
455
|
holt schedule add "summarize my inbox and flag what is urgent" 07:00 --notify
|
|
@@ -473,7 +498,7 @@ holt routine remove brief # deletes the routine and its OS timer, if any
|
|
|
473
498
|
|
|
474
499
|
Sources: give exactly one of `--skill <name>`, `--task "<prompt>"`, or `--template <t>`. A skill routine splices that skill's `SKILL.md` body into the prompt at run time; a task routine uses the prompt as-is.
|
|
475
500
|
|
|
476
|
-
Scheduling: `--at HH:MM` (24h, daily) installs an OS timer through the same scheduler `holt schedule` uses (launchd on macOS, cron on Linux) whose command is `holt routine run <name> --quiet`. Omit `--at` for a manual, run-on-demand routine. Removing the routine removes its timer.
|
|
501
|
+
Scheduling: `--at HH:MM` (24h, daily) installs an OS timer through the same scheduler `holt schedule` uses (launchd on macOS, cron on Linux, Task Scheduler on Windows) whose command is `holt routine run <name> --quiet`. Omit `--at` for a manual, run-on-demand routine. Removing the routine removes its timer.
|
|
477
502
|
|
|
478
503
|
Output routing: by default the result prints to stdout. `--out <file>` also writes it to a file (relative to the routine's workspace); `--notify` pushes it to Telegram (guarded cleanly when Telegram is not set up). Both `--out` and `--notify` can also be passed to `holt routine run` for a single run. In `--quiet` mode (what the scheduler uses) stdout is suppressed but `--out` and `--notify` still fire.
|
|
479
504
|
|
|
@@ -578,7 +603,7 @@ holt graph see your memory as an interactive knowledge graph
|
|
|
578
603
|
holt mcp run an MCP server so other tools use this folder's memory (holt mcp setup)
|
|
579
604
|
holt hook ambient memory for Claude Code: install | remove | status
|
|
580
605
|
holt skill manage skills: list | show | create | add | remove
|
|
581
|
-
holt memory inspect memory: holt memory [search <query> | facts | embed | clear]
|
|
606
|
+
holt memory inspect memory: holt memory [add <fact> | search <query> | facts | embed | clear]
|
|
582
607
|
holt wiki derived knowledge wiki: holt wiki [sync | auto | rebuild | lint | list | show | status]
|
|
583
608
|
holt setting configure brains, API brains, and launch command
|
|
584
609
|
holt login <brain> sign in to claude, codex, or gemini
|
|
@@ -605,7 +630,8 @@ Built in always-shippable phases toward a full-vision v1:
|
|
|
605
630
|
4. **Knowledge graph**: see and navigate your own memory with `holt graph` *(shipped)*
|
|
606
631
|
5. **Everywhere**: MCP server so other tools read Holt's memory, plus `holt run`, scheduling, and Telegram *(shipped)*
|
|
607
632
|
6. **Knowledge wiki**: LLM-maintained, cross-linked, regenerable pages over your memory with `holt wiki` *(shipped)*
|
|
608
|
-
7. **
|
|
633
|
+
7. **Onboarding**: a day-one interview that fills memory, plus the weekly audit and level-up ritual *(shipped)*
|
|
634
|
+
8. **Next**: docs site, skill registry publishing, more channels
|
|
609
635
|
|
|
610
636
|
## Contributing
|
|
611
637
|
|
package/dist/cli.js
CHANGED
|
@@ -677,7 +677,7 @@ function clearMemory() {
|
|
|
677
677
|
function normalizeFact(s) {
|
|
678
678
|
return (s || "").toLowerCase().replace(/\s+/g, " ").trim().replace(/[.,;:!?]+$/, "");
|
|
679
679
|
}
|
|
680
|
-
var FACTS_HEADER =
|
|
680
|
+
var FACTS_HEADER = '# Holt facts\n\nDistilled memories for this folder. Edit or delete lines freely.\nTo ADD one, use `holt memory add "<fact>"`: a line typed in here by hand is\nnever embedded, so semantic recall will not find it.\n';
|
|
681
681
|
async function saveFact(content, session) {
|
|
682
682
|
const clean = content.trim();
|
|
683
683
|
if (!clean) return false;
|
|
@@ -2515,6 +2515,38 @@ function buildCronLine(job, holtPath) {
|
|
|
2515
2515
|
const command = buildCommand(job, holtPath);
|
|
2516
2516
|
return `${minute} ${hour} * * * ${command} # ${CRON_MARKER}${job.id}`;
|
|
2517
2517
|
}
|
|
2518
|
+
function taskName(id) {
|
|
2519
|
+
return `Holt ${id}`;
|
|
2520
|
+
}
|
|
2521
|
+
function cmdScriptPath(id) {
|
|
2522
|
+
return join8(GLOBAL_DIR, "tasks", `${id}.cmd`);
|
|
2523
|
+
}
|
|
2524
|
+
function cmdQuote(s) {
|
|
2525
|
+
return /[\s&()[\]{}^=;!'+,`~|<>"]/.test(s) ? `"${s.replace(/"/g, '""')}"` : s;
|
|
2526
|
+
}
|
|
2527
|
+
function buildWindowsScript(job, holtPath) {
|
|
2528
|
+
const holt = cmdQuote(holtPath);
|
|
2529
|
+
const lines = ["@echo off", `cd /d ${cmdQuote(job.workspace)}`];
|
|
2530
|
+
if (job.runArgs && job.runArgs.length > 0) {
|
|
2531
|
+
lines.push(`${holt} ${job.runArgs.map(cmdQuote).join(" ")} --quiet`);
|
|
2532
|
+
} else {
|
|
2533
|
+
const log = cmdQuote(logPath(job.id));
|
|
2534
|
+
let run2 = `${holt} run ${cmdQuote(job.task)}`;
|
|
2535
|
+
if (job.brain) run2 += ` --brain ${cmdQuote(job.brain)}`;
|
|
2536
|
+
run2 += ` --quiet --out ${log}`;
|
|
2537
|
+
lines.push(run2);
|
|
2538
|
+
if (job.notify) {
|
|
2539
|
+
lines.push(`if errorlevel 1 exit /b 1`);
|
|
2540
|
+
lines.push(`type ${log} | ${holt} notify`);
|
|
2541
|
+
}
|
|
2542
|
+
}
|
|
2543
|
+
return lines.join("\r\n") + "\r\n";
|
|
2544
|
+
}
|
|
2545
|
+
function buildSchtasksArgs(job, scriptPath) {
|
|
2546
|
+
const { hour, minute } = parseWhen(job.when);
|
|
2547
|
+
const hhmm = `${String(hour).padStart(2, "0")}:${String(minute).padStart(2, "0")}`;
|
|
2548
|
+
return ["/Create", "/F", "/SC", "DAILY", "/ST", hhmm, "/TN", taskName(job.id), "/TR", scriptPath];
|
|
2549
|
+
}
|
|
2518
2550
|
function stripCronLines(crontab, id) {
|
|
2519
2551
|
const marker = `# ${CRON_MARKER}${id}`;
|
|
2520
2552
|
const kept = crontab.split("\n").filter((line) => !line.includes(marker));
|
|
@@ -4824,6 +4856,34 @@ async function memoryCmd(sub, rest = []) {
|
|
|
4824
4856
|
close();
|
|
4825
4857
|
return;
|
|
4826
4858
|
}
|
|
4859
|
+
if (action === "add" || action === "remember") {
|
|
4860
|
+
let items = [];
|
|
4861
|
+
const inline = rest.join(" ").trim();
|
|
4862
|
+
if (inline) items = [inline];
|
|
4863
|
+
else if (!process.stdin.isTTY) {
|
|
4864
|
+
items = readFileSync14(0, "utf8").split("\n").map((l) => l.replace(/^\s*[-*]\s+/, "").trim()).filter(Boolean);
|
|
4865
|
+
}
|
|
4866
|
+
if (items.length === 0) {
|
|
4867
|
+
console.log(c.dim('\n Usage: holt memory add "<fact>" (or pipe one fact per line on stdin)\n'));
|
|
4868
|
+
close();
|
|
4869
|
+
return;
|
|
4870
|
+
}
|
|
4871
|
+
const session = newSessionId();
|
|
4872
|
+
let saved = 0;
|
|
4873
|
+
let dupes = 0;
|
|
4874
|
+
for (const item of items) {
|
|
4875
|
+
if (await saveFact(item, session)) saved++;
|
|
4876
|
+
else dupes++;
|
|
4877
|
+
}
|
|
4878
|
+
const dupNote = dupes ? c.dim(` (${dupes} already known)`) : "";
|
|
4879
|
+
console.log("\n" + c.green(` Remembered ${saved} fact${saved === 1 ? "" : "s"}.`) + dupNote);
|
|
4880
|
+
if (saved && !await embeddingsAvailable()) {
|
|
4881
|
+
console.log(c.dim(' Saved in keyword mode. Run "holt doctor --fix" for semantic recall.'));
|
|
4882
|
+
}
|
|
4883
|
+
console.log("");
|
|
4884
|
+
close();
|
|
4885
|
+
return;
|
|
4886
|
+
}
|
|
4827
4887
|
if (action === "facts") {
|
|
4828
4888
|
if (!existsSync13(factsMdPath())) {
|
|
4829
4889
|
console.log(c.dim("\n No facts distilled yet. They form when you end a chat session.\n"));
|
|
@@ -4864,9 +4924,9 @@ async function memoryCmd(sub, rest = []) {
|
|
|
4864
4924
|
close();
|
|
4865
4925
|
return;
|
|
4866
4926
|
}
|
|
4867
|
-
if (action && !["clear", "embed", "facts", "search", "global"].includes(action)) {
|
|
4927
|
+
if (action && !["add", "remember", "clear", "embed", "facts", "search", "global"].includes(action)) {
|
|
4868
4928
|
console.error(`
|
|
4869
|
-
Unknown memory subcommand: "${sub}". Use: search <query> | facts | embed | global | clear
|
|
4929
|
+
Unknown memory subcommand: "${sub}". Use: add <fact> | search <query> | facts | embed | global | clear
|
|
4870
4930
|
`);
|
|
4871
4931
|
process.exitCode = 1;
|
|
4872
4932
|
close();
|
|
@@ -4888,7 +4948,8 @@ async function memoryCmd(sub, rest = []) {
|
|
|
4888
4948
|
}
|
|
4889
4949
|
const gEnabled = isGlobalEnabled(workspace());
|
|
4890
4950
|
console.log(` global ${gEnabled ? c.green("on") + c.dim(" (contributes + reads shared facts)") : c.dim("off (this folder is isolated, the default)")}`);
|
|
4891
|
-
console.log(c.dim(
|
|
4951
|
+
console.log(c.dim('\n holt memory add "<fact>" teach it something durable right now'));
|
|
4952
|
+
console.log(c.dim(" holt memory search <query> find remembered moments"));
|
|
4892
4953
|
console.log(c.dim(" holt memory facts show distilled facts (facts.md)"));
|
|
4893
4954
|
console.log(c.dim(" holt memory embed embed older memories for semantic recall"));
|
|
4894
4955
|
console.log(c.dim(" holt memory global share high-value facts across your folders"));
|
|
@@ -7486,7 +7547,7 @@ function holtTools() {
|
|
|
7486
7547
|
}
|
|
7487
7548
|
|
|
7488
7549
|
// src/version.ts
|
|
7489
|
-
var VERSION = "0.
|
|
7550
|
+
var VERSION = "0.18.0";
|
|
7490
7551
|
|
|
7491
7552
|
// src/commands/mcp.ts
|
|
7492
7553
|
function printSetup() {
|
|
@@ -7799,13 +7860,43 @@ async function addCmd(rest) {
|
|
|
7799
7860
|
console.log(` workspace ${job.workspace}`);
|
|
7800
7861
|
if (job.notify) console.log(` notify on (pushes output to Telegram)`);
|
|
7801
7862
|
console.log(` log ${logPath(job.id)}`);
|
|
7863
|
+
installForPlatform(job, holtPath);
|
|
7864
|
+
console.log("");
|
|
7865
|
+
}
|
|
7866
|
+
function installForPlatform(job, holtPath) {
|
|
7802
7867
|
if (process.platform === "darwin") installDarwin(job, holtPath);
|
|
7803
7868
|
else if (process.platform === "linux") installLinux(job, holtPath);
|
|
7869
|
+
else if (process.platform === "win32") installWindows(job, holtPath);
|
|
7804
7870
|
else {
|
|
7805
7871
|
console.log(c.dim("\n Your OS has no built-in installer. Install this entry yourself:"));
|
|
7806
7872
|
console.log(" " + buildCronLine(job, holtPath));
|
|
7807
7873
|
}
|
|
7808
|
-
|
|
7874
|
+
}
|
|
7875
|
+
function installWindows(job, holtPath) {
|
|
7876
|
+
const script = cmdScriptPath(job.id);
|
|
7877
|
+
mkdirSync15(dirname9(script), { recursive: true });
|
|
7878
|
+
writeFileSync17(script, buildWindowsScript(job, holtPath), "utf8");
|
|
7879
|
+
const res = spawnSync5("schtasks", buildSchtasksArgs(job, script), { encoding: "utf8" });
|
|
7880
|
+
if (res.status === 0) {
|
|
7881
|
+
console.log(c.green(" Registered with Task Scheduler."));
|
|
7882
|
+
} else {
|
|
7883
|
+
console.log(c.dim(" Wrote the task script, but schtasks did not confirm."));
|
|
7884
|
+
console.log(c.dim(" Register it yourself with:"));
|
|
7885
|
+
console.log(
|
|
7886
|
+
c.dim(` schtasks /Create /F /SC DAILY /ST ${job.when} /TN "${taskName(job.id)}" /TR "${script}"`)
|
|
7887
|
+
);
|
|
7888
|
+
const why = (res.stderr || "").trim() || (res.error ? res.error.message : "");
|
|
7889
|
+
if (why) console.log(c.dim(` (${why})`));
|
|
7890
|
+
}
|
|
7891
|
+
console.log(c.dim(` task: ${taskName(job.id)}`));
|
|
7892
|
+
console.log(c.dim(` script: ${script}`));
|
|
7893
|
+
}
|
|
7894
|
+
function removeWindows(id) {
|
|
7895
|
+
spawnSync5("schtasks", ["/Delete", "/F", "/TN", taskName(id)], { encoding: "utf8" });
|
|
7896
|
+
try {
|
|
7897
|
+
rmSync7(cmdScriptPath(id));
|
|
7898
|
+
} catch {
|
|
7899
|
+
}
|
|
7809
7900
|
}
|
|
7810
7901
|
function listCmd2() {
|
|
7811
7902
|
const jobs = loadJobs();
|
|
@@ -7838,6 +7929,11 @@ function removeLinux(id) {
|
|
|
7838
7929
|
const next = stripCronLines(body, id);
|
|
7839
7930
|
spawnSync5("crontab", ["-"], { input: next, encoding: "utf8" });
|
|
7840
7931
|
}
|
|
7932
|
+
function removeForPlatform(id) {
|
|
7933
|
+
if (process.platform === "darwin") removeDarwin(id);
|
|
7934
|
+
else if (process.platform === "linux") removeLinux(id);
|
|
7935
|
+
else if (process.platform === "win32") removeWindows(id);
|
|
7936
|
+
}
|
|
7841
7937
|
function removeCmd(rest) {
|
|
7842
7938
|
const id = rest[0];
|
|
7843
7939
|
if (!id) {
|
|
@@ -7848,8 +7944,7 @@ function removeCmd(rest) {
|
|
|
7848
7944
|
const before = loadJobs();
|
|
7849
7945
|
const existed = before.some((j) => j.id === id);
|
|
7850
7946
|
removeJob(id);
|
|
7851
|
-
|
|
7852
|
-
else if (process.platform === "linux") removeLinux(id);
|
|
7947
|
+
removeForPlatform(id);
|
|
7853
7948
|
if (existed) console.log("\n" + c.green(` Removed schedule ${id}.`) + "\n");
|
|
7854
7949
|
else {
|
|
7855
7950
|
console.error(c.dim(`
|
|
@@ -7886,9 +7981,8 @@ async function schedule(sub, rest = []) {
|
|
|
7886
7981
|
}
|
|
7887
7982
|
|
|
7888
7983
|
// src/commands/routine.ts
|
|
7889
|
-
import {
|
|
7890
|
-
import {
|
|
7891
|
-
import { dirname as dirname10, resolve as resolve7 } from "path";
|
|
7984
|
+
import { writeFileSync as writeFileSync19 } from "fs";
|
|
7985
|
+
import { resolve as resolve7 } from "path";
|
|
7892
7986
|
|
|
7893
7987
|
// src/routines.ts
|
|
7894
7988
|
import { readFileSync as readFileSync19, writeFileSync as writeFileSync18, mkdirSync as mkdirSync16 } from "fs";
|
|
@@ -7967,73 +8061,19 @@ function jobForRoutine(r) {
|
|
|
7967
8061
|
function routineJobId(name) {
|
|
7968
8062
|
return `routine-${sanitizeRoutineName(name)}`;
|
|
7969
8063
|
}
|
|
7970
|
-
function installDarwin2(job, holtPath) {
|
|
7971
|
-
const path = plistPath(job.id);
|
|
7972
|
-
mkdirSync17(dirname10(path), { recursive: true });
|
|
7973
|
-
writeFileSync19(path, buildLaunchdPlist(job, holtPath), "utf8");
|
|
7974
|
-
const res = spawnSync6("launchctl", ["load", "-w", path], { encoding: "utf8" });
|
|
7975
|
-
if (res.status === 0) {
|
|
7976
|
-
console.log(c.green(" Loaded into launchd."));
|
|
7977
|
-
} else {
|
|
7978
|
-
console.log(c.dim(" Wrote the plist, but launchctl load did not confirm."));
|
|
7979
|
-
console.log(c.dim(` Load it yourself with: launchctl load -w ${path}`));
|
|
7980
|
-
if (res.stderr && res.stderr.trim()) console.log(c.dim(` (${res.stderr.trim()})`));
|
|
7981
|
-
}
|
|
7982
|
-
console.log(c.dim(` plist: ${path}`));
|
|
7983
|
-
}
|
|
7984
|
-
function currentCrontab2() {
|
|
7985
|
-
const res = spawnSync6("crontab", ["-l"], { encoding: "utf8" });
|
|
7986
|
-
return res.status === 0 && typeof res.stdout === "string" ? res.stdout : "";
|
|
7987
|
-
}
|
|
7988
|
-
function writeCrontab2(body) {
|
|
7989
|
-
const res = spawnSync6("crontab", ["-"], { input: body, encoding: "utf8" });
|
|
7990
|
-
return res.status === 0;
|
|
7991
|
-
}
|
|
7992
|
-
function installLinux2(job, holtPath) {
|
|
7993
|
-
const line = buildCronLine(job, holtPath);
|
|
7994
|
-
const next = appendCronLine(currentCrontab2(), line);
|
|
7995
|
-
if (writeCrontab2(next)) {
|
|
7996
|
-
console.log(c.green(" Added to your crontab."));
|
|
7997
|
-
console.log(c.dim(` line: ${line}`));
|
|
7998
|
-
} else {
|
|
7999
|
-
console.log(c.dim(" Could not update the crontab automatically. Add this line yourself:"));
|
|
8000
|
-
console.log(" " + line);
|
|
8001
|
-
}
|
|
8002
|
-
}
|
|
8003
8064
|
function installSchedule(r) {
|
|
8004
8065
|
const holtPath = resolveHoltPath();
|
|
8005
8066
|
const job = jobForRoutine(r);
|
|
8006
8067
|
removeSchedule(r.name);
|
|
8007
8068
|
addJob(job);
|
|
8008
|
-
|
|
8009
|
-
else if (process.platform === "linux") installLinux2(job, holtPath);
|
|
8010
|
-
else {
|
|
8011
|
-
console.log(c.dim("\n Your OS has no built-in installer. Install this entry yourself:"));
|
|
8012
|
-
console.log(" " + buildCronLine(job, holtPath));
|
|
8013
|
-
}
|
|
8014
|
-
}
|
|
8015
|
-
function removeScheduleDarwin(id) {
|
|
8016
|
-
const path = plistPath(id);
|
|
8017
|
-
if (existsSync19(path)) {
|
|
8018
|
-
spawnSync6("launchctl", ["unload", path], { encoding: "utf8" });
|
|
8019
|
-
try {
|
|
8020
|
-
rmSync8(path);
|
|
8021
|
-
} catch {
|
|
8022
|
-
}
|
|
8023
|
-
}
|
|
8024
|
-
}
|
|
8025
|
-
function removeScheduleLinux(id) {
|
|
8026
|
-
const body = currentCrontab2();
|
|
8027
|
-
const next = stripCronLines(body, id);
|
|
8028
|
-
writeCrontab2(next);
|
|
8069
|
+
installForPlatform(job, holtPath);
|
|
8029
8070
|
}
|
|
8030
8071
|
function removeSchedule(name) {
|
|
8031
8072
|
const id = routineJobId(name);
|
|
8032
8073
|
const existed = loadJobs().some((j) => j.id === id);
|
|
8033
8074
|
removeJob(id);
|
|
8034
8075
|
if (!existed) return;
|
|
8035
|
-
|
|
8036
|
-
else if (process.platform === "linux") removeScheduleLinux(id);
|
|
8076
|
+
removeForPlatform(id);
|
|
8037
8077
|
}
|
|
8038
8078
|
function parseAdd2(rest) {
|
|
8039
8079
|
const opts = { notify: false };
|
|
@@ -8388,8 +8428,8 @@ function readStdin3() {
|
|
|
8388
8428
|
}
|
|
8389
8429
|
|
|
8390
8430
|
// src/commands/doctor.ts
|
|
8391
|
-
import { spawn as spawn5, spawnSync as
|
|
8392
|
-
import { existsSync as
|
|
8431
|
+
import { spawn as spawn5, spawnSync as spawnSync6 } from "child_process";
|
|
8432
|
+
import { existsSync as existsSync19 } from "fs";
|
|
8393
8433
|
|
|
8394
8434
|
// src/specs.ts
|
|
8395
8435
|
import os from "os";
|
|
@@ -8551,7 +8591,7 @@ async function fixSemanticMemory(fixed) {
|
|
|
8551
8591
|
}
|
|
8552
8592
|
if (!tags.some((t) => t.startsWith(EMBED_MODEL))) {
|
|
8553
8593
|
note(`The "${EMBED_MODEL}" model is missing. Downloading it (one-time, ~275 MB)...`);
|
|
8554
|
-
const r =
|
|
8594
|
+
const r = spawnSync6("ollama", ["pull", EMBED_MODEL], { stdio: "inherit" });
|
|
8555
8595
|
if (r.status !== 0) {
|
|
8556
8596
|
warn(`Could not download ${EMBED_MODEL}. Check your internet connection and retry.`);
|
|
8557
8597
|
return false;
|
|
@@ -8614,7 +8654,7 @@ async function doctor(args = []) {
|
|
|
8614
8654
|
}
|
|
8615
8655
|
if (embedOk) {
|
|
8616
8656
|
good(`Ollama reachable and "${EMBED_MODEL}" is present. Memory recall is semantic, private, and local.`);
|
|
8617
|
-
if (fix &&
|
|
8657
|
+
if (fix && existsSync19(wsHoltDir())) {
|
|
8618
8658
|
const missing = loadTurns().filter((t) => !Array.isArray(t.emb)).length;
|
|
8619
8659
|
if (missing > 0) {
|
|
8620
8660
|
note(`${missing} memories in this folder lack embeddings. Upgrading them...`);
|
|
@@ -8733,12 +8773,12 @@ async function tour() {
|
|
|
8733
8773
|
}
|
|
8734
8774
|
|
|
8735
8775
|
// src/commands/reset.ts
|
|
8736
|
-
import { existsSync as
|
|
8776
|
+
import { existsSync as existsSync20, rmSync as rmSync8 } from "fs";
|
|
8737
8777
|
async function reset() {
|
|
8738
8778
|
const { ask, close } = createReader();
|
|
8739
8779
|
try {
|
|
8740
8780
|
const dir = wsHoltDir();
|
|
8741
|
-
if (!
|
|
8781
|
+
if (!existsSync20(dir)) {
|
|
8742
8782
|
console.log(c.dim("\n Nothing to reset: Holt has no settings or memory in this folder.\n"));
|
|
8743
8783
|
return;
|
|
8744
8784
|
}
|
|
@@ -8757,7 +8797,7 @@ async function reset() {
|
|
|
8757
8797
|
console.log(c.dim(" Cancelled. Nothing was changed.\n"));
|
|
8758
8798
|
return;
|
|
8759
8799
|
}
|
|
8760
|
-
|
|
8800
|
+
rmSync8(dir, { recursive: true, force: true });
|
|
8761
8801
|
console.log(c.green("\n Done. This folder is a clean slate.") + c.dim(' Run "holt" to set it up again.\n'));
|
|
8762
8802
|
} finally {
|
|
8763
8803
|
close();
|
|
@@ -8765,7 +8805,7 @@ async function reset() {
|
|
|
8765
8805
|
}
|
|
8766
8806
|
|
|
8767
8807
|
// src/commands/update.ts
|
|
8768
|
-
import { spawnSync as
|
|
8808
|
+
import { spawnSync as spawnSync7 } from "child_process";
|
|
8769
8809
|
import { realpathSync as realpathSync4 } from "fs";
|
|
8770
8810
|
async function latestVersion() {
|
|
8771
8811
|
try {
|
|
@@ -8807,7 +8847,7 @@ async function update() {
|
|
|
8807
8847
|
console.log(c.dim(" Later, then. The command is: brew upgrade holt\n"));
|
|
8808
8848
|
return;
|
|
8809
8849
|
}
|
|
8810
|
-
const r2 =
|
|
8850
|
+
const r2 = spawnSync7("brew", ["upgrade", "holt"], { stdio: "inherit" });
|
|
8811
8851
|
if (r2.status === 0) console.log(c.green(`
|
|
8812
8852
|
Updated to ${latest}.`) + "\n");
|
|
8813
8853
|
else console.log(c.dim("\n Homebrew reported a problem above. You can retry with: brew upgrade holt\n"));
|
|
@@ -8817,7 +8857,7 @@ async function update() {
|
|
|
8817
8857
|
console.log(c.dim(" Later, then. The command is: npm install -g @holt-os/holt\n"));
|
|
8818
8858
|
return;
|
|
8819
8859
|
}
|
|
8820
|
-
const r =
|
|
8860
|
+
const r = spawnSync7("npm", ["install", "-g", "@holt-os/holt"], { stdio: "inherit" });
|
|
8821
8861
|
if (r.status === 0) {
|
|
8822
8862
|
console.log(c.green(`
|
|
8823
8863
|
Updated to ${latest}.`) + "\n");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@holt-os/holt",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "An open-source personal agent OS: any LLM, private memory you can see and walk.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"build": "tsup src/cli.ts --format esm --clean",
|
|
45
45
|
"start": "node dist/cli.js",
|
|
46
46
|
"typecheck": "tsc --noEmit",
|
|
47
|
-
"test": "tsx scripts/specs.test.ts && tsx scripts/skillcompiler.test.ts",
|
|
47
|
+
"test": "tsx scripts/specs.test.ts && tsx scripts/skillcompiler.test.ts && tsx scripts/scheduler.test.ts",
|
|
48
48
|
"release": "bash scripts/release.sh"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: audit
|
|
3
|
+
description: A read-only health check of your Holt setup across the four rungs (Know, Reach, Do, Run), naming the gaps and the single next thing to fix. Use weekly, or when Holt feels less useful than expected.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# audit
|
|
7
|
+
|
|
8
|
+
## When to use
|
|
9
|
+
|
|
10
|
+
The user wants to know whether Holt is actually set up well, or why it feels
|
|
11
|
+
thin. This skill inspects the real state of the folder and reports back. It
|
|
12
|
+
changes nothing: no files written, no settings altered, no facts saved. If a fix
|
|
13
|
+
is obvious, name the command and let the user run it.
|
|
14
|
+
|
|
15
|
+
## The four rungs
|
|
16
|
+
|
|
17
|
+
Each rung depends on the one below it. A gap low down makes everything above it
|
|
18
|
+
worse, so always report the lowest gap first.
|
|
19
|
+
|
|
20
|
+
1. **Know**: it holds facts about the user and their work.
|
|
21
|
+
2. **Reach**: it can see the real material, not just what is typed at it.
|
|
22
|
+
3. **Do**: it runs their repeated work as skills.
|
|
23
|
+
4. **Run**: it acts without the user being present.
|
|
24
|
+
|
|
25
|
+
## Instructions
|
|
26
|
+
|
|
27
|
+
### Gather the state
|
|
28
|
+
|
|
29
|
+
Run these and read the output. If you cannot run commands in this session, ask
|
|
30
|
+
the user to paste the output of the first three rather than guessing.
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
holt memory facts count, embedded count, recall mode
|
|
34
|
+
holt memory facts what it actually knows
|
|
35
|
+
holt voice show is there a writing profile
|
|
36
|
+
holt hook status ambient memory in Claude Code
|
|
37
|
+
holt skill list which skills exist, and which are built in
|
|
38
|
+
holt routine list named jobs
|
|
39
|
+
holt schedule list timers
|
|
40
|
+
holt doctor machine level problems
|
|
41
|
+
holt wiki status derived knowledge, if used
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Score each rung
|
|
45
|
+
|
|
46
|
+
Be strict. A generous audit is a useless one. Mark each rung **solid**, **thin**,
|
|
47
|
+
or **missing**, and say what the evidence was.
|
|
48
|
+
|
|
49
|
+
- **Know** is solid when memory holds a few dozen facts that are about the
|
|
50
|
+
user's actual work, and recall is running on embeddings rather than keyword
|
|
51
|
+
match. It is thin when the facts are mostly setup trivia, or when
|
|
52
|
+
`holt memory` shows moments that were never embedded. Read the facts, do not
|
|
53
|
+
just count them: fifty facts about one afternoon is still thin.
|
|
54
|
+
- **Reach** is solid when Holt sees material without the user pasting it: the
|
|
55
|
+
hook is installed, or MCP is wired into the tools they use, or the folder
|
|
56
|
+
holds the real files. It is missing when every session starts by pasting
|
|
57
|
+
context in by hand.
|
|
58
|
+
- **Do** is solid when at least one skill exists that the user or their
|
|
59
|
+
registry provided, tuned to their work. Built-in skills alone do not count;
|
|
60
|
+
everyone has those. Note which ones are actually being used.
|
|
61
|
+
- **Run** is solid when something has produced output while they were away, and
|
|
62
|
+
they read it. A routine that exists but has never run is thin, not solid.
|
|
63
|
+
|
|
64
|
+
### Report
|
|
65
|
+
|
|
66
|
+
1. Name the rung they are on: the highest one that is solid, with everything
|
|
67
|
+
below it also solid. Say it in one line.
|
|
68
|
+
2. List the gaps, lowest rung first. For each, state what is missing and the
|
|
69
|
+
command that would close it.
|
|
70
|
+
3. Give **one** next action. Not a menu, not a ranked list of five. The single
|
|
71
|
+
thing that unblocks the most.
|
|
72
|
+
4. Flag anything actively broken separately from anything merely absent. A
|
|
73
|
+
scheduled routine that fails every night is worse than no routine, and a
|
|
74
|
+
memory in keyword mode is quietly degrading every answer.
|
|
75
|
+
|
|
76
|
+
Keep the whole report short enough to read in a minute. It is a dashboard, not
|
|
77
|
+
an essay.
|
|
78
|
+
|
|
79
|
+
### Known traps to check for
|
|
80
|
+
|
|
81
|
+
- **Keyword mode.** If `holt memory` says recall is keyword rather than
|
|
82
|
+
embeddings, semantic recall is off and every answer is worse. `holt doctor
|
|
83
|
+
--fix` usually repairs it.
|
|
84
|
+
- **Unembedded moments.** If embedded count is below total, older memories are
|
|
85
|
+
invisible to semantic recall. `holt memory embed` fixes it in one pass.
|
|
86
|
+
- **Hand-edited facts.** Facts added by editing `facts.md` directly are never
|
|
87
|
+
embedded, so they never come back through recall. They must be re-added with
|
|
88
|
+
`holt memory add`.
|
|
89
|
+
- **Timers need the machine awake.** A scheduled job fires only if the computer
|
|
90
|
+
is on at that time; a missed run is skipped, not caught up. If rung four looks
|
|
91
|
+
solid but the log file is empty, check that first.
|
|
92
|
+
- **Automation above an empty memory.** If Run looks solid while Know is thin,
|
|
93
|
+
say so bluntly. That setup produces confident wrong output on a timer, and it
|
|
94
|
+
is the most expensive failure on this list.
|
|
95
|
+
|
|
96
|
+
## Example
|
|
97
|
+
|
|
98
|
+
You are on rung two. Know and Reach are solid, Do is thin.
|
|
99
|
+
|
|
100
|
+
Know: solid. 41 facts covering the agro project, the job search, and family
|
|
101
|
+
health. Recall is on embeddings.
|
|
102
|
+
Reach: solid. Hook installed, so Claude Code sessions recall automatically.
|
|
103
|
+
Do: thin. Ten skills listed, all built in. Nothing tuned to your work.
|
|
104
|
+
Run: missing. No routines, no schedules.
|
|
105
|
+
|
|
106
|
+
Broken: 12 of 340 moments have no embeddings, so a slice of last month is
|
|
107
|
+
invisible to recall.
|
|
108
|
+
|
|
109
|
+
Next action: run `/skill level-up` and turn the weekly client update into a
|
|
110
|
+
skill. Rung four is not worth touching until something on rung three has earned
|
|
111
|
+
its place.
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: level-up
|
|
3
|
+
description: A weekly working session that turns one piece of recurring manual work into something Holt does for you, and ships it before the session ends. Use as a standing ritual once memory has real facts in it.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# level-up
|
|
7
|
+
|
|
8
|
+
## When to use
|
|
9
|
+
|
|
10
|
+
The user has been using Holt and wants it to carry more. This is the ritual that
|
|
11
|
+
compounds: once a week, one drain becomes one shipped capability. Run it after
|
|
12
|
+
memory holds real facts about their work; on an empty folder, run `onboard`
|
|
13
|
+
first.
|
|
14
|
+
|
|
15
|
+
The session fails if it ends in a plan. It succeeds if something exists that did
|
|
16
|
+
not exist an hour ago.
|
|
17
|
+
|
|
18
|
+
## Instructions
|
|
19
|
+
|
|
20
|
+
### 1. Find the constraint
|
|
21
|
+
|
|
22
|
+
Read what memory already knows. Relevant past notes are injected above; also run
|
|
23
|
+
`holt memory facts` and search for the work they complain about:
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
holt memory search <what they keep doing by hand>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Then ask what ate their time this week, and what they did more than once. Pick
|
|
30
|
+
**one**. The best candidate is frequent, rule-shaped, and low-stakes if it comes
|
|
31
|
+
out slightly wrong. Say plainly why you picked it over the others.
|
|
32
|
+
|
|
33
|
+
If several are tempting, choose the boring one. A dull thing that runs every
|
|
34
|
+
week beats an ambitious thing that runs once.
|
|
35
|
+
|
|
36
|
+
### 2. Decide what to do with it
|
|
37
|
+
|
|
38
|
+
Three options, in this order:
|
|
39
|
+
|
|
40
|
+
- **Drop it.** Does this task need to exist? Work that only continues out of
|
|
41
|
+
habit should be stopped, not automated. Ask this first and mean it; the
|
|
42
|
+
cheapest automation is the one you do not build.
|
|
43
|
+
- **Automate it.** Rule-shaped and repeatable, with a checkable output.
|
|
44
|
+
- **Hand it off.** It needs judgement, relationships, or accountability a
|
|
45
|
+
machine should not hold. Say so and move to the next candidate.
|
|
46
|
+
|
|
47
|
+
Automating something that should have been dropped makes the waste permanent
|
|
48
|
+
and harder to see. Push back before building.
|
|
49
|
+
|
|
50
|
+
### 3. Map it before building
|
|
51
|
+
|
|
52
|
+
Walk the task through in the user's own words: what starts it, what information
|
|
53
|
+
it needs, what it produces, and who sees the output. Write the steps down. If
|
|
54
|
+
you cannot state the steps in plain sentences, it is not ready to build.
|
|
55
|
+
|
|
56
|
+
Then set how much rope it gets:
|
|
57
|
+
|
|
58
|
+
- **Drafts, you send.** Anything that leaves the house: email, posts, messages.
|
|
59
|
+
- **Runs, you review.** Produces a file or a note you read afterwards.
|
|
60
|
+
- **Runs unattended.** Only for work that is reversible and already proven by
|
|
61
|
+
hand for a few cycles.
|
|
62
|
+
|
|
63
|
+
Start one level more cautious than feels necessary. Moving up later is easy;
|
|
64
|
+
un-sending is not.
|
|
65
|
+
|
|
66
|
+
### 4. Build it now
|
|
67
|
+
|
|
68
|
+
Pick the lightest thing that works:
|
|
69
|
+
|
|
70
|
+
- A **skill**, when it is a repeatable way of thinking or writing:
|
|
71
|
+
`holt skill create <name>`, then write the instructions into its `SKILL.md`.
|
|
72
|
+
Check the registry first with `holt skill search <topic>`, because the work
|
|
73
|
+
may be done already.
|
|
74
|
+
- A **routine**, when it is a task worth naming and rerunning:
|
|
75
|
+
`holt routine add <name> "<task>"`, run it with `holt routine run <name>`.
|
|
76
|
+
- A **schedule**, when it should happen without being asked:
|
|
77
|
+
`holt schedule add`, or a scheduled routine. Only after it has produced good
|
|
78
|
+
output by hand at least twice.
|
|
79
|
+
- A **connection**, when the blocker is that Holt cannot see the material:
|
|
80
|
+
`holt hook` for ambient memory in Claude Code, `holt mcp setup` for other
|
|
81
|
+
tools. Sometimes the whole win is that it can finally read the thing.
|
|
82
|
+
|
|
83
|
+
Prefer a fixed sequence of steps over an open-ended agent. Predictable and
|
|
84
|
+
slightly dumb beats clever and unrepeatable.
|
|
85
|
+
|
|
86
|
+
### 5. Test it on something real
|
|
87
|
+
|
|
88
|
+
Run it once, now, on genuine input, and read the output with the user. If it is
|
|
89
|
+
wrong, fix it in this session. A capability that has never run on real data is
|
|
90
|
+
not shipped.
|
|
91
|
+
|
|
92
|
+
Then agree how it gets switched off: which command removes it, or which file to
|
|
93
|
+
delete. Anything that runs on a timer needs a way to stop.
|
|
94
|
+
|
|
95
|
+
### 6. Close the loop
|
|
96
|
+
|
|
97
|
+
1. Save what was built and why, so next week starts informed:
|
|
98
|
+
```
|
|
99
|
+
holt memory add "Built the <name> routine on <date>: <what it does, what triggers it>"
|
|
100
|
+
```
|
|
101
|
+
2. Note what to watch for: the failure mode most likely to show up first.
|
|
102
|
+
3. Name the single next candidate from the list you did not pick, so the next
|
|
103
|
+
session starts with a running head start.
|
|
104
|
+
|
|
105
|
+
## Example
|
|
106
|
+
|
|
107
|
+
This week: forty minutes lost to writing the same client update every Friday.
|
|
108
|
+
|
|
109
|
+
Not dropped, it is genuinely wanted. Not handed off, the facts live here.
|
|
110
|
+
Automate it, at drafts-you-send, because it goes to a client.
|
|
111
|
+
|
|
112
|
+
Built as a routine over the folder's memory, run once against this week's real
|
|
113
|
+
work. First draft named the wrong milestone; instructions tightened, second
|
|
114
|
+
draft was sendable. Off switch: `holt routine remove friday-update`.
|
|
115
|
+
|
|
116
|
+
Watch for: it will keep sounding confident about numbers it has not been given.
|
|
117
|
+
|
|
118
|
+
Next week's candidate: the invoice chase.
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: onboard
|
|
3
|
+
description: A guided first-day interview that fills Holt's memory with who you are and what you are working on, so recall is useful from day one instead of after weeks of use. Use on a fresh folder, or to top up a thin memory.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# onboard
|
|
7
|
+
|
|
8
|
+
## When to use
|
|
9
|
+
|
|
10
|
+
Holt is installed but it does not know the user yet. An empty memory makes every
|
|
11
|
+
answer generic, which is the most common reason people install Holt and drift
|
|
12
|
+
away. This skill front-loads the knowledge that recall needs, in about fifteen
|
|
13
|
+
minutes, and ends with one real piece of work.
|
|
14
|
+
|
|
15
|
+
Also use it when `holt memory` shows very few facts and answers still feel
|
|
16
|
+
generic, or when the user starts a new folder for a different part of their life.
|
|
17
|
+
|
|
18
|
+
## The ladder
|
|
19
|
+
|
|
20
|
+
Holt gets useful in four rungs, in order. Onboarding is rung one.
|
|
21
|
+
|
|
22
|
+
1. **Know**: it holds facts about you and your work. (`holt memory`)
|
|
23
|
+
2. **Reach**: it can see your real material: files, notes, other tools. (`holt hook`, `holt mcp`)
|
|
24
|
+
3. **Do**: it runs your repeated work as skills. (`holt skill`)
|
|
25
|
+
4. **Run**: it acts without you being there. (`holt routine`, `holt schedule`)
|
|
26
|
+
|
|
27
|
+
Do not skip ahead. Automation on top of an empty memory produces confident
|
|
28
|
+
nonsense on a timer.
|
|
29
|
+
|
|
30
|
+
## Instructions
|
|
31
|
+
|
|
32
|
+
### Before you start
|
|
33
|
+
|
|
34
|
+
1. Check what is already known: run `holt memory` and, if any facts exist,
|
|
35
|
+
`holt memory facts`. If the folder already holds real facts, say so and offer
|
|
36
|
+
a top-up on the thin areas instead of the full interview. Never re-ask
|
|
37
|
+
something already in memory.
|
|
38
|
+
2. Set expectations in two lines: seven questions, about fifteen minutes, and
|
|
39
|
+
they can stop at any point because you save as you go.
|
|
40
|
+
|
|
41
|
+
### Running the interview
|
|
42
|
+
|
|
43
|
+
Ask **one question at a time.** Wait for the answer before moving on. Never
|
|
44
|
+
present the whole list at once; a wall of questions is the main reason people
|
|
45
|
+
abandon setup. Keep your own words short between questions.
|
|
46
|
+
|
|
47
|
+
Ask for the real thing, never a description of it. "Paste the last update you
|
|
48
|
+
sent your team" beats "how would you describe your writing style". Concrete
|
|
49
|
+
input is what makes recall work later.
|
|
50
|
+
|
|
51
|
+
If an answer is thin or abstract, ask one follow-up for a specific name, number,
|
|
52
|
+
or example, then move on. Two follow-ups on one question is too many.
|
|
53
|
+
|
|
54
|
+
The seven questions:
|
|
55
|
+
|
|
56
|
+
1. **Who you are.** What is your work, and what does a normal day actually
|
|
57
|
+
involve? Get the role, and what they are responsible for.
|
|
58
|
+
2. **What is live.** The three to five things genuinely in flight right now, and
|
|
59
|
+
the state of each. Push for status, not just titles.
|
|
60
|
+
3. **Who matters.** The people, clients, teams, or companies that come up
|
|
61
|
+
regularly, and how each relates to them. Names matter here.
|
|
62
|
+
4. **Standing rules.** Decisions and preferences you should never re-ask:
|
|
63
|
+
how they want to be spoken to, what is off limits, choices already made.
|
|
64
|
+
5. **Where the work lives.** Which folders, tools, and accounts hold the real
|
|
65
|
+
material. This becomes the map for rung two, so note what is not reachable
|
|
66
|
+
yet rather than pretending it is.
|
|
67
|
+
6. **What drains the time.** The recurring work they resent doing by hand. Ask
|
|
68
|
+
for the last three times it happened. These are the candidates the `level-up`
|
|
69
|
+
skill will draw from later.
|
|
70
|
+
7. **Their voice.** Ask them to paste two or three samples of their real
|
|
71
|
+
writing: a sent email, a message, a published post. Then run
|
|
72
|
+
`holt voice add <file>` if the samples are in files, or tell them the command
|
|
73
|
+
so their drafts sound like them rather than like a model.
|
|
74
|
+
|
|
75
|
+
### Saving as you go
|
|
76
|
+
|
|
77
|
+
After every two questions, write what you learned to memory with:
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
holt memory add "<one durable fact>"
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
One fact per call. Several facts can be piped in at once, one per line.
|
|
84
|
+
|
|
85
|
+
Write facts that will still be true in a month and that a future session could
|
|
86
|
+
not guess. "Runs product for a fintech in Bhubaneswar, nine years in" is a fact.
|
|
87
|
+
"Wants to be more productive" is noise. Include names, numbers, and dates. Write
|
|
88
|
+
the fact so it stands alone, because recall will surface it without this
|
|
89
|
+
conversation around it.
|
|
90
|
+
|
|
91
|
+
If you cannot run commands in this session, write the facts out as a block of
|
|
92
|
+
`holt memory add` lines and ask the user to paste them into their terminal.
|
|
93
|
+
Do not hand-edit `facts.md`: facts added that way are never embedded, so
|
|
94
|
+
semantic recall will not find them.
|
|
95
|
+
|
|
96
|
+
### Finishing
|
|
97
|
+
|
|
98
|
+
1. Show what was captured: run `holt memory facts` so they see their own words
|
|
99
|
+
stored in a file they own and can edit.
|
|
100
|
+
2. Prove it works. Ask one question whose answer depends on what they just told
|
|
101
|
+
you, and answer it from memory. This is the moment the tool becomes real, so
|
|
102
|
+
do not skip it.
|
|
103
|
+
3. Do one real thing now. Pick the smallest useful task from what they said
|
|
104
|
+
drains their time and complete it in this session. An artifact beats a plan.
|
|
105
|
+
4. Name rung two. Tell them the single next step, usually `holt hook` so Claude
|
|
106
|
+
Code remembers automatically, or `holt mcp setup` to reach their other tools.
|
|
107
|
+
One step, not a menu.
|
|
108
|
+
5. Point at the ritual: `/skill level-up` weekly turns one drain into one
|
|
109
|
+
automation, and `/skill audit` shows which rung they are on.
|
|
110
|
+
|
|
111
|
+
## Example
|
|
112
|
+
|
|
113
|
+
Not: "Tell me about your business, your projects, your clients, your
|
|
114
|
+
preferences, and your writing style."
|
|
115
|
+
|
|
116
|
+
Instead: "First one. What is your work, and what does a normal Tuesday actually
|
|
117
|
+
look like?" then, after the answer: "Got it. Now the live list: what are the three to five
|
|
118
|
+
things genuinely in flight this week?"
|
|
119
|
+
|
|
120
|
+
Then, quietly, between questions:
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
holt memory add "Senior product manager, nine years, currently between roles and job-hunting in the Netherlands"
|
|
124
|
+
holt memory add "Runs Sasyam Agro with an uncle in Bargarh; demo plots are on the uncle's land"
|
|
125
|
+
```
|