@ulysses-ai/create-workspace 0.21.0-beta.0 → 0.23.0-beta.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/lib/init.mjs +9 -0
- package/lib/init.test.mjs +75 -0
- package/lib/payload.mjs +170 -2
- package/lib/payload.test.mjs +158 -3
- package/lib/scaffold.mjs +8 -0
- package/lib/scaffold.test.mjs +20 -0
- package/lib/upgrade.mjs +148 -6
- package/lib/upgrade.test.mjs +319 -15
- package/package.json +1 -1
- package/template/_claude/rules/forge-operations.md +27 -6
- package/template/_claude/scripts/chat-record.mjs +51 -4
- package/template/_claude/scripts/classify-update.mjs +474 -38
- package/template/_claude/scripts/cleanup-work-session.mjs +64 -3
- package/template/_claude/scripts/forges/gitlab.mjs +450 -18
- package/template/_claude/scripts/forges/interface.mjs +39 -6
- package/template/_claude/scripts/maintenance-audit.mjs +0 -0
- package/template/_claude/scripts/merge-mode.mjs +96 -12
- package/template/_claude/scripts/migrate-sessions.mjs +232 -24
- package/template/_claude/scripts/task-pr.mjs +52 -13
- package/template/_claude/scripts/task-worktree.mjs +79 -10
- package/template/_claude/scripts/template-baseline.mjs +239 -0
- package/template/_claude/scripts/trackers/gitlab-issues.mjs +276 -0
- package/template/_claude/scripts/trackers/interface.mjs +3 -0
- package/template/_claude/skills/complete-work/SKILL.md +7 -4
- package/template/_claude/skills/migrate-sessions/SKILL.md +20 -4
- package/template/_claude/skills/release/SKILL.md +24 -8
- package/template/_claude/skills/setup-tracker/SKILL.md +46 -12
- package/template/_claude/skills/start-work/SKILL.md +15 -2
- package/template/_claude/skills/workspace-init/SKILL.md +6 -0
- package/template/_claude/skills/workspace-update/SKILL.md +63 -27
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
// node chat-record.mjs --root <dir> --read <chat-name>
|
|
22
22
|
// node chat-record.mjs --root <dir> --reconcile --session-id <id> --name <n>
|
|
23
23
|
// node chat-record.mjs --root <dir> --whoami
|
|
24
|
+
// node chat-record.mjs --root <dir> --owner <work-item>
|
|
24
25
|
// node chat-record.mjs --root <dir> --add-task --chat <n> --branch <b> [--repo <r>] [--work-item <id>]
|
|
25
26
|
// node chat-record.mjs --root <dir> --remove-task --chat <n> (--work-item <id> | --branch <b>) [--repo <r>]
|
|
26
27
|
//
|
|
@@ -31,11 +32,14 @@
|
|
|
31
32
|
// should read `action`. --whoami prints this chat's record name by matching
|
|
32
33
|
// $CLAUDE_CODE_SESSION_ID against the records' sessionId, nothing and exit
|
|
33
34
|
// 1 when there is no match: the `Chat record:` hook line can be missing
|
|
34
|
-
// after context compaction, and this is the recovery path.
|
|
35
|
+
// after context compaction, and this is the recovery path. --owner prints
|
|
36
|
+
// the chat a work item's open task belongs to as `{ chat, branch, repos }`,
|
|
37
|
+
// and nothing with exit 1 when no record lists it — /start-work uses it to
|
|
38
|
+
// offer adopting a task another chat started (gh:188).
|
|
35
39
|
|
|
36
40
|
import {
|
|
37
41
|
readFileSync, writeFileSync, existsSync, mkdirSync,
|
|
38
|
-
readdirSync, renameSync, rmSync, realpathSync,
|
|
42
|
+
readdirSync, renameSync, rmSync, realpathSync, statSync,
|
|
39
43
|
} from 'node:fs';
|
|
40
44
|
import { join, resolve } from 'node:path';
|
|
41
45
|
import { homedir } from 'node:os';
|
|
@@ -241,6 +245,36 @@ function whoami(root, { env = process.env } = {}) {
|
|
|
241
245
|
return mine ? mine.chat : null;
|
|
242
246
|
}
|
|
243
247
|
|
|
248
|
+
// The chat a work item's open task belongs to (gh:188). /start-work on an
|
|
249
|
+
// issue that already has a task needs the owner before it can ask the
|
|
250
|
+
// operator whether to adopt the task or leave it — and the record name
|
|
251
|
+
// returned here is the session-registry name SendMessage reaches a chat
|
|
252
|
+
// by, so it doubles as the address for "ask the owner". Records are scanned
|
|
253
|
+
// most-recently-modified first, so a stray duplicate answers with the chat
|
|
254
|
+
// that touched its record last. Null when no record lists the work item.
|
|
255
|
+
function findOwner(root, workItem) {
|
|
256
|
+
if (!workItem) return null;
|
|
257
|
+
const dir = chatsDir(root);
|
|
258
|
+
let names;
|
|
259
|
+
try {
|
|
260
|
+
names = readdirSync(dir).filter((n) => n.endsWith('.json'));
|
|
261
|
+
} catch {
|
|
262
|
+
return null; // no records at all
|
|
263
|
+
}
|
|
264
|
+
names.sort((a, b) => statSync(join(dir, b)).mtimeMs - statSync(join(dir, a)).mtimeMs);
|
|
265
|
+
for (const name of names) {
|
|
266
|
+
const rec = readRecord(root, name.slice(0, -5));
|
|
267
|
+
const hits = rec && Array.isArray(rec.tasks)
|
|
268
|
+
? rec.tasks.filter((t) => (t.workItem ?? null) === workItem)
|
|
269
|
+
: [];
|
|
270
|
+
if (hits.length === 0) continue;
|
|
271
|
+
// One issue is one task: a shared branch across repos. All matching
|
|
272
|
+
// entries are that task's repos.
|
|
273
|
+
return { chat: rec.chat, branch: hits[0].branch, repos: hits.map((t) => t.repo) };
|
|
274
|
+
}
|
|
275
|
+
return null;
|
|
276
|
+
}
|
|
277
|
+
|
|
244
278
|
// Scope is what a chat declares it owns. The Aug 26 coordination burst had
|
|
245
279
|
// sessions declaring this by hand in chat messages; recording it makes it
|
|
246
280
|
// answerable without asking.
|
|
@@ -262,6 +296,7 @@ function parseArgs(argv) {
|
|
|
262
296
|
if (a === '--read') { args.mode = 'read'; args.chat = rest[++i]; continue; }
|
|
263
297
|
if (a === '--reconcile') { args.mode = 'reconcile'; continue; }
|
|
264
298
|
if (a === '--whoami') { args.mode = 'whoami'; continue; }
|
|
299
|
+
if (a === '--owner') { args.mode = 'owner'; args.workItem = rest[++i]; continue; }
|
|
265
300
|
if (a === '--add-task') { args.mode = 'add-task'; continue; }
|
|
266
301
|
if (a === '--remove-task') { args.mode = 'remove-task'; continue; }
|
|
267
302
|
if (a === '--chat') { args.chat = rest[++i]; continue; }
|
|
@@ -272,7 +307,10 @@ function parseArgs(argv) {
|
|
|
272
307
|
if (a === '--name') { args.name = rest[++i]; continue; }
|
|
273
308
|
throw new Error(`unknown argument: ${a}`);
|
|
274
309
|
}
|
|
275
|
-
if (!args.mode) throw new Error('one of --list, --read <chat>, --reconcile, --whoami is required');
|
|
310
|
+
if (!args.mode) throw new Error('one of --list, --read <chat>, --reconcile, --whoami, --owner <work-item> is required');
|
|
311
|
+
if (args.mode === 'owner' && !args.workItem) {
|
|
312
|
+
throw new Error('--owner requires a work item');
|
|
313
|
+
}
|
|
276
314
|
if (args.mode === 'reconcile' && (!args.sessionId || !args.name)) {
|
|
277
315
|
throw new Error('--reconcile requires --session-id and --name');
|
|
278
316
|
}
|
|
@@ -296,6 +334,15 @@ function main() {
|
|
|
296
334
|
process.stdout.write(`${name}\n`);
|
|
297
335
|
return;
|
|
298
336
|
}
|
|
337
|
+
if (args.mode === 'owner') {
|
|
338
|
+
// Same exit-1 contract as --whoami: no owning chat prints nothing, and
|
|
339
|
+
// the caller (a /start-work adoption check) treats that as "no task
|
|
340
|
+
// exists for this issue yet".
|
|
341
|
+
const owner = findOwner(args.root, args.workItem);
|
|
342
|
+
if (owner === null) process.exit(1);
|
|
343
|
+
process.stdout.write(`${JSON.stringify(owner, null, 2)}\n`);
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
299
346
|
let out;
|
|
300
347
|
if (args.mode === 'list') out = listRecords(args.root);
|
|
301
348
|
else if (args.mode === 'read') out = readRecord(args.root, args.chat);
|
|
@@ -319,5 +366,5 @@ if (isMainModule(import.meta.url)) {
|
|
|
319
366
|
export {
|
|
320
367
|
recordPath, drawerPath, emptyRecord, readRecord, writeRecord,
|
|
321
368
|
listRecords, reconcile, parseArgs, readSessionRegistry, resolveChatName,
|
|
322
|
-
addTask, removeTask, setScope, whoami, CHATS_DIR,
|
|
369
|
+
addTask, removeTask, setScope, whoami, findOwner, CHATS_DIR,
|
|
323
370
|
};
|