felenova-backlog 0.1.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 +58 -0
- package/bin/backlog.js +2 -0
- package/dist/_core/criteria.d.ts +13 -0
- package/dist/_core/criteria.js +44 -0
- package/dist/_core/criteria.js.map +1 -0
- package/dist/_core/errors.d.ts +53 -0
- package/dist/_core/errors.js +91 -0
- package/dist/_core/errors.js.map +1 -0
- package/dist/_core/index.d.ts +24 -0
- package/dist/_core/index.js +27 -0
- package/dist/_core/index.js.map +1 -0
- package/dist/_core/loop.d.ts +73 -0
- package/dist/_core/loop.js +46 -0
- package/dist/_core/loop.js.map +1 -0
- package/dist/_core/markdown/items.d.ts +35 -0
- package/dist/_core/markdown/items.js +256 -0
- package/dist/_core/markdown/items.js.map +1 -0
- package/dist/_core/markdown/matrix.d.ts +20 -0
- package/dist/_core/markdown/matrix.js +147 -0
- package/dist/_core/markdown/matrix.js.map +1 -0
- package/dist/_core/markdown/track.d.ts +71 -0
- package/dist/_core/markdown/track.js +79 -0
- package/dist/_core/markdown/track.js.map +1 -0
- package/dist/_core/matrix-status.d.ts +9 -0
- package/dist/_core/matrix-status.js +31 -0
- package/dist/_core/matrix-status.js.map +1 -0
- package/dist/_core/matrix.d.ts +46 -0
- package/dist/_core/matrix.js +0 -0
- package/dist/_core/matrix.js.map +1 -0
- package/dist/_core/permissions.d.ts +14 -0
- package/dist/_core/permissions.js +87 -0
- package/dist/_core/permissions.js.map +1 -0
- package/dist/_core/promote.d.ts +20 -0
- package/dist/_core/promote.js +47 -0
- package/dist/_core/promote.js.map +1 -0
- package/dist/_core/ready.d.ts +8 -0
- package/dist/_core/ready.js +22 -0
- package/dist/_core/ready.js.map +1 -0
- package/dist/_core/review.d.ts +49 -0
- package/dist/_core/review.js +137 -0
- package/dist/_core/review.js.map +1 -0
- package/dist/_core/schema.d.ts +636 -0
- package/dist/_core/schema.js +304 -0
- package/dist/_core/schema.js.map +1 -0
- package/dist/_core/ship.d.ts +23 -0
- package/dist/_core/ship.js +31 -0
- package/dist/_core/ship.js.map +1 -0
- package/dist/_core/spec-diff.d.ts +19 -0
- package/dist/_core/spec-diff.js +25 -0
- package/dist/_core/spec-diff.js.map +1 -0
- package/dist/_core/status.d.ts +7 -0
- package/dist/_core/status.js +25 -0
- package/dist/_core/status.js.map +1 -0
- package/dist/_core/util/deep-equal.d.ts +8 -0
- package/dist/_core/util/deep-equal.js +31 -0
- package/dist/_core/util/deep-equal.js.map +1 -0
- package/dist/api.d.ts +46 -0
- package/dist/api.js +104 -0
- package/dist/api.js.map +1 -0
- package/dist/cache.d.ts +53 -0
- package/dist/cache.js +114 -0
- package/dist/cache.js.map +1 -0
- package/dist/commands/common.d.ts +59 -0
- package/dist/commands/common.js +94 -0
- package/dist/commands/common.js.map +1 -0
- package/dist/commands/export.d.ts +20 -0
- package/dist/commands/export.js +56 -0
- package/dist/commands/export.js.map +1 -0
- package/dist/commands/matrix.d.ts +16 -0
- package/dist/commands/matrix.js +48 -0
- package/dist/commands/matrix.js.map +1 -0
- package/dist/commands/next.d.ts +14 -0
- package/dist/commands/next.js +30 -0
- package/dist/commands/next.js.map +1 -0
- package/dist/commands/promote.d.ts +15 -0
- package/dist/commands/promote.js +172 -0
- package/dist/commands/promote.js.map +1 -0
- package/dist/commands/report.d.ts +18 -0
- package/dist/commands/report.js +45 -0
- package/dist/commands/report.js.map +1 -0
- package/dist/commands/ship.d.ts +15 -0
- package/dist/commands/ship.js +231 -0
- package/dist/commands/ship.js.map +1 -0
- package/dist/commands/status.d.ts +13 -0
- package/dist/commands/status.js +141 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/sync.d.ts +17 -0
- package/dist/commands/sync.js +90 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/conductor/registry.d.ts +45 -0
- package/dist/conductor/registry.js +164 -0
- package/dist/conductor/registry.js.map +1 -0
- package/dist/config.d.ts +47 -0
- package/dist/config.js +137 -0
- package/dist/config.js.map +1 -0
- package/dist/doctor.d.ts +28 -0
- package/dist/doctor.js +377 -0
- package/dist/doctor.js.map +1 -0
- package/dist/errors.d.ts +23 -0
- package/dist/errors.js +114 -0
- package/dist/errors.js.map +1 -0
- package/dist/export.d.ts +1 -0
- package/dist/export.js +9 -0
- package/dist/export.js.map +1 -0
- package/dist/import.d.ts +3 -0
- package/dist/import.js +89 -0
- package/dist/import.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +183 -0
- package/dist/index.js.map +1 -0
- package/dist/init/auth.d.ts +85 -0
- package/dist/init/auth.js +194 -0
- package/dist/init/auth.js.map +1 -0
- package/dist/init/claudemd.d.ts +16 -0
- package/dist/init/claudemd.js +62 -0
- package/dist/init/claudemd.js.map +1 -0
- package/dist/init/index.d.ts +47 -0
- package/dist/init/index.js +159 -0
- package/dist/init/index.js.map +1 -0
- package/dist/init/mcp.d.ts +18 -0
- package/dist/init/mcp.js +97 -0
- package/dist/init/mcp.js.map +1 -0
- package/dist/init/plugin.d.ts +60 -0
- package/dist/init/plugin.js +120 -0
- package/dist/init/plugin.js.map +1 -0
- package/dist/init/scaffold.d.ts +19 -0
- package/dist/init/scaffold.js +46 -0
- package/dist/init/scaffold.js.map +1 -0
- package/dist/init/templates.d.ts +48 -0
- package/dist/init/templates.js +147 -0
- package/dist/init/templates.js.map +1 -0
- package/dist/plugin/.claude-plugin/plugin.json +11 -0
- package/dist/plugin/README.md +117 -0
- package/dist/plugin/commands/next.md +28 -0
- package/dist/plugin/commands/promote.md +30 -0
- package/dist/plugin/commands/report.md +26 -0
- package/dist/plugin/commands/ship.md +30 -0
- package/dist/plugin/skills/backlog-management/SKILL.md +63 -0
- package/dist/plugin/skills/backlog-query/SKILL.md +57 -0
- package/dist/plugin/skills/feature-kickoff/SKILL.md +112 -0
- package/dist/snapshot.d.ts +24 -0
- package/dist/snapshot.js +2 -0
- package/dist/snapshot.js.map +1 -0
- package/dist/token.d.ts +29 -0
- package/dist/token.js +99 -0
- package/dist/token.js.map +1 -0
- package/package.json +52 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { asArray, asRecord, CommandError, jsonResult, pad, runCommand, text, } from './common.js';
|
|
2
|
+
export async function runNext(io, flags) {
|
|
3
|
+
return runCommand('next', io, flags, async (ctx) => {
|
|
4
|
+
let query = '';
|
|
5
|
+
if (flags.limit !== undefined) {
|
|
6
|
+
if (!Number.isInteger(flags.limit) || flags.limit < 1) {
|
|
7
|
+
throw new CommandError(`--limit must be a positive whole number, not ${flags.limit}`);
|
|
8
|
+
}
|
|
9
|
+
query = `?limit=${flags.limit}`;
|
|
10
|
+
}
|
|
11
|
+
const payload = await ctx.client.get(`/v1/projects/${encodeURIComponent(ctx.project)}/next${query}`);
|
|
12
|
+
if (flags.json)
|
|
13
|
+
return jsonResult(payload);
|
|
14
|
+
const result = asRecord(payload);
|
|
15
|
+
const items = asArray(result.items);
|
|
16
|
+
const lines = [];
|
|
17
|
+
for (const entry of items) {
|
|
18
|
+
const item = asRecord(entry);
|
|
19
|
+
lines.push(`${pad(text(item.id), 12)}${pad(text(item.priority), 4)}${pad(text(item.domain), 14)}${text(item.title, '(untitled)')}`);
|
|
20
|
+
}
|
|
21
|
+
if (items.length === 0) {
|
|
22
|
+
lines.push(`nothing is ready — lock some criteria (${ctx.url}/lock-queue)`);
|
|
23
|
+
}
|
|
24
|
+
for (const warning of asArray(result.warnings)) {
|
|
25
|
+
lines.push(`warning: ${text(warning, 'a prerequisite does not resolve')}`);
|
|
26
|
+
}
|
|
27
|
+
return { ok: true, lines };
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=next.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"next.js","sourceRoot":"","sources":["../../src/commands/next.ts"],"names":[],"mappings":"AACA,OAAO,EACL,OAAO,EACP,QAAQ,EACR,YAAY,EAGZ,UAAU,EACV,GAAG,EACH,UAAU,EACV,IAAI,GACL,MAAM,aAAa,CAAC;AAerB,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,EAAa,EAAE,KAAgB;IAC3D,OAAO,UAAU,CAAC,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QACjD,IAAI,KAAK,GAAG,EAAE,CAAC;QACf,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;YAC9B,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,KAAK,GAAG,CAAC,EAAE,CAAC;gBACtD,MAAM,IAAI,YAAY,CAAC,gDAAgD,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;YACxF,CAAC;YACD,KAAK,GAAG,UAAU,KAAK,CAAC,KAAK,EAAE,CAAC;QAClC,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,GAAG,CAClC,gBAAgB,kBAAkB,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,KAAK,EAAE,CAC/D,CAAC;QACF,IAAI,KAAK,CAAC,IAAI;YAAE,OAAO,UAAU,CAAC,OAAO,CAAC,CAAC;QAE3C,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;QACjC,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACpC,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,KAAK,MAAM,KAAK,IAAI,KAAK,EAAE,CAAC;YAC1B,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;YAC7B,KAAK,CAAC,IAAI,CACR,GAAG,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,GAAG,GAAG,CAC3D,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,EACjB,EAAE,CACH,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,YAAY,CAAC,EAAE,CACrC,CAAC;QACJ,CAAC;QACD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,KAAK,CAAC,IAAI,CAAC,0CAA0C,GAAG,CAAC,GAAG,cAAc,CAAC,CAAC;QAC9E,CAAC;QACD,KAAK,MAAM,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC/C,KAAK,CAAC,IAAI,CAAC,YAAY,IAAI,CAAC,OAAO,EAAE,iCAAiC,CAAC,EAAE,CAAC,CAAC;QAC7E,CAAC;QACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;IAC7B,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { TargetFlags } from '../config.js';
|
|
2
|
+
import { type CommandIo, type CommandResult } from './common.js';
|
|
3
|
+
export interface PromoteFlags extends TargetFlags {
|
|
4
|
+
ids: string[];
|
|
5
|
+
/** `--track <slug>`; absent means the service builds the slug from the first item's title. */
|
|
6
|
+
track?: string | undefined;
|
|
7
|
+
type?: string | undefined;
|
|
8
|
+
/** The track's human name for `metadata.json`; defaults to `<base> — <first item title>`. */
|
|
9
|
+
name?: string | undefined;
|
|
10
|
+
plan?: string | undefined;
|
|
11
|
+
/** Skips the items read: with it, `promote` is exactly one HTTP call. */
|
|
12
|
+
priority?: string | undefined;
|
|
13
|
+
json?: boolean | undefined;
|
|
14
|
+
}
|
|
15
|
+
export declare function runPromote(io: CommandIo, flags: PromoteFlags): Promise<CommandResult>;
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { join, relative } from 'node:path';
|
|
3
|
+
import { PRIORITIES, renderTrackFiles, TRACK_FILENAMES, trackBaseName, } from '../_core/index.js';
|
|
4
|
+
import { addActiveTrack, dropNextRow } from '../conductor/registry.js';
|
|
5
|
+
import { asArray, asRecord, CommandError, jsonResult, renderFailure, runCommand, text, } from './common.js';
|
|
6
|
+
/**
|
|
7
|
+
* `backlog promote <ID…>` — the local half of `promote_items` (§6.2). The service owns the
|
|
8
|
+
* promotion; the CLI owns the files. Two things make the order matter:
|
|
9
|
+
*
|
|
10
|
+
* 1. **The directory is checked first.** A promotion the service accepted while the local
|
|
11
|
+
* write refuses leaves an item `promoted` with no track files — the worst state in the
|
|
12
|
+
* system. So when `--track` names a directory that already exists, nothing is sent at all.
|
|
13
|
+
* 2. **Nothing is written until every path is clear.** The three files are rendered in
|
|
14
|
+
* memory, the leaf directory is created with `recursive: false` so a race still fails,
|
|
15
|
+
* and only then does anything land on disk.
|
|
16
|
+
*/
|
|
17
|
+
const TRACK_TYPES = ['feature', 'infra', 'fix'];
|
|
18
|
+
/** `plugin_20260912` → `2026-09-12`. The slug's stamp *is* the promotion date (the service's). */
|
|
19
|
+
function createdFrom(slug) {
|
|
20
|
+
const stamp = /_(\d{4})(\d{2})(\d{2})$/.exec(slug);
|
|
21
|
+
if (stamp)
|
|
22
|
+
return `${stamp[1]}-${stamp[2]}-${stamp[3]}`;
|
|
23
|
+
return new Date().toISOString().slice(0, 10);
|
|
24
|
+
}
|
|
25
|
+
function highestPriority(items, refs) {
|
|
26
|
+
const wanted = new Set(refs);
|
|
27
|
+
let best = null;
|
|
28
|
+
for (const entry of items) {
|
|
29
|
+
const item = asRecord(entry);
|
|
30
|
+
if (typeof item.id !== 'string' || !wanted.has(item.id))
|
|
31
|
+
continue;
|
|
32
|
+
const rank = PRIORITIES.indexOf(text(item.priority, ''));
|
|
33
|
+
if (rank !== -1 && (best === null || rank < best))
|
|
34
|
+
best = rank;
|
|
35
|
+
}
|
|
36
|
+
return best === null ? null : PRIORITIES[best];
|
|
37
|
+
}
|
|
38
|
+
/** `plugin — the command set`: the track's own word, then what the first item is called. */
|
|
39
|
+
function trackName(slug, spec) {
|
|
40
|
+
const base = trackBaseName(slug);
|
|
41
|
+
const first = text(spec[0]?.title, '');
|
|
42
|
+
return first === '' ? base : `${base} — ${first}`;
|
|
43
|
+
}
|
|
44
|
+
function normaliseIds(ids) {
|
|
45
|
+
const seen = new Set();
|
|
46
|
+
const out = [];
|
|
47
|
+
for (const raw of ids) {
|
|
48
|
+
const ref = raw.trim().toUpperCase();
|
|
49
|
+
if (ref === '' || seen.has(ref))
|
|
50
|
+
continue;
|
|
51
|
+
seen.add(ref);
|
|
52
|
+
out.push(ref);
|
|
53
|
+
}
|
|
54
|
+
return out;
|
|
55
|
+
}
|
|
56
|
+
export async function runPromote(io, flags) {
|
|
57
|
+
const ids = normaliseIds(flags.ids ?? []);
|
|
58
|
+
if (ids.length === 0) {
|
|
59
|
+
return {
|
|
60
|
+
ok: false,
|
|
61
|
+
lines: ['promote needs at least one item ref — e.g. backlog promote PLG-001'],
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
const type = (flags.type ?? 'feature').trim().toLowerCase();
|
|
65
|
+
if (!TRACK_TYPES.includes(type)) {
|
|
66
|
+
return {
|
|
67
|
+
ok: false,
|
|
68
|
+
lines: [`unknown track type "${flags.type}" — allowed: ${TRACK_TYPES.join(', ')}`],
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
if (flags.priority !== undefined && !PRIORITIES.includes(flags.priority)) {
|
|
72
|
+
return {
|
|
73
|
+
ok: false,
|
|
74
|
+
lines: [`unknown priority "${flags.priority}" — allowed: ${PRIORITIES.join(', ')}`],
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
return runCommand('promote', io, flags, async (ctx) => {
|
|
78
|
+
const asked = flags.track?.trim();
|
|
79
|
+
// (1) the refusal that must happen before the service is told anything.
|
|
80
|
+
if (asked) {
|
|
81
|
+
const target = trackDir(ctx, asked);
|
|
82
|
+
if (existsSync(target)) {
|
|
83
|
+
return {
|
|
84
|
+
ok: false,
|
|
85
|
+
lines: [
|
|
86
|
+
`track ${asked} already exists at ${rel(ctx, target)} — pick another --track, or run: backlog sync ${asked}`,
|
|
87
|
+
],
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// The register does not carry a track priority, and `metadata.json` v3 needs one: the
|
|
92
|
+
// highest across the promoted items, read before the write so a refusal costs nothing.
|
|
93
|
+
const priority = flags.priority !== undefined
|
|
94
|
+
? flags.priority
|
|
95
|
+
: highestPriority(asArray(await ctx.client.get(`/v1/projects/${encodeURIComponent(ctx.project)}/items`)), ids);
|
|
96
|
+
const payload = await ctx.client.post(`/v1/projects/${encodeURIComponent(ctx.project)}/tracks`, {
|
|
97
|
+
ids,
|
|
98
|
+
...(asked ? { track_slug: asked } : {}),
|
|
99
|
+
type,
|
|
100
|
+
});
|
|
101
|
+
const detail = asRecord(payload);
|
|
102
|
+
const slug = text(asRecord(detail.track).slug, asked ?? '');
|
|
103
|
+
if (slug === '') {
|
|
104
|
+
throw new CommandError('the service promoted the items but returned no track slug — run: backlog status');
|
|
105
|
+
}
|
|
106
|
+
// (2) the same refusal again, for the slug the service chose. Nothing is written.
|
|
107
|
+
const dir = trackDir(ctx, slug);
|
|
108
|
+
if (existsSync(dir)) {
|
|
109
|
+
return {
|
|
110
|
+
ok: false,
|
|
111
|
+
lines: [
|
|
112
|
+
`${ids.join(', ')} promoted into ${slug}, but ${rel(ctx, dir)} already exists — nothing was written; the items are promoted on the service — fix the filesystem and re-run \`backlog sync ${slug}\``,
|
|
113
|
+
],
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
const files = renderTrackFiles(detail, {
|
|
117
|
+
name: flags.name?.trim() ||
|
|
118
|
+
trackName(slug, asArray(detail.spec).map((e) => asRecord(e))),
|
|
119
|
+
priority: priority ?? 'P1',
|
|
120
|
+
created: createdFrom(slug),
|
|
121
|
+
...(flags.plan?.trim() ? { planPath: flags.plan.trim() } : {}),
|
|
122
|
+
backlogIds: ids,
|
|
123
|
+
});
|
|
124
|
+
const written = [];
|
|
125
|
+
try {
|
|
126
|
+
mkdirSync(join(ctx.root, ctx.conductorDir, 'tracks'), { recursive: true });
|
|
127
|
+
// `recursive: false`: a second promoter that won the race gets EEXIST, not a silent merge.
|
|
128
|
+
mkdirSync(dir, { recursive: false });
|
|
129
|
+
for (const name of TRACK_FILENAMES) {
|
|
130
|
+
writeFileSync(join(dir, name), files[name], 'utf8');
|
|
131
|
+
written.push(rel(ctx, join(dir, name)));
|
|
132
|
+
}
|
|
133
|
+
written.push(rel(ctx, writeRegistry(ctx, slug, files, priority ?? 'P1', type, ids)));
|
|
134
|
+
}
|
|
135
|
+
catch (err) {
|
|
136
|
+
// The service half is done and is not idempotent: never re-POST. Say exactly what is
|
|
137
|
+
// on disk and hand the reader the one command that finishes the job.
|
|
138
|
+
return {
|
|
139
|
+
ok: false,
|
|
140
|
+
lines: [
|
|
141
|
+
`${ids.join(', ')} promoted into ${slug} on the service`,
|
|
142
|
+
...written.map((p) => `wrote ${p}`),
|
|
143
|
+
`but the local write failed: ${renderFailure(err, ctx.ctx)}`,
|
|
144
|
+
`the items are promoted on the service — fix the filesystem and re-run \`backlog sync ${slug}\``,
|
|
145
|
+
],
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
if (flags.json)
|
|
149
|
+
return jsonResult(payload);
|
|
150
|
+
return {
|
|
151
|
+
ok: true,
|
|
152
|
+
lines: [...written.map((p) => `wrote ${p}`), `promoted: ${ids.join(', ')} → ${slug}`],
|
|
153
|
+
};
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
function trackDir(ctx, slug) {
|
|
157
|
+
return join(ctx.root, ctx.conductorDir, 'tracks', slug);
|
|
158
|
+
}
|
|
159
|
+
function rel(ctx, path) {
|
|
160
|
+
return relative(ctx.root, path) || path;
|
|
161
|
+
}
|
|
162
|
+
/** Appends the Active row and drops the Next row this promotion consumed. */
|
|
163
|
+
function writeRegistry(ctx, slug, files, priority, type, ids) {
|
|
164
|
+
const path = join(ctx.root, ctx.conductorDir, 'tracks.md');
|
|
165
|
+
const current = existsSync(path) ? readFileSync(path, 'utf8') : null;
|
|
166
|
+
const name = JSON.parse(files['metadata.json']).name;
|
|
167
|
+
let updated = addActiveTrack(current, { track_id: slug, name, type, priority });
|
|
168
|
+
updated = dropNextRow(updated, slug, ids);
|
|
169
|
+
writeFileSync(path, updated, 'utf8');
|
|
170
|
+
return path;
|
|
171
|
+
}
|
|
172
|
+
//# sourceMappingURL=promote.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"promote.js","sourceRoot":"","sources":["../../src/commands/promote.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { TargetFlags } from '../config.js';
|
|
2
|
+
import { type CommandIo, type CommandResult } from './common.js';
|
|
3
|
+
/**
|
|
4
|
+
* `backlog report <track> <step> <status> [--note]` — the Evaluate-Loop half of the bridge
|
|
5
|
+
* (§6.2). The **vocabulary** is checked locally first, because a typo is the one refusal the
|
|
6
|
+
* CLI can diagnose without asking: a round trip to hear "BUILD is not a step" costs the user
|
|
7
|
+
* a network wait and tells them nothing the closed set would not have. Everything else —
|
|
8
|
+
* whether that transition is legal, whether the role may report, which refs the report moves
|
|
9
|
+
* to `in-progress` — is the service's call, and is never second-guessed here.
|
|
10
|
+
*/
|
|
11
|
+
export interface ReportFlags extends TargetFlags {
|
|
12
|
+
track: string;
|
|
13
|
+
step: string;
|
|
14
|
+
status: string;
|
|
15
|
+
note?: string | undefined;
|
|
16
|
+
json?: boolean | undefined;
|
|
17
|
+
}
|
|
18
|
+
export declare function runReport(io: CommandIo, flags: ReportFlags): Promise<CommandResult>;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { LOOP_STATUSES, LOOP_STEPS } from '../_core/index.js';
|
|
2
|
+
import { asArray, asRecord, jsonResult, runCommand, text, } from './common.js';
|
|
3
|
+
/** The closed sets live in core, so the CLI cannot drift from what the service accepts. */
|
|
4
|
+
function oneOf(raw, allowed, label) {
|
|
5
|
+
const candidate = raw
|
|
6
|
+
.trim()
|
|
7
|
+
.toUpperCase()
|
|
8
|
+
.replace(/[\s-]+/g, '_');
|
|
9
|
+
if (allowed.includes(candidate))
|
|
10
|
+
return { value: candidate };
|
|
11
|
+
return {
|
|
12
|
+
line: `unknown ${label} "${raw}" — allowed: ${allowed.join(', ')}`,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export async function runReport(io, flags) {
|
|
16
|
+
const step = oneOf(flags.step, LOOP_STEPS, 'step');
|
|
17
|
+
if ('line' in step)
|
|
18
|
+
return { ok: false, lines: [step.line] };
|
|
19
|
+
const status = oneOf(flags.status, LOOP_STATUSES, 'status');
|
|
20
|
+
if ('line' in status)
|
|
21
|
+
return { ok: false, lines: [status.line] };
|
|
22
|
+
return runCommand('report', io, flags, async (ctx) => {
|
|
23
|
+
const note = flags.note?.trim();
|
|
24
|
+
const payload = await ctx.client.post(`/v1/tracks/${encodeURIComponent(ctx.project)}/${encodeURIComponent(flags.track)}/progress`, { step: step.value, status: status.value, ...(note ? { note } : {}) });
|
|
25
|
+
if (flags.json)
|
|
26
|
+
return jsonResult(payload);
|
|
27
|
+
const result = asRecord(payload);
|
|
28
|
+
const report = asRecord(result.report);
|
|
29
|
+
const loop = asRecord(result.loop);
|
|
30
|
+
const lines = [
|
|
31
|
+
`${flags.track}: recorded ${text(report.step, step.value)} ${text(report.status, status.value)} at ${text(report.at, 'now')}`,
|
|
32
|
+
];
|
|
33
|
+
if (typeof report.note === 'string' && report.note !== '')
|
|
34
|
+
lines.push(`note: ${report.note}`);
|
|
35
|
+
lines.push(`loop: ${text(loop.loop_step, step.value)} ${text(loop.loop_status, status.value)}`);
|
|
36
|
+
const started = asArray(result.started).map((ref) => text(ref, '?'));
|
|
37
|
+
if (started.length > 0)
|
|
38
|
+
lines.push(`started: ${started.join(', ')}`);
|
|
39
|
+
if (asRecord(result.track).spec_drift === true) {
|
|
40
|
+
lines.push('spec drift: the items moved since promotion — run: backlog sync');
|
|
41
|
+
}
|
|
42
|
+
return { ok: true, lines };
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.js","sourceRoot":"","sources":["../../src/commands/report.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { TargetFlags } from '../config.js';
|
|
2
|
+
import { type CommandIo, type CommandResult } from './common.js';
|
|
3
|
+
export interface ShipFlags extends TargetFlags {
|
|
4
|
+
track: string;
|
|
5
|
+
commit?: string | undefined;
|
|
6
|
+
date?: string | undefined;
|
|
7
|
+
/** Fill `--commit` from `git rev-parse --short HEAD` and `--date` from today. */
|
|
8
|
+
auto?: boolean | undefined;
|
|
9
|
+
overrideReason?: string | undefined;
|
|
10
|
+
/** `PLG-001=1,3` — repeatable. */
|
|
11
|
+
criteriaDone?: string[] | undefined;
|
|
12
|
+
note?: string | undefined;
|
|
13
|
+
json?: boolean | undefined;
|
|
14
|
+
}
|
|
15
|
+
export declare function runShip(io: CommandIo, flags: ShipFlags): Promise<CommandResult>;
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { join, relative } from 'node:path';
|
|
4
|
+
import { ROLES } from '../_core/index.js';
|
|
5
|
+
import { ApiError } from '../api.js';
|
|
6
|
+
import { addRecentCompletion, setTrackStatus } from '../conductor/registry.js';
|
|
7
|
+
import { asArray, asRecord, jsonResult, runCommand, text, } from './common.js';
|
|
8
|
+
/**
|
|
9
|
+
* `backlog ship <track> --commit <sha> --date <YYYY-MM-DD>` — the gate is the service's
|
|
10
|
+
* (§5.6); the CLI's job is the local half of the §6 shipment sweep, and the one refusal
|
|
11
|
+
* PLG-001 pins to a byte: an Engineer trying to ship gets **one sentence and nothing else**,
|
|
12
|
+
* and no file is touched. So every local write happens strictly after a 2xx.
|
|
13
|
+
*/
|
|
14
|
+
const ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;
|
|
15
|
+
/** `PLG-001=1,3` → `{ 'PLG-001': [1, 3] }`. A malformed pair is a local refusal. */
|
|
16
|
+
function parseCriteriaDone(pairs) {
|
|
17
|
+
const out = {};
|
|
18
|
+
for (const pair of pairs) {
|
|
19
|
+
const at = pair.indexOf('=');
|
|
20
|
+
const ref = at === -1 ? '' : pair.slice(0, at).trim().toUpperCase();
|
|
21
|
+
const numbers = at === -1 ? [] : pair.slice(at + 1).split(',');
|
|
22
|
+
const parsed = numbers.map((n) => Number.parseInt(n.trim(), 10));
|
|
23
|
+
if (ref === '' || parsed.length === 0 || parsed.some((n) => !Number.isInteger(n) || n < 1)) {
|
|
24
|
+
return {
|
|
25
|
+
line: `--criteria-done wants <REF>=<n,n> (e.g. PLG-001=1,3) — got "${pair}"`,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
out[ref] = parsed;
|
|
29
|
+
}
|
|
30
|
+
return { value: out };
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* The role, for the sentence. The service's own `forbidden` message already names it
|
|
34
|
+
* (`role engineer may not ship_track`), so prefer that and spend no extra request; only when
|
|
35
|
+
* it does not, ask `/v1/me` for the caller's role **on the configured project**.
|
|
36
|
+
*/
|
|
37
|
+
async function roleFor(ctx, err) {
|
|
38
|
+
const named = /\brole\s+([a-z_]+)\b/i.exec(err.message);
|
|
39
|
+
if (named && ROLES.includes(named[1].toLowerCase())) {
|
|
40
|
+
return named[1].toLowerCase();
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
const me = asRecord(await ctx.client.get('/v1/me'));
|
|
44
|
+
for (const entry of asArray(me.projects)) {
|
|
45
|
+
const project = asRecord(entry);
|
|
46
|
+
if (project.slug === ctx.project)
|
|
47
|
+
return text(project.role, '') || null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// The sentence must still be one sentence: a second failure is not worth reporting.
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
function engineerSentence(project, role) {
|
|
56
|
+
return role === null
|
|
57
|
+
? `ship_track is Product+; you are not allowed to ship on ${project}`
|
|
58
|
+
: `ship_track is Product+; you are ${role} on ${project}`;
|
|
59
|
+
}
|
|
60
|
+
export async function runShip(io, flags) {
|
|
61
|
+
const commit = flags.commit?.trim();
|
|
62
|
+
const date = flags.date?.trim();
|
|
63
|
+
if (!flags.auto && (!commit || !date)) {
|
|
64
|
+
return {
|
|
65
|
+
ok: false,
|
|
66
|
+
lines: [
|
|
67
|
+
'ship needs the shipment: pass --commit <sha> and --date <YYYY-MM-DD> (or --auto to read HEAD and today)',
|
|
68
|
+
],
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
if (date && !ISO_DATE.test(date)) {
|
|
72
|
+
return { ok: false, lines: [`--date wants YYYY-MM-DD — got "${flags.date}"`] };
|
|
73
|
+
}
|
|
74
|
+
const criteria = parseCriteriaDone(flags.criteriaDone ?? []);
|
|
75
|
+
if ('line' in criteria)
|
|
76
|
+
return { ok: false, lines: [criteria.line] };
|
|
77
|
+
return runCommand('ship', io, flags, async (ctx) => {
|
|
78
|
+
const shipment = resolveShipment(ctx, commit, date, Boolean(flags.auto));
|
|
79
|
+
if ('line' in shipment)
|
|
80
|
+
return { ok: false, lines: [shipment.line] };
|
|
81
|
+
const reason = flags.overrideReason?.trim();
|
|
82
|
+
const body = {
|
|
83
|
+
commit: shipment.commit,
|
|
84
|
+
production_date: shipment.date,
|
|
85
|
+
...(reason ? { override_reason: reason } : {}),
|
|
86
|
+
...(Object.keys(criteria.value).length > 0 ? { criteria_done: criteria.value } : {}),
|
|
87
|
+
};
|
|
88
|
+
let payload;
|
|
89
|
+
try {
|
|
90
|
+
payload = await ctx.client.post(`/v1/tracks/${encodeURIComponent(ctx.project)}/${encodeURIComponent(flags.track)}/ship`, body);
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
// The criterion, to the byte: one sentence, no JSON, no status code, no file touched.
|
|
94
|
+
if (err instanceof ApiError && err.code === 'forbidden') {
|
|
95
|
+
return { ok: false, lines: [engineerSentence(ctx.project, await roleFor(ctx, err))] };
|
|
96
|
+
}
|
|
97
|
+
throw err;
|
|
98
|
+
}
|
|
99
|
+
const detail = asRecord(payload);
|
|
100
|
+
const refs = asArray(asRecord(detail.track).backlog_ids).map((r) => text(r, '?'));
|
|
101
|
+
const lines = [
|
|
102
|
+
`shipped ${flags.track}: ${refs.length === 0 ? '(no items)' : refs.join(', ')}`,
|
|
103
|
+
`commit ${shipment.commit} · production ${shipment.date}`,
|
|
104
|
+
...asArray(detail.warnings).map((row) => `⚠ ${text(row, '?')} is not verified (permissive)`),
|
|
105
|
+
];
|
|
106
|
+
if (flags.json)
|
|
107
|
+
return jsonResult(payload);
|
|
108
|
+
lines.push(...sweepConductor(ctx, flags, shipment, refs));
|
|
109
|
+
lines.push(...sweepRegister(ctx, shipment, refs));
|
|
110
|
+
return { ok: true, lines };
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
/** `--auto` reads HEAD and today; both are printed, so a wrong guess is visible. */
|
|
114
|
+
function resolveShipment(ctx, commit, date, auto) {
|
|
115
|
+
if (commit && date)
|
|
116
|
+
return { commit, date };
|
|
117
|
+
if (!auto)
|
|
118
|
+
return { line: 'ship needs --commit and --date' };
|
|
119
|
+
let head = commit;
|
|
120
|
+
if (!head) {
|
|
121
|
+
try {
|
|
122
|
+
// The only place the CLI shells out, and only under `--auto`.
|
|
123
|
+
head = execFileSync('git', ['rev-parse', '--short', 'HEAD'], {
|
|
124
|
+
cwd: ctx.root,
|
|
125
|
+
encoding: 'utf8',
|
|
126
|
+
}).trim();
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
return { line: 'could not read HEAD — pass --commit <sha> instead of --auto' };
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return { commit: head, date: date ?? new Date().toISOString().slice(0, 10) };
|
|
133
|
+
}
|
|
134
|
+
/** §6 as it applies to conductor: the track's metadata, its registry row, Recent Completions. */
|
|
135
|
+
function sweepConductor(ctx, flags, shipment, refs) {
|
|
136
|
+
const lines = [];
|
|
137
|
+
const dir = join(ctx.root, ctx.conductorDir, 'tracks', flags.track);
|
|
138
|
+
const metadataPath = join(dir, 'metadata.json');
|
|
139
|
+
if (existsSync(metadataPath)) {
|
|
140
|
+
const doc = JSON.parse(readFileSync(metadataPath, 'utf8'));
|
|
141
|
+
// Four fields, and not one more: `loop_state` is the Evaluate-Loop's and conductor is not
|
|
142
|
+
// modified by this track (§7.3).
|
|
143
|
+
doc.status = 'done';
|
|
144
|
+
doc.completed = shipment.date;
|
|
145
|
+
doc.shipped_commit = shipment.commit;
|
|
146
|
+
doc.production_date = shipment.date;
|
|
147
|
+
writeFileSync(metadataPath, `${JSON.stringify(doc, null, 2)}\n`, 'utf8');
|
|
148
|
+
lines.push(`wrote ${relative(ctx.root, metadataPath)}`);
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
lines.push(`no ${relative(ctx.root, metadataPath)} — the track files are not in this repo`);
|
|
152
|
+
}
|
|
153
|
+
const tracksPath = join(ctx.root, ctx.conductorDir, 'tracks.md');
|
|
154
|
+
if (existsSync(tracksPath)) {
|
|
155
|
+
const updated = setTrackStatus(readFileSync(tracksPath, 'utf8'), flags.track, 'done');
|
|
156
|
+
if (updated === null) {
|
|
157
|
+
lines.push(`${relative(ctx.root, tracksPath)} has no row for ${flags.track} — left alone`);
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
writeFileSync(tracksPath, updated, 'utf8');
|
|
161
|
+
lines.push(`wrote ${relative(ctx.root, tracksPath)}`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
const indexPath = join(ctx.root, ctx.conductorDir, 'index.md');
|
|
165
|
+
if (existsSync(indexPath)) {
|
|
166
|
+
const notes = flags.note?.trim() || `${refs.join(', ')} shipped in ${shipment.commit}`;
|
|
167
|
+
const updated = addRecentCompletion(readFileSync(indexPath, 'utf8'), {
|
|
168
|
+
track: flags.track,
|
|
169
|
+
date: shipment.date,
|
|
170
|
+
notes,
|
|
171
|
+
});
|
|
172
|
+
if (updated === null) {
|
|
173
|
+
lines.push(`${relative(ctx.root, indexPath)} has no "## Recent Completions" table (or already lists ${flags.track}) — left alone`);
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
writeFileSync(indexPath, updated, 'utf8');
|
|
177
|
+
lines.push(`wrote ${relative(ctx.root, indexPath)}`);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return lines;
|
|
181
|
+
}
|
|
182
|
+
const FRONTMATTER_SWEEP = ['status', 'shipped_commit', 'production_date', 'updated'];
|
|
183
|
+
/**
|
|
184
|
+
* §6 steps 2–3 only apply where the repo still holds the markdown register. In a consumer
|
|
185
|
+
* repo it does not — the items live on the service — and saying so is better than silence.
|
|
186
|
+
*
|
|
187
|
+
* Where it does exist, the edit is **surgical**: four frontmatter values by line, and the body
|
|
188
|
+
* is not re-serialised, so a human's formatting survives. Reconciling the criteria prose is
|
|
189
|
+
* the one step §6 calls out as the most-skipped, and it is a human's, not this command's.
|
|
190
|
+
*/
|
|
191
|
+
function sweepRegister(ctx, shipment, refs) {
|
|
192
|
+
const itemsDir = join(ctx.root, 'docs', 'backlog', 'items');
|
|
193
|
+
if (!existsSync(itemsDir)) {
|
|
194
|
+
return [
|
|
195
|
+
'docs/backlog/items/ is not in this repo — the item register is service-side; nothing to sweep',
|
|
196
|
+
];
|
|
197
|
+
}
|
|
198
|
+
const lines = [];
|
|
199
|
+
const values = {
|
|
200
|
+
status: 'shipped',
|
|
201
|
+
shipped_commit: shipment.commit,
|
|
202
|
+
production_date: shipment.date,
|
|
203
|
+
updated: shipment.date,
|
|
204
|
+
};
|
|
205
|
+
for (const ref of refs) {
|
|
206
|
+
const path = join(itemsDir, `${ref}.md`);
|
|
207
|
+
if (!existsSync(path)) {
|
|
208
|
+
lines.push(`${ref}: no ${relative(ctx.root, path)} — reconcile it in the register`);
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
const body = readFileSync(path, 'utf8');
|
|
212
|
+
const end = body.indexOf('\n---', 4);
|
|
213
|
+
if (!body.startsWith('---\n') || end === -1) {
|
|
214
|
+
lines.push(`${ref}: ${relative(ctx.root, path)} has no frontmatter — left alone`);
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
const head = body.slice(0, end);
|
|
218
|
+
let updated = head;
|
|
219
|
+
for (const key of FRONTMATTER_SWEEP) {
|
|
220
|
+
const line = new RegExp(`^${key}:.*$`, 'm');
|
|
221
|
+
updated = line.test(updated)
|
|
222
|
+
? updated.replace(line, `${key}: ${values[key]}`)
|
|
223
|
+
: `${updated}\n${key}: ${values[key]}`;
|
|
224
|
+
}
|
|
225
|
+
writeFileSync(path, `${updated}${body.slice(end)}`, 'utf8');
|
|
226
|
+
lines.push(`wrote ${relative(ctx.root, path)}`);
|
|
227
|
+
}
|
|
228
|
+
lines.push('§6 still wants a human: reconcile the acceptance criteria with what actually shipped, and update docs/backlog/README.md');
|
|
229
|
+
return lines;
|
|
230
|
+
}
|
|
231
|
+
//# sourceMappingURL=ship.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ship.js","sourceRoot":"","sources":["../../src/commands/ship.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAClE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE/E,OAAO,EACL,OAAO,EACP,QAAQ,EAIR,UAAU,EACV,UAAU,EACV,IAAI,GACL,MAAM,aAAa,CAAC;AAErB;;;;;GAKG;AAEH,MAAM,QAAQ,GAAG,qBAAqB,CAAC;AAevC,oFAAoF;AACpF,SAAS,iBAAiB,CACxB,KAAwB;IAExB,MAAM,GAAG,GAA6B,EAAE,CAAC;IACzC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC7B,MAAM,GAAG,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACpE,MAAM,OAAO,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC/D,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;QACjE,IAAI,GAAG,KAAK,EAAE,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;YAC3F,OAAO;gBACL,IAAI,EAAE,+DAA+D,IAAI,GAAG;aAC7E,CAAC;QACJ,CAAC;QACD,GAAG,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC;IACpB,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;AACxB,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,OAAO,CAAC,GAAmB,EAAE,GAAa;IACvD,MAAM,KAAK,GAAG,uBAAuB,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACxD,IAAI,KAAK,IAAK,KAA2B,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC;QAC3E,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;IAChC,CAAC;IACD,IAAI,CAAC;QACH,MAAM,EAAE,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC;QACpD,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC;YACzC,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;YAChC,IAAI,OAAO,CAAC,IAAI,KAAK,GAAG,CAAC,OAAO;gBAAE,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC;QAC1E,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,oFAAoF;IACtF,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,gBAAgB,CAAC,OAAe,EAAE,IAAmB;IAC5D,OAAO,IAAI,KAAK,IAAI;QAClB,CAAC,CAAC,0DAA0D,OAAO,EAAE;QACrE,CAAC,CAAC,mCAAmC,IAAI,OAAO,OAAO,EAAE,CAAC;AAC9D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,EAAa,EAAE,KAAgB;IAC3D,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC;IACpC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC;IAChC,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACtC,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE;gBACL,yGAAyG;aAC1G;SACF,CAAC;IACJ,CAAC;IACD,IAAI,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACjC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,kCAAkC,KAAK,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;IACjF,CAAC;IACD,MAAM,QAAQ,GAAG,iBAAiB,CAAC,KAAK,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC;IAC7D,IAAI,MAAM,IAAI,QAAQ;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;IAErE,OAAO,UAAU,CAAC,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QACjD,MAAM,QAAQ,GAAG,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;QACzE,IAAI,MAAM,IAAI,QAAQ;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QAErE,MAAM,MAAM,GAAG,KAAK,CAAC,cAAc,EAAE,IAAI,EAAE,CAAC;QAC5C,MAAM,IAAI,GAAG;YACX,MAAM,EAAE,QAAQ,CAAC,MAAM;YACvB,eAAe,EAAE,QAAQ,CAAC,IAAI;YAC9B,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC9C,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACrF,CAAC;QAEF,IAAI,OAAgB,CAAC;QACrB,IAAI,CAAC;YACH,OAAO,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,CAC7B,cAAc,kBAAkB,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,kBAAkB,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,EACvF,IAAI,CACL,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,sFAAsF;YACtF,IAAI,GAAG,YAAY,QAAQ,IAAI,GAAG,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;gBACxD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,gBAAgB,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YACxF,CAAC;YACD,MAAM,GAAG,CAAC;QACZ,CAAC;QAED,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;QACjC,MAAM,IAAI,GAAG,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,WAAW,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;QAClF,MAAM,KAAK,GAAG;YACZ,WAAW,KAAK,CAAC,KAAK,KAAK,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;YAC/E,UAAU,QAAQ,CAAC,MAAM,iBAAiB,QAAQ,CAAC,IAAI,EAAE;YACzD,GAAG,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,+BAA+B,CAAC;SAC7F,CAAC;QAEF,IAAI,KAAK,CAAC,IAAI;YAAE,OAAO,UAAU,CAAC,OAAO,CAAC,CAAC;QAE3C,KAAK,CAAC,IAAI,CAAC,GAAG,cAAc,CAAC,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC;QAC1D,KAAK,CAAC,IAAI,CAAC,GAAG,aAAa,CAAC,GAAG,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC;QAClD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;IAC7B,CAAC,CAAC,CAAC;AACL,CAAC;AAOD,oFAAoF;AACpF,SAAS,eAAe,CACtB,GAAmB,EACnB,MAA0B,EAC1B,IAAwB,EACxB,IAAa;IAEb,IAAI,MAAM,IAAI,IAAI;QAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAC5C,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,IAAI,EAAE,gCAAgC,EAAE,CAAC;IAC7D,IAAI,IAAI,GAAG,MAAM,CAAC;IAClB,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,IAAI,CAAC;YACH,8DAA8D;YAC9D,IAAI,GAAG,YAAY,CAAC,KAAK,EAAE,CAAC,WAAW,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE;gBAC3D,GAAG,EAAE,GAAG,CAAC,IAAI;gBACb,QAAQ,EAAE,MAAM;aACjB,CAAC,CAAC,IAAI,EAAE,CAAC;QACZ,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,IAAI,EAAE,6DAA6D,EAAE,CAAC;QACjF,CAAC;IACH,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;AAC/E,CAAC;AAED,iGAAiG;AACjG,SAAS,cAAc,CACrB,GAAmB,EACnB,KAAgB,EAChB,QAAkB,EAClB,IAAuB;IAEvB,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,YAAY,EAAE,QAAQ,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;IAEpE,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;IAChD,IAAI,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QAC7B,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,YAAY,EAAE,MAAM,CAAC,CAA4B,CAAC;QACtF,0FAA0F;QAC1F,iCAAiC;QACjC,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC;QACpB,GAAG,CAAC,SAAS,GAAG,QAAQ,CAAC,IAAI,CAAC;QAC9B,GAAG,CAAC,cAAc,GAAG,QAAQ,CAAC,MAAM,CAAC;QACrC,GAAG,CAAC,eAAe,GAAG,QAAQ,CAAC,IAAI,CAAC;QACpC,aAAa,CAAC,YAAY,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACzE,KAAK,CAAC,IAAI,CAAC,SAAS,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,YAAY,CAAC,EAAE,CAAC,CAAC;IAC1D,CAAC;SAAM,CAAC;QACN,KAAK,CAAC,IAAI,CAAC,MAAM,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,YAAY,CAAC,yCAAyC,CAAC,CAAC;IAC9F,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC;IACjE,IAAI,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC3B,MAAM,OAAO,GAAG,cAAc,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,EAAE,KAAK,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QACtF,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YACrB,KAAK,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,UAAU,CAAC,mBAAmB,KAAK,CAAC,KAAK,eAAe,CAAC,CAAC;QAC7F,CAAC;aAAM,CAAC;YACN,aAAa,CAAC,UAAU,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;YAC3C,KAAK,CAAC,IAAI,CAAC,SAAS,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,UAAU,CAAC,EAAE,CAAC,CAAC;QACxD,CAAC;IACH,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC;IAC/D,IAAI,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC1B,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,QAAQ,CAAC,MAAM,EAAE,CAAC;QACvF,MAAM,OAAO,GAAG,mBAAmB,CAAC,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,EAAE;YACnE,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,IAAI,EAAE,QAAQ,CAAC,IAAI;YACnB,KAAK;SACN,CAAC,CAAC;QACH,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YACrB,KAAK,CAAC,IAAI,CACR,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,SAAS,CAAC,2DAA2D,KAAK,CAAC,KAAK,gBAAgB,CACvH,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,aAAa,CAAC,SAAS,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;YAC1C,KAAK,CAAC,IAAI,CAAC,SAAS,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,SAAS,CAAC,EAAE,CAAC,CAAC;QACvD,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,iBAAiB,GAAG,CAAC,QAAQ,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,SAAS,CAAU,CAAC;AAE9F;;;;;;;GAOG;AACH,SAAS,aAAa,CAAC,GAAmB,EAAE,QAAkB,EAAE,IAAuB;IACrF,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;IAC5D,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1B,OAAO;YACL,+FAA+F;SAChG,CAAC;IACJ,CAAC;IACD,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,MAAM,GAA2B;QACrC,MAAM,EAAE,SAAS;QACjB,cAAc,EAAE,QAAQ,CAAC,MAAM;QAC/B,eAAe,EAAE,QAAQ,CAAC,IAAI;QAC9B,OAAO,EAAE,QAAQ,CAAC,IAAI;KACvB,CAAC;IACF,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,EAAE,GAAG,GAAG,KAAK,CAAC,CAAC;QACzC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACtB,KAAK,CAAC,IAAI,CAAC,GAAG,GAAG,QAAQ,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,iCAAiC,CAAC,CAAC;YACpF,SAAS;QACX,CAAC;QACD,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACxC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QACrC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC;YAC5C,KAAK,CAAC,IAAI,CAAC,GAAG,GAAG,KAAK,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,kCAAkC,CAAC,CAAC;YAClF,SAAS;QACX,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QAChC,IAAI,OAAO,GAAG,IAAI,CAAC;QACnB,KAAK,MAAM,GAAG,IAAI,iBAAiB,EAAE,CAAC;YACpC,MAAM,IAAI,GAAG,IAAI,MAAM,CAAC,IAAI,GAAG,MAAM,EAAE,GAAG,CAAC,CAAC;YAC5C,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC;gBAC1B,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,GAAG,KAAK,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;gBACjD,CAAC,CAAC,GAAG,OAAO,KAAK,GAAG,KAAK,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;QAC3C,CAAC;QACD,aAAa,CAAC,IAAI,EAAE,GAAG,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QAC5D,KAAK,CAAC,IAAI,CAAC,SAAS,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;IAClD,CAAC;IACD,KAAK,CAAC,IAAI,CACR,yHAAyH,CAC1H,CAAC;IACF,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { TargetFlags } from '../config.js';
|
|
2
|
+
import { type CommandIo, type CommandResult } from './common.js';
|
|
3
|
+
/**
|
|
4
|
+
* `backlog status` — the one command that must answer when the service does not (PLG-001:
|
|
5
|
+
* "prints the last fetched view from `.backlog/cache.json` when the service is unreachable,
|
|
6
|
+
* marked cached"). Three reads, all of them `/v1`: the visible projects (for the role in the
|
|
7
|
+
* header), the active tracks, and the items. Every successful run leaves the cache warm; only
|
|
8
|
+
* `unreachable` falls back to it, and a failed run never touches it.
|
|
9
|
+
*/
|
|
10
|
+
export interface StatusFlags extends TargetFlags {
|
|
11
|
+
json?: boolean | undefined;
|
|
12
|
+
}
|
|
13
|
+
export declare function runStatus(io: CommandIo, flags: StatusFlags): Promise<CommandResult>;
|