fraim-hub 2.0.208 → 2.0.209
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/dist/src/ai-hub/hosts.js
CHANGED
|
@@ -686,12 +686,22 @@ function parseFraimInvocation(message) {
|
|
|
686
686
|
const trimmed = message.trim();
|
|
687
687
|
if (!trimmed)
|
|
688
688
|
return null;
|
|
689
|
-
const match = trimmed.match(/^[
|
|
689
|
+
const match = trimmed.match(/^([$\/@]fraim)([\s\S]*)$/);
|
|
690
690
|
if (!match)
|
|
691
691
|
return null;
|
|
692
|
+
const rest = match[2] || '';
|
|
693
|
+
if (rest && !/^\s/.test(rest))
|
|
694
|
+
return null;
|
|
695
|
+
const sameLineJobMatch = rest.match(/^[ \t]+(\S+)([\s\S]*)$/);
|
|
696
|
+
if (sameLineJobMatch) {
|
|
697
|
+
return {
|
|
698
|
+
jobId: sameLineJobMatch[1],
|
|
699
|
+
remainder: (sameLineJobMatch[2] || '').trim(),
|
|
700
|
+
};
|
|
701
|
+
}
|
|
692
702
|
return {
|
|
693
|
-
jobId:
|
|
694
|
-
remainder:
|
|
703
|
+
jobId: null,
|
|
704
|
+
remainder: rest.trim(),
|
|
695
705
|
};
|
|
696
706
|
}
|
|
697
707
|
// Rewrite UI-facing /fraim or $fraim invocations into direct MCP tool
|
|
@@ -994,10 +1004,10 @@ function ensureEmptyMcpConfig() {
|
|
|
994
1004
|
// --append-system-prompt alone doesn't block MCP tools; --strict-mcp-config
|
|
995
1005
|
// alone doesn't suppress CLAUDE.md's "scan job stubs" instruction.
|
|
996
1006
|
const DIRECT_SYSTEM_PROMPT_OVERRIDE = 'DO NOT USE FRAIM FOR THIS SESSION. ' +
|
|
997
|
-
'You are operating in Direct mode for an A/B comparison — no FRAIM, no phases,
|
|
998
|
-
'no job stubs, no structured workflow. Ignore all
|
|
1007
|
+
'You are operating in Direct mode for an A/B comparison — no FRAIM, no phases, ' +
|
|
1008
|
+
'no job stubs, no structured workflow. Ignore all repository instructions to scan fraim/ or load FRAIM jobs. ' +
|
|
999
1009
|
'Answer the user directly and conversationally.';
|
|
1000
|
-
const DIRECT_PREAMBLE = 'DO NOT USE FRAIM FOR THIS SESSION. No phases, no
|
|
1010
|
+
const DIRECT_PREAMBLE = 'DO NOT USE FRAIM FOR THIS SESSION. No phases, no FRAIM workflow tools, no structured workflow.\n\n';
|
|
1001
1011
|
// Issue #442: builds a CLI plan for the Direct (B) side of an A/B run.
|
|
1002
1012
|
// All agents supported: Codex and Gemini run raw (no FRAIM preamble);
|
|
1003
1013
|
// Claude uses --strict-mcp-config + --append-system-prompt for full isolation.
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.extractExplicitFraimInvocation = extractExplicitFraimInvocation;
|
|
4
4
|
exports.fraimInvocationFor = fraimInvocationFor;
|
|
5
|
+
exports.fraimDirectiveFor = fraimDirectiveFor;
|
|
5
6
|
exports.buildCommunicationStyleNote = buildCommunicationStyleNote;
|
|
6
7
|
exports.buildSameJobContinueMessage = buildSameJobContinueMessage;
|
|
7
8
|
exports.buildManagerMessage = buildManagerMessage;
|
|
@@ -25,6 +26,9 @@ function fraimInvocationFor(employeeId, jobId) {
|
|
|
25
26
|
const symbol = employeeId === 'codex' ? '$fraim' : '/fraim';
|
|
26
27
|
return `${symbol} ${jobId}`;
|
|
27
28
|
}
|
|
29
|
+
function fraimDirectiveFor(employeeId) {
|
|
30
|
+
return employeeId === 'codex' ? '$fraim' : '/fraim';
|
|
31
|
+
}
|
|
28
32
|
// #521: a Hub-injected communication-style note so the employee's messages to the
|
|
29
33
|
// manager stay about the JOB and its outcomes — not the FRAIM machinery (seeking
|
|
30
34
|
// mentoring, tool calls, git, phase switches). The structured signals (the
|
|
@@ -54,6 +58,11 @@ function buildManagerMessage(employeeId, jobId, kind, instructions, stubPath) {
|
|
|
54
58
|
const explicit = extractExplicitFraimInvocation(trimmed);
|
|
55
59
|
const effectiveJobId = explicit?.jobId || jobId;
|
|
56
60
|
const invocation = fraimInvocationFor(employeeId, effectiveJobId);
|
|
61
|
+
if (!invocation && effectiveJobId === '__freeform__' && kind === 'start') {
|
|
62
|
+
const directive = fraimDirectiveFor(employeeId);
|
|
63
|
+
const remainder = explicit ? explicit.remainder : trimmed;
|
|
64
|
+
return remainder ? `${directive}\n\n${remainder}` : directive;
|
|
65
|
+
}
|
|
57
66
|
if (!invocation)
|
|
58
67
|
return explicit?.remainder || trimmed;
|
|
59
68
|
const remainder = explicit ? explicit.remainder : trimmed;
|
|
@@ -39,18 +39,21 @@ function projectPathExists(projectPath) {
|
|
|
39
39
|
function projectIdForPath(projectPath) {
|
|
40
40
|
return `p-${(0, crypto_1.createHash)('sha1').update(canonicalProjectPath(projectPath)).digest('base64url').slice(0, 16)}`;
|
|
41
41
|
}
|
|
42
|
-
|
|
42
|
+
// Issue #788: tombstones are canonicalized and de-duplicated, but NEVER dropped for
|
|
43
|
+
// being the current project. The "current project is always visible" rule (#719) is a
|
|
44
|
+
// DISPLAY concern handled in normalizeAiHubProjectList (the current entry bypasses the
|
|
45
|
+
// tombstone filter). The original #726 code dropped the current-project tombstone HERE,
|
|
46
|
+
// so load()/saveProjects()/bootstrap silently erased it from disk whenever the deleted
|
|
47
|
+
// folder was the open workspace — an implicit revive that resurrected the project on the
|
|
48
|
+
// next add. Revive must be explicit (reviveRemovedPaths only; #726 contract).
|
|
49
|
+
function normalizeRemovedProjectPaths(raw) {
|
|
43
50
|
if (!Array.isArray(raw))
|
|
44
51
|
return [];
|
|
45
|
-
const currentKey = currentProjectPath ? canonicalProjectPath(currentProjectPath) : null;
|
|
46
52
|
const seen = new Set();
|
|
47
53
|
for (const value of raw) {
|
|
48
54
|
if (typeof value !== 'string' || value.trim().length === 0)
|
|
49
55
|
continue;
|
|
50
|
-
|
|
51
|
-
if (currentKey && key === currentKey)
|
|
52
|
-
continue;
|
|
53
|
-
seen.add(key);
|
|
56
|
+
seen.add(canonicalProjectPath(value));
|
|
54
57
|
}
|
|
55
58
|
return Array.from(seen);
|
|
56
59
|
}
|
|
@@ -100,22 +103,33 @@ function normalizeProjectEntry(raw, fallbackPath) {
|
|
|
100
103
|
};
|
|
101
104
|
}
|
|
102
105
|
function normalizeAiHubProjectList(projects, currentProjectPath, options = {}) {
|
|
106
|
+
// Issue #788: project identity is the CANONICAL path (case-insensitive on win32),
|
|
107
|
+
// matching both the tombstone comparison and the conversation store. De-duping by the
|
|
108
|
+
// case-preserving path (the old behavior) let the same folder in two casings — one from
|
|
109
|
+
// preferences.projects, one from a conversation bucket key — survive as two entries: the
|
|
110
|
+
// reported "2 instances". The map value keeps the first-seen case-preserving folderPath
|
|
111
|
+
// for display (the current project is added first, so its real casing wins).
|
|
103
112
|
const byPath = new Map();
|
|
104
|
-
const
|
|
105
|
-
const removedKeys = new Set(normalizeRemovedProjectPaths(options.removedProjectPaths
|
|
106
|
-
const add = (entry) => {
|
|
113
|
+
const currentCanonical = currentProjectPath ? canonicalProjectPath(currentProjectPath) : null;
|
|
114
|
+
const removedKeys = new Set(normalizeRemovedProjectPaths(options.removedProjectPaths));
|
|
115
|
+
const add = (entry, isCurrent = false) => {
|
|
107
116
|
if (!entry)
|
|
108
117
|
return;
|
|
109
|
-
const
|
|
110
|
-
|
|
118
|
+
const displayPath = normalizeProjectPath(entry.folderPath);
|
|
119
|
+
const dedupKey = canonicalProjectPath(displayPath);
|
|
120
|
+
// Issue #788: the current project is always shown in its own workspace, even when
|
|
121
|
+
// tombstoned (#719 never-empty Hub). This is a display override ONLY — the tombstone
|
|
122
|
+
// is neither erased here nor in persistence, so switching away re-hides the project
|
|
123
|
+
// and no subsequent add can resurrect it.
|
|
124
|
+
if (!isCurrent && removedKeys.has(dedupKey))
|
|
111
125
|
return;
|
|
112
|
-
if (!options.includeMissing &&
|
|
126
|
+
if (!options.includeMissing && dedupKey !== currentCanonical && !projectPathExists(displayPath))
|
|
113
127
|
return;
|
|
114
|
-
const existing = byPath.get(
|
|
115
|
-
byPath.set(
|
|
128
|
+
const existing = byPath.get(dedupKey);
|
|
129
|
+
byPath.set(dedupKey, existing ? { ...existing, ...entry, folderPath: existing.folderPath } : { ...entry, folderPath: displayPath });
|
|
116
130
|
};
|
|
117
131
|
if (currentProjectPath)
|
|
118
|
-
add(normalizeProjectEntry({ folderPath: currentProjectPath }, currentProjectPath));
|
|
132
|
+
add(normalizeProjectEntry({ folderPath: currentProjectPath }, currentProjectPath), true);
|
|
119
133
|
for (const project of projects)
|
|
120
134
|
add(normalizeProjectEntry(project));
|
|
121
135
|
return withUniqueProjectIds(Array.from(byPath.values()));
|
|
@@ -130,7 +144,7 @@ class AiHubPreferencesStore {
|
|
|
130
144
|
}
|
|
131
145
|
try {
|
|
132
146
|
const raw = JSON.parse(fs_1.default.readFileSync(this.stateFilePath, 'utf8'));
|
|
133
|
-
const removedProjectPaths = normalizeRemovedProjectPaths(raw.removedProjectPaths
|
|
147
|
+
const removedProjectPaths = normalizeRemovedProjectPaths(raw.removedProjectPaths);
|
|
134
148
|
return {
|
|
135
149
|
projectPath: raw.projectPath || projectPath,
|
|
136
150
|
employeeId: (raw.employeeId === 'claude' || raw.employeeId === 'codex' || raw.employeeId === 'gemini' || raw.employeeId === 'copilot') ? raw.employeeId : DEFAULT_EMPLOYEE,
|
|
@@ -158,8 +172,8 @@ class AiHubPreferencesStore {
|
|
|
158
172
|
saveProjects(projectPath, projects, options = {}) {
|
|
159
173
|
const normalizedProjectPath = normalizeProjectPath(projectPath);
|
|
160
174
|
const preferences = this.load(normalizedProjectPath);
|
|
161
|
-
const reviveKeys = new Set(normalizeRemovedProjectPaths(options.reviveRemovedPaths
|
|
162
|
-
const removedProjectPaths = normalizeRemovedProjectPaths(preferences.removedProjectPaths
|
|
175
|
+
const reviveKeys = new Set(normalizeRemovedProjectPaths(options.reviveRemovedPaths));
|
|
176
|
+
const removedProjectPaths = normalizeRemovedProjectPaths(preferences.removedProjectPaths)
|
|
163
177
|
.filter((removedPath) => !reviveKeys.has(removedPath));
|
|
164
178
|
const nextProjects = normalizeAiHubProjectList(projects, normalizedProjectPath, { removedProjectPaths });
|
|
165
179
|
this.save({ ...preferences, projectPath: normalizedProjectPath, projects: nextProjects, removedProjectPaths });
|
|
@@ -168,7 +182,7 @@ class AiHubPreferencesStore {
|
|
|
168
182
|
mergeProjects(projectPath, projects) {
|
|
169
183
|
const normalizedProjectPath = normalizeProjectPath(projectPath);
|
|
170
184
|
const preferences = this.load(normalizedProjectPath);
|
|
171
|
-
const removedProjectPaths = normalizeRemovedProjectPaths(preferences.removedProjectPaths
|
|
185
|
+
const removedProjectPaths = normalizeRemovedProjectPaths(preferences.removedProjectPaths);
|
|
172
186
|
const nextProjects = normalizeAiHubProjectList([...(preferences.projects || []), ...projects], normalizedProjectPath, { removedProjectPaths });
|
|
173
187
|
this.save({ ...preferences, projectPath: normalizedProjectPath, projects: nextProjects, removedProjectPaths });
|
|
174
188
|
return nextProjects;
|
|
@@ -176,7 +190,7 @@ class AiHubPreferencesStore {
|
|
|
176
190
|
removeProject(projectPath, projects, removedProjectPath) {
|
|
177
191
|
const normalizedProjectPath = normalizeProjectPath(projectPath);
|
|
178
192
|
const preferences = this.load(normalizedProjectPath);
|
|
179
|
-
const removedProjectPaths = normalizeRemovedProjectPaths([...(preferences.removedProjectPaths || []), removedProjectPath]
|
|
193
|
+
const removedProjectPaths = normalizeRemovedProjectPaths([...(preferences.removedProjectPaths || []), removedProjectPath]);
|
|
180
194
|
const nextProjects = normalizeAiHubProjectList(projects, normalizedProjectPath, { removedProjectPaths });
|
|
181
195
|
this.save({ ...preferences, projectPath: normalizedProjectPath, projects: nextProjects, removedProjectPaths });
|
|
182
196
|
return nextProjects;
|
|
@@ -146,6 +146,13 @@ const FRAIM_INTERNAL_JOB_IDS = new Set([
|
|
|
146
146
|
'setup-remote-hub',
|
|
147
147
|
'update-registry-override',
|
|
148
148
|
]);
|
|
149
|
+
const DEFAULT_CONVERSATION_FLUSH_DELAY_MS = 2000;
|
|
150
|
+
function conversationFlushDelayMs() {
|
|
151
|
+
const configured = Number(process.env.FRAIM_HUB_CONVERSATION_FLUSH_MS);
|
|
152
|
+
if (Number.isFinite(configured) && configured >= 0)
|
|
153
|
+
return configured;
|
|
154
|
+
return DEFAULT_CONVERSATION_FLUSH_DELAY_MS;
|
|
155
|
+
}
|
|
149
156
|
function listHubPersonaBundles() {
|
|
150
157
|
return loadPersonaCapabilityModule()?.listPersonaCapabilityBundles() ?? [];
|
|
151
158
|
}
|
|
@@ -1209,6 +1216,8 @@ class AiHubServer {
|
|
|
1209
1216
|
this.app = (0, express_1.default)();
|
|
1210
1217
|
this.runRegistry = new AiHubRunRegistry();
|
|
1211
1218
|
this.cronHandles = new Map();
|
|
1219
|
+
this.pendingConversationWrites = new Map();
|
|
1220
|
+
this.conversationFlushTimer = null;
|
|
1212
1221
|
this.preferencesStore = options.preferencesStore || new preferences_1.AiHubPreferencesStore();
|
|
1213
1222
|
this.projectPath = options.projectPath
|
|
1214
1223
|
? path_1.default.resolve(options.projectPath)
|
|
@@ -1398,6 +1407,7 @@ class AiHubServer {
|
|
|
1398
1407
|
}
|
|
1399
1408
|
}
|
|
1400
1409
|
async stop() {
|
|
1410
|
+
this.flushPendingRunConversations();
|
|
1401
1411
|
const closeServer = (srv) => new Promise((resolve, reject) => {
|
|
1402
1412
|
const closable = srv;
|
|
1403
1413
|
closable.closeIdleConnections?.();
|
|
@@ -1617,7 +1627,7 @@ class AiHubServer {
|
|
|
1617
1627
|
},
|
|
1618
1628
|
};
|
|
1619
1629
|
}
|
|
1620
|
-
|
|
1630
|
+
persistRunConversationNow(run, activeId) {
|
|
1621
1631
|
try {
|
|
1622
1632
|
// Issue #708: route the record to its scope bucket (manager/company runs get a
|
|
1623
1633
|
// project-independent home); project runs continue to key by project path.
|
|
@@ -1629,6 +1639,30 @@ class AiHubServer {
|
|
|
1629
1639
|
console.warn('[ai-hub] conversation store write failed:', error instanceof Error ? error.message : error);
|
|
1630
1640
|
}
|
|
1631
1641
|
}
|
|
1642
|
+
persistRunConversation(run, activeId) {
|
|
1643
|
+
this.pendingConversationWrites.delete(run.id);
|
|
1644
|
+
this.persistRunConversationNow(run, activeId);
|
|
1645
|
+
}
|
|
1646
|
+
scheduleRunConversationPersistence(run, activeId) {
|
|
1647
|
+
this.pendingConversationWrites.set(run.id, { run, activeId });
|
|
1648
|
+
if (this.conversationFlushTimer)
|
|
1649
|
+
return;
|
|
1650
|
+
this.conversationFlushTimer = setTimeout(() => {
|
|
1651
|
+
this.flushPendingRunConversations();
|
|
1652
|
+
}, conversationFlushDelayMs());
|
|
1653
|
+
this.conversationFlushTimer.unref?.();
|
|
1654
|
+
}
|
|
1655
|
+
flushPendingRunConversations() {
|
|
1656
|
+
if (this.conversationFlushTimer) {
|
|
1657
|
+
clearTimeout(this.conversationFlushTimer);
|
|
1658
|
+
this.conversationFlushTimer = null;
|
|
1659
|
+
}
|
|
1660
|
+
const pending = Array.from(this.pendingConversationWrites.values());
|
|
1661
|
+
this.pendingConversationWrites.clear();
|
|
1662
|
+
for (const entry of pending) {
|
|
1663
|
+
this.persistRunConversationNow(entry.run, entry.activeId);
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1632
1666
|
// Issue #512 (S3, R13) — derive the four Get-started step states, then let any
|
|
1633
1667
|
// persisted `true` in ~/.fraim/{install-state,preferences}.json override the
|
|
1634
1668
|
// derivation (so a completed step stays completed even if its signal vanishes).
|
|
@@ -1743,7 +1777,7 @@ class AiHubServer {
|
|
|
1743
1777
|
});
|
|
1744
1778
|
const updatedChild = this.runRegistry.get(childRun.id);
|
|
1745
1779
|
if (updatedChild)
|
|
1746
|
-
this.
|
|
1780
|
+
this.scheduleRunConversationPersistence(updatedChild);
|
|
1747
1781
|
},
|
|
1748
1782
|
onExit: (exitCode) => {
|
|
1749
1783
|
this.runRegistry.update(childRun.id, (current) => {
|
|
@@ -1893,7 +1927,7 @@ class AiHubServer {
|
|
|
1893
1927
|
});
|
|
1894
1928
|
const updated = this.runRegistry.get(managerRun.id);
|
|
1895
1929
|
if (updated)
|
|
1896
|
-
this.
|
|
1930
|
+
this.scheduleRunConversationPersistence(updated, updated.conversationId || updated.id);
|
|
1897
1931
|
},
|
|
1898
1932
|
onExit: (exitCode) => {
|
|
1899
1933
|
this.runRegistry.update(managerRun.id, (current) => {
|
|
@@ -2969,7 +3003,7 @@ class AiHubServer {
|
|
|
2969
3003
|
const updated = this.runRegistry.get(run.id);
|
|
2970
3004
|
if (updated) {
|
|
2971
3005
|
this.maybeStartDelegatedChildRuns(updated);
|
|
2972
|
-
this.
|
|
3006
|
+
this.scheduleRunConversationPersistence(updated, updated.conversationId || updated.id);
|
|
2973
3007
|
}
|
|
2974
3008
|
},
|
|
2975
3009
|
onExit: (exitCode) => {
|
|
@@ -3164,7 +3198,7 @@ class AiHubServer {
|
|
|
3164
3198
|
const updated = this.runRegistry.get(run.id);
|
|
3165
3199
|
if (updated) {
|
|
3166
3200
|
this.maybeStartDelegatedChildRuns(updated);
|
|
3167
|
-
this.
|
|
3201
|
+
this.scheduleRunConversationPersistence(updated, updated.conversationId || updated.id);
|
|
3168
3202
|
}
|
|
3169
3203
|
},
|
|
3170
3204
|
onExit: (exitCode) => {
|
|
@@ -3270,7 +3304,7 @@ class AiHubServer {
|
|
|
3270
3304
|
});
|
|
3271
3305
|
const updated = this.runRegistry.get(run.id);
|
|
3272
3306
|
if (updated)
|
|
3273
|
-
this.
|
|
3307
|
+
this.scheduleRunConversationPersistence(updated, updated.conversationId || updated.id);
|
|
3274
3308
|
},
|
|
3275
3309
|
onExit: (exitCode) => {
|
|
3276
3310
|
this.runRegistry.update(run.id, (current) => {
|
|
@@ -3828,7 +3862,7 @@ class AiHubServer {
|
|
|
3828
3862
|
});
|
|
3829
3863
|
const updated = this.runRegistry.get(run.id);
|
|
3830
3864
|
if (updated)
|
|
3831
|
-
this.
|
|
3865
|
+
this.scheduleRunConversationPersistence(updated, updated.conversationId || updated.id);
|
|
3832
3866
|
},
|
|
3833
3867
|
onExit: (exitCode) => {
|
|
3834
3868
|
this.runRegistry.update(run.id, (r) => {
|
|
@@ -56,17 +56,20 @@ function buildDeferredToolBootstrapSection(profile) {
|
|
|
56
56
|
function buildFraimInvocationBody(profile = 'none') {
|
|
57
57
|
return `Follow this process:
|
|
58
58
|
|
|
59
|
-
${buildDeferredToolBootstrapSection(profile)}1. **
|
|
59
|
+
${buildDeferredToolBootstrapSection(profile)}1. **Confirm FRAIM activation**:
|
|
60
|
+
Use this process only when the user explicitly invokes FRAIM, names a FRAIM job, asks what FRAIM job to run, or the active surface has already selected a FRAIM job. For ordinary requests, answer or work normally; do not scan FRAIM stubs first.
|
|
61
|
+
|
|
62
|
+
2. **If the user did not specify a FRAIM job or topic after activation**:
|
|
60
63
|
If local FRAIM job stubs are present in the workspace, inspect those first and match the request locally. Also inspect \`fraim/personalized-employee/jobs/\` for local overrides or repo-specific jobs. If local files are missing or you cannot inspect workspace files, call \`list_fraim_jobs()\` to view the full catalog, including any proxy-discoverable personalized jobs.
|
|
61
64
|
|
|
62
|
-
|
|
63
|
-
|
|
65
|
+
3. **Find the match**:
|
|
66
|
+
If the user names an exact FRAIM job, call \`get_fraim_job({ job: "<job-name>" })\` directly. Otherwise, match the user's request to a FRAIM job from the local stub catalog, \`fraim/personalized-employee/jobs/\`, or the full \`list_fraim_jobs()\` response. If no exact or high-confidence job match exists, say that no FRAIM job matches and continue with normal tools or ask one concise clarification. Do not pick the nearest catalog job.
|
|
64
67
|
|
|
65
|
-
|
|
68
|
+
4. **Load the full content**:
|
|
66
69
|
- For jobs, call \`get_fraim_job({ job: "<matched-job-name>" })\`.
|
|
67
70
|
- For skills, use the content returned by \`get_fraim_file(...)\`.
|
|
68
71
|
|
|
69
|
-
|
|
72
|
+
5. **Execute**:
|
|
70
73
|
- For jobs, follow the phased instructions and use \`seekMentoring\` when the job requires phase transitions.
|
|
71
74
|
- For skills, apply the skill steps directly to the user's current context.
|
|
72
75
|
`;
|
|
@@ -96,18 +99,18 @@ ${buildFraimInvocationBody('generic-tool-discovery')}
|
|
|
96
99
|
`;
|
|
97
100
|
}
|
|
98
101
|
function buildCodexSkillContent() {
|
|
99
|
-
return `# FRAIM
|
|
100
|
-
|
|
102
|
+
return `# FRAIM
|
|
103
|
+
|
|
101
104
|
${buildFraimInvocationBody('codex-tool-search')}`;
|
|
102
105
|
}
|
|
103
106
|
function buildGrokSkillContent() {
|
|
104
|
-
return `# FRAIM
|
|
105
|
-
|
|
107
|
+
return `# FRAIM
|
|
108
|
+
|
|
106
109
|
${buildFraimInvocationBody('generic-tool-discovery')}`;
|
|
107
110
|
}
|
|
108
111
|
function buildWindsurfCommandContent() {
|
|
109
|
-
return `# FRAIM
|
|
110
|
-
|
|
112
|
+
return `# FRAIM
|
|
113
|
+
|
|
111
114
|
${buildFraimInvocationBody('generic-tool-discovery')}`;
|
|
112
115
|
}
|
|
113
116
|
function buildKiroCommandContent() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fraim-hub",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.209",
|
|
4
4
|
"description": "FRAIM Hub local companion package.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
"dotenv": "^16.4.7",
|
|
90
90
|
"electron": "^41.2.2",
|
|
91
91
|
"express": "^5.2.1",
|
|
92
|
-
"fraim": "2.0.
|
|
92
|
+
"fraim": "2.0.209",
|
|
93
93
|
"mongodb": "^7.0.0",
|
|
94
94
|
"node-cron": "4.2.1",
|
|
95
95
|
"node-edge-tts": "^1.2.10",
|