tenbo-dashboard 0.10.3 → 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.
Files changed (105) hide show
  1. package/README.md +6 -0
  2. package/bin/tenbo-dashboard.mjs +36 -2
  3. package/package.json +7 -4
  4. package/scripts/cliArgs.ts +15 -2
  5. package/scripts/code-map.ts +45 -0
  6. package/scripts/commit-context.ts +33 -0
  7. package/scripts/context.ts +32 -3
  8. package/scripts/hook.ts +585 -17
  9. package/scripts/hub.ts +356 -0
  10. package/scripts/impact.ts +25 -0
  11. package/scripts/index.ts +41 -0
  12. package/scripts/item.ts +154 -23
  13. package/scripts/itemCommandRegistry.mjs +9 -0
  14. package/scripts/reconcile.ts +99 -0
  15. package/scripts/reminder.ts +24 -0
  16. package/scripts/sync.ts +66 -5
  17. package/src/api/lib/codeMapGenerator.ts +544 -0
  18. package/src/api/lib/commitContext.ts +156 -0
  19. package/src/api/lib/contextResolver.ts +809 -16
  20. package/src/api/lib/doneWhenChecks.ts +274 -0
  21. package/src/api/lib/doneWhenShape.ts +21 -0
  22. package/src/api/lib/health/collectAll.ts +26 -54
  23. package/src/api/lib/health/coupling.ts +3 -0
  24. package/src/api/lib/health/deadCode.ts +1 -0
  25. package/src/api/lib/health/importGraph.ts +3 -65
  26. package/src/api/lib/health/structuralGraph.ts +87 -0
  27. package/src/api/lib/health/types.ts +7 -2
  28. package/src/api/lib/hub/autostart.ts +95 -0
  29. package/src/api/lib/hub/client.ts +57 -0
  30. package/src/api/lib/hub/discover.ts +74 -0
  31. package/src/api/lib/hub/hubRepoRoot.ts +54 -0
  32. package/src/api/lib/hub/hubServer.ts +204 -0
  33. package/src/api/lib/hub/identity.ts +47 -0
  34. package/src/api/lib/hub/journal.ts +181 -0
  35. package/src/api/lib/hub/lockfile.ts +105 -0
  36. package/src/api/lib/hub/mutationClaim.ts +296 -0
  37. package/src/api/lib/hub/mutationQueue.ts +37 -0
  38. package/src/api/lib/hub/mutations.ts +164 -0
  39. package/src/api/lib/impact.ts +234 -0
  40. package/src/api/lib/reconciliationFlag.ts +338 -0
  41. package/src/api/lib/roadmapStore.ts +26 -2
  42. package/src/api/lib/sourceIndex/build.ts +161 -0
  43. package/src/api/lib/sourceIndex/query.ts +80 -0
  44. package/src/api/lib/sourceIndex/store.ts +128 -0
  45. package/src/api/lib/sourceIndex/types.ts +46 -0
  46. package/src/api/lib/tenboFs.ts +59 -1
  47. package/src/api/lib/validator.ts +49 -0
  48. package/src/api/plugin.ts +20 -0
  49. package/src/api/routes/hubHealth.ts +27 -0
  50. package/src/api/routes/hubMutate.ts +43 -0
  51. package/src/api/routes/items.ts +27 -9
  52. package/src/types.ts +44 -2
  53. package/src/ui/ItemModal/EnrichmentSections.tsx +18 -1
  54. package/vite.config.ts +0 -1
  55. package/scripts/bin-output.test.ts +0 -153
  56. package/scripts/commit-ready.test.ts +0 -47
  57. package/scripts/compute-metrics.test.ts +0 -35
  58. package/scripts/context.test.ts +0 -77
  59. package/scripts/hook.test.ts +0 -144
  60. package/scripts/item.test.ts +0 -234
  61. package/scripts/items.test.ts +0 -157
  62. package/scripts/next-id.test.ts +0 -128
  63. package/scripts/next.test.ts +0 -92
  64. package/scripts/validate-cli.test.ts +0 -152
  65. package/scripts/work-queue.test.ts +0 -91
  66. package/src/api/lib/contextResolver.test.ts +0 -161
  67. package/src/api/lib/health/agingTodos.test.ts +0 -32
  68. package/src/api/lib/health/archCompliance.test.ts +0 -26
  69. package/src/api/lib/health/collectAll.test.ts +0 -110
  70. package/src/api/lib/health/config.test.ts +0 -45
  71. package/src/api/lib/health/coupling.test.ts +0 -40
  72. package/src/api/lib/health/deadCode.test.ts +0 -27
  73. package/src/api/lib/health/docDrift.test.ts +0 -72
  74. package/src/api/lib/health/hotspotFiles.test.ts +0 -49
  75. package/src/api/lib/health/importGraph.test.ts +0 -73
  76. package/src/api/lib/health/layerFiles.test.ts +0 -46
  77. package/src/api/lib/health/redundancy.test.ts +0 -24
  78. package/src/api/lib/health/testCoverage.test.ts +0 -33
  79. package/src/api/lib/health/types.test.ts +0 -36
  80. package/src/api/lib/metrics.test.ts +0 -81
  81. package/src/api/lib/metricsRefresh.test.ts +0 -54
  82. package/src/api/lib/parseCache.test.ts +0 -96
  83. package/src/api/lib/phases.test.ts +0 -90
  84. package/src/api/lib/priority.test.ts +0 -54
  85. package/src/api/lib/roadmapStore.test.ts +0 -163
  86. package/src/api/lib/tenboFs.layerContent.test.ts +0 -36
  87. package/src/api/lib/tenboFs.v2.test.ts +0 -86
  88. package/src/api/lib/tenboFs.workspaceContent.test.ts +0 -42
  89. package/src/api/lib/validator.docUpdate.test.ts +0 -99
  90. package/src/api/lib/validator.duplicateIds.test.ts +0 -89
  91. package/src/api/lib/validator.goalRef.test.ts +0 -76
  92. package/src/api/lib/validator.phases.test.ts +0 -98
  93. package/src/api/lib/validator.preflightViolations.test.ts +0 -67
  94. package/src/api/lib/validator.relationships.test.ts +0 -132
  95. package/src/api/lib/validator.specLinks.test.ts +0 -100
  96. package/src/api/lib/validator.v2.test.ts +0 -55
  97. package/src/api/lib/validator.verification.test.ts +0 -52
  98. package/src/api/routes/state.test.ts +0 -148
  99. package/src/router/useHashRoute.test.ts +0 -69
  100. package/src/test-setup.ts +0 -1
  101. package/src/ui/FindingModal/FindingModal.test.tsx +0 -36
  102. package/src/ui/HealthPage/HealthPage.test.tsx +0 -71
  103. package/src/ui/HealthPage/severity.test.ts +0 -46
  104. package/src/ui/LayerDetailPage/LayerDetailPage.test.tsx +0 -30
  105. package/src/ui/LayerPage/isTemplateOnly.test.ts +0 -45
