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.
- package/CHANGELOG.md +41 -3821
- package/core/agentic/context-builder.ts +4 -4
- package/core/agentic/memory-system.ts +3 -1
- package/core/agentic/prompt-builder.ts +6 -2
- package/core/agentic/smart-context.ts +2 -2
- package/core/ai-tools/generator.ts +11 -1
- package/core/cli/linear.ts +5 -4
- package/core/commands/analytics.ts +3 -1
- package/core/commands/commands.ts +1 -1
- package/core/commands/registry.ts +3 -2
- package/core/constants/index.ts +3 -1
- package/core/context-tools/imports-tool.ts +1 -1
- package/core/context-tools/signatures-tool.ts +1 -1
- package/core/plugin/hooks.ts +1 -1
- package/core/services/agent-generator.ts +58 -6
- package/core/services/context-generator.ts +35 -15
- package/core/utils/help.ts +0 -1
- package/core/utils/markdown-builder.ts +9 -3
- package/core/utils/preserve-sections.ts +1 -1
- package/core/utils/project-commands.ts +0 -6
- package/core/utils/subtask-table.ts +1 -1
- package/dist/bin/prjct.mjs +149 -77
- package/package.json +1 -1
- package/templates/global/CLAUDE.md +27 -0
package/dist/bin/prjct.mjs
CHANGED
|
@@ -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)
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
12419
|
-
`
|
|
12426
|
+
for (const c of thinkBlock.conclusions) {
|
|
12427
|
+
parts.push(` \u2192 ${c}
|
|
12428
|
+
`);
|
|
12429
|
+
}
|
|
12420
12430
|
}
|
|
12421
12431
|
parts.push("Plan:\n");
|
|
12422
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
@@ -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
|