prjct-cli 1.6.0 → 1.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,90 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.6.2] - 2026-02-06
4
+
5
+ ### Bug Fixes
6
+
7
+ - replace console.error with logger in routes.ts (PRJ-73) (#125)
8
+
9
+
10
+ ## [1.6.4] - 2026-02-06
11
+
12
+ ### Bug Fixes
13
+
14
+ - **Replace console.error with logger in routes.ts**: Server routes now use the centralized `log` module instead of raw `console.error` for JSON read/write and context read errors. Production remains quiet by default; enable with `PRJCT_DEBUG=1`.
15
+
16
+ ## [1.6.3] - 2026-02-06
17
+
18
+ ### Improvements
19
+
20
+ - **Typed event bus payloads**: Added 15 typed payload interfaces (`SessionStartedPayload`, `SnapshotCreatedPayload`, etc.) and an `EventMap` type mapping event strings to their payloads. Convenience `emit` methods now enforce required fields at compile time. Backward compatible — callers with `Record<string, unknown>` still work.
21
+
22
+ ### Implementation Details
23
+ - `core/types/bus.ts`: Added `EventMap` interface and all payload types
24
+ - `core/bus/bus.ts`: Generic overload `emit<K extends keyof EventMap>()`, typed convenience methods
25
+ - `core/types/index.ts`: Exported all new payload types
26
+
27
+ ### Test Plan
28
+
29
+ #### For QA
30
+ 1. `bun run typecheck` — zero errors
31
+ 2. `emit.sessionStarted({})` → TS error for missing fields (IntelliSense works)
32
+ 3. `bun test` — all 416 tests pass
33
+
34
+ #### For Users
35
+ - **What changed:** Event emit methods have typed payloads with autocomplete
36
+ - **Breaking changes:** None
37
+
38
+ ## [1.6.2] - 2026-02-06
39
+
40
+ ### Improvements
41
+
42
+ - **Diff preview before sync overwrites**: `prjct sync --dry-run` (or `--preview`) now shows what would change in CLAUDE.md without applying changes. Cancelling interactive sync restores the original file. Previously, files were written before the diff was shown, so cancel/preview still applied changes.
43
+
44
+ ### Implementation Details
45
+ - Added `--dry-run` CLI flag as alias for `--preview`
46
+ - Added `restoreOriginal()` helper that writes back saved CLAUDE.md content on cancel/preview
47
+ - Non-interactive mode (LLM) restores original and returns JSON — apply with `prjct sync --yes`
48
+
49
+ ### Test Plan
50
+
51
+ #### For QA
52
+ 1. `prjct sync --dry-run` — diff shown, CLAUDE.md NOT modified
53
+ 2. `prjct sync` → cancel — CLAUDE.md restored
54
+ 3. `prjct sync` → approve — changes kept
55
+ 4. `prjct sync --yes` — applied directly
56
+ 5. `prjct sync --json` — JSON returned, CLAUDE.md restored
57
+
58
+ #### For Users
59
+ - **What changed:** `--dry-run` flag works correctly; cancel restores original
60
+ - **How to use:** `prjct sync --dry-run` to preview, `prjct sync --yes` to apply
61
+ - **Breaking changes:** None
62
+
63
+ ## [1.6.1] - 2026-02-06
64
+
65
+ ### Bug Fixes
66
+
67
+ - replace console.error with logger in bus.ts (PRJ-72) (#122)
68
+
69
+
70
+ ## [1.6.1] - 2026-02-06
71
+
72
+ ### Bug Fixes
73
+
74
+ - **Replace console.error with logger in bus.ts**: Event bus now uses the centralized `log` module instead of raw `console.error` for error logging. Silent catch block in `logEvent()` now logs at debug level with `getErrorMessage()` context. Production remains quiet by default; enable with `PRJCT_DEBUG=1`.
75
+
76
+ ### Test Plan
77
+
78
+ #### For QA
79
+ 1. Run `PRJCT_DEBUG=1 bun test` — verify bus errors appear with `[prjct:error]` prefix
80
+ 2. Run `bun test` (no debug) — verify bus errors are silent by default
81
+ 3. Trigger a listener error — verify it logs via logger, not console.error
82
+
83
+ #### For Users
84
+ - **What changed:** Error logging in event bus uses centralized logger
85
+ - **How to use:** `PRJCT_DEBUG=1` to see bus errors; quiet by default
86
+ - **Breaking changes:** None
87
+
3
88
  ## [1.6.0] - 2026-02-06
4
89
 
5
90
  ### Features
package/core/bus/bus.ts CHANGED
@@ -9,8 +9,30 @@
9
9
 
10
10
  import fs from 'node:fs/promises'
11
11
  import path from 'node:path'
12
+ import { getErrorMessage } from '../errors'
12
13
  import pathManager from '../infrastructure/path-manager'
13
- import { type EventCallback, type EventData, EventTypes } from '../types'
14
+ import {
15
+ type AnalysisCompletedPayload,
16
+ type EventCallback,
17
+ type EventData,
18
+ type EventMap,
19
+ EventTypes,
20
+ type FeaturePayload,
21
+ type GitCommitPayload,
22
+ type GitPushPayload,
23
+ type IdeaCapturedPayload,
24
+ type ProjectInitializedPayload,
25
+ type ProjectSyncedPayload,
26
+ type SessionCompletedPayload,
27
+ type SessionPausedPayload,
28
+ type SessionResumedPayload,
29
+ type SessionStartedPayload,
30
+ type SnapshotCreatedPayload,
31
+ type SnapshotRestoredPayload,
32
+ type TaskCompletedPayload,
33
+ type TaskCreatedPayload,
34
+ } from '../types'
35
+ import log from '../utils/logger'
14
36
 
15
37
  class EventBus {
16
38
  private listeners: Map<string, Set<EventCallback>>
@@ -75,8 +97,10 @@ class EventBus {
75
97
  }
76
98
 
77
99
  /**
78
- * Emit an event
100
+ * Emit an event with typed payload
79
101
  */
102
+ async emit<K extends keyof EventMap>(event: K, data: EventMap[K]): Promise<void>
103
+ async emit(event: string, data: Record<string, unknown>): Promise<void>
80
104
  async emit(event: string, data: Record<string, unknown> = {}): Promise<void> {
81
105
  const timestamp = new Date().toISOString()
82
106
  const eventData: EventData = {
@@ -108,7 +132,7 @@ class EventBus {
108
132
  // Log any errors
109
133
  results.forEach((result) => {
110
134
  if (result.status === 'rejected') {
111
- console.error(`Event listener error for ${event}:`, result.reason)
135
+ log.error(`Event listener error for ${event}:`, result.reason)
112
136
  }
113
137
  })
114
138
 
@@ -169,7 +193,7 @@ class EventBus {
169
193
  await result
170
194
  }
171
195
  } catch (error) {
172
- console.error('Event callback error:', error)
196
+ log.error('Event callback error:', error)
173
197
  throw error
174
198
  }
175
199
  }
@@ -188,8 +212,8 @@ class EventBus {
188
212
  // Append event
189
213
  const line = `${JSON.stringify(eventData)}\n`
190
214
  await fs.appendFile(eventsPath, line)
191
- } catch (_error) {
192
- // Silently fail - logging should not break functionality
215
+ } catch (error) {
216
+ log.debug('Failed to log event:', getErrorMessage(error))
193
217
  }
194
218
  }
195
219
 
@@ -231,37 +255,34 @@ class EventBus {
231
255
  // Singleton instance
232
256
  const eventBus = new EventBus()
233
257
 
234
- // Convenience methods for common events
258
+ // Convenience methods for common events (typed payloads)
235
259
  const emit = {
236
- sessionStarted: (data: Record<string, unknown>) =>
237
- eventBus.emit(EventTypes.SESSION_STARTED, data),
238
- sessionPaused: (data: Record<string, unknown>) => eventBus.emit(EventTypes.SESSION_PAUSED, data),
239
- sessionResumed: (data: Record<string, unknown>) =>
240
- eventBus.emit(EventTypes.SESSION_RESUMED, data),
241
- sessionCompleted: (data: Record<string, unknown>) =>
260
+ sessionStarted: (data: SessionStartedPayload) => eventBus.emit(EventTypes.SESSION_STARTED, data),
261
+ sessionPaused: (data: SessionPausedPayload) => eventBus.emit(EventTypes.SESSION_PAUSED, data),
262
+ sessionResumed: (data: SessionResumedPayload) => eventBus.emit(EventTypes.SESSION_RESUMED, data),
263
+ sessionCompleted: (data: SessionCompletedPayload) =>
242
264
  eventBus.emit(EventTypes.SESSION_COMPLETED, data),
243
265
 
244
- taskCreated: (data: Record<string, unknown>) => eventBus.emit(EventTypes.TASK_CREATED, data),
245
- taskCompleted: (data: Record<string, unknown>) => eventBus.emit(EventTypes.TASK_COMPLETED, data),
266
+ taskCreated: (data: TaskCreatedPayload) => eventBus.emit(EventTypes.TASK_CREATED, data),
267
+ taskCompleted: (data: TaskCompletedPayload) => eventBus.emit(EventTypes.TASK_COMPLETED, data),
246
268
 
247
- featureAdded: (data: Record<string, unknown>) => eventBus.emit(EventTypes.FEATURE_ADDED, data),
248
- featureShipped: (data: Record<string, unknown>) =>
249
- eventBus.emit(EventTypes.FEATURE_SHIPPED, data),
269
+ featureAdded: (data: FeaturePayload) => eventBus.emit(EventTypes.FEATURE_ADDED, data),
270
+ featureShipped: (data: FeaturePayload) => eventBus.emit(EventTypes.FEATURE_SHIPPED, data),
250
271
 
251
- ideaCaptured: (data: Record<string, unknown>) => eventBus.emit(EventTypes.IDEA_CAPTURED, data),
272
+ ideaCaptured: (data: IdeaCapturedPayload) => eventBus.emit(EventTypes.IDEA_CAPTURED, data),
252
273
 
253
- snapshotCreated: (data: Record<string, unknown>) =>
274
+ snapshotCreated: (data: SnapshotCreatedPayload) =>
254
275
  eventBus.emit(EventTypes.SNAPSHOT_CREATED, data),
255
- snapshotRestored: (data: Record<string, unknown>) =>
276
+ snapshotRestored: (data: SnapshotRestoredPayload) =>
256
277
  eventBus.emit(EventTypes.SNAPSHOT_RESTORED, data),
257
278
 
258
- commitCreated: (data: Record<string, unknown>) => eventBus.emit(EventTypes.COMMIT_CREATED, data),
259
- pushCompleted: (data: Record<string, unknown>) => eventBus.emit(EventTypes.PUSH_COMPLETED, data),
279
+ commitCreated: (data: GitCommitPayload) => eventBus.emit(EventTypes.COMMIT_CREATED, data),
280
+ pushCompleted: (data: GitPushPayload) => eventBus.emit(EventTypes.PUSH_COMPLETED, data),
260
281
 
261
- projectInitialized: (data: Record<string, unknown>) =>
282
+ projectInitialized: (data: ProjectInitializedPayload) =>
262
283
  eventBus.emit(EventTypes.PROJECT_INITIALIZED, data),
263
- projectSynced: (data: Record<string, unknown>) => eventBus.emit(EventTypes.PROJECT_SYNCED, data),
264
- analysisCompleted: (data: Record<string, unknown>) =>
284
+ projectSynced: (data: ProjectSyncedPayload) => eventBus.emit(EventTypes.PROJECT_SYNCED, data),
285
+ analysisCompleted: (data: AnalysisCompletedPayload) =>
265
286
  eventBus.emit(EventTypes.ANALYSIS_COMPLETED, data),
266
287
  }
267
288
 
@@ -347,8 +347,18 @@ export class AnalysisCommands extends PrjctCommandsBase {
347
347
  return { success: true, message: 'No changes' }
348
348
  }
349
349
 
350
+ // Helper to restore original CLAUDE.md (undo sync's write)
351
+ const restoreOriginal = async () => {
352
+ if (existingContent != null) {
353
+ await fs.writeFile(claudeMdPath, existingContent, 'utf-8')
354
+ }
355
+ }
356
+
350
357
  // Non-interactive mode: return JSON for LLM to handle
351
358
  if (isNonInteractive) {
359
+ // Restore original — LLM will call `prjct sync --yes` to apply
360
+ await restoreOriginal()
361
+
352
362
  // Build a plain-text diff summary for LLM to show user
353
363
  const diffSummary = {
354
364
  added: diff.added.map((s) => ({ name: s.name, lineCount: s.lineCount })),
@@ -388,8 +398,9 @@ export class AnalysisCommands extends PrjctCommandsBase {
388
398
  // Show diff preview (interactive mode)
389
399
  console.log(formatDiffPreview(diff))
390
400
 
391
- // Preview-only mode - don't apply
401
+ // Preview-only mode (--preview / --dry-run) - restore and don't apply
392
402
  if (options.preview) {
403
+ await restoreOriginal()
393
404
  return {
394
405
  success: true,
395
406
  isPreview: true,
@@ -411,7 +422,8 @@ export class AnalysisCommands extends PrjctCommandsBase {
411
422
  })
412
423
 
413
424
  if (response.action === 'cancel' || !response.action) {
414
- out.warn('Sync cancelled')
425
+ await restoreOriginal()
426
+ out.warn('Sync cancelled — no changes applied')
415
427
  return { success: false, message: 'Cancelled by user' }
416
428
  }
417
429
 
@@ -424,12 +436,13 @@ export class AnalysisCommands extends PrjctCommandsBase {
424
436
  initial: true,
425
437
  })
426
438
  if (!confirm.apply) {
427
- out.warn('Sync cancelled')
439
+ await restoreOriginal()
440
+ out.warn('Sync cancelled — no changes applied')
428
441
  return { success: false, message: 'Cancelled by user' }
429
442
  }
430
443
  }
431
444
 
432
- // Changes already applied from dry-run, just show success
445
+ // User approved changes already applied by sync
433
446
  out.done('Changes applied')
434
447
  return this.showSyncResult(result, startTime)
435
448
  }
package/core/index.ts CHANGED
@@ -162,7 +162,7 @@ async function main(): Promise<void> {
162
162
  sync: () =>
163
163
  commands.sync(process.cwd(), {
164
164
  aiTools: options.agents ? String(options.agents).split(',') : undefined,
165
- preview: options.preview === true,
165
+ preview: options.preview === true || options['dry-run'] === true,
166
166
  yes: options.yes === true,
167
167
  json: options.json === true,
168
168
  package: options.package ? String(options.package) : undefined,
@@ -12,6 +12,7 @@ import { Hono } from 'hono'
12
12
  import * as jsonc from 'jsonc-parser'
13
13
  import pathManager from '../infrastructure/path-manager'
14
14
  import { isNotFoundError } from '../types/fs'
15
+ import log from '../utils/logger'
15
16
 
16
17
  // Storage paths relative to project data directory
17
18
  const STORAGE_PATHS = {
@@ -34,7 +35,7 @@ async function readJsonFile<T>(filePath: string): Promise<T | null> {
34
35
  } catch (error) {
35
36
  // ENOENT or parse error - expected for new projects
36
37
  if (!isNotFoundError(error) && !(error instanceof SyntaxError)) {
37
- console.error(`JSON read error: ${(error as Error).message}`)
38
+ log.error(`JSON read error: ${(error as Error).message}`)
38
39
  }
39
40
  return null
40
41
  }
@@ -49,7 +50,7 @@ async function writeJsonFile(filePath: string, data: unknown): Promise<boolean>
49
50
  await fs.writeFile(filePath, `${JSON.stringify(data, null, 2)}\n`, 'utf-8')
50
51
  return true
51
52
  } catch (error) {
52
- console.error(`JSON write error: ${(error as Error).message}`)
53
+ log.error(`JSON write error: ${(error as Error).message}`)
53
54
  return false
54
55
  }
55
56
  }
@@ -165,7 +166,7 @@ export function createRoutes(projectId: string, _projectPath: string): Hono {
165
166
  } catch (error) {
166
167
  // ENOENT - context file doesn't exist yet (expected)
167
168
  if (!isNotFoundError(error)) {
168
- console.error(`Context read error: ${(error as Error).message}`)
169
+ log.error(`Context read error: ${(error as Error).message}`)
169
170
  }
170
171
  return c.text('', 200, { 'Content-Type': 'text/markdown' })
171
172
  }
package/core/types/bus.ts CHANGED
@@ -58,7 +58,7 @@ export const EventTypes = {
58
58
  export type BusEventType = (typeof EventTypes)[keyof typeof EventTypes]
59
59
 
60
60
  /**
61
- * Event data payload
61
+ * Event data payload (base interface for all events)
62
62
  */
63
63
  export interface EventData {
64
64
  type: string
@@ -67,6 +67,117 @@ export interface EventData {
67
67
  [key: string]: unknown
68
68
  }
69
69
 
70
+ // =============================================================================
71
+ // Typed Event Payloads
72
+ // =============================================================================
73
+
74
+ export interface SessionStartedPayload {
75
+ sessionId: string
76
+ task: unknown
77
+ projectId: string | null
78
+ }
79
+
80
+ export interface SessionPausedPayload {
81
+ sessionId: string
82
+ task: unknown
83
+ duration: number
84
+ projectId: string | null
85
+ }
86
+
87
+ export interface SessionResumedPayload {
88
+ sessionId: string
89
+ task: unknown
90
+ projectId: string | null
91
+ }
92
+
93
+ export interface SessionCompletedPayload {
94
+ sessionId: string
95
+ task: unknown
96
+ duration: number
97
+ metrics: unknown
98
+ projectId: string | null
99
+ }
100
+
101
+ export interface TaskCreatedPayload {
102
+ taskId: string
103
+ description: string
104
+ [key: string]: unknown
105
+ }
106
+
107
+ export interface TaskCompletedPayload {
108
+ taskId: string
109
+ [key: string]: unknown
110
+ }
111
+
112
+ export interface FeaturePayload {
113
+ [key: string]: unknown
114
+ }
115
+
116
+ export interface IdeaCapturedPayload {
117
+ [key: string]: unknown
118
+ }
119
+
120
+ export interface SnapshotCreatedPayload {
121
+ hash: string
122
+ message: string
123
+ timestamp: string
124
+ filesCount: number
125
+ projectId: string | null
126
+ }
127
+
128
+ export interface SnapshotRestoredPayload {
129
+ hash: string
130
+ filesCount: number
131
+ timestamp: string
132
+ projectId: string | null
133
+ }
134
+
135
+ export interface GitCommitPayload {
136
+ [key: string]: unknown
137
+ }
138
+
139
+ export interface GitPushPayload {
140
+ [key: string]: unknown
141
+ }
142
+
143
+ export interface ProjectInitializedPayload {
144
+ [key: string]: unknown
145
+ }
146
+
147
+ export interface ProjectSyncedPayload {
148
+ [key: string]: unknown
149
+ }
150
+
151
+ export interface AnalysisCompletedPayload {
152
+ [key: string]: unknown
153
+ }
154
+
155
+ /**
156
+ * Maps event type strings to their typed payloads
157
+ */
158
+ export interface EventMap {
159
+ [EventTypes.SESSION_STARTED]: SessionStartedPayload
160
+ [EventTypes.SESSION_PAUSED]: SessionPausedPayload
161
+ [EventTypes.SESSION_RESUMED]: SessionResumedPayload
162
+ [EventTypes.SESSION_COMPLETED]: SessionCompletedPayload
163
+ [EventTypes.TASK_CREATED]: TaskCreatedPayload
164
+ [EventTypes.TASK_COMPLETED]: TaskCompletedPayload
165
+ [EventTypes.TASK_UPDATED]: Record<string, unknown>
166
+ [EventTypes.FEATURE_ADDED]: FeaturePayload
167
+ [EventTypes.FEATURE_SHIPPED]: FeaturePayload
168
+ [EventTypes.FEATURE_UPDATED]: FeaturePayload
169
+ [EventTypes.IDEA_CAPTURED]: IdeaCapturedPayload
170
+ [EventTypes.IDEA_PROMOTED]: Record<string, unknown>
171
+ [EventTypes.SNAPSHOT_CREATED]: SnapshotCreatedPayload
172
+ [EventTypes.SNAPSHOT_RESTORED]: SnapshotRestoredPayload
173
+ [EventTypes.COMMIT_CREATED]: GitCommitPayload
174
+ [EventTypes.PUSH_COMPLETED]: GitPushPayload
175
+ [EventTypes.PROJECT_INITIALIZED]: ProjectInitializedPayload
176
+ [EventTypes.PROJECT_SYNCED]: ProjectSyncedPayload
177
+ [EventTypes.ANALYSIS_COMPLETED]: AnalysisCompletedPayload
178
+ [EventTypes.ALL]: Record<string, unknown>
179
+ }
180
+
70
181
  /**
71
182
  * Event callback handler
72
183
  */
@@ -118,10 +118,26 @@ export type {
118
118
  // Bus Types
119
119
  // =============================================================================
120
120
  export type {
121
+ AnalysisCompletedPayload,
121
122
  BusEventType,
122
123
  EventCallback,
123
124
  EventData,
125
+ EventMap,
124
126
  EventSubscription,
127
+ FeaturePayload,
128
+ GitCommitPayload,
129
+ GitPushPayload,
130
+ IdeaCapturedPayload,
131
+ ProjectInitializedPayload,
132
+ ProjectSyncedPayload,
133
+ SessionCompletedPayload,
134
+ SessionPausedPayload,
135
+ SessionResumedPayload,
136
+ SessionStartedPayload,
137
+ SnapshotCreatedPayload,
138
+ SnapshotRestoredPayload,
139
+ TaskCompletedPayload,
140
+ TaskCreatedPayload,
125
141
  } from './bus'
126
142
  export { EventTypes } from './bus'
127
143
  // =============================================================================
@@ -2092,6 +2092,45 @@ var init_editors_config = __esm({
2092
2092
  }
2093
2093
  });
2094
2094
 
2095
+ // core/utils/logger.ts
2096
+ function getLogLevel() {
2097
+ const debugEnv = process.env.PRJCT_DEBUG || process.env.DEBUG || "";
2098
+ if (!debugEnv) return { level: -1, name: "disabled" };
2099
+ if (TRUTHY_VALUES.has(debugEnv) || debugEnv.includes("prjct")) {
2100
+ return { level: LEVELS.debug, name: "debug" };
2101
+ }
2102
+ const levelValue = LEVELS[debugEnv] ?? -1;
2103
+ const levelName = levelValue >= 0 ? debugEnv : "disabled";
2104
+ return { level: levelValue, name: levelName };
2105
+ }
2106
+ function createLogMethod(levelThreshold, prefix, method) {
2107
+ return currentLevel >= levelThreshold ? (...args2) => console[method](prefix, ...args2) : noop;
2108
+ }
2109
+ var LEVELS, TRUTHY_VALUES, currentLevel, currentLevelName, noop, logger, logger_default;
2110
+ var init_logger = __esm({
2111
+ "core/utils/logger.ts"() {
2112
+ "use strict";
2113
+ LEVELS = { error: 0, warn: 1, info: 2, debug: 3 };
2114
+ TRUTHY_VALUES = /* @__PURE__ */ new Set(["1", "true", "*"]);
2115
+ __name(getLogLevel, "getLogLevel");
2116
+ ({ level: currentLevel, name: currentLevelName } = getLogLevel());
2117
+ noop = /* @__PURE__ */ __name(() => {
2118
+ }, "noop");
2119
+ __name(createLogMethod, "createLogMethod");
2120
+ logger = {
2121
+ error: createLogMethod(LEVELS.error, "[prjct:error]", "error"),
2122
+ warn: createLogMethod(LEVELS.warn, "[prjct:warn]", "warn"),
2123
+ info: createLogMethod(LEVELS.info, "[prjct:info]", "log"),
2124
+ debug: createLogMethod(LEVELS.debug, "[prjct:debug]", "log"),
2125
+ // Check if logging is enabled (useful for expensive log prep)
2126
+ isEnabled: /* @__PURE__ */ __name(() => currentLevel >= 0, "isEnabled"),
2127
+ // Get current level name (pre-computed, no runtime lookup)
2128
+ level: /* @__PURE__ */ __name(() => currentLevelName, "level")
2129
+ };
2130
+ logger_default = logger;
2131
+ }
2132
+ });
2133
+
2095
2134
  // core/utils/branding.ts
2096
2135
  import chalk from "chalk";
2097
2136
  var SPINNER_FRAMES, SPINNER_SPEED, branding, branding_default;
@@ -19160,7 +19199,13 @@ var init_analysis2 = __esm({
19160
19199
  output_default.done("No changes detected (context is up to date)");
19161
19200
  return { success: true, message: "No changes" };
19162
19201
  }
19202
+ const restoreOriginal = /* @__PURE__ */ __name(async () => {
19203
+ if (existingContent != null) {
19204
+ await fs36.writeFile(claudeMdPath, existingContent, "utf-8");
19205
+ }
19206
+ }, "restoreOriginal");
19163
19207
  if (isNonInteractive) {
19208
+ await restoreOriginal();
19164
19209
  const diffSummary = {
19165
19210
  added: diff.added.map((s) => ({ name: s.name, lineCount: s.lineCount })),
19166
19211
  modified: diff.modified.map((s) => ({ name: s.name, lineCount: s.lineCount })),
@@ -19193,6 +19238,7 @@ var init_analysis2 = __esm({
19193
19238
  }
19194
19239
  console.log(formatDiffPreview(diff));
19195
19240
  if (options.preview) {
19241
+ await restoreOriginal();
19196
19242
  return {
19197
19243
  success: true,
19198
19244
  isPreview: true,
@@ -19211,7 +19257,8 @@ var init_analysis2 = __esm({
19211
19257
  ]
19212
19258
  });
19213
19259
  if (response.action === "cancel" || !response.action) {
19214
- output_default.warn("Sync cancelled");
19260
+ await restoreOriginal();
19261
+ output_default.warn("Sync cancelled \u2014 no changes applied");
19215
19262
  return { success: false, message: "Cancelled by user" };
19216
19263
  }
19217
19264
  if (response.action === "diff") {
@@ -19224,7 +19271,8 @@ ${formatFullDiff(diff)}`);
19224
19271
  initial: true
19225
19272
  });
19226
19273
  if (!confirm.apply) {
19227
- output_default.warn("Sync cancelled");
19274
+ await restoreOriginal();
19275
+ output_default.warn("Sync cancelled \u2014 no changes applied");
19228
19276
  return { success: false, message: "Cancelled by user" };
19229
19277
  }
19230
19278
  }
@@ -21052,45 +21100,6 @@ var init_ai_tools = __esm({
21052
21100
  }
21053
21101
  });
21054
21102
 
21055
- // core/utils/logger.ts
21056
- function getLogLevel() {
21057
- const debugEnv = process.env.PRJCT_DEBUG || process.env.DEBUG || "";
21058
- if (!debugEnv) return { level: -1, name: "disabled" };
21059
- if (TRUTHY_VALUES.has(debugEnv) || debugEnv.includes("prjct")) {
21060
- return { level: LEVELS.debug, name: "debug" };
21061
- }
21062
- const levelValue = LEVELS[debugEnv] ?? -1;
21063
- const levelName = levelValue >= 0 ? debugEnv : "disabled";
21064
- return { level: levelValue, name: levelName };
21065
- }
21066
- function createLogMethod(levelThreshold, prefix, method) {
21067
- return currentLevel >= levelThreshold ? (...args2) => console[method](prefix, ...args2) : noop;
21068
- }
21069
- var LEVELS, TRUTHY_VALUES, currentLevel, currentLevelName, noop, logger2, logger_default;
21070
- var init_logger = __esm({
21071
- "core/utils/logger.ts"() {
21072
- "use strict";
21073
- LEVELS = { error: 0, warn: 1, info: 2, debug: 3 };
21074
- TRUTHY_VALUES = /* @__PURE__ */ new Set(["1", "true", "*"]);
21075
- __name(getLogLevel, "getLogLevel");
21076
- ({ level: currentLevel, name: currentLevelName } = getLogLevel());
21077
- noop = /* @__PURE__ */ __name(() => {
21078
- }, "noop");
21079
- __name(createLogMethod, "createLogMethod");
21080
- logger2 = {
21081
- error: createLogMethod(LEVELS.error, "[prjct:error]", "error"),
21082
- warn: createLogMethod(LEVELS.warn, "[prjct:warn]", "warn"),
21083
- info: createLogMethod(LEVELS.info, "[prjct:info]", "log"),
21084
- debug: createLogMethod(LEVELS.debug, "[prjct:debug]", "log"),
21085
- // Check if logging is enabled (useful for expensive log prep)
21086
- isEnabled: /* @__PURE__ */ __name(() => currentLevel >= 0, "isEnabled"),
21087
- // Get current level name (pre-computed, no runtime lookup)
21088
- level: /* @__PURE__ */ __name(() => currentLevelName, "level")
21089
- };
21090
- logger_default = logger2;
21091
- }
21092
- });
21093
-
21094
21103
  // core/services/context-generator.ts
21095
21104
  import fs39 from "node:fs/promises";
21096
21105
  import path43 from "node:path";
@@ -28604,7 +28613,7 @@ var require_package = __commonJS({
28604
28613
  "package.json"(exports, module) {
28605
28614
  module.exports = {
28606
28615
  name: "prjct-cli",
28607
- version: "1.6.0",
28616
+ version: "1.6.2",
28608
28617
  description: "Context layer for AI agents. Project context for Claude Code, Gemini CLI, and more.",
28609
28618
  main: "core/index.ts",
28610
28619
  bin: {
@@ -28812,7 +28821,7 @@ async function main() {
28812
28821
  // Setup
28813
28822
  sync: /* @__PURE__ */ __name(() => commands.sync(process.cwd(), {
28814
28823
  aiTools: options.agents ? String(options.agents).split(",") : void 0,
28815
- preview: options.preview === true,
28824
+ preview: options.preview === true || options["dry-run"] === true,
28816
28825
  yes: options.yes === true,
28817
28826
  json: options.json === true,
28818
28827
  package: options.package ? String(options.package) : void 0
@@ -29058,7 +29067,7 @@ import chalk19 from "chalk";
29058
29067
  // core/server/server.ts
29059
29068
  import { Hono as Hono3 } from "hono";
29060
29069
  import { cors } from "hono/cors";
29061
- import { logger } from "hono/logger";
29070
+ import { logger as logger2 } from "hono/logger";
29062
29071
 
29063
29072
  // core/utils/runtime.ts
29064
29073
  function detectRuntime() {
@@ -29076,6 +29085,7 @@ __name(isBun, "isBun");
29076
29085
  // core/server/routes.ts
29077
29086
  init_path_manager();
29078
29087
  init_fs();
29088
+ init_logger();
29079
29089
  import fs7 from "node:fs/promises";
29080
29090
  import path7 from "node:path";
29081
29091
  import { Hono } from "hono";
@@ -29095,7 +29105,7 @@ async function readJsonFile(filePath) {
29095
29105
  return errors.length > 0 ? null : result;
29096
29106
  } catch (error) {
29097
29107
  if (!isNotFoundError(error) && !(error instanceof SyntaxError)) {
29098
- console.error(`JSON read error: ${error.message}`);
29108
+ logger_default.error(`JSON read error: ${error.message}`);
29099
29109
  }
29100
29110
  return null;
29101
29111
  }
@@ -29108,7 +29118,7 @@ async function writeJsonFile(filePath, data) {
29108
29118
  `, "utf-8");
29109
29119
  return true;
29110
29120
  } catch (error) {
29111
- console.error(`JSON write error: ${error.message}`);
29121
+ logger_default.error(`JSON write error: ${error.message}`);
29112
29122
  return false;
29113
29123
  }
29114
29124
  }
@@ -29198,7 +29208,7 @@ function createRoutes(projectId, _projectPath) {
29198
29208
  return c.text(content, 200, { "Content-Type": "text/markdown" });
29199
29209
  } catch (error) {
29200
29210
  if (!isNotFoundError(error)) {
29201
- console.error(`Context read error: ${error.message}`);
29211
+ logger_default.error(`Context read error: ${error.message}`);
29202
29212
  }
29203
29213
  return c.text("", 200, { "Content-Type": "text/markdown" });
29204
29214
  }
@@ -29704,7 +29714,7 @@ function createServer(config) {
29704
29714
  );
29705
29715
  }
29706
29716
  if (config.enableLogging !== false) {
29707
- app.use("*", logger());
29717
+ app.use("*", logger2());
29708
29718
  }
29709
29719
  app.get("/health", (c) => c.json({ status: "ok", timestamp: (/* @__PURE__ */ new Date()).toISOString() }));
29710
29720
  app.get(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prjct-cli",
3
- "version": "1.6.0",
3
+ "version": "1.6.2",
4
4
  "description": "Context layer for AI agents. Project context for Claude Code, Gemini CLI, and more.",
5
5
  "main": "core/index.ts",
6
6
  "bin": {