fraim 2.0.281 → 2.0.283
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 +467 -467
- package/bin/fraim.js +12 -12
- package/dist/src/cli/mcp/fraim-mcp-latest-launcher.js +266 -182
- package/dist/src/cli/mcp/mcp-server-registry.js +11 -3
- package/dist/src/cli/setup/ide-invocation-surfaces.js +64 -64
- package/dist/src/cli/utils/agent-adapters.js +61 -61
- package/dist/src/core/handoff-contracts.js +39 -39
- package/dist/src/core/utils/stub-generator.js +53 -53
- package/dist/src/first-run/server.js +5 -1
- package/dist/src/first-run/session-service.js +48 -12
- package/dist/src/fraim/issues.js +4 -4
- package/dist/src/mcp/tool-schemas.js +40 -40
- package/dist/src/middleware/telemetry.js +21 -21
- package/dist/src/services/email-service.js +623 -623
- package/dist/src/services/installer-service.js +22 -22
- package/index.js +83 -83
- package/package.json +59 -59
- package/public/first-run/error-frame.js +100 -100
- package/public/first-run/index.html +35 -35
- package/public/first-run/script.js +747 -742
- package/public/first-run/styles.css +929 -929
- package/dist/src/cli/commands/learning-usage.js +0 -412
- package/dist/src/cli/commands/test-mcp.js +0 -171
- package/dist/src/cli/setup/first-run.js +0 -242
- package/dist/src/core/config-writer.js +0 -75
- package/dist/src/core/utils/job-aliases.js +0 -47
- package/dist/src/core/utils/workflow-parser.js +0 -174
- package/dist/src/services/email-service-clean.js +0 -782
|
@@ -14,9 +14,9 @@ exports.buildAntigravityCommandContent = buildAntigravityCommandContent;
|
|
|
14
14
|
exports.buildGeminiCommandContent = buildGeminiCommandContent;
|
|
15
15
|
exports.describeInvocationSurface = describeInvocationSurface;
|
|
16
16
|
exports.FRAIM_LAUNCH_PHRASE = 'Use FRAIM for <job or task>';
|
|
17
|
-
exports.CURSOR_MDC_FRONTMATTER = `---
|
|
18
|
-
description: FRAIM discovery and execution contract
|
|
19
|
-
alwaysApply: true
|
|
17
|
+
exports.CURSOR_MDC_FRONTMATTER = `---
|
|
18
|
+
description: FRAIM discovery and execution contract
|
|
19
|
+
alwaysApply: true
|
|
20
20
|
---`;
|
|
21
21
|
exports.FRAIM_DEFERRED_TOOL_PRELOAD = [
|
|
22
22
|
'fraim_connect',
|
|
@@ -38,112 +38,112 @@ function buildDeferredToolBootstrapSection(profile) {
|
|
|
38
38
|
const preloadList = exports.FRAIM_DEFERRED_TOOL_PRELOAD.map((toolName) => `\`${toolName}\``).join(', ');
|
|
39
39
|
switch (profile) {
|
|
40
40
|
case 'claude-toolsearch':
|
|
41
|
-
return `0. **Preload deferred FRAIM tools when needed**:
|
|
42
|
-
- If FRAIM MCP tools are unavailable because this host lazily loads deferred tool schemas, call \`ToolSearch\` once to load ${preloadList}.
|
|
43
|
-
- Do the preload as one batched discovery step, not one search per tool.
|
|
44
|
-
|
|
41
|
+
return `0. **Preload deferred FRAIM tools when needed**:
|
|
42
|
+
- If FRAIM MCP tools are unavailable because this host lazily loads deferred tool schemas, call \`ToolSearch\` once to load ${preloadList}.
|
|
43
|
+
- Do the preload as one batched discovery step, not one search per tool.
|
|
44
|
+
|
|
45
45
|
`;
|
|
46
46
|
case 'codex-tool-search':
|
|
47
|
-
return `0. **Preload deferred FRAIM tools when needed**:
|
|
48
|
-
- If FRAIM MCP tools are unavailable because this host lazily loads deferred tool schemas, call \`tool_search\` once to load ${preloadList}.
|
|
49
|
-
- Do the preload as one batched discovery step, not one search per tool.
|
|
50
|
-
|
|
47
|
+
return `0. **Preload deferred FRAIM tools when needed**:
|
|
48
|
+
- If FRAIM MCP tools are unavailable because this host lazily loads deferred tool schemas, call \`tool_search\` once to load ${preloadList}.
|
|
49
|
+
- Do the preload as one batched discovery step, not one search per tool.
|
|
50
|
+
|
|
51
51
|
`;
|
|
52
52
|
case 'generic-tool-discovery':
|
|
53
|
-
return `0. **Preload deferred FRAIM tools when needed**:
|
|
54
|
-
- If FRAIM MCP tools are unavailable because this host lazily loads deferred tool schemas, use the host's tool discovery surface once to load ${preloadList}.
|
|
55
|
-
- Do the preload as one batched discovery step, not one search per tool.
|
|
56
|
-
|
|
53
|
+
return `0. **Preload deferred FRAIM tools when needed**:
|
|
54
|
+
- If FRAIM MCP tools are unavailable because this host lazily loads deferred tool schemas, use the host's tool discovery surface once to load ${preloadList}.
|
|
55
|
+
- Do the preload as one batched discovery step, not one search per tool.
|
|
56
|
+
|
|
57
57
|
`;
|
|
58
58
|
default:
|
|
59
59
|
return '';
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
function buildFraimInvocationBody(profile = 'none') {
|
|
63
|
-
return `Follow this process:
|
|
64
|
-
|
|
65
|
-
${buildDeferredToolBootstrapSection(profile)}1. **Confirm FRAIM activation**:
|
|
66
|
-
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.
|
|
67
|
-
|
|
68
|
-
2. **If the user did not specify a FRAIM job or topic after activation**:
|
|
69
|
-
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.
|
|
70
|
-
|
|
71
|
-
3. **Find the match**:
|
|
72
|
-
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.
|
|
73
|
-
|
|
74
|
-
4. **Load the full content**:
|
|
75
|
-
- For jobs, call \`get_fraim_job({ job: "<matched-job-name>" })\`.
|
|
76
|
-
- For skills, use the content returned by \`get_fraim_file(...)\`.
|
|
77
|
-
|
|
78
|
-
5. **Execute**:
|
|
79
|
-
- For jobs, follow the phased instructions and use \`seekMentoring\` when the job requires phase transitions.
|
|
80
|
-
- For skills, apply the skill steps directly to the user's current context.
|
|
81
|
-
- ${exports.FRAIM_MCP_UNAVAILABLE_MANAGER_GUIDANCE}
|
|
63
|
+
return `Follow this process:
|
|
64
|
+
|
|
65
|
+
${buildDeferredToolBootstrapSection(profile)}1. **Confirm FRAIM activation**:
|
|
66
|
+
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.
|
|
67
|
+
|
|
68
|
+
2. **If the user did not specify a FRAIM job or topic after activation**:
|
|
69
|
+
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.
|
|
70
|
+
|
|
71
|
+
3. **Find the match**:
|
|
72
|
+
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.
|
|
73
|
+
|
|
74
|
+
4. **Load the full content**:
|
|
75
|
+
- For jobs, call \`get_fraim_job({ job: "<matched-job-name>" })\`.
|
|
76
|
+
- For skills, use the content returned by \`get_fraim_file(...)\`.
|
|
77
|
+
|
|
78
|
+
5. **Execute**:
|
|
79
|
+
- For jobs, follow the phased instructions and use \`seekMentoring\` when the job requires phase transitions.
|
|
80
|
+
- For skills, apply the skill steps directly to the user's current context.
|
|
81
|
+
- ${exports.FRAIM_MCP_UNAVAILABLE_MANAGER_GUIDANCE}
|
|
82
82
|
`;
|
|
83
83
|
}
|
|
84
84
|
exports.FRAIM_INVOCATION_BODY = buildFraimInvocationBody();
|
|
85
85
|
function buildClaudeSkillContent() {
|
|
86
|
-
return `# FRAIM
|
|
87
|
-
|
|
86
|
+
return `# FRAIM
|
|
87
|
+
|
|
88
88
|
${buildFraimInvocationBody('claude-toolsearch')}`;
|
|
89
89
|
}
|
|
90
90
|
function buildClaudeCommandShimContent() {
|
|
91
|
-
return `# FRAIM Compatibility Command
|
|
92
|
-
|
|
93
|
-
Use the FRAIM skill when Claude exposes skills directly. This compatibility command keeps \`/fraim\` working on surfaces that still discover legacy command files.
|
|
94
|
-
|
|
91
|
+
return `# FRAIM Compatibility Command
|
|
92
|
+
|
|
93
|
+
Use the FRAIM skill when Claude exposes skills directly. This compatibility command keeps \`/fraim\` working on surfaces that still discover legacy command files.
|
|
94
|
+
|
|
95
95
|
${buildFraimInvocationBody('claude-toolsearch')}`;
|
|
96
96
|
}
|
|
97
97
|
function buildClaudeSlashCommandContent() {
|
|
98
98
|
return buildClaudeCommandShimContent();
|
|
99
99
|
}
|
|
100
100
|
function buildCursorMentionRuleContent() {
|
|
101
|
-
return `${exports.CURSOR_MDC_FRONTMATTER}
|
|
102
|
-
|
|
103
|
-
# FRAIM
|
|
104
|
-
|
|
105
|
-
${buildFraimInvocationBody('generic-tool-discovery')}
|
|
101
|
+
return `${exports.CURSOR_MDC_FRONTMATTER}
|
|
102
|
+
|
|
103
|
+
# FRAIM
|
|
104
|
+
|
|
105
|
+
${buildFraimInvocationBody('generic-tool-discovery')}
|
|
106
106
|
`;
|
|
107
107
|
}
|
|
108
|
-
exports.CODEX_SKILL_FRONTMATTER = `---
|
|
109
|
-
name: fraim
|
|
110
|
-
description: Discover and execute FRAIM jobs and skills from Codex.
|
|
108
|
+
exports.CODEX_SKILL_FRONTMATTER = `---
|
|
109
|
+
name: fraim
|
|
110
|
+
description: Discover and execute FRAIM jobs and skills from Codex.
|
|
111
111
|
---`;
|
|
112
112
|
function buildCodexSkillContent() {
|
|
113
|
-
return `${exports.CODEX_SKILL_FRONTMATTER}
|
|
114
|
-
# FRAIM
|
|
115
|
-
|
|
113
|
+
return `${exports.CODEX_SKILL_FRONTMATTER}
|
|
114
|
+
# FRAIM
|
|
115
|
+
|
|
116
116
|
${buildFraimInvocationBody('codex-tool-search')}`;
|
|
117
117
|
}
|
|
118
118
|
function buildGrokSkillContent() {
|
|
119
|
-
return `# FRAIM
|
|
120
|
-
|
|
119
|
+
return `# FRAIM
|
|
120
|
+
|
|
121
121
|
${buildFraimInvocationBody('generic-tool-discovery')}`;
|
|
122
122
|
}
|
|
123
123
|
function buildWindsurfCommandContent() {
|
|
124
|
-
return `# FRAIM
|
|
125
|
-
|
|
124
|
+
return `# FRAIM
|
|
125
|
+
|
|
126
126
|
${buildFraimInvocationBody('generic-tool-discovery')}`;
|
|
127
127
|
}
|
|
128
128
|
function buildKiroCommandContent() {
|
|
129
|
-
return `# FRAIM
|
|
130
|
-
|
|
129
|
+
return `# FRAIM
|
|
130
|
+
|
|
131
131
|
${buildFraimInvocationBody('generic-tool-discovery')}`;
|
|
132
132
|
}
|
|
133
133
|
function escapeTomlMultiline(value) {
|
|
134
134
|
return value.replace(/"""/g, '\\"""');
|
|
135
135
|
}
|
|
136
136
|
function buildAntigravityCommandContent() {
|
|
137
|
-
return `# FRAIM
|
|
138
|
-
|
|
137
|
+
return `# FRAIM
|
|
138
|
+
|
|
139
139
|
${buildFraimInvocationBody('generic-tool-discovery')}`;
|
|
140
140
|
}
|
|
141
141
|
function buildGeminiCommandContent() {
|
|
142
|
-
return `description = "Discover and execute FRAIM jobs and skills"
|
|
143
|
-
prompt = """
|
|
144
|
-
# FRAIM
|
|
145
|
-
|
|
146
|
-
${escapeTomlMultiline(buildFraimInvocationBody('generic-tool-discovery'))}
|
|
142
|
+
return `description = "Discover and execute FRAIM jobs and skills"
|
|
143
|
+
prompt = """
|
|
144
|
+
# FRAIM
|
|
145
|
+
|
|
146
|
+
${escapeTomlMultiline(buildFraimInvocationBody('generic-tool-discovery'))}
|
|
147
147
|
"""`;
|
|
148
148
|
}
|
|
149
149
|
function describeInvocationSurface(ideName, invocationProfile) {
|
|
@@ -25,9 +25,9 @@ function adapterConfigTypes(file) {
|
|
|
25
25
|
return Array.isArray(file.configType) ? file.configType : [file.configType];
|
|
26
26
|
}
|
|
27
27
|
function buildManagedSection(body) {
|
|
28
|
-
return `${START_MARKER}
|
|
29
|
-
${body.trim()}
|
|
30
|
-
${END_MARKER}
|
|
28
|
+
return `${START_MARKER}
|
|
29
|
+
${body.trim()}
|
|
30
|
+
${END_MARKER}
|
|
31
31
|
`;
|
|
32
32
|
}
|
|
33
33
|
function mergeManagedSection(existingContent, managedSection) {
|
|
@@ -59,69 +59,69 @@ function getAdapterFiles(allowedConfigTypes = null) {
|
|
|
59
59
|
const employeeRulesPath = (0, project_fraim_paths_1.getWorkspaceFraimDisplayPath)('ai-employee/rules');
|
|
60
60
|
const personalizedRootPath = (0, project_fraim_paths_1.getWorkspaceFraimDisplayPath)('personalized-employee');
|
|
61
61
|
const projectRulesPath = (0, project_fraim_paths_1.getWorkspaceFraimDisplayPath)('personalized-employee/rules/project_rules.md');
|
|
62
|
-
const markdownBody = buildManagedSection(`
|
|
63
|
-
## FRAIM
|
|
64
|
-
|
|
65
|
-
This repository uses FRAIM.
|
|
66
|
-
|
|
67
|
-
- The FRAIM discovery catalog lives under \`${fraimRoot}/\`.
|
|
68
|
-
- Jobs under \`${employeeJobsPath}/\` and \`${managerJobsPath}/\` are FRAIM's primary execution units. Treat them like first-class workflows when deciding how to execute work.
|
|
69
|
-
- Skills under \`${employeeSkillsPath}/\` are reusable capabilities that jobs compose.
|
|
70
|
-
- Rules under \`${employeeRulesPath}/\` are always-on constraints and conventions.
|
|
71
|
-
- Repo-specific overrides and learning artifacts live under \`${personalizedRootPath}/\` and take precedence over synced baseline content.
|
|
72
|
-
- Use FRAIM 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.
|
|
73
|
-
- For ordinary requests, answer or work normally. Do not scan FRAIM stubs first.
|
|
74
|
-
- If the user names an exact FRAIM job, call \`get_fraim_job({ job: "<job-name>" })\` directly.
|
|
75
|
-
- When FRAIM routing is active but the job is not exact, scan the job stubs under \`${employeeJobsPath}/\` and \`${managerJobsPath}/\` to identify the most appropriate job. Read stub filenames and their Intent/Outcome sections before using catalog tools.
|
|
76
|
-
- Once you identify the relevant job, call \`get_fraim_job({ job: "<job-name>" })\` to get the full phased instructions.
|
|
77
|
-
- 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.
|
|
78
|
-
- For deeper capability detail, call \`get_fraim_file({ path: "skills/<category>/<skill-name>.md" })\` or \`get_fraim_file({ path: "rules/<category>/<rule-name>.md" })\`.
|
|
79
|
-
- Read \`${projectRulesPath}\` if it exists before doing work.
|
|
80
|
-
- When users ask for next step recommendations, use recommend-next-job skill under \`${employeeSkillsPath}/\` to gather context before suggesting jobs.
|
|
81
|
-
- ${ide_invocation_surfaces_1.FRAIM_MCP_UNAVAILABLE_MANAGER_GUIDANCE}
|
|
82
|
-
|
|
83
|
-
> [!IMPORTANT]
|
|
84
|
-
> **Job stubs are for discovery only.** When a user mentions or references any file under \`${employeeJobsPath}/\` or \`${managerJobsPath}/\`, do NOT attempt to execute the job from the stub content. The stub only shows intent and phase names. Always call \`get_fraim_job({ job: "<job-name>" })\` first to get the full phased instructions before doing any work.
|
|
62
|
+
const markdownBody = buildManagedSection(`
|
|
63
|
+
## FRAIM
|
|
64
|
+
|
|
65
|
+
This repository uses FRAIM.
|
|
66
|
+
|
|
67
|
+
- The FRAIM discovery catalog lives under \`${fraimRoot}/\`.
|
|
68
|
+
- Jobs under \`${employeeJobsPath}/\` and \`${managerJobsPath}/\` are FRAIM's primary execution units. Treat them like first-class workflows when deciding how to execute work.
|
|
69
|
+
- Skills under \`${employeeSkillsPath}/\` are reusable capabilities that jobs compose.
|
|
70
|
+
- Rules under \`${employeeRulesPath}/\` are always-on constraints and conventions.
|
|
71
|
+
- Repo-specific overrides and learning artifacts live under \`${personalizedRootPath}/\` and take precedence over synced baseline content.
|
|
72
|
+
- Use FRAIM 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.
|
|
73
|
+
- For ordinary requests, answer or work normally. Do not scan FRAIM stubs first.
|
|
74
|
+
- If the user names an exact FRAIM job, call \`get_fraim_job({ job: "<job-name>" })\` directly.
|
|
75
|
+
- When FRAIM routing is active but the job is not exact, scan the job stubs under \`${employeeJobsPath}/\` and \`${managerJobsPath}/\` to identify the most appropriate job. Read stub filenames and their Intent/Outcome sections before using catalog tools.
|
|
76
|
+
- Once you identify the relevant job, call \`get_fraim_job({ job: "<job-name>" })\` to get the full phased instructions.
|
|
77
|
+
- 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.
|
|
78
|
+
- For deeper capability detail, call \`get_fraim_file({ path: "skills/<category>/<skill-name>.md" })\` or \`get_fraim_file({ path: "rules/<category>/<rule-name>.md" })\`.
|
|
79
|
+
- Read \`${projectRulesPath}\` if it exists before doing work.
|
|
80
|
+
- When users ask for next step recommendations, use recommend-next-job skill under \`${employeeSkillsPath}/\` to gather context before suggesting jobs.
|
|
81
|
+
- ${ide_invocation_surfaces_1.FRAIM_MCP_UNAVAILABLE_MANAGER_GUIDANCE}
|
|
82
|
+
|
|
83
|
+
> [!IMPORTANT]
|
|
84
|
+
> **Job stubs are for discovery only.** When a user mentions or references any file under \`${employeeJobsPath}/\` or \`${managerJobsPath}/\`, do NOT attempt to execute the job from the stub content. The stub only shows intent and phase names. Always call \`get_fraim_job({ job: "<job-name>" })\` first to get the full phased instructions before doing any work.
|
|
85
85
|
`);
|
|
86
|
-
const cursorManagedBody = buildManagedSection(`
|
|
87
|
-
# FRAIM
|
|
88
|
-
|
|
89
|
-
${(0, ide_invocation_surfaces_1.buildFraimInvocationBody)('generic-tool-discovery')}
|
|
86
|
+
const cursorManagedBody = buildManagedSection(`
|
|
87
|
+
# FRAIM
|
|
88
|
+
|
|
89
|
+
${(0, ide_invocation_surfaces_1.buildFraimInvocationBody)('generic-tool-discovery')}
|
|
90
90
|
`);
|
|
91
|
-
const copilotBody = buildManagedSection(`
|
|
92
|
-
## FRAIM
|
|
93
|
-
|
|
94
|
-
- Use \`${fraimRoot}/\` as the repository's FRAIM catalog.
|
|
95
|
-
- FRAIM jobs are the primary execution units and should be treated like first-class workflows.
|
|
96
|
-
- FRAIM skills are reusable capabilities jobs compose.
|
|
97
|
-
- FRAIM rules are always-on constraints and conventions.
|
|
98
|
-
- Repo-specific overrides and learnings live under \`${personalizedRootPath}/\`.
|
|
99
|
-
- Use FRAIM 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.
|
|
100
|
-
- For ordinary requests, answer or work normally. Do not scan FRAIM stubs first.
|
|
101
|
-
- If the user names an exact FRAIM job, fetch that full job directly with FRAIM MCP tools.
|
|
102
|
-
- When FRAIM routing is active but the job is not exact, use local stubs to identify which job to invoke before fetching full content with FRAIM MCP tools.
|
|
103
|
-
- 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.
|
|
104
|
-
- **Job stubs are for discovery only.** Never execute a job from stub content - always call \`get_fraim_job({ job: "<job-name>" })\` first.
|
|
105
|
-
- ${ide_invocation_surfaces_1.FRAIM_MCP_UNAVAILABLE_MANAGER_GUIDANCE}
|
|
91
|
+
const copilotBody = buildManagedSection(`
|
|
92
|
+
## FRAIM
|
|
93
|
+
|
|
94
|
+
- Use \`${fraimRoot}/\` as the repository's FRAIM catalog.
|
|
95
|
+
- FRAIM jobs are the primary execution units and should be treated like first-class workflows.
|
|
96
|
+
- FRAIM skills are reusable capabilities jobs compose.
|
|
97
|
+
- FRAIM rules are always-on constraints and conventions.
|
|
98
|
+
- Repo-specific overrides and learnings live under \`${personalizedRootPath}/\`.
|
|
99
|
+
- Use FRAIM 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.
|
|
100
|
+
- For ordinary requests, answer or work normally. Do not scan FRAIM stubs first.
|
|
101
|
+
- If the user names an exact FRAIM job, fetch that full job directly with FRAIM MCP tools.
|
|
102
|
+
- When FRAIM routing is active but the job is not exact, use local stubs to identify which job to invoke before fetching full content with FRAIM MCP tools.
|
|
103
|
+
- 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.
|
|
104
|
+
- **Job stubs are for discovery only.** Never execute a job from stub content - always call \`get_fraim_job({ job: "<job-name>" })\` first.
|
|
105
|
+
- ${ide_invocation_surfaces_1.FRAIM_MCP_UNAVAILABLE_MANAGER_GUIDANCE}
|
|
106
106
|
`);
|
|
107
|
-
const fraimReadme = `# FRAIM Catalog
|
|
108
|
-
|
|
109
|
-
This directory is the repository-visible FRAIM surface.
|
|
110
|
-
|
|
111
|
-
- \`ai-employee/jobs/\`: employee job stubs
|
|
112
|
-
- \`ai-manager/jobs/\`: manager job stubs
|
|
113
|
-
- \`ai-employee/skills/\`: skill stubs
|
|
114
|
-
- \`ai-employee/rules/\`: rule stubs
|
|
115
|
-
- \`personalized-employee/\`: repo-specific overrides and learnings
|
|
116
|
-
|
|
117
|
-
When FRAIM routing is active and no exact FRAIM job is named, use the stubs here to discover which FRAIM job, skill, or rule is relevant, then load the full content through FRAIM MCP tools. If an exact FRAIM job is named, load it directly. For ordinary requests, do not scan this catalog first.
|
|
107
|
+
const fraimReadme = `# FRAIM Catalog
|
|
108
|
+
|
|
109
|
+
This directory is the repository-visible FRAIM surface.
|
|
110
|
+
|
|
111
|
+
- \`ai-employee/jobs/\`: employee job stubs
|
|
112
|
+
- \`ai-manager/jobs/\`: manager job stubs
|
|
113
|
+
- \`ai-employee/skills/\`: skill stubs
|
|
114
|
+
- \`ai-employee/rules/\`: rule stubs
|
|
115
|
+
- \`personalized-employee/\`: repo-specific overrides and learnings
|
|
116
|
+
|
|
117
|
+
When FRAIM routing is active and no exact FRAIM job is named, use the stubs here to discover which FRAIM job, skill, or rule is relevant, then load the full content through FRAIM MCP tools. If an exact FRAIM job is named, load it directly. For ordinary requests, do not scan this catalog first.
|
|
118
118
|
`;
|
|
119
|
-
const vscodePrompt = `# FRAIM
|
|
120
|
-
|
|
119
|
+
const vscodePrompt = `# FRAIM
|
|
120
|
+
|
|
121
121
|
${(0, ide_invocation_surfaces_1.buildFraimInvocationBody)('generic-tool-discovery')}`;
|
|
122
|
-
const geminiProjectInstructions = `# Gemini Project Instructions
|
|
123
|
-
|
|
124
|
-
@../AGENTS.md
|
|
122
|
+
const geminiProjectInstructions = `# Gemini Project Instructions
|
|
123
|
+
|
|
124
|
+
@../AGENTS.md
|
|
125
125
|
`;
|
|
126
126
|
const all = [
|
|
127
127
|
{ path: 'AGENTS.md', content: markdownBody, configType: 'standard' },
|
|
@@ -273,48 +273,48 @@ function validateHandoffContracts(args) {
|
|
|
273
273
|
// ---------------------------------------------------------------------------
|
|
274
274
|
// Rejection message builder
|
|
275
275
|
// ---------------------------------------------------------------------------
|
|
276
|
-
const ADDRESS_FEEDBACK_APPROVAL_SCHEMA = `\`\`\`javascript
|
|
277
|
-
evidence: {
|
|
278
|
-
approved: true // Set only after the manager explicitly approves via the Hub review action
|
|
279
|
-
}
|
|
276
|
+
const ADDRESS_FEEDBACK_APPROVAL_SCHEMA = `\`\`\`javascript
|
|
277
|
+
evidence: {
|
|
278
|
+
approved: true // Set only after the manager explicitly approves via the Hub review action
|
|
279
|
+
}
|
|
280
280
|
\`\`\``;
|
|
281
|
-
const REVIEW_HANDOFF_SCHEMA = `\`\`\`javascript
|
|
282
|
-
evidence: {
|
|
283
|
-
reviewHandoff: {
|
|
284
|
-
reviewRequired: true,
|
|
285
|
-
reviewTarget: { kind: "pull_request", url: "<PR URL>" } | { kind: "artifact_set", files: ["<path>"] } | null,
|
|
286
|
-
artifacts: [{ label: "<label>", path: "<path>", kind: "<kind>" }],
|
|
287
|
-
summary: "<optional summary>",
|
|
288
|
-
reviewActions: [
|
|
289
|
-
{ kind: "approve", label: "Approve" },
|
|
290
|
-
{ kind: "request_changes", label: "Request Changes" }
|
|
291
|
-
]
|
|
292
|
-
}
|
|
293
|
-
}
|
|
281
|
+
const REVIEW_HANDOFF_SCHEMA = `\`\`\`javascript
|
|
282
|
+
evidence: {
|
|
283
|
+
reviewHandoff: {
|
|
284
|
+
reviewRequired: true,
|
|
285
|
+
reviewTarget: { kind: "pull_request", url: "<PR URL>" } | { kind: "artifact_set", files: ["<path>"] } | null,
|
|
286
|
+
artifacts: [{ label: "<label>", path: "<path>", kind: "<kind>" }],
|
|
287
|
+
summary: "<optional summary>",
|
|
288
|
+
reviewActions: [
|
|
289
|
+
{ kind: "approve", label: "Approve" },
|
|
290
|
+
{ kind: "request_changes", label: "Request Changes" }
|
|
291
|
+
]
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
294
|
\`\`\``;
|
|
295
|
-
const NEXT_JOB_RECOMMENDATIONS_SCHEMA = `\`\`\`javascript
|
|
296
|
-
evidence: {
|
|
297
|
-
nextJobRecommendations: [
|
|
298
|
-
{
|
|
299
|
-
jobId: "<job-slug>",
|
|
300
|
-
label: "<human-readable label>",
|
|
301
|
-
reason: "<optional: why this job is recommended>",
|
|
302
|
-
contextSummary: "<optional: one sentence context for the next agent>"
|
|
303
|
-
}
|
|
304
|
-
// 0–3 entries; an empty array [] is valid
|
|
305
|
-
]
|
|
306
|
-
}
|
|
295
|
+
const NEXT_JOB_RECOMMENDATIONS_SCHEMA = `\`\`\`javascript
|
|
296
|
+
evidence: {
|
|
297
|
+
nextJobRecommendations: [
|
|
298
|
+
{
|
|
299
|
+
jobId: "<job-slug>",
|
|
300
|
+
label: "<human-readable label>",
|
|
301
|
+
reason: "<optional: why this job is recommended>",
|
|
302
|
+
contextSummary: "<optional: one sentence context for the next agent>"
|
|
303
|
+
}
|
|
304
|
+
// 0–3 entries; an empty array [] is valid
|
|
305
|
+
]
|
|
306
|
+
}
|
|
307
307
|
\`\`\``;
|
|
308
|
-
const DELEGATION_LEDGER_SCHEMA = `\`\`\`javascript
|
|
309
|
-
evidence: {
|
|
310
|
-
delegationLedger: {
|
|
311
|
-
delegationRequired: true,
|
|
312
|
-
objective: "<optional objective string>",
|
|
313
|
-
tasks: [
|
|
314
|
-
{ jobId: "<job-slug>", personaKey: "<persona key or null>", briefing: "<optional briefing>" }
|
|
315
|
-
]
|
|
316
|
-
}
|
|
317
|
-
}
|
|
308
|
+
const DELEGATION_LEDGER_SCHEMA = `\`\`\`javascript
|
|
309
|
+
evidence: {
|
|
310
|
+
delegationLedger: {
|
|
311
|
+
delegationRequired: true,
|
|
312
|
+
objective: "<optional objective string>",
|
|
313
|
+
tasks: [
|
|
314
|
+
{ jobId: "<job-slug>", personaKey: "<persona key or null>", briefing: "<optional briefing>" }
|
|
315
|
+
]
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
318
|
\`\`\``;
|
|
319
319
|
const FIELD_SCHEMAS = {
|
|
320
320
|
approved: ADDRESS_FEEDBACK_APPROVAL_SCHEMA,
|
|
@@ -50,71 +50,71 @@ function generateJobStub(jobName, _jobPath, intent, outcome, steps, displayName)
|
|
|
50
50
|
const metadata = displayName
|
|
51
51
|
? `---\n${JSON.stringify({ name: jobName, displayName }, null, 2)}\n---\n`
|
|
52
52
|
: '';
|
|
53
|
-
return `${STUB_MARKER}
|
|
54
|
-
${metadata}
|
|
55
|
-
# FRAIM Job: ${jobName}
|
|
56
|
-
|
|
57
|
-
## Intent
|
|
58
|
-
${intent}
|
|
59
|
-
|
|
60
|
-
## Outcome
|
|
61
|
-
${outcome}
|
|
62
|
-
|
|
63
|
-
## Steps
|
|
64
|
-
${steps}
|
|
65
|
-
|
|
66
|
-
---
|
|
67
|
-
|
|
68
|
-
> [!IMPORTANT]
|
|
69
|
-
> **For AI Agents:** Do NOT attempt to execute this job based on the Intent/Outcome above.
|
|
70
|
-
> This stub is for discoverability only. The actual job has multiple phases with
|
|
71
|
-
> detailed steps, validation criteria, and required skills.
|
|
72
|
-
>
|
|
73
|
-
> To get full phase-by-phase instructions, call:
|
|
74
|
-
> \`get_fraim_job({ job: "${jobName}" })\`
|
|
75
|
-
>
|
|
76
|
-
> Then follow all phases using \`seekMentoring\` at each phase transition.
|
|
53
|
+
return `${STUB_MARKER}
|
|
54
|
+
${metadata}
|
|
55
|
+
# FRAIM Job: ${jobName}
|
|
56
|
+
|
|
57
|
+
## Intent
|
|
58
|
+
${intent}
|
|
59
|
+
|
|
60
|
+
## Outcome
|
|
61
|
+
${outcome}
|
|
62
|
+
|
|
63
|
+
## Steps
|
|
64
|
+
${steps}
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
> [!IMPORTANT]
|
|
69
|
+
> **For AI Agents:** Do NOT attempt to execute this job based on the Intent/Outcome above.
|
|
70
|
+
> This stub is for discoverability only. The actual job has multiple phases with
|
|
71
|
+
> detailed steps, validation criteria, and required skills.
|
|
72
|
+
>
|
|
73
|
+
> To get full phase-by-phase instructions, call:
|
|
74
|
+
> \`get_fraim_job({ job: "${jobName}" })\`
|
|
75
|
+
>
|
|
76
|
+
> Then follow all phases using \`seekMentoring\` at each phase transition.
|
|
77
77
|
`;
|
|
78
78
|
}
|
|
79
79
|
/**
|
|
80
80
|
* Generates a lightweight markdown stub for a skill.
|
|
81
81
|
*/
|
|
82
82
|
function generateSkillStub(skillName, skillPath, skillInput, skillOutput) {
|
|
83
|
-
return `${STUB_MARKER}
|
|
84
|
-
# FRAIM Skill: ${skillName}
|
|
85
|
-
|
|
86
|
-
## Skill Input
|
|
87
|
-
${skillInput}
|
|
88
|
-
|
|
89
|
-
## Skill Output
|
|
90
|
-
${skillOutput}
|
|
91
|
-
|
|
92
|
-
---
|
|
93
|
-
|
|
94
|
-
> [!IMPORTANT]
|
|
95
|
-
> **For AI Agents:** This is a discoverability stub for the skill.
|
|
96
|
-
> All execution details must be fetched from MCP before use.
|
|
97
|
-
> To retrieve the complete skill instructions, call:
|
|
98
|
-
> \`get_fraim_file({ path: "skills/${skillPath}" })\`
|
|
83
|
+
return `${STUB_MARKER}
|
|
84
|
+
# FRAIM Skill: ${skillName}
|
|
85
|
+
|
|
86
|
+
## Skill Input
|
|
87
|
+
${skillInput}
|
|
88
|
+
|
|
89
|
+
## Skill Output
|
|
90
|
+
${skillOutput}
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
> [!IMPORTANT]
|
|
95
|
+
> **For AI Agents:** This is a discoverability stub for the skill.
|
|
96
|
+
> All execution details must be fetched from MCP before use.
|
|
97
|
+
> To retrieve the complete skill instructions, call:
|
|
98
|
+
> \`get_fraim_file({ path: "skills/${skillPath}" })\`
|
|
99
99
|
`;
|
|
100
100
|
}
|
|
101
101
|
/**
|
|
102
102
|
* Generates a lightweight markdown stub for a rule.
|
|
103
103
|
*/
|
|
104
104
|
function generateRuleStub(ruleName, rulePath, intent) {
|
|
105
|
-
return `${STUB_MARKER}
|
|
106
|
-
# FRAIM Rule: ${ruleName}
|
|
107
|
-
|
|
108
|
-
## Intent
|
|
109
|
-
${intent}
|
|
110
|
-
|
|
111
|
-
---
|
|
112
|
-
|
|
113
|
-
> [!IMPORTANT]
|
|
114
|
-
> **For AI Agents:** This is a discoverability stub for the rule.
|
|
115
|
-
> All rule details must be fetched from MCP before use.
|
|
116
|
-
> To retrieve the complete rule instructions, call:
|
|
117
|
-
> \`get_fraim_file({ path: "rules/${rulePath}" })\`
|
|
105
|
+
return `${STUB_MARKER}
|
|
106
|
+
# FRAIM Rule: ${ruleName}
|
|
107
|
+
|
|
108
|
+
## Intent
|
|
109
|
+
${intent}
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
> [!IMPORTANT]
|
|
114
|
+
> **For AI Agents:** This is a discoverability stub for the rule.
|
|
115
|
+
> All rule details must be fetched from MCP before use.
|
|
116
|
+
> To retrieve the complete rule instructions, call:
|
|
117
|
+
> \`get_fraim_file({ path: "rules/${rulePath}" })\`
|
|
118
118
|
`;
|
|
119
119
|
}
|
|
120
120
|
/**
|
|
@@ -121,6 +121,7 @@ class FirstRunServer {
|
|
|
121
121
|
constructor(options) {
|
|
122
122
|
this.app = (0, express_1.default)();
|
|
123
123
|
this.sessionService = options.sessionService;
|
|
124
|
+
this.onOpenHub = options.onOpenHub;
|
|
124
125
|
this.finishPromise = new Promise((resolve) => {
|
|
125
126
|
this.finishResolver = resolve;
|
|
126
127
|
});
|
|
@@ -299,13 +300,16 @@ class FirstRunServer {
|
|
|
299
300
|
// a durable launcher binary that survives independently.
|
|
300
301
|
this.app.post('/api/first-run/open-hub', async (_req, res) => {
|
|
301
302
|
try {
|
|
302
|
-
|
|
303
|
+
let result = await this.sessionService.openHub();
|
|
303
304
|
// Write the next-prompt artifact as a side effect of opening the
|
|
304
305
|
// Hub so the client doesn't need a separate /finish call. We
|
|
305
306
|
// intentionally do NOT resolve the finishPromise - see /finish
|
|
306
307
|
// handler comment above.
|
|
307
308
|
if (result.ok) {
|
|
308
309
|
this.sessionService.finish();
|
|
310
|
+
if (this.onOpenHub) {
|
|
311
|
+
result = { ...result, ...(await this.onOpenHub()) };
|
|
312
|
+
}
|
|
309
313
|
}
|
|
310
314
|
return res.json(result);
|
|
311
315
|
}
|