prjct-cli 0.48.0 → 0.49.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.
package/CHANGELOG.md CHANGED
@@ -1,10 +1,21 @@
1
1
  # Changelog
2
2
 
3
- ## [0.48.0] - 2026-01-30
3
+ ## [0.49.0] - 2026-01-30
4
4
 
5
5
  ### Features
6
6
 
7
- - Read-before-write enforcement for templates - PRJ-108 (#74)
7
+ - Error messages with context and recovery hints - PRJ-131 (#75)
8
+
9
+
10
+ ## [0.49.1] - 2026-01-30
11
+
12
+ ### Improved
13
+
14
+ - **Error messages with context and recovery hints** (PRJ-131)
15
+ - Created error catalog with 15+ error types
16
+ - Added `out.failWithHint()` for rich error output
17
+ - Errors now show file path and recovery hints
18
+ - Example: `✗ Project ID not found` → `💡 Run 'prjct init'`
8
19
 
9
20
 
10
21
  ## [0.49.0] - 2026-01-30
@@ -39,7 +39,7 @@ export class AnalyticsCommands extends PrjctCommandsBase {
39
39
 
40
40
  const projectId = await configManager.getProjectId(projectPath)
41
41
  if (!projectId) {
42
- out.fail('no project ID')
42
+ out.failWithHint('NO_PROJECT_ID')
43
43
  return { success: false, error: 'No project ID found' }
44
44
  }
45
45
 
@@ -79,7 +79,7 @@ export async function cleanup(
79
79
 
80
80
  const projectId = await configManager.getProjectId(projectPath)
81
81
  if (!projectId) {
82
- out.fail('no project ID')
82
+ out.failWithHint('NO_PROJECT_ID')
83
83
  return { success: false, error: 'No project ID found' }
84
84
  }
85
85
 
@@ -254,7 +254,7 @@ export class PlanningCommands extends PrjctCommandsBase {
254
254
 
255
255
  const projectId = await configManager.getProjectId(projectPath)
256
256
  if (!projectId) {
257
- out.fail('no project ID')
257
+ out.failWithHint('NO_PROJECT_ID')
258
258
  return { success: false, error: 'No project ID found' }
259
259
  }
260
260
 
@@ -325,7 +325,7 @@ export class PlanningCommands extends PrjctCommandsBase {
325
325
 
326
326
  const projectId = await configManager.getProjectId(projectPath)
327
327
  if (!projectId) {
328
- out.fail('no project ID')
328
+ out.failWithHint('NO_PROJECT_ID')
329
329
  return { success: false, error: 'No project ID found' }
330
330
  }
331
331
 
@@ -485,7 +485,7 @@ export class PlanningCommands extends PrjctCommandsBase {
485
485
 
486
486
  const projectId = await configManager.getProjectId(projectPath)
487
487
  if (!projectId) {
488
- out.fail('no project ID')
488
+ out.failWithHint('NO_PROJECT_ID')
489
489
  return { success: false, error: 'No project ID found' }
490
490
  }
491
491
 
@@ -562,7 +562,7 @@ Generated: ${new Date().toLocaleString()}
562
562
 
563
563
  const projectId = await configManager.getProjectId(projectPath)
564
564
  if (!projectId) {
565
- out.fail('no project ID')
565
+ out.failWithHint('NO_PROJECT_ID')
566
566
  return { success: false, error: 'No project ID found' }
567
567
  }
568
568
 
@@ -60,7 +60,7 @@ export class ShippingCommands extends PrjctCommandsBase {
60
60
 
61
61
  const projectId = await configManager.getProjectId(projectPath)
62
62
  if (!projectId) {
63
- out.fail('no project ID')
63
+ out.failWithHint('NO_PROJECT_ID')
64
64
  return { success: false, error: 'No project ID found' }
65
65
  }
66
66
 
@@ -28,7 +28,7 @@ export async function recover(projectPath: string = process.cwd()): Promise<Comm
28
28
  try {
29
29
  const projectId = await configManager.getProjectId(projectPath)
30
30
  if (!projectId) {
31
- out.fail('no project ID')
31
+ out.failWithHint('NO_PROJECT_ID')
32
32
  return { success: false, error: 'No project ID found' }
33
33
  }
34
34
 
@@ -85,7 +85,7 @@ export async function undo(projectPath: string = process.cwd()): Promise<Command
85
85
 
86
86
  const projectId = await configManager.getProjectId(projectPath)
87
87
  if (!projectId) {
88
- out.fail('no project ID')
88
+ out.failWithHint('NO_PROJECT_ID')
89
89
  return { success: false, error: 'No project ID found' }
90
90
  }
91
91
 
@@ -147,7 +147,7 @@ export async function undo(projectPath: string = process.cwd()): Promise<Command
147
147
  out.done('changes stashed (use /p:redo to restore)')
148
148
  return { success: true, snapshotId: stashMessage }
149
149
  } catch (gitError) {
150
- out.fail('git operation failed')
150
+ out.failWithHint('GIT_OPERATION_FAILED')
151
151
  return { success: false, error: (gitError as Error).message }
152
152
  }
153
153
  } catch (error) {
@@ -165,7 +165,7 @@ export async function redo(projectPath: string = process.cwd()): Promise<Command
165
165
 
166
166
  const projectId = await configManager.getProjectId(projectPath)
167
167
  if (!projectId) {
168
- out.fail('no project ID')
168
+ out.failWithHint('NO_PROJECT_ID')
169
169
  return { success: false, error: 'No project ID found' }
170
170
  }
171
171
 
@@ -231,7 +231,7 @@ export async function redo(projectPath: string = process.cwd()): Promise<Command
231
231
  out.done('changes restored')
232
232
  return { success: true }
233
233
  } catch (gitError) {
234
- out.fail('git operation failed')
234
+ out.failWithHint('GIT_OPERATION_FAILED')
235
235
  return { success: false, error: (gitError as Error).message }
236
236
  }
237
237
  } catch (error) {
@@ -247,7 +247,7 @@ export async function history(projectPath: string = process.cwd()): Promise<Comm
247
247
  try {
248
248
  const projectId = await configManager.getProjectId(projectPath)
249
249
  if (!projectId) {
250
- out.fail('no project ID')
250
+ out.failWithHint('NO_PROJECT_ID')
251
251
  return { success: false, error: 'No project ID found' }
252
252
  }
253
253
 
@@ -43,7 +43,7 @@ export class WorkflowCommands extends PrjctCommandsBase {
43
43
 
44
44
  const projectId = await configManager.getProjectId(projectPath)
45
45
  if (!projectId) {
46
- out.fail('no project ID')
46
+ out.failWithHint('NO_PROJECT_ID')
47
47
  return { success: false, error: 'No project ID found' }
48
48
  }
49
49
 
@@ -163,7 +163,7 @@ export class WorkflowCommands extends PrjctCommandsBase {
163
163
 
164
164
  const projectId = await configManager.getProjectId(projectPath)
165
165
  if (!projectId) {
166
- out.fail('no project ID')
166
+ out.failWithHint('NO_PROJECT_ID')
167
167
  return { success: false, error: 'No project ID found' }
168
168
  }
169
169
 
@@ -246,7 +246,7 @@ export class WorkflowCommands extends PrjctCommandsBase {
246
246
 
247
247
  const projectId = await configManager.getProjectId(projectPath)
248
248
  if (!projectId) {
249
- out.fail('no project ID')
249
+ out.failWithHint('NO_PROJECT_ID')
250
250
  return { success: false, error: 'No project ID found' }
251
251
  }
252
252
 
@@ -278,7 +278,7 @@ export class WorkflowCommands extends PrjctCommandsBase {
278
278
 
279
279
  const projectId = await configManager.getProjectId(projectPath)
280
280
  if (!projectId) {
281
- out.fail('no project ID')
281
+ out.failWithHint('NO_PROJECT_ID')
282
282
  return { success: false, error: 'No project ID found' }
283
283
  }
284
284
 
@@ -323,7 +323,7 @@ export class WorkflowCommands extends PrjctCommandsBase {
323
323
 
324
324
  const projectId = await configManager.getProjectId(projectPath)
325
325
  if (!projectId) {
326
- out.fail('no project ID')
326
+ out.failWithHint('NO_PROJECT_ID')
327
327
  return { success: false, error: 'No project ID found' }
328
328
  }
329
329
 
@@ -374,7 +374,7 @@ export class WorkflowCommands extends PrjctCommandsBase {
374
374
 
375
375
  const projectId = await configManager.getProjectId(projectPath)
376
376
  if (!projectId) {
377
- out.fail('no project ID')
377
+ out.failWithHint('NO_PROJECT_ID')
378
378
  return { success: false, error: 'No project ID found' }
379
379
  }
380
380
 
@@ -0,0 +1,161 @@
1
+ /**
2
+ * Error Messages Catalog
3
+ *
4
+ * Centralized error messages with context and recovery hints.
5
+ * Provides consistent, helpful error output across the CLI.
6
+ *
7
+ * @see PRJ-131
8
+ * @module utils/error-messages
9
+ */
10
+
11
+ export interface ErrorWithHint {
12
+ message: string
13
+ hint?: string
14
+ file?: string
15
+ docs?: string
16
+ code?: string
17
+ }
18
+
19
+ /**
20
+ * Common error messages with recovery hints
21
+ */
22
+ export const ERRORS = {
23
+ // Project errors
24
+ NO_PROJECT: {
25
+ message: 'No prjct project found in this directory',
26
+ hint: "Run 'prjct init' to set up a new project",
27
+ file: '.prjct/prjct.config.json',
28
+ },
29
+
30
+ NO_PROJECT_ID: {
31
+ message: 'Project ID not found',
32
+ hint: "Run 'prjct init' or check .prjct/prjct.config.json",
33
+ file: '.prjct/prjct.config.json',
34
+ },
35
+
36
+ CONFIG_NOT_FOUND: {
37
+ message: 'Configuration file not found',
38
+ hint: "Run 'prjct init' to create project configuration",
39
+ file: '.prjct/prjct.config.json',
40
+ },
41
+
42
+ CONFIG_INVALID: {
43
+ message: 'Invalid configuration file',
44
+ hint: 'Check JSON syntax or delete .prjct/ and run init again',
45
+ file: '.prjct/prjct.config.json',
46
+ },
47
+
48
+ // Git errors
49
+ GIT_NOT_FOUND: {
50
+ message: 'Git repository not detected',
51
+ hint: "Run 'git init' first, then 'prjct init'",
52
+ },
53
+
54
+ GIT_NO_COMMITS: {
55
+ message: 'No commits in repository',
56
+ hint: 'Make an initial commit before using prjct',
57
+ },
58
+
59
+ GIT_DIRTY: {
60
+ message: 'Working directory has uncommitted changes',
61
+ hint: "Commit or stash changes, or use '--force' to override",
62
+ },
63
+
64
+ GIT_ON_MAIN: {
65
+ message: 'Cannot ship from main/master branch',
66
+ hint: 'Create a feature branch first: git checkout -b feature/your-feature',
67
+ },
68
+
69
+ GIT_OPERATION_FAILED: {
70
+ message: 'Git operation failed',
71
+ hint: 'Check git status and resolve any conflicts',
72
+ },
73
+
74
+ // Auth errors
75
+ GH_NOT_AUTHENTICATED: {
76
+ message: 'GitHub CLI not authenticated',
77
+ hint: "Run 'gh auth login' to authenticate",
78
+ docs: 'https://cli.github.com/manual/gh_auth_login',
79
+ },
80
+
81
+ LINEAR_NOT_CONFIGURED: {
82
+ message: 'Linear integration not configured',
83
+ hint: "Run 'p. linear setup' to configure Linear",
84
+ },
85
+
86
+ LINEAR_API_ERROR: {
87
+ message: 'Linear API error',
88
+ hint: 'Check your API key or network connection',
89
+ },
90
+
91
+ // Task errors
92
+ NO_ACTIVE_TASK: {
93
+ message: 'No active task',
94
+ hint: 'Start a task with \'p. task "description"\'',
95
+ },
96
+
97
+ TASK_ALREADY_ACTIVE: {
98
+ message: 'A task is already in progress',
99
+ hint: "Complete it with 'p. done' or pause with 'p. pause'",
100
+ },
101
+
102
+ // Sync errors
103
+ SYNC_FAILED: {
104
+ message: 'Project sync failed',
105
+ hint: 'Check file permissions and try again',
106
+ },
107
+
108
+ // Ship errors
109
+ NOTHING_TO_SHIP: {
110
+ message: 'Nothing to ship',
111
+ hint: 'Make some changes first, then run ship',
112
+ },
113
+
114
+ PR_CREATE_FAILED: {
115
+ message: 'Failed to create pull request',
116
+ hint: 'Check GitHub auth and remote configuration',
117
+ },
118
+
119
+ // Provider errors
120
+ NO_AI_PROVIDER: {
121
+ message: 'No AI provider detected',
122
+ hint: "Install Claude Code or Gemini CLI, then run 'prjct start'",
123
+ docs: 'https://prjct.app/docs',
124
+ },
125
+
126
+ PROVIDER_NOT_CONFIGURED: {
127
+ message: 'AI provider not configured for prjct',
128
+ hint: "Run 'prjct start' to configure your provider",
129
+ },
130
+
131
+ // Generic
132
+ UNKNOWN: {
133
+ message: 'An unexpected error occurred',
134
+ hint: 'Check the error details and try again',
135
+ },
136
+ } as const
137
+
138
+ export type ErrorCode = keyof typeof ERRORS
139
+
140
+ /**
141
+ * Get error with optional overrides
142
+ */
143
+ export function getError(code: ErrorCode, overrides?: Partial<ErrorWithHint>): ErrorWithHint {
144
+ const base = ERRORS[code]
145
+ return { ...base, ...overrides }
146
+ }
147
+
148
+ /**
149
+ * Create a custom error with hint
150
+ */
151
+ export function createError(
152
+ message: string,
153
+ hint?: string,
154
+ options?: { file?: string; docs?: string }
155
+ ): ErrorWithHint {
156
+ return {
157
+ message,
158
+ hint,
159
+ ...options,
160
+ }
161
+ }
@@ -8,6 +8,8 @@
8
8
 
9
9
  import chalk from 'chalk'
10
10
  import branding from './branding'
11
+ import type { ErrorCode, ErrorWithHint } from './error-messages'
12
+ import { getError } from './error-messages'
11
13
 
12
14
  const _FRAMES = branding.spinner.frames
13
15
  const SPEED = branding.spinner.speed
@@ -53,6 +55,7 @@ interface Output {
53
55
  spin(msg: string): Output
54
56
  done(msg: string, metrics?: OutputMetrics): Output
55
57
  fail(msg: string): Output
58
+ failWithHint(error: ErrorWithHint | ErrorCode): Output
56
59
  warn(msg: string): Output
57
60
  stop(): Output
58
61
  step(current: number, total: number, msg: string): Output
@@ -108,6 +111,25 @@ const out: Output = {
108
111
  return this
109
112
  },
110
113
 
114
+ // Rich error with context and recovery hint
115
+ failWithHint(error: ErrorWithHint | ErrorCode) {
116
+ this.stop()
117
+ const err = typeof error === 'string' ? getError(error) : error
118
+ console.error()
119
+ console.error(`${chalk.red('✗')} ${err.message}`)
120
+ if (err.file) {
121
+ console.error(chalk.dim(` File: ${err.file}`))
122
+ }
123
+ if (err.hint) {
124
+ console.error(chalk.yellow(` 💡 ${err.hint}`))
125
+ }
126
+ if (err.docs) {
127
+ console.error(chalk.dim(` Docs: ${err.docs}`))
128
+ }
129
+ console.error()
130
+ return this
131
+ },
132
+
111
133
  warn(msg: string) {
112
134
  this.stop()
113
135
  if (!quietMode) console.log(`${chalk.yellow('⚠')} ${truncate(msg, 65)}`)
@@ -151,4 +173,6 @@ const out: Output = {
151
173
  }
152
174
 
153
175
  export type { OutputMetrics }
176
+ export type { ErrorCode, ErrorWithHint } from './error-messages'
177
+ export { createError, ERRORS, getError } from './error-messages'
154
178
  export default out
@@ -1944,10 +1944,130 @@ var init_branding = __esm({
1944
1944
  }
1945
1945
  });
1946
1946
 
1947
+ // core/utils/error-messages.ts
1948
+ function getError(code, overrides) {
1949
+ const base = ERRORS[code];
1950
+ return { ...base, ...overrides };
1951
+ }
1952
+ function createError(message, hint, options) {
1953
+ return {
1954
+ message,
1955
+ hint,
1956
+ ...options
1957
+ };
1958
+ }
1959
+ var ERRORS;
1960
+ var init_error_messages = __esm({
1961
+ "core/utils/error-messages.ts"() {
1962
+ "use strict";
1963
+ ERRORS = {
1964
+ // Project errors
1965
+ NO_PROJECT: {
1966
+ message: "No prjct project found in this directory",
1967
+ hint: "Run 'prjct init' to set up a new project",
1968
+ file: ".prjct/prjct.config.json"
1969
+ },
1970
+ NO_PROJECT_ID: {
1971
+ message: "Project ID not found",
1972
+ hint: "Run 'prjct init' or check .prjct/prjct.config.json",
1973
+ file: ".prjct/prjct.config.json"
1974
+ },
1975
+ CONFIG_NOT_FOUND: {
1976
+ message: "Configuration file not found",
1977
+ hint: "Run 'prjct init' to create project configuration",
1978
+ file: ".prjct/prjct.config.json"
1979
+ },
1980
+ CONFIG_INVALID: {
1981
+ message: "Invalid configuration file",
1982
+ hint: "Check JSON syntax or delete .prjct/ and run init again",
1983
+ file: ".prjct/prjct.config.json"
1984
+ },
1985
+ // Git errors
1986
+ GIT_NOT_FOUND: {
1987
+ message: "Git repository not detected",
1988
+ hint: "Run 'git init' first, then 'prjct init'"
1989
+ },
1990
+ GIT_NO_COMMITS: {
1991
+ message: "No commits in repository",
1992
+ hint: "Make an initial commit before using prjct"
1993
+ },
1994
+ GIT_DIRTY: {
1995
+ message: "Working directory has uncommitted changes",
1996
+ hint: "Commit or stash changes, or use '--force' to override"
1997
+ },
1998
+ GIT_ON_MAIN: {
1999
+ message: "Cannot ship from main/master branch",
2000
+ hint: "Create a feature branch first: git checkout -b feature/your-feature"
2001
+ },
2002
+ GIT_OPERATION_FAILED: {
2003
+ message: "Git operation failed",
2004
+ hint: "Check git status and resolve any conflicts"
2005
+ },
2006
+ // Auth errors
2007
+ GH_NOT_AUTHENTICATED: {
2008
+ message: "GitHub CLI not authenticated",
2009
+ hint: "Run 'gh auth login' to authenticate",
2010
+ docs: "https://cli.github.com/manual/gh_auth_login"
2011
+ },
2012
+ LINEAR_NOT_CONFIGURED: {
2013
+ message: "Linear integration not configured",
2014
+ hint: "Run 'p. linear setup' to configure Linear"
2015
+ },
2016
+ LINEAR_API_ERROR: {
2017
+ message: "Linear API error",
2018
+ hint: "Check your API key or network connection"
2019
+ },
2020
+ // Task errors
2021
+ NO_ACTIVE_TASK: {
2022
+ message: "No active task",
2023
+ hint: `Start a task with 'p. task "description"'`
2024
+ },
2025
+ TASK_ALREADY_ACTIVE: {
2026
+ message: "A task is already in progress",
2027
+ hint: "Complete it with 'p. done' or pause with 'p. pause'"
2028
+ },
2029
+ // Sync errors
2030
+ SYNC_FAILED: {
2031
+ message: "Project sync failed",
2032
+ hint: "Check file permissions and try again"
2033
+ },
2034
+ // Ship errors
2035
+ NOTHING_TO_SHIP: {
2036
+ message: "Nothing to ship",
2037
+ hint: "Make some changes first, then run ship"
2038
+ },
2039
+ PR_CREATE_FAILED: {
2040
+ message: "Failed to create pull request",
2041
+ hint: "Check GitHub auth and remote configuration"
2042
+ },
2043
+ // Provider errors
2044
+ NO_AI_PROVIDER: {
2045
+ message: "No AI provider detected",
2046
+ hint: "Install Claude Code or Gemini CLI, then run 'prjct start'",
2047
+ docs: "https://prjct.app/docs"
2048
+ },
2049
+ PROVIDER_NOT_CONFIGURED: {
2050
+ message: "AI provider not configured for prjct",
2051
+ hint: "Run 'prjct start' to configure your provider"
2052
+ },
2053
+ // Generic
2054
+ UNKNOWN: {
2055
+ message: "An unexpected error occurred",
2056
+ hint: "Check the error details and try again"
2057
+ }
2058
+ };
2059
+ __name(getError, "getError");
2060
+ __name(createError, "createError");
2061
+ }
2062
+ });
2063
+
1947
2064
  // core/utils/output.ts
1948
2065
  var output_exports = {};
1949
2066
  __export(output_exports, {
2067
+ ERRORS: () => ERRORS,
2068
+ createError: () => createError,
1950
2069
  default: () => output_default,
2070
+ getError: () => getError,
1951
2071
  isQuietMode: () => isQuietMode,
1952
2072
  setQuietMode: () => setQuietMode
1953
2073
  });
@@ -1963,6 +2083,8 @@ var init_output = __esm({
1963
2083
  "core/utils/output.ts"() {
1964
2084
  "use strict";
1965
2085
  init_branding();
2086
+ init_error_messages();
2087
+ init_error_messages();
1966
2088
  _FRAMES = branding_default.spinner.frames;
1967
2089
  SPEED = branding_default.spinner.speed;
1968
2090
  interval = null;
@@ -2015,6 +2137,24 @@ var init_output = __esm({
2015
2137
  console.error(`${chalk2.red("\u2717")} ${truncate(msg, 65)}`);
2016
2138
  return this;
2017
2139
  },
2140
+ // Rich error with context and recovery hint
2141
+ failWithHint(error) {
2142
+ this.stop();
2143
+ const err = typeof error === "string" ? getError(error) : error;
2144
+ console.error();
2145
+ console.error(`${chalk2.red("\u2717")} ${err.message}`);
2146
+ if (err.file) {
2147
+ console.error(chalk2.dim(` File: ${err.file}`));
2148
+ }
2149
+ if (err.hint) {
2150
+ console.error(chalk2.yellow(` \u{1F4A1} ${err.hint}`));
2151
+ }
2152
+ if (err.docs) {
2153
+ console.error(chalk2.dim(` Docs: ${err.docs}`));
2154
+ }
2155
+ console.error();
2156
+ return this;
2157
+ },
2018
2158
  warn(msg) {
2019
2159
  this.stop();
2020
2160
  if (!quietMode) console.log(`${chalk2.yellow("\u26A0")} ${truncate(msg, 65)}`);
@@ -16738,7 +16878,7 @@ Generated: ${(/* @__PURE__ */ new Date()).toLocaleString()}
16738
16878
  }
16739
16879
  const projectId = await config_manager_default.getProjectId(projectPath);
16740
16880
  if (!projectId) {
16741
- output_default.fail("no project ID");
16881
+ output_default.failWithHint("NO_PROJECT_ID");
16742
16882
  return { success: false, error: "No project ID found" };
16743
16883
  }
16744
16884
  output_default.spin(`planning ${description}...`);
@@ -16795,7 +16935,7 @@ Generated: ${(/* @__PURE__ */ new Date()).toLocaleString()}
16795
16935
  }
16796
16936
  const projectId = await config_manager_default.getProjectId(projectPath);
16797
16937
  if (!projectId) {
16798
- output_default.fail("no project ID");
16938
+ output_default.failWithHint("NO_PROJECT_ID");
16799
16939
  return { success: false, error: "No project ID found" };
16800
16940
  }
16801
16941
  output_default.spin("tracking bug...");
@@ -16918,7 +17058,7 @@ ${"=".repeat(60)}`);
16918
17058
  }
16919
17059
  const projectId = await config_manager_default.getProjectId(projectPath);
16920
17060
  if (!projectId) {
16921
- output_default.fail("no project ID");
17061
+ output_default.failWithHint("NO_PROJECT_ID");
16922
17062
  return { success: false, error: "No project ID found" };
16923
17063
  }
16924
17064
  const wordCount = description.split(/\s+/).length;
@@ -16976,7 +17116,7 @@ Generated: ${(/* @__PURE__ */ new Date()).toLocaleString()}
16976
17116
  if (!initResult.success) return initResult;
16977
17117
  const projectId = await config_manager_default.getProjectId(projectPath);
16978
17118
  if (!projectId) {
16979
- output_default.fail("no project ID");
17119
+ output_default.failWithHint("NO_PROJECT_ID");
16980
17120
  return { success: false, error: "No project ID found" };
16981
17121
  }
16982
17122
  if (!featureName) {
@@ -21067,7 +21207,7 @@ var init_analytics = __esm({
21067
21207
  if (!initResult.success) return initResult;
21068
21208
  const projectId = await config_manager_default.getProjectId(projectPath);
21069
21209
  if (!projectId) {
21070
- output_default.fail("no project ID");
21210
+ output_default.failWithHint("NO_PROJECT_ID");
21071
21211
  return { success: false, error: "No project ID found" };
21072
21212
  }
21073
21213
  const projectName = path45.basename(projectPath);
@@ -21507,7 +21647,7 @@ async function cleanup(options = {}, projectPath = process.cwd()) {
21507
21647
  output_default.spin("cleaning up...");
21508
21648
  const projectId = await config_manager_default.getProjectId(projectPath);
21509
21649
  if (!projectId) {
21510
- output_default.fail("no project ID");
21650
+ output_default.failWithHint("NO_PROJECT_ID");
21511
21651
  return { success: false, error: "No project ID found" };
21512
21652
  }
21513
21653
  const cleaned = [];
@@ -21666,7 +21806,7 @@ async function recover(projectPath = process.cwd()) {
21666
21806
  try {
21667
21807
  const projectId = await config_manager_default.getProjectId(projectPath);
21668
21808
  if (!projectId) {
21669
- output_default.fail("no project ID");
21809
+ output_default.failWithHint("NO_PROJECT_ID");
21670
21810
  return { success: false, error: "No project ID found" };
21671
21811
  }
21672
21812
  output_default.spin("checking for abandoned sessions...");
@@ -21710,7 +21850,7 @@ async function undo(projectPath = process.cwd()) {
21710
21850
  output_default.spin("creating undo point...");
21711
21851
  const projectId = await config_manager_default.getProjectId(projectPath);
21712
21852
  if (!projectId) {
21713
- output_default.fail("no project ID");
21853
+ output_default.failWithHint("NO_PROJECT_ID");
21714
21854
  return { success: false, error: "No project ID found" };
21715
21855
  }
21716
21856
  const snapshotsPath = path49.join(path_manager_default.getGlobalProjectPath(projectId), "snapshots");
@@ -21755,7 +21895,7 @@ async function undo(projectPath = process.cwd()) {
21755
21895
  output_default.done("changes stashed (use /p:redo to restore)");
21756
21896
  return { success: true, snapshotId: stashMessage };
21757
21897
  } catch (gitError) {
21758
- output_default.fail("git operation failed");
21898
+ output_default.failWithHint("GIT_OPERATION_FAILED");
21759
21899
  return { success: false, error: gitError.message };
21760
21900
  }
21761
21901
  } catch (error) {
@@ -21768,7 +21908,7 @@ async function redo(projectPath = process.cwd()) {
21768
21908
  output_default.spin("restoring changes...");
21769
21909
  const projectId = await config_manager_default.getProjectId(projectPath);
21770
21910
  if (!projectId) {
21771
- output_default.fail("no project ID");
21911
+ output_default.failWithHint("NO_PROJECT_ID");
21772
21912
  return { success: false, error: "No project ID found" };
21773
21913
  }
21774
21914
  const snapshotsPath = path49.join(path_manager_default.getGlobalProjectPath(projectId), "snapshots");
@@ -21816,7 +21956,7 @@ async function redo(projectPath = process.cwd()) {
21816
21956
  output_default.done("changes restored");
21817
21957
  return { success: true };
21818
21958
  } catch (gitError) {
21819
- output_default.fail("git operation failed");
21959
+ output_default.failWithHint("GIT_OPERATION_FAILED");
21820
21960
  return { success: false, error: gitError.message };
21821
21961
  }
21822
21962
  } catch (error) {
@@ -21828,7 +21968,7 @@ async function history(projectPath = process.cwd()) {
21828
21968
  try {
21829
21969
  const projectId = await config_manager_default.getProjectId(projectPath);
21830
21970
  if (!projectId) {
21831
- output_default.fail("no project ID");
21971
+ output_default.failWithHint("NO_PROJECT_ID");
21832
21972
  return { success: false, error: "No project ID found" };
21833
21973
  }
21834
21974
  const snapshotsPath = path49.join(path_manager_default.getGlobalProjectPath(projectId), "snapshots");
@@ -22483,7 +22623,7 @@ ${result.stderr}`.trim();
22483
22623
  if (!initResult.success) return initResult;
22484
22624
  const projectId = await config_manager_default.getProjectId(projectPath);
22485
22625
  if (!projectId) {
22486
- output_default.fail("no project ID");
22626
+ output_default.failWithHint("NO_PROJECT_ID");
22487
22627
  return { success: false, error: "No project ID found" };
22488
22628
  }
22489
22629
  let featureName = feature;
@@ -23557,7 +23697,7 @@ var init_workflow = __esm({
23557
23697
  if (!initResult.success) return initResult;
23558
23698
  const projectId = await config_manager_default.getProjectId(projectPath);
23559
23699
  if (!projectId) {
23560
- output_default.fail("no project ID");
23700
+ output_default.failWithHint("NO_PROJECT_ID");
23561
23701
  return { success: false, error: "No project ID found" };
23562
23702
  }
23563
23703
  if (task) {
@@ -23649,7 +23789,7 @@ var init_workflow = __esm({
23649
23789
  if (!initResult.success) return initResult;
23650
23790
  const projectId = await config_manager_default.getProjectId(projectPath);
23651
23791
  if (!projectId) {
23652
- output_default.fail("no project ID");
23792
+ output_default.failWithHint("NO_PROJECT_ID");
23653
23793
  return { success: false, error: "No project ID found" };
23654
23794
  }
23655
23795
  const currentTask = await stateStorage.getCurrentTask(projectId);
@@ -23715,7 +23855,7 @@ var init_workflow = __esm({
23715
23855
  if (!initResult.success) return initResult;
23716
23856
  const projectId = await config_manager_default.getProjectId(projectPath);
23717
23857
  if (!projectId) {
23718
- output_default.fail("no project ID");
23858
+ output_default.failWithHint("NO_PROJECT_ID");
23719
23859
  return { success: false, error: "No project ID found" };
23720
23860
  }
23721
23861
  const tasks = await queueStorage.getActiveTasks(projectId);
@@ -23740,7 +23880,7 @@ var init_workflow = __esm({
23740
23880
  if (!initResult.success) return initResult;
23741
23881
  const projectId = await config_manager_default.getProjectId(projectPath);
23742
23882
  if (!projectId) {
23743
- output_default.fail("no project ID");
23883
+ output_default.failWithHint("NO_PROJECT_ID");
23744
23884
  return { success: false, error: "No project ID found" };
23745
23885
  }
23746
23886
  const currentTask = await stateStorage.getCurrentTask(projectId);
@@ -23773,7 +23913,7 @@ var init_workflow = __esm({
23773
23913
  if (!initResult.success) return initResult;
23774
23914
  const projectId = await config_manager_default.getProjectId(projectPath);
23775
23915
  if (!projectId) {
23776
- output_default.fail("no project ID");
23916
+ output_default.failWithHint("NO_PROJECT_ID");
23777
23917
  return { success: false, error: "No project ID found" };
23778
23918
  }
23779
23919
  const currentTask = await stateStorage.getCurrentTask(projectId);
@@ -23811,7 +23951,7 @@ var init_workflow = __esm({
23811
23951
  if (!initResult.success) return initResult;
23812
23952
  const projectId = await config_manager_default.getProjectId(projectPath);
23813
23953
  if (!projectId) {
23814
- output_default.fail("no project ID");
23954
+ output_default.failWithHint("NO_PROJECT_ID");
23815
23955
  return { success: false, error: "No project ID found" };
23816
23956
  }
23817
23957
  if (!input) {
@@ -24080,7 +24220,7 @@ var require_package = __commonJS({
24080
24220
  "package.json"(exports, module) {
24081
24221
  module.exports = {
24082
24222
  name: "prjct-cli",
24083
- version: "0.48.0",
24223
+ version: "0.49.0",
24084
24224
  description: "Context layer for AI agents. Project context for Claude Code, Gemini CLI, and more.",
24085
24225
  main: "core/index.ts",
24086
24226
  bin: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prjct-cli",
3
- "version": "0.48.0",
3
+ "version": "0.49.0",
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": {