td-ai-tools 1.2.0 → 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/package.json +1 -1
  2. package/skills/README.md +3 -0
  3. package/skills/client-overview/SKILL.md +93 -0
  4. package/skills/client-overview/agents/openai.yaml +4 -0
  5. package/skills/client-overview/scripts/branch_client_overview_context.py +317 -0
  6. package/skills/debugging-ios-webkit/SKILL.md +60 -0
  7. package/skills/debugging-ios-webkit/references/device.md +62 -0
  8. package/skills/debugging-ios-webkit/references/playwright.md +60 -0
  9. package/skills/debugging-ios-webkit/references/simulator.md +76 -0
  10. package/skills/debugging-ios-webkit/scripts/device_eval.py +62 -0
  11. package/skills/debugging-ios-webkit/scripts/device_experiment.py +99 -0
  12. package/skills/debugging-ios-webkit/scripts/device_snapshot.py +62 -0
  13. package/skills/record-changes/SKILL.md +3 -3
  14. package/skills/shopify-lint/SKILL.md +50 -0
  15. package/skills/shopify-lint/agents/openai.yaml +4 -0
  16. package/skills/shopify-lint/scripts/setup.sh +60 -0
  17. package/skills/shopify-lint/scripts/shopify_lint.py +259 -0
  18. package/skills/shopify-lint/tests/test_shopify_lint.py +137 -0
  19. package/skills/shopify-lint/theme-check-theory/.theme-check.example.yml +14 -0
  20. package/skills/shopify-lint/theme-check-theory/README.md +123 -0
  21. package/skills/shopify-lint/theme-check-theory/configs/recommended.yml +8 -0
  22. package/skills/shopify-lint/theme-check-theory/package-lock.json +1947 -0
  23. package/skills/shopify-lint/theme-check-theory/package.json +44 -0
  24. package/skills/shopify-lint/theme-check-theory/src/checks/unguarded-text-setting.test.ts +198 -0
  25. package/skills/shopify-lint/theme-check-theory/src/checks/unguarded-text-setting.ts +143 -0
  26. package/skills/shopify-lint/theme-check-theory/src/checks/unused-section-settings.test.ts +137 -0
  27. package/skills/shopify-lint/theme-check-theory/src/checks/unused-section-settings.ts +64 -0
  28. package/skills/shopify-lint/theme-check-theory/src/index.test.ts +20 -0
  29. package/skills/shopify-lint/theme-check-theory/src/index.ts +11 -0
  30. package/skills/shopify-lint/theme-check-theory/src/test-utils.ts +31 -0
  31. package/skills/shopify-lint/theme-check-theory/src/utils/ast.ts +126 -0
  32. package/skills/shopify-lint/theme-check-theory/tsconfig.build.json +10 -0
  33. package/skills/shopify-lint/theme-check-theory/tsconfig.json +15 -0
  34. package/skills/shopify-lint/theme-check-theory/vitest.config.ts +11 -0
