xtrm-tools 2.0.0 → 2.0.2

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
@@ -12,6 +12,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
12
12
  ### Added
13
13
 
14
14
  - **`AGENTS.md` — bd (beads) issue tracking section**: Comprehensive reference for the `bd` CLI covering session protocol, issue creation, dependency management, search/view commands, advanced features (`agent`, `gate`, `mol`, `audit`), and a bd vs TodoWrite decision table.
15
+ - **`xtrm install project all` / `xtrm install project '*'`**: Non-interactive install of every available project skill into the current repository.
16
+
17
+ ### Fixed
18
+
19
+ - **Claude-only target detection**: `xtrm install all` now enumerates Claude Code targets only, instead of surfacing stale Gemini/Qwen/Agents paths.
20
+ - **Project-skill install-all coverage**: Added regression tests to verify merged hook counts and copied assets across all shipped project skills.
15
21
 
16
22
  ### Roadmap
17
23
 
@@ -38,6 +44,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
38
44
  #### Installation Commands
39
45
  - **`xtrm install`**: Global installation (replaces `sync`)
40
46
  - **`xtrm install all` / `xtrm install '*'`**: Non-interactive global install across all known targets
47
+ - **`~/.agents/skills`**: Skills-only target added so the installed `skills/` tree is available without touching hooks/config
48
+ - **`xtrm install project all` / `xtrm install project '*'`**: Install every project-specific skill package into the current repository
41
49
  - **`xtrm install project <tool-name>`**: Install project-specific skill package
42
50
  - **`xtrm install project list`**: List available project skills with descriptions
43
51
 
package/README.md CHANGED
@@ -221,7 +221,7 @@ Task intake and service routing for Docker service projects.
221
221
  | Skill | Description | Hook Type |
222
222
  |-------|-------------|-----------|
223
223
  | `service-skills-set` | Docker service expertise — gives Claude persistent knowledge about your services | SessionStart, PreToolUse, PostToolUse |
224
- | `tdd-guard` | Enforce Test-Driven Development — blocks implementation until failing tests exist | PreToolUse, UserPromptSubmit |
224
+ | `tdd-guard` | Enforce Test-Driven Development — blocks implementation until failing tests exist | SessionStart, PreToolUse, UserPromptSubmit |
225
225
  | `ts-quality-gate` | TypeScript/ESLint/Prettier quality gate — runs on every edit, auto-fixes issues | PostToolUse |
226
226
  | `py-quality-gate` | Python ruff/mypy quality gate — linting, formatting, and type checking | PostToolUse |
227
227
  | `main-guard` | Git branch protection — blocks direct edits to main/master branches | PreToolUse |
@@ -239,6 +239,8 @@ xtrm install project tdd-guard # TDD enforcement
239
239
  xtrm install project ts-quality-gate # TypeScript quality
240
240
  xtrm install project py-quality-gate # Python quality
241
241
  xtrm install project main-guard # Git branch protection
242
+ xtrm install project all # Install every available project skill
243
+ xtrm install project '*' # Same as above; quote to avoid shell expansion
242
244
  ```
243
245
 
244
246
  **Note:** Project skills install Claude hooks and skills into your project's `.claude/` directory. Some skills require additional manual setup (e.g., installing npm packages or Python dependencies). Always read the documentation at `.claude/docs/<skill>-readme.md` after installation.
@@ -315,10 +317,10 @@ The main command. Detects your agent environments, calculates what's changed, an
315
317
 
316
318
  ```bash
317
319
  xtrm install # interactive — prompts for targets and confirmation
318
- xtrm install all # install to all known targets without target prompt
320
+ xtrm install all # install to all Claude Code targets without target prompt
319
321
  xtrm install '*' # same as above; quote * to avoid shell expansion
320
322
  xtrm install --dry-run # preview what WOULD change, write nothing
