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.
@@ -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)}`);
@@ -5863,7 +6003,7 @@ function formatSubtaskLine(index, subtask, spinnerFrame = "\u25B6") {
5863
6003
  const num = `${DIM2}${String(index + 1).padStart(2)}${RESET2}`;
5864
6004
  const domainColor = getDomainColor(subtask.domain);
5865
6005
  const domain = `${domainColor}${subtask.domain.padEnd(10)}${RESET2}`;
5866
- const desc = subtask.description.length > 32 ? subtask.description.slice(0, 29) + "..." : subtask.description.padEnd(32);
6006
+ const desc = subtask.description.length > 32 ? `${subtask.description.slice(0, 29)}...` : subtask.description.padEnd(32);
5867
6007
  let status;
5868
6008
  switch (subtask.status) {
5869
6009
  case "completed":
@@ -8755,7 +8895,9 @@ var init_memory_system = __esm({
8755
8895
  const matchingIds = /* @__PURE__ */ new Set();
8756
8896
  for (const tag of parsedTags) {
8757
8897
  const ids = db.index[tag];
8758
- ids.forEach((id) => matchingIds.add(id));
8898
+ for (const id of ids) {
8899
+ matchingIds.add(id);
8900
+ }
8759
8901
  }
8760
8902
  return db.memories.filter((m) => matchingIds.has(m.id));
8761
8903
  }
@@ -9986,14 +10128,18 @@ var init_markdown_builder = __esm({
9986
10128
  * Add multiple list items
9987
10129
  */
9988
10130
  list(items, options) {
9989
- items.forEach((item) => this.li(item, options));
10131
+ for (const item of items) {
10132
+ this.li(item, options);
10133
+ }
9990
10134
  return this;
9991
10135
  }
9992
10136
  /**
9993
10137
  * Add multiple numbered list items
9994
10138
  */
9995
10139
  orderedList(items) {
9996
- items.forEach((item, i) => this.oli(item, i + 1));
10140
+ for (let i = 0; i < items.length; i++) {
10141
+ this.oli(items[i], i + 1);
10142
+ }
9997
10143
  return this;
9998
10144
  }
9999
10145
  /**
@@ -10079,7 +10225,9 @@ var init_markdown_builder = __esm({
10079
10225
  * Iterate and build for each item
10080
10226
  */
10081
10227
  each(items, builder) {
10082
- items.forEach((item, i) => builder(this, item, i));
10228
+ for (let i = 0; i < items.length; i++) {
10229
+ builder(this, items[i], i);
10230
+ }
10083
10231
  return this;
10084
10232
  }
10085
10233
  /**
@@ -12415,12 +12563,16 @@ Stack: ${stack}
12415
12563
  parts.push("\n## THINK FIRST (reasoning from analysis)\n");
12416
12564
  if (thinkBlock.conclusions && thinkBlock.conclusions.length > 0) {
12417
12565
  parts.push("Conclusions:\n");
12418
- thinkBlock.conclusions.forEach((c) => parts.push(` \u2192 ${c}
12419
- `));
12566
+ for (const c of thinkBlock.conclusions) {
12567
+ parts.push(` \u2192 ${c}
12568
+ `);
12569
+ }
12420
12570
  }
12421
12571
  parts.push("Plan:\n");
12422
- thinkBlock.plan.forEach((p, i) => parts.push(` ${i + 1}. ${p}
12423
- `));
12572
+ for (let i = 0; i < thinkBlock.plan.length; i++) {
12573
+ parts.push(` ${i + 1}. ${thinkBlock.plan[i]}
12574
+ `);
12575
+ }
12424
12576
  parts.push(`Confidence: ${Math.round((thinkBlock.confidence || 0.5) * 100)}%
