stackmem 1.0.4 → 1.0.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/dist/cli.js +69 -12
- package/package.json +1 -1
- package/src/cli.ts +69 -12
package/dist/cli.js
CHANGED
|
@@ -483,21 +483,60 @@ function isSchemaFile(filePath) {
|
|
|
483
483
|
);
|
|
484
484
|
}
|
|
485
485
|
|
|
486
|
+
const LOW_QUALITY_PREFIXES = [
|
|
487
|
+
"wip", "fix", "update", "misc", "temp", "test", "patch", "minor",
|
|
488
|
+
"tweak", "change", "stuff", "done", "commit", "save", "ok", "m", "x",
|
|
489
|
+
];
|
|
490
|
+
|
|
491
|
+
// Filters out lazy/placeholder commit messages ("wip", "fix", "m", "...")
|
|
492
|
+
// so only messages worth remembering become decisions. Checked against the
|
|
493
|
+
// first word, not a raw prefix match, so a real message like "Migrate auth
|
|
494
|
+
// to JWT" isn't rejected just for starting with the letter "m".
|
|
495
|
+
function isQualityCommitMessage(message) {
|
|
496
|
+
const trimmed = message.trim();
|
|
497
|
+
if (trimmed.length <= 20) return false;
|
|
498
|
+
if (!trimmed.includes(" ")) return false;
|
|
499
|
+
|
|
500
|
+
const stripped = trimmed.replace(/\\s/g, "");
|
|
501
|
+
if (stripped.length > 0 && new Set(stripped).size === 1) return false;
|
|
502
|
+
|
|
503
|
+
const lowerTrimmed = trimmed.toLowerCase();
|
|
504
|
+
if (LOW_QUALITY_PREFIXES.includes(lowerTrimmed)) return false;
|
|
505
|
+
|
|
506
|
+
const firstWord = (lowerTrimmed.match(/^[a-z0-9]+/) || [""])[0];
|
|
507
|
+
if (LOW_QUALITY_PREFIXES.includes(firstWord)) return false;
|
|
508
|
+
|
|
509
|
+
return true;
|
|
510
|
+
}
|
|
511
|
+
|
|
486
512
|
const savedItems = [];
|
|
487
513
|
|
|
488
514
|
const commitMessage = git(["log", "-1", "--pretty=%B"]);
|
|
489
|
-
cmSave("decision", commitMessage);
|
|
490
|
-
savedItems.push(\`decision: \${commitMessage}\`);
|
|
491
515
|
|
|
492
|
-
const
|
|
516
|
+
const isFirstCommit = (() => {
|
|
517
|
+
try {
|
|
518
|
+
execFileSync("git", ["rev-parse", "HEAD~1"], { stdio: "pipe" });
|
|
519
|
+
return false;
|
|
520
|
+
} catch {
|
|
521
|
+
return true;
|
|
522
|
+
}
|
|
523
|
+
})();
|
|
524
|
+
|
|
525
|
+
const changedFiles = git(
|
|
526
|
+
isFirstCommit
|
|
527
|
+
? ["diff-tree", "--root", "--no-commit-id", "-r", "--name-only", "HEAD"]
|
|
528
|
+
: ["diff-tree", "--no-commit-id", "-r", "--name-only", "HEAD"]
|
|
529
|
+
)
|
|
493
530
|
.split("\\n")
|
|
494
531
|
.filter(Boolean);
|
|
495
532
|
|
|
496
533
|
let diff = "";
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
534
|
+
if (!isFirstCommit) {
|
|
535
|
+
try {
|
|
536
|
+
diff = execFileSync("git", ["diff", "HEAD~1", "HEAD"], { encoding: "utf-8" });
|
|
537
|
+
} catch {
|
|
538
|
+
diff = "";
|
|
539
|
+
}
|
|
501
540
|
}
|
|
502
541
|
|
|
503
542
|
const lines = diff.split("\\n");
|
|
@@ -505,7 +544,7 @@ let currentFile = null;
|
|
|
505
544
|
let isNewFile = false;
|
|
506
545
|
let inDependenciesSection = false;
|
|
507
546
|
|
|
508
|
-
for (const line of lines) {
|
|
547
|
+
for (const line of isFirstCommit ? [] : lines) {
|
|
509
548
|
if (line.startsWith("diff --git")) {
|
|
510
549
|
isNewFile = false;
|
|
511
550
|
inDependenciesSection = false;
|
|
@@ -555,10 +594,20 @@ for (const line of lines) {
|
|
|
555
594
|
if (line.startsWith("-") && !line.startsWith("---")) {
|
|
556
595
|
const codeLine = line.slice(1).trim();
|
|
557
596
|
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
597
|
+
const importMatch = line.match(
|
|
598
|
+
/^-\\s*(import\\s+.*\\s+from\\s+['"]([^'"]+)['"]|import\\s+['"]([^'"]+)['"])/
|
|
599
|
+
);
|
|
600
|
+
const requireMatch = line.match(
|
|
601
|
+
/^-\\s*(?:const|let|var)\\s+\\S+\\s*=\\s*require\\(['"]([^'"]+)['"]\\)/
|
|
602
|
+
);
|
|
603
|
+
const removedModulePath = importMatch
|
|
604
|
+
? importMatch[2] || importMatch[3]
|
|
605
|
+
: requireMatch
|
|
606
|
+
? requireMatch[1]
|
|
607
|
+
: null;
|
|
608
|
+
|
|
609
|
+
if (removedModulePath) {
|
|
610
|
+
const content = \`removed dependency: \${removedModulePath}\`;
|
|
562
611
|
cmSave("rejection", content);
|
|
563
612
|
savedItems.push(\`rejection: \${content}\`);
|
|
564
613
|
}
|
|
@@ -614,6 +663,14 @@ for (const line of lines) {
|
|
|
614
663
|
}
|
|
615
664
|
}
|
|
616
665
|
|
|
666
|
+
// Saved last, after every diff-detected memory, and only if it clears the
|
|
667
|
+
// quality filter — diff analysis always runs regardless of commit message.
|
|
668
|
+
if (isQualityCommitMessage(commitMessage)) {
|
|
669
|
+
const trimmedMessage = commitMessage.trim();
|
|
670
|
+
cmSave("decision", trimmedMessage);
|
|
671
|
+
savedItems.push(\`decision: \${trimmedMessage}\`);
|
|
672
|
+
}
|
|
673
|
+
|
|
617
674
|
console.log(\`coding-memory post-commit summary (\${changedFiles.length} file(s) changed):\`);
|
|
618
675
|
for (const item of savedItems) {
|
|
619
676
|
console.log(\` - \${item}\`);
|
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -573,21 +573,60 @@ function isSchemaFile(filePath) {
|
|
|
573
573
|
);
|
|
574
574
|
}
|
|
575
575
|
|
|
576
|
+
const LOW_QUALITY_PREFIXES = [
|
|
577
|
+
"wip", "fix", "update", "misc", "temp", "test", "patch", "minor",
|
|
578
|
+
"tweak", "change", "stuff", "done", "commit", "save", "ok", "m", "x",
|
|
579
|
+
];
|
|
580
|
+
|
|
581
|
+
// Filters out lazy/placeholder commit messages ("wip", "fix", "m", "...")
|
|
582
|
+
// so only messages worth remembering become decisions. Checked against the
|
|
583
|
+
// first word, not a raw prefix match, so a real message like "Migrate auth
|
|
584
|
+
// to JWT" isn't rejected just for starting with the letter "m".
|
|
585
|
+
function isQualityCommitMessage(message) {
|
|
586
|
+
const trimmed = message.trim();
|
|
587
|
+
if (trimmed.length <= 20) return false;
|
|
588
|
+
if (!trimmed.includes(" ")) return false;
|
|
589
|
+
|
|
590
|
+
const stripped = trimmed.replace(/\\s/g, "");
|
|
591
|
+
if (stripped.length > 0 && new Set(stripped).size === 1) return false;
|
|
592
|
+
|
|
593
|
+
const lowerTrimmed = trimmed.toLowerCase();
|
|
594
|
+
if (LOW_QUALITY_PREFIXES.includes(lowerTrimmed)) return false;
|
|
595
|
+
|
|
596
|
+
const firstWord = (lowerTrimmed.match(/^[a-z0-9]+/) || [""])[0];
|
|
597
|
+
if (LOW_QUALITY_PREFIXES.includes(firstWord)) return false;
|
|
598
|
+
|
|
599
|
+
return true;
|
|
600
|
+
}
|
|
601
|
+
|
|
576
602
|
const savedItems = [];
|
|
577
603
|
|
|
578
604
|
const commitMessage = git(["log", "-1", "--pretty=%B"]);
|
|
579
|
-
cmSave("decision", commitMessage);
|
|
580
|
-
savedItems.push(\`decision: \${commitMessage}\`);
|
|
581
605
|
|
|
582
|
-
const
|
|
606
|
+
const isFirstCommit = (() => {
|
|
607
|
+
try {
|
|
608
|
+
execFileSync("git", ["rev-parse", "HEAD~1"], { stdio: "pipe" });
|
|
609
|
+
return false;
|
|
610
|
+
} catch {
|
|
611
|
+
return true;
|
|
612
|
+
}
|
|
613
|
+
})();
|
|
614
|
+
|
|
615
|
+
const changedFiles = git(
|
|
616
|
+
isFirstCommit
|
|
617
|
+
? ["diff-tree", "--root", "--no-commit-id", "-r", "--name-only", "HEAD"]
|
|
618
|
+
: ["diff-tree", "--no-commit-id", "-r", "--name-only", "HEAD"]
|
|
619
|
+
)
|
|
583
620
|
.split("\\n")
|
|
584
621
|
.filter(Boolean);
|
|
585
622
|
|
|
586
623
|
let diff = "";
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
624
|
+
if (!isFirstCommit) {
|
|
625
|
+
try {
|
|
626
|
+
diff = execFileSync("git", ["diff", "HEAD~1", "HEAD"], { encoding: "utf-8" });
|
|
627
|
+
} catch {
|
|
628
|
+
diff = "";
|
|
629
|
+
}
|
|
591
630
|
}
|
|
592
631
|
|
|
593
632
|
const lines = diff.split("\\n");
|
|
@@ -595,7 +634,7 @@ let currentFile = null;
|
|
|
595
634
|
let isNewFile = false;
|
|
596
635
|
let inDependenciesSection = false;
|
|
597
636
|
|
|
598
|
-
for (const line of lines) {
|
|
637
|
+
for (const line of isFirstCommit ? [] : lines) {
|
|
599
638
|
if (line.startsWith("diff --git")) {
|
|
600
639
|
isNewFile = false;
|
|
601
640
|
inDependenciesSection = false;
|
|
@@ -645,10 +684,20 @@ for (const line of lines) {
|
|
|
645
684
|
if (line.startsWith("-") && !line.startsWith("---")) {
|
|
646
685
|
const codeLine = line.slice(1).trim();
|
|
647
686
|
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
687
|
+
const importMatch = line.match(
|
|
688
|
+
/^-\\s*(import\\s+.*\\s+from\\s+['"]([^'"]+)['"]|import\\s+['"]([^'"]+)['"])/
|
|
689
|
+
);
|
|
690
|
+
const requireMatch = line.match(
|
|
691
|
+
/^-\\s*(?:const|let|var)\\s+\\S+\\s*=\\s*require\\(['"]([^'"]+)['"]\\)/
|
|
692
|
+
);
|
|
693
|
+
const removedModulePath = importMatch
|
|
694
|
+
? importMatch[2] || importMatch[3]
|
|
695
|
+
: requireMatch
|
|
696
|
+
? requireMatch[1]
|
|
697
|
+
: null;
|
|
698
|
+
|
|
699
|
+
if (removedModulePath) {
|
|
700
|
+
const content = \`removed dependency: \${removedModulePath}\`;
|
|
652
701
|
cmSave("rejection", content);
|
|
653
702
|
savedItems.push(\`rejection: \${content}\`);
|
|
654
703
|
}
|
|
@@ -704,6 +753,14 @@ for (const line of lines) {
|
|
|
704
753
|
}
|
|
705
754
|
}
|
|
706
755
|
|
|
756
|
+
// Saved last, after every diff-detected memory, and only if it clears the
|
|
757
|
+
// quality filter — diff analysis always runs regardless of commit message.
|
|
758
|
+
if (isQualityCommitMessage(commitMessage)) {
|
|
759
|
+
const trimmedMessage = commitMessage.trim();
|
|
760
|
+
cmSave("decision", trimmedMessage);
|
|
761
|
+
savedItems.push(\`decision: \${trimmedMessage}\`);
|
|
762
|
+
}
|
|
763
|
+
|
|
707
764
|
console.log(\`coding-memory post-commit summary (\${changedFiles.length} file(s) changed):\`);
|
|
708
765
|
for (const item of savedItems) {
|
|
709
766
|
console.log(\` - \${item}\`);
|