321
- xtrm install all --dry-run -y # non-interactive preview across all targets
323
+ xtrm install all --dry-run -y # non-interactive preview across all Claude targets
322
324
  xtrm install -y # skip confirmation prompts (CI-friendly)
323
325
  xtrm install --prune # also remove system items no longer in the repo
324
326
  xtrm install --backport # reverse direction: copy drifted local edits → repo
@@ -330,7 +332,7 @@ xtrm install --backport # reverse direction: copy drifted local edits → re
330
332
  - **boxen summary card**: Completion summary with green/yellow border based on drift
331
333
  - **Themed output**: Semantic colors (success, error, warning, muted, accent) via `theme.ts`
332
334
  - **Interactive consent**: Multiselect for MCP servers (space to toggle, all pre-selected)
333
- - **Auto-detection**: Scans `~/.claude`, `~/.gemini`, `~/.qwen`, `~/.agents/skills` automatically
335
+ - **Auto-detection**: Scans Claude Code targets automatically (`~/.claude`, `%APPDATA%/Claude` on Windows) plus the `.agents/skills` cache for skills-only sync
334
336
  - **Inline sync**: `status` command offers to apply sync immediately after showing changes
335
337
  - **Single confirmation**: See full plan across all targets, confirm once
336
338
  - **Safety guards**: Prune mode aborts on read failures; clean errors (no stack traces)
@@ -339,15 +341,12 @@ xtrm install --backport # reverse direction: copy drifted local edits → re
339
341
 
340
342
  **What it syncs per target environment:**
341
343
 
342
- | Item | Claude | Gemini | Qwen | Agents (skills-only) |
343
- | --------------- | -------------------- | -------------------- | ------------------ | -------------------- |
344
- | `skills/` | ✅ copy/symlink | ✅ copy/symlink | ✅ copy/symlink | ✅ direct copy |
345
- | `hooks/` | ✅ copy/symlink | ✅ copy/symlink | ✅ copy/symlink | ❌ skipped |
346
- | `settings.json` | ✅ safe merge | ✅ safe merge | ✅ safe merge | ❌ skipped |
347
- | MCP servers | `mcp add` CLI | `mcp add` CLI | `mcp add` CLI | ❌ skipped |
348
- | Slash commands | auto-generated | `.toml` files | `.toml` files | ❌ skipped |
349
-
350
- **New in v1.7.0**: `~/.agents/skills` is now a first-class sync target for skills-only sync (no hooks/config/MCP).
344
+ | Item | Claude Code (full) | `~/.agents/skills` (skills-only) |
345
+ | --------------- | -------------------- | --------------------------------- |
346
+ | `skills/` | ✅ copy/symlink | ✅ direct copy |
347
+ | `hooks/` | ✅ copy/symlink | ❌ skipped |
348
+ | `settings.json` | ✅ safe merge | ❌ skipped |
349
+ | MCP servers | `mcp add` CLI | ❌ skipped |
351
350
 
352
351
  **Diff categories shown before sync:**
353
352
 
