baller-maester 0.4.0 → 0.4.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/CHANGELOG.md CHANGED
@@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.4.1] - 2026-05-21
11
+
12
+ ### Fixed
13
+ - **Docs and CLI strings now use the correct npm package name.** Every `npx maester ...` invocation has been replaced with `npx baller-maester ...` (or `npx -y baller-maester ...` for automation contexts). `npx` resolves by npm package name and this project publishes as `baller-maester`, so the old form would have failed at the registry. Touches the README, CHANGELOG, every `gspec/` document that named the command, both skill-template content files (freshness-awareness and connector-policy-fallback), the `maester init` outro, the missing-config error messages in the config loader, and the auto-generated `citadel.yaml` / `maester.yaml` header comments.
14
+ - **Grand Maester `PreToolUse` hook command is now actually invokable.** The `.claude/settings.json` block installed by `maester skill install` previously embedded `npx maester skill runtime preread`, which would fail npm-side resolution. Updated to `npx -y baller-maester skill runtime preread` (matching the existing MCP-registration convention). The `-y` flag skips npx's first-run confirmation so the hook does not stall Claude Code's tool-call flow.
15
+ - **Stale docstrings on the MCP registration writers** for Claude Code and Codex CLI claimed the entry embedded `process.argv[1]` rather than `npx maester`; the code has actually emitted `npx -y baller-maester mcp` since 0.4.0. Rewrote both docstrings to match reality.
16
+
10
17
  ## [0.4.0] - 2026-05-21
11
18
 
12
19
  ### Added
@@ -56,5 +63,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
56
63
 
57
64
  ### Changed
58
65
  - Sync orchestration consolidated into a single `src/core/sources/fetcher.ts` (replacing the previously planned per-kind modules). The fetcher branches internally on whether the source declares an `includes` list.
59
- - **Repo-root detection is now always the current working directory.** `npx maester` (and every subcommand) treats `process.cwd()` as the root unconditionally — the old walk-upward-for-`.git`/`package.json` behavior is removed. `citadel.yaml` and `maester.yaml` always land in the directory where you typed the command, never in an ancestor. An existing config file in an ancestor directory is invisible to the cwd model.
66
+ - **Repo-root detection is now always the current working directory.** `npx baller-maester` (and every subcommand) treats `process.cwd()` as the root unconditionally — the old walk-upward-for-`.git`/`package.json` behavior is removed. `citadel.yaml` and `maester.yaml` always land in the directory where you typed the command, never in an ancestor. An existing config file in an ancestor directory is invisible to the cwd model.
60
67
  - Top-level `baseDir` field on `citadel.yaml` (optional). When set, every source whose `destination` is unset is surfaced at `<baseDir>/<source-name>/` instead of `citadel/<source-name>/`. Per-source `destination` overrides always win. Omitting `baseDir` is identical to today's behavior — fully backward compatible. The citadel-init walkthrough prompts for it with `citadel` pre-filled and omits the field from the generated YAML when the default is accepted.
package/README.md CHANGED
@@ -21,7 +21,7 @@ A directory can hold one role, the other, or both. There is at most one of each
21
21
 
22
22
  ### Where do the files land?
23
23
 
24
- **Every maester command uses the current working directory as the root.** When you run `npx maester init` from a directory, `citadel.yaml` is created in that exact directory — never in an ancestor. The same goes for `npx maester publish` (writes `maester.yaml` here), `npx maester sync` (reads `citadel.yaml` from here), and the interactive menu (`npx maester`).
24
+ **Every maester command uses the current working directory as the root.** When you run `npx baller-maester init` from a directory, `citadel.yaml` is created in that exact directory — never in an ancestor. The same goes for `npx baller-maester publish` (writes `maester.yaml` here), `npx baller-maester sync` (reads `citadel.yaml` from here), and the interactive menu (`npx baller-maester`).
25
25
 
26
26
  If you run a maester command from the wrong directory, `cd` to the intended directory and re-run. The CLI does not walk upward to find a project root, so a stray `.git/` or `package.json` in a parent directory will not pull configuration files away from where you typed the command.
27
27
 
@@ -30,15 +30,15 @@ If you run a maester command from the wrong directory, `cd` to the intended dire
30
30
  In the directory you want to populate with aggregated knowledge:
31
31
 
32
32
  ```sh
33
- npx maester # interactive menu (uses cwd as root)
34
- npx maester init # citadel walkthrough — creates ./citadel.yaml
35
- npx maester sync # fetch all configured sources — reads ./citadel.yaml
33
+ npx baller-maester # interactive menu (uses cwd as root)
34
+ npx baller-maester init # citadel walkthrough — creates ./citadel.yaml
35
+ npx baller-maester sync # fetch all configured sources — reads ./citadel.yaml
36
36
  ```