12425
12577
  `);
12426
12578
  }
@@ -13378,10 +13530,100 @@ var init_update_checker = __esm({
13378
13530
  }
13379
13531
  });
13380
13532
 
13533
+ // core/utils/preserve-sections.ts
13534
+ function createPreserveStartRegex() {
13535
+ return /<!-- prjct:preserve(?::([\w-]+))? -->/g;
13536
+ }
13537
+ function extractPreservedSections(content) {
13538
+ const sections = [];
13539
+ const regex = createPreserveStartRegex();
13540
+ let match;
13541
+ let sectionIndex = 0;
13542
+ while ((match = regex.exec(content)) !== null) {
13543
+ const startIndex = match.index;
13544
+ const startTag = match[0];
13545
+ const sectionId = match[1] || `section-${sectionIndex++}`;
13546
+ const endTagStart = content.indexOf(PRESERVE_END_PATTERN, startIndex + startTag.length);
13547
+ if (endTagStart === -1) {
13548
+ continue;
13549
+ }
13550
+ const endIndex = endTagStart + PRESERVE_END_PATTERN.length;
13551
+ const fullContent = content.substring(startIndex, endIndex);
13552
+ sections.push({
13553
+ id: sectionId,
13554
+ content: fullContent,
13555
+ startIndex,
13556
+ endIndex
13557
+ });
13558
+ }
13559
+ return sections;
13560
+ }
13561
+ function mergePreservedSections(newContent, oldContent) {
13562
+ const preservedSections = extractPreservedSections(oldContent);
13563
+ if (preservedSections.length === 0) {
13564
+ return newContent;
13565
+ }
13566
+ let merged = newContent.trimEnd();
13567
+ merged += "\n\n---\n\n";
13568
+ merged += "## Your Customizations\n\n";
13569
+ merged += "_The sections below are preserved during sync. Edit freely._\n\n";
13570
+ for (const section of preservedSections) {
13571
+ merged += section.content;
13572
+ merged += "\n\n";
13573
+ }
13574
+ return `${merged.trimEnd()}
13575
+ `;
13576
+ }
13577
+ function validatePreserveBlocks(content) {
13578
+ const errors = [];
13579
+ const startMatches = content.match(/<!-- prjct:preserve(?::\w+)? -->/g) || [];
13580
+ const endMatches = content.match(/<!-- \/prjct:preserve -->/g) || [];
13581
+ if (startMatches.length !== endMatches.length) {
13582
+ errors.push(
13583
+ `Mismatched preserve markers: ${startMatches.length} opening, ${endMatches.length} closing`
13584
+ );
13585
+ }
13586
+ let depth = 0;
13587
+ let maxDepth = 0;
13588
+ const lines = content.split("\n");
13589
+ for (let i = 0; i < lines.length; i++) {
13590
+ const line = lines[i];
13591
+ if (/<!-- prjct:preserve(?::\w+)? -->/.test(line)) {
13592
+ depth++;
13593
+ maxDepth = Math.max(maxDepth, depth);
13594
+ }
13595
+ if (line.includes(PRESERVE_END_PATTERN)) {
13596
+ depth--;
13597
+ }
13598
+ if (depth > 1) {
13599
+ errors.push(`Nested preserve blocks detected at line ${i + 1} (not supported)`);
13600
+ }
13601
+ if (depth < 0) {
13602
+ errors.push(`Unexpected closing marker at line ${i + 1}`);
13603
+ }
13604
+ }
13605
+ return {
13606
+ valid: errors.length === 0,
13607
+ errors
13608
+ };
13609
+ }
13610
+ var PRESERVE_END_PATTERN;
13611
+ var init_preserve_sections = __esm({
13612
+ "core/utils/preserve-sections.ts"() {
13613
+ "use strict";
13614
+ PRESERVE_END_PATTERN = "<!-- /prjct:preserve -->";
13615
+ __name(createPreserveStartRegex, "createPreserveStartRegex");
13616
+ __name(extractPreservedSections, "extractPreservedSections");
13617
+ __name(mergePreservedSections, "mergePreservedSections");
13618
+ __name(validatePreserveBlocks, "validatePreserveBlocks");
13619
+ }
13620
+ });
13621
+
13381
13622
  // core/services/agent-generator.ts
13382
13623
  var init_agent_generator = __esm({
13383
13624
  "core/services/agent-generator.ts"() {
13384
13625
  "use strict";
13626
+ init_preserve_sections();
13385
13627
  }
13386
13628
  });
13387
13629
 
@@ -16636,7 +16878,7 @@ Generated: ${(/* @__PURE__ */ new Date()).toLocaleString()}
16636
16878
  }
16637
16879
  const projectId = await config_manager_default.getProjectId(projectPath);
16638
16880
  if (!projectId) {
16639
- output_default.fail("no project ID");
16881
+ output_default.failWithHint("NO_PROJECT_ID");
16640
16882
  return { success: false, error: "No project ID found" };
16641
16883
  }
16642
16884
  output_default.spin(`planning ${description}...`);
@@ -16693,7 +16935,7 @@ Generated: ${(/* @__PURE__ */ new Date()).toLocaleString()}
16693
16935
  }
16694
16936
  const projectId = await config_manager_default.getProjectId(projectPath);
16695
16937
  if (!projectId) {
16696
- output_default.fail("no project ID");
16938
+ output_default.failWithHint("NO_PROJECT_ID");
16697
16939
  return { success: false, error: "No project ID found" };
16698
16940
  }
16699
16941
  output_default.spin("tracking bug...");
@@ -16816,7 +17058,7 @@ ${"=".repeat(60)}`);
16816
17058
  }
