tenbo-dashboard 0.11.0 → 0.13.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/bin/tenbo-dashboard.mjs +29 -2
- package/package.json +1 -1
- package/scripts/cliArgs.ts +15 -2
- package/scripts/code-map.ts +45 -0
- package/scripts/commit-context.ts +33 -0
- package/scripts/context.ts +32 -3
- package/scripts/hook.ts +585 -17
- package/scripts/hub.ts +356 -0
- package/scripts/item.ts +154 -23
- package/scripts/itemCommandRegistry.mjs +9 -0
- package/scripts/reconcile.ts +99 -0
- package/src/api/lib/codeMapGenerator.ts +544 -0
- package/src/api/lib/commitContext.ts +156 -0
- package/src/api/lib/contextResolver.ts +626 -6
- package/src/api/lib/doneWhenChecks.ts +274 -0
- package/src/api/lib/doneWhenShape.ts +21 -0
- package/src/api/lib/hub/autostart.ts +95 -0
- package/src/api/lib/hub/client.ts +57 -0
- package/src/api/lib/hub/discover.ts +74 -0
- package/src/api/lib/hub/hubRepoRoot.ts +54 -0
- package/src/api/lib/hub/hubServer.ts +204 -0
- package/src/api/lib/hub/identity.ts +47 -0
- package/src/api/lib/hub/journal.ts +181 -0
- package/src/api/lib/hub/lockfile.ts +105 -0
- package/src/api/lib/hub/mutationClaim.ts +296 -0
- package/src/api/lib/hub/mutationQueue.ts +37 -0
- package/src/api/lib/hub/mutations.ts +164 -0
- package/src/api/lib/impact.ts +33 -2
- package/src/api/lib/reconciliationFlag.ts +338 -0
- package/src/api/lib/roadmapStore.ts +26 -2
- package/src/api/lib/tenboFs.ts +39 -0
- package/src/api/lib/validator.ts +49 -0
- package/src/api/plugin.ts +20 -0
- package/src/api/routes/hubHealth.ts +27 -0
- package/src/api/routes/hubMutate.ts +43 -0
- package/src/api/routes/items.ts +27 -9
- package/src/types.ts +44 -2
- package/src/ui/ItemModal/EnrichmentSections.tsx +18 -1
package/bin/tenbo-dashboard.mjs
CHANGED
|
@@ -43,6 +43,7 @@ const commands = {
|
|
|
43
43
|
sync: 'scripts/sync.ts',
|
|
44
44
|
archive: 'scripts/archive.ts',
|
|
45
45
|
'commit-ready': 'scripts/commit-ready.ts',
|
|
46
|
+
'commit-context': 'scripts/commit-context.ts',
|
|
46
47
|
item: 'scripts/item.ts',
|
|
47
48
|
items: 'scripts/items.ts',
|
|
48
49
|
list: 'scripts/items.ts',
|
|
@@ -51,8 +52,11 @@ const commands = {
|
|
|
51
52
|
context: 'scripts/context.ts',
|
|
52
53
|
impact: 'scripts/impact.ts',
|
|
53
54
|
index: 'scripts/index.ts',
|
|
55
|
+
'code-map': 'scripts/code-map.ts',
|
|
54
56
|
reminder: 'scripts/reminder.ts',
|
|
55
57
|
hook: 'scripts/hook.ts',
|
|
58
|
+
reconcile: 'scripts/reconcile.ts',
|
|
59
|
+
hub: 'scripts/hub.ts',
|
|
56
60
|
};
|
|
57
61
|
|
|
58
62
|
// Documented aliases for the bare-launch behavior. Users (and agents) reading
|
|
@@ -100,9 +104,14 @@ ${topLevelItemHelpLines().join('\n')}
|
|
|
100
104
|
[--type <type>] [--json]
|
|
101
105
|
tenbo-dashboard context feature Fetch agent planning context for a natural-language request
|
|
102
106
|
--query "<request>" [--json]
|
|
107
|
+
tenbo-dashboard context session Deterministic session-gate briefing bundle [--json]
|
|
108
|
+
tenbo-dashboard context item <id> Deterministic per-item context bundle [--json]
|
|
103
109
|
tenbo-dashboard impact Map git changes to affected layers, docs, items,
|
|
104
110
|
and verification checks [--since <ref>] [--json]
|
|
105
111
|
tenbo-dashboard index Build or refresh the derived source index [--if-stale] [--json]
|
|
112
|
+
tenbo-dashboard code-map generate Regenerate the auto-generated structural region of a
|
|
113
|
+
layer's code-map.md from the source index
|
|
114
|
+
[--scope <id>] [--layer <id>] [--check] [--json]
|
|
106
115
|
tenbo-dashboard reminder print Print advisory session reminder text [--json]
|
|
107
116
|
tenbo-dashboard validate Run validation rules
|
|
108
117
|
tenbo-dashboard init-check Strict completeness check for fresh init (errors on missing skeletons, file_count:0, etc)
|
|
@@ -111,10 +120,28 @@ ${topLevelItemHelpLines().join('\n')}
|
|
|
111
120
|
tenbo-dashboard archive Move old done/dropped items to roadmap-archive.yaml
|
|
112
121
|
[--scope <id>] [--days 30] [--max 20]
|
|
113
122
|
tenbo-dashboard hook install Install opt-in pre-commit validation hook
|
|
114
|
-
|
|
115
|
-
|
|
123
|
+
(or 'prepare-commit-msg' for commit-context
|
|
124
|
+
suggestions, or 'post-commit' for doc-flag
|
|
125
|
+
detection) [--dry-run] [--force]
|
|
126
|
+
tenbo-dashboard hook uninstall Remove a tenbo hook (idempotent)
|
|
127
|
+
[pre-commit|prepare-commit-msg|post-commit]
|
|
116
128
|
tenbo-dashboard commit-ready Report branch, diff, validation, and staging gates
|
|
117
129
|
without committing [--json]
|
|
130
|
+
tenbo-dashboard commit-context Propose roadmap item refs + a draft commit message
|
|
131
|
+
from the pending diff [--since <ref>] [--json] [--hook-format]
|
|
132
|
+
tenbo-dashboard reconcile flag Map a commit's diff to affected layers and append
|
|
133
|
+
pending-reconciliation doc-flag entries
|
|
134
|
+
[--since <ref>] [--dry-run] [--json]
|
|
135
|
+
tenbo-dashboard reconcile clear Remove pending-reconciliation entries once a doc
|
|
136
|
+
update lands [--layer <scope:layer>] [--commit <sha>]
|
|
137
|
+
tenbo-dashboard hub start Run a headless, API-only hub server in the
|
|
138
|
+
foreground (health+mutate only, no browser UI)
|
|
139
|
+
[--port <n>]
|
|
140
|
+
tenbo-dashboard hub ensure Idempotent: start a hub for this repo if none
|
|
141
|
+
is running yet, else no-op [--json]
|
|
142
|
+
tenbo-dashboard hub stop Stop this repo's running hub, if any [--json]
|
|
143
|
+
tenbo-dashboard hub status Report whether a hub is running
|
|
144
|
+
(pid/port/uptime) [--json]
|
|
118
145
|
tenbo-dashboard --version Print package version
|
|
119
146
|
tenbo-dashboard help Show this help
|
|
120
147
|
|
package/package.json
CHANGED
package/scripts/cliArgs.ts
CHANGED
|
@@ -16,12 +16,25 @@ export function readRepeated(args: string[], flag: string): string[] {
|
|
|
16
16
|
return out;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
export
|
|
19
|
+
export interface PositionalArgsOptions {
|
|
20
|
+
/**
|
|
21
|
+
* Flags that consume the token immediately after them (e.g. `--status
|
|
22
|
+
* done`). Any `--flag` NOT in this set is treated as boolean/valueless
|
|
23
|
+
* (e.g. `--json`, `--check`) — the token that follows it is left in place
|
|
24
|
+
* as a positional, regardless of where the flag appears among the args.
|
|
25
|
+
* Defaults to no value-taking flags, so bare `positionalArgs(args)` never
|
|
26
|
+
* consumes a token after a `--flag`.
|
|
27
|
+
*/
|
|
28
|
+
valueFlags?: readonly string[];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function positionalArgs(args: string[], options: PositionalArgsOptions = {}): string[] {
|
|
32
|
+
const valueFlags = new Set(options.valueFlags ?? []);
|
|
20
33
|
const out: string[] = [];
|
|
21
34
|
for (let i = 0; i < args.length; i++) {
|
|
22
35
|
const arg = args[i];
|
|
23
36
|
if (arg.startsWith('--')) {
|
|
24
|
-
if (
|
|
37
|
+
if (valueFlags.has(arg) && i + 1 < args.length) i++;
|
|
25
38
|
continue;
|
|
26
39
|
}
|
|
27
40
|
out.push(arg);
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { generateCodeMaps, isCodeMapGeneratorError } from '../src/api/lib/codeMapGenerator';
|
|
2
|
+
import { hasFlag, readOption } from './cliArgs';
|
|
3
|
+
import { fail, handleCliError, isMain, misuse, repoRootFromCwd, runMain, type CliResult } from './cliResult';
|
|
4
|
+
|
|
5
|
+
const USAGE = 'Usage: tenbo-dashboard code-map generate [--scope <id>] [--layer <id>] [--check] [--json]';
|
|
6
|
+
|
|
7
|
+
export function runCodeMapCli(repoRoot: string, args: string[]): CliResult {
|
|
8
|
+
const json = hasFlag(args, '--json');
|
|
9
|
+
const command = args.find((arg) => !arg.startsWith('--'));
|
|
10
|
+
|
|
11
|
+
if (command !== 'generate') {
|
|
12
|
+
return misuse(USAGE, json);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const scope = readOption(args, '--scope');
|
|
16
|
+
const layer = readOption(args, '--layer');
|
|
17
|
+
const check = hasFlag(args, '--check');
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
const result = generateCodeMaps(repoRoot, { scope, layer, check });
|
|
21
|
+
const staleCount = result.stale_layers.length;
|
|
22
|
+
const isStaleFailure = check && staleCount > 0;
|
|
23
|
+
const summary = check
|
|
24
|
+
? (isStaleFailure
|
|
25
|
+
? `code-map: ${staleCount} layer(s) stale: ${result.stale_layers.join(', ')}\n`
|
|
26
|
+
: `code-map: ${result.layers.length} layer(s) checked, all fresh\n`)
|
|
27
|
+
: `code-map: ${result.layers.length} layer(s) generated (${staleCount} changed)\n`;
|
|
28
|
+
|
|
29
|
+
if (json) {
|
|
30
|
+
return { stdout: `${JSON.stringify(result, null, 2)}\n`, stderr: '', exitCode: isStaleFailure ? 1 : 0 };
|
|
31
|
+
}
|
|
32
|
+
return isStaleFailure
|
|
33
|
+
? { stdout: '', stderr: summary, exitCode: 1 }
|
|
34
|
+
: { stdout: summary, stderr: '', exitCode: 0 };
|
|
35
|
+
} catch (err) {
|
|
36
|
+
if (isCodeMapGeneratorError(err)) {
|
|
37
|
+
return fail(err.code, err.message, json);
|
|
38
|
+
}
|
|
39
|
+
return handleCliError(err, json);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (isMain(import.meta.url)) {
|
|
44
|
+
runMain(runCodeMapCli(repoRootFromCwd(), process.argv.slice(2)));
|
|
45
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { resolveCommitContext } from '../src/api/lib/commitContext';
|
|
2
|
+
import { hasFlag, readOption } from './cliArgs';
|
|
3
|
+
import { isMain, misuse, repoRootFromCwd, runMain, serialize, type CliResult } from './cliResult';
|
|
4
|
+
|
|
5
|
+
export function runCommitContextCli(repoRoot: string, args: string[]): CliResult {
|
|
6
|
+
const json = hasFlag(args, '--json');
|
|
7
|
+
const hookFormat = hasFlag(args, '--hook-format');
|
|
8
|
+
const since = readOption(args, '--since');
|
|
9
|
+
if (args.includes('--since') && (!since || since.startsWith('--'))) {
|
|
10
|
+
return misuse('Usage: tenbo-dashboard commit-context [--since <ref>] [--json] [--hook-format]', json);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// resolveCommitContext is fail-open by design (never throws), so there is
|
|
14
|
+
// no try/catch here — any internal failure already surfaces as a payload
|
|
15
|
+
// with empty suggestions and a `note` explaining why.
|
|
16
|
+
const payload = resolveCommitContext(repoRoot, { since });
|
|
17
|
+
|
|
18
|
+
if (hookFormat) {
|
|
19
|
+
return { stdout: payload.hook_format ? `${payload.hook_format}\n` : '', stderr: '', exitCode: 0 };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const text = payload.suggestions.length
|
|
23
|
+
? `${payload.suggestions.length} suggestion(s):\n${payload.suggestions
|
|
24
|
+
.map((s) => ` ${s.id} (${s.status}) ${s.title} — ${s.reason}`)
|
|
25
|
+
.join('\n')}\n\nDraft message:\n${payload.draft_message}\n`
|
|
26
|
+
: `${payload.note}\n${payload.draft_message ? `\nDraft message:\n${payload.draft_message}\n` : ''}`;
|
|
27
|
+
|
|
28
|
+
return serialize(payload, json, text);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (isMain(import.meta.url)) {
|
|
32
|
+
runMain(runCommitContextCli(repoRootFromCwd(), process.argv.slice(2)));
|
|
33
|
+
}
|
package/scripts/context.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
import { resolveFeatureContext } from '../src/api/lib/contextResolver';
|
|
2
|
-
import { hasFlag, readOption } from './cliArgs';
|
|
1
|
+
import { resolveFeatureContext, resolveItemContext, resolveSessionContext } from '../src/api/lib/contextResolver';
|
|
2
|
+
import { hasFlag, positionalArgs, readOption } from './cliArgs';
|
|
3
3
|
import { handleCliError, isMain, misuse, repoRootFromCwd, runMain, serialize, type CliResult } from './cliResult';
|
|
4
4
|
|
|
5
|
+
const USAGE = 'Usage: tenbo context <feature|session|item> ... [--json]\n'
|
|
6
|
+
+ ' tenbo context feature --query "<request>" [--json]\n'
|
|
7
|
+
+ ' tenbo context session [--json]\n'
|
|
8
|
+
+ ' tenbo context item <id> [--json]';
|
|
9
|
+
|
|
5
10
|
export function runContextCli(repoRoot: string, args: string[]): CliResult {
|
|
6
11
|
const json = hasFlag(args, '--json');
|
|
7
12
|
const command = args.find((arg) => !arg.startsWith('--'));
|
|
@@ -18,7 +23,31 @@ export function runContextCli(repoRoot: string, args: string[]): CliResult {
|
|
|
18
23
|
);
|
|
19
24
|
}
|
|
20
25
|
|
|
21
|
-
|
|
26
|
+
if (command === 'session') {
|
|
27
|
+
const bundle = resolveSessionContext(repoRoot);
|
|
28
|
+
return serialize(
|
|
29
|
+
bundle,
|
|
30
|
+
json,
|
|
31
|
+
`now: ${bundle.now.count} (${bundle.stale.now.length} stale), stale next: ${bundle.stale.next.length}\n`,
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (command === 'item') {
|
|
36
|
+
// `item` itself has no value-taking flags (only boolean --json), so
|
|
37
|
+
// any --flag encountered here never consumes the following token —
|
|
38
|
+
// this keeps `context item --json <id>` and `context item <id>
|
|
39
|
+
// --json` equivalent (td-032).
|
|
40
|
+
const [, itemId] = positionalArgs(args);
|
|
41
|
+
if (!itemId) return misuse('Usage: tenbo context item <id> [--json]', json);
|
|
42
|
+
const bundle = resolveItemContext(repoRoot, itemId);
|
|
43
|
+
return serialize(
|
|
44
|
+
bundle,
|
|
45
|
+
json,
|
|
46
|
+
`${bundle.item_id}: ${bundle.item.title} (${bundle.item.status})\n`,
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return misuse(USAGE, json);
|
|
22
51
|
} catch (err) {
|
|
23
52
|
return handleCliError(err, json);
|
|
24
53
|
}
|