@@ -0,0 +1,76 @@
1
+
2
+ # Debugging in the iOS Simulator
3
+
4
+ Real iOS WebKit + real Safari chrome, scriptable from the terminal. The one
5
+ hard limitation: **you cannot synthesize taps** (`simctl` has no touch API,
6
+ and macOS blocks synthetic clicks without Accessibility permission).
7
+
8
+ ## Quick Start
9
+
10
+ ```bash
11
+ xcrun simctl list devices available # find a device + UDID
12
+ open -a Simulator --args -CurrentDeviceUDID <UDID>
13
+ xcrun simctl bootstatus <UDID> -b # wait for boot
14
+ xcrun simctl openurl booted "https://site.example/page"
15
+ xcrun simctl io booted screenshot /tmp/shot.png
16
+ ```
17
+
18
+ ## Driving state without touch
19
+
20
+ - **URL parameters the page already supports** — e.g. many themes open the
21
+ cart drawer on load with `?cart=open`; check the theme's JS for what it reads.
22
+ - **Temporary debug hooks** — add a query-param-gated script to the page that
23
+ auto-runs the interaction sequence, sync it to a dev theme, load with the
24
+ param, screenshot on a timer, then remove the hook:
25
+
26
+ ```js
27
+ if (new URLSearchParams(location.search).has('debug_cycles')) {
28
+ window.addEventListener('load', async () => {
29
+ const t = (ms) => new Promise(r => setTimeout(r, ms));
30
+ // example: cycle whatever component is under test
31
+ const d = document.querySelector('cart-drawer');
32
+ await t(2000); d.open(); await t(2500); d.close(); await t(1500); d.open();
33
+ });
34
+ }
35
+ ```
36
+
37
+ - **App-switch nudge** (forces visibility/viewport recalc):
38
+ `xcrun simctl launch booted com.apple.Preferences; sleep 2; xcrun simctl launch booted com.apple.mobilesafari`
39
+
40
+ ## Measuring screenshots
41
+
42
+ Screenshots are 3x scale on Pro-class devices — divide pixels by 3 for CSS px.
43
+ Pixel-scan with PIL rather than eyeballing:
44
+
45
+ ```python
46
+ from PIL import Image
47
+ im = Image.open('/tmp/shot.png').convert('RGB')
48
+ # find rows containing the element's known background colour, measure the band
49
+ ```
50
+
51
+ Comparing the measured painted box against the element's expected border-box
52
+ identifies which box was painted: a background at exactly content-box size
53
+ means padding wasn't painted (stale layer or native form-control painter).
54
+
55
+ ## Calibrating viewport units
56
+
57
+ The simulator reaches your Mac's `localhost` directly. Serve a test page
58
+ (`python3 -m http.server 8899`) with fixed-position divs of `100svh`,
59
+ `100dvh`, `100lvh`, `100%` height and a `bottom: 0` marker, screenshot, and
60
+ measure where each ends. This settles "which viewport is this unit tracking"
61
+ arguments with data.
62
+
63
+ ## Shopify-specific gotchas
64
+
65
+ - Preview/development themes show the **Draft bar**, which overlays the
66
+ bottom ~46pt of the viewport and covers bottom-anchored CTAs — it looks
67
+ exactly like a layout bug. Append `&pb=0` to hide it before judging.
68
+ - Add items to the cart with
69
+ `openurl booted ".../cart/add?id=<variant>&quantity=1&return_to=/cart"`
70
+ (a `/cart/<variant>:1` permalink jumps straight to checkout — not useful).
71
+
72
+ ## When this level is not enough
73
+
74
+ If the bug needs real touch gestures, device GPU behavior, or you have the
75
+ broken state live on someone's phone, use references/device.md. For fast CSS iteration first, use
76
+ references/playwright.md.
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env python3
2
+ """Evaluate JavaScript in a live Safari tab on a USB-connected iOS device.
3
+
4
+ Usage:
5
+ python device_eval.py <url-substring> ['<js-expression>']
6
+
7
+ The expression runs in the page and its return value is printed. Wrap DOM
8
+ reads in an IIFE returning JSON.stringify(...) for structured output.
9
+ Defaults to a generic element diagnostic if no expression is given.
10
+
11
+ Requires: pip install pymobiledevice3 (Python >= 3.10)
12
+ Phone: Settings -> Safari -> Advanced -> Web Inspector ON, tab foreground,
13
+ screen unlocked, and no other inspector attached (close Mac Safari Develop).
14
+ """
15
+ import asyncio
16
+ import sys
17
+ import uuid
18
+
19
+ from pymobiledevice3.lockdown import create_using_usbmux
20
+ from pymobiledevice3.services.webinspector import WebinspectorService
21
+ from pymobiledevice3.services.web_protocol.inspector_session import InspectorSession
22
+ from pymobiledevice3.services.web_protocol.session_protocol import SessionProtocol
23
+
24
+ DEFAULT_EXPR = r"""(() => {
25
+ return JSON.stringify({
26
+ href: location.href.slice(0, 120),
27
+ theme: (window.Shopify && Shopify.theme) ? Shopify.theme : undefined,
28
+ vv: { h: Math.round(visualViewport.height), ih: innerHeight },
29
+ });
30
+ })()"""
31
+
32
+
33
+ async def attach(inspector, match):
34
+ pages = await inspector.get_open_application_pages(timeout=2)
35
+ ap = next((p for p in pages if match in (getattr(p.page, 'web_url', '') or '')), None)
36
+ if ap is None:
37
+ urls = [getattr(p.page, 'web_url', None) for p in pages]
38
+ raise SystemExit(f'no tab matching {match!r}; open tabs: {urls}')
39
+ session_id = str(uuid.uuid4()).upper()
40
+ # wait_target=True is required on iOS 17+ (Target-domain wrapped protocol)
41
+ session = await asyncio.wait_for(
42
+ InspectorSession.create(
43
+ SessionProtocol(inspector, session_id, ap.application, ap.page, method_prefix=""),
44
+ wait_target=True),
45
+ timeout=10)
46
+ await session.runtime_enable()
47
+ return session
48
+
49
+
50
+ async def main():
51
+ match = sys.argv[1] if len(sys.argv) > 1 else 'http'
52
+ expr = sys.argv[2] if len(sys.argv) > 2 else DEFAULT_EXPR
53
+ lockdown = await create_using_usbmux()
54
+ inspector = WebinspectorService(lockdown=lockdown)
55
+ async with inspector:
56
+ session = await attach(inspector, match)
57
+ result = await asyncio.wait_for(session.runtime_evaluate(expr, return_by_value=True), timeout=15)
58
+ print(result)
59
+
60
+
61
+ if __name__ == '__main__':
62
+ asyncio.run(main())
@@ -0,0 +1,99 @@
1
+ #!/usr/bin/env python3
2
+ """Run stepped repair experiments on a live broken page while a human watches
3
+ the device. Each step is announced with a colored banner injected into the
4
+ page; the watcher reports which step number visually fixed the element.
5
+
6
+ Usage:
7
+ python device_experiment.py <url-substring> [target-css-selector]
8
+
9
+ Edit STEPS for the element/bug at hand. The winning step becomes the
10
+ permanent fix, wired into the code path that precedes the broken state.
11
+ """
12
+ import asyncio
13
+ import sys
14
+ import uuid
15
+
16
+ from pymobiledevice3.lockdown import create_using_usbmux
17
+ from pymobiledevice3.services.webinspector import WebinspectorService
18
+ from pymobiledevice3.services.web_protocol.inspector_session import InspectorSession
19
+ from pymobiledevice3.services.web_protocol.session_protocol import SessionProtocol
20
+
21
+ TARGET = sys.argv[2] if len(sys.argv) > 2 else 'body'
22
+
23
+ STEPS = [
24
+ ("STEP 1: banner only (sanity)", "void 0"),
25
+ ("STEP 2: opacity nudge", f"""
26
+ (() => {{ const el = document.querySelector('{TARGET}'); if (!el) return;
27
+ el.style.opacity = '0.99';
28
+ requestAnimationFrame(() => {{ el.style.opacity = ''; }}); }})()"""),
29
+ ("STEP 3: own compositing layer", f"""
30
+ (() => {{ const el = document.querySelector('{TARGET}'); if (!el) return;
31
+ el.style.transform = 'translateZ(0)'; }})()"""),
32
+ ("STEP 4: display rebuild", f"""
33
+ (() => {{ const el = document.querySelector('{TARGET}'); if (!el) return;
34
+ el.style.display = 'none'; void el.offsetHeight; el.style.display = ''; }})()"""),
35
+ ("STEP 5: reflow via class re-toggle", f"""
36
+ (() => {{ const el = document.querySelector('{TARGET}'); if (!el) return;
37
+ const p = el.parentElement; p.classList.add('td-exp-retoggle');
38
+ void p.offsetHeight; p.classList.remove('td-exp-retoggle'); }})()"""),
39
+ ("STEP 6: scroll nudge", """
40
+ (() => { const s = document.scrollingElement; s.scrollTop += 1; s.scrollTop -= 1; })()"""),
41
+ ]
42
+
43
+ BANNER = """
44
+ (() => {
45
+ let el = document.getElementById('td-exp-banner');
46
+ if (!el) {
47
+ el = document.createElement('div');
48
+ el.id = 'td-exp-banner';
49
+ el.style.cssText = 'position:fixed;top:0;left:0;right:0;z-index:2147483647;padding:18px;font:bold 24px -apple-system;text-align:center;color:#fff;';
50
+ document.body.appendChild(el);
51
+ }
52
+ el.style.background = COLOR;
53
+ el.textContent = LABEL;
54
+ })()
55
+ """
56
+
57
+ COLORS = ['#c0392b', '#d35400', '#f39c12', '#27ae60', '#2980b9', '#8e44ad']
58
+
59
+
60
+ async def main():
61
+ match = sys.argv[1] if len(sys.argv) > 1 else 'http'
62
+ lockdown = await create_using_usbmux()
63
+ inspector = WebinspectorService(lockdown=lockdown)
64
+ async with inspector:
65
+ pages = await inspector.get_open_application_pages(timeout=2)
66
+ ap = next((p for p in pages if match in (getattr(p.page, 'web_url', '') or '')), None)
67
+ if ap is None:
68
+ raise SystemExit(f'no tab matching {match!r}')
69
+ session_id = str(uuid.uuid4()).upper()
70
+ session = await asyncio.wait_for(
71
+ InspectorSession.create(
72
+ SessionProtocol(inspector, session_id, ap.application, ap.page, method_prefix=""),
73
+ wait_target=True),
74
+ timeout=10)
75
+ await session.runtime_enable()
76
+
77
+ async def banner(color, label):
78
+ await session.runtime_evaluate(BANNER.replace('COLOR', repr(color)).replace('LABEL', repr(label)))
79
+
80
+ # Lead-in so the watcher can reproduce the broken state first
81
+ for remaining in range(20, 0, -5):
82
+ await banner('#111', f'BREAK IT NOW — reproduce the bug. Steps start in {remaining}s')
83
+ print(f'lead-in {remaining}s', flush=True)
84
+ await asyncio.sleep(5)
85
+
86
+ for i, (label, code) in enumerate(STEPS):
87
+ await banner(COLORS[i % len(COLORS)], label + ' — fixed?')
88
+ await session.runtime_evaluate(code)
89
+ print('ran', label, flush=True)
90
+ await asyncio.sleep(5)
91
+
92
+ await banner('#111', 'DONE — report which step fixed it')
93
+ await asyncio.sleep(6)
94
+ await session.runtime_evaluate("document.getElementById('td-exp-banner')?.remove()")
95
+ print('done', flush=True)
96
+
97
+
98
+ if __name__ == '__main__':
99
+ asyncio.run(main())
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env python3
2
+ """Capture a Page.snapshotRect PNG from a live Safari tab on a USB device.
3
+
4
+ Usage:
5
+ python device_snapshot.py <url-substring> [out.png] [x y w h]
6
+
7
+ IMPORTANT: the snapshot repaints from the render tree. If the snapshot looks
8
+ CORRECT while the device screen looks BROKEN, the bug is a stale compositor
9
+ texture (paint bug), not a layout bug — fix with a paint invalidation (e.g.
10
+ opacity nudge), not with CSS.
11
+ """
12
+ import asyncio
13
+ import base64
14
+ import json
15
+ import sys
16
+ import uuid
17
+
18
+ from pymobiledevice3.lockdown import create_using_usbmux
19
+ from pymobiledevice3.services.webinspector import WebinspectorService
20
+ from pymobiledevice3.services.web_protocol.inspector_session import InspectorSession
21
+ from pymobiledevice3.services.web_protocol.session_protocol import SessionProtocol
22
+
23
+
24
+ async def main():
25
+ match = sys.argv[1] if len(sys.argv) > 1 else 'http'
26
+ out = sys.argv[2] if len(sys.argv) > 2 else 'snapshot.png'
27
+ x, y, w, h = (int(a) for a in sys.argv[3:7]) if len(sys.argv) > 6 else (0, 0, 430, 932)
28
+
29
+ lockdown = await create_using_usbmux()
30
+ inspector = WebinspectorService(lockdown=lockdown)
31
+ async with inspector:
32
+ pages = await inspector.get_open_application_pages(timeout=2)
33
+ ap = next((p for p in pages if match in (getattr(p.page, 'web_url', '') or '')), None)
34
+ if ap is None:
35
+ raise SystemExit(f'no tab matching {match!r}')
36
+ session_id = str(uuid.uuid4()).upper()
37
+ session = await asyncio.wait_for(
38
+ InspectorSession.create(
39
+ SessionProtocol(inspector, session_id, ap.application, ap.page, method_prefix=""),
40
+ wait_target=True),
41
+ timeout=10)
42
+
43
+ mid = await session.send_message_to_target({'method': 'Page.enable', 'params': {}})
44
+ await asyncio.wait_for(session.receive_response_by_id(mid), timeout=10)
45
+
46
+ mid = await session.send_message_to_target(
47
+ {'method': 'Page.snapshotRect',
48
+ 'params': {'x': x, 'y': y, 'width': w, 'height': h, 'coordinateSystem': 'Viewport'}})
49
+ res = await asyncio.wait_for(session.receive_response_by_id(mid), timeout=15)
50
+ # iOS 17+ wraps responses in the Target domain — unwrap
51
+ if res.get('method') == 'Target.dispatchMessageFromTarget':
52
+ res = json.loads(res['params']['message'])
53
+ data = res.get('result', res).get('dataURL', '')
54
+ if not data.startswith('data:image/png;base64,'):
55
+ raise SystemExit(f'snapshot failed: {json.dumps(res)[:200]}')
56
+ with open(out, 'wb') as f:
57
+ f.write(base64.b64decode(data.split(',', 1)[1]))
58
+ print('saved', out)
59
+
60
+
61
+ if __name__ == '__main__':
62
+ asyncio.run(main())
@@ -33,8 +33,8 @@ Use the script output to identify changed files, then inspect the relevant diffs
33
33
  Prioritize:
34
34
 
35
35
  - User-facing behavior changes
36
- - Theme setting/schema changes
37
- - CSS or markup changes that affect storefront rendering
36
+ - CMS setting/schema changes
37
+ - CSS or markup changes that affect rendering
38
38
  - Renamed files and vendor-theme hotspots
39
39
 
40
40
  Do not summarize the `docs/changes.md` edit itself as part of the branch work. If the branch contains unrelated skill or tooling files, either omit them from the changelog entry or separate them clearly when they are relevant to the project's maintenance history.
@@ -57,7 +57,7 @@ Guidelines:
57
57
  - Make the title describe the feature or fix, not the branch name.
58
58
  - Write `Purpose` in plain language with outcome-focused bullets.
59
59
  - Use the `Files changed` table to explain why each file matters.
60
- - Call out non-`td-` theme or vendor files in a separate subsection when they are upgrade-sensitive.
60
+ - Call out non-`td-` theme or vendor files in a separate subsection when in a Shopify project.
61
61
  - Mention renamed files explicitly.
62
62
  - Keep `Upgrade impact` brief and concrete.
63
63
  - Use `Notes` for implementation details, edge cases, or assumptions.
@@ -0,0 +1,50 @@
1
+ ---
2
+ name: shopify-lint
3
+ version: 1.0.0
4
+ description: Run Shopify CLI Theme Check with Theory Digital's bundled custom checks while reporting and failing only on offenses in files modified on the current Git branch. Use when Codex needs to lint a Shopify theme, validate branch-scoped Liquid or theme changes, enforce Theory theme rules, or avoid surfacing pre-existing Theme Check offenses from untouched files.
5
+ ---
6
+
7
+ # Shopify Lint
8
+
9
+ ## Setup
10
+
11
+ The bundled `theme-check-theory` custom-check package ships as TypeScript source only; its `node_modules` and compiled `dist/` are not committed. Run setup once before the first run (and after upgrading the skill):
12
+
13
+ ```bash
14
+ bash .agents/skills/shopify-lint/scripts/setup.sh
15
+ ```
16
+
17
+ Installing with `td-ai-tools install --setup shopify-lint` runs this automatically. The script:
18
+
19
+ 1. Runs `npm install && npm run build` inside `theme-check-theory/`, producing `dist/index.js` — the CommonJS entry point the root `.theme-check.yml` requires.
20
+ 2. Writes a `.theme-check.yml` at the project root wiring in the bundled checks. If one already exists it is left untouched; ensure its `require:` list includes `./.agents/skills/shopify-lint/theme-check-theory`.
21
+
22
+ Keep these dependencies inside `theme-check-theory/node_modules`; do not install Node dependencies at the theme root.
23
+
24
+ ## Usage
25
+
26
+ Run the bundled Python script from the Shopify theme root:
27
+
28
+ ```bash
29
+ python3 .agents/skills/shopify-lint/scripts/shopify_lint.py --path .
30
+ ```
31
+
32
+ The script runs `shopify theme check --output json`, identifies files changed since the current branch's merge-base, adds staged, unstaged, and untracked files, and emits only reports whose paths are in that set. The root `.theme-check.yml` directly requires the bundled `theme-check-theory` package from this skill directory.
33
+
34
+ ## Workflow
35
+
36
+ 1. Confirm `shopify` and `python3` are available.
37
+ 2. Run the bundled script instead of calling `shopify theme check` directly.
38
+ 3. Treat exit code `0` as no failing offenses in modified files, `1` as filtered offenses at or above the fail level, and `2` as a Git, CLI, or JSON-processing error.
39
+ 4. Fix reported issues and rerun until the command passes. Do not fix offenses in untouched files unless the user expands the scope.
40
+ 5. Keep custom-check dependencies inside `theme-check-theory/node_modules`. Do not install Node dependencies at the theme root.
41
+
42
+ The default base is `origin/HEAD`, then `origin/main`, `main`, `origin/master`, or `master`. Override it when needed:
43
+
44
+ ```bash
45
+ python3 .agents/skills/shopify-lint/scripts/shopify_lint.py --path . --base-ref origin/develop
46
+ ```
47
+
48
+ Set `SHOPIFY_LINT_BASE_REF` for the same override in automation. Use `--format json` for machine-readable filtered output and `--fail-level warning` or `--fail-level info` for stricter runs. When passing `--config <path>`, preserve the bundled package's `require` entry or the Theory checks will not load.
49
+
50
+ Do not use Theme Check auto-correction through this workflow because it can modify untouched files before filtering.
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "Shopify Lint"
3
+ short_description: "Lint modified theme files with Theory checks"
4
+ default_prompt: "Use $shopify-lint to run Theme Check for files modified on my current branch."
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ # shopify-lint setup
5
+ #
6
+ # Run automatically by the installer (`--setup`) from the installed skill
7
+ # directory, or manually with `bash scripts/setup.sh`. It:
8
+ # 1. Builds the bundled `theme-check-theory` custom-check package
9
+ # (its node_modules and dist/ are intentionally not committed).
10
+ # 2. Writes a `.theme-check.yml` at the project root that wires the bundled
11
+ # checks into Shopify CLI Theme Check.
12
+
13
+ SKILL_DIR="$(cd "$(dirname "$0")/.." && pwd)"
14
+ PKG_DIR="$SKILL_DIR/theme-check-theory"
15
+
16
+ # 1. Build the bundled custom-check package.
17
+ echo "shopify-lint setup: building theme-check-theory in $PKG_DIR"
18
+ cd "$PKG_DIR"
19
+ npm install
20
+ npm run build
21
+
22
+ # 2. Generate the project-root .theme-check.yml.
23
+ #
24
+ # The installed layout is <project>/<.agents|.claude>/skills/shopify-lint, so
25
+ # the project root is three levels above the skill directory. Guard against
26
+ # running outside that layout (e.g. from the catalog repo) to avoid writing the
27
+ # config into an unexpected directory.
28
+ TARGET_DIR="$(cd "$SKILL_DIR/../.." && pwd)"
29
+ TARGET_BASE="$(basename "$TARGET_DIR")"
30
+ if [[ "$TARGET_BASE" != ".agents" && "$TARGET_BASE" != ".claude" ]]; then
31
+ echo "shopify-lint setup: unrecognized install layout ($TARGET_DIR); skipping .theme-check.yml generation." >&2
32
+ exit 0
33
+ fi
34
+
35
+ PROJECT_ROOT="$(dirname "$TARGET_DIR")"
36
+ THEME_CHECK_FILE="$PROJECT_ROOT/.theme-check.yml"
37
+
38
+ if [[ -f "$THEME_CHECK_FILE" ]]; then
39
+ echo "shopify-lint setup: $THEME_CHECK_FILE already exists; leaving it unchanged."
40
+ echo " Ensure its 'require:' list includes ./.agents/skills/shopify-lint/theme-check-theory"
41
+ exit 0
42
+ fi
43
+
44
+ cat > "$THEME_CHECK_FILE" <<'YAML'
45
+ extends:
46
+ - theme-check:recommended
47
+
48
+ require:
49
+ - ./.agents/skills/shopify-lint/theme-check-theory
50
+
51
+ UnusedSectionSettings:
52
+ enabled: true
53
+ severity: warning
54
+
55
+ UnguardedTextSetting:
56
+ enabled: true
57
+ severity: warning
58
+ YAML
59
+
60
+ echo "shopify-lint setup: wrote $THEME_CHECK_FILE"