meetsoma 0.3.8 → 0.6.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.
Files changed (2) hide show
  1. package/dist/thin-cli.js +261 -21
  2. package/package.json +46 -42
package/dist/thin-cli.js CHANGED
@@ -213,6 +213,7 @@ import { join as join3, dirname as dirname3 } from "path";
213
213
  import { homedir as homedir3, platform as platform2 } from "os";
214
214
  import { fileURLToPath } from "url";
215
215
  import { execSync as execSync2, execFileSync } from "child_process";
216
+ import { randomBytes, createHash } from "crypto";
216
217
 
217
218
  // personality.js
218
219
  var _state = {
@@ -1222,7 +1223,7 @@ function showHelp() {
1222
1223
  console.log("");
1223
1224
  console.log(` ${bold("Maintenance")}`);
1224
1225
  console.log(` ${green("soma doctor")} Verify installation + project health`);
1225
- console.log(` ${green("soma update")} Update the Soma runtime`);
1226
+ console.log(` ${green("soma update")} Update the Soma runtime ${dim("(--yes / -y to skip prompt)")}`);
1226
1227
  console.log(` ${green("soma check-updates")} Check for updates without installing`);
1227
1228
  console.log(` ${green("soma status")} Show installation status`);
1228
1229
  console.log("");
@@ -1454,13 +1455,17 @@ async function checkAndUpdate() {
1454
1455
  }
1455
1456
  } catch {
1456
1457
  }
1457
- const shouldUpdate = await confirmYN(` ${dim("\u2192")} Update now?`);
1458
+ const autoYes = process.argv.includes("--yes") || process.argv.includes("-y");
1459
+ const shouldUpdate = autoYes ? true : await confirmYN(` ${dim("\u2192")} Update now?`);
1458
1460
  if (!shouldUpdate) {
1459
1461
  console.log("");
1460
1462
  console.log(` ${dim("Skipped. Run")} ${green("soma update")} ${dim("anytime to update.")}`);
1461
1463
  console.log("");
1462
1464
  return;
1463
1465
  }
1466
+ if (autoYes) {
1467
+ console.log(` ${dim("\u2192")} Auto-confirmed via --yes`);
1468
+ }
1464
1469
  console.log("");
1465
1470
  try {
1466
1471
  execSync2("git pull --ff-only", { cwd: installPath, stdio: "ignore" });
@@ -1648,20 +1653,42 @@ async function healthCheck() {
1648
1653
  if (existsSync3(pkgPath)) {
1649
1654
  const pkg = JSON.parse(readFileSync3(pkgPath, "utf-8"));
1650
1655
  const declaredPi = (pkg.dependencies || {})["@mariozechner/pi-coding-agent"];
1651
- const piPkgPath = join3(CORE_DIR, "node_modules", "@mariozechner", "pi-coding-agent", "package.json");
1652
- if (existsSync3(piPkgPath)) {
1653
- const installedPi = JSON.parse(readFileSync3(piPkgPath, "utf-8")).version;
1654
- const declaredClean = (declaredPi || "").replace(/^[\^~]/, "");
1655
- if (declaredClean && installedPi && declaredClean !== installedPi) {
1656
- console.log(` ${yellow("\u26A0")} Pi runtime drift: declared ${cyan(declaredClean)}, installed ${cyan(installedPi)}`);
1657
- console.log(` ${dim("Fix:")} ${green("soma update")}`);
1658
- warnings++;
1659
- } else if (installedPi) {
1660
- console.log(` ${green("\u2713")} Pi runtime ${installedPi}`);
1656
+ const declaredClean = (declaredPi || "").replace(/^[\^~]/, "");
1657
+ let bundledPi = null;
1658
+ let bundledSource = null;
1659
+ const manifestPath = join3(CORE_DIR, "dist", "manifest.json");
1660
+ if (existsSync3(manifestPath)) {
1661
+ try {
1662
+ const m = JSON.parse(readFileSync3(manifestPath, "utf-8"));
1663
+ if (m.piVersion) {
1664
+ bundledPi = m.piVersion;
1665
+ bundledSource = "dist/manifest.json";
1666
+ }
1667
+ } catch {
1661
1668
  }
1662
- } else {
1663
- console.log(` ${yellow("\u26A0")} Pi not installed \u2014 run ${green("soma update")}`);
1669
+ }
1670
+ let fallbackUsed = false;
1671
+ if (!bundledPi) {
1672
+ const piPkgPath = join3(CORE_DIR, "node_modules", "@mariozechner", "pi-coding-agent", "package.json");
1673
+ if (existsSync3(piPkgPath)) {
1674
+ try {
1675
+ bundledPi = JSON.parse(readFileSync3(piPkgPath, "utf-8")).version;
1676
+ bundledSource = "node_modules (fallback \u2014 dist/manifest.json missing)";
1677
+ fallbackUsed = true;
1678
+ } catch {
1679
+ }
1680
+ }
1681
+ }
1682
+ if (!bundledPi) {
1683
+ console.log(` ${yellow("\u26A0")} Pi not found \u2014 run ${green("soma update")}`);
1684
+ warnings++;
1685
+ } else if (declaredClean && declaredClean !== bundledPi) {
1686
+ console.log(` ${yellow("\u26A0")} Pi runtime drift: declared ${cyan(declaredClean)}, bundled ${cyan(bundledPi)}`);
1687
+ console.log(` ${dim("Fix:")} ${green("soma update")}${fallbackUsed ? dim(" (also rebuild dist: node scripts/build-dist.mjs --clean)") : ""}`);
1664
1688
  warnings++;
1689
+ } else {
1690
+ const suffix = fallbackUsed ? dim(" (via node_modules fallback)") : "";
1691
+ console.log(` ${green("\u2713")} Pi runtime ${bundledPi}${suffix}`);
1665
1692
  }
1666
1693
  }
1667
1694
  } catch {
@@ -1705,7 +1732,7 @@ async function projectDoctor() {
1705
1732
  }
1706
1733
  printSigma();
1707
1734
  const agentV = getAgentVersion();
1708
- const projectV = getProjectVersion();
1735
+ let projectV = getProjectVersion();
1709
1736
  const installed = isInstalled();
1710
1737
  console.log(` ${bold("Soma")} \u2014 Doctor`);
1711
1738
  console.log("");
@@ -1728,11 +1755,190 @@ async function projectDoctor() {
1728
1755
  return;
1729
1756
  }
1730
1757
  if (!projectV) {
1731
- console.log(` ${yellow("\u26A0")} Project .soma/ has no version (pre-versioning).`);
1732
- console.log(` This project was likely created before v0.6.3.`);
1733
- console.log(` Run ${green("/soma doctor")} inside a session to migrate, or re-init with ${green("soma init")}.`);
1734
- console.log("");
1735
- return;
1758
+ console.log(` ${yellow("\u26A0")} Project .soma/ has no version (pre-versioning). Stamping...`);
1759
+ try {
1760
+ const settingsPath = join3(process.cwd(), ".soma", "settings.json");
1761
+ let cfg = {};
1762
+ if (existsSync3(settingsPath)) {
1763
+ try {
1764
+ cfg = JSON.parse(readFileSync3(settingsPath, "utf8"));
1765
+ } catch {
1766
+ }
1767
+ } else {
1768
+ mkdirSync2(dirname3(settingsPath), { recursive: true });
1769
+ }
1770
+ cfg.version = agentV;
1771
+ writeFileSync2(settingsPath, JSON.stringify(cfg, null, 2) + "\n");
1772
+ console.log(` ${green("\u2713")} Stamped .soma/settings.json \u2192 v${agentV}`);
1773
+ console.log("");
1774
+ projectV = agentV;
1775
+ } catch (e) {
1776
+ console.log(` ${red("\u2717")} Failed to stamp settings.json: ${e.message}`);
1777
+ console.log(` Run ${green("soma init")} to scaffold a fresh .soma/settings.json.`);
1778
+ console.log("");
1779
+ return;
1780
+ }
1781
+ }
1782
+ try {
1783
+ const softSomaDir = join3(process.cwd(), ".soma");
1784
+ const softSettings = join3(softSomaDir, "settings.json");
1785
+ if (existsSync3(softSettings)) {
1786
+ const templateRoots = [CORE_DIR, join3(CORE_DIR, "dist")];
1787
+ try {
1788
+ const devCore = readlinkSync2(join3(CORE_DIR, "core"));
1789
+ if (devCore) {
1790
+ const devRoot = dirname3(devCore);
1791
+ templateRoots.push(devRoot, join3(devRoot, "dist"));
1792
+ }
1793
+ } catch {
1794
+ }
1795
+ const phaseDirsSeen = /* @__PURE__ */ new Set();
1796
+ const phaseDirs = [];
1797
+ for (const root of templateRoots) {
1798
+ const d = join3(root, "migrations", "phases");
1799
+ if (existsSync3(d) && !phaseDirsSeen.has(d)) {
1800
+ phaseDirs.push(d);
1801
+ phaseDirsSeen.add(d);
1802
+ }
1803
+ }
1804
+ const seenMaps = /* @__PURE__ */ new Set();
1805
+ const maps = [];
1806
+ for (const dir of phaseDirs) {
1807
+ for (const f of readdirSync(dir).filter((f2) => f2.endsWith(".md"))) {
1808
+ if (seenMaps.has(f)) continue;
1809
+ seenMaps.add(f);
1810
+ maps.push(join3(dir, f));
1811
+ }
1812
+ }
1813
+ let obj = JSON.parse(readFileSync3(softSettings, "utf-8"));
1814
+ let objChanged = false;
1815
+ let replayFixes = 0;
1816
+ for (const mapPath of maps) {
1817
+ let raw;
1818
+ try {
1819
+ raw = readFileSync3(mapPath, "utf-8");
1820
+ } catch {
1821
+ continue;
1822
+ }
1823
+ const fmMatch = raw.match(/^---\n([\s\S]*?)\n---/);
1824
+ if (!fmMatch) continue;
1825
+ const replayLine = fmMatch[1].match(/^replay-until:\s*["']?([^"'\n]+?)["']?\s*$/m);
1826
+ if (!replayLine) continue;
1827
+ const replayUntil = replayLine[1].trim();
1828
+ if (agentV && semverCmp2(agentV, replayUntil) >= 0) continue;
1829
+ const actionsBlock = raw.match(/^## Doctor Actions[\s\S]*?```json\s*\n([\s\S]*?)```/m);
1830
+ if (!actionsBlock) continue;
1831
+ let actions;
1832
+ try {
1833
+ actions = JSON.parse(actionsBlock[1]);
1834
+ } catch {
1835
+ continue;
1836
+ }
1837
+ if (actions["settings-defaults"] && typeof actions["settings-defaults"] === "object") {
1838
+ for (const [k, v] of Object.entries(actions["settings-defaults"])) {
1839
+ if (!(k in obj)) {
1840
+ obj[k] = v;
1841
+ objChanged = true;
1842
+ replayFixes++;
1843
+ }
1844
+ }
1845
+ }
1846
+ if (actions["settings-subkeys"] && typeof actions["settings-subkeys"] === "object") {
1847
+ for (const [parent, sub] of Object.entries(actions["settings-subkeys"])) {
1848
+ if (!obj[parent] || typeof obj[parent] !== "object") continue;
1849
+ for (const [k, v] of Object.entries(sub)) {
1850
+ if (obj[parent][k] === void 0) {
1851
+ obj[parent][k] = v;
1852
+ objChanged = true;
1853
+ replayFixes++;
1854
+ }
1855
+ }
1856
+ }
1857
+ }
1858
+ if (Array.isArray(actions["scaffold-files"])) {
1859
+ for (const entry of actions["scaffold-files"]) {
1860
+ if (!entry || !entry.target || !entry.template) continue;
1861
+ const target = join3(softSomaDir, entry.target);
1862
+ if (existsSync3(target)) continue;
1863
+ for (const root of templateRoots) {
1864
+ const src = join3(root, entry.template);
1865
+ if (!existsSync3(src)) continue;
1866
+ const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
1867
+ mkdirSync2(dirname3(target), { recursive: true });
1868
+ writeFileSync2(target, readFileSync3(src, "utf-8").replace(/\{\{today\}\}/g, today));
1869
+ replayFixes++;
1870
+ break;
1871
+ }
1872
+ }
1873
+ }
1874
+ }
1875
+ if (objChanged) writeFileSync2(softSettings, JSON.stringify(obj, null, " ") + "\n");
1876
+ if (replayFixes > 0) {
1877
+ console.log(` ${green("\u2713")} Migration replay applied ${replayFixes} backfill(s)`);
1878
+ console.log("");
1879
+ }
1880
+ }
1881
+ } catch {
1882
+ }
1883
+ try {
1884
+ const softSomaDir = join3(process.cwd(), ".soma");
1885
+ if (existsSync3(softSomaDir)) {
1886
+ const templateRoots = [CORE_DIR, join3(CORE_DIR, "dist")];
1887
+ try {
1888
+ const devCore = readlinkSync2(join3(CORE_DIR, "core"));
1889
+ if (devCore) {
1890
+ const devRoot = dirname3(devCore);
1891
+ templateRoots.push(devRoot, join3(devRoot, "dist"));
1892
+ }
1893
+ } catch {
1894
+ }
1895
+ let registry = null;
1896
+ let bundledTemplatesDir = null;
1897
+ for (const root of templateRoots) {
1898
+ const regPath = join3(root, "migrations", "template-hashes.json");
1899
+ if (existsSync3(regPath) && !registry) {
1900
+ try {
1901
+ registry = JSON.parse(readFileSync3(regPath, "utf-8"));
1902
+ } catch {
1903
+ }
1904
+ }
1905
+ const td = join3(root, "templates", "default");
1906
+ if (existsSync3(td) && !bundledTemplatesDir) bundledTemplatesDir = td;
1907
+ }
1908
+ if (registry?.templates && bundledTemplatesDir) {
1909
+ const sha256 = (buf) => createHash("sha256").update(buf).digest("hex");
1910
+ let refreshed = 0;
1911
+ let customized = 0;
1912
+ const customizedFiles = [];
1913
+ for (const [filename, meta] of Object.entries(registry.templates)) {
1914
+ const localPath = join3(softSomaDir, "body", filename);
1915
+ const bundledPath = join3(bundledTemplatesDir, filename);
1916
+ if (!existsSync3(localPath) || !existsSync3(bundledPath)) continue;
1917
+ const localContent = readFileSync3(localPath);
1918
+ const bundledContent = readFileSync3(bundledPath);
1919
+ const localHash = sha256(localContent);
1920
+ const bundledHash = sha256(bundledContent);
1921
+ if (localHash === bundledHash) continue;
1922
+ const historical = (meta.historical || []).map((h) => h.sha256);
1923
+ if (historical.includes(localHash)) {
1924
+ writeFileSync2(localPath, bundledContent);
1925
+ console.log(` ${green("\u2713")} Refreshed body/${filename} (pristine older bundled \u2192 current)`);
1926
+ refreshed++;
1927
+ } else {
1928
+ customized++;
1929
+ customizedFiles.push(filename);
1930
+ }
1931
+ }
1932
+ if (refreshed > 0) console.log("");
1933
+ if (customized > 0) {
1934
+ console.log(` ${yellow("\u26A0")} Customized body files with bundled updates available: ${customizedFiles.join(", ")}`);
1935
+ console.log(` ${dim("Review updates: diff body/<file> vs " + bundledTemplatesDir + "/<file>")}`);
1936
+ console.log(` ${dim("To adopt bundled: rm body/<file> && soma init --force (safe \u2014 writeIfMissing preserves others)")}`);
1937
+ console.log("");
1938
+ }
1939
+ }
1940
+ }
1941
+ } catch {
1736
1942
  }
1737
1943
  if (agentV && semverCmp2(projectV, agentV) === 0) {
1738
1944
  console.log(` ${green("\u2713")} Project is up to date.`);
@@ -1758,14 +1964,21 @@ async function projectDoctor() {
1758
1964
  }
1759
1965
  };
1760
1966
  add("doctor", { autoUpdate: true, declinedVersion: null });
1967
+ add("keepalive", { maxPings: 5, autoExhale: true, autoExhaleMinTokens: 75e3 });
1761
1968
  add("breathe", { auto: false, triggerAt: 50, rotateAt: 70, graceSeconds: 30 });
1762
1969
  add("context", { notifyAt: 50, warnAt: 70, urgentAt: 80, autoExhaleAt: 85 });
1763
- add("preload", { staleAfterHours: 48, lastSessionLogs: 0 });
1970
+ add("preload", { staleAfterHours: 48, lastSessionLogs: 0, recentNotesCount: 3 });
1764
1971
  add("scratch", { autoInject: false });
1765
1972
  add("guard", { coreFiles: "warn", bashCommands: "warn", gitIdentity: null });
1766
1973
  add("checkpoints", { enabled: true, intervalMinutes: 5, squashOnPush: true });
1974
+ add("cache", { retention: null });
1767
1975
  add("persona", { name: null, emoji: "\u03C3" });
1768
1976
  add("inherit", { identity: true, protocols: true, muscles: true, tools: true });
1977
+ if (current.preload && typeof current.preload === "object" && current.preload.recentNotesCount === void 0) {
1978
+ current.preload.recentNotesCount = 3;
1979
+ changed = true;
1980
+ fixes++;
1981
+ }
1769
1982
  if (changed) writeFileSync2(settingsPath, JSON.stringify(current, null, " ") + "\n");
1770
1983
  } catch {
1771
1984
  }
@@ -1818,6 +2031,25 @@ async function projectDoctor() {
1818
2031
  }
1819
2032
  }
1820
2033
  }
2034
+ try {
2035
+ const notesTarget = join3(somaDir, "memory", "notes", "soma-log.md");
2036
+ if (!existsSync3(notesTarget)) {
2037
+ const candidates = [
2038
+ join3(agentRoot, "templates", "default", "memory", "notes", "soma-log.md"),
2039
+ join3(agentRoot, "dist", "templates", "default", "memory", "notes", "soma-log.md")
2040
+ ];
2041
+ for (const src of candidates) {
2042
+ if (!existsSync3(src)) continue;
2043
+ const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
2044
+ const content = readFileSync3(src, "utf-8").replace(/\{\{today\}\}/g, today);
2045
+ mkdirSync2(dirname3(notesTarget), { recursive: true });
2046
+ writeFileSync2(notesTarget, content);
2047
+ fixes++;
2048
+ break;
2049
+ }
2050
+ }
2051
+ } catch {
2052
+ }
1821
2053
  if (fixes > 0) {
1822
2054
  try {
1823
2055
  const s = JSON.parse(readFileSync3(settingsPath, "utf-8"));
@@ -1976,6 +2208,14 @@ try {
1976
2208
  }
1977
2209
  } catch {
1978
2210
  }
2211
+ try {
2212
+ const tokenPath = join3(SOMA_HOME, "somadian-token");
2213
+ if (!existsSync3(tokenPath)) {
2214
+ mkdirSync2(SOMA_HOME, { recursive: true, mode: 448 });
2215
+ writeFileSync2(tokenPath, randomBytes(24).toString("hex"), { mode: 384 });
2216
+ }
2217
+ } catch {
2218
+ }
1979
2219
  if (cmd === "--version" || cmd === "-v" || cmd === "-V" || cmd === "version") {
1980
2220
  showVersion();
1981
2221
  } else if (cmd === "--help" || cmd === "-h" || cmd === "help") {
package/package.json CHANGED
@@ -1,44 +1,48 @@
1
1
  {
2
- "name": "meetsoma",
3
- "version": "0.3.8",
4
- "description": "Soma — the AI coding agent with self-growing memory",
5
- "type": "module",
6
- "bin": {
7
- "soma": "dist/thin-cli.js"
8
- },
9
- "files": [
10
- "dist/thin-cli.js",
11
- "dist/personality.js",
12
- "dist/postinstall.js",
13
- "LICENSE",
14
- "README.md",
15
- "CHANGELOG.md"
16
- ],
17
- "keywords": [
18
- "soma",
19
- "meetsoma",
20
- "ai",
21
- "agent",
22
- "memory",
23
- "coding-agent",
24
- "identity",
25
- "muscles",
26
- "protocols",
27
- "self-evolving",
28
- "cli",
29
- "llm"
30
- ],
31
- "author": "Gravicity <hello@gravicity.ai>",
32
- "license": "BSL-1.1",
33
- "homepage": "https://soma.gravicity.ai",
34
- "repository": {
35
- "type": "git",
36
- "url": "git+https://github.com/meetsoma/soma-agent.git"
37
- },
38
- "scripts": {
39
- "postinstall": "node dist/postinstall.js"
40
- },
41
- "engines": {
42
- "node": ">=20.6.0"
43
- }
2
+ "name": "meetsoma",
3
+ "version": "0.6.0",
4
+ "description": "Soma — the AI coding agent with self-growing memory",
5
+ "type": "module",
6
+ "bin": {
7
+ "soma": "dist/thin-cli.js"
8
+ },
9
+ "files": [
10
+ "dist/thin-cli.js",
11
+ "dist/personality.js",
12
+ "dist/postinstall.js",
13
+ "LICENSE",
14
+ "README.md",
15
+ "CHANGELOG.md"
16
+ ],
17
+ "keywords": [
18
+ "soma",
19
+ "meetsoma",
20
+ "ai",
21
+ "agent",
22
+ "memory",
23
+ "coding-agent",
24
+ "identity",
25
+ "muscles",
26
+ "protocols",
27
+ "self-evolving",
28
+ "cli",
29
+ "llm"
30
+ ],
31
+ "author": "Gravicity <hello@gravicity.ai>",
32
+ "license": "BSL-1.1",
33
+ "homepage": "https://soma.gravicity.ai",
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "git+https://github.com/meetsoma/soma-agent.git"
37
+ },
38
+ "scripts": {
39
+ "postinstall": "node dist/postinstall.js"
40
+ },
41
+ "engines": {
42
+ "node": ">=20.6.0"
43
+ },
44
+ "piConfig": {
45
+ "name": "soma",
46
+ "configDir": ".soma"
47
+ }
44
48
  }