@juancr11/sibu 0.13.1 → 0.14.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 +8 -3
- package/bin/modules/mcp-server-selection-management/use-mcp-server/handler.js +57 -9
- package/bin/modules/project-adoption/handler.js +13 -2
- package/bin/modules/skill-selection-management/use-skill/handler.js +82 -30
- package/bin/modules/sync-review/apply-action.js +11 -0
- package/bin/modules/sync-review/sync-preview.js +60 -29
- package/bin/modules/workflow-target-planning/catalog.js +62 -0
- package/bin/modules/workflow-target-planning/index.js +1 -1
- package/package.json +1 -1
- package/templates/AGENTS.md +1 -0
- package/templates/manifest.json +34 -13
- package/templates/skills/export-to-github/SKILL.md +39 -0
- package/templates/skills/export-to-notion/SKILL.md +60 -0
- package/templates/skills/feature-brief-writer/SKILL.md +11 -25
- package/templates/skills/feature-idea-capture/SKILL.md +32 -0
- package/templates/skills/react/SKILL.md +57 -9
- package/templates/skills/scrum-master-planner/SKILL.md +0 -32
- package/templates/skills/technical-design-writer/SKILL.md +0 -25
- package/templates/skills/ux-expert/SKILL.md +0 -25
- package/bin/shared/templates.js +0 -60
- package/bin/shared/workflow-targets.js +0 -125
|
@@ -1 +1 @@
|
|
|
1
|
-
export { askForArchitectureSkill, askForDatabaseSkills, askForFrameworkSkills, askForLanguageSkills, askForMcpServers, askForNotionDocsParentPage, askForMissingFrameworkSkills, askForNewArchitectureSkill, askForNewLanguageSkills, askForProjectOverview, askForSupportedAgents, askForWorkflowSkills, MCP_SERVER_SELECTION_MESSAGE, renderIntro, shouldAskForNewLanguageSkills, } from './prompts.js';
|
|
1
|
+
export { askForArchitectureSkill, askForDatabaseSkills, askForFrameworkSkills, askForLanguageSkills, askForMcpServers, askForNotionDocsParentPage, askForMissingFrameworkSkills, askForNewArchitectureSkill, askForNewLanguageSkills, askForProjectOverview, askForSupportedAgents, askForWorkflowSkills, MCP_SERVER_SELECTION_MESSAGE, getPromptableWorkflowSkills, renderIntro, shouldAskForNewLanguageSkills, } from './prompts.js';
|
|
@@ -148,11 +148,16 @@ export async function askForArchitectureSkill() {
|
|
|
148
148
|
}
|
|
149
149
|
return SELECTABLE_ARCHITECTURE_SKILLS.find((skill) => skill.id === selectedArchitectureSkillId);
|
|
150
150
|
}
|
|
151
|
-
export
|
|
151
|
+
export function getPromptableWorkflowSkills(excludedWorkflowSkillIds = []) {
|
|
152
|
+
const excludedIds = new Set(excludedWorkflowSkillIds);
|
|
153
|
+
return SELECTABLE_WORKFLOW_SKILLS.filter((skill) => !excludedIds.has(skill.id));
|
|
154
|
+
}
|
|
155
|
+
export async function askForWorkflowSkills(excludedWorkflowSkillIds = []) {
|
|
156
|
+
const promptableWorkflowSkills = getPromptableWorkflowSkills(excludedWorkflowSkillIds);
|
|
152
157
|
const selectedWorkflowSkillIds = await multiselect({
|
|
153
158
|
message: 'Select optional workflow skills for this project.',
|
|
154
159
|
required: false,
|
|
155
|
-
options:
|
|
160
|
+
options: promptableWorkflowSkills.map((skill) => ({
|
|
156
161
|
value: skill.id,
|
|
157
162
|
label: skill.name,
|
|
158
163
|
hint: skill.description,
|
|
@@ -162,7 +167,7 @@ export async function askForWorkflowSkills() {
|
|
|
162
167
|
cancel('Initialization cancelled.');
|
|
163
168
|
process.exit(0);
|
|
164
169
|
}
|
|
165
|
-
return
|
|
170
|
+
return promptableWorkflowSkills.filter((skill) => selectedWorkflowSkillIds.includes(skill.id));
|
|
166
171
|
}
|
|
167
172
|
export function shouldAskForNewLanguageSkills(state) {
|
|
168
173
|
return (state.selectedLanguageSkills?.length ?? 0) === 0;
|
|
@@ -2,10 +2,12 @@ import fs from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { log } from '@clack/prompts';
|
|
4
4
|
import { STATE_RELATIVE_PATH } from '../../../shared/catalog.js';
|
|
5
|
+
import { sha256 } from '../../../shared/hash.js';
|
|
5
6
|
import { getProjectContext } from '../../../shared/paths.js';
|
|
6
7
|
import { askForNotionDocsParentPage } from '../../interactive-guidance/index.js';
|
|
8
|
+
import { renderTemplateForSync } from '../../template-catalog-rendering/index.js';
|
|
7
9
|
import { getWorkflowMutationReadiness } from '../../workflow-mutation-readiness/index.js';
|
|
8
|
-
import { getSelectedAgentsFromState, getSelectedArchitectureSkillFromState, getSelectedDatabaseSkillsFromState, getSelectedFrameworkSkillsFromState, getSelectedLanguageSkillsFromState, getSelectedMcpServersFromState, getSelectedWorkflowSkillsFromState, getWorkflowTargets, renderMissingWorkflowFiles, resolveSelectableMcpServerById, writeSibuState, } from '../../workflow-target-planning/index.js';
|
|
10
|
+
import { getSelectedAgentsFromState, getSelectedArchitectureSkillFromState, getSelectedDatabaseSkillsFromState, getSelectedFrameworkSkillsFromState, getSelectedLanguageSkillsFromState, getSelectedMcpServersFromState, getSelectedWorkflowSkillsFromState, getWorkflowSkillsImpliedByMcpServers, getWorkflowTargets, renderMissingWorkflowFiles, resolveSelectableMcpServerById, writeSibuState, } from '../../workflow-target-planning/index.js';
|
|
9
11
|
const defaultDependencies = {
|
|
10
12
|
askForNotionDocsParentPage,
|
|
11
13
|
};
|
|
@@ -71,11 +73,13 @@ function applySelectedMcpServer({ rootPath, statePath, state, selectionResult, m
|
|
|
71
73
|
const selectedLanguageSkills = getSelectedLanguageSkillsFromState(state);
|
|
72
74
|
const selectedFrameworkSkills = getSelectedFrameworkSkillsFromState(state);
|
|
73
75
|
const selectedArchitectureSkill = getSelectedArchitectureSkillFromState(state);
|
|
74
|
-
const
|
|
76
|
+
const currentWorkflowSkills = getSelectedWorkflowSkillsFromState(state);
|
|
77
|
+
const impliedWorkflowSkills = getWorkflowSkillsImpliedByMcpServers(selectionResult.selectedMcpServers.map((server) => server.id));
|
|
78
|
+
const selectedWorkflowSkills = mergeWorkflowSkills(currentWorkflowSkills, impliedWorkflowSkills);
|
|
75
79
|
const selectedDatabaseSkills = getSelectedDatabaseSkillsFromState(state);
|
|
76
|
-
const previousTargets = getWorkflowTargets(rootPath, selectedAgents, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill,
|
|
80
|
+
const previousTargets = getWorkflowTargets(rootPath, selectedAgents, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, currentWorkflowSkills, selectedDatabaseSkills, getSelectedMcpServersFromState(state));
|
|
77
81
|
const targets = getWorkflowTargets(rootPath, selectedAgents, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, selectedWorkflowSkills, selectedDatabaseSkills, selectionResult.selectedMcpServers);
|
|
78
|
-
const affectedTargets =
|
|
82
|
+
const affectedTargets = getAffectedTargets(previousTargets, targets);
|
|
79
83
|
const preflightError = getMcpUsePreflightError({ rootPath, state, affectedTargets, previousTargets });
|
|
80
84
|
if (preflightError) {
|
|
81
85
|
log.error(preflightError);
|
|
@@ -83,8 +87,13 @@ function applySelectedMcpServer({ rootPath, statePath, state, selectionResult, m
|
|
|
83
87
|
process.exitCode = 1;
|
|
84
88
|
return;
|
|
85
89
|
}
|
|
90
|
+
const agentsTarget = targets.find((target) => target.label === 'AGENTS.md');
|
|
91
|
+
if (!agentsTarget) {
|
|
92
|
+
throw new Error('AGENTS.md target is missing from workflow targets.');
|
|
93
|
+
}
|
|
94
|
+
const nonAgentTargets = affectedTargets.filter((target) => target.label !== 'AGENTS.md');
|
|
86
95
|
const files = renderMissingWorkflowFiles({
|
|
87
|
-
missingTargets:
|
|
96
|
+
missingTargets: nonAgentTargets,
|
|
88
97
|
selectedLanguageSkills,
|
|
89
98
|
selectedFrameworkSkills,
|
|
90
99
|
selectedArchitectureSkill,
|
|
@@ -95,9 +104,23 @@ function applySelectedMcpServer({ rootPath, statePath, state, selectionResult, m
|
|
|
95
104
|
for (const file of files) {
|
|
96
105
|
const fileAlreadyExists = fs.existsSync(file.targetPath);
|
|
97
106
|
fs.mkdirSync(path.dirname(file.targetPath), { recursive: true });
|
|
98
|
-
fs.writeFileSync(file.targetPath, file.contents, 'utf8');
|
|
107
|
+
fs.writeFileSync(file.targetPath, file.contents, fileAlreadyExists ? 'utf8' : { encoding: 'utf8', flag: 'wx' });
|
|
99
108
|
log.success(`${fileAlreadyExists ? 'Updated' : 'Created'} ${file.label}`);
|
|
100
109
|
}
|
|
110
|
+
if (affectedTargets.some((target) => target.label === 'AGENTS.md')) {
|
|
111
|
+
const agentsContents = renderTemplateForSync({
|
|
112
|
+
templateRelativePath: agentsTarget.templateRelativePath,
|
|
113
|
+
currentPath: agentsTarget.targetPath,
|
|
114
|
+
selectedLanguageSkills,
|
|
115
|
+
selectedFrameworkSkills,
|
|
116
|
+
selectedArchitectureSkill,
|
|
117
|
+
selectedWorkflowSkills,
|
|
118
|
+
selectedDatabaseSkills,
|
|
119
|
+
selectedMcpServers: selectionResult.selectedMcpServers,
|
|
120
|
+
});
|
|
121
|
+
fs.writeFileSync(agentsTarget.targetPath, agentsContents, 'utf8');
|
|
122
|
+
log.success('Updated AGENTS.md skill routing');
|
|
123
|
+
}
|
|
101
124
|
writeSibuState({
|
|
102
125
|
rootPath,
|
|
103
126
|
statePath,
|
|
@@ -113,20 +136,45 @@ function applySelectedMcpServer({ rootPath, statePath, state, selectionResult, m
|
|
|
113
136
|
});
|
|
114
137
|
log.success(`Updated ${STATE_RELATIVE_PATH}`);
|
|
115
138
|
log.success(`Added ${selectionResult.serverName}.`);
|
|
139
|
+
for (const skill of impliedWorkflowSkills.filter((skill) => !currentWorkflowSkills.some((currentSkill) => currentSkill.id === skill.id))) {
|
|
140
|
+
log.success(`Added ${skill.name}.`);
|
|
141
|
+
}
|
|
116
142
|
log.info('Sibu configured MCP files only; prerequisites, credentials, and provider authentication remain user-owned.');
|
|
117
143
|
}
|
|
118
|
-
function
|
|
144
|
+
function getAffectedTargets(previousTargets, targets) {
|
|
119
145
|
const previousTargetPaths = new Set(previousTargets.map((target) => target.targetPath));
|
|
120
|
-
|
|
146
|
+
const newTargets = targets.filter((target) => !previousTargetPaths.has(target.targetPath) && (target.targetKind === 'skill' || target.targetKind === 'mcp-config'));
|
|
147
|
+
const mcpConfigTargets = targets.filter((target) => target.mcpConfigAgentId);
|
|
148
|
+
const agentsTarget = targets.find((target) => target.label === 'AGENTS.md');
|
|
149
|
+
const affectedTargets = new Map();
|
|
150
|
+
for (const target of [...mcpConfigTargets, ...newTargets, ...(newTargets.some((target) => target.targetKind === 'skill') && agentsTarget ? [agentsTarget] : [])]) {
|
|
151
|
+
affectedTargets.set(target.targetPath, target);
|
|
152
|
+
}
|
|
153
|
+
return [...affectedTargets.values()];
|
|
154
|
+
}
|
|
155
|
+
function mergeWorkflowSkills(...skillGroups) {
|
|
156
|
+
const skillsById = new Map();
|
|
157
|
+
for (const skill of skillGroups.flat()) {
|
|
158
|
+
skillsById.set(skill.id, skill);
|
|
159
|
+
}
|
|
160
|
+
return [...skillsById.values()];
|
|
121
161
|
}
|
|
122
162
|
function getMcpUsePreflightError({ rootPath, state, affectedTargets, previousTargets, }) {
|
|
123
163
|
const previousTargetPaths = new Set(previousTargets.map((target) => target.targetPath));
|
|
124
164
|
for (const target of affectedTargets) {
|
|
125
165
|
const relativePath = path.relative(rootPath, target.targetPath);
|
|
126
166
|
if (previousTargetPaths.has(target.targetPath)) {
|
|
127
|
-
|
|
167
|
+
const managedFile = state.managedFiles[relativePath];
|
|
168
|
+
if (!managedFile) {
|
|
128
169
|
return `${relativePath} is not recorded in ${STATE_RELATIVE_PATH}.`;
|
|
129
170
|
}
|
|
171
|
+
if (!fs.existsSync(target.targetPath)) {
|
|
172
|
+
return `${relativePath} is missing.`;
|
|
173
|
+
}
|
|
174
|
+
const currentHash = sha256(fs.readFileSync(target.targetPath, 'utf8'));
|
|
175
|
+
if (currentHash !== managedFile.sha256) {
|
|
176
|
+
return `${relativePath} has changed since Sibu last recorded it.`;
|
|
177
|
+
}
|
|
130
178
|
continue;
|
|
131
179
|
}
|
|
132
180
|
if (fs.existsSync(target.targetPath)) {
|
|
@@ -6,7 +6,14 @@ import { STATE_RELATIVE_PATH } from '../../shared/catalog.js';
|
|
|
6
6
|
import { getProjectContext } from '../../shared/paths.js';
|
|
7
7
|
import { askForArchitectureSkill, askForDatabaseSkills, askForFrameworkSkills, askForLanguageSkills, askForMcpServers, askForNotionDocsParentPage, askForProjectOverview, askForSupportedAgents, askForWorkflowSkills, renderIntro, } from '../interactive-guidance/index.js';
|
|
8
8
|
import { readStateForDoctor } from '../workflow-state-registry/index.js';
|
|
9
|
-
import { getWorkflowTargets, renderMissingWorkflowFiles, writeSibuState } from '../workflow-target-planning/index.js';
|
|
9
|
+
import { getWorkflowSkillsImpliedByMcpServers, getWorkflowTargets, renderMissingWorkflowFiles, writeSibuState } from '../workflow-target-planning/index.js';
|
|
10
|
+
function mergeWorkflowSkills(...skillGroups) {
|
|
11
|
+
const skillsById = new Map();
|
|
12
|
+
for (const skill of skillGroups.flat()) {
|
|
13
|
+
skillsById.set(skill.id, skill);
|
|
14
|
+
}
|
|
15
|
+
return [...skillsById.values()];
|
|
16
|
+
}
|
|
10
17
|
const defaultDependencies = {
|
|
11
18
|
renderIntro,
|
|
12
19
|
askForSupportedAgents,
|
|
@@ -41,12 +48,16 @@ export async function handleInitProject(_command, dependencies = defaultDependen
|
|
|
41
48
|
}
|
|
42
49
|
const selectedAgents = await dependencies.askForSupportedAgents();
|
|
43
50
|
const selectedMcpServers = await dependencies.askForMcpServers();
|
|
51
|
+
const impliedWorkflowSkills = getWorkflowSkillsImpliedByMcpServers(selectedMcpServers.map((server) => server.id));
|
|
52
|
+
for (const skill of impliedWorkflowSkills) {
|
|
53
|
+
log.info(`${skill.name} will be included because its matching MCP server was selected.`);
|
|
54
|
+
}
|
|
44
55
|
const notionDocsParentPage = selectedMcpServers.some((server) => server.id === 'notion') ? await dependencies.askForNotionDocsParentPage() : undefined;
|
|
45
56
|
const selectedLanguageSkills = await dependencies.askForLanguageSkills();
|
|
46
57
|
const selectedFrameworkSkills = await dependencies.askForFrameworkSkills();
|
|
47
58
|
const selectedDatabaseSkills = await dependencies.askForDatabaseSkills();
|
|
48
59
|
const selectedArchitectureSkill = await dependencies.askForArchitectureSkill();
|
|
49
|
-
const selectedWorkflowSkills = await dependencies.askForWorkflowSkills();
|
|
60
|
+
const selectedWorkflowSkills = mergeWorkflowSkills(impliedWorkflowSkills, await dependencies.askForWorkflowSkills(impliedWorkflowSkills.map((skill) => skill.id)));
|
|
50
61
|
const targets = getWorkflowTargets(rootPath, selectedAgents, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, selectedWorkflowSkills, selectedDatabaseSkills, selectedMcpServers);
|
|
51
62
|
const missingTargets = targets.filter((target) => !fs.existsSync(target.targetPath));
|
|
52
63
|
log.message('I will create this project’s initial AI workflow files.');
|
|
@@ -2,13 +2,17 @@ import fs from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { log } from '@clack/prompts';
|
|
4
4
|
import { STATE_RELATIVE_PATH } from '../../../shared/catalog.js';
|
|
5
|
-
import {
|
|
5
|
+
import { askForNotionDocsParentPage } from '../../interactive-guidance/index.js';
|
|
6
|
+
import { getMcpServersRequiredByWorkflowSkills, resolveSelectableSkillById } from '../../workflow-target-planning/index.js';
|
|
6
7
|
import { sha256 } from '../../../shared/hash.js';
|
|
7
8
|
import { getProjectContext } from '../../../shared/paths.js';
|
|
8
9
|
import { renderTemplateForSync } from '../../template-catalog-rendering/index.js';
|
|
9
10
|
import { getWorkflowMutationReadiness } from '../../workflow-mutation-readiness/index.js';
|
|
10
|
-
import { getSelectedAgentsFromState, getWorkflowTargets, renderMissingWorkflowFiles, writeSibuState } from '../../workflow-target-planning/index.js';
|
|
11
|
-
|
|
11
|
+
import { getSelectedAgentsFromState, getSelectedMcpServersFromState, getWorkflowTargets, renderMissingWorkflowFiles, writeSibuState } from '../../workflow-target-planning/index.js';
|
|
12
|
+
const defaultDependencies = {
|
|
13
|
+
askForNotionDocsParentPage,
|
|
14
|
+
};
|
|
15
|
+
export async function handleUseSkill(command, dependencies = defaultDependencies) {
|
|
12
16
|
const { rootPath, statePath } = getProjectContext();
|
|
13
17
|
const readiness = getWorkflowMutationReadiness({ rootPath, statePath });
|
|
14
18
|
if (!readiness.ok) {
|
|
@@ -34,7 +38,7 @@ export async function handleUseSkill(command) {
|
|
|
34
38
|
process.exitCode = 1;
|
|
35
39
|
return;
|
|
36
40
|
case 'selected':
|
|
37
|
-
applySelectedSkill({ rootPath, statePath, state: readiness.state, selectionResult });
|
|
41
|
+
await applySelectedSkill({ rootPath, statePath, state: readiness.state, selectionResult, dependencies });
|
|
38
42
|
return;
|
|
39
43
|
}
|
|
40
44
|
}
|
|
@@ -132,7 +136,7 @@ export function getNextSkillSelection(state, skillName) {
|
|
|
132
136
|
};
|
|
133
137
|
}
|
|
134
138
|
}
|
|
135
|
-
function applySelectedSkill({ rootPath, statePath, state, selectionResult, }) {
|
|
139
|
+
async function applySelectedSkill({ rootPath, statePath, state, selectionResult, dependencies, }) {
|
|
136
140
|
const plan = buildSkillApplicationPlan({ rootPath, state, selectionResult });
|
|
137
141
|
const preflightError = getSkillApplicationPreflightError({ rootPath, state, plan });
|
|
138
142
|
if (preflightError) {
|
|
@@ -141,25 +145,37 @@ function applySelectedSkill({ rootPath, statePath, state, selectionResult, }) {
|
|
|
141
145
|
process.exitCode = 1;
|
|
142
146
|
return;
|
|
143
147
|
}
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
148
|
+
const mcpServerConfigs = await getNextMcpServerConfigs({ state, selectedMcpServers: plan.selectedMcpServers, dependencies });
|
|
149
|
+
const dependencyMcpServers = plan.selectedMcpServers.filter((server) => !(state.selectedMcpServers ?? []).includes(server.id));
|
|
150
|
+
for (const server of dependencyMcpServers) {
|
|
151
|
+
log.info(`${selectionResult.skillName} requires ${server.name}. I will add them together.`);
|
|
152
|
+
}
|
|
153
|
+
const nonAgentTargets = plan.affectedTargets.filter((target) => target.label !== 'AGENTS.md');
|
|
154
|
+
const files = renderMissingWorkflowFiles({
|
|
155
|
+
missingTargets: nonAgentTargets,
|
|
147
156
|
selectedLanguageSkills: selectionResult.selection.selectedLanguageSkills,
|
|
148
157
|
selectedFrameworkSkills: selectionResult.selection.selectedFrameworkSkills,
|
|
149
158
|
selectedArchitectureSkill: selectionResult.selection.selectedArchitectureSkill,
|
|
150
159
|
selectedWorkflowSkills: selectionResult.selection.selectedWorkflowSkills,
|
|
151
160
|
selectedDatabaseSkills: selectionResult.selection.selectedDatabaseSkills,
|
|
161
|
+
selectedMcpServers: plan.selectedMcpServers,
|
|
152
162
|
});
|
|
153
|
-
const
|
|
154
|
-
|
|
163
|
+
for (const file of files) {
|
|
164
|
+
const fileAlreadyExists = fs.existsSync(file.targetPath);
|
|
165
|
+
fs.mkdirSync(path.dirname(file.targetPath), { recursive: true });
|
|
166
|
+
fs.writeFileSync(file.targetPath, file.contents, fileAlreadyExists ? 'utf8' : { encoding: 'utf8', flag: 'wx' });
|
|
167
|
+
log.success(`${fileAlreadyExists ? 'Updated' : 'Created'} ${file.label}`);
|
|
168
|
+
}
|
|
169
|
+
const agentsContents = renderTemplateForSync({
|
|
170
|
+
templateRelativePath: plan.agentsTarget.templateRelativePath,
|
|
171
|
+
currentPath: plan.agentsTarget.targetPath,
|
|
155
172
|
selectedLanguageSkills: selectionResult.selection.selectedLanguageSkills,
|
|
156
173
|
selectedFrameworkSkills: selectionResult.selection.selectedFrameworkSkills,
|
|
157
174
|
selectedArchitectureSkill: selectionResult.selection.selectedArchitectureSkill,
|
|
175
|
+
selectedWorkflowSkills: selectionResult.selection.selectedWorkflowSkills,
|
|
158
176
|
selectedDatabaseSkills: selectionResult.selection.selectedDatabaseSkills,
|
|
177
|
+
selectedMcpServers: plan.selectedMcpServers,
|
|
159
178
|
});
|
|
160
|
-
fs.mkdirSync(path.dirname(newSkillFile.targetPath), { recursive: true });
|
|
161
|
-
fs.writeFileSync(newSkillFile.targetPath, newSkillFile.contents, { encoding: 'utf8', flag: 'wx' });
|
|
162
|
-
log.success(`Created ${newSkillFile.label}`);
|
|
163
179
|
fs.writeFileSync(plan.agentsTarget.targetPath, agentsContents, 'utf8');
|
|
164
180
|
log.success('Updated AGENTS.md skill routing');
|
|
165
181
|
writeSibuState({
|
|
@@ -171,43 +187,79 @@ function applySelectedSkill({ rootPath, statePath, state, selectionResult, }) {
|
|
|
171
187
|
selectedArchitectureSkill: selectionResult.selection.selectedArchitectureSkill,
|
|
172
188
|
selectedWorkflowSkills: selectionResult.selection.selectedWorkflowSkills,
|
|
173
189
|
selectedDatabaseSkills: selectionResult.selection.selectedDatabaseSkills,
|
|
190
|
+
selectedMcpServers: plan.selectedMcpServers,
|
|
191
|
+
mcpServerConfigs,
|
|
174
192
|
targets: plan.targets,
|
|
175
193
|
});
|
|
176
194
|
log.success(`Updated ${STATE_RELATIVE_PATH}`);
|
|
177
195
|
log.success(`Added ${selectionResult.skillName}.`);
|
|
196
|
+
for (const server of dependencyMcpServers) {
|
|
197
|
+
log.success(`Added ${server.name}.`);
|
|
198
|
+
}
|
|
178
199
|
}
|
|
179
200
|
function buildSkillApplicationPlan({ rootPath, state, selectionResult, }) {
|
|
180
201
|
const selectedAgents = getSelectedAgentsFromState(state);
|
|
181
|
-
const
|
|
182
|
-
const
|
|
202
|
+
const currentMcpServers = getSelectedMcpServersFromState(state);
|
|
203
|
+
const requiredMcpServers = getMcpServersRequiredByWorkflowSkills(selectionResult.selection.selectedWorkflowSkills.map((skill) => skill.id));
|
|
204
|
+
const selectedMcpServers = mergeMcpServers(currentMcpServers, requiredMcpServers);
|
|
205
|
+
const previousTargets = getWorkflowTargets(rootPath, selectedAgents, getSelectedLanguageSkillsFromState(state), getSelectedFrameworkSkillsFromState(state), getArchitectureSkillById(state.selectedArchitectureSkill), getSelectedWorkflowSkillsFromState(state), getSelectedDatabaseSkillsFromState(state), currentMcpServers);
|
|
206
|
+
const targets = getWorkflowTargets(rootPath, selectedAgents, selectionResult.selection.selectedLanguageSkills, selectionResult.selection.selectedFrameworkSkills, selectionResult.selection.selectedArchitectureSkill, selectionResult.selection.selectedWorkflowSkills, selectionResult.selection.selectedDatabaseSkills, selectedMcpServers);
|
|
183
207
|
const previousTargetPaths = new Set(previousTargets.map((target) => target.targetPath));
|
|
184
|
-
const
|
|
208
|
+
const newTargets = targets.filter((target) => !previousTargetPaths.has(target.targetPath) && (target.targetKind === 'skill' || target.targetKind === 'mcp-config'));
|
|
209
|
+
const mcpConfigTargets = targets.filter((target) => target.mcpConfigAgentId && selectedMcpServers.length > currentMcpServers.length);
|
|
185
210
|
const agentsTarget = targets.find((target) => target.label === 'AGENTS.md');
|
|
186
|
-
if (
|
|
211
|
+
if (newTargets.length === 0) {
|
|
187
212
|
throw new Error(`No new workflow target found for ${selectionResult.skillName}.`);
|
|
188
213
|
}
|
|
189
214
|
if (!agentsTarget) {
|
|
190
215
|
throw new Error('AGENTS.md target is missing from workflow targets.');
|
|
191
216
|
}
|
|
192
|
-
|
|
217
|
+
const affectedTargets = dedupeTargets([...newTargets, ...mcpConfigTargets, agentsTarget]);
|
|
218
|
+
return { agentsTarget, newTargets, affectedTargets, targets, selectedAgents, selectedMcpServers };
|
|
193
219
|
}
|
|
194
220
|
function getSkillApplicationPreflightError({ rootPath, state, plan }) {
|
|
195
|
-
|
|
196
|
-
|
|
221
|
+
const existingRecordedTargets = plan.affectedTargets.filter((target) => state.managedFiles[path.relative(rootPath, target.targetPath)]);
|
|
222
|
+
const newTargets = plan.affectedTargets.filter((target) => !state.managedFiles[path.relative(rootPath, target.targetPath)]);
|
|
223
|
+
for (const target of newTargets) {
|
|
224
|
+
if (fs.existsSync(target.targetPath)) {
|
|
225
|
+
return `${path.relative(rootPath, target.targetPath)} already exists but is not recorded for this selection.`;
|
|
226
|
+
}
|
|
197
227
|
}
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
228
|
+
for (const target of existingRecordedTargets) {
|
|
229
|
+
const relativePath = path.relative(rootPath, target.targetPath);
|
|
230
|
+
const managedFile = state.managedFiles[relativePath];
|
|
231
|
+
if (!managedFile) {
|
|
232
|
+
return `${relativePath} is not recorded in ${STATE_RELATIVE_PATH}.`;
|
|
233
|
+
}
|
|
234
|
+
if (!fs.existsSync(target.targetPath)) {
|
|
235
|
+
return `${relativePath} is missing.`;
|
|
236
|
+
}
|
|
237
|
+
const currentHash = sha256(fs.readFileSync(target.targetPath, 'utf8'));
|
|
238
|
+
if (currentHash !== managedFile.sha256) {
|
|
239
|
+
return `${relativePath} has changed since Sibu last recorded it.`;
|
|
240
|
+
}
|
|
202
241
|
}
|
|
203
|
-
|
|
204
|
-
|
|
242
|
+
return undefined;
|
|
243
|
+
}
|
|
244
|
+
function dedupeTargets(targets) {
|
|
245
|
+
return [...new Map(targets.map((target) => [target.targetPath, target])).values()];
|
|
246
|
+
}
|
|
247
|
+
function mergeMcpServers(...serverGroups) {
|
|
248
|
+
const serversById = new Map();
|
|
249
|
+
for (const server of serverGroups.flat()) {
|
|
250
|
+
serversById.set(server.id, server);
|
|
205
251
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
252
|
+
return [...serversById.values()];
|
|
253
|
+
}
|
|
254
|
+
async function getNextMcpServerConfigs({ state, selectedMcpServers, dependencies, }) {
|
|
255
|
+
if (!selectedMcpServers.some((server) => server.id === 'notion') || state.selectedMcpServers?.includes('notion')) {
|
|
256
|
+
return state.mcpServerConfigs;
|
|
209
257
|
}
|
|
210
|
-
|
|
258
|
+
const docsParentPage = await dependencies.askForNotionDocsParentPage();
|
|
259
|
+
return {
|
|
260
|
+
...state.mcpServerConfigs,
|
|
261
|
+
notion: { docsParentPage },
|
|
262
|
+
};
|
|
211
263
|
}
|
|
212
264
|
function getSelectedLanguageSkillsFromState(state) {
|
|
213
265
|
return (state.selectedLanguageSkills ?? []).map(getLanguageSkillById);
|
|
@@ -13,6 +13,7 @@ export function applySyncAction({ rootPath, state, manifest, preview, action, })
|
|
|
13
13
|
const currentTemplateVersion = preview.currentTemplateVersion ?? getTemplateVersion(manifest, managedFile.template);
|
|
14
14
|
switch (action) {
|
|
15
15
|
case 'apply-update': {
|
|
16
|
+
recordImpliedWorkflowSkill(nextState, preview);
|
|
16
17
|
const contents = renderTemplateForSync({
|
|
17
18
|
templateRelativePath: managedFile.template,
|
|
18
19
|
currentPath: targetPath,
|
|
@@ -37,6 +38,7 @@ export function applySyncAction({ rootPath, state, manifest, preview, action, })
|
|
|
37
38
|
return { state: nextState, changedFiles: true, changedState: true };
|
|
38
39
|
}
|
|
39
40
|
case 'mark-reviewed': {
|
|
41
|
+
recordImpliedWorkflowSkill(nextState, preview);
|
|
40
42
|
const contents = fs.readFileSync(targetPath, 'utf8');
|
|
41
43
|
nextState.templateVersion = manifest.templateVersion;
|
|
42
44
|
nextState.updatedAt = new Date().toISOString();
|
|
@@ -85,3 +87,12 @@ export function applySyncAction({ rootPath, state, manifest, preview, action, })
|
|
|
85
87
|
return { state, changedFiles: false, changedState: false };
|
|
86
88
|
}
|
|
87
89
|
}
|
|
90
|
+
function recordImpliedWorkflowSkill(state, preview) {
|
|
91
|
+
if (!preview.impliedWorkflowSkillId) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (state.selectedWorkflowSkills?.includes(preview.impliedWorkflowSkillId)) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
state.selectedWorkflowSkills = [...(state.selectedWorkflowSkills ?? []), preview.impliedWorkflowSkillId];
|
|
98
|
+
}
|
|
@@ -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, getSelectedDatabaseSkillsFromState, getSelectedFrameworkSkillsFromState, getSelectedLanguageSkillsFromState, getSelectedMcpServersFromState, getSelectedWorkflowSkillsFromState, getWorkflowTargets, } from '../workflow-target-planning/index.js';
|
|
6
|
+
import { getSelectedAgentsFromState, getSelectedArchitectureSkillFromState, getSelectedDatabaseSkillsFromState, getSelectedFrameworkSkillsFromState, getSelectedLanguageSkillsFromState, getSelectedMcpServersFromState, getSelectedWorkflowSkillsFromState, getWorkflowSkillsImpliedByMcpServers, getWorkflowTargets, } from '../workflow-target-planning/index.js';
|
|
7
7
|
const ACTIONABLE_SYNC_PREVIEW_STATUSES = new Set([
|
|
8
8
|
'new-template',
|
|
9
9
|
'missing',
|
|
@@ -32,7 +32,19 @@ export function getSyncPreviews({ rootPath, state, manifest }) {
|
|
|
32
32
|
const selectedWorkflowSkills = getSelectedWorkflowSkillsFromState(state);
|
|
33
33
|
const selectedDatabaseSkills = getSelectedDatabaseSkillsFromState(state);
|
|
34
34
|
const selectedMcpServers = getSelectedMcpServersFromState(state);
|
|
35
|
-
const
|
|
35
|
+
const impliedWorkflowSkills = getWorkflowSkillsImpliedByMcpServers(selectedMcpServers.map((server) => server.id));
|
|
36
|
+
const plannedWorkflowSkills = mergeWorkflowSkills(selectedWorkflowSkills, impliedWorkflowSkills);
|
|
37
|
+
const missingImpliedWorkflowSkills = impliedWorkflowSkills.filter((skill) => !selectedWorkflowSkills.some((selectedSkill) => selectedSkill.id === skill.id));
|
|
38
|
+
const previews = [];
|
|
39
|
+
const expectedTargets = getWorkflowTargets(rootPath, getSelectedAgentsFromState(state), selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, plannedWorkflowSkills, selectedDatabaseSkills, selectedMcpServers);
|
|
40
|
+
const alreadyPreviewedPaths = new Set();
|
|
41
|
+
for (const skill of missingImpliedWorkflowSkills) {
|
|
42
|
+
for (const preview of getNewExpectedTargetPreviews({ rootPath, manifest, state, expectedTargets, workflowSkill: skill })) {
|
|
43
|
+
previews.push(preview);
|
|
44
|
+
alreadyPreviewedPaths.add(preview.relativePath);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
previews.push(...Object.entries(state.managedFiles).map(([relativePath, managedFile]) => getManagedFileSyncPreview({
|
|
36
48
|
rootPath,
|
|
37
49
|
manifest,
|
|
38
50
|
relativePath,
|
|
@@ -40,46 +52,65 @@ export function getSyncPreviews({ rootPath, state, manifest }) {
|
|
|
40
52
|
selectedLanguageSkills,
|
|
41
53
|
selectedFrameworkSkills,
|
|
42
54
|
selectedArchitectureSkill,
|
|
43
|
-
selectedWorkflowSkills,
|
|
55
|
+
selectedWorkflowSkills: plannedWorkflowSkills,
|
|
44
56
|
selectedDatabaseSkills,
|
|
45
57
|
selectedMcpServers,
|
|
46
|
-
}));
|
|
47
|
-
const expectedTargets = getWorkflowTargets(rootPath, getSelectedAgentsFromState(state), selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, selectedWorkflowSkills, selectedDatabaseSkills, selectedMcpServers);
|
|
58
|
+
})));
|
|
48
59
|
for (const target of expectedTargets) {
|
|
49
60
|
const relativePath = path.relative(rootPath, target.targetPath);
|
|
50
|
-
if (state.managedFiles[relativePath]) {
|
|
51
|
-
continue;
|
|
52
|
-
}
|
|
53
|
-
const template = manifest.templates[target.templateRelativePath];
|
|
54
|
-
if (!template) {
|
|
55
|
-
previews.push({
|
|
56
|
-
relativePath,
|
|
57
|
-
managedFile: {
|
|
58
|
-
template: target.templateRelativePath,
|
|
59
|
-
templateVersion: 'unknown',
|
|
60
|
-
sha256: '',
|
|
61
|
-
},
|
|
62
|
-
status: 'unknown-template',
|
|
63
|
-
changes: [],
|
|
64
|
-
hasLocalFile: fs.existsSync(target.targetPath),
|
|
65
|
-
});
|
|
61
|
+
if (state.managedFiles[relativePath] || alreadyPreviewedPaths.has(relativePath)) {
|
|
66
62
|
continue;
|
|
67
63
|
}
|
|
68
|
-
previews.push({
|
|
64
|
+
previews.push(getNewExpectedTargetPreview({ rootPath, manifest, target }));
|
|
65
|
+
}
|
|
66
|
+
return previews;
|
|
67
|
+
}
|
|
68
|
+
function getNewExpectedTargetPreviews({ rootPath, manifest, state, expectedTargets, workflowSkill, }) {
|
|
69
|
+
const workflowSkillTargetPaths = new Set(Object.values(workflowSkill.targetRelativePathsByAgent));
|
|
70
|
+
return expectedTargets
|
|
71
|
+
.filter((target) => workflowSkillTargetPaths.has(path.relative(rootPath, target.targetPath)))
|
|
72
|
+
.filter((target) => !state.managedFiles[path.relative(rootPath, target.targetPath)])
|
|
73
|
+
.map((target) => ({
|
|
74
|
+
...getNewExpectedTargetPreview({ rootPath, manifest, target }),
|
|
75
|
+
impliedWorkflowSkillId: workflowSkill.id,
|
|
76
|
+
}));
|
|
77
|
+
}
|
|
78
|
+
function getNewExpectedTargetPreview({ rootPath, manifest, target }) {
|
|
79
|
+
const relativePath = path.relative(rootPath, target.targetPath);
|
|
80
|
+
const template = manifest.templates[target.templateRelativePath];
|
|
81
|
+
if (!template) {
|
|
82
|
+
return {
|
|
69
83
|
relativePath,
|
|
70
84
|
managedFile: {
|
|
71
85
|
template: target.templateRelativePath,
|
|
72
|
-
templateVersion:
|
|
86
|
+
templateVersion: 'unknown',
|
|
73
87
|
sha256: '',
|
|
74
|
-
status: 'managed',
|
|
75
88
|
},
|
|
76
|
-
status: '
|
|
77
|
-
|
|
78
|
-
changes: template.changes,
|
|
89
|
+
status: 'unknown-template',
|
|
90
|
+
changes: [],
|
|
79
91
|
hasLocalFile: fs.existsSync(target.targetPath),
|
|
80
|
-
}
|
|
92
|
+
};
|
|
81
93
|
}
|
|
82
|
-
return
|
|
94
|
+
return {
|
|
95
|
+
relativePath,
|
|
96
|
+
managedFile: {
|
|
97
|
+
template: target.templateRelativePath,
|
|
98
|
+
templateVersion: template.version,
|
|
99
|
+
sha256: '',
|
|
100
|
+
status: 'managed',
|
|
101
|
+
},
|
|
102
|
+
status: 'new-template',
|
|
103
|
+
currentTemplateVersion: template.version,
|
|
104
|
+
changes: template.changes,
|
|
105
|
+
hasLocalFile: fs.existsSync(target.targetPath),
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
function mergeWorkflowSkills(...skillGroups) {
|
|
109
|
+
const skillsById = new Map();
|
|
110
|
+
for (const skill of skillGroups.flat()) {
|
|
111
|
+
skillsById.set(skill.id, skill);
|
|
112
|
+
}
|
|
113
|
+
return [...skillsById.values()];
|
|
83
114
|
}
|
|
84
115
|
function getManagedFileSyncPreview({ rootPath, manifest, relativePath, managedFile, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, selectedWorkflowSkills, selectedDatabaseSkills, selectedMcpServers, }) {
|
|
85
116
|
if (managedFile.status === 'unmanaged') {
|
|
@@ -71,6 +71,15 @@ export const MANDATORY_SKILLS = [
|
|
|
71
71
|
windsurf: '.agents/skills/ai-implementation-plan-executor/SKILL.md',
|
|
72
72
|
},
|
|
73
73
|
},
|
|
74
|
+
{
|
|
75
|
+
templateRelativePath: 'skills/feature-idea-capture/SKILL.md',
|
|
76
|
+
targetRelativePathsByAgent: {
|
|
77
|
+
codex: '.agents/skills/feature-idea-capture/SKILL.md',
|
|
78
|
+
gemini: '.agents/skills/feature-idea-capture/SKILL.md',
|
|
79
|
+
claude: '.agents/skills/feature-idea-capture/SKILL.md',
|
|
80
|
+
windsurf: '.agents/skills/feature-idea-capture/SKILL.md',
|
|
81
|
+
},
|
|
82
|
+
},
|
|
74
83
|
];
|
|
75
84
|
export const SELECTABLE_LANGUAGE_SKILLS = [
|
|
76
85
|
{
|
|
@@ -211,7 +220,60 @@ export const SELECTABLE_WORKFLOW_SKILLS = [
|
|
|
211
220
|
windsurf: '.agents/skills/ux-expert/SKILL.md',
|
|
212
221
|
},
|
|
213
222
|
},
|
|
223
|
+
{
|
|
224
|
+
id: 'export-to-github',
|
|
225
|
+
name: 'Export to GitHub',
|
|
226
|
+
description: "Install guidance for exporting a named feature's Epics and User Stories to GitHub using the configured GitHub MCP server",
|
|
227
|
+
routingInstruction: "For exporting a feature's Epics and User Stories to GitHub issues or sub-issues, use `export-to-github`.",
|
|
228
|
+
templateRelativePath: 'skills/export-to-github/SKILL.md',
|
|
229
|
+
targetRelativePathsByAgent: {
|
|
230
|
+
codex: '.agents/skills/export-to-github/SKILL.md',
|
|
231
|
+
gemini: '.agents/skills/export-to-github/SKILL.md',
|
|
232
|
+
claude: '.agents/skills/export-to-github/SKILL.md',
|
|
233
|
+
windsurf: '.agents/skills/export-to-github/SKILL.md',
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
id: 'export-to-notion',
|
|
238
|
+
name: 'Export to Notion',
|
|
239
|
+
description: "Install guidance for exporting a named feature's feature brief, UX design, and technical design to Notion using the configured Notion MCP server",
|
|
240
|
+
routingInstruction: "For exporting a feature's feature brief, UX design, or technical design to Notion, use `export-to-notion`.",
|
|
241
|
+
templateRelativePath: 'skills/export-to-notion/SKILL.md',
|
|
242
|
+
targetRelativePathsByAgent: {
|
|
243
|
+
codex: '.agents/skills/export-to-notion/SKILL.md',
|
|
244
|
+
gemini: '.agents/skills/export-to-notion/SKILL.md',
|
|
245
|
+
claude: '.agents/skills/export-to-notion/SKILL.md',
|
|
246
|
+
windsurf: '.agents/skills/export-to-notion/SKILL.md',
|
|
247
|
+
},
|
|
248
|
+
},
|
|
214
249
|
];
|
|
250
|
+
const EXPORT_WORKFLOW_SKILL_MCP_REQUIREMENTS = {
|
|
251
|
+
'export-to-github': 'github',
|
|
252
|
+
'export-to-notion': 'notion',
|
|
253
|
+
};
|
|
254
|
+
export function getMcpServersRequiredByWorkflowSkills(workflowSkillIds) {
|
|
255
|
+
const requiredServerIds = new Set();
|
|
256
|
+
for (const workflowSkillId of workflowSkillIds) {
|
|
257
|
+
const requiredServerId = getRequiredMcpServerIdForWorkflowSkill(workflowSkillId);
|
|
258
|
+
if (requiredServerId) {
|
|
259
|
+
requiredServerIds.add(requiredServerId);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return SELECTABLE_MCP_SERVERS.filter((server) => requiredServerIds.has(server.id));
|
|
263
|
+
}
|
|
264
|
+
export function getWorkflowSkillsImpliedByMcpServers(mcpServerIds) {
|
|
265
|
+
const selectedServerIds = new Set(mcpServerIds);
|
|
266
|
+
const impliedSkillIds = Object.entries(EXPORT_WORKFLOW_SKILL_MCP_REQUIREMENTS)
|
|
267
|
+
.filter(([, requiredServerId]) => selectedServerIds.has(requiredServerId))
|
|
268
|
+
.map(([workflowSkillId]) => workflowSkillId);
|
|
269
|
+
return SELECTABLE_WORKFLOW_SKILLS.filter((skill) => impliedSkillIds.includes(skill.id));
|
|
270
|
+
}
|
|
271
|
+
function getRequiredMcpServerIdForWorkflowSkill(workflowSkillId) {
|
|
272
|
+
if (workflowSkillId === 'export-to-github' || workflowSkillId === 'export-to-notion') {
|
|
273
|
+
return EXPORT_WORKFLOW_SKILL_MCP_REQUIREMENTS[workflowSkillId];
|
|
274
|
+
}
|
|
275
|
+
return undefined;
|
|
276
|
+
}
|
|
215
277
|
export const SELECTABLE_MCP_SERVERS = [
|
|
216
278
|
{
|
|
217
279
|
id: 'github',
|
|
@@ -1,2 +1,2 @@
|
|
|
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';
|
|
1
|
+
export { MANDATORY_SKILLS, SELECTABLE_ARCHITECTURE_SKILLS, SELECTABLE_DATABASE_SKILLS, SELECTABLE_FRAMEWORK_SKILLS, SELECTABLE_LANGUAGE_SKILLS, SELECTABLE_MCP_SERVERS, SELECTABLE_WORKFLOW_SKILLS, SUPPORTED_AGENTS, getMcpServersRequiredByWorkflowSkills, getWorkflowSkillsImpliedByMcpServers, resolveSelectableMcpServerById, resolveSelectableSkillById, } from './catalog.js';
|
|
2
2
|
export { getSelectedAgentsFromState, getSelectedArchitectureSkillFromState, getSelectedDatabaseSkillsFromState, getSelectedFrameworkSkillsFromState, getSelectedLanguageSkillsFromState, getSelectedMcpServersFromState, getSelectedMcpTargetsForAgents, getSelectedSkillTargetsForAgents, getSelectedWorkflowSkillsFromState, getWorkflowTargets, renderMissingWorkflowFiles, writeSibuState, } from './workflow-targets.js';
|
package/package.json
CHANGED
package/templates/AGENTS.md
CHANGED
|
@@ -47,6 +47,7 @@ For planned product/feature work, use this pipeline: product vision -> deep modu
|
|
|
47
47
|
- For requests to create Epics, User Stories, Scrum planning artifacts, backlog slices, or delivery plans from an approved feature brief and technical design, use `scrum-master-planner`.
|
|
48
48
|
- For explicit planning-only requests to turn a specific User Story into an implementation checklist, coding plan, step-by-step execution plan, or baby-step plan without implementation, use `ai-implementation-planner` and stop after writing the plan. Treat a request as planning-only only when the user explicitly says planning-only, "do not implement," or asks to create the plan without execution.
|
|
49
49
|
- For requests to plan, implement, execute, continue, or work through a specific User Story under `docs/features/<feature-slug>/epics/<epic-slug>/stories/<order>-<story-slug>.md`, an Epic, or an existing story implementation plan under `docs/features/<feature-slug>/epics/<epic-slug>/stories/<order>-<story-slug>.impl_plan/`, use `ai-implementation-plan-executor`; when the story plan is missing, the executor must create it with `ai-implementation-planner` and immediately continue into implementation without a separate plan approval gate.
|
|
50
|
+
- For requests to capture, save, jot down, or add rough future feature ideas, use `feature-idea-capture`.
|
|
50
51
|
{{OPTIONAL_SKILL_ROUTING}}
|
|
51
52
|
|
|
52
53
|
## Sibu maintenance
|