@lebronj/pi-suite 0.1.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/LICENSE +21 -0
- package/README.md +86 -0
- package/extensions/pet.ts +1033 -0
- package/extensions/prompt-url-widget.ts +158 -0
- package/extensions/redraws.ts +24 -0
- package/extensions/snake.ts +343 -0
- package/extensions/tps.ts +47 -0
- package/package.json +69 -0
- package/prompts/cl.md +54 -0
- package/prompts/is.md +25 -0
- package/prompts/pr.md +37 -0
- package/prompts/wr.md +35 -0
- package/scripts/bootstrap.sh +95 -0
- package/skills/add-llm-provider.md +57 -0
- package/skills/image-to-editable-ppt-slide/SKILL.md +113 -0
- package/skills/image-to-editable-ppt-slide/scripts/generate_spec_template.py +91 -0
- package/skills/image-to-editable-ppt-slide/scripts/pptx_rebuilder.py +181 -0
- package/skills/leetcode-array/SKILL.md +40 -0
- package/skills/leetcode-array/problems/best_time_to_buy_and_sell_stock.py +19 -0
- package/skills/leetcode-array/problems/product_of_array_except_self.py +22 -0
- package/skills/leetcode-array/problems/two_sum.py +19 -0
- package/skills/pi-skill/SKILL.md +154 -0
- package/skills/weather.md +49 -0
- package/vendor/pi-memory/LICENSE +21 -0
- package/vendor/pi-memory/README.md +223 -0
- package/vendor/pi-memory/index.ts +2367 -0
- package/vendor/pi-memory/package.json +68 -0
- package/vendor/pi-memory/scripts/postinstall.cjs +44 -0
- package/vendor/pi-memory/src/cli.ts +79 -0
- package/vendor/pi-memory/src/curator-core/audit.ts +45 -0
- package/vendor/pi-memory/src/curator-core/curate.ts +90 -0
- package/vendor/pi-memory/src/curator-core/metadata.ts +55 -0
- package/vendor/pi-memory/src/curator-core/patch.ts +24 -0
- package/vendor/pi-memory/src/curator-core/policy.ts +77 -0
- package/vendor/pi-memory/src/curator-store/file-store.ts +51 -0
- package/vendor/pi-memory/src/curator-store/types.ts +21 -0
- package/vendor/pi-memory/src/index.ts +35 -0
- package/vendor/pi-memory/src/learning/candidates.ts +205 -0
- package/vendor/pi-memory/src/learning/memory.ts +144 -0
- package/vendor/pi-memory/src/learning/skills.ts +200 -0
- package/vendor/pi-memory/src/service-controller.ts +248 -0
- package/vendor/pi-memory/test/curate.test.ts +68 -0
- package/vendor/pi-memory/test/learning-candidates.test.ts +107 -0
- package/vendor/pi-memory/test/memory-promotions.test.ts +44 -0
- package/vendor/pi-memory/test/metadata.test.ts +17 -0
- package/vendor/pi-memory/test/skill-drafts.test.ts +57 -0
- package/vendor/pi-memory/test/transition-handoff.test.ts +86 -0
package/prompts/is.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Analyze GitHub issues (bugs or feature requests)
|
|
3
|
+
argument-hint: "<issue>"
|
|
4
|
+
---
|
|
5
|
+
Analyze GitHub issue(s): $ARGUMENTS
|
|
6
|
+
|
|
7
|
+
For each issue:
|
|
8
|
+
|
|
9
|
+
1. Add the `inprogress` label to the issue via GitHub CLI and assign the issue to the local `gh` user before analysis starts. If either action fails, report that explicitly and continue.
|
|
10
|
+
2. Read the issue in full, including all comments and linked issues/PRs.
|
|
11
|
+
3. Do not trust analysis written in the issue. Independently verify behavior and derive your own analysis from the code and execution path.
|
|
12
|
+
|
|
13
|
+
4. **For bugs**:
|
|
14
|
+
- Ignore any root cause analysis in the issue (likely wrong)
|
|
15
|
+
- Read all related code files in full (no truncation)
|
|
16
|
+
- Trace the code path and identify the actual root cause
|
|
17
|
+
- Propose a fix
|
|
18
|
+
|
|
19
|
+
5. **For feature requests**:
|
|
20
|
+
- Do not trust implementation proposals in the issue without verification
|
|
21
|
+
- Read all related code files in full (no truncation)
|
|
22
|
+
- Propose the most concise implementation approach
|
|
23
|
+
- List affected files and changes needed
|
|
24
|
+
|
|
25
|
+
Do NOT implement unless explicitly asked. Analyze and propose only.
|
package/prompts/pr.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Review PRs from URLs with structured issue and code analysis
|
|
3
|
+
argument-hint: "<PR-URL>"
|
|
4
|
+
---
|
|
5
|
+
You are given one or more GitHub PR URLs: $@
|
|
6
|
+
|
|
7
|
+
For each PR URL, do the following in order:
|
|
8
|
+
1. Add the `inprogress` label to the PR via GitHub CLI before analysis starts. If adding the label fails, report that explicitly and continue.
|
|
9
|
+
2. Read the PR page in full. Include description, all comments, all commits, and all changed files.
|
|
10
|
+
3. Identify any linked issues referenced in the PR body, comments, commit messages, or cross links. Read each issue in full, including all comments.
|
|
11
|
+
4. Analyze the PR diff without checking out or switching to the PR branch. Use `gh pr diff`, `gh pr view`, `gh api`, and local main-branch files; if PR file contents are needed, use fetched refs with `git show <ref>:<path>` or temporary files. Read all relevant code files in full with no truncation and compare against the diff. Do not fetch PR file blobs unless a file is missing on main or the diff context is insufficient. Include related code paths that are not in the diff but are required to validate behavior.
|
|
12
|
+
5. Do not check for a changelog entry. Per CONTRIBUTING.md, contributor PRs must not edit `CHANGELOG.md` — the maintainer adds the entry when merging.
|
|
13
|
+
6. Check if packages/coding-agent/README.md, packages/coding-agent/docs/*.md, packages/coding-agent/examples/**/*.md require modification. This is usually the case when existing features have been changed, or new features have been added.
|
|
14
|
+
7. Provide a structured review with these sections:
|
|
15
|
+
- What it does: one short paragraph describing the change and its intent.
|
|
16
|
+
- Good: solid choices or improvements.
|
|
17
|
+
- Bad: concrete issues, regressions, missing tests, or risks.
|
|
18
|
+
- Ugly: subtle or high impact problems.
|
|
19
|
+
- Tests: what is covered, what is missing, and whether existing tests are adequate.
|
|
20
|
+
- Open questions for you: only things blocking a merge decision that need the user's input. Omit the section entirely if there are none.
|
|
21
|
+
|
|
22
|
+
Output format per PR:
|
|
23
|
+
PR: <url>
|
|
24
|
+
What it does:
|
|
25
|
+
- ...
|
|
26
|
+
Good:
|
|
27
|
+
- ...
|
|
28
|
+
Bad:
|
|
29
|
+
- ...
|
|
30
|
+
Ugly:
|
|
31
|
+
- ...
|
|
32
|
+
Tests:
|
|
33
|
+
- ...
|
|
34
|
+
Open questions for you:
|
|
35
|
+
- ...
|
|
36
|
+
|
|
37
|
+
If no issues are found, say so under Bad and Ugly.
|
package/prompts/wr.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Finish the current task end-to-end with changelog, commit, and push
|
|
3
|
+
argument-hint: "[instructions]"
|
|
4
|
+
---
|
|
5
|
+
Wrap it.
|
|
6
|
+
|
|
7
|
+
Additional instructions: $ARGUMENTS
|
|
8
|
+
|
|
9
|
+
Determine context from the conversation history first.
|
|
10
|
+
|
|
11
|
+
Rules for context detection:
|
|
12
|
+
- If the conversation already mentions a GitHub issue or PR, use that existing context.
|
|
13
|
+
- If the work came from `/is` or `/pr`, assume the issue or PR context is already known from the conversation and from the analysis work already done.
|
|
14
|
+
- If there is no GitHub issue or PR in the conversation history, treat this as non-GitHub work.
|
|
15
|
+
|
|
16
|
+
Unless I explicitly override something in this request, do the following in order:
|
|
17
|
+
|
|
18
|
+
1. Add or update the relevant package changelog entry under `## [Unreleased]` using the repo changelog rules.
|
|
19
|
+
2. If this task is tied to a GitHub issue or PR and a final issue or PR comment has not already been posted in this session, draft it in my tone, preview it, and post exactly one final comment. The comment must end with this exact standalone disclaimer line, with no variations:
|
|
20
|
+
|
|
21
|
+
```text
|
|
22
|
+
This comment is AI-generated by `/wr`
|
|
23
|
+
```
|
|
24
|
+
3. Commit only files you changed in this session.
|
|
25
|
+
4. If this task is tied to exactly one GitHub issue, include `closes #<issue>` in the commit message. If it is tied to multiple issues, stop and ask which one to use. If it is not tied to any issue, do not include `closes #` or `fixes #` in the commit message.
|
|
26
|
+
5. Check the current git branch. If it is not `main`, stop and ask what to do. Do not push from another branch unless I explicitly say so.
|
|
27
|
+
6. Push the current branch.
|
|
28
|
+
|
|
29
|
+
Constraints:
|
|
30
|
+
- Never stage unrelated files.
|
|
31
|
+
- Never use `git add .` or `git add -A`.
|
|
32
|
+
- Run required checks before committing if code changed.
|
|
33
|
+
- Do not open a PR unless I explicitly ask.
|
|
34
|
+
- If this is not GitHub issue or PR work, do not post a GitHub comment.
|
|
35
|
+
- If a final issue or PR comment was already posted in this session, do not post another one unless I explicitly ask.
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
TEAM_BASE_URL="${TEAM_BASE_URL:-https://claude-code.club/openai/v1}"
|
|
5
|
+
TEAM_MODEL="${TEAM_MODEL:-gpt-5.5}"
|
|
6
|
+
PI_SUITE="${PI_SUITE:-npm:@lebronj/pi-suite}"
|
|
7
|
+
|
|
8
|
+
if ! command -v npm >/dev/null 2>&1; then
|
|
9
|
+
echo "npm is required. Install Node.js first." >&2
|
|
10
|
+
exit 1
|
|
11
|
+
fi
|
|
12
|
+
|
|
13
|
+
printf "OpenAI-compatible API key: " >&2
|
|
14
|
+
stty_state=""
|
|
15
|
+
if command -v stty >/dev/null 2>&1; then
|
|
16
|
+
stty_state=$(stty -g 2>/dev/null || true)
|
|
17
|
+
stty -echo 2>/dev/null || true
|
|
18
|
+
fi
|
|
19
|
+
IFS= read -r TEAM_API_KEY
|
|
20
|
+
if [ -n "$stty_state" ]; then
|
|
21
|
+
stty "$stty_state" 2>/dev/null || true
|
|
22
|
+
fi
|
|
23
|
+
printf "\n" >&2
|
|
24
|
+
|
|
25
|
+
if [ -z "$TEAM_API_KEY" ]; then
|
|
26
|
+
echo "API key is required." >&2
|
|
27
|
+
exit 1
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
echo "Installing Pi CLI..."
|
|
31
|
+
npm install -g --ignore-scripts @earendil-works/pi-coding-agent
|
|
32
|
+
|
|
33
|
+
AGENT_DIR="$HOME/.pi/agent"
|
|
34
|
+
mkdir -p "$AGENT_DIR"
|
|
35
|
+
|
|
36
|
+
MODELS_FILE="$AGENT_DIR/models.json"
|
|
37
|
+
SETTINGS_FILE="$AGENT_DIR/settings.json"
|
|
38
|
+
|
|
39
|
+
MODELS_FILE="$MODELS_FILE" TEAM_BASE_URL="$TEAM_BASE_URL" TEAM_API_KEY="$TEAM_API_KEY" node <<'NODE'
|
|
40
|
+
const fs = require("node:fs");
|
|
41
|
+
const path = process.env.MODELS_FILE;
|
|
42
|
+
const current = fs.existsSync(path) ? JSON.parse(fs.readFileSync(path, "utf8")) : {};
|
|
43
|
+
const providers = current.providers && typeof current.providers === "object" ? current.providers : {};
|
|
44
|
+
providers.openai = {
|
|
45
|
+
...(providers.openai && typeof providers.openai === "object" ? providers.openai : {}),
|
|
46
|
+
baseUrl: process.env.TEAM_BASE_URL,
|
|
47
|
+
apiKey: process.env.TEAM_API_KEY,
|
|
48
|
+
};
|
|
49
|
+
fs.writeFileSync(path, `${JSON.stringify({ ...current, providers }, null, 2)}\n`);
|
|
50
|
+
NODE
|
|
51
|
+
|
|
52
|
+
SETTINGS_FILE="$SETTINGS_FILE" TEAM_MODEL="$TEAM_MODEL" node <<'NODE'
|
|
53
|
+
const fs = require("node:fs");
|
|
54
|
+
const path = process.env.SETTINGS_FILE;
|
|
55
|
+
const current = fs.existsSync(path) ? JSON.parse(fs.readFileSync(path, "utf8")) : {};
|
|
56
|
+
const next = {
|
|
57
|
+
...current,
|
|
58
|
+
defaultProvider: "openai",
|
|
59
|
+
defaultModel: process.env.TEAM_MODEL,
|
|
60
|
+
theme: current.theme ?? "light",
|
|
61
|
+
};
|
|
62
|
+
fs.writeFileSync(path, `${JSON.stringify(next, null, 2)}\n`);
|
|
63
|
+
NODE
|
|
64
|
+
|
|
65
|
+
echo "Installing Pi extension suite: $PI_SUITE"
|
|
66
|
+
pi install "$PI_SUITE"
|
|
67
|
+
|
|
68
|
+
echo "Setting up qmd for memory_search when possible..."
|
|
69
|
+
if command -v bun >/dev/null 2>&1; then
|
|
70
|
+
bun install -g https://github.com/tobi/qmd
|
|
71
|
+
export PATH="$HOME/.bun/bin:$PATH"
|
|
72
|
+
mkdir -p "$HOME/.pi/agent/memory"
|
|
73
|
+
if command -v qmd >/dev/null 2>&1; then
|
|
74
|
+
qmd collection add "$HOME/.pi/agent/memory" --name pi-memory || true
|
|
75
|
+
qmd embed || true
|
|
76
|
+
else
|
|
77
|
+
echo "qmd was installed but is not on PATH. Add ~/.bun/bin to PATH, then run qmd embed."
|
|
78
|
+
fi
|
|
79
|
+
else
|
|
80
|
+
cat <<'MSG'
|
|
81
|
+
Bun not found. Core memory tools still work, but memory_search needs qmd.
|
|
82
|
+
Install qmd later with:
|
|
83
|
+
bun install -g https://github.com/tobi/qmd
|
|
84
|
+
qmd collection add ~/.pi/agent/memory --name pi-memory
|
|
85
|
+
qmd embed
|
|
86
|
+
MSG
|
|
87
|
+
fi
|
|
88
|
+
|
|
89
|
+
cat <<MSG
|
|
90
|
+
Done.
|
|
91
|
+
Provider: openai
|
|
92
|
+
Base URL: $TEAM_BASE_URL
|
|
93
|
+
Model: $TEAM_MODEL
|
|
94
|
+
Run: pi
|
|
95
|
+
MSG
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: add-llm-provider
|
|
3
|
+
description: Checklist for adding a new LLM provider to packages/ai. Covers core types, provider implementation, lazy registration, model generation, the full test matrix, coding-agent wiring, and docs.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Adding a New LLM Provider (packages/ai)
|
|
7
|
+
|
|
8
|
+
A new provider touches multiple files. Work through these steps in order.
|
|
9
|
+
|
|
10
|
+
## 1. Core Types (`packages/ai/src/types.ts`)
|
|
11
|
+
|
|
12
|
+
- Add API identifier to `Api` type union (e.g. `"bedrock-converse-stream"`).
|
|
13
|
+
- Create options interface extending `StreamOptions`.
|
|
14
|
+
- Add mapping to `ApiOptionsMap`.
|
|
15
|
+
- Add provider name to `KnownProvider` type union.
|
|
16
|
+
|
|
17
|
+
## 2. Provider Implementation (`packages/ai/src/providers/`)
|
|
18
|
+
|
|
19
|
+
Create a provider file exporting:
|
|
20
|
+
|
|
21
|
+
- `stream<Provider>()` returning `AssistantMessageEventStream`.
|
|
22
|
+
- `streamSimple<Provider>()` for `SimpleStreamOptions` mapping.
|
|
23
|
+
- Provider-specific options interface.
|
|
24
|
+
- Message/tool conversion functions.
|
|
25
|
+
- Response parsing that emits standardized events (`text`, `tool_call`, `thinking`, `usage`, `stop`).
|
|
26
|
+
|
|
27
|
+
## 3. Provider Exports and Lazy Registration
|
|
28
|
+
|
|
29
|
+
- Add a package subpath export in `packages/ai/package.json` pointing at `./dist/providers/<provider>.js`.
|
|
30
|
+
- Add `export type` re-exports in `packages/ai/src/index.ts` for provider option types that should remain available from the root entry.
|
|
31
|
+
- Register the provider in `packages/ai/src/providers/register-builtins.ts` via lazy loader wrappers; do not statically import provider implementation modules there.
|
|
32
|
+
- Add credential detection in `packages/ai/src/env-api-keys.ts`.
|
|
33
|
+
|
|
34
|
+
## 4. Model Generation (`packages/ai/scripts/generate-models.ts`)
|
|
35
|
+
|
|
36
|
+
- Add logic to fetch/parse models from the provider source.
|
|
37
|
+
- Map to the standardized `Model` interface.
|
|
38
|
+
|
|
39
|
+
## 5. Tests (`packages/ai/test/`)
|
|
40
|
+
|
|
41
|
+
- Always add the provider to `stream.test.ts` with at least one representative model, even if it reuses an existing API impl such as `openai-completions`.
|
|
42
|
+
- Add the provider to the broader matrix where applicable: `tokens.test.ts`, `abort.test.ts`, `empty.test.ts`, `context-overflow.test.ts`, `unicode-surrogate.test.ts`, `tool-call-without-result.test.ts`, `image-tool-result.test.ts`, `total-tokens.test.ts`, `cross-provider-handoff.test.ts`.
|
|
43
|
+
- For `cross-provider-handoff.test.ts`, add at least one provider/model pair. If the provider exposes multiple model families (e.g. GPT and Claude), add at least one pair per family.
|
|
44
|
+
- For non-standard auth, create a utility (e.g. `bedrock-utils.ts`) with credential detection.
|
|
45
|
+
|
|
46
|
+
## 6. Coding Agent (`packages/coding-agent/`)
|
|
47
|
+
|
|
48
|
+
- `src/core/model-resolver.ts`: add default model ID to `defaultModelPerProvider`.
|
|
49
|
+
- `src/core/provider-display-names.ts`: add API-key login display name so `/login` and related UI show the provider for built-in API-key auth.
|
|
50
|
+
- `src/cli/args.ts`: add env var documentation.
|
|
51
|
+
- `README.md`: add provider setup instructions.
|
|
52
|
+
- `docs/providers.md`: add setup instructions, env var, and `auth.json` key.
|
|
53
|
+
|
|
54
|
+
## 7. Documentation
|
|
55
|
+
|
|
56
|
+
- `packages/ai/README.md`: add to providers table, document options/auth, add env vars.
|
|
57
|
+
- `packages/ai/CHANGELOG.md`: add entry under `## [Unreleased]`.
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: image-to-editable-ppt-slide
|
|
3
|
+
description: Rebuild one or more reference images as visually matching editable PowerPoint slides using native shapes, text, fills, and layout instead of a flat screenshot. Use when the user wants an image, flowchart, infographic, dashboard, process diagram, or designed slide converted into an editable PPT/PPTX deck that stays editable and closely matches the source.
|
|
4
|
+
homepage: https://github.com/benjaminlee/image-to-editable-ppt-slide
|
|
5
|
+
metadata:
|
|
6
|
+
clawdbot:
|
|
7
|
+
emoji: "🖼️"
|
|
8
|
+
requires:
|
|
9
|
+
env: []
|
|
10
|
+
files: ["scripts/*"]
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Image to Editable PPT Slide
|
|
14
|
+
|
|
15
|
+
Convert a reference image into a visually matching **editable** PowerPoint slide or deck.
|
|
16
|
+
|
|
17
|
+
## Use this skill when
|
|
18
|
+
|
|
19
|
+
- the user wants an image turned into an editable PPT/PPTX slide
|
|
20
|
+
- the source is a flowchart, infographic, dashboard, process diagram, or designed slide
|
|
21
|
+
- fidelity matters and the result should closely match the source
|
|
22
|
+
- the user wants multiple source images recreated as a multi-slide deck
|
|
23
|
+
|
|
24
|
+
## Core rules
|
|
25
|
+
|
|
26
|
+
- Rebuild the slide with editable text boxes, shapes, lines, fills, and layout
|
|
27
|
+
- Do **not** default to placing the whole image as a flat background unless the user explicitly asks for that
|
|
28
|
+
- Save both the generated `.pptx` and the script/spec used to create it
|
|
29
|
+
- Do at least one refinement pass when visual fidelity matters
|
|
30
|
+
- Commit updates in the workspace after edits
|
|
31
|
+
|
|
32
|
+
## Workflow
|
|
33
|
+
|
|
34
|
+
1. **Inspect the image(s)**
|
|
35
|
+
- Identify aspect ratio, title, sections, cards, arrows, connectors, icons, labels, and palette
|
|
36
|
+
- Note alignment, spacing, font weight, repeated motifs, and line thickness
|
|
37
|
+
|
|
38
|
+
2. **Choose structure**
|
|
39
|
+
- Single image → one editable slide
|
|
40
|
+
- Multiple images/pages → multi-slide deck, usually one source image per slide unless the user asks otherwise
|
|
41
|
+
|
|
42
|
+
3. **Build with editable primitives**
|
|
43
|
+
- Use `python-pptx`
|
|
44
|
+
- Prefer rectangles, rounded rectangles, chevrons, circles, arrows, lines, and text boxes
|
|
45
|
+
- Approximate unknown fonts with standard installed fonts
|
|
46
|
+
|
|
47
|
+
4. **Use helpers**
|
|
48
|
+
- `scripts/pptx_rebuilder.py` builds a deck from a JSON spec
|
|
49
|
+
- `scripts/generate_spec_template.py` generates a starter JSON template for one or more slides
|
|
50
|
+
|
|
51
|
+
5. **Refine**
|
|
52
|
+
- Tighten spacing, font sizes, colors, line widths, corner radii, and proportions
|
|
53
|
+
- If needed, do a second pass before presenting the result
|
|
54
|
+
|
|
55
|
+
6. **Deliver**
|
|
56
|
+
- Tell the user where the `.pptx`, generator script, and/or JSON spec were saved
|
|
57
|
+
- Mention any approximations if the match is not exact
|
|
58
|
+
|
|
59
|
+
## File pattern
|
|
60
|
+
|
|
61
|
+
For one-off jobs, create:
|
|
62
|
+
|
|
63
|
+
- `make_<name>.py`
|
|
64
|
+
- `<Name>_editable.pptx`
|
|
65
|
+
- optional: `<name>_spec.json`
|
|
66
|
+
|
|
67
|
+
For repeated use, adapt the reusable scripts in `scripts/`.
|
|
68
|
+
|
|
69
|
+
## Multi-slide deck guidance
|
|
70
|
+
|
|
71
|
+
- Keep slide size consistent across the deck
|
|
72
|
+
- Usually map one reference image to one slide
|
|
73
|
+
- Reuse colors, text styles, and spacing where slides belong to the same presentation
|
|
74
|
+
- If slides differ a lot, treat each slide as its own reconstruction while keeping the deck coherent
|
|
75
|
+
|
|
76
|
+
## External Endpoints
|
|
77
|
+
|
|
78
|
+
This skill itself does not call any external APIs or web services.
|
|
79
|
+
|
|
80
|
+
| Endpoint | Purpose | Data sent |
|
|
81
|
+
|---|---|---|
|
|
82
|
+
| None | N/A | Nothing leaves the machine by default |
|
|
83
|
+
|
|
84
|
+
## Security & Privacy
|
|
85
|
+
|
|
86
|
+
- By default, this skill works locally with `python-pptx` and local files only
|
|
87
|
+
- It reads local image/reference material and writes local `.pptx`, `.json`, and helper script files
|
|
88
|
+
- It does **not** require credentials or network access for its built-in helpers
|
|
89
|
+
- If a future adaptation adds external APIs, document every endpoint and every environment variable before publishing
|
|
90
|
+
|
|
91
|
+
## Model Invocation Note
|
|
92
|
+
|
|
93
|
+
OpenClaw may invoke this skill autonomously when the request matches its description. That is normal skill behavior. If the user wants to avoid autonomous invocation, they can ask for a manual or one-off approach instead.
|
|
94
|
+
|
|
95
|
+
## Trust Statement
|
|
96
|
+
|
|
97
|
+
By using this skill, you are trusting the local helper scripts in this package to read local spec/input files and write local PowerPoint output files. This packaged version does not send data to third-party services. Only install it if you trust the skill contents and your execution environment.
|
|
98
|
+
|
|
99
|
+
## ClawHub-ready note
|
|
100
|
+
|
|
101
|
+
This skill folder is structured so it can be published with `clawhub publish` once authenticated. If publishing is requested, verify `clawhub whoami` first.
|
|
102
|
+
|
|
103
|
+
## Quality bar
|
|
104
|
+
|
|
105
|
+
Good:
|
|
106
|
+
- text and shapes are individually editable
|
|
107
|
+
- visual hierarchy matches the source at normal viewing size
|
|
108
|
+
- spacing, colors, and proportions are close enough to feel effectively identical
|
|
109
|
+
|
|
110
|
+
Bad:
|
|
111
|
+
- whole image pasted as one picture
|
|
112
|
+
- major layout drift or incorrect proportions
|
|
113
|
+
- unnecessary conversion of text into non-editable elements
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# SECURITY MANIFEST:
|
|
3
|
+
# Environment variables accessed: none
|
|
4
|
+
# External endpoints called: none
|
|
5
|
+
# Local files read: none
|
|
6
|
+
# Local files written: output JSON spec file
|
|
7
|
+
"""
|
|
8
|
+
Generate a starter JSON spec for building editable PPTX slides.
|
|
9
|
+
|
|
10
|
+
Usage examples:
|
|
11
|
+
python scripts/generate_spec_template.py --title "Sample" --output sample_spec.json
|
|
12
|
+
python scripts/generate_spec_template.py --title "Deck" --slides 3 --output deck_spec.json
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import argparse
|
|
18
|
+
import json
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def make_slide(index: int, title: str) -> dict:
|
|
23
|
+
return {
|
|
24
|
+
"background": "F8F8FA",
|
|
25
|
+
"items": [
|
|
26
|
+
{
|
|
27
|
+
"kind": "textbox",
|
|
28
|
+
"x": 0.0,
|
|
29
|
+
"y": 0.4,
|
|
30
|
+
"w": 13.333,
|
|
31
|
+
"h": 0.55,
|
|
32
|
+
"text": f"{title}" if index == 1 else f"{title} — Slide {index}",
|
|
33
|
+
"size": 32,
|
|
34
|
+
"color": "1B3460",
|
|
35
|
+
"align": "center",
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"kind": "shape",
|
|
39
|
+
"shape": "round_rect",
|
|
40
|
+
"x": 0.8,
|
|
41
|
+
"y": 1.4,
|
|
42
|
+
"w": 4.0,
|
|
43
|
+
"h": 0.9,
|
|
44
|
+
"fill": "E9EBF1",
|
|
45
|
+
"text": "Replace with reconstructed section",
|
|
46
|
+
"size": 18,
|
|
47
|
+
"color": "1B3460",
|
|
48
|
+
"align": "center",
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"kind": "textbox",
|
|
52
|
+
"x": 0.9,
|
|
53
|
+
"y": 2.55,
|
|
54
|
+
"w": 4.4,
|
|
55
|
+
"h": 0.45,
|
|
56
|
+
"text": "Use shapes and text to match the source image.",
|
|
57
|
+
"size": 16,
|
|
58
|
+
"color": "243A5C",
|
|
59
|
+
"align": "left",
|
|
60
|
+
}
|
|
61
|
+
]
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def main():
|
|
66
|
+
parser = argparse.ArgumentParser()
|
|
67
|
+
parser.add_argument("--title", default="Editable Slide")
|
|
68
|
+
parser.add_argument("--slides", type=int, default=1)
|
|
69
|
+
parser.add_argument("--width", type=float, default=13.333)
|
|
70
|
+
parser.add_argument("--height", type=float, default=7.5)
|
|
71
|
+
parser.add_argument("--background", default="F8F8FA")
|
|
72
|
+
parser.add_argument("--output", required=True)
|
|
73
|
+
args = parser.parse_args()
|
|
74
|
+
|
|
75
|
+
spec = {
|
|
76
|
+
"presentation": {
|
|
77
|
+
"width": args.width,
|
|
78
|
+
"height": args.height,
|
|
79
|
+
"background": args.background,
|
|
80
|
+
},
|
|
81
|
+
"slides": [make_slide(i + 1, args.title) for i in range(args.slides)],
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
out = Path(args.output)
|
|
85
|
+
out.parent.mkdir(parents=True, exist_ok=True)
|
|
86
|
+
out.write_text(json.dumps(spec, indent=2) + "\n")
|
|
87
|
+
print(out)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
if __name__ == "__main__":
|
|
91
|
+
main()
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# SECURITY MANIFEST:
|
|
3
|
+
# Environment variables accessed: none
|
|
4
|
+
# External endpoints called: none
|
|
5
|
+
# Local files read: input JSON spec
|
|
6
|
+
# Local files written: output PPTX file
|
|
7
|
+
"""
|
|
8
|
+
Build a PPTX deck from a compact JSON spec.
|
|
9
|
+
|
|
10
|
+
Usage:
|
|
11
|
+
python scripts/pptx_rebuilder.py spec.json output.pptx
|
|
12
|
+
|
|
13
|
+
Supports single-slide and multi-slide specs. The goal is not to auto-trace an
|
|
14
|
+
image; it is a reusable editable-slide builder that makes it faster to recreate
|
|
15
|
+
reference images with native PowerPoint shapes.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import json
|
|
21
|
+
import sys
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
from typing import Any, Dict
|
|
24
|
+
|
|
25
|
+
from pptx import Presentation
|
|
26
|
+
from pptx.dml.color import RGBColor
|
|
27
|
+
from pptx.enum.shapes import MSO_AUTO_SHAPE_TYPE
|
|
28
|
+
from pptx.enum.text import MSO_ANCHOR, PP_ALIGN
|
|
29
|
+
from pptx.util import Inches, Pt
|
|
30
|
+
|
|
31
|
+
SHAPE_MAP = {
|
|
32
|
+
"rect": MSO_AUTO_SHAPE_TYPE.RECTANGLE,
|
|
33
|
+
"round_rect": MSO_AUTO_SHAPE_TYPE.ROUNDED_RECTANGLE,
|
|
34
|
+
"chevron": MSO_AUTO_SHAPE_TYPE.CHEVRON,
|
|
35
|
+
"oval": MSO_AUTO_SHAPE_TYPE.OVAL,
|
|
36
|
+
"right_arrow": MSO_AUTO_SHAPE_TYPE.RIGHT_ARROW,
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
ALIGN_MAP = {
|
|
40
|
+
"left": PP_ALIGN.LEFT,
|
|
41
|
+
"center": PP_ALIGN.CENTER,
|
|
42
|
+
"right": PP_ALIGN.RIGHT,
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def rgb(value: str | None, default: str = "000000") -> RGBColor:
|
|
47
|
+
value = (value or default).replace("#", "")
|
|
48
|
+
if len(value) != 6:
|
|
49
|
+
value = default
|
|
50
|
+
return RGBColor.from_string(value.upper())
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def inches(v: float) -> int:
|
|
54
|
+
return Inches(v)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def add_textbox(slide, item: Dict[str, Any]):
|
|
58
|
+
tb = slide.shapes.add_textbox(
|
|
59
|
+
inches(item["x"]), inches(item["y"]), inches(item["w"]), inches(item["h"])
|
|
60
|
+
)
|
|
61
|
+
tf = tb.text_frame
|
|
62
|
+
tf.clear()
|
|
63
|
+
tf.vertical_anchor = MSO_ANCHOR.MIDDLE
|
|
64
|
+
tf.margin_left = Inches(item.get("margin", 0.02))
|
|
65
|
+
tf.margin_right = Inches(item.get("margin", 0.02))
|
|
66
|
+
tf.margin_top = 0
|
|
67
|
+
tf.margin_bottom = 0
|
|
68
|
+
p = tf.paragraphs[0]
|
|
69
|
+
p.alignment = ALIGN_MAP.get(item.get("align", "center"), PP_ALIGN.CENTER)
|
|
70
|
+
r = p.add_run()
|
|
71
|
+
r.text = item.get("text", "")
|
|
72
|
+
font = r.font
|
|
73
|
+
font.name = item.get("font", "Aptos")
|
|
74
|
+
font.size = Pt(item.get("size", 18))
|
|
75
|
+
font.bold = item.get("bold", False)
|
|
76
|
+
font.color.rgb = rgb(item.get("color"), "000000")
|
|
77
|
+
return tb
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def add_shape(slide, item: Dict[str, Any]):
|
|
81
|
+
shape_type = SHAPE_MAP[item["shape"]]
|
|
82
|
+
shp = slide.shapes.add_shape(
|
|
83
|
+
shape_type,
|
|
84
|
+
inches(item["x"]),
|
|
85
|
+
inches(item["y"]),
|
|
86
|
+
inches(item["w"]),
|
|
87
|
+
inches(item["h"]),
|
|
88
|
+
)
|
|
89
|
+
shp.fill.solid()
|
|
90
|
+
shp.fill.fore_color.rgb = rgb(item.get("fill"), "FFFFFF")
|
|
91
|
+
line = item.get("line")
|
|
92
|
+
if line:
|
|
93
|
+
shp.line.color.rgb = rgb(line.get("color"), "000000")
|
|
94
|
+
shp.line.width = Pt(line.get("width", 1))
|
|
95
|
+
else:
|
|
96
|
+
shp.line.fill.background()
|
|
97
|
+
|
|
98
|
+
if item.get("text"):
|
|
99
|
+
tf = shp.text_frame
|
|
100
|
+
tf.clear()
|
|
101
|
+
tf.vertical_anchor = MSO_ANCHOR.MIDDLE
|
|
102
|
+
p = tf.paragraphs[0]
|
|
103
|
+
p.alignment = ALIGN_MAP.get(item.get("align", "center"), PP_ALIGN.CENTER)
|
|
104
|
+
r = p.add_run()
|
|
105
|
+
r.text = item["text"]
|
|
106
|
+
font = r.font
|
|
107
|
+
font.name = item.get("font", "Aptos")
|
|
108
|
+
font.size = Pt(item.get("size", 18))
|
|
109
|
+
font.bold = item.get("bold", False)
|
|
110
|
+
font.color.rgb = rgb(item.get("color"), "000000")
|
|
111
|
+
return shp
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def apply_background(slide, color: str | None):
|
|
115
|
+
bg = slide.background.fill
|
|
116
|
+
bg.solid()
|
|
117
|
+
bg.fore_color.rgb = rgb(color, "FFFFFF")
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def render_slide(prs: Presentation, slide_spec: Dict[str, Any], default_bg: str):
|
|
121
|
+
slide = prs.slides.add_slide(prs.slide_layouts[6])
|
|
122
|
+
apply_background(slide, slide_spec.get("background", default_bg))
|
|
123
|
+
for item in slide_spec.get("items", []):
|
|
124
|
+
kind = item["kind"]
|
|
125
|
+
if kind == "textbox":
|
|
126
|
+
add_textbox(slide, item)
|
|
127
|
+
elif kind == "shape":
|
|
128
|
+
add_shape(slide, item)
|
|
129
|
+
else:
|
|
130
|
+
raise ValueError(f"Unsupported item kind: {kind}")
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def normalize_spec(spec: Dict[str, Any]) -> Dict[str, Any]:
|
|
134
|
+
# Backward compatibility with old single-slide format.
|
|
135
|
+
if "slides" in spec:
|
|
136
|
+
return spec
|
|
137
|
+
presentation = spec.get("presentation") or spec.get("slide") or {}
|
|
138
|
+
return {
|
|
139
|
+
"presentation": {
|
|
140
|
+
"width": presentation.get("width", 13.333),
|
|
141
|
+
"height": presentation.get("height", 7.5),
|
|
142
|
+
"background": presentation.get("background", "FFFFFF"),
|
|
143
|
+
},
|
|
144
|
+
"slides": [
|
|
145
|
+
{
|
|
146
|
+
"background": presentation.get("background", "FFFFFF"),
|
|
147
|
+
"items": spec.get("items", []),
|
|
148
|
+
}
|
|
149
|
+
],
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def main():
|
|
154
|
+
if len(sys.argv) != 3:
|
|
155
|
+
print("Usage: python pptx_rebuilder.py spec.json output.pptx", file=sys.stderr)
|
|
156
|
+
sys.exit(1)
|
|
157
|
+
|
|
158
|
+
spec_path = Path(sys.argv[1])
|
|
159
|
+
out_path = Path(sys.argv[2])
|
|
160
|
+
spec = normalize_spec(json.loads(spec_path.read_text()))
|
|
161
|
+
|
|
162
|
+
prs = Presentation()
|
|
163
|
+
presentation = spec.get("presentation", {})
|
|
164
|
+
prs.slide_width = Inches(presentation.get("width", 13.333))
|
|
165
|
+
prs.slide_height = Inches(presentation.get("height", 7.5))
|
|
166
|
+
default_bg = presentation.get("background", "FFFFFF")
|
|
167
|
+
|
|
168
|
+
# remove default starter slide behavior by creating fresh deck via add_slide only
|
|
169
|
+
if len(prs.slides) > 0:
|
|
170
|
+
pass
|
|
171
|
+
|
|
172
|
+
for slide_spec in spec.get("slides", []):
|
|
173
|
+
render_slide(prs, slide_spec, default_bg)
|
|
174
|
+
|
|
175
|
+
out_path.parent.mkdir(parents=True, exist_ok=True)
|
|
176
|
+
prs.save(out_path)
|
|
177
|
+
print(out_path)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
if __name__ == "__main__":
|
|
181
|
+
main()
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: leetcode-array
|
|
3
|
+
description: Common LeetCode array problems with Python reference solutions. Use when the user wants array practice, standard solution patterns, or quick runnable examples.
|
|
4
|
+
metadata:
|
|
5
|
+
clawdbot:
|
|
6
|
+
emoji: "🧮"
|
|
7
|
+
requires:
|
|
8
|
+
files: ["problems/*.py"]
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# LeetCode Array
|
|
12
|
+
|
|
13
|
+
Use this skill when the user wants representative LeetCode array exercises with runnable Python solutions.
|
|
14
|
+
|
|
15
|
+
## Included problems
|
|
16
|
+
|
|
17
|
+
- `problems/two_sum.py` - hash map lookup for complement matching
|
|
18
|
+
- `problems/best_time_to_buy_and_sell_stock.py` - one-pass minimum tracking
|
|
19
|
+
- `problems/product_of_array_except_self.py` - prefix and suffix products
|
|
20
|
+
|
|
21
|
+
## How to use
|
|
22
|
+
|
|
23
|
+
Read the matching problem file first, then explain:
|
|
24
|
+
- the core pattern
|
|
25
|
+
- time and space complexity
|
|
26
|
+
- one common mistake
|
|
27
|
+
|
|
28
|
+
Run examples locally if needed:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
python3 problems/two_sum.py
|
|
32
|
+
python3 problems/best_time_to_buy_and_sell_stock.py
|
|
33
|
+
python3 problems/product_of_array_except_self.py
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Answering style
|
|
37
|
+
|
|
38
|
+
- Start with the pattern before the code
|
|
39
|
+
- Prefer the runnable Python examples in `problems/`
|
|
40
|
+
- If the user asks for another language, translate from the Python reference solution
|