@massu/core 0.8.0 → 0.8.1

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/dist/cli.js CHANGED
@@ -1485,7 +1485,6 @@ var init_memory_db = __esm({
1485
1485
  // src/memory-file-ingest.ts
1486
1486
  import { readFileSync as readFileSync2, existsSync as existsSync3, readdirSync } from "fs";
1487
1487
  import { join } from "path";
1488
- import { parse as parseYaml2 } from "yaml";
1489
1488
  function ingestMemoryFile(db, sessionId, filePath) {
1490
1489
  if (!existsSync3(filePath)) return "skipped";
1491
1490
  const content = readFileSync2(filePath, "utf-8");
@@ -1497,7 +1496,13 @@ function ingestMemoryFile(db, sessionId, filePath) {
1497
1496
  let confidence;
1498
1497
  if (frontmatterMatch) {
1499
1498
  try {
1500
- const fm = parseYaml2(frontmatterMatch[1]);
1499
+ const fm = {};
1500
+ for (const line of frontmatterMatch[1].split("\n")) {
1501
+ const sep = line.indexOf(":");
1502
+ if (sep > 0) {
1503
+ fm[line.slice(0, sep).trim()] = line.slice(sep + 1).trim();
1504
+ }
1505
+ }
1501
1506
  name = fm.name ?? basename8;
1502
1507
  description = fm.description ?? "";
1503
1508
  type = fm.type ?? "discovery";
@@ -1924,19 +1929,11 @@ function registerMcpServer(projectRoot) {
1924
1929
  return true;
1925
1930
  }
1926
1931
  function resolveHooksDir() {
1927
- const cwd = process.cwd();
1928
- const nodeModulesPath = resolve4(cwd, "node_modules/@massu/core/dist/hooks");
1929
- if (existsSync5(nodeModulesPath)) {
1930
- return "node_modules/@massu/core/dist/hooks";
1931
- }
1932
- const localPath = resolve4(__dirname2, "../dist/hooks");
1933
- if (existsSync5(localPath)) {
1934
- return localPath;
1935
- }
1936
1932
  return "node_modules/@massu/core/dist/hooks";
1937
1933
  }
1938
1934
  function hookCmd(hooksDir, hookFile) {
1939
- return `node ${hooksDir}/${hookFile}`;
1935
+ const hookPath = `${hooksDir}/${hookFile}`;
1936
+ return `d="$PWD"; while [ "$d" != "/" ] && [ ! -f "$d/${hookPath}" ]; do d="$(dirname "$d")"; done; cd "$d" && node ${hookPath}`;
1940
1937
  }
1941
1938
  function buildHooksConfig(hooksDir) {
1942
1939
  return {
@@ -2475,7 +2472,7 @@ __export(doctor_exports, {
2475
2472
  import { existsSync as existsSync6, readFileSync as readFileSync5, readdirSync as readdirSync4 } from "fs";
2476
2473
  import { resolve as resolve5, dirname as dirname5 } from "path";
2477
2474
  import { fileURLToPath as fileURLToPath3 } from "url";
2478
- import { parse as parseYaml3 } from "yaml";
2475
+ import { parse as parseYaml2 } from "yaml";
2479
2476
  function checkConfig(projectRoot) {
2480
2477
  const configPath = resolve5(projectRoot, "massu.config.yaml");
2481
2478
  if (!existsSync6(configPath)) {
@@ -2483,7 +2480,7 @@ function checkConfig(projectRoot) {
2483
2480
  }
2484
2481
  try {
2485
2482
  const content = readFileSync5(configPath, "utf-8");
2486
- const parsed = parseYaml3(content);
2483
+ const parsed = parseYaml2(content);
2487
2484
  if (!parsed || typeof parsed !== "object") {
2488
2485
  return { name: "Configuration", status: "fail", detail: "massu.config.yaml is empty or invalid YAML" };
2489
2486
  }
@@ -2801,7 +2798,7 @@ async function runValidateConfig() {
2801
2798
  }
2802
2799
  try {
2803
2800
  const content = readFileSync5(configPath, "utf-8");
2804
- const parsed = parseYaml3(content);
2801
+ const parsed = parseYaml2(content);
2805
2802
  if (!parsed || typeof parsed !== "object") {
2806
2803
  console.error("Error: massu.config.yaml is empty or not a valid YAML object");
2807
2804
  process.exit(1);
@@ -12241,7 +12238,6 @@ var init_mcp_bridge_tools = __esm({
12241
12238
  });
12242
12239
  process.on("SIGTERM", () => {
12243
12240
  for (const [name] of connections) disconnectServer(name);
12244
- process.exit(0);
12245
12241
  });
12246
12242
  ENV_ALLOW_LIST = /* @__PURE__ */ new Set([
12247
12243
  "PATH",
@@ -1666,11 +1666,10 @@ function storeSecurityScore(db, sessionId, filePath, riskScore, findings) {
1666
1666
  // src/hooks/post-tool-use.ts
1667
1667
  import { readFileSync as readFileSync6, existsSync as existsSync7 } from "fs";
1668
1668
  import { join as join2 } from "path";
1669
- import { parse as parseYaml3 } from "yaml";
1669
+ import { parse as parseYaml2 } from "yaml";
1670
1670
 
1671
1671
  // src/memory-file-ingest.ts
1672
1672
  import { readFileSync as readFileSync5, existsSync as existsSync6, readdirSync } from "fs";
1673
- import { parse as parseYaml2 } from "yaml";
1674
1673
  function ingestMemoryFile(db, sessionId, filePath) {
1675
1674
  if (!existsSync6(filePath)) return "skipped";
1676
1675
  const content = readFileSync5(filePath, "utf-8");
@@ -1682,7 +1681,13 @@ function ingestMemoryFile(db, sessionId, filePath) {
1682
1681
  let confidence;
1683
1682
  if (frontmatterMatch) {
1684
1683
  try {
1685
- const fm = parseYaml2(frontmatterMatch[1]);
1684
+ const fm = {};
1685
+ for (const line of frontmatterMatch[1].split("\n")) {
1686
+ const sep = line.indexOf(":");
1687
+ if (sep > 0) {
1688
+ fm[line.slice(0, sep).trim()] = line.slice(sep + 1).trim();
1689
+ }
1690
+ }
1686
1691
  name = fm.name ?? basename2;
1687
1692
  description = fm.description ?? "";
1688
1693
  type = fm.type ?? "discovery";
@@ -1897,7 +1902,7 @@ function readConventions(cwd) {
1897
1902
  const configPath = join2(projectRoot, "massu.config.yaml");
1898
1903
  if (!existsSync7(configPath)) return defaults;
1899
1904
  const content = readFileSync6(configPath, "utf-8");
1900
- const parsed = parseYaml3(content);
1905
+ const parsed = parseYaml2(content);
1901
1906
  if (!parsed || typeof parsed !== "object") return defaults;
1902
1907
  const conventions = parsed.conventions;
1903
1908
  if (!conventions || typeof conventions !== "object") return defaults;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@massu/core",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "type": "module",
5
5
  "description": "AI Engineering Governance MCP Server - Session memory, knowledge system, feature registry, code intelligence, rule enforcement, auto-learning pipeline, tiered tooling (12 free / 72 total), 55+ workflow commands, 15 agents, 20+ patterns",
6
6
  "main": "src/server.ts",
@@ -346,25 +346,18 @@ type HooksConfig = Record<string, HookGroup[]>;
346
346
  * Handles both local development and npm-installed scenarios.
347
347
  */
348
348
  export function resolveHooksDir(): string {
349
- // Try to find the hooks in node_modules first (installed via npm)
350
- const cwd = process.cwd();
351
- const nodeModulesPath = resolve(cwd, 'node_modules/@massu/core/dist/hooks');
352
- if (existsSync(nodeModulesPath)) {
353
- return 'node_modules/@massu/core/dist/hooks';
354
- }
355
-
356
- // Fall back to finding relative to this source file
357
- const localPath = resolve(__dirname, '../dist/hooks');
358
- if (existsSync(localPath)) {
359
- return localPath;
360
- }
361
-
362
- // Default to node_modules path (will be created on npm install)
349
+ // Always use node_modules/@massu/core/dist/hooks relative to project root.
350
+ // hookCmd() wraps each command with a parent-directory walk to find the
351
+ // project root, so hooks resolve correctly even from subdirectories.
363
352
  return 'node_modules/@massu/core/dist/hooks';
364
353
  }
365
354
 
366
355
  function hookCmd(hooksDir: string, hookFile: string): string {
367
- return `node ${hooksDir}/${hookFile}`;
356
+ // Walk up from cwd to find the directory containing node_modules/@massu/core,
357
+ // then cd there before running the hook. This handles subdirectories like
358
+ // website/, packages/foo/, etc. where node_modules doesn't exist.
359
+ const hookPath = `${hooksDir}/${hookFile}`;
360
+ return `d="$PWD"; while [ "$d" != "/" ] && [ ! -f "$d/${hookPath}" ]; do d="$(dirname "$d")"; done; cd "$d" && node ${hookPath}`;
368
361
  }
369
362
 
370
363
  export function buildHooksConfig(hooksDir: string): HooksConfig {
@@ -50,7 +50,6 @@ process.on('exit', () => {
50
50
  });
51
51
  process.on('SIGTERM', () => {
52
52
  for (const [name] of connections) disconnectServer(name);
53
- process.exit(0);
54
53
  });
55
54
 
56
55
  // Environment variables safe to forward to MCP subprocesses.
@@ -11,7 +11,6 @@
11
11
  import type Database from 'better-sqlite3';
12
12
  import { readFileSync, existsSync, readdirSync } from 'fs';
13
13
  import { join } from 'path';
14
- import { parse as parseYaml } from 'yaml';
15
14
  import { addObservation } from './memory-db.ts';
16
15
 
17
16
  export type IngestResult = 'inserted' | 'updated' | 'skipped';
@@ -42,13 +41,21 @@ export function ingestMemoryFile(
42
41
 
43
42
  if (frontmatterMatch) {
44
43
  try {
45
- const fm = parseYaml(frontmatterMatch[1]) as Record<string, unknown>;
46
- name = (fm.name as string) ?? basename;
47
- description = (fm.description as string) ?? '';
48
- type = (fm.type as string) ?? 'discovery';
44
+ // Simple key: value parser for memory file frontmatter
45
+ // (avoids importing yaml library — frontmatter is flat key-value pairs)
46
+ const fm: Record<string, string> = {};
47
+ for (const line of frontmatterMatch[1].split('\n')) {
48
+ const sep = line.indexOf(':');
49
+ if (sep > 0) {
50
+ fm[line.slice(0, sep).trim()] = line.slice(sep + 1).trim();
51
+ }
52
+ }
53
+ name = fm.name ?? basename;
54
+ description = fm.description ?? '';
55
+ type = fm.type ?? 'discovery';
49
56
  confidence = fm.confidence != null ? Number(fm.confidence) : undefined;
50
57
  } catch {
51
- // Use defaults if YAML parsing fails
58
+ // Use defaults if frontmatter parsing fails
52
59
  }
53
60
  }
54
61