@juancr11/sibu 0.2.0 → 0.3.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.
@@ -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 } from '../../shared/catalog.js';
3
+ import { SELECTABLE_ARCHITECTURE_SKILLS, SELECTABLE_FRAMEWORK_SKILLS, SELECTABLE_LANGUAGE_SKILLS, SELECTABLE_WORKFLOW_SKILLS } from '../../shared/catalog.js';
4
4
  import { getProjectContext } from '../../shared/paths.js';
5
5
  import { renderIntro } from '../../shared/prompts.js';
6
6
  import { readStateForDoctor } from '../../shared/state.js';
@@ -17,6 +17,7 @@ export async function handleListSkills(_command) {
17
17
  logSkillGroup('Languages', getLanguageSkillItems(state));
18
18
  logSkillGroup('Frameworks', getFrameworkSkillItems(state));
19
19
  logSkillGroup('Architecture', getArchitectureSkillItems(state));
20
+ logSkillGroup('Workflow', getWorkflowSkillItems(state));
20
21
  outro(chalk.green('Skill list ready.'));
21
22
  }
22
23
  function getLanguageSkillItems(state) {
@@ -45,6 +46,15 @@ function getArchitectureSkillItems(state) {
45
46
  selected: state?.selectedArchitectureSkill === skill.id,
46
47
  }));
47
48
  }
49
+ function getWorkflowSkillItems(state) {
50
+ const selectedSkillIds = new Set(state?.selectedWorkflowSkills ?? []);
51
+ return SELECTABLE_WORKFLOW_SKILLS.map((skill) => ({
52
+ name: skill.name,
53
+ id: skill.id,
54
+ description: skill.description,
55
+ selected: selectedSkillIds.has(skill.id),
56
+ }));
57
+ }
48
58
  function logSkillGroup(label, skills) {
49
59
  log.message(chalk.bold(label));
50
60
  for (const skill of skills) {
@@ -9,7 +9,7 @@ import { renderIntro } from '../../shared/prompts.js';
9
9
  import { cloneState, readStateForDoctor, writeStateFile } from '../../shared/state.js';
10
10
  import { getTemplateVersion, readTemplateManifest, renderTemplateForSync } from '../../shared/templates.js';
11
11
  import { removeUndefinedFields } from '../../shared/object.js';
12
- import { getSelectedAgentsFromState, getSelectedArchitectureSkillFromState, getSelectedFrameworkSkillsFromState, getSelectedLanguageSkillsFromState, } from '../../shared/workflow-targets.js';
12
+ import { getSelectedAgentsFromState, getSelectedArchitectureSkillFromState, getSelectedFrameworkSkillsFromState, getSelectedLanguageSkillsFromState, getSelectedWorkflowSkillsFromState, } from '../../shared/workflow-targets.js';
13
13
  export async function handleStopManagingFile({ skillName }) {
14
14
  await renderIntro();
15
15
  intro(chalk.cyan('Updating skill management'));
@@ -109,6 +109,8 @@ function isSkillSelected(state, resolved) {
109
109
  return state.selectedFrameworkSkills?.includes(resolved.skill.id) ?? false;
110
110
  case 'architecture':
111
111
  return state.selectedArchitectureSkill === resolved.skill.id;
112
+ case 'workflow':
113
+ return state.selectedWorkflowSkills?.includes(resolved.skill.id) ?? false;
112
114
  }
113
115
  }
114
116
  function removeSelectedSkill(state, resolved) {
@@ -122,6 +124,9 @@ function removeSelectedSkill(state, resolved) {
122
124
  case 'architecture':
123
125
  delete state.selectedArchitectureSkill;
124
126
  return;
127
+ case 'workflow':
128
+ state.selectedWorkflowSkills = (state.selectedWorkflowSkills ?? []).filter((skillId) => skillId !== resolved.skill.id);
129
+ return;
125
130
  }
126
131
  }
127
132
  function getSkillManagedPaths(rootPath, state, resolved) {
@@ -161,6 +166,7 @@ function getAgentsUpdate(rootPath, state) {
161
166
  selectedLanguageSkills: getSelectedLanguageSkillsFromState(state),
162
167
  selectedFrameworkSkills: getSelectedFrameworkSkillsFromState(state),
163
168
  selectedArchitectureSkill: getSelectedArchitectureSkillFromState(state),
169
+ selectedWorkflowSkills: getSelectedWorkflowSkillsFromState(state),
164
170
  }),
165
171
  };
166
172
  }
