devflow-kit 1.0.0 → 1.2.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 (134) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/README.md +35 -11
  3. package/dist/cli.js +5 -1
  4. package/dist/commands/ambient.d.ts +18 -0
  5. package/dist/commands/ambient.js +136 -0
  6. package/dist/commands/init.d.ts +2 -0
  7. package/dist/commands/init.js +97 -10
  8. package/dist/commands/memory.d.ts +22 -0
  9. package/dist/commands/memory.js +175 -0
  10. package/dist/commands/uninstall.js +72 -5
  11. package/dist/plugins.js +74 -3
  12. package/dist/utils/post-install.d.ts +12 -0
  13. package/dist/utils/post-install.js +82 -1
  14. package/dist/utils/safe-delete-install.d.ts +7 -0
  15. package/dist/utils/safe-delete-install.js +40 -5
  16. package/package.json +2 -1
  17. package/plugins/devflow-accessibility/.claude-plugin/plugin.json +15 -0
  18. package/plugins/devflow-ambient/.claude-plugin/plugin.json +7 -0
  19. package/plugins/devflow-ambient/README.md +49 -0
  20. package/plugins/devflow-ambient/commands/ambient.md +110 -0
  21. package/plugins/devflow-ambient/skills/ambient-router/SKILL.md +89 -0
  22. package/plugins/devflow-ambient/skills/ambient-router/references/skill-catalog.md +68 -0
  23. package/plugins/devflow-audit-claude/.claude-plugin/plugin.json +1 -1
  24. package/plugins/devflow-code-review/.claude-plugin/plugin.json +1 -4
  25. package/plugins/devflow-code-review/agents/reviewer.md +8 -0
  26. package/plugins/devflow-code-review/commands/code-review-teams.md +11 -1
  27. package/plugins/devflow-code-review/commands/code-review.md +12 -2
  28. package/plugins/devflow-core-skills/.claude-plugin/plugin.json +3 -6
  29. package/plugins/devflow-core-skills/skills/docs-framework/SKILL.md +10 -6
  30. package/plugins/devflow-core-skills/skills/test-driven-development/SKILL.md +139 -0
  31. package/plugins/devflow-core-skills/skills/test-driven-development/references/rationalization-prevention.md +111 -0
  32. package/plugins/devflow-debug/.claude-plugin/plugin.json +1 -1
  33. package/plugins/devflow-frontend-design/.claude-plugin/plugin.json +15 -0
  34. package/plugins/devflow-go/.claude-plugin/plugin.json +15 -0
  35. package/plugins/devflow-go/skills/go/SKILL.md +187 -0
  36. package/plugins/devflow-go/skills/go/references/concurrency.md +312 -0
  37. package/plugins/devflow-go/skills/go/references/detection.md +129 -0
  38. package/plugins/devflow-go/skills/go/references/patterns.md +232 -0
  39. package/plugins/devflow-go/skills/go/references/violations.md +205 -0
  40. package/plugins/devflow-implement/.claude-plugin/plugin.json +1 -3
  41. package/plugins/devflow-implement/agents/coder.md +11 -6
  42. package/plugins/devflow-java/.claude-plugin/plugin.json +15 -0
  43. package/plugins/devflow-java/skills/java/SKILL.md +183 -0
  44. package/plugins/devflow-java/skills/java/references/detection.md +120 -0
  45. package/plugins/devflow-java/skills/java/references/modern-java.md +270 -0
  46. package/plugins/devflow-java/skills/java/references/patterns.md +235 -0
  47. package/plugins/devflow-java/skills/java/references/violations.md +213 -0
  48. package/plugins/devflow-python/.claude-plugin/plugin.json +15 -0
  49. package/plugins/devflow-python/skills/python/SKILL.md +188 -0
  50. package/plugins/devflow-python/skills/python/references/async.md +220 -0
  51. package/plugins/devflow-python/skills/python/references/detection.md +128 -0
  52. package/plugins/devflow-python/skills/python/references/patterns.md +226 -0
  53. package/plugins/devflow-python/skills/python/references/violations.md +204 -0
  54. package/plugins/devflow-react/.claude-plugin/plugin.json +15 -0
  55. package/plugins/{devflow-core-skills → devflow-react}/skills/react/SKILL.md +1 -1
  56. package/plugins/{devflow-core-skills → devflow-react}/skills/react/references/patterns.md +3 -3
  57. package/plugins/devflow-resolve/.claude-plugin/plugin.json +1 -1
  58. package/plugins/devflow-rust/.claude-plugin/plugin.json +15 -0
  59. package/plugins/devflow-rust/skills/rust/SKILL.md +193 -0
  60. package/plugins/devflow-rust/skills/rust/references/detection.md +131 -0
  61. package/plugins/devflow-rust/skills/rust/references/ownership.md +242 -0
  62. package/plugins/devflow-rust/skills/rust/references/patterns.md +210 -0
  63. package/plugins/devflow-rust/skills/rust/references/violations.md +191 -0
  64. package/plugins/devflow-self-review/.claude-plugin/plugin.json +1 -1
  65. package/plugins/devflow-specify/.claude-plugin/plugin.json +1 -1
  66. package/plugins/devflow-typescript/.claude-plugin/plugin.json +15 -0
  67. package/plugins/{devflow-core-skills → devflow-typescript}/skills/typescript/references/patterns.md +3 -3
  68. package/scripts/hooks/ambient-prompt.sh +48 -0
  69. package/scripts/hooks/background-memory-update.sh +49 -8
  70. package/scripts/hooks/ensure-memory-gitignore.sh +17 -0
  71. package/scripts/hooks/pre-compact-memory.sh +12 -6
  72. package/scripts/hooks/session-start-memory.sh +50 -8
  73. package/scripts/hooks/stop-update-memory.sh +10 -6
  74. package/shared/agents/coder.md +11 -6
  75. package/shared/agents/reviewer.md +8 -0
  76. package/shared/skills/ambient-router/SKILL.md +89 -0
  77. package/shared/skills/ambient-router/references/skill-catalog.md +68 -0
  78. package/shared/skills/docs-framework/SKILL.md +10 -6
  79. package/shared/skills/go/SKILL.md +187 -0
  80. package/shared/skills/go/references/concurrency.md +312 -0
  81. package/shared/skills/go/references/detection.md +129 -0
  82. package/shared/skills/go/references/patterns.md +232 -0
  83. package/shared/skills/go/references/violations.md +205 -0
  84. package/shared/skills/java/SKILL.md +183 -0
  85. package/shared/skills/java/references/detection.md +120 -0
  86. package/shared/skills/java/references/modern-java.md +270 -0
  87. package/shared/skills/java/references/patterns.md +235 -0
  88. package/shared/skills/java/references/violations.md +213 -0
  89. package/shared/skills/python/SKILL.md +188 -0
  90. package/shared/skills/python/references/async.md +220 -0
  91. package/shared/skills/python/references/detection.md +128 -0
  92. package/shared/skills/python/references/patterns.md +226 -0
  93. package/shared/skills/python/references/violations.md +204 -0
  94. package/shared/skills/react/SKILL.md +1 -1
  95. package/shared/skills/react/references/patterns.md +3 -3
  96. package/shared/skills/rust/SKILL.md +193 -0
  97. package/shared/skills/rust/references/detection.md +131 -0
  98. package/shared/skills/rust/references/ownership.md +242 -0
  99. package/shared/skills/rust/references/patterns.md +210 -0
  100. package/shared/skills/rust/references/violations.md +191 -0
  101. package/shared/skills/test-driven-development/SKILL.md +139 -0
  102. package/shared/skills/test-driven-development/references/rationalization-prevention.md +111 -0
  103. package/shared/skills/typescript/references/patterns.md +3 -3
  104. package/src/templates/managed-settings.json +14 -0
  105. package/plugins/devflow-code-review/skills/react/SKILL.md +0 -276
  106. package/plugins/devflow-code-review/skills/react/references/patterns.md +0 -1331
  107. package/plugins/devflow-core-skills/skills/accessibility/SKILL.md +0 -229
  108. package/plugins/devflow-core-skills/skills/accessibility/references/detection.md +0 -171
  109. package/plugins/devflow-core-skills/skills/accessibility/references/patterns.md +0 -670
  110. package/plugins/devflow-core-skills/skills/accessibility/references/violations.md +0 -419
  111. package/plugins/devflow-core-skills/skills/frontend-design/SKILL.md +0 -254
  112. package/plugins/devflow-core-skills/skills/frontend-design/references/detection.md +0 -184
  113. package/plugins/devflow-core-skills/skills/frontend-design/references/patterns.md +0 -511
  114. package/plugins/devflow-core-skills/skills/frontend-design/references/violations.md +0 -453
  115. package/plugins/devflow-core-skills/skills/react/references/violations.md +0 -565
  116. package/plugins/devflow-implement/skills/accessibility/SKILL.md +0 -229
  117. package/plugins/devflow-implement/skills/accessibility/references/detection.md +0 -171
  118. package/plugins/devflow-implement/skills/accessibility/references/patterns.md +0 -670
  119. package/plugins/devflow-implement/skills/accessibility/references/violations.md +0 -419
  120. package/plugins/devflow-implement/skills/frontend-design/SKILL.md +0 -254
  121. package/plugins/devflow-implement/skills/frontend-design/references/detection.md +0 -184
  122. package/plugins/devflow-implement/skills/frontend-design/references/patterns.md +0 -511
  123. package/plugins/devflow-implement/skills/frontend-design/references/violations.md +0 -453
  124. /package/plugins/{devflow-code-review → devflow-accessibility}/skills/accessibility/SKILL.md +0 -0
  125. /package/plugins/{devflow-code-review → devflow-accessibility}/skills/accessibility/references/detection.md +0 -0
  126. /package/plugins/{devflow-code-review → devflow-accessibility}/skills/accessibility/references/patterns.md +0 -0
  127. /package/plugins/{devflow-code-review → devflow-accessibility}/skills/accessibility/references/violations.md +0 -0
  128. /package/plugins/{devflow-code-review → devflow-frontend-design}/skills/frontend-design/SKILL.md +0 -0
  129. /package/plugins/{devflow-code-review → devflow-frontend-design}/skills/frontend-design/references/detection.md +0 -0
  130. /package/plugins/{devflow-code-review → devflow-frontend-design}/skills/frontend-design/references/patterns.md +0 -0
  131. /package/plugins/{devflow-code-review → devflow-frontend-design}/skills/frontend-design/references/violations.md +0 -0
  132. /package/plugins/{devflow-code-review → devflow-react}/skills/react/references/violations.md +0 -0
  133. /package/plugins/{devflow-core-skills → devflow-typescript}/skills/typescript/SKILL.md +0 -0
  134. /package/plugins/{devflow-core-skills → devflow-typescript}/skills/typescript/references/violations.md +0 -0