16817
17059
  const projectId = await config_manager_default.getProjectId(projectPath);
16818
17060
  if (!projectId) {
16819
- output_default.fail("no project ID");
17061
+ output_default.failWithHint("NO_PROJECT_ID");
16820
17062
  return { success: false, error: "No project ID found" };
16821
17063
  }
16822
17064
  const wordCount = description.split(/\s+/).length;
@@ -16874,7 +17116,7 @@ Generated: ${(/* @__PURE__ */ new Date()).toLocaleString()}
16874
17116
  if (!initResult.success) return initResult;
16875
17117
  const projectId = await config_manager_default.getProjectId(projectPath);
16876
17118
  if (!projectId) {
16877
- output_default.fail("no project ID");
17119
+ output_default.failWithHint("NO_PROJECT_ID");
16878
17120
  return { success: false, error: "No project ID found" };
16879
17121
  }
16880
17122
  if (!featureName) {
@@ -17312,60 +17554,6 @@ var init_formatters = __esm({
17312
17554
  }
17313
17555
  });
17314
17556
 
17315
- // core/utils/preserve-sections.ts
17316
- function createPreserveStartRegex() {
17317
- return /<!-- prjct:preserve(?::([\w-]+))? -->/g;
17318
- }
17319
- function extractPreservedSections(content) {
17320
- const sections = [];
17321
- const regex = createPreserveStartRegex();
17322
- let match;
17323
- let sectionIndex = 0;
17324
- while ((match = regex.exec(content)) !== null) {
17325
- const startIndex = match.index;
17326
- const startTag = match[0];
17327
- const sectionId = match[1] || `section-${sectionIndex++}`;
17328
- const endTagStart = content.indexOf(PRESERVE_END_PATTERN, startIndex + startTag.length);
17329
- if (endTagStart === -1) {
17330
- continue;
17331
- }
17332
- const endIndex = endTagStart + PRESERVE_END_PATTERN.length;
17333
- const fullContent = content.substring(startIndex, endIndex);
17334
- sections.push({
17335
- id: sectionId,
17336
- content: fullContent,
17337
- startIndex,
17338
- endIndex
17339
- });
17340
- }
17341
- return sections;
17342
- }
17343
- function mergePreservedSections(newContent, oldContent) {
17344
- const preservedSections = extractPreservedSections(oldContent);
17345
- if (preservedSections.length === 0) {
17346
- return newContent;
17347
- }
17348
- let merged = newContent.trimEnd();
17349
- merged += "\n\n---\n\n";
17350
- merged += "## Your Customizations\n\n";
17351
- merged += "_The sections below are preserved during sync. Edit freely._\n\n";
17352
- for (const section of preservedSections) {
17353
- merged += section.content;
17354
- merged += "\n\n";
17355
- }
17356
- return merged.trimEnd() + "\n";
17357
- }
17358
- var PRESERVE_END_PATTERN;
17359
- var init_preserve_sections = __esm({
17360
- "core/utils/preserve-sections.ts"() {
17361
- "use strict";
17362
- PRESERVE_END_PATTERN = "<!-- /prjct:preserve -->";
17363
- __name(createPreserveStartRegex, "createPreserveStartRegex");
17364
- __name(extractPreservedSections, "extractPreservedSections");
17365
- __name(mergePreservedSections, "mergePreservedSections");
17366
- }
17367
- });
17368
-
17369
17557
  // core/ai-tools/registry.ts