package/README.md CHANGED
@@ -33,6 +33,10 @@ Project overview, principles, and glossary alongside per-layer narratives, inten
33
33
 
34
34
  Surfaces the things that would otherwise become tech debt nobody mentions: oversized layers (hotspots), code-map references that no longer match real files (doc drift), unreferenced files, coupling violations, dead code. Each finding carries a severity, the file it points at, and a suggested fix.
35
35
 
36
+ Some health findings include generated graph evidence such as importers, exports,
37
+ and layer ownership. This evidence is rebuilt from source and `.tenbo/` metadata;
38
+ it is not separate project memory.
39
+
36
40
  ## CLI tools
37
41
 
38
42
  The same package ships commands the skill uses behind the scenes — also runnable directly:
@@ -46,6 +50,8 @@ npx tenbo-dashboard item link-commit sk-030 7fc09a5
46
50
  npx tenbo-dashboard items --status done --verification pending_live --json
47
51
  npx tenbo-dashboard next --json
48
52
  npx tenbo-dashboard context feature --query "help me build X" --json
53
+ npx tenbo-dashboard impact --json # Map git changes to affected layers/docs/items
54
+ npx tenbo-dashboard index --json # Rebuild the derived source index
49
55
  npx tenbo-dashboard validate # Schema + consistency checks
50
56
  npx tenbo-dashboard init-check # Strict completeness check after fresh init
