@juancr11/sibu 0.7.0 → 0.8.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/bin/modules/interactive-guidance/index.js +1 -1
- package/bin/modules/interactive-guidance/prompts.js +17 -1
- package/bin/modules/project-adoption/handler.js +5 -3
- 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 +3 -1
- package/bin/modules/sync-review/sync-preview.js +8 -4
- package/bin/modules/template-catalog-rendering/templates.js +4 -4
- package/bin/modules/workflow-health-diagnosis/handler.js +8 -3
- package/bin/modules/workflow-state-registry/state.js +2 -0
- package/bin/modules/workflow-target-planning/catalog.js +19 -0
- package/bin/modules/workflow-target-planning/index.js +2 -2
- package/bin/modules/workflow-target-planning/workflow-targets.js +12 -7
- package/package.json +1 -1
- package/templates/manifest.json +8 -1
- package/templates/skills/postgresql-expert/SKILL.md +72 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
export { askForArchitectureSkill, askForFrameworkSkills, askForLanguageSkills, askForMissingFrameworkSkills, askForNewArchitectureSkill, askForNewLanguageSkills, askForProjectOverview, askForSupportedAgents, askForWorkflowSkills, renderIntro, shouldAskForNewLanguageSkills, } from './prompts.js';
|
|
1
|
+
export { askForArchitectureSkill, askForDatabaseSkills, askForFrameworkSkills, askForLanguageSkills, askForMissingFrameworkSkills, askForNewArchitectureSkill, askForNewLanguageSkills, askForProjectOverview, askForSupportedAgents, askForWorkflowSkills, renderIntro, shouldAskForNewLanguageSkills, } from './prompts.js';
|
|
@@ -3,7 +3,7 @@ import { cancel, isCancel, multiselect, select, text } from '@clack/prompts';
|
|
|
3
3
|
import gradient from 'gradient-string';
|
|
4
4
|
import { Box, Text, render, useApp } from 'ink';
|
|
5
5
|
import { useEffect } from 'react';
|
|
6
|
-
import { SELECTABLE_ARCHITECTURE_SKILLS, SELECTABLE_FRAMEWORK_SKILLS, SELECTABLE_LANGUAGE_SKILLS, SELECTABLE_WORKFLOW_SKILLS, SUPPORTED_AGENTS } from '../workflow-target-planning/index.js';
|
|
6
|
+
import { SELECTABLE_ARCHITECTURE_SKILLS, SELECTABLE_DATABASE_SKILLS, SELECTABLE_FRAMEWORK_SKILLS, SELECTABLE_LANGUAGE_SKILLS, SELECTABLE_WORKFLOW_SKILLS, SUPPORTED_AGENTS } from '../workflow-target-planning/index.js';
|
|
7
7
|
const NONE_OPTION_ID = 'none';
|
|
8
8
|
export async function renderIntro() {
|
|
9
9
|
console.log(gradient(['#39ff14', '#00e5ff', '#9b5de5']).multiline('⧖ S I B U ⧖'));
|
|
@@ -56,6 +56,22 @@ export async function askForFrameworkSkills() {
|
|
|
56
56
|
cancelMessage: 'Initialization cancelled.',
|
|
57
57
|
});
|
|
58
58
|
}
|
|
59
|
+
export async function askForDatabaseSkills() {
|
|
60
|
+
const selectedDatabaseSkillIds = await multiselect({
|
|
61
|
+
message: 'Select the database skills this project should support.',
|
|
62
|
+
required: false,
|
|
63
|
+
options: SELECTABLE_DATABASE_SKILLS.map((skill) => ({
|
|
64
|
+
value: skill.id,
|
|
65
|
+
label: skill.name,
|
|
66
|
+
hint: skill.description,
|
|
67
|
+
})),
|
|
68
|
+
});
|
|
69
|
+
if (isCancel(selectedDatabaseSkillIds)) {
|
|
70
|
+
cancel('Initialization cancelled.');
|
|
71
|
+
process.exit(0);
|
|
72
|
+
}
|
|
73
|
+
return SELECTABLE_DATABASE_SKILLS.filter((skill) => selectedDatabaseSkillIds.includes(skill.id));
|
|
74
|
+
}
|
|
59
75
|
async function askForFrameworkSkillSelection({ message, cancelMessage, }) {
|
|
60
76
|
const selectedFrameworkSkillIds = await multiselect({
|
|
61
77
|
message,
|
|
@@ -4,7 +4,7 @@ import { intro, log, outro } from '@clack/prompts';
|
|
|
4
4
|
import chalk from 'chalk';
|
|
5
5
|
import { STATE_RELATIVE_PATH } from '../../shared/catalog.js';
|
|
6
6
|
import { getProjectContext } from '../../shared/paths.js';
|
|
7
|
-
import { askForArchitectureSkill, askForFrameworkSkills, askForLanguageSkills, askForProjectOverview, askForSupportedAgents, askForWorkflowSkills, renderIntro } from '../interactive-guidance/index.js';
|
|
7
|
+
import { askForArchitectureSkill, askForDatabaseSkills, askForFrameworkSkills, askForLanguageSkills, askForProjectOverview, askForSupportedAgents, askForWorkflowSkills, renderIntro } from '../interactive-guidance/index.js';
|
|
8
8
|
import { readStateForDoctor } from '../workflow-state-registry/index.js';
|
|
9
9
|
import { getWorkflowTargets, renderMissingWorkflowFiles, writeSibuState } from '../workflow-target-planning/index.js';
|
|
10
10
|
export async function handleInitProject(_command) {
|
|
@@ -30,9 +30,10 @@ export async function handleInitProject(_command) {
|
|
|
30
30
|
const selectedAgents = await askForSupportedAgents();
|
|
31
31
|
const selectedLanguageSkills = await askForLanguageSkills();
|
|
32
32
|
const selectedFrameworkSkills = await askForFrameworkSkills();
|
|
33
|
+
const selectedDatabaseSkills = await askForDatabaseSkills();
|
|
33
34
|
const selectedArchitectureSkill = await askForArchitectureSkill();
|
|
34
35
|
const selectedWorkflowSkills = await askForWorkflowSkills();
|
|
35
|
-
const targets = getWorkflowTargets(rootPath, selectedAgents, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, selectedWorkflowSkills);
|
|
36
|
+
const targets = getWorkflowTargets(rootPath, selectedAgents, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, selectedWorkflowSkills, selectedDatabaseSkills);
|
|
36
37
|
const missingTargets = targets.filter((target) => !fs.existsSync(target.targetPath));
|
|
37
38
|
log.message('I will create this project’s initial AI workflow files.');
|
|
38
39
|
for (const target of targets) {
|
|
@@ -53,13 +54,14 @@ export async function handleInitProject(_command) {
|
|
|
53
54
|
selectedFrameworkSkills,
|
|
54
55
|
selectedArchitectureSkill,
|
|
55
56
|
selectedWorkflowSkills,
|
|
57
|
+
selectedDatabaseSkills,
|
|
56
58
|
});
|
|
57
59
|
for (const file of files) {
|
|
58
60
|
fs.mkdirSync(path.dirname(file.targetPath), { recursive: true });
|
|
59
61
|
fs.writeFileSync(file.targetPath, file.contents, { encoding: 'utf8', flag: 'wx' });
|
|
60
62
|
log.success(`Created ${file.label}`);
|
|
61
63
|
}
|
|
62
|
-
writeSibuState({ rootPath, statePath, selectedAgents, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, selectedWorkflowSkills, targets });
|
|
64
|
+
writeSibuState({ rootPath, statePath, selectedAgents, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, selectedWorkflowSkills, selectedDatabaseSkills, targets });
|
|
63
65
|
log.success(`Created ${STATE_RELATIVE_PATH}`);
|
|
64
66
|
outro(chalk.green('Setup complete.'));
|
|
65
67
|
}
|
|
@@ -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, 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,7 @@ 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),
|
|
23
24
|
});
|
|
24
25
|
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
25
26
|
fs.writeFileSync(targetPath, contents, 'utf8');
|
|
@@ -56,6 +57,7 @@ export function applySyncAction({ rootPath, state, manifest, preview, action, })
|
|
|
56
57
|
selectedFrameworkSkills: getSelectedFrameworkSkillsFromState(state),
|
|
57
58
|
selectedArchitectureSkill: getSelectedArchitectureSkillFromState(state),
|
|
58
59
|
selectedWorkflowSkills: getSelectedWorkflowSkillsFromState(state),
|
|
60
|
+
selectedDatabaseSkills: getSelectedDatabaseSkillsFromState(state),
|
|
59
61
|
});
|
|
60
62
|
fs.mkdirSync(path.dirname(sideTemplatePath), { recursive: true });
|
|
61
63
|
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, 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,7 @@ 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);
|
|
33
34
|
const previews = Object.entries(state.managedFiles).map(([relativePath, managedFile]) => getManagedFileSyncPreview({
|
|
34
35
|
rootPath,
|
|
35
36
|
manifest,
|
|
@@ -39,8 +40,9 @@ export function getSyncPreviews({ rootPath, state, manifest }) {
|
|
|
39
40
|
selectedFrameworkSkills,
|
|
40
41
|
selectedArchitectureSkill,
|
|
41
42
|
selectedWorkflowSkills,
|
|
43
|
+
selectedDatabaseSkills,
|
|
42
44
|
}));
|
|
43
|
-
const expectedTargets = getWorkflowTargets(rootPath, getSelectedAgentsFromState(state), selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, selectedWorkflowSkills);
|
|
45
|
+
const expectedTargets = getWorkflowTargets(rootPath, getSelectedAgentsFromState(state), selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, selectedWorkflowSkills, selectedDatabaseSkills);
|
|
44
46
|
for (const target of expectedTargets) {
|
|
45
47
|
const relativePath = path.relative(rootPath, target.targetPath);
|
|
46
48
|
if (state.managedFiles[relativePath]) {
|
|
@@ -77,7 +79,7 @@ export function getSyncPreviews({ rootPath, state, manifest }) {
|
|
|
77
79
|
}
|
|
78
80
|
return previews;
|
|
79
81
|
}
|
|
80
|
-
function getManagedFileSyncPreview({ rootPath, manifest, relativePath, managedFile, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, selectedWorkflowSkills, }) {
|
|
82
|
+
function getManagedFileSyncPreview({ rootPath, manifest, relativePath, managedFile, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, selectedWorkflowSkills, selectedDatabaseSkills, }) {
|
|
81
83
|
if (managedFile.status === 'unmanaged') {
|
|
82
84
|
return { relativePath, managedFile, status: 'unmanaged', recordedTemplateVersion: managedFile.templateVersion, changes: [] };
|
|
83
85
|
}
|
|
@@ -97,6 +99,7 @@ function getManagedFileSyncPreview({ rootPath, manifest, relativePath, managedFi
|
|
|
97
99
|
selectedFrameworkSkills,
|
|
98
100
|
selectedArchitectureSkill,
|
|
99
101
|
selectedWorkflowSkills,
|
|
102
|
+
selectedDatabaseSkills,
|
|
100
103
|
});
|
|
101
104
|
const hasUpdate = hasTemplateUpdate || hasSelectionUpdate;
|
|
102
105
|
const changes = hasTemplateUpdate ? template.changes : ['Refreshes generated skill routing for the current selected skills.'];
|
|
@@ -154,7 +157,7 @@ function getManagedFileSyncPreview({ rootPath, manifest, relativePath, managedFi
|
|
|
154
157
|
hasLocalFile,
|
|
155
158
|
};
|
|
156
159
|
}
|
|
157
|
-
function hasRenderedSelectionUpdate({ relativePath, currentPath, managedFile, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, selectedWorkflowSkills, }) {
|
|
160
|
+
function hasRenderedSelectionUpdate({ relativePath, currentPath, managedFile, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, selectedWorkflowSkills, selectedDatabaseSkills, }) {
|
|
158
161
|
if (relativePath !== 'AGENTS.md') {
|
|
159
162
|
return false;
|
|
160
163
|
}
|
|
@@ -165,6 +168,7 @@ function hasRenderedSelectionUpdate({ relativePath, currentPath, managedFile, se
|
|
|
165
168
|
selectedFrameworkSkills,
|
|
166
169
|
selectedArchitectureSkill,
|
|
167
170
|
selectedWorkflowSkills,
|
|
171
|
+
selectedDatabaseSkills,
|
|
168
172
|
});
|
|
169
173
|
return sha256(renderedContents) !== managedFile.sha256;
|
|
170
174
|
}
|
|
@@ -18,18 +18,18 @@ 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 = [], }) {
|
|
22
22
|
let contents = readTemplate(templateRelativePath);
|
|
23
23
|
if (contents.includes('{{PROJECT_OVERVIEW}}')) {
|
|
24
24
|
contents = contents.replace('{{PROJECT_OVERVIEW}}', extractProjectOverview(currentPath) ?? 'Describe this project.');
|
|
25
25
|
}
|
|
26
|
-
return renderSkillRouting(contents, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, selectedWorkflowSkills);
|
|
26
|
+
return renderSkillRouting(contents, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, selectedWorkflowSkills, selectedDatabaseSkills);
|
|
27
27
|
}
|
|
28
|
-
export function renderSkillRouting(contents, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, selectedWorkflowSkills = []) {
|
|
28
|
+
export function renderSkillRouting(contents, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, selectedWorkflowSkills = [], selectedDatabaseSkills = []) {
|
|
29
29
|
if (!contents.includes('{{OPTIONAL_SKILL_ROUTING}}')) {
|
|
30
30
|
return contents;
|
|
31
31
|
}
|
|
32
|
-
const optionalRouting = [...selectedLanguageSkills, ...selectedFrameworkSkills, ...(selectedArchitectureSkill ? [selectedArchitectureSkill] : []), ...selectedWorkflowSkills]
|
|
32
|
+
const optionalRouting = [...selectedLanguageSkills, ...selectedFrameworkSkills, ...selectedDatabaseSkills, ...(selectedArchitectureSkill ? [selectedArchitectureSkill] : []), ...selectedWorkflowSkills]
|
|
33
33
|
.map((skill) => `- ${skill.routingInstruction}`)
|
|
34
34
|
.join('\n');
|
|
35
35
|
return contents.replace('{{OPTIONAL_SKILL_ROUTING}}', optionalRouting);
|
|
@@ -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, 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, 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,11 @@ 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
|
+
}
|
|
120
125
|
for (const reviewedArchitectureSkill of state.reviewedArchitectureSkills ?? []) {
|
|
121
126
|
if (!SELECTABLE_ARCHITECTURE_SKILLS.some((skill) => skill.id === reviewedArchitectureSkill)) {
|
|
122
127
|
issues.push({ severity: 'warning', message: `State references unsupported reviewed architecture skill: ${reviewedArchitectureSkill}.` });
|
|
@@ -125,7 +130,7 @@ function addUnsupportedSelectionIssues(state, issues) {
|
|
|
125
130
|
}
|
|
126
131
|
function addExpectedTargetIssues(rootPath, state, issues) {
|
|
127
132
|
const reportedMissingPaths = new Set();
|
|
128
|
-
const expectedTargets = getWorkflowTargets(rootPath, getSelectedAgentsFromState(state), getSelectedLanguageSkillsFromState(state), getSelectedFrameworkSkillsFromState(state), getSelectedArchitectureSkillFromState(state));
|
|
133
|
+
const expectedTargets = getWorkflowTargets(rootPath, getSelectedAgentsFromState(state), getSelectedLanguageSkillsFromState(state), getSelectedFrameworkSkillsFromState(state), getSelectedArchitectureSkillFromState(state), getSelectedWorkflowSkillsFromState(state), getSelectedDatabaseSkillsFromState(state));
|
|
129
134
|
for (const target of expectedTargets) {
|
|
130
135
|
const relativePath = path.relative(rootPath, target.targetPath);
|
|
131
136
|
const managedFile = state.managedFiles[relativePath];
|
|
@@ -57,6 +57,8 @@ 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'))) &&
|
|
60
62
|
(state.reviewedArchitectureSkills === undefined ||
|
|
61
63
|
(Array.isArray(state.reviewedArchitectureSkills) && state.reviewedArchitectureSkills.every((skill) => typeof skill === 'string'))) &&
|
|
62
64
|
!!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',
|
|
@@ -221,6 +236,10 @@ export function resolveSelectableSkillById(skillId) {
|
|
|
221
236
|
if (frameworkSkill) {
|
|
222
237
|
return { ok: true, resolved: { kind: 'framework', skill: frameworkSkill } };
|
|
223
238
|
}
|
|
239
|
+
const databaseSkill = SELECTABLE_DATABASE_SKILLS.find((skill) => skill.id === skillId);
|
|
240
|
+
if (databaseSkill) {
|
|
241
|
+
return { ok: true, resolved: { kind: 'database', skill: databaseSkill } };
|
|
242
|
+
}
|
|
224
243
|
const architectureSkill = SELECTABLE_ARCHITECTURE_SKILLS.find((skill) => skill.id === skillId);
|
|
225
244
|
if (architectureSkill) {
|
|
226
245
|
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_WORKFLOW_SKILLS, SUPPORTED_AGENTS, resolveSelectableSkillById, } from './catalog.js';
|
|
2
|
+
export { getSelectedAgentsFromState, getSelectedArchitectureSkillFromState, getSelectedDatabaseSkillsFromState, getSelectedFrameworkSkillsFromState, getSelectedLanguageSkillsFromState, getSelectedSkillTargetsForAgents, getSelectedWorkflowSkillsFromState, getWorkflowTargets, renderMissingWorkflowFiles, writeSibuState, } from './workflow-targets.js';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { SIBU_VERSION } from '../version-advisory/index.js';
|
|
4
|
-
import { MANDATORY_SKILLS, SELECTABLE_ARCHITECTURE_SKILLS, SELECTABLE_FRAMEWORK_SKILLS, SELECTABLE_LANGUAGE_SKILLS, SELECTABLE_WORKFLOW_SKILLS, SUPPORTED_AGENTS, } from './index.js';
|
|
4
|
+
import { MANDATORY_SKILLS, SELECTABLE_ARCHITECTURE_SKILLS, SELECTABLE_DATABASE_SKILLS, SELECTABLE_FRAMEWORK_SKILLS, SELECTABLE_LANGUAGE_SKILLS, SELECTABLE_WORKFLOW_SKILLS, SUPPORTED_AGENTS, } from './index.js';
|
|
5
5
|
import { sha256 } from '../../shared/hash.js';
|
|
6
6
|
import { removeUndefinedFields } from '../../shared/object.js';
|
|
7
7
|
import { readExistingState } from '../workflow-state-registry/index.js';
|
|
@@ -18,12 +18,16 @@ export function getSelectedArchitectureSkillFromState(state) {
|
|
|
18
18
|
export function getSelectedWorkflowSkillsFromState(state) {
|
|
19
19
|
return SELECTABLE_WORKFLOW_SKILLS.filter((skill) => state.selectedWorkflowSkills?.includes(skill.id));
|
|
20
20
|
}
|
|
21
|
-
export function
|
|
21
|
+
export function getSelectedDatabaseSkillsFromState(state) {
|
|
22
|
+
return SELECTABLE_DATABASE_SKILLS.filter((skill) => state.selectedDatabaseSkills?.includes(skill.id));
|
|
23
|
+
}
|
|
24
|
+
export function getSelectedSkillTargetsForAgents(selectedAgents, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, selectedWorkflowSkills = [], selectedDatabaseSkills = []) {
|
|
22
25
|
const skillTargets = new Map();
|
|
23
26
|
const selectedSkills = [
|
|
24
27
|
...MANDATORY_SKILLS,
|
|
25
28
|
...selectedLanguageSkills,
|
|
26
29
|
...selectedFrameworkSkills,
|
|
30
|
+
...selectedDatabaseSkills,
|
|
27
31
|
...(selectedArchitectureSkill ? [selectedArchitectureSkill] : []),
|
|
28
32
|
...selectedWorkflowSkills,
|
|
29
33
|
];
|
|
@@ -41,7 +45,7 @@ export function getSelectedSkillTargetsForAgents(selectedAgents, selectedLanguag
|
|
|
41
45
|
}
|
|
42
46
|
return [...skillTargets.values()];
|
|
43
47
|
}
|
|
44
|
-
export function getWorkflowTargets(rootPath, selectedAgents, selectedLanguageSkills = [], selectedFrameworkSkills = [], selectedArchitectureSkill, selectedWorkflowSkills = []) {
|
|
48
|
+
export function getWorkflowTargets(rootPath, selectedAgents, selectedLanguageSkills = [], selectedFrameworkSkills = [], selectedArchitectureSkill, selectedWorkflowSkills = [], selectedDatabaseSkills = []) {
|
|
45
49
|
return [
|
|
46
50
|
{
|
|
47
51
|
label: 'AGENTS.md',
|
|
@@ -62,7 +66,7 @@ export function getWorkflowTargets(rootPath, selectedAgents, selectedLanguageSki
|
|
|
62
66
|
},
|
|
63
67
|
];
|
|
64
68
|
}),
|
|
65
|
-
...getSelectedSkillTargetsForAgents(selectedAgents, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, selectedWorkflowSkills).map((skillTarget) => ({
|
|
69
|
+
...getSelectedSkillTargetsForAgents(selectedAgents, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, selectedWorkflowSkills, selectedDatabaseSkills).map((skillTarget) => ({
|
|
66
70
|
label: skillTarget.targetRelativePath,
|
|
67
71
|
targetPath: path.join(rootPath, skillTarget.targetRelativePath),
|
|
68
72
|
templateRelativePath: skillTarget.templateRelativePath,
|
|
@@ -70,7 +74,7 @@ export function getWorkflowTargets(rootPath, selectedAgents, selectedLanguageSki
|
|
|
70
74
|
})),
|
|
71
75
|
];
|
|
72
76
|
}
|
|
73
|
-
export function renderMissingWorkflowFiles({ missingTargets, overview, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, selectedWorkflowSkills = [], }) {
|
|
77
|
+
export function renderMissingWorkflowFiles({ missingTargets, overview, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, selectedWorkflowSkills = [], selectedDatabaseSkills = [], }) {
|
|
74
78
|
return missingTargets.map((target) => {
|
|
75
79
|
let contents = readTemplate(target.templateRelativePath);
|
|
76
80
|
if (target.requiresProjectOverview) {
|
|
@@ -79,7 +83,7 @@ export function renderMissingWorkflowFiles({ missingTargets, overview, selectedL
|
|
|
79
83
|
}
|
|
80
84
|
contents = contents.replace('{{PROJECT_OVERVIEW}}', overview.trim());
|
|
81
85
|
}
|
|
82
|
-
contents = renderSkillRouting(contents, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, selectedWorkflowSkills);
|
|
86
|
+
contents = renderSkillRouting(contents, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, selectedWorkflowSkills, selectedDatabaseSkills);
|
|
83
87
|
return {
|
|
84
88
|
label: target.label,
|
|
85
89
|
targetPath: target.targetPath,
|
|
@@ -87,7 +91,7 @@ export function renderMissingWorkflowFiles({ missingTargets, overview, selectedL
|
|
|
87
91
|
};
|
|
88
92
|
});
|
|
89
93
|
}
|
|
90
|
-
export function writeSibuState({ rootPath, statePath, selectedAgents, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, selectedWorkflowSkills = [], targets, }) {
|
|
94
|
+
export function writeSibuState({ rootPath, statePath, selectedAgents, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, selectedWorkflowSkills = [], selectedDatabaseSkills = [], targets, }) {
|
|
91
95
|
const previousState = readExistingState(statePath);
|
|
92
96
|
const now = new Date().toISOString();
|
|
93
97
|
const manifest = readTemplateManifest();
|
|
@@ -101,6 +105,7 @@ export function writeSibuState({ rootPath, statePath, selectedAgents, selectedLa
|
|
|
101
105
|
selectedFrameworkSkills: selectedFrameworkSkills.map((skill) => skill.id),
|
|
102
106
|
selectedArchitectureSkill: selectedArchitectureSkill?.id,
|
|
103
107
|
selectedWorkflowSkills: selectedWorkflowSkills.map((skill) => skill.id),
|
|
108
|
+
selectedDatabaseSkills: selectedDatabaseSkills.map((skill) => skill.id),
|
|
104
109
|
managedFiles: Object.fromEntries(targets
|
|
105
110
|
.filter((target) => fs.existsSync(target.targetPath))
|
|
106
111
|
.map((target) => {
|
package/package.json
CHANGED
package/templates/manifest.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"templateVersion": "
|
|
2
|
+
"templateVersion": "70",
|
|
3
3
|
"templates": {
|
|
4
4
|
"AGENTS.md": {
|
|
5
5
|
"version": "25",
|
|
@@ -80,6 +80,13 @@
|
|
|
80
80
|
"Adds an optional Go skill with concise, Effective Go\u2013style guidance for .go files, package APIs, interfaces, errors, receivers, concurrency, and tests."
|
|
81
81
|
]
|
|
82
82
|
},
|
|
83
|
+
"skills/postgresql-expert/SKILL.md": {
|
|
84
|
+
"version": "1",
|
|
85
|
+
"description": "Selectable PostgreSQL Expert skill for practical database schema design and PostgreSQL-specific tradeoff guidance.",
|
|
86
|
+
"changes": [
|
|
87
|
+
"Adds an optional PostgreSQL Expert database skill focused on simple schema design, data integrity, migrations, access patterns, and purposeful indexing."
|
|
88
|
+
]
|
|
89
|
+
},
|
|
83
90
|
"skills/architecture/ddd-hexagonal/SKILL.md": {
|
|
84
91
|
"version": "4",
|
|
85
92
|
"description": "Selectable back-end architecture skill for DDD and Hexagonal Architecture.",
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: postgresql-expert
|
|
3
|
+
description: Use this skill for PostgreSQL schema design, migrations, constraints, queries, indexing, and database tradeoffs when practical database judgment is needed.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# PostgreSQL Expert
|
|
7
|
+
|
|
8
|
+
Use this skill when working with PostgreSQL database design or database-facing application changes.
|
|
9
|
+
|
|
10
|
+
Apply this skill together with `clean-code`. Keep the guidance practical: prefer the simplest database design that protects data integrity and supports the application's real access patterns.
|
|
11
|
+
|
|
12
|
+
## Use this skill for
|
|
13
|
+
|
|
14
|
+
- designing or reviewing PostgreSQL schemas
|
|
15
|
+
- planning migrations and safe schema evolution
|
|
16
|
+
- choosing keys, constraints, relationships, and transactions
|
|
17
|
+
- reasoning about queries, access patterns, and indexes
|
|
18
|
+
- deciding whether PostgreSQL-specific features are worth using
|
|
19
|
+
- reviewing database-related tradeoffs in application code
|
|
20
|
+
|
|
21
|
+
## Core principles
|
|
22
|
+
|
|
23
|
+
### 1. Start with the product model
|
|
24
|
+
- Model the real domain concepts, not speculative future reporting or scaling needs.
|
|
25
|
+
- Prefer clear table and column names that match product language.
|
|
26
|
+
- Keep schemas boring until the product needs something more specialized.
|
|
27
|
+
- Do not add tables, abstractions, or generic metadata columns just in case.
|
|
28
|
+
|
|
29
|
+
### 2. Protect data integrity in the database
|
|
30
|
+
- Use primary keys, foreign keys, `NOT NULL`, `UNIQUE`, and `CHECK` constraints when they express real invariants.
|
|
31
|
+
- Prefer database-enforced integrity over relying only on application checks for durable rules.
|
|
32
|
+
- Make deletion behavior explicit with appropriate foreign-key actions.
|
|
33
|
+
- Use transactions when a change must succeed or fail as one unit.
|
|
34
|
+
|
|
35
|
+
### 3. Evolve schemas safely
|
|
36
|
+
- Treat migrations as production changes that need clear rollback or recovery thinking.
|
|
37
|
+
- Prefer small, reversible migration steps for risky changes.
|
|
38
|
+
- Avoid destructive changes until data has been backfilled, verified, and callers have moved over.
|
|
39
|
+
- Consider lock behavior and table size before changing large production tables.
|
|
40
|
+
|
|
41
|
+
### 4. Design from access patterns, not guesses
|
|
42
|
+
- Understand the queries the application actually needs before optimizing.
|
|
43
|
+
- Keep common reads and writes straightforward before introducing denormalization.
|
|
44
|
+
- Use normalization for clarity and integrity; denormalize only for a measured or well-understood reason.
|
|
45
|
+
- Explain tradeoffs when choosing between simpler schema shape and query convenience.
|
|
46
|
+
|
|
47
|
+
### 5. Add indexes only when justified
|
|
48
|
+
- Do not add indexes just because a column is used, foreign-keyed, or might be queried someday.
|
|
49
|
+
- Add an index when there is a clear query, filtering, joining, sorting, uniqueness, or constraint need.
|
|
50
|
+
- Remember that indexes speed some reads but add write cost, storage cost, and maintenance overhead.
|
|
51
|
+
- Prefer a small number of purposeful indexes over broad defensive indexing.
|
|
52
|
+
|
|
53
|
+
### 6. Use PostgreSQL features with restraint
|
|
54
|
+
- Consider PostgreSQL capabilities such as JSONB, arrays, partial indexes, generated columns, enums, extensions, and full-text search when they simplify a real requirement.
|
|
55
|
+
- Do not use PostgreSQL-specific features to appear sophisticated when standard relational modeling is clearer.
|
|
56
|
+
- Be explicit about portability tradeoffs when choosing PostgreSQL-specific behavior.
|
|
57
|
+
- Prefer built-in PostgreSQL capabilities before adding extra infrastructure.
|
|
58
|
+
|
|
59
|
+
### 7. Keep database advice actionable
|
|
60
|
+
- Tie recommendations to the schema, query, migration, or invariant at hand.
|
|
61
|
+
- Say what evidence would change the recommendation, such as query plans, row counts, write volume, or latency targets.
|
|
62
|
+
- When context is missing, ask for the relevant schema, migration, query, or access pattern instead of guessing.
|
|
63
|
+
- Avoid broad DBA checklists unless the task is explicitly about operations or performance investigation.
|
|
64
|
+
|
|
65
|
+
## Decision rule
|
|
66
|
+
|
|
67
|
+
When unsure, prefer:
|
|
68
|
+
1. simple schema design that matches current product needs
|
|
69
|
+
2. explicit database constraints for real invariants
|
|
70
|
+
3. safe, incremental migrations
|
|
71
|
+
4. query-aware design over speculative optimization
|
|
72
|
+
5. purposeful indexes over blanket indexing
|