@@ -5,7 +5,7 @@ import { sha256 } from '../../shared/hash.js';
5
5
  import { getSideTemplatePath } from '../../shared/paths.js';
6
6
  import { cloneState } from '../../shared/state.js';
7
7
  import { getTemplateVersion, renderTemplateForSync } from '../../shared/templates.js';
8
- import { getSelectedArchitectureSkillFromState, getSelectedFrameworkSkillsFromState, getSelectedLanguageSkillsFromState } from '../../shared/workflow-targets.js';
8
+ import { getSelectedArchitectureSkillFromState, getSelectedFrameworkSkillsFromState, getSelectedLanguageSkillsFromState, getSelectedWorkflowSkillsFromState, } from '../../shared/workflow-targets.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;
@@ -19,6 +19,7 @@ export function applySyncAction({ rootPath, state, manifest, preview, action, })
19
19
  selectedLanguageSkills: getSelectedLanguageSkillsFromState(nextState),
20
20
  selectedFrameworkSkills: getSelectedFrameworkSkillsFromState(nextState),
21
21
  selectedArchitectureSkill: getSelectedArchitectureSkillFromState(nextState),
22
+ selectedWorkflowSkills: getSelectedWorkflowSkillsFromState(nextState),
22
23
  });
23
24
  fs.mkdirSync(path.dirname(targetPath), { recursive: true });
24
25
  fs.writeFileSync(targetPath, contents, 'utf8');
@@ -54,6 +55,7 @@ export function applySyncAction({ rootPath, state, manifest, preview, action, })
54
55
  selectedLanguageSkills: getSelectedLanguageSkillsFromState(state),
55
56
  selectedFrameworkSkills: getSelectedFrameworkSkillsFromState(state),
56
57
  selectedArchitectureSkill: getSelectedArchitectureSkillFromState(state),
58
+ selectedWorkflowSkills: getSelectedWorkflowSkillsFromState(state),
57
59
  });
58
60
  fs.mkdirSync(path.dirname(sideTemplatePath), { recursive: true });
59
61
  fs.writeFileSync(sideTemplatePath, contents, 'utf8');
@@ -44,6 +44,7 @@ export function getNextSkillSelection(state, skillName) {
44
44
  }
45
45
  const selectedLanguageSkills = [...(state.selectedLanguageSkills ?? [])];
46
46
  const selectedFrameworkSkills = [...(state.selectedFrameworkSkills ?? [])];
47
+ const selectedWorkflowSkills = [...(state.selectedWorkflowSkills ?? [])];
47
48
  switch (resolution.resolved.kind) {
48
49
  case 'language':
49
50
  if (selectedLanguageSkills.includes(resolution.resolved.skill.id)) {
@@ -56,6 +57,7 @@ export function getNextSkillSelection(state, skillName) {
56
57
  selectedLanguageSkills: [...selectedLanguageSkills, resolution.resolved.skill.id].map(getLanguageSkillById),
57
58
  selectedFrameworkSkills: selectedFrameworkSkills.map(getFrameworkSkillById),
58
59
  selectedArchitectureSkill: getArchitectureSkillById(state.selectedArchitectureSkill),
60
+ selectedWorkflowSkills: selectedWorkflowSkills.map(getWorkflowSkillById),
59
61
  },
60
62
  };
61
63
  case 'framework':
@@ -69,6 +71,7 @@ export function getNextSkillSelection(state, skillName) {
69
71
  selectedLanguageSkills: selectedLanguageSkills.map(getLanguageSkillById),
70
72
  selectedFrameworkSkills: [...selectedFrameworkSkills, resolution.resolved.skill.id].map(getFrameworkSkillById),
71
73
  selectedArchitectureSkill: getArchitectureSkillById(state.selectedArchitectureSkill),
74
+ selectedWorkflowSkills: selectedWorkflowSkills.map(getWorkflowSkillById),
72
75
  },
73
76
  };
74
77
  case 'architecture':
