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 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('**Learned Patterns**')
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## LEARNED PATTERNS (use these, do NOT ask user)\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## RELEVANT MEMORIES (apply these learnings)\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
+ })
@@ -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("**Learned Patterns**");
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## LEARNED PATTERNS (use these, do NOT ask user)\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## RELEVANT MEMORIES (apply these learnings)\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.4",
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.4",
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 .",