devflow-kit 1.6.1 → 1.8.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 (203) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/README.md +31 -0
  3. package/dist/cli.js +3 -1
  4. package/dist/commands/hud.d.ts +22 -0
  5. package/dist/commands/hud.js +180 -0
  6. package/dist/commands/init.d.ts +1 -0
  7. package/dist/commands/init.js +114 -5
  8. package/dist/commands/list.js +1 -0
  9. package/dist/commands/uninstall.js +9 -16
  10. package/dist/hud/cache.d.ts +14 -0
  11. package/dist/hud/cache.js +54 -0
  12. package/dist/hud/colors.d.ts +23 -0
  13. package/dist/hud/colors.js +62 -0
  14. package/dist/hud/components/config-counts.d.ts +8 -0
  15. package/dist/hud/components/config-counts.js +91 -0
  16. package/dist/hud/components/context-usage.d.ts +8 -0
  17. package/dist/hud/components/context-usage.js +49 -0
  18. package/dist/hud/components/diff-stats.d.ts +3 -0
  19. package/dist/hud/components/diff-stats.js +36 -0
  20. package/dist/hud/components/directory.d.ts +3 -0
  21. package/dist/hud/components/directory.js +10 -0
  22. package/dist/hud/components/git-ahead-behind.d.ts +3 -0
  23. package/dist/hud/components/git-ahead-behind.js +16 -0
  24. package/dist/hud/components/git-branch.d.ts +3 -0
  25. package/dist/hud/components/git-branch.js +14 -0
  26. package/dist/hud/components/model.d.ts +3 -0
  27. package/dist/hud/components/model.js +21 -0
  28. package/dist/hud/components/release-info.d.ts +3 -0
  29. package/dist/hud/components/release-info.js +9 -0
  30. package/dist/hud/components/session-cost.d.ts +3 -0
  31. package/dist/hud/components/session-cost.js +9 -0
  32. package/dist/hud/components/session-duration.d.ts +3 -0
  33. package/dist/hud/components/session-duration.js +18 -0
  34. package/dist/hud/components/todo-progress.d.ts +3 -0
  35. package/dist/hud/components/todo-progress.js +11 -0
  36. package/dist/hud/components/usage-quota.d.ts +3 -0
  37. package/dist/hud/components/usage-quota.js +45 -0
  38. package/dist/hud/components/version-badge.d.ts +3 -0
  39. package/dist/hud/components/version-badge.js +80 -0
  40. package/dist/hud/components/worktree-count.d.ts +3 -0
  41. package/dist/hud/components/worktree-count.js +8 -0
  42. package/dist/hud/config.d.ts +10 -0
  43. package/dist/hud/config.js +55 -0
  44. package/dist/hud/credentials.d.ts +17 -0
  45. package/dist/hud/credentials.js +99 -0
  46. package/dist/hud/git.d.ts +7 -0
  47. package/dist/hud/git.js +153 -0
  48. package/dist/hud/index.d.ts +2 -0
  49. package/dist/hud/index.js +81 -0
  50. package/dist/hud/render.d.ts +7 -0
  51. package/dist/hud/render.js +111 -0
  52. package/dist/hud/stdin.d.ts +6 -0
  53. package/dist/hud/stdin.js +25 -0
  54. package/dist/hud/transcript.d.ts +7 -0
  55. package/dist/hud/transcript.js +130 -0
  56. package/dist/hud/types.d.ts +116 -0
  57. package/dist/hud/types.js +2 -0
  58. package/dist/hud/usage-api.d.ts +7 -0
  59. package/dist/hud/usage-api.js +84 -0
  60. package/dist/utils/manifest.d.ts +1 -0
  61. package/package.json +5 -2
  62. package/plugins/devflow-accessibility/.claude-plugin/plugin.json +1 -1
  63. package/plugins/devflow-ambient/.claude-plugin/plugin.json +1 -1
  64. package/plugins/devflow-ambient/agents/scrutinizer.md +6 -4
  65. package/plugins/devflow-ambient/agents/shepherd.md +26 -12
  66. package/plugins/devflow-ambient/agents/simplifier.md +24 -15
  67. package/plugins/devflow-ambient/agents/skimmer.md +71 -21
  68. package/plugins/devflow-audit-claude/.claude-plugin/plugin.json +1 -1
  69. package/plugins/devflow-code-review/.claude-plugin/plugin.json +1 -1
  70. package/plugins/devflow-core-skills/.claude-plugin/plugin.json +1 -1
  71. package/plugins/devflow-debug/.claude-plugin/plugin.json +1 -1
  72. package/plugins/devflow-frontend-design/.claude-plugin/plugin.json +1 -1
  73. package/plugins/devflow-go/.claude-plugin/plugin.json +1 -1
  74. package/plugins/devflow-implement/.claude-plugin/plugin.json +1 -1
  75. package/plugins/devflow-implement/agents/scrutinizer.md +6 -4
  76. package/plugins/devflow-implement/agents/shepherd.md +26 -12
  77. package/plugins/devflow-implement/agents/simplifier.md +24 -15
  78. package/plugins/devflow-implement/agents/skimmer.md +71 -21
  79. package/plugins/devflow-implement/commands/implement-teams.md +1 -1
  80. package/plugins/devflow-implement/commands/implement.md +1 -1
  81. package/plugins/devflow-implement/skills/self-review/references/stub-detection.md +135 -0
  82. package/plugins/devflow-java/.claude-plugin/plugin.json +1 -1
  83. package/plugins/devflow-python/.claude-plugin/plugin.json +1 -1
  84. package/plugins/devflow-react/.claude-plugin/plugin.json +1 -1
  85. package/plugins/devflow-resolve/.claude-plugin/plugin.json +1 -1
  86. package/plugins/devflow-resolve/agents/simplifier.md +24 -15
  87. package/plugins/devflow-rust/.claude-plugin/plugin.json +1 -1
  88. package/plugins/devflow-self-review/.claude-plugin/plugin.json +1 -1
  89. package/plugins/devflow-self-review/agents/scrutinizer.md +6 -4
  90. package/plugins/devflow-self-review/agents/simplifier.md +24 -15
  91. package/plugins/devflow-self-review/skills/self-review/references/stub-detection.md +135 -0
  92. package/plugins/devflow-specify/.claude-plugin/plugin.json +1 -1
  93. package/plugins/devflow-specify/agents/skimmer.md +71 -21
  94. package/plugins/devflow-specify/commands/specify-teams.md +1 -1
  95. package/plugins/devflow-specify/commands/specify.md +1 -1
  96. package/plugins/devflow-typescript/.claude-plugin/plugin.json +1 -1
  97. package/scripts/hud/cache.d.ts +14 -0
  98. package/scripts/hud/cache.d.ts.map +1 -0
  99. package/scripts/hud/cache.js +54 -0
  100. package/scripts/hud/cache.js.map +1 -0
  101. package/scripts/hud/colors.d.ts +23 -0
  102. package/scripts/hud/colors.d.ts.map +1 -0
  103. package/scripts/hud/colors.js +62 -0
  104. package/scripts/hud/colors.js.map +1 -0
  105. package/scripts/hud/components/config-counts.d.ts +8 -0
  106. package/scripts/hud/components/config-counts.d.ts.map +1 -0
  107. package/scripts/hud/components/config-counts.js +91 -0
  108. package/scripts/hud/components/config-counts.js.map +1 -0
  109. package/scripts/hud/components/context-usage.d.ts +8 -0
  110. package/scripts/hud/components/context-usage.d.ts.map +1 -0
  111. package/scripts/hud/components/context-usage.js +49 -0
  112. package/scripts/hud/components/context-usage.js.map +1 -0
  113. package/scripts/hud/components/diff-stats.d.ts +3 -0
  114. package/scripts/hud/components/diff-stats.d.ts.map +1 -0
  115. package/scripts/hud/components/diff-stats.js +36 -0
  116. package/scripts/hud/components/diff-stats.js.map +1 -0
  117. package/scripts/hud/components/directory.d.ts +3 -0
  118. package/scripts/hud/components/directory.d.ts.map +1 -0
  119. package/scripts/hud/components/directory.js +10 -0
  120. package/scripts/hud/components/directory.js.map +1 -0
  121. package/scripts/hud/components/git-ahead-behind.d.ts +3 -0
  122. package/scripts/hud/components/git-ahead-behind.d.ts.map +1 -0
  123. package/scripts/hud/components/git-ahead-behind.js +16 -0
  124. package/scripts/hud/components/git-ahead-behind.js.map +1 -0
  125. package/scripts/hud/components/git-branch.d.ts +3 -0
  126. package/scripts/hud/components/git-branch.d.ts.map +1 -0
  127. package/scripts/hud/components/git-branch.js +14 -0
  128. package/scripts/hud/components/git-branch.js.map +1 -0
  129. package/scripts/hud/components/model.d.ts +3 -0
  130. package/scripts/hud/components/model.d.ts.map +1 -0
  131. package/scripts/hud/components/model.js +21 -0
  132. package/scripts/hud/components/model.js.map +1 -0
  133. package/scripts/hud/components/release-info.d.ts +3 -0
  134. package/scripts/hud/components/release-info.d.ts.map +1 -0
  135. package/scripts/hud/components/release-info.js +9 -0
  136. package/scripts/hud/components/release-info.js.map +1 -0
  137. package/scripts/hud/components/session-cost.d.ts +3 -0
  138. package/scripts/hud/components/session-cost.d.ts.map +1 -0
  139. package/scripts/hud/components/session-cost.js +9 -0
  140. package/scripts/hud/components/session-cost.js.map +1 -0
  141. package/scripts/hud/components/session-duration.d.ts +3 -0
  142. package/scripts/hud/components/session-duration.d.ts.map +1 -0
  143. package/scripts/hud/components/session-duration.js +18 -0
  144. package/scripts/hud/components/session-duration.js.map +1 -0
  145. package/scripts/hud/components/todo-progress.d.ts +3 -0
  146. package/scripts/hud/components/todo-progress.d.ts.map +1 -0
  147. package/scripts/hud/components/todo-progress.js +11 -0
  148. package/scripts/hud/components/todo-progress.js.map +1 -0
  149. package/scripts/hud/components/usage-quota.d.ts +3 -0
  150. package/scripts/hud/components/usage-quota.d.ts.map +1 -0
  151. package/scripts/hud/components/usage-quota.js +45 -0
  152. package/scripts/hud/components/usage-quota.js.map +1 -0
  153. package/scripts/hud/components/version-badge.d.ts +3 -0
  154. package/scripts/hud/components/version-badge.d.ts.map +1 -0
  155. package/scripts/hud/components/version-badge.js +80 -0
  156. package/scripts/hud/components/version-badge.js.map +1 -0
  157. package/scripts/hud/components/worktree-count.d.ts +3 -0
  158. package/scripts/hud/components/worktree-count.d.ts.map +1 -0
  159. package/scripts/hud/components/worktree-count.js +8 -0
  160. package/scripts/hud/components/worktree-count.js.map +1 -0
  161. package/scripts/hud/config.d.ts +10 -0
  162. package/scripts/hud/config.d.ts.map +1 -0
  163. package/scripts/hud/config.js +55 -0
  164. package/scripts/hud/config.js.map +1 -0
  165. package/scripts/hud/credentials.d.ts +17 -0
  166. package/scripts/hud/credentials.d.ts.map +1 -0
  167. package/scripts/hud/credentials.js +99 -0
  168. package/scripts/hud/credentials.js.map +1 -0
  169. package/scripts/hud/git.d.ts +7 -0
  170. package/scripts/hud/git.d.ts.map +1 -0
  171. package/scripts/hud/git.js +153 -0
  172. package/scripts/hud/git.js.map +1 -0
  173. package/scripts/hud/index.d.ts +2 -0
  174. package/scripts/hud/index.d.ts.map +1 -0
  175. package/scripts/hud/index.js +81 -0
  176. package/scripts/hud/index.js.map +1 -0
  177. package/scripts/hud/render.d.ts +7 -0
  178. package/scripts/hud/render.d.ts.map +1 -0
  179. package/scripts/hud/render.js +111 -0
  180. package/scripts/hud/render.js.map +1 -0
  181. package/scripts/hud/stdin.d.ts +6 -0
  182. package/scripts/hud/stdin.d.ts.map +1 -0
  183. package/scripts/hud/stdin.js +25 -0
  184. package/scripts/hud/stdin.js.map +1 -0
  185. package/scripts/hud/transcript.d.ts +7 -0
  186. package/scripts/hud/transcript.d.ts.map +1 -0
  187. package/scripts/hud/transcript.js +130 -0
  188. package/scripts/hud/transcript.js.map +1 -0
  189. package/scripts/hud/types.d.ts +116 -0
  190. package/scripts/hud/types.d.ts.map +1 -0
  191. package/scripts/hud/types.js +2 -0
  192. package/scripts/hud/types.js.map +1 -0
  193. package/scripts/hud/usage-api.d.ts +7 -0
  194. package/scripts/hud/usage-api.d.ts.map +1 -0
  195. package/scripts/hud/usage-api.js +84 -0
  196. package/scripts/hud/usage-api.js.map +1 -0
  197. package/scripts/hud.sh +5 -0
  198. package/shared/agents/scrutinizer.md +6 -4
  199. package/shared/agents/shepherd.md +26 -12
  200. package/shared/agents/simplifier.md +24 -15
  201. package/shared/agents/skimmer.md +71 -21
  202. package/shared/skills/self-review/references/stub-detection.md +135 -0
  203. package/src/templates/settings.json +1 -1
