prjct-cli 1.6.7 → 1.6.9

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 (85) hide show
  1. package/CHANGELOG.md +106 -5
  2. package/README.md +46 -0
  3. package/core/agentic/chain-of-thought.ts +3 -1
  4. package/core/agentic/ground-truth.ts +12 -5
  5. package/core/agentic/index.ts +14 -2
  6. package/core/agentic/memory-system.ts +86 -23
  7. package/core/agentic/services.ts +1 -1
  8. package/core/agentic/template-executor.ts +4 -4
  9. package/core/agentic/template-loader.ts +2 -8
  10. package/core/ai-tools/registry.ts +2 -9
  11. package/core/bus/bus.ts +0 -1
  12. package/core/bus/index.ts +1 -1
  13. package/core/cli/start.ts +0 -2
  14. package/core/commands/base.ts +2 -2
  15. package/core/commands/planning.ts +4 -6
  16. package/core/constants/index.ts +19 -0
  17. package/core/context/generator.ts +0 -2
  18. package/core/context-tools/files-tool.ts +0 -6
  19. package/core/context-tools/imports-tool.ts +0 -6
  20. package/core/context-tools/recent-tool.ts +0 -6
  21. package/core/context-tools/signatures-tool.ts +0 -6
  22. package/core/context-tools/summary-tool.ts +0 -6
  23. package/core/context-tools/token-counter.ts +0 -13
  24. package/core/infrastructure/agent-detector.ts +2 -15
  25. package/core/infrastructure/ai-provider.ts +0 -29
  26. package/core/infrastructure/author-detector.ts +0 -14
  27. package/core/infrastructure/config-manager.ts +1 -1
  28. package/core/infrastructure/path-manager.ts +3 -17
  29. package/core/infrastructure/setup.ts +0 -3
  30. package/core/integrations/jira/client.ts +3 -77
  31. package/core/plugin/hooks.ts +0 -2
  32. package/core/plugin/index.ts +0 -13
  33. package/core/plugin/loader.ts +0 -2
  34. package/core/plugin/registry.ts +0 -2
  35. package/core/server/server.ts +2 -4
  36. package/core/server/sse.ts +115 -59
  37. package/core/services/agent-service.ts +1 -1
  38. package/core/services/context-generator.ts +23 -48
  39. package/core/services/diff-generator.ts +18 -55
  40. package/core/services/hooks-service.ts +0 -1
  41. package/core/services/memory-service.ts +1 -1
  42. package/core/services/project-service.ts +1 -1
  43. package/core/services/stack-detector.ts +4 -20
  44. package/core/services/sync-service.ts +36 -107
  45. package/core/services/sync-verifier.ts +17 -37
  46. package/core/services/watch-service.ts +1 -1
  47. package/core/storage/index.ts +1 -1
  48. package/core/storage/storage.ts +0 -2
  49. package/core/types/citations.ts +22 -0
  50. package/core/types/commands.ts +10 -0
  51. package/core/types/diff.ts +41 -0
  52. package/core/types/errors.ts +111 -0
  53. package/core/types/index.ts +80 -0
  54. package/core/types/infrastructure.ts +14 -0
  55. package/core/types/jira.ts +51 -0
  56. package/core/types/logger.ts +17 -0
  57. package/core/types/output.ts +47 -0
  58. package/core/types/project-sync.ts +109 -0
  59. package/core/types/server.ts +28 -10
  60. package/core/types/services.ts +14 -0
  61. package/core/types/stack.ts +19 -0
  62. package/core/types/sync-verifier.ts +33 -0
  63. package/core/types/workflow.ts +23 -0
  64. package/core/utils/animations.ts +0 -18
  65. package/core/utils/cache.ts +0 -6
  66. package/core/utils/citations.ts +2 -16
  67. package/core/utils/collection-filters.ts +0 -24
  68. package/core/utils/date-helper.ts +0 -20
  69. package/core/utils/error-messages.ts +5 -139
  70. package/core/utils/file-helper.ts +0 -26
  71. package/core/utils/help.ts +0 -9
  72. package/core/utils/jsonl-helper.ts +0 -21
  73. package/core/utils/logger.ts +3 -11
  74. package/core/utils/markdown-builder.ts +0 -3
  75. package/core/utils/next-steps.ts +0 -2
  76. package/core/utils/output.ts +6 -45
  77. package/core/utils/runtime.ts +0 -11
  78. package/core/utils/session-helper.ts +0 -12
  79. package/core/utils/version.ts +0 -12
  80. package/core/workflow/workflow-preferences.ts +18 -31
  81. package/dist/bin/prjct.mjs +319 -351
  82. package/dist/core/infrastructure/command-installer.js +1 -26
  83. package/dist/core/infrastructure/setup.js +1 -28
  84. package/dist/core/utils/version.js +0 -11
  85. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,106 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.6.9] - 2026-02-07