51
57
  npx tenbo-dashboard metrics --all # Recompute scope metrics + health findings
@@ -43,13 +43,20 @@ 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',
49
50
  next: 'scripts/next.ts',
50
51
  'work-queue': 'scripts/work-queue.ts',
51
52
  context: 'scripts/context.ts',
53
+ impact: 'scripts/impact.ts',
54
+ index: 'scripts/index.ts',
55
+ 'code-map': 'scripts/code-map.ts',
56
+ reminder: 'scripts/reminder.ts',
52
57
  hook: 'scripts/hook.ts',
58
+ reconcile: 'scripts/reconcile.ts',
59
+ hub: 'scripts/hub.ts',
53
60
  };
54
61
 
55
62
  // Documented aliases for the bare-launch behavior. Users (and agents) reading
@@ -97,6 +104,15 @@ ${topLevelItemHelpLines().join('\n')}
97
104
  [--type <type>] [--json]
98
105
  tenbo-dashboard context feature Fetch agent planning context for a natural-language request
99
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]
109
+ tenbo-dashboard impact Map git changes to affected layers, docs, items,
110
+ and verification checks [--since <ref>] [--json]
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]
115
+ tenbo-dashboard reminder print Print advisory session reminder text [--json]
100
116
  tenbo-dashboard validate Run validation rules
101
117
  tenbo-dashboard init-check Strict completeness check for fresh init (errors on missing skeletons, file_count:0, etc)
102
118
  tenbo-dashboard next-id <prefix> Allocate next roadmap item ID
@@ -104,10 +120,28 @@ ${topLevelItemHelpLines().join('\n')}
104
120
  tenbo-dashboard archive Move old done/dropped items to roadmap-archive.yaml
105
121
  [--scope <id>] [--days 30] [--max 20]
106
122
  tenbo-dashboard hook install Install opt-in pre-commit validation hook
107
- [--dry-run] [--force]
108
- tenbo-dashboard hook uninstall Remove the tenbo pre-commit hook (idempotent)
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]
109
128
  tenbo-dashboard commit-ready Report branch, diff, validation, and staging gates
110
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]
111
145
  tenbo-dashboard --version Print package version
112
146
  tenbo-dashboard help Show this help
113
147
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tenbo-dashboard",
3
- "version": "0.10.3",
3
+ "version": "0.13.0",
4
4
  "description": "Local-first architecture dashboard and CLI tools for .tenbo/ repos",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -30,6 +30,9 @@
30
30
  "bin/",
31
31
  "src/",
32
32
  "scripts/",
33
+ "!**/*.test.ts",
34
+ "!**/*.test.tsx",
35
+ "!src/test-setup.ts",
33
36
  "index.html",
34
37
  "vite.config.ts",
35
38
  "tsconfig.json"
@@ -47,7 +50,7 @@
47
50
  "@dnd-kit/sortable": "^8.0.0",
48
51
  "@dnd-kit/utilities": "^3.2.2",
49
52
  "@parcel/watcher": "^2.5.6",
50
- "@vitejs/plugin-react": "^4.3.1",
53
+ "@vitejs/plugin-react": "^4.7.0",
51
54
  "@xyflow/react": "^12.10.2",
52
55
  "chokidar": "^3.6.0",
53
56
  "dagre": "^0.8.5",
@@ -59,9 +62,9 @@
59
62
  "react-markdown": "^9.0.1",
60
63
  "remark-gfm": "^4.0.0",
61
64
  "ts-morph": "^25",
62
- "tsx": "^4.19.0",
65
+ "tsx": "^4.22.4",
63
66
  "typescript": "^5.5.4",
64
- "vite": "^5.4.3",
67
+ "vite": "^6.4.3",
65
68
  "yaml": "^2.5.1"
66
69
  },
67
70
  "devDependencies": {
@@ -16,12 +16,25 @@ export function readRepeated(args: string[], flag: string): string[] {
16
16
  return out;
17
17
  }
18
18
 
19
- export function positionalArgs(args: string[]): string[] {
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 (args[i + 1] && !args[i + 1].startsWith('--')) i++;
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
+ }
@@ -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
- return misuse('Usage: tenbo context feature --query "<request>" [--json]', json);
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
  }