coding-friend-cli 1.7.0 → 1.9.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/README.md +4 -0
- package/dist/chunk-7N64TDZ6.js +277 -0
- package/dist/{chunk-HSQX3PKW.js → chunk-BPLN4LDL.js} +3 -5
- package/dist/chunk-FYHHNX7K.js +35 -0
- package/dist/{chunk-MRTR7TJ4.js → chunk-HFLBFX6J.js} +2 -4
- package/dist/{chunk-WXBI2HUL.js → chunk-PGLUEN7D.js} +0 -1
- package/dist/chunk-QQ5SVZET.js +135 -0
- package/dist/chunk-RZRT7NGT.js +18 -0
- package/dist/{chunk-WHCJT7E2.js → chunk-TPRZHSFS.js} +38 -1
- package/dist/{chunk-DHPWBSF5.js → chunk-VYMXERKM.js} +18 -14
- package/dist/{chunk-6DUFTBTO.js → chunk-W5CD7WTX.js} +1 -0
- package/dist/config-VAML7F7K.js +567 -0
- package/dist/{dev-QW6VPG4G.js → dev-2GBY3GKC.js} +9 -11
- package/dist/{host-EERZVOHY.js → host-LOG5RPZ7.js} +7 -6
- package/dist/index.js +36 -13
- package/dist/init-CIEDOFNC.js +512 -0
- package/dist/{install-ORIDNWRW.js → install-D4NW3OAA.js} +7 -8
- package/dist/{mcp-3MUUQZQD.js → mcp-ORMYETXQ.js} +7 -6
- package/dist/postinstall.js +2 -2
- package/dist/session-NCQQJRSH.js +222 -0
- package/dist/{statusline-BWGI5PQ5.js → statusline-5HWRTSVL.js} +4 -5
- package/dist/{uninstall-KOAJFPD6.js → uninstall-SOHU5WGK.js} +8 -10
- package/dist/update-LA4B3LN4.js +16 -0
- package/package.json +1 -1
- package/dist/chunk-4PLV2ENL.js +0 -144
- package/dist/chunk-IUTXHCP7.js +0 -28
- package/dist/chunk-WK5YYHXM.js +0 -44
- package/dist/init-5BJVESH7.js +0 -529
- package/dist/json-2XS56OJY.js +0 -10
- package/dist/update-GW37S23M.js +0 -17
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import {
|
|
2
|
+
loadConfig
|
|
3
|
+
} from "./chunk-FYHHNX7K.js";
|
|
4
|
+
import "./chunk-PGLUEN7D.js";
|
|
5
|
+
import {
|
|
6
|
+
claudeSessionDir,
|
|
7
|
+
encodeProjectPath,
|
|
8
|
+
readJson,
|
|
9
|
+
writeJson
|
|
10
|
+
} from "./chunk-TPRZHSFS.js";
|
|
11
|
+
import {
|
|
12
|
+
log
|
|
13
|
+
} from "./chunk-W5CD7WTX.js";
|
|
14
|
+
|
|
15
|
+
// src/commands/session.ts
|
|
16
|
+
import { input, select } from "@inquirer/prompts";
|
|
17
|
+
import { existsSync as existsSync2, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
18
|
+
import { homedir as homedir2 } from "os";
|
|
19
|
+
import { join as join2, basename } from "path";
|
|
20
|
+
|
|
21
|
+
// src/lib/session.ts
|
|
22
|
+
import {
|
|
23
|
+
readdirSync,
|
|
24
|
+
statSync,
|
|
25
|
+
copyFileSync,
|
|
26
|
+
existsSync,
|
|
27
|
+
readFileSync
|
|
28
|
+
} from "fs";
|
|
29
|
+
import { join } from "path";
|
|
30
|
+
import { hostname as osHostname } from "os";
|
|
31
|
+
function buildPreviewText(jsonlPath, maxChars = 200) {
|
|
32
|
+
try {
|
|
33
|
+
const content = readFileSync(jsonlPath, "utf-8");
|
|
34
|
+
const lines = content.split("\n").filter(Boolean);
|
|
35
|
+
for (const line of lines) {
|
|
36
|
+
try {
|
|
37
|
+
const entry = JSON.parse(line);
|
|
38
|
+
if (entry.type === "user") {
|
|
39
|
+
const msg = entry.message;
|
|
40
|
+
const msgContent = msg?.content;
|
|
41
|
+
if (typeof msgContent === "string" && msgContent.trim()) {
|
|
42
|
+
return msgContent.trim().slice(0, maxChars);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
} catch {
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return "(preview unavailable)";
|
|
49
|
+
} catch {
|
|
50
|
+
return "(preview unavailable)";
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function listSyncedSessions(syncDir) {
|
|
54
|
+
const sessionsDir = join(syncDir, "sessions");
|
|
55
|
+
if (!existsSync(sessionsDir)) return [];
|
|
56
|
+
const entries = readdirSync(sessionsDir).filter((entry) => {
|
|
57
|
+
const entryPath = join(sessionsDir, entry);
|
|
58
|
+
return statSync(entryPath).isDirectory();
|
|
59
|
+
});
|
|
60
|
+
const metas = [];
|
|
61
|
+
for (const entry of entries) {
|
|
62
|
+
const metaPath = join(sessionsDir, entry, "meta.json");
|
|
63
|
+
const meta = readJson(metaPath);
|
|
64
|
+
if (meta) metas.push(meta);
|
|
65
|
+
}
|
|
66
|
+
return metas.sort(
|
|
67
|
+
(a, b) => new Date(b.savedAt).getTime() - new Date(a.savedAt).getTime()
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
function remapProjectPath(originalPath, currentHome) {
|
|
71
|
+
const homePattern = /^(\/(?:Users|home)\/[^/]+)(\/.*)?$/;
|
|
72
|
+
const match = originalPath.match(homePattern);
|
|
73
|
+
if (!match) return originalPath;
|
|
74
|
+
const origHomeDir = match[1];
|
|
75
|
+
const rest = match[2] ?? "";
|
|
76
|
+
if (origHomeDir === currentHome) return originalPath;
|
|
77
|
+
return currentHome + rest;
|
|
78
|
+
}
|
|
79
|
+
function saveSession(opts) {
|
|
80
|
+
const { jsonlPath, sessionId, label, projectPath, syncDir, previewText } = opts;
|
|
81
|
+
const destDir = join(syncDir, "sessions", sessionId);
|
|
82
|
+
const destJsonl = join(destDir, "session.jsonl");
|
|
83
|
+
const destMeta = join(destDir, "meta.json");
|
|
84
|
+
copyFileSync(jsonlPath, destJsonl);
|
|
85
|
+
const meta = {
|
|
86
|
+
sessionId,
|
|
87
|
+
label,
|
|
88
|
+
projectPath,
|
|
89
|
+
savedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
90
|
+
machine: hostname(),
|
|
91
|
+
previewText
|
|
92
|
+
};
|
|
93
|
+
writeJson(destMeta, meta);
|
|
94
|
+
}
|
|
95
|
+
function loadSession(meta, localProjectPath, syncDir) {
|
|
96
|
+
const encodedPath = encodeProjectPath(localProjectPath);
|
|
97
|
+
const destDir = claudeSessionDir(encodedPath);
|
|
98
|
+
const destPath = join(destDir, `${meta.sessionId}.jsonl`);
|
|
99
|
+
const srcPath = join(syncDir, "sessions", meta.sessionId, "session.jsonl");
|
|
100
|
+
copyFileSync(srcPath, destPath);
|
|
101
|
+
}
|
|
102
|
+
function hostname() {
|
|
103
|
+
try {
|
|
104
|
+
return osHostname();
|
|
105
|
+
} catch {
|
|
106
|
+
return "unknown";
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// src/commands/session.ts
|
|
111
|
+
function resolveDocsDir() {
|
|
112
|
+
const config = loadConfig();
|
|
113
|
+
const docsDir = config.docsDir ?? "docs";
|
|
114
|
+
return join2(process.cwd(), docsDir);
|
|
115
|
+
}
|
|
116
|
+
function formatSessionChoice(meta) {
|
|
117
|
+
const date = new Date(meta.savedAt).toLocaleString();
|
|
118
|
+
const preview = meta.previewText.slice(0, 60).replace(/\n/g, " ");
|
|
119
|
+
return `[${meta.label}] ${date} @${meta.machine} \u2014 ${preview}`;
|
|
120
|
+
}
|
|
121
|
+
async function sessionSaveCommand(opts = {}) {
|
|
122
|
+
const docsDir = resolveDocsDir();
|
|
123
|
+
const cwd = process.cwd();
|
|
124
|
+
let jsonlPath = null;
|
|
125
|
+
if (opts.sessionId) {
|
|
126
|
+
const candidate = join2(
|
|
127
|
+
claudeSessionDir(encodeProjectPath(cwd)),
|
|
128
|
+
`${opts.sessionId}.jsonl`
|
|
129
|
+
);
|
|
130
|
+
if (!existsSync2(candidate)) {
|
|
131
|
+
log.error(`Session file not found: ${candidate}`);
|
|
132
|
+
process.exit(1);
|
|
133
|
+
}
|
|
134
|
+
jsonlPath = candidate;
|
|
135
|
+
} else {
|
|
136
|
+
const sessionDir = claudeSessionDir(encodeProjectPath(cwd));
|
|
137
|
+
if (!existsSync2(sessionDir)) {
|
|
138
|
+
log.error(
|
|
139
|
+
`No sessions found for current directory. Run this inside a project that has Claude Code sessions.`
|
|
140
|
+
);
|
|
141
|
+
process.exit(1);
|
|
142
|
+
}
|
|
143
|
+
const files = readdirSync2(sessionDir).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-")).map((f) => ({ name: f, mtime: statSync2(join2(sessionDir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
|
|
144
|
+
if (files.length === 0) {
|
|
145
|
+
log.error("No session files found in current project directory.");
|
|
146
|
+
process.exit(1);
|
|
147
|
+
}
|
|
148
|
+
const recentThreshold = 6e4;
|
|
149
|
+
const recentFiles = files.filter(
|
|
150
|
+
(f) => files[0].mtime - f.mtime < recentThreshold
|
|
151
|
+
);
|
|
152
|
+
if (recentFiles.length > 1) {
|
|
153
|
+
const chosen = await select({
|
|
154
|
+
message: "Multiple recent sessions found. Which one to save?",
|
|
155
|
+
choices: recentFiles.map((f) => ({
|
|
156
|
+
name: `${f.name} (modified ${new Date(f.mtime).toLocaleString()})`,
|
|
157
|
+
value: join2(sessionDir, f.name)
|
|
158
|
+
}))
|
|
159
|
+
});
|
|
160
|
+
jsonlPath = chosen;
|
|
161
|
+
} else {
|
|
162
|
+
jsonlPath = join2(sessionDir, files[0].name);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
const sessionId = basename(jsonlPath, ".jsonl");
|
|
166
|
+
const label = opts.label ?? await input({
|
|
167
|
+
message: "Give this session a label:",
|
|
168
|
+
default: `session-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`
|
|
169
|
+
});
|
|
170
|
+
const previewText = buildPreviewText(jsonlPath);
|
|
171
|
+
saveSession({
|
|
172
|
+
jsonlPath,
|
|
173
|
+
sessionId,
|
|
174
|
+
label,
|
|
175
|
+
projectPath: cwd,
|
|
176
|
+
syncDir: docsDir,
|
|
177
|
+
previewText
|
|
178
|
+
});
|
|
179
|
+
log.success(`Session saved: "${label}"`);
|
|
180
|
+
log.dim(` \u2192 ${join2(docsDir, "sessions", sessionId)}`);
|
|
181
|
+
}
|
|
182
|
+
async function sessionLoadCommand() {
|
|
183
|
+
const docsDir = resolveDocsDir();
|
|
184
|
+
const sessions = listSyncedSessions(docsDir);
|
|
185
|
+
if (sessions.length === 0) {
|
|
186
|
+
log.warn("No saved sessions found.");
|
|
187
|
+
log.dim(` Sessions dir: ${join2(docsDir, "sessions")}`);
|
|
188
|
+
log.dim(" Run /cf-session inside a Claude Code conversation to save one.");
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
const chosen = await select({
|
|
192
|
+
message: "Choose a session to load:",
|
|
193
|
+
choices: sessions.map((s) => ({
|
|
194
|
+
name: formatSessionChoice(s),
|
|
195
|
+
value: s
|
|
196
|
+
}))
|
|
197
|
+
});
|
|
198
|
+
const currentHome = homedir2();
|
|
199
|
+
const remapped = remapProjectPath(chosen.projectPath, currentHome);
|
|
200
|
+
let localProjectPath = remapped;
|
|
201
|
+
if (remapped !== chosen.projectPath) {
|
|
202
|
+
log.step(
|
|
203
|
+
`Original path: ${chosen.projectPath}
|
|
204
|
+
Remapped to: ${remapped}`
|
|
205
|
+
);
|
|
206
|
+
const confirmed = await input({
|
|
207
|
+
message: "Local project path (press Enter to accept or edit):",
|
|
208
|
+
default: remapped
|
|
209
|
+
});
|
|
210
|
+
localProjectPath = confirmed.trim() || remapped;
|
|
211
|
+
}
|
|
212
|
+
loadSession(chosen, localProjectPath, docsDir);
|
|
213
|
+
log.success(`Session "${chosen.label}" loaded.`);
|
|
214
|
+
log.info(`To resume, run:`);
|
|
215
|
+
console.log(`
|
|
216
|
+
claude --resume ${chosen.sessionId}
|
|
217
|
+
`);
|
|
218
|
+
}
|
|
219
|
+
export {
|
|
220
|
+
sessionLoadCommand,
|
|
221
|
+
sessionSaveCommand
|
|
222
|
+
};
|
|
@@ -4,15 +4,14 @@ import {
|
|
|
4
4
|
saveStatuslineConfig,
|
|
5
5
|
selectStatuslineComponents,
|
|
6
6
|
writeStatuslineSettings
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-BPLN4LDL.js";
|
|
8
8
|
import {
|
|
9
9
|
ALL_COMPONENT_IDS
|
|
10
|
-
} from "./chunk-
|
|
11
|
-
import "./chunk-
|
|
10
|
+
} from "./chunk-PGLUEN7D.js";
|
|
11
|
+
import "./chunk-TPRZHSFS.js";
|
|
12
12
|
import {
|
|
13
13
|
log
|
|
14
|
-
} from "./chunk-
|
|
15
|
-
import "./chunk-IUTXHCP7.js";
|
|
14
|
+
} from "./chunk-W5CD7WTX.js";
|
|
16
15
|
|
|
17
16
|
// src/commands/statusline.ts
|
|
18
17
|
import { confirm } from "@inquirer/prompts";
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
isMarketplaceRegistered,
|
|
3
3
|
isPluginInstalled
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-HFLBFX6J.js";
|
|
5
5
|
import {
|
|
6
6
|
hasShellCompletion,
|
|
7
7
|
removeShellCompletion
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-7N64TDZ6.js";
|
|
9
9
|
import {
|
|
10
10
|
commandExists,
|
|
11
11
|
run
|
|
@@ -15,15 +15,13 @@ import {
|
|
|
15
15
|
devStatePath,
|
|
16
16
|
globalConfigDir,
|
|
17
17
|
marketplaceCachePath,
|
|
18
|
-
marketplaceClonePath
|
|
19
|
-
} from "./chunk-WHCJT7E2.js";
|
|
20
|
-
import {
|
|
21
|
-
log
|
|
22
|
-
} from "./chunk-6DUFTBTO.js";
|
|
23
|
-
import {
|
|
18
|
+
marketplaceClonePath,
|
|
24
19
|
readJson,
|
|
25
20
|
writeJson
|
|
26
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-TPRZHSFS.js";
|
|
22
|
+
import {
|
|
23
|
+
log
|
|
24
|
+
} from "./chunk-W5CD7WTX.js";
|
|
27
25
|
|
|
28
26
|
// src/commands/uninstall.ts
|
|
29
27
|
import { existsSync, rmSync } from "fs";
|
|
@@ -80,7 +78,7 @@ function nothingToRemove(d) {
|
|
|
80
78
|
}
|
|
81
79
|
async function uninstallCommand() {
|
|
82
80
|
console.log(`
|
|
83
|
-
=== ${chalk.red("Coding Friend Uninstall")} ===`);
|
|
81
|
+
=== \u{1F44B} ${chalk.red("Coding Friend Uninstall")} \u{1F44B} ===`);
|
|
84
82
|
if (!commandExists("claude")) {
|
|
85
83
|
log.error("Claude CLI not found. Cannot uninstall plugin without it.");
|
|
86
84
|
log.dim(
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getLatestVersion,
|
|
3
|
+
semverCompare,
|
|
4
|
+
updateCommand
|
|
5
|
+
} from "./chunk-VYMXERKM.js";
|
|
6
|
+
import "./chunk-BPLN4LDL.js";
|
|
7
|
+
import "./chunk-7N64TDZ6.js";
|
|
8
|
+
import "./chunk-PGLUEN7D.js";
|
|
9
|
+
import "./chunk-UFGNO6CW.js";
|
|
10
|
+
import "./chunk-TPRZHSFS.js";
|
|
11
|
+
import "./chunk-W5CD7WTX.js";
|
|
12
|
+
export {
|
|
13
|
+
getLatestVersion,
|
|
14
|
+
semverCompare,
|
|
15
|
+
updateCommand
|
|
16
|
+
};
|
package/package.json
CHANGED
package/dist/chunk-4PLV2ENL.js
DELETED
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
log
|
|
3
|
-
} from "./chunk-6DUFTBTO.js";
|
|
4
|
-
|
|
5
|
-
// src/lib/shell-completion.ts
|
|
6
|
-
import { appendFileSync, existsSync, readFileSync, writeFileSync } from "fs";
|
|
7
|
-
import { homedir } from "os";
|
|
8
|
-
var MARKER_START = "# >>> coding-friend CLI completion >>>";
|
|
9
|
-
var MARKER_END = "# <<< coding-friend CLI completion <<<";
|
|
10
|
-
var BASH_BLOCK = `
|
|
11
|
-
|
|
12
|
-
${MARKER_START}
|
|
13
|
-
_cf_completions() {
|
|
14
|
-
local cur="\${COMP_WORDS[COMP_CWORD]}"
|
|
15
|
-
local prev="\${COMP_WORDS[COMP_CWORD-1]}"
|
|
16
|
-
local commands="install uninstall init host mcp statusline update dev"
|
|
17
|
-
|
|
18
|
-
# Subcommands for 'dev'
|
|
19
|
-
if [[ "\${COMP_WORDS[1]}" == "dev" && \${COMP_CWORD} -eq 2 ]]; then
|
|
20
|
-
COMPREPLY=($(compgen -W "on off status restart sync update" -- "$cur"))
|
|
21
|
-
return
|
|
22
|
-
fi
|
|
23
|
-
|
|
24
|
-
# Path completion for 'dev on|restart|update'
|
|
25
|
-
if [[ "\${COMP_WORDS[1]}" == "dev" && ("$prev" == "on" || "$prev" == "restart" || "$prev" == "update") ]]; then
|
|
26
|
-
COMPREPLY=($(compgen -d -- "$cur"))
|
|
27
|
-
return
|
|
28
|
-
fi
|
|
29
|
-
|
|
30
|
-
COMPREPLY=($(compgen -W "$commands" -- "$cur"))
|
|
31
|
-
}
|
|
32
|
-
complete -o default -F _cf_completions cf
|
|
33
|
-
${MARKER_END}
|
|
34
|
-
`;
|
|
35
|
-
var ZSH_BLOCK = `
|
|
36
|
-
|
|
37
|
-
${MARKER_START}
|
|
38
|
-
_cf() {
|
|
39
|
-
local -a commands
|
|
40
|
-
commands=(
|
|
41
|
-
'install:Install the Coding Friend plugin into Claude Code'
|
|
42
|
-
'uninstall:Uninstall the Coding Friend plugin from Claude Code'
|
|
43
|
-
'init:Initialize coding-friend in current project'
|
|
44
|
-
'host:Build and serve learning docs as a static website'
|
|
45
|
-
'mcp:Setup MCP server for learning docs'
|
|
46
|
-
'statusline:Setup coding-friend statusline in Claude Code'
|
|
47
|
-
'update:Update coding-friend plugin and refresh statusline'
|
|
48
|
-
'dev:Switch between local and remote plugin for development'
|
|
49
|
-
)
|
|
50
|
-
|
|
51
|
-
if (( CURRENT == 2 )); then
|
|
52
|
-
_describe 'command' commands
|
|
53
|
-
elif (( CURRENT == 3 )) && [[ "\${words[2]}" == "dev" ]]; then
|
|
54
|
-
local -a subcommands
|
|
55
|
-
subcommands=(
|
|
56
|
-
'on:Switch to local plugin source'
|
|
57
|
-
'off:Switch back to remote marketplace'
|
|
58
|
-
'status:Show current dev mode'
|
|
59
|
-
'restart:Restart dev mode (re-apply local plugin)'
|
|
60
|
-
'sync:Sync local plugin files without restarting'
|
|
61
|
-
'update:Update local dev plugin to latest version'
|
|
62
|
-
)
|
|
63
|
-
_describe 'subcommand' subcommands
|
|
64
|
-
elif (( CURRENT == 4 )) && [[ "\${words[2]}" == "dev" && ("\${words[3]}" == "on" || "\${words[3]}" == "restart" || "\${words[3]}" == "update") ]]; then
|
|
65
|
-
_path_files -/
|
|
66
|
-
fi
|
|
67
|
-
}
|
|
68
|
-
compdef _cf cf
|
|
69
|
-
${MARKER_END}
|
|
70
|
-
`;
|
|
71
|
-
function getShellRcPath() {
|
|
72
|
-
const shell = process.env.SHELL ?? "";
|
|
73
|
-
if (shell.includes("zsh")) return `${homedir()}/.zshrc`;
|
|
74
|
-
return `${homedir()}/.bashrc`;
|
|
75
|
-
}
|
|
76
|
-
function getRcName(rcPath) {
|
|
77
|
-
return rcPath.endsWith(".zshrc") ? ".zshrc" : ".bashrc";
|
|
78
|
-
}
|
|
79
|
-
function isZsh(rcPath) {
|
|
80
|
-
return rcPath.endsWith(".zshrc");
|
|
81
|
-
}
|
|
82
|
-
function hasShellCompletion() {
|
|
83
|
-
const rcPath = getShellRcPath();
|
|
84
|
-
if (!existsSync(rcPath)) return false;
|
|
85
|
-
return readFileSync(rcPath, "utf-8").includes(MARKER_START);
|
|
86
|
-
}
|
|
87
|
-
function extractExistingBlock(content) {
|
|
88
|
-
const startIdx = content.indexOf(MARKER_START);
|
|
89
|
-
const endIdx = content.indexOf(MARKER_END);
|
|
90
|
-
if (startIdx === -1 || endIdx === -1) return null;
|
|
91
|
-
return content.slice(startIdx, endIdx + MARKER_END.length);
|
|
92
|
-
}
|
|
93
|
-
function replaceBlock(content, newBlock) {
|
|
94
|
-
const startIdx = content.indexOf(MARKER_START);
|
|
95
|
-
const endIdx = content.indexOf(MARKER_END);
|
|
96
|
-
let sliceStart = startIdx;
|
|
97
|
-
while (sliceStart > 0 && content[sliceStart - 1] === "\n") sliceStart--;
|
|
98
|
-
return content.slice(0, sliceStart) + newBlock + content.slice(endIdx + MARKER_END.length);
|
|
99
|
-
}
|
|
100
|
-
function removeShellCompletion() {
|
|
101
|
-
const rcPath = getShellRcPath();
|
|
102
|
-
if (!existsSync(rcPath)) return false;
|
|
103
|
-
const content = readFileSync(rcPath, "utf-8");
|
|
104
|
-
if (!content.includes(MARKER_START)) return false;
|
|
105
|
-
const updated = replaceBlock(content, "");
|
|
106
|
-
writeFileSync(rcPath, updated, "utf-8");
|
|
107
|
-
const rcName = getRcName(rcPath);
|
|
108
|
-
log.success(`Tab completion removed from ~/${rcName}`);
|
|
109
|
-
return true;
|
|
110
|
-
}
|
|
111
|
-
function ensureShellCompletion(opts) {
|
|
112
|
-
const rcPath = getShellRcPath();
|
|
113
|
-
const rcName = getRcName(rcPath);
|
|
114
|
-
const newBlock = isZsh(rcPath) ? ZSH_BLOCK : BASH_BLOCK;
|
|
115
|
-
if (hasShellCompletion()) {
|
|
116
|
-
const content = readFileSync(rcPath, "utf-8");
|
|
117
|
-
const existing = extractExistingBlock(content);
|
|
118
|
-
const expectedBlock = newBlock.trim();
|
|
119
|
-
if (existing && existing.trim() === expectedBlock) {
|
|
120
|
-
if (!opts?.silent)
|
|
121
|
-
log.dim(`Tab completion already up-to-date in ~/${rcName}`);
|
|
122
|
-
return false;
|
|
123
|
-
}
|
|
124
|
-
const updated = replaceBlock(content, newBlock);
|
|
125
|
-
writeFileSync(rcPath, updated, "utf-8");
|
|
126
|
-
if (!opts?.silent) {
|
|
127
|
-
log.success(`Tab completion updated in ~/${rcName}`);
|
|
128
|
-
log.dim(`Run \`source ~/${rcName}\` or open a new terminal to activate.`);
|
|
129
|
-
}
|
|
130
|
-
return true;
|
|
131
|
-
}
|
|
132
|
-
appendFileSync(rcPath, newBlock);
|
|
133
|
-
if (!opts?.silent) {
|
|
134
|
-
log.success(`Tab completion added to ~/${rcName}`);
|
|
135
|
-
log.dim(`Run \`source ~/${rcName}\` or open a new terminal to activate.`);
|
|
136
|
-
}
|
|
137
|
-
return true;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
export {
|
|
141
|
-
hasShellCompletion,
|
|
142
|
-
removeShellCompletion,
|
|
143
|
-
ensureShellCompletion
|
|
144
|
-
};
|
package/dist/chunk-IUTXHCP7.js
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
// src/lib/json.ts
|
|
2
|
-
import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
|
|
3
|
-
import { dirname } from "path";
|
|
4
|
-
function readJson(filePath) {
|
|
5
|
-
try {
|
|
6
|
-
const content = readFileSync(filePath, "utf-8");
|
|
7
|
-
return JSON.parse(content);
|
|
8
|
-
} catch {
|
|
9
|
-
return null;
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
function writeJson(filePath, data) {
|
|
13
|
-
const dir = dirname(filePath);
|
|
14
|
-
if (!existsSync(dir)) {
|
|
15
|
-
mkdirSync(dir, { recursive: true });
|
|
16
|
-
}
|
|
17
|
-
writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
18
|
-
}
|
|
19
|
-
function mergeJson(filePath, data) {
|
|
20
|
-
const existing = readJson(filePath) ?? {};
|
|
21
|
-
writeJson(filePath, { ...existing, ...data });
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export {
|
|
25
|
-
readJson,
|
|
26
|
-
writeJson,
|
|
27
|
-
mergeJson
|
|
28
|
-
};
|
package/dist/chunk-WK5YYHXM.js
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
globalConfigPath,
|
|
3
|
-
localConfigPath,
|
|
4
|
-
resolvePath
|
|
5
|
-
} from "./chunk-WHCJT7E2.js";
|
|
6
|
-
import {
|
|
7
|
-
readJson
|
|
8
|
-
} from "./chunk-IUTXHCP7.js";
|
|
9
|
-
|
|
10
|
-
// src/lib/config.ts
|
|
11
|
-
function resolveDocsDir(explicitPath) {
|
|
12
|
-
if (explicitPath) {
|
|
13
|
-
return resolvePath(explicitPath);
|
|
14
|
-
}
|
|
15
|
-
const local = readJson(localConfigPath());
|
|
16
|
-
if (local?.learn?.outputDir) {
|
|
17
|
-
return resolvePath(local.learn.outputDir);
|
|
18
|
-
}
|
|
19
|
-
const global = readJson(globalConfigPath());
|
|
20
|
-
if (global?.learn?.outputDir) {
|
|
21
|
-
return resolvePath(global.learn.outputDir);
|
|
22
|
-
}
|
|
23
|
-
return resolvePath("docs/learn");
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// src/lib/lib-path.ts
|
|
27
|
-
import { existsSync } from "fs";
|
|
28
|
-
import { dirname, join } from "path";
|
|
29
|
-
import { fileURLToPath } from "url";
|
|
30
|
-
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
31
|
-
function getLibPath(name) {
|
|
32
|
-
const bundled = join(__dirname, "..", "lib", name);
|
|
33
|
-
if (existsSync(bundled)) return bundled;
|
|
34
|
-
const dev = join(__dirname, "..", "..", "lib", name);
|
|
35
|
-
if (existsSync(dev)) return dev;
|
|
36
|
-
throw new Error(
|
|
37
|
-
`Could not find lib/${name}. Ensure it exists in the CLI package.`
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export {
|
|
42
|
-
resolveDocsDir,
|
|
43
|
-
getLibPath
|
|
44
|
-
};
|