@@ -374,7 +373,7 @@ xtrm status --json # machine-readable output
374
373
  ```
375
374
 
376
375
  **Output includes (v1.7.0+)**:
377
- - Auto-detected environments: `~/.claude`, `~/.gemini`, `~/.qwen`, `~/.agents/skills`
376
+ - Auto-detected environments: Claude Code targets and the `~/.agents/skills` cache
378
377
  - cli-table3 formatted table with per-target change breakdown
379
378
  - Last synced time (relative: "3 hours ago")
380
379
  - Item counts from manifest (skills, hooks, config)
@@ -37026,22 +37026,25 @@ var Conf = class {
37026
37026
 
37027
37027
  // src/core/context.ts
37028
37028
  var import_prompts = __toESM(require_prompts3(), 1);
37029
- var config = new Conf({
37030
- projectName: "jaggers-config-manager",
37031
- defaults: {
37032
- syncMode: "copy"
37029
+ var config = null;
37030
+ function getConfig() {
37031
+ if (!config) {
37032
+ config = new Conf({
37033
+ projectName: "xtrm-cli",
37034
+ defaults: {
37035
+ syncMode: "copy"
37036
+ }
37037
+ });
37033
37038
  }
37034
- });
37039
+ return config;
37040
+ }
37035
37041
  function getCandidatePaths() {
37036
37042
  const home = import_os2.default.homedir();
37037
37043
  const appData = process.env.APPDATA;
37038
37044
  const isWindows3 = process.platform === "win32";
37039
37045
  const paths = [
37040
37046
  { label: ".claude", path: import_path.default.join(home, ".claude") },
37041
- { label: ".gemini", path: import_path.default.join(home, ".gemini") },
37042
- { label: ".qwen", path: import_path.default.join(home, ".qwen") },
37043
- { label: "~/.gemini/antigravity", path: import_path.default.join(home, ".gemini", "antigravity") },
37044
- { label: "~/.agents/skills", path: import_path.default.join(home, ".agents", "skills") }
37047
+ { label: ".agents/skills", path: import_path.default.join(home, ".agents", "skills") }
37045
37048
  ];
37046
37049
  if (isWindows3 && appData) {
37047
37050
  paths.push({ label: "Claude (AppData)", path: import_path.default.join(appData, "Claude") });
@@ -37062,6 +37065,7 @@ async function getContext(options = {}) {
37062
37065
  const candidates = getCandidatePaths();
37063
37066
  const directTargets = resolveTargets(selector, candidates);
37064
37067
  if (directTargets) {
37068
+ const activeConfig2 = getConfig();
37065
37069
  if (createMissingDirs) {
37066
37070
  for (const target of directTargets) {
37067
37071
  await import_fs_extra.default.ensureDir(target);
@@ -37069,10 +37073,11 @@ async function getContext(options = {}) {
37069
37073
  }
37070
37074
  return {
37071
37075
  targets: directTargets,
37072
- syncMode: config.get("syncMode"),
37073
- config
37076
+ syncMode: activeConfig2.get("syncMode"),
37077
+ config: activeConfig2
37074
37078
  };
37075
37079
  }
37080
+ const activeConfig = getConfig();
37076
37081
  for (const c of candidates) {
37077
37082
  const exists = await import_fs_extra.default.pathExists(c.path);
37078
37083
  const icon = exists ? kleur_default.green("\u25CF") : kleur_default.gray("\u25CB");
@@ -37108,12 +37113,12 @@ async function getContext(options = {}) {
37108
37113
  }
37109
37114
  return {
37110
37115
  targets: response.targets,
37111
- syncMode: config.get("syncMode"),
37112
- config
37116
+ syncMode: activeConfig.get("syncMode"),
37117
+ config: activeConfig
37113
37118
  };
37114
37119
  }
37115
37120
  function resetContext() {
37116
- config.clear();
37121
+ getConfig().clear();
37117
37122
  console.log(kleur_default.yellow("Configuration cleared."));
37118
37123
  }
37119
37124
 
@@ -40671,6 +40676,21 @@ function resolvePkgRoot() {
40671
40676
  }
40672
40677
  var PKG_ROOT = resolvePkgRoot();
40673
40678
  var PROJECT_SKILLS_DIR = import_path10.default.join(PKG_ROOT, "project-skills");
40679
+ async function getAvailableProjectSkills() {
40680
+ if (!await import_fs_extra10.default.pathExists(PROJECT_SKILLS_DIR)) {
40681
+ return [];
40682
+ }
40683
+ const entries = await import_fs_extra10.default.readdir(PROJECT_SKILLS_DIR);
40684
+ const skills2 = [];
40685
+ for (const entry of entries) {
40686
+ const entryPath = import_path10.default.join(PROJECT_SKILLS_DIR, entry);
40687
+ const stat = await import_fs_extra10.default.stat(entryPath);
40688
+ if (stat.isDirectory()) {
40689
+ skills2.push(entry);
40690
+ }
40691
+ }
40692
+ return skills2.sort();
40693
+ }
40674
40694
  function deepMergeHooks(existing, incoming) {
40675
40695
  const result = { ...existing };
40676
40696
  if (!result.hooks) result.hooks = {};
@@ -40789,26 +40809,41 @@ async function installProjectSkill(toolName, projectRootOverride) {
40789
40809
  }
40790
40810
  console.log(kleur_default.green(" \u2713 Installation complete!\n"));
40791
40811
  }
40812
+ async function installAllProjectSkills(projectRootOverride) {
40813
+ const skills2 = await getAvailableProjectSkills();
40814
+ if (skills2.length === 0) {
40815
+ console.log(kleur_default.dim(" No project skills available.\n"));
40816
+ return;
40817
+ }
40818
+ const projectRoot = projectRootOverride ?? getProjectRoot();
40819
+ console.log(kleur_default.bold(`
40820
+ Installing ${skills2.length} project skills:
40821
+ `));
40822
+ for (const skill of skills2) {
40823
+ console.log(kleur_default.dim(` \u2022 ${skill}`));
40824
+ }
40825
+ console.log("");
40826
+ for (const skill of skills2) {
40827
+ await installProjectSkill(skill, projectRoot);
40828
+ }
40829
+ }
40792
40830
  async function listProjectSkills() {
40793
- if (!await import_fs_extra10.default.pathExists(PROJECT_SKILLS_DIR)) {
40831
+ const entries = await getAvailableProjectSkills();
40832
+ if (entries.length === 0) {
40794
40833
  console.log(kleur_default.dim(" No project skills available.\n"));
40795
40834
  return;
40796
40835
  }
40797
- const entries = await import_fs_extra10.default.readdir(PROJECT_SKILLS_DIR);
40798
- const skills = [];
40836
+ const skills2 = [];
40799
40837
  for (const entry of entries) {
40800
- const entryPath = import_path10.default.join(PROJECT_SKILLS_DIR, entry);
40801
- const stat = await import_fs_extra10.default.stat(entryPath);
40802
- if (!stat.isDirectory()) continue;
40803
- const readmePath = import_path10.default.join(entryPath, "README.md");
40838
+ const readmePath = import_path10.default.join(PROJECT_SKILLS_DIR, entry, "README.md");
40804
40839
  let description = "No description available";
40805
40840
  if (await import_fs_extra10.default.pathExists(readmePath)) {
40806
40841
  const readmeContent = await import_fs_extra10.default.readFile(readmePath, "utf8");
40807
40842
  description = extractReadmeDescription(readmeContent).slice(0, 80);
40808
40843
  }
40809
- skills.push({ name: entry, description });
40844
+ skills2.push({ name: entry, description });
40810
40845
  }
40811
- if (skills.length === 0) {
40846
+ if (skills2.length === 0) {
40812
40847
  console.log(kleur_default.dim(" No project skills available.\n"));
40813
40848
  return;
40814
40849
  }
@@ -40819,12 +40854,13 @@ async function listProjectSkills() {
40819
40854
  colWidths: [25, 60],
40820
40855
  style: { head: [], border: [] }
40821
40856
  });
40822
- for (const skill of skills) {
40857
+ for (const skill of skills2) {
40823
40858
  table.push([kleur_default.white(skill.name), kleur_default.dim(skill.description)]);
40824
40859
  }
40825
40860
  console.log(table.toString());
40826
40861
  console.log(kleur_default.bold("\n\nUsage:\n"));
40827
40862
  console.log(kleur_default.dim(" xtrm install project <skill-name> Install a project skill"));
40863
+ console.log(kleur_default.dim(" xtrm install project all Install all project skills"));
40828
40864
  console.log(kleur_default.dim(" xtrm install project list List available skills\n"));
40829
40865
  console.log(kleur_default.bold("Example:\n"));
40830
40866
  console.log(kleur_default.dim(" xtrm install project tdd-guard\n"));
@@ -40843,6 +40879,10 @@ function createInstallProjectCommand() {
40843
40879
  const installProjectCmd = new Command("project").description("Install a project-specific skill package");
40844
40880
  installProjectCmd.argument("<tool-name>", "Name of the project skill to install").action(async (toolName) => {
40845
40881
  try {
40882
+ if (toolName === "all" || toolName === "*") {
40883
+ await installAllProjectSkills();
40884
+ return;
40885
+ }
40846
40886
  await installProjectSkill(toolName);
40847
40887
  } catch (err) {
40848
40888
  console.error(kleur_default.red(`