37
37
 
38
38
  In a directory that *publishes* docs to other citadels:
39
39
 
40
40
  ```sh
41
- npx maester publish # maester manifest walkthrough — creates ./maester.yaml
41
+ npx baller-maester publish # maester manifest walkthrough — creates ./maester.yaml
42
42
  ```
43
43
 
44
44
  ## Pulling from sources you don't own
@@ -67,7 +67,7 @@ sources:
67
67
  description: Upstream React documentation snapshot.
68
68
  ```
69
69
 
70
- `npx maester sync` processes every source in one pass. The trade-off for the includes-driven mode: when the remote repo restructures, the citadel's `includes` may need to be updated. Sync prints a warning when an includes-driven source resolves to zero files so drift is visible.
70
+ `npx baller-maester sync` processes every source in one pass. The trade-off for the includes-driven mode: when the remote repo restructures, the citadel's `includes` may need to be updated. Sync prints a warning when an includes-driven source resolves to zero files so drift is visible.
71
71
 
72
72
  ## Prerequisites
73
73
 
package/dist/cli/main.js CHANGED
@@ -995,7 +995,7 @@ async function loadCitadelConfig(repoRoot) {
995
995
  const path9 = citadelConfigPath(repoRoot);
996
996
  if (!existsSync(path9)) {
997
997
  throw new ConfigError(
998
- "No citadel.yaml found at the repository root. Run `npx maester init` to create one.",
998
+ "No citadel.yaml found at the repository root. Run `npx baller-maester init` to create one.",
999
999
  { filePath: path9 }
1000
1000
  );
1001
1001
  }
@@ -1275,7 +1275,7 @@ var CITADEL_HEADER = `# citadel.yaml
1275
1275
  # per-source \`destination\` always wins over the configured base.
1276
1276
  #
1277
1277
  # Run \`maester sync\` (or \`npm run maester:sync\`) to refresh every source in
1278
- # one pass. Generated by \`npx maester init\` and safe to commit. Secret
1278
+ # one pass. Generated by \`npx baller-maester init\` and safe to commit. Secret
1279
1279
  # values are never stored here \u2014 only the names of environment variables
1280
1280
  # that hold them.
1281
1281
 
@@ -1286,7 +1286,7 @@ var MAESTER_HEADER = `# maester.yaml
1286
1286
  # that pulls from it. It is a manifest only \u2014 the documents themselves live
1287
1287
  # wherever the \`path\` fields point, and \`maester\` does not modify them.
1288
1288
  #
1289
- # Generated by \`npx maester publish\` and safe to commit.
1289
+ # Generated by \`npx baller-maester publish\` and safe to commit.
1290
1290
 
1291
1291
  `;
