prjct-cli 0.47.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.
@@ -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,7 +8,6 @@
8
8
  */
9
9
 
10
10
  import { CATEGORIES, COMMANDS } from '../commands/command-data'
11
- import type { CommandMeta } from '../types'
12
11
  import { VERSION } from './version'
13
12
 
14
13
  // ANSI colors
@@ -117,7 +117,9 @@ export class MarkdownBuilder {
117
117
  * Add multiple list items
118
118
  */
119
119
  list(items: string[], options?: { checked?: boolean }): this {
120
- items.forEach((item) => this.li(item, options))
120
+ for (const item of items) {
121
+ this.li(item, options)
122
+ }
121
123
  return this
122
124
  }
123
125
 
@@ -125,7 +127,9 @@ export class MarkdownBuilder {
125
127
  * Add multiple numbered list items
126
128
  */
127
129
  orderedList(items: string[]): this {
128
- items.forEach((item, i) => this.oli(item, i + 1))
130
+ for (let i = 0; i < items.length; i++) {
131
+ this.oli(items[i], i + 1)
132
+ }
129
133
  return this
130
134
  }
131
135
 
@@ -225,7 +229,9 @@ export class MarkdownBuilder {
225
229
  * Iterate and build for each item
226
230
  */
227
231
  each<T>(items: T[], builder: (md: MarkdownBuilder, item: T, index: number) => void): this {
228
- items.forEach((item, i) => builder(this, item, i))
232
+ for (let i = 0; i < items.length; i++) {
233
+ builder(this, items[i], i)
234
+ }
229
235
  return this
230
236
  }
231
237
 
@@ -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
@@ -128,7 +128,7 @@ export function mergePreservedSections(newContent: string, oldContent: string):
128
128
  merged += '\n\n'
129
129
  }
130
130
 
131
- return merged.trimEnd() + '\n'
131
+ return `${merged.trimEnd()}\n`
132
132
  }
133
133
 
134
134
  /**
@@ -3,12 +3,6 @@ import type { DetectedProjectCommands } from '../types'
3
3
  import * as fileHelper from './file-helper'
4
4
 
5
5
  type PackageManager = 'npm' | 'pnpm' | 'yarn' | 'bun'
6
- type DetectedStack = 'js' | 'python' | 'go' | 'rust' | 'dotnet' | 'java' | 'unknown'
7
-
8
- interface DetectedCommand {
9
- command: string
10
- tool: string
11
- }
12
6
 
13
7
  interface PackageJson {
14
8
  scripts?: Record<string, string>
@@ -72,7 +72,7 @@ function formatSubtaskLine(
72
72
  const domain = `${domainColor}${subtask.domain.padEnd(10)}${RESET}`
73
73
  const desc =
74
74
  subtask.description.length > 32
75
- ? subtask.description.slice(0, 29) + '...'
75
+ ? `${subtask.description.slice(0, 29)}...`
76
76
  : subtask.description.padEnd(32)
77
77
 
78
78
  let status: string