@@ -54958,10 +54998,10 @@ ${kleur_default.cyan("COMMANDS:")}
54958
54998
 
54959
54999
  Examples:
54960
55000
  xtrm install # Interactive install with confirmation
54961
- xtrm install all # Install to all configured targets without prompting
55001
+ xtrm install all # Install to all Claude Code targets without prompting
54962
55002
  xtrm install '*' # Same as above; quote to avoid shell expansion
54963
55003
  xtrm install --dry-run # Preview what would be installed
54964
- xtrm install all --dry-run -y # CI-friendly preview across all targets
55004
+ xtrm install all --dry-run -y # CI-friendly preview across all Claude targets
54965
55005
  xtrm install -y # Non-interactive install
54966
55006
 
54967
55007
  ${kleur_default.bold("install project")} <tool-name>
@@ -54973,6 +55013,8 @@ ${kleur_default.cyan("COMMANDS:")}
54973
55013
  Examples:
54974
55014
  xtrm install project tdd-guard # Install TDD Guard
54975
55015
  xtrm install project ts-quality-gate # Install TypeScript Quality Gate
55016
+ xtrm install project all # Install every available project skill
55017
+ xtrm install project '*' # Same as above; quote to avoid shell expansion
54976
55018
 
54977
55019
  ${kleur_default.bold("install project list")}
