@aiwg/cli 2026.8.12 → 2026.8.13

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.
@@ -674,7 +674,7 @@ const SESSION_RELOAD_NOTICE = {
674
674
  rationale: 'OpenCode loads agent files on session start and does not hot-reload.',
675
675
  },
676
676
  hermes: {
677
- action: 'In an active Hermes session, run /reload-skills to pick up new skills in ~/.hermes/skills/ and /reload-mcp to pick up MCP server changes (~/.hermes/config.yaml) — both are in-session slash commands, no chat restart needed. Restart the chat only as a fallback if the slash commands are unavailable.',
677
+ action: 'In an active Hermes session, run /reload-skills to pick up new skills in $HERMES_HOME/skills/ and /reload-mcp to pick up MCP server changes ($HERMES_HOME/config.yaml) — both are in-session slash commands, no chat restart needed. Restart the chat only as a fallback if the slash commands are unavailable.',
678
678
  rationale: 'Hermes loads skills and MCP config at session start (verified in hermes_cli/commands.py:178 and hermes_cli/config.py:1228). The /reload-skills and /reload-mcp slash commands re-scan in place; /reload-mcp prompts for confirmation by default.',
679
679
  symptom: 'Until reloaded, newly deployed kernel skills are missing from `hermes skills list` and unreachable via natural-language invocation; new MCP servers (incl. AIWG) are missing from the tool surface.',
680
680
  },