@@ -89,6 +92,21 @@ export function getNextSkillSelection(state, skillName) {
89
92
  selectedLanguageSkills: selectedLanguageSkills.map(getLanguageSkillById),
90
93
  selectedFrameworkSkills: selectedFrameworkSkills.map(getFrameworkSkillById),
91
94
  selectedArchitectureSkill: resolution.resolved.skill,
95
+ selectedWorkflowSkills: selectedWorkflowSkills.map(getWorkflowSkillById),
96
+ },
97
+ };
98
+ case 'workflow':
99
+ if (selectedWorkflowSkills.includes(resolution.resolved.skill.id)) {
100
+ return { status: 'noop', message: `${resolution.resolved.skill.name} is already selected.` };
101
+ }
102
+ return {
103
+ status: 'selected',
104
+ skillName: resolution.resolved.skill.name,
105
+ selection: {
106
+ selectedLanguageSkills: selectedLanguageSkills.map(getLanguageSkillById),
107
+ selectedFrameworkSkills: selectedFrameworkSkills.map(getFrameworkSkillById),
108
+ selectedArchitectureSkill: getArchitectureSkillById(state.selectedArchitectureSkill),
109
+ selectedWorkflowSkills: [...selectedWorkflowSkills, resolution.resolved.skill.id].map(getWorkflowSkillById),
92
110
  },
93
111
  };
94
112
  }
@@ -108,6 +126,7 @@ function applySelectedSkill({ rootPath, statePath, state, selectionResult, }) {
108
126
  selectedLanguageSkills: selectionResult.selection.selectedLanguageSkills,
109
127
  selectedFrameworkSkills: selectionResult.selection.selectedFrameworkSkills,
110
128
  selectedArchitectureSkill: selectionResult.selection.selectedArchitectureSkill,
129
+ selectedWorkflowSkills: selectionResult.selection.selectedWorkflowSkills,
111
130
  });