package/CHANGELOG.md CHANGED
@@ -5,6 +5,73 @@ 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
+ ## [Unreleased]
9
+
10
+ ---
11
+
12
+ ## [1.2.0] - 2026-03-05
13
+
14
+ ### Added
15
+ - **Polyglot language skills** — Go, Java, Python, and Rust skill plugins with comprehensive patterns
16
+ - Go: error handling, interfaces, concurrency (errgroup, worker pools, fan-out/fan-in)
17
+ - Java: records, sealed classes, streams, composition over inheritance
18
+ - Python: type hints, protocols, dataclasses, async patterns
19
+ - Rust: ownership, error handling (`thiserror`/`anyhow`), type system, concurrency
20
+ - Skills: 26 → 30, Plugins: 9 → 17
21
+ - **Optional plugin architecture** — Language/ecosystem plugins (`optional: true`) not installed by default
22
+ - Install selectively: `devflow init --plugin=go --plugin=python`
23
+ - Existing skills (typescript, react, accessibility, frontend-design) moved to optional plugins
24
+ - `devflow-core-skills` no longer bundles language-specific skills
25
+ - **Conditional language reviews** in `/code-review` command
26
+ - Spawns language-specific Reviewer agents when matching files are in the diff
27
+ - Skill availability check: skips review if optional plugin not installed
28
+ - **Dynamic skill loading in Coder agent** — Reads language skills at runtime based on DOMAIN hint instead of static frontmatter dependencies
29
+
30
+ ### Changed
31
+ - **`devflow-core-skills`** no longer includes typescript, react, accessibility, or frontend-design skills (moved to optional plugins)
32
+ - **Coder agent** frontmatter trimmed from 14 skills to 6 core skills; language skills loaded dynamically
33
+
34
+ ### Fixed
35
+ - **Deprecated `grpc.WithInsecure()`** in Go concurrency examples → replaced with `grpc.WithTransportCredentials(insecure.NewCredentials())`
36
+ - **Deprecated `datetime.utcnow`** in Python dataclass example → replaced with `datetime.now(timezone.utc)`
37
+ - **SQL injection** in Python async streaming example → replaced raw query with parameterized query
38
+ - **Deprecated `<Context.Provider>`** in React examples → replaced with `<Context>` (React 19+)
39
+ - **Deprecated `useRef<T>()`** without argument in React patterns → replaced with `useRef<T | undefined>(undefined)` (React 19+)
40
+ - **Non-portable `NodeJS.Timeout`** in TypeScript debounce/throttle → replaced with `ReturnType<typeof setTimeout>`
41
+ - **Unsafe `Function` type** in TypeScript type guard → replaced with `(...args: unknown[]) => unknown`
42
+ - **Go test file exclusion** removed from go skill activation (test files are valid Go code)
43
+
44
+ ---
45
+
46
+ ## [1.1.0] - 2026-03-04
47
+
48
+ ### Added
49
+ - **Ambient mode** — New `devflow-ambient` plugin with `/ambient` command for proportional quality enforcement
50
+ - Intent classification (BUILD/DEBUG/REVIEW/PLAN/EXPLORE/CHAT) auto-loads relevant skills
51
+ - Three depth tiers: QUICK (zero overhead), STANDARD (2-3 skills), ESCALATE (nudge to workflows)
52
+ - Always-on mode via `devflow ambient --enable` or `devflow init --ambient`
53
+ - New `ambient-router` skill for intent/depth classification
54
+ - New `test-driven-development` skill (auto-activates for BUILD tasks)
55
+ - Skills: 24 → 26, Plugins: 8 → 9
56
+ - **Working memory enhancements** — Structured cross-session context preservation
57
+ - Structured sections: Now, Progress, Decisions, Modified Files, Context, Session Log
58
+ - Toggleable via `devflow memory --enable/--disable/--status` or `devflow init --memory/--no-memory`
59
+ - `PROJECT-PATTERNS.md` extraction — background hook accumulates patterns across sessions
60
+ - Directory separation: `.memory/` (session state) vs `.docs/` (reviews/design artifacts)
61
+ - Auto-migration from `.docs/` to `.memory/` with no-clobber semantics
62
+ - Auto-adds `.memory/` and `.docs/` to `.gitignore` on first hook run
63
+
64
+ ### Changed
65
+ - **Background agent permissions** — Replaced `--dangerously-skip-permissions` with `--tools "Write"` + `--allowedTools` for restricted file access in memory update hooks
66
+ - **Safe-delete auto-upgrade** — `devflow init` now detects outdated safe-delete blocks and silently upgrades them; no manual uninstall/reinstall needed
67
+
68
+ ### Fixed
69
+ - **Ambient depth classification** — Intent now drives depth exclusively; removed 20-word threshold that silently downgraded ~32% of BUILD/DEBUG prompts to QUICK (#73)
70
+ - **Safe-delete file existence** — Filter non-existent files before calling `trash` in bash/zsh, fish, and PowerShell Unix blocks; prevents noisy `trash: file doesn't exist` errors on `rm -f` of missing files (#74)
71
+ - **Safe-delete deny list** — Expanded `rm` deny patterns from 8 to 21, covering `rm -r`, `rm -fr`, and `rm -f` flag variations that could bypass the `rm -rf`-only patterns (#74)
72
+
73
+ ---
74
+
8
75
  ## [1.0.0] - 2026-02-25
9
76
 
10
77
  ### Added
@@ -720,6 +787,8 @@ devflow init
720
787
 
721
788
  ---
722
789
 
790
+ [1.2.0]: https://github.com/dean0x/devflow/compare/v1.1.0...v1.2.0
791
+ [1.1.0]: https://github.com/dean0x/devflow/compare/v1.0.0...v1.1.0
723
792
  [1.0.0]: https://github.com/dean0x/devflow/compare/v0.9.0...v1.0.0
724
793
  [0.9.0]: https://github.com/dean0x/devflow/releases/tag/v0.9.0
725
794
  [0.8.1]: https://github.com/dean0x/devflow/releases/tag/v0.8.1
package/README.md CHANGED
@@ -24,7 +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
- - **24 quality skills** — 11 auto-activating, plus specialized review and agent skills
27
+ - **30 quality skills** — 8 auto-activating core, 8 optional language/ecosystem, plus specialized review and agent skills
28
28
 
29
29
  ## Quick Start
30
30
 
@@ -48,6 +48,7 @@ Then in Claude Code:
48
48
  | `devflow-resolve` | `/resolve` | Process review issues — fix or defer to tech debt |
49
49
  | `devflow-debug` | `/debug` | Parallel hypothesis debugging |
50
50
  | `devflow-self-review` | `/self-review` | Self-review workflow (Simplifier + Scrutinizer) |
51
+ | `devflow-ambient` | `/ambient` | Ambient mode — auto-loads relevant skills based on each prompt |
51
52
  | `devflow-core-skills` | (auto) | Auto-activating quality enforcement skills |
52
53
 
53
54
  ## Command Details
@@ -80,7 +81,7 @@ Creates a PR when complete.
80
81
  Multi-perspective code review with specialized reviewers:
81
82
 
82
83
  - **Core**: Security, Architecture, Performance, Quality
83
- - **Conditional** (activated when relevant): TypeScript, React, Accessibility, Database, Dependencies, Documentation
84
+ - **Conditional** (activated when relevant): TypeScript, React, Accessibility, Go, Python, Java, Rust, Database, Dependencies, Documentation
84
85
  - Findings classified as must-fix, should-fix, or nit with severity and confidence levels
85
86
 
86
87
  Provides actionable feedback with specific file locations and suggested fixes.
@@ -116,12 +117,30 @@ The `devflow-core-skills` plugin provides quality enforcement skills that activa
116
117
  | `git-safety` | Rebasing, force-pushing, merge conflicts |
117
118
  | `git-workflow` | Staging files, creating commits, PRs |
118
119
  | `github-patterns` | GitHub API operations, PR comments, releases |
120
+ | `test-driven-development` | Implementing new features (RED-GREEN-REFACTOR) |
119
121
  | `test-patterns` | Writing or modifying tests |
120
122
  | `input-validation` | Creating API endpoints |
121
- | `typescript` | Working in TypeScript codebases |
122
- | `react` | Working with React components |
123
- | `accessibility` | Creating UI components, forms, interactive elements |
124
- | `frontend-design` | Working with CSS, styling, visual design |
123
+
124
+ ## Language & Ecosystem Plugins
125
+
126
+ Optional plugins for language-specific patterns. Install only what you need:
127
+
128
+ | Plugin | Skill | Triggers When |
129
+ |--------|-------|---------------|
130
+ | `devflow-typescript` | `typescript` | Working in TypeScript codebases |
131
+ | `devflow-react` | `react` | Working with React components |
132
+ | `devflow-accessibility` | `accessibility` | Creating UI components, forms |
133
+ | `devflow-frontend-design` | `frontend-design` | Working with CSS, styling |
134
+ | `devflow-go` | `go` | Working in Go codebases |
135
+ | `devflow-python` | `python` | Working in Python codebases |
136
+ | `devflow-java` | `java` | Working in Java codebases |
137
+ | `devflow-rust` | `rust` | Working in Rust codebases |
138
+
139
+ ```bash
140
+ # Install specific language plugins
141
+ npx devflow-kit init --plugin=typescript,react
142
+ npx devflow-kit init --plugin=go
143
+ ```
125
144
 
126
145
  ## Requirements
127
146
 
@@ -160,22 +179,25 @@ Three shell hooks run behind the scenes:
160
179
 
161
180
  | Hook | When | What |
162
181
  |------|------|------|
163
- | **Stop** | After each response | Updates `.docs/WORKING-MEMORY.md` with current focus, decisions, and progress. Throttled — skips if updated <2 min ago. |
182
+ | **Stop** | After each response | Updates `.memory/WORKING-MEMORY.md` with current focus, decisions, and progress. Throttled — skips if updated <2 min ago. |
164
183
  | **SessionStart** | On startup, `/clear`, resume, compaction | Injects previous working memory + fresh git state as system context. Warns if memory is >1h stale. |
165
184
  | **PreCompact** | Before context compaction | Backs up git state to JSON. Bootstraps a minimal working memory from git if none exists yet. |
166
185
 
167
- Working memory is **per-project** — scoped to each repo's `.docs/` directory. Multiple sessions across different repos don't interfere.
186
+ Working memory is **per-project** — scoped to each repo's `.memory/` directory. Multiple sessions across different repos don't interfere.
168
187
 
169
188
  ## Documentation Structure
170
189
 
171
- DevFlow creates project documentation in `.docs/`:
190
+ DevFlow creates project documentation in `.docs/` and working memory in `.memory/`:
172
191
 
173
192
  ```
174
193
  .docs/
175
194
  ├── reviews/{branch}/ # Review reports per branch
176
- ├── design/ # Implementation plans
195
+ └── design/ # Implementation plans
196
+
197
+ .memory/
177
198
  ├── WORKING-MEMORY.md # Auto-maintained by Stop hook
178
- └── working-memory-backup.json # Pre-compact git state snapshot
199
+ ├── PROJECT-PATTERNS.md # Accumulated patterns across sessions
200
+ └── backup.json # Pre-compact git state snapshot
179
201
  ```
180
202
 
181
203
  ## Workflow Examples
@@ -209,6 +231,8 @@ Session context is saved and restored automatically via Working Memory hooks —
209
231
  | `npx devflow-kit init` | Install all plugins |
210
232
  | `npx devflow-kit init --plugin=<names>` | Install specific plugin(s) |
211
233
  | `npx devflow-kit list` | List available plugins |
234
+ | `npx devflow-kit ambient --enable` | Enable always-on ambient mode |
235
+ | `npx devflow-kit ambient --disable` | Disable ambient mode |
212
236
  | `npx devflow-kit uninstall` | Remove DevFlow |
213
237
 
214
238
  ### Init Options
package/dist/cli.js CHANGED
@@ -6,6 +6,8 @@ import { dirname, join } from 'path';
6
6
  import { initCommand } from './commands/init.js';
7
7
  import { uninstallCommand } from './commands/uninstall.js';
8
8
  import { listCommand } from './commands/list.js';
9
+ import { ambientCommand } from './commands/ambient.js';
10
+ import { memoryCommand } from './commands/memory.js';
9
11
  const __filename = fileURLToPath(import.meta.url);
10
12
  const __dirname = dirname(__filename);
11
13
  // Read version from package.json
@@ -16,11 +18,13 @@ program
16
18
  .description('Agentic Development Toolkit for Claude Code\n\nEnhance your AI-assisted development with intelligent commands and workflows.')
17
19
  .version(packageJson.version, '-v, --version', 'Display version number')
18
20
  .helpOption('-h, --help', 'Display help information')
19
- .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 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');
21
+ .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');
20
22
  // Register commands
21
23
  program.addCommand(initCommand);
22
24
  program.addCommand(uninstallCommand);
23
25
  program.addCommand(listCommand);
26
+ program.addCommand(ambientCommand);
27
+ program.addCommand(memoryCommand);
24
28
  // Handle no command
25
29
  program.action(() => {
26
30
  program.help();
@@ -0,0 +1,18 @@
1
+ import { Command } from 'commander';
2
+ /**
3
+ * Add the ambient UserPromptSubmit hook to settings JSON.
4
+ * Idempotent — returns unchanged JSON if hook already exists.
5
+ */
6
+ export declare function addAmbientHook(settingsJson: string, devflowDir: string): string;
7
+ /**
8
+ * Remove the ambient UserPromptSubmit hook from settings JSON.
9
+ * Idempotent — returns unchanged JSON if hook not present.
10
+ * Preserves other UserPromptSubmit hooks. Cleans empty arrays/objects.
11
+ */
12
+ export declare function removeAmbientHook(settingsJson: string): string;
13
+ /**
14
+ * Check if the ambient hook is registered in settings JSON.
15
+ */
16
+ export declare function hasAmbientHook(settingsJson: string): boolean;
17
+ export declare const ambientCommand: Command;
18
+ //# sourceMappingURL=ambient.d.ts.map
@@ -0,0 +1,136 @@
1
+ import { Command } from 'commander';
2
+ import { promises as fs } from 'fs';
3
+ import * as path from 'path';
4
+ import * as p from '@clack/prompts';
5
+ import color from 'picocolors';
6
+ import { getClaudeDirectory } from '../utils/paths.js';
7
+ const AMBIENT_HOOK_MARKER = 'ambient-prompt.sh';
8
+ /**
9
+ * Add the ambient UserPromptSubmit hook to settings JSON.
10
+ * Idempotent — returns unchanged JSON if hook already exists.
11
+ */
12
+ export function addAmbientHook(settingsJson, devflowDir) {
13
+ const settings = JSON.parse(settingsJson);
14
+ if (hasAmbientHook(settingsJson)) {
15
+ return settingsJson;
16
+ }
17
+ if (!settings.hooks) {
18
+ settings.hooks = {};
19
+ }
20
+ const hookCommand = path.join(devflowDir, 'scripts', 'hooks', AMBIENT_HOOK_MARKER);
21
+ const newEntry = {
22
+ hooks: [
23
+ {
24
+ type: 'command',
25
+ command: hookCommand,
26
+ timeout: 5,
27
+ },
28
+ ],
29
+ };
30
+ if (!settings.hooks.UserPromptSubmit) {
31
+ settings.hooks.UserPromptSubmit = [];
32
+ }
33
+ settings.hooks.UserPromptSubmit.push(newEntry);
34
+ return JSON.stringify(settings, null, 2) + '\n';
35
+ }
36
+ /**
37
+ * Remove the ambient UserPromptSubmit hook from settings JSON.
38
+ * Idempotent — returns unchanged JSON if hook not present.
39
+ * Preserves other UserPromptSubmit hooks. Cleans empty arrays/objects.
40
+ */
41
+ export function removeAmbientHook(settingsJson) {
42
+ const settings = JSON.parse(settingsJson);
43
+ if (!settings.hooks?.UserPromptSubmit) {
44
+ return settingsJson;
45
+ }
46
+ settings.hooks.UserPromptSubmit = settings.hooks.UserPromptSubmit.filter((matcher) => !matcher.hooks.some((h) => h.command.includes(AMBIENT_HOOK_MARKER)));
47
+ if (settings.hooks.UserPromptSubmit.length === 0) {
48
+ delete settings.hooks.UserPromptSubmit;
49
+ }
50
+ if (settings.hooks && Object.keys(settings.hooks).length === 0) {
51
+ delete settings.hooks;
52
+ }
53
+ return JSON.stringify(settings, null, 2) + '\n';
54
+ }
55
+ /**
56
+ * Check if the ambient hook is registered in settings JSON.
57
+ */
58
+ export function hasAmbientHook(settingsJson) {
59
+ const settings = JSON.parse(settingsJson);
60
+ if (!settings.hooks?.UserPromptSubmit) {
61
+ return false;
62
+ }
63
+ return settings.hooks.UserPromptSubmit.some((matcher) => matcher.hooks.some((h) => h.command.includes(AMBIENT_HOOK_MARKER)));
64
+ }
65
+ export const ambientCommand = new Command('ambient')
66
+ .description('Enable or disable ambient mode (always-on quality enforcement)')
67
+ .option('--enable', 'Register UserPromptSubmit hook for ambient mode')
68
+ .option('--disable', 'Remove ambient mode hook')
69
+ .option('--status', 'Check if ambient mode is enabled')
70
+ .action(async (options) => {
71
+ const hasFlag = options.enable || options.disable || options.status;
72
+ if (!hasFlag) {
73
+ p.intro(color.bgMagenta(color.white(' Ambient Mode ')));
74
+ p.note(`${color.cyan('devflow ambient --enable')} Register always-on hook\n` +
75
+ `${color.cyan('devflow ambient --disable')} Remove always-on hook\n` +
76
+ `${color.cyan('devflow ambient --status')} Check current state`, 'Usage');
77
+ p.outro(color.dim('Or use /ambient <prompt> for one-shot classification'));
78
+ return;
79
+ }
80
+ const claudeDir = getClaudeDirectory();
81
+ const settingsPath = path.join(claudeDir, 'settings.json');
82
+ let settingsContent;
83
+ try {
84
+ settingsContent = await fs.readFile(settingsPath, 'utf-8');
85
+ }
86
+ catch {
87
+ if (options.status) {
88
+ p.log.info('Ambient mode: disabled (no settings.json found)');
89
+ return;
90
+ }
91
+ // Create minimal settings.json
92
+ settingsContent = '{}';
93
+ }
94
+ if (options.status) {
95
+ const enabled = hasAmbientHook(settingsContent);
96
+ p.log.info(`Ambient mode: ${enabled ? color.green('enabled') : color.dim('disabled')}`);
97
+ return;
98
+ }
99
+ // Resolve devflow scripts directory from settings.json hooks or default
100
+ let devflowDir;
101
+ try {
102
+ const settings = JSON.parse(settingsContent);
103
+ // Try to extract devflowDir from existing hooks (e.g., Stop hook path)
104
+ const stopHook = settings.hooks?.Stop?.[0]?.hooks?.[0]?.command;
105
+ if (stopHook) {
106
+ // e.g., /Users/dean/.devflow/scripts/hooks/stop-update-memory.sh → /Users/dean/.devflow
107
+ devflowDir = path.resolve(stopHook, '..', '..', '..');
108
+ }
109
+ else {
110
+ devflowDir = path.join(process.env.HOME || '~', '.devflow');
111
+ }
112
+ }
113
+ catch {
114
+ devflowDir = path.join(process.env.HOME || '~', '.devflow');
115
+ }
116
+ if (options.enable) {
117
+ const updated = addAmbientHook(settingsContent, devflowDir);
118
+ if (updated === settingsContent) {
119
+ p.log.info('Ambient mode already enabled');
120
+ return;
121
+ }
122
+ await fs.writeFile(settingsPath, updated, 'utf-8');
123
+ p.log.success('Ambient mode enabled — UserPromptSubmit hook registered');
124
+ p.log.info(color.dim('Relevant skills will now auto-load based on each prompt'));
125
+ }
126
+ if (options.disable) {
127
+ const updated = removeAmbientHook(settingsContent);
128
+ if (updated === settingsContent) {
129
+ p.log.info('Ambient mode already disabled');
130
+ return;
131
+ }
132
+ await fs.writeFile(settingsPath, updated, 'utf-8');
133
+ p.log.success('Ambient mode disabled — hook removed');
134
+ }
135
+ });
136
+ //# sourceMappingURL=ambient.js.map
@@ -1,6 +1,8 @@
1
1
  import { Command } from 'commander';
2
2
  import { type PluginDefinition } from '../plugins.js';
3
3
  export { substituteSettingsTemplate, computeGitignoreAppend, applyTeamsConfig, stripTeamsConfig, mergeDenyList } from '../utils/post-install.js';
4
+ export { addAmbientHook, removeAmbientHook, hasAmbientHook } from './ambient.js';
5
+ export { addMemoryHooks, removeMemoryHooks, hasMemoryHooks } from './memory.js';
4
6
  /**
5
7
  * Parse a comma-separated plugin selection string into normalized plugin names.
6
8
  * Validates against known plugins; returns invalid names as errors.
@@ -9,12 +9,16 @@ import { getInstallationPaths } from '../utils/paths.js';
9
9
  import { getGitRoot } from '../utils/git.js';
10
10
  import { isClaudeCliAvailable } from '../utils/cli.js';
11
11
  import { installViaCli, installViaFileCopy } from '../utils/installer.js';
12
- import { installSettings, installManagedSettings, installClaudeignore, updateGitignore, createDocsStructure, } from '../utils/post-install.js';
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
- import { generateSafeDeleteBlock, isAlreadyInstalled, installToProfile } from '../utils/safe-delete-install.js';
15
+ import { generateSafeDeleteBlock, installToProfile, removeFromProfile, getInstalledVersion, SAFE_DELETE_BLOCK_VERSION } from '../utils/safe-delete-install.js';
16
+ import { addAmbientHook } from './ambient.js';
17
+ import { addMemoryHooks, removeMemoryHooks } from './memory.js';
16
18
  // Re-export pure functions for tests (canonical source is post-install.ts)
17
19
  export { substituteSettingsTemplate, computeGitignoreAppend, applyTeamsConfig, stripTeamsConfig, mergeDenyList } from '../utils/post-install.js';
20
+ export { addAmbientHook, removeAmbientHook, hasAmbientHook } from './ambient.js';
21
+ export { addMemoryHooks, removeMemoryHooks, hasMemoryHooks } from './memory.js';
18
22
  const __filename = fileURLToPath(import.meta.url);
19
23
  const __dirname = dirname(__filename);
20
24
  /**
@@ -57,6 +61,10 @@ export const initCommand = new Command('init')
57
61
  .option('--plugin <names>', 'Install specific plugin(s), comma-separated (e.g., implement,code-review)')
58
62
  .option('--teams', 'Enable Agent Teams (peer debate, adversarial review)')
59
63
  .option('--no-teams', 'Disable Agent Teams (use parallel subagents instead)')
64
+ .option('--ambient', 'Enable ambient mode (auto-loads relevant skills for every prompt)')
65
+ .option('--no-ambient', 'Disable ambient mode')
66
+ .option('--memory', 'Enable working memory (session context preservation)')
67
+ .option('--no-memory', 'Disable working memory hooks')
60
68
  .action(async (options) => {
61
69
  // Get package version
62
70
  const packageJsonPath = path.resolve(__dirname, '../../package.json');
@@ -152,6 +160,44 @@ export const initCommand = new Command('init')
152
160
  }
153
161
  teamsEnabled = teamsChoice;
154
162
  }
163
+ // Ambient mode selection
164
+ let ambientEnabled;
165
+ if (options.ambient !== undefined) {
166
+ ambientEnabled = options.ambient;
167
+ }
168
+ else if (!process.stdin.isTTY) {
169
+ ambientEnabled = false;
170
+ }
171
+ else {
172
+ const ambientChoice = await p.confirm({
173
+ message: 'Enable ambient mode? (auto-loads relevant skills based on each prompt)',
174
+ initialValue: false,
175
+ });
176
+ if (p.isCancel(ambientChoice)) {
177
+ p.cancel('Installation cancelled.');
178
+ process.exit(0);
179
+ }
180
+ ambientEnabled = ambientChoice;
181
+ }
182
+ // Working memory selection (defaults ON — foundational, unlike ambient's false)
183
+ let memoryEnabled;
184
+ if (options.memory !== undefined) {
185
+ memoryEnabled = options.memory;
186
+ }
187
+ else if (!process.stdin.isTTY) {
188
+ memoryEnabled = true;
189
+ }
190
+ else {
191
+ const memoryChoice = await p.confirm({
192
+ message: 'Enable working memory? (automatic session context preservation)',
193
+ initialValue: true,
194
+ });
195
+ if (p.isCancel(memoryChoice)) {
196
+ p.cancel('Installation cancelled.');
197
+ process.exit(0);
198
+ }
199
+ memoryEnabled = memoryChoice;
200
+ }
155
201
  // Security deny list placement (user scope + TTY only)
156
202
  let securityMode = 'user';
157
203
  if (scope === 'user' && process.stdin.isTTY) {
@@ -314,6 +360,41 @@ export const initCommand = new Command('init')
314
360
  }
315
361
  }
316
362
  await installSettings(claudeDir, rootDir, devflowDir, verbose, teamsEnabled, effectiveSecurityMode);
363
+ // Install ambient hook if enabled
364
+ if (ambientEnabled) {
365
+ const settingsPath = path.join(claudeDir, 'settings.json');
366
+ try {
367
+ const content = await fs.readFile(settingsPath, 'utf-8');
368
+ const updated = addAmbientHook(content, devflowDir);
369
+ if (updated !== content) {
370
+ await fs.writeFile(settingsPath, updated, 'utf-8');
371
+ if (verbose) {
372
+ p.log.success('Ambient mode hook installed');
373
+ }
374
+ }
375
+ }
376
+ catch { /* settings.json may not exist yet */ }
377
+ }
378
+ // Manage memory hooks based on user choice
379
+ const settingsPath = path.join(claudeDir, 'settings.json');
380
+ try {
381
+ const content = await fs.readFile(settingsPath, 'utf-8');
382
+ const updated = memoryEnabled
383
+ ? addMemoryHooks(content, devflowDir)
384
+ : removeMemoryHooks(content);
385
+ if (updated !== content) {
386
+ await fs.writeFile(settingsPath, updated, 'utf-8');
387
+ if (verbose) {
388
+ p.log.info(`Working memory ${memoryEnabled ? 'enabled' : 'disabled'}`);
389
+ }
390
+ }
391
+ }
392
+ catch { /* settings.json may not exist yet */ }
393
+ // Ensure .memory/ exists when memory is enabled (hooks are no-ops without it)
394
+ if (memoryEnabled) {
395
+ await createMemoryDir(verbose);
396
+ await migrateMemoryFiles(verbose);
397
+ }
317
398
  }
318
399
  const fileExtras = selectedExtras.filter(e => e !== 'settings' && e !== 'safe-delete');
319
400
  if (fileExtras.length > 0) {
@@ -357,14 +438,20 @@ export const initCommand = new Command('init')
357
438
  p.log.info(`Then re-run ${color.cyan('devflow init')} to auto-configure safe-delete.`);
358
439
  }
359
440
  else if (safeDeleteAvailable) {
360
- const alreadyInstalled = await isAlreadyInstalled(profilePath);
361
- if (alreadyInstalled) {
362
- p.log.info(`Safe-delete already configured in ${color.dim(profilePath)}`);
363
- }
364
- else {
365
- const trashCmd = safeDeleteInfo.command;
366
- const block = generateSafeDeleteBlock(shell, process.platform, trashCmd);
367
- if (block) {
441
+ const trashCmd = safeDeleteInfo.command;
442
+ const block = generateSafeDeleteBlock(shell, process.platform, trashCmd);
443
+ if (block) {
444
+ const installedVersion = await getInstalledVersion(profilePath);
445
+ if (installedVersion === SAFE_DELETE_BLOCK_VERSION) {
446
+ p.log.info(`Safe-delete already configured in ${color.dim(profilePath)}`);
447
+ }
448
+ else if (installedVersion > 0) {
449
+ await removeFromProfile(profilePath);
450
+ await installToProfile(profilePath, block);
451
+ p.log.success(`Safe-delete upgraded in ${color.dim(profilePath)}`);
452
+ p.log.info('Restart your shell or run: ' + color.cyan(`source ${profilePath}`));
453
+ }
454
+ else {
368
455
  const confirm = await p.confirm({
369
456
  message: `Install safe-delete to ${profilePath}? (overrides rm to use ${trashCmd ?? 'recycle bin'})`,
370
457
  initialValue: true,
@@ -0,0 +1,22 @@
1
+ import { Command } from 'commander';
2
+ /**
3
+ * Add all 3 memory hooks (Stop, SessionStart, PreCompact) to settings JSON.
4
+ * Idempotent — skips hooks that already exist. Returns unchanged JSON if all 3 present.
5
+ */
6
+ export declare function addMemoryHooks(settingsJson: string, devflowDir: string): string;
7
+ /**
8
+ * Remove all memory hooks (Stop, SessionStart, PreCompact) from settings JSON.
9
+ * Idempotent — returns unchanged JSON if no memory hooks present.
10
+ * Preserves non-memory hooks. Cleans empty arrays/objects.
11
+ */
12
+ export declare function removeMemoryHooks(settingsJson: string): string;
13
+ /**
14
+ * Check if ALL 3 memory hooks are registered in settings JSON.
15
+ */
16
+ export declare function hasMemoryHooks(settingsJson: string): boolean;
17
+ /**
18
+ * Count how many of the 3 memory hooks are present (0-3).
19
+ */
20
+ export declare function countMemoryHooks(settingsJson: string): number;
21
+ export declare const memoryCommand: Command;
22
+ //# sourceMappingURL=memory.d.ts.map