17370
17558
  import { execSync as execSync4 } from "node:child_process";
17371
17559
  import fs34 from "node:fs";
@@ -17515,6 +17703,13 @@ async function generateForTool(context2, config, globalPath, repoPath) {
17515
17703
  await fs35.mkdir(path38.dirname(outputPath), { recursive: true });
17516
17704
  try {
17517
17705
  const existingContent = await fs35.readFile(outputPath, "utf-8");
17706
+ const validation = validatePreserveBlocks(existingContent);
17707
+ if (!validation.valid) {
17708
+ console.warn(`\u26A0\uFE0F ${config.outputFile} has invalid preserve blocks:`);
17709
+ for (const error of validation.errors) {
17710
+ console.warn(` ${error}`);
17711
+ }
17712
+ }
17518
17713
  content = mergePreservedSections(content, existingContent);
17519
17714
  } catch {
17520
17715
  }
@@ -17573,6 +17768,27 @@ var init_context_generator = __esm({
17573
17768
  constructor(config) {
17574
17769
  this.config = config;
17575
17770
  }
17771
+ /**
17772
+ * Write file with preserved sections from existing content
17773
+ * This ensures user customizations survive regeneration
17774
+ */
17775
+ async writeWithPreservation(filePath, content) {
17776
+ let finalContent = content;
17777
+ try {
17778
+ const existingContent = await fs36.readFile(filePath, "utf-8");
17779
+ const validation = validatePreserveBlocks(existingContent);
17780
+ if (!validation.valid) {
17781
+ const filename = path39.basename(filePath);
17782
+ console.warn(`\u26A0\uFE0F ${filename} has invalid preserve blocks:`);
17783
+ for (const error of validation.errors) {
17784
+ console.warn(` ${error}`);
17785
+ }
17786
+ }
17787
+ finalContent = mergePreservedSections(content, existingContent);
17788
+ } catch {
17789
+ }
17790
+ await fs36.writeFile(filePath, finalContent, "utf-8");
17791
+ }
17576
17792
  /**
17577
17793
  * Generate all context files in parallel
17578
17794
  */
@@ -17672,13 +17888,7 @@ Load from \`~/.prjct-cli/projects/${this.config.projectId}/agents/\`:
17672
17888
  **Domain**: ${domainAgents.join(", ") || "none"}
17673
17889
  `;
17674
17890
  const claudePath = path39.join(contextPath, "CLAUDE.md");
17675
- let finalContent = content;
17676
- try {
17677
- const existingContent = await fs36.readFile(claudePath, "utf-8");
17678
- finalContent = mergePreservedSections(content, existingContent);
17679
- } catch {
17680
- }
17681
- await fs36.writeFile(claudePath, finalContent, "utf-8");
17891
+ await this.writeWithPreservation(claudePath, content);
17682
17892
  }
17683
17893
  /**
17684
17894
  * Generate now.md - current task status
@@ -17703,7 +17913,7 @@ _No active task_
17703
17913
 
17704
17914
  Use \`p. task "description"\` to start working.
17705
17915
  `;
17706
- await fs36.writeFile(path39.join(contextPath, "now.md"), content, "utf-8");
17916
+ await this.writeWithPreservation(path39.join(contextPath, "now.md"), content);
17707
17917
  }
17708
17918
  /**
17709
17919
  * Generate next.md - task queue
@@ -17719,7 +17929,7 @@ Use \`p. task "description"\` to start working.
17719
17929
 
17720
17930
  ${queue.tasks.length > 0 ? queue.tasks.map((t, i) => `${i + 1}. ${t.description}${t.priority ? ` [${t.priority}]` : ""}`).join("\n") : "_Empty queue_"}
17721
17931
  `;
17722
- await fs36.writeFile(path39.join(contextPath, "next.md"), content, "utf-8");
17932
+ await this.writeWithPreservation(path39.join(contextPath, "next.md"), content);
17723
17933
  }
17724
17934
  /**
17725
17935
  * Generate ideas.md - captured ideas
@@ -17735,7 +17945,7 @@ ${queue.tasks.length > 0 ? queue.tasks.map((t, i) => `${i + 1}. ${t.description}
17735
17945
 
17736
17946
  ${ideas.ideas.length > 0 ? ideas.ideas.map((i) => `- ${i.text}${i.priority ? ` [${i.priority}]` : ""}`).join("\n") : "_No ideas captured yet_"}
17737
17947
  `;
17738
- await fs36.writeFile(path39.join(contextPath, "ideas.md"), content, "utf-8");
17948
+ await this.writeWithPreservation(path39.join(contextPath, "ideas.md"), content);
17739
17949
  }
17740
17950
  /**
17741
17951
  * Generate shipped.md - completed features
@@ -17755,7 +17965,7 @@ ${shipped.shipped.length > 0 ? shipped.shipped.slice(-10).map((s) => `- **${s.na
17755
17965
 
17756
17966
  **Total shipped:** ${shipped.shipped.length}
17757
17967
  `;
17758
- await fs36.writeFile(path39.join(contextPath, "shipped.md"), content, "utf-8");
17968
+ await this.writeWithPreservation(path39.join(contextPath, "shipped.md"), content);
17759
17969
  }
17760
17970
  };
17761
17971
  }
@@ -20997,7 +21207,7 @@ var init_analytics = __esm({
20997
21207
  if (!initResult.success) return initResult;
20998
21208
  const projectId = await config_manager_default.getProjectId(projectPath);
20999
21209
  if (!projectId) {
21000
- output_default.fail("no project ID");
21210
+ output_default.failWithHint("NO_PROJECT_ID");
21001
21211
  return { success: false, error: "No project ID found" };
21002
21212
  }
21003
21213
  const projectName = path45.basename(projectPath);
@@ -21173,7 +21383,9 @@ ${"\u2550".repeat(50)}`);
21173
21383
  }
21174
21384
  if (command.features) {
21175
21385
  console.log("\nFeatures:");
21176
- command.features.forEach((f) => console.log(` \u2022 ${f}`));
21386
+ for (const f of command.features) {
21387
+ console.log(` \u2022 ${f}`);
21388
+ }
21177
21389
  }
21178
21390
  console.log(`
21179
21391
  ${"\u2550".repeat(50)}
@@ -21435,7 +21647,7 @@ async function cleanup(options = {}, projectPath = process.cwd()) {
21435
21647
  output_default.spin("cleaning up...");
21436
21648
  const projectId = await config_manager_default.getProjectId(projectPath);
21437
21649
  if (!projectId) {
21438
- output_default.fail("no project ID");
21650
+ output_default.failWithHint("NO_PROJECT_ID");
21439
21651
  return { success: false, error: "No project ID found" };
21440
21652
  }
21441
21653
  const cleaned = [];
@@ -21594,7 +21806,7 @@ async function recover(projectPath = process.cwd()) {
21594
21806
  try {
21595
21807
  const projectId = await config_manager_default.getProjectId(projectPath);
21596
21808
  if (!projectId) {
21597
- output_default.fail("no project ID");
21809
+ output_default.failWithHint("NO_PROJECT_ID");
21598
21810
  return { success: false, error: "No project ID found" };
21599
21811
  }
21600
21812
  output_default.spin("checking for abandoned sessions...");
@@ -21638,7 +21850,7 @@ async function undo(projectPath = process.cwd()) {
21638
21850
  output_default.spin("creating undo point...");
21639
21851
  const projectId = await config_manager_default.getProjectId(projectPath);
21640
21852
  if (!projectId) {
21641
- output_default.fail("no project ID");
21853
+ output_default.failWithHint("NO_PROJECT_ID");
21642
21854
  return { success: false, error: "No project ID found" };
21643
21855
  }
21644
21856
  const snapshotsPath = path49.join(path_manager_default.getGlobalProjectPath(projectId), "snapshots");
@@ -21683,7 +21895,7 @@ async function undo(projectPath = process.cwd()) {
21683
21895
  output_default.done("changes stashed (use /p:redo to restore)");
21684
21896
  return { success: true, snapshotId: stashMessage };
21685
21897
  } catch (gitError) {
21686
- output_default.fail("git operation failed");
21898
+ output_default.failWithHint("GIT_OPERATION_FAILED");
21687
21899
  return { success: false, error: gitError.message };
21688
21900
  }
21689
21901
  } catch (error) {
@@ -21696,7 +21908,7 @@ async function redo(projectPath = process.cwd()) {
21696
21908
  output_default.spin("restoring changes...");
21697
21909
  const projectId = await config_manager_default.getProjectId(projectPath);
21698
21910
  if (!projectId) {
21699
- output_default.fail("no project ID");
21911
+ output_default.failWithHint("NO_PROJECT_ID");
21700
21912
  return { success: false, error: "No project ID found" };
21701
21913
  }
21702
21914
  const snapshotsPath = path49.join(path_manager_default.getGlobalProjectPath(projectId), "snapshots");
@@ -21744,7 +21956,7 @@ async function redo(projectPath = process.cwd()) {
21744
21956
  output_default.done("changes restored");
21745
21957
  return { success: true };
21746
21958
  } catch (gitError) {
21747
- output_default.fail("git operation failed");
21959
+ output_default.failWithHint("GIT_OPERATION_FAILED");
21748
21960
  return { success: false, error: gitError.message };
21749
21961
  }
21750
21962
  } catch (error) {
@@ -21756,7 +21968,7 @@ async function history(projectPath = process.cwd()) {
21756
21968
  try {
21757
21969
  const projectId = await config_manager_default.getProjectId(projectPath);
21758
21970
  if (!projectId) {
21759
- output_default.fail("no project ID");
21971
+ output_default.failWithHint("NO_PROJECT_ID");
21760
21972
  return { success: false, error: "No project ID found" };
21761
21973
  }
21762
21974
  const snapshotsPath = path49.join(path_manager_default.getGlobalProjectPath(projectId), "snapshots");
@@ -22411,7 +22623,7 @@ ${result.stderr}`.trim();
22411
22623
  if (!initResult.success) return initResult;
22412
22624
  const projectId = await config_manager_default.getProjectId(projectPath);
22413
22625
  if (!projectId) {
22414
- output_default.fail("no project ID");
22626
+ output_default.failWithHint("NO_PROJECT_ID");
22415
22627
  return { success: false, error: "No project ID found" };
22416
22628
  }
22417
22629
  let featureName = feature;
@@ -23485,7 +23697,7 @@ var init_workflow = __esm({
23485
23697
  if (!initResult.success) return initResult;
23486
23698
  const projectId = await config_manager_default.getProjectId(projectPath);
23487
23699
  if (!projectId) {
23488
- output_default.fail("no project ID");
23700
+ output_default.failWithHint("NO_PROJECT_ID");
23489
23701
  return { success: false, error: "No project ID found" };
23490
23702
  }
23491
23703
  if (task) {
@@ -23577,7 +23789,7 @@ var init_workflow = __esm({
23577
23789
  if (!initResult.success) return initResult;
23578
23790
  const projectId = await config_manager_default.getProjectId(projectPath);
23579
23791
  if (!projectId) {
23580
- output_default.fail("no project ID");
23792
+ output_default.failWithHint("NO_PROJECT_ID");
23581
23793
  return { success: false, error: "No project ID found" };
23582
23794
  }
23583
23795
  const currentTask = await stateStorage.getCurrentTask(projectId);
@@ -23643,7 +23855,7 @@ var init_workflow = __esm({
23643
23855
  if (!initResult.success) return initResult;
23644
23856
  const projectId = await config_manager_default.getProjectId(projectPath);
23645
23857
  if (!projectId) {
23646
- output_default.fail("no project ID");
23858
+ output_default.failWithHint("NO_PROJECT_ID");
23647
23859
  return { success: false, error: "No project ID found" };
23648
23860
  }
23649
23861
  const tasks = await queueStorage.getActiveTasks(projectId);
@@ -23668,7 +23880,7 @@ var init_workflow = __esm({
23668
23880
  if (!initResult.success) return initResult;
23669
23881
  const projectId = await config_manager_default.getProjectId(projectPath);
23670
23882
  if (!projectId) {
23671
- output_default.fail("no project ID");
23883
+ output_default.failWithHint("NO_PROJECT_ID");
23672
23884
  return { success: false, error: "No project ID found" };
23673
23885
  }
23674
23886
  const currentTask = await stateStorage.getCurrentTask(projectId);
@@ -23701,7 +23913,7 @@ var init_workflow = __esm({
23701
23913
  if (!initResult.success) return initResult;
23702
23914
  const projectId = await config_manager_default.getProjectId(projectPath);
23703
23915
  if (!projectId) {
23704
- output_default.fail("no project ID");
23916
+ output_default.failWithHint("NO_PROJECT_ID");
23705
23917
  return { success: false, error: "No project ID found" };
23706
23918
  }
23707
23919
  const currentTask = await stateStorage.getCurrentTask(projectId);
@@ -23739,7 +23951,7 @@ var init_workflow = __esm({
23739
23951
  if (!initResult.success) return initResult;
23740
23952
  const projectId = await config_manager_default.getProjectId(projectPath);
23741
23953
  if (!projectId) {
23742
- output_default.fail("no project ID");
23954
+ output_default.failWithHint("NO_PROJECT_ID");
23743
23955
  return { success: false, error: "No project ID found" };
23744
23956
  }
23745
23957
  if (!input) {
@@ -23903,7 +24115,7 @@ var init_commands = __esm({
23903
24115
  return this.setupCmds.installStatusLine();
23904
24116
  }
23905
24117
  showAsciiArt() {
23906
- return this.setupCmds.showAsciiArt();
24118
+ this.setupCmds.showAsciiArt();
23907
24119
  }
23908
24120
  // ========== Delegated Base Methods ==========
23909
24121
  async initializeAgent() {
@@ -24008,7 +24220,7 @@ var require_package = __commonJS({
24008
24220
  "package.json"(exports, module) {
24009
24221
  module.exports = {
24010
24222
  name: "prjct-cli",
24011
- version: "0.47.0",
24223
+ version: "0.49.0",
24012
24224
  description: "Context layer for AI agents. Project context for Claude Code, Gemini CLI, and more.",
24013
24225
  main: "core/index.ts",
24014
24226
  bin: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prjct-cli",
3
- "version": "0.47.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": {