prjct-cli 0.47.0 → 0.48.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.
@@ -5863,7 +5863,7 @@ function formatSubtaskLine(index, subtask, spinnerFrame = "\u25B6") {
5863
5863
  const num = `${DIM2}${String(index + 1).padStart(2)}${RESET2}`;
5864
5864
  const domainColor = getDomainColor(subtask.domain);
5865
5865
  const domain = `${domainColor}${subtask.domain.padEnd(10)}${RESET2}`;
5866
- const desc = subtask.description.length > 32 ? subtask.description.slice(0, 29) + "..." : subtask.description.padEnd(32);
5866
+ const desc = subtask.description.length > 32 ? `${subtask.description.slice(0, 29)}...` : subtask.description.padEnd(32);
5867
5867
  let status;
5868
5868
  switch (subtask.status) {
5869
5869
  case "completed":
@@ -8755,7 +8755,9 @@ var init_memory_system = __esm({
8755
8755
  const matchingIds = /* @__PURE__ */ new Set();
8756
8756
  for (const tag of parsedTags) {
8757
8757
  const ids = db.index[tag];
8758
- ids.forEach((id) => matchingIds.add(id));
8758
+ for (const id of ids) {
8759
+ matchingIds.add(id);
8760
+ }
8759
8761
  }
8760
8762
  return db.memories.filter((m) => matchingIds.has(m.id));
8761
8763
  }
@@ -9986,14 +9988,18 @@ var init_markdown_builder = __esm({
9986
9988
  * Add multiple list items
9987
9989
  */
9988
9990
  list(items, options) {
9989
- items.forEach((item) => this.li(item, options));
9991
+ for (const item of items) {
9992
+ this.li(item, options);
9993
+ }
9990
9994
  return this;
9991
9995
  }
9992
9996
  /**
9993
9997
  * Add multiple numbered list items
9994
9998
  */
9995
9999
  orderedList(items) {
9996
- items.forEach((item, i) => this.oli(item, i + 1));
10000
+ for (let i = 0; i < items.length; i++) {
10001
+ this.oli(items[i], i + 1);
10002
+ }
9997
10003
  return this;
9998
10004
  }
9999
10005
  /**
@@ -10079,7 +10085,9 @@ var init_markdown_builder = __esm({
10079
10085
  * Iterate and build for each item
10080
10086
  */
10081
10087
  each(items, builder) {
10082
- items.forEach((item, i) => builder(this, item, i));
10088
+ for (let i = 0; i < items.length; i++) {
10089
+ builder(this, items[i], i);
10090
+ }
10083
10091
  return this;
10084
10092
  }
10085
10093
  /**
@@ -12415,12 +12423,16 @@ Stack: ${stack}
12415
12423
  parts.push("\n## THINK FIRST (reasoning from analysis)\n");
12416
12424
  if (thinkBlock.conclusions && thinkBlock.conclusions.length > 0) {
12417
12425
  parts.push("Conclusions:\n");
12418
- thinkBlock.conclusions.forEach((c) => parts.push(` \u2192 ${c}
12419
- `));
12426
+ for (const c of thinkBlock.conclusions) {
12427
+ parts.push(` \u2192 ${c}
12428
+ `);
12429
+ }
12420
12430
  }
12421
12431
  parts.push("Plan:\n");
12422
- thinkBlock.plan.forEach((p, i) => parts.push(` ${i + 1}. ${p}
12423
- `));
12432
+ for (let i = 0; i < thinkBlock.plan.length; i++) {
12433
+ parts.push(` ${i + 1}. ${thinkBlock.plan[i]}
12434
+ `);
12435
+ }
12424
12436
  parts.push(`Confidence: ${Math.round((thinkBlock.confidence || 0.5) * 100)}%
12425
12437
  `);
12426
12438
  }
@@ -13378,10 +13390,100 @@ var init_update_checker = __esm({
13378
13390
  }
13379
13391
  });
13380
13392
 
13393
+ // core/utils/preserve-sections.ts
13394
+ function createPreserveStartRegex() {
13395
+ return /<!-- prjct:preserve(?::([\w-]+))? -->/g;
13396
+ }
13397
+ function extractPreservedSections(content) {
13398
+ const sections = [];
13399
+ const regex = createPreserveStartRegex();
13400
+ let match;
13401
+ let sectionIndex = 0;
13402
+ while ((match = regex.exec(content)) !== null) {
13403
+ const startIndex = match.index;
13404
+ const startTag = match[0];
13405
+ const sectionId = match[1] || `section-${sectionIndex++}`;
13406
+ const endTagStart = content.indexOf(PRESERVE_END_PATTERN, startIndex + startTag.length);
13407
+ if (endTagStart === -1) {
13408
+ continue;
13409
+ }
13410
+ const endIndex = endTagStart + PRESERVE_END_PATTERN.length;
13411
+ const fullContent = content.substring(startIndex, endIndex);
13412
+ sections.push({
13413
+ id: sectionId,
13414
+ content: fullContent,
13415
+ startIndex,
13416
+ endIndex
13417
+ });
13418
+ }
13419
+ return sections;
13420
+ }
13421
+ function mergePreservedSections(newContent, oldContent) {
13422
+ const preservedSections = extractPreservedSections(oldContent);
13423
+ if (preservedSections.length === 0) {
13424
+ return newContent;
13425
+ }
13426
+ let merged = newContent.trimEnd();
13427
+ merged += "\n\n---\n\n";
13428
+ merged += "## Your Customizations\n\n";
13429
+ merged += "_The sections below are preserved during sync. Edit freely._\n\n";
13430
+ for (const section of preservedSections) {
13431
+ merged += section.content;
13432
+ merged += "\n\n";
13433
+ }
13434
+ return `${merged.trimEnd()}
13435
+ `;
13436
+ }
13437
+ function validatePreserveBlocks(content) {
13438
+ const errors = [];
13439
+ const startMatches = content.match(/<!-- prjct:preserve(?::\w+)? -->/g) || [];
13440
+ const endMatches = content.match(/<!-- \/prjct:preserve -->/g) || [];
13441
+ if (startMatches.length !== endMatches.length) {
13442
+ errors.push(
13443
+ `Mismatched preserve markers: ${startMatches.length} opening, ${endMatches.length} closing`
13444
+ );
13445
+ }
13446
+ let depth = 0;
13447
+ let maxDepth = 0;
13448
+ const lines = content.split("\n");
13449
+ for (let i = 0; i < lines.length; i++) {
13450
+ const line = lines[i];
13451
+ if (/<!-- prjct:preserve(?::\w+)? -->/.test(line)) {
13452
+ depth++;
13453
+ maxDepth = Math.max(maxDepth, depth);
13454
+ }
13455
+ if (line.includes(PRESERVE_END_PATTERN)) {
13456
+ depth--;
13457
+ }
13458
+ if (depth > 1) {
13459
+ errors.push(`Nested preserve blocks detected at line ${i + 1} (not supported)`);
13460
+ }
13461
+ if (depth < 0) {
13462
+ errors.push(`Unexpected closing marker at line ${i + 1}`);
13463
+ }
13464
+ }
13465
+ return {
13466
+ valid: errors.length === 0,
13467
+ errors
13468
+ };
13469
+ }
13470
+ var PRESERVE_END_PATTERN;
13471
+ var init_preserve_sections = __esm({
13472
+ "core/utils/preserve-sections.ts"() {
13473
+ "use strict";
13474
+ PRESERVE_END_PATTERN = "<!-- /prjct:preserve -->";
13475
+ __name(createPreserveStartRegex, "createPreserveStartRegex");
13476
+ __name(extractPreservedSections, "extractPreservedSections");
13477
+ __name(mergePreservedSections, "mergePreservedSections");
13478
+ __name(validatePreserveBlocks, "validatePreserveBlocks");
13479
+ }
13480
+ });
13481
+
13381
13482
  // core/services/agent-generator.ts
13382
13483
  var init_agent_generator = __esm({
13383
13484
  "core/services/agent-generator.ts"() {
13384
13485
  "use strict";
13486
+ init_preserve_sections();
13385
13487
  }
13386
13488
  });
13387
13489
 
@@ -17312,60 +17414,6 @@ var init_formatters = __esm({
17312
17414
  }
17313
17415
  });
17314
17416
 
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
17417
  // core/ai-tools/registry.ts
17370
17418
  import { execSync as execSync4 } from "node:child_process";
17371
17419
  import fs34 from "node:fs";
@@ -17515,6 +17563,13 @@ async function generateForTool(context2, config, globalPath, repoPath) {
17515
17563
  await fs35.mkdir(path38.dirname(outputPath), { recursive: true });
17516
17564
  try {
17517
17565
  const existingContent = await fs35.readFile(outputPath, "utf-8");
17566
+ const validation = validatePreserveBlocks(existingContent);
17567
+ if (!validation.valid) {
17568
+ console.warn(`\u26A0\uFE0F ${config.outputFile} has invalid preserve blocks:`);
17569
+ for (const error of validation.errors) {
17570
+ console.warn(` ${error}`);
17571
+ }
17572
+ }
17518
17573
  content = mergePreservedSections(content, existingContent);
17519
17574
  } catch {
17520
17575
  }
@@ -17573,6 +17628,27 @@ var init_context_generator = __esm({
17573
17628
  constructor(config) {
17574
17629
  this.config = config;
17575
17630
  }
17631
+ /**
17632
+ * Write file with preserved sections from existing content
17633
+ * This ensures user customizations survive regeneration
17634
+ */
17635
+ async writeWithPreservation(filePath, content) {
17636
+ let finalContent = content;
17637
+ try {
17638
+ const existingContent = await fs36.readFile(filePath, "utf-8");
17639
+ const validation = validatePreserveBlocks(existingContent);
17640
+ if (!validation.valid) {
17641
+ const filename = path39.basename(filePath);
17642
+ console.warn(`\u26A0\uFE0F ${filename} has invalid preserve blocks:`);
17643
+ for (const error of validation.errors) {
17644
+ console.warn(` ${error}`);
17645
+ }
17646
+ }
17647
+ finalContent = mergePreservedSections(content, existingContent);
17648
+ } catch {
17649
+ }
17650
+ await fs36.writeFile(filePath, finalContent, "utf-8");
17651
+ }
17576
17652
  /**
17577
17653
  * Generate all context files in parallel
17578
17654
  */
@@ -17672,13 +17748,7 @@ Load from \`~/.prjct-cli/projects/${this.config.projectId}/agents/\`:
17672
17748
  **Domain**: ${domainAgents.join(", ") || "none"}
17673
17749
  `;
17674
17750
  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");
17751
+ await this.writeWithPreservation(claudePath, content);
17682
17752
  }
17683
17753
  /**
17684
17754
  * Generate now.md - current task status
@@ -17703,7 +17773,7 @@ _No active task_
17703
17773
 
17704
17774
  Use \`p. task "description"\` to start working.
17705
17775
  `;
17706
- await fs36.writeFile(path39.join(contextPath, "now.md"), content, "utf-8");
17776
+ await this.writeWithPreservation(path39.join(contextPath, "now.md"), content);
17707
17777
  }
17708
17778
  /**
17709
17779
  * Generate next.md - task queue
@@ -17719,7 +17789,7 @@ Use \`p. task "description"\` to start working.
17719
17789
 
17720
17790
  ${queue.tasks.length > 0 ? queue.tasks.map((t, i) => `${i + 1}. ${t.description}${t.priority ? ` [${t.priority}]` : ""}`).join("\n") : "_Empty queue_"}
17721
17791
  `;
17722
- await fs36.writeFile(path39.join(contextPath, "next.md"), content, "utf-8");
17792
+ await this.writeWithPreservation(path39.join(contextPath, "next.md"), content);
17723
17793
  }
17724
17794
  /**
17725
17795
  * Generate ideas.md - captured ideas
@@ -17735,7 +17805,7 @@ ${queue.tasks.length > 0 ? queue.tasks.map((t, i) => `${i + 1}. ${t.description}
17735
17805
 
17736
17806
  ${ideas.ideas.length > 0 ? ideas.ideas.map((i) => `- ${i.text}${i.priority ? ` [${i.priority}]` : ""}`).join("\n") : "_No ideas captured yet_"}
17737
17807
  `;
17738
- await fs36.writeFile(path39.join(contextPath, "ideas.md"), content, "utf-8");
17808
+ await this.writeWithPreservation(path39.join(contextPath, "ideas.md"), content);
17739
17809
  }
17740
17810
  /**
17741
17811
  * Generate shipped.md - completed features
@@ -17755,7 +17825,7 @@ ${shipped.shipped.length > 0 ? shipped.shipped.slice(-10).map((s) => `- **${s.na
17755
17825
 
17756
17826
  **Total shipped:** ${shipped.shipped.length}
17757
17827
  `;
17758
- await fs36.writeFile(path39.join(contextPath, "shipped.md"), content, "utf-8");
17828
+ await this.writeWithPreservation(path39.join(contextPath, "shipped.md"), content);
17759
17829
  }
17760
17830
  };
17761
17831
  }
@@ -21173,7 +21243,9 @@ ${"\u2550".repeat(50)}`);
21173
21243
  }
21174
21244
  if (command.features) {
21175
21245
  console.log("\nFeatures:");
21176
- command.features.forEach((f) => console.log(` \u2022 ${f}`));
21246
+ for (const f of command.features) {
21247
+ console.log(` \u2022 ${f}`);
21248
+ }
21177
21249
  }
21178
21250
  console.log(`
21179
21251
  ${"\u2550".repeat(50)}
@@ -23903,7 +23975,7 @@ var init_commands = __esm({
23903
23975
  return this.setupCmds.installStatusLine();
23904
23976
  }
23905
23977
  showAsciiArt() {
23906
- return this.setupCmds.showAsciiArt();
23978
+ this.setupCmds.showAsciiArt();
23907
23979
  }
23908
23980
  // ========== Delegated Base Methods ==========
23909
23981
  async initializeAgent() {
@@ -24008,7 +24080,7 @@ var require_package = __commonJS({
24008
24080
  "package.json"(exports, module) {
24009
24081
  module.exports = {
24010
24082
  name: "prjct-cli",
24011
- version: "0.47.0",
24083
+ version: "0.48.0",
24012
24084
  description: "Context layer for AI agents. Project context for Claude Code, Gemini CLI, and more.",
24013
24085
  main: "core/index.ts",
24014
24086
  bin: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prjct-cli",
3
- "version": "0.47.0",
3
+ "version": "0.48.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": {
@@ -147,6 +147,33 @@ fs.writeFileSync(path, JSON.stringify(data, null, 2))
147
147
 
148
148
  **Full specification**: Install prjct-cli and see `{npm root -g}/prjct-cli/templates/global/STORAGE-SPEC.md`
149
149
 
150
+ ### 6. Preserve Markers (User Customizations)
151
+
152
+ User customizations in context files and agents survive regeneration using preserve markers:
153
+
154
+ ```markdown
155
+ <!-- prjct:preserve -->
156
+ # My Custom Rules
157
+ - Always use tabs
158
+ - Prefer functional patterns
159
+ <!-- /prjct:preserve -->
160
+ ```
161
+
162
+ **How it works:**
163
+ - Content between markers is extracted before regeneration
164
+ - After regeneration, preserved content is appended under "Your Customizations"
165
+ - Named sections: `<!-- prjct:preserve:my-rules -->` for identification
166
+
167
+ **Where to use:**
168
+ - `context/CLAUDE.md` - Project-specific AI instructions
169
+ - `agents/*.md` - Domain-specific patterns
170
+ - Any regenerated context file
171
+
172
+ **⚠️ Invalid blocks show warnings:**
173
+ - Unclosed markers are ignored
174
+ - Nested blocks are not supported
175
+ - Mismatched markers trigger console warnings
176
+
150
177
  ---
151
178
 
152
179
  ## CORE WORKFLOW