4
+
5
+ ### Bug Fixes
6
+
7
+ - resolve SSE zombie connections and infinite promise leak (PRJ-286) (#134)
8
+
9
+
10
+ ## [1.6.11] - 2026-02-07
11
+
12
+ ### Bug Fixes
13
+ - **Fix SSE zombie connections and infinite promise leak (PRJ-286)**: Replaced infinite `await new Promise(() => {})` with AbortController-based mechanism that resolves on client removal. Added max client lifetime (1 hour) with per-client TTL timeout. Added periodic reaper (every 5 min) that scans for and removes zombie entries from the clients Map. Consolidated duplicate cleanup paths into single idempotent `removeClient(id)` function. Added `shutdown()` to SSEManager for clean server stop. All timers use `unref()` to avoid blocking process exit.
14
+
15
+ ### Implementation Details
16
+ Replaced the infinite pending promise in `streamSSE` callback with an `AbortController` whose signal resolves the await when the client is removed. Internal client state (`heartbeatInterval`, `ttlTimeout`, `abortController`) is tracked in an `InternalClient` wrapper separate from the public `SSEClient` type. The public `SSEClient` interface gained only `connectedAt` for staleness detection.
17
+
18
+ ### Learnings
19
+ - AbortController integrates cleanly with Hono's `streamSSE` — the async callback needs to await *something*, and a signal-based promise is the right primitive.
20
+ - Timer `unref()` has different shapes between Bun (number) and Node (Timeout object) — use `typeof` check before calling.
21
+ - Idempotent cleanup functions eliminate race conditions between heartbeat failure and stream abort handlers.
22
+
23
+ ### Test Plan
24
+
25
+ #### For QA
26
+ 1. Start prjct server, connect SSE client to `/api/events` — verify `connected` event
27
+ 2. Disconnect client gracefully — verify `clients.size === 0`
28
+ 3. Kill client process (ungraceful) — verify heartbeat cleanup within 30s
29
+ 4. Connect client, wait >1 hour — verify TTL auto-disconnect
30
+ 5. Connect 5+ clients, kill all — verify reaper cleans all within 5 min
31
+ 6. Call `server.stop()` — verify all clients disconnected and reaper stopped
32
+
33
+ #### For Users
34
+ **What changed:** SSE connections now clean up reliably on disconnect, have a 1-hour max lifetime, and a background reaper removes zombie connections every 5 minutes.
35
+ **Breaking changes:** `SSEManager` interface now includes `shutdown()`. `SSEClient` now includes `connectedAt`.
36
+
37
+ ## [1.6.10] - 2026-02-07
38
+
39
+ ### Documentation
40
+ - **Document all environment variables (PRJ-90)**: Added comprehensive environment variable documentation to README.md covering all 13 env vars used by prjct-cli. Organized into Configuration, JIRA Integration, and Agent Detection categories with defaults, descriptions, and usage examples. Added inline comments at all `process.env` read sites in 6 source files.
41
+
42
+ ### Test Plan
43
+
44
+ #### For QA
45
+ 1. `bun run build` — should succeed
46
+ 2. `bun run lint` — no errors
47
+ 3. Verify README.md renders correctly on GitHub (env vars tables)
48
+
49
+ #### For Users
50
+ **What changed:** New "Environment Variables" section in README.md with full documentation of all configurable env vars.
51
+ **Breaking changes:** None.
52
+
53
+ ## [1.6.8] - 2026-02-07
54
+
55
+ ### Refactoring
56
+
57
+ - standardize export patterns to ESM (PRJ-99) (#132)
58
+
59
+
60
+ ## [1.6.9] - 2026-02-07
61
+
62
+ ### Refactor
63
+ - **Standardize export patterns to ESM (PRJ-99)**: Removed redundant `export default { ... }` CJS-compat patterns from 33 files across `core/`. Updated 19 import sites to use namespace imports (`import * as X`). Cleaned 3 barrel re-exports in `agentic/index.ts`, `bus/index.ts`, and `storage/index.ts`. 3 function-collection modules (chain-of-thought, ground-truth, template-loader) retain proper singleton defaults for test mocking compatibility. Net reduction of 274 lines.
64
+
65
+ ### Implementation Details
66
+ Removed the redundant pattern where files had both named exports (`export function X`) and a CJS-compat default export object (`export default { X, Y, Z }`). All 33 cleaned files already had proper named exports, making the default objects unnecessary. Import sites referencing removed defaults were converted to `import * as X from` namespace imports, which preserves the `X.method()` usage pattern.
67
+
68
+ ### Learnings
69
+ - Bun enforces read-only properties on ESM namespace objects (`import * as X`) — direct property assignment for test mocking fails at runtime
70
+ - Function-collection modules that need test mocking should export a named singleton object as default, consistent with class-instance modules like `pathManager`, `loopDetector`
71
+ - Barrel file (`index.ts`) re-exports of `default` must be updated when removing default exports from source modules
72
+
73
+ ### Test Plan
74
+
75
+ #### For QA
76
+ 1. Run `bun run build` — should complete with no errors
77
+ 2. Run `bun run test` — all 416 tests should pass (1 pre-existing timeout flake in DependencyValidator)
78
+ 3. Run `bun run lint` — no lint errors
79
+ 4. Run `npx tsc -p core/tsconfig.json --noEmit` — no type errors
80
+ 5. Verify `prjct sync --yes` still works end-to-end
81
+
82
+ #### For Users
83
+ **What changed:** Internal refactor only — no API or CLI behavior changes.
84
+ **How to use:** No user action needed.
85
+ **Breaking changes:** None.
86
+
87
+ ## [1.6.8] - 2026-02-07
88
+
89
+ ### Documentation
90
+ - **Add JSDoc to CachedStore class methods (PRJ-91)**: Enhanced all 12 public/protected methods on the `CachedStore<T>` base class with comprehensive JSDoc including `@param`, `@returns`, `@throws`, `@example`, and `@typeParam` annotations. Improved class-level documentation with usage example and cross-references to subclasses.
91
+
92
+ ### Test Plan
93
+
94
+ #### For QA
95
+ 1. Build (`bun run build`) — should succeed
96
+ 2. Typecheck (`npx tsc -p core/tsconfig.json --noEmit`) — should pass
97
+ 3. Verify JSDoc renders in IDE hover tooltips for CachedStore methods
98
+
99
+ #### For Users
100
+ **What changed:** Better IDE documentation for CachedStore internals.
101
+ **Breaking changes:** None
102
+
103
+
3
104
  ## [1.6.7] - 2026-02-07
4
105
 
5
106
  ### Bug Fixes
@@ -787,7 +888,7 @@ Added visual workflow status template showing:
787
888
  - add automatic npm publication and update detection system
788
889
  - publish prjct-cli to npm registry
789
890
  - remove bun and homebrew installation methods
790
- - add natural language interface with English and Spanish support
891
+ - add natural language interface with multi-language support
791
892
  - add interactive workflow system with capability detection and installation
792
893
  - release v0.3.0 with interactive editor selection and codebase analysis
793
894
  - add project management workflows for analyzing, tracking, and fixing tasks
@@ -810,7 +911,7 @@ Added visual workflow status template showing:
810
911
  - make Linear/JIRA templates explicitly ignore MCP tools
811
912
  - remove MCP inheritance from Linear/JIRA templates
812
913
  - standardize confirmation pattern across all commands (#85)
813
- - LLM debe manejar los prompts, no el CLI - PRJ-149 (#84)
914
+ - LLM must handle the prompts, not the CLI - PRJ-149 (#84)
814
915
  - Claude over-plans simple commands like p. sync - PRJ-148 (#83)
815
916
  - implement silent memory application - PRJ-103 (#69)
816
917
  - ignore tar warning in release workflow - PRJ-147
@@ -1436,14 +1537,14 @@ Implemented hierarchical agent resolution allowing AGENTS.md files at any direct
1436
1537
 
1437
1538
  ### Bug Fixes
1438
1539
 
1439
- - LLM debe manejar los prompts, no el CLI - PRJ-149 (#84)
1540
+ - LLM must handle the prompts, not the CLI - PRJ-149 (#84)
1440
1541
 
1441
1542
 
1442
1543
  ## [0.55.3] - 2026-01-30
1443
1544
 
1444
1545
  ### Fixed
1445
1546
 
1446
- - **LLM debe manejar los prompts, no el CLI** (PRJ-149)
1547
+ - **LLM must handle the prompts, not the CLI** (PRJ-149)
1447
1548
  - Added `--json` flag to `prjct sync` for non-interactive mode
1448
1549
  - CLI now detects non-TTY mode and outputs structured JSON instead of interactive prompts
1449
1550
  - Updated `sync.md` template so LLM uses AskUserQuestion for confirmation
@@ -1653,7 +1754,7 @@ Implemented hierarchical agent resolution allowing AGENTS.md files at any direct
1653
1754
  ### Added
1654
1755
 
1655
1756
  - **Workflow hooks via natural language** (PRJ-137)
1656
- - Configure hooks with `p. workflow "antes de ship corre los tests"`
1757
+ - Configure hooks with `p. workflow "before ship run the tests"`
1657
1758
  - Supports before/after hooks for task, done, ship, sync commands
1658
1759
  - Three scopes: permanent (persisted), session, one-time
1659
1760
  - Uses existing memory system for storage
package/README.md CHANGED
@@ -132,6 +132,52 @@ prjct --version # Show version + provider status
132
132
  prjct --help # Show help
133
133
  ```
134
134
 
135
+ ## Environment Variables
136
+
137
+ ### Configuration
138
+
139
+ | Variable | Default | Description |
140
+ |----------|---------|-------------|
141
+ | `PRJCT_CLI_HOME` | `~/.prjct-cli` | Override global storage location. Useful for tests or sandboxed environments. |
142
+ | `PRJCT_DEBUG` | _(unset)_ | Enable debug logging. Values: `1`, `true`, or a log level (`error`, `warn`, `info`, `debug`). |
143
+ | `DEBUG` | _(unset)_ | Fallback debug flag (used if `PRJCT_DEBUG` is not set). Values: `1`, `true`, or `prjct`. |
144
+ | `CI` | _(unset)_ | Set automatically in CI environments. Skips interactive prompts. |
145
+
146
+ ### JIRA Integration
147
+
148
+ | Variable | Default | Description |
149
+ |----------|---------|-------------|
150
+ | `JIRA_BASE_URL` | _(none)_ | JIRA instance URL (e.g., `https://myorg.atlassian.net`). |
151
+ | `JIRA_EMAIL` | _(none)_ | Email for JIRA API authentication. |
152
+ | `JIRA_API_TOKEN` | _(none)_ | API token for JIRA authentication. Generate at [Atlassian API tokens](https://id.atlassian.com/manage-profile/security/api-tokens). |
153
+
154
+ ### Agent Detection (Auto-set)
155
+
156
+ These are typically set by the AI agent runtime, not by users:
157
+
158
+ | Variable | Description |
159
+ |----------|-------------|
160
+ | `CLAUDE_AGENT` | Set when running inside Claude Code. |
161
+ | `ANTHROPIC_CLAUDE` | Alternative Claude environment indicator. |
162
+ | `MCP_AVAILABLE` | Set when MCP (Model Context Protocol) is available. |
163
+ | `HOME` / `USERPROFILE` | Standard OS home directory (used for path resolution). |
164
+
165
+ ### Usage Examples
166
+
167
+ ```bash
168
+ # Enable debug logging
169
+ PRJCT_DEBUG=1 prjct sync
170
+
171
+ # Use a custom storage location
172
+ PRJCT_CLI_HOME=/tmp/prjct-test prjct init
173
+
174
+ # Configure JIRA integration via env vars
175
+ export JIRA_BASE_URL=https://myorg.atlassian.net
176
+ export JIRA_EMAIL=you@example.com
177
+ export JIRA_API_TOKEN=your-api-token
178
+ prjct jira setup
179
+ ```
180
+
135
181
  ## Requirements
136
182
 
137
183
  - Node.js 18+ or Bun 1.0+
@@ -229,4 +229,6 @@ function formatPlan(result: ReasoningResult): string {
229
229
  }
230
230
 
231
231
  export { requiresReasoning, reason, formatPlan, REASONING_REQUIRED_COMMANDS }
232
- export default { requiresReasoning, reason, formatPlan }
232
+
233
+ const chainOfThought = { requiresReasoning, reason, formatPlan, REASONING_REQUIRED_COMMANDS }
234
+ export default chainOfThought
@@ -686,14 +686,21 @@ export function requiresVerification(commandName: string): boolean {
686
686
  return ['done', 'ship', 'feature', 'spec', 'now', 'init', 'sync', 'analyze'].includes(commandName)
687
687
  }
688
688
 
689
- // =============================================================================
690
- // Default Export
691
- // =============================================================================
692
-
693
- export default {
689
+ const groundTruth = {
694
690
  verify,
695
691
  prepareCommand,
696
692
  requiresVerification,
697
693
  verifiers,
698
694
  formatWarnings,
695
+ formatDuration,
696
+ escapeRegex,
697
+ verifyDone,
698
+ verifyShip,
699
+ verifyFeature,
700
+ verifyNow,
701
+ verifyInit,
702
+ verifySync,
703
+ verifyAnalyze,
704
+ verifySpec,
699
705
  }
706
+ export default groundTruth
@@ -104,7 +104,13 @@ export type {
104
104
  export { default as AgentRouter } from './agent-router'
105
105
  // ============ Utilities ============
106
106
  // Chain of thought, services
107
- export { default as chainOfThought } from './chain-of-thought'
107
+ export {
108
+ default as chainOfThought,
109
+ formatPlan,
110
+ REASONING_REQUIRED_COMMANDS,
111
+ reason,
112
+ requiresReasoning,
113
+ } from './chain-of-thought'
108
114
  // ============ Execution ============
109
115
  // Command execution, loop detection
110
116
  export {
@@ -174,7 +180,13 @@ export { default as promptBuilder } from './prompt-builder'
174
180
  export { default as services } from './services'
175
181
  export { default as smartContext } from './smart-context'
176
182
  export { default as templateExecutor, TemplateExecutor } from './template-executor'
177
- export { default as templateLoader } from './template-loader'
183
+ export {
184
+ clearCache,
185
+ default as templateLoader,
186
+ getAllowedTools,
187
+ load,
188
+ parseFrontmatter,
189
+ } from './template-loader'
178
190
  // ============ Tools ============
179
191
  // Tool and template management
180
192
  export { default as toolRegistry } from './tool-registry'
@@ -65,17 +65,26 @@ import { calculateConfidence, MEMORY_TAGS } from '../types/memory'
65
65
  // =============================================================================
66
66
 
67
67
  /**
68
- * CachedStore - Abstract base class for memory system stores
68
+ * Abstract base class for project-scoped, disk-backed stores with in-memory caching.
69
69
  *
70
- * Eliminates duplicated cache/load/save patterns across:
71
- * - PatternStore (~40 lines of boilerplate)
72
- * - SemanticMemories (~40 lines of boilerplate)
70
+ * Provides lazy loading, automatic directory creation on save, and project-scoped
71
+ * cache invalidation. Subclasses only need to define the filename, default data
72
+ * structure, and optionally a subdirectory or post-load normalization hook.
73
73
  *
74
- * Provides:
75
- * - Lazy loading with project-scoped cache
76
- * - Automatic directory creation on save
77
- * - Reset functionality
78
- * - Path management via pathManager
74
+ * Extended by {@link PatternStore} and {@link SemanticMemories}.
75
+ *
76
+ * @typeParam T - The shape of the stored data (e.g., `Patterns`, `MemoryDatabase`)
77
+ *
78
+ * @example
79
+ * ```ts
80
+ * class MyStore extends CachedStore<MyData> {
81
+ * protected getFilename() { return 'my-data.json' }
82
+ * protected getDefault() { return { items: [] } }
83
+ * }
84
+ *
85
+ * const store = new MyStore()
86
+ * const data = await store.load('project-id')
87
+ * ```
79
88
  */
80
89
  export abstract class CachedStore<T> {
81
90
  private _data: T | null = null
@@ -83,24 +92,33 @@ export abstract class CachedStore<T> {
83
92
  private _projectId: string | null = null
84
93
 
85
94
  /**
86
- * Get the filename for this store (e.g., 'patterns.json', 'memories.json')
95
+ * Return the filename for this store (e.g., `'patterns.json'`).
96
+ * @returns The JSON filename used for disk persistence
87
97
  */
88
98
  protected abstract getFilename(): string
89
99
 
90
100
  /**
91
- * Get default data structure when file doesn't exist
101
+ * Return the default data structure when the file does not exist on disk.
102
+ * @returns A fresh default instance of `T`
92
103
  */
93
104
  protected abstract getDefault(): T
94
105
 
95
106
  /**
96
- * Optional: subdirectory within memory folder
107
+ * Optional subdirectory within the project's `memory/` folder.
108
+ * Override to nest the store file under a subfolder.
109
+ *
110
+ * @returns Subdirectory name, or `null` to store directly in `memory/`
97
111
  */
98
112
  protected getSubdirectory(): string | null {
99
113
  return null
100
114
  }
101
115
 
102
116
  /**
103
- * Get full path for the store file
117
+ * Build the full filesystem path for this store's JSON file.
118
+ *
119
+ * @param projectId - The project identifier used for path resolution
120
+ * @returns Absolute path to the store file
121
+ * (e.g., `~/.prjct-cli/projects/{id}/memory/patterns.json`)
104
122
  */
105
123
  protected getPath(projectId: string): string {
106
124
  const basePath = path.join(pathManager.getGlobalProjectPath(projectId), 'memory')
@@ -114,8 +132,20 @@ export abstract class CachedStore<T> {
114
132
  }
115
133
 
116
134
  /**
117
- * Load data from disk (with caching)
118
- * Returns cached data if same project and already loaded
135
+ * Load data from disk with project-scoped caching.
136
+ *
137
+ * Returns cached data immediately if already loaded for the same project.
138
+ * Otherwise reads from disk, falling back to {@link getDefault} when the
139
+ * file does not exist. Calls {@link afterLoad} after a successful disk read.
140
+ *
141
+ * @param projectId - The project identifier
142
+ * @returns The loaded (or cached) data
143
+ * @throws {Error} If the file read fails for reasons other than ENOENT
144
+ *
145
+ * @example
146
+ * ```ts
147
+ * const patterns = await patternStore.load('my-project-id')
148
+ * ```
119
149
  */
120
150
  async load(projectId: string): Promise<T> {
121
151
  // Return cached if same project and loaded
@@ -146,15 +176,25 @@ export abstract class CachedStore<T> {
146
176
  }
147
177
 
148
178
  /**
149
- * Hook for subclasses to normalize data after loading
150
- * E.g., ensuring all index keys exist
179
+ * Hook for subclasses to normalize or migrate data after loading from disk.
180
+ *
181
+ * Called once per disk read (not on cache hits). Override to ensure
182
+ * structural invariants — e.g., adding missing index keys.
183
+ *
184
+ * @param _data - The freshly loaded data to normalize (mutate in place)
151
185
  */
152
186
  protected afterLoad(_data: T): void {
153
187
  // Override in subclass if needed
154
188
  }
155
189
 
156
190
  /**
157
- * Save data to disk
191
+ * Persist the current in-memory data to disk.
192
+ *
193
+ * Creates parent directories automatically if they don't exist.
194
+ * No-op if no data has been loaded yet.
195
+ *
196
+ * @param projectId - The project identifier for path resolution
197
+ * @throws {Error} If the file write fails
158
198
  */
159
199
  async save(projectId: string): Promise<void> {
160
200
  if (!this._data) return
@@ -165,21 +205,39 @@ export abstract class CachedStore<T> {
165
205
  }
166
206
 
167
207
  /**
168
- * Get cached data without loading (may be null)
208
+ * Access the cached data without triggering a disk read.
209
+ *
210
+ * @returns The cached data, or `null` if nothing has been loaded
169
211
  */
170
212
  protected getData(): T | null {
171
213
  return this._data
172
214
  }
173
215
 
174
216
  /**
175
- * Set data directly (for subclass modifications)
217
+ * Replace the in-memory data directly. Does not persist to disk —
218
+ * call {@link save} afterwards if persistence is needed.
219
+ *
220
+ * @param data - The new data to cache
176
221
  */
177
222
  protected setData(data: T): void {
178
223
  this._data = data
179
224
  }
180
225
 
181
226
  /**
182
- * Update data with a transform function, then save
227
+ * Atomically load, transform, and save data in one operation.
228
+ *
229
+ * @param projectId - The project identifier
230
+ * @param updater - Pure function that receives current data and returns updated data
231
+ * @returns The updated data after saving
232
+ * @throws {Error} If load or save fails
233
+ *
234
+ * @example
235
+ * ```ts
236
+ * await store.update('my-project', (data) => ({
237
+ * ...data,
238
+ * count: data.count + 1,
239
+ * }))
240
+ * ```
183
241
  */
184
242
  async update(projectId: string, updater: (data: T) => T): Promise<T> {
185
243
  const data = await this.load(projectId)
@@ -190,7 +248,11 @@ export abstract class CachedStore<T> {
190
248
  }
191
249
 
192
250
  /**
193
- * Check if data has been loaded for a project
251
+ * Check whether data has been loaded into the cache.
252
+ *
253
+ * @param projectId - If provided, checks that data is loaded for this specific project.
254
+ * If omitted, returns `true` if any project's data is cached.
255
+ * @returns `true` if data is loaded (and matches the project, when specified)
194
256
  */
195
257
  isLoaded(projectId?: string): boolean {
196
258
  if (projectId) {
@@ -200,7 +262,8 @@ export abstract class CachedStore<T> {
200
262
  }
201
263
 
202
264
  /**
203
- * Reset cache (forces reload on next access)
265
+ * Clear the in-memory cache, forcing a fresh disk read on the next {@link load} call.
266
+ * Does not delete or modify the file on disk.
204
267
  */
205
268
  reset(): void {
206
269
  this._data = null
@@ -12,7 +12,7 @@
12
12
  * import { services } from './services'
13
13
  *
14
14
  * // Instead of:
15
- * import templateLoader from './template-loader'
15
+ * import * as templateLoader from './template-loader'
16
16
  * import contextBuilder from './context-builder'
17
17
  * // ... 12 more imports
18
18
  *
@@ -208,11 +208,11 @@ You are executing a prjct command as ${context.agentName}. Follow the template-f
208
208
  b) What technologies exist in this project
209
209
  c) What agents are available in ${context.paths.agentsDir}
210
210
 
211
- - **IMPORTANTE**: Los agentes en ${context.paths.agentsDir} YA son específicos del proyecto
212
- (fueron generados durante p. sync con las tecnologías reales)
211
+ - **IMPORTANT**: The agents in ${context.paths.agentsDir} are already project-specific
212
+ (they were generated during p. sync with the actual project technologies)
213
213
 
214
- - SIEMPRE usar el especialista si existe para el dominio
215
- - Solo usar generalista si NO existe agente para ese dominio
214
+ - ALWAYS use the specialist if one exists for the domain
215
+ - Only use the generalist if there is NO agent for that domain
216
216
 
217
217
  - Check if task should be fragmented (read: ${context.paths.taskFragmentation})
218
218
  - If agents loaded, check their skills and load from ${context.paths.skillsDir}
@@ -105,11 +105,5 @@ export function clearCache(): void {
105
105
  cacheOrder.length = 0
106
106
  }
107
107
 
108
- // ============ Default Export (backwards compat) ============
109
-
110
- export default {
111
- load,
112
- parseFrontmatter,
113
- getAllowedTools,
114
- clearCache,
115
- }
108
+ const templateLoader = { load, parseFrontmatter, getAllowedTools, clearCache }
109
+ export default templateLoader
@@ -13,19 +13,12 @@ import { exec } from 'node:child_process'
13
13
  import os from 'node:os'
14
14
  import path from 'node:path'
15
15
  import { promisify } from 'node:util'
16
+ import type { AIToolConfig } from '../types'
16
17
  import { fileExists } from '../utils/fs-helpers'
17
18
 
18
19
  const execAsync = promisify(exec)
19
20
 
20
- export interface AIToolConfig {
21
- id: string
22
- name: string
23
- outputFile: string
24
- outputPath: 'repo' | 'global' // 'repo' = project root, 'global' = ~/.prjct-cli/projects/{id}/context/
25
- maxTokens: number
26
- format: 'detailed' | 'concise' | 'minimal' | 'json'
27
- description: string
28
- }
21
+ export type { AIToolConfig } from '../types'
29
22
 
30
23
  /**
31
24
  * Supported AI tools registry
package/core/bus/bus.ts CHANGED
@@ -288,4 +288,3 @@ const emit = {
288
288
  }
289
289
 
290
290
  export { EventBus, eventBus, emit }
291
- export default { EventBus, EventTypes, eventBus, emit }
package/core/bus/index.ts CHANGED
@@ -5,4 +5,4 @@
5
5
  */
6
6
 
7
7
  export { type BusEventType, type EventCallback, type EventData, EventTypes } from '../types'
8
- export { default, EventBus, emit, eventBus } from './bus'
8
+ export { EventBus, emit, eventBus } from './bus'
package/core/cli/start.ts CHANGED
@@ -384,5 +384,3 @@ export async function runStart(): Promise<void> {
384
384
  // Show completion
385
385
  showCompletion(selectedProviders)
386
386
  }
387
-
388
- export default { runStart }
@@ -20,9 +20,9 @@ import type {
20
20
  CommandResult,
21
21
  ProjectContext,
22
22
  } from '../types'
23
- import dateHelper from '../utils/date-helper'
23
+ import * as dateHelper from '../utils/date-helper'
24
24
  import * as fileHelper from '../utils/file-helper'
25
- import jsonlHelper from '../utils/jsonl-helper'
25
+ import * as jsonlHelper from '../utils/jsonl-helper'
26
26
  import out from '../utils/output'
27
27
 
28
28
  /**
@@ -4,12 +4,12 @@
4
4
  */
5
5
 
6
6
  import path from 'node:path'
7
- import authorDetector from '../infrastructure/author-detector'
7
+ import * as authorDetector from '../infrastructure/author-detector'
8
8
  import commandInstaller from '../infrastructure/command-installer'
9
9
  import { generateUUID } from '../schemas'
10
10
  import type { Priority, TaskSection, TaskType } from '../schemas/state'
11
11
  import { ideasStorage, queueStorage } from '../storage'
12
- import type { CommandResult, ProjectContext } from '../types'
12
+ import type { CommandResult, InitOptions, ProjectContext } from '../types'
13
13
  import { getErrorMessage } from '../types/fs'
14
14
  import { showNextSteps } from '../utils/next-steps'
15
15
  import { OnboardingWizard } from '../wizard'
@@ -34,10 +34,7 @@ async function getAnalysisCommands(): Promise<import('./analysis').AnalysisComma
34
34
  return _analysisCommands
35
35
  }
36
36
 
37
- export interface InitOptions {
38
- yes?: boolean // Skip interactive wizard, use defaults
39
- idea?: string | null // Initial idea for architect mode
40
- }
37
+ export type { InitOptions } from '../types'
41
38
 
42
39
  export class PlanningCommands extends PrjctCommandsBase {
43
40
  /**
@@ -71,6 +68,7 @@ export class PlanningCommands extends PrjctCommandsBase {
71
68
 
72
69
  // Determine if we should run interactive wizard
73
70
  const isTTY = process.stdout.isTTY && process.stdin.isTTY
71
+ // CI: Skip interactive prompts in CI environments
74
72
  const skipWizard = opts.yes || !isTTY || process.env.CI === 'true'
75
73
 
76
74
  // Run wizard if interactive
@@ -323,6 +323,25 @@ export const EVENT_LIMITS = {
323
323
  HISTORY_MAX: 100,
324
324
  } as const
325
325
 
326
+ // =============================================================================
327
+ // Workflow Help Strings (user-facing, no magic strings)
328
+ // =============================================================================
329
+
330
+ /**
331
+ * Example commands shown in workflow preferences help.
332
+ * Keeps UI copy in English and centralized.
333
+ */
334
+ export const WORKFLOW_HELP = {
335
+ /** Message when no workflow preferences are configured */
336
+ NO_PREFERENCES: 'No workflow preferences configured.',
337
+ /** Example: set a before-ship hook to run tests */
338
+ SET_EXAMPLE: 'p. workflow before ship run the tests',
339
+ /** Example: modify workflow to run npm test */
340
+ MODIFY_EXAMPLE: 'p. workflow before ship run npm test',
341
+ /** Example: remove the ship hook */
342
+ REMOVE_EXAMPLE: 'p. workflow remove the ship hook',
343
+ } as const
344
+
326
345
  // =============================================================================
327
346
  // Combined Exports
328
347
  // =============================================================================
@@ -366,5 +366,3 @@ async function generateSummaryMd(
366
366
 
367
367
  await fs.writeFile(path.join(contextPath, 'summary.md'), content, 'utf-8')
368
368
  }
369
-
370
- export default { generateContext }
@@ -575,9 +575,3 @@ function isTestFile(filePath: string): boolean {
575
575
  lower.endsWith('_test.py')
576
576
  )
577
577
  }
578
-
579
- // =============================================================================
580
- // Exports
581
- // =============================================================================
582
-
583
- export default { findRelevantFiles }
@@ -395,9 +395,3 @@ async function buildDependencyTree(
395
395
 
396
396
  return node
397
397
  }
398
-
399
- // =============================================================================
400
- // Exports
401
- // =============================================================================
402
-
403
- export default { analyzeImports }
@@ -299,9 +299,3 @@ function shouldIgnore(filePath: string): boolean {
299
299
 
300
300
  return false
301
301
  }
302
-
303
- // =============================================================================
304
- // Exports
305
- // =============================================================================
306
-
307
- export default { getRecentFiles }