Skip to content

Commit b0f80b6

Browse files
v1.1.0: Fix CLI flags, lock verification, init SKILL.md copy
- Fix: enqueue CLI now accepts --files, --skills, --depends-on, --dag-id - Fix: startExecution() re-verifies lock conflicts (prevents double-acquire) - Fix: init copies bundled SKILL.md guides to target directory - Add: lock conflict rejection test (13/13 tests pass) - Bump: v1.0.0 → v1.1.0
1 parent 78eed6a commit b0f80b6

File tree

4 files changed

+55
-5
lines changed

4 files changed

+55
-5
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "agent-skill-bus",
3-
"version": "1.0.0",
3+
"version": "1.1.0",
44
"description": "Self-improving task orchestration framework for AI agent systems",
55
"type": "module",
66
"main": "src/index.js",

src/cli.js

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,9 @@ switch (command) {
5353
case 'init': {
5454
// Initialize skill-bus in current directory
5555
const initDir = getFlag('dir', process.cwd());
56-
const { mkdirSync, writeFileSync: wf, existsSync: ex } = await import('node:fs');
56+
const { mkdirSync, writeFileSync: wf, existsSync: ex, readFileSync: rf } = await import('node:fs');
57+
const { dirname: dn, join: jn } = await import('node:path');
58+
const { fileURLToPath } = await import('node:url');
5759

5860
const dirs = [
5961
'skills/prompt-request-bus',
@@ -66,6 +68,7 @@ switch (command) {
6668
mkdirSync(full, { recursive: true });
6769
}
6870

71+
// Data files (empty/default)
6972
const files = {
7073
'skills/prompt-request-bus/prompt-request-queue.jsonl': '',
7174
'skills/prompt-request-bus/active-locks.jsonl': '',
@@ -87,23 +90,50 @@ switch (command) {
8790
}
8891
}
8992

93+
// Copy bundled SKILL.md files from package
94+
const pkgDir = dn(dn(fileURLToPath(import.meta.url)));
95+
const skillMds = [
96+
'skills/prompt-request-bus/SKILL.md',
97+
'skills/self-improving-skills/SKILL.md',
98+
'skills/knowledge-watcher/SKILL.md',
99+
];
100+
101+
for (const s of skillMds) {
102+
const dest = resolve(initDir, s);
103+
const src = jn(pkgDir, s);
104+
if (!ex(dest) && ex(src)) {
105+
wf(dest, rf(src, 'utf-8'));
106+
created++;
107+
}
108+
}
109+
90110
output({
91111
status: 'initialized',
92112
directory: initDir,
93113
filesCreated: created,
94-
message: `Agent Skill Bus initialized. Run 'skill-bus stats' to verify.`,
114+
message: `Agent Skill Bus initialized with data files and SKILL.md guides. Run 'skill-bus stats' to verify.`,
95115
});
96116
break;
97117
}
98118

99119
case 'enqueue': {
120+
// Parse comma-separated list flags
121+
const parseList = (name) => {
122+
const val = getFlag(name, '');
123+
return val ? val.split(',').map(s => s.trim()).filter(Boolean) : [];
124+
};
125+
100126
const pr = queue.enqueue({
101127
source: getFlag('source', 'human'),
102128
priority: getFlag('priority', 'medium'),
103129
agent: getFlag('agent', 'default'),
104130
task: getFlag('task', ''),
105131
context: getFlag('context', ''),
106132
deadline: getFlag('deadline', 'none'),
133+
affectedFiles: parseList('files'),
134+
affectedSkills: parseList('skills'),
135+
dependsOn: parseList('depends-on'),
136+
dagId: getFlag('dag-id', null),
107137
});
108138
if (pr) {
109139
output({ status: 'enqueued', pr });
@@ -209,7 +239,7 @@ switch (command) {
209239
}
210240

211241
default:
212-
console.log(`Agent Skill Bus v1.0.0
242+
console.log(`Agent Skill Bus v1.1.0
213243
214244
Usage: skill-bus <command> [options]
215245
npx agent-skill-bus <command> [options]

src/queue.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,16 @@ export class PromptRequestQueue {
137137
const pr = all.find(p => p.id === prId);
138138
if (!pr) throw new Error(`PR not found: ${prId}`);
139139

140-
// Acquire file locks
140+
// Re-verify lock conflicts at execution time (not just dispatch time)
141141
if (pr.affectedFiles.length > 0) {
142+
const currentLocks = this.readLocks();
143+
const lockedFiles = new Set(currentLocks.flatMap(l => l.files));
144+
const conflicts = pr.affectedFiles.filter(f => lockedFiles.has(f));
145+
if (conflicts.length > 0) {
146+
throw new Error(`Lock conflict: files [${conflicts.join(', ')}] are already locked. PR ${prId} cannot start.`);
147+
}
148+
149+
// Acquire file locks
142150
const lock = {
143151
agent: pr.agent,
144152
files: pr.affectedFiles,

src/queue.test.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,18 @@ describe('PromptRequestQueue', () => {
107107
assert.strictEqual(pr2.status, 'blocked');
108108
});
109109

110+
it('should reject startExecution when files are already locked', () => {
111+
const pr1 = queue.enqueue({ source: 'r1', agent: 'a', task: 'edit main', affectedFiles: ['src/main.ts'] });
112+
const pr2 = queue.enqueue({ source: 'r2', agent: 'b', task: 'also edit main', affectedFiles: ['src/main.ts'] });
113+
114+
queue.startExecution(pr1.id);
115+
116+
// pr2 should throw on start because src/main.ts is locked
117+
assert.throws(() => {
118+
queue.startExecution(pr2.id);
119+
}, /Lock conflict/);
120+
});
121+
110122
it('should return stats', () => {
111123
queue.enqueue({ source: 's1', agent: 'a', task: 'task 1' });
112124
queue.enqueue({ source: 's2', agent: 'b', task: 'task 2' });

0 commit comments

Comments
 (0)