fraim-hub 2.0.284 → 2.0.285

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.
@@ -15,11 +15,12 @@ const child_process_1 = require("child_process");
15
15
  const path_1 = __importDefault(require("path"));
16
16
  const managed_agent_paths_1 = require("../../utils/managed-agent-paths");
17
17
  const command_resolution_1 = require("../../mcp/command-resolution");
18
- // Codex is FRAIM's first Hub-compatible CLI with a managed-install fallback
19
- // (npm install -g into FRAIM's portable Node when no system install is
20
- // found). Extend this list as claude/gemini/copilot gain the same fallback.
18
+ // CLIs with a managed-install fallback (npm install -g into FRAIM's portable
19
+ // Node when no system install is found). Add gemini/copilot here when their
20
+ // installManagedAgent wiring lands.
21
21
  const MANAGED_CLIS = [
22
22
  { id: 'codex', label: 'Codex', command: 'codex' },
23
+ { id: 'claude-code', label: 'Claude Code', command: 'claude' },
23
24
  ];
24
25
  // Windows cannot CreateProcess a `.cmd`/`.bat` file directly (spawnSync on a
25
26
  // resolved absolute `.cmd` path throws EINVAL) — it must go through cmd.exe,
@@ -83,6 +83,13 @@ function cleanupOrphanedManagedShims() {
83
83
  const removed = [];
84
84
  for (const basename of basenames) {
85
85
  const candidate = path_1.default.join(nodeRoot, basename);
86
+ // Guard: only delete the flat-dir shim once the versioned dir already has
87
+ // an equivalent. Without this, upgrading users lose their shims before a
88
+ // reinstall has had a chance to place them in the versioned dir — the
89
+ // agent CLI disappears entirely until the next explicit reinstall.
90
+ const versionedEquiv = path_1.default.join(versionedDir, basename);
91
+ if (!fs_1.default.existsSync(versionedEquiv))
92
+ continue;
86
93
  try {
87
94
  if (fs_1.default.statSync(candidate).isFile()) {
88
95
  fs_1.default.unlinkSync(candidate);
@@ -63,7 +63,7 @@ exports.PERSONA_CAPABILITY_BUNDLES = {
63
63
  personaKey: 'swen',
64
64
  bundleId: 'persona-swen-core',
65
65
  catalogMetadata: buildCatalogMetadata('swen', ['feature-implementation', 'technical-design', 'code-refactoring']),
66
- protectedJobs: ['feature-implementation', 'technical-design', 'implementation-design-review', 'code-refactoring', 'pr-iteration', 'mobile-app-development', 'mcp-server-creation', 'cloud-application-deployment', 'cloud-cost-optimization', 'cloud-performance-diagnosis', 'route-llm-spend-to-cloud-credits', 'set-up-cloud-cost-alerts', 'gitlabs-to-github', 'system-migration', 'cross-cloud-migration', 'data-pipeline-design', 'data-quality-monitoring', 'data-platform-architecture', 'write-dev-docs', 'database-schema-design', 'create-architecture', 'project-scaffolding', 'codebase-analysis-and-ideation', 'github-org-setup', 'google-workspace-setup', 'mobile-app-rejection-response', 'mobile-app-submission', 'publish-mcp-app', 'application-replication-workflow'],
66
+ protectedJobs: ['feature-implementation', 'technical-design', 'implementation-design-review', 'code-refactoring', 'pr-iteration', 'mobile-app-development', 'cloud-application-deployment', 'cloud-cost-optimization', 'cloud-performance-diagnosis', 'route-llm-spend-to-cloud-credits', 'set-up-cloud-cost-alerts', 'gitlabs-to-github', 'system-migration', 'cross-cloud-migration', 'data-pipeline-design', 'data-quality-monitoring', 'data-platform-architecture', 'write-dev-docs', 'database-schema-design', 'create-architecture', 'project-scaffolding', 'codebase-analysis-and-ideation', 'github-org-setup', 'google-workspace-setup', 'mobile-app-rejection-response', 'mobile-app-submission', 'application-replication-workflow'],
67
67
  protectedAliases: ['software-engineering', 'implementation'],
68
68
  defaultHireMode: 'job',
69
69
  lockCopy: 'Hire SWEn to unlock software-engineering delivery for this request.'
@@ -262,6 +262,23 @@ exports.PERSONA_CAPABILITY_BUNDLES = {
262
262
  protectedAliases: ['banking-audit', 'kyc-audit', 'audit'],
263
263
  defaultHireMode: 'job',
264
264
  lockCopy: 'Hire AUDITya to unlock banking evidence audit work for this request.'
265
+ },
266
+ aida: {
267
+ personaKey: 'aida',
268
+ bundleId: 'persona-aida-core',
269
+ catalogMetadata: buildCatalogMetadata('aida', ['create-ai-agent', 'author-ai-evals', 'enable-web-mcp']),
270
+ protectedJobs: [
271
+ 'create-ai-agent',
272
+ 'author-ai-evals',
273
+ 'enable-web-mcp',
274
+ 'evaluate-ai-agent',
275
+ 'mcp-server-creation',
276
+ 'publish-mcp-app',
277
+ 'create-hub-configured-agent',
278
+ ],
279
+ protectedAliases: ['ai-engineering', 'agent-engineering', 'ai-agents'],
280
+ defaultHireMode: 'job',
281
+ lockCopy: 'Hire AIda to unlock AI agent design, MCP enablement, eval authoring, and agent evaluation work for this request.'
265
282
  }
266
283
  };
267
284
  const PROTECTED_JOB_TO_PERSONA = new Map();
@@ -275,7 +292,6 @@ for (const bundle of Object.values(exports.PERSONA_CAPABILITY_BUNDLES)) {
275
292
  // as "free") so the Hub attributes them to FRAIMworker, but they are never
276
293
  // hire-gated because FRAIMworker is not a purchasable persona.
277
294
  const GENERIC_WORKER_OWNED_JOBS = new Set([
278
- 'create-hub-configured-agent',
279
295
  'contribute-to-fraim',
280
296
  'file-fraim-issue',
281
297
  'praise-fraim',
@@ -19,32 +19,14 @@ exports.getPersonaHireAmountCents = getPersonaHireAmountCents;
19
19
  * GET /api/personas/catalog.
20
20
  */
21
21
  exports.PERSONA_HIRE_CATALOG = {
22
- maestro: {
23
- displayName: 'MAESTRO',
24
- role: 'Full-Brained AI Employee',
25
- emoji: '',
26
- gradient: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 50%, #d946ef 100%)',
27
- blurb: 'One AI employee who can take a job from any function and ship it back with evidence. You set the direction. You sign off on what ships. Maestro does the work.',
28
- jobPriceCents: 24900, // $249full-time only; job mode maps to same price
29
- fulltimePriceCents: 24900, // $249/mo
30
- },
31
- beza: {
32
- displayName: 'BeZa',
33
- role: 'AI Business Strategist',
34
- emoji: '🧭',
35
- gradient: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)',
36
- blurb: 'Turns ideas into structured business plans, validates founder-market fit, and pressure-tests strategy.',
37
- jobPriceCents: 990,
38
- fulltimePriceCents: 4990,
39
- },
40
- pam: {
41
- displayName: 'PaM',
42
- role: 'AI Product Manager',
43
- emoji: '📋',
44
- gradient: 'linear-gradient(135deg, #8b5cf6 0%, #d946ef 100%)',
45
- blurb: 'Owns specs, PRDs, technical design, issue prep, and the path from idea to shippable artifact.',
46
- jobPriceCents: 790,
47
- fulltimePriceCents: 4990,
22
+ aida: {
23
+ displayName: 'AIda',
24
+ role: 'AI Engineer',
25
+ emoji: '\u{1F9E0}',
26
+ gradient: 'linear-gradient(135deg, #4f46e5 0%, #06b6d4 50%, #10b981 100%)',
27
+ blurb: 'Designs production AI agents, connects them to tools and data, and proves their behavior with evals before deployment.',
28
+ jobPriceCents: 1290, // placeholderproduct owner approval required before launch
29
+ fulltimePriceCents: 5990, // placeholder — product owner approval required before launch
48
30
  },
49
31
  swen: {
50
32
  displayName: 'SWEn',
@@ -64,33 +46,6 @@ exports.PERSONA_HIRE_CATALOG = {
64
46
  jobPriceCents: 590,
65
47
  fulltimePriceCents: 3990,
66
48
  },
67
- huxley: {
68
- displayName: 'hUXley',
69
- role: 'AI UX / Brand Designer',
70
- emoji: '🎨',
71
- gradient: 'linear-gradient(135deg, #ec4899 0%, #f472b6 100%)',
72
- blurb: 'Builds design systems, prototypes polished user-facing surfaces, and carries brand decisions into shipped product experiences.',
73
- jobPriceCents: 1490,
74
- fulltimePriceCents: 5490,
75
- },
76
- gautam: {
77
- displayName: 'GauTaM',
78
- role: 'AI GTM & Marketing Manager',
79
- emoji: '📣',
80
- gradient: 'linear-gradient(135deg, #f97316 0%, #f59e0b 100%)',
81
- blurb: 'Defines marketing strategy, ships content, runs launches, and owns the brand voice in market.',
82
- jobPriceCents: 890,
83
- fulltimePriceCents: 4990,
84
- },
85
- cela: {
86
- displayName: 'CELiA',
87
- role: 'AI Legal Counsel',
88
- emoji: '⚖️',
89
- gradient: 'linear-gradient(135deg, #475569 0%, #6366f1 100%)',
90
- blurb: 'Drafts and reviews contracts, NDAs, patents, trademarks, and the SaaS legal stack.',
91
- jobPriceCents: 1990,
92
- fulltimePriceCents: 6990,
93
- },
94
49
  sekhar: {
95
50
  displayName: 'SEChar',
96
51
  role: 'AI Security Engineer',
@@ -100,51 +55,15 @@ exports.PERSONA_HIRE_CATALOG = {
100
55
  jobPriceCents: 1790,
101
56
  fulltimePriceCents: 6490,
102
57
  },
103
- ashley: {
104
- displayName: 'AshLey',
105
- role: 'AI Executive Assistant',
106
- emoji: '📅',
107
- gradient: 'linear-gradient(135deg, #f59e0b 0%, #fbbf24 100%)',
108
- blurb: 'Owns executive coordination, weekly operating reviews, and portfolio reporting across the workforce.',
109
- jobPriceCents: 490,
110
- fulltimePriceCents: 2990,
111
- },
112
- mandy: {
113
- displayName: 'MANdy',
114
- role: 'AI Manager',
115
- emoji: '🎯',
116
- gradient: 'linear-gradient(135deg, #7c3aed 0%, #4338ca 100%)',
117
- blurb: 'Plans the job sequence, runs sub-agents in parallel, coaches them through verification loops, and hands back a synthesized DRAFT for your approval.',
118
- jobPriceCents: 1490,
58
+ sreya: {
59
+ displayName: 'SREya',
60
+ role: 'AI Site Reliability Engineer',
61
+ emoji: '☁️',
62
+ gradient: 'linear-gradient(135deg, #2563eb 0%, #10b981 100%)',
63
+ blurb: 'Manages deployments, monitors uptime, optimizes cloud cost, and keeps infrastructure resilient and observable.',
64
+ jobPriceCents: 1290,
119
65
  fulltimePriceCents: 5990,
120
66
  },
121
- ricardo: {
122
- displayName: 'RECardo',
123
- role: 'AI Recruiter',
124
- emoji: '🤝',
125
- gradient: 'linear-gradient(135deg, #6366f1 0%, #ec4899 100%)',
126
- blurb: 'Sources candidates, writes job descriptions, screens pipelines, and manages the hiring loop end-to-end.',
127
- jobPriceCents: 790,
128
- fulltimePriceCents: 4490,
129
- },
130
- hari: {
131
- displayName: 'HaRi',
132
- role: 'AI HR Manager',
133
- emoji: '👥',
134
- gradient: 'linear-gradient(135deg, #0d9488 0%, #059669 100%)',
135
- blurb: 'Manages onboarding, performance reviews, benefits analysis, payroll coordination, and HR business-partner advisory.',
136
- jobPriceCents: 790,
137
- fulltimePriceCents: 4490,
138
- },
139
- careena: {
140
- displayName: 'CAREEna',
141
- role: 'AI Career Coach',
142
- emoji: '🎓',
143
- gradient: 'linear-gradient(135deg, #0ea5e9 0%, #6366f1 100%)',
144
- blurb: 'Runs the candidate-side search loop: role sourcing, application execution, networking, interview prep, and close-stage offer strategy.',
145
- jobPriceCents: 890,
146
- fulltimePriceCents: 4990,
147
- },
148
67
  sade: {
149
68
  displayName: 'SADE',
150
69
  role: 'AI Salesforce Developer',
@@ -154,6 +73,33 @@ exports.PERSONA_HIRE_CATALOG = {
154
73
  jobPriceCents: 1490,
155
74
  fulltimePriceCents: 6490,
156
75
  },
76
+ pam: {
77
+ displayName: 'PaM',
78
+ role: 'AI Product Manager',
79
+ emoji: '📋',
80
+ gradient: 'linear-gradient(135deg, #8b5cf6 0%, #d946ef 100%)',
81
+ blurb: 'Owns specs, PRDs, technical design, issue prep, and the path from idea to shippable artifact.',
82
+ jobPriceCents: 790,
83
+ fulltimePriceCents: 4990,
84
+ },
85
+ huxley: {
86
+ displayName: 'hUXley',
87
+ role: 'AI UX / Brand Designer',
88
+ emoji: '🎨',
89
+ gradient: 'linear-gradient(135deg, #ec4899 0%, #f472b6 100%)',
90
+ blurb: 'Builds design systems, prototypes polished user-facing surfaces, and carries brand decisions into shipped product experiences.',
91
+ jobPriceCents: 1490,
92
+ fulltimePriceCents: 5490,
93
+ },
94
+ gautam: {
95
+ displayName: 'GauTaM',
96
+ role: 'AI GTM & Marketing Manager',
97
+ emoji: '📣',
98
+ gradient: 'linear-gradient(135deg, #f97316 0%, #f59e0b 100%)',
99
+ blurb: 'Defines marketing strategy, ships content, runs launches, and owns the brand voice in market.',
100
+ jobPriceCents: 890,
101
+ fulltimePriceCents: 4990,
102
+ },
157
103
  sam: {
158
104
  displayName: 'SAM',
159
105
  role: 'AI Sales Account Manager',
@@ -172,15 +118,6 @@ exports.PERSONA_HIRE_CATALOG = {
172
118
  jobPriceCents: 990,
173
119
  fulltimePriceCents: 4990,
174
120
  },
175
- deidre: {
176
- displayName: 'DEIdre',
177
- role: 'AI Inclusion Leader',
178
- emoji: '🌍',
179
- gradient: 'linear-gradient(135deg, #9333ea 0%, #d946ef 100%)',
180
- blurb: 'Audits equity gaps, designs bias-aware AI governance, builds ERG toolkits, and creates inclusion-fluency programs.',
181
- jobPriceCents: 890,
182
- fulltimePriceCents: 4490,
183
- },
184
121
  mona: {
185
122
  displayName: 'MONa',
186
123
  role: 'AI Finance Manager',
@@ -190,14 +127,32 @@ exports.PERSONA_HIRE_CATALOG = {
190
127
  jobPriceCents: 990,
191
128
  fulltimePriceCents: 4990,
192
129
  },
193
- sreya: {
194
- displayName: 'SREya',
195
- role: 'AI Site Reliability Engineer',
196
- emoji: '☁️',
197
- gradient: 'linear-gradient(135deg, #2563eb 0%, #10b981 100%)',
198
- blurb: 'Manages deployments, monitors uptime, optimizes cloud cost, and keeps infrastructure resilient and observable.',
199
- jobPriceCents: 1290,
200
- fulltimePriceCents: 5990,
130
+ hari: {
131
+ displayName: 'HaRi',
132
+ role: 'AI HR Manager',
133
+ emoji: '👥',
134
+ gradient: 'linear-gradient(135deg, #0d9488 0%, #059669 100%)',
135
+ blurb: 'Manages onboarding, performance reviews, benefits analysis, payroll coordination, and HR business-partner advisory.',
136
+ jobPriceCents: 790,
137
+ fulltimePriceCents: 4490,
138
+ },
139
+ ricardo: {
140
+ displayName: 'RECardo',
141
+ role: 'AI Recruiter',
142
+ emoji: '🤝',
143
+ gradient: 'linear-gradient(135deg, #6366f1 0%, #ec4899 100%)',
144
+ blurb: 'Sources candidates, writes job descriptions, screens pipelines, and manages the hiring loop end-to-end.',
145
+ jobPriceCents: 790,
146
+ fulltimePriceCents: 4490,
147
+ },
148
+ cela: {
149
+ displayName: 'CELiA',
150
+ role: 'AI Legal Counsel',
151
+ emoji: '⚖️',
152
+ gradient: 'linear-gradient(135deg, #475569 0%, #6366f1 100%)',
153
+ blurb: 'Drafts and reviews contracts, NDAs, patents, trademarks, and the SaaS legal stack.',
154
+ jobPriceCents: 1990,
155
+ fulltimePriceCents: 6990,
201
156
  },
202
157
  procella: {
203
158
  displayName: 'PROCella',
@@ -226,6 +181,60 @@ exports.PERSONA_HIRE_CATALOG = {
226
181
  jobPriceCents: 1490,
227
182
  fulltimePriceCents: 6490,
228
183
  },
184
+ deidre: {
185
+ displayName: 'DEIdre',
186
+ role: 'AI Inclusion Leader',
187
+ emoji: '🌍',
188
+ gradient: 'linear-gradient(135deg, #9333ea 0%, #d946ef 100%)',
189
+ blurb: 'Audits equity gaps, designs bias-aware AI governance, builds ERG toolkits, and creates inclusion-fluency programs.',
190
+ jobPriceCents: 890,
191
+ fulltimePriceCents: 4490,
192
+ },
193
+ careena: {
194
+ displayName: 'CAREEna',
195
+ role: 'AI Career Coach',
196
+ emoji: '🎓',
197
+ gradient: 'linear-gradient(135deg, #0ea5e9 0%, #6366f1 100%)',
198
+ blurb: 'Runs the candidate-side search loop: role sourcing, application execution, networking, interview prep, and close-stage offer strategy.',
199
+ jobPriceCents: 890,
200
+ fulltimePriceCents: 4990,
201
+ },
202
+ ashley: {
203
+ displayName: 'AshLey',
204
+ role: 'AI Executive Assistant',
205
+ emoji: '📅',
206
+ gradient: 'linear-gradient(135deg, #f59e0b 0%, #fbbf24 100%)',
207
+ blurb: 'Owns executive coordination, weekly operating reviews, and portfolio reporting across the workforce.',
208
+ jobPriceCents: 490,
209
+ fulltimePriceCents: 2990,
210
+ },
211
+ mandy: {
212
+ displayName: 'MANdy',
213
+ role: 'AI Manager',
214
+ emoji: '🎯',
215
+ gradient: 'linear-gradient(135deg, #7c3aed 0%, #4338ca 100%)',
216
+ blurb: 'Plans the job sequence, runs sub-agents in parallel, coaches them through verification loops, and hands back a synthesized DRAFT for your approval.',
217
+ jobPriceCents: 1490,
218
+ fulltimePriceCents: 5990,
219
+ },
220
+ beza: {
221
+ displayName: 'BeZa',
222
+ role: 'AI Business Strategist',
223
+ emoji: '🧭',
224
+ gradient: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)',
225
+ blurb: 'Turns ideas into structured business plans, validates founder-market fit, and pressure-tests strategy.',
226
+ jobPriceCents: 990,
227
+ fulltimePriceCents: 4990,
228
+ },
229
+ maestro: {
230
+ displayName: 'MAESTRO',
231
+ role: 'Full-Brained AI Employee',
232
+ emoji: '★',
233
+ gradient: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 50%, #d946ef 100%)',
234
+ blurb: 'One AI employee who can take a job from any function and ship it back with evidence. You set the direction. You sign off on what ships. Maestro does the work.',
235
+ jobPriceCents: 24900, // $249 — full-time only; job mode maps to same price
236
+ fulltimePriceCents: 24900, // $249/mo
237
+ },
229
238
  };
230
239
  exports.PERSONA_AVATAR_CATALOG = {
231
240
  maestro: { seed: 'MAESTRO-founder-mode', bg: 'fde68a', style: 'notionists' },
@@ -251,6 +260,7 @@ exports.PERSONA_AVATAR_CATALOG = {
251
260
  procella: { seed: 'PROCELLA-procurement', bg: 'ccfbf1', style: 'notionists' },
252
261
  banke: { seed: 'BANKe-banking-kyc', bg: 'ccfbf1', style: 'notionists' },
253
262
  auditya: { seed: 'AUDITya-banking-audit', bg: 'e9d5ff', style: 'notionists' },
263
+ aida: { seed: 'AIda-ai-engineer', bg: 'c7d2fe', style: 'notionists' },
254
264
  };
255
265
  function buildPersonaAvatarUrl(personaKey) {
256
266
  const avatar = exports.PERSONA_AVATAR_CATALOG[personaKey];
@@ -121,10 +121,26 @@ function buildPersistShellPathWindowsCommand(bins) {
121
121
  }
122
122
  function persistShellPath(includePackagedFraimBin = false) {
123
123
  const marker = '# FRAIM managed binaries';
124
- const legacyExportLine = 'export PATH="$HOME/.fraim/node/bin:$PATH"';
125
- const exportLine = includePackagedFraimBin
126
- ? 'export PATH="$PATH:$HOME/.fraim/bin:$HOME/.fraim/node/bin"'
127
- : 'export PATH="$PATH:$HOME/.fraim/node/bin"';
124
+ const legacyExportLine = 'export PATH=”$HOME/.fraim/node/bin:$PATH';
125
+ // On Windows/Git Bash: add both the flat dir (bare POSIX agent scripts npm
126
+ // generates) and the versioned node subdir (node.exe). $HOME in Git Bash
127
+ // resolves to the Windows home directory, matching the registry paths.
128
+ let exportLine;
129
+ if (process.platform === 'win32') {
130
+ const portableNodeBin = (0, managed_agent_paths_1.getPortableNodeBinPath)();
131
+ const nodeRoot = (0, managed_agent_paths_1.getManagedNodeRoot)();
132
+ const fraimBinEntry = includePackagedFraimBin ? ':$HOME/.fraim/bin' : '';
133
+ // Versioned dir first (has fresh installs, matches getManagedAgentBinDirs()
134
+ // order), flat dir second (retains bare POSIX agent scripts as fallback).
135
+ exportLine = portableNodeBin !== nodeRoot
136
+ ? `export PATH=”$PATH:$HOME/.fraim/node/${path_1.default.basename(portableNodeBin)}:$HOME/.fraim/node${fraimBinEntry}”`
137
+ : `export PATH=”$PATH:$HOME/.fraim/node${fraimBinEntry}”`;
138
+ }
139
+ else {
140
+ exportLine = includePackagedFraimBin
141
+ ? 'export PATH=”$PATH:$HOME/.fraim/bin:$HOME/.fraim/node/bin”'
142
+ : 'export PATH=”$PATH:$HOME/.fraim/node/bin”';
143
+ }
128
144
  const stanza = `\n${marker}\n${exportLine}\n`;
129
145
  if (process.platform === 'win32') {
130
146
  const bins = includePackagedFraimBin
@@ -132,18 +148,34 @@ function persistShellPath(includePackagedFraimBin = false) {
132
148
  : (0, managed_agent_paths_1.getManagedAgentBinDirs)();
133
149
  const psCmd = buildPersistShellPathWindowsCommand(bins);
134
150
  (0, child_process_1.spawnSync)('powershell', ['-NoProfile', '-NonInteractive', '-Command', psCmd], { encoding: 'utf8' });
135
- return;
151
+ // Fall through to write POSIX profile files for Git Bash (VS Code
152
+ // integrated terminal, Claude Code, etc.) which do not inherit the
153
+ // Windows user PATH written above.
136
154
  }
137
155
  const profiles = [
138
156
  path_1.default.join(os_1.default.homedir(), '.zshrc'),
139
157
  path_1.default.join(os_1.default.homedir(), '.bash_profile'),
140
158
  path_1.default.join(os_1.default.homedir(), '.bashrc'),
141
159
  ];
142
- // macOS defaults to zsh since Catalina — create .zshrc if it doesn't exist.
160
+ // macOS defaults to zsh since Catalina create .zshrc if it doesn't exist.
143
161
  if (process.platform === 'darwin' && !fs_1.default.existsSync(profiles[0])) {
144
162
  fs_1.default.writeFileSync(profiles[0], stanza, 'utf8');
145
163
  }
164
+ // On Windows, Git Bash reads .bash_profile (login) and .bashrc (interactive).
165
+ // Neither is created by the OS or Git Bash installer, so create .bashrc if
166
+ // neither exists — otherwise the PATH entry is never written and Git Bash
167
+ // (VS Code integrated terminal, Claude Code) never sees managed node dirs.
168
+ if (process.platform === 'win32') {
169
+ const bashProfile = profiles[1]; // .bash_profile
170
+ const bashrc = profiles[2]; // .bashrc
171
+ if (!fs_1.default.existsSync(bashProfile) && !fs_1.default.existsSync(bashrc)) {
172
+ fs_1.default.writeFileSync(bashrc, stanza, 'utf8');
173
+ }
174
+ }
146
175
  for (const profile of profiles) {
176
+ // .zshrc is macOS/Linux only; Git Bash on Windows uses .bash_profile/.bashrc.
177
+ if (process.platform === 'win32' && profile.endsWith('.zshrc'))
178
+ continue;
147
179
  try {
148
180
  if (!fs_1.default.existsSync(profile))
149
181
  continue;
@@ -153,8 +185,10 @@ function persistShellPath(includePackagedFraimBin = false) {
153
185
  continue;
154
186
  }
155
187
  if (content.includes(marker)) {
156
- if (includePackagedFraimBin && !content.includes('$HOME/.fraim/bin')) {
157
- fs_1.default.writeFileSync(profile, content.replace(exportLine.replace(':$HOME/.fraim/bin', ''), exportLine), 'utf8');
188
+ if (!content.includes(exportLine)) {
189
+ // Stale export line: versioned dir changed, includePackagedFraimBin
190
+ // toggled, or platform-specific line was never written. Replace it.
191
+ fs_1.default.writeFileSync(profile, content.replace(/export PATH=”[^”]*\.fraim[^”]*”/, exportLine), 'utf8');
158
192
  }
159
193
  continue;
160
194
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fraim-hub",
3
- "version": "2.0.284",
3
+ "version": "2.0.285",
4
4
  "description": "FRAIM Hub local companion package.",
5
5
  "author": "Sid Mathur <sid.mathur@gmail.com>",
6
6
  "homepage": "https://github.com/mathursrus/FRAIM#readme",
@@ -210,7 +210,7 @@
210
210
  "electron-updater": "^6.8.9",
211
211
  "express": "^5.2.1",
212
212
  "extract-zip": "^2.0.1",
213
- "fraim": "2.0.284",
213
+ "fraim": "2.0.285",
214
214
  "mongodb": "^7.0.0",
215
215
  "node-cron": "4.2.1",
216
216
  "node-edge-tts": "^1.2.10",