@juancr11/sibu 0.7.0 → 0.9.0
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 +89 -0
- package/bin/entrypoints/cli/create-program.js +13 -0
- package/bin/entrypoints/cli/execute-command.js +10 -0
- package/bin/modules/interactive-guidance/index.js +1 -1
- package/bin/modules/interactive-guidance/prompts.js +34 -1
- package/bin/modules/mcp-server-selection-management/index.js +3 -0
- package/bin/modules/mcp-server-selection-management/list-mcp-servers/command.js +1 -0
- package/bin/modules/mcp-server-selection-management/list-mcp-servers/handler.js +46 -0
- package/bin/modules/mcp-server-selection-management/stop-mcp-server/command.js +1 -0
- package/bin/modules/mcp-server-selection-management/stop-mcp-server/handler.js +194 -0
- package/bin/modules/mcp-server-selection-management/use-mcp-server/command.js +1 -0
- package/bin/modules/mcp-server-selection-management/use-mcp-server/handler.js +127 -0
- package/bin/modules/project-adoption/handler.js +37 -11
- package/bin/modules/skill-selection-management/list-skills/handler.js +11 -1
- package/bin/modules/skill-selection-management/stop-managing-file/handler.js +7 -1
- package/bin/modules/skill-selection-management/use-skill/handler.js +35 -2
- package/bin/modules/sync-review/apply-action.js +5 -1
- package/bin/modules/sync-review/sync-preview.js +20 -6
- package/bin/modules/template-catalog-rendering/index.js +1 -1
- package/bin/modules/template-catalog-rendering/templates.js +59 -4
- package/bin/modules/workflow-health-diagnosis/handler.js +13 -3
- package/bin/modules/workflow-state-registry/state.js +4 -0
- package/bin/modules/workflow-target-planning/catalog.js +34 -0
- package/bin/modules/workflow-target-planning/index.js +2 -2
- package/bin/modules/workflow-target-planning/workflow-targets.js +82 -9
- package/package.json +1 -1
- package/templates/manifest.json +27 -6
- package/templates/mcp/claude/.mcp.json +3 -0
- package/templates/mcp/gemini/settings.json +3 -0
- package/templates/skills/postgresql-expert/SKILL.md +72 -0
- package/templates/skills/scrum-master-planner/SKILL.md +22 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { intro, log, outro } from '@clack/prompts';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
|
-
import { SELECTABLE_ARCHITECTURE_SKILLS, SELECTABLE_FRAMEWORK_SKILLS, SELECTABLE_LANGUAGE_SKILLS, SELECTABLE_WORKFLOW_SKILLS } from '../../workflow-target-planning/index.js';
|
|
3
|
+
import { SELECTABLE_ARCHITECTURE_SKILLS, SELECTABLE_DATABASE_SKILLS, SELECTABLE_FRAMEWORK_SKILLS, SELECTABLE_LANGUAGE_SKILLS, SELECTABLE_WORKFLOW_SKILLS } from '../../workflow-target-planning/index.js';
|
|
4
4
|
import { getProjectContext } from '../../../shared/paths.js';
|
|
5
5
|
import { renderIntro } from '../../interactive-guidance/index.js';
|
|
6
6
|
import { readStateForDoctor } from '../../workflow-state-registry/index.js';
|
|
@@ -16,6 +16,7 @@ export async function handleListSkills(_command) {
|
|
|
16
16
|
}
|
|
17
17
|
logSkillGroup('Languages', getLanguageSkillItems(state));
|
|
18
18
|
logSkillGroup('Frameworks', getFrameworkSkillItems(state));
|
|
19
|
+
logSkillGroup('Databases', getDatabaseSkillItems(state));
|
|
19
20
|
logSkillGroup('Architecture', getArchitectureSkillItems(state));
|
|
20
21
|
logSkillGroup('Workflow', getWorkflowSkillItems(state));
|
|
21
22
|
outro(chalk.green('Skill list ready.'));
|
|
@@ -38,6 +39,15 @@ function getFrameworkSkillItems(state) {
|
|
|
38
39
|
selected: selectedSkillIds.has(skill.id),
|
|
39
40
|
}));
|
|
40
41
|
}
|
|
42
|
+
function getDatabaseSkillItems(state) {
|
|
43
|
+
const selectedSkillIds = new Set(state?.selectedDatabaseSkills ?? []);
|
|
44
|
+
return SELECTABLE_DATABASE_SKILLS.map((skill) => ({
|
|
45
|
+
name: skill.name,
|
|
46
|
+
id: skill.id,
|
|
47
|
+
description: skill.description,
|
|
48
|
+
selected: selectedSkillIds.has(skill.id),
|
|
49
|
+
}));
|
|
50
|
+
}
|
|
41
51
|
function getArchitectureSkillItems(state) {
|
|
42
52
|
return SELECTABLE_ARCHITECTURE_SKILLS.map((skill) => ({
|
|
43
53
|
name: skill.name,
|
|
@@ -10,7 +10,7 @@ import { renderIntro } from '../../interactive-guidance/index.js';
|
|
|
10
10
|
import { cloneState, readStateForDoctor, writeStateFile } from '../../workflow-state-registry/index.js';
|
|
11
11
|
import { getTemplateVersion, readTemplateManifest, renderTemplateForSync } from '../../template-catalog-rendering/index.js';
|
|
12
12
|
import { removeUndefinedFields } from '../../../shared/object.js';
|
|
13
|
-
import { getSelectedAgentsFromState, getSelectedArchitectureSkillFromState, getSelectedFrameworkSkillsFromState, getSelectedLanguageSkillsFromState, getSelectedWorkflowSkillsFromState, } from '../../workflow-target-planning/index.js';
|
|
13
|
+
import { getSelectedAgentsFromState, getSelectedArchitectureSkillFromState, getSelectedDatabaseSkillsFromState, getSelectedFrameworkSkillsFromState, getSelectedLanguageSkillsFromState, getSelectedWorkflowSkillsFromState, } from '../../workflow-target-planning/index.js';
|
|
14
14
|
export async function handleStopManagingFile({ skillName }) {
|
|
15
15
|
await renderIntro();
|
|
16
16
|
intro(chalk.cyan('Updating skill management'));
|
|
@@ -110,6 +110,8 @@ function isSkillSelected(state, resolved) {
|
|
|
110
110
|
return state.selectedFrameworkSkills?.includes(resolved.skill.id) ?? false;
|
|
111
111
|
case 'architecture':
|
|
112
112
|
return state.selectedArchitectureSkill === resolved.skill.id;
|
|
113
|
+
case 'database':
|
|
114
|
+
return state.selectedDatabaseSkills?.includes(resolved.skill.id) ?? false;
|
|
113
115
|
case 'workflow':
|
|
114
116
|
return state.selectedWorkflowSkills?.includes(resolved.skill.id) ?? false;
|
|
115
117
|
}
|
|
@@ -125,6 +127,9 @@ function removeSelectedSkill(state, resolved) {
|
|
|
125
127
|
case 'architecture':
|
|
126
128
|
delete state.selectedArchitectureSkill;
|
|
127
129
|
return;
|
|
130
|
+
case 'database':
|
|
131
|
+
state.selectedDatabaseSkills = (state.selectedDatabaseSkills ?? []).filter((skillId) => skillId !== resolved.skill.id);
|
|
132
|
+
return;
|
|
128
133
|
case 'workflow':
|
|
129
134
|
state.selectedWorkflowSkills = (state.selectedWorkflowSkills ?? []).filter((skillId) => skillId !== resolved.skill.id);
|
|
130
135
|
return;
|
|
@@ -168,6 +173,7 @@ function getAgentsUpdate(rootPath, state) {
|
|
|
168
173
|
selectedFrameworkSkills: getSelectedFrameworkSkillsFromState(state),
|
|
169
174
|
selectedArchitectureSkill: getSelectedArchitectureSkillFromState(state),
|
|
170
175
|
selectedWorkflowSkills: getSelectedWorkflowSkillsFromState(state),
|
|
176
|
+
selectedDatabaseSkills: getSelectedDatabaseSkillsFromState(state),
|
|
171
177
|
}),
|
|
172
178
|
};
|
|
173
179
|
}
|
|
@@ -46,6 +46,7 @@ export function getNextSkillSelection(state, skillName) {
|
|
|
46
46
|
const selectedLanguageSkills = [...(state.selectedLanguageSkills ?? [])];
|
|
47
47
|
const selectedFrameworkSkills = [...(state.selectedFrameworkSkills ?? [])];
|
|
48
48
|
const selectedWorkflowSkills = [...(state.selectedWorkflowSkills ?? [])];
|
|
49
|
+
const selectedDatabaseSkills = [...(state.selectedDatabaseSkills ?? [])];
|
|
49
50
|
switch (resolution.resolved.kind) {
|
|
50
51
|
case 'language':
|
|
51
52
|
if (selectedLanguageSkills.includes(resolution.resolved.skill.id)) {
|
|
@@ -59,6 +60,7 @@ export function getNextSkillSelection(state, skillName) {
|
|
|
59
60
|
selectedFrameworkSkills: selectedFrameworkSkills.map(getFrameworkSkillById),
|
|
60
61
|
selectedArchitectureSkill: getArchitectureSkillById(state.selectedArchitectureSkill),
|
|
61
62
|
selectedWorkflowSkills: selectedWorkflowSkills.map(getWorkflowSkillById),
|
|
63
|
+
selectedDatabaseSkills: selectedDatabaseSkills.map(getDatabaseSkillById),
|
|
62
64
|
},
|
|
63
65
|
};
|
|
64
66
|
case 'framework':
|
|
@@ -73,6 +75,7 @@ export function getNextSkillSelection(state, skillName) {
|
|
|
73
75
|
selectedFrameworkSkills: [...selectedFrameworkSkills, resolution.resolved.skill.id].map(getFrameworkSkillById),
|
|
74
76
|
selectedArchitectureSkill: getArchitectureSkillById(state.selectedArchitectureSkill),
|
|
75
77
|
selectedWorkflowSkills: selectedWorkflowSkills.map(getWorkflowSkillById),
|
|
78
|
+
selectedDatabaseSkills: selectedDatabaseSkills.map(getDatabaseSkillById),
|
|
76
79
|
},
|
|
77
80
|
};
|
|
78
81
|
case 'architecture':
|
|
@@ -94,6 +97,22 @@ export function getNextSkillSelection(state, skillName) {
|
|
|
94
97
|
selectedFrameworkSkills: selectedFrameworkSkills.map(getFrameworkSkillById),
|
|
95
98
|
selectedArchitectureSkill: resolution.resolved.skill,
|
|
96
99
|
selectedWorkflowSkills: selectedWorkflowSkills.map(getWorkflowSkillById),
|
|
100
|
+
selectedDatabaseSkills: selectedDatabaseSkills.map(getDatabaseSkillById),
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
case 'database':
|
|
104
|
+
if (selectedDatabaseSkills.includes(resolution.resolved.skill.id)) {
|
|
105
|
+
return { status: 'noop', message: `${resolution.resolved.skill.name} is already selected.` };
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
status: 'selected',
|
|
109
|
+
skillName: resolution.resolved.skill.name,
|
|
110
|
+
selection: {
|
|
111
|
+
selectedLanguageSkills: selectedLanguageSkills.map(getLanguageSkillById),
|
|
112
|
+
selectedFrameworkSkills: selectedFrameworkSkills.map(getFrameworkSkillById),
|
|
113
|
+
selectedArchitectureSkill: getArchitectureSkillById(state.selectedArchitectureSkill),
|
|
114
|
+
selectedWorkflowSkills: selectedWorkflowSkills.map(getWorkflowSkillById),
|
|
115
|
+
selectedDatabaseSkills: [...selectedDatabaseSkills, resolution.resolved.skill.id].map(getDatabaseSkillById),
|
|
97
116
|
},
|
|
98
117
|
};
|
|
99
118
|
case 'workflow':
|
|
@@ -108,6 +127,7 @@ export function getNextSkillSelection(state, skillName) {
|
|
|
108
127
|
selectedFrameworkSkills: selectedFrameworkSkills.map(getFrameworkSkillById),
|
|
109
128
|
selectedArchitectureSkill: getArchitectureSkillById(state.selectedArchitectureSkill),
|
|
110
129
|
selectedWorkflowSkills: [...selectedWorkflowSkills, resolution.resolved.skill.id].map(getWorkflowSkillById),
|
|
130
|
+
selectedDatabaseSkills: selectedDatabaseSkills.map(getDatabaseSkillById),
|
|
111
131
|
},
|
|
112
132
|
};
|
|
113
133
|
}
|
|
@@ -128,12 +148,14 @@ function applySelectedSkill({ rootPath, statePath, state, selectionResult, }) {
|
|
|
128
148
|
selectedFrameworkSkills: selectionResult.selection.selectedFrameworkSkills,
|
|
129
149
|
selectedArchitectureSkill: selectionResult.selection.selectedArchitectureSkill,
|
|
130
150
|
selectedWorkflowSkills: selectionResult.selection.selectedWorkflowSkills,
|
|
151
|
+
selectedDatabaseSkills: selectionResult.selection.selectedDatabaseSkills,
|
|
131
152
|
});
|
|
132
153
|
const [newSkillFile] = renderMissingWorkflowFiles({
|
|
133
154
|
missingTargets: [plan.newSkillTarget],
|
|
134
155
|
selectedLanguageSkills: selectionResult.selection.selectedLanguageSkills,
|
|
135
156
|
selectedFrameworkSkills: selectionResult.selection.selectedFrameworkSkills,
|
|
136
157
|
selectedArchitectureSkill: selectionResult.selection.selectedArchitectureSkill,
|
|
158
|
+
selectedDatabaseSkills: selectionResult.selection.selectedDatabaseSkills,
|
|
137
159
|
});
|
|
138
160
|
fs.mkdirSync(path.dirname(newSkillFile.targetPath), { recursive: true });
|
|
139
161
|
fs.writeFileSync(newSkillFile.targetPath, newSkillFile.contents, { encoding: 'utf8', flag: 'wx' });
|
|
@@ -148,6 +170,7 @@ function applySelectedSkill({ rootPath, statePath, state, selectionResult, }) {
|
|
|
148
170
|
selectedFrameworkSkills: selectionResult.selection.selectedFrameworkSkills,
|
|
149
171
|
selectedArchitectureSkill: selectionResult.selection.selectedArchitectureSkill,
|
|
150
172
|
selectedWorkflowSkills: selectionResult.selection.selectedWorkflowSkills,
|
|
173
|
+
selectedDatabaseSkills: selectionResult.selection.selectedDatabaseSkills,
|
|
151
174
|
targets: plan.targets,
|
|
152
175
|
});
|
|
153
176
|
log.success(`Updated ${STATE_RELATIVE_PATH}`);
|
|
@@ -155,8 +178,8 @@ function applySelectedSkill({ rootPath, statePath, state, selectionResult, }) {
|
|
|
155
178
|
}
|
|
156
179
|
function buildSkillApplicationPlan({ rootPath, state, selectionResult, }) {
|
|
157
180
|
const selectedAgents = getSelectedAgentsFromState(state);
|
|
158
|
-
const previousTargets = getWorkflowTargets(rootPath, selectedAgents, getSelectedLanguageSkillsFromState(state), getSelectedFrameworkSkillsFromState(state), getArchitectureSkillById(state.selectedArchitectureSkill), getSelectedWorkflowSkillsFromState(state));
|
|
159
|
-
const targets = getWorkflowTargets(rootPath, selectedAgents, selectionResult.selection.selectedLanguageSkills, selectionResult.selection.selectedFrameworkSkills, selectionResult.selection.selectedArchitectureSkill, selectionResult.selection.selectedWorkflowSkills);
|
|
181
|
+
const previousTargets = getWorkflowTargets(rootPath, selectedAgents, getSelectedLanguageSkillsFromState(state), getSelectedFrameworkSkillsFromState(state), getArchitectureSkillById(state.selectedArchitectureSkill), getSelectedWorkflowSkillsFromState(state), getSelectedDatabaseSkillsFromState(state));
|
|
182
|
+
const targets = getWorkflowTargets(rootPath, selectedAgents, selectionResult.selection.selectedLanguageSkills, selectionResult.selection.selectedFrameworkSkills, selectionResult.selection.selectedArchitectureSkill, selectionResult.selection.selectedWorkflowSkills, selectionResult.selection.selectedDatabaseSkills);
|
|
160
183
|
const previousTargetPaths = new Set(previousTargets.map((target) => target.targetPath));
|
|
161
184
|
const newSkillTarget = targets.find((target) => !previousTargetPaths.has(target.targetPath));
|
|
162
185
|
const agentsTarget = targets.find((target) => target.label === 'AGENTS.md');
|
|
@@ -195,6 +218,9 @@ function getSelectedFrameworkSkillsFromState(state) {
|
|
|
195
218
|
function getSelectedWorkflowSkillsFromState(state) {
|
|
196
219
|
return (state.selectedWorkflowSkills ?? []).map(getWorkflowSkillById);
|
|
197
220
|
}
|
|
221
|
+
function getSelectedDatabaseSkillsFromState(state) {
|
|
222
|
+
return (state.selectedDatabaseSkills ?? []).map(getDatabaseSkillById);
|
|
223
|
+
}
|
|
198
224
|
function getLanguageSkillById(skillId) {
|
|
199
225
|
const resolution = resolveSelectableSkillById(skillId);
|
|
200
226
|
if (!resolution.ok || resolution.resolved.kind !== 'language') {
|
|
@@ -226,3 +252,10 @@ function getWorkflowSkillById(skillId) {
|
|
|
226
252
|
}
|
|
227
253
|
return resolution.resolved.skill;
|
|
228
254
|
}
|
|
255
|
+
function getDatabaseSkillById(skillId) {
|
|
256
|
+
const resolution = resolveSelectableSkillById(skillId);
|
|
257
|
+
if (!resolution.ok || resolution.resolved.kind !== 'database') {
|
|
258
|
+
throw new Error(`Unsupported database skill in state: ${skillId}`);
|
|
259
|
+
}
|
|
260
|
+
return resolution.resolved.skill;
|
|
261
|
+
}
|
|
@@ -5,7 +5,7 @@ import { sha256 } from '../../shared/hash.js';
|
|
|
5
5
|
import { getSideTemplatePath } from '../../shared/paths.js';
|
|
6
6
|
import { cloneState } from '../workflow-state-registry/index.js';
|
|
7
7
|
import { getTemplateVersion, renderTemplateForSync } from '../template-catalog-rendering/index.js';
|
|
8
|
-
import { getSelectedArchitectureSkillFromState, getSelectedFrameworkSkillsFromState, getSelectedLanguageSkillsFromState, getSelectedWorkflowSkillsFromState, } from '../workflow-target-planning/index.js';
|
|
8
|
+
import { getSelectedArchitectureSkillFromState, getSelectedDatabaseSkillsFromState, getSelectedFrameworkSkillsFromState, getSelectedLanguageSkillsFromState, getSelectedMcpServersFromState, getSelectedWorkflowSkillsFromState, } from '../workflow-target-planning/index.js';
|
|
9
9
|
export function applySyncAction({ rootPath, state, manifest, preview, action, }) {
|
|
10
10
|
const nextState = cloneState(state);
|
|
11
11
|
const managedFile = nextState.managedFiles[preview.relativePath] ?? preview.managedFile;
|
|
@@ -20,6 +20,8 @@ export function applySyncAction({ rootPath, state, manifest, preview, action, })
|
|
|
20
20
|
selectedFrameworkSkills: getSelectedFrameworkSkillsFromState(nextState),
|
|
21
21
|
selectedArchitectureSkill: getSelectedArchitectureSkillFromState(nextState),
|
|
22
22
|
selectedWorkflowSkills: getSelectedWorkflowSkillsFromState(nextState),
|
|
23
|
+
selectedDatabaseSkills: getSelectedDatabaseSkillsFromState(nextState),
|
|
24
|
+
selectedMcpServers: getSelectedMcpServersFromState(nextState),
|
|
23
25
|
});
|
|
24
26
|
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
25
27
|
fs.writeFileSync(targetPath, contents, 'utf8');
|
|
@@ -56,6 +58,8 @@ export function applySyncAction({ rootPath, state, manifest, preview, action, })
|
|
|
56
58
|
selectedFrameworkSkills: getSelectedFrameworkSkillsFromState(state),
|
|
57
59
|
selectedArchitectureSkill: getSelectedArchitectureSkillFromState(state),
|
|
58
60
|
selectedWorkflowSkills: getSelectedWorkflowSkillsFromState(state),
|
|
61
|
+
selectedDatabaseSkills: getSelectedDatabaseSkillsFromState(state),
|
|
62
|
+
selectedMcpServers: getSelectedMcpServersFromState(state),
|
|
59
63
|
});
|
|
60
64
|
fs.mkdirSync(path.dirname(sideTemplatePath), { recursive: true });
|
|
61
65
|
fs.writeFileSync(sideTemplatePath, contents, 'utf8');
|
|
@@ -3,7 +3,7 @@ import path from 'node:path';
|
|
|
3
3
|
import { sha256 } from '../../shared/hash.js';
|
|
4
4
|
import { hasReviewedTemplateVersion } from '../workflow-state-registry/index.js';
|
|
5
5
|
import { renderTemplateForSync } from '../template-catalog-rendering/index.js';
|
|
6
|
-
import { getSelectedAgentsFromState, getSelectedArchitectureSkillFromState, getSelectedFrameworkSkillsFromState, getSelectedLanguageSkillsFromState, getSelectedWorkflowSkillsFromState, getWorkflowTargets, } from '../workflow-target-planning/index.js';
|
|
6
|
+
import { getSelectedAgentsFromState, getSelectedArchitectureSkillFromState, getSelectedDatabaseSkillsFromState, getSelectedFrameworkSkillsFromState, getSelectedLanguageSkillsFromState, getSelectedMcpServersFromState, getSelectedWorkflowSkillsFromState, getWorkflowTargets, } from '../workflow-target-planning/index.js';
|
|
7
7
|
const ACTIONABLE_SYNC_PREVIEW_STATUSES = new Set([
|
|
8
8
|
'new-template',
|
|
9
9
|
'missing',
|
|
@@ -30,6 +30,8 @@ export function getSyncPreviews({ rootPath, state, manifest }) {
|
|
|
30
30
|
const selectedFrameworkSkills = getSelectedFrameworkSkillsFromState(state);
|
|
31
31
|
const selectedArchitectureSkill = getSelectedArchitectureSkillFromState(state);
|
|
32
32
|
const selectedWorkflowSkills = getSelectedWorkflowSkillsFromState(state);
|
|
33
|
+
const selectedDatabaseSkills = getSelectedDatabaseSkillsFromState(state);
|
|
34
|
+
const selectedMcpServers = getSelectedMcpServersFromState(state);
|
|
33
35
|
const previews = Object.entries(state.managedFiles).map(([relativePath, managedFile]) => getManagedFileSyncPreview({
|
|
34
36
|
rootPath,
|
|
35
37
|
manifest,
|
|
@@ -39,8 +41,10 @@ export function getSyncPreviews({ rootPath, state, manifest }) {
|
|
|
39
41
|
selectedFrameworkSkills,
|
|
40
42
|
selectedArchitectureSkill,
|
|
41
43
|
selectedWorkflowSkills,
|
|
44
|
+
selectedDatabaseSkills,
|
|
45
|
+
selectedMcpServers,
|
|
42
46
|
}));
|
|
43
|
-
const expectedTargets = getWorkflowTargets(rootPath, getSelectedAgentsFromState(state), selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, selectedWorkflowSkills);
|
|
47
|
+
const expectedTargets = getWorkflowTargets(rootPath, getSelectedAgentsFromState(state), selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, selectedWorkflowSkills, selectedDatabaseSkills, selectedMcpServers);
|
|
44
48
|
for (const target of expectedTargets) {
|
|
45
49
|
const relativePath = path.relative(rootPath, target.targetPath);
|
|
46
50
|
if (state.managedFiles[relativePath]) {
|
|
@@ -77,7 +81,7 @@ export function getSyncPreviews({ rootPath, state, manifest }) {
|
|
|
77
81
|
}
|
|
78
82
|
return previews;
|
|
79
83
|
}
|
|
80
|
-
function getManagedFileSyncPreview({ rootPath, manifest, relativePath, managedFile, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, selectedWorkflowSkills, }) {
|
|
84
|
+
function getManagedFileSyncPreview({ rootPath, manifest, relativePath, managedFile, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, selectedWorkflowSkills, selectedDatabaseSkills, selectedMcpServers, }) {
|
|
81
85
|
if (managedFile.status === 'unmanaged') {
|
|
82
86
|
return { relativePath, managedFile, status: 'unmanaged', recordedTemplateVersion: managedFile.templateVersion, changes: [] };
|
|
83
87
|
}
|
|
@@ -97,9 +101,11 @@ function getManagedFileSyncPreview({ rootPath, manifest, relativePath, managedFi
|
|
|
97
101
|
selectedFrameworkSkills,
|
|
98
102
|
selectedArchitectureSkill,
|
|
99
103
|
selectedWorkflowSkills,
|
|
104
|
+
selectedDatabaseSkills,
|
|
105
|
+
selectedMcpServers,
|
|
100
106
|
});
|
|
101
107
|
const hasUpdate = hasTemplateUpdate || hasSelectionUpdate;
|
|
102
|
-
const changes = hasTemplateUpdate ? template.changes : [
|
|
108
|
+
const changes = hasTemplateUpdate ? template.changes : [getSelectionRefreshChange(managedFile.template)];
|
|
103
109
|
if (!hasLocalFile) {
|
|
104
110
|
return {
|
|
105
111
|
relativePath,
|
|
@@ -154,8 +160,14 @@ function getManagedFileSyncPreview({ rootPath, manifest, relativePath, managedFi
|
|
|
154
160
|
hasLocalFile,
|
|
155
161
|
};
|
|
156
162
|
}
|
|
157
|
-
function
|
|
158
|
-
if (
|
|
163
|
+
function getSelectionRefreshChange(templateRelativePath) {
|
|
164
|
+
if (templateRelativePath === '.codex/config.toml' || templateRelativePath === 'mcp/claude/.mcp.json' || templateRelativePath === 'mcp/gemini/settings.json') {
|
|
165
|
+
return 'Refreshes generated MCP configuration for the current selected MCP servers.';
|
|
166
|
+
}
|
|
167
|
+
return 'Refreshes generated skill routing for the current selected skills.';
|
|
168
|
+
}
|
|
169
|
+
function hasRenderedSelectionUpdate({ relativePath, currentPath, managedFile, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, selectedWorkflowSkills, selectedDatabaseSkills, selectedMcpServers, }) {
|
|
170
|
+
if (relativePath !== 'AGENTS.md' && selectedMcpServers.length === 0) {
|
|
159
171
|
return false;
|
|
160
172
|
}
|
|
161
173
|
const renderedContents = renderTemplateForSync({
|
|
@@ -165,6 +177,8 @@ function hasRenderedSelectionUpdate({ relativePath, currentPath, managedFile, se
|
|
|
165
177
|
selectedFrameworkSkills,
|
|
166
178
|
selectedArchitectureSkill,
|
|
167
179
|
selectedWorkflowSkills,
|
|
180
|
+
selectedDatabaseSkills,
|
|
181
|
+
selectedMcpServers,
|
|
168
182
|
});
|
|
169
183
|
return sha256(renderedContents) !== managedFile.sha256;
|
|
170
184
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { extractProjectOverview, getTemplateVersion, readTemplate, readTemplateManifest, renderSkillRouting, renderTemplateForSync, } from './templates.js';
|
|
1
|
+
export { extractProjectOverview, getTemplateVersion, readTemplate, readTemplateManifest, renderMcpConfig, renderSkillRouting, renderTemplateForSync, } from './templates.js';
|
|
@@ -18,22 +18,38 @@ export function getTemplateVersion(manifest, templateRelativePath) {
|
|
|
18
18
|
}
|
|
19
19
|
return template.version;
|
|
20
20
|
}
|
|
21
|
-
export function renderTemplateForSync({ templateRelativePath, currentPath, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, selectedWorkflowSkills = [], }) {
|
|
21
|
+
export function renderTemplateForSync({ templateRelativePath, currentPath, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, selectedWorkflowSkills = [], selectedDatabaseSkills = [], selectedMcpServers = [], }) {
|
|
22
22
|
let contents = readTemplate(templateRelativePath);
|
|
23
|
+
const mcpConfigAgentId = getMcpConfigAgentId(templateRelativePath);
|
|
24
|
+
if (mcpConfigAgentId) {
|
|
25
|
+
return renderMcpConfig({ agentId: mcpConfigAgentId, baseContents: contents, selectedMcpServers });
|
|
26
|
+
}
|
|
23
27
|
if (contents.includes('{{PROJECT_OVERVIEW}}')) {
|
|
24
28
|
contents = contents.replace('{{PROJECT_OVERVIEW}}', extractProjectOverview(currentPath) ?? 'Describe this project.');
|
|
25
29
|
}
|
|
26
|
-
return renderSkillRouting(contents, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, selectedWorkflowSkills);
|
|
30
|
+
return renderSkillRouting(contents, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, selectedWorkflowSkills, selectedDatabaseSkills);
|
|
27
31
|
}
|
|
28
|
-
export function renderSkillRouting(contents, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, selectedWorkflowSkills = []) {
|
|
32
|
+
export function renderSkillRouting(contents, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, selectedWorkflowSkills = [], selectedDatabaseSkills = []) {
|
|
29
33
|
if (!contents.includes('{{OPTIONAL_SKILL_ROUTING}}')) {
|
|
30
34
|
return contents;
|
|
31
35
|
}
|
|
32
|
-
const optionalRouting = [...selectedLanguageSkills, ...selectedFrameworkSkills, ...(selectedArchitectureSkill ? [selectedArchitectureSkill] : []), ...selectedWorkflowSkills]
|
|
36
|
+
const optionalRouting = [...selectedLanguageSkills, ...selectedFrameworkSkills, ...selectedDatabaseSkills, ...(selectedArchitectureSkill ? [selectedArchitectureSkill] : []), ...selectedWorkflowSkills]
|
|
33
37
|
.map((skill) => `- ${skill.routingInstruction}`)
|
|
34
38
|
.join('\n');
|
|
35
39
|
return contents.replace('{{OPTIONAL_SKILL_ROUTING}}', optionalRouting);
|
|
36
40
|
}
|
|
41
|
+
export function renderMcpConfig({ agentId, baseContents, selectedMcpServers, }) {
|
|
42
|
+
const githubServer = selectedMcpServers.find((server) => server.id === 'github');
|
|
43
|
+
if (!githubServer) {
|
|
44
|
+
return baseContents ?? renderJsonMcpConfig({});
|
|
45
|
+
}
|
|
46
|
+
if (agentId === 'codex') {
|
|
47
|
+
return renderCodexGithubMcpConfig(baseContents ?? '');
|
|
48
|
+
}
|
|
49
|
+
return renderJsonMcpConfig({
|
|
50
|
+
github: buildGithubMcpServerConfig(agentId),
|
|
51
|
+
});
|
|
52
|
+
}
|
|
37
53
|
export function extractProjectOverview(filePath) {
|
|
38
54
|
if (!fs.existsSync(filePath)) {
|
|
39
55
|
return undefined;
|
|
@@ -43,6 +59,45 @@ export function extractProjectOverview(filePath) {
|
|
|
43
59
|
const overview = match?.[1]?.trim();
|
|
44
60
|
return overview || undefined;
|
|
45
61
|
}
|
|
62
|
+
function renderCodexGithubMcpConfig(baseContents) {
|
|
63
|
+
const trimmedBaseContents = baseContents.trimEnd();
|
|
64
|
+
const separator = trimmedBaseContents ? '\n\n' : '';
|
|
65
|
+
return `${trimmedBaseContents}${separator}[mcp_servers.github]
|
|
66
|
+
url = "https://api.githubcopilot.com/mcp/"
|
|
67
|
+
bearer_token_env_var = "GITHUB_PERSONAL_ACCESS_TOKEN"
|
|
68
|
+
`;
|
|
69
|
+
}
|
|
70
|
+
function getMcpConfigAgentId(templateRelativePath) {
|
|
71
|
+
if (templateRelativePath === '.codex/config.toml') {
|
|
72
|
+
return 'codex';
|
|
73
|
+
}
|
|
74
|
+
if (templateRelativePath === 'mcp/claude/.mcp.json') {
|
|
75
|
+
return 'claude';
|
|
76
|
+
}
|
|
77
|
+
if (templateRelativePath === 'mcp/gemini/settings.json') {
|
|
78
|
+
return 'gemini';
|
|
79
|
+
}
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
function buildGithubMcpServerConfig(agentId) {
|
|
83
|
+
const headers = {
|
|
84
|
+
Authorization: 'Bearer ${GITHUB_PERSONAL_ACCESS_TOKEN}',
|
|
85
|
+
};
|
|
86
|
+
if (agentId === 'gemini') {
|
|
87
|
+
return {
|
|
88
|
+
httpUrl: 'https://api.githubcopilot.com/mcp/',
|
|
89
|
+
headers,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
type: 'http',
|
|
94
|
+
url: 'https://api.githubcopilot.com/mcp/',
|
|
95
|
+
headers,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
function renderJsonMcpConfig(mcpServers) {
|
|
99
|
+
return `${JSON.stringify({ mcpServers }, null, 2)}\n`;
|
|
100
|
+
}
|
|
46
101
|
function isTemplateManifest(value) {
|
|
47
102
|
if (!value || typeof value !== 'object') {
|
|
48
103
|
return false;
|
|
@@ -3,14 +3,14 @@ import path from 'node:path';
|
|
|
3
3
|
import { intro, log, outro } from '@clack/prompts';
|
|
4
4
|
import chalk from 'chalk';
|
|
5
5
|
import { STATE_RELATIVE_PATH } from '../../shared/catalog.js';
|
|
6
|
-
import { SELECTABLE_ARCHITECTURE_SKILLS, SELECTABLE_FRAMEWORK_SKILLS, SELECTABLE_LANGUAGE_SKILLS, SUPPORTED_AGENTS } from '../workflow-target-planning/index.js';
|
|
6
|
+
import { SELECTABLE_ARCHITECTURE_SKILLS, SELECTABLE_DATABASE_SKILLS, SELECTABLE_FRAMEWORK_SKILLS, SELECTABLE_LANGUAGE_SKILLS, SELECTABLE_MCP_SERVERS, SUPPORTED_AGENTS } from '../workflow-target-planning/index.js';
|
|
7
7
|
import { sha256 } from '../../shared/hash.js';
|
|
8
8
|
import { checkForLatestSibuVersion } from '../version-advisory/index.js';
|
|
9
9
|
import { getProjectContext } from '../../shared/paths.js';
|
|
10
10
|
import { renderIntro } from '../interactive-guidance/index.js';
|
|
11
11
|
import { hasReviewedTemplateVersion, readStateForDoctor } from '../workflow-state-registry/index.js';
|
|
12
12
|
import { getTemplateVersion, readTemplateManifest } from '../template-catalog-rendering/index.js';
|
|
13
|
-
import { getSelectedAgentsFromState, getSelectedArchitectureSkillFromState, getSelectedFrameworkSkillsFromState, getSelectedLanguageSkillsFromState, getWorkflowTargets, } from '../workflow-target-planning/index.js';
|
|
13
|
+
import { getSelectedAgentsFromState, getSelectedArchitectureSkillFromState, getSelectedDatabaseSkillsFromState, getSelectedFrameworkSkillsFromState, getSelectedLanguageSkillsFromState, getSelectedMcpServersFromState, getSelectedWorkflowSkillsFromState, getWorkflowTargets, } from '../workflow-target-planning/index.js';
|
|
14
14
|
export async function handleDoctorProject(_command) {
|
|
15
15
|
await renderIntro();
|
|
16
16
|
intro(chalk.cyan('Checking workflow state'));
|
|
@@ -117,6 +117,16 @@ function addUnsupportedSelectionIssues(state, issues) {
|
|
|
117
117
|
if (state.selectedArchitectureSkill && !SELECTABLE_ARCHITECTURE_SKILLS.some((skill) => skill.id === state.selectedArchitectureSkill)) {
|
|
118
118
|
issues.push({ severity: 'warning', message: `State references unsupported architecture skill: ${state.selectedArchitectureSkill}.` });
|
|
119
119
|
}
|
|
120
|
+
for (const selectedDatabaseSkill of state.selectedDatabaseSkills ?? []) {
|
|
121
|
+
if (!SELECTABLE_DATABASE_SKILLS.some((skill) => skill.id === selectedDatabaseSkill)) {
|
|
122
|
+
issues.push({ severity: 'warning', message: `State references unsupported database skill: ${selectedDatabaseSkill}.` });
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
for (const selectedMcpServer of state.selectedMcpServers ?? []) {
|
|
126
|
+
if (!SELECTABLE_MCP_SERVERS.some((server) => server.id === selectedMcpServer)) {
|
|
127
|
+
issues.push({ severity: 'warning', message: `State references unsupported MCP server: ${selectedMcpServer}.` });
|
|
128
|
+
}
|
|
129
|
+
}
|
|
120
130
|
for (const reviewedArchitectureSkill of state.reviewedArchitectureSkills ?? []) {
|
|
121
131
|
if (!SELECTABLE_ARCHITECTURE_SKILLS.some((skill) => skill.id === reviewedArchitectureSkill)) {
|
|
122
132
|
issues.push({ severity: 'warning', message: `State references unsupported reviewed architecture skill: ${reviewedArchitectureSkill}.` });
|
|
@@ -125,7 +135,7 @@ function addUnsupportedSelectionIssues(state, issues) {
|
|
|
125
135
|
}
|
|
126
136
|
function addExpectedTargetIssues(rootPath, state, issues) {
|
|
127
137
|
const reportedMissingPaths = new Set();
|
|
128
|
-
const expectedTargets = getWorkflowTargets(rootPath, getSelectedAgentsFromState(state), getSelectedLanguageSkillsFromState(state), getSelectedFrameworkSkillsFromState(state), getSelectedArchitectureSkillFromState(state));
|
|
138
|
+
const expectedTargets = getWorkflowTargets(rootPath, getSelectedAgentsFromState(state), getSelectedLanguageSkillsFromState(state), getSelectedFrameworkSkillsFromState(state), getSelectedArchitectureSkillFromState(state), getSelectedWorkflowSkillsFromState(state), getSelectedDatabaseSkillsFromState(state), getSelectedMcpServersFromState(state));
|
|
129
139
|
for (const target of expectedTargets) {
|
|
130
140
|
const relativePath = path.relative(rootPath, target.targetPath);
|
|
131
141
|
const managedFile = state.managedFiles[relativePath];
|
|
@@ -57,6 +57,10 @@ function isSibuState(value) {
|
|
|
57
57
|
(state.selectedArchitectureSkill === undefined || typeof state.selectedArchitectureSkill === 'string') &&
|
|
58
58
|
(state.selectedWorkflowSkills === undefined ||
|
|
59
59
|
(Array.isArray(state.selectedWorkflowSkills) && state.selectedWorkflowSkills.every((skill) => typeof skill === 'string'))) &&
|
|
60
|
+
(state.selectedDatabaseSkills === undefined ||
|
|
61
|
+
(Array.isArray(state.selectedDatabaseSkills) && state.selectedDatabaseSkills.every((skill) => typeof skill === 'string'))) &&
|
|
62
|
+
(state.selectedMcpServers === undefined ||
|
|
63
|
+
(Array.isArray(state.selectedMcpServers) && state.selectedMcpServers.every((server) => typeof server === 'string'))) &&
|
|
60
64
|
(state.reviewedArchitectureSkills === undefined ||
|
|
61
65
|
(Array.isArray(state.reviewedArchitectureSkills) && state.reviewedArchitectureSkills.every((skill) => typeof skill === 'string'))) &&
|
|
62
66
|
!!state.managedFiles &&
|
|
@@ -100,6 +100,21 @@ export const SELECTABLE_LANGUAGE_SKILLS = [
|
|
|
100
100
|
},
|
|
101
101
|
},
|
|
102
102
|
];
|
|
103
|
+
export const SELECTABLE_DATABASE_SKILLS = [
|
|
104
|
+
{
|
|
105
|
+
id: 'postgresql-expert',
|
|
106
|
+
name: 'PostgreSQL Expert',
|
|
107
|
+
description: 'Install practical guidance for PostgreSQL schema design, migrations, constraints, queries, indexing, and database tradeoffs',
|
|
108
|
+
routingInstruction: 'For PostgreSQL schema design, migrations, constraints, queries, indexing, or database tradeoffs, use `postgresql-expert`.',
|
|
109
|
+
templateRelativePath: 'skills/postgresql-expert/SKILL.md',
|
|
110
|
+
targetRelativePathsByAgent: {
|
|
111
|
+
codex: '.agents/skills/postgresql-expert/SKILL.md',
|
|
112
|
+
gemini: '.agents/skills/postgresql-expert/SKILL.md',
|
|
113
|
+
claude: '.agents/skills/postgresql-expert/SKILL.md',
|
|
114
|
+
windsurf: '.agents/skills/postgresql-expert/SKILL.md',
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
];
|
|
103
118
|
export const SELECTABLE_FRAMEWORK_SKILLS = [
|
|
104
119
|
{
|
|
105
120
|
id: 'react',
|
|
@@ -184,6 +199,14 @@ export const SELECTABLE_WORKFLOW_SKILLS = [
|
|
|
184
199
|
},
|
|
185
200
|
},
|
|
186
201
|
];
|
|
202
|
+
export const SELECTABLE_MCP_SERVERS = [
|
|
203
|
+
{
|
|
204
|
+
id: 'github',
|
|
205
|
+
name: 'GitHub MCP Server',
|
|
206
|
+
description: "Configure GitHub's official MCP server; Sibu writes config only, while prerequisites, runtime availability, credentials, and authentication remain user-owned",
|
|
207
|
+
source: 'github/github-mcp-server',
|
|
208
|
+
},
|
|
209
|
+
];
|
|
187
210
|
export const SUPPORTED_AGENTS = [
|
|
188
211
|
{
|
|
189
212
|
id: 'codex',
|
|
@@ -212,6 +235,13 @@ export const SUPPORTED_AGENTS = [
|
|
|
212
235
|
description: 'Use root AGENTS.md and shared .agents/skills/ discovery',
|
|
213
236
|
},
|
|
214
237
|
];
|
|
238
|
+
export function resolveSelectableMcpServerById(serverId) {
|
|
239
|
+
const server = SELECTABLE_MCP_SERVERS.find((selectableServer) => selectableServer.id === serverId);
|
|
240
|
+
if (server) {
|
|
241
|
+
return { ok: true, resolved: { server } };
|
|
242
|
+
}
|
|
243
|
+
return { ok: false, message: `Unknown MCP server \`${serverId}\`. Run \`sibu mcp list\` to see available MCP servers.` };
|
|
244
|
+
}
|
|
215
245
|
export function resolveSelectableSkillById(skillId) {
|
|
216
246
|
const languageSkill = SELECTABLE_LANGUAGE_SKILLS.find((skill) => skill.id === skillId);
|
|
217
247
|
if (languageSkill) {
|
|
@@ -221,6 +251,10 @@ export function resolveSelectableSkillById(skillId) {
|
|
|
221
251
|
if (frameworkSkill) {
|
|
222
252
|
return { ok: true, resolved: { kind: 'framework', skill: frameworkSkill } };
|
|
223
253
|
}
|
|
254
|
+
const databaseSkill = SELECTABLE_DATABASE_SKILLS.find((skill) => skill.id === skillId);
|
|
255
|
+
if (databaseSkill) {
|
|
256
|
+
return { ok: true, resolved: { kind: 'database', skill: databaseSkill } };
|
|
257
|
+
}
|
|
224
258
|
const architectureSkill = SELECTABLE_ARCHITECTURE_SKILLS.find((skill) => skill.id === skillId);
|
|
225
259
|
if (architectureSkill) {
|
|
226
260
|
return { ok: true, resolved: { kind: 'architecture', skill: architectureSkill } };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { MANDATORY_SKILLS, SELECTABLE_ARCHITECTURE_SKILLS, SELECTABLE_FRAMEWORK_SKILLS, SELECTABLE_LANGUAGE_SKILLS, SELECTABLE_WORKFLOW_SKILLS, SUPPORTED_AGENTS, resolveSelectableSkillById, } from './catalog.js';
|
|
2
|
-
export { getSelectedAgentsFromState, getSelectedArchitectureSkillFromState, getSelectedFrameworkSkillsFromState, getSelectedLanguageSkillsFromState, getSelectedSkillTargetsForAgents, getSelectedWorkflowSkillsFromState, getWorkflowTargets, renderMissingWorkflowFiles, writeSibuState, } from './workflow-targets.js';
|
|
1
|
+
export { MANDATORY_SKILLS, SELECTABLE_ARCHITECTURE_SKILLS, SELECTABLE_DATABASE_SKILLS, SELECTABLE_FRAMEWORK_SKILLS, SELECTABLE_LANGUAGE_SKILLS, SELECTABLE_MCP_SERVERS, SELECTABLE_WORKFLOW_SKILLS, SUPPORTED_AGENTS, resolveSelectableMcpServerById, resolveSelectableSkillById, } from './catalog.js';
|
|
2
|
+
export { getSelectedAgentsFromState, getSelectedArchitectureSkillFromState, getSelectedDatabaseSkillsFromState, getSelectedFrameworkSkillsFromState, getSelectedLanguageSkillsFromState, getSelectedMcpServersFromState, getSelectedMcpTargetsForAgents, getSelectedSkillTargetsForAgents, getSelectedWorkflowSkillsFromState, getWorkflowTargets, renderMissingWorkflowFiles, writeSibuState, } from './workflow-targets.js';
|