1292
1292
  async function writeCitadelConfig(repoRoot, config) {
@@ -1443,7 +1443,7 @@ var citadel_awareness_default = '## Citadel awareness\n\nThis repository is a **
1443
1443
  var connector_policy_default = "## Connector tools (traveling maesters)\n\nThis citadel may expose one or more **traveling maesters** as MCP tools whose\nnames begin with the connector slug (e.g. `team_gl__list_issues`).\n\n- Their output is **live, point-in-time data** from an external service. Cite\n specific identifiers (issue iids, ticket numbers) when surfacing it, do not\n treat it as a stable corpus, and flag the **freshness verdict** in your\n answer when it is not `up-to-date`.\n- The tools' arguments and return shapes are described in MCP `tools/list`;\n do not assume undocumented fields.\n- Connector tool results are JSON envelopes carrying a `dataSchema` version\n alongside the payload \u2014 if your reading of the data depends on a specific\n shape, branch on `dataSchema`.\n";
1444
1444
 
1445
1445
  // src/core/skill/templates/content/freshness-awareness.md
1446
- var freshness_awareness_default = '## Freshness awareness\n\nCitadel content can drift out of date when remote sources advance after the\nlast sync. The Grand Maester is **auto-sync directed**: when the citadel is\nbehind, refresh it before reading rather than relying on stale content.\n\n**Auto-sync policy**\n\nWhen you discover that the citadel is behind \u2014 either from the Claude Code\npre-read hook\'s `additionalContext` message, or from a manual run of\n`npx maester status` \u2014 follow these steps without prompting the user:\n\n1. Run `npx maester sync` to refresh every configured source.\n - Exits 0 on success.\n - Exits non-zero if any source failed (missing auth env var, ref typo,\n network error). Report the failure to the user and proceed cautiously.\n2. **Invalidate the pre-read hook\'s debounce cache** so the next read\n observes fresh state instead of the stale verdict captured before the\n sync:\n ```\n rm -f .maester/.skill-cache.json\n ```\n3. Proceed with the original read of the citadel file.\n\nYou do not need to ask the user before syncing. `maester sync` is read-only\nagainst the remote sources it pulls from; it only materializes content the\n`citadel.yaml` already declared. The same sync the user would run manually.\n\n**Don\'t loop on failures.** If `maester sync` fails (or the hook reports a\n`failed` verdict from `maester status`), do **not** retry sync repeatedly.\nSurface the failure to the user, proceed with the read, and flag that cited\ncontent may be stale.\n\n**Avoid redundant syncs within a session.** Once you have synced and\ninvalidated the cache, ignore any further "citadel is behind" messages that\narrive before you have done another citadel read \u2014 they are cached signals\ncaptured before your sync completed.\n\n**Manual status check**\n\n```\nnpx maester status\n```\n\nExit codes:\n\n- **`0`** \u2014 every source is up to date.\n- **`1`** \u2014 at least one source is behind (remote advanced, manifest\n changed, or never-synced). Run the auto-sync policy above.\n- **`2`** \u2014 the status check itself failed. Surface to the user; proceed\n with a caveat that staleness cannot be verified.\n\nFor machine-readable output, pass `--json` and parse the NDJSON stream on\nstdout. The final line contains `{ "type": "summary", "upToDate": N,\n"behind": N, "failed": N }`.\n\n**On Claude Code specifically**, a `PreToolUse` hook installed by\n`maester skill install` runs the status check automatically before any\n`Read`, `Glob`, or `Grep` targeting a path under `{{baseDir}}/`. The\nhook debounces (default 300s, override with `MAESTER_SKILL_STATUS_TTL`) so\nthe check does not run more than once per session for routine reads.\n';
1446
+ var freshness_awareness_default = '## Freshness awareness\n\nCitadel content can drift out of date when remote sources advance after the\nlast sync. The Grand Maester is **auto-sync directed**: when the citadel is\nbehind, refresh it before reading rather than relying on stale content.\n\n**Auto-sync policy**\n\nWhen you discover that the citadel is behind \u2014 either from the Claude Code\npre-read hook\'s `additionalContext` message, or from a manual run of\n`npx baller-maester status` \u2014 follow these steps without prompting the user:\n\n1. Run `npx baller-maester sync` to refresh every configured source.\n - Exits 0 on success.\n - Exits non-zero if any source failed (missing auth env var, ref typo,\n network error). Report the failure to the user and proceed cautiously.\n2. **Invalidate the pre-read hook\'s debounce cache** so the next read\n observes fresh state instead of the stale verdict captured before the\n sync:\n ```\n rm -f .maester/.skill-cache.json\n ```\n3. Proceed with the original read of the citadel file.\n\nYou do not need to ask the user before syncing. `maester sync` is read-only\nagainst the remote sources it pulls from; it only materializes content the\n`citadel.yaml` already declared. The same sync the user would run manually.\n\n**Don\'t loop on failures.** If `maester sync` fails (or the hook reports a\n`failed` verdict from `maester status`), do **not** retry sync repeatedly.\nSurface the failure to the user, proceed with the read, and flag that cited\ncontent may be stale.\n\n**Avoid redundant syncs within a session.** Once you have synced and\ninvalidated the cache, ignore any further "citadel is behind" messages that\narrive before you have done another citadel read \u2014 they are cached signals\ncaptured before your sync completed.\n\n**Manual status check**\n\n```\nnpx baller-maester status\n```\n\nExit codes:\n\n- **`0`** \u2014 every source is up to date.\n- **`1`** \u2014 at least one source is behind (remote advanced, manifest\n changed, or never-synced). Run the auto-sync policy above.\n- **`2`** \u2014 the status check itself failed. Surface to the user; proceed\n with a caveat that staleness cannot be verified.\n\nFor machine-readable output, pass `--json` and parse the NDJSON stream on\nstdout. The final line contains `{ "type": "summary", "upToDate": N,\n"behind": N, "failed": N }`.\n\n**On Claude Code specifically**, a `PreToolUse` hook installed by\n`maester skill install` runs the status check automatically before any\n`Read`, `Glob`, or `Grep` targeting a path under `{{baseDir}}/`. The\nhook debounces (default 300s, override with `MAESTER_SKILL_STATUS_TTL`) so\nthe check does not run more than once per session for routine reads.\n';
1447
1447
 
1448
1448
  // src/core/skill/templates/content/state-awareness.md
1449
1449
  var state_awareness_default = '## State awareness (canon vs draft)\n\nEvery citadel file may declare a publication state of `canon` (authoritative)\nor `draft` (work-in-progress). The state lives **inline** in the file using\nthe format\'s native convention:\n\n- **Markdown / MDX (`.md`, `.mdx`)** \u2014 `state` field inside YAML frontmatter\n at the top of the file:\n ```\n ---\n state: canon\n ---\n ```\n- **HTML (`.html`, `.htm`)** \u2014 first-line HTML comment:\n `<!-- state: canon -->`\n- **YAML / JSON (`.yaml`, `.yml`, `.json`)** \u2014 a top-level `state` key.\n- **Plain text (`.txt`)** \u2014 `state: canon` as the very first line.\n\nFiles without inline state default to `draft`.\n\n**Policy when answering from the citadel:**\n\n1. **Prefer `canon` files** as the authoritative source of truth. When a\n `canon` file answers the question, cite it and stop there.\n2. **`draft` files are informational only.** Cite them when no `canon`\n alternative exists, but mark the citation explicitly: "(draft \u2014 work in\n progress)" alongside the file path so the user knows the source is not yet\n stable.\n3. **Never mix the two without labeling.** If you draw from both canon and\n draft files in one answer, separate the two and tell the user which fact\n came from which kind of source.\n';
@@ -1483,7 +1483,7 @@ function buildClaudeMaesterBlock(version) {
1483
1483
  PreToolUse: [
1484
1484
  {
1485
1485
  matcher: "Read|Glob|Grep",
1486
- hooks: [{ type: "command", command: "npx maester skill runtime preread" }]
1486
+ hooks: [{ type: "command", command: "npx -y baller-maester skill runtime preread" }]
1487
1487
  }
1488
1488
  ]
1489
1489
  }
@@ -1729,7 +1729,7 @@ function decideAction2(existing, previousVersion, newVersion, newContent) {
1729
1729
  }
1730
1730
 
1731
1731
  // src/core/skill/templates/content/connector-policy-fallback.md
1732
- var connector_policy_fallback_default = "## Connector tools (traveling maesters)\n\nThis citadel may expose one or more **traveling maesters** as connectors. Your\nagent platform does not speak MCP, so connector operations are reached via the\nfallback CLI:\n\n```\nnpx maester connector list\nnpx maester connector exec <connector-name> <operation> [--key value]...\n```\n\n- `connector list` prints the configured connectors and the operations they\n expose.\n- `connector exec` invokes an operation and writes a JSON envelope to stdout.\n Exit code `0` is success, `1` is a connector-level failure (auth, remote\n error, invalid args), `2` is an invocation-level error (no such connector,\n no citadel.yaml).\n\nTreat the data the same way as MCP tool output: live, point-in-time, cite\nspecific identifiers, flag freshness when it isn't `up-to-date`, and don't\nassume undocumented fields.\n";
1732
+ var connector_policy_fallback_default = "## Connector tools (traveling maesters)\n\nThis citadel may expose one or more **traveling maesters** as connectors. Your\nagent platform does not speak MCP, so connector operations are reached via the\nfallback CLI:\n\n```\nnpx baller-maester connector list\nnpx baller-maester connector exec <connector-name> <operation> [--key value]...\n```\n\n- `connector list` prints the configured connectors and the operations they\n expose.\n- `connector exec` invokes an operation and writes a JSON envelope to stdout.\n Exit code `0` is success, `1` is a connector-level failure (auth, remote\n error, invalid args), `2` is an invocation-level error (no such connector,\n no citadel.yaml).\n\nTreat the data the same way as MCP tool output: live, point-in-time, cite\nspecific identifiers, flag freshness when it isn't `up-to-date`, and don't\nassume undocumented fields.\n";
1733
1733
 
1734
1734
  // src/core/skill/templates/shells/agents-md.ts
1735
1735
  var PREAMBLE = `# AGENTS.md
@@ -2518,7 +2518,7 @@ function validateTag(value) {
2518
2518
 
2519
2519
  // package.json
2520
2520
  var package_default = {
2521
- version: "0.4.0"};
2521
+ version: "0.4.1"};
2522
2522
  var PACKAGE_VERSION = package_default.version;
2523
2523
 
2524
2524
  // src/core/skill/version.ts
@@ -2755,7 +2755,7 @@ async function runInit(ctx) {
2755
2755
  ctx.prompts.log.info(`Remember to set these env vars before syncing: ${summary}`);
2756
2756
  }
2757
2757
  await maybeInstallSkill(ctx, baseDir ?? DEFAULT_BASE_DIR);
2758
- ctx.prompts.outro("Next: run `npx maester sync` to fetch your sources.");
2758
+ ctx.prompts.outro("Next: run `npx baller-maester sync` to fetch your sources.");
2759
2759
  return 0;
2760
2760
  } catch (err) {
2761
2761
  if (err instanceof PromptCancelledError) {
@@ -3946,7 +3946,7 @@ function summarize(result) {
3946
3946
  return parts.join(", ");
3947
3947
  }
3948
3948
  function buildHookResponse(verdict) {
3949
- const detail = verdict.verdict === "failed" ? `Citadel status check failed: ${verdict.summary}. Proceed with the read and flag that cited content may be stale; do not retry sync in a loop.` : `Citadel is behind (${verdict.summary}). Auto-sync policy: run \`npx maester sync\`, then \`rm -f .maester/.skill-cache.json\` to invalidate this hook's cache, then proceed with the read. Do not prompt the user \u2014 sync is read-only against the configured remotes.`;
3949
+ const detail = verdict.verdict === "failed" ? `Citadel status check failed: ${verdict.summary}. Proceed with the read and flag that cited content may be stale; do not retry sync in a loop.` : `Citadel is behind (${verdict.summary}). Auto-sync policy: run \`npx baller-maester sync\`, then \`rm -f .maester/.skill-cache.json\` to invalidate this hook's cache, then proceed with the read. Do not prompt the user \u2014 sync is read-only against the configured remotes.`;
3950
3950
  const response = {
3951
3951
  hookSpecificOutput: {
3952
3952
  hookEventName: "PreToolUse",
@@ -5098,7 +5098,7 @@ function maybeRenderHelpVersionBanner(argv) {
5098
5098
  const wantsVersion = tail.includes("--version") || tail.includes("-V");
5099
5099
  if (!wantsHelp && !wantsVersion) return;
5100
5100
  const theming = createTheming();
5101
- const subtitle = wantsVersion ? "v0.4.0 \xB7 living specs" : "living specs \xB7 v0.4.0";
5101
+ const subtitle = wantsVersion ? "v0.4.1 \xB7 living specs" : "living specs \xB7 v0.4.1";
5102
5102
  const banner = bannerForContext(theming, readColumns(), subtitle);
5103
5103
  if (banner.length > 0) {
5104
5104
  process.stdout.write(`${banner}
@@ -5116,7 +5116,7 @@ function toExitCode(value) {
5116
5116
  }
5117
5117
  function buildProgram() {
5118
5118
  const program = new Command();
5119
- program.name("maester").description("Aggregate documentation from many sources into one citadel.").version("0.4.0", "-V, --version", "Print the maester version.").option("--verbose", "Show verbose output").option("--quiet", "Suppress all output except errors").option("--json", "Emit machine-readable JSON output (one object per line)").option("--color", "Force colored output (overrides auto-detection)").option("--no-color", "Disable colored output (overrides auto-detection)").option("--theme <theme>", "Theme override: 'dark' or 'light'").option("--no-welcome", "Suppress the first-run welcome banner").enablePositionalOptions(false).allowExcessArguments(false);
5119
+ program.name("maester").description("Aggregate documentation from many sources into one citadel.").version("0.4.1", "-V, --version", "Print the maester version.").option("--verbose", "Show verbose output").option("--quiet", "Suppress all output except errors").option("--json", "Emit machine-readable JSON output (one object per line)").option("--color", "Force colored output (overrides auto-detection)").option("--no-color", "Disable colored output (overrides auto-detection)").option("--theme <theme>", "Theme override: 'dark' or 'light'").option("--no-welcome", "Suppress the first-run welcome banner").enablePositionalOptions(false).allowExcessArguments(false);
5120
5120
  registerConnector(program, () => buildContext(extractFlags(program.opts())));
5121
5121
  registerInit(program, () => buildContext(extractFlags(program.opts())));
5122
5122
  registerMcp(program, () => buildContext(extractFlags(program.opts())));
@@ -5154,7 +5154,7 @@ async function runNoArgs(ctx) {
5154
5154
  const roles = detectRoles(ctx.repoRoot.path);
5155
5155
  const showWelcome = !roles.hasCitadel && !roles.hasMaester && !ctx.flags.noWelcome;
5156
5156
  if (showWelcome) {
5157
- const banner = bannerForContext(ctx.theming, readColumns(), "living specs \xB7 v0.4.0");
5157
+ const banner = bannerForContext(ctx.theming, readColumns(), "living specs \xB7 v0.4.1");
5158
5158
  if (banner.length > 0) {
5159
5159
  process.stdout.write(`${banner}
5160
5160