tenbo-dashboard 0.10.0 → 0.10.2
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/bin/tenbo-dashboard.mjs +2 -6
- package/package.json +1 -1
- package/scripts/bin-output.test.ts +94 -0
- package/scripts/item.ts +25 -9
- package/scripts/itemCommandRegistry.d.mts +12 -0
- package/scripts/itemCommandRegistry.mjs +98 -0
- package/scripts/items.test.ts +20 -0
- package/scripts/items.ts +6 -3
- package/scripts/next-id.test.ts +1 -1
- package/scripts/next-id.ts +1 -5
- package/src/api/lib/itemProjection.ts +3 -0
- package/tsconfig.json +1 -1
package/bin/tenbo-dashboard.mjs
CHANGED
|
@@ -4,6 +4,7 @@ import { spawn } from 'node:child_process';
|
|
|
4
4
|
import { existsSync, readFileSync } from 'node:fs';
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
6
|
import path from 'node:path';
|
|
7
|
+
import { topLevelItemHelpLines } from '../scripts/itemCommandRegistry.mjs';
|
|
7
8
|
|
|
8
9
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
9
10
|
const root = path.resolve(__dirname, '..');
|
|
@@ -85,12 +86,7 @@ Usage:
|
|
|
85
86
|
tenbo-dashboard Launch the dashboard (http://localhost:5174)
|
|
86
87
|
tenbo-dashboard serve Same as bare invocation (also: 'start', 'dev')
|
|
87
88
|
tenbo-dashboard sync Refresh tenbo state after a change (metrics + init-check + validate, surfaces new findings)
|
|
88
|
-
|
|
89
|
-
tenbo-dashboard item set-status <id> <now|next|later|done|dropped> [--json]
|
|
90
|
-
tenbo-dashboard item add-note <id> "<note>" [--json]
|
|
91
|
-
tenbo-dashboard item verify <id> --status <not_required|pending_live|verified|failed>
|
|
92
|
-
[--evidence "<text>"] [--note "<text>"] [--json]
|
|
93
|
-
tenbo-dashboard item link-commit <id> <sha> [--json]
|
|
89
|
+
${topLevelItemHelpLines().join('\n')}
|
|
94
90
|
tenbo-dashboard items Query roadmap items [--status <status>]
|
|
95
91
|
[--verification <status>] [--goal <goal>]
|
|
96
92
|
[--type <type>] [--priority <p0|p1|p2|p3>]
|
package/package.json
CHANGED
|
@@ -30,6 +30,19 @@ beforeEach(() => {
|
|
|
30
30
|
afterEach(() => rmSync(dir, { recursive: true, force: true }));
|
|
31
31
|
|
|
32
32
|
describe('bin output', () => {
|
|
33
|
+
it('lists item completion in top-level help', () => {
|
|
34
|
+
const bin = path.resolve('bin/tenbo-dashboard.mjs');
|
|
35
|
+
const result = spawnSync(process.execPath, [bin, 'help'], {
|
|
36
|
+
cwd: dir,
|
|
37
|
+
encoding: 'utf8',
|
|
38
|
+
maxBuffer: 1024 * 1024,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
expect(result.status).toBe(0);
|
|
42
|
+
expect(result.stderr).toBe('');
|
|
43
|
+
expect(result.stdout).toContain('tenbo-dashboard item complete <id>');
|
|
44
|
+
});
|
|
45
|
+
|
|
33
46
|
it('flushes large JSON output before exiting', () => {
|
|
34
47
|
const bin = path.resolve('bin/tenbo-dashboard.mjs');
|
|
35
48
|
const result = spawnSync(process.execPath, [bin, 'items', '--status', 'done', '--json'], {
|
|
@@ -56,4 +69,85 @@ describe('bin output', () => {
|
|
|
56
69
|
{ id: 'ed-001', title: 'Large Output', status: 'done' },
|
|
57
70
|
]);
|
|
58
71
|
});
|
|
72
|
+
|
|
73
|
+
it('prints items help without running the query', () => {
|
|
74
|
+
const bin = path.resolve('bin/tenbo-dashboard.mjs');
|
|
75
|
+
const result = spawnSync(process.execPath, [bin, 'items', '--help'], {
|
|
76
|
+
cwd: dir,
|
|
77
|
+
encoding: 'utf8',
|
|
78
|
+
maxBuffer: 1024 * 1024,
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
expect(result.status).toBe(0);
|
|
82
|
+
expect(result.stderr).toBe('');
|
|
83
|
+
expect(result.stdout).toContain('Usage: tenbo-dashboard items');
|
|
84
|
+
expect(result.stdout).toContain('--fields <a,b>');
|
|
85
|
+
expect(result.stdout).not.toContain('ed-001');
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('prints item help without requiring an item id', () => {
|
|
89
|
+
const bin = path.resolve('bin/tenbo-dashboard.mjs');
|
|
90
|
+
const result = spawnSync(process.execPath, [bin, 'item', '--help'], {
|
|
91
|
+
cwd: dir,
|
|
92
|
+
encoding: 'utf8',
|
|
93
|
+
maxBuffer: 1024 * 1024,
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
expect(result.status).toBe(0);
|
|
97
|
+
expect(result.stderr).toBe('');
|
|
98
|
+
expect(result.stdout).toContain('Usage: tenbo-dashboard item <command> <id>');
|
|
99
|
+
expect(result.stdout).toContain('complete');
|
|
100
|
+
expect(result.stdout).toContain('link-commit');
|
|
101
|
+
expect(result.stdout).not.toContain('ed-001');
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('prints item complete help without requiring an item id', () => {
|
|
105
|
+
const bin = path.resolve('bin/tenbo-dashboard.mjs');
|
|
106
|
+
const result = spawnSync(process.execPath, [bin, 'item', 'complete', '--help'], {
|
|
107
|
+
cwd: dir,
|
|
108
|
+
encoding: 'utf8',
|
|
109
|
+
maxBuffer: 1024 * 1024,
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
expect(result.status).toBe(0);
|
|
113
|
+
expect(result.stderr).toBe('');
|
|
114
|
+
expect(result.stdout).toContain('Usage: tenbo-dashboard item complete <id>');
|
|
115
|
+
expect(result.stdout).toContain('--doc-update today|YYYY-MM-DD');
|
|
116
|
+
expect(result.stdout).toContain('--commit <sha>');
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('completes status, doc_update, verification, and commit through the bin wrapper', () => {
|
|
120
|
+
const bin = path.resolve('bin/tenbo-dashboard.mjs');
|
|
121
|
+
const result = spawnSync(process.execPath, [
|
|
122
|
+
bin,
|
|
123
|
+
'item',
|
|
124
|
+
'complete',
|
|
125
|
+
'ed-001',
|
|
126
|
+
'--evidence',
|
|
127
|
+
'npm test -- --run',
|
|
128
|
+
'--doc-update',
|
|
129
|
+
'today',
|
|
130
|
+
'--commit',
|
|
131
|
+
'abc123',
|
|
132
|
+
'--json',
|
|
133
|
+
], {
|
|
134
|
+
cwd: dir,
|
|
135
|
+
encoding: 'utf8',
|
|
136
|
+
maxBuffer: 1024 * 1024,
|
|
137
|
+
});
|
|
138
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
139
|
+
|
|
140
|
+
expect(result.status).toBe(0);
|
|
141
|
+
expect(result.stderr).toBe('');
|
|
142
|
+
const payload = JSON.parse(result.stdout);
|
|
143
|
+
expect(payload.item).toMatchObject({
|
|
144
|
+
status: 'done',
|
|
145
|
+
doc_update: today,
|
|
146
|
+
links: ['commit:abc123'],
|
|
147
|
+
verification: {
|
|
148
|
+
status: 'verified',
|
|
149
|
+
evidence: ['npm test -- --run'],
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
});
|
|
59
153
|
});
|
package/scripts/item.ts
CHANGED
|
@@ -10,12 +10,28 @@ import {
|
|
|
10
10
|
import { summarizeItem } from '../src/api/lib/itemProjection';
|
|
11
11
|
import { checkItemEvidence } from '../src/api/lib/itemEvidence';
|
|
12
12
|
import { hasFlag, positionalArgs, readOption, readRepeated } from './cliArgs';
|
|
13
|
-
import { handleCliError, isMain, misuse, repoRootFromCwd, runMain, serialize, type CliResult } from './cliResult';
|
|
13
|
+
import { handleCliError, isMain, misuse, ok, repoRootFromCwd, runMain, serialize, type CliResult } from './cliResult';
|
|
14
|
+
import { findItemCommand, formatItemCommandHelp, formatItemHelp } from './itemCommandRegistry.mjs';
|
|
14
15
|
|
|
15
16
|
export function runItemCli(repoRoot: string, args: string[]): CliResult {
|
|
16
17
|
const json = hasFlag(args, '--json');
|
|
17
18
|
const [command, itemId, value] = positionalArgs(args);
|
|
18
|
-
|
|
19
|
+
|
|
20
|
+
if (hasFlag(args, '--help') || command === 'help') {
|
|
21
|
+
const helpCommand = command === 'help' ? itemId : command;
|
|
22
|
+
if (helpCommand) {
|
|
23
|
+
const help = formatItemCommandHelp(helpCommand);
|
|
24
|
+
if (help) return ok(help);
|
|
25
|
+
return misuse(`unknown item command: ${helpCommand}`, json);
|
|
26
|
+
}
|
|
27
|
+
return ok(formatItemHelp());
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (!command) return misuse(formatItemHelp().trimEnd(), json);
|
|
31
|
+
if (!itemId) {
|
|
32
|
+
const definition = findItemCommand(command);
|
|
33
|
+
return misuse(definition ? `Usage: ${definition.usage}` : `unknown item command: ${command}`, json);
|
|
34
|
+
}
|
|
19
35
|
|
|
20
36
|
try {
|
|
21
37
|
if (command === 'show') {
|
|
@@ -76,13 +92,13 @@ export function runItemCli(repoRoot: string, args: string[]): CliResult {
|
|
|
76
92
|
}
|
|
77
93
|
|
|
78
94
|
if (command === 'set-status') {
|
|
79
|
-
if (!value) return misuse(
|
|
95
|
+
if (!value) return misuse(`Usage: ${findItemCommand('set-status')?.usage ?? 'tenbo-dashboard item set-status <id> <status>'}`, json);
|
|
80
96
|
const result = setItemStatus(repoRoot, itemId, value);
|
|
81
97
|
return serialize({ ok: true, scope: result.scopeId, item: result.item, validation: result.validation }, json, `${itemId} status: ${result.item.status}\n`);
|
|
82
98
|
}
|
|
83
99
|
|
|
84
100
|
if (command === 'add-note') {
|
|
85
|
-
if (!value) return misuse(
|
|
101
|
+
if (!value) return misuse(`Usage: ${findItemCommand('add-note')?.usage ?? 'tenbo-dashboard item add-note <id> <note>'}`, json);
|
|
86
102
|
const result = addItemNote(repoRoot, itemId, value);
|
|
87
103
|
return serialize({ ok: true, scope: result.scopeId, item: result.item, validation: result.validation }, json, `${itemId} note added\n`);
|
|
88
104
|
}
|
|
@@ -93,7 +109,7 @@ export function runItemCli(repoRoot: string, args: string[]): CliResult {
|
|
|
93
109
|
return serialize(report, json, `${itemId} evidence: ${report.verdict}\n`);
|
|
94
110
|
}
|
|
95
111
|
const status = readOption(args, '--status');
|
|
96
|
-
if (!status) return misuse(
|
|
112
|
+
if (!status) return misuse(`Usage: ${findItemCommand('verify')?.usage ?? 'tenbo-dashboard item verify <id> --status <status>'}`, json);
|
|
97
113
|
const result = setItemVerification(repoRoot, itemId, {
|
|
98
114
|
status,
|
|
99
115
|
evidence: readRepeated(args, '--evidence'),
|
|
@@ -106,8 +122,8 @@ export function runItemCli(repoRoot: string, args: string[]): CliResult {
|
|
|
106
122
|
const date = readOption(args, '--date');
|
|
107
123
|
const skipped = hasFlag(args, '--skipped');
|
|
108
124
|
const reason = readOption(args, '--reason');
|
|
109
|
-
if (!date && !skipped) return misuse(
|
|
110
|
-
if (skipped && !reason) return misuse('Usage: tenbo item doc-update <
|
|
125
|
+
if (!date && !skipped) return misuse(`Usage: ${findItemCommand('doc-update')?.usage ?? 'tenbo-dashboard item doc-update <id> --date <today|YYYY-MM-DD>'}`, json);
|
|
126
|
+
if (skipped && !reason) return misuse('Usage: tenbo-dashboard item doc-update <id> --skipped --reason "<text>"', json);
|
|
111
127
|
const today = new Date().toISOString().slice(0, 10);
|
|
112
128
|
const docUpdate = skipped ? `skipped — ${reason}` : date === 'today' ? today : date;
|
|
113
129
|
const result = setItemDocUpdate(repoRoot, itemId, docUpdate ?? today);
|
|
@@ -119,7 +135,7 @@ export function runItemCli(repoRoot: string, args: string[]): CliResult {
|
|
|
119
135
|
const docUpdateArg = readOption(args, '--doc-update');
|
|
120
136
|
const commit = readOption(args, '--commit');
|
|
121
137
|
const verificationStatus = readOption(args, '--verification');
|
|
122
|
-
if (evidence.length === 0) return misuse(
|
|
138
|
+
if (evidence.length === 0) return misuse(`Usage: ${findItemCommand('complete')?.usage ?? 'tenbo-dashboard item complete <id> --evidence "<text>"'}`, json);
|
|
123
139
|
const today = new Date().toISOString().slice(0, 10);
|
|
124
140
|
const docUpdate = docUpdateArg === 'today' ? today : docUpdateArg;
|
|
125
141
|
const result = completeItem(repoRoot, itemId, {
|
|
@@ -132,7 +148,7 @@ export function runItemCli(repoRoot: string, args: string[]): CliResult {
|
|
|
132
148
|
}
|
|
133
149
|
|
|
134
150
|
if (command === 'link-commit') {
|
|
135
|
-
if (!value) return misuse(
|
|
151
|
+
if (!value) return misuse(`Usage: ${findItemCommand('link-commit')?.usage ?? 'tenbo-dashboard item link-commit <id> <sha>'}`, json);
|
|
136
152
|
const result = linkItemCommit(repoRoot, itemId, value);
|
|
137
153
|
return serialize({ ok: true, scope: result.scopeId, item: result.item, validation: result.validation }, json, `${itemId} linked commit: ${value}\n`);
|
|
138
154
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface ItemCommandDefinition {
|
|
2
|
+
name: string;
|
|
3
|
+
usage: string;
|
|
4
|
+
summary: string;
|
|
5
|
+
details?: string[];
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const ITEM_COMMANDS: ItemCommandDefinition[];
|
|
9
|
+
export function findItemCommand(name: string): ItemCommandDefinition | undefined;
|
|
10
|
+
export function formatItemHelp(): string;
|
|
11
|
+
export function formatItemCommandHelp(name: string): string | undefined;
|
|
12
|
+
export function topLevelItemHelpLines(): string[];
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
export const ITEM_COMMANDS = [
|
|
2
|
+
{
|
|
3
|
+
name: 'show',
|
|
4
|
+
usage: 'tenbo-dashboard item show <id> [--json]',
|
|
5
|
+
summary: 'Show the full roadmap item.',
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
name: 'brief',
|
|
9
|
+
usage: 'tenbo-dashboard item brief <id> [--json]',
|
|
10
|
+
summary: 'Print compact execution context for one item.',
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
name: 'handoff',
|
|
14
|
+
usage: 'tenbo-dashboard item handoff <id> [--json]',
|
|
15
|
+
summary: 'Generate a copy/paste handoff prompt.',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
name: 'set-status',
|
|
19
|
+
usage: 'tenbo-dashboard item set-status <id> <now|next|later|done|dropped> [--json]',
|
|
20
|
+
summary: 'Set the roadmap status.',
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: 'add-note',
|
|
24
|
+
usage: 'tenbo-dashboard item add-note <id> "<note>" [--json]',
|
|
25
|
+
summary: 'Append a dated item note.',
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
name: 'verify',
|
|
29
|
+
usage: 'tenbo-dashboard item verify <id> --check [--json]',
|
|
30
|
+
summary: 'Check whether recorded evidence matches completion state.',
|
|
31
|
+
details: [
|
|
32
|
+
'tenbo-dashboard item verify <id> --status <not_required|pending_live|verified|failed>',
|
|
33
|
+
' [--evidence "<text>"] [--note "<text>"] [--json]',
|
|
34
|
+
],
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: 'doc-update',
|
|
38
|
+
usage: 'tenbo-dashboard item doc-update <id> --date <today|YYYY-MM-DD> [--json]',
|
|
39
|
+
summary: 'Record documentation freshness for an item.',
|
|
40
|
+
details: [
|
|
41
|
+
'tenbo-dashboard item doc-update <id> --skipped --reason "<text>" [--json]',
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: 'complete',
|
|
46
|
+
usage: 'tenbo-dashboard item complete <id> --evidence "<text>" [--doc-update today|YYYY-MM-DD] [--commit <sha>] [--verification <status>] [--json]',
|
|
47
|
+
summary: 'Complete status, verification, docs, and optional commit link in one transaction.',
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: 'link-commit',
|
|
51
|
+
usage: 'tenbo-dashboard item link-commit <id> <sha> [--json]',
|
|
52
|
+
summary: 'Attach a commit link to an item.',
|
|
53
|
+
},
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
const commandByName = new Map(ITEM_COMMANDS.map((command) => [command.name, command]));
|
|
57
|
+
|
|
58
|
+
export function findItemCommand(name) {
|
|
59
|
+
return commandByName.get(name);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function formatItemHelp() {
|
|
63
|
+
const maxNameLength = Math.max(...ITEM_COMMANDS.map((command) => command.name.length));
|
|
64
|
+
const commandLines = ITEM_COMMANDS.flatMap((command) => [
|
|
65
|
+
` ${command.name.padEnd(maxNameLength)} ${command.summary}`,
|
|
66
|
+
` ${' '.repeat(maxNameLength)} ${command.usage}`,
|
|
67
|
+
]);
|
|
68
|
+
|
|
69
|
+
return [
|
|
70
|
+
'Usage: tenbo-dashboard item <command> <id> [options]',
|
|
71
|
+
'',
|
|
72
|
+
'Commands:',
|
|
73
|
+
...commandLines,
|
|
74
|
+
'',
|
|
75
|
+
'Run tenbo-dashboard item <command> --help for command-specific usage.',
|
|
76
|
+
'',
|
|
77
|
+
].join('\n');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function formatItemCommandHelp(name) {
|
|
81
|
+
const command = findItemCommand(name);
|
|
82
|
+
if (!command) return undefined;
|
|
83
|
+
return [
|
|
84
|
+
`Usage: ${command.usage}`,
|
|
85
|
+
'',
|
|
86
|
+
command.summary,
|
|
87
|
+
...(command.details?.length ? ['', 'Additional forms:', ...command.details.map((line) => ` ${line}`)] : []),
|
|
88
|
+
'',
|
|
89
|
+
].join('\n');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function topLevelItemHelpLines() {
|
|
93
|
+
return ITEM_COMMANDS.flatMap((command) => {
|
|
94
|
+
const lines = [` ${command.usage}`];
|
|
95
|
+
if (command.details?.length) lines.push(...command.details.map((line) => ` ${line}`));
|
|
96
|
+
return lines;
|
|
97
|
+
});
|
|
98
|
+
}
|
package/scripts/items.test.ts
CHANGED
|
@@ -82,6 +82,26 @@ describe('items CLI', () => {
|
|
|
82
82
|
]);
|
|
83
83
|
});
|
|
84
84
|
|
|
85
|
+
it('includes goal_ref in selected compact fields', () => {
|
|
86
|
+
writeFileSync(path.join(dir, '.tenbo/scopes/editor/roadmap.yaml'), [
|
|
87
|
+
'items:',
|
|
88
|
+
' - id: ed-001',
|
|
89
|
+
' title: First',
|
|
90
|
+
' layer: app',
|
|
91
|
+
' status: next',
|
|
92
|
+
' description: first item',
|
|
93
|
+
' goal_ref: [g1, g2]',
|
|
94
|
+
'',
|
|
95
|
+
].join('\n'));
|
|
96
|
+
|
|
97
|
+
const result = runItemsCli(dir, ['--fields', 'id,title,goal_ref', '--json']);
|
|
98
|
+
|
|
99
|
+
expect(result.exitCode).toBe(0);
|
|
100
|
+
expect(JSON.parse(result.stdout).items).toEqual([
|
|
101
|
+
{ id: 'ed-001', title: 'First', goal_ref: ['g1', 'g2'] },
|
|
102
|
+
]);
|
|
103
|
+
});
|
|
104
|
+
|
|
85
105
|
it('returns compact summaries without changing full JSON by default', () => {
|
|
86
106
|
const summary = runItemsCli(dir, ['--status', 'next', '--summary', '--json']);
|
|
87
107
|
const full = runItemsCli(dir, ['--status', 'next', '--json']);
|
package/scripts/items.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { listItems, type ListItemFilters } from '../src/api/lib/roadmapStore';
|
|
2
2
|
import { parseItemFields, projectItem, summarizeItem } from '../src/api/lib/itemProjection';
|
|
3
3
|
import { hasFlag, readOption } from './cliArgs';
|
|
4
|
-
import { handleCliError, isMain, misuse, repoRootFromCwd, runMain, serialize, type CliResult } from './cliResult';
|
|
4
|
+
import { handleCliError, isMain, misuse, ok, repoRootFromCwd, runMain, serialize, type CliResult } from './cliResult';
|
|
5
5
|
|
|
6
6
|
const STATUSES = new Set(['now', 'next', 'later', 'done', 'dropped']);
|
|
7
|
+
const USAGE = `Usage: tenbo-dashboard items [--status <status[,status]>] [--verification <status>] [--goal <goal>] [--type <type>] [--priority <p0|p1|p2|p3>] [--layer <id>] [--fields <a,b>] [--summary] [--json]
|
|
8
|
+
`;
|
|
7
9
|
|
|
8
10
|
function readStatusFilter(value: string | undefined): ListItemFilters['status'] {
|
|
9
11
|
if (!value) return undefined;
|
|
@@ -49,7 +51,8 @@ export function runItemsCli(repoRoot: string, args: string[]): CliResult {
|
|
|
49
51
|
if (isMain(import.meta.url)) {
|
|
50
52
|
const args = process.argv.slice(2);
|
|
51
53
|
if (args.includes('--help')) {
|
|
52
|
-
runMain(
|
|
54
|
+
runMain(ok(USAGE));
|
|
55
|
+
} else {
|
|
56
|
+
runMain(runItemsCli(repoRootFromCwd(), args));
|
|
53
57
|
}
|
|
54
|
-
runMain(runItemsCli(repoRootFromCwd(), args));
|
|
55
58
|
}
|
package/scripts/next-id.test.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect, afterEach } from 'vitest';
|
|
2
|
-
import { mkdtempSync, mkdirSync, writeFileSync, rmSync,
|
|
2
|
+
import { mkdtempSync, mkdirSync, writeFileSync, rmSync, readdirSync, utimesSync } from 'node:fs';
|
|
3
3
|
import { tmpdir } from 'node:os';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { spawnSync } from 'node:child_process';
|
package/scripts/next-id.ts
CHANGED
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
* decoration so callers can capture it via `$(...)`. Errors go to stderr
|
|
28
28
|
* and the process exits non-zero.
|
|
29
29
|
*/
|
|
30
|
-
import { readFileSync, existsSync, mkdirSync,
|
|
30
|
+
import { readFileSync, existsSync, mkdirSync, statSync, unlinkSync, openSync, closeSync, writeSync } from 'node:fs';
|
|
31
31
|
import path from 'node:path';
|
|
32
32
|
import { parse as parseYaml } from 'yaml';
|
|
33
33
|
import { findRepoRoot } from '../src/api/lib/repoRoot';
|
|
@@ -41,10 +41,6 @@ interface WorkspaceScope {
|
|
|
41
41
|
path?: string;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
interface RoadmapFile {
|
|
45
|
-
items?: Array<{ id?: string }>;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
44
|
/** Find which roadmap.yaml files contain ids for this prefix. */
|
|
49
45
|
export function resolveRoadmapPaths(repoRoot: string, prefix: string): string[] {
|
|
50
46
|
const tenbo = path.join(repoRoot, '.tenbo');
|
|
@@ -10,6 +10,7 @@ export interface ItemSummaryRecord {
|
|
|
10
10
|
layers?: string[];
|
|
11
11
|
type?: Item['type'];
|
|
12
12
|
priority?: Priority;
|
|
13
|
+
goal_ref?: Item['goal_ref'];
|
|
13
14
|
verification?: VerificationStatus;
|
|
14
15
|
risk_count?: number;
|
|
15
16
|
done_when_count?: number;
|
|
@@ -25,6 +26,7 @@ export const ITEM_FIELD_NAMES = [
|
|
|
25
26
|
'layers',
|
|
26
27
|
'type',
|
|
27
28
|
'priority',
|
|
29
|
+
'goal_ref',
|
|
28
30
|
'verification',
|
|
29
31
|
'risk_count',
|
|
30
32
|
'done_when_count',
|
|
@@ -60,6 +62,7 @@ export function summarizeItem(entry: LocatedItem): ItemSummaryRecord {
|
|
|
60
62
|
...(item.layers ? { layers: item.layers } : {}),
|
|
61
63
|
...(item.type ? { type: item.type } : {}),
|
|
62
64
|
...(item.priority ? { priority: item.priority } : {}),
|
|
65
|
+
...(item.goal_ref ? { goal_ref: item.goal_ref } : {}),
|
|
63
66
|
...(item.verification?.status ? { verification: item.verification.status } : {}),
|
|
64
67
|
...(item.risks ? { risk_count: item.risks.length } : {}),
|
|
65
68
|
...(item.done_when ? { done_when_count: item.done_when.length } : {}),
|
package/tsconfig.json
CHANGED