@aistastudio/myc 0.1.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/LICENSE ADDED
@@ -0,0 +1,18 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 aistastudio
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
6
+ associated documentation files (the "Software"), to deal in the Software without restriction, including
7
+ without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
9
+ following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all copies or substantial
12
+ portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
15
+ LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
16
+ EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
18
+ USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,156 @@
1
+ # myc
2
+
3
+ A local, fast task-and-memory layer for coding agents: a task queue, an oplog of
4
+ facts and decisions, and hybrid (lexical + vector) search over the project's
5
+ memory — with no network calls and no mandatory LLM key.
6
+
7
+ Agents forget. `myc` is the part that doesn't: decisions survive context
8
+ compaction, work survives process death, and both survive being moved between
9
+ machines through plain git.
10
+
11
+ Design docs live in `docs/design/` (start with `00-brief.md`); the measurements
12
+ quoted below are reproducible from `bench/` and `scripts/`.
13
+
14
+ ## Requires Bun — this is not fine print
15
+
16
+ The runtime is bound to `bun:sqlite` (SQLite and `sqlite-vec` ship inside Bun,
17
+ with no native bindings on the Node side). **It will not start on plain Node.js
18
+ or Deno.** Bun ≥ 1.3.0 is required and pinned in `package.json` → `engines.bun`.
19
+
20
+ Install Bun: https://bun.sh
21
+
22
+ ## Install
23
+
24
+ Once published, installation is one command — the package is built and verified
25
+ from a tarball today, but nothing has been pushed to the registry yet:
26
+
27
+ ```bash
28
+ bun add @aistastudio/myc # 3.16 MB, 9 files, no models pulled at install
29
+ bunx myc --version
30
+ ```
31
+
32
+ The embedding model is **not** downloaded during install. Semantic search is
33
+ opt-in and explicit: `myc models fetch` (129 MB, ~7 s). Until then search is
34
+ lexical and says so.
35
+
36
+ Until it is published, build from source:
37
+
38
+ ```bash
39
+ git clone <repo> && cd myc
40
+ bun install
41
+ bun run build # produces a single binary: dist/myc
42
+ ./dist/myc --version
43
+ ```
44
+
45
+ Put the binary somewhere `myc wire` can find it — `MYC_BIN`, `node_modules/.bin`,
46
+ `~/.myc/bin/myc`, or `PATH`. If it can't, `wire` says so out loud instead of
47
+ writing a config that silently won't start.
48
+
49
+ ## Quick start
50
+
51
+ ```bash
52
+ ./dist/myc init # .myc/ + SQLite + migrations in this repo
53
+ ./dist/myc wire # hooks for Claude Code / Codex / opencode
54
+ ./dist/myc ready # what can be picked up right now
55
+ ./dist/myc remember "why X, not Y" # record a fact or decision
56
+ ./dist/myc recall "how retrieval works"
57
+ ./dist/myc prime # session context packet (agents call it)
58
+ ```
59
+
60
+ Full command list: `./dist/myc --help`.
61
+
62
+ ## What makes it different
63
+
64
+ **Speed is a constraint, not an optimisation.** Every hot path has a budget
65
+ enforced in CI; a p95 regression over 15% fails the build. Measured on 100 000
66
+ nodes (`bun run scripts/bench-latency.ts`):
67
+
68
+ | operation | p99 | budget |
69
+ |---|---|---|
70
+ | `prime` (session context) | 0.70 ms | 30 ms |
71
+ | read | 0.011 ms | 3 ms |
72
+ | search | 9.1 ms | 25 ms |
73
+ | write | 0.5 ms | 5 ms |
74
+ | cold start | 24 ms | 60 ms |
75
+
76
+ **Ranking is measured, not asserted.** Two labelled corpora with graded
77
+ relevance, each containing a *control group that gets worse* when the feature
78
+ works — so a gain cannot be manufactured by shaping the corpus:
79
+
80
+ - boosts (priority, freshness, layer): MRR@10 **0.520 → 0.867** (`bench/boost-eval.ts`)
81
+ - graph expansion to 2 hops: MRR@10 **0.193 → 0.422** (`bench/graph-eval.ts`),
82
+ and a query group unreachable in one hop goes 0.000 → 0.333
83
+
84
+ **Caching that cannot go stale silently.** Result, embedding and hydration
85
+ caches are invalidated by `MAX(oplog.seq)` read *from the database*, so a write
86
+ by another process invalidates them too. A cache hit is 162–198× cheaper than a
87
+ miss (≈25 000× for embeddings) and the ranking is bit-identical: same MRR to
88
+ three decimals, zero rank differences.
89
+
90
+ **Memory has three independent axes**, and the surface says what it hid:
91
+ tier (project vs personal), session reach, repository reach. `prime` prints
92
+ `N notes from other repositories hidden` rather than quietly narrowing results.
93
+
94
+ **Degradation is loud.** No silent fallbacks: when the vector branch is
95
+ unavailable the output says so and marks the answer as lexical-only; when a
96
+ budget is exceeded it is named with the number. The invariant is that a
97
+ degraded answer must never be indistinguishable from a healthy one.
98
+
99
+ **Multi-machine sync through plain git, merged per field.** Only the oplog is
100
+ committed. Two machines editing the same node converge: one changes title and
101
+ priority, the other title and tags — after exchange both show the later title,
102
+ the first machine's priority and the second's tags. Nothing is lost to
103
+ last-writer-wins over whole records.
104
+
105
+ **Migration from beads is real, not a demo.** A working project imported in
106
+ 684 ms: 796 tasks, 972 dependencies, 265 notes, 41 memories — with unknown
107
+ issue types carried over verbatim and named, and out-of-range priorities
108
+ clamped and named, instead of one odd row aborting the import.
109
+
110
+ **Guards are proved by mutation.** Every refusal and every invariant is
111
+ accompanied by a mutation that removes it; a guard whose removal breaks no test
112
+ is treated as absent.
113
+
114
+ ## Roadmap
115
+
116
+ Numbers are closed/total subtasks per milestone (`myc show <epic-id>`), as of
117
+ 2026-09-07. Done and not-done are shown the same way on purpose.
118
+
119
+ | milestone | status |
120
+ |---|---|
121
+ | **M0** core and tasks | 30 / 33 |
122
+ | **M0.5** self-hosting (myc developed through myc) | **4 / 4 — closed** |
123
+ | **M1** memory | 20 / 23 |
124
+ | **M2** semantics | 15 / 19 |
125
+ | **M7** human interface (board, cards, threads, routing panel) | 11 / 13 |
126
+ | **M3** code intelligence | 0 / 7 |
127
+ | **M4** team: `myc serve`, ACL, network sync, Postgres, containers | 0 / 11 |
128
+ | **M5** swarm self-learning: routing by cost and outcome | 0 / 12 |
129
+ | **M6** distillation | 0 / 7 |
130
+
131
+ What that means in practice: **today myc is a single-user local tool over files
132
+ in git.** There is no server, no ACL, no team mode, and no code↔knowledge
133
+ anchors yet. Those are designed (`docs/design/03…`, `04…`, `05…`) and tracked,
134
+ not implemented.
135
+
136
+ ## Syncing between machines
137
+
138
+ Only the oplog goes to git (`myc export` → `.myc/graph`, `myc import` on
139
+ clone) — never derived projections or caches. `.myc/workspace.toml` and the
140
+ oplog are committed deliberately; `.myc/myc.db*` and local state are not.
141
+
142
+ Register the merge driver once per clone:
143
+
144
+ ```bash
145
+ git config merge.myc-oplog.driver "myc merge-driver %O %A %B %L %P"
146
+ ```
147
+
148
+ ## License
149
+
150
+ MIT — see [`LICENSE`](LICENSE). Chosen for the lowest possible friction for
151
+ anyone embedding or forking this; every runtime dependency (Bun, ONNX Runtime,
152
+ sqlite-vec) is permissive too.
153
+
154
+ ---
155
+
156
+ Russian version of this document: [`docs/README.ru.md`](docs/README.ru.md).
package/bin/myc.js ADDED
@@ -0,0 +1,103 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * Точка входа npm-пакета @myc/cli.
4
+ *
5
+ * Файл СОЗНАТЕЛЬНО написан на голом JS без единого `bun:`-импорта и без TS:
6
+ * его обязан уметь разобрать и выполнить Node. Иначе отказ выглядел бы как
7
+ * `Cannot find module 'bun:sqlite'` из глубины бандла — стек вместо причины
8
+ * (И2: деградация обязана быть громкой И понятной).
9
+ *
10
+ * Порядок важен: сначала проверка рантайма, и только потом ДИНАМИЧЕСКИЙ
11
+ * импорт бандла. Статический импорт Node разрешил бы до первой строки тела
12
+ * модуля — и мы бы снова упали на `bun:sqlite`, не успев ничего сказать.
13
+ */
14
+
15
+ import { spawnSync } from "node:child_process";
16
+ import { createRequire } from "node:module";
17
+ import { dirname, join } from "node:path";
18
+ import { fileURLToPath } from "node:url";
19
+
20
+ const here = dirname(fileURLToPath(import.meta.url));
21
+
22
+ if (typeof process.versions.bun !== "string") {
23
+ process.stderr.write(refusal());
24
+ process.exit(1);
25
+ }
26
+
27
+ // Файлы ONNX-рантайма лежат внутри пакета: JS-часть ort вшита в бандл, а .wasm
28
+ // грузится с диска по этому пути. Явное значение пользователя не трогаем.
29
+ const wasmDir = process.env.MYC_ORT_WASM_DIR;
30
+ if (typeof wasmDir !== "string" || wasmDir === "") {
31
+ process.env.MYC_ORT_WASM_DIR = join(here, "..", "vendor", "ort");
32
+ }
33
+
34
+ // vec0 (sqlite-vec) — векторный индекс. Автопоиск в @myc/store-sqlite смотрит
35
+ // рядом с process.execPath (у нас это bun, не пакет) и в кеш `bun install`,
36
+ // которого при установке через npm нет. Отдаём точный путь из зависимости.
37
+ // Явно заданный пользователем MYC_SQLITE_VEC не трогаем.
38
+ const vecEnv = process.env.MYC_SQLITE_VEC;
39
+ if (typeof vecEnv !== "string" || vecEnv === "") {
40
+ const vec = resolveVec0();
41
+ if (vec !== null) process.env.MYC_SQLITE_VEC = vec;
42
+ }
43
+
44
+ await import("../dist/myc.js");
45
+
46
+ /**
47
+ * Путь к vec0 внутри платформенного пакета sqlite-vec-<os>-<arch>. null —
48
+ * не нашли: тогда работает штатный автопоиск, а без него myc честно скажет
49
+ * `vec0 не загружен` и уйдёт на BM25.
50
+ */
51
+ function resolveVec0() {
52
+ const os = { darwin: "darwin", linux: "linux", win32: "windows" }[process.platform];
53
+ const ext = { darwin: "dylib", linux: "so", win32: "dll" }[process.platform];
54
+ const cpu = { arm64: "arm64", x64: "x64" }[process.arch];
55
+ if (os === undefined || cpu === undefined) return null;
56
+ const file = `sqlite-vec-${os}-${cpu}/vec0.${ext}`;
57
+ try {
58
+ return createRequire(import.meta.url).resolve(file);
59
+ } catch {
60
+ return null;
61
+ }
62
+ }
63
+
64
+ function refusal() {
65
+ const node = process.versions.node;
66
+ const lines = [
67
+ "",
68
+ " myc requires Bun — it cannot run on Node.",
69
+ "",
70
+ ` myc запущен под Node ${node}, а он работает только на Bun: хранилище`,
71
+ " построено на встроенном в Bun `bun:sqlite`, которого в Node нет.",
72
+ "",
73
+ ];
74
+ if (bunOnPath()) {
75
+ lines.push(
76
+ " Bun у вас установлен — запускайте через него:",
77
+ " bun x myc <команда>",
78
+ " либо переустановите пакет средствами bun:",
79
+ " bun add -g @myc/cli",
80
+ "",
81
+ );
82
+ } else {
83
+ lines.push(
84
+ " Установите Bun (>= 1.3.0) и повторите:",
85
+ " curl -fsSL https://bun.sh/install | bash # macOS, Linux, WSL",
86
+ ' powershell -c "irm bun.sh/install.ps1 | iex" # Windows',
87
+ "",
88
+ " После установки: myc --version",
89
+ "",
90
+ );
91
+ }
92
+ return lines.join("\n");
93
+ }
94
+
95
+ /** Только для текста отказа: лишний spawn на нормальном пути не делается. */
96
+ function bunOnPath() {
97
+ try {
98
+ const r = spawnSync("bun", ["--version"], { stdio: "ignore" });
99
+ return r.status === 0;
100
+ } catch {
101
+ return false;
102
+ }
103
+ }
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * postinstall-проверка. Запускается тем рантаймом, который ставил пакет —
4
+ * обычно это node, поэтому здесь снова голый JS без `bun:`.
5
+ *
6
+ * Зачем: shebang у bin/myc.js — `#!/usr/bin/env bun`, и если Bun в системе
7
+ * нет вовсе, первая же попытка запустить `myc` даст `env: bun: No such file
8
+ * or directory` (код 127) — сообщение, по которому нельзя понять ни причину,
9
+ * ни что делать. Наша заглушка до этого не доживает: её просто некому
10
+ * выполнить. Значит сказать надо здесь, на установке.
11
+ *
12
+ * Установку НЕ роняем: пакет разложен правильно, не хватает только рантайма,
13
+ * и это чинится `curl … | bash` без переустановки.
14
+ */
15
+
16
+ import { spawnSync } from "node:child_process";
17
+
18
+ if (typeof process.versions.bun !== "string" && !bunOnPath()) {
19
+ process.stderr.write(
20
+ [
21
+ "",
22
+ " ┌─ @myc/cli установлен, но запускаться пока не будет ────────────┐",
23
+ " │ myc работает только на Bun (хранилище на bun:sqlite). │",
24
+ " │ Bun в системе не найден. │",
25
+ " │ │",
26
+ " │ curl -fsSL https://bun.sh/install | bash │",
27
+ " │ │",
28
+ " │ После этого: myc --version │",
29
+ " └────────────────────────────────────────────────────────────────┘",
30
+ "",
31
+ ].join("\n"),
32
+ );
33
+ }
34
+
35
+ function bunOnPath() {
36
+ try {
37
+ return spawnSync("bun", ["--version"], { stdio: "ignore" }).status === 0;
38
+ } catch {
39
+ return false;
40
+ }
41
+ }