@litfamily/litopencode 1.0.15 → 1.0.17
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/CHANGELOG.md +11 -0
- package/README-Ko-KR.md +11 -11
- package/README.md +11 -11
- package/dist/activation-routing.js +1 -1
- package/dist/auto-handoff.js +34 -1
- package/dist/features.js +2 -2
- package/dist/skills.js +6 -4
- package/docs/assets/readme/badge-version.svg +1 -1
- package/docs/reference-Ko-KR.md +1 -1
- package/package.json +1 -1
- package/skills/frontend-ui-ux/data/THIRD-PARTY-NOTICE.txt +1 -3
- package/skills/lit-docx/SKILL.md +80 -19
- package/skills/lit-docx/references/components.md +253 -0
- package/skills/lit-docx/references/direction-step.md +234 -0
- package/skills/lit-docx/references/examples/01-report-ko.md +160 -0
- package/skills/lit-docx/references/examples/02-brief-ko.md +58 -0
- package/skills/lit-docx/references/examples/03-manual-ko.md +107 -0
- package/skills/lit-docx/references/examples/04-proposal-ko.md +93 -0
- package/skills/lit-docx/references/examples/05-memo-ko.md +42 -0
- package/skills/lit-docx/references/examples/06-journal-en.md +84 -0
- package/skills/lit-docx/references/examples/07-report-en.md +137 -0
- package/skills/lit-docx/references/examples/08-journal-ko.md +93 -0
- package/skills/lit-docx/references/examples/09-brief-en.md +64 -0
- package/skills/lit-docx/references/page-composition.md +306 -0
- package/skills/lit-docx/references/tonalities/brief.md +104 -0
- package/skills/lit-docx/references/tonalities/journal.md +101 -0
- package/skills/lit-docx/references/tonalities/manual.md +111 -0
- package/skills/lit-docx/references/tonalities/memo.md +105 -0
- package/skills/lit-docx/references/tonalities/proposal.md +101 -0
- package/skills/lit-docx/references/tonalities/report.md +126 -0
- package/skills/lit-docx/run.py +2 -1
- package/skills/lit-docx/scripts/convert_md_to_docx.py +384 -67
- package/skills/lit-docx/scripts/craft_docx.py +204 -1
- package/skills/lit-docx/scripts/docx_design.py +2445 -0
- package/skills/lit-docx/scripts/docx_layout.py +959 -0
- package/skills/lit-docx/scripts/slop_lint.py +23 -6
- package/skills/lit-docx/templates/tonalities/brief.yaml +29 -0
- package/skills/lit-docx/templates/tonalities/journal.yaml +26 -0
- package/skills/lit-docx/templates/tonalities/manual.yaml +27 -0
- package/skills/lit-docx/templates/tonalities/memo.yaml +27 -0
- package/skills/lit-docx/templates/tonalities/proposal.yaml +27 -0
- package/skills/lit-docx/templates/tonalities/report.yaml +27 -0
- package/skills/lit-pptx/NOTICE.md +1 -1
- package/skills/lit-pptx/SKILL.md +108 -19
- package/skills/lit-pptx/pretendard-font/public/static/Pretendard-Bold.otf +0 -0
- package/skills/lit-pptx/pretendard-font/public/static/Pretendard-Regular.otf +0 -0
- package/skills/lit-pptx/references/density-and-fill.md +283 -0
- package/skills/lit-pptx/references/direction-step.md +249 -0
- package/skills/lit-pptx/references/examples/01-ops-review-ledger-ko.md +173 -0
- package/skills/lit-pptx/references/examples/02-launch-pitch-signal-en.md +153 -0
- package/skills/lit-pptx/references/examples/03-field-survey-atlas-ko.md +120 -0
- package/skills/lit-pptx/references/examples/04-lecture-chalk-ko.md +132 -0
- package/skills/lit-pptx/references/examples/05-research-talk-paper-en.md +162 -0
- package/skills/lit-pptx/references/examples/06-policy-briefing-gazette-ko.md +176 -0
- package/skills/lit-pptx/references/examples/07-annual-story-studio-en.md +131 -0
- package/skills/lit-pptx/references/examples/08-reliability-keynote-night-en.md +182 -0
- package/skills/lit-pptx/references/examples/09-status-update-ledger-en.md +147 -0
- package/skills/lit-pptx/references/examples/10-investor-pitch-signal-ko.md +136 -0
- package/skills/lit-pptx/references/examples/11-forecast-review-night-ko.md +177 -0
- package/skills/lit-pptx/references/examples/12-exhibition-proposal-studio-ko.md +149 -0
- package/skills/lit-pptx/references/layout-families.md +470 -0
- package/skills/lit-pptx/references/title-treatments.md +271 -0
- package/skills/lit-pptx/references/tonalities/atlas.md +132 -0
- package/skills/lit-pptx/references/tonalities/chalk.md +132 -0
- package/skills/lit-pptx/references/tonalities/gazette.md +137 -0
- package/skills/lit-pptx/references/tonalities/ledger.md +151 -0
- package/skills/lit-pptx/references/tonalities/night.md +148 -0
- package/skills/lit-pptx/references/tonalities/paper.md +136 -0
- package/skills/lit-pptx/references/tonalities/signal.md +148 -0
- package/skills/lit-pptx/references/tonalities/studio.md +149 -0
- package/skills/lit-pptx/scripts/compile-deck.js +68 -21
- package/skills/lit-pptx/scripts/craft-thresholds.json +45 -1
- package/skills/lit-pptx/scripts/craft_extras.py +37 -8
- package/skills/lit-pptx/scripts/deck_output.py +654 -0
- package/skills/lit-pptx/scripts/embed_fonts.py +2 -2
- package/skills/lit-pptx/scripts/layout_inventory.py +35 -2
- package/skills/lit-pptx/scripts/lib/directive-parser.js +2 -2
- package/skills/lit-pptx/scripts/lib/font-map.js +4 -4
- package/skills/lit-pptx/scripts/lib/grid-resolver.js +521 -0
- package/skills/lit-pptx/scripts/lib/layout-resolver.js +403 -21
- package/skills/lit-pptx/scripts/lib/markdown-loader.js +6 -4
- package/skills/lit-pptx/scripts/lib/render-adapter-html.js +3 -9
- package/skills/lit-pptx/scripts/lib/render-adapter-pptx.js +500 -113
- package/skills/lit-pptx/scripts/lib/render-pack.js +3558 -0
- package/skills/lit-pptx/scripts/lib/slide-ast.js +55 -24
- package/skills/lit-pptx/scripts/lib/spec-validator.js +2 -2
- package/skills/lit-pptx/scripts/lib/template-registry.js +335 -11
- package/skills/lit-pptx/scripts/qa_deck.py +48 -13
- package/skills/lit-pptx/scripts/validate_pptx.py +17 -5
- package/skills/lit-pptx/specs/template-enrollment-contract.md +4 -4
- package/skills/lit-pptx/templates/boilerplate_professional.css +13 -13
- package/skills/lit-pptx/templates/enrolled/AZURE-A2Z/layout-mapping.yaml +1 -1
- package/skills/lit-pptx/templates/enrolled/AZURE-A2Z/template.yaml +5 -2
- package/skills/lit-pptx/templates/enrolled/AZURE-PRO/capabilities.yaml +20 -20
- package/skills/lit-pptx/templates/enrolled/AZURE-PRO/layout-mapping.yaml +1 -11
- package/skills/lit-pptx/templates/enrolled/AZURE-PRO/template.yaml +10 -7
- package/skills/lit-pptx/templates/enrolled/BOILERPLATE-PRETENDARD/capabilities.yaml +17 -17
- package/skills/lit-pptx/templates/enrolled/BOILERPLATE-PRETENDARD/template.yaml +8 -8
- package/skills/lit-pptx/templates/tonalities/atlas/pack.yaml +105 -0
- package/skills/lit-pptx/templates/tonalities/chalk/pack.yaml +110 -0
- package/skills/lit-pptx/templates/tonalities/gazette/pack.yaml +106 -0
- package/skills/lit-pptx/templates/tonalities/ledger/pack.yaml +107 -0
- package/skills/lit-pptx/templates/tonalities/night/pack.yaml +106 -0
- package/skills/lit-pptx/templates/tonalities/paper/pack.yaml +111 -0
- package/skills/lit-pptx/templates/tonalities/signal/pack.yaml +107 -0
- package/skills/lit-pptx/templates/tonalities/studio/pack.yaml +105 -0
- package/skills/lit-typographic-motion/THIRD_PARTY_NOTICES +1 -1
- package/skills/lit-typographic-motion/engine/stage/capture.mjs +2 -2
- package/skills/lit-typographic-motion/engine/treatment.mjs +1 -1
- package/skills/lit-typographic-motion/fonts/pins.json +2 -2
- package/skills/lit-typographic-motion/gate.mjs +1 -1
- package/skills/lit-typographic-motion/references/stage.md +1 -1
- package/skills/lit-typographic-motion/references/style-bibles.md +2 -2
- package/skills/lit-typographic-motion/references/treatment.md +1 -1
- package/skills/lit-typographic-motion/references/type-craft.md +3 -3
- package/skills/managed-skill-manifest.json +288 -44
- package/skills/lit-pptx/pretendard-font/public/static/LitOpenCodeSans-Bold.otf +0 -0
- package/skills/lit-pptx/pretendard-font/public/static/LitOpenCodeSans-Regular.otf +0 -0
|
@@ -1,9 +1,34 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""DOCX craft checks with explicit measurement tiers, and the document gate built on them.
|
|
2
|
+
|
|
3
|
+
`analyse()` is the source-only craft floor (OF-301 prose measure, OF-302 numeric alignment) that
|
|
4
|
+
slop_lint's audit route reports. Run as a script, this file is the document gate (`run.py qa`):
|
|
5
|
+
|
|
6
|
+
1. Integrity the package opens, its parts parse and python-docx reopens it
|
|
7
|
+
2. Content the document has body text, every Markdown heading reached it, no unfilled blank
|
|
8
|
+
([제품명], XXX, TBD, ○○, {{…}}) is left and no frontmatter leaked into the body
|
|
9
|
+
3. Lint slop_lint's prose rules on the source (report kind drops the manuscript outline rules),
|
|
10
|
+
its design, Hangul-pairing and craft audit on the DOCX
|
|
11
|
+
4. Output docx_layout's checks: noun-phrase headings and heading order always, the restraint
|
|
12
|
+
checks (fail under a tonality), with --layout the rendered pages (fill, stranded headings,
|
|
13
|
+
splits, component variety, cover block), with --compare the structural difference
|
|
14
|
+
between two tonalities of one source
|
|
15
|
+
|
|
16
|
+
python3 run.py qa draft.docx --source draft.md --layout [--tonality Report] [--kind report]
|
|
17
|
+
python3 run.py qa draft.docx --source draft.md --publisher elsevier --kind manuscript
|
|
18
|
+
python3 run.py qa a.docx --source draft.md --compare b.docx
|
|
19
|
+
|
|
20
|
+
Prints JSON; exit 0 on pass, 1 on fail. A pass is defect absence, never a verdict on the pages.
|
|
21
|
+
"""
|
|
2
22
|
|
|
3
23
|
from __future__ import annotations
|
|
4
24
|
|
|
25
|
+
import argparse
|
|
5
26
|
import json
|
|
6
27
|
import re
|
|
28
|
+
import shutil
|
|
29
|
+
import subprocess
|
|
30
|
+
import sys
|
|
31
|
+
import tempfile
|
|
7
32
|
import zipfile
|
|
8
33
|
from pathlib import Path
|
|
9
34
|
from xml.etree import ElementTree as ET
|
|
@@ -81,3 +106,181 @@ def analyse(path):
|
|
|
81
106
|
if value != "right":
|
|
82
107
|
findings.append({"rule": "OF-302", "severity": "HIGH" if value else "MEDIUM", "tier": "measured" if value else "derived", "where": f"table {table_no} row {row_no} col {column + 1}", "value": value, "threshold": "right"})
|
|
83
108
|
return findings
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
HEADING = re.compile(r"^(#{1,6})\s+(.+?)\s*#*\s*$")
|
|
112
|
+
# A bracket holding a digit is a citation ([1], [S3]); a blank holds none.
|
|
113
|
+
BLANK = re.compile(r"\[(?![^\]\n]*\d)[^\]\n]{1,24}\]|\bX{3,}\b|\bTBD\b|\bTODO\b|○○|OOO|\{\{[^}]*\}\}|_{4,}|_{2,}")
|
|
114
|
+
LEAKED_FRONTMATTER = re.compile(r"^(?:---|title:|author:|authors:|date:|subtitle:)\s*", re.M)
|
|
115
|
+
# Under the plain profile only the rules every profile shares apply to the Word file.
|
|
116
|
+
SHARED_DESIGN_RULES = {"rule-45-highlighted-text", "rule-46-tracked-changes", "rule-55-ellipsis", "rule-62-numeric-alignment"}
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def _integrity(path):
|
|
120
|
+
problems = []
|
|
121
|
+
try:
|
|
122
|
+
with zipfile.ZipFile(path) as archive:
|
|
123
|
+
names = set(archive.namelist())
|
|
124
|
+
for part in ("[Content_Types].xml", "word/document.xml", "word/styles.xml"):
|
|
125
|
+
if part not in names:
|
|
126
|
+
problems.append(f"missing {part}")
|
|
127
|
+
for name in names:
|
|
128
|
+
if name.endswith((".xml", ".rels")):
|
|
129
|
+
try:
|
|
130
|
+
ET.fromstring(archive.read(name))
|
|
131
|
+
except ET.ParseError as error:
|
|
132
|
+
problems.append(f"{name}: {error}")
|
|
133
|
+
except (OSError, zipfile.BadZipFile) as error:
|
|
134
|
+
problems.append(str(error))
|
|
135
|
+
if not problems:
|
|
136
|
+
from docx import Document
|
|
137
|
+
try:
|
|
138
|
+
Document(str(path))
|
|
139
|
+
except Exception as error: # python-docx raises many kinds on a broken part
|
|
140
|
+
problems.append(f"python-docx cannot reopen it: {error}")
|
|
141
|
+
return {"pass": not problems, "problems": problems[:20]}
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def _headings(markdown_text):
|
|
145
|
+
found, fenced = [], False
|
|
146
|
+
for line in markdown_text.splitlines():
|
|
147
|
+
if line.lstrip().startswith(("```", "~~~")):
|
|
148
|
+
fenced = not fenced
|
|
149
|
+
continue
|
|
150
|
+
match = None if fenced else HEADING.match(line)
|
|
151
|
+
if match:
|
|
152
|
+
found.append(re.sub(r"[*_`]", "", match.group(2)).strip())
|
|
153
|
+
return found
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def _content(path, markdown_text, layout):
|
|
157
|
+
from docx import Document
|
|
158
|
+
document = Document(str(path))
|
|
159
|
+
paragraphs = [p.text.strip() for p in document.paragraphs if p.text.strip()]
|
|
160
|
+
cells = [cell.text for table in document.tables for row in table.rows for cell in row.cells]
|
|
161
|
+
blanks = sorted({match.group(0) for text in paragraphs + cells for match in BLANK.finditer(text)})
|
|
162
|
+
leaked = [text for text in paragraphs[:8] if LEAKED_FRONTMATTER.match(text)]
|
|
163
|
+
missing = []
|
|
164
|
+
if markdown_text:
|
|
165
|
+
# A heading may stand inside a side-by-side columns row, so table text counts too.
|
|
166
|
+
text = layout.norm("".join(paragraphs + cells))
|
|
167
|
+
missing = [heading for heading in _headings(markdown_text) if layout.norm(heading) not in text]
|
|
168
|
+
chars = sum(map(len, paragraphs)) + sum(map(len, cells))
|
|
169
|
+
return {"pass": chars > 0 and not missing and not blanks and not leaked, "blanks": blanks[:20],
|
|
170
|
+
"frontmatter_leak": leaked[:4], "missing_headings": missing[:20], "paragraphs": len(paragraphs),
|
|
171
|
+
"tables": len(document.tables), "body_chars": chars}
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def _lint(source, path, publisher_name, kind, explicit):
|
|
175
|
+
import slop_lint
|
|
176
|
+
here = Path(__file__).resolve().parent
|
|
177
|
+
publisher = slop_lint.load_publisher(here.parent / "templates" / "registry.yaml", publisher_name)
|
|
178
|
+
prose, locale = [], "auto"
|
|
179
|
+
if source is not None:
|
|
180
|
+
phrases = slop_lint.load_phrase_rules(here.parent / "references" / "slop_phrase_list.yaml")
|
|
181
|
+
prose, _front, _body, locale = slop_lint.lint_text(source.read_text(encoding="utf-8"), publisher, phrases, "auto")
|
|
182
|
+
if kind == "report":
|
|
183
|
+
prose = [item for item in prose if item.rule_id not in slop_lint.MANUSCRIPT_ONLY_RULES]
|
|
184
|
+
elif not explicit:
|
|
185
|
+
prose = [item for item in prose if item.rule_id != "rule-08-structure-order"]
|
|
186
|
+
design = slop_lint.audit_docx_design(path, publisher)
|
|
187
|
+
if locale in {"ko", "mixed"} or source is None:
|
|
188
|
+
design += slop_lint.audit_docx_cjk(path)
|
|
189
|
+
craft = slop_lint.audit_docx_craft(path)
|
|
190
|
+
design += [item for item in craft if item.severity == "HIGH"]
|
|
191
|
+
if not explicit:
|
|
192
|
+
design = [item for item in design if item.rule_id in SHARED_DESIGN_RULES]
|
|
193
|
+
entry = lambda item: {"rule": item.rule_id, "line": item.line, "section": item.section, "message": item.message, "excerpt": item.excerpt} # noqa: E731
|
|
194
|
+
return {"pass": not prose and not design, "locale": locale, "prose": [entry(item) for item in prose][:40],
|
|
195
|
+
"design": [entry(item) for item in design][:40], "advisories": [entry(item) for item in craft if item.severity != "HIGH"][:40]}
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def _render(path, folder):
|
|
199
|
+
"""The document as PDF through soffice with a private profile, or (None, reason)."""
|
|
200
|
+
office = shutil.which("soffice") or shutil.which("libreoffice")
|
|
201
|
+
if office is None:
|
|
202
|
+
return None, "soffice is not on PATH; the page checks did not run"
|
|
203
|
+
profile = Path(folder) / "profile"
|
|
204
|
+
command = [office, f"-env:UserInstallation=file://{profile}", "--headless", "--convert-to", "pdf", "--outdir", str(folder), str(path)]
|
|
205
|
+
try:
|
|
206
|
+
subprocess.run(command, capture_output=True, timeout=180, check=False)
|
|
207
|
+
except subprocess.TimeoutExpired:
|
|
208
|
+
return None, "soffice did not finish within 180 s; the page checks did not run"
|
|
209
|
+
pdf = Path(folder) / (Path(path).stem + ".pdf")
|
|
210
|
+
return (pdf, "soffice") if pdf.exists() else (None, "soffice wrote no PDF; the page checks did not run")
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def _output(path, source, args, layout):
|
|
214
|
+
from convert_md_to_docx import parse_frontmatter
|
|
215
|
+
front = parse_frontmatter(source.read_text(encoding="utf-8"))[0] if source else {}
|
|
216
|
+
tonality = bool(args.tonality or front.get("tonality"))
|
|
217
|
+
info = layout.read_docx(path)
|
|
218
|
+
found = layout.structural(info, front, args.kind == "manuscript", tonality) + layout.restraint(path, tonality)
|
|
219
|
+
result = {"components": info["components"], "rendered": False}
|
|
220
|
+
if args.compare:
|
|
221
|
+
mine, other = layout.structure(path), layout.structure(args.compare.expanduser().resolve())
|
|
222
|
+
differ = layout.structure_diff(mine, other)
|
|
223
|
+
result["structure"] = {"this": mine, "other": other, "differ": differ}
|
|
224
|
+
if len(differ) < 3:
|
|
225
|
+
found.append(layout.finding("tonality.structure", "FAIL", None,
|
|
226
|
+
f"the two documents differ in {len(differ)} structural feature(s): {', '.join(differ) or 'none'} (at least 3)",
|
|
227
|
+
"Set the tonalities apart in structure: title block, summary form, numbering, component set, running head, contents."))
|
|
228
|
+
if args.layout:
|
|
229
|
+
with tempfile.TemporaryDirectory(prefix="litopencode-docx-pages-") as folder:
|
|
230
|
+
pdf, how = _render(path, folder)
|
|
231
|
+
if pdf is None:
|
|
232
|
+
result["render"] = how
|
|
233
|
+
else:
|
|
234
|
+
page_found, pages = layout.paged(info, layout.read_pdf(pdf), tonality, front)
|
|
235
|
+
found += page_found
|
|
236
|
+
result.update(pages, rendered=True)
|
|
237
|
+
result["findings"] = [item for item in found if item["severity"] == "FAIL"]
|
|
238
|
+
result["advisories"] = [item for item in found if item["severity"] != "FAIL"]
|
|
239
|
+
return result
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def main(argv=None):
|
|
243
|
+
parser = argparse.ArgumentParser(description="Pass/fail gate for a converted DOCX.")
|
|
244
|
+
parser.add_argument("docx", type=Path)
|
|
245
|
+
parser.add_argument("--source", type=Path, default=None, help="the Markdown the DOCX was converted from")
|
|
246
|
+
parser.add_argument("--publisher", default=None, help="the publisher profile it was converted with; omit for a tonality or the plain profile")
|
|
247
|
+
parser.add_argument("--kind", choices=("report", "manuscript"), default="report")
|
|
248
|
+
parser.add_argument("--layout", action="store_true", help="render through soffice and check the pages")
|
|
249
|
+
parser.add_argument("--tonality", default=None, help="the tonality it was converted with, when the frontmatter does not name it")
|
|
250
|
+
parser.add_argument("--compare", type=Path, default=None, help="the same source in another tonality; fail under three structural differences")
|
|
251
|
+
args = parser.parse_args(argv)
|
|
252
|
+
sys.dont_write_bytecode = True
|
|
253
|
+
import docx_layout as layout
|
|
254
|
+
|
|
255
|
+
path = args.docx.expanduser().resolve()
|
|
256
|
+
source = args.source.expanduser().resolve() if args.source else None
|
|
257
|
+
integrity = _integrity(path)
|
|
258
|
+
if integrity["pass"]:
|
|
259
|
+
content = _content(path, source.read_text(encoding="utf-8") if source else None, layout)
|
|
260
|
+
lint = _lint(source, path, args.publisher or "korean-generic", args.kind, args.publisher is not None)
|
|
261
|
+
output = _output(path, source, args, layout)
|
|
262
|
+
else:
|
|
263
|
+
content, lint, output = {"pass": False}, {"pass": False}, {"findings": [], "advisories": []}
|
|
264
|
+
reasons = []
|
|
265
|
+
if not integrity["pass"]:
|
|
266
|
+
reasons.append("integrity: " + "; ".join(integrity["problems"][:5]))
|
|
267
|
+
if content.get("blanks"):
|
|
268
|
+
reasons.append(f"content: unfilled blanks {content['blanks'][:5]} — fill them with realistic values labelled as examples or assumptions")
|
|
269
|
+
if content.get("frontmatter_leak"):
|
|
270
|
+
reasons.append("content: frontmatter printed as body text — convert with run.py md-to-docx, which reads it")
|
|
271
|
+
if integrity["pass"] and not content["pass"] and not content["blanks"] and not content["frontmatter_leak"]:
|
|
272
|
+
reasons.append(f"content: empty document or headings missing from the output {content['missing_headings'][:5]}")
|
|
273
|
+
if lint.get("prose"):
|
|
274
|
+
reasons.append(f"prose lint: {len(lint['prose'])} finding(s)")
|
|
275
|
+
if lint.get("design"):
|
|
276
|
+
reasons.append(f"design audit: {len(lint['design'])} finding(s)")
|
|
277
|
+
if output["findings"]:
|
|
278
|
+
reasons.append("output checks: " + ", ".join(sorted({item["check"] for item in output["findings"]})))
|
|
279
|
+
report = {"file": str(path), "pass": not reasons, "failure_reasons": reasons, "profile": args.publisher or "plain",
|
|
280
|
+
"kind": args.kind, "integrity": integrity, "content": content, "lint": lint, "output": output}
|
|
281
|
+
print(json.dumps(report, ensure_ascii=False, indent=2))
|
|
282
|
+
return 0 if report["pass"] else 1
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
if __name__ == "__main__":
|
|
286
|
+
raise SystemExit(main())
|