prjct-cli 0.45.4 → 0.45.5
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 +21 -0
- package/core/agentic/prompt-builder.ts +3 -3
- package/core/cli/lint-meta-commentary.ts +177 -0
- package/dist/bin/prjct.mjs +5 -4
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.45.5] - 2026-01-30
|
|
4
|
+
|
|
5
|
+
### Bug Fixes
|
|
6
|
+
|
|
7
|
+
- implement silent memory application - PRJ-103 (#69)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
## [0.45.5] - 2026-01-29
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- Remove meta-commentary from memory output (PRJ-103)
|
|
15
|
+
- "LEARNED PATTERNS" → "PROJECT DEFAULTS"
|
|
16
|
+
- "RELEVANT MEMORIES" → "CONTEXT"
|
|
17
|
+
- "Learned Patterns" → "Project Conventions"
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- `lint:meta` script to catch meta-commentary anti-patterns
|
|
22
|
+
|
|
23
|
+
|
|
3
24
|
## [0.45.4] - 2026-01-29
|
|
4
25
|
|
|
5
26
|
### Bug Fixes
|
|
@@ -174,7 +174,7 @@ class PromptBuilder {
|
|
|
174
174
|
try {
|
|
175
175
|
const patterns = await outcomeAnalyzer.detectPatterns(projectId)
|
|
176
176
|
if (patterns.length > 0) {
|
|
177
|
-
parts.push('**
|
|
177
|
+
parts.push('**Project Conventions**')
|
|
178
178
|
for (const pattern of patterns.slice(0, 3)) {
|
|
179
179
|
parts.push(`- ${pattern.description}`)
|
|
180
180
|
if (pattern.suggestedAction) {
|
|
@@ -489,7 +489,7 @@ class PromptBuilder {
|
|
|
489
489
|
|
|
490
490
|
// P1.1: Learned Patterns
|
|
491
491
|
if (learnedPatterns && Object.keys(learnedPatterns).some((k) => learnedPatterns[k])) {
|
|
492
|
-
parts.push('\n##
|
|
492
|
+
parts.push('\n## PROJECT DEFAULTS (apply automatically)\n')
|
|
493
493
|
for (const [key, value] of Object.entries(learnedPatterns)) {
|
|
494
494
|
if (value) {
|
|
495
495
|
parts.push(`- ${key}: ${value}\n`)
|
|
@@ -511,7 +511,7 @@ class PromptBuilder {
|
|
|
511
511
|
|
|
512
512
|
// P3.3: Relevant Memories
|
|
513
513
|
if (relevantMemories && relevantMemories.length > 0) {
|
|
514
|
-
parts.push('\n##
|
|
514
|
+
parts.push('\n## CONTEXT (apply these)\n')
|
|
515
515
|
for (const memory of relevantMemories) {
|
|
516
516
|
parts.push(`- **${memory.title}**: ${memory.content}\n`)
|
|
517
517
|
if (memory.tags && memory.tags.length > 0) {
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* Lint rule: no-meta-commentary
|
|
4
|
+
*
|
|
5
|
+
* Catches AI memory meta-commentary patterns that should be avoided.
|
|
6
|
+
* Preferences should be presented as facts, not as memories.
|
|
7
|
+
*
|
|
8
|
+
* Anti-patterns:
|
|
9
|
+
* - "I remember you prefer..."
|
|
10
|
+
* - "Based on your history..."
|
|
11
|
+
* - "I've learned that..."
|
|
12
|
+
* - "From previous sessions..."
|
|
13
|
+
*
|
|
14
|
+
* Correct pattern:
|
|
15
|
+
* - "Use TypeScript strict mode" (not "I remember you like strict mode")
|
|
16
|
+
*
|
|
17
|
+
* @see PRJ-103
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { readdir, readFile } from 'node:fs/promises'
|
|
21
|
+
import { join, relative } from 'node:path'
|
|
22
|
+
|
|
23
|
+
const META_COMMENTARY_PATTERNS = [
|
|
24
|
+
// Direct memory references
|
|
25
|
+
/\bI remember\b/gi,
|
|
26
|
+
/\bI recall\b/gi,
|
|
27
|
+
/\bI('ve| have) learned\b/gi,
|
|
28
|
+
|
|
29
|
+
// History references
|
|
30
|
+
/\bBased on (your |)history\b/gi,
|
|
31
|
+
/\bFrom previous (session|conversation)s?\b/gi,
|
|
32
|
+
/\bIn (the |)past,? you\b/gi,
|
|
33
|
+
|
|
34
|
+
// Preference meta-commentary
|
|
35
|
+
/\byou (usually|typically|always|often) prefer\b/gi,
|
|
36
|
+
/\byou mentioned (before|earlier|previously)\b/gi,
|
|
37
|
+
/\blast time you\b/gi,
|
|
38
|
+
|
|
39
|
+
// Section headers that expose memory mechanism
|
|
40
|
+
/\bLEARNED PATTERNS\b/g,
|
|
41
|
+
/\bRELEVANT MEMORIES\b/g,
|
|
42
|
+
/\bLearned Patterns\b/g,
|
|
43
|
+
/\bRelevant Memories\b/g,
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
// Files/directories to skip
|
|
47
|
+
const SKIP_PATTERNS = [
|
|
48
|
+
/node_modules/,
|
|
49
|
+
/\.git/,
|
|
50
|
+
/dist/,
|
|
51
|
+
/build/,
|
|
52
|
+
/coverage/,
|
|
53
|
+
/\.test\.ts$/,
|
|
54
|
+
/\.spec\.ts$/,
|
|
55
|
+
/lint-meta-commentary\.ts$/, // Skip self
|
|
56
|
+
/\/docs\//, // Skip documentation (describes features, not agent output)
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
// Only check these extensions
|
|
60
|
+
const INCLUDE_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx', '.md']
|
|
61
|
+
|
|
62
|
+
interface Violation {
|
|
63
|
+
file: string
|
|
64
|
+
line: number
|
|
65
|
+
column: number
|
|
66
|
+
match: string
|
|
67
|
+
pattern: string
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function* walkDir(dir: string): AsyncGenerator<string> {
|
|
71
|
+
const entries = await readdir(dir, { withFileTypes: true })
|
|
72
|
+
|
|
73
|
+
for (const entry of entries) {
|
|
74
|
+
const fullPath = join(dir, entry.name)
|
|
75
|
+
|
|
76
|
+
if (SKIP_PATTERNS.some((p) => p.test(fullPath))) {
|
|
77
|
+
continue
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (entry.isDirectory()) {
|
|
81
|
+
yield* walkDir(fullPath)
|
|
82
|
+
} else if (entry.isFile() && INCLUDE_EXTENSIONS.some((ext) => entry.name.endsWith(ext))) {
|
|
83
|
+
yield fullPath
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function checkFile(filePath: string): Promise<Violation[]> {
|
|
89
|
+
const violations: Violation[] = []
|
|
90
|
+
const content = await readFile(filePath, 'utf-8')
|
|
91
|
+
const lines = content.split('\n')
|
|
92
|
+
|
|
93
|
+
for (let lineNum = 0; lineNum < lines.length; lineNum++) {
|
|
94
|
+
const line = lines[lineNum]
|
|
95
|
+
|
|
96
|
+
// Skip comments - we only care about string literals that become agent output
|
|
97
|
+
const trimmed = line.trim()
|
|
98
|
+
if (
|
|
99
|
+
trimmed.startsWith('//') || // Inline comments
|
|
100
|
+
trimmed.startsWith('*') || // JSDoc/block comments
|
|
101
|
+
trimmed.startsWith('/*') || // Block comment start
|
|
102
|
+
line.includes('Anti-pattern') || // Documentation
|
|
103
|
+
line.includes('@see') || // JSDoc refs
|
|
104
|
+
line.includes('@example') // JSDoc examples
|
|
105
|
+
) {
|
|
106
|
+
continue
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
for (const pattern of META_COMMENTARY_PATTERNS) {
|
|
110
|
+
// Reset lastIndex for global patterns
|
|
111
|
+
pattern.lastIndex = 0
|
|
112
|
+
|
|
113
|
+
let match: RegExpExecArray | null
|
|
114
|
+
while ((match = pattern.exec(line)) !== null) {
|
|
115
|
+
violations.push({
|
|
116
|
+
file: filePath,
|
|
117
|
+
line: lineNum + 1,
|
|
118
|
+
column: match.index + 1,
|
|
119
|
+
match: match[0],
|
|
120
|
+
pattern: pattern.source,
|
|
121
|
+
})
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return violations
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async function main() {
|
|
130
|
+
const rootDir = process.cwd()
|
|
131
|
+
const allViolations: Violation[] = []
|
|
132
|
+
|
|
133
|
+
console.log('Checking for meta-commentary patterns...\n')
|
|
134
|
+
|
|
135
|
+
for await (const filePath of walkDir(rootDir)) {
|
|
136
|
+
const violations = await checkFile(filePath)
|
|
137
|
+
if (violations.length > 0) {
|
|
138
|
+
allViolations.push(...violations)
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (allViolations.length === 0) {
|
|
143
|
+
console.log('✅ No meta-commentary patterns found\n')
|
|
144
|
+
process.exit(0)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
console.log(`❌ Found ${allViolations.length} meta-commentary violation(s):\n`)
|
|
148
|
+
|
|
149
|
+
// Group by file
|
|
150
|
+
const byFile = new Map<string, Violation[]>()
|
|
151
|
+
for (const v of allViolations) {
|
|
152
|
+
const rel = relative(rootDir, v.file)
|
|
153
|
+
if (!byFile.has(rel)) {
|
|
154
|
+
byFile.set(rel, [])
|
|
155
|
+
}
|
|
156
|
+
byFile.get(rel)!.push(v)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
for (const [file, violations] of byFile) {
|
|
160
|
+
console.log(` ${file}`)
|
|
161
|
+
for (const v of violations) {
|
|
162
|
+
console.log(` ${v.line}:${v.column} "${v.match}"`)
|
|
163
|
+
}
|
|
164
|
+
console.log('')
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
console.log('Fix: Present preferences as facts, not memories.')
|
|
168
|
+
console.log(' ❌ "I remember you prefer TypeScript"')
|
|
169
|
+
console.log(' ✅ "Use TypeScript"\n')
|
|
170
|
+
|
|
171
|
+
process.exit(1)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
main().catch((err) => {
|
|
175
|
+
console.error('Error:', err.message)
|
|
176
|
+
process.exit(1)
|
|
177
|
+
})
|
package/dist/bin/prjct.mjs
CHANGED
|
@@ -12055,7 +12055,7 @@ var init_prompt_builder = __esm({
|
|
|
12055
12055
|
try {
|
|
12056
12056
|
const patterns = await analyzer_default.detectPatterns(projectId);
|
|
12057
12057
|
if (patterns.length > 0) {
|
|
12058
|
-
parts.push("**
|
|
12058
|
+
parts.push("**Project Conventions**");
|
|
12059
12059
|
for (const pattern of patterns.slice(0, 3)) {
|
|
12060
12060
|
parts.push(`- ${pattern.description}`);
|
|
12061
12061
|
if (pattern.suggestedAction) {
|
|
@@ -12315,7 +12315,7 @@ Stack: ${stack}
|
|
|
12315
12315
|
}
|
|
12316
12316
|
parts.push(this.buildCriticalRules());
|
|
12317
12317
|
if (learnedPatterns && Object.keys(learnedPatterns).some((k) => learnedPatterns[k])) {
|
|
12318
|
-
parts.push("\n##
|
|
12318
|
+
parts.push("\n## PROJECT DEFAULTS (apply automatically)\n");
|
|
12319
12319
|
for (const [key, value] of Object.entries(learnedPatterns)) {
|
|
12320
12320
|
if (value) {
|
|
12321
12321
|
parts.push(`- ${key}: ${value}
|
|
@@ -12337,7 +12337,7 @@ Stack: ${stack}
|
|
|
12337
12337
|
`);
|
|
12338
12338
|
}
|
|
12339
12339
|
if (relevantMemories && relevantMemories.length > 0) {
|
|
12340
|
-
parts.push("\n##
|
|
12340
|
+
parts.push("\n## CONTEXT (apply these)\n");
|
|
12341
12341
|
for (const memory of relevantMemories) {
|
|
12342
12342
|
parts.push(`- **${memory.title}**: ${memory.content}
|
|
12343
12343
|
`);
|
|
@@ -23330,7 +23330,7 @@ var require_package = __commonJS({
|
|
|
23330
23330
|
"package.json"(exports, module) {
|
|
23331
23331
|
module.exports = {
|
|
23332
23332
|
name: "prjct-cli",
|
|
23333
|
-
version: "0.45.
|
|
23333
|
+
version: "0.45.5",
|
|
23334
23334
|
description: "Context layer for AI agents. Project context for Claude Code, Gemini CLI, and more.",
|
|
23335
23335
|
main: "core/index.ts",
|
|
23336
23336
|
bin: {
|
|
@@ -23359,6 +23359,7 @@ var require_package = __commonJS({
|
|
|
23359
23359
|
validate: "bun scripts/validate-commands.js",
|
|
23360
23360
|
lint: "biome lint .",
|
|
23361
23361
|
"lint:fix": "biome lint --write .",
|
|
23362
|
+
"lint:meta": "bun core/cli/lint-meta-commentary.ts",
|
|
23362
23363
|
format: "biome format --write .",
|
|
23363
23364
|
"format:check": "biome format .",
|
|
23364
23365
|
check: "biome check .",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prjct-cli",
|
|
3
|
-
"version": "0.45.
|
|
3
|
+
"version": "0.45.5",
|
|
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": {
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"validate": "bun scripts/validate-commands.js",
|
|
30
30
|
"lint": "biome lint .",
|
|
31
31
|
"lint:fix": "biome lint --write .",
|
|
32
|
+
"lint:meta": "bun core/cli/lint-meta-commentary.ts",
|
|
32
33
|
"format": "biome format --write .",
|
|
33
34
|
"format:check": "biome format .",
|
|
34
35
|
"check": "biome check .",
|