@generativereality/content-studio 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +11 -0
- package/LICENSE +21 -0
- package/README.md +52 -2
- package/package.json +23 -4
- package/skills/content-studio/SKILL.md +150 -0
- package/skills/content-studio/references/audio.md +159 -0
- package/skills/content-studio/references/brief.md +66 -0
- package/skills/content-studio/references/channels.md +144 -0
- package/skills/content-studio/references/copy.md +201 -0
- package/skills/content-studio/references/costs-and-licences.md +75 -0
- package/skills/content-studio/references/generation.md +192 -0
- package/skills/content-studio/references/real-people.md +111 -0
- package/skills/content-studio/references/review.md +80 -0
- package/skills/content-studio/references/search.md +135 -0
- package/skills/content-studio/references/stills.md +112 -0
- package/skills/content-studio/references/tools.md +26 -0
- package/skills/content-studio/references/video.md +155 -0
- package/skills/content-studio/references/voice.md +115 -0
- package/skills/content-studio/scripts/render-frames.sh +30 -0
- package/skills/content-studio/scripts/spend.mjs +44 -0
- package/skills/content-studio/scripts/voice-sweep.sh +31 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Render HTML frames to PNG with headless Chrome/Chromium (references/stills.md, recipe A).
|
|
3
|
+
# Usage: render-frames.sh OUT_DIR WIDTH HEIGHT frame.html [frame.html...]
|
|
4
|
+
# Output: OUT_DIR/<frame-basename>.png at WIDTH x HEIGHT CSS px (SCALE=2 for 2x pixels).
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
[ $# -ge 4 ] || { echo "usage: $0 OUT_DIR WIDTH HEIGHT frame.html..." >&2; exit 2; }
|
|
7
|
+
OUT=$1; W=$2; H=$3; shift 3
|
|
8
|
+
mkdir -p "$OUT"
|
|
9
|
+
CHROME=${CHROME:-}
|
|
10
|
+
if [ -z "$CHROME" ]; then
|
|
11
|
+
for c in "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
|
|
12
|
+
"/Applications/Chromium.app/Contents/MacOS/Chromium" \
|
|
13
|
+
"/c/Program Files/Google/Chrome/Application/chrome.exe" \
|
|
14
|
+
"/c/Program Files (x86)/Google/Chrome/Application/chrome.exe" \
|
|
15
|
+
"$(command -v google-chrome 2>/dev/null || true)" \
|
|
16
|
+
"$(command -v chromium 2>/dev/null || true)" \
|
|
17
|
+
"$(command -v chromium-browser 2>/dev/null || true)"; do
|
|
18
|
+
if [ -n "$c" ] && [ -x "$c" ]; then CHROME=$c; break; fi
|
|
19
|
+
done
|
|
20
|
+
fi
|
|
21
|
+
[ -n "$CHROME" ] || { echo "no Chrome/Chromium found; set CHROME=/path/to/chrome" >&2; exit 1; }
|
|
22
|
+
for f in "$@"; do
|
|
23
|
+
abs=$(cd "$(dirname "$f")" && pwd)/$(basename "$f")
|
|
24
|
+
png="$OUT/$(basename "${f%.*}").png"
|
|
25
|
+
"$CHROME" --headless=new --disable-gpu --hide-scrollbars --no-first-run --no-default-browser-check \
|
|
26
|
+
--force-device-scale-factor="${SCALE:-1}" --window-size="$W,$H" \
|
|
27
|
+
--virtual-time-budget=3000 --screenshot="$png" "file://$abs" >/dev/null 2>&1 || true
|
|
28
|
+
if [ -s "$png" ]; then echo "$png"; else echo "failed: $f" >&2; exit 1; fi
|
|
29
|
+
done
|
|
30
|
+
echo "Now open every PNG and look for collisions, crops and contrast before calling it done." >&2
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Per-piece spend ledger (references/costs-and-licences.md). No dependencies.
|
|
3
|
+
// node spend.mjs add <dir> --model M --usd 0.05 [--seconds 23] [--out file] [--note "..."] [--local]
|
|
4
|
+
// node spend.mjs total <dir> [--budget 5] exit 3 when over budget
|
|
5
|
+
import { appendFileSync, existsSync, readFileSync } from 'node:fs';
|
|
6
|
+
import { join } from 'node:path';
|
|
7
|
+
|
|
8
|
+
const [cmd, dir, ...rest] = process.argv.slice(2);
|
|
9
|
+
const opt = {};
|
|
10
|
+
for (let i = 0; i < rest.length; i++) {
|
|
11
|
+
const k = rest[i].replace(/^--/, '');
|
|
12
|
+
if (rest[i + 1] !== undefined && !rest[i + 1].startsWith('--')) opt[k] = rest[++i];
|
|
13
|
+
else opt[k] = true;
|
|
14
|
+
}
|
|
15
|
+
if (!cmd || !dir) { console.error('usage: spend.mjs add|total <dir> [...]'); process.exit(2); }
|
|
16
|
+
const file = join(dir, 'spend.jsonl');
|
|
17
|
+
|
|
18
|
+
if (cmd === 'add') {
|
|
19
|
+
if (!opt.model || opt.usd === undefined) { console.error('add needs --model and --usd'); process.exit(2); }
|
|
20
|
+
const row = {
|
|
21
|
+
at: new Date().toISOString(), model: opt.model, usd: Number(opt.usd),
|
|
22
|
+
seconds: opt.seconds ? Number(opt.seconds) : null, out: opt.out || null,
|
|
23
|
+
note: opt.note || null, local: !!opt.local,
|
|
24
|
+
};
|
|
25
|
+
if (Number.isNaN(row.usd)) { console.error('--usd must be a number'); process.exit(2); }
|
|
26
|
+
appendFileSync(file, JSON.stringify(row) + '\n');
|
|
27
|
+
console.log(JSON.stringify(row));
|
|
28
|
+
} else if (cmd === 'total') {
|
|
29
|
+
const rows = existsSync(file)
|
|
30
|
+
? readFileSync(file, 'utf8').split('\n').filter(Boolean).map((l) => JSON.parse(l)) : [];
|
|
31
|
+
const by = {}; let usd = 0; let secs = 0;
|
|
32
|
+
for (const r of rows) {
|
|
33
|
+
usd += r.usd; secs += r.seconds || 0;
|
|
34
|
+
by[r.model] ??= { calls: 0, usd: 0 };
|
|
35
|
+
by[r.model].calls++; by[r.model].usd += r.usd;
|
|
36
|
+
}
|
|
37
|
+
for (const [m, v] of Object.entries(by)) console.log(`${m}\t${v.calls} calls\t$${v.usd.toFixed(3)}`);
|
|
38
|
+
console.log(`TOTAL\t${rows.length} calls\t$${usd.toFixed(3)}\t${Math.round(secs)} s`);
|
|
39
|
+
if (opt.budget !== undefined) {
|
|
40
|
+
const b = Number(opt.budget);
|
|
41
|
+
if (usd > b) { console.log(`OVER BUDGET by $${(usd - b).toFixed(3)}: stop and say so`); process.exit(3); }
|
|
42
|
+
console.log(`within budget ($${(b - usd).toFixed(3)} left)`);
|
|
43
|
+
}
|
|
44
|
+
} else { console.error(`unknown command ${cmd}`); process.exit(2); }
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Mechanical sweep for machine-prose tells (references/voice.md §3-4).
|
|
3
|
+
# A candidate list, not a verdict: read every hit, then read the text aloud.
|
|
4
|
+
# Usage: voice-sweep.sh FILE [FILE...]
|
|
5
|
+
set -u
|
|
6
|
+
for F in "$@"; do
|
|
7
|
+
[ -f "$F" ] || { echo "no such file: $F" >&2; continue; }
|
|
8
|
+
# Strip YAML frontmatter, fenced code, HTML comments and markdown quote markers.
|
|
9
|
+
TXT=$(awk 'NR==1&&/^---$/{fm=1;next} fm&&/^---$/{fm=0;next} fm{next}
|
|
10
|
+
/^```/{code=!code;next} code{next}
|
|
11
|
+
/<!--/{c=1} !c{print} /-->/{c=0}' "$F" | sed 's/^[[:space:]]*>[[:space:]]*//')
|
|
12
|
+
# In code files, drop comment lines too, or a dash in a JSDoc counts as one in the copy.
|
|
13
|
+
case "$F" in *.svelte|*.ts|*.tsx|*.js|*.jsx|*.mjs) TXT=$(printf '%s\n' "$TXT" | grep -vE '^[[:space:]]*(\*|/\*|\*/|//)');; esac
|
|
14
|
+
t() { printf '%s\n' "$TXT"; }
|
|
15
|
+
paras=$(t | awk 'BEGIN{RS="";n=0}{n++}END{print n}')
|
|
16
|
+
dashes=$(t | grep -oE '—|—' | wc -l | tr -d ' ')
|
|
17
|
+
echo "== $F"
|
|
18
|
+
echo "paragraphs: $paras · em dashes: $dashes (about one per paragraph is the tell)"
|
|
19
|
+
echo "-- banned words and phrases"
|
|
20
|
+
t | grep -noiE "\b(delve|tapestry|testament|pivotal|vibrant|boasts?|foster|landscape|intricate|meticulous|showcase|garner|seamless(ly)?|unlock|supercharge|game.changer|underscore|honest(ly)?|serves as|functions as|in today's|the reality is|fast-paced)\b" | head -40
|
|
21
|
+
echo "-- meta-narration and negative parallelism"
|
|
22
|
+
t | grep -noiE "(here'?s (the|what|why|how)|here (is|are) (the|what|why|how)|the part (that|nobody)|let me (explain|close)|that'?s not (what|how|why|the)|\. not [a-z]+[.,]|it'?s not (about )?[a-z]+[,.] it'?s|, not [a-z]+[.,]|, not (from|about|for|because|just|only|what|the|a|an) |not just [a-z]+ but|(isn'?t|wasn'?t) [a-z]+[^.]{0,40}\. (it'?s|it was))" | head -40
|
|
23
|
+
t | grep -noiE "(is|was|are|were) not [^.]{1,50}\. (It|That|They) (is|was|are|were) " | head -20
|
|
24
|
+
echo "-- short fragments (candidates: brand names with dots are false positives)"
|
|
25
|
+
t | grep -oE "(^|[.!?]\"?[[:space:]]+)[A-Z][A-Za-z'-]{0,11}[.!?]" | sed 's/^[.!?"]*[[:space:]]*//' | sort | uniq -c | sort -rn | head -20
|
|
26
|
+
echo "-- drumbeat lines (2+ adjacent hits = three parallel sentences)"
|
|
27
|
+
t | grep -nE "^(The|An|A|Your|It|We|You) [a-z]+ [a-z]+ .{0,70}\.$" | head -10
|
|
28
|
+
echo "-- one-line paragraphs"
|
|
29
|
+
t | awk 'BEGIN{RS=""}{n=split($0,l,"\n"); if(n==1 && length($0)<60 && $0 !~ /^#|^[-*|]/) print " " $0}' | head -15
|
|
30
|
+
done
|
|
31
|
+
echo "Now read it aloud as the person would say it. The sweep is a net with holes."
|