@@ -3249,11 +3249,12 @@ export class UseHandler {
3249
3249
  // Collect deployment counts for registry persistence and the final
3250
3250
  // orchestrated report. Presentation happens once, after verification, so
3251
3251
  // users do not see a second competing summary.
3252
- let counts = { agents: 0, commands: 0, skills: 0, rules: 0, behaviors: 0 };
3253
- if (quiet) {
3254
- const paths = getProviderPaths(provider);
3255
- counts = await countDeployedArtifacts(target, paths, provider);
3256
- }
3252
+ //
3253
+ // Counts are always populated from the on-disk artifacts so that the
3254
+ // registry record written below (#621) reflects the real deploy even on
3255
+ // a verbose run — the prior `if (quiet)` guard left the record
3256
+ // `{agents: 0, commands: 0, skills: 0, rules: 0}` on `-v` runs.
3257
+ const counts = await countDeployedArtifacts(target, paths, provider);
3257
3258
  // Deploy CI workflow files when --ci-hooks-enabled is set (#661)
3258
3259
  if (ciHooksEnabled) {
3259
3260
  await deployCiHooks({ frameworkRoot, framework, target, dryRun });
@@ -11,6 +11,8 @@
11
11
  */
12
12
  import { homedir } from 'node:os';
13
13
  import * as path from 'node:path';
14
+ import { resolveHermesHome, resolveHermesHomePath } from '../providers/hermes-home.js';
15
+ export const hermesHome = resolveHermesHome;
14
16
  /**
15
17
  * User-scope deploy paths per provider per ADR-4 §2. Each path is absolute
16
18
  * (rooted in os.homedir()) so the orchestrator's existing path-join logic
@@ -159,7 +161,10 @@ export const USER_SCOPE_PATHS = {
159
161
  },
160
162
  hermes: {
161
163
  agents: '',
162
- skills: path.join(homedir(), '.hermes', 'skills'),
164
+ // #2119: honor HERMES_HOME so `--scope user` deploys land under the same
165
+ // root the running Hermes session scans, matching the hermes provider's
166
+ // paths.skills resolution.
167
+ skills: resolveHermesHomePath('skills'),
163
168
  commands: '',
164
169
  rules: '',
165
170
  behaviors: '',
@@ -11,6 +11,7 @@ import { basename, dirname, isAbsolute, join, resolve } from 'path';
11
11
  import { homedir } from 'os';
12
12
  import { z } from 'zod';
13
13
  import { getProviderDefinition, normalizeProviderDefinitionId, } from '../providers/provider-definitions.js';
14
+ import { resolveHermesHome } from '../providers/hermes-home.js';
14
15
  import { OPERATIONAL_SHOW_TYPES } from '../artifacts/types.js';
15
16
  import { projectAiwgPath } from '../config/project-artifacts.js';
16
17
  import { appendAiwgSourceTrackBlock } from './project-local-gitignore.js';
@@ -339,6 +340,14 @@ function resolveProviderSkillsRoot(provider, projectDir, homeDir) {
339
340
  const definition = getProviderDefinition(normalized);
340
341
  if (!definition)
341
342
  throw new Error(`Provider definition unavailable for '${provider}'`);
343
+ if (normalized === 'hermes') {
344
+ return {
345
+ provider: normalized,
346
+ root: resolve(resolveHermesHome(homeDir), 'skills'),
347
+ emulated: false,
348
+ global: true,
349
+ };
350
+ }
342
351
  const configured = definition.paths.kernelSkills ?? definition.paths.artifacts.skills;
343
352
  if (!configured)
344
353
  throw new Error(`Provider '${normalized}' has no supported skill or aggregation target`);
@@ -0,0 +1,20 @@
1
+ import { homedir } from 'node:os';
2
+ import * as path from 'node:path';
3
+ /** Match Hermes Agent's process-level HERMES_HOME resolution contract. */
4
+ export function resolveHermesHome(userHome = homedir()) {
5
+ const configured = (process.env.HERMES_HOME || '').trim();
6
+ if (configured)
7
+ return configured;
8
+ if (process.platform === 'win32') {
9
+ const localAppData = (process.env.LOCALAPPDATA || '').trim();
10
+ return localAppData
11
+ ? path.join(localAppData, 'hermes')
12
+ : path.join(userHome, 'AppData', 'Local', 'hermes');
13
+ }
14
+ return path.join(userHome, '.hermes');
15
+ }
16
+ /** Resolve a path exactly as a Hermes process would consume HERMES_HOME. */
17
+ export function resolveHermesHomePath(...segments) {
18
+ return path.resolve(resolveHermesHome(), ...segments);
19
+ }
20
+ //# sourceMappingURL=hermes-home.js.map
@@ -1,6 +1,7 @@
1
1
  import { z } from 'zod';
2
2
  import { homedir } from 'os';
3
3
  import { join } from 'path';
4
+ import { resolveHermesHomePath } from './hermes-home.js';
4
5
  import { getProviderCapabilities, } from './capability-matrix.js';
5
6
  const ArtifactPathsSchema = z.object({
6
7
  agents: z.string().nullable(),
@@ -464,7 +465,7 @@ const BUILT_IN_SEEDS = [
464
465
  id: 'hermes',
465
466
  aliases: [],
466
467
  builtIn: true,
467
- surfaces: { primary: 'hermes', compatibility: [], precedence: ['AGENTS.md', '.hermes.md', '~/.hermes/skills/'], related: [] },
468
+ surfaces: { primary: 'hermes', compatibility: [], precedence: ['.hermes.md', 'AGENTS.md', resolveHermesHomePath('skills')], related: [] },
468
469
  detection: {
469
470
  env: [],
470
471
  process: ['hermes'],
@@ -474,18 +475,18 @@ const BUILT_IN_SEEDS = [
474
475
  artifacts: {
475
476
  agents: null,
476
477
  commands: null,
477
- skills: '~/.hermes/.aiwg/skills',
478
+ skills: resolveHermesHomePath('skills', '.aiwg'),
478
479
  rules: null,
479
480
  behaviors: null,
480
481
  },
481
- kernelSkills: '~/.hermes/skills',
482
+ kernelSkills: resolveHermesHomePath('skills'),
482
483
  configFile: 'AGENTS.md',
483
484
  contextFiles: { aiwgMd: true, agentsMd: true, claudeMdHook: false, hookFile: '.hermes.md', contextFile: 'AGENTS.md' },
484
485
  },
485
486
  smithPaths: {
486
487
  agents: null,
487
488
  commands: null,
488
- skills: '~/.hermes/skills',
489
+ skills: resolveHermesHomePath('skills'),
489
490
  rules: null,
490
491
  fileExtension: '.md',
491
492
  configFile: 'AGENTS.md',
@@ -15,6 +15,7 @@ import { getProviderDefinition, normalizeProviderDefinitionId, } from '../provid
15
15
  import { AGENT_SKILLS_SIDECAR_SCHEMA, AIWG_SKILL_CONTROL_FIELDS, createAgentSkillSidecar, projectStrictAgentSkill, } from './agent-skills.js';
16
16
  import { getImportedAgentSkill } from './importer.js';
17
17
  import { validateAgentSkillContent } from './validator.js';
18
+ import { resolveHermesHomePath } from '../providers/hermes-home.js';
18
19
  export const AGENT_SKILL_MANAGED_MARKER = '.aiwg-managed';
19
20
  export const AGENT_SKILL_DEPLOYMENT_SIDECAR = '.aiwg-agent-skill.json';
20
21
  const MARKER_CONTENT = 'aiwg-agent-skill-v1\n';
@@ -78,7 +79,10 @@ function resolvePolicy(target, options) {
78
79
  reasons.push('applies the Factory description guidance before strict validation');
79
80
  break;
80
81
  case 'hermes':
81
- reasons.push('uses the user-global ~/.hermes/skills bundle surface with strict managed ownership markers');
82
+ if (options.homeDir === undefined) {
83
+ root = resolveHermesHomePath('skills');
84
+ }
85
+ reasons.push('uses the active HERMES_HOME skills surface with strict managed ownership markers');
82
86
  break;
83
87
  case 'openhuman':
84
88
  status = 'projected';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiwg/cli",
3
- "version": "2026.8.12",
3
+ "version": "2026.8.13",
4
4
  "description": "Lightweight AIWG CLI for signed, versioned web-backed resources.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -210,7 +210,13 @@ function resolveCommandMirrorDir(provider, target) {
210
210
  // Closest conventional location for providers whose primary command
211
211
  // surface is MCP/aggregation rather than a documented command directory.
212
212
  if (provider.name === 'hermes') {
213
- return path.join(os.homedir(), '.hermes', 'commands');
213
+ // #2119: HERMES_HOME is the single source of truth the running Hermes
214
+ // runtime uses to locate its files — resolve the home the same way the
215
+ // provider does instead of hardcoding $HOME/.hermes.
216
+ const hermesHome = typeof provider.getHermesHome === 'function'
217
+ ? provider.getHermesHome()
218
+ : path.join(os.homedir(), '.hermes');
219
+ return path.resolve(hermesHome, 'commands');
214
220
  }
215
221
 
216
222
  return null;
@@ -530,7 +536,7 @@ Providers (all deploy agents, commands, skills, and rules):
530
536
  devin - Devin Desktop (preferred; aliases: devin-desktop, windsurf)
531
537
  Paths: .windsurf/agents/, .windsurf/workflows/, .windsurf/skills/, .windsurf/rules/
532
538
  hermes - Hermes Agent (MCP-based integration)
533
- Skills: ~/.hermes/skills/ (user-global) | Agents: AGENTS.md (lean routing guide)
539
+ Skills: $HERMES_HOME/skills/ (user-global; defaults to ~/.hermes/skills/) | Agents: AGENTS.md
534
540
  Commands/Rules: served via MCP, not file-deployed
535
541
 
536
542
  Modes:
@@ -463,9 +463,36 @@ export function injectPlatformInContent(content, targetPlatform) {
463
463
  return open + fmLines.join('\n') + close + body;
464
464
  }
465
465
 
466
- /** @deprecated Use injectPlatformInContent instead */
466
+ /**
467
+ * Remove the `platforms:` field from a SKILL.md frontmatter block.
468
+ *
469
+ * Hermes (and other providers that treat `platforms:` as an OS gate —
470
+ * linux / macos / windows) hide any skill whose value isn't a recognized
471
+ * OS. AIWG source skills use the field as a *provider* restriction token
472
+ * (`[all]`, provider names), which is the opposite meaning, so deployed
473
+ * copies destined for such providers must drop the field entirely. An
474
+ * absent field is the documented "compatible with all platforms" default.
475
+ */
467
476
  export function stripPlatformsFromContent(content) {
468
- return injectPlatformInContent(content, null);
477
+ const fmMatch = content.match(/^(---\r?\n)([\s\S]*?)(\r?\n---(?:\r?\n|$))([\s\S]*)$/);
478
+ if (!fmMatch) return content;
479
+
480
+ const [, open, fm, close, body] = fmMatch;
481
+ let updated = fm.replace(/^platforms:[^\r\n]*(?:\r?\n|$)/m, '');
482
+
483
+ // Multi-line list form:
484
+ // platforms:
485
+ // - claude-code
486
+ // - hermes
487
+ if (updated === fm) {
488
+ updated = fm.replace(
489
+ /^platforms:[ \t]*\r?\n(?:[ \t]+-[ \t]+\S[^\r\n]*(?:\r?\n|$))*/m,
490
+ '',
491
+ );
492
+ }
493
+
494
+ if (updated === fm) return content;
495
+ return open + updated + close + body;
469
496
  }
470
497
 
471
498
  /**
@@ -5,8 +5,8 @@
5
5
  * server is an optional enrichment hook that Hermes can call when configured.
6
6
  *
7
7
  * What this provider DOES deploy:
8
- * - Skills: ~/.hermes/skills/ (user-global, for agentic skills callable by Hermes)
9
- * - AGENTS.md: project root (lean routing guide that Hermes loads on every turn)
8
+ * - Skills: $HERMES_HOME/skills/ (user-global, for agentic skills callable by Hermes)
9
+ * - AGENTS.md: project root (full AIWG routing guide referenced by .hermes.md)
10
10
  *
11
11
  * What this provider SKIPS:
12
12
  * - Commands: Hermes has no AIWG slash-command file surface
@@ -22,6 +22,7 @@ let fs;
22
22
  try { const gfs = _require('graceful-fs'); gfs.gracefulify(realFs); fs = realFs; } catch { fs = realFs; }
23
23
  import path from 'path';
24
24
  import os from 'os';
25
+ import { parse as parseYaml, stringify as stringifyYaml } from 'yaml';
25
26
  import {
26
27
  ensureDir,
27
28
  listMdFiles,
@@ -37,8 +38,61 @@ import {
37
38
  collectFrameworkArtifacts,
38
39
  listOnDemandRuleFiles,
39
40
  renderOnDemandRuleSection,
41
+ stripPlatformsFromContent,
40
42
  } from './base.mjs';
41
43
 
44
+ // ============================================================================
45
+ // Hermes home resolution (HERMES_HOME) — #2119
46
+ // ============================================================================
47
+ //
48
+ // Mirrors `hermes_constants.get_hermes_home()` in the Hermes Agent runtime.
49
+ // Resolution order (upstream, hermes_constants.py:114-):
50
+ // 1. A context-local override installed in-process via
51
+ // set_hermes_home_override() — AIWG runs in a separate node process and
52
+ // cannot observe that token, so step 1 is intentionally NOT part of the
53
+ // cross-process contract.
54
+ // 2. The process `HERMES_HOME` env var.
55
+ // 3. The platform-native default:
56
+ // win32 → %LOCALAPPDATA%/hermes (falls back to %USERPROFILE%/AppData/
57
+ // Local/hermes when LOCALAPPDATA is unset)
58
+ // other → $HOME/.hermes
59
+ //
60
+ // AIWG reads the env var when `getHermesHome()` is invoked; the module-level
61
+ // constants below lock in the value captured at first use so every consumer
62
+ // (paths, kernels, orchestrate, legacy migration) sees the same resolved root
63
+ // for the life of the process.
64
+
65
+ /**
66
+ * Resolve the Hermes home directory.
67
+ *
68
+ * Honors HERMES_HOME the way the running Hermes runtime does, so that
69
+ * `aiwg use --provider hermes` writes skills under the same root the live
70
+ * session scans. See #2119 — before this helper, the provider hardcoded
71
+ * `os.homedir()/.hermes` and any operator running Hermes under a non-default
72
+ * HERMES_HOME (multi-profile, hermes-role wrappers, dev containers) got a
73
+ * silently divergent deployment.
74
+ */
75
+ export function getHermesHome() {
76
+ const env = (process.env.HERMES_HOME || '').trim();
77
+ if (env) {
78
+ // Match upstream's Path(env) contract exactly. Hermes does not expand a
79
+ // leading `~` or resolve relative values here; both remain relative to the
80
+ // process working directory when the path is consumed.
81
+ return env;
82
+ }
83
+ if (process.platform === 'win32') {
84
+ const localAppData = (process.env.LOCALAPPDATA || '').trim();
85
+ return localAppData
86
+ ? path.join(localAppData, 'hermes')
87
+ : path.join(os.homedir(), 'AppData', 'Local', 'hermes');
88
+ }
89
+ return path.join(os.homedir(), '.hermes');
90
+ }
91
+
92
+ // The value captured below is the target any running Hermes session with the
93
+ // same environ would use as its scan root.
94
+ const HERMES_HOME = getHermesHome();
95
+
42
96
  // ============================================================================
43
97
  // Provider Configuration
44
98
  // ============================================================================
@@ -49,10 +103,15 @@ export const aliases = [];
49
103
  export const paths = {
50
104
  agents: 'AGENTS.md', // Aggregated routing guide at project root
51
105
  commands: '', // Not applicable — no AIWG slash-command file surface
52
- // Standard skills under ~/.hermes/skills/.aiwg/ — child of Hermes's scanned root,
53
- // recursively discovered (verified `agent/skill_utils.py:478-489`, os.walk follows
54
- // subdirs except .git/.github/.hub/.archive). Kernel skills land in the parent.
55
- skills: path.join(os.homedir(), '.hermes', 'skills', '.aiwg'),
106
+ // Standard skills under <HERMES_HOME>/skills/.aiwg/ — child of Hermes's
107
+ // scanned root, recursively discovered (verified `agent/skill_utils.py:478-489`,
108
+ // os.walk follows subdirs except .git/.github/.hub/.archive).
109
+ //
110
+ // HERMES_HOME honors the process env var and falls back to $HOME/.hermes
111
+ // (win32: %LOCALAPPDATA%/hermes), matching hermes_constants.get_hermes_home().
112
+ // #2119: previously hardcoded os.homedir() — wrong for any operator running
113
+ // Hermes under a non-default HERMES_HOME (multi-profile, hermes-role, etc.).
114
+ skills: path.resolve(HERMES_HOME, 'skills', '.aiwg'),
56
115
  rules: '', // Inlined into AGENTS.md + reachable via `aiwg show rule`
57
116
  };
58
117
 
@@ -60,12 +119,17 @@ export const paths = {
60
119
  // Standard skills land in the .aiwg/ subdirectory under the same root —
61
120
  // Hermes recursively walks the skill root (verified against upstream v0.13.0,
62
121
  // `agent/skill_utils.py:478-489`).
63
- export const kernelSkillsPath = path.join(os.homedir(), '.hermes', 'skills');
122
+ export const kernelSkillsPath = path.resolve(HERMES_HOME, 'skills');
123
+
124
+ // Resolved home directory this provider's paths were computed against.
125
+ // Consumers (deploy verification, doctor, status) should use this rather than
126
+ // re-reading os.homedir() to stay consistent with the deploy target.
127
+ export const hermesHome = HERMES_HOME;
64
128
 
65
129
  export const support = {
66
130
  agents: 'aggregated', // Agents aggregated into lean AGENTS.md
67
131
  commands: 'none', // Hermes has no AIWG slash-command file surface
68
- skills: 'native', // ~/.hermes/skills/ is native Hermes skill location
132
+ skills: 'native', // $HERMES_HOME/skills/ is the native skill location
69
133
  rules: 'agents-md+cli', // compressed in AGENTS.md; full bodies via CLI/MCP
70
134
  };
71
135
 
@@ -77,6 +141,43 @@ export const capabilities = {
77
141
  homeDirectoryDeploy: true, // Skills deploy to home dir
78
142
  };
79
143
 
144
+ /**
145
+ * Project portable Agent Skills metadata into Hermes's native frontmatter.
146
+ *
147
+ * The portable Agent Skills contract restricts `metadata` values to strings,
148
+ * while Hermes expects tags at `metadata.hermes.tags`. AIWG stores the tag
149
+ * list as a comma-separated `metadata.hermes-tags` string in source and
150
+ * performs the provider-specific projection only in the deployed copy.
151
+ */
152
+ export function transformHermesSkillContent(content) {
153
+ const stripped = stripPlatformsFromContent(content);
154
+ const match = stripped.match(/^---\r?\n([\s\S]*?)\r?\n---(\r?\n|$)/);
155
+ if (!match) return stripped;
156
+
157
+ let frontmatter;
158
+ try {
159
+ frontmatter = parseYaml(match[1]);
160
+ } catch {
161
+ return stripped;
162
+ }
163
+ if (!frontmatter || typeof frontmatter !== 'object' || Array.isArray(frontmatter)) {
164
+ return stripped;
165
+ }
166
+
167
+ const metadata = frontmatter.metadata;
168
+ const encodedTags = metadata && typeof metadata === 'object' && !Array.isArray(metadata)
169
+ ? metadata['hermes-tags']
170
+ : undefined;
171
+ if (typeof encodedTags !== 'string') return stripped;
172
+
173
+ const tags = encodedTags.split(',').map((tag) => tag.trim()).filter(Boolean);
174
+ delete metadata['hermes-tags'];
175
+ metadata.hermes = { tags };
176
+
177
+ const body = stripped.slice(match[0].length);
178
+ return `---\n${stringifyYaml(frontmatter).trimEnd()}\n---\n${body}`;
179
+ }
180
+
80
181
  // ============================================================================
81
182
  // Model Mapping (not applicable — Hermes uses local Ollama models)
82
183
  // ============================================================================
@@ -203,7 +304,8 @@ export function generateAgentsMd(agentCount, skillCount, targetDir, opts) {
203
304
  const header = `# AIWG Integration
204
305
 
205
306
  AIWG connected through file-based deployment. Native Hermes skills are available
206
- at \`~/.hermes/skills/\` (kernel) and \`~/.hermes/skills/.aiwg/\` (standard).
307
+ at \`$HERMES_HOME/skills/\` (kernel) and \`$HERMES_HOME/skills/.aiwg/\` (standard).
308
+ When unset, \`HERMES_HOME\` defaults to the platform-native Hermes home.
207
309
  Use \`aiwg discover\` and \`aiwg show <type> <name>\` for the on-demand catalog.
208
310
  The MCP sidecar (\`aiwg mcp serve\`) is optional.
209
311
 
@@ -285,10 +387,12 @@ AIWG project context lives in \`AGENTS.md\` (this file is a thin Hermes pointer)
285
387
 
286
388
  **Routing**: see \`AGENTS.md\` in this directory.
287
389
  **MCP**: AIWG is reachable via \`mcp_aiwg_*\` tools.
288
- **Skills**: kernel skills at \`~/.hermes/skills/\`; standard skills at \`~/.hermes/skills/.aiwg/\`.
390
+ **Skills**: kernel skills at \`$HERMES_HOME/skills/\`; standard skills at \`$HERMES_HOME/skills/.aiwg/\`.
391
+ When unset, \`HERMES_HOME\` defaults to the platform-native Hermes home.
289
392
 
290
- Hermes loads \`.hermes.md\` before \`AGENTS.md\` (first-match-wins). Keep this
291
- file minimal Hermes will load AGENTS.md content next via the routing chain.
393
+ Hermes loads only \`.hermes.md\` when it is present (first-match-wins). Keep
394
+ this file minimal; its routing instruction tells the agent to read \`AGENTS.md\`
395
+ when the full AIWG project context is needed.
292
396
  `;
293
397
  const destPath = path.join(targetDir, '.hermes.md');
294
398
  if (dryRun) {
@@ -309,8 +413,8 @@ file minimal — Hermes will load AGENTS.md content next via the routing chain.
309
413
  *
310
414
  * Skills are user-global in Hermes, deployed once, available in all
311
415
  * projects. Kernel routing per the cross-provider pattern:
312
- * - kernel skills → ~/.hermes/skills/ (platform-native, always-loaded)
313
- * - standard → ~/.hermes/skills/.aiwg/ (recursively walked by Hermes)
416
+ * - kernel skills → $HERMES_HOME/skills/ (platform-native, always-loaded)
417
+ * - standard → $HERMES_HOME/skills/.aiwg/ (recursively walked by Hermes)
314
418
  */
315
419
  export function deploySkills(skillDirs, opts) {
316
420
  const standardDestDir = paths.skills;
@@ -375,7 +479,18 @@ export async function deploy(opts) {
375
479
  allSkillDirs.push(...(artifacts.skills || []));
376
480
 
377
481
  if (allSkillDirs.length > 0) {
378
- deploySkills(allSkillDirs, opts);
482
+ // Hermes's skill loader reads `platforms:` as an OS gate
483
+ // (linux / macos / windows). AIWG's shared deploy path injects
484
+ // `[hermes]` into that field, which then filters every skill out
485
+ // on Linux. Strip the field post-injection — hermes documents
486
+ // "absent field = all platforms" as its default. This leaves the
487
+ // other providers' `transformSkillMd` pipeline untouched.
488
+ const skillOpts = {
489
+ ...opts,
490
+ provider: 'hermes', // ensure deploySkillDir's injectPlatform branch runs
491
+ transformSkillMd: transformHermesSkillContent,
492
+ };
493
+ deploySkills(allSkillDirs, skillOpts);
379
494
  } else if (!opts.quiet) {
380
495
  console.log(' No skills found to deploy');
381
496
  }
@@ -424,7 +539,7 @@ export async function deploy(opts) {
424
539
 
425
540
  // ── aiwg-orchestrate convenience skill (#1242) ──────────────────────────────
426
541
  // First-deploy-only copy: lays down the delegate_task wrapper at
427
- // ~/.hermes/skills/aiwg-orchestrate/SKILL.md if it isn't already present.
542
+ // $HERMES_HOME/skills/aiwg-orchestrate/SKILL.md if it isn't already present.
428
543
  // The skill provides ~95% per-workflow context reduction by routing AIWG
429
544
  // calls through Hermes's `delegate_task` instead of inline MCP. Idempotent
430
545
  // on re-run — operator edits are preserved across `aiwg use` invocations.
@@ -435,10 +550,35 @@ export async function deploy(opts) {
435
550
  // ── Post-deployment hint ───────────────────────────────────────────────────
436
551
  if (!opts.quiet) {
437
552
  console.log('');
553
+ console.log(`Skills root: ${kernelSkillsPath}`);
438
554
  console.log('Rules are in AGENTS.md as compressed directives; full bodies via `aiwg show rule <name>`.');
439
- console.log('Optional: configure ~/.hermes/config.yaml to connect AIWG MCP server.');
555
+ console.log('Optional: configure config.yaml to connect AIWG MCP server.');
440
556
  console.log('See: docs/integrations/hermes-quickstart.md (optional MCP setup)');
441
557
  }
558
+
559
+ // ── Consumer visibility check (#2119) ──────────────────────────────────────
560
+ // The running Hermes runtime reads skills from get_skills_dir(), which is
561
+ // HERMES_HOME/skills (hermes_constants.get_hermes_home: context-local
562
+ // override → HERMES_HOME env → $HOME/.hermes). AIWG can observe only ITS
563
+ // own process environment, so this check reports which root AIWG resolved
564
+ // and warns when that root may be invisible to a Hermes session the
565
+ // operator launched elsewhere (custom HERMES_HOME, hermes-role wrapper).
566
+ //
567
+ // Before #2119 this was a silent failure: AIWG wrote to $HOME/.hermes/skills
568
+ // unconditionally and `aiwg status --probe` reported healthy even when the
569
+ // live session scanned a different HERMES_HOME.
570
+ if (!dryRun && !opts.quiet) {
571
+ const envHome = (process.env.HERMES_HOME || '').trim();
572
+ const hermesActive = Boolean(
573
+ process.env.HERMES_SESSION_ID ||
574
+ (process.env.AI_AGENT || '').includes('hermes')
575
+ );
576
+ if (hermesActive && !envHome) {
577
+ console.warn(`Warning: HERMES_HOME is not set in this AIWG process — deployed skills landed under \`${kernelSkillsPath}\` (default $HOME/.hermes).`);
578
+ console.warn(' If your running Hermes session uses a non-default HERMES_HOME, it CANNOT see these skills.');
579
+ console.warn(' Re-run with the matching value: HERMES_HOME=<that value> aiwg use --provider hermes');
580
+ }
581
+ }
442
582
  }
443
583
 
444
584
  // ============================================================================
@@ -584,7 +724,7 @@ export function migrateLegacySkillPath(opts) {
584
724
  // ============================================================================
585
725
 
586
726
  /**
587
- * Copy the aiwg-orchestrate skill template to ~/.hermes/skills/ on first
727
+ * Copy the aiwg-orchestrate skill template to $HERMES_HOME/skills/ on first
588
728
  * deploy. Skip if a SKILL.md already exists — preserves operator edits and
589
729
  * any prior version they're running. Errors during the copy are non-fatal:
590
730
  * the rest of the deploy must succeed even if the home dir is read-only or
@@ -631,7 +771,11 @@ function deployAiwgOrchestrateSkill(srcRoot, opts) {
631
771
  try {
632
772
  ensureDir(destDir);
633
773
  const content = fs.readFileSync(templatePath, 'utf8');
634
- fs.writeFileSync(destPath, content, 'utf8');
774
+ // Defensive: the template is hermes-specific and should not carry a
775
+ // `platforms:` field (hermes reads that as an OS gate). Strip it if a
776
+ // future template regression reintroduces one so this orphan path
777
+ // stays consistent with the main deploy pipeline.
778
+ fs.writeFileSync(destPath, transformHermesSkillContent(content), 'utf8');
635
779
  if (!opts.quiet) {
636
780
  console.log(` Installed aiwg-orchestrate to ${destPath} (delegate_task wrapper, 95% context reduction)`);
637
781
  }