package/CHANGELOG.md CHANGED
@@ -5,6 +5,49 @@ All notable changes to DevFlow will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.8.0] - 2026-03-22
9
+
10
+ ### Added
11
+ - **Configurable HUD** replacing bash statusline — 14 components, on/off model (#155)
12
+ - **HUD components**: directory, git branch, ahead/behind, diff stats, release info, worktree count, model, context usage, version badge, session duration, session cost, usage quota, todo progress, config counts
13
+ - **`--hud-only` flag** for standalone HUD install
14
+ - **`--no-hud` flag** to skip HUD during init
15
+ - **`devflow hud` command** (--status, --enable, --disable, --detail, --no-detail)
16
+ - **Version upgrade notification**: `✦ Devflow vX.Y.Z · update: npx devflow-kit init` (yellow, always visible even when HUD disabled)
17
+ - **Skill shadowing docs** and HUD options added to README (#156)
18
+ - **Simplifier agent** — 8 structured slop detection categories (#120)
19
+ - **Scrutinizer agent** — stub detection patterns with reference file (#121)
20
+ - **Shepherd agent** — goal-backward verification, artifact depth checking, stub type, re-verification (#124)
21
+
22
+ ### Changed
23
+ - Init flow: HUD preset picker (5 options) → simple yes/no confirm
24
+ - `--disable` keeps statusLine registered (version badge still renders)
25
+ - Manifest `features.hud` field: `string|false` → `boolean`
26
+
27
+ ### Fixed
28
+ - HUD base branch detection matching raw commit hashes from reflog (#156)
29
+ - HUD comparing main vs main (0/0 always) — now compares against origin/main
30
+
31
+ ### Removed
32
+ - HUD preset system (minimal/classic/standard/full)
33
+ - `--configure`, `--preset`, `--hud <preset>` flags
34
+ - `speed`, `tool-activity`, `agent-activity` components
35
+
36
+ ---
37
+
38
+ ## [1.7.0] - 2026-03-20
39
+
40
+ ### Added
41
+ - **Version update notification** — statusline shows magenta `⬆ X.Y.Z` badge when newer devflow-kit is available (24h cached npm check, fully async)
42
+
43
+ ### Fixed
44
+ - **Skimmer agent** — enforce rskim usage via `tools: ["Bash", "Read"]` platform restriction and strict sequential workflow; prevents fallback to Grep/Glob
45
+ - **Init multiselect** — remove redundant "(optional)" suffix from plugin hints
46
+ - **Init multiselect** — hide `audit-claude` plugin (not production-ready; still installable via `--plugin=audit-claude`)
47
+ - **Statusline portability** — replace macOS-only `stat -f %m` with portable `get_mtime()` helper (macOS + Linux)
48
+
49
+ ---
50
+
8
51
  ## [1.6.1] - 2026-03-20
9
52
 
10
53
  ### Added
@@ -915,6 +958,8 @@ devflow init
915
958
  ---
916
959
 
917
960
  [Unreleased]: https://github.com/dean0x/devflow/compare/v1.4.0...HEAD
961
+ [1.8.0]: https://github.com/dean0x/devflow/compare/v1.7.0...v1.8.0
962
+ [1.7.0]: https://github.com/dean0x/devflow/compare/v1.6.1...v1.7.0
918
963
  [1.6.1]: https://github.com/dean0x/devflow/compare/v1.6.0...v1.6.1
919
964
  [1.6.0]: https://github.com/dean0x/devflow/compare/v1.5.0...v1.6.0
920
965
  [1.5.0]: https://github.com/dean0x/devflow/compare/v1.4.0...v1.5.0
package/README.md CHANGED
@@ -24,6 +24,7 @@ DevFlow adds structured commands that handle the full lifecycle: specify feature
24
24
  - **Full-lifecycle implementation** — spec, explore, plan, code, validate, refine in one command
25
25
  - **Automatic session memory** — survives restarts, `/clear`, and context compaction
26
26
  - **Parallel debugging** — competing hypotheses investigated simultaneously
27
+ - **Version notifications** — HUD shows `✦ Devflow vX.Y.Z · update: npx devflow-kit init` when a newer version is available
27
28
  - **35 quality skills** — 9 auto-activating core, 8 optional language/ecosystem, plus specialized review, agent, and orchestration skills
28
29
 
29
30
  ## Quick Start
@@ -247,8 +248,38 @@ Session context is saved and restored automatically via Working Memory hooks —
247
248
  | `--teams` / `--no-teams` | Enable/disable Agent Teams (experimental, default: off) |
248
249
  | `--ambient` / `--no-ambient` | Enable/disable ambient mode (default: on) |
249
250
  | `--memory` / `--no-memory` | Enable/disable working memory (default: on) |
251
+ | `--hud-only` | Install only the HUD (no plugins, hooks, or extras) |
252
+ | `--no-hud` | Disable HUD status line |
250
253
  | `--verbose` | Show detailed output |
251
254
 
255
+ ### HUD Options
256
+
257
+ | Command | Description |
258
+ |---------|-------------|
259
+ | `npx devflow-kit hud --status` | Show current HUD config |
260
+ | `npx devflow-kit hud --enable` | Enable HUD |
261
+ | `npx devflow-kit hud --disable` | Disable HUD (version notifications still appear) |
262
+ | `npx devflow-kit hud --detail` | Show tool/agent descriptions |
263
+ | `npx devflow-kit hud --no-detail` | Hide tool/agent descriptions |
264
+
265
+ ### Skill Shadowing
266
+
267
+ Override any DevFlow skill with your own version. Shadowed skills survive `devflow init` — they won't be overwritten on reinstall or upgrade.
268
+
269
+ ```bash
270
+ # Create a personal override (copies current version as reference)
271
+ npx devflow-kit skills shadow core-patterns
272
+
273
+ # Edit your override
274
+ vim ~/.claude/skills/core-patterns/SKILL.md
275
+
276
+ # List all overrides
277
+ npx devflow-kit skills list-shadowed
278
+
279
+ # Remove override (next init restores DevFlow's version)
280
+ npx devflow-kit skills unshadow core-patterns
281
+ ```
282
+
252
283
  ### Uninstall Options
253
284
 
254
285
  | Option | Description |
package/dist/cli.js CHANGED
@@ -9,6 +9,7 @@ import { listCommand } from './commands/list.js';
9
9
  import { ambientCommand } from './commands/ambient.js';
10
10
  import { memoryCommand } from './commands/memory.js';
11
11
  import { skillsCommand } from './commands/skills.js';
12
+ import { hudCommand } from './commands/hud.js';
12
13
  const __filename = fileURLToPath(import.meta.url);
13
14
  const __dirname = dirname(__filename);
14
15
  // Read version from package.json
@@ -19,7 +20,7 @@ program
19
20
  .description('Agentic Development Toolkit for Claude Code\n\nEnhance your AI-assisted development with intelligent commands and workflows.')
20
21
  .version(packageJson.version, '-v, --version', 'Display version number')
21
22
  .helpOption('-h, --help', 'Display help information')
22
- .addHelpText('after', '\nExamples:\n $ devflow init Install all DevFlow plugins\n $ devflow init --plugin=implement Install specific plugin\n $ devflow init --plugin=implement,review Install multiple plugins\n $ devflow list List available plugins\n $ devflow ambient --enable Enable always-on ambient mode\n $ devflow memory --status Check working memory state\n $ devflow uninstall Remove DevFlow from Claude Code\n $ devflow --version Show version\n $ devflow --help Show help\n\nDocumentation:\n https://github.com/dean0x/devflow#readme');
23
+ .addHelpText('after', '\nExamples:\n $ devflow init Install all DevFlow plugins\n $ devflow init --plugin=implement Install specific plugin\n $ devflow init --plugin=implement,review Install multiple plugins\n $ devflow list List available plugins\n $ devflow ambient --enable Enable always-on ambient mode\n $ devflow memory --status Check working memory state\n $ devflow hud --configure Configure HUD preset\n $ devflow uninstall Remove DevFlow from Claude Code\n $ devflow --version Show version\n $ devflow --help Show help\n\nDocumentation:\n https://github.com/dean0x/devflow#readme');
23
24
  // Register commands
24
25
  program.addCommand(initCommand);
25
26
  program.addCommand(uninstallCommand);
@@ -27,6 +28,7 @@ program.addCommand(listCommand);
27
28
  program.addCommand(ambientCommand);
28
29
  program.addCommand(memoryCommand);
29
30
  program.addCommand(skillsCommand);
31
+ program.addCommand(hudCommand);
30
32
  // Handle no command
31
33
  program.action(() => {
32
34
  program.help();
@@ -0,0 +1,22 @@
1
+ import { Command } from 'commander';
2
+ /**
3
+ * Add the HUD statusLine to settings JSON.
4
+ * Idempotent — returns unchanged JSON if HUD already set.
5
+ * Upgrades legacy statusline.sh to hud.sh automatically.
6
+ */
7
+ export declare function addHudStatusLine(settingsJson: string, devflowDir: string): string;
8
+ /**
9
+ * Remove the HUD statusLine from settings JSON.
10
+ * Idempotent — returns unchanged JSON if statusLine not present or not DevFlow.
11
+ */
12
+ export declare function removeHudStatusLine(settingsJson: string): string;
13
+ /**
14
+ * Check if the statusLine in settings JSON points to the DevFlow HUD.
15
+ */
16
+ export declare function hasHudStatusLine(settingsJson: string): boolean;
17
+ /**
18
+ * Check if an existing statusLine belongs to a non-DevFlow tool.
19
+ */
20
+ export declare function hasNonDevFlowStatusLine(settingsJson: string): boolean;
21
+ export declare const hudCommand: Command;
22
+ //# sourceMappingURL=hud.d.ts.map
@@ -0,0 +1,180 @@
1
+ import { Command } from 'commander';
2
+ import { promises as fs } from 'node:fs';
3
+ import * as path from 'node:path';
4
+ import * as p from '@clack/prompts';
5
+ import color from 'picocolors';
6
+ import { getClaudeDirectory, getDevFlowDirectory } from '../utils/paths.js';
7
+ import { HUD_COMPONENTS, loadConfig, saveConfig, } from '../hud/config.js';
8
+ /**
9
+ * Add the HUD statusLine to settings JSON.
10
+ * Idempotent — returns unchanged JSON if HUD already set.
11
+ * Upgrades legacy statusline.sh to hud.sh automatically.
12
+ */
13
+ export function addHudStatusLine(settingsJson, devflowDir) {
14
+ const settings = JSON.parse(settingsJson);
15
+ const hudCommand = path.join(devflowDir, 'scripts', 'hud.sh');
16
+ // Already pointing to this exact HUD — nothing to do
17
+ if (settings.statusLine?.command === hudCommand) {
18
+ return settingsJson;
19
+ }
20
+ // If there's a non-DevFlow statusLine, don't overwrite (caller should check first)
21
+ if (settings.statusLine && !isDevFlowStatusLine(settings.statusLine)) {
22
+ return settingsJson;
23
+ }
24
+ settings.statusLine = {
25
+ type: 'command',
26
+ command: hudCommand,
27
+ };
28
+ return JSON.stringify(settings, null, 2) + '\n';
29
+ }
30
+ /**
31
+ * Remove the HUD statusLine from settings JSON.
32
+ * Idempotent — returns unchanged JSON if statusLine not present or not DevFlow.
33
+ */
34
+ export function removeHudStatusLine(settingsJson) {
35
+ const settings = JSON.parse(settingsJson);
36
+ if (!settings.statusLine) {
37
+ return settingsJson;
38
+ }
39
+ // Only remove if it's a DevFlow HUD/statusline
40
+ if (!isDevFlowStatusLine(settings.statusLine)) {
41
+ return settingsJson;
42
+ }
43
+ delete settings.statusLine;
44
+ return JSON.stringify(settings, null, 2) + '\n';
45
+ }
46
+ /**
47
+ * Check if the statusLine in settings JSON points to the DevFlow HUD.
48
+ */
49
+ export function hasHudStatusLine(settingsJson) {
50
+ const settings = JSON.parse(settingsJson);
51
+ if (!settings.statusLine)
52
+ return false;
53
+ return isDevFlowStatusLine(settings.statusLine);
54
+ }
55
+ /**
56
+ * Check if an existing statusLine belongs to DevFlow (HUD or legacy statusline).
57
+ * Matches paths containing 'hud.sh', 'statusline.sh', or a '/devflow/' directory segment.
58
+ */
59
+ function isDevFlowStatusLine(statusLine) {
60
+ const cmd = statusLine.command ?? '';
61
+ return (cmd.includes('hud.sh') ||
62
+ cmd.includes('statusline.sh') ||
63
+ cmd.includes('/devflow/') ||
64
+ cmd.includes('\\devflow\\'));
65
+ }
66
+ /**
67
+ * Check if an existing statusLine belongs to a non-DevFlow tool.
68
+ */
69
+ export function hasNonDevFlowStatusLine(settingsJson) {
70
+ const settings = JSON.parse(settingsJson);
71
+ if (!settings.statusLine?.command)
72
+ return false;
73
+ return !isDevFlowStatusLine(settings.statusLine);
74
+ }
75
+ export const hudCommand = new Command('hud')
76
+ .description('Configure the HUD (status line)')
77
+ .option('--status', 'Show current HUD config')
78
+ .option('--detail', 'Show tool/agent descriptions in HUD')
79
+ .option('--no-detail', 'Hide tool/agent descriptions')
80
+ .option('--enable', 'Enable HUD in settings')
81
+ .option('--disable', 'Disable HUD (remove statusLine)')
82
+ .action(async (options) => {
83
+ const hasFlag = options.status ||
84
+ options.enable ||
85
+ options.disable ||
86
+ options.detail !== undefined;
87
+ if (!hasFlag) {
88
+ p.intro(color.bgCyan(color.white(' HUD ')));
89
+ p.note(`${color.cyan('devflow hud --detail')} Show tool/agent descriptions\n` +
90
+ `${color.cyan('devflow hud --no-detail')} Hide tool/agent descriptions\n` +
91
+ `${color.cyan('devflow hud --status')} Show current config\n` +
92
+ `${color.cyan('devflow hud --enable')} Enable HUD in settings\n` +
93
+ `${color.cyan('devflow hud --disable')} Remove HUD from settings`, 'Usage');
94
+ p.note(`${HUD_COMPONENTS.length} components: ${HUD_COMPONENTS.join(', ')}`, 'Components');
95
+ p.outro(color.dim('Toggle with --enable / --disable'));
96
+ return;
97
+ }
98
+ if (options.status) {
99
+ const config = loadConfig();
100
+ p.intro(color.bgCyan(color.white(' HUD Status ')));
101
+ p.note(`${color.dim('Enabled:')} ${config.enabled ? color.green('yes') : color.dim('no')}\n` +
102
+ `${color.dim('Detail:')} ${config.detail ? color.green('on') : color.dim('off')}\n` +
103
+ `${color.dim('Components:')} ${HUD_COMPONENTS.length}`, 'Current config');
104
+ // Check settings.json
105
+ const claudeDir = getClaudeDirectory();
106
+ const settingsPath = path.join(claudeDir, 'settings.json');
107
+ try {
108
+ const content = await fs.readFile(settingsPath, 'utf-8');
109
+ const enabled = hasHudStatusLine(content);
110
+ p.log.info(`Status line: ${enabled ? color.green('enabled') : color.dim('disabled')}`);
111
+ }
112
+ catch {
113
+ p.log.info(`Status line: ${color.dim('no settings.json found')}`);
114
+ }
115
+ return;
116
+ }
117
+ if (options.detail !== undefined) {
118
+ const config = loadConfig();
119
+ config.detail = options.detail;
120
+ saveConfig(config);
121
+ p.log.success(`HUD detail ${config.detail ? 'enabled' : 'disabled'}`);
122
+ return;
123
+ }
124
+ if (options.enable) {
125
+ const claudeDir = getClaudeDirectory();
126
+ const settingsPath = path.join(claudeDir, 'settings.json');
127
+ let settingsContent;
128
+ try {
129
+ settingsContent = await fs.readFile(settingsPath, 'utf-8');
130
+ }
131
+ catch {
132
+ settingsContent = '{}';
133
+ }
134
+ // Ensure statusLine is registered
135
+ if (!hasHudStatusLine(settingsContent)) {
136
+ // Check for non-DevFlow statusLine
137
+ if (hasNonDevFlowStatusLine(settingsContent)) {
138
+ const settings = JSON.parse(settingsContent);
139
+ p.log.warn(`Existing statusLine found: ${color.dim(settings.statusLine?.command ?? 'unknown')}`);
140
+ if (process.stdin.isTTY) {
141
+ const overwrite = await p.confirm({
142
+ message: 'Replace existing statusLine with DevFlow HUD?',
143
+ initialValue: false,
144
+ });
145
+ if (p.isCancel(overwrite) || !overwrite) {
146
+ p.log.info('HUD not enabled — existing statusLine preserved');
147
+ return;
148
+ }
149
+ }
150
+ else {
151
+ p.log.info('Non-interactive mode — skipping (existing statusLine would be overwritten)');
152
+ return;
153
+ }
154
+ }
155
+ const devflowDir = getDevFlowDirectory();
156
+ const updated = addHudStatusLine(settingsContent, devflowDir);
157
+ await fs.writeFile(settingsPath, updated, 'utf-8');
158
+ }
159
+ // Update config
160
+ const config = loadConfig();
161
+ if (config.enabled) {
162
+ p.log.info('HUD already enabled');
163
+ return;
164
+ }
165
+ saveConfig({ ...config, enabled: true });
166
+ p.log.success('HUD enabled');
167
+ p.log.info(color.dim('Restart Claude Code to see the HUD'));
168
+ }
169
+ if (options.disable) {
170
+ const config = loadConfig();
171
+ if (!config.enabled) {
172
+ p.log.info('HUD already disabled');
173
+ return;
174
+ }
175
+ saveConfig({ ...config, enabled: false });
176
+ p.log.success('HUD disabled');
177
+ p.log.info(color.dim('Version upgrade notifications will still appear'));
178
+ }
179
+ });
180
+ //# sourceMappingURL=hud.js.map
@@ -3,6 +3,7 @@ import { type PluginDefinition } from '../plugins.js';
3
3
  export { substituteSettingsTemplate, computeGitignoreAppend, applyTeamsConfig, stripTeamsConfig, mergeDenyList } from '../utils/post-install.js';
4
4
  export { addAmbientHook, removeAmbientHook, hasAmbientHook } from './ambient.js';
5
5
  export { addMemoryHooks, removeMemoryHooks, hasMemoryHooks } from './memory.js';
6
+ export { addHudStatusLine, removeHudStatusLine, hasHudStatusLine } from './hud.js';
6
7
  /**
7
8
  * Parse a comma-separated plugin selection string into normalized plugin names.
8
9
  * Validates against known plugins; returns invalid names as errors.
@@ -8,18 +8,21 @@ import color from 'picocolors';
8
8
  import { getInstallationPaths } from '../utils/paths.js';
9
9
  import { getGitRoot } from '../utils/git.js';
10
10
  import { isClaudeCliAvailable } from '../utils/cli.js';
11
- import { installViaCli, installViaFileCopy } from '../utils/installer.js';
11
+ import { installViaCli, installViaFileCopy, copyDirectory } from '../utils/installer.js';
12
12
  import { installSettings, installManagedSettings, installClaudeignore, updateGitignore, createDocsStructure, createMemoryDir, migrateMemoryFiles, } from '../utils/post-install.js';
13
13
  import { DEVFLOW_PLUGINS, LEGACY_SKILL_NAMES, LEGACY_COMMAND_NAMES, buildAssetMaps } from '../plugins.js';
14
14
  import { detectPlatform, detectShell, getProfilePath, getSafeDeleteInfo, hasSafeDelete } from '../utils/safe-delete.js';
15
15
  import { generateSafeDeleteBlock, installToProfile, removeFromProfile, getInstalledVersion, SAFE_DELETE_BLOCK_VERSION } from '../utils/safe-delete-install.js';
16
16
  import { addAmbientHook } from './ambient.js';
17
17
  import { addMemoryHooks, removeMemoryHooks } from './memory.js';
18
+ import { addHudStatusLine, removeHudStatusLine } from './hud.js';
19
+ import { loadConfig as loadHudConfig, saveConfig as saveHudConfig } from '../hud/config.js';
18
20
  import { readManifest, writeManifest, resolvePluginList, detectUpgrade } from '../utils/manifest.js';
19
21
  // Re-export pure functions for tests (canonical source is post-install.ts)
20
22
  export { substituteSettingsTemplate, computeGitignoreAppend, applyTeamsConfig, stripTeamsConfig, mergeDenyList } from '../utils/post-install.js';
21
23
  export { addAmbientHook, removeAmbientHook, hasAmbientHook } from './ambient.js';
22
24
  export { addMemoryHooks, removeMemoryHooks, hasMemoryHooks } from './memory.js';
25
+ export { addHudStatusLine, removeHudStatusLine, hasHudStatusLine } from './hud.js';
23
26
  const __filename = fileURLToPath(import.meta.url);
24
27
  const __dirname = dirname(__filename);
25
28
  /**
@@ -66,6 +69,8 @@ export const initCommand = new Command('init')
66
69
  .option('--no-ambient', 'Disable ambient mode')
67
70
  .option('--memory', 'Enable working memory (session context preservation)')
68
71
  .option('--no-memory', 'Disable working memory hooks')
72
+ .option('--no-hud', 'Disable HUD status line')
73
+ .option('--hud-only', 'Install only the HUD (no plugins, hooks, or extras)')
69
74
  .action(async (options) => {
70
75
  // Get package version
71
76
  const packageJsonPath = path.resolve(__dirname, '../../package.json');
@@ -82,7 +87,11 @@ export const initCommand = new Command('init')
82
87
  p.intro(color.bgCyan(color.black(` DevFlow v${version} `)));
83
88
  // Determine installation scope
84
89
  let scope = 'user';
85
- if (options.scope) {
90
+ if (options.hudOnly) {
91
+ // --hud-only: skip scope prompt, always user scope
92
+ scope = 'user';
93
+ }
94
+ else if (options.scope) {
86
95
  const normalizedScope = options.scope.toLowerCase();
87
96
  if (normalizedScope !== 'user' && normalizedScope !== 'local') {
88
97
  p.log.error('Invalid scope. Use "user" or "local"');
@@ -108,6 +117,70 @@ export const initCommand = new Command('init')
108
117
  }
109
118
  scope = selected;
110
119
  }
120
+ // --hud-only: install only HUD (skip plugins, hooks, extras)
121
+ if (options.hudOnly) {
122
+ // Resolve paths
123
+ const paths = await getInstallationPaths(scope);
124
+ const claudeDir = paths.claudeDir;
125
+ const devflowDir = paths.devflowDir;
126
+ // Save HUD config
127
+ const existingHud = loadHudConfig();
128
+ saveHudConfig({ enabled: true, detail: existingHud.detail });
129
+ // Update statusLine in settings.json
130
+ const settingsPath = path.join(claudeDir, 'settings.json');
131
+ try {
132
+ let content;
133
+ try {
134
+ content = await fs.readFile(settingsPath, 'utf-8');
135
+ }
136
+ catch {
137
+ content = '{}';
138
+ }
139
+ const updated = addHudStatusLine(content, devflowDir);
140
+ await fs.writeFile(settingsPath, updated, 'utf-8');
141
+ }
142
+ catch (error) {
143
+ p.log.error(`Failed to update settings: ${error instanceof Error ? error.message : error}`);
144
+ process.exit(1);
145
+ }
146
+ // Copy HUD scripts to devflow dir
147
+ const rootDir = path.resolve(__dirname, '..', '..');
148
+ const scriptsSource = path.join(rootDir, 'scripts');
149
+ const scriptsTarget = path.join(devflowDir, 'scripts');
150
+ try {
151
+ await fs.mkdir(scriptsTarget, { recursive: true });
152
+ // Copy hud.sh
153
+ await fs.copyFile(path.join(scriptsSource, 'hud.sh'), path.join(scriptsTarget, 'hud.sh'));
154
+ // Copy hud/ directory
155
+ const hudSource = path.join(scriptsSource, 'hud');
156
+ const hudTarget = path.join(scriptsTarget, 'hud');
157
+ await copyDirectory(hudSource, hudTarget);
158
+ if (process.platform !== 'win32') {
159
+ await fs.chmod(path.join(scriptsTarget, 'hud.sh'), 0o755);
160
+ }
161
+ }
162
+ catch (error) {
163
+ p.log.error(`Failed to copy HUD scripts: ${error instanceof Error ? error.message : error}`);
164
+ process.exit(1);
165
+ }
166
+ // Write minimal manifest
167
+ const now = new Date().toISOString();
168
+ try {
169
+ await writeManifest(devflowDir, {
170
+ version,
171
+ plugins: [],
172
+ scope,
173
+ features: { teams: false, ambient: false, memory: false, hud: true },
174
+ installedAt: now,
175
+ updatedAt: now,
176
+ });
177
+ }
178
+ catch { /* non-fatal */ }
179
+ p.log.success('HUD installed');
180
+ p.log.info(`Configure later: ${color.cyan('devflow hud --status')}`);
181
+ p.outro(color.green('HUD-only install complete.'));
182
+ return;
183
+ }
111
184
  // Select plugins to install
112
185
  let selectedPlugins = [];
113
186
  if (options.plugin) {
@@ -121,11 +194,11 @@ export const initCommand = new Command('init')
121
194
  }
122
195
  else if (process.stdin.isTTY) {
123
196
  const choices = DEVFLOW_PLUGINS
124
- .filter(pl => pl.name !== 'devflow-core-skills' && pl.name !== 'devflow-ambient')
197
+ .filter(pl => pl.name !== 'devflow-core-skills' && pl.name !== 'devflow-ambient' && pl.name !== 'devflow-audit-claude')
125
198
  .map(pl => ({
126
199
  value: pl.name,
127
200
  label: pl.name.replace('devflow-', ''),
128
- hint: pl.description + (pl.optional ? ' (optional)' : ''),
201
+ hint: pl.description,
129
202
  }));
130
203
  const preSelected = DEVFLOW_PLUGINS
131
204
  .filter(pl => !pl.optional && pl.name !== 'devflow-core-skills' && pl.name !== 'devflow-ambient')
@@ -205,6 +278,25 @@ export const initCommand = new Command('init')
205
278
  }
206
279
  memoryEnabled = memoryChoice;
207
280
  }
281
+ // HUD selection (yes/no)
282
+ let hudEnabled;
283
+ if (options.hud !== undefined) {
284
+ hudEnabled = options.hud;
285
+ }
286
+ else if (!process.stdin.isTTY) {
287
+ hudEnabled = true;
288
+ }
289
+ else {
290
+ const hudChoice = await p.confirm({
291
+ message: 'Enable HUD status line?',
292
+ initialValue: true,
293
+ });
294
+ if (p.isCancel(hudChoice)) {
295
+ p.cancel('Installation cancelled.');
296
+ process.exit(0);
297
+ }
298
+ hudEnabled = hudChoice;
299
+ }
208
300
  // Security deny list placement (user scope + TTY only)
209
301
  let securityMode = 'user';
210
302
  if (scope === 'user' && process.stdin.isTTY) {
@@ -442,6 +534,23 @@ export const initCommand = new Command('init')
442
534
  await createMemoryDir(verbose);
443
535
  await migrateMemoryFiles(verbose);
444
536
  }
537
+ // Configure HUD
538
+ const existingHud = loadHudConfig();
539
+ saveHudConfig({ enabled: hudEnabled, detail: existingHud.detail });
540
+ // Update statusLine in settings.json (add or remove based on choice)
541
+ try {
542
+ const hudContent = await fs.readFile(settingsPath, 'utf-8');
543
+ const hudUpdated = hudEnabled
544
+ ? addHudStatusLine(hudContent, devflowDir)
545
+ : removeHudStatusLine(hudContent);
546
+ if (hudUpdated !== hudContent) {
547
+ await fs.writeFile(settingsPath, hudUpdated, 'utf-8');
548
+ if (verbose) {
549
+ p.log.info(`HUD ${hudEnabled ? 'enabled' : 'disabled'}`);
550
+ }
551
+ }
552
+ }
553
+ catch { /* settings.json may not exist yet */ }
445
554
  }
446
555
  const fileExtras = selectedExtras.filter(e => e !== 'settings' && e !== 'safe-delete');
447
556
  if (fileExtras.length > 0) {
@@ -542,7 +651,7 @@ export const initCommand = new Command('init')
542
651
  version,
543
652
  plugins: resolvePluginList(installedPluginNames, existingManifest, !!options.plugin),
544
653
  scope,
545
- features: { teams: teamsEnabled, ambient: ambientEnabled, memory: memoryEnabled },
654
+ features: { teams: teamsEnabled, ambient: ambientEnabled, memory: memoryEnabled, hud: hudEnabled },
546
655
  installedAt: existingManifest?.installedAt ?? now,
547
656
  updatedAt: now,
548
657
  };
@@ -15,6 +15,7 @@ export function formatFeatures(features) {
15
15
  features.teams ? 'teams' : null,
16
16
  features.ambient ? 'ambient' : null,
17
17
  features.memory ? 'memory' : null,
18
+ features.hud ? 'hud' : null,
18
19
  ].filter(Boolean).join(', ') || 'none';
19
20
  }
20
21
  /**
@@ -11,6 +11,7 @@ import { isClaudeCliAvailable } from '../utils/cli.js';
11
11
  import { DEVFLOW_PLUGINS, getAllSkillNames, LEGACY_SKILL_NAMES } from '../plugins.js';
12
12
  import { removeAmbientHook } from './ambient.js';
13
13
  import { removeMemoryHooks } from './memory.js';
14
+ import { removeHudStatusLine } from './hud.js';
14
15
  import { listShadowed } from './skills.js';
15
16
  import { detectShell, getProfilePath } from '../utils/safe-delete.js';
16
17
  import { isAlreadyInstalled, removeFromProfile } from '../utils/safe-delete-install.js';
@@ -360,23 +361,15 @@ export const uninstallCommand = new Command('uninstall')
360
361
  try {
361
362
  const paths = await getInstallationPaths(scope);
362
363
  const settingsPath = path.join(paths.claudeDir, 'settings.json');
363
- let settingsContent = await fs.readFile(settingsPath, 'utf-8');
364
- // Always remove ambient hook on full uninstall (idempotent)
365
- const withoutAmbient = removeAmbientHook(settingsContent);
366
- if (withoutAmbient !== settingsContent) {
367
- await fs.writeFile(settingsPath, withoutAmbient, 'utf-8');
368
- settingsContent = withoutAmbient;
364
+ const originalContent = await fs.readFile(settingsPath, 'utf-8');
365
+ // Remove all DevFlow hooks in one pass (idempotent)
366
+ let settingsContent = removeAmbientHook(originalContent);
367
+ settingsContent = removeMemoryHooks(settingsContent);
368
+ settingsContent = removeHudStatusLine(settingsContent);
369
+ if (settingsContent !== originalContent) {
370
+ await fs.writeFile(settingsPath, settingsContent, 'utf-8');
369
371
  if (verbose) {
370
- p.log.success(`Ambient mode hook removed from settings.json (${scope})`);
371
- }
372
- }
373
- // Always remove memory hooks on full uninstall (idempotent)
374
- const withoutMemory = removeMemoryHooks(settingsContent);
375
- if (withoutMemory !== settingsContent) {
376
- await fs.writeFile(settingsPath, withoutMemory, 'utf-8');
377
- settingsContent = withoutMemory;
378
- if (verbose) {
379
- p.log.success(`Memory hooks removed from settings.json (${scope})`);
372
+ p.log.success(`DevFlow hooks removed from settings.json (${scope})`);
380
373
  }
381
374
  }
382
375
  const settings = JSON.parse(settingsContent);
@@ -0,0 +1,14 @@
1
+ export declare function getCacheDir(): string;
2
+ /**
3
+ * Read a cached value. Returns null if missing or expired.
4
+ */
5
+ export declare function readCache<T>(key: string): T | null;
6
+ /**
7
+ * Read a cached value regardless of TTL (stale data). Returns null if missing.
8
+ */
9
+ export declare function readCacheStale<T>(key: string): T | null;
10
+ /**
11
+ * Write a value to cache with a TTL in milliseconds.
12
+ */
13
+ export declare function writeCache<T>(key: string, data: T, ttlMs: number): void;
14
+ //# sourceMappingURL=cache.d.ts.map
@@ -0,0 +1,54 @@
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ import { homedir } from 'node:os';
4
+ export function getCacheDir() {
5
+ const devflowDir = process.env.DEVFLOW_DIR || path.join(process.env.HOME || homedir(), '.devflow');
6
+ return path.join(devflowDir, 'cache');
7
+ }
8
+ /**
9
+ * Read a cached value. Returns null if missing or expired.
10
+ * When `ignoreExpiry` is true, returns data regardless of TTL (stale read).
11
+ */
12
+ function readCacheEntry(key, ignoreExpiry) {
13
+ try {
14
+ const filePath = path.join(getCacheDir(), `${key}.json`);
15
+ const raw = fs.readFileSync(filePath, 'utf-8');
16
+ const entry = JSON.parse(raw);
17
+ if (ignoreExpiry || Date.now() - entry.timestamp < entry.ttl) {
18
+ return entry.data;
19
+ }
20
+ return null;
21
+ }
22
+ catch {
23
+ return null;
24
+ }
25
+ }
26
+ /**
27
+ * Read a cached value. Returns null if missing or expired.
28
+ */
29
+ export function readCache(key) {
30
+ return readCacheEntry(key, false);
31
+ }
32
+ /**
33
+ * Read a cached value regardless of TTL (stale data). Returns null if missing.
34
+ */
35
+ export function readCacheStale(key) {
36
+ return readCacheEntry(key, true);
37
+ }
38
+ /**
39
+ * Write a value to cache with a TTL in milliseconds.
40
+ */
41
+ export function writeCache(key, data, ttlMs) {
42
+ try {
43
+ const dir = getCacheDir();
44
+ if (!fs.existsSync(dir)) {
45
+ fs.mkdirSync(dir, { recursive: true });
46
+ }
47
+ const entry = { data, timestamp: Date.now(), ttl: ttlMs };
48
+ fs.writeFileSync(path.join(dir, `${key}.json`), JSON.stringify(entry));
49
+ }
50
+ catch {
51
+ // Cache write failure is non-fatal
52
+ }
53
+ }
54
+ //# sourceMappingURL=cache.js.map