@lumoai/cli 1.11.0 → 1.17.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/README.md +13 -13
- package/assets/skill/SKILL.md +111 -0
- package/assets/skill/references/artifacts-figma.md +124 -0
- package/assets/skill/references/docs.md +306 -0
- package/assets/skill/references/memory.md +69 -0
- package/assets/skill/references/milestones.md +244 -0
- package/assets/skill/references/onboarding.md +102 -0
- package/assets/skill/references/sessions.md +142 -0
- package/assets/skill/references/sprints.md +157 -0
- package/assets/skill/references/task-context.md +109 -0
- package/assets/skill/references/tasks.md +205 -0
- package/dist/cli/src/commands/milestone-archive.js +60 -0
- package/dist/cli/src/commands/milestone-list.js +24 -5
- package/dist/cli/src/commands/milestone-move.js +84 -0
- package/dist/cli/src/commands/milestone-reorder.js +72 -0
- package/dist/cli/src/commands/milestone-show.js +35 -0
- package/dist/cli/src/commands/milestone-unarchive.js +60 -0
- package/dist/cli/src/commands/session-wrap.js +5 -2
- package/dist/cli/src/commands/setup.js +50 -22
- package/dist/cli/src/commands/sprint-show.js +32 -3
- package/dist/cli/src/commands/task-context.js +4 -0
- package/dist/cli/src/commands/task-update.js +12 -4
- package/dist/cli/src/commands/wrap/blocked-prompt-section.js +64 -0
- package/dist/cli/src/index.js +31 -2
- package/dist/cli/src/lib/failure-summary-api.js +43 -0
- package/dist/cli/src/lib/hook-runner.js +1 -0
- package/dist/cli/src/lib/milestone-reorder.js +92 -0
- package/dist/cli/src/lib/resolve.js +17 -6
- package/package.json +1 -1
- package/assets/skill.md +0 -1333
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.fetchFailureSummary = fetchFailureSummary;
|
|
4
|
+
exports.markTaskBlocked = markTaskBlocked;
|
|
5
|
+
const api_1 = require("./api");
|
|
6
|
+
function base(creds) {
|
|
7
|
+
return (0, api_1.trimTrailingSlash)((0, api_1.resolveAuthedApiUrl)(creds.apiUrl));
|
|
8
|
+
}
|
|
9
|
+
/** GET the blocked-tag prompt draft for the session. Throws on transport / non-200. */
|
|
10
|
+
async function fetchFailureSummary(creds, sessionId) {
|
|
11
|
+
const url = `${base(creds)}/api/sessions/${encodeURIComponent(sessionId)}/failure-summary`;
|
|
12
|
+
const res = await fetch(url, {
|
|
13
|
+
headers: { Authorization: `Bearer ${creds.token}` },
|
|
14
|
+
});
|
|
15
|
+
if (res.status === 401)
|
|
16
|
+
throw new Error('API key invalid or revoked. Run `lumo auth login`.');
|
|
17
|
+
if (!res.ok)
|
|
18
|
+
throw new Error(`failure summary fetch failed (HTTP ${res.status})`);
|
|
19
|
+
return (await res.json());
|
|
20
|
+
}
|
|
21
|
+
/** POST to attach the `blocked` tag to the session's bound task. Throws the server message on non-201. */
|
|
22
|
+
async function markTaskBlocked(creds, sessionId) {
|
|
23
|
+
const url = `${base(creds)}/api/sessions/${encodeURIComponent(sessionId)}/mark-blocked`;
|
|
24
|
+
const res = await fetch(url, {
|
|
25
|
+
method: 'POST',
|
|
26
|
+
headers: { Authorization: `Bearer ${creds.token}` },
|
|
27
|
+
});
|
|
28
|
+
if (res.status === 401)
|
|
29
|
+
throw new Error('API key invalid or revoked. Run `lumo auth login`.');
|
|
30
|
+
if (res.status !== 201) {
|
|
31
|
+
let serverMsg = null;
|
|
32
|
+
try {
|
|
33
|
+
const errBody = (await res.json());
|
|
34
|
+
if (typeof errBody.error === 'string')
|
|
35
|
+
serverMsg = errBody.error;
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
// body wasn't JSON
|
|
39
|
+
}
|
|
40
|
+
throw new Error(serverMsg ?? `mark blocked failed (HTTP ${res.status})`);
|
|
41
|
+
}
|
|
42
|
+
return (await res.json());
|
|
43
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveOrderedIds = resolveOrderedIds;
|
|
4
|
+
exports.computeMoveOrder = computeMoveOrder;
|
|
5
|
+
function findRef(ref, milestones) {
|
|
6
|
+
// Try by id first (exact match — milestone ids are cuids, unique).
|
|
7
|
+
const byId = milestones.find(m => m.id === ref);
|
|
8
|
+
if (byId)
|
|
9
|
+
return { kind: 'found', ref: byId };
|
|
10
|
+
// Then by name (case-insensitive). Names are NOT unique within a project
|
|
11
|
+
// (only slug is), so a name can match more than one milestone.
|
|
12
|
+
const needle = ref.trim().toLowerCase();
|
|
13
|
+
const hits = milestones.filter(m => m.name.toLowerCase() === needle);
|
|
14
|
+
if (hits.length === 0)
|
|
15
|
+
return { kind: 'not-found' };
|
|
16
|
+
if (hits.length === 1)
|
|
17
|
+
return { kind: 'found', ref: hits[0] };
|
|
18
|
+
return { kind: 'ambiguous', candidates: hits };
|
|
19
|
+
}
|
|
20
|
+
function ambiguousError(ref, candidates) {
|
|
21
|
+
const ids = candidates.map(c => c.id).join(', ');
|
|
22
|
+
return `ambiguous milestone name "${ref}" matches ${candidates.length} milestones; re-run with the id: ${ids}`;
|
|
23
|
+
}
|
|
24
|
+
/** Milestones in current display order (sortOrder asc, stable). */
|
|
25
|
+
function sorted(milestones) {
|
|
26
|
+
return [...milestones].sort((a, b) => a.sortOrder - b.sortOrder);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Resolve a full list of milestone refs (name or UUID) to ids in the given
|
|
30
|
+
* order. The list must name EVERY milestone in the project exactly once.
|
|
31
|
+
*/
|
|
32
|
+
function resolveOrderedIds(refs, milestones) {
|
|
33
|
+
const ids = [];
|
|
34
|
+
const seen = new Set();
|
|
35
|
+
for (const ref of refs) {
|
|
36
|
+
const found = findRef(ref, milestones);
|
|
37
|
+
if (found.kind === 'not-found') {
|
|
38
|
+
return { ok: false, error: `unknown milestone: "${ref}"` };
|
|
39
|
+
}
|
|
40
|
+
if (found.kind === 'ambiguous') {
|
|
41
|
+
return { ok: false, error: ambiguousError(ref, found.candidates) };
|
|
42
|
+
}
|
|
43
|
+
const match = found.ref;
|
|
44
|
+
if (seen.has(match.id)) {
|
|
45
|
+
return { ok: false, error: `duplicate milestone: "${ref}"` };
|
|
46
|
+
}
|
|
47
|
+
seen.add(match.id);
|
|
48
|
+
ids.push(match.id);
|
|
49
|
+
}
|
|
50
|
+
if (ids.length !== milestones.length) {
|
|
51
|
+
const missing = milestones
|
|
52
|
+
.filter(m => !seen.has(m.id))
|
|
53
|
+
.map(m => m.name);
|
|
54
|
+
return {
|
|
55
|
+
ok: false,
|
|
56
|
+
error: `incomplete order: list every milestone. Missing: ${missing.join(', ')}`,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
return { ok: true, orderedIds: ids };
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Compute the full orderedIds after moving `moveRef` immediately before/after
|
|
63
|
+
* `targetRef` in the project's current order.
|
|
64
|
+
*/
|
|
65
|
+
function computeMoveOrder(moveRef, targetRef, position, milestones) {
|
|
66
|
+
const moveFound = findRef(moveRef, milestones);
|
|
67
|
+
if (moveFound.kind === 'not-found') {
|
|
68
|
+
return { ok: false, error: `unknown milestone: "${moveRef}"` };
|
|
69
|
+
}
|
|
70
|
+
if (moveFound.kind === 'ambiguous') {
|
|
71
|
+
return { ok: false, error: ambiguousError(moveRef, moveFound.candidates) };
|
|
72
|
+
}
|
|
73
|
+
const targetFound = findRef(targetRef, milestones);
|
|
74
|
+
if (targetFound.kind === 'not-found') {
|
|
75
|
+
return { ok: false, error: `unknown milestone: "${targetRef}"` };
|
|
76
|
+
}
|
|
77
|
+
if (targetFound.kind === 'ambiguous') {
|
|
78
|
+
return { ok: false, error: ambiguousError(targetRef, targetFound.candidates) };
|
|
79
|
+
}
|
|
80
|
+
const move = moveFound.ref;
|
|
81
|
+
const target = targetFound.ref;
|
|
82
|
+
if (move.id === target.id) {
|
|
83
|
+
return { ok: false, error: 'cannot move a milestone relative to itself' };
|
|
84
|
+
}
|
|
85
|
+
const order = sorted(milestones)
|
|
86
|
+
.map(m => m.id)
|
|
87
|
+
.filter(id => id !== move.id);
|
|
88
|
+
const targetIndex = order.indexOf(target.id);
|
|
89
|
+
const insertAt = position === 'before' ? targetIndex : targetIndex + 1;
|
|
90
|
+
order.splice(insertAt, 0, move.id);
|
|
91
|
+
return { ok: true, orderedIds: order };
|
|
92
|
+
}
|
|
@@ -86,8 +86,7 @@ async function resolveTeamId(base, token, ref) {
|
|
|
86
86
|
throw new Error('workspace has multiple teams; pass --team <slug>.');
|
|
87
87
|
}
|
|
88
88
|
const needle = ref.trim().toLowerCase();
|
|
89
|
-
const match = teams.find(t => t.name.toLowerCase() === needle ||
|
|
90
|
-
t.identifier.toLowerCase() === needle);
|
|
89
|
+
const match = teams.find(t => t.name.toLowerCase() === needle || t.identifier.toLowerCase() === needle);
|
|
91
90
|
if (!match) {
|
|
92
91
|
throw new Error(`team "${ref}" not found. Try \`lumo team list\`.`);
|
|
93
92
|
}
|
|
@@ -160,11 +159,23 @@ async function resolveMilestoneId(base, token, identifier, projectRef) {
|
|
|
160
159
|
return { id: identifier, name: '', projectId: '' };
|
|
161
160
|
}
|
|
162
161
|
const projectId = await resolveProjectId(base, token, projectRef);
|
|
163
|
-
const { milestones } = await fetchJson(base, token, `/api/projects/${projectId}/milestones`);
|
|
162
|
+
const { milestones } = await fetchJson(base, token, `/api/projects/${projectId}/milestones?filter=all`);
|
|
163
|
+
// Exact id match first (milestone ids are cuids, unique).
|
|
164
|
+
const byId = milestones.find(m => m.id === identifier);
|
|
165
|
+
if (byId) {
|
|
166
|
+
return { id: byId.id, name: byId.name, projectId };
|
|
167
|
+
}
|
|
168
|
+
// Then by name (case-insensitive). Names are NOT unique within a project
|
|
169
|
+
// (only slug is), so reject an ambiguous name rather than silently picking
|
|
170
|
+
// the first match.
|
|
164
171
|
const needle = identifier.trim().toLowerCase();
|
|
165
|
-
const
|
|
166
|
-
if (
|
|
172
|
+
const hits = milestones.filter(m => m.name.toLowerCase() === needle);
|
|
173
|
+
if (hits.length === 0) {
|
|
167
174
|
throw new Error(`no milestone matches "${identifier}" in this project. Try \`lumo milestone list\`.`);
|
|
168
175
|
}
|
|
169
|
-
|
|
176
|
+
if (hits.length > 1) {
|
|
177
|
+
const ids = hits.map(h => h.id).join(', ');
|
|
178
|
+
throw new Error(`ambiguous milestone name "${identifier}" matches ${hits.length} milestones; re-run with the id: ${ids}`);
|
|
179
|
+
}
|
|
180
|
+
return { id: hits[0].id, name: hits[0].name, projectId };
|
|
170
181
|
}
|