orchestr8 2.1.3

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/src/init.js ADDED
@@ -0,0 +1,107 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const readline = require('readline');
4
+
5
+ const PACKAGE_ROOT = path.resolve(__dirname, '..');
6
+ const TARGET_DIR = process.cwd();
7
+
8
+ async function prompt(question) {
9
+ const rl = readline.createInterface({
10
+ input: process.stdin,
11
+ output: process.stdout
12
+ });
13
+
14
+ return new Promise((resolve) => {
15
+ rl.question(question, (answer) => {
16
+ rl.close();
17
+ resolve(answer.toLowerCase().trim());
18
+ });
19
+ });
20
+ }
21
+
22
+ function copyDir(src, dest) {
23
+ fs.mkdirSync(dest, { recursive: true });
24
+
25
+ const entries = fs.readdirSync(src, { withFileTypes: true });
26
+
27
+ for (const entry of entries) {
28
+ const srcPath = path.join(src, entry.name);
29
+ const destPath = path.join(dest, entry.name);
30
+
31
+ if (entry.isDirectory()) {
32
+ copyDir(srcPath, destPath);
33
+ } else {
34
+ fs.copyFileSync(srcPath, destPath);
35
+ }
36
+ }
37
+ }
38
+
39
+ function updateGitignore() {
40
+ const gitignorePath = path.join(TARGET_DIR, '.gitignore');
41
+ const entriesToAdd = [
42
+ '# agent-workflow',
43
+ '.claude/implement-queue.json'
44
+ ];
45
+
46
+ let content = '';
47
+ if (fs.existsSync(gitignorePath)) {
48
+ content = fs.readFileSync(gitignorePath, 'utf8');
49
+ }
50
+
51
+ const newEntries = entriesToAdd.filter(entry => !content.includes(entry));
52
+
53
+ if (newEntries.length > 0) {
54
+ const addition = '\n' + newEntries.join('\n') + '\n';
55
+ fs.appendFileSync(gitignorePath, addition);
56
+ console.log('Updated .gitignore');
57
+ }
58
+ }
59
+
60
+ async function init() {
61
+ const blueprintSrc = path.join(PACKAGE_ROOT, '.blueprint');
62
+ const blueprintDest = path.join(TARGET_DIR, '.blueprint');
63
+ const skillSrc = path.join(PACKAGE_ROOT, 'SKILL.md');
64
+ const skillDest = path.join(TARGET_DIR, 'SKILL.md');
65
+
66
+ // Check if .blueprint already exists
67
+ if (fs.existsSync(blueprintDest)) {
68
+ const answer = await prompt('.blueprint directory already exists. Overwrite? (y/N): ');
69
+ if (answer !== 'y' && answer !== 'yes') {
70
+ console.log('Aborted. Use "agent-workflow update" to update existing installation.');
71
+ return;
72
+ }
73
+ fs.rmSync(blueprintDest, { recursive: true });
74
+ }
75
+
76
+ // Check if SKILL.md already exists
77
+ if (fs.existsSync(skillDest)) {
78
+ const answer = await prompt('SKILL.md already exists. Overwrite? (y/N): ');
79
+ if (answer !== 'y' && answer !== 'yes') {
80
+ console.log('Skipping SKILL.md');
81
+ } else {
82
+ fs.copyFileSync(skillSrc, skillDest);
83
+ console.log('Copied SKILL.md');
84
+ }
85
+ } else {
86
+ fs.copyFileSync(skillSrc, skillDest);
87
+ console.log('Copied SKILL.md');
88
+ }
89
+
90
+ // Copy .blueprint directory
91
+ console.log('Copying .blueprint directory...');
92
+ copyDir(blueprintSrc, blueprintDest);
93
+ console.log('Copied .blueprint directory');
94
+
95
+ // Update .gitignore
96
+ updateGitignore();
97
+
98
+ console.log(`
99
+ agent-workflow initialized successfully!
100
+
101
+ Next steps:
102
+ 1. Add business context to .blueprint/.business_context/
103
+ 2. Run /implement-feature in Claude Code to start your first feature
104
+ `);
105
+ }
106
+
107
+ module.exports = { init };
@@ -0,0 +1,217 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ const QUEUE_PATH = '.claude/implement-queue.json';
5
+
6
+ function ensureQueueDir() {
7
+ const dir = path.dirname(QUEUE_PATH);
8
+ if (!fs.existsSync(dir)) {
9
+ fs.mkdirSync(dir, { recursive: true });
10
+ }
11
+ }
12
+
13
+ function createEmptyQueue() {
14
+ return {
15
+ lastUpdated: new Date().toISOString(),
16
+ current: null,
17
+ alexQueue: [],
18
+ cassQueue: [],
19
+ nigelQueue: [],
20
+ codeyQueue: [],
21
+ completed: [],
22
+ failed: []
23
+ };
24
+ }
25
+
26
+ function readQueue() {
27
+ ensureQueueDir();
28
+ if (!fs.existsSync(QUEUE_PATH)) {
29
+ const queue = createEmptyQueue();
30
+ writeQueue(queue);
31
+ return queue;
32
+ }
33
+ const content = fs.readFileSync(QUEUE_PATH, 'utf8');
34
+ return JSON.parse(content);
35
+ }
36
+
37
+ function writeQueue(queue) {
38
+ ensureQueueDir();
39
+ queue.lastUpdated = new Date().toISOString();
40
+ fs.writeFileSync(QUEUE_PATH, JSON.stringify(queue, null, 2));
41
+ }
42
+
43
+ function updateQueue(updates) {
44
+ const queue = readQueue();
45
+ Object.assign(queue, updates);
46
+ writeQueue(queue);
47
+ return queue;
48
+ }
49
+
50
+ function setCurrent(slug, stage) {
51
+ const queue = readQueue();
52
+ queue.current = {
53
+ slug,
54
+ stage,
55
+ startedAt: new Date().toISOString()
56
+ };
57
+ writeQueue(queue);
58
+ return queue;
59
+ }
60
+
61
+ function clearCurrent() {
62
+ const queue = readQueue();
63
+ queue.current = null;
64
+ writeQueue(queue);
65
+ return queue;
66
+ }
67
+
68
+ function moveToNextStage(slug, fromStage, toStage) {
69
+ const queue = readQueue();
70
+ const fromQueueName = `${fromStage}Queue`;
71
+ const toQueueName = `${toStage}Queue`;
72
+
73
+ // Remove from source queue
74
+ if (queue[fromQueueName]) {
75
+ queue[fromQueueName] = queue[fromQueueName].filter(item => item.slug !== slug);
76
+ }
77
+
78
+ // Add to destination queue
79
+ if (!queue[toQueueName]) {
80
+ queue[toQueueName] = [];
81
+ }
82
+
83
+ const entry = { slug, movedAt: new Date().toISOString() };
84
+ queue[toQueueName].push(entry);
85
+
86
+ // Update current stage
87
+ if (queue.current && queue.current.slug === slug) {
88
+ queue.current.stage = toStage;
89
+ }
90
+
91
+ writeQueue(queue);
92
+ return queue;
93
+ }
94
+
95
+ function markCompleted(slug, metadata = {}) {
96
+ const queue = readQueue();
97
+
98
+ // Remove from codeyQueue
99
+ queue.codeyQueue = queue.codeyQueue.filter(item => item.slug !== slug);
100
+
101
+ // Add to completed
102
+ queue.completed.push({
103
+ slug,
104
+ completedAt: new Date().toISOString(),
105
+ ...metadata
106
+ });
107
+
108
+ // Clear current if this was it
109
+ if (queue.current && queue.current.slug === slug) {
110
+ queue.current = null;
111
+ }
112
+
113
+ writeQueue(queue);
114
+ return queue;
115
+ }
116
+
117
+ function markFailed(slug, stage, reason) {
118
+ const queue = readQueue();
119
+
120
+ // Remove from all stage queues
121
+ ['alexQueue', 'cassQueue', 'nigelQueue', 'codeyQueue'].forEach(queueName => {
122
+ if (queue[queueName]) {
123
+ queue[queueName] = queue[queueName].filter(item => item.slug !== slug);
124
+ }
125
+ });
126
+
127
+ // Add to failed
128
+ queue.failed.push({
129
+ slug,
130
+ stage,
131
+ reason,
132
+ failedAt: new Date().toISOString()
133
+ });
134
+
135
+ // Clear current if this was it
136
+ if (queue.current && queue.current.slug === slug) {
137
+ queue.current = null;
138
+ }
139
+
140
+ writeQueue(queue);
141
+ return queue;
142
+ }
143
+
144
+ function resetQueue() {
145
+ const queue = createEmptyQueue();
146
+ writeQueue(queue);
147
+ return queue;
148
+ }
149
+
150
+ function getQueueStatus() {
151
+ const queue = readQueue();
152
+ return {
153
+ current: queue.current,
154
+ pending: {
155
+ alex: queue.alexQueue.length,
156
+ cass: queue.cassQueue.length,
157
+ nigel: queue.nigelQueue.length,
158
+ codey: queue.codeyQueue.length
159
+ },
160
+ completed: queue.completed.length,
161
+ failed: queue.failed.length
162
+ };
163
+ }
164
+
165
+ function displayQueue() {
166
+ const queue = readQueue();
167
+
168
+ console.log('\nImplement Feature Queue Status');
169
+ console.log('==============================\n');
170
+
171
+ if (queue.current) {
172
+ console.log('Current:');
173
+ console.log(` ${queue.current.slug} (stage: ${queue.current.stage})`);
174
+ console.log(` Started: ${queue.current.startedAt}\n`);
175
+ } else {
176
+ console.log('Current: (none)\n');
177
+ }
178
+
179
+ console.log('Queues:');
180
+ console.log(` Alex: ${queue.alexQueue.length} pending`);
181
+ console.log(` Cass: ${queue.cassQueue.length} pending`);
182
+ console.log(` Nigel: ${queue.nigelQueue.length} pending`);
183
+ console.log(` Codey: ${queue.codeyQueue.length} pending\n`);
184
+
185
+ if (queue.completed.length > 0) {
186
+ console.log('Completed:');
187
+ queue.completed.forEach(item => {
188
+ console.log(` - ${item.slug} (${item.completedAt})`);
189
+ });
190
+ console.log('');
191
+ }
192
+
193
+ if (queue.failed.length > 0) {
194
+ console.log('Failed:');
195
+ queue.failed.forEach(item => {
196
+ console.log(` - ${item.slug} at ${item.stage}: ${item.reason}`);
197
+ });
198
+ console.log('');
199
+ }
200
+
201
+ console.log(`Last updated: ${queue.lastUpdated}`);
202
+ }
203
+
204
+ module.exports = {
205
+ QUEUE_PATH,
206
+ readQueue,
207
+ writeQueue,
208
+ updateQueue,
209
+ setCurrent,
210
+ clearCurrent,
211
+ moveToNextStage,
212
+ markCompleted,
213
+ markFailed,
214
+ resetQueue,
215
+ getQueueStatus,
216
+ displayQueue
217
+ };
package/src/skills.js ADDED
@@ -0,0 +1,93 @@
1
+ const { execSync } = require('child_process');
2
+
3
+ const AGENT_SKILLS = {
4
+ alex: {
5
+ name: 'Alex',
6
+ role: 'System Specification & Chief-of-Staff',
7
+ skills: [
8
+ { repo: 'https://github.com/waynesutton/convexskills', skill: 'avoid-feature-creep' },
9
+ { repo: 'https://github.com/pproenca/dot-skills', skill: 'feature-spec' }
10
+ ]
11
+ },
12
+ cass: {
13
+ name: 'Cass',
14
+ role: 'Story Writer / BA',
15
+ skills: [
16
+ { repo: 'https://github.com/aj-geddes/useful-ai-prompts', skill: 'user-story-writing' }
17
+ ]
18
+ },
19
+ nigel: {
20
+ name: 'Nigel',
21
+ role: 'Tester',
22
+ skills: [
23
+ { repo: 'https://github.com/wshobson/agents', skill: 'javascript-testing-patterns' },
24
+ { repo: 'https://github.com/wshobson/agents', skill: 'modern-javascript-patterns' }
25
+ ]
26
+ },
27
+ codey: {
28
+ name: 'Codey',
29
+ role: 'Developer',
30
+ skills: [
31
+ { repo: 'https://github.com/martinholovsky/claude-skills-generator', skill: 'javascript-expert' },
32
+ { repo: 'https://github.com/wshobson/agents', skill: 'modern-javascript-patterns' }
33
+ ]
34
+ }
35
+ };
36
+
37
+ function installSkill(repo, skill) {
38
+ const cmd = `npx skills add ${repo} --skill ${skill}`;
39
+ console.log(` Running: ${cmd}`);
40
+ try {
41
+ execSync(cmd, { stdio: 'inherit' });
42
+ return true;
43
+ } catch (error) {
44
+ console.error(` Failed to install ${skill}`);
45
+ return false;
46
+ }
47
+ }
48
+
49
+ async function addSkills(agent) {
50
+ const agents = agent === 'all' ? Object.keys(AGENT_SKILLS) : [agent.toLowerCase()];
51
+
52
+ for (const agentKey of agents) {
53
+ const agentConfig = AGENT_SKILLS[agentKey];
54
+
55
+ if (!agentConfig) {
56
+ console.error(`Unknown agent: ${agentKey}`);
57
+ console.error(`Available agents: ${Object.keys(AGENT_SKILLS).join(', ')}, all`);
58
+ process.exit(1);
59
+ }
60
+
61
+ console.log(`\nInstalling skills for ${agentConfig.name} (${agentConfig.role}):`);
62
+
63
+ for (const { repo, skill } of agentConfig.skills) {
64
+ installSkill(repo, skill);
65
+ }
66
+ }
67
+
68
+ console.log('\nSkills installation complete.');
69
+ }
70
+
71
+ function listSkills(agent) {
72
+ const agents = agent ? [agent.toLowerCase()] : Object.keys(AGENT_SKILLS);
73
+
74
+ console.log('\nAgent Workflow - Recommended Skills\n');
75
+
76
+ for (const agentKey of agents) {
77
+ const agentConfig = AGENT_SKILLS[agentKey];
78
+
79
+ if (!agentConfig) {
80
+ console.error(`Unknown agent: ${agentKey}`);
81
+ process.exit(1);
82
+ }
83
+
84
+ console.log(`${agentConfig.name} (${agentConfig.role}):`);
85
+ for (const { repo, skill } of agentConfig.skills) {
86
+ console.log(` - ${skill}`);
87
+ console.log(` ${repo}`);
88
+ }
89
+ console.log('');
90
+ }
91
+ }
92
+
93
+ module.exports = { addSkills, listSkills, AGENT_SKILLS };
package/src/update.js ADDED
@@ -0,0 +1,105 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const readline = require('readline');
4
+
5
+ const PACKAGE_ROOT = path.resolve(__dirname, '..');
6
+ const TARGET_DIR = process.cwd();
7
+
8
+ // Directories that contain user content and should NOT be overwritten
9
+ const USER_CONTENT_DIRS = [
10
+ 'features',
11
+ 'system_specification',
12
+ '.business_context'
13
+ ];
14
+
15
+ // Directories/files that should be updated
16
+ const UPDATABLE = [
17
+ 'agents',
18
+ 'templates',
19
+ 'ways_of_working'
20
+ ];
21
+
22
+ async function prompt(question) {
23
+ const rl = readline.createInterface({
24
+ input: process.stdin,
25
+ output: process.stdout
26
+ });
27
+
28
+ return new Promise((resolve) => {
29
+ rl.question(question, (answer) => {
30
+ rl.close();
31
+ resolve(answer.toLowerCase().trim());
32
+ });
33
+ });
34
+ }
35
+
36
+ function copyDir(src, dest) {
37
+ fs.mkdirSync(dest, { recursive: true });
38
+
39
+ const entries = fs.readdirSync(src, { withFileTypes: true });
40
+
41
+ for (const entry of entries) {
42
+ const srcPath = path.join(src, entry.name);
43
+ const destPath = path.join(dest, entry.name);
44
+
45
+ if (entry.isDirectory()) {
46
+ copyDir(srcPath, destPath);
47
+ } else {
48
+ fs.copyFileSync(srcPath, destPath);
49
+ }
50
+ }
51
+ }
52
+
53
+ async function update() {
54
+ const blueprintDest = path.join(TARGET_DIR, '.blueprint');
55
+ const blueprintSrc = path.join(PACKAGE_ROOT, '.blueprint');
56
+ const skillSrc = path.join(PACKAGE_ROOT, 'SKILL.md');
57
+ const skillDest = path.join(TARGET_DIR, 'SKILL.md');
58
+
59
+ // Check if .blueprint exists
60
+ if (!fs.existsSync(blueprintDest)) {
61
+ console.log('.blueprint directory not found. Run "agent-workflow init" first.');
62
+ process.exit(1);
63
+ }
64
+
65
+ console.log('Updating agent-workflow...');
66
+ console.log('(Preserving: features/, system_specification/, .business_context/)\n');
67
+
68
+ // Update each updatable directory
69
+ for (const dir of UPDATABLE) {
70
+ const srcDir = path.join(blueprintSrc, dir);
71
+ const destDir = path.join(blueprintDest, dir);
72
+
73
+ if (fs.existsSync(srcDir)) {
74
+ if (fs.existsSync(destDir)) {
75
+ fs.rmSync(destDir, { recursive: true });
76
+ }
77
+ copyDir(srcDir, destDir);
78
+ console.log(`Updated .blueprint/${dir}/`);
79
+ }
80
+ }
81
+
82
+ // Update SKILL.md
83
+ const answer = await prompt('\nUpdate SKILL.md? (Y/n): ');
84
+ if (answer !== 'n' && answer !== 'no') {
85
+ fs.copyFileSync(skillSrc, skillDest);
86
+ console.log('Updated SKILL.md');
87
+ }
88
+
89
+ console.log(`
90
+ Update complete!
91
+
92
+ Updated:
93
+ - .blueprint/agents/
94
+ - .blueprint/templates/
95
+ - .blueprint/ways_of_working/
96
+ - SKILL.md
97
+
98
+ Preserved:
99
+ - .blueprint/features/
100
+ - .blueprint/system_specification/
101
+ - .blueprint/.business_context/
102
+ `);
103
+ }
104
+
105
+ module.exports = { update };