112
131
  const [newSkillFile] = renderMissingWorkflowFiles({
113
132
  missingTargets: [plan.newSkillTarget],
@@ -127,6 +146,7 @@ function applySelectedSkill({ rootPath, statePath, state, selectionResult, }) {
127
146
  selectedLanguageSkills: selectionResult.selection.selectedLanguageSkills,
128
147
  selectedFrameworkSkills: selectionResult.selection.selectedFrameworkSkills,
129
148
  selectedArchitectureSkill: selectionResult.selection.selectedArchitectureSkill,
149
+ selectedWorkflowSkills: selectionResult.selection.selectedWorkflowSkills,
130
150
  targets: plan.targets,
131
151
  });
132
152
  log.success(`Updated ${STATE_RELATIVE_PATH}`);
@@ -134,8 +154,8 @@ function applySelectedSkill({ rootPath, statePath, state, selectionResult, }) {
134
154
  }
135
155
  function buildSkillApplicationPlan({ rootPath, state, selectionResult, }) {
136
156
  const selectedAgents = getSelectedAgentsFromState(state);
137
- const previousTargets = getWorkflowTargets(rootPath, selectedAgents, getSelectedLanguageSkillsFromState(state), getSelectedFrameworkSkillsFromState(state), getArchitectureSkillById(state.selectedArchitectureSkill));
138
- const targets = getWorkflowTargets(rootPath, selectedAgents, selectionResult.selection.selectedLanguageSkills, selectionResult.selection.selectedFrameworkSkills, selectionResult.selection.selectedArchitectureSkill);
157
+ const previousTargets = getWorkflowTargets(rootPath, selectedAgents, getSelectedLanguageSkillsFromState(state), getSelectedFrameworkSkillsFromState(state), getArchitectureSkillById(state.selectedArchitectureSkill), getSelectedWorkflowSkillsFromState(state));
158
+ const targets = getWorkflowTargets(rootPath, selectedAgents, selectionResult.selection.selectedLanguageSkills, selectionResult.selection.selectedFrameworkSkills, selectionResult.selection.selectedArchitectureSkill, selectionResult.selection.selectedWorkflowSkills);
139
159
  const previousTargetPaths = new Set(previousTargets.map((target) => target.targetPath));
140
160
  const newSkillTarget = targets.find((target) => !previousTargetPaths.has(target.targetPath));
141
161
  const agentsTarget = targets.find((target) => target.label === 'AGENTS.md');
@@ -171,6 +191,9 @@ function getSelectedLanguageSkillsFromState(state) {
171
191
  function getSelectedFrameworkSkillsFromState(state) {
172
192
  return (state.selectedFrameworkSkills ?? []).map(getFrameworkSkillById);
173
193
  }
194
+ function getSelectedWorkflowSkillsFromState(state) {
195
+ return (state.selectedWorkflowSkills ?? []).map(getWorkflowSkillById);
196
+ }
174
197
  function getLanguageSkillById(skillId) {
175
198
  const resolution = resolveSelectableSkillById(skillId);
176
199
  if (!resolution.ok || resolution.resolved.kind !== 'language') {
@@ -195,3 +218,10 @@ function getArchitectureSkillById(skillId) {
195
218
  }
196
219
  return resolution.resolved.skill;
197
220
  }
221
+ function getWorkflowSkillById(skillId) {
222
+ const resolution = resolveSelectableSkillById(skillId);
223
+ if (!resolution.ok || resolution.resolved.kind !== 'workflow') {
224
+ throw new Error(`Unsupported workflow skill in state: ${skillId}`);
225
+ }
226
+ return resolution.resolved.skill;
227
+ }
@@ -154,6 +154,21 @@ export const SELECTABLE_ARCHITECTURE_SKILLS = [
154
154
  },
155
155
  },
156
156
  ];
157
+ export const SELECTABLE_WORKFLOW_SKILLS = [
158
+ {
159
+ id: 'ai-prompt-engineer-master',
160
+ name: 'AI Prompt Engineer Master',
161
+ description: 'Install guidance for creating, rewriting, optimizing, compressing, and evaluating AI prompts',
162
+ routingInstruction: 'For requests to create, rewrite, optimize, compress, evaluate, or systematize prompts for AI models, agents, tools, coding assistants, product workflows, or reusable prompt templates, use `ai-prompt-engineer-master`.',
163
+ templateRelativePath: 'skills/ai-prompt-engineer-master/SKILL.md',
164
+ targetRelativePathsByAgent: {
165
+ codex: '.agents/skills/ai-prompt-engineer-master/SKILL.md',
166
+ gemini: '.agents/skills/ai-prompt-engineer-master/SKILL.md',
167
+ claude: '.agents/skills/ai-prompt-engineer-master/SKILL.md',
168
+ windsurf: '.agents/skills/ai-prompt-engineer-master/SKILL.md',
169
+ },
170
+ },
171
+ ];
157
172
  export const SUPPORTED_AGENTS = [
158
173
  {
159
174
  id: 'codex',
@@ -195,5 +210,9 @@ export function resolveSelectableSkillById(skillId) {
195
210
  if (architectureSkill) {
196
211
  return { ok: true, resolved: { kind: 'architecture', skill: architectureSkill } };
197
212
  }
213
+ const workflowSkill = SELECTABLE_WORKFLOW_SKILLS.find((skill) => skill.id === skillId);
214
+ if (workflowSkill) {
215
+ return { ok: true, resolved: { kind: 'workflow', skill: workflowSkill } };
216
+ }
198
217
  return { ok: false, message: `Unknown skill \`${skillId}\`. Run \`sibu skills list\` to see available skills.` };
199
218
  }
@@ -55,6 +55,8 @@ function isSibuState(value) {
55
55
  (state.selectedFrameworkSkills === undefined ||
56
56
  (Array.isArray(state.selectedFrameworkSkills) && state.selectedFrameworkSkills.every((skill) => typeof skill === 'string'))) &&
57
57
  (state.selectedArchitectureSkill === undefined || typeof state.selectedArchitectureSkill === 'string') &&
58
+ (state.selectedWorkflowSkills === undefined ||
59
+ (Array.isArray(state.selectedWorkflowSkills) && state.selectedWorkflowSkills.every((skill) => typeof skill === 'string'))) &&
58
60
  (state.reviewedArchitectureSkills === undefined ||
59
61
  (Array.isArray(state.reviewedArchitectureSkills) && state.reviewedArchitectureSkills.every((skill) => typeof skill === 'string'))) &&
60
62
  !!state.managedFiles &&
@@ -3,7 +3,7 @@ import path from 'node:path';
3
3
  import { sha256 } from './hash.js';
4
4
  import { hasReviewedTemplateVersion } from './state.js';
5
5
  import { renderTemplateForSync } from './templates.js';
6
- import { getSelectedAgentsFromState, getSelectedArchitectureSkillFromState, getSelectedFrameworkSkillsFromState, getSelectedLanguageSkillsFromState, getWorkflowTargets, } from './workflow-targets.js';
6
+ import { getSelectedAgentsFromState, getSelectedArchitectureSkillFromState, getSelectedFrameworkSkillsFromState, getSelectedLanguageSkillsFromState, getSelectedWorkflowSkillsFromState, getWorkflowTargets, } from './workflow-targets.js';
7
7
  const ACTIONABLE_SYNC_PREVIEW_STATUSES = new Set([
8
8
  'new-template',
9
9
  'missing',
@@ -29,6 +29,7 @@ export function getSyncPreviews({ rootPath, state, manifest }) {
29
29
  const selectedLanguageSkills = getSelectedLanguageSkillsFromState(state);
30
30
  const selectedFrameworkSkills = getSelectedFrameworkSkillsFromState(state);
31
31
  const selectedArchitectureSkill = getSelectedArchitectureSkillFromState(state);
32
+ const selectedWorkflowSkills = getSelectedWorkflowSkillsFromState(state);
32
33
  const previews = Object.entries(state.managedFiles).map(([relativePath, managedFile]) => getManagedFileSyncPreview({
33
34
  rootPath,
34
35
  manifest,
@@ -37,8 +38,9 @@ export function getSyncPreviews({ rootPath, state, manifest }) {
37
38
  selectedLanguageSkills,
38
39
  selectedFrameworkSkills,
39
40
  selectedArchitectureSkill,
41
+ selectedWorkflowSkills,
40
42
  }));
41
- const expectedTargets = getWorkflowTargets(rootPath, getSelectedAgentsFromState(state), selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill);
43
+ const expectedTargets = getWorkflowTargets(rootPath, getSelectedAgentsFromState(state), selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, selectedWorkflowSkills);
42
44
  for (const target of expectedTargets) {
43
45
  const relativePath = path.relative(rootPath, target.targetPath);
44
46
  if (state.managedFiles[relativePath]) {
@@ -75,7 +77,7 @@ export function getSyncPreviews({ rootPath, state, manifest }) {
75
77
  }
76
78
  return previews;
77
79
  }
78
- function getManagedFileSyncPreview({ rootPath, manifest, relativePath, managedFile, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, }) {
80
+ function getManagedFileSyncPreview({ rootPath, manifest, relativePath, managedFile, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, selectedWorkflowSkills, }) {
79
81
  if (managedFile.status === 'unmanaged') {
80
82
  return { relativePath, managedFile, status: 'unmanaged', recordedTemplateVersion: managedFile.templateVersion, changes: [] };
81
83
  }
@@ -94,6 +96,7 @@ function getManagedFileSyncPreview({ rootPath, manifest, relativePath, managedFi
94
96
  selectedLanguageSkills,
95
97
  selectedFrameworkSkills,
96
98
  selectedArchitectureSkill,
99
+ selectedWorkflowSkills,
97
100
  });
98
101
  const hasUpdate = hasTemplateUpdate || hasSelectionUpdate;
99
102
  const changes = hasTemplateUpdate ? template.changes : ['Refreshes generated skill routing for the current selected skills.'];
@@ -151,7 +154,7 @@ function getManagedFileSyncPreview({ rootPath, manifest, relativePath, managedFi
151
154
  hasLocalFile,
152
155
  };
153
156
  }
154
- function hasRenderedSelectionUpdate({ relativePath, currentPath, managedFile, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, }) {
157
+ function hasRenderedSelectionUpdate({ relativePath, currentPath, managedFile, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, selectedWorkflowSkills, }) {
155
158
  if (relativePath !== 'AGENTS.md') {
156
159
  return false;
157
160
  }
@@ -161,6 +164,7 @@ function hasRenderedSelectionUpdate({ relativePath, currentPath, managedFile, se
161
164
  selectedLanguageSkills,
162
165
  selectedFrameworkSkills,
163
166
  selectedArchitectureSkill,
167
+ selectedWorkflowSkills,
164
168
  });
165
169
  return sha256(renderedContents) !== managedFile.sha256;
166
170
  }
@@ -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, }) {
21
+ export function renderTemplateForSync({ templateRelativePath, currentPath, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, selectedWorkflowSkills = [], }) {
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);
26
+ return renderSkillRouting(contents, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, selectedWorkflowSkills);
27
27
  }
28
- export function renderSkillRouting(contents, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill) {
28
+ export function renderSkillRouting(contents, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, selectedWorkflowSkills = []) {
29
29
  if (!contents.includes('{{OPTIONAL_SKILL_ROUTING}}')) {
30
30
  return contents;
31
31
  }
32
- const optionalRouting = [...selectedLanguageSkills, ...selectedFrameworkSkills, ...(selectedArchitectureSkill ? [selectedArchitectureSkill] : [])]
32
+ const optionalRouting = [...selectedLanguageSkills, ...selectedFrameworkSkills, ...(selectedArchitectureSkill ? [selectedArchitectureSkill] : []), ...selectedWorkflowSkills]
33
33
  .map((skill) => `- ${skill.routingInstruction}`)
34
34
  .join('\n');
35
35
  return contents.replace('{{OPTIONAL_SKILL_ROUTING}}', optionalRouting);
@@ -1,6 +1,6 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
- import { SIBU_VERSION, MANDATORY_SKILLS, SELECTABLE_ARCHITECTURE_SKILLS, SELECTABLE_FRAMEWORK_SKILLS, SELECTABLE_LANGUAGE_SKILLS, SUPPORTED_AGENTS } from './catalog.js';
3
+ import { SIBU_VERSION, MANDATORY_SKILLS, SELECTABLE_ARCHITECTURE_SKILLS, SELECTABLE_FRAMEWORK_SKILLS, SELECTABLE_LANGUAGE_SKILLS, SELECTABLE_WORKFLOW_SKILLS, SUPPORTED_AGENTS, } from './catalog.js';
4
4
  import { sha256 } from './hash.js';
5
5
  import { removeUndefinedFields } from './object.js';
6
6
  import { readExistingState } from './state.js';
@@ -14,13 +14,17 @@ export function getSelectedFrameworkSkillsFromState(state) {
14
14
  export function getSelectedArchitectureSkillFromState(state) {
15
15
  return SELECTABLE_ARCHITECTURE_SKILLS.find((skill) => skill.id === state.selectedArchitectureSkill);
16
16
  }
17
- export function getSelectedSkillTargetsForAgents(selectedAgents, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill) {
17
+ export function getSelectedWorkflowSkillsFromState(state) {
18
+ return SELECTABLE_WORKFLOW_SKILLS.filter((skill) => state.selectedWorkflowSkills?.includes(skill.id));
19
+ }
20
+ export function getSelectedSkillTargetsForAgents(selectedAgents, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, selectedWorkflowSkills = []) {
18
21
  const skillTargets = new Map();
19
22
  const selectedSkills = [
20
23
  ...MANDATORY_SKILLS,
21
24
  ...selectedLanguageSkills,
22
25
  ...selectedFrameworkSkills,
23
26
  ...(selectedArchitectureSkill ? [selectedArchitectureSkill] : []),
27
+ ...selectedWorkflowSkills,
24
28
  ];
25
29
  for (const agent of selectedAgents) {
26
30
  for (const skill of selectedSkills) {
@@ -36,7 +40,7 @@ export function getSelectedSkillTargetsForAgents(selectedAgents, selectedLanguag
36
40
  }
37
41
  return [...skillTargets.values()];
38
42
  }
39
- export function getWorkflowTargets(rootPath, selectedAgents, selectedLanguageSkills = [], selectedFrameworkSkills = [], selectedArchitectureSkill) {
43
+ export function getWorkflowTargets(rootPath, selectedAgents, selectedLanguageSkills = [], selectedFrameworkSkills = [], selectedArchitectureSkill, selectedWorkflowSkills = []) {
40
44
  return [
41
45
  {
42
46
  label: 'AGENTS.md',
@@ -57,7 +61,7 @@ export function getWorkflowTargets(rootPath, selectedAgents, selectedLanguageSki
57
61
  },
58
62
  ];
59
63
  }),
60
- ...getSelectedSkillTargetsForAgents(selectedAgents, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill).map((skillTarget) => ({
64
+ ...getSelectedSkillTargetsForAgents(selectedAgents, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, selectedWorkflowSkills).map((skillTarget) => ({
61
65
  label: skillTarget.targetRelativePath,
62
66
  targetPath: path.join(rootPath, skillTarget.targetRelativePath),
63
67
  templateRelativePath: skillTarget.templateRelativePath,
@@ -65,7 +69,7 @@ export function getWorkflowTargets(rootPath, selectedAgents, selectedLanguageSki
65
69
  })),
66
70
  ];
67
71
  }
68
- export function renderMissingWorkflowFiles({ missingTargets, overview, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, }) {
72
+ export function renderMissingWorkflowFiles({ missingTargets, overview, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, selectedWorkflowSkills = [], }) {
69
73
  return missingTargets.map((target) => {
70
74
  let contents = readTemplate(target.templateRelativePath);
71
75
  if (target.requiresProjectOverview) {
@@ -74,7 +78,7 @@ export function renderMissingWorkflowFiles({ missingTargets, overview, selectedL
74
78
  }
75
79
  contents = contents.replace('{{PROJECT_OVERVIEW}}', overview.trim());
76
80
  }
77
- contents = renderSkillRouting(contents, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill);
81
+ contents = renderSkillRouting(contents, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, selectedWorkflowSkills);
78
82
  return {
79
83
  label: target.label,
80
84
  targetPath: target.targetPath,
@@ -82,7 +86,7 @@ export function renderMissingWorkflowFiles({ missingTargets, overview, selectedL
82
86
  };
83
87
  });
84
88
  }
85
- export function writeSibuState({ rootPath, statePath, selectedAgents, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, targets, }) {
89
+ export function writeSibuState({ rootPath, statePath, selectedAgents, selectedLanguageSkills, selectedFrameworkSkills, selectedArchitectureSkill, selectedWorkflowSkills = [], targets, }) {
86
90
  const previousState = readExistingState(statePath);
87
91
  const now = new Date().toISOString();
88
92
  const manifest = readTemplateManifest();
@@ -95,6 +99,7 @@ export function writeSibuState({ rootPath, statePath, selectedAgents, selectedLa
95
99
  selectedLanguageSkills: selectedLanguageSkills.map((skill) => skill.id),
96
100
  selectedFrameworkSkills: selectedFrameworkSkills.map((skill) => skill.id),
97
101
  selectedArchitectureSkill: selectedArchitectureSkill?.id,
102
+ selectedWorkflowSkills: selectedWorkflowSkills.map((skill) => skill.id),
98
103
  managedFiles: Object.fromEntries(targets
99
104
  .filter((target) => fs.existsSync(target.targetPath))
100
105
  .map((target) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juancr11/sibu",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "CLI for setting up a local AI-augmented development workflow.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1,5 +1,5 @@
1
1
  {
2
- "templateVersion": "45",
2
+ "templateVersion": "46",
3
3
  "templates": {
4
4
  "AGENTS.md": {
5
5
  "version": "20",
@@ -71,7 +71,7 @@
71
71
  "version": "1",
72
72
  "description": "Selectable Go skill installed when Go language support is selected.",
73
73
  "changes": [
74
- "Adds an optional Go skill with concise, Effective Go–style guidance for .go files, package APIs, interfaces, errors, receivers, concurrency, and tests."
74
+ "Adds an optional Go skill with concise, Effective Go\u2013style guidance for .go files, package APIs, interfaces, errors, receivers, concurrency, and tests."
75
75
  ]
76
76
  },
77
77
  "skills/architecture/ddd-hexagonal/SKILL.md": {
@@ -126,6 +126,13 @@
126
126
  "Allows agents to begin the first unapproved implementation step immediately when a valid plan exists, while preserving review approval between steps.",
127
127
  "Clarifies that after user approval, agents should mark the step approved, commit it, and continue to the next step without another pre-implementation confirmation."
128
128
  ]
129
+ },
130
+ "skills/ai-prompt-engineer-master/SKILL.md": {
131
+ "version": "1",
132
+ "description": "Selectable AI prompt engineering skill for prompt creation, optimization, compression, and evaluation.",
133
+ "changes": [
134
+ "Adds an optional AI prompt engineering skill for projects that create, improve, evaluate, or maintain reusable prompts for AI models, agents, tools, and coding assistants."
135
+ ]
129
136
  }
130
137
  }
131
138
  }
@@ -0,0 +1,98 @@
1
+ ---
2
+ name: ai-prompt-engineer-master
3
+ description: "Use when creating, rewriting, optimizing, compressing, evaluating, or systematizing prompts for AI models, agents, tools, coding assistants, product workflows, or reusable prompt templates. Focus on top-quality results with the smallest effective token budget: preserve or improve output quality first, then remove every token that does not contribute to that quality."
4
+ ---
5
+
6
+ # AI Prompt Engineer Master
7
+
8
+ ## Core principle
9
+
10
+ Optimize for the **smallest prompt that reliably produces the required quality**.
11
+
12
+ This skill is model-agnostic: adapt wording to the target model when known, but do not assume any specific provider or model family.
13
+
14
+ Do not trade quality away for fewer tokens. First define the quality bar, then find the leanest prompt that meets it.
15
+
16
+ ## Workflow
17
+
18
+ 1. **Clarify the prompt job**
19
+ - Identify the target model or agent if known.
20
+ - Identify the user, task, input context, output format, quality bar, and failure modes.
21
+ - Ask only when missing information would materially change the prompt.
22
+
23
+ 2. **Define the quality contract**
24
+ - State the outcome the prompt must achieve.
25
+ - Include success criteria, required constraints, and non-negotiable safety or compliance rules.
26
+ - Define what the model should do when context is missing, ambiguous, or contradictory.
27
+
28
+ 3. **Design outcome-first**
29
+ - Prefer destination over step-by-step process.
30
+ - Use process instructions only when order, auditability, or tool behavior truly matters.
31
+ - Put stable instructions before dynamic task/input content.
32
+
33
+ 4. **Compress without quality loss**
34
+ - Remove duplicated rules, generic advice, filler, motivational language, and obvious model capabilities.
35
+ - Replace long explanations with precise constraints or decision rules.
36
+ - Merge overlapping instructions.
37
+ - Keep examples only when they prevent likely errors or define subtle style/format expectations.
38
+
39
+ 5. **Make the output easy to verify**
40
+ - Specify the output shape only as tightly as needed.
41
+ - Prefer schemas, examples, or bullets when they reduce ambiguity.
42
+ - Include acceptance checks for high-stakes or reusable prompts.
43
+
44
+ 6. **Deliver usable artifacts**
45
+ - Provide the final prompt in a copy-ready block.
46
+ - If helpful, include a short note explaining the token-saving choices.
47
+ - For reusable prompts, separate stable prompt text from runtime variables.
48
+
49
+ ## Prompt quality checklist
50
+
51
+ A strong prompt usually has:
52
+
53
+ - **Role or perspective** only when it changes behavior.
54
+ - **Task** stated as a concrete outcome.
55
+ - **Context** limited to information the model needs.
56
+ - **Constraints** that prevent real failures.
57
+ - **Output format** specific enough for the consumer.
58
+ - **Missing-context behavior** so the model does not invent facts.
59
+ - **Tone/style** only when user-facing quality depends on it.
60
+ - **Validation criteria** for important workflows.
61
+
62
+ ## Token discipline rules
63
+
64
+ Prefer:
65
+
66
+ - “Return JSON matching this schema” over a prose explanation of each field when a schema is available.
67
+ - “If evidence is missing, say what is missing” over long hallucination warnings.
68
+ - “Use concise bullets” over multiple style paragraphs.
69
+ - One representative example over many near-duplicates.
70
+ - A short decision rule over absolute words like “always” or “never” unless the rule is truly invariant.
71
+
72
+ Avoid:
73
+
74
+ - Repeating the same constraint in multiple sections.
75
+ - Explaining why the model should follow ordinary instructions.
76
+ - Adding chain-of-thought requests.
77
+ - Keeping legacy prompt scaffolding because it “might help.”
78
+ - Compressing away domain terms, examples, or constraints that carry real quality value.
79
+
80
+ ## Output pattern
81
+
82
+ When optimizing a prompt, use this structure unless the user asks otherwise:
83
+
84
+ ```md
85
+ ## Final prompt
86
+
87
+ <prompt>
88
+
89
+ ## Why this is token-efficient
90
+
91
+ - <1-3 bullets about what was preserved or removed>
92
+
93
+ ## Optional stronger version
94
+
95
+ <only include if quality risks justify extra tokens>
96
+ ```
97
+
98
+ If the user only wants the prompt, return only the final prompt.