craftar 0.37.0 → 0.37.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/README.md +44 -1
- package/dist/cli.js +2 -2
- package/dist/cli.js.map +1 -1
- package/dist/core/doctor.js +49 -10
- package/dist/core/doctor.js.map +1 -1
- package/dist/core/mcp-server.js +42 -0
- package/dist/core/mcp-server.js.map +1 -1
- package/dist/emitters/claude-code.js +3 -3
- package/dist/emitters/claude-code.js.map +1 -1
- package/dist/emitters/kiro.js +3 -3
- package/dist/emitters/kiro.js.map +1 -1
- package/dist/emitters/shared.js +5 -0
- package/dist/emitters/shared.js.map +1 -1
- package/dist/importers/claude-code.js +4 -1
- package/dist/importers/claude-code.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -90,7 +90,7 @@ From then on, change a rule in `forge/ingredients/rules/<name>/rule.md`, run `cr
|
|
|
90
90
|
| `craftar explain <path>` | Which ingredient, recipe chain, target and origin produced a file and, for an ingredient with sections, which layer filled each one: `sections flavors (profile globex), extra (default)` (`default`, `profile <p>` or `workspace`). For an ingredient whose files cite parameters, each cited key with the layer that filled it for this ingredient: `params org (profile globex), port (default), root (unset)` — `default` (the ingredient's own), `recipe <name>`, `profile <p>`, `workspace` (either workspace file) or `unset`. `AGENTS.md` gets neither line: it is not one ingredient. |
|
|
91
91
|
| `craftar ls` | Recipes and ingredients resolved for this workspace. |
|
|
92
92
|
| `craftar workspaces [--fetch] [--json]` | Every workspace a writing `sync` registered on this machine, sorted by path: name, path, profile, stack (`slot=recipe`), targets, the Forge, the last sync, and a status computed now — `up to date`, `outdated` (`new`, `update` or `orphan` pending), `drift` (a `drift`, `orphan-drift` or `collision`), `no lock`, `missing` (directory or `craftar.yaml` gone) or `error` (it no longer loads, plans or reads its lock, or its `sync` would refuse for a declared parameter with no value — the reason is one of the row's warnings: `sync refused: declared parameter(s) with no value: <keys>`), plus `forge moved` when the Forge's commit differs from the lock's. Read without the network: a remote Forge comes from the cache; `--fetch` fetches first. Each row's warnings print after the table, prefixed with its path. Times are UTC. Writes nothing. Exits 0 whenever the table prints. Takes no `--workspace`. `--json` is a contract: `{registry, fetch, workspaces, warnings}`. |
|
|
93
|
-
| `craftar doctor [--workspace <dir>] [--fetch] [--strict] [--json]` | Checks this machine — Node ≥ 22, `git`, `$CRAFTAR_HOME`, the registry (entries whose workspace is gone), the Forge cache (its size, entries nothing names, a fetch that never completed) — and, when run inside one (the current directory, or `--workspace`), this workspace: the configuration and its Forge (cached, fetched or pinned), whether the workspace sits inside its Forge, the plan's warnings, declared parameters with no value, the `${NAME}` variables a written MCP server expects and this environment lacks — `mcp-env` reads `authEnv` next to `${NAME}` (names only, never values), `templates` when the profile declares a `templates` block (an empty `templates:` line declares none), `dotnet` when that block is accepted and declares a `dotnet-new` entry (the .NET SDK answering `dotnet --version` at the workspace root); neither goes above `warn` — `agents-md-size` when the plan writes `AGENTS.md` (`ok` within the budget or with the budget off, `warn` over the default budget, `error` over an explicit one — the cause is reported there, not by the plan's warnings), under `agents-md.primary` `agents-md-primary` (a `CLAUDE.md` that keeps Claude Code from reading `AGENTS.md`, at the root or above it; at most `warn`) and `kiro-resources` (a hand-written Kiro agent `resources` entry the plan does not write: `error`, or `warn` when the file is only on disk), the lock, `status`, and registration; in a workspace with a service: `docker` — Docker and its Compose plugin, 2.20.0 or later — and `services-env` — the variables a fragment needs. One line per finding — `ok`, `warn` or `error` — each problem with the command that fixes it; each cause is reported once. A declared parameter with no value is an `error` when a file the plan writes cites it (`sync` refuses that workspace) and a `warn` when nothing cites it. It only reports: nothing is written to a workspace, a lock or the registry, and the Forge cache is used as every reader uses it (`.used` stamps, a rebuilt tree; under `--fetch`, a fetch and the cleanup of trees unused for 14 days). Read without the network; `--fetch` fetches a remote Forge first. Exits 1 on an `error`, or on a `warn` under `--strict`; an explicit `--workspace` without `craftar.yaml` exits 1, while the current directory without one runs the machine checks only. `--json` is a contract: `{version, workspace, fetch, strict, checks: [{id, scope, level, message, fix}], summary}`. In a schema 4 Forge `mcp-env` reads the server as rendered, so a `${NAME}` a param brings into `env` is checked. |
|
|
93
|
+
| `craftar doctor [--workspace <dir>] [--fetch] [--strict] [--json]` | Checks this machine — Node ≥ 22, `git`, `$CRAFTAR_HOME`, the registry (entries whose workspace is gone), the Forge cache (its size, entries nothing names, a fetch that never completed) — and, when run inside one (the current directory, or `--workspace`), this workspace: the configuration and its Forge (cached, fetched or pinned), whether the workspace sits inside its Forge, the plan's warnings, declared parameters with no value, the `${NAME}` variables a written MCP server expects and this environment lacks — `mcp-env` reads `authEnv` next to `${NAME}` (names only, never values), `mcp-launcher` when the plan writes an MCP server (a `warn` per server whose command is `cmd` (or a path to `cmd.exe`) and whose first argument is `/c` or `/k` — it starts on Windows only — naming the server and the files, never its command or arguments; see *MCP servers on Windows and Linux*), `templates` when the profile declares a `templates` block (an empty `templates:` line declares none), `dotnet` when that block is accepted and declares a `dotnet-new` entry (the .NET SDK answering `dotnet --version` at the workspace root); neither goes above `warn` — `agents-md-size` when the plan writes `AGENTS.md` (`ok` within the budget or with the budget off, `warn` over the default budget, `error` over an explicit one — the cause is reported there, not by the plan's warnings), under `agents-md.primary` `agents-md-primary` (a `CLAUDE.md` that keeps Claude Code from reading `AGENTS.md`, at the root or above it; at most `warn`) and `kiro-resources` (a hand-written Kiro agent `resources` entry the plan does not write: `error`, or `warn` when the file is only on disk), the lock, `status`, and registration; in a workspace with a service: `docker` — Docker and its Compose plugin, 2.20.0 or later — and `services-env` — the variables a fragment needs. One line per finding — `ok`, `warn` or `error` — each problem with the command that fixes it; each cause is reported once. A declared parameter with no value is an `error` when a file the plan writes cites it (`sync` refuses that workspace) and a `warn` when nothing cites it. It only reports: nothing is written to a workspace, a lock or the registry, and the Forge cache is used as every reader uses it (`.used` stamps, a rebuilt tree; under `--fetch`, a fetch and the cleanup of trees unused for 14 days). Read without the network; `--fetch` fetches a remote Forge first. Exits 1 on an `error`, or on a `warn` under `--strict`; an explicit `--workspace` without `craftar.yaml` exits 1, while the current directory without one runs the machine checks only. `--json` is a contract: `{version, workspace, fetch, strict, checks: [{id, scope, level, message, fix}], summary}`. In a schema 4 Forge `mcp-env` reads the server as rendered, so a `${NAME}` a param brings into `env` is checked. |
|
|
94
94
|
| `craftar templates list [--json] [--workspace <dir>] [--offline]` | What the profile's `templates` block declares: one line per package — kind, package, version or `latest`, source or `default feeds` — and one per entry this craftar cannot install. Runs no program and does not know what is installed. `--json` is a contract: `{profile, templates: [{entry, kind, package, version, source, installable}], warnings}`. Exits 1 when the block is refused, naming the position and the rule, never a value. |
|
|
95
95
|
| `craftar templates install [--dry-run] [--force] [--interactive] [--workspace <dir>] [--offline]` | Runs `dotnet new install <pkg>[::<version>]... [--add-source <source>]` once per `dotnet-new` entry, in the profile's order, at the workspace root. `--dry-run` prints the exact lines, needs no `dotnet` and exits 0 over an accepted block, even with a skipped entry. A real run exits 1 when the .NET SDK is not available. Both exit 1 when the block is refused or a package ID exists as a file or folder at the workspace root. `--force` and `--interactive` are passed to `dotnet` as typed, never otherwise; `--interactive` also gives `dotnet` the terminal's input. An entry that fails does not stop the others; a real run exits 1 when any failed or was skipped. When an entry declares a `source` and the workspace root has no `NuGet.Config`, a warning line is printed before the runs. Writes nothing in the workspace, the lock or the registry; nothing else (`sync`, `init`, `doctor`) runs it. See *Project templates*. |
|
|
96
96
|
| `craftar mcp [--workspace <dir>] [--fetch] [--allow-writes]` | An MCP server (Model Context Protocol, revision `2025-06-18`) over stdin and stdout, started by an AI coder as a subprocess — not typed in a terminal, where it refuses to start. It serves **one workspace**, the one it was started in, through seven tools that only read: `status`, `diff` (the pending files; with a `path`, that file's diff), `explain`, `recipes`, `ingredients`, `targets` and `doctor` (the checks about this workspace, plus Node and `git`; like the command, it asks `docker compose` for its version when the workspace has a service and `dotnet` for its version when the profile declares a template it can install) — each answers with the data its command prints, as one JSON object. Without `--allow-writes` nothing is written to the workspace, the lock, `craftar.yaml`, the registry or the Forge. With `--allow-writes` it also serves three writing tools — `sync` (a `craftar sync` that skips and lists every `drift`, `orphan-drift` and `collision`; a remote Forge needs `--fetch`), `add_recipe` and `remove_recipe` (an `add recipe` / `remove recipe`, which do not sync) — and each call writes only when this machine's registry holds the consent `craftar mcp allow-writes` recorded for this workspace, for the `forge`, `ref` and `profile` the server started with; otherwise the call is an error and nothing is written. A writing result carries what the command prints, never a file's content or the registry's path. The Forge cache is used as every reader uses it (`.used` stamps, a rebuilt tree; under `--fetch`, a fetch and the cleanup of trees unused for 14 days). Without `--fetch` no tool touches the network: a remote Forge is read from the cached copy, and the answer says so — for `doctor` in its `forge` check, for the others but `targets` in `warnings` unless `ref` is a full commit SHA, which cannot go stale (`targets` reads only which targets the configuration resolves, never fetches and carries no such line). The `forge`, `ref` and `profile` of `craftar.yaml` / `craftar.local.yaml` are fixed when the server starts: after one of them changes, every tool but `targets` answers with an error until it is put back or the server is restarted (`targets` answers the matrix unmarked, with a warning). A hand-edited `.mcp.json`, `.kiro/settings/mcp.json` or `.claude/settings.craftar.example.json` is never returned — a note stands in for its diff. A line that is not JSON, or longer than 1 MiB, is not answered — the server reads on. Exits 0 when stdin closes; exits 1, with one line on stderr, in a terminal, without a `craftar.yaml`, or when the configuration does not read. See *Craftar inside the AI coder*. |
|
|
@@ -356,6 +356,43 @@ steering = `inclusion` frontmatter + `GENERATED` banner + rule body, with `.clau
|
|
|
356
356
|
|
|
357
357
|
Conversion of hooks/subagents to other tools is out of scope here: the plan is to delegate that to [rulesync](https://github.com/dyoshikawa/rulesync) rather than reimplement it.
|
|
358
358
|
|
|
359
|
+
## MCP servers on Windows and Linux
|
|
360
|
+
|
|
361
|
+
`.mcp.json` and `.kiro/settings/mcp.json` are committed, and one file serves every machine. Write an `npx` server in
|
|
362
|
+
the **portable form** — the program as `command`, the rest as `args`:
|
|
363
|
+
|
|
364
|
+
```yaml
|
|
365
|
+
# ingredients/mcp/example/ingredient.yaml
|
|
366
|
+
type: mcp
|
|
367
|
+
name: example
|
|
368
|
+
server:
|
|
369
|
+
command: npx
|
|
370
|
+
args: ["-y", "@acme/example-mcp"]
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
Not `command: cmd` with `args: ["/c", "npx", …]`: that form was once required on native Windows, starts on Windows only
|
|
374
|
+
(`cmd` does not exist on Linux or macOS), and is no longer needed — a bare `npx` starts on native Windows in both tools
|
|
375
|
+
(measured below). Move every such server to the portable form in the Forge; the next `sync` writes `update` for the two
|
|
376
|
+
files in every workspace of that Forge. craftar never rewrites it for you: `craftar doctor` warns for each one
|
|
377
|
+
(`mcp-launcher`) and `craftar import` warns when it reads one, keeping it as written. `command: "cmd /c npx"` as one
|
|
378
|
+
string starts in neither tool.
|
|
379
|
+
|
|
380
|
+
What was measured, by running each tool (stdio servers; Claude Code read from a project `.mcp.json`, Kiro CLI from the
|
|
381
|
+
workspace `.kiro/settings/mcp.json`):
|
|
382
|
+
|
|
383
|
+
| | Claude Code | Kiro CLI |
|
|
384
|
+
|---|---|---|
|
|
385
|
+
| `command: npx`, `args: ["-y", <package>]`, on native Windows | starts | starts |
|
|
386
|
+
| `command: cmd`, `args: ["/c", "npx", …]`, on native Windows | starts | starts |
|
|
387
|
+
| `command: "cmd /c npx"` (one string), on native Windows | fails | fails |
|
|
388
|
+
| `${VAR}` / `${VAR:-default}` in `command` | expanded | expanded |
|
|
389
|
+
| `${VAR}` / `${VAR:-default}` in `args` | expanded | **not expanded** — the text reaches the program as written |
|
|
390
|
+
|
|
391
|
+
Measured on Linux on 2026-10-23 (Claude Code 2.1.309, Kiro CLI 2.29.1) and on native Windows on 2026-10-24 (Claude Code
|
|
392
|
+
2.1.310, Kiro CLI 2.33.1, Node 22); the `${VAR:-default}` cases agree on both, and plain `${VAR}` was run on Linux only. The Kiro IDE and macOS were not measured. Later
|
|
393
|
+
versions may differ. So a `${…}` default in `args` works for Claude Code and not for Kiro: keep variables out of `args`
|
|
394
|
+
of a server Kiro writes.
|
|
395
|
+
|
|
359
396
|
## Design rules learned from the previous generator
|
|
360
397
|
|
|
361
398
|
These come straight from workspaces that removed an earlier generator, and the code enforces them:
|
|
@@ -533,6 +570,12 @@ Later: rulesync bridge for Codex/Kimi/Cursor specifics; `craftar docs validate`;
|
|
|
533
570
|
|
|
534
571
|
## Upgrading
|
|
535
572
|
|
|
573
|
+
### to 0.37.1
|
|
574
|
+
|
|
575
|
+
- **`craftar doctor` gains the check `mcp-launcher`**, present when the plan writes an MCP server: `ok`, or a `warn` per server whose command is `cmd` (or a path to `cmd.exe`) and whose first argument is `/c` or `/k` (it starts on Windows only — see *MCP servers on Windows and Linux*). A workspace that writes such a server now has a warning, so **`doctor --strict` exits 1 where it exited 0**; move the server to the portable form in the Forge. `doctor --json` gains the id; the `doctor` tool of `craftar mcp` returns it too.
|
|
576
|
+
- **`craftar import` warns** when it reads such a server from `.mcp.json`, and writes it as read.
|
|
577
|
+
- No emitted byte, lock, registry entry or `--json` shape changes, and no exit code but `doctor --strict`'s.
|
|
578
|
+
|
|
536
579
|
### to 0.37.0
|
|
537
580
|
|
|
538
581
|
- **New flag: `craftar ui --allow-writes`** (see *Commands*). Without it, `craftar ui` is the read-only dashboard — the page, its script and stylesheet and the three terminal lines byte for byte, and every answer too except one: `GET /api/workspaces` gains the key `writes` (`false`). With it the terminal's first line reads `writes allowed` in place of `read-only`, a second line names the two actions, and the page offers Sync and Discard edit for a workspace with a path Forge.
|
package/dist/cli.js
CHANGED
|
@@ -58,7 +58,7 @@ process.stdout.on("error", (e) => { if (e.code === "EPIPE")
|
|
|
58
58
|
*/
|
|
59
59
|
const DIFF_EXIT_CODE_FETCH_MODE = "sync";
|
|
60
60
|
const program = new Command();
|
|
61
|
-
program.name("craftar").description("Craft, sync and convert AI-coding workspace harnesses.").version("0.37.
|
|
61
|
+
program.name("craftar").description("Craft, sync and convert AI-coding workspace harnesses.").version("0.37.1");
|
|
62
62
|
const SAVE_PLAN_GATE = { what: "plan", who: "unify", outside: "save plans outside the Forge", never: "unify never overwrites" };
|
|
63
63
|
const REPORT_GATE = { what: "report", who: "import", outside: "write the report outside the Forge", never: "import never overwrites" };
|
|
64
64
|
program
|
|
@@ -1307,7 +1307,7 @@ program
|
|
|
1307
1307
|
/* ---------------------------------------------------------------- doctor */
|
|
1308
1308
|
program
|
|
1309
1309
|
.command("doctor")
|
|
1310
|
-
.description("Check this machine (Node, git, $CRAFTAR_HOME, the registry, the Forge cache) and, inside one, this workspace (configuration and Forge, plan, declared parameters, MCP environment variables, the profile's templates block and dotnet when it has one, lock, status, registration): one line per finding — ok, warn or error — each with its fix. Reports only; reads without the network unless --fetch")
|
|
1310
|
+
.description("Check this machine (Node, git, $CRAFTAR_HOME, the registry, the Forge cache) and, inside one, this workspace (configuration and Forge, plan, declared parameters, MCP environment variables, Windows-only MCP launchers, the profile's templates block and dotnet when it has one, lock, status, registration): one line per finding — ok, warn or error — each with its fix. Reports only; reads without the network unless --fetch")
|
|
1311
1311
|
// No commander default: an explicit --workspace without craftar.yaml exits 1, the current directory without one runs the machine checks (spec 24 §4.1).
|
|
1312
1312
|
.option("--workspace <dir>", "the workspace to check (default: the current directory, when it holds craftar.yaml)")
|
|
1313
1313
|
.option("--fetch", "fetch a remote Forge first instead of reading the cached copy", false)
|