@riconext/hermes-repo 0.13.2 → 0.15.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 +42 -0
- package/README.md +23 -4
- package/dist/cli.js +312 -144
- package/dist/cli.js.map +1 -1
- package/package.json +8 -2
package/dist/cli.js
CHANGED
|
@@ -621,6 +621,90 @@ ${digest}`
|
|
|
621
621
|
}
|
|
622
622
|
}
|
|
623
623
|
|
|
624
|
+
// src/capture/convergence.ts
|
|
625
|
+
function analyzeMessagePattern(session) {
|
|
626
|
+
let userCorrections = 0;
|
|
627
|
+
let lastUserMessage = "";
|
|
628
|
+
for (let i = session.messages.length - 1; i >= 0; i--) {
|
|
629
|
+
const msg = session.messages[i];
|
|
630
|
+
if (msg.role === "user") {
|
|
631
|
+
lastUserMessage = msg.text;
|
|
632
|
+
for (const userMsg of session.messages) {
|
|
633
|
+
if (userMsg.role === "user" && /不对|错了|改|改成|改为|应该是|应该用/i.test(userMsg.text)) {
|
|
634
|
+
userCorrections++;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
break;
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
const hasFinalApproval = /好的|可以|就这样|同意|对|yes|ok|looks good|perfect|确认|同意/i.test(
|
|
641
|
+
lastUserMessage
|
|
642
|
+
);
|
|
643
|
+
const hasUncertainty = /不明白|还是|有问题|不太对|感觉|好像|可能|应该|不是很|似乎/i.test(
|
|
644
|
+
lastUserMessage
|
|
645
|
+
);
|
|
646
|
+
return {
|
|
647
|
+
userCorrections,
|
|
648
|
+
lastUserMessage,
|
|
649
|
+
hasFinalApproval,
|
|
650
|
+
hasUncertainty
|
|
651
|
+
};
|
|
652
|
+
}
|
|
653
|
+
function isConvergent(session) {
|
|
654
|
+
const pattern = analyzeMessagePattern(session);
|
|
655
|
+
if (pattern.hasFinalApproval) {
|
|
656
|
+
return true;
|
|
657
|
+
}
|
|
658
|
+
if (pattern.userCorrections > 1 && !pattern.hasFinalApproval) {
|
|
659
|
+
if (pattern.hasUncertainty) {
|
|
660
|
+
return false;
|
|
661
|
+
}
|
|
662
|
+
return true;
|
|
663
|
+
}
|
|
664
|
+
return true;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
// src/capture/externalSignals.ts
|
|
668
|
+
function analyzeExternalSignals(session) {
|
|
669
|
+
return {
|
|
670
|
+
hasCIFailed: session.ciStatus === "failed",
|
|
671
|
+
hasCIPassed: session.ciStatus === "passed",
|
|
672
|
+
hasUserDislike: session.userEmoji === "\u{1F44E}",
|
|
673
|
+
hasUserLike: session.userEmoji === "\u{1F44D}"
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
function scoreExternalSignals(session) {
|
|
677
|
+
const signals = analyzeExternalSignals(session);
|
|
678
|
+
let score = 0;
|
|
679
|
+
if (signals.hasUserLike) {
|
|
680
|
+
score += 2;
|
|
681
|
+
}
|
|
682
|
+
if (signals.hasUserDislike) {
|
|
683
|
+
score -= 2;
|
|
684
|
+
}
|
|
685
|
+
if (signals.hasCIPassed && session.fileChanges > 0) {
|
|
686
|
+
score += 1;
|
|
687
|
+
}
|
|
688
|
+
if (signals.hasCIFailed && session.fileChanges > 0) {
|
|
689
|
+
score -= 1.5;
|
|
690
|
+
}
|
|
691
|
+
return score;
|
|
692
|
+
}
|
|
693
|
+
function shouldRejectByExternalSignals(session) {
|
|
694
|
+
if (!session.ciStatus && !session.userEmoji) {
|
|
695
|
+
return false;
|
|
696
|
+
}
|
|
697
|
+
const signals = analyzeExternalSignals(session);
|
|
698
|
+
const score = scoreExternalSignals(session);
|
|
699
|
+
if (signals.hasCIFailed && session.fileChanges > 0 && score < -0.5) {
|
|
700
|
+
return true;
|
|
701
|
+
}
|
|
702
|
+
if (signals.hasUserDislike) {
|
|
703
|
+
return true;
|
|
704
|
+
}
|
|
705
|
+
return false;
|
|
706
|
+
}
|
|
707
|
+
|
|
624
708
|
// src/capture/shouldCapture.ts
|
|
625
709
|
var CHINESE_STRONG_SIGNALS = [
|
|
626
710
|
"\u4FEE\u590D",
|
|
@@ -649,9 +733,6 @@ var ENGLISH_STRONG_SIGNAL_PATTERNS = [
|
|
|
649
733
|
];
|
|
650
734
|
var CORRECTION_RE = /不对|错了|不是这样|不应该|别用|stop|wrong|incorrect|改成|修正/i;
|
|
651
735
|
var SEMANTIC_SIGNAL_RE = /约定|必须|架构|决策|规范|convention|pattern|always|never/i;
|
|
652
|
-
function countUserMessages(session) {
|
|
653
|
-
return session.messages.filter((m) => m.role === "user").length;
|
|
654
|
-
}
|
|
655
736
|
function hasStrongSignal(text) {
|
|
656
737
|
const lower = text.toLowerCase();
|
|
657
738
|
if (CHINESE_STRONG_SIGNALS.some((w) => lower.includes(w.toLowerCase()))) {
|
|
@@ -665,14 +746,26 @@ function hasUserCorrection(session) {
|
|
|
665
746
|
);
|
|
666
747
|
}
|
|
667
748
|
function shouldCapture(session) {
|
|
668
|
-
if (session
|
|
749
|
+
if (shouldRejectByExternalSignals(session)) {
|
|
669
750
|
return false;
|
|
670
751
|
}
|
|
671
|
-
if (
|
|
752
|
+
if (hasStrongSignal(session.text) || hasUserCorrection(session)) {
|
|
753
|
+
if (hasUserCorrection(session) && !isConvergent(session)) {
|
|
754
|
+
return false;
|
|
755
|
+
}
|
|
756
|
+
return true;
|
|
757
|
+
}
|
|
758
|
+
if (session.toolCalls > 5) {
|
|
759
|
+
return true;
|
|
760
|
+
}
|
|
761
|
+
if (session.fileChanges > 0 && session.messages.length >= 3) {
|
|
762
|
+
return true;
|
|
763
|
+
}
|
|
764
|
+
const isGreetingOnly = session.messages.length <= 2 && session.toolCalls === 0 && session.fileChanges === 0;
|
|
765
|
+
if (isGreetingOnly) {
|
|
672
766
|
return false;
|
|
673
767
|
}
|
|
674
|
-
|
|
675
|
-
return hasStrongSignal(session.text) || hasUserCorrection(session) || hasComplexTask;
|
|
768
|
+
return false;
|
|
676
769
|
}
|
|
677
770
|
function inferCaptureType(session) {
|
|
678
771
|
if (SEMANTIC_SIGNAL_RE.test(session.text)) {
|
|
@@ -3735,27 +3828,101 @@ var claudeCodeAdapter = {
|
|
|
3735
3828
|
}
|
|
3736
3829
|
};
|
|
3737
3830
|
|
|
3738
|
-
// src/init/assistants/
|
|
3831
|
+
// src/init/assistants/codex.ts
|
|
3739
3832
|
import { mkdirSync as mkdirSync9, writeFileSync as writeFileSync14 } from "fs";
|
|
3740
3833
|
import { join as join27 } from "path";
|
|
3741
3834
|
|
|
3742
|
-
// src/init/
|
|
3835
|
+
// src/init/mergeCodexConfig.ts
|
|
3743
3836
|
import { existsSync as existsSync24, readFileSync as readFileSync24 } from "fs";
|
|
3744
3837
|
import { join as join26 } from "path";
|
|
3838
|
+
var CODEX_CONFIG_REL = ".codex/config.toml";
|
|
3839
|
+
var CODEX_HERMES_START_MARKER = "# >>> hermes-repo codex (do not edit this block manually)";
|
|
3840
|
+
var CODEX_HERMES_END_MARKER = "# <<< hermes-repo codex";
|
|
3841
|
+
function buildCodexHermesBlock() {
|
|
3842
|
+
return [
|
|
3843
|
+
CODEX_HERMES_START_MARKER,
|
|
3844
|
+
"# Hermes uses AGENTS.md as the shared Codex project guidance entry.",
|
|
3845
|
+
"# Run `npx @riconext/hermes-repo search <keyword>` to inspect project memory.",
|
|
3846
|
+
'# Run `npx @riconext/hermes-repo ref --capture <path> --reason "..."` after using memory.',
|
|
3847
|
+
CODEX_HERMES_END_MARKER
|
|
3848
|
+
].join("\n");
|
|
3849
|
+
}
|
|
3850
|
+
function codexConfigPath(repoRoot) {
|
|
3851
|
+
return join26(repoRoot, ".codex", "config.toml");
|
|
3852
|
+
}
|
|
3853
|
+
function spliceHermesBlock(existing, block) {
|
|
3854
|
+
const startIdx = existing.indexOf(CODEX_HERMES_START_MARKER);
|
|
3855
|
+
const endIdx = existing.indexOf(CODEX_HERMES_END_MARKER);
|
|
3856
|
+
if (startIdx >= 0 && endIdx >= startIdx) {
|
|
3857
|
+
const before = existing.slice(0, startIdx).trimEnd();
|
|
3858
|
+
const after = existing.slice(endIdx + CODEX_HERMES_END_MARKER.length).trimStart();
|
|
3859
|
+
return `${before ? `${before}
|
|
3860
|
+
|
|
3861
|
+
` : ""}${block}${after ? `
|
|
3862
|
+
|
|
3863
|
+
${after}` : ""}
|
|
3864
|
+
`;
|
|
3865
|
+
}
|
|
3866
|
+
const trimmed = existing.trimEnd();
|
|
3867
|
+
return `${trimmed ? `${trimmed}
|
|
3868
|
+
|
|
3869
|
+
` : ""}${block}
|
|
3870
|
+
`;
|
|
3871
|
+
}
|
|
3872
|
+
function mergeCodexConfig(repoRoot) {
|
|
3873
|
+
const configPath = codexConfigPath(repoRoot);
|
|
3874
|
+
const existed = existsSync24(configPath);
|
|
3875
|
+
const block = buildCodexHermesBlock();
|
|
3876
|
+
if (!existed) {
|
|
3877
|
+
return {
|
|
3878
|
+
content: `${block}
|
|
3879
|
+
`,
|
|
3880
|
+
action: "created"
|
|
3881
|
+
};
|
|
3882
|
+
}
|
|
3883
|
+
const existing = readFileSync24(configPath, "utf8");
|
|
3884
|
+
const hasBlock = existing.includes(CODEX_HERMES_START_MARKER) && existing.includes(CODEX_HERMES_END_MARKER);
|
|
3885
|
+
return {
|
|
3886
|
+
content: spliceHermesBlock(existing, block),
|
|
3887
|
+
action: hasBlock ? "replaced" : "appended"
|
|
3888
|
+
};
|
|
3889
|
+
}
|
|
3890
|
+
|
|
3891
|
+
// src/init/assistants/codex.ts
|
|
3892
|
+
var codexAdapter = {
|
|
3893
|
+
id: "codex",
|
|
3894
|
+
label: "OpenAI Codex\uFF08AGENTS.md + .codex/config.toml\uFF09",
|
|
3895
|
+
available: true,
|
|
3896
|
+
scaffoldPaths: [CODEX_CONFIG_REL],
|
|
3897
|
+
write(ctx) {
|
|
3898
|
+
mkdirSync9(join27(ctx.repoRoot, ".codex"), { recursive: true });
|
|
3899
|
+
const { content, action } = mergeCodexConfig(ctx.repoRoot);
|
|
3900
|
+
writeFileSync14(codexConfigPath(ctx.repoRoot), content, "utf8");
|
|
3901
|
+
ctx.report.files.push({ path: CODEX_CONFIG_REL, action });
|
|
3902
|
+
}
|
|
3903
|
+
};
|
|
3904
|
+
|
|
3905
|
+
// src/init/assistants/codebuddy.ts
|
|
3906
|
+
import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync15 } from "fs";
|
|
3907
|
+
import { join as join29 } from "path";
|
|
3908
|
+
|
|
3909
|
+
// src/init/mergeCodebuddySettings.ts
|
|
3910
|
+
import { existsSync as existsSync25, readFileSync as readFileSync25 } from "fs";
|
|
3911
|
+
import { join as join28 } from "path";
|
|
3745
3912
|
var CODEBUDDY_SETTINGS_LOCAL_REL = ".codebuddy/settings.local.json";
|
|
3746
3913
|
function codebuddySettingsLocalPath(repoRoot) {
|
|
3747
|
-
return
|
|
3914
|
+
return join28(repoRoot, ".codebuddy", "settings.local.json");
|
|
3748
3915
|
}
|
|
3749
3916
|
function mergeCodebuddyLocalSettings(repoRoot) {
|
|
3750
3917
|
const settingsPath = codebuddySettingsLocalPath(repoRoot);
|
|
3751
|
-
const existed =
|
|
3918
|
+
const existed = existsSync25(settingsPath);
|
|
3752
3919
|
const templateParsed = JSON.parse(
|
|
3753
3920
|
renderTemplate("hooks.codebuddy.json.tpl")
|
|
3754
3921
|
);
|
|
3755
3922
|
let existing = {};
|
|
3756
3923
|
if (existed) {
|
|
3757
3924
|
try {
|
|
3758
|
-
existing = JSON.parse(
|
|
3925
|
+
existing = JSON.parse(readFileSync25(settingsPath, "utf8"));
|
|
3759
3926
|
} catch {
|
|
3760
3927
|
existing = {};
|
|
3761
3928
|
}
|
|
@@ -3783,32 +3950,32 @@ var codebuddyAdapter = {
|
|
|
3783
3950
|
available: true,
|
|
3784
3951
|
scaffoldPaths: [CODEBUDDY_SETTINGS_LOCAL_REL],
|
|
3785
3952
|
write(ctx) {
|
|
3786
|
-
|
|
3953
|
+
mkdirSync10(join29(ctx.repoRoot, ".codebuddy"), { recursive: true });
|
|
3787
3954
|
const { content, action } = mergeCodebuddyLocalSettings(ctx.repoRoot);
|
|
3788
|
-
|
|
3955
|
+
writeFileSync15(codebuddySettingsLocalPath(ctx.repoRoot), content, "utf8");
|
|
3789
3956
|
ctx.report.files.push({ path: CODEBUDDY_SETTINGS_LOCAL_REL, action });
|
|
3790
3957
|
}
|
|
3791
3958
|
};
|
|
3792
3959
|
|
|
3793
3960
|
// src/init/assistants/cursor.ts
|
|
3794
|
-
import { mkdirSync as
|
|
3795
|
-
import { join as
|
|
3961
|
+
import { mkdirSync as mkdirSync11, writeFileSync as writeFileSync16 } from "fs";
|
|
3962
|
+
import { join as join31 } from "path";
|
|
3796
3963
|
|
|
3797
3964
|
// src/init/mergeCursorHooks.ts
|
|
3798
|
-
import { existsSync as
|
|
3799
|
-
import { join as
|
|
3965
|
+
import { existsSync as existsSync26, readFileSync as readFileSync26 } from "fs";
|
|
3966
|
+
import { join as join30 } from "path";
|
|
3800
3967
|
var CURSOR_HOOKS_REL = ".cursor/hooks.json";
|
|
3801
3968
|
function cursorHooksPath(repoRoot) {
|
|
3802
|
-
return
|
|
3969
|
+
return join30(repoRoot, ".cursor", "hooks.json");
|
|
3803
3970
|
}
|
|
3804
3971
|
function mergeCursorHooks(repoRoot) {
|
|
3805
3972
|
const hooksPath = cursorHooksPath(repoRoot);
|
|
3806
|
-
const existed =
|
|
3973
|
+
const existed = existsSync26(hooksPath);
|
|
3807
3974
|
const templateParsed = JSON.parse(renderTemplate("hooks.cursor.json.tpl"));
|
|
3808
3975
|
let existing = {};
|
|
3809
3976
|
if (existed) {
|
|
3810
3977
|
try {
|
|
3811
|
-
existing = JSON.parse(
|
|
3978
|
+
existing = JSON.parse(readFileSync26(hooksPath, "utf8"));
|
|
3812
3979
|
} catch {
|
|
3813
3980
|
existing = {};
|
|
3814
3981
|
}
|
|
@@ -3837,9 +4004,9 @@ var cursorAdapter = {
|
|
|
3837
4004
|
available: true,
|
|
3838
4005
|
scaffoldPaths: [CURSOR_HOOKS_REL],
|
|
3839
4006
|
write(ctx) {
|
|
3840
|
-
|
|
4007
|
+
mkdirSync11(join31(ctx.repoRoot, ".cursor"), { recursive: true });
|
|
3841
4008
|
const { content, action } = mergeCursorHooks(ctx.repoRoot);
|
|
3842
|
-
|
|
4009
|
+
writeFileSync16(cursorHooksPath(ctx.repoRoot), content, "utf8");
|
|
3843
4010
|
ctx.report.files.push({ path: CURSOR_HOOKS_REL, action });
|
|
3844
4011
|
}
|
|
3845
4012
|
};
|
|
@@ -3849,7 +4016,8 @@ var DEFAULT_ASSISTANT_IDS = ["claude-code"];
|
|
|
3849
4016
|
var ALL_ADAPTERS = [
|
|
3850
4017
|
claudeCodeAdapter,
|
|
3851
4018
|
cursorAdapter,
|
|
3852
|
-
codebuddyAdapter
|
|
4019
|
+
codebuddyAdapter,
|
|
4020
|
+
codexAdapter
|
|
3853
4021
|
];
|
|
3854
4022
|
var ADAPTER_BY_ID = new Map(
|
|
3855
4023
|
ALL_ADAPTERS.map((a) => [a.id, a])
|
|
@@ -3898,30 +4066,30 @@ function validateAssistantSelection(ids) {
|
|
|
3898
4066
|
}
|
|
3899
4067
|
|
|
3900
4068
|
// src/init/ensureDirs.ts
|
|
3901
|
-
import { mkdirSync as
|
|
3902
|
-
import { join as
|
|
4069
|
+
import { mkdirSync as mkdirSync12, writeFileSync as writeFileSync17 } from "fs";
|
|
4070
|
+
import { join as join32 } from "path";
|
|
3903
4071
|
function ensureMemoryTree(repoRoot) {
|
|
3904
|
-
const memoryRoot =
|
|
3905
|
-
|
|
4072
|
+
const memoryRoot = join32(repoRoot, MEMORY_DIR);
|
|
4073
|
+
mkdirSync12(memoryRoot, { recursive: true });
|
|
3906
4074
|
for (const sub of MEMORY_SUBDIRS) {
|
|
3907
|
-
|
|
4075
|
+
mkdirSync12(join32(memoryRoot, sub), { recursive: true });
|
|
3908
4076
|
}
|
|
3909
|
-
|
|
4077
|
+
mkdirSync12(join32(repoRoot, ".claude"), { recursive: true });
|
|
3910
4078
|
for (const sub of GITKEEP_DIRS) {
|
|
3911
|
-
const keepPath =
|
|
3912
|
-
|
|
4079
|
+
const keepPath = join32(memoryRoot, sub, ".gitkeep");
|
|
4080
|
+
writeFileSync17(keepPath, "", { flag: "a" });
|
|
3913
4081
|
}
|
|
3914
4082
|
}
|
|
3915
4083
|
|
|
3916
4084
|
// src/init/mergeAssistants.ts
|
|
3917
|
-
import { existsSync as
|
|
4085
|
+
import { existsSync as existsSync27, readFileSync as readFileSync27 } from "fs";
|
|
3918
4086
|
function readExistingAssistants(repoRoot) {
|
|
3919
4087
|
const configPath = memoryPath(repoRoot, "config.json");
|
|
3920
|
-
if (!
|
|
4088
|
+
if (!existsSync27(configPath)) {
|
|
3921
4089
|
return [];
|
|
3922
4090
|
}
|
|
3923
4091
|
try {
|
|
3924
|
-
const config = JSON.parse(
|
|
4092
|
+
const config = JSON.parse(readFileSync27(configPath, "utf8"));
|
|
3925
4093
|
if (!Array.isArray(config.assistants)) {
|
|
3926
4094
|
return [];
|
|
3927
4095
|
}
|
|
@@ -3936,17 +4104,17 @@ function mergeAssistants(repoRoot, selected) {
|
|
|
3936
4104
|
}
|
|
3937
4105
|
|
|
3938
4106
|
// src/init/mergeGitignore.ts
|
|
3939
|
-
import { existsSync as
|
|
3940
|
-
import { join as
|
|
4107
|
+
import { existsSync as existsSync28, readFileSync as readFileSync28, writeFileSync as writeFileSync18 } from "fs";
|
|
4108
|
+
import { join as join33 } from "path";
|
|
3941
4109
|
var START_MARKER = "# >>> hermes-repo memory (do not edit this block manually)";
|
|
3942
4110
|
var END_MARKER = "# <<< hermes-repo memory";
|
|
3943
4111
|
function mergeHermesGitignore(repoRoot) {
|
|
3944
4112
|
const block = readTemplate("gitignore-block.txt").trimEnd() + "\n";
|
|
3945
|
-
const gitignorePath =
|
|
3946
|
-
const contentBefore =
|
|
4113
|
+
const gitignorePath = join33(repoRoot, ".gitignore");
|
|
4114
|
+
const contentBefore = existsSync28(gitignorePath) ? readFileSync28(gitignorePath, "utf8") : "";
|
|
3947
4115
|
const warnBroadMemoryIgnore = contentBefore.length > 0 && !contentBefore.includes(START_MARKER) && /(^|\n)\.memory\/\s*$/m.test(contentBefore);
|
|
3948
|
-
if (!
|
|
3949
|
-
|
|
4116
|
+
if (!existsSync28(gitignorePath)) {
|
|
4117
|
+
writeFileSync18(gitignorePath, `${block}
|
|
3950
4118
|
`, "utf8");
|
|
3951
4119
|
return { action: "created", warnBroadMemoryIgnore: false };
|
|
3952
4120
|
}
|
|
@@ -3956,7 +4124,7 @@ function mergeHermesGitignore(repoRoot) {
|
|
|
3956
4124
|
const before = contentBefore.slice(0, startIdx);
|
|
3957
4125
|
const after = contentBefore.slice(endIdx + END_MARKER.length);
|
|
3958
4126
|
const next = `${before}${block}${after}`.replace(/\n{3,}/g, "\n\n");
|
|
3959
|
-
|
|
4127
|
+
writeFileSync18(gitignorePath, next.endsWith("\n") ? next : `${next}
|
|
3960
4128
|
`, "utf8");
|
|
3961
4129
|
return { action: "replaced", warnBroadMemoryIgnore };
|
|
3962
4130
|
}
|
|
@@ -3964,11 +4132,11 @@ function mergeHermesGitignore(repoRoot) {
|
|
|
3964
4132
|
const before = contentBefore.slice(0, startIdx);
|
|
3965
4133
|
const next = `${before}${block}
|
|
3966
4134
|
`;
|
|
3967
|
-
|
|
4135
|
+
writeFileSync18(gitignorePath, next, "utf8");
|
|
3968
4136
|
return { action: "updated", warnBroadMemoryIgnore };
|
|
3969
4137
|
}
|
|
3970
4138
|
const separator = contentBefore.endsWith("\n") || contentBefore.length === 0 ? "\n" : "\n\n";
|
|
3971
|
-
|
|
4139
|
+
writeFileSync18(gitignorePath, `${contentBefore}${separator}${block}
|
|
3972
4140
|
`, "utf8");
|
|
3973
4141
|
return { action: "appended", warnBroadMemoryIgnore };
|
|
3974
4142
|
}
|
|
@@ -4039,8 +4207,8 @@ async function withSpinnerProgress(label, fn, formatDone, stream = process.stder
|
|
|
4039
4207
|
}
|
|
4040
4208
|
|
|
4041
4209
|
// src/coldstart/collectors/existingRules.ts
|
|
4042
|
-
import { existsSync as
|
|
4043
|
-
import { join as
|
|
4210
|
+
import { existsSync as existsSync29, readFileSync as readFileSync29, readdirSync as readdirSync9 } from "fs";
|
|
4211
|
+
import { join as join34 } from "path";
|
|
4044
4212
|
var MAX_EXCERPT = 3500;
|
|
4045
4213
|
var RULE_CANDIDATES = [
|
|
4046
4214
|
"AGENTS.md",
|
|
@@ -4048,12 +4216,12 @@ var RULE_CANDIDATES = [
|
|
|
4048
4216
|
".cursorrules"
|
|
4049
4217
|
];
|
|
4050
4218
|
function readExcerpt(repoRoot, rel) {
|
|
4051
|
-
const full =
|
|
4052
|
-
if (!
|
|
4219
|
+
const full = join34(repoRoot, rel);
|
|
4220
|
+
if (!existsSync29(full)) {
|
|
4053
4221
|
return null;
|
|
4054
4222
|
}
|
|
4055
4223
|
try {
|
|
4056
|
-
const raw =
|
|
4224
|
+
const raw = readFileSync29(full, "utf8");
|
|
4057
4225
|
return raw.length > MAX_EXCERPT ? `${raw.slice(0, MAX_EXCERPT)}
|
|
4058
4226
|
...(truncated)` : raw;
|
|
4059
4227
|
} catch {
|
|
@@ -4061,8 +4229,8 @@ function readExcerpt(repoRoot, rel) {
|
|
|
4061
4229
|
}
|
|
4062
4230
|
}
|
|
4063
4231
|
function collectCursorRules(repoRoot) {
|
|
4064
|
-
const dir =
|
|
4065
|
-
if (!
|
|
4232
|
+
const dir = join34(repoRoot, ".cursor", "rules");
|
|
4233
|
+
if (!existsSync29(dir)) {
|
|
4066
4234
|
return [];
|
|
4067
4235
|
}
|
|
4068
4236
|
const out = [];
|
|
@@ -4113,8 +4281,8 @@ function collectGitLog(repoRoot, limit = 50) {
|
|
|
4113
4281
|
}
|
|
4114
4282
|
|
|
4115
4283
|
// src/coldstart/collectors/packageJson.ts
|
|
4116
|
-
import { existsSync as
|
|
4117
|
-
import { join as
|
|
4284
|
+
import { existsSync as existsSync30, readFileSync as readFileSync30 } from "fs";
|
|
4285
|
+
import { join as join35 } from "path";
|
|
4118
4286
|
var STACK_HINTS = {
|
|
4119
4287
|
react: "react",
|
|
4120
4288
|
vue: "vue",
|
|
@@ -4147,12 +4315,12 @@ function inferStackTags(deps) {
|
|
|
4147
4315
|
return [...tags];
|
|
4148
4316
|
}
|
|
4149
4317
|
function collectPackageJson(repoRoot) {
|
|
4150
|
-
const path =
|
|
4151
|
-
if (!
|
|
4318
|
+
const path = join35(repoRoot, "package.json");
|
|
4319
|
+
if (!existsSync30(path)) {
|
|
4152
4320
|
return null;
|
|
4153
4321
|
}
|
|
4154
4322
|
try {
|
|
4155
|
-
const raw = JSON.parse(
|
|
4323
|
+
const raw = JSON.parse(readFileSync30(path, "utf8"));
|
|
4156
4324
|
return {
|
|
4157
4325
|
name: raw.name,
|
|
4158
4326
|
dependencies: depKeys(raw.dependencies),
|
|
@@ -4164,8 +4332,8 @@ function collectPackageJson(repoRoot) {
|
|
|
4164
4332
|
}
|
|
4165
4333
|
|
|
4166
4334
|
// src/coldstart/collectors/repoSignals.ts
|
|
4167
|
-
import { existsSync as
|
|
4168
|
-
import { join as
|
|
4335
|
+
import { existsSync as existsSync31, readdirSync as readdirSync10 } from "fs";
|
|
4336
|
+
import { join as join36 } from "path";
|
|
4169
4337
|
var SIGNAL_FILES = [
|
|
4170
4338
|
"Dockerfile",
|
|
4171
4339
|
"Makefile",
|
|
@@ -4177,12 +4345,12 @@ var SIGNAL_FILES = [
|
|
|
4177
4345
|
function collectRepoSignals(repoRoot) {
|
|
4178
4346
|
const signals = [];
|
|
4179
4347
|
for (const rel of SIGNAL_FILES) {
|
|
4180
|
-
const full =
|
|
4181
|
-
if (
|
|
4348
|
+
const full = join36(repoRoot, rel);
|
|
4349
|
+
if (existsSync31(full)) {
|
|
4182
4350
|
signals.push(rel);
|
|
4183
4351
|
}
|
|
4184
4352
|
}
|
|
4185
|
-
if (
|
|
4353
|
+
if (existsSync31(join36(repoRoot, ".gitignore"))) {
|
|
4186
4354
|
signals.push(".gitignore");
|
|
4187
4355
|
}
|
|
4188
4356
|
try {
|
|
@@ -4327,7 +4495,7 @@ function runProjectScan(repoRoot) {
|
|
|
4327
4495
|
|
|
4328
4496
|
// src/init/prompts.ts
|
|
4329
4497
|
import { checkbox, confirm, input, password } from "@inquirer/prompts";
|
|
4330
|
-
import { existsSync as
|
|
4498
|
+
import { existsSync as existsSync32, readFileSync as readFileSync31 } from "fs";
|
|
4331
4499
|
import { resolve as resolve6 } from "path";
|
|
4332
4500
|
|
|
4333
4501
|
// src/coldstart/countCaptures.ts
|
|
@@ -4338,11 +4506,11 @@ function countExistingCaptures(repoRoot) {
|
|
|
4338
4506
|
// src/init/prompts.ts
|
|
4339
4507
|
function isInitialized(targetDir) {
|
|
4340
4508
|
const configPath = memoryPath(targetDir, "config.json");
|
|
4341
|
-
if (!
|
|
4509
|
+
if (!existsSync32(configPath)) {
|
|
4342
4510
|
return false;
|
|
4343
4511
|
}
|
|
4344
4512
|
try {
|
|
4345
|
-
const config = JSON.parse(
|
|
4513
|
+
const config = JSON.parse(readFileSync31(configPath, "utf8"));
|
|
4346
4514
|
return config.version === 1;
|
|
4347
4515
|
} catch {
|
|
4348
4516
|
return false;
|
|
@@ -4441,7 +4609,7 @@ async function promptLlmFields(defaults = {}, options = {}) {
|
|
|
4441
4609
|
}
|
|
4442
4610
|
async function gatherLlmInitInput(targetDir) {
|
|
4443
4611
|
const llmPath = memoryPath(targetDir, "llm.json");
|
|
4444
|
-
if (
|
|
4612
|
+
if (existsSync32(llmPath)) {
|
|
4445
4613
|
const overwrite = await confirm({
|
|
4446
4614
|
message: "\u68C0\u6D4B\u5230\u5DF2\u6709 .memory/llm.json\uFF0C\u662F\u5426\u8986\u76D6\u5E76\u91CD\u65B0\u914D\u7F6E\uFF1F",
|
|
4447
4615
|
default: false
|
|
@@ -4472,17 +4640,17 @@ async function gatherLlmInitInput(targetDir) {
|
|
|
4472
4640
|
}
|
|
4473
4641
|
|
|
4474
4642
|
// src/init/writeScaffoldFile.ts
|
|
4475
|
-
import { copyFileSync, writeFileSync as
|
|
4643
|
+
import { copyFileSync, writeFileSync as writeFileSync22 } from "fs";
|
|
4476
4644
|
|
|
4477
4645
|
// src/init/mergeConfig.ts
|
|
4478
|
-
import { existsSync as
|
|
4646
|
+
import { existsSync as existsSync33, readFileSync as readFileSync32 } from "fs";
|
|
4479
4647
|
function mergeConfigForInit(repoRoot, assistants) {
|
|
4480
4648
|
const configPath = memoryPath(repoRoot, "config.json");
|
|
4481
|
-
const existed =
|
|
4649
|
+
const existed = existsSync33(configPath);
|
|
4482
4650
|
let existing = {};
|
|
4483
4651
|
if (existed) {
|
|
4484
4652
|
try {
|
|
4485
|
-
existing = JSON.parse(
|
|
4653
|
+
existing = JSON.parse(readFileSync32(configPath, "utf8"));
|
|
4486
4654
|
} catch {
|
|
4487
4655
|
existing = {};
|
|
4488
4656
|
}
|
|
@@ -4506,8 +4674,8 @@ function mergeConfigForInit(repoRoot, assistants) {
|
|
|
4506
4674
|
}
|
|
4507
4675
|
|
|
4508
4676
|
// src/init/mergeAgentsMd.ts
|
|
4509
|
-
import { existsSync as
|
|
4510
|
-
import { join as
|
|
4677
|
+
import { existsSync as existsSync34, readFileSync as readFileSync33, writeFileSync as writeFileSync19 } from "fs";
|
|
4678
|
+
import { join as join37 } from "path";
|
|
4511
4679
|
var HERMES_AGENTS_START_MARKER = "<!-- >>> hermes-repo agents (do not edit this block manually) -->";
|
|
4512
4680
|
var HERMES_AGENTS_END_MARKER = "<!-- <<< hermes-repo agents -->";
|
|
4513
4681
|
function buildHermesAgentsBlockBody() {
|
|
@@ -4544,7 +4712,7 @@ function withGapBeforeHermesBlock(prefix) {
|
|
|
4544
4712
|
}
|
|
4545
4713
|
return `${trimmed}${GAP_BEFORE_HERMES_BLOCK}`;
|
|
4546
4714
|
}
|
|
4547
|
-
function
|
|
4715
|
+
function spliceHermesBlock2(existing, block) {
|
|
4548
4716
|
const startIdx = existing.indexOf(HERMES_AGENTS_START_MARKER);
|
|
4549
4717
|
const endIdx = existing.indexOf(HERMES_AGENTS_END_MARKER);
|
|
4550
4718
|
if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {
|
|
@@ -4563,32 +4731,32 @@ function spliceHermesBlock(existing, block) {
|
|
|
4563
4731
|
`;
|
|
4564
4732
|
}
|
|
4565
4733
|
function mergeAgentsMd(repoRoot, force) {
|
|
4566
|
-
const agentsPath =
|
|
4734
|
+
const agentsPath = join37(repoRoot, "AGENTS.md");
|
|
4567
4735
|
const block = buildHermesAgentsMarkedBlock();
|
|
4568
|
-
if (!
|
|
4569
|
-
|
|
4736
|
+
if (!existsSync34(agentsPath)) {
|
|
4737
|
+
writeFileSync19(agentsPath, buildNewAgentsMd(), "utf8");
|
|
4570
4738
|
return "created";
|
|
4571
4739
|
}
|
|
4572
|
-
const content =
|
|
4740
|
+
const content = readFileSync33(agentsPath, "utf8");
|
|
4573
4741
|
if (agentsMdHasHermesBlock(content)) {
|
|
4574
4742
|
if (!force) {
|
|
4575
4743
|
return "skipped";
|
|
4576
4744
|
}
|
|
4577
|
-
|
|
4745
|
+
writeFileSync19(agentsPath, spliceHermesBlock2(content, block), "utf8");
|
|
4578
4746
|
return "replaced";
|
|
4579
4747
|
}
|
|
4580
4748
|
if (agentsMdHasLegacyHermesContent(content)) {
|
|
4581
4749
|
return "skipped";
|
|
4582
4750
|
}
|
|
4583
|
-
|
|
4751
|
+
writeFileSync19(agentsPath, spliceHermesBlock2(content, block), "utf8");
|
|
4584
4752
|
return "appended";
|
|
4585
4753
|
}
|
|
4586
4754
|
|
|
4587
4755
|
// src/init/mergeLlmConfig.ts
|
|
4588
|
-
import { existsSync as
|
|
4756
|
+
import { existsSync as existsSync35, writeFileSync as writeFileSync20 } from "fs";
|
|
4589
4757
|
function mergeLlmConfigForInit(repoRoot, input2) {
|
|
4590
4758
|
const llmPath = memoryPath(repoRoot, "llm.json");
|
|
4591
|
-
const existed =
|
|
4759
|
+
const existed = existsSync35(llmPath);
|
|
4592
4760
|
let existing = {};
|
|
4593
4761
|
if (existed) {
|
|
4594
4762
|
try {
|
|
@@ -4617,14 +4785,14 @@ function mergeLlmConfigForInit(repoRoot, input2) {
|
|
|
4617
4785
|
}
|
|
4618
4786
|
function writeLlmJson(repoRoot, input2) {
|
|
4619
4787
|
const { content, action } = mergeLlmConfigForInit(repoRoot, input2);
|
|
4620
|
-
|
|
4788
|
+
writeFileSync20(memoryPath(repoRoot, "llm.json"), content, "utf8");
|
|
4621
4789
|
return action;
|
|
4622
4790
|
}
|
|
4623
4791
|
|
|
4624
4792
|
// src/init/scaffoldWrite.ts
|
|
4625
|
-
import { existsSync as
|
|
4793
|
+
import { existsSync as existsSync36, writeFileSync as writeFileSync21 } from "fs";
|
|
4626
4794
|
function shouldWriteFile(absolutePath, force) {
|
|
4627
|
-
if (!
|
|
4795
|
+
if (!existsSync36(absolutePath)) {
|
|
4628
4796
|
return { write: true, action: "created" };
|
|
4629
4797
|
}
|
|
4630
4798
|
if (force) {
|
|
@@ -4638,7 +4806,7 @@ function writeIfAllowed(report, absolutePath, relativePath, content, force) {
|
|
|
4638
4806
|
report.files.push({ path: relativePath, action });
|
|
4639
4807
|
return;
|
|
4640
4808
|
}
|
|
4641
|
-
|
|
4809
|
+
writeFileSync21(absolutePath, content, "utf8");
|
|
4642
4810
|
report.files.push({ path: relativePath, action });
|
|
4643
4811
|
}
|
|
4644
4812
|
|
|
@@ -4646,7 +4814,7 @@ function writeIfAllowed(report, absolutePath, relativePath, content, force) {
|
|
|
4646
4814
|
function writeConfigJson(report, repoRoot, assistants) {
|
|
4647
4815
|
const { content, action } = mergeConfigForInit(repoRoot, assistants);
|
|
4648
4816
|
const absolutePath = memoryPath(repoRoot, "config.json");
|
|
4649
|
-
|
|
4817
|
+
writeFileSync22(absolutePath, content, "utf8");
|
|
4650
4818
|
report.files.push({ path: ".memory/config.json", action });
|
|
4651
4819
|
}
|
|
4652
4820
|
function copyTemplateIfAllowed(report, templateName, destAbsolute, relativePath, force) {
|
|
@@ -4933,10 +5101,10 @@ async function runInitCommand(opts) {
|
|
|
4933
5101
|
}
|
|
4934
5102
|
|
|
4935
5103
|
// src/feedback/writeRef.ts
|
|
4936
|
-
import { existsSync as
|
|
4937
|
-
import { join as
|
|
5104
|
+
import { existsSync as existsSync37, mkdirSync as mkdirSync13, writeFileSync as writeFileSync23 } from "fs";
|
|
5105
|
+
import { join as join38 } from "path";
|
|
4938
5106
|
function targetExists(repoRoot, target) {
|
|
4939
|
-
return
|
|
5107
|
+
return existsSync37(join38(repoRoot, ".memory", target));
|
|
4940
5108
|
}
|
|
4941
5109
|
function writeRef(opts) {
|
|
4942
5110
|
const { repoRoot, reason, session } = opts;
|
|
@@ -4958,16 +5126,16 @@ function writeRef(opts) {
|
|
|
4958
5126
|
date,
|
|
4959
5127
|
...session ? { session } : {}
|
|
4960
5128
|
};
|
|
4961
|
-
|
|
5129
|
+
mkdirSync13(refsDir(repoRoot), { recursive: true });
|
|
4962
5130
|
const filePath = refFilePath(repoRoot, target, date);
|
|
4963
5131
|
const base = filePath.replace(/\.json$/, "");
|
|
4964
5132
|
let finalPath = `${filePath}`;
|
|
4965
5133
|
let n = 0;
|
|
4966
|
-
while (
|
|
5134
|
+
while (existsSync37(finalPath)) {
|
|
4967
5135
|
n++;
|
|
4968
5136
|
finalPath = `${base}-${n}.json`;
|
|
4969
5137
|
}
|
|
4970
|
-
|
|
5138
|
+
writeFileSync23(finalPath, `${JSON.stringify(record, null, 2)}
|
|
4971
5139
|
`, "utf8");
|
|
4972
5140
|
const file = finalPath.split("/refs/").pop() ?? finalPath;
|
|
4973
5141
|
return { target, file: `refs/${file}` };
|
|
@@ -5007,15 +5175,15 @@ function runRefCommand(opts) {
|
|
|
5007
5175
|
}
|
|
5008
5176
|
|
|
5009
5177
|
// src/search/runSearch.ts
|
|
5010
|
-
import { existsSync as
|
|
5011
|
-
import { join as
|
|
5178
|
+
import { existsSync as existsSync38, readdirSync as readdirSync11, readFileSync as readFileSync35 } from "fs";
|
|
5179
|
+
import { join as join39 } from "path";
|
|
5012
5180
|
function walkMdFiles(dir) {
|
|
5013
|
-
if (!
|
|
5181
|
+
if (!existsSync38(dir)) {
|
|
5014
5182
|
return [];
|
|
5015
5183
|
}
|
|
5016
5184
|
const out = [];
|
|
5017
5185
|
for (const name of readdirSync11(dir)) {
|
|
5018
|
-
const full =
|
|
5186
|
+
const full = join39(dir, name);
|
|
5019
5187
|
if (name.endsWith(".md")) {
|
|
5020
5188
|
out.push(full);
|
|
5021
5189
|
} else if (!name.startsWith(".")) {
|
|
@@ -5024,7 +5192,7 @@ function walkMdFiles(dir) {
|
|
|
5024
5192
|
if (Array.isArray(st)) {
|
|
5025
5193
|
for (const child of readdirSync11(full)) {
|
|
5026
5194
|
if (child.endsWith(".md")) {
|
|
5027
|
-
out.push(
|
|
5195
|
+
out.push(join39(full, child));
|
|
5028
5196
|
}
|
|
5029
5197
|
}
|
|
5030
5198
|
}
|
|
@@ -5036,13 +5204,13 @@ function walkMdFiles(dir) {
|
|
|
5036
5204
|
}
|
|
5037
5205
|
function listSkillMd(repoRoot) {
|
|
5038
5206
|
const skillsDir = memoryPath(repoRoot, "skills");
|
|
5039
|
-
if (!
|
|
5207
|
+
if (!existsSync38(skillsDir)) {
|
|
5040
5208
|
return [];
|
|
5041
5209
|
}
|
|
5042
5210
|
const out = [];
|
|
5043
5211
|
for (const slug of readdirSync11(skillsDir)) {
|
|
5044
|
-
const f =
|
|
5045
|
-
if (
|
|
5212
|
+
const f = join39(skillsDir, slug, "SKILL.md");
|
|
5213
|
+
if (existsSync38(f)) {
|
|
5046
5214
|
out.push(f);
|
|
5047
5215
|
}
|
|
5048
5216
|
}
|
|
@@ -5050,7 +5218,7 @@ function listSkillMd(repoRoot) {
|
|
|
5050
5218
|
}
|
|
5051
5219
|
function summaryFromFile(absPath, relFromMemory) {
|
|
5052
5220
|
try {
|
|
5053
|
-
const content =
|
|
5221
|
+
const content = readFileSync35(absPath, "utf8");
|
|
5054
5222
|
if (relFromMemory.startsWith("captures/")) {
|
|
5055
5223
|
const parsed = parseCaptureMarkdown(content, relFromMemory, absPath);
|
|
5056
5224
|
if (parsed) {
|
|
@@ -5073,10 +5241,10 @@ function runSearch(opts) {
|
|
|
5073
5241
|
const memRoot = memoryPath(opts.repoRoot);
|
|
5074
5242
|
const captureTypes = opts.type ? [opts.type] : ["semantic", "episodic", "procedural"];
|
|
5075
5243
|
for (const t of captureTypes) {
|
|
5076
|
-
const dir =
|
|
5244
|
+
const dir = join39(memRoot, "captures", t);
|
|
5077
5245
|
for (const abs of walkMdFiles(dir)) {
|
|
5078
5246
|
const rel = abs.replace(memRoot + "/", "").replace(/\\/g, "/");
|
|
5079
|
-
const content =
|
|
5247
|
+
const content = readFileSync35(abs, "utf8").toLowerCase();
|
|
5080
5248
|
if (content.includes(kw)) {
|
|
5081
5249
|
hits.push({
|
|
5082
5250
|
path: rel,
|
|
@@ -5088,10 +5256,10 @@ function runSearch(opts) {
|
|
|
5088
5256
|
}
|
|
5089
5257
|
}
|
|
5090
5258
|
}
|
|
5091
|
-
const topicsDir =
|
|
5259
|
+
const topicsDir = join39(memRoot, "topics");
|
|
5092
5260
|
for (const abs of walkMdFiles(topicsDir)) {
|
|
5093
5261
|
const rel = abs.replace(memRoot + "/", "").replace(/\\/g, "/");
|
|
5094
|
-
if (
|
|
5262
|
+
if (readFileSync35(abs, "utf8").toLowerCase().includes(kw)) {
|
|
5095
5263
|
hits.push({ path: rel, summary: summaryFromFile(abs, rel) });
|
|
5096
5264
|
}
|
|
5097
5265
|
if (hits.length >= limit) {
|
|
@@ -5100,7 +5268,7 @@ function runSearch(opts) {
|
|
|
5100
5268
|
}
|
|
5101
5269
|
for (const abs of listSkillMd(opts.repoRoot)) {
|
|
5102
5270
|
const rel = abs.replace(memRoot + "/", "").replace(/\\/g, "/");
|
|
5103
|
-
if (
|
|
5271
|
+
if (readFileSync35(abs, "utf8").toLowerCase().includes(kw)) {
|
|
5104
5272
|
hits.push({ path: rel, summary: summaryFromFile(abs, rel) });
|
|
5105
5273
|
}
|
|
5106
5274
|
if (hits.length >= limit) {
|
|
@@ -5153,7 +5321,7 @@ function runSearchCommand(opts) {
|
|
|
5153
5321
|
}
|
|
5154
5322
|
|
|
5155
5323
|
// src/stats/runStats.ts
|
|
5156
|
-
import { existsSync as
|
|
5324
|
+
import { existsSync as existsSync39, statSync as statSync6 } from "fs";
|
|
5157
5325
|
function collectStats(repoRoot, nowMs = Date.now()) {
|
|
5158
5326
|
const all = listAllCaptures(repoRoot);
|
|
5159
5327
|
const active = filterActiveCaptures(all);
|
|
@@ -5183,7 +5351,7 @@ function collectStats(repoRoot, nowMs = Date.now()) {
|
|
|
5183
5351
|
}
|
|
5184
5352
|
}
|
|
5185
5353
|
const memoryFile = memoryPath(repoRoot, "MEMORY.md");
|
|
5186
|
-
const memoryBytes =
|
|
5354
|
+
const memoryBytes = existsSync39(memoryFile) ? statSync6(memoryFile).size : 0;
|
|
5187
5355
|
const allSkills = listSkillIndex(repoRoot);
|
|
5188
5356
|
const skillUsage = readSkillUsage(repoRoot);
|
|
5189
5357
|
const inMemory = filterSkillIndexForMemory(allSkills, skillUsage, repoRoot, nowMs);
|
|
@@ -5250,11 +5418,11 @@ function runStatsCommand(opts) {
|
|
|
5250
5418
|
}
|
|
5251
5419
|
|
|
5252
5420
|
// src/promote/runPromote.ts
|
|
5253
|
-
import { existsSync as
|
|
5421
|
+
import { existsSync as existsSync45, mkdirSync as mkdirSync15, writeFileSync as writeFileSync25 } from "fs";
|
|
5254
5422
|
import { resolve as resolve8 } from "path";
|
|
5255
5423
|
|
|
5256
5424
|
// src/promote/buildTopicDraft.ts
|
|
5257
|
-
import { existsSync as
|
|
5425
|
+
import { existsSync as existsSync40, readFileSync as readFileSync36 } from "fs";
|
|
5258
5426
|
function ruleTopicDraftBody(tag, captures, existing) {
|
|
5259
5427
|
const lines = captures.map(
|
|
5260
5428
|
(c) => `- [${c.date}] [${c.type}] ${c.summary.slice(0, 120)} (${c.path}) [\u664B\u5347\u5019\u9009]`
|
|
@@ -5281,9 +5449,9 @@ async function buildTopicDraftBody(repoRoot, captures, llm) {
|
|
|
5281
5449
|
const slug = tagToSlug(tag);
|
|
5282
5450
|
const abs = memoryPath(repoRoot, "topics", `${slug}.md`);
|
|
5283
5451
|
let existing = "";
|
|
5284
|
-
if (
|
|
5452
|
+
if (existsSync40(abs)) {
|
|
5285
5453
|
try {
|
|
5286
|
-
existing =
|
|
5454
|
+
existing = readFileSync36(abs, "utf8");
|
|
5287
5455
|
} catch {
|
|
5288
5456
|
existing = "";
|
|
5289
5457
|
}
|
|
@@ -5308,7 +5476,7 @@ ${c.findings.slice(0, 300)}`
|
|
|
5308
5476
|
}
|
|
5309
5477
|
|
|
5310
5478
|
// src/promote/detectTopicConflict.ts
|
|
5311
|
-
import { existsSync as
|
|
5479
|
+
import { existsSync as existsSync41, readFileSync as readFileSync37 } from "fs";
|
|
5312
5480
|
var MUTEX_PAIRS2 = [
|
|
5313
5481
|
["localstorage", "httponly"],
|
|
5314
5482
|
["local storage", "httponly"],
|
|
@@ -5338,12 +5506,12 @@ function detectTopicConflict(repoRoot, capture) {
|
|
|
5338
5506
|
const slug = tagToSlug(tag);
|
|
5339
5507
|
const topicPath = memoryPath(repoRoot, "topics", `${slug}.md`);
|
|
5340
5508
|
const relTopic = `topics/${slug}.md`;
|
|
5341
|
-
if (!
|
|
5509
|
+
if (!existsSync41(topicPath)) {
|
|
5342
5510
|
return { hasConflict: false, reason: "none" };
|
|
5343
5511
|
}
|
|
5344
5512
|
let existing = "";
|
|
5345
5513
|
try {
|
|
5346
|
-
existing =
|
|
5514
|
+
existing = readFileSync37(topicPath, "utf8");
|
|
5347
5515
|
} catch {
|
|
5348
5516
|
return { hasConflict: false, reason: "none" };
|
|
5349
5517
|
}
|
|
@@ -5509,17 +5677,17 @@ async function buildMergedStagingDrafts(repoRoot, analyses, llm) {
|
|
|
5509
5677
|
|
|
5510
5678
|
// src/promote/applyDecisions.ts
|
|
5511
5679
|
import {
|
|
5512
|
-
existsSync as
|
|
5513
|
-
mkdirSync as
|
|
5680
|
+
existsSync as existsSync42,
|
|
5681
|
+
mkdirSync as mkdirSync14,
|
|
5514
5682
|
readdirSync as readdirSync12,
|
|
5515
|
-
readFileSync as
|
|
5683
|
+
readFileSync as readFileSync38,
|
|
5516
5684
|
unlinkSync as unlinkSync3,
|
|
5517
|
-
writeFileSync as
|
|
5685
|
+
writeFileSync as writeFileSync24
|
|
5518
5686
|
} from "fs";
|
|
5519
|
-
import { join as
|
|
5687
|
+
import { join as join41 } from "path";
|
|
5520
5688
|
|
|
5521
5689
|
// src/promote/paths.ts
|
|
5522
|
-
import { join as
|
|
5690
|
+
import { join as join40 } from "path";
|
|
5523
5691
|
function promoteDir(repoRoot) {
|
|
5524
5692
|
return memoryPath(repoRoot, "promote");
|
|
5525
5693
|
}
|
|
@@ -5537,7 +5705,7 @@ function resolvePromoteTemplatePath(repoRoot) {
|
|
|
5537
5705
|
return inMemory;
|
|
5538
5706
|
}
|
|
5539
5707
|
function stagingTopicPath(repoRoot, slug) {
|
|
5540
|
-
return
|
|
5708
|
+
return join40(promoteStagingTopicsDir(repoRoot), `${slug}.md`);
|
|
5541
5709
|
}
|
|
5542
5710
|
|
|
5543
5711
|
// src/promote/applyDecisions.ts
|
|
@@ -5573,17 +5741,17 @@ function buildManifestTemplate(capturePaths, dateIso) {
|
|
|
5573
5741
|
}
|
|
5574
5742
|
function removePromoteMarker(repoRoot, capturePath) {
|
|
5575
5743
|
const marker = promoteMarkerPath(repoRoot, capturePath);
|
|
5576
|
-
if (
|
|
5744
|
+
if (existsSync42(marker)) {
|
|
5577
5745
|
unlinkSync3(marker);
|
|
5578
5746
|
}
|
|
5579
5747
|
}
|
|
5580
5748
|
function annotateReject(repoRoot, capturePath, note) {
|
|
5581
|
-
const abs =
|
|
5582
|
-
if (!
|
|
5749
|
+
const abs = join41(repoRoot, ".memory", capturePath);
|
|
5750
|
+
if (!existsSync42(abs)) {
|
|
5583
5751
|
return;
|
|
5584
5752
|
}
|
|
5585
5753
|
try {
|
|
5586
|
-
let content =
|
|
5754
|
+
let content = readFileSync38(abs, "utf8");
|
|
5587
5755
|
const fields = {
|
|
5588
5756
|
promote_rejected_at: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10)
|
|
5589
5757
|
};
|
|
@@ -5591,22 +5759,22 @@ function annotateReject(repoRoot, capturePath, note) {
|
|
|
5591
5759
|
fields.promote_note = note.trim().slice(0, 200);
|
|
5592
5760
|
}
|
|
5593
5761
|
content = setFrontmatterScalars(content, fields);
|
|
5594
|
-
|
|
5762
|
+
writeFileSync24(abs, content, "utf8");
|
|
5595
5763
|
} catch {
|
|
5596
5764
|
}
|
|
5597
5765
|
}
|
|
5598
5766
|
function mergeTopicFromStaging(repoRoot, slug, dryRun) {
|
|
5599
5767
|
const staging = stagingTopicPath(repoRoot, slug);
|
|
5600
|
-
if (!
|
|
5768
|
+
if (!existsSync42(staging)) {
|
|
5601
5769
|
return null;
|
|
5602
5770
|
}
|
|
5603
|
-
const draft =
|
|
5771
|
+
const draft = readFileSync38(staging, "utf8");
|
|
5604
5772
|
const dest = memoryPath(repoRoot, "topics", `${slug}.md`);
|
|
5605
5773
|
if (dryRun) {
|
|
5606
5774
|
return `topics/${slug}.md`;
|
|
5607
5775
|
}
|
|
5608
|
-
|
|
5609
|
-
|
|
5776
|
+
mkdirSync14(memoryPath(repoRoot, "topics"), { recursive: true });
|
|
5777
|
+
writeFileSync24(dest, draft.endsWith("\n") ? draft : `${draft}
|
|
5610
5778
|
`, "utf8");
|
|
5611
5779
|
return `topics/${slug}.md`;
|
|
5612
5780
|
}
|
|
@@ -5642,7 +5810,7 @@ function applyDecisions(repoRoot, manifest, opts) {
|
|
|
5642
5810
|
}
|
|
5643
5811
|
if (slugsToMerge.size === 0 && result.approved.length > 0) {
|
|
5644
5812
|
const stagingDir = promoteStagingTopicsDir(repoRoot);
|
|
5645
|
-
if (
|
|
5813
|
+
if (existsSync42(stagingDir)) {
|
|
5646
5814
|
for (const name of readdirSync12(stagingDir)) {
|
|
5647
5815
|
if (name.endsWith(".md")) {
|
|
5648
5816
|
slugsToMerge.add(name.replace(/\.md$/, ""));
|
|
@@ -5659,21 +5827,21 @@ function applyDecisions(repoRoot, manifest, opts) {
|
|
|
5659
5827
|
return result;
|
|
5660
5828
|
}
|
|
5661
5829
|
function readManifestFile(manifestPath) {
|
|
5662
|
-
const raw =
|
|
5830
|
+
const raw = readFileSync38(manifestPath, "utf8");
|
|
5663
5831
|
return parseManifestJson(raw);
|
|
5664
5832
|
}
|
|
5665
5833
|
function writeManifestTemplate(repoRoot, capturePaths) {
|
|
5666
5834
|
const dateIso = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
5667
5835
|
const manifest = buildManifestTemplate(capturePaths, dateIso);
|
|
5668
5836
|
const path = decisionsTemplatePath(repoRoot);
|
|
5669
|
-
|
|
5670
|
-
|
|
5837
|
+
mkdirSync14(memoryPath(repoRoot, "promote"), { recursive: true });
|
|
5838
|
+
writeFileSync24(path, `${JSON.stringify(manifest, null, 2)}
|
|
5671
5839
|
`, "utf8");
|
|
5672
5840
|
return path;
|
|
5673
5841
|
}
|
|
5674
5842
|
|
|
5675
5843
|
// src/promote/buildPrBody.ts
|
|
5676
|
-
import { existsSync as
|
|
5844
|
+
import { existsSync as existsSync43, readFileSync as readFileSync39 } from "fs";
|
|
5677
5845
|
function actionLabel(action) {
|
|
5678
5846
|
if (action === "approve") {
|
|
5679
5847
|
return "\u6279\u51C6\u664B\u5347";
|
|
@@ -5713,9 +5881,9 @@ ${conflictLine}
|
|
|
5713
5881
|
}
|
|
5714
5882
|
function loadTemplate(repoRoot) {
|
|
5715
5883
|
const path = resolvePromoteTemplatePath(repoRoot);
|
|
5716
|
-
if (
|
|
5884
|
+
if (existsSync43(path)) {
|
|
5717
5885
|
try {
|
|
5718
|
-
return
|
|
5886
|
+
return readFileSync39(path, "utf8");
|
|
5719
5887
|
} catch {
|
|
5720
5888
|
}
|
|
5721
5889
|
}
|
|
@@ -5763,7 +5931,7 @@ ${items}
|
|
|
5763
5931
|
}
|
|
5764
5932
|
|
|
5765
5933
|
// src/promote/listPromoteCandidates.ts
|
|
5766
|
-
import { existsSync as
|
|
5934
|
+
import { existsSync as existsSync44, readdirSync as readdirSync13 } from "fs";
|
|
5767
5935
|
var TYPES2 = ["semantic", "episodic", "procedural"];
|
|
5768
5936
|
function normalizeCapturePath(input2) {
|
|
5769
5937
|
let p = input2.replace(/\\/g, "/").trim();
|
|
@@ -5780,7 +5948,7 @@ function listPromoteCandidates(repoRoot, filterPaths) {
|
|
|
5780
5948
|
const results = [];
|
|
5781
5949
|
for (const type of TYPES2) {
|
|
5782
5950
|
const dir = memoryPath(repoRoot, "captures", type);
|
|
5783
|
-
if (!
|
|
5951
|
+
if (!existsSync44(dir)) {
|
|
5784
5952
|
continue;
|
|
5785
5953
|
}
|
|
5786
5954
|
for (const name of readdirSync13(dir)) {
|
|
@@ -5807,8 +5975,8 @@ function listPromoteCandidates(repoRoot, filterPaths) {
|
|
|
5807
5975
|
|
|
5808
5976
|
// src/promote/runPromote.ts
|
|
5809
5977
|
function ensurePromoteDirs(repoRoot) {
|
|
5810
|
-
|
|
5811
|
-
|
|
5978
|
+
mkdirSync15(promoteDir(repoRoot), { recursive: true });
|
|
5979
|
+
mkdirSync15(promoteStagingTopicsDir(repoRoot), { recursive: true });
|
|
5812
5980
|
}
|
|
5813
5981
|
function formatPreviewTable(analyses) {
|
|
5814
5982
|
const lines = [
|
|
@@ -5852,15 +6020,15 @@ async function runPromote(opts) {
|
|
|
5852
6020
|
const stagingTopicPaths = [];
|
|
5853
6021
|
for (const [slug, body] of mergedDrafts) {
|
|
5854
6022
|
const path = stagingTopicPath(repoRoot, slug);
|
|
5855
|
-
|
|
6023
|
+
writeFileSync25(path, body, "utf8");
|
|
5856
6024
|
stagingTopicPaths.push(
|
|
5857
6025
|
`.memory/promote/staging/topics/${slug}.md`
|
|
5858
6026
|
);
|
|
5859
6027
|
}
|
|
5860
6028
|
const prBody = buildPrBody(repoRoot, analyses);
|
|
5861
6029
|
const prBodyPath = opts.outPath ? resolve8(opts.outPath) : defaultPrBodyPath(repoRoot, dateIso);
|
|
5862
|
-
|
|
5863
|
-
|
|
6030
|
+
mkdirSync15(memoryPath(repoRoot, "promote"), { recursive: true });
|
|
6031
|
+
writeFileSync25(prBodyPath, prBody, "utf8");
|
|
5864
6032
|
const manifestTemplatePath = writeManifestTemplate(
|
|
5865
6033
|
repoRoot,
|
|
5866
6034
|
candidates.map((c) => c.path)
|