@kaydash9999/banker-plugins 0.18.0 → 0.19.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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +52 -0
- package/README.md +18 -7
- package/codex/manifest.json +1 -0
- package/codex/transform-matrix.md +1 -0
- package/hooks/graceful-pause.mjs +4 -2
- package/hooks/obsidize-hook.mjs +38 -28
- package/hooks/progress-plan.mjs +14 -8
- package/hooks/progress.mjs +174 -47
- package/hooks/register.mjs +14 -7
- package/hooks/update-checkin.mjs +29 -27
- package/package.json +1 -1
- package/skills/anti-drm/SKILL.md +36 -12
- package/skills/anti-drm/scripts/anti-drm.mjs +121 -30
- package/skills/anti-drm/scripts/instruction-toggle.mjs +241 -35
- package/skills/lineage/SKILL.md +1 -1
- package/skills/lineage/lineage.py +29 -2
- package/skills/omc-patch/SKILL.md +18 -6
- package/skills/omc-patch/scripts/omc-patch.mjs +400 -87
- package/skills/theme-selection/SKILL.md +165 -0
- package/skills/theme-selection/assets/LICENSE +21 -0
- package/skills/theme-selection/assets/NOTICE.md +30 -0
- package/skills/theme-selection/assets/palettes.json +81 -0
- package/skills/theme-selection/references/contrast.md +138 -0
- package/skills/theme-selection/references/integrate.md +161 -0
- package/skills/theme-selection/scripts/themes.mjs +1067 -0
- package/skills/tone-compact/SKILL.md +8 -4
- package/skills/tone-compact/scripts/instruction-toggle.mjs +241 -35
- package/skills/tone-compact/scripts/tone-compact.mjs +2 -3
- package/skills/update-banker/SKILL.md +1 -1
|
@@ -9,8 +9,13 @@
|
|
|
9
9
|
* node anti-drm.mjs off --runtime claude|codex
|
|
10
10
|
* node anti-drm.mjs status
|
|
11
11
|
* node anti-drm.mjs config --deny <list> --alt <list> [--tsv tab|comma]
|
|
12
|
+
* node anti-drm.mjs on --runtime claude|codex --stdin
|
|
13
|
+
* node anti-drm.mjs config --stdin
|
|
12
14
|
*
|
|
13
15
|
* <list> is extensions split by commas or spaces, such as ".txt,.pdf".
|
|
16
|
+
* --stdin reads the settings from standard input instead, one line each:
|
|
17
|
+
* deny=<list>, alt=<list> and tsv=tab|comma. The skill passes them in a
|
|
18
|
+
* quoted heredoc, so no value the user chose is ever read by a shell.
|
|
14
19
|
*
|
|
15
20
|
* The settings are one JSON file both runtimes share, ~/.config/banker/anti-drm.json
|
|
16
21
|
* ($BANKER_ANTI_DRM_CONFIG names another): the denied extensions, the
|
|
@@ -40,6 +45,7 @@ import {
|
|
|
40
45
|
mkdirSync,
|
|
41
46
|
openSync,
|
|
42
47
|
readFileSync,
|
|
48
|
+
readSync,
|
|
43
49
|
realpathSync,
|
|
44
50
|
statSync,
|
|
45
51
|
} from "node:fs";
|
|
@@ -50,9 +56,14 @@ import {
|
|
|
50
56
|
KEEP_CLOSE,
|
|
51
57
|
KEEP_OPEN,
|
|
52
58
|
RUNTIMES,
|
|
59
|
+
count,
|
|
53
60
|
denied,
|
|
61
|
+
done,
|
|
54
62
|
hasMarker,
|
|
63
|
+
lf,
|
|
64
|
+
noBom,
|
|
55
65
|
present,
|
|
66
|
+
refuse,
|
|
56
67
|
stateLines,
|
|
57
68
|
toggleSpec,
|
|
58
69
|
turnOff as toggleOff,
|
|
@@ -80,12 +91,7 @@ const DEFAULTS = {
|
|
|
80
91
|
skillFile: fileURLToPath(new URL("../SKILL.md", import.meta.url)),
|
|
81
92
|
};
|
|
82
93
|
|
|
83
|
-
const BOM = String.fromCharCode(0xfeff);
|
|
84
94
|
const RUNTIME_NAME = { claude: "Claude Code", codex: "Codex" };
|
|
85
|
-
const done = (message) => ({ ok: true, message });
|
|
86
|
-
const refuse = (message) => ({ ok: false, message });
|
|
87
|
-
const count = (text, needle) => text.split(needle).length - 1;
|
|
88
|
-
const lf = (text) => text.replace(/\r\n/g, "\n");
|
|
89
95
|
// A value from outside, as a message shows it. An extension is printable
|
|
90
96
|
// ASCII, so anything else becomes "?" (a line break or a direction mark cannot
|
|
91
97
|
// forge a line or reorder the text), at most 40 characters stay, and a value
|
|
@@ -136,10 +142,12 @@ export function checkConfig({ deny, alt, tsvDelimiter = "tab" }) {
|
|
|
136
142
|
if (reserved.length > 0) return { error: `anti-drm 이 쓰는 형식이라 금지할 수 없습니다: ${reserved.join(" ")}` };
|
|
137
143
|
const both = d.list.filter((x) => a.list.includes(x));
|
|
138
144
|
if (both.length > 0) return { error: `금지 확장자와 대체 확장자에 함께 있습니다: ${both.join(" ")}` };
|
|
139
|
-
|
|
145
|
+
// In any letter case, as the --tsv flag takes it; kept lower-cased.
|
|
146
|
+
const tsv = typeof tsvDelimiter === "string" ? tsvDelimiter.toLowerCase() : tsvDelimiter;
|
|
147
|
+
if (tsv !== "tab" && tsv !== "comma") {
|
|
140
148
|
return { error: `TSV 구분자는 tab 이나 comma 여야 합니다: ${shown(tsvDelimiter)}` };
|
|
141
149
|
}
|
|
142
|
-
return { config: { version: 1, deny: d.list, alt: a.list, tsvDelimiter } };
|
|
150
|
+
return { config: { version: 1, deny: d.list, alt: a.list, tsvDelimiter: tsv } };
|
|
143
151
|
}
|
|
144
152
|
|
|
145
153
|
/**
|
|
@@ -188,7 +196,7 @@ export function readConfig(file) {
|
|
|
188
196
|
const { text } = got;
|
|
189
197
|
let raw;
|
|
190
198
|
try {
|
|
191
|
-
raw = JSON.parse(
|
|
199
|
+
raw = JSON.parse(noBom(text));
|
|
192
200
|
} catch {
|
|
193
201
|
return { error: "JSON 이 아닙니다" };
|
|
194
202
|
}
|
|
@@ -244,7 +252,7 @@ const OWN_KEYS = ["version", "deny", "alt", "tsvDelimiter"];
|
|
|
244
252
|
function ownSettings(bytes) {
|
|
245
253
|
try {
|
|
246
254
|
const text = bytes.toString("utf8");
|
|
247
|
-
const raw = JSON.parse(
|
|
255
|
+
const raw = JSON.parse(noBom(text));
|
|
248
256
|
const plain = Boolean(raw) && typeof raw === "object" && !Array.isArray(raw);
|
|
249
257
|
return plain && Object.keys(raw).every((k) => OWN_KEYS.includes(k)) && Boolean(checkConfig(raw).config);
|
|
250
258
|
} catch {
|
|
@@ -337,13 +345,16 @@ const code = (ext) => `\`${ext}\``;
|
|
|
337
345
|
export function renderRules(template, config) {
|
|
338
346
|
const map = table(["금지 확장자", "대신 쓸 확장자"], config.deny.map((d) => [code(d), code(replacementFor(d, config.alt))]));
|
|
339
347
|
const alts = table(["확장자", "쓰는 곳"], config.alt.map((a) => [code(a), useOf(a, config)]));
|
|
340
|
-
|
|
348
|
+
const rules = template.replace("{{map}}", () => map).replace("{{alts}}", () => alts);
|
|
349
|
+
if (rules.includes("{{")) throw new Error("규칙을 채운 뒤에도 자리 표시({{)가 남았습니다");
|
|
350
|
+
return rules;
|
|
341
351
|
}
|
|
342
352
|
|
|
343
353
|
// ---- commands ----
|
|
344
354
|
|
|
345
355
|
const USAGE =
|
|
346
|
-
"사용법: anti-drm on | off | status | config [--runtime claude|codex] [--deny <확장자 목록> --alt <확장자 목록> [--tsv tab|comma]]"
|
|
356
|
+
"사용법: anti-drm on | off | status | config [--runtime claude|codex] [--stdin | --deny <확장자 목록> --alt <확장자 목록> [--tsv tab|comma]]\n" +
|
|
357
|
+
"--stdin 은 표준 입력이 끝날 때까지 기다립니다.";
|
|
347
358
|
|
|
348
359
|
// The settings as `status` shows them.
|
|
349
360
|
function configLines(o, found) {
|
|
@@ -416,8 +427,7 @@ function refresh(o, rules, skip) {
|
|
|
416
427
|
// The settings from --deny, --alt and --tsv: { config }, { error }, or { usage } when a list is missing.
|
|
417
428
|
function configFromFlags(flags) {
|
|
418
429
|
if (flags.deny === undefined || flags.alt === undefined) return { usage: true };
|
|
419
|
-
|
|
420
|
-
return checkConfig({ deny: splitList(flags.deny), alt: splitList(flags.alt), tsvDelimiter });
|
|
430
|
+
return checkConfig({ deny: splitList(flags.deny), alt: splitList(flags.alt), tsvDelimiter: flags.tsv ?? "tab" });
|
|
421
431
|
}
|
|
422
432
|
|
|
423
433
|
const settingsGiven = (flags) => ["deny", "alt", "tsv"].some((k) => flags[k] !== undefined);
|
|
@@ -438,7 +448,7 @@ function applySettings(o, flags, skip) {
|
|
|
438
448
|
// The rules from the saved settings: { rules, lines, ok } or { refusal }.
|
|
439
449
|
function savedRules(o) {
|
|
440
450
|
const found = readConfig(o.configFile);
|
|
441
|
-
if (found === null) return { refusal: refuse("설정이 없습니다. 금지 확장자와 대체 확장자를 --deny, --alt 로 함께 주세요.") };
|
|
451
|
+
if (found === null) return { refusal: refuse("설정이 없습니다. 금지 확장자와 대체 확장자를 --stdin 이나 --deny, --alt 로 함께 주세요.") };
|
|
442
452
|
if (found.error) {
|
|
443
453
|
return { refusal: refuse(`설정 파일이 올바르지 않습니다 (${found.error}): ${where(o.configFile)}. config 로 다시 만드세요.`) };
|
|
444
454
|
}
|
|
@@ -481,17 +491,89 @@ export const configure = (opts = {}, flags = {}) => cmdConfig({ ...DEFAULTS, ...
|
|
|
481
491
|
const FLAG_NAMES = ["runtime", "deny", "alt", "tsv"];
|
|
482
492
|
const COMMANDS = ["on", "off", "status", "config"];
|
|
483
493
|
|
|
484
|
-
/**
|
|
494
|
+
/**
|
|
495
|
+
* { command, runtime, flags, extra, stdin } from the command line; each value
|
|
496
|
+
* flag takes "x" or "=x", and --stdin takes nothing.
|
|
497
|
+
*/
|
|
485
498
|
export function parseArgs(argv) {
|
|
486
499
|
const rest = [];
|
|
487
500
|
const flags = {};
|
|
501
|
+
let stdin = false;
|
|
488
502
|
for (let i = 0; i < argv.length; i++) {
|
|
489
503
|
const m = /^--([a-z]+)(?:=(.*))?$/.exec(argv[i]);
|
|
490
504
|
if (m && FLAG_NAMES.includes(m[1])) flags[m[1]] = m[2] ?? argv[++i] ?? "";
|
|
505
|
+
else if (argv[i] === "--stdin") stdin = true;
|
|
491
506
|
else rest.push(argv[i]);
|
|
492
507
|
}
|
|
493
508
|
const { runtime, ...settings } = flags;
|
|
494
|
-
|
|
509
|
+
const command = (rest[0] ?? "status").toLowerCase();
|
|
510
|
+
return { command, runtime: runtime?.toLowerCase() ?? null, flags: settings, extra: rest.slice(1), stdin };
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// Lines on standard input that hold the settings: the key, "=", the value.
|
|
514
|
+
const STDIN_LINE = /^(deny|alt|tsv)=(.*)$/;
|
|
515
|
+
const UTF8_STRICT = new TextDecoder("utf-8", { fatal: true });
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
* The settings on standard input as flags: { flags } or { error }. At most
|
|
519
|
+
* 64 KiB of UTF-8; a leading byte order mark and CRLF line ends are fine;
|
|
520
|
+
* each of deny=, alt= and tsv= once; blank lines are skipped.
|
|
521
|
+
*/
|
|
522
|
+
export function stdinSettings(bytes) {
|
|
523
|
+
if (bytes.length > MAX_BYTES) return { error: "표준 입력이 64KiB 보다 큽니다" };
|
|
524
|
+
let text;
|
|
525
|
+
try {
|
|
526
|
+
text = UTF8_STRICT.decode(bytes);
|
|
527
|
+
} catch {
|
|
528
|
+
return { error: "표준 입력이 UTF-8 이 아닙니다" };
|
|
529
|
+
}
|
|
530
|
+
const flags = {};
|
|
531
|
+
for (const line of noBom(text).split(/\r?\n/)) {
|
|
532
|
+
if (line.trim() === "") continue;
|
|
533
|
+
const m = STDIN_LINE.exec(line);
|
|
534
|
+
if (!m || flags[m[1]] !== undefined) return { error: `표준 입력의 설정 줄을 읽을 수 없습니다: ${shown(line)}` };
|
|
535
|
+
flags[m[1]] = m[2].trim();
|
|
536
|
+
}
|
|
537
|
+
return { flags };
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
const PAUSE = new Int32Array(new SharedArrayBuffer(4));
|
|
541
|
+
|
|
542
|
+
// Standard input, read up to cap bytes and one more: { bytes } at the end of input, or { tooBig: true } as soon as
|
|
543
|
+
// more than cap came. A pipe left non-blocking answers EAGAIN until data comes: wait a little and read again.
|
|
544
|
+
function readCapped(fd, cap) {
|
|
545
|
+
const chunks = [];
|
|
546
|
+
const buf = Buffer.alloc(64 * 1024);
|
|
547
|
+
let total = 0;
|
|
548
|
+
for (;;) {
|
|
549
|
+
const n = readSome(fd, buf);
|
|
550
|
+
if (n === 0) return { bytes: Buffer.concat(chunks) };
|
|
551
|
+
total += n;
|
|
552
|
+
if (total > cap) return { tooBig: true };
|
|
553
|
+
chunks.push(Buffer.from(buf.subarray(0, n)));
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
// One read into buf: how many bytes came, 0 at the end of input (Windows may say EOF instead).
|
|
558
|
+
function readSome(fd, buf) {
|
|
559
|
+
for (;;) {
|
|
560
|
+
try {
|
|
561
|
+
return readSync(fd, buf, 0, buf.length, null);
|
|
562
|
+
} catch (e) {
|
|
563
|
+
if (e.code === "EOF") return 0;
|
|
564
|
+
if (e.code !== "EAGAIN") throw e;
|
|
565
|
+
Atomics.wait(PAUSE, 0, 0, 10);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
// The settings standard input held: an error when it was too big or unreadable, or held no settings line, which
|
|
571
|
+
// on and config both refuse rather than reach for the saved settings.
|
|
572
|
+
function stdinFrom(got) {
|
|
573
|
+
if (got.tooBig) return { error: "표준 입력이 64KiB 보다 큽니다" };
|
|
574
|
+
const read = stdinSettings(got.bytes);
|
|
575
|
+
if (read.error || Object.keys(read.flags).length > 0) return read;
|
|
576
|
+
return { error: "표준 입력에 설정 줄이 없습니다" };
|
|
495
577
|
}
|
|
496
578
|
|
|
497
579
|
// Settings flags go only with on and config, and config needs both lists.
|
|
@@ -501,9 +583,11 @@ function settingsUsage(command, flags) {
|
|
|
501
583
|
}
|
|
502
584
|
|
|
503
585
|
// Why the command line cannot run as given, or "" when it can.
|
|
504
|
-
function usageError({ command, runtime, flags, extra }) {
|
|
586
|
+
function usageError({ command, runtime, flags, extra, stdin }) {
|
|
505
587
|
if (extra.length > 0 || !COMMANDS.includes(command)) return USAGE;
|
|
506
588
|
if (runtime !== null && !RUNTIMES.includes(runtime)) return USAGE;
|
|
589
|
+
// --stdin brings all the settings, so it goes with on and config only and never with a settings flag.
|
|
590
|
+
if (stdin) return (command === "on" || command === "config") && Object.keys(flags).length === 0 ? "" : USAGE;
|
|
507
591
|
return settingsUsage(command, flags);
|
|
508
592
|
}
|
|
509
593
|
|
|
@@ -517,25 +601,32 @@ function run(command, runtime, flags) {
|
|
|
517
601
|
return command === "on" ? cmdOn(o, runtime, flags) : cmdOff(o, runtime);
|
|
518
602
|
}
|
|
519
603
|
|
|
520
|
-
|
|
604
|
+
// Writes a message line and gives back the exit status.
|
|
605
|
+
const say = (text, code) => {
|
|
606
|
+
process.stdout.write(`${text}\n`);
|
|
607
|
+
return code;
|
|
608
|
+
};
|
|
609
|
+
|
|
610
|
+
// Whether a command acts on one runtime and so needs to know which.
|
|
611
|
+
const needsRuntime = (command) => command === "on" || command === "off";
|
|
612
|
+
// The settings flags: from standard input with --stdin, else from the command line.
|
|
613
|
+
const settingsOf = (args, input) => (args.stdin ? stdinFrom(input()) : { flags: args.flags });
|
|
614
|
+
|
|
615
|
+
function main(argv, env = process.env, input = () => readCapped(0, MAX_BYTES)) {
|
|
521
616
|
const args = parseArgs(argv);
|
|
522
617
|
const why = usageError(args);
|
|
523
|
-
if (why)
|
|
524
|
-
process.stdout.write(`${why}\n`);
|
|
525
|
-
return 1;
|
|
526
|
-
}
|
|
618
|
+
if (why) return say(why, 1);
|
|
527
619
|
const runtime = runtimeOf(args.runtime, env);
|
|
528
|
-
if ((args.command
|
|
529
|
-
|
|
530
|
-
return 1;
|
|
620
|
+
if (needsRuntime(args.command) && !RUNTIMES.includes(runtime)) {
|
|
621
|
+
return say(`런타임을 정할 수 없습니다. --runtime claude 또는 --runtime codex 를 붙이세요.\n${USAGE}`, 1);
|
|
531
622
|
}
|
|
532
623
|
try {
|
|
533
|
-
const
|
|
534
|
-
|
|
535
|
-
|
|
624
|
+
const got = settingsOf(args, input);
|
|
625
|
+
if (got.error) return say(`${got.error}\n${USAGE}`, 1);
|
|
626
|
+
const { ok, message } = run(args.command, runtime, got.flags);
|
|
627
|
+
return say(message, ok ? 0 : 1);
|
|
536
628
|
} catch (e) {
|
|
537
|
-
|
|
538
|
-
return 2;
|
|
629
|
+
return say(`예기치 못한 오류로 중단했습니다: ${where(e.message)}\nstatus 로 상태를 확인하세요.`, 2);
|
|
539
630
|
}
|
|
540
631
|
}
|
|
541
632
|
|