54978
55020
  List all available project skills with descriptions and usage examples.
@@ -55007,6 +55049,13 @@ ${kleur_default.cyan("PROJECT SKILLS:")}
55007
55049
  \u2022 ${kleur_default.white("py-quality-gate")} \u2014 Python ruff/mypy quality gate (PostToolUse)
55008
55050
  \u2022 ${kleur_default.white("main-guard")} \u2014 Git branch protection (PreToolUse)
55009
55051
 
55052
+ ${kleur_default.cyan("INSTALL TARGETS:")}
55053
+
55054
+ xtrm-tools v2.0.0 installs into Claude Code targets and the `.agents / skills` cache:
55055
+ • ~/.claude
55056
+ • %APPDATA%/Claude on Windows
55057
+ • ~/.agents/skills (skills-only copy)
55058
+
55010
55059
  ${kleur_default.cyan("ARCHITECTURE:")}
55011
55060
 
55012
55061
  xtrm-tools v2.0.0 supports Claude Code exclusively. This decision was made
@@ -55018,9 +55067,9 @@ ${kleur_default.cyan("ARCHITECTURE:")}
55018
55067
 
55019
55068
  ${kleur_default.cyan("RESOURCES:")}
55020
55069
 
55021
- \u2022 Repository: https://github.com/Jaggerxtrm/xtrm-tools
55022
- \u2022 Documentation: See README.md in the repository
55023
- \u2022 Report Issues: https://github.com/Jaggerxtrm/xtrm-tools/issues
55070
+ Repository: https://github.com/Jaggerxtrm/xtrm-tools
55071
+ Documentation: See README.md in the repository
55072
+ Report Issues: https://github.com/Jaggerxtrm/xtrm-tools/issues
55024
55073
 
55025
55074
  ${kleur_default.dim("Run 'xtrm <command> --help' for more information on a specific command.")}
55026
55075
  `);