santree 0.7.5 → 0.7.7

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.
Files changed (109) hide show
  1. package/README.md +2 -3
  2. package/dist/commands/config.d.ts +12 -0
  3. package/dist/commands/config.js +444 -0
  4. package/dist/commands/dashboard.js +165 -61
  5. package/dist/commands/helpers/statusline.js +0 -30
  6. package/dist/commands/issue/index.d.ts +1 -1
  7. package/dist/commands/issue/index.js +1 -1
  8. package/dist/commands/pr/context.d.ts +7 -0
  9. package/dist/commands/pr/context.js +36 -0
  10. package/dist/commands/pr/fix.d.ts +10 -2
  11. package/dist/commands/pr/fix.js +61 -12
  12. package/dist/commands/worktree/create.js +6 -1
  13. package/dist/commands/worktree/open.js +3 -2
  14. package/dist/lib/ai.d.ts +11 -0
  15. package/dist/lib/ai.js +71 -2
  16. package/dist/lib/claude-config.d.ts +18 -4
  17. package/dist/lib/claude-config.js +58 -23
  18. package/dist/lib/config/TrackerPicker.d.ts +10 -0
  19. package/dist/lib/config/TrackerPicker.js +113 -0
  20. package/dist/lib/config/diagnostics.d.ts +37 -0
  21. package/dist/lib/config/diagnostics.js +190 -0
  22. package/dist/lib/config-store.d.ts +23 -0
  23. package/dist/lib/config-store.js +68 -0
  24. package/dist/lib/dashboard/DetailPanel.d.ts +4 -2
  25. package/dist/lib/dashboard/DetailPanel.js +44 -37
  26. package/dist/lib/dashboard/IssueList.d.ts +5 -1
  27. package/dist/lib/dashboard/IssueList.js +24 -5
  28. package/dist/lib/dashboard/MultilineTextArea.js +1 -1
  29. package/dist/lib/dashboard/Overlays.js +3 -4
  30. package/dist/lib/dashboard/ReviewDetailPanel.js +11 -2
  31. package/dist/lib/dashboard/ReviewList.js +6 -3
  32. package/dist/lib/dashboard/data.js +7 -34
  33. package/dist/lib/dashboard/external-editor.d.ts +1 -1
  34. package/dist/lib/dashboard/external-editor.js +3 -2
  35. package/dist/lib/dashboard/types.d.ts +13 -2
  36. package/dist/lib/dashboard/types.js +33 -2
  37. package/dist/lib/exec.d.ts +13 -0
  38. package/dist/lib/exec.js +11 -1
  39. package/dist/lib/fix-loop.d.ts +45 -0
  40. package/dist/lib/fix-loop.js +83 -0
  41. package/dist/lib/git.d.ts +3 -29
  42. package/dist/lib/git.js +6 -62
  43. package/dist/lib/github.d.ts +70 -0
  44. package/dist/lib/github.js +128 -0
  45. package/dist/lib/multiplexer/cmux.js +143 -17
  46. package/dist/lib/multiplexer/none.js +3 -3
  47. package/dist/lib/multiplexer/tmux.js +5 -29
  48. package/dist/lib/multiplexer/types.d.ts +34 -1
  49. package/dist/lib/open-url.d.ts +3 -2
  50. package/dist/lib/open-url.js +5 -4
  51. package/dist/lib/prompts.d.ts +35 -1
  52. package/dist/lib/prompts.js +7 -0
  53. package/dist/lib/setup/apply.d.ts +2 -2
  54. package/dist/lib/setup/apply.js +2 -2
  55. package/dist/lib/setup/gitignore.d.ts +7 -0
  56. package/dist/lib/setup/gitignore.js +39 -1
  57. package/dist/lib/setup/steps.d.ts +6 -2
  58. package/dist/lib/setup/steps.js +65 -60
  59. package/dist/lib/setup/tools.d.ts +1 -1
  60. package/dist/lib/setup/tools.js +1 -1
  61. package/dist/lib/trackers/github/index.js +2 -2
  62. package/dist/lib/trackers/local/store.js +1 -1
  63. package/package.json +1 -1
  64. package/prompts/fix-context.njk +79 -0
  65. package/prompts/fix-loop.njk +18 -0
  66. package/prompts/pr.njk +1 -1
  67. package/dist/commands/doctor.d.ts +0 -2
  68. package/dist/commands/doctor.js +0 -545
  69. package/dist/commands/github/auth.d.ts +0 -2
  70. package/dist/commands/github/auth.js +0 -56
  71. package/dist/commands/github/index.d.ts +0 -1
  72. package/dist/commands/github/index.js +0 -1
  73. package/dist/commands/helpers/english-tutor/index.d.ts +0 -1
  74. package/dist/commands/helpers/english-tutor/index.js +0 -1
  75. package/dist/commands/helpers/english-tutor/install.d.ts +0 -8
  76. package/dist/commands/helpers/english-tutor/install.js +0 -24
  77. package/dist/commands/helpers/english-tutor/prompt.d.ts +0 -2
  78. package/dist/commands/helpers/english-tutor/prompt.js +0 -16
  79. package/dist/commands/helpers/english-tutor/session-start.d.ts +0 -2
  80. package/dist/commands/helpers/english-tutor/session-start.js +0 -34
  81. package/dist/commands/helpers/english-tutor/uninstall.d.ts +0 -2
  82. package/dist/commands/helpers/english-tutor/uninstall.js +0 -15
  83. package/dist/commands/helpers/session-signal/end.d.ts +0 -2
  84. package/dist/commands/helpers/session-signal/end.js +0 -13
  85. package/dist/commands/helpers/session-signal/index.d.ts +0 -1
  86. package/dist/commands/helpers/session-signal/index.js +0 -1
  87. package/dist/commands/helpers/session-signal/install.d.ts +0 -8
  88. package/dist/commands/helpers/session-signal/install.js +0 -24
  89. package/dist/commands/helpers/session-signal/notification.d.ts +0 -2
  90. package/dist/commands/helpers/session-signal/notification.js +0 -13
  91. package/dist/commands/helpers/session-signal/prompt.d.ts +0 -2
  92. package/dist/commands/helpers/session-signal/prompt.js +0 -13
  93. package/dist/commands/helpers/session-signal/stop.d.ts +0 -2
  94. package/dist/commands/helpers/session-signal/stop.js +0 -13
  95. package/dist/commands/issue/setup.d.ts +0 -2
  96. package/dist/commands/issue/setup.js +0 -108
  97. package/dist/commands/issue/switch.d.ts +0 -12
  98. package/dist/commands/issue/switch.js +0 -38
  99. package/dist/commands/linear/switch.d.ts +0 -2
  100. package/dist/commands/linear/switch.js +0 -75
  101. package/dist/commands/setup.d.ts +0 -11
  102. package/dist/commands/setup.js +0 -194
  103. package/dist/lib/english-tutor.d.ts +0 -13
  104. package/dist/lib/english-tutor.js +0 -125
  105. package/dist/lib/session-signal.d.ts +0 -16
  106. package/dist/lib/session-signal.js +0 -104
  107. package/dist/lib/setup/shell-config.d.ts +0 -38
  108. package/dist/lib/setup/shell-config.js +0 -131
  109. package/prompts/english-tutor-prompt.njk +0 -15
