claude-token-saver 3.26.1 → 3.26.2
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.en.md +14 -4
- package/README.md +14 -4
- package/bin/cli.js +16 -0
- package/package.json +1 -1
- package/src/commands/doc2md.js +38 -1
- package/src/doc2md.cjs +76 -1
package/README.en.md
CHANGED
|
@@ -382,18 +382,23 @@ Installs with nobody attached — npm `postinstall`, CI, piped stdin — skip th
|
|
|
382
382
|
|
|
383
383
|
`Read` a pptx, xlsx, pdf or docx and the raw bytes go into the context window, where the model cannot read them. This intercepts that `Read`, converts the file once, and hands over the Markdown instead.
|
|
384
384
|
|
|
385
|
+
**This is opt-in.** Installing the CLI does not turn it on: both commands below are required, and a registered hook with no converter behind it does nothing at all.
|
|
386
|
+
|
|
385
387
|
```bash
|
|
386
|
-
|
|
387
|
-
claude-token-saver doc2md on
|
|
388
|
-
claude-token-saver doc2md
|
|
388
|
+
claude-token-saver doc2md install-converter # markitdown into a dedicated venv
|
|
389
|
+
claude-token-saver doc2md on # register the Read hook
|
|
390
|
+
claude-token-saver doc2md # check converter + hook registration
|
|
391
|
+
claude-token-saver doc2md report.pptx # convert by hand and see the result
|
|
389
392
|
```
|
|
390
393
|
|
|
394
|
+
The converter goes into a venv this tool owns (`<state dir>/doc2md-venv`): no system interpreter is touched, and uninstalling the CLI takes it along. An existing markitdown on `uv tool` or `PATH` is preferred over building a new one.
|
|
395
|
+
|
|
391
396
|
Conversion is [markitdown](https://github.com/microsoft/markitdown). Slide numbers, heading levels, tables, speaker notes and per-sheet headings all survive, and non-Latin text comes through intact.
|
|
392
397
|
|
|
393
398
|
Several things it deliberately does not do:
|
|
394
399
|
|
|
395
400
|
- **Images are not converted.** markitdown returns nothing for them, and OCR misread resource names in testing (`c5.xlarge` as `c.xlarge`). In a document where those names *are* the content, wrong text is worse than none. The model reads images natively anyway.
|
|
396
|
-
- **A missing
|
|
401
|
+
- **A missing converter never fails silently.** The install command is shown once, then the original `Read` proceeds untouched. Repeating the notice on every read would be its own nuisance; saying nothing is how a broken converter hides. Run `doc2md` with no arguments to see the converter and hook registration together.
|
|
397
402
|
- **Conversions never land in your project.** They go under the tool's own state directory with mode `0700`, so there is nothing to add to `.gitignore`. Filenames matching payroll/contract/secret patterns are skipped entirely.
|
|
398
403
|
- **Zip bombs are refused.** pptx/xlsx/docx are zip containers: the declared sizes are checked first, and since those are written by whoever built the file, the real decompressed bytes are counted against a ceiling too.
|
|
399
404
|
- **Spreadsheets are capped by rows, not bytes.** Conversion time tracks row count (measured: a 6.3MB PDF in 0.9s, a 5.8MB workbook in 47.75s). Past 50,000 rows only the head is converted, and **the truncation and the true row count are both stated** in what the model is told.
|
|
@@ -497,6 +502,11 @@ Also update `statusLine.command` in `~/.claude/settings.json` to `claude-token-s
|
|
|
497
502
|
|
|
498
503
|
## Release notes
|
|
499
504
|
|
|
505
|
+
### v3.26.2 (2026-09-04)
|
|
506
|
+
- **The converter installs itself.** The old instruction was `pip install`, which asks the user to modify a system interpreter — and if they skipped it, the hook sat registered and did nothing. `doc2md install-converter` builds a dedicated venv and puts markitdown in it.
|
|
507
|
+
- **`doc2md` status now reports hook registration too.** Showing only the converter made "hook but no converter" and "converter but no hook" look identical, and both look like a broken feature.
|
|
508
|
+
- **An unknown subcommand under `--hook` prints nothing.** A 3.25.0 global install meeting a settings.json written by 3.26.0 did not recognise `doc2md`, fell through to the default report, and pushed a full statistics table into the hook stream on every `Read`.
|
|
509
|
+
|
|
500
510
|
### v3.26.0 (2026-09-04)
|
|
501
511
|
- **Attached documents are converted to Markdown before the model reads them.** Reading a pptx/xlsx/pdf/docx put unreadable bytes into the context window. `doc2md on` registers a `Read` hook that converts the file once, caches it outside your project, and points the model at the Markdown. A missing converter is announced once and then gets out of the way, zip bombs are refused, and workbooks past 50,000 rows are converted head-first with the truncation stated. See [doc2md](#-doc2md--attached-documents-become-markdown-before-the-model-reads-them).
|
|
502
512
|
- **TTL display fixed for Bedrock/Vertex sessions.** Gateways never report the per-bucket split, and the fallback assumed an hour — twelvefold too long for a 5-minute-only backend. The gateway is now detected from the model ids, the fallback follows that evidence, and the label reads `5m?` to mark it as inferred. Pin it manually with `mode ttl=5m` if the detection is wrong.
|
package/README.md
CHANGED
|
@@ -372,18 +372,23 @@ npm의 `postinstall`이나 CI처럼 사람이 붙어 있지 않은 설치에서
|
|
|
372
372
|
|
|
373
373
|
pptx·xlsx·pdf·docx 를 그대로 `Read` 하면 모델이 읽지 못하는 바이트가 컨텍스트에 그대로 올라갑니다. 이 기능은 그 `Read` 를 가로채 파일을 한 번 변환해 두고, 원본 대신 변환본을 읽게 합니다.
|
|
374
374
|
|
|
375
|
+
**이 기능은 옵트인입니다.** 설치만으로는 켜지지 않고, 아래 두 명령을 모두 실행해야 동작합니다. 훅만 등록하고 변환기가 없으면 아무 일도 일어나지 않습니다.
|
|
376
|
+
|
|
375
377
|
```bash
|
|
376
|
-
|
|
377
|
-
claude-token-saver doc2md on
|
|
378
|
-
claude-token-saver doc2md
|
|
378
|
+
claude-token-saver doc2md install-converter # 전용 venv에 markitdown 설치
|
|
379
|
+
claude-token-saver doc2md on # Read 훅 등록
|
|
380
|
+
claude-token-saver doc2md # 변환기·훅 등록 상태 확인
|
|
381
|
+
claude-token-saver doc2md 보고서.pptx # 직접 변환해 결과 확인
|
|
379
382
|
```
|
|
380
383
|
|
|
384
|
+
변환기는 도구 전용 venv(`<상태 디렉터리>/doc2md-venv`)에 설치합니다. 시스템 파이썬을 건드리지 않고, CLI를 지우면 함께 사라집니다. 이미 `uv tool` 이나 다른 경로에 markitdown 이 있으면 그쪽을 먼저 씁니다.
|
|
385
|
+
|
|
381
386
|
변환은 [markitdown](https://github.com/microsoft/markitdown)이 담당하며, 슬라이드 번호와 제목 계층, 표, 발표자 노트, 시트 구분이 모두 남습니다. 한글도 깨지지 않습니다.
|
|
382
387
|
|
|
383
388
|
몇 가지는 의도적으로 하지 않습니다.
|
|
384
389
|
|
|
385
390
|
- **이미지는 변환하지 않습니다.** markitdown 이 빈 결과를 돌려주고, OCR 은 실측에서 리소스 이름을 틀리게 읽었습니다(`c5.xlarge` 를 `c.xlarge` 로). 이름 자체가 내용인 문서에서는 텍스트가 없느니만 못합니다. 모델이 이미지는 직접 읽습니다.
|
|
386
|
-
-
|
|
391
|
+
- **변환기가 없으면 조용히 실패하지 않습니다.** 설치 명령을 한 번 안내한 뒤 원본 `Read` 를 그대로 통과시킵니다. 매번 알리면 그것대로 방해가 되고, 아무 말도 하지 않으면 고장을 숨기게 됩니다. `doc2md` 를 인자 없이 실행하면 변환기와 훅 등록 상태를 한 번에 확인할 수 있습니다.
|
|
387
392
|
- **변환본은 프로젝트 안에 남기지 않습니다.** 도구의 상태 디렉터리 아래 권한 `0700` 으로 저장하므로 `.gitignore` 에 무엇을 추가할 필요가 없습니다. 파일명이 급여·계약·개인정보 같은 패턴에 걸리면 아예 변환하지 않습니다.
|
|
388
393
|
- **압축 폭탄은 막습니다.** pptx·xlsx·docx 는 zip 컨테이너입니다. 선언된 크기를 먼저 걸러 내고, 선언은 조작될 수 있으므로 실제 해제 바이트도 상한과 대조합니다.
|
|
389
394
|
- **엑셀은 행 수로 자릅니다.** 변환 시간은 파일 크기가 아니라 행 수를 따릅니다(실측: PDF 6.3MB 0.9초, 엑셀 5.8MB 47.75초). 5만 행을 넘으면 앞부분만 변환하고, **잘랐다는 사실과 전체 행 수를 안내에 함께 적습니다.**
|
|
@@ -463,6 +468,11 @@ npm uninstall -g claude-cache-monitor && npm i -g claude-token-saver
|
|
|
463
468
|
|
|
464
469
|
## 릴리스 노트
|
|
465
470
|
|
|
471
|
+
### v3.26.2 (2026-09-04)
|
|
472
|
+
- **doc2md 변환기를 도구가 직접 설치합니다.** 지금까지의 안내는 `pip install` 이었는데, 시스템 파이썬을 건드리라는 요구인 데다 실행하지 않으면 훅만 등록된 채 아무 일도 일어나지 않았습니다. `doc2md install-converter` 가 전용 venv 를 만들어 markitdown 을 넣습니다.
|
|
473
|
+
- **`doc2md` 상태 출력에 훅 등록 여부를 함께 적습니다.** 변환기만 알려 주면 "훅은 있는데 변환기가 없다"와 "변환기는 있는데 훅이 없다"가 똑같이 아무 일도 안 하는 상태로 보여서, 어느 쪽이 빠졌는지 알 수 없었습니다.
|
|
474
|
+
- **모르는 서브커맨드를 `--hook` 으로 부르면 아무것도 출력하지 않습니다.** 3.25.0 전역 설치본이 3.26.0 이 쓴 `settings.json` 을 만나면 `doc2md` 를 인식하지 못하고 기본 리포트로 흘러가, `Read` 할 때마다 통계 표 전문을 훅 스트림에 밀어 넣었습니다.
|
|
475
|
+
|
|
466
476
|
### v3.26.0 (2026-09-04)
|
|
467
477
|
- **첨부 문서를 읽기 전에 Markdown 으로 변환합니다.** pptx·xlsx·pdf·docx 를 그대로 `Read` 하면 모델이 읽지 못하는 바이트가 컨텍스트에 올라갑니다. `doc2md on` 으로 `Read` 훅을 등록하면 파일을 한 번 변환해 캐시에 두고 변환본을 읽게 합니다. 변환기가 없으면 안내를 한 번만 하고 원본 `Read` 를 통과시키며, 압축 폭탄은 막고, 5만 행이 넘는 엑셀은 앞부분만 변환한 뒤 잘랐다는 사실을 함께 알립니다. 자세한 내용은 [doc2md](#-doc2md-첨부-문서를-읽기-전에-markdown-으로-바꿉니다) 절을 참고하십시오.
|
|
468
478
|
- **Bedrock·Vertex 경유 환경의 TTL 표시를 바로잡았습니다.** 게이트웨이는 버킷별 분해 값을 내려보내지 않는데, 판정 불가일 때 1시간을 기본값으로 잡고 있었습니다. 5분 버킷만 제공하는 환경에서 남은 시간이 최대 12배로 부풀어 보였습니다. 이제 모델 ID로 게이트웨이를 감지해 5분을 기본값으로 쓰고, 라벨을 `5m?` 로 적어 추정임을 밝힙니다. `mode ttl=5m` 으로 직접 지정할 수도 있습니다.
|
package/bin/cli.js
CHANGED
|
@@ -83,7 +83,23 @@ function readUpdateChip() {
|
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
+
// Subcommands this build knows how to run. Used only by the guard below.
|
|
87
|
+
const KNOWN_SUBCOMMANDS = new Set([
|
|
88
|
+
'last', 'brief', 'history', 'handoff', 'install', 'uninstall', 'mode', 'korean',
|
|
89
|
+
'doc2md', 'harness', 'route-scan', 'compact-window', 'update-check', 'upgrade',
|
|
90
|
+
]);
|
|
91
|
+
|
|
86
92
|
async function main() {
|
|
93
|
+
// A hook invocation names a subcommand and expects either silence or that
|
|
94
|
+
// subcommand's own protocol on stdout. If this build does not have the
|
|
95
|
+
// subcommand — an older global install against a newer settings.json, which
|
|
96
|
+
// is exactly what a mid-upgrade machine looks like — falling through to the
|
|
97
|
+
// default report would push a full table into the hook stream on every
|
|
98
|
+
// matching tool call. Say nothing instead.
|
|
99
|
+
if (hasFlag('--hook') && args[0] && !KNOWN_SUBCOMMANDS.has(args[0])) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
87
103
|
// Subcommand: last — print the most recent warning + how to handle it.
|
|
88
104
|
// Designed for the auto-trigger skill so the user immediately sees
|
|
89
105
|
// "what just fired and how to fix it" without having to read the whole
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-token-saver",
|
|
3
|
-
"version": "3.26.
|
|
3
|
+
"version": "3.26.2",
|
|
4
4
|
"description": "Route the easy work your expensive Claude model keeps repeating down to haiku/sonnet — post-hoc session analysis, no realtime router, no extra LLM calls.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/commands/doc2md.js
CHANGED
|
@@ -15,6 +15,27 @@ import { join } from 'node:path';
|
|
|
15
15
|
|
|
16
16
|
const require = createRequire(import.meta.url);
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Whether the Read hook is actually in settings.json.
|
|
20
|
+
*
|
|
21
|
+
* Status output that reports only the converter is misleading: a working
|
|
22
|
+
* converter with no hook, and a hook with no converter, both add up to
|
|
23
|
+
* "nothing happens", and the user has no way to tell which half is missing.
|
|
24
|
+
*/
|
|
25
|
+
function hookRegistered() {
|
|
26
|
+
try {
|
|
27
|
+
const { homedir } = require('node:os');
|
|
28
|
+
const settings = JSON.parse(
|
|
29
|
+
require('node:fs').readFileSync(join(homedir(), '.claude', 'settings.json'), 'utf8'),
|
|
30
|
+
);
|
|
31
|
+
return (settings?.hooks?.PreToolUse || []).some((m) =>
|
|
32
|
+
(m.hooks || []).some((h) => typeof h.command === 'string' && h.command.includes('doc2md --hook')),
|
|
33
|
+
);
|
|
34
|
+
} catch {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
18
39
|
export async function run({ args, hasFlag }) {
|
|
19
40
|
const doc2md = require('../doc2md.cjs');
|
|
20
41
|
const sub = args[1];
|
|
@@ -37,16 +58,30 @@ export async function run({ args, hasFlag }) {
|
|
|
37
58
|
return;
|
|
38
59
|
}
|
|
39
60
|
|
|
61
|
+
if (sub === 'install-converter') {
|
|
62
|
+
const res = doc2md.installConverter({ onProgress: (m) => console.log(` ${m}`) });
|
|
63
|
+
if (res.ok) {
|
|
64
|
+
console.log(`✓ converter ready: ${res.python}`);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
console.error(`✗ ${res.reason}: ${res.detail}`);
|
|
68
|
+
process.exitCode = 1;
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
40
72
|
if (sub === 'on') {
|
|
41
73
|
const { installDoc2mdHook } = await import('../installer.js');
|
|
42
74
|
const res = installDoc2mdHook();
|
|
43
75
|
console.log(res.action === 'skipped'
|
|
44
76
|
? `✗ ${res.reason}`
|
|
45
77
|
: `✓ Read hook ${res.action} (${res.path})`);
|
|
78
|
+
// A registered hook with no converter behind it does nothing at all, and
|
|
79
|
+
// says nothing about it either, which reads as a broken feature. Offer the
|
|
80
|
+
// one command that closes the gap right where the gap is visible.
|
|
46
81
|
const python = doc2md.findInterpreter();
|
|
47
82
|
console.log(python
|
|
48
83
|
? ` converter: markitdown via ${python}`
|
|
49
|
-
: ` converter:
|
|
84
|
+
: ` converter: missing — run \`${doc2md.INSTALL_HINT}\` or the hook will do nothing`);
|
|
50
85
|
return;
|
|
51
86
|
}
|
|
52
87
|
|
|
@@ -95,6 +130,8 @@ export async function run({ args, hasFlag }) {
|
|
|
95
130
|
console.log(` formats: ${doc2md.TARGET_EXTENSIONS.join(' ')}`);
|
|
96
131
|
console.log(` converter: ${python ? `markitdown via ${python}` : `not installed — ${doc2md.INSTALL_HINT}`}`);
|
|
97
132
|
console.log(` cache: ${dir} (${cached} file(s))`);
|
|
133
|
+
console.log(` hook: ${hookRegistered() ? 'registered on Read' : 'not registered'}`);
|
|
98
134
|
console.log('');
|
|
135
|
+
if (!python) console.log(`Install the converter: ${doc2md.INSTALL_HINT}`);
|
|
99
136
|
console.log('Enable with: claude-token-saver doc2md on');
|
|
100
137
|
}
|
package/src/doc2md.cjs
CHANGED
|
@@ -158,6 +158,11 @@ function findInterpreter() {
|
|
|
158
158
|
const candidates = [];
|
|
159
159
|
if (process.env.CTS_DOC2MD_PYTHON) candidates.push(process.env.CTS_DOC2MD_PYTHON);
|
|
160
160
|
candidates.push(
|
|
161
|
+
// The tool's own venv, created by `doc2md install-converter`. First
|
|
162
|
+
// because it is the only one this tool controls: telling people to
|
|
163
|
+
// `pip install` into the system interpreter is how a token-saving CLI
|
|
164
|
+
// ends up owning a break in someone else's project.
|
|
165
|
+
managedPython(),
|
|
161
166
|
path.join(os.homedir(), '.local', 'share', 'uv', 'tools', 'markitdown', 'bin', 'python'),
|
|
162
167
|
path.join(os.homedir(), '.local', 'bin', 'markitdown-python'),
|
|
163
168
|
'python3',
|
|
@@ -178,6 +183,60 @@ function findInterpreter() {
|
|
|
178
183
|
|
|
179
184
|
const CONVERTER = path.join(__dirname, '..', 'presets', 'doc2md', 'convert.py');
|
|
180
185
|
|
|
186
|
+
/** Path to the interpreter inside the venv this tool manages. */
|
|
187
|
+
function managedPython() {
|
|
188
|
+
const dir = path.join(userDataDir(), 'doc2md-venv');
|
|
189
|
+
return process.platform === 'win32'
|
|
190
|
+
? path.join(dir, 'Scripts', 'python.exe')
|
|
191
|
+
: path.join(dir, 'bin', 'python');
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const MARKITDOWN_SPEC = 'markitdown[pptx,pdf,xlsx,docx]';
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Build the managed venv and install markitdown into it.
|
|
198
|
+
*
|
|
199
|
+
* Kept behind an explicit command: creating a 300MB virtualenv is not
|
|
200
|
+
* something to do because somebody opened a spreadsheet once. But once asked
|
|
201
|
+
* for, it goes somewhere this tool owns, so uninstalling the CLI takes the
|
|
202
|
+
* whole thing with it and no system interpreter is touched.
|
|
203
|
+
*/
|
|
204
|
+
function installConverter({ onProgress = () => {} } = {}) {
|
|
205
|
+
const venv = path.join(userDataDir(), 'doc2md-venv');
|
|
206
|
+
const target = managedPython();
|
|
207
|
+
|
|
208
|
+
if (!fs.existsSync(target)) {
|
|
209
|
+
onProgress(`creating ${venv}`);
|
|
210
|
+
let created = false;
|
|
211
|
+
for (const base of ['python3', 'python']) {
|
|
212
|
+
const r = spawnSync(base, ['-m', 'venv', venv], { encoding: 'utf8', timeout: 180_000 });
|
|
213
|
+
if (r.status === 0) { created = true; break; }
|
|
214
|
+
}
|
|
215
|
+
if (!created) {
|
|
216
|
+
return { ok: false, reason: 'no-python', detail: 'no python3 with the venv module on PATH' };
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
onProgress(`installing ${MARKITDOWN_SPEC}`);
|
|
221
|
+
const install = spawnSync(target, ['-m', 'pip', 'install', '--quiet', MARKITDOWN_SPEC], {
|
|
222
|
+
encoding: 'utf8',
|
|
223
|
+
timeout: 900_000,
|
|
224
|
+
});
|
|
225
|
+
if (install.status !== 0) {
|
|
226
|
+
return { ok: false, reason: 'pip-failed', detail: (install.stderr || '').slice(0, 400) };
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// The probe is the actual acceptance test: pip can exit 0 and still leave an
|
|
230
|
+
// interpreter that cannot import what was asked for.
|
|
231
|
+
const probe = spawnSync(target, ['-c', 'import markitdown'], { timeout: 60_000, stdio: 'ignore' });
|
|
232
|
+
if (probe.status !== 0) {
|
|
233
|
+
return { ok: false, reason: 'import-failed', detail: 'installed, but markitdown does not import' };
|
|
234
|
+
}
|
|
235
|
+
interpreterCache = target;
|
|
236
|
+
clearNotice();
|
|
237
|
+
return { ok: true, python: target };
|
|
238
|
+
}
|
|
239
|
+
|
|
181
240
|
/**
|
|
182
241
|
* Convert one file. Returns `{ ok: true, cacheFile, meta }`, or
|
|
183
242
|
* `{ ok: false, reason, detail }` where reason is one of:
|
|
@@ -258,6 +317,18 @@ function noticeAlreadyShown() {
|
|
|
258
317
|
}
|
|
259
318
|
}
|
|
260
319
|
|
|
320
|
+
/**
|
|
321
|
+
* Forget that the notice was shown.
|
|
322
|
+
*
|
|
323
|
+
* Called after the converter is installed, so that if it later disappears the
|
|
324
|
+
* user is told once more instead of meeting permanent silence.
|
|
325
|
+
*/
|
|
326
|
+
function clearNotice() {
|
|
327
|
+
try {
|
|
328
|
+
fs.rmSync(noticePath(), { force: true });
|
|
329
|
+
} catch { /* nothing to forget */ }
|
|
330
|
+
}
|
|
331
|
+
|
|
261
332
|
function markNoticeShown() {
|
|
262
333
|
try {
|
|
263
334
|
fs.mkdirSync(userDataDir(), { recursive: true });
|
|
@@ -265,7 +336,7 @@ function markNoticeShown() {
|
|
|
265
336
|
} catch { /* an unwritable state dir just means the notice repeats */ }
|
|
266
337
|
}
|
|
267
338
|
|
|
268
|
-
const INSTALL_HINT = '
|
|
339
|
+
const INSTALL_HINT = 'claude-token-saver doc2md install-converter';
|
|
269
340
|
|
|
270
341
|
/**
|
|
271
342
|
* Decide what to tell Claude Code about one PreToolUse(Read) payload.
|
|
@@ -364,6 +435,10 @@ module.exports = {
|
|
|
364
435
|
TARGET_EXTENSIONS,
|
|
365
436
|
MAX_SOURCE_BYTES,
|
|
366
437
|
INSTALL_HINT,
|
|
438
|
+
MARKITDOWN_SPEC,
|
|
439
|
+
managedPython,
|
|
440
|
+
installConverter,
|
|
441
|
+
clearNotice,
|
|
367
442
|
cacheDir,
|
|
368
443
|
cachePathFor,
|
|
369
444
|
metaPathFor,
|