@holt-os/holt 0.8.2 → 0.8.4
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 +14 -1
- package/dist/cli.js +125 -43
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -10,10 +10,23 @@ Holt is an open-source, self-hosted personal agent OS. Clone it, pick your skill
|
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
With Homebrew (installs Node for you, so you do not need npm first):
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
brew install holt-os/tap/holt
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Or with npm, if you already have Node:
|
|
14
22
|
|
|
15
23
|
```bash
|
|
16
24
|
npm install -g @holt-os/holt
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Quickstart
|
|
28
|
+
|
|
29
|
+
```bash
|
|
17
30
|
cd ~/where-you-want-to-work
|
|
18
31
|
holt init # trust this folder, choose and install brains, sign in, set defaults
|
|
19
32
|
holt chat # start talking (or use your custom command, e.g. `ai`)
|
package/dist/cli.js
CHANGED
|
@@ -324,8 +324,29 @@ ${END}`;
|
|
|
324
324
|
return { ok: false, kind: "none", file, immediate: false, message: `Could not write ${file}: ${e.message}` };
|
|
325
325
|
}
|
|
326
326
|
}
|
|
327
|
+
function isValidAliasName(name) {
|
|
328
|
+
return /^[A-Za-z_][A-Za-z0-9_-]{0,31}$/.test(name);
|
|
329
|
+
}
|
|
327
330
|
function installAlias(name) {
|
|
331
|
+
if (!isValidAliasName(name)) {
|
|
332
|
+
return {
|
|
333
|
+
ok: false,
|
|
334
|
+
kind: "none",
|
|
335
|
+
file: "",
|
|
336
|
+
immediate: false,
|
|
337
|
+
message: `"${name}" is not a valid launch command. Use a single word: a letter or _ then letters, digits, _ or - (max 32).`
|
|
338
|
+
};
|
|
339
|
+
}
|
|
328
340
|
removeAlias();
|
|
341
|
+
if (process.platform !== "win32" && isInstalled(name)) {
|
|
342
|
+
return {
|
|
343
|
+
ok: false,
|
|
344
|
+
kind: "none",
|
|
345
|
+
file: "",
|
|
346
|
+
immediate: false,
|
|
347
|
+
message: `"${name}" is already a command on your PATH. Pick another word.`
|
|
348
|
+
};
|
|
349
|
+
}
|
|
329
350
|
if (process.platform !== "win32") {
|
|
330
351
|
const binDir = launcherBinDir();
|
|
331
352
|
if (binDir) {
|
|
@@ -480,9 +501,10 @@ function appendTurn(t) {
|
|
|
480
501
|
}
|
|
481
502
|
function clearMemory() {
|
|
482
503
|
if (existsSync4(memPath())) rmSync2(memPath());
|
|
504
|
+
if (existsSync4(factsMdPath())) rmSync2(factsMdPath());
|
|
483
505
|
}
|
|
484
506
|
function normalizeFact(s) {
|
|
485
|
-
return s.toLowerCase().replace(/\s+/g, " ").trim().replace(/[.,;:!?]+$/, "");
|
|
507
|
+
return (s || "").toLowerCase().replace(/\s+/g, " ").trim().replace(/[.,;:!?]+$/, "");
|
|
486
508
|
}
|
|
487
509
|
var FACTS_HEADER = "# Holt facts\n\nDistilled memories for this folder. Safe to edit by hand.\n";
|
|
488
510
|
async function saveFact(content, session) {
|
|
@@ -550,7 +572,7 @@ function cosine(a, b) {
|
|
|
550
572
|
}
|
|
551
573
|
function tokens(s) {
|
|
552
574
|
return new Set(
|
|
553
|
-
s.toLowerCase().split(/[^a-z0-9]+/).filter((w) => w.length > 2)
|
|
575
|
+
(s || "").toLowerCase().split(/[^a-z0-9]+/).filter((w) => w.length > 2)
|
|
554
576
|
);
|
|
555
577
|
}
|
|
556
578
|
function keywordScore(q, text) {
|
|
@@ -612,7 +634,7 @@ function loadTelegramConfig() {
|
|
|
612
634
|
if (!existsSync5(path)) return null;
|
|
613
635
|
try {
|
|
614
636
|
const raw = JSON.parse(readFileSync5(path, "utf8"));
|
|
615
|
-
if (typeof raw.token !== "string" || typeof raw.allowedChatId !== "number") return null;
|
|
637
|
+
if (typeof raw.token !== "string" || typeof raw.allowedChatId !== "number" || raw.allowedChatId === 0) return null;
|
|
616
638
|
return { token: raw.token, allowedChatId: raw.allowedChatId };
|
|
617
639
|
} catch {
|
|
618
640
|
return null;
|
|
@@ -674,11 +696,11 @@ async function sendMessage(text, chatId) {
|
|
|
674
696
|
}
|
|
675
697
|
return allOk;
|
|
676
698
|
}
|
|
677
|
-
async function getUpdates(offset) {
|
|
678
|
-
const
|
|
679
|
-
if (!
|
|
699
|
+
async function getUpdates(offset, tokenOverride) {
|
|
700
|
+
const token = tokenOverride ?? loadTelegramConfig()?.token;
|
|
701
|
+
if (!token) return [];
|
|
680
702
|
const timeoutSec = 30;
|
|
681
|
-
const url = buildGetUpdatesUrl(
|
|
703
|
+
const url = buildGetUpdatesUrl(token, offset, timeoutSec);
|
|
682
704
|
const ac = new AbortController();
|
|
683
705
|
const timer = setTimeout(() => ac.abort(), (timeoutSec + 10) * 1e3);
|
|
684
706
|
try {
|
|
@@ -1062,9 +1084,8 @@ async function setupTelegram(ask) {
|
|
|
1062
1084
|
const mode = (await ask("\n Auto-detect from a message you just sent? [Y/n] ") ?? "").trim().toLowerCase();
|
|
1063
1085
|
let allowedChatId = null;
|
|
1064
1086
|
if (mode === "" || mode === "y" || mode === "yes") {
|
|
1065
|
-
saveTelegramConfig({ token, allowedChatId: 0 });
|
|
1066
1087
|
console.log(c.dim("\n Checking for a recent message..."));
|
|
1067
|
-
const updates = await getUpdates(0);
|
|
1088
|
+
const updates = await getUpdates(0, token);
|
|
1068
1089
|
allowedChatId = latestChatId(updates);
|
|
1069
1090
|
if (allowedChatId === null) {
|
|
1070
1091
|
console.log(c.red(" Did not find a recent message."));
|
|
@@ -1586,7 +1607,7 @@ async function extractAndSaveFacts(active, cfg, history, session) {
|
|
|
1586
1607
|
|
|
1587
1608
|
// src/output.ts
|
|
1588
1609
|
import { writeFileSync as writeFileSync6 } from "fs";
|
|
1589
|
-
import { join as join7 } from "path";
|
|
1610
|
+
import { join as join7, isAbsolute } from "path";
|
|
1590
1611
|
function escapeHtml(s) {
|
|
1591
1612
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
1592
1613
|
}
|
|
@@ -1597,8 +1618,12 @@ function renderInline(text) {
|
|
|
1597
1618
|
return `\0${codes.length - 1}\0`;
|
|
1598
1619
|
});
|
|
1599
1620
|
s = escapeHtml(s);
|
|
1600
|
-
s = s.replace(/\[([^\]]+)\]\(([^)\s]
|
|
1601
|
-
const
|
|
1621
|
+
s = s.replace(/\[([^\]]+)\]\((\S*?(?:\([^)\s]*\)[^)\s]*)*)\)/g, (_m, label, url) => {
|
|
1622
|
+
const trimmed = url.trim();
|
|
1623
|
+
const scheme = /^([a-z][a-z0-9+.-]*):/i.exec(trimmed);
|
|
1624
|
+
const safeScheme = !scheme || /^(https?|mailto)$/i.test(scheme[1]);
|
|
1625
|
+
if (!safeScheme) return label;
|
|
1626
|
+
const safe = trimmed.replace(/"/g, "%22");
|
|
1602
1627
|
return `<a href="${safe}">${label}</a>`;
|
|
1603
1628
|
});
|
|
1604
1629
|
s = s.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>");
|
|
@@ -1718,6 +1743,9 @@ function saveReply(reply, format, name) {
|
|
|
1718
1743
|
const ext = format === "html" ? ".html" : ".md";
|
|
1719
1744
|
let base = (name ?? "").trim();
|
|
1720
1745
|
if (!base) base = `holt-reply-${timestamp()}`;
|
|
1746
|
+
if (isAbsolute(base) || /[\\/]/.test(base) || base === "." || base === "..") {
|
|
1747
|
+
throw new Error('name must be a plain file name in this folder (no "/", "\\", or "..").');
|
|
1748
|
+
}
|
|
1721
1749
|
if (!/\.[a-z0-9]+$/i.test(base)) base += ext;
|
|
1722
1750
|
const path = join7(process.cwd(), base);
|
|
1723
1751
|
writeFileSync6(path, renderReply(reply, format), "utf8");
|
|
@@ -1877,15 +1905,17 @@ async function chat() {
|
|
|
1877
1905
|
const cliEnabled = BRAIN_IDS.filter((id) => cfg.brains[id].enabled);
|
|
1878
1906
|
const apiIds = cfg.apiBrains.map((a) => a.id);
|
|
1879
1907
|
const all = [...cliEnabled, ...apiIds];
|
|
1880
|
-
|
|
1881
|
-
|
|
1908
|
+
const rawArg = parts[1] || "";
|
|
1909
|
+
const match = all.find((id) => id.toLowerCase() === rawArg.toLowerCase());
|
|
1910
|
+
if (rawArg && match) {
|
|
1911
|
+
const next = resolveActive2(cfg, match);
|
|
1882
1912
|
if (next) {
|
|
1883
1913
|
active = next;
|
|
1884
1914
|
const turns = Math.floor(history.length / 2);
|
|
1885
1915
|
console.log(c.green(` switched to ${active.label}. Context kept (${turns} turn${turns === 1 ? "" : "s"}).`));
|
|
1886
1916
|
}
|
|
1887
|
-
} else if (
|
|
1888
|
-
console.log(c.dim(` "${
|
|
1917
|
+
} else if (rawArg) {
|
|
1918
|
+
console.log(c.dim(` "${rawArg}" is not available. Available: ${all.join(", ") || "none"}`));
|
|
1889
1919
|
} else {
|
|
1890
1920
|
const cfgRef = cfg;
|
|
1891
1921
|
const activeId = active.id;
|
|
@@ -1958,9 +1988,16 @@ async function chat() {
|
|
|
1958
1988
|
async function login(which) {
|
|
1959
1989
|
const id = (which || "").toLowerCase();
|
|
1960
1990
|
if (!BRAIN_IDS.includes(id)) {
|
|
1961
|
-
|
|
1991
|
+
if (which) {
|
|
1992
|
+
console.error(`
|
|
1993
|
+
Unknown brain "${which}". Use one of: ${BRAIN_IDS.join(", ")}
|
|
1994
|
+
`);
|
|
1995
|
+
process.exitCode = 1;
|
|
1996
|
+
} else {
|
|
1997
|
+
console.log(c.dim(`
|
|
1962
1998
|
Usage: holt login <${BRAIN_IDS.join("|")}>
|
|
1963
1999
|
`));
|
|
2000
|
+
}
|
|
1964
2001
|
return;
|
|
1965
2002
|
}
|
|
1966
2003
|
const s = BRAIN_SETUP[id];
|
|
@@ -2046,6 +2083,14 @@ async function memoryCmd(sub, rest = []) {
|
|
|
2046
2083
|
close();
|
|
2047
2084
|
return;
|
|
2048
2085
|
}
|
|
2086
|
+
if (action && !["clear", "embed", "facts", "search"].includes(action)) {
|
|
2087
|
+
console.error(`
|
|
2088
|
+
Unknown memory subcommand: "${sub}". Use: search <query> | facts | embed | clear
|
|
2089
|
+
`);
|
|
2090
|
+
process.exitCode = 1;
|
|
2091
|
+
close();
|
|
2092
|
+
return;
|
|
2093
|
+
}
|
|
2049
2094
|
const s = memStats();
|
|
2050
2095
|
const embedOk = await embeddingsAvailable();
|
|
2051
2096
|
const sessions = new Set(loadTurns().map((t) => t.session)).size;
|
|
@@ -2170,9 +2215,10 @@ function cmdShow(name) {
|
|
|
2170
2215
|
}
|
|
2171
2216
|
const loaded = loadSkill(name);
|
|
2172
2217
|
if (!loaded) {
|
|
2173
|
-
console.
|
|
2218
|
+
console.error(c.dim(`
|
|
2174
2219
|
No skill named "${sanitizeName(name)}". Try "holt skill list".
|
|
2175
2220
|
`));
|
|
2221
|
+
process.exitCode = 1;
|
|
2176
2222
|
return;
|
|
2177
2223
|
}
|
|
2178
2224
|
const { skill, body } = loaded;
|
|
@@ -2192,9 +2238,10 @@ function cmdCreate(name, global) {
|
|
|
2192
2238
|
const scope = global ? "global" : "workspace";
|
|
2193
2239
|
const dir = join8(skillsRoot(scope), clean);
|
|
2194
2240
|
if (existsSync8(dir)) {
|
|
2195
|
-
console.
|
|
2241
|
+
console.error(c.red(`
|
|
2196
2242
|
A ${scope} skill named "${clean}" already exists.`));
|
|
2197
|
-
console.
|
|
2243
|
+
console.error(c.dim(" " + dir + "\n"));
|
|
2244
|
+
process.exitCode = 1;
|
|
2198
2245
|
return;
|
|
2199
2246
|
}
|
|
2200
2247
|
mkdirSync6(dir, { recursive: true });
|
|
@@ -2219,22 +2266,25 @@ function cmdAdd(source, global) {
|
|
|
2219
2266
|
Cloning ${source} ...`));
|
|
2220
2267
|
const res = spawnSync2("git", ["clone", "--depth", "1", source, tempDir], { stdio: "ignore" });
|
|
2221
2268
|
if (res.status !== 0) {
|
|
2222
|
-
console.
|
|
2269
|
+
console.error(c.red(" Clone failed. Check the URL and that git is installed.\n"));
|
|
2270
|
+
process.exitCode = 1;
|
|
2223
2271
|
return;
|
|
2224
2272
|
}
|
|
2225
2273
|
fetched = tempDir;
|
|
2226
2274
|
} else {
|
|
2227
2275
|
fetched = resolve(source);
|
|
2228
2276
|
if (!existsSync8(fetched)) {
|
|
2229
|
-
console.
|
|
2277
|
+
console.error(c.red(`
|
|
2230
2278
|
No such path: ${fetched}
|
|
2231
2279
|
`));
|
|
2280
|
+
process.exitCode = 1;
|
|
2232
2281
|
return;
|
|
2233
2282
|
}
|
|
2234
2283
|
}
|
|
2235
2284
|
const skillDir = findSkillDir(fetched);
|
|
2236
2285
|
if (!skillDir) {
|
|
2237
|
-
console.
|
|
2286
|
+
console.error(c.red("\n Could not find a SKILL.md at the source root or in a single subfolder.\n"));
|
|
2287
|
+
process.exitCode = 1;
|
|
2238
2288
|
return;
|
|
2239
2289
|
}
|
|
2240
2290
|
let skillName = "";
|
|
@@ -2246,14 +2296,16 @@ function cmdAdd(source, global) {
|
|
|
2246
2296
|
}
|
|
2247
2297
|
if (!skillName) skillName = sanitizeName(skillDir.split(/[\\/]/).pop() || "");
|
|
2248
2298
|
if (!skillName) {
|
|
2249
|
-
console.
|
|
2299
|
+
console.error(c.red("\n Could not determine a valid skill name.\n"));
|
|
2300
|
+
process.exitCode = 1;
|
|
2250
2301
|
return;
|
|
2251
2302
|
}
|
|
2252
2303
|
const dest = join8(skillsRoot(scope), skillName);
|
|
2253
2304
|
if (existsSync8(dest)) {
|
|
2254
|
-
console.
|
|
2305
|
+
console.error(c.red(`
|
|
2255
2306
|
A ${scope} skill named "${skillName}" already exists.`));
|
|
2256
|
-
console.
|
|
2307
|
+
console.error(c.dim(" " + dest + "\n"));
|
|
2308
|
+
process.exitCode = 1;
|
|
2257
2309
|
return;
|
|
2258
2310
|
}
|
|
2259
2311
|
mkdirSync6(skillsRoot(scope), { recursive: true });
|
|
@@ -2282,9 +2334,10 @@ async function cmdRemove(name, ask) {
|
|
|
2282
2334
|
const globalDir = join8(skillsRoot("global"), clean);
|
|
2283
2335
|
const target = existsSync8(wsDir) ? wsDir : existsSync8(globalDir) ? globalDir : "";
|
|
2284
2336
|
if (!target) {
|
|
2285
|
-
console.
|
|
2337
|
+
console.error(c.dim(`
|
|
2286
2338
|
No skill named "${clean}" found.
|
|
2287
2339
|
`));
|
|
2340
|
+
process.exitCode = 1;
|
|
2288
2341
|
return;
|
|
2289
2342
|
}
|
|
2290
2343
|
const scope = target === wsDir ? "workspace" : "global";
|
|
@@ -2330,6 +2383,7 @@ async function skillCmd(sub, rest = []) {
|
|
|
2330
2383
|
await cmdRemove(args[0], ask);
|
|
2331
2384
|
break;
|
|
2332
2385
|
default:
|
|
2386
|
+
if (action) process.exitCode = 1;
|
|
2333
2387
|
usage();
|
|
2334
2388
|
}
|
|
2335
2389
|
} finally {
|
|
@@ -2504,7 +2558,7 @@ function buildGraph(turns) {
|
|
|
2504
2558
|
return { nodes, edges };
|
|
2505
2559
|
}
|
|
2506
2560
|
function escapeForScript(json) {
|
|
2507
|
-
return json.replace(/<\/(script)/gi, "<\\/$1").replace(/<!--/g, "
|
|
2561
|
+
return json.replace(/<\/(script)/gi, "<\\/$1").replace(/<!--/g, "<\\u0021--");
|
|
2508
2562
|
}
|
|
2509
2563
|
function escapeHtml2(s) {
|
|
2510
2564
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
@@ -2617,7 +2671,14 @@ function renderGraphHtml(graph2, meta) {
|
|
|
2617
2671
|
<script>
|
|
2618
2672
|
(function () {
|
|
2619
2673
|
"use strict";
|
|
2620
|
-
var DATA
|
|
2674
|
+
var DATA;
|
|
2675
|
+
try {
|
|
2676
|
+
DATA = JSON.parse(document.getElementById("graph-data").textContent);
|
|
2677
|
+
} catch (e) {
|
|
2678
|
+
document.getElementById("empty").textContent = "Could not read the graph data.";
|
|
2679
|
+
document.getElementById("empty").style.display = "flex";
|
|
2680
|
+
return;
|
|
2681
|
+
}
|
|
2621
2682
|
var nodes = DATA.nodes || [];
|
|
2622
2683
|
var edges = DATA.edges || [];
|
|
2623
2684
|
|
|
@@ -3177,6 +3238,7 @@ function runMcpServer(opts) {
|
|
|
3177
3238
|
|
|
3178
3239
|
// src/mcp/tools.ts
|
|
3179
3240
|
var MCP_SESSION = "__mcp__";
|
|
3241
|
+
var MCP_RECALL_SESSION = "__mcp_recall__";
|
|
3180
3242
|
function holtTools() {
|
|
3181
3243
|
return [
|
|
3182
3244
|
{
|
|
@@ -3195,7 +3257,7 @@ function holtTools() {
|
|
|
3195
3257
|
const query = String(args.query || "").trim();
|
|
3196
3258
|
if (!query) return "Provide a query to recall.";
|
|
3197
3259
|
const k = Number(args.k) || 5;
|
|
3198
|
-
const hits = await recall(query,
|
|
3260
|
+
const hits = await recall(query, MCP_RECALL_SESSION, k);
|
|
3199
3261
|
if (hits.length === 0) return "No relevant memory found.";
|
|
3200
3262
|
const lines = hits.map(
|
|
3201
3263
|
(h) => `${h.score.toFixed(2)} (${h.turn.role}) ${h.turn.content.replace(/\s+/g, " ").trim()}`
|
|
@@ -3287,6 +3349,9 @@ function holtTools() {
|
|
|
3287
3349
|
];
|
|
3288
3350
|
}
|
|
3289
3351
|
|
|
3352
|
+
// src/version.ts
|
|
3353
|
+
var VERSION = "0.8.4";
|
|
3354
|
+
|
|
3290
3355
|
// src/commands/mcp.ts
|
|
3291
3356
|
function printSetup() {
|
|
3292
3357
|
const config = JSON.stringify(
|
|
@@ -3315,10 +3380,15 @@ async function mcp(sub, rest = []) {
|
|
|
3315
3380
|
printSetup();
|
|
3316
3381
|
return;
|
|
3317
3382
|
}
|
|
3383
|
+
if (sub !== void 0) {
|
|
3384
|
+
console.error(`Unknown mcp subcommand: "${sub}". Use "holt mcp" to serve, or "holt mcp setup".`);
|
|
3385
|
+
process.exitCode = 1;
|
|
3386
|
+
return;
|
|
3387
|
+
}
|
|
3318
3388
|
try {
|
|
3319
3389
|
if (!isTrusted()) trustDir();
|
|
3320
3390
|
console.error("Holt MCP: serving memory for " + workspace());
|
|
3321
|
-
await runMcpServer({ name: "holt", version:
|
|
3391
|
+
await runMcpServer({ name: "holt", version: VERSION, tools: holtTools() });
|
|
3322
3392
|
} catch (err2) {
|
|
3323
3393
|
console.error("Holt MCP failed: " + (err2 instanceof Error ? err2.message : String(err2)));
|
|
3324
3394
|
process.exitCode = 1;
|
|
@@ -3403,21 +3473,27 @@ async function run(args) {
|
|
|
3403
3473
|
process.stdout.write(ch);
|
|
3404
3474
|
}
|
|
3405
3475
|
});
|
|
3476
|
+
if (!result.ok) {
|
|
3477
|
+
if (streamed) process.stdout.write("\n");
|
|
3478
|
+
process.stderr.write(c.red(result.text) + "\n");
|
|
3479
|
+
process.exitCode = 1;
|
|
3480
|
+
return;
|
|
3481
|
+
}
|
|
3406
3482
|
if (!p.quiet && !streamed) {
|
|
3407
3483
|
process.stdout.write(result.text);
|
|
3408
3484
|
}
|
|
3409
3485
|
if (!p.quiet && (streamed || result.text) && !result.text.endsWith("\n")) {
|
|
3410
3486
|
process.stdout.write("\n");
|
|
3411
3487
|
}
|
|
3412
|
-
if (!result.ok) {
|
|
3413
|
-
process.stderr.write(c.red(result.text) + "\n");
|
|
3414
|
-
process.exitCode = 1;
|
|
3415
|
-
return;
|
|
3416
|
-
}
|
|
3417
3488
|
if (p.out) {
|
|
3418
3489
|
const target = resolve3(process.cwd(), p.out);
|
|
3419
|
-
|
|
3420
|
-
|
|
3490
|
+
try {
|
|
3491
|
+
writeFileSync9(target, result.text, "utf8");
|
|
3492
|
+
process.stderr.write(c.dim(`saved to ${target}`) + "\n");
|
|
3493
|
+
} catch (e) {
|
|
3494
|
+
process.stderr.write(c.red(`Could not write ${target}: ${e.message}`) + "\n");
|
|
3495
|
+
process.exitCode = 1;
|
|
3496
|
+
}
|
|
3421
3497
|
}
|
|
3422
3498
|
}
|
|
3423
3499
|
|
|
@@ -3619,12 +3695,14 @@ async function addCmd(rest) {
|
|
|
3619
3695
|
const { task, when, opts } = parseAdd(rest);
|
|
3620
3696
|
if (!task || !when) {
|
|
3621
3697
|
console.log(c.dim('\n Usage: holt schedule add "<task>" <HH:MM> [--name <n>] [--notify] [--brain <id>]\n'));
|
|
3698
|
+
process.exitCode = 1;
|
|
3622
3699
|
return;
|
|
3623
3700
|
}
|
|
3624
3701
|
try {
|
|
3625
3702
|
parseWhen(when);
|
|
3626
3703
|
} catch (e) {
|
|
3627
|
-
console.
|
|
3704
|
+
console.error("\n " + c.red(e.message) + "\n");
|
|
3705
|
+
process.exitCode = 1;
|
|
3628
3706
|
return;
|
|
3629
3707
|
}
|
|
3630
3708
|
const ws = workspace();
|
|
@@ -3689,6 +3767,7 @@ function removeCmd(rest) {
|
|
|
3689
3767
|
const id = rest[0];
|
|
3690
3768
|
if (!id) {
|
|
3691
3769
|
console.log(c.dim("\n Usage: holt schedule remove <id> (see ids with: holt schedule list)\n"));
|
|
3770
|
+
process.exitCode = 1;
|
|
3692
3771
|
return;
|
|
3693
3772
|
}
|
|
3694
3773
|
const before = loadJobs();
|
|
@@ -3697,9 +3776,12 @@ function removeCmd(rest) {
|
|
|
3697
3776
|
if (process.platform === "darwin") removeDarwin(id);
|
|
3698
3777
|
else if (process.platform === "linux") removeLinux(id);
|
|
3699
3778
|
if (existed) console.log("\n" + c.green(` Removed schedule ${id}.`) + "\n");
|
|
3700
|
-
else
|
|
3779
|
+
else {
|
|
3780
|
+
console.error(c.dim(`
|
|
3701
3781
|
No schedule with id ${id}. Nothing to remove.
|
|
3702
3782
|
`));
|
|
3783
|
+
process.exitCode = 1;
|
|
3784
|
+
}
|
|
3703
3785
|
}
|
|
3704
3786
|
function usage2() {
|
|
3705
3787
|
console.log("\n" + c.accent("holt schedule") + c.dim(" run a task automatically on a timer"));
|
|
@@ -3714,6 +3796,7 @@ async function schedule(sub, rest = []) {
|
|
|
3714
3796
|
return;
|
|
3715
3797
|
}
|
|
3716
3798
|
if (action !== "add" && action !== "remove" && action !== "rm") {
|
|
3799
|
+
if (action) process.exitCode = 1;
|
|
3717
3800
|
usage2();
|
|
3718
3801
|
return;
|
|
3719
3802
|
}
|
|
@@ -3768,7 +3851,6 @@ function readStdin() {
|
|
|
3768
3851
|
}
|
|
3769
3852
|
|
|
3770
3853
|
// src/cli.ts
|
|
3771
|
-
var VERSION = "0.8.2";
|
|
3772
3854
|
var BANNER = `
|
|
3773
3855
|
\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557
|
|
3774
3856
|
\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D
|
|
@@ -3857,9 +3939,9 @@ async function main() {
|
|
|
3857
3939
|
await notify(process.argv.slice(3));
|
|
3858
3940
|
break;
|
|
3859
3941
|
default:
|
|
3860
|
-
console.
|
|
3942
|
+
console.error(`
|
|
3861
3943
|
Unknown command: "${cmd}"`);
|
|
3862
|
-
console.
|
|
3944
|
+
console.error(` Run "holt help" for usage.
|
|
3863
3945
|
`);
|
|
3864
3946
|
process.exitCode = 1;
|
|
3865
3947
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@holt-os/holt",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.4",
|
|
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",
|
|
@@ -42,7 +42,8 @@
|
|
|
42
42
|
"dev": "tsx src/cli.ts",
|
|
43
43
|
"build": "tsup src/cli.ts --format esm --clean",
|
|
44
44
|
"start": "node dist/cli.js",
|
|
45
|
-
"typecheck": "tsc --noEmit"
|
|
45
|
+
"typecheck": "tsc --noEmit",
|
|
46
|
+
"release": "bash scripts/release.sh"
|
|
46
47
|
},
|
|
47
48
|
"devDependencies": {
|
|
48
49
|
"tsup": "^8.1.0",
|