@kaydash9999/banker-plugins 0.18.0 โ 0.19.1
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 +103 -0
- package/README.md +18 -7
- package/bin/lib/telemetry-config.mjs +25 -0
- 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 +61 -32
- package/hooks/progress-plan.mjs +14 -8
- package/hooks/progress.mjs +187 -48
- package/hooks/register.mjs +15 -7
- package/hooks/telemetry-count-skill.mjs +4 -6
- package/hooks/telemetry-count.mjs +4 -6
- package/hooks/update-checkin.mjs +46 -43
- package/hooks/update-fetch.mjs +8 -7
- package/package.json +1 -1
- package/skills/anti-drm/SKILL.md +46 -15
- package/skills/anti-drm/scripts/anti-drm.mjs +213 -61
- package/skills/anti-drm/scripts/instruction-toggle.mjs +280 -44
- package/skills/lineage/SKILL.md +4 -1
- package/skills/lineage/lineage.py +196 -73
- package/skills/omc-patch/SKILL.md +37 -11
- package/skills/omc-patch/scripts/omc-patch.mjs +635 -103
- 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 +14 -5
- package/skills/tone-compact/scripts/instruction-toggle.mjs +280 -44
- package/skills/tone-compact/scripts/tone-compact.mjs +4 -4
- package/skills/update-banker/SKILL.md +1 -1
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
Deterministic core. One-line summary per assistant turn (cached, redacted).
|
|
5
5
|
Renders assistant markdown, filters harness noise, folds long messages, and
|
|
6
6
|
self-verifies the output markup. See SKILL.md for the full design and the
|
|
7
|
-
`
|
|
7
|
+
`docs/lineage-change-request.html` spec for the classification rules.
|
|
8
8
|
|
|
9
9
|
Runtime-agnostic: works from any transcript via --session / --from-transcript.
|
|
10
10
|
Auto-discovery assumes Claude Code's ~/.claude/projects/<encoded-cwd>/ layout.
|
|
@@ -18,8 +18,10 @@ import math
|
|
|
18
18
|
import os
|
|
19
19
|
import pathlib
|
|
20
20
|
import re
|
|
21
|
+
import stat
|
|
21
22
|
import sys
|
|
22
23
|
import tempfile
|
|
24
|
+
import unicodedata
|
|
23
25
|
from html.parser import HTMLParser
|
|
24
26
|
|
|
25
27
|
if sys.version_info < (3, 7):
|
|
@@ -424,6 +426,96 @@ KNOWN_CONTENT_ITEMS = {"text", "tool_use", "tool_result", "thinking"}
|
|
|
424
426
|
_CTRL_RE = re.compile(r"[\x00-\x08\x0b\x0c\x0e-\x1f]") # strip control chars (keeps \t\n)
|
|
425
427
|
|
|
426
428
|
|
|
429
|
+
def _json_object(raw, line_no):
|
|
430
|
+
"""The JSON object a transcript line holds, else None after a WARN: a line that is not JSON, is nested
|
|
431
|
+
deeper than the parser reads, holds a number longer than it reads (ValueError since 3.11), or holds
|
|
432
|
+
something other than an object is skipped, not fatal."""
|
|
433
|
+
try:
|
|
434
|
+
obj = json.loads(raw)
|
|
435
|
+
except (ValueError, RecursionError): # JSONDecodeError is a ValueError
|
|
436
|
+
print(f"[WARN] line {line_no}: invalid JSON skipped", file=sys.stderr)
|
|
437
|
+
return None
|
|
438
|
+
if not isinstance(obj, dict):
|
|
439
|
+
print(f"[WARN] line {line_no}: not a JSON object skipped", file=sys.stderr)
|
|
440
|
+
return None
|
|
441
|
+
return obj
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
def _is_turn_record(obj, seen_unknown_type, unsafe_schema, line_no):
|
|
445
|
+
"""Whether a record is a user or assistant turn. A side chain or a team record is not, nor is another type:
|
|
446
|
+
one nobody knows is named once, and a schema marker (v...) stops the run unless --unsafe-schema. A record
|
|
447
|
+
whose type, or a turn whose message, has a shape a transcript does not give is named and skipped."""
|
|
448
|
+
if obj.get("isSidechain") or obj.get("teamName") or obj.get("agentName"):
|
|
449
|
+
return False
|
|
450
|
+
t = obj.get("type")
|
|
451
|
+
if (t and not isinstance(t, str)) or (t in ("user", "assistant") and _odd_message(obj.get("message"))):
|
|
452
|
+
print(f"[WARN] line {line_no}: record of an unknown shape skipped", file=sys.stderr)
|
|
453
|
+
return False
|
|
454
|
+
if t in ("user", "assistant"):
|
|
455
|
+
return True
|
|
456
|
+
if t and t not in KNOWN_TYPES and t not in seen_unknown_type:
|
|
457
|
+
seen_unknown_type.add(t)
|
|
458
|
+
print(f"[WARN] unknown record type {t!r} skipped", file=sys.stderr) # repr: no raw control character
|
|
459
|
+
if not unsafe_schema and t.startswith("v"):
|
|
460
|
+
print(f"[ERROR] schema marker {t!r} unrecognized โ "
|
|
461
|
+
"rerun with --unsafe-schema to proceed",
|
|
462
|
+
file=sys.stderr)
|
|
463
|
+
raise SystemExit(2)
|
|
464
|
+
return False
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
def _stamp(ts):
|
|
468
|
+
"""A record's timestamp when it is text, else None: a number or another shape reads as a missing one."""
|
|
469
|
+
return ts if isinstance(ts, str) else None
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
def _odd_message(msg):
|
|
473
|
+
"""Whether a turn's message has a shape a transcript does not give (a missing or empty one reads as empty)."""
|
|
474
|
+
return bool(msg) and not isinstance(msg, dict)
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
def _odd_item(it, text, name):
|
|
478
|
+
"""Whether a content item's type, text or tool name has a shape a transcript does not give. A missing or empty
|
|
479
|
+
text reads as empty and a missing or null tool name as '?'."""
|
|
480
|
+
return ((it is not None and not isinstance(it, str)) or (it == "text" and not isinstance(text, str))
|
|
481
|
+
or (it == "tool_use" and not isinstance(name, str)))
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
def _take_item(item, line_no, text_parts, tools):
|
|
485
|
+
"""Add one item of a message's content list to the text parts or the tool counts. An item whose type, text or
|
|
486
|
+
tool name has a shape a transcript does not give is named and skipped."""
|
|
487
|
+
if not isinstance(item, dict):
|
|
488
|
+
return
|
|
489
|
+
it, text, name = item.get("type"), item.get("text") or "", item.get("name")
|
|
490
|
+
name = "?" if name is None else name
|
|
491
|
+
if _odd_item(it, text, name):
|
|
492
|
+
print(f"[WARN] line {line_no}: content item of an unknown shape skipped", file=sys.stderr)
|
|
493
|
+
elif it == "text":
|
|
494
|
+
text_parts.append(text)
|
|
495
|
+
elif it == "tool_use":
|
|
496
|
+
tools[name] = tools.get(name, 0) + 1
|
|
497
|
+
elif it not in KNOWN_CONTENT_ITEMS:
|
|
498
|
+
print(f"[WARN] line {line_no}: unknown content type "
|
|
499
|
+
f"{it!r} skipped", file=sys.stderr)
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
def _content_parts(content, line_no):
|
|
503
|
+
"""(text parts, tool counts) of a message's content, or None for a shape nobody knows (named in a WARN
|
|
504
|
+
unless the content is missing)."""
|
|
505
|
+
text_parts, tools = [], {}
|
|
506
|
+
if isinstance(content, str):
|
|
507
|
+
text_parts.append(content)
|
|
508
|
+
elif isinstance(content, list):
|
|
509
|
+
for item in content:
|
|
510
|
+
_take_item(item, line_no, text_parts, tools)
|
|
511
|
+
else:
|
|
512
|
+
if content is not None:
|
|
513
|
+
print(f"[WARN] line {line_no}: unknown content shape "
|
|
514
|
+
f"({type(content).__name__})", file=sys.stderr)
|
|
515
|
+
return None
|
|
516
|
+
return text_parts, tools
|
|
517
|
+
|
|
518
|
+
|
|
427
519
|
def parse_turns(stream, unsafe_schema: bool = False, session_id=None,
|
|
428
520
|
session_name=None):
|
|
429
521
|
"""Yield dicts: {role, text, ts, uuid, tools, line_no, parts, session,
|
|
@@ -438,59 +530,18 @@ def parse_turns(stream, unsafe_schema: bool = False, session_id=None,
|
|
|
438
530
|
raw = raw.strip()
|
|
439
531
|
if not raw:
|
|
440
532
|
continue
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
except json.JSONDecodeError:
|
|
444
|
-
print(f"[WARN] line {line_no}: invalid JSON skipped", file=sys.stderr)
|
|
445
|
-
continue
|
|
446
|
-
if obj.get("isSidechain"):
|
|
447
|
-
continue
|
|
448
|
-
if obj.get("teamName") or obj.get("agentName"):
|
|
533
|
+
obj = _json_object(raw, line_no)
|
|
534
|
+
if obj is None or not _is_turn_record(obj, seen_unknown_type, unsafe_schema, line_no):
|
|
449
535
|
continue
|
|
450
|
-
t = obj.get("type")
|
|
451
|
-
if t not in ("user", "assistant"):
|
|
452
|
-
if t and t not in KNOWN_TYPES and t not in seen_unknown_type:
|
|
453
|
-
seen_unknown_type.add(t)
|
|
454
|
-
print(f"[WARN] unknown record type '{t}' skipped", file=sys.stderr)
|
|
455
|
-
if not unsafe_schema and t and t.startswith("v"):
|
|
456
|
-
print(f"[ERROR] schema marker '{t}' unrecognized โ "
|
|
457
|
-
"rerun with --unsafe-schema to proceed",
|
|
458
|
-
file=sys.stderr)
|
|
459
|
-
raise SystemExit(2)
|
|
460
|
-
continue
|
|
461
|
-
|
|
462
536
|
msg = obj.get("message") or {}
|
|
463
|
-
role = msg.get("role") or
|
|
464
|
-
|
|
465
|
-
ts = obj.get("timestamp")
|
|
537
|
+
role = msg.get("role") or obj.get("type")
|
|
538
|
+
ts = _stamp(obj.get("timestamp"))
|
|
466
539
|
uuid_ = obj.get("uuid") or hashlib.sha256(
|
|
467
540
|
f"{line_no}:{ts}".encode()).hexdigest()[:16]
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
tools = {}
|
|
471
|
-
if isinstance(content, str):
|
|
472
|
-
text_parts.append(content)
|
|
473
|
-
elif isinstance(content, list):
|
|
474
|
-
for item in content:
|
|
475
|
-
if not isinstance(item, dict):
|
|
476
|
-
continue
|
|
477
|
-
it = item.get("type")
|
|
478
|
-
if it == "text":
|
|
479
|
-
text_parts.append(item.get("text") or "")
|
|
480
|
-
elif it == "tool_use":
|
|
481
|
-
tools[item.get("name", "?")] = tools.get(
|
|
482
|
-
item.get("name", "?"), 0) + 1
|
|
483
|
-
elif it in ("tool_result", "thinking"):
|
|
484
|
-
continue
|
|
485
|
-
elif it not in KNOWN_CONTENT_ITEMS:
|
|
486
|
-
print(f"[WARN] line {line_no}: unknown content type "
|
|
487
|
-
f"'{it}' skipped", file=sys.stderr)
|
|
488
|
-
else:
|
|
489
|
-
if content is not None:
|
|
490
|
-
print(f"[WARN] line {line_no}: unknown content shape "
|
|
491
|
-
f"({type(content).__name__})", file=sys.stderr)
|
|
541
|
+
got = _content_parts(msg.get("content"), line_no)
|
|
542
|
+
if got is None:
|
|
492
543
|
continue
|
|
493
|
-
|
|
544
|
+
text_parts, tools = got
|
|
494
545
|
text = _CTRL_RE.sub("", "\n".join(p for p in text_parts if p).strip())
|
|
495
546
|
if not text and not tools:
|
|
496
547
|
continue
|
|
@@ -692,18 +743,16 @@ def _stdin_id_key():
|
|
|
692
743
|
to a model that never sees the key, so an id gives no way to test a guessed password. With no
|
|
693
744
|
cache folder to keep it in, the key lasts one run."""
|
|
694
745
|
path = CACHE_BASE / "stdin-id.key"
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
return key
|
|
699
|
-
except OSError:
|
|
700
|
-
pass
|
|
746
|
+
key = _read_small(path, 64)
|
|
747
|
+
if key is not None and len(key) == 32:
|
|
748
|
+
return key
|
|
701
749
|
key = os.urandom(32)
|
|
702
750
|
tmp = path.with_name("%s.%d" % (path.name, os.getpid()))
|
|
703
751
|
try:
|
|
704
752
|
CACHE_BASE.mkdir(parents=True, exist_ok=True)
|
|
705
753
|
os.chmod(str(CACHE_BASE), 0o700)
|
|
706
|
-
|
|
754
|
+
# O_EXCL: a name that is already there (a link planted at this guessable name among them) is refused
|
|
755
|
+
fd = os.open(str(tmp), os.O_WRONLY | os.O_CREAT | os.O_EXCL | getattr(os, "O_BINARY", 0), 0o600)
|
|
707
756
|
with os.fdopen(fd, "wb") as f:
|
|
708
757
|
f.write(key)
|
|
709
758
|
os.replace(str(tmp), str(path)) # whole or not at all
|
|
@@ -734,14 +783,72 @@ def _page_clean_turn(t, extra, mode):
|
|
|
734
783
|
return dict(t, text=clean(t["text"]), parts=[clean(p) for p in t.get("parts") or []])
|
|
735
784
|
|
|
736
785
|
|
|
786
|
+
# Letters that print as nothing: the Hangul fillers, the braille blank, and the combining marks Unicode ignores by
|
|
787
|
+
# default (the grapheme joiner, the Khmer inherent vowels, the Mongolian free variation selectors). With the control,
|
|
788
|
+
# format, private-use, unassigned and separator categories, a cache made only of them holds no summary a person can
|
|
789
|
+
# read (3.0.5).
|
|
790
|
+
_BLANK_LETTERS = frozenset(map(chr, (0x115F, 0x1160, 0x3164, 0xFFA0, 0x2800,
|
|
791
|
+
0x034F, 0x17B4, 0x17B5, 0x180B, 0x180C, 0x180D, 0x180F)))
|
|
792
|
+
# Cn follows the Unicode table of the Python that runs: a character newer than that table reads as unassigned, so
|
|
793
|
+
# a cache made only of such characters is summarized again every run (the page is the same).
|
|
794
|
+
_BLANK_CATEGORIES = frozenset(("Cc", "Cf", "Co", "Cn", "Zs", "Zl", "Zp"))
|
|
795
|
+
|
|
796
|
+
|
|
797
|
+
def _blank(ch):
|
|
798
|
+
"""Whether ch prints as nothing: whitespace, a blank-looking letter, or a variation selector.
|
|
799
|
+
White space is in the blank categories too (Zs, Zl, Zp, Cc); str.isspace() only answers it sooner."""
|
|
800
|
+
if ch.isspace() or ch in _BLANK_LETTERS or unicodedata.category(ch) in _BLANK_CATEGORIES:
|
|
801
|
+
return True
|
|
802
|
+
o = ord(ch)
|
|
803
|
+
return 0xFE00 <= o <= 0xFE0F or 0xE0100 <= o <= 0xE01EF
|
|
804
|
+
|
|
805
|
+
|
|
806
|
+
def _has_visible(text):
|
|
807
|
+
"""Whether text holds a letter a person can see."""
|
|
808
|
+
return not all(_blank(ch) for ch in text)
|
|
809
|
+
|
|
810
|
+
|
|
811
|
+
CACHE_READ_MAX = 64 * 1024 # bytes: a cached summary or decision is some hundred (120 characters), so this is generous
|
|
812
|
+
|
|
813
|
+
|
|
814
|
+
def _read_small(path, limit=CACHE_READ_MAX):
|
|
815
|
+
"""The bytes of the regular file at path when it holds at most `limit`, else None (missing, a link, a pipe, a
|
|
816
|
+
folder, a larger file). Opened without blocking and without following a link, so a pipe or a link left at a
|
|
817
|
+
cache place neither stops a run nor sends it to another file. Windows has neither flag: there the lstat
|
|
818
|
+
turns a link away, but a link swapped in between the lstat and the open is followed."""
|
|
819
|
+
flags = (os.O_RDONLY | getattr(os, "O_NONBLOCK", 0) | getattr(os, "O_NOFOLLOW", 0)
|
|
820
|
+
| getattr(os, "O_BINARY", 0))
|
|
821
|
+
try:
|
|
822
|
+
if not stat.S_ISREG(os.lstat(str(path)).st_mode): # a link (Windows has no O_NOFOLLOW), pipe, folder
|
|
823
|
+
return None
|
|
824
|
+
fd = os.open(str(path), flags)
|
|
825
|
+
with os.fdopen(fd, "rb") as f:
|
|
826
|
+
if not stat.S_ISREG(os.fstat(f.fileno()).st_mode):
|
|
827
|
+
return None
|
|
828
|
+
data = f.read(limit + 1)
|
|
829
|
+
except OSError:
|
|
830
|
+
return None
|
|
831
|
+
return data if len(data) <= limit else None
|
|
832
|
+
|
|
833
|
+
|
|
834
|
+
def _read_text(path):
|
|
835
|
+
"""The UTF-8 text _read_small gives, else None (bytes that are not UTF-8: a DRM encrypted them)."""
|
|
836
|
+
data = _read_small(path)
|
|
837
|
+
try:
|
|
838
|
+
return None if data is None else data.decode("utf-8")
|
|
839
|
+
except UnicodeDecodeError:
|
|
840
|
+
return None
|
|
841
|
+
|
|
842
|
+
|
|
737
843
|
def _read_cached(p):
|
|
738
844
|
"""The summary cached at p, else at the same name with .txt, which 3.0.2 and earlier wrote.
|
|
739
|
-
None when neither
|
|
845
|
+
None when neither holds one: missing, bytes that are not UTF-8 (a DRM encrypted them), or
|
|
846
|
+
nothing a person can see (a write that stopped early, or only blank-looking letters); the
|
|
847
|
+
turn is then summarized again (3.0.4, 3.0.5)."""
|
|
740
848
|
for cached in (p, p.with_name(p.stem + ".txt")):
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
continue
|
|
849
|
+
text = (_read_text(cached) or "").strip()
|
|
850
|
+
if _has_visible(text):
|
|
851
|
+
return text
|
|
745
852
|
return None
|
|
746
853
|
|
|
747
854
|
|
|
@@ -766,9 +873,7 @@ def read_or_summarize(turn: dict, session_id: str, rebuild: bool = False,
|
|
|
766
873
|
redacted = summarize_turn(_page_clean_turn(turn, redact_extra, redact_mode))
|
|
767
874
|
if p is not None:
|
|
768
875
|
try:
|
|
769
|
-
|
|
770
|
-
with os.fdopen(fd, "w", encoding="utf-8") as f:
|
|
771
|
-
f.write(redacted)
|
|
876
|
+
_write_private(p, redacted)
|
|
772
877
|
except OSError:
|
|
773
878
|
pass
|
|
774
879
|
return redacted, False
|
|
@@ -1371,7 +1476,7 @@ def _write_private(path, text):
|
|
|
1371
1476
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
1372
1477
|
fd, tmp = tempfile.mkstemp(dir=str(path.parent), prefix=f".{path.name}.", suffix=".tmp")
|
|
1373
1478
|
try:
|
|
1374
|
-
with os.fdopen(fd, "w", encoding="utf-8") as f:
|
|
1479
|
+
with os.fdopen(fd, "w", encoding="utf-8", newline="\n") as f:
|
|
1375
1480
|
f.write(text)
|
|
1376
1481
|
os.replace(tmp, str(path))
|
|
1377
1482
|
except BaseException:
|
|
@@ -1382,6 +1487,13 @@ def _write_private(path, text):
|
|
|
1382
1487
|
raise
|
|
1383
1488
|
|
|
1384
1489
|
|
|
1490
|
+
def _write_text_lf(path, text):
|
|
1491
|
+
"""Write `text` to `path` as UTF-8 with LF line ends on every OS. Text mode on Windows
|
|
1492
|
+
would write CRLF, so the same transcript would give a different page there."""
|
|
1493
|
+
with open(str(path), "w", encoding="utf-8", newline="\n") as f:
|
|
1494
|
+
f.write(text)
|
|
1495
|
+
|
|
1496
|
+
|
|
1385
1497
|
def _safe_name(s):
|
|
1386
1498
|
"""A file-name-safe form of an id read from a transcript or a pack."""
|
|
1387
1499
|
s = re.sub(r"[^A-Za-z0-9_.-]", "_", str(s or "default"))[:80]
|
|
@@ -1417,8 +1529,8 @@ def read_llm_cache(turn_id, session, digest):
|
|
|
1417
1529
|
"every turn is reviewed")
|
|
1418
1530
|
return None
|
|
1419
1531
|
try:
|
|
1420
|
-
got = json.loads(path
|
|
1421
|
-
except (
|
|
1532
|
+
got = json.loads(_read_text(path) or "")
|
|
1533
|
+
except (ValueError, RecursionError):
|
|
1422
1534
|
return None
|
|
1423
1535
|
if not isinstance(got, dict):
|
|
1424
1536
|
return None
|
|
@@ -1700,7 +1812,7 @@ def _is_pack_turn(x):
|
|
|
1700
1812
|
def _load_pack(path):
|
|
1701
1813
|
try:
|
|
1702
1814
|
pack = json.loads(path.read_text(encoding="utf-8"))
|
|
1703
|
-
except (OSError, ValueError, UnicodeDecodeError) as e:
|
|
1815
|
+
except (OSError, ValueError, UnicodeDecodeError, RecursionError) as e:
|
|
1704
1816
|
return _bad(f"cannot read review pack {path}: {e}")
|
|
1705
1817
|
if not isinstance(pack, dict) or pack.get("schema") != REVIEW_SCHEMA:
|
|
1706
1818
|
return _bad(f"{path} is not a {REVIEW_SCHEMA} pack (made by another lineage version? "
|
|
@@ -1819,7 +1931,8 @@ def _clean_summary(s, extra, mode):
|
|
|
1819
1931
|
next reviewer and the gate's critic, so no part of a key may stay in it."""
|
|
1820
1932
|
pre, n = review_redact(" ".join(s.split()), extra)
|
|
1821
1933
|
red, found = redact(pre, extra=extra, mode=mode)
|
|
1822
|
-
|
|
1934
|
+
cut = _cut(red, SUMMARY_MAX)
|
|
1935
|
+
return (cut if _has_visible(cut) else None), n + sum(found.values())
|
|
1823
1936
|
|
|
1824
1937
|
|
|
1825
1938
|
def _warn_coverage(pack, decided):
|
|
@@ -2304,6 +2417,16 @@ def default_output(args, jsonl_path, session_id):
|
|
|
2304
2417
|
return f"work/lineage-{slug}.html"
|
|
2305
2418
|
|
|
2306
2419
|
|
|
2420
|
+
# The controls that embed, override or isolate text direction (U+202A..U+202E, U+2066..U+2069). In a page they
|
|
2421
|
+
# reorder what a reader sees and can reach past the element that holds them, so the page shows each as a visible
|
|
2422
|
+
# [U+XXXX] instead. Plain letters of right-to-left scripts, and the direction marks, stay.
|
|
2423
|
+
_BIDI_CONTROLS = re.compile("[" + "".join(map(chr, (*range(0x202A, 0x202F), *range(0x2066, 0x206A)))) + "]")
|
|
2424
|
+
|
|
2425
|
+
|
|
2426
|
+
def _show_bidi_controls(text):
|
|
2427
|
+
return _BIDI_CONTROLS.sub(lambda m: "[U+%04X]" % ord(m.group()), text)
|
|
2428
|
+
|
|
2429
|
+
|
|
2307
2430
|
def render_and_write(turns, session_id, args, all_sessions=False, markdown=True,
|
|
2308
2431
|
open_details=False, title="Session Lineage", summaries=None,
|
|
2309
2432
|
prior_redactions=None):
|
|
@@ -2333,8 +2456,8 @@ def render_and_write(turns, session_id, args, all_sessions=False, markdown=True,
|
|
|
2333
2456
|
html_doc = HTML_TEMPLATE
|
|
2334
2457
|
html_doc = html_doc.replace("{{TITLE}}", html.escape(title), 1)
|
|
2335
2458
|
html_doc = html_doc.replace("{{HEADER_TITLE}}", html.escape(title), 1)
|
|
2336
|
-
html_doc = html_doc.replace("{{DATE_RANGE}}", html.escape(date_range), 1)
|
|
2337
|
-
html_doc = html_doc.replace("{{TURNS}}", "\n".join(rows), 1)
|
|
2459
|
+
html_doc = html_doc.replace("{{DATE_RANGE}}", html.escape(_show_bidi_controls(date_range)), 1)
|
|
2460
|
+
html_doc = html_doc.replace("{{TURNS}}", _show_bidi_controls("\n".join(rows)), 1)
|
|
2338
2461
|
|
|
2339
2462
|
errs = self_verify(html_doc)
|
|
2340
2463
|
if errs:
|
|
@@ -2342,7 +2465,7 @@ def render_and_write(turns, session_id, args, all_sessions=False, markdown=True,
|
|
|
2342
2465
|
|
|
2343
2466
|
out = _with_timestamp_suffix(args.output)
|
|
2344
2467
|
out.parent.mkdir(parents=True, exist_ok=True)
|
|
2345
|
-
out
|
|
2468
|
+
_write_text_lf(out, html_doc)
|
|
2346
2469
|
|
|
2347
2470
|
_run_reviewer_gate(turns, out, session_id, args, summaries=summaries)
|
|
2348
2471
|
|
|
@@ -2586,7 +2709,7 @@ def _check_coverage(verdict, samples):
|
|
|
2586
2709
|
"""Exit 2 unless the verdict answers each of the samples in file `samples`."""
|
|
2587
2710
|
try:
|
|
2588
2711
|
gaps = _verdict_gaps(verdict, json.loads(samples.read_text(encoding="utf-8")))
|
|
2589
|
-
except (OSError, ValueError, UnicodeDecodeError, AttributeError) as e:
|
|
2712
|
+
except (OSError, ValueError, UnicodeDecodeError, AttributeError, RecursionError) as e:
|
|
2590
2713
|
gaps = f"cannot read the samples: {e}"
|
|
2591
2714
|
if gaps:
|
|
2592
2715
|
print(f"[lineage] ERROR: the verdict does not answer {samples} ({gaps}); have the "
|
|
@@ -41,11 +41,17 @@ OMC(oh-my-claudecode)๊ฐ ์ ๋ฒ์ ์ผ๋ก ์๋ ์
๋ฐ์ดํธ๋๋ฉด ๋ก์ปฌ ํ
|
|
|
41
41
|
- `--root` ๋ ํด๋์ ์ด๋ฆํ๊ฐ OMC ๊ฐ ์๋๋ฉด `OMC ํ๋ฌ๊ทธ์ธ ํด๋๊ฐ ์๋` ์ผ๋ก ๋ฉ์ถ๋ค. `.claude-plugin/plugin.json` ์ name ์ด ์์ผ๋ฉด ๊ทธ๊ฒ์ผ๋ก, ์์ผ๋ฉด `package.json` ์ name ์ผ๋ก ์ ํ๋ค. ๋ค๋ฅธ ํด๋๋ฅผ ์๋ชป ์ค ์ค์๋ฅผ ๋ง๋ ํ์ธ์ด๊ณ , ์ด๋ฆํ๊ฐ ์๋ ํด๋๋ ํต๊ณผํ๋ค.
|
|
42
42
|
- ์ธ ํ์ผ์ด ๋งํฌ๋ก ํ๋ฌ๊ทธ์ธ ํด๋ ๋ฐ์ ๊ฐ๋ฆฌํค๊ฑฐ๋ ๋ฐฑ์
์๋ฆฌ(`.omcbak`)๊ฐ ๋งํฌ๋ฉด ๋๊ตฌ๋ ์ฐ์ง ์๊ณ ์คํจ๋ก ์ผ๋ค. ๋ฌธ๊ตฌ๋ ์ ์ฉ ๋ `์ฐ์ง ์์`, ๋๋๋ฆฌ๊ธฐ ๋ `๋๋๋ฆฌ์ง ์์`, hooks.json ์ `hooks.json ์ ๊ฑด๋๋ฆฌ์ง ์์` ์ด๋ค.
|
|
43
43
|
- ํจ์น ๊ท์น์ OMC ํ
์ฝ๋์ ๋ชจ์์ ๋ง์ถฐ์ ธ ์๋ค. ์ฒ์ ๋ณด๋ OMC ๋ฒ์ ์ด๋ฉด ๋ฐ๋์ `check` ๋ฅผ ๋จผ์ ์คํํ๋ค.
|
|
44
|
-
- `์คํจ N๊ฑด` ์ด 1๊ฑด ์ด์์ด๋ฉด ๋ฉ์ถ๊ณ ์ถ๋ ฅ์ ๊ทธ๋๋ก ๋ณด๊ณ ํ๋ค. ๋ฌธ๋ฒ ์ค๋ฅ๊ฐ ๋ ํ์ผ์ ๋๊ตฌ๊ฐ ์๋ณธ์ผ๋ก ์๋ ๋กค๋ฐฑํ๋ค.
|
|
44
|
+
- `์คํจ N๊ฑด` ์ด 1๊ฑด ์ด์์ด๋ฉด ๋ฉ์ถ๊ณ ์ถ๋ ฅ์ ๊ทธ๋๋ก ๋ณด๊ณ ํ๋ค. ์์ธ๋ 2๋จ๊ณ์์ ๋จ๊ธฐ๊ธฐ๋ก ํ `PINNED` ์ค ํ๋๋ฟ์ด๋ค(3๋จ๊ณ 2). ๋ฌธ๋ฒ ์ค๋ฅ๊ฐ ๋ ํ์ผ์ ๋๊ตฌ๊ฐ ์๋ณธ์ผ๋ก ์๋ ๋กค๋ฐฑํ๋ค. ๊ณ ์น hooks.json ์ด ๋ค์ ์ฝ์ด JSON ์ด ์๋์ด๋ ์๋ณธ์ผ๋ก ๋๋๋ฆฌ๊ณ (`hooks.json ์์์ผ๋ก ๋กค๋ฐฑ`) ์คํจ๋ก ์ผ๋ค.
|
|
45
45
|
- ํ์ ์ ์ถ๋ ฅ ๋ฌธ๊ตฌ๋ก ํ๋ค. ์ข
๋ฃ ์ฝ๋ 0 ๋ง์ผ๋ก ์๋ฃ๋ผ๊ณ ๋ณด๊ณ ํ์ง ์๋๋ค.
|
|
46
46
|
- ์บ์์๋ ์ ์
์ด๋ ์ฌ๋ณผ๋ฆญ ๋งํฌ๋ก ์ด์ด์ง ๋ฒ์ ํด๋๊ฐ ์์ ์ ์๋ค(์: ์ ๋ฒ์ ํด๋๊ฐ ์ ๋ฒ์ ์ ๊ฐ๋ฆฌํด). ์ด๋ ํ ๋ฒ์ ๋ณ๊ฒฝ์ด ๋ ๋ฒ์ ํด๋์ ํจ๊ป ๋ณด์ธ๋ค. ์ ์์ด๋ค.
|
|
47
47
|
- ๋๊ตฌ๋ `os.homedir()/.claude` ๋ฅผ ์ด๋ค. `CLAUDE_CONFIG_DIR` ๋ก ์ค์ ํด๋๋ฅผ ์ฎ๊ฒผ๋ค๋ฉด ์ด ๋๊ตฌ๋ ๊ทธ ํด๋๋ฅผ ๋ณด์ง ์๋๋ค.
|
|
48
|
-
- ์ถ๋ ฅ์ origin ์ `https://์ฌ์ฉ์:ํ ํฐ@...` ์ฒ๋ผ ์๊ฒฉ์ฆ๋ช
์ด ๋ค์ด ์์ผ๋ฉด, ๋ณด๊ณ ํ ๋ `//***@` ๋ก ๊ฐ๋ฆฐ๋ค. ์ถ๋ ฅ์ ๊ทธ๋๋ก ๋ณด๊ณ ํ๋ผ๋ ๋จ๊ณ์์๋ ์ด ๊ฐ๋ฆผ์ ์งํจ๋ค. ๊ทธ URL ์ `PINNED`
|
|
48
|
+
- ์ถ๋ ฅ์ origin ์ `https://์ฌ์ฉ์:ํ ํฐ@...` ์ฒ๋ผ ์๊ฒฉ์ฆ๋ช
์ด ๋ค์ด ์์ผ๋ฉด, ๋ณด๊ณ ํ ๋ `//***@` ๋ก ๊ฐ๋ฆฐ๋ค. ์ถ๋ ฅ์ ๊ทธ๋๋ก ๋ณด๊ณ ํ๋ผ๋ ๋จ๊ณ์์๋ ์ด ๊ฐ๋ฆผ์ ์งํจ๋ค. ์ ํ์ ๋๊ตฌ๊ฐ ๋ง๋ `PINNED` ์๋ ๊ทธ URL ์ด ๋จ์ ์์ ์ ์๋ค. `--check` ๋ ๊ทธ ์นธ์ `[!] ๊ณ ์ ๊ธฐ๋ก(PINNED)์ ์ URL ์นธ์ด ๋จ์ ์์ (์ ์ฉํ๋ฉด ์ง์)` ์ผ๋ก ์๋ฆฌ๊ณ ๋ชจ๋ ์ ์ฉ๋ ์ํ๋ก ๋ณด์ง ์๋๋ค. ์ ์ฉ(`--check` ๊ฐ ์๋ ์คํ)์ ๊ทธ ์นธ์ ์ง์ฐ๊ณ ๊ถํ์ 0600 ์ผ๋ก ๋ง์ถ๋ค(POSIX. Windows ๋ chmod ๊ฐ ์ฐ๊ธฐ ๋นํธ๋ง ๋ค๋ค ํด๋์ ์์ ๊ถํ์ ๋ฐ๋ฅธ๋ค). ์ด ๋๊ตฌ๊ฐ ๋ค ์ง์ธ ์ ์์ผ๋ฉด(์นธ์ด ๋ ๊ธฐ๋ก์ด ๋งํฌ, JSON ์ด ์๋, ํด๋์ ์ฐ์ง ๋ชปํจ, ์ฝ์ง ๋ชปํจ, ๊ฐ์ ํ์ผ์ ๋ค๋ฅธ ์ด๋ฆ์ด ์์) `[!] ๊ณ ์ ๊ธฐ๋ก(PINNED)์ ์ URL ์นธ์ ...` ์ค๋ก ์๋ฆฌ๊ณ ์ ์ฉ์ ์คํจ๋ก ์ผ๋ค(์ข
๋ฃ 1). ๋ค๋ฅธ ์ด๋ฆ์ด ์์ผ๋ฉด ์ ์ฉ์ ์ด ์ด๋ฆ์์๋ง ์ง์ฐ๊ณ ๋ค๋ฅธ ์ด๋ฆ์๋ ๋จ๊ธด๋ค. ๋งํฌ๋ ๋ฐ๋ผ๊ฐ ์ฐ์ง ์์ผ๋ฉฐ, ๋งํฌ ๋๋จธ์ ๊ทธ ์นธ์ด ์์ผ๋ฉด ์๋ฆฌ์ง ์๋๋ค. ๋๋๋ฆฌ๊ธฐ๋ `PINNED` ๋ฅผ ์ง์ด๋ค. ๋งํฌ ๋๋จธ์ ๊ทธ ์นธ์ด ์๊ฑฐ๋ ๋งํฌ ๋๋จธ๋ฅผ ์ฝ์ง ๋ชปํ๊ฑฐ๋, ์ง์ฐ์ง ๋ชปํ๊ฑฐ๋, ๊ฐ์ ํ์ผ์ ๋ค๋ฅธ ์ด๋ฆ์ด ์์ ๋๋ง ์คํจ๋ก ์ธ๊ณ , ๋งํฌ๊ฐ ์๋ ๊ธฐ๋ก์ JSON ์ด ์๋๊ฑฐ๋ ์ฝ์ง ๋ชปํด๋ ๊ทธ๋ฅ ์ง์ด๋ค. `PINNED` ๋ ๊ธฐ๋ก์ผ ๋ฟ์ด๋ผ ์ง์๋ ๊ณ ์ ๊ณผ ๋๋๋ฆฌ๊ธฐ์ ์ํฅ์ด ์๋ค. ์๋ฆฐ ์ค์ ์๋ `PINNED` ์ค ๋ค๋ฃจ๊ธฐ๋๋ก ๋ฌป๋๋ค. ๊ธฐ๋ก ํ์ผ์ ๋ด์ฉ์ ์ฝ์ด ๋ณด์ด์ง ์๋๋ค(์ธ์ฆ ์ ๋ณด๊ฐ ๋ค ์ ์์).
|
|
49
|
+
- `PINNED` ์ค ๋ค๋ฃจ๊ธฐ. `[!] ๊ณ ์ ๊ธฐ๋ก(PINNED)์ ์ URL ์นธ์ ...` ์ค์ด ๋์ค๋ฉด ๊ทธ ์ค์ ๊ฒฝ๋ก๋ฅผ ์ฌ์ฉ์์๊ฒ ๋ณด์ด๊ณ ๊ทธ ๊ธฐ๋ก์ ์ง์ธ์ง ๋จ๊ธธ์ง ๋ฌป๋๋ค. `--check` ์์ ๋์จ ์ค์ 2๋จ๊ณ ์น์ธ ์ง๋ฌธ์์ ํจ๊ป ๋ฌป๋๋ค. ์ ์ฉ์ด๋ ๋๋๋ฆฌ๊ธฐ์์ ์ฒ์ ๋์จ ์ค์ ๋ฉ์ถ๊ณ ๋ณด๊ณ ํ ๋ค ๋ฌป๋๋ค. ์ด๋๋ ๊ธฐ๋ก์ ๋ด์ฉ์ ์ฝ์ง ์๊ณ ๊ฒฝ๋ก๋ง ๋ณด์ธ๋ค.
|
|
50
|
+
- ํด๋ ๊ถํ์ด ์์ธ(`์ฐ์ง ๋ชปํจ`)์ด๋ฉด ๊ถํ์ ๊ณ ์น ์ง๋ ๋ฌป๋๋ค.
|
|
51
|
+
- ๋งํฌ๋ฉด URL ์นธ์ ๋งํฌ๊ฐ ๊ฐ๋ฆฌํค๋ ํ์ผ์ ์๋ค. ๊ทธ ํ์ผ์ ๊ฒฝ๋ก๋ ๋ณด์ธ๋ค. ๊ฒฝ๋ก๋ `node -e "console.log(require('fs').realpathSync(process.argv[1]))" "<๋งํฌ ๊ฒฝ๋ก>"` ๋ก ๊ตฌํ๋ค(bash, PowerShell ๊ฐ์). ๊ทธ ํ์ผ์ด `PINNED` ๊ธฐ๋ก์ด ๋ง๋์ง๋ ์ฌ์ฉ์๊ฐ ์ง์ ์ด์ด ํ์ธํ ๋ค ๋ตํ๊ฒ ํ๋ค(๋งํฌ๋ ๊ธ์๋ง ๋ณด๊ณ ๋ง์ผ๋ฏ๋ก ๋ฌด๊ดํ ํ์ผ์ผ ์ ์๋ค).
|
|
52
|
+
- ๊ฐ์ ํ์ผ์ ๋ค๋ฅธ ์ด๋ฆ์ด ์๋ค๋ ์ค์ด๋ฉด ๊ทธ ์๋ `๋ค๋ฅธ ์ด๋ฆ์ ... ๋ก ์ฐพ์ต๋๋ค.` ์ค์ ๋ฒํธ๋ก ๋ค๋ฅธ ์ด๋ฆ์ ์ฐพ์ ๊ฒฝ๋ก๋ฅผ ๋ณด์ธ๋ค. ์ฐพ์ ์ด๋ฆ ์ค `PINNED` ๊ฐ ์๋ ๊ฒ์ด ๋ค๋ฅธ ์ด๋ฆ์ด๋ค. POSIX ๋ `find "<๋งจ ์ ํด๋>" -xdev -inum <๋ฒํธ> 2>/dev/null` ์ด๊ณ , ๋งจ ์ ํด๋๋ `df -P "<PINNED ๊ฐ ๋ ํด๋>"` ๋์งธ ์ค์ ๋ง์ง๋ง ์นธ์ด๋ค. Windows ๋ `fsutil file queryFileNameById <๋๋ผ์ด๋ธ>\ <๋ฒํธ>` ์ด๋ค(๊ด๋ฆฌ์ ์
ธ์์๋ง ํ์ธํจ). ์ฐพ์ง ๋ชปํ๋ฉด(๊ถํ์ผ๋ก ๋ชป ๋ณธ ํด๋ ๋ฑ) ๋ฒํธ๋ฅผ ๊ทธ๋๋ก ์ฌ์ฉ์์๊ฒ ์๋ฆฐ๋ค.
|
|
53
|
+
- ์ฌ์ฉ์๊ฐ ์ง์ฐ๊ธฐ๋ฅผ ๊ณ ๋ฅด๋ฉด ๊ทธ๊ฒ์ด ์น์ธ์ด๋ค. ๋ณด์ธ ๊ฒฝ๋ก๋ง ์ง์ด๋ค. ๋งํฌ๋ฉด ๋งํฌ์ ๊ทธ๊ฒ์ด ๊ฐ๋ฆฌํค๋ ํ์ผ์, ๋ค๋ฅธ ์ด๋ฆ์ด ์์ผ๋ฉด `PINNED` ์ ์ฐพ์ ๋ค๋ฅธ ์ด๋ฆ์ ๋ชจ๋ ์ง์ด๋ค. ํ๋๋ผ๋ ๋จ๊ธฐ๋ฉด ๊ทธ ์์ URL ์ด ๋จ๋๋ค. ๋งํฌ๊ฐ ๊ฐ๋ฆฌํค๋ ํ์ผ์ด `PINNED` ๊ธฐ๋ก์ด ์๋๋ผ๊ณ ์ฌ์ฉ์๊ฐ ๋ตํ๋ฉด ๊ทธ ํ์ผ์ ์ง์ฐ์ง ์๊ณ ๋งํฌ๋ง ์ง์ธ์ง ๋ฌป๋๋ค.
|
|
54
|
+
- ๋จ๊ธฐ๊ธฐ๋ก ํ๋ฉด ๊ทธ ์ค์ ์์๋ ์คํจ๋ค. ์ ์ฉ์์๋ 3๋จ๊ณ 2 ์ ์์ธ, ๊ฒ์ฆ์์๋ 4๋จ๊ณ 1 ์ ์์ธ๋ฅผ ๋ฐ๋ฅธ๋ค. ๋ค๋ฅธ ์ด๋ฆ์ด ์๋ ๊ธฐ๋ก์ ๋จ๊ธฐ๊ณ ์ ์ฉํ๋ฉด ์ ์ฉ์ `PINNED` ์ ์นธ๋ง ์ง์ด๋ค. ๊ทธ ๋ค `--check` ๋ ๋ค๋ฅธ ์ด๋ฆ์ ๋ณด์ง ๋ชปํ๋ 5๋จ๊ณ ๋ณด๊ณ ์ ๊ทธ ๊ฒฝ๋ก๋ฅผ ์ ๋๋ค.
|
|
49
55
|
|
|
50
56
|
## ์ ์ฐจ
|
|
51
57
|
|
|
@@ -70,23 +76,30 @@ node "<์ด ์คํฌ ๋๋ ํฐ๋ฆฌ ์ ๋๊ฒฝ๋ก>/scripts/omc-patch.mjs" --check --no
|
|
|
70
76
|
|
|
71
77
|
- `ํ์ฑ ๋ฒ์ : X (installed_plugins.json)` ์ค๋ก ์ค์ ํ์ฑ ๋ฒ์ ์ ํ์ธํ๋ค.
|
|
72
78
|
- ๊ทธ ์ค์ด `(์ถ์ : ...)` ๋ก ๋๋๋ฉด ๋๊ตฌ๊ฐ `installed_plugins.json` ์์ ํ์ฑ ๋ฒ์ ์ ์ฐพ์ง ๋ชปํ ๊ฒ์ด๋ค. ์ ์ฉํ์ง ์๊ณ ์ถ๋ ฅ์ ๊ทธ๋๋ก ๋ณด๊ณ ํ๊ณ ๋๋ธ๋ค. ์ถ์ ํ ํด๋๋ฅผ ํจ์นํ๋ฉด ์ค์ ํ์ฑ ๋ฒ์ ์ ๊ทธ๋๋ก ๋จ๋๋ฐ `check` ๋ ์ ์ฉ๋ ๊ฒ์ผ๋ก ๋ณด์ผ ์ ์๋ค.
|
|
73
|
-
- `ํจ์น ํ์ N๊ฑด` ์ ์ค์น๋ ๋ชจ๋ ๋ฒ์ ํด๋๋ฅผ ํฉ์น ์๋ค. ํ์ ์
|
|
79
|
+
- `ํจ์น ํ์ N๊ฑด` ์ ์ค์น๋ ๋ชจ๋ ๋ฒ์ ํด๋๋ฅผ ํฉ์น ์๋ค. ํ์ ์ `๊ฒฐ๊ณผ:` ์ค๋ก ํ๋ค. ๊ทธ ์๋ `์ฐธ๊ณ :` ์ค์ ์๋ ํญ๋ชฉ๋๋ก ๋ค๋ฃฌ๋ค.
|
|
74
80
|
|
|
75
81
|
| `๊ฒฐ๊ณผ:` ์ค | ๋ค์ |
|
|
76
82
|
|---|---|
|
|
77
83
|
| `๋ชจ๋ ์ ์ฉ๋ ์ํ์
๋๋ค.` | 4๋จ๊ณ ๊ฒ์ฆ์ผ๋ก ๊ฑด๋๋ |
|
|
78
84
|
| `ํฌํผ ์๋ฆฌ ๊ฒฐํจ N๊ฑด์ด ์์ต๋๋ค.` | 2๋จ๊ณ ํ์ธ ๋ค 3๋จ๊ณ์์ ํ์ฑ ํด๋๋ถํฐ ๋๋๋ฆผ |
|
|
79
85
|
| `์ ์ฉ๋์ง ์์ ํญ๋ชฉ์ด ์์ต๋๋ค.` | 2๋จ๊ณ ํ์ธ ๋ค 3๋จ๊ณ ์ ์ฉ |
|
|
86
|
+
| `hooks.json ์ ํ์ธํ์ง ๋ชปํ์ต๋๋ค.` | ์ ์ฉํ์ง ์๊ณ ์์ `[!]` ์ค์ ๊ทธ๋๋ก ๋ณด๊ณ |
|
|
87
|
+
| `๋ง์ผํ๋ ์ด์ค origin ์ ํ์ธํ์ง ๋ชปํ์ต๋๋ค.` | ์ ์ฉํ์ง ์๊ณ ์์ `[!]` ์ค์ ๊ทธ๋๋ก ๋ณด๊ณ (git ์ด ์๊ฑฐ๋ ๋ง์ผํ๋ ์ด์ค ์ ์ฅ์๋ฅผ ์ฝ์ง ๋ชปํจ) |
|
|
88
|
+
| `๊ณ ์ ๊ธฐ๋ก(PINNED)์ ์ URL ์นธ์ด ์ด ๋๊ตฌ๋ก ์ง์ธ ์ ์๋ ๊ณณ์ ์์ต๋๋ค.` | ์ ์ฉํ ํญ๋ชฉ์ ์๋ค. 2๋จ๊ณ์์ ๊ทธ ๊ธฐ๋ก์ ์ง์ธ์ง ๋จ๊ธธ์ง๋ง ๋ฌป๋๋ค(`๊ท์น` ์ ์ `PINNED` ์ค ๋ค๋ฃจ๊ธฐ). ์ด๋ ์ชฝ์ด๋ ๊ทธ ๋ค 4๋จ๊ณ ๊ฒ์ฆ์ผ๋ก ๊ฑด๋๋ |
|
|
80
89
|
|
|
81
90
|
- `์ฐธ๊ณ : ๋นํ์ฑ ๋ฒ์ ...` ์ ์ง๊ธ ์ฐ์ง ์๋ ๋ฒ์ ์ด๋ผ ํ์ ์ ๋ค์ด๊ฐ์ง ์๋๋ค.
|
|
91
|
+
- `๊ฒฐ๊ณผ:` ์ค ์๋์ `์ฐธ๊ณ : ๊ณ ์ ๊ธฐ๋ก(PINNED)์ ์ URL ์นธ์ด ์ด ๋๊ตฌ๋ก ์ง์ธ ์ ์๋ ๊ณณ์ ์์ต๋๋ค.` ๋ ์ ์ฉํ ํญ๋ชฉ๋ ์๋ค๋ ๋ป์ด๋ค. ์ ์ฉํด๋ ๊ทธ๊ณณ์ ์นธ์ ๋จ๊ณ ์ ์ฉ์ `์คํจ 1๊ฑด` ์ผ๋ก ๋๋๋ค. 2๋จ๊ณ์์ ํจ๊ป ๋ฌป๋๋ค. ์ ํ์ ๊ฐ์ ๋ฌธ๊ตฌ ๊ฒฐ๊ณผ ์ค์ ์ ์ฉํ ํญ๋ชฉ ์์ด ์ด๊ฒ๋ง ๋จ์์ ๋ ๋์จ๋ค.
|
|
82
92
|
- ์ธ์๊ฐ `check` ์ด๋ฉด ์ฌ๊ธฐ์ ๊ฒฐ๊ณผ๋ง ๋ณด๊ณ ํ๊ณ ๋๋ธ๋ค.
|
|
83
93
|
|
|
84
94
|
### 2. ํ์ธ (์ ์ฉ ์ ํ์)
|
|
85
95
|
|
|
86
|
-
`--check` ๊ฒฐ๊ณผ๋ฅผ ๋ณด์ฌ ์ฃผ๊ณ ์ฌ์ฉ์์๊ฒ ์ ์ฉํด๋ ๋๋์ง ๋ฌป๋๋ค. ๋ฌป๊ธฐ ์ ์ ์๋๋ฅผ ์๋ฆฐ๋ค.
|
|
96
|
+
`--check` ๊ฒฐ๊ณผ๋ฅผ ๋ณด์ฌ ์ฃผ๊ณ ์ฌ์ฉ์์๊ฒ ์ ์ฉํด๋ ๋๋์ง ๋ฌป๋๋ค. ๋ฌป๊ธฐ ์ ์ ์๋๋ฅผ ์๋ฆฐ๋ค. 1๋จ๊ณ ๊ฒฐ๊ณผ๊ฐ ์ ํ์ `PINNED` ์ค์ด๋ฉด ์ ์ฉํ ํญ๋ชฉ์ด ์๋ค. ์ ์ฉ์ ๋ฌป์ง ์๊ณ ์๋ `PINNED` ํญ๋ชฉ๋ง ํ๋ค.
|
|
87
97
|
- ํ์ฑ OMC ๋ฒ์ ์ `scripts/*.mjs` ์ `hooks/hooks.json` ์ ๊ณ ์น๋ค. ์๋ณธ์ `.omcbak` ๋ก ๋จ๋๋ค.
|
|
88
98
|
- ํฌํผ ์๋ฆฌ ๊ฒฐํจ์ด ์์ผ๋ฉด ํ์ฑ ํด๋๋ฅผ `.omcbak` ์๋ณธ์ผ๋ก ๋จผ์ ๋๋๋ฆฐ ๋ค ๋ค์ ํจ์นํ๋ค.
|
|
89
|
-
- OMC ๋ง์ผํ๋ ์ด์ค `origin` ์ `omc-pinned://` ๋ก
|
|
99
|
+
- OMC ๋ง์ผํ๋ ์ด์ค `origin` ์ `omc-pinned://` ๋ก ๋ฐ๊พผ๋ค(`์ด๋ฏธ ๊ณ ์ ๋จ` ์ด๋ฉด ๊ทธ๋๋ก ๋๋ค). OMC ๊ฐ ๋ ์ด์ ์๋ ์
๋ฐ์ดํธ๋์ง ์๋๋ค. ๋๋๋ฆฌ๊ธฐ๋ `revert`.
|
|
100
|
+
- 1๋จ๊ณ์ `[!] ๊ณ ์ ๊ธฐ๋ก(PINNED)์ ์ URL ์นธ์ ...` ์ค์ด ์์์ผ๋ฉด ๊ทธ ๊ธฐ๋ก์ ์ด๋ป๊ฒ ํ ์ง ํจ๊ป ๋ฌป๋๋ค(`๊ท์น` ์ ์ `PINNED` ์ค ๋ค๋ฃจ๊ธฐ).
|
|
101
|
+
- ์ ์ฉํ ํญ๋ชฉ๋ ์์ผ๋ฉด ๋ต์ ์
์ด๋ค. ์ง์ฐ๊ณ ์ ์ฉ(์น์ธํ ํ์ผ์ ์ง์ด ๋ค 3๋จ๊ณ๋ก, ์ ์ฉ์ ์คํจ 0๊ฑด), ๋จ๊ธฐ๊ณ ์ ์ฉ(3๋จ๊ณ๋ก, ๊ทธ ์ค ํ๋๊ฐ ์คํจ๋ก ๋จ์ `์คํจ 1๊ฑด`), ์ ์ฉํ์ง ์์(์ฌ๊ธฐ์ ๋๋).
|
|
102
|
+
- 1๋จ๊ณ ๊ฒฐ๊ณผ๊ฐ ์ ํ์ `PINNED` ์ค์ด๋ฉด ์ ์ฉํ ํญ๋ชฉ์ด ์๋ค. ์ ์ฉ์ ๋ฌป์ง ์๊ณ ์ง์ธ์ง ๋จ๊ธธ์ง๋ง ๋ฌป๋๋ค. ์ด๋ ์ชฝ์ด๋ ๊ทธ ๋ค 4๋จ๊ณ๋ก ๊ฑด๋๋ด๋ค.
|
|
90
103
|
- ์น์ธ์ด ์์ผ๋ฉด ์ ์ฉํ์ง ์๋๋ค.
|
|
91
104
|
|
|
92
105
|
### 3. ์ ์ฉ
|
|
@@ -115,12 +128,13 @@ node "<์ด ์คํฌ ๋๋ ํฐ๋ฆฌ ์ ๋๊ฒฝ๋ก>/scripts/omc-patch.mjs" --check --no
|
|
|
115
128
|
```
|
|
116
129
|
|
|
117
130
|
- `ํจ์น N๊ฑด / ์คํจ 0๊ฑด`, `[3/4]` ์ PostToolUse ์ ๊ฑฐ, `[4/4]` ์ ๊ณ ์ ๊ฒฐ๊ณผ๋ฅผ ํ์ธํ๋ค.
|
|
118
|
-
-
|
|
131
|
+
- ๋ ์ค `์๋ฃ. ์คํจ N๊ฑด.` ์ N ์ด 1 ์ด์์ด๋ฉด ๋ฉ์ถ๊ณ ์ถ๋ ฅ ๊ทธ๋๋ก ๋ณด๊ณ ํ๋ค. ์์ธ๋ ํ๋๋ค. `์๋ฃ. ์คํจ 1๊ฑด.` ์ด๊ณ ๊ทธ 1๊ฑด์ด 2๋จ๊ณ์์ ๋จ๊ธฐ๊ธฐ๋ก ํ `PINNED` ์ `[!]` ์ค์ด๋ฉด ๋ฉ์ถ์ง ์๊ณ 4๋จ๊ณ๋ก ๊ฐ๋ค. ๋์ `[!] PostToolUse ํ
์ ๊ฑฐ๋ ...` ์ค์ ์ฌ์์ ์๋ด๋ผ ์คํจ๊ฐ ์๋๋ค.
|
|
132
|
+
- `์๋ฃ. ์คํจ 1๊ฑด.` ์ด๊ณ ๊ทธ 1๊ฑด์ด 1๋จ๊ณ์ ์๋ `[!] ๊ณ ์ ๊ธฐ๋ก(PINNED)` ์ค์ด๋ฉด ๋ฉ์ถ๊ณ ๋ณด๊ณ ํ ๋ค `PINNED` ์ค ๋ค๋ฃจ๊ธฐ๋๋ก ๋ฌป๋๋ค. ํ
ํจ์น๋ ์ด๋ฏธ ์ ์ฉ๋ผ ์๋ค. ์ง์ฐ๊ธฐ๋ก ํ๋ฉด ์ง์ด ๋ค, ๋จ๊ธฐ๊ธฐ๋ก ํ๋ฉด ๊ทธ๋๋ก 4๋จ๊ณ๋ก ๊ฐ๋ค.
|
|
119
133
|
- `[!] ํฌํผ ์๋ฆฌ ๊ฒฐํจ N๊ฑด์ ๊ณ ์น์ง ์์์ต๋๋ค.` ๊ฐ ๋์ค๋ฉด 1 ์ ๊ฑด๋๋ฐ์๊ฑฐ๋ 1 ์ด ๊ทธ ํ์ผ์ ๋๋๋ฆฌ์ง ๋ชปํ ๊ฒ์ด๋ค. 1 ๊ณผ 2 ๋ฅผ ํ ๋ฒ๋ง ๋ค์ ํ๋ค. ๋ค์ ํด๋ ๊ฐ์ผ๋ฉด ๋ฉ์ถ๊ณ ๋ณด๊ณ ํ๋ค.
|
|
120
134
|
|
|
121
135
|
### 4. ๊ฒ์ฆ (๋ค์ฏ ๊ฐ์ง ๋ชจ๋)
|
|
122
136
|
|
|
123
|
-
1. ๋ค์ ์ง๋จํ๋ค. `๊ฒฐ๊ณผ: ๋ชจ๋ ์ ์ฉ๋ ์ํ์
๋๋ค.` ๊ฐ ๋์์ผ ํ๊ณ , ํ์ฑ ๋ฒ์ ์ `ํฌํผ ์๋ฆฌ ๊ฒฐํจ` ์ค์ด ์์ด์ผ ํ๋ค.
|
|
137
|
+
1. ๋ค์ ์ง๋จํ๋ค. `๊ฒฐ๊ณผ: ๋ชจ๋ ์ ์ฉ๋ ์ํ์
๋๋ค.` ๊ฐ ๋์์ผ ํ๊ณ , ํ์ฑ ๋ฒ์ ์ `ํฌํผ ์๋ฆฌ ๊ฒฐํจ` ์ค์ด ์์ด์ผ ํ๋ค. ์ฌ์ฉ์๊ฐ `PINNED` ๋ฅผ ๋จ๊ธฐ๊ธฐ๋ก ํ์ผ๋ฉด `๊ฒฐ๊ณผ: ๊ณ ์ ๊ธฐ๋ก(PINNED)์ ์ URL ์นธ์ด ์ด ๋๊ตฌ๋ก ์ง์ธ ์ ์๋ ๊ณณ์ ์์ต๋๋ค.` ์ ๊ทธ `[!]` ์ค์ด ๋์ค๋ ๊ฒ์ ์ ์์ด๋ค. ๋๋จธ์ง ๊ฒ์ฆ์ ๊ณ์ํ๋ค.
|
|
124
138
|
|
|
125
139
|
```bash
|
|
126
140
|
node "<์ด ์คํฌ ๋๋ ํฐ๋ฆฌ ์ ๋๊ฒฝ๋ก>/scripts/omc-patch.mjs" --check --no-update
|
|
@@ -184,17 +198,22 @@ node "<์ด ์คํฌ ๋๋ ํฐ๋ฆฌ ์ ๋๊ฒฝ๋ก>/scripts/omc-patch.mjs" --check --no
|
|
|
184
198
|
| ๊ทธ ๋ฐ์ `probe-pm:` ์ค(์ข
๋ฃ ์ฝ๋ 2) | ๋ช
๋ น ์ค๋ฅ๋ค. ๊ฒฝ๋ก๋ฅผ ๋ค์ ๊ตฌํด ํ ๋ฒ๋ง ๋ค์ ์คํํ๋ค. ๊ฐ์ผ๋ฉด ์ถ๋ ฅ์ ๋ณด๊ณ ํ๊ณ ๋ฉ์ถ๋ค |
|
|
185
199
|
| ํ์ ์ค ๋ค์ `probe-pm: ์์ ํด๋๋ฅผ ์ง์ฐ์ง ๋ชปํจ` | ํ์ ์ ๊ทธ๋๋ก ์ด๋ค. ๋จ์ ์์ ํด๋ ๊ฒฝ๋ก๋ฅผ ๋ณด๊ณ ์ ์ ์ด ์ฌ์ฉ์๊ฐ ์ง์ฐ๊ฒ ํ๋ค |
|
|
186
200
|
|
|
187
|
-
5. ๋ง์ผํ๋ ์ด์ค ๊ณ ์ ์ ํ์ธํ๋ค. `omc-pinned://` ๋ก
|
|
201
|
+
5. ๋ง์ผํ๋ ์ด์ค ๊ณ ์ ์ ํ์ธํ๋ค. ๋ช
๋ น์ origin ์ด `omc-pinned://` ๋ก ์์ํ๋์ง๋ง ๋ณด๊ณ `pinned` ๋ `not-pinned` ํ ๋จ์ด๋ฅผ ๋ธ๋ค. origin ์ ์๊ฒฉ ์ฆ๋ช
๊ณผ ์ฟผ๋ฆฌ ๊ฐ์ ์ถ๋ ฅ์ ๋์ค์ง ์๋๋ค. `pinned` ๋ฉด ๊ณ ์ ๋ ๊ฒ์ด๋ค.
|
|
188
202
|
|
|
189
203
|
```bash
|
|
190
|
-
git -C "$HOME/.claude/plugins/marketplaces/omc"
|
|
204
|
+
case "$(git -C "$HOME/.claude/plugins/marketplaces/omc" config --get remote.origin.url)" in omc-pinned://*) echo pinned;; *) echo not-pinned;; esac
|
|
191
205
|
```
|
|
192
206
|
|
|
193
|
-
PowerShell
|
|
207
|
+
PowerShell:
|
|
208
|
+
|
|
209
|
+
```powershell
|
|
210
|
+
if ((git -C "$env:USERPROFILE/.claude/plugins/marketplaces/omc" config --get remote.origin.url) -like 'omc-pinned://*') { 'pinned' } else { 'not-pinned' }
|
|
211
|
+
```
|
|
194
212
|
|
|
195
213
|
### 5. ๋ณด๊ณ
|
|
196
214
|
|
|
197
215
|
- ํ์ฑ ๋ฒ์ , ํจ์น ๊ฑด์, ํฌํผ ์๋ฆฌ ๊ฒฐํจ ์ฒ๋ฆฌ ์ฌ๋ถ, PostToolUse ์ ๊ฑฐ ๋ชฉ๋ก, ๊ณ ์ ์ํ, project-memory ํ์ธ ๊ฒฐ๊ณผ
|
|
216
|
+
- `PINNED` ์ค์ด ์์์ผ๋ฉด ์ด๋ป๊ฒ ํ๋์ง(์ง์ด ๊ฒฝ๋ก, ๋๋ ๋จ๊ธด ๊ธฐ๋ก๊ณผ ๊ทธ ๊ฒฝ๋ก. ๋ค๋ฅธ ์ด๋ฆ์ด ์๋ ๊ธฐ๋ก์ ๋ค๋ฅธ ์ด๋ฆ์ ๊ฒฝ๋ก)
|
|
198
217
|
- ํ
์คํฌ๋ฆฝํธ ํจ์น๋ ๋ฐ๋ก ์ ์ฉ๋๋ค. ํ
์ด ํธ์ถ๋ ๋๋ง๋ค ํ์ผ์ ์๋ก ์ฝ๊ธฐ ๋๋ฌธ์ด๋ค.
|
|
199
218
|
- PostToolUse ์ ๊ฑฐ๋ `hooks.json` ๋ณ๊ฒฝ์ด๋ผ ๊ฐ ์ธ์
์ ๋ค์ ์์ํด์ผ ์ ์ฉ๋๋ค.
|
|
200
219
|
- Windows ์์ ์ด๋ฏธ ์์ธ ์ปค๋ ํ์ ์ฌ๋ถํ
์ผ๋ก๋ง ํ์๋๋ค. ์ฌ๋ถํ
์ ์ฌ์ฉ์๊ฐ ์ง์ ํ๋ค.
|
|
@@ -209,6 +228,7 @@ node "<์ด ์คํฌ ๋๋ ํฐ๋ฆฌ ์ ๋๊ฒฝ๋ก>/scripts/omc-patch.mjs" --revert
|
|
|
209
228
|
- `.omcbak` ๋ฐฑ์
์ผ๋ก ์ค์น๋ ๋ชจ๋ ๋ฒ์ ์ ํ
์คํฌ๋ฆฝํธ์ `hooks.json` ์ ๋๋๋ฆฌ๊ณ ๋ง์ผํ๋ ์ด์ค `origin` ์ ์๋ URL ๋ก ๋ณต๊ตฌํ๋ค.
|
|
210
229
|
- ๋๋๋ฆฐ ๋ค Claude Code ๋ฅผ ๋ค์ ์์ํด์ผ ๋ฐ์๋๋ค.
|
|
211
230
|
- `๋๋๋ฆฌ๊ธฐ๋ฅผ ๋๋ด์ง ๋ชปํ์ต๋๋ค.` ๊ฐ ๋์ค๋ฉด ์์ `[!]` ์ค์ ๊ทธ๋๋ก ๋ณด๊ณ ํ๋ค. ์ข
๋ฃ ์ฝ๋๋ 1 ์ด๋ค. ๋ฐฑ์
์ด ์์ด ๋จ์ ํ์ผ๊ณผ hooks.json ๋ ์ด ๋ ์ค๋ก ๋๋๋ค.
|
|
231
|
+
- ๊ณ ์ ๊ธฐ๋ก(`PINNED`)์ ์ง์ฐ์ง ๋ชปํ๋ฉด(์ฝ๊ธฐ ์ ์ฉ ํด๋, ํด๋์ธ `PINNED`) `[!] ๊ณ ์ ๊ธฐ๋ก(PINNED)์ ์ง์ฐ์ง ๋ชปํจ` ์ผ๋ก ์๋ฆฌ๊ณ ์คํจ๋ก ์ผ๋ค. ๋งํฌ์ธ `PINNED` ๋๋จธ์ ์ URL ์นธ์ด ์์ผ๋ฉด ๋งํฌ๋ฅผ ๋๊ณ `[!]` ๋ก ์๋ฆฐ๋ค. ์ URL ์นธ์ด ๋ ๊ธฐ๋ก์ ๊ฐ์ ํ์ผ์ ๋ค๋ฅธ ์ด๋ฆ์ด ์์ผ๋ฉด ์ด ์ด๋ฆ๋ง ์ง์ฐ๊ณ `[!]` ๋ก ์๋ฆฐ๋ค. ์
๋ค origin ์ ๊ทธ๋๋ก ๋๋๋ฆฐ๋ค. ์ `๊ท์น` ์ ์ `PINNED` ์ค ๋ค๋ฃจ๊ธฐ๋๋ก ์ฌ์ฉ์์๊ฒ ๋ฌป๋๋ค.
|
|
212
232
|
- `์ด ๋๊ตฌ๊ฐ ๋ง๋ค์ง ์์ ์์ ์ด ๋จ์ ์์ต๋๋ค` ๊ฐ ๋์ค๋ฉด ๋ฐฑ์
์ด ์๋ ์ ์์ ์ด๋ค. ์ถ๋ ฅ์ ์ ํ ๋ณต๊ตฌ ๋ช
๋ น์ ๊ทธ๋๋ก ๋ณด๊ณ ํ๊ณ , ์ฌ์ฉ์ ์น์ธ ์์ด ์คํํ์ง ์๋๋ค.
|
|
213
233
|
|
|
214
234
|
## ํจ์
|
|
@@ -218,9 +238,15 @@ node "<์ด ์คํฌ ๋๋ ํฐ๋ฆฌ ์ ๋๊ฒฝ๋ก>/scripts/omc-patch.mjs" --revert
|
|
|
218
238
|
- ํฌํผ ์๋ฆฌ ๊ฒฐํจ์ด ๋จ์ ์ฑ `revert` ์์ด ์ ์ฉ์ ๋ค์ ๋๋ ค๋ ๋ฐ๋๋ ๊ฒ์ด ์๋ค. ์ด๋ฏธ ๊ฐ์ผ ํ์ผ์ ๊ฑด๋๋ฐ๊ธฐ ๋๋ฌธ์ด๋ค.
|
|
219
239
|
- `--root` ์ `--revert --root` ๋ ๊ทธ ํด๋๋ง ๋ค๋ฃฌ๋ค. ๊ณ ์ ํด์ ๊น์ง ํ๋ ค๋ฉด ์ธ์ ์๋ `--revert` ๋ฅผ ์ด๋ค.
|
|
220
240
|
- ๊ณ ์ ์ ํ์ง ์๊ณ OMC ๋ฅผ ์ฌ๋ฆฌ๋ ค๋ฉด ๋จผ์ `revert` ํ๋ค. ๊ทธ๋ ์ง ์์ผ๋ฉด ๋ง์ผํ๋ ์ด์ค fetch ๊ฐ ๊ณ์ ์คํจํ๋ค.
|
|
221
|
-
- ๊ณ ์ ๊ธฐ๋ก(`PINNED`)์ `~/.claude/omc-local-patches/PINNED` ์ ์๊ธด๋ค. ๊ธฐ๋ก์ผ ๋ฟ์ด๋ผ ์ง์๋ ๊ณ ์ ์ ํ๋ฆฌ์ง ์๊ณ , ์ด๋ฏธ ๊ณ ์ ๋ ์ํ์์๋ ๋ค์ ๋ง๋ค์ด์ง์ง ์๋๋ค. `revert` ๋ origin ๊ฐ์์๋ง ์๋ URL ์
|
|
241
|
+
- ๊ณ ์ ๊ธฐ๋ก(`PINNED`)์ `~/.claude/omc-local-patches/PINNED` ์ ์๊ธด๋ค. ๊ณ ์ ํ ์๊ฐ, ํ, ๊ธฐ๊ธฐ ์ด๋ฆ๋ง ๋ด๊ณ URL ์ ๋ด์ง ์๋๋ค. ์ ํ์ ๊ธฐ๋ก์ URL ์นธ์ด ์์ผ๋ฉด `--check` ๊ฐ ์๋ฆฌ๊ณ ๋ค์ ์ ์ฉ ๋ ์ง์ด๋ค. ์ง์ฐ์ง ๋ชปํ๋ฉด `[!]` ๋ก ์๋ฆฌ๊ณ ์คํจ๋ก ์ผ๋ค(๋งํฌ๋ ๋ฐ๋ผ๊ฐ ์ฐ์ง ์์). ํ๋ ๋งํฌ๋ก ์ด์ด์ง ๋ค๋ฅธ ์ด๋ฆ์๋ ๊ทธ ์นธ์ด ๋จ๋๋ค. ๋๊ตฌ๋ ๊ทธ ์ด๋ฆ์ ์ฐพ์ ๋ฒํธ๋ฅผ ์๋ฆฐ๋ค. POSIX ์์๋ ์ด ๊ณ์ ๋ง ์ฝ์ ์ ์๋ค(0600). Windows ์์๋ ํด๋์ ์์ ๊ถํ์ ๋ฐ๋ฅธ๋ค. ๊ธฐ๋ก์ผ ๋ฟ์ด๋ผ ์ง์๋ ๊ณ ์ ์ ํ๋ฆฌ์ง ์๊ณ , ์ด๋ฏธ ๊ณ ์ ๋ ์ํ์์๋ ๋ค์ ๋ง๋ค์ด์ง์ง ์๋๋ค. `revert` ๋ origin ๊ฐ์์๋ง ์๋ URL ์ ๋์ฐพ๋๋ค. ๋ค๋ฅธ ๊ธฐ๊ธฐ์ ๊ธฐ๋ก์ด๋ฉด ์๋ฆฌ๊ธฐ๋ง ํ๋ค.
|
|
222
242
|
- `--check` ์ `--revert` ๋ฅผ ํจ๊ป ์ฃผ๋ฉด ๋๊ตฌ๋ ์๋ฌด๊ฒ๋ ํ์ง ์๊ณ ์ข
๋ฃ ์ฝ๋ 1 ๋ก ๋ฉ์ถ๋ค. ์ง๋จ๋ง ํ๋ ค๋ฉด `--check` ๋ง ์ค๋ค.
|
|
223
243
|
- ๋๊ตฌ๋ git ์ ๋ถ๋ฅผ ๋ ์ ์ฅ์๋ฅผ ๊ณ ๋ฅด๋ ๋ณ์(`GIT_DIR`, `GIT_WORK_TREE` ๋ฑ `git rev-parse --local-env-vars` ๋ชฉ๋ก)๋ฅผ ์ง์ด๋ค. git ํ
์์์ ๋ถ๋ฌ๋ ๋ง์ผํ๋ ์ด์ค ์ ์ฅ์๋ง ๋ค๋ฃฌ๋ค.
|
|
224
244
|
- ๋ง์ผํ๋ ์ด์ค์ `.git` ์ด ๊นจ์ก์ผ๋ฉด(์ค๋จ๋ clone ๋ฑ) git ์ด ์์ชฝ ์ ์ฅ์๋ก ์ฌ๋ผ๊ฐ์ง ๋ชปํ๊ฒ ๋ง๋๋ค. ๊ทธ๋ `[4/4]` ๋ `origin remote ์์` ์ผ๋ก ๋๋๊ณ ๊ณ ์ ํ์ง ์๋๋ค.
|
|
225
245
|
- `--scan` ์ ์ฝ๊ธฐ๋ง ํ๋ค. ํ๋ฌ๊ทธ์ธ ์บ์๋ `installed_plugins.json` ๋ ์ฝ์ง ์๊ณ ์ค ํ์ผ๋ง ๋ณธ๋ค.
|
|
246
|
+
- ๋๊ตฌ๋ ๋ชจ๋ฅด๋ ์ธ์๋ฅผ ๋ฐ์ง ์๋๋ค. `--check`, `--revert`, `--no-update`, `--root <ํด๋>`, `--scan <ํ์ผ>...`, `--help` ๋ฐ์ ์ธ์๋ `--root=<ํด๋>` ๊ผด์ ์ฃผ๋ฉด ์๋ฌด๊ฒ๋ ํ์ง ์๊ณ ์ฌ์ฉ๋ฒ๊ณผ ํจ๊ป ์ข
๋ฃ 1 ๋ก ๋ฉ์ถ๋ค.
|
|
247
|
+
- ์ถ๋ ฅ์ origin URL ์ ์ฌ์ฉ์ ์ด๋ฆ๊ณผ ๋น๋ฐ๋ฒํธ๊ฐ ์์ผ๋ฉด `//***@` ๋ก ๊ฐ๋ฆฐ๋ค. ๋๊ตฌ๋ origin ์ ์ค์ ๊ฐ ๊ทธ๋๋ก ์ฝ์ด ๊ณ ์ ํ๊ณ ๋๋๋ฆฐ๋ค(์ ์ญ `insteadOf` ๋ฅผ ํผ์น์ง ์์).
|
|
248
|
+
- origin ์ ๋ฐ๊พธ์ง ๋ชปํ๋ฉด(`.git` ์ฐ๊ธฐ ๊ถํ ๋ฑ) `[!] ๊ณ ์ ์คํจ` ๋ `[!] ๊ณ ์ ํด์ ์คํจ` ์ ์ข
๋ฃ 1 ๋ก ๋๋๋ค. ๊ทธ ์ค์ ๊ทธ๋๋ก ๋ณด๊ณ ํ๋ค. ๊ณ ์ ํด์ ์ ์คํจํ๋ฉด ๋๊ตฌ๊ฐ ์๋ URL ์ ํธ์คํธ๊น์ง๋ง ๋ณด์ธ๋ค. ์๋ฌธ์ origin ์ `omc-pinned://` ๋ค์ ๊ทธ๋๋ก ๋จ๋๋ค.
|
|
249
|
+
- `--check` ๋ ์ธ ์ ์๋ ํ์ผ์ `ํจ์น ํ์, ์ธ ์ ์์` ์ผ๋ก ๋ฏธ๋ฆฌ ์๋ฆฐ๋ค. ํด๋ ๋ฐ ๋งํฌ ๋๋ฌธ์ ๋๋๋ฆฌ์ง ๋ชปํ ํ์ผ์ ์ ๋ณต๊ตฌ ๋ฐฉ๋ฒ์ ํจ๊ป ๋ธ๋ค. ๋งํฌ ๋์ ํ์ผ์ ์ด ๋๊ตฌ ๊ณ์ด์ ํจ์น๊ฐ ๋ณด์ด์ง ์์ผ๋ฉด ๋ฎ์ด์ฐ๋ผ๊ณ ํ์ง ์๊ณ , ๋งํฌ๊ฐ ๊ฐ๋ฆฌํค๋ ๊ณณ์ด ๋ง๋์ง ๋จผ์ ํ์ธํ๋ผ๊ณ ํ๋ค. ์ฌ์ฉ์ ์น์ธ ์์ด ๊ทธ ๋ฐฉ๋ฒ์ ์คํํ์ง ์๋๋ค.
|
|
250
|
+
- ํ์ผ์ ์ ์์ ํ์ผ์ ๋์คํฌ๊น์ง ๋ด๋ฆฐ ๋ค rename ์ผ๋ก ๋ฐ๊พผ๋ค. ํ๋ ๋งํฌ๋ก ์ด์ด์ง ํด๋ ๋ฐ ํ์ผ์ ๋ฐ๋์ง ์๋๋ค. ์ฐ์ง ๋ชปํ ํ์ผ์ `์ฐ์ง ๋ชปํจ` ์ผ๋ก ์ด๋ฆ์ ๋๊ณ ์คํจ๋ก ์ผ๋ค.
|
|
251
|
+
- ์ฝ์ ์ ์๋ ํ
(๊ถํ, ํด๋, FIFO)์ `์ฝ์ง ๋ชปํจ: <์ด๋ฆ> (<์ฝ๋>)`, ์ธ ๊ถํ์ด ์๊ฑฐ๋ ์ผ๋ฐ ํ์ผ์ด ์๋ ํ
์ `์ฐ์ง ์์: <์ด๋ฆ> (์ธ ๊ถํ์ด ์์ ๋๋ ์ผ๋ฐ ํ์ผ์ด ์๋)` ์ผ๋ก ์๋ฆฌ๊ณ ์คํจ๋ก ์ผ๋ค. `--check` ๋ ์ฝ์ง ๋ชปํ ํ
์ด ์์ผ๋ฉด ๋ชจ๋ ์ ์ฉ๋ ์ํ๋ก ๋ณด์ง ์๋๋ค. ์ค์ ํ์ผ๋ ์ผ๋ฐ ํ์ผ๋ง ์ฝ๋๋ค. `installed_plugins.json` ์ด FIFO ์ฒ๋ผ ์ผ๋ฐ ํ์ผ์ด ์๋๋ฉด ํ์ฑ ๋ฒ์ ์ด `(์ถ์ : ...)` ์ผ๋ก ๋์ค๊ณ , `settings.json` ์ด๋ฉด `DISABLE_AUTOUPDATER` ์ค์ ์๋ตํ๋ค. ๋๊ตฌ๊ฐ ์ฒ๋ฆฌํ์ง ๋ชปํ ์ค๋ฅ๋ ๊ฒฝ๋ก๋ฅผ ์ ํํ ํ ์ค๊ณผ ์ข
๋ฃ 1 ๋ก ๋๋๋ค.
|
|
226
252
|
- ๋๊ตฌ์ ์ฃผ์๊ณผ ์ถ๋ ฅ ๋ฌธ๊ตฌ๋ ํ๊ธ์ด๋ค.
|