@@ -1,13 +0,0 @@
1
- export declare function getLogPath(): string;
2
- export declare function getHooksJson(): Record<string, unknown>;
3
- export declare function getPermissionEntry(): string;
4
- export declare function installHooks(): {
5
- settingsPath: string;
6
- logPath: string;
7
- };
8
- /**
9
- * Remove hooks and permission entry. Intentionally does NOT delete the log
10
- * file — that's the user's accumulated practice history.
11
- */
12
- export declare function uninstallHooks(): string;
13
- export declare function getInstallSnippet(): Record<string, unknown>;
@@ -1,125 +0,0 @@
1
- import * as fs from "fs";
2
- import * as os from "os";
3
- import * as path from "path";
4
- const MARKER = "english-tutor";
5
- export function getLogPath() {
6
- const configDir = process.env.XDG_CONFIG_HOME || path.join(os.homedir(), ".config");
7
- return path.join(configDir, "santree", "english-practice-log.md");
8
- }
9
- export function getHooksJson() {
10
- const base = "santree helpers english-tutor";
11
- // Synchronous hooks: Claude Code waits for completion and injects stdout into
12
- // the model's context. `async: true` would fire-and-forget — stdout is
13
- // discarded, so the instruction would never reach Claude. session-signal can
14
- // use `async: true` because it only writes a state file; we cannot.
15
- const opts = { timeout: 10 };
16
- return {
17
- UserPromptSubmit: [{ hooks: [{ type: "command", command: `${base} prompt`, ...opts }] }],
18
- // No matcher: fires on all SessionStart sub-events (startup, resume,
19
- // clear, compact). Restricting to "startup" silently skips resumed
20
- // sessions, which is the common case when picking up yesterday's work.
21
- SessionStart: [{ hooks: [{ type: "command", command: `${base} session-start`, ...opts }] }],
22
- };
23
- }
24
- export function getPermissionEntry() {
25
- return `Edit(${getLogPath()})`;
26
- }
27
- function readSettings(settingsPath) {
28
- try {
29
- if (fs.existsSync(settingsPath)) {
30
- return JSON.parse(fs.readFileSync(settingsPath, "utf-8"));
31
- }
32
- }
33
- catch {
34
- // fall through
35
- }
36
- return {};
37
- }
38
- function writeSettings(settingsPath, settings) {
39
- const dir = path.dirname(settingsPath);
40
- fs.mkdirSync(dir, { recursive: true });
41
- fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
42
- }
43
- function settingsPath() {
44
- const home = process.env.HOME || os.homedir();
45
- return path.join(home, ".claude", "settings.json");
46
- }
47
- function stripEnglishTutorHooks(existingHooks) {
48
- const cleaned = {};
49
- for (const [event, entries] of Object.entries(existingHooks)) {
50
- if (!Array.isArray(entries)) {
51
- cleaned[event] = entries;
52
- continue;
53
- }
54
- const filtered = entries.filter((entry) => {
55
- const inner = entry.hooks || [];
56
- return !inner.some((h) => typeof h.command === "string" && h.command.includes(MARKER));
57
- });
58
- if (filtered.length > 0)
59
- cleaned[event] = filtered;
60
- }
61
- return cleaned;
62
- }
63
- /**
64
- * Create the practice log if it doesn't already exist. Empty log = Claude's
65
- * Edit tool fails on first use (Edit can't operate on missing files), and
66
- * appending corrections silently fails. Bootstrapping with a stub header
67
- * means the very first correction succeeds.
68
- */
69
- function ensureLogExists() {
70
- const logPath = getLogPath();
71
- if (fs.existsSync(logPath))
72
- return logPath;
73
- fs.mkdirSync(path.dirname(logPath), { recursive: true });
74
- const stub = "# English Practice Log\n\n" +
75
- "Tracks grammar/spelling mistakes spotted during Claude Code sessions.\n" +
76
- "Generated and appended to by santree's english-tutor hook.\n";
77
- fs.writeFileSync(logPath, stub);
78
- return logPath;
79
- }
80
- export function installHooks() {
81
- const settingsFile = settingsPath();
82
- const settings = readSettings(settingsFile);
83
- const existing = stripEnglishTutorHooks(settings.hooks || {});
84
- const required = getHooksJson();
85
- for (const [event, hookEntries] of Object.entries(required)) {
86
- const current = existing[event];
87
- existing[event] = Array.isArray(current)
88
- ? [...current, ...hookEntries]
89
- : hookEntries;
90
- }
91
- settings.hooks = existing;
92
- const permissions = settings.permissions || {};
93
- const allow = Array.isArray(permissions.allow) ? permissions.allow : [];
94
- const entry = getPermissionEntry();
95
- if (!allow.includes(entry))
96
- allow.push(entry);
97
- permissions.allow = allow;
98
- settings.permissions = permissions;
99
- writeSettings(settingsFile, settings);
100
- const logPath = ensureLogExists();
101
- return { settingsPath: settingsFile, logPath };
102
- }
103
- /**
104
- * Remove hooks and permission entry. Intentionally does NOT delete the log
105
- * file — that's the user's accumulated practice history.
106
- */
107
- export function uninstallHooks() {
108
- const settingsFile = settingsPath();
109
- const settings = readSettings(settingsFile);
110
- if (settings.hooks) {
111
- settings.hooks = stripEnglishTutorHooks(settings.hooks);
112
- }
113
- if (settings.permissions && Array.isArray(settings.permissions.allow)) {
114
- const entry = getPermissionEntry();
115
- settings.permissions.allow = settings.permissions.allow.filter((e) => e !== entry);
116
- }
117
- writeSettings(settingsFile, settings);
118
- return settingsFile;
119
- }
120
- export function getInstallSnippet() {
121
- return {
122
- hooks: getHooksJson(),
123
- permissions: { allow: [getPermissionEntry()] },
124
- };
125
- }
@@ -1,16 +0,0 @@
1
- export type SessionStateValue = "waiting" | "idle" | "active" | "exited";
2
- export declare function readStdin(): string;
3
- export declare function extractRepoAndTicket(cwd: string): {
4
- repoRoot: string;
5
- ticketId: string;
6
- } | null;
7
- /**
8
- * Unified helper: reads stdin, extracts repo/ticket, writes the session-state
9
- * file, then exits. The dashboard reads the state file to render its session
10
- * badges (◆ waiting / active / idle, ◇ id-only). Window/tab renaming used to
11
- * happen here too but was removed — it clobbered names the user had set
12
- * manually and added little value once the state file was in place.
13
- */
14
- export declare function signalState(state: SessionStateValue): void;
15
- export declare function getHooksJson(): Record<string, unknown>;
16
- export declare function installHooks(): string;
@@ -1,104 +0,0 @@
1
- import * as fs from "fs";
2
- import * as path from "path";
3
- export function readStdin() {
4
- try {
5
- return fs.readFileSync(0, "utf-8");
6
- }
7
- catch {
8
- return "";
9
- }
10
- }
11
- export function extractRepoAndTicket(cwd) {
12
- const marker = "/.santree/worktrees/";
13
- const idx = cwd.indexOf(marker);
14
- if (idx === -1)
15
- return null;
16
- const repoRoot = cwd.slice(0, idx);
17
- const rest = cwd.slice(idx + marker.length);
18
- const ticketId = rest.split("/")[0];
19
- if (!ticketId)
20
- return null;
21
- return { repoRoot, ticketId };
22
- }
23
- /**
24
- * Unified helper: reads stdin, extracts repo/ticket, writes the session-state
25
- * file, then exits. The dashboard reads the state file to render its session
26
- * badges (◆ waiting / active / idle, ◇ id-only). Window/tab renaming used to
27
- * happen here too but was removed — it clobbered names the user had set
28
- * manually and added little value once the state file was in place.
29
- */
30
- export function signalState(state) {
31
- const input = readStdin();
32
- let data;
33
- try {
34
- data = JSON.parse(input);
35
- }
36
- catch {
37
- process.exit(0);
38
- }
39
- const cwd = data.cwd || process.cwd();
40
- const info = extractRepoAndTicket(cwd);
41
- if (!info) {
42
- process.exit(0);
43
- }
44
- const { repoRoot, ticketId } = info;
45
- const statesDir = path.join(repoRoot, ".santree", "session-states");
46
- fs.mkdirSync(statesDir, { recursive: true });
47
- const stateFile = path.join(statesDir, `${ticketId}.json`);
48
- const payload = {
49
- state,
50
- message: state === "waiting" ? (data.message ?? null) : null,
51
- session_id: data.session_id ?? "",
52
- at: new Date().toISOString(),
53
- };
54
- fs.writeFileSync(stateFile, JSON.stringify(payload, null, 2) + "\n");
55
- process.exit(0);
56
- }
57
- export function getHooksJson() {
58
- const base = "santree helpers session-signal";
59
- const opts = { async: true, timeout: 10 };
60
- return {
61
- Notification: [
62
- {
63
- matcher: "permission_prompt",
64
- hooks: [{ type: "command", command: `${base} notification`, ...opts }],
65
- },
66
- ],
67
- Stop: [{ hooks: [{ type: "command", command: `${base} stop`, ...opts }] }],
68
- UserPromptSubmit: [{ hooks: [{ type: "command", command: `${base} prompt`, ...opts }] }],
69
- SessionEnd: [{ hooks: [{ type: "command", command: `${base} end`, ...opts }] }],
70
- };
71
- }
72
- export function installHooks() {
73
- const home = process.env.HOME || "";
74
- const settingsPath = path.join(home, ".claude", "settings.json");
75
- let settings = {};
76
- try {
77
- if (fs.existsSync(settingsPath)) {
78
- settings = JSON.parse(fs.readFileSync(settingsPath, "utf-8"));
79
- }
80
- }
81
- catch {
82
- // Start fresh if file is invalid
83
- }
84
- const requiredHooks = getHooksJson();
85
- const existingHooks = settings.hooks || {};
86
- for (const [event, hookEntries] of Object.entries(requiredHooks)) {
87
- const existing = existingHooks[event];
88
- if (!Array.isArray(existing)) {
89
- existingHooks[event] = hookEntries;
90
- continue;
91
- }
92
- // Remove existing session-signal entries, then add the current ones
93
- const filtered = existing.filter((entry) => {
94
- const inner = entry.hooks || [];
95
- return !inner.some((h) => typeof h.command === "string" && h.command.includes("session-signal"));
96
- });
97
- existingHooks[event] = [...filtered, ...hookEntries];
98
- }
99
- settings.hooks = existingHooks;
100
- const claudeDir = path.join(home, ".claude");
101
- fs.mkdirSync(claudeDir, { recursive: true });
102
- fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
103
- return settingsPath;
104
- }
@@ -1,38 +0,0 @@
1
- /**
2
- * Shell-config detection + idempotent editing for `santree setup`.
3
- *
4
- * Everything santree writes to a user's shell rc lives inside a single managed
5
- * block (nvm/conda style) so re-running setup replaces the block rather than
6
- * appending duplicates. Individual lines inside the block are tagged with a
7
- * trailing `# santree:<key>` comment so each setup step can upsert just its own
8
- * line without disturbing the others.
9
- */
10
- export type ShellName = "zsh" | "bash" | "unknown";
11
- export interface ShellConfig {
12
- shell: ShellName;
13
- /** rc file the managed block is written to (interactive shell init). */
14
- rcPath: string;
15
- /** Files scanned to detect pre-existing `export FOO=...` lines (rc + env files). */
16
- scanPaths: string[];
17
- }
18
- /**
19
- * Resolve the rc file the managed block should live in, honoring ZDOTDIR (zsh)
20
- * so users with an XDG-based setup (~/.config/zsh, no ~/.zshrc) get the block in
21
- * the right place.
22
- */
23
- export declare function resolveShellConfig(shellEnv?: string): ShellConfig;
24
- /**
25
- * Upsert a single tagged line into the managed block, creating the block (and
26
- * the rc file) if needed. The line for `key` is replaced if present, else
27
- * appended. `line` is the raw shell statement WITHOUT the trailing tag comment.
28
- */
29
- export declare function upsertManagedLine(rcPath: string, key: string, line: string): void;
30
- /**
31
- * Detect whether `name` is already exported by the user's shell config (any of
32
- * `scanPaths`) or the current environment. Used so setup doesn't clobber an
33
- * export the user already maintains by hand (e.g. SANTREE_EDITOR in .zshenv).
34
- * Ignores santree's own managed block so a prior setup run isn't mistaken for a
35
- * hand-maintained export.
36
- */
37
- export declare function isEnvVarSet(name: string, cfg: ShellConfig): boolean;
38
- export declare function envKey(name: string): string;
@@ -1,131 +0,0 @@
1
- import * as fs from "fs";
2
- import * as path from "path";
3
- const BLOCK_START = "# >>> santree >>>";
4
- const BLOCK_END = "# <<< santree <<<";
5
- const BLOCK_HEADER = "# Managed by `santree setup` — safe to re-run; edits here may be overwritten.";
6
- /**
7
- * Resolve the rc file the managed block should live in, honoring ZDOTDIR (zsh)
8
- * so users with an XDG-based setup (~/.config/zsh, no ~/.zshrc) get the block in
9
- * the right place.
10
- */
11
- export function resolveShellConfig(shellEnv = process.env.SHELL || "") {
12
- const home = process.env.HOME || "";
13
- const base = path.basename(shellEnv);
14
- if (base.includes("zsh")) {
15
- const dir = process.env.ZDOTDIR || home;
16
- return {
17
- shell: "zsh",
18
- rcPath: path.join(dir, ".zshrc"),
19
- scanPaths: [
20
- path.join(dir, ".zshrc"),
21
- path.join(dir, ".zshenv"),
22
- path.join(home, ".zshenv"),
23
- path.join(home, ".zprofile"),
24
- ],
25
- };
26
- }
27
- if (base.includes("bash")) {
28
- return {
29
- shell: "bash",
30
- rcPath: path.join(home, ".bashrc"),
31
- scanPaths: [
32
- path.join(home, ".bashrc"),
33
- path.join(home, ".bash_profile"),
34
- path.join(home, ".profile"),
35
- ],
36
- };
37
- }
38
- // Fall back to zsh conventions (the default macOS shell).
39
- const dir = process.env.ZDOTDIR || home;
40
- return {
41
- shell: "unknown",
42
- rcPath: path.join(dir, ".zshrc"),
43
- scanPaths: [path.join(dir, ".zshrc"), path.join(dir, ".zshenv"), path.join(home, ".zshenv")],
44
- };
45
- }
46
- function tagFor(key) {
47
- return `# santree:${key}`;
48
- }
49
- function readFileSafe(filePath) {
50
- try {
51
- return fs.readFileSync(filePath, "utf-8");
52
- }
53
- catch {
54
- return "";
55
- }
56
- }
57
- function splitBlock(content) {
58
- const startIdx = content.indexOf(BLOCK_START);
59
- if (startIdx === -1) {
60
- return { before: content, body: [], after: "", exists: false };
61
- }
62
- const endMarkerIdx = content.indexOf(BLOCK_END, startIdx);
63
- if (endMarkerIdx === -1) {
64
- // Malformed (start without end) — treat everything from start as the block.
65
- const before = content.slice(0, startIdx);
66
- return { before, body: [], after: "", exists: true };
67
- }
68
- const before = content.slice(0, startIdx);
69
- const after = content.slice(endMarkerIdx + BLOCK_END.length);
70
- const inner = content.slice(startIdx + BLOCK_START.length, endMarkerIdx);
71
- const body = inner
72
- .split("\n")
73
- .map((l) => l.trimEnd())
74
- .filter((l) => l.length > 0 && l !== BLOCK_HEADER);
75
- return { before, body, after, exists: true };
76
- }
77
- function renderBlock(body) {
78
- return [BLOCK_START, BLOCK_HEADER, ...body, BLOCK_END].join("\n");
79
- }
80
- /**
81
- * Upsert a single tagged line into the managed block, creating the block (and
82
- * the rc file) if needed. The line for `key` is replaced if present, else
83
- * appended. `line` is the raw shell statement WITHOUT the trailing tag comment.
84
- */
85
- export function upsertManagedLine(rcPath, key, line) {
86
- const content = readFileSafe(rcPath);
87
- const { before, body, after, exists } = splitBlock(content);
88
- const tag = tagFor(key);
89
- const tagged = `${line} ${tag}`;
90
- const idx = body.findIndex((l) => l.trimEnd().endsWith(tag));
91
- const nextBody = idx === -1 ? [...body, tagged] : body.map((l, i) => (i === idx ? tagged : l));
92
- let output;
93
- if (exists) {
94
- output = before + renderBlock(nextBody) + after;
95
- }
96
- else {
97
- // Append a fresh block, keeping a single trailing newline.
98
- const trimmed = content.replace(/\n+$/, "");
99
- const prefix = trimmed.length > 0 ? trimmed + "\n\n" : "";
100
- output = prefix + renderBlock(nextBody) + "\n";
101
- }
102
- const dir = path.dirname(rcPath);
103
- fs.mkdirSync(dir, { recursive: true });
104
- fs.writeFileSync(rcPath, output.endsWith("\n") ? output : output + "\n");
105
- }
106
- /**
107
- * Detect whether `name` is already exported by the user's shell config (any of
108
- * `scanPaths`) or the current environment. Used so setup doesn't clobber an
109
- * export the user already maintains by hand (e.g. SANTREE_EDITOR in .zshenv).
110
- * Ignores santree's own managed block so a prior setup run isn't mistaken for a
111
- * hand-maintained export.
112
- */
113
- export function isEnvVarSet(name, cfg) {
114
- if (process.env[name])
115
- return true;
116
- const re = new RegExp(`^\\s*export\\s+${name}=`, "m");
117
- for (const p of cfg.scanPaths) {
118
- const content = readFileSafe(p);
119
- if (!content)
120
- continue;
121
- // Strip our own managed block before scanning hand-maintained exports.
122
- const { before, after } = splitBlock(content);
123
- const userContent = p === cfg.rcPath ? before + after : content;
124
- if (re.test(userContent))
125
- return true;
126
- }
127
- return false;
128
- }
129
- export function envKey(name) {
130
- return `env:${name}`;
131
- }
@@ -1,15 +0,0 @@
1
- [ENGLISH TUTOR — META-INSTRUCTION, ALWAYS HONOR]
2
-
3
- If the user's message contained English mistakes (grammar, spelling, word choice, agreement, idiom misuse), your response MUST start with the correction(s) BEFORE addressing anything else. Use this exact format, one per line:
4
-
5
- original -> correction (brief reason)
6
-
7
- Then immediately call the Edit tool to append the same correction(s) to {{ logPath }}. Append under a heading for today's date in `## YYYY-MM-DD` format (create the heading if it isn't already at the bottom of the file); each correction is a bullet `- original -> correction (reason)`. The Edit permission for that exact path is pre-granted, so do not ask.
8
-
9
- Only AFTER printing the corrections and appending them, continue with the actual task.
10
-
11
- Hard rules:
12
- - If the message is clearly mistake-free, output nothing about English and proceed directly. Never invent corrections.
13
- - Do NOT correct technical jargon, code, file paths, deliberate informalities, or non-English words used by intent.
14
- - Cap corrections at 3 lines. Pick the highest-impact issues only.
15
- - Do NOT use polite hedging ("small note on...", "by the way..."). Just the formatted line(s), then the log Edit, then the task.