wicked-garden 12.48.0 → 12.49.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/components.json +2 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +3 -3
- package/CHANGELOG.md +16 -0
- package/README.md +24 -6
- package/docs/extending.md +10 -0
- package/package.json +1 -1
- package/scripts/editor/_pack.py +95 -0
- package/scripts/editor/conformance.py +416 -0
- package/scripts/editor/install.py +315 -0
- package/scripts/editor/pin.py +45 -0
- package/scripts/editor/scaffold.py +136 -0
- package/scripts/qe/runner/package.json +1 -1
- package/skills/core/SKILL.md +1 -0
- package/skills/core/refs/inventory-report.md +26 -0
- package/skills/editor-scaffold/SKILL.md +153 -0
- package/skills/editor-scaffold/assets/pack/README.md +32 -0
- package/skills/editor-scaffold/assets/pack/editors/__EDITOR_ID__/index.html +159 -0
- package/skills/platform/gh-cli/SKILL.md +7 -0
- package/skills/qe/SKILL.md +6 -2
- package/skills/qe/refs/accept.md +8 -8
- package/skills/qe/refs/evidence.md +1 -1
- package/skills/qe/refs/execute.md +1 -1
- package/skills/qe/refs/review.md +1 -1
- package/skills/qe/refs/setup.md +9 -5
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"_comment": "Issue #328: Component declarations for wicked-garden plugin. Claude Code plugin.json does not yet support inline component declarations beyond path overrides. This manifest documents all components for tooling, validation, and future schema support. When plugin.json gains a 'components' field, merge this data into plugin.json. The plugin is skills-only: former commands/ became actions of consolidated per-domain skills, and former agents/ became standalone context:fork worker skills (enumerated under fork_skills). Regenerate derived blocks with scripts/ci/sync_components.py.",
|
|
3
3
|
"domains": ["agentic", "crew", "data", "demo", "engineering", "jam", "mem", "persona", "platform", "product", "qe", "search"],
|
|
4
4
|
"summary": {
|
|
5
|
-
"skills":
|
|
5
|
+
"skills": 137,
|
|
6
6
|
"fork_skills": 69,
|
|
7
7
|
"hooks": 13,
|
|
8
8
|
"specialists": 7
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"domain-extractor": ["domain-extractor"],
|
|
25
25
|
"domain-modeler": ["domain-modeler"],
|
|
26
26
|
"draft": ["draft"],
|
|
27
|
+
"editor-scaffold": ["editor-scaffold"],
|
|
27
28
|
"engineering": ["architecture", "backend", "debugging", "docs", "engineering", "frontend", "integration", "large-scale-migration", "patch", "system-design", "unit-test-quality"],
|
|
28
29
|
"engineering-api-documentarian": ["engineering-api-documentarian"],
|
|
29
30
|
"engineering-migration-engineer": ["engineering-migration-engineer"],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name": "wicked-garden", "owner": {"name": "Mike Parcewski"}, "plugins": [{"name": "wicked-garden", "source": "./", "description": "Your coding agent already plans and swarms \u2014 Wicked Garden is the curated toolkit for what it can't: proof-not-claims gates, code links grep can't see, deterministic refactor, cross-session memory,
|
|
1
|
+
{"name": "wicked-garden", "owner": {"name": "Mike Parcewski"}, "plugins": [{"name": "wicked-garden", "source": "./", "description": "Your coding agent already plans and swarms \u2014 Wicked Garden is the curated toolkit for what it can't: proof-not-claims gates, code links grep can't see, deterministic refactor, cross-session memory, multi-model second opinions (fallback seats labelled). Don't fight the harness; fill its gaps. The gate needs wicked-vault (a self-contained infra peer, installed directly) and re-derives through garden's built-in engine; estate/bus are opt-in layers (wicked-testing and wicked-brain are retired \u2014 QE ships in-catalog as the qe domain, memory lives in wicked-estate).", "version": "12.49.0"}], "version": "12.49.0"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wicked-garden",
|
|
3
|
-
"version": "12.
|
|
4
|
-
"description": "Your coding agent's harness already plans and swarms; Wicked Garden is the curated toolkit for what it can't do alone. Proof, not claims \u2014 gates re-derive 'done' through the internal loom engine (fail-closed, independently attested via wicked-vault), so a self-asserted pass can't lie its way green. Relationships grep can't see \u2014 the wicked-estate code graph + injected bus/dispatch/capability/archetype edges power blast-radius & lineage. Plus deterministic multi-file refactor (wicked-patch), cross-session memory + knowledge (the mem domain over wicked-estate),
|
|
3
|
+
"version": "12.49.0",
|
|
4
|
+
"description": "Your coding agent's harness already plans and swarms; Wicked Garden is the curated toolkit for what it can't do alone. Proof, not claims \u2014 gates re-derive 'done' through the internal loom engine (fail-closed, independently attested via wicked-vault), so a self-asserted pass can't lie its way green. Relationships grep can't see \u2014 the wicked-estate code graph + injected bus/dispatch/capability/archetype edges power blast-radius & lineage. Plus deterministic multi-file refactor (wicked-patch), cross-session memory + knowledge (the mem domain over wicked-estate), multi-model second opinions (fallback seats labelled) (jam:council), portable expertise as on-demand skill-refs, and evidence-gated testing (the in-catalog qe domain). It reads the shape of your work to apply the right rigor \u2014 steering, not a pipeline \u2014 then gets out of the way. The compiler stamps a self-contained evidence gate into any repo that runs with no wicked-garden installed. Don't fight the harness; fill its gaps. The evidence gate requires wicked-vault, a self-contained infra peer wicked-garden installs directly; wicked-bus is an opt-in layer \u2014 install what you'll use, the toolkit works without the rest.",
|
|
5
5
|
"wicked_vault_version": "^0.5.0",
|
|
6
|
-
"wicked_ledger_version": "^0.
|
|
6
|
+
"wicked_ledger_version": "^0.6.0",
|
|
7
7
|
"wicked_bus_version": "^2.0.0",
|
|
8
8
|
"author": {
|
|
9
9
|
"name": "Mike Parcewski",
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [12.49.0] — 2026-10-11
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- **`wicked-garden-editor-scaffold`: build, check and install an artifact editor plugin (X3, PR #1269).** A new skill plus `scripts/editor/`.
|
|
9
|
+
- **Inventory-report codebook (crew#721, PR #1266).** An enumerating step says whether its list is complete.
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
- **QE acceptance inside a governed crew run records into the run's ledger, not the worktree (wicked-crew#951, PR #1270).** When `WICKED_QE_LEDGER_DIR` is set, it is the ledger root. A governed run hands its QE unit the run's own ledger, outside the worktree, this way.
|
|
13
|
+
- The preflight config check, setup's scaffold and `config.json`, accept's evidence dir, the executor and reviewer briefs, and the vault-record argument all use that root.
|
|
14
|
+
- accept resolves it through wicked-ledger's `resolveLedgerRoot`.
|
|
15
|
+
- Before, a governed `qe accept` wrote `<worktree>/.wicked-qe`; the run's guard discarded it, the PASS with it, and delivery was refused.
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
- **`wicked_ledger_version` is `^0.6.0`** (PR #1270): `resolveLedgerRoot` honours `WICKED_QE_LEDGER_DIR`. The QE runner requires wicked-ledger `^0.5.0` (PR #1267).
|
|
19
|
+
- Docs: guidance and the site say what ships, and what blocks vs what advises (PR #1265). Python 3.15 (PR #1268).
|
|
20
|
+
|
|
5
21
|
## [12.48.0] — 2026-10-09
|
|
6
22
|
|
|
7
23
|
### Added
|
package/README.md
CHANGED
|
@@ -32,11 +32,11 @@ Most plugins try to boss them around — re-implement planning, impose a workflo
|
|
|
32
32
|
| refactors on a hope and a prayer | renames across files as a **graph operation**, not find-replace roulette → wicked-patch |
|
|
33
33
|
| forgets everything at `exit` | remembers what session 1 decided when you're in session 47 → the mem domain over wicked-estate |
|
|
34
34
|
| re-derives *how to work in this repo* every task — which file owns the bug, the wiring step, the test command | loads the repo's own playbooks (`fix-bug`/`add-feature`/`verify`…), generated from HEAD → wicked-understanding |
|
|
35
|
-
| asks *itself* for a second opinion | convenes a **
|
|
35
|
+
| asks *itself* for a second opinion | convenes a **review panel** → the jam skill's `council` action: usable external CLIs (Antigravity / Codex / …) take the seats; with fewer than two it fills them with isolated same-family workers and **labels them as weaker diversity**, and below two isolated seats it reports *no quorum* instead of synthesizing |
|
|
36
36
|
| re-derives WCAG/CWE/SOC2 from memory every time | loads the rubric on demand, ships it to any repo |
|
|
37
|
-
| grades its own homework | author ≠ executor ≠ reviewer → evidence-gated testing |
|
|
37
|
+
| grades its own homework | author ≠ executor ≠ reviewer as separate workers → evidence-gated testing (the `qe` domain) |
|
|
38
38
|
|
|
39
|
-
The throughline: **done is re-derived, not asserted
|
|
39
|
+
The throughline: **done is re-derived, not asserted** — wherever the gate actually runs. Which entry paths block and which only advise is spelled out in [What blocks, what advises](#what-blocks-what-advises).
|
|
40
40
|
|
|
41
41
|
## What it's *not*
|
|
42
42
|
|
|
@@ -106,14 +106,14 @@ Optional, lights up the code graph: **wicked-estate** (single binary; `wicked-es
|
|
|
106
106
|
## Try it
|
|
107
107
|
|
|
108
108
|
```bash
|
|
109
|
-
# Just work — the hook
|
|
109
|
+
# Just work — in Claude Code the plugin's hook reads the work's shape and suggests the rigor (advisory)
|
|
110
110
|
"implement caching for the dashboard"
|
|
111
111
|
|
|
112
112
|
# Or reach for a gap-filler on purpose — everything is a skill now
|
|
113
113
|
/wicked-garden-prove # re-derive "done" from evidence (fail-closed)
|
|
114
114
|
/wicked-garden-search blast-radius emit_event # impact, incl. edges grep can't see
|
|
115
115
|
/wicked-garden-engineering-patch rename oldField newField # deterministic, graph-driven
|
|
116
|
-
/wicked-garden-jam council "redis or memcached?" # a panel
|
|
116
|
+
/wicked-garden-jam council "redis or memcached?" # a panel; it says which seats were external models
|
|
117
117
|
|
|
118
118
|
# Stamp the evidence gate into ANY repo (runs with no wicked-garden installed)
|
|
119
119
|
/wicked-garden-prove compile ~/path/to/repo --trigger ci
|
|
@@ -121,6 +121,24 @@ Optional, lights up the code graph: **wicked-estate** (single binary; `wicked-es
|
|
|
121
121
|
|
|
122
122
|
---
|
|
123
123
|
|
|
124
|
+
## What blocks, what advises
|
|
125
|
+
|
|
126
|
+
"Done is re-derived" is a property of the **gate**, not of every surface the toolkit touches. What a
|
|
127
|
+
false "done" runs into depends on the host and the entry path:
|
|
128
|
+
|
|
129
|
+
| Host · entry path | Blocks | Only advises |
|
|
130
|
+
|---|---|---|
|
|
131
|
+
| **Any host — the gate, invoked** (`wicked-garden-prove`, an archetype's produces-gate → `scripts/qe/vault_gate.py`) | Re-hashes the recorded evidence and re-runs its verifier through `wicked-vault`: a false claim is **rejected**, an unresolvable backend **fails closed** (`gate: "unavailable"`). Hard gates also need an attestation under an explicit `--actor` that differs from the author's — a local label, not an authenticated identity. The verdict stops whatever acts on it. | With `--no-require`, a backend that cannot run falls back to the claim-only tracker and says so (`claim-only`, not re-derived); a working backend still re-derives. |
|
|
132
|
+
| **Claude Code — plugin hooks** (plain conversation, "just work") | `PreToolUse` denies a few specific tool calls: native `EnterPlanMode` (always — planning goes through the archetype playbooks), writes to `MEMORY.md` / the auto-memory dir, build-phase writes on a high-complexity crew project with no challenge artifacts, a `phase_manager.py approve` whose gate preflight fails, a worktree cwd leak, and — only in `strict` mode (default `warn`) — invalid task metadata (`WG_TASK_METADATA`) and orphan state writes (`WG_BUS_EMIT_LINT`). Prompts are held until `/wicked-garden-core setup` completes. | Everything else. The archetype detector **steers** (it does not gate), the Stop-time claim sentinel is a fail-open **nudge** when a "done/passing" claim has no verdict for HEAD, `TaskCompleted` never blocks, and output governance is advisory. A completion claim is **not** blocked unless a gate is run. |
|
|
133
|
+
| **Codex · OpenCode · Pi · Antigravity — skills only** (`npx wicked-installer install-<cli> wicked-garden`) | Nothing by itself: no plugin hooks are installed on those hosts. | The skill text tells the agent to run the gate; it blocks only when the agent does run it (and the launcher + `wicked-vault` are present). |
|
|
134
|
+
| **Compiled repo gate** (`/wicked-garden-prove compile <repo> --trigger hook,ci`) | `.wicked/gate.py` fails the **git pre-push hook / CI job** it is wired into, with no wicked-garden installed. | Without a trigger it is a script you run by hand. |
|
|
135
|
+
| **wicked-crew governed run** | crew's engine decides each gated phase deny-dominates, on the layers that actually ran — deterministic floors, repo checks, policies, a judge. Review moves to a distinct seat when the roster has an eligible one; team runs refuse without one. | An ordinary run on a too-small roster keeps review on the creator seat and labels it "evaluator ≠ creator not held"; a pinned floor with no distinct judge is labelled "floor only"; a phase nothing gated is "approved by default, not verified". |
|
|
136
|
+
|
|
137
|
+
Garden's own daemon `/council` endpoint is a single-model synthesis (v0.1); the multi-model panel is
|
|
138
|
+
the jam skill's `council` action above.
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
124
142
|
## Build on it
|
|
125
143
|
|
|
126
144
|
The catalog is open: ship your own domain pack — a `wicked-pack.json`
|
|
@@ -141,7 +159,7 @@ Full author guide: [`docs/extending.md`](docs/extending.md).
|
|
|
141
159
|
## Principles
|
|
142
160
|
|
|
143
161
|
- **Don't fight the harness.** Fill the gaps; never re-implement what it already does well.
|
|
144
|
-
- **Done is re-derived, not asserted.** Every gate recomputes the evidence; the gates that matter
|
|
162
|
+
- **Done is re-derived, not asserted.** Every gate recomputes the evidence; the gates that matter need an attestation recorded under an actor other than the author (a declared label, not an authenticated identity).
|
|
145
163
|
- **Steering, not blocking.** Rigor follows the shape of the work, applied only where it earns its keep.
|
|
146
164
|
- **Enforcement that travels.** The gate compiles into any repo and runs without wicked-garden present.
|
|
147
165
|
- **Borrow the harness's primitives.** Extend `TaskCreate`/`Task()`/skills/hooks — don't rebuild them.
|
package/docs/extending.md
CHANGED
|
@@ -97,6 +97,16 @@ Manifest `spec: 2` keeps every spec-1 field and adds two arrays. A spec-1 pack s
|
|
|
97
97
|
wicked-studio and first-party blocks are core presets. Example (garden's own spec-2 fixture):
|
|
98
98
|
[`tests/fixtures/packs/acme-terms/`](../tests/fixtures/packs/acme-terms/).
|
|
99
99
|
|
|
100
|
+
**Building an editor-only pack.** The `wicked-garden-editor-scaffold` skill writes one
|
|
101
|
+
(`scripts/editor/scaffold.py`), from a template that already speaks `wicked.editor/1`; `scripts/editor/pin.py`
|
|
102
|
+
re-pins the entry hash after each edit; `scripts/editor/conformance.py` runs the plugin's conformance
|
|
103
|
+
cases in headless Chromium inside studio's own conformance host page; and `scripts/editor/install.py`
|
|
104
|
+
stages the pack under `~/.wicked/editors/<name>/current/` and installs it into a wicked-crew daemon,
|
|
105
|
+
which re-runs `pack check` and the conformance cases itself before it records the editor (fail closed:
|
|
106
|
+
no headless browser is a refusal). A pack that also ships skills or blocks is not installable yet: their
|
|
107
|
+
own approval step is still to come. The governed route is the `editor-plugin` workflow (studio
|
|
108
|
+
Settings → Developer → Create an editor plugin).
|
|
109
|
+
|
|
100
110
|
## Validate: the shipped conformance gate
|
|
101
111
|
|
|
102
112
|
```bash
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wicked-garden",
|
|
3
|
-
"version": "12.
|
|
3
|
+
"version": "12.49.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Curated toolkit for what AI coding agents can't do alone: evidence-gated work, codegraph relationships, deterministic multi-file refactor, multi-model second opinions, and cross-session memory. Installs as a Claude Code plugin.",
|
|
6
6
|
"keywords": [
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Shared helpers for the editor-plugin scripts (scaffold, pin, conformance, install).
|
|
3
|
+
|
|
4
|
+
An editor plugin is a ``wicked-pack.json`` spec-2 pack (DES-artifact-editor-plugins §5.2): the
|
|
5
|
+
manifest lists ``editors[]``, each with ONE self-contained HTML ``entry`` inside the pack and that
|
|
6
|
+
file's full sha256. These helpers read such a pack; none of them writes outside the pack directory.
|
|
7
|
+
Standard library only.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import hashlib
|
|
13
|
+
import json
|
|
14
|
+
import re
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
MANIFEST_NAME = "wicked-pack.json"
|
|
18
|
+
# The editor id and pack name shape (garden's kebab names, <= 64); `wicked`/`wicked-*` is reserved
|
|
19
|
+
# for the editors that ship inside studio and is refused in every pack (schema, PK061).
|
|
20
|
+
ID_RE = re.compile(r"^[a-z0-9]+(?:-[a-z0-9]+)*$")
|
|
21
|
+
RESERVED_RE = re.compile(r"^wicked(?:$|-)")
|
|
22
|
+
KIND_RE = re.compile(r"^[a-z][a-z0-9-]{1,40}$")
|
|
23
|
+
SEMVER_RE = re.compile(r"^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$")
|
|
24
|
+
SIZES = ("inline", "pane", "full")
|
|
25
|
+
# §6.1's permission table, with the plain words the install sheet shows.
|
|
26
|
+
PERMISSIONS = {
|
|
27
|
+
"artifact.read": "to read the artifact it opens",
|
|
28
|
+
"artifact.write": "to change it, as new versions you can undo",
|
|
29
|
+
"selection.chip": "to point at parts of it in your message box",
|
|
30
|
+
"composer.draft": "to suggest what to ask the helpers (you send it)",
|
|
31
|
+
"checks.read": "to see the checks and their evidence",
|
|
32
|
+
"checks.contribute": "to point things out, marked as its own notes",
|
|
33
|
+
"sources.read": "to see the sources attached to it",
|
|
34
|
+
"media.read": "to play the recording",
|
|
35
|
+
"artifact.export": "to download it",
|
|
36
|
+
"ui.fullscreen": "to go full screen",
|
|
37
|
+
"network.media": "to load images and fonts from the internet",
|
|
38
|
+
}
|
|
39
|
+
HOST_BUNDLE_CAP = 5 * 1024 * 1024
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class PackError(Exception):
|
|
43
|
+
"""A pack that cannot be read as an editor pack, with the reason."""
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def sha256_file(path: Path) -> str:
|
|
47
|
+
return hashlib.sha256(path.read_bytes()).hexdigest()
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def read_manifest(pack: Path) -> dict:
|
|
51
|
+
path = pack / MANIFEST_NAME
|
|
52
|
+
if not path.is_file():
|
|
53
|
+
raise PackError(f"{pack} holds no {MANIFEST_NAME}")
|
|
54
|
+
try:
|
|
55
|
+
manifest = json.loads(path.read_text(encoding="utf-8"))
|
|
56
|
+
except (OSError, ValueError) as exc:
|
|
57
|
+
raise PackError(f"{path} is not JSON: {exc}") from exc
|
|
58
|
+
if not isinstance(manifest, dict):
|
|
59
|
+
raise PackError(f"{path} is not a JSON object")
|
|
60
|
+
return manifest
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def editors_of(manifest: dict) -> list[dict]:
|
|
64
|
+
editors = manifest.get("editors")
|
|
65
|
+
if manifest.get("spec") != 2 or not isinstance(editors, list) or not editors:
|
|
66
|
+
raise PackError("the pack declares no editors (wicked-pack.json spec 2 `editors[]`)")
|
|
67
|
+
return [e for e in editors if isinstance(e, dict)]
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def entry_path(pack: Path, editor: dict) -> Path:
|
|
71
|
+
"""The editor's entry file, resolved inside the pack (a path that escapes it is refused)."""
|
|
72
|
+
entry = editor.get("entry")
|
|
73
|
+
if not isinstance(entry, str) or not entry.endswith(".html"):
|
|
74
|
+
raise PackError(f"editor {editor.get('id')!r}: entry must name one .html file")
|
|
75
|
+
# A plain relative POSIX path: no absolute or drive-qualified form, no backslash, no `..` or `.`
|
|
76
|
+
# part — the same string is joined under a staging directory, so it must not climb out of one.
|
|
77
|
+
parts = entry.split("/")
|
|
78
|
+
if entry.startswith("/") or "\\" in entry or ":" in entry or any(p in ("", ".", "..") for p in parts):
|
|
79
|
+
raise PackError(f"editor {editor.get('id')!r}: entry {entry!r} must be a plain relative path inside the pack")
|
|
80
|
+
root = pack.resolve()
|
|
81
|
+
target = (pack / entry).resolve()
|
|
82
|
+
if root != target and root not in target.parents:
|
|
83
|
+
raise PackError(f"editor {editor.get('id')!r}: entry {entry} is outside the pack")
|
|
84
|
+
if not target.is_file():
|
|
85
|
+
raise PackError(f"editor {editor.get('id')!r}: entry {entry} is missing")
|
|
86
|
+
if target.stat().st_size > HOST_BUNDLE_CAP:
|
|
87
|
+
raise PackError(f"editor {editor.get('id')!r}: entry {entry} is over the host's {HOST_BUNDLE_CAP}-byte cap")
|
|
88
|
+
return target
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def write_manifest(pack: Path, manifest: dict) -> None:
|
|
92
|
+
path = pack / MANIFEST_NAME
|
|
93
|
+
tmp = path.with_suffix(".json.tmp")
|
|
94
|
+
tmp.write_text(json.dumps(manifest, indent=2) + "\n", encoding="utf-8", newline="\n")
|
|
95
|
+
tmp.replace(path)
|