@cloudglue/tinycloud 0.3.2 → 0.3.4

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/README.md CHANGED
@@ -16,11 +16,11 @@ The npm package is a small launcher: on first run it downloads the matching
16
16
  platform distribution from Cloudglue's CDN (cached under
17
17
  `~/.tinycloud/versions/<version>/`), verifies its checksum, and execs the real
18
18
  binary. The package version pins the binary version, so
19
- `npx @cloudglue/tinycloud@0.3.0` always runs tinycloud 0.3.0. It also adds two
19
+ `npx @cloudglue/tinycloud@0.3.4` always runs tinycloud 0.3.4. It also adds two
20
20
  wrapper commands:
21
21
 
22
22
  ```bash
23
- tinycloud install --version 0.3.0 # pre-download a version
23
+ tinycloud install --version 0.3.4 # pre-download a version
24
24
  tinycloud install --latest # install latest stable and pin to it
25
25
  tinycloud update # move to latest stable, prune old versions
26
26
  ```
@@ -50,14 +50,21 @@ This repo also distributes agent skills that teach coding agents (Claude
50
50
  Code, Codex, and anything else following the
51
51
  [Agent Skills](https://agentskills.io) standard) to drive the tinycloud CLI.
52
52
 
53
- **One command** (detects your agent and installs the bundled skills):
53
+ **One command** (in a terminal it prompts you to pick the target agents):
54
54
 
55
55
  ```bash
56
- npx @cloudglue/tinycloud skills install # project-level (.claude/skills, .agents/skills)
57
- npx @cloudglue/tinycloud skills install --global # ~/.claude/skills (all your projects)
56
+ npx @cloudglue/tinycloud skills install # menu: claude-code, agents, codex, cursor
57
+ npx @cloudglue/tinycloud skills install --harness cursor,codex # pick targets non-interactively
58
+ npx @cloudglue/tinycloud skills install --global # ~/.claude/skills (Claude Code only)
58
59
  npx @cloudglue/tinycloud skills install --skill tinycloud,blog-post # just some
59
60
  ```
60
61
 
62
+ Each agent reads skills from its own `<dir>/skills`: `claude-code` → `.claude`,
63
+ `agents` → `.agents` (the universal [Agent Skills](https://agentskills.io)
64
+ layout), `codex` → `.codex`, `cursor` → `.cursor`. The menu preselects dirs
65
+ that already exist; piped/CI runs (or `--yes`) skip it and install into
66
+ whichever dirs exist, defaulting to `.claude/skills` when none do.
67
+
61
68
  **Claude Code** (as a plugin):
62
69
 
63
70
  ```text
@@ -91,14 +98,133 @@ To give every agent session in a repo the same skills, commit them:
91
98
 
92
99
  ```bash
93
100
  cd your-project
94
- npx @cloudglue/tinycloud skills install # writes .claude/skills/ (and .agents/skills/ if present)
95
- git add .claude .agents 2>/dev/null; git commit -m "Add tinycloud agent skills"
101
+ npx @cloudglue/tinycloud skills install --harness claude-code,agents # or pick from the menu
102
+ git add .claude .agents .codex .cursor 2>/dev/null; git commit -m "Add tinycloud agent skills"
96
103
  ```
97
104
 
98
105
  Optionally add a line to your project's `CLAUDE.md` so agents reach for them:
99
106
  `Video work (analysis, captions, clips, workflows) goes through the tinycloud
100
107
  CLI — see the tinycloud skill; run tinycloud-init if the CLI isn't set up.`
101
108
 
109
+ ## Commands
110
+
111
+ Cloud commands run through your configured Cloudglue API key (billed per the
112
+ [rate card](https://app.cloudglue.dev/home/billing/rate-card)); local and
113
+ network commands are free. Every command prints a JSON envelope on stdout (logs
114
+ go to stderr) — pass `--json`.
115
+
116
+ | Command | What it does |
117
+ |---|---|
118
+ | `watch` | Analyze a video → reusable cached context + Cloudglue-ready ref |
119
+ | `extract` | Pull structured facts, entities, or moments (free-form or JSON-schema) |
120
+ | `caption` | Subtitles and transcripts (SRT/VTT/ASS) |
121
+ | `search` | Keyword search over cached video context |
122
+ | `probe` | Semantic moment/video search over a Cloudglue scope |
123
+ | `ask` | Grounded Q&A over one or more videos |
124
+ | `clip` | ffmpeg-backed cut, thumbs, stitch, transcode, burn, split, audio, info |
125
+ | `grab` | Download a remote video (YouTube, TikTok, Loom, direct) |
126
+ | `face` | Detect faces in a video, or match/search a known face, ranked by similarity |
127
+ | `library` | Build & query Cloudglue collections (create/add/remove/delete) and browse connectors |
128
+ | `jobs` | Poll, wait on, or forget async jobs |
129
+ | `workflow` | Run packaged pipeline recipes (see below) |
130
+ | `publish` | Publish HTML artifacts as Cloudglue Sites; share videos |
131
+ | `setup` | Configure the Cloudglue API key and service connections |
132
+
133
+ A few common invocations:
134
+
135
+ ```bash
136
+ # Analyze a video into reusable, cached context + a Cloudglue-ready ref
137
+ tinycloud watch ./demo.mp4 --json
138
+ # Pull structured findings (free-form query here; pass --schema for a fixed shape)
139
+ tinycloud extract "key moments with timestamps" ./demo.mp4 --json
140
+ # Subtitles plus a markdown transcript
141
+ tinycloud caption ./demo.mp4 --format srt --transcript --json
142
+ # Trim a clip locally — no upload, ffmpeg-backed
143
+ tinycloud clip cut ./demo.mp4 --start 12 --end 28 -o clip.mp4 --json
144
+ # Grounded Q&A over one or more videos
145
+ tinycloud ask "What objections came up?" --in ./demo.mp4 --json
146
+ # Detect faces, or match a known face against a video (0.3.4+)
147
+ tinycloud face match ./person.jpg ./demo.mp4 --max-faces 10 --json
148
+ ```
149
+
150
+ **Collections (0.3.4+)** turn a set of videos into a reusable, queryable
151
+ knowledge base. Build one and query it — every type follows the same
152
+ `create → add → poll show → query → delete` shape, differing only in `--type`
153
+ and the verb that reads it:
154
+
155
+ ```bash
156
+ tinycloud library collections create "calls" --type media-descriptions --json
157
+ tinycloud library collections add ./call.mp4 --to col_123 --json # enrichment is async
158
+ tinycloud library collections show col_123 --json # poll files[].status → completed
159
+ tinycloud ask "What did customers object to?" --in collection:col_123 --json
160
+ ```
161
+
162
+ `media-descriptions` backs `ask`/`probe`/`search`, `face-analysis` backs
163
+ `face list`/`face search`, and `entities` (created with `--prompt`/`--schema`)
164
+ backs `library collections entities`.
165
+
166
+ `tinycloud commands --json` is the authoritative, machine-readable list of
167
+ every command and flag. Full per-verb flags and cost classes:
168
+ [skills/tinycloud/reference/verbs.md](skills/tinycloud/reference/verbs.md).
169
+ The envelope contract — statuses (`ready`, `pending`, `needs_credentials`, …)
170
+ and exit codes:
171
+ [skills/tinycloud/reference/envelope.md](skills/tinycloud/reference/envelope.md).
172
+
173
+ ### Global flags & profiles (0.3.3+)
174
+
175
+ A few options are host-level — they isolate state rather than run a video
176
+ operation, so they go *before* the verb and don't appear in `commands --json`:
177
+
178
+ - `--home <dir>` (or `$TINYCLOUD_HOME`) — run against an isolated state home
179
+ (config, sessions, cache, jobs, artifacts, skills) instead of `~/.tinycloud`.
180
+ - `--profile <name>` — use a named profile's home, so multiple accounts or
181
+ installs run side by side without cross-contamination.
182
+
183
+ ```bash
184
+ tinycloud --home ./.tc watch ./demo.mp4 --json # isolated state for this repo
185
+ tinycloud profile list # profiles and their homes
186
+ tinycloud profile create work --default # create one and make it default
187
+ tinycloud profile create staging --copy-from work # clone an existing home
188
+ tinycloud --profile work ask "..." --in ./demo.mp4 --json
189
+ ```
190
+
191
+ Sessions are scoped per project (keyed by the git root), the agent takes a
192
+ `--skills <list>` allowlist alongside `--tools`, and a project-local
193
+ `.tinycloud/config.json` can pin tool/skill allowlists and an output base.
194
+ Details:
195
+ [skills/tinycloud/reference/setup.md](skills/tinycloud/reference/setup.md).
196
+
197
+ ## Workflows
198
+
199
+ Workflows are packaged, repeatable pipelines that run with a single command
200
+ and write their outputs into a run directory under
201
+ `./tinycloud-output/runs/<run_id>/`. The five flagship recipes — each with a
202
+ matching agent skill — are:
203
+
204
+ | Workflow | Turns a video into |
205
+ |---|---|
206
+ | `sales-coaching` | Coaching dashboard — call scores, speech metrics, objections |
207
+ | `blog-post` | Rich blog post — sections, thumbnails, takeaways |
208
+ | `ad-analysis` | Ad breakdown — shot timeline, hook, pacing, CTA |
209
+ | `meeting-breakdown` | Speaker timeline, chapter summaries, action items |
210
+ | `youtube-publish` | YouTube title, description, chapters, tags, subtitles |
211
+
212
+ ```bash
213
+ tinycloud workflow list --json # all available recipes
214
+ tinycloud workflow sales-coaching ./call.mp4 --json # run one
215
+ tinycloud workflow plan blog-post ./demo.mp4 --json # preview steps (free, no side effects)
216
+ tinycloud workflow validate ad-analysis --json # check a recipe (or a path)
217
+ ```
218
+
219
+ The final envelope reports `data.status`
220
+ (`completed | partial | failed | paused`), `data.outputs` (named outputs such
221
+ as `outputs.html`), and `data.artifacts[].path`. The five above each have a
222
+ matching agent skill (see the table), so coding agents can run them directly;
223
+ `tinycloud workflow list` shows the full set, including building blocks like
224
+ `summary` and `clip-highlights`. Author your own recipes:
225
+ [skills/tinycloud/reference/workflow-authoring.md](skills/tinycloud/reference/workflow-authoring.md)
226
+ (or scaffold one with the `tinycloud-skill-creator` skill).
227
+
102
228
  ## License
103
229
 
104
230
  © Aviary Inc. (d/b/a Cloudglue). All rights reserved. Use is subject to [Aviary Inc. Terms of Service](https://cloudglue.dev/terms).
package/lib/skills.js CHANGED
@@ -3,6 +3,7 @@
3
3
  const fs = require("node:fs");
4
4
  const os = require("node:os");
5
5
  const path = require("node:path");
6
+ const readline = require("node:readline");
6
7
 
7
8
  // Agent skills bundled with this package (the npm tarball includes skills/).
8
9
  function bundledSkillsDir() {
@@ -19,28 +20,95 @@ function listBundledSkills() {
19
20
  }
20
21
 
21
22
  /**
22
- * Pick install targets. Each harness keeps skills in its own directory; we
23
- * detect harnesses by their config dir to avoid littering projects that
24
- * don't use them.
23
+ * Known agent harnesses. Each keeps skills in its own <configDir>/skills dir,
24
+ * all using the SKILL.md layout. `id` is both the menu label and the
25
+ * --harness token; `aliases` are extra accepted tokens.
25
26
  * claude-code: <project>/.claude/skills (global: ~/.claude/skills)
26
- * codex: <project>/.agents/skills (agentskills.io layout)
27
+ * agents: <project>/.agents/skills (universal agentskills.io layout)
28
+ * codex: <project>/.codex/skills
29
+ * cursor: <project>/.cursor/skills
27
30
  */
28
- function resolveTargets({ global: isGlobal, dir, cwd = process.cwd() }) {
31
+ const HARNESSES = [
32
+ { id: "claude-code", aliases: ["claude"], configDir: ".claude" },
33
+ { id: "agents", aliases: [], configDir: ".agents" },
34
+ { id: "codex", aliases: [], configDir: ".codex" },
35
+ { id: "cursor", aliases: [], configDir: ".cursor" },
36
+ ];
37
+
38
+ function harnessIds() {
39
+ return HARNESSES.map((h) => h.id);
40
+ }
41
+
42
+ // Resolve a --harness token (id or alias, case-insensitive) to its entry.
43
+ function findHarness(token) {
44
+ const t = String(token).trim().toLowerCase();
45
+ return HARNESSES.find((h) => h.id === t || h.aliases.includes(t));
46
+ }
47
+
48
+ function harnessTarget(h, cwd) {
49
+ return { name: h.id, dir: path.join(cwd, h.configDir, "skills") };
50
+ }
51
+
52
+ function detectHarnesses(cwd) {
53
+ return HARNESSES.filter((h) => fs.existsSync(path.join(cwd, h.configDir)));
54
+ }
55
+
56
+ /**
57
+ * Pick install targets from flags/detection only — no prompting or I/O beyond
58
+ * existence checks, so it stays deterministic for tests. The interactive menu
59
+ * lives in promptForTargets(). Precedence: --dir > --global > --harness >
60
+ * detection (and .claude when nothing is detected).
61
+ */
62
+ function resolveTargets({ global: isGlobal, dir, harness, cwd = process.cwd() }) {
29
63
  if (dir) return [{ name: "custom", dir: path.resolve(dir) }];
30
64
  if (isGlobal) return [{ name: "claude-code (global)", dir: path.join(os.homedir(), ".claude", "skills") }];
31
65
 
32
- const targets = [];
33
- if (fs.existsSync(path.join(cwd, ".claude"))) {
34
- targets.push({ name: "claude-code", dir: path.join(cwd, ".claude", "skills") });
35
- }
36
- if (fs.existsSync(path.join(cwd, ".agents"))) {
37
- targets.push({ name: "codex", dir: path.join(cwd, ".agents", "skills") });
66
+ if (harness && harness.length) {
67
+ return harness.map((token) => {
68
+ const h = findHarness(token);
69
+ if (!h) throw new Error(`Unknown harness: ${token}. Valid: ${harnessIds().join(", ")}`);
70
+ return harnessTarget(h, cwd);
71
+ });
38
72
  }
39
- if (targets.length === 0) {
40
- // No harness detected: default to claude-code project layout.
41
- targets.push({ name: "claude-code", dir: path.join(cwd, ".claude", "skills") });
73
+
74
+ const detected = detectHarnesses(cwd);
75
+ if (detected.length) return detected.map((h) => harnessTarget(h, cwd));
76
+ // No harness detected: default to claude-code project layout.
77
+ return [harnessTarget(HARNESSES[0], cwd)];
78
+ }
79
+
80
+ /**
81
+ * Interactive picker: list the four harnesses (detected ones preselected) and
82
+ * read a comma-separated choice. Streams are injectable for testing; empty or
83
+ * unparseable input falls back to the preselection.
84
+ */
85
+ async function promptForTargets({ cwd = process.cwd(), input = process.stdin, output = process.stdout } = {}) {
86
+ const detected = new Set(detectHarnesses(cwd).map((h) => h.id));
87
+ const rows = HARNESSES.map((h, i) => ({ n: i + 1, h, detected: detected.has(h.id) }));
88
+ const preselected = rows.some((r) => r.detected)
89
+ ? rows.filter((r) => r.detected).map((r) => r.n)
90
+ : [1];
91
+
92
+ output.write("Which agents should get the skills?\n");
93
+ for (const r of rows) {
94
+ const mark = preselected.includes(r.n) ? "x" : " ";
95
+ const tag = r.detected ? " (detected)" : "";
96
+ output.write(` ${r.n}) [${mark}] ${r.h.id.padEnd(11)} ${path.join(r.h.configDir, "skills")}${tag}\n`);
42
97
  }
43
- return targets;
98
+
99
+ const rl = readline.createInterface({ input, output });
100
+ const answer = await new Promise((resolve) => {
101
+ rl.question(`Enter numbers (comma-separated) [${preselected.join(",")}]: `, resolve);
102
+ });
103
+ rl.close();
104
+
105
+ let nums = answer
106
+ .split(",")
107
+ .map((s) => parseInt(s.trim(), 10))
108
+ .filter((n) => Number.isInteger(n) && n >= 1 && n <= rows.length);
109
+ if (!nums.length) nums = preselected;
110
+
111
+ return rows.filter((r) => nums.includes(r.n)).map((r) => harnessTarget(r.h, cwd));
44
112
  }
45
113
 
46
114
  function installSkills({ skills, targets, force }) {
@@ -70,12 +138,17 @@ const USAGE = `Usage: tinycloud skills <list|install> [options]
70
138
 
71
139
  Install options:
72
140
  --skill <a,b,...> Only these skills (default: all)
73
- --global Install to ~/.claude/skills instead of the project
141
+ --harness <a,b,...> Install into these agents: claude-code, agents, codex, cursor
142
+ (default: pick from a menu in a terminal; auto-detect otherwise)
143
+ --global Install to ~/.claude/skills instead of the project (Claude Code only)
74
144
  --dir <path> Install to an explicit directory
145
+ --yes, -y Skip the interactive menu; use auto-detection
75
146
  --force Overwrite skills that are already installed
76
147
 
77
- Detection: a project .claude/ dir targets Claude Code (.claude/skills),
78
- a .agents/ dir targets Codex (.agents/skills); both when both exist.
148
+ Each harness holds skills under <dir>/skills: claude-code .claude, agents → .agents
149
+ (universal agentskills.io layout), codex .codex, cursor .cursor. Run in a terminal
150
+ with no target and you'll be prompted to pick (detected dirs preselected); piped/CI runs
151
+ install into whichever dirs exist, or .claude when none do.
79
152
  `;
80
153
 
81
154
  async function cmdSkills(args) {
@@ -97,7 +170,9 @@ async function cmdSkills(args) {
97
170
  let wanted = available;
98
171
  let isGlobal = false;
99
172
  let force = false;
173
+ let yes = false;
100
174
  let dir;
175
+ let harness;
101
176
  for (let i = 1; i < args.length; i++) {
102
177
  if (args[i] === "--skill" && args[i + 1]) {
103
178
  const names = args[++i].split(",").map((s) => s.trim()).filter(Boolean);
@@ -106,8 +181,15 @@ async function cmdSkills(args) {
106
181
  throw new Error(`Unknown skill(s): ${unknown.join(", ")}. Available: ${available.join(", ")}`);
107
182
  }
108
183
  wanted = names;
184
+ } else if (args[i] === "--harness" && args[i + 1]) {
185
+ harness = args[++i].split(",").map((s) => s.trim()).filter(Boolean);
186
+ const unknown = harness.filter((id) => !findHarness(id));
187
+ if (unknown.length) {
188
+ throw new Error(`Unknown harness(es): ${unknown.join(", ")}. Valid: ${harnessIds().join(", ")}`);
189
+ }
109
190
  } else if (args[i] === "--global") isGlobal = true;
110
191
  else if (args[i] === "--force") force = true;
192
+ else if (args[i] === "--yes" || args[i] === "-y") yes = true;
111
193
  else if (args[i] === "--dir" && args[i + 1]) dir = args[++i];
112
194
  else throw new Error(`Unknown install option: ${args[i]}\n${USAGE}`);
113
195
  }
@@ -116,11 +198,24 @@ async function cmdSkills(args) {
116
198
  throw new Error("No bundled skills found in this package installation");
117
199
  }
118
200
 
119
- const targets = resolveTargets({ global: isGlobal, dir });
201
+ const explicit = dir || isGlobal || (harness && harness.length);
202
+ const targets =
203
+ !explicit && !yes && process.stdin.isTTY && process.stdout.isTTY
204
+ ? await promptForTargets({})
205
+ : resolveTargets({ global: isGlobal, dir, harness });
206
+
120
207
  const results = installSkills({ skills: wanted, targets, force });
121
208
  for (const r of results) console.log(`${r.status === "installed" ? "✓" : "-"} ${r.skill} → ${r.dir} [${r.status}]`);
122
209
  const installed = results.filter((r) => r.status === "installed").length;
123
210
  console.log(`\n${installed} skill(s) installed${installed ? ". Restart your agent session to pick them up." : "."}`);
124
211
  }
125
212
 
126
- module.exports = { cmdSkills, resolveTargets, installSkills, listBundledSkills, bundledSkillsDir };
213
+ module.exports = {
214
+ cmdSkills,
215
+ resolveTargets,
216
+ promptForTargets,
217
+ installSkills,
218
+ listBundledSkills,
219
+ bundledSkillsDir,
220
+ HARNESSES,
221
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudglue/tinycloud",
3
- "version": "0.3.2",
3
+ "version": "0.3.4",
4
4
  "description": "Agent CLI for deep video work, by Cloudglue. Downloads the tinycloud binary on first run.",
5
5
  "bin": {
6
6
  "tinycloud": "bin/tinycloud.js"
@@ -23,6 +23,6 @@
23
23
  "url": "git+https://github.com/cloudglue/tinycloud.git"
24
24
  },
25
25
  "homepage": "https://tinycloud.sh",
26
- "keywords": ["video", "cloudglue", "cli", "agent", "captions", "clips"],
26
+ "keywords": ["video", "cloudglue", "cli", "agent", "captions", "clips", "face-detection"],
27
27
  "license": "SEE LICENSE IN LICENSE.md"
28
28
  }
@@ -61,8 +61,8 @@ Full schema and error codes: [reference/envelope.md](reference/envelope.md).
61
61
 
62
62
  ## 2. Core verbs (cheat sheet)
63
63
 
64
- Cloud verbs (`watch extract probe ask publish`) call the Cloudglue API using
65
- the configured key — usage is billed per the
64
+ Cloud verbs (`watch extract probe ask publish face`) call the Cloudglue API
65
+ using the configured key — usage is billed per the
66
66
  [rate card](https://app.cloudglue.dev/home/billing/rate-card). `search clip
67
67
  setup` are local and free; `grab jobs` are network-only.
68
68
  `tinycloud commands --json` is the authoritative command/flag list.
@@ -89,13 +89,29 @@ tinycloud clip cut ./demo.mp4 --start 12 --end 28 -o ./tinycloud-output/clip.mp4
89
89
  tinycloud clip thumbs ./demo.mp4 --interval 5 -o ./tinycloud-output/thumbs/ --json
90
90
  tinycloud clip burn ./demo.mp4 --subtitle-file ./captions/demo.srt -o ./out.mp4 --json
91
91
 
92
- # Remote videos, collections, async jobs
92
+ # Faces on a single video (cloud, 0.3.4+) — for collection-scale face search/list, see Collections below
93
+ tinycloud face detect ./demo.mp4 --json # every face → normalized box + timestamp
94
+ tinycloud face match ./person.jpg ./demo.mp4 --max-faces 10 --json # query image, ranked 0–100 similarity
95
+
96
+ # Remote videos, async jobs
93
97
  tinycloud grab https://youtu.be/<id> -o ./tinycloud-output/grabbed/ --json
94
98
  tinycloud library connectors sync https://example.com/clip.mp4 --json # public URL → Cloudglue file (not YouTube — use grab)
95
- tinycloud library collections list --json
96
99
  tinycloud watch ./long.mp4 --background --json # returns pending + meta.job_id
97
100
  tinycloud jobs wait <job-id> --timeout 120s --json
98
101
 
102
+ # Collections (0.3.4+) — turn videos into a reusable, queryable knowledge base.
103
+ # Lifecycle (every --type): create → add → poll show → query → delete.
104
+ tinycloud library collections list --json
105
+ tinycloud library collections create my-desc --type media-descriptions --json # types: media-descriptions | face-analysis | entities (--prompt) | rich-transcripts
106
+ tinycloud library collections add ./demo.mp4 --to col_desc --json # uploads a local source first; enrichment is async (pending)
107
+ tinycloud library collections show col_desc --json # poll files[].status until completed, then query —
108
+ # the collection's --type decides the read verb (each line below is a DIFFERENT, matching-type collection):
109
+ tinycloud ask "what's discussed?" --in collection:col_desc --json # media-descriptions → ask / probe / search
110
+ tinycloud face search ./person.jpg --in collection:col_faces --json # face-analysis → face list / face search
111
+ tinycloud library collections entities col_ents ./demo.mp4 --json # entities → collections entities
112
+ tinycloud library collections remove cloudglue://files/<id> --from col_desc --json
113
+ tinycloud library collections delete col_desc --json
114
+
99
115
  # Publish an HTML artifact to Cloudglue Sites (manage with list / unpublish)
100
116
  tinycloud publish ./tinycloud-output/html/report.html --name report --visibility private --json
101
117
  tinycloud publish list --json
@@ -110,6 +126,13 @@ Evaluating a video the host project's code rendered (render → evaluate →
110
126
  edit → rerender): the render-review loop in
111
127
  [reference/pipelines.md](reference/pipelines.md).
112
128
 
129
+ Isolation & scope (0.3.3+): `--home <dir>` / `$TINYCLOUD_HOME` and
130
+ `--profile <name>` are leading flags (before the verb) that run against an
131
+ isolated state home; the agent also takes `--skills <list>` alongside
132
+ `--tools <list>`, and a project-local `.tinycloud/config.json` can pin those
133
+ allowlists and an output base. Sessions are scoped per project. Details:
134
+ [reference/setup.md](reference/setup.md).
135
+
113
136
  ## 3. Workflows (packaged recipes)
114
137
 
115
138
  Repeatable pipelines run with one command and write outputs into a run
@@ -170,7 +193,7 @@ Authoring your own recipes: [reference/workflow-authoring.md](reference/workflow
170
193
 
171
194
  ## 5. Reference (load on demand)
172
195
 
173
- - [reference/setup.md](reference/setup.md) — install, credentials, env vars, preflight details
196
+ - [reference/setup.md](reference/setup.md) — install, credentials, env vars, preflight, profiles & project scope
174
197
  - [reference/verbs.md](reference/verbs.md) — every verb, flag, and cost class
175
198
  - [reference/envelope.md](reference/envelope.md) — full envelope schema, statuses, error codes, exit codes
176
199
  - [reference/pipelines.md](reference/pipelines.md) — pipes, batching, jobs, cache/spend-control flags
@@ -23,7 +23,15 @@ connector?" or an envelope field needs explaining.
23
23
  reuse the file.
24
24
  - **Collection** — a named group of Cloudglue files (id `col_…`), e.g. "all
25
25
  sales calls". Verbs scope to one with `--in collection:col_…`. Collection
26
- ids are stable; display names are not.
26
+ ids are stable; display names are not. A collection has a type that decides
27
+ which verb reads it: `media-descriptions` (default) backs `ask`/`probe`/`search`,
28
+ `face-analysis` backs `face list`/`face search`, and `entities` (created with
29
+ `--prompt`/`--schema`) backs `library collections entities` (`rich-transcripts`
30
+ also exists). Manage them with `library collections create|add|remove|delete`
31
+ (0.3.4+); every type follows `create → add → poll show → query → delete`. `add`
32
+ enriches each file asynchronously and returns `pending` — poll
33
+ `library collections show <col>` until every `files[].status` is `completed`
34
+ before querying.
27
35
  - **Data connector** — a linked external source of recordings (Zoom, Grain,
28
36
  Google Drive, Dropbox, Loom, S3/GCS). `tinycloud library connectors …`
29
37
  lists, browses (`files`, with provider-specific filters), and syncs
@@ -88,3 +96,17 @@ connector?" or an envelope field needs explaining.
88
96
  events re-dispatched on the element) for custom site HTML, and plays
89
97
  standalone or inside the container components (`<cg-playlist>`,
90
98
  `<cg-grid>`, `<cg-chapters>`) — see reference/verbs.md.
99
+
100
+ ## State and isolation (0.3.3+)
101
+
102
+ - **Home** — the directory holding all tinycloud state for a run: config,
103
+ sessions, cache, jobs, artifacts, and skills. Default `~/.tinycloud`;
104
+ relocate it with `--home <dir>` or `$TINYCLOUD_HOME`.
105
+ - **Profile** — a named, fully isolated home, selected with `--profile <name>`
106
+ and managed by `tinycloud profile list|show|create|use|remove`. Lets multiple
107
+ accounts or installs run side by side without cross-contamination. (Distinct
108
+ from `watch --profile`, which selects an analysis profile.)
109
+ - **Project scope** — sessions and capabilities keyed to a project (its git
110
+ root). Sessions live under `<home>/projects/<project-key>/sessions`, and a
111
+ project-local `.tinycloud/config.json` can pin tool/skill allowlists and an
112
+ output base — precedence is CLI flags > project config > global config.
@@ -92,10 +92,34 @@ tinycloud clip burn ./demo.mp4 \
92
92
  tinycloud grab https://youtu.be/<id> -o ./tinycloud-output/grabbed/ --json
93
93
  tinycloud watch ./tinycloud-output/grabbed/<file>.mp4 --json
94
94
 
95
- # Collection: mirror artifacts locally, then search/Q&A against it
95
+ # Collections (0.3.4+) all follow one lifecycle create → add → poll show → query → delete.
96
+ # `add` enriches each file asynchronously and returns pending; poll `collections show`
97
+ # and wait until every files[].status is `completed` before querying.
98
+
99
+ # media-descriptions (default) → ask / probe / search
100
+ tinycloud library collections create "sales-calls" --type media-descriptions --json
101
+ tinycloud library collections add ./call-1.mp4 --to col_123 --json # → pending
102
+ tinycloud library collections show col_123 --json # poll files[].status → completed
103
+ tinycloud probe "pricing objections" --in collection:col_123 --scope segment --json
104
+ tinycloud ask "What did customers object to across these calls?" --in collection:col_123 --json
105
+ tinycloud library collections delete col_123 --json
106
+
107
+ # face-analysis → face list / face search
108
+ tinycloud library collections create faces --type face-analysis --json
109
+ tinycloud library collections add ./interview.mp4 --to col_123 --json # → pending
110
+ tinycloud library collections show col_123 --json # poll files[].status → completed
111
+ tinycloud face list ./interview.mp4 --in collection:col_123 --json # stored detections for that video
112
+ tinycloud face search ./headshot.jpg --in collection:col_123 --group-by file --json
113
+
114
+ # entities (create needs --prompt or --schema) → library collections entities
115
+ tinycloud library collections create ents --type entities --prompt "people, places, objects" --json
116
+ tinycloud library collections add ./interview.mp4 --to col_123 --json # → pending
117
+ tinycloud library collections show col_123 --json # poll files[].status → completed
118
+ tinycloud library collections entities col_123 ./interview.mp4 --json # structured entities (video + segment level)
119
+
120
+ # Already-built collection: mirror description/transcript artifacts locally for free `search`
96
121
  tinycloud library collections sync col_123 --artifacts descriptions,transcripts --json
97
- tinycloud probe "pricing discussion" --in collection:col_123 --scope segment --json
98
- tinycloud ask "What did customers object to?" --in collection:col_123 --json
122
+ tinycloud search "discount" --in collection:col_123 --json
99
123
 
100
124
  # Extract timestamped findings → cut them into clips
101
125
  tinycloud watch ./talk.mp4 --json \
@@ -25,7 +25,8 @@ supported — use WSL2. More at https://tinycloud.sh.
25
25
 
26
26
  ## Credentials
27
27
 
28
- Cloud verbs (`watch extract probe ask publish`) need a Cloudglue API key.
28
+ Cloud verbs (`watch extract probe ask publish face`) need a Cloudglue API key
29
+ (as do `library collections add` and the collection reads).
29
30
  Usage is billed to that key per the
30
31
  [rate card](https://app.cloudglue.dev/home/billing/rate-card).
31
32
 
@@ -95,9 +96,57 @@ binary reports in `--version --json`.
95
96
  | `CLOUDGLUE_API_KEY` | Cloudglue API key (alternative to `tinycloud setup cloudglue`) |
96
97
  | `TINYCLOUD_VERSION` | npm launcher: run a specific binary version |
97
98
  | `TINYCLOUD_INSTALL_DIR` | npm launcher: cache root (default `~/.tinycloud`) |
99
+ | `TINYCLOUD_HOME` | Isolated state home — config, sessions, cache, jobs, artifacts, skills (default `~/.tinycloud`; same as `--home`). 0.3.3+ |
100
+ | `TINYCLOUD_OUT` | Output base for generated files (wins over a config `outputBase`) |
98
101
  | `TINYCLOUD_HTTP_TIMEOUT_MS` | Hard deadline per Cloudglue request (default 120s; `0` disables) |
99
102
  | `TINYCLOUD_UPLOAD_TIMEOUT_MS` | Deadline for upload-shaped requests (default 60min; `0` disables) |
100
103
 
101
104
  Every Cloudglue request carries a hard deadline, so a stalled route can never
102
105
  hang the CLI indefinitely; a timeout surfaces as a retryable `upstream` error
103
106
  envelope whose message names the knob to adjust.
107
+
108
+ ## Profiles & isolated homes (0.3.3+)
109
+
110
+ Every piece of tinycloud state — config, sessions, cache, jobs, artifacts, and
111
+ skills — lives under one home (default `~/.tinycloud`). Relocate it to run
112
+ multiple accounts or installs side by side without cross-contamination. Both
113
+ options are *leading* (before the verb) and work with any command:
114
+
115
+ - `--home <dir>` (or `$TINYCLOUD_HOME`) — use that directory as the home.
116
+ - `--profile <name>` — use a named profile's home (from the profiles registry).
117
+
118
+ Named profiles are managed by the host-level `profile` verb (a CLI/host
119
+ concern, so it is not in `commands --json`):
120
+
121
+ ```bash
122
+ tinycloud profile list # profiles and their homes (active marked *)
123
+ tinycloud profile show [<name>] # home path + exists/default/active
124
+ tinycloud profile create <name> [--home <dir>] [--copy-from <name>] \
125
+ [--description <text>] [--default]
126
+ tinycloud profile use <name> # set the default profile
127
+ tinycloud profile remove <name> # unregister (does not delete the home)
128
+ ```
129
+
130
+ `--copy-from` seeds the new profile's home from an existing one. The registry
131
+ lives at `$XDG_CONFIG_HOME/tinycloud/profiles.json`; `default` is reserved.
132
+ This global `--profile <name>` is unrelated to `watch --profile
133
+ default|light|custom` (that flag selects an analysis profile).
134
+
135
+ ## Project scope (0.3.3+)
136
+
137
+ Within a home, sessions are scoped per project — keyed by the canonical git
138
+ root — under `<home>/projects/<project-key>/sessions`. In the interactive
139
+ agent, `/sessions` lists the current project's sessions (`/sessions all` spans
140
+ every project); `-c` resumes the most recent and still falls back to legacy
141
+ flat sessions (read-only, migrated forward on resume).
142
+
143
+ A project can also carry a local `.tinycloud/config.json` that scopes a run:
144
+
145
+ - `preferences.tools` / `preferences.skills` — allowlists of agent tool / skill
146
+ names (omit = all; the `--tools` / `--skills` flags override them).
147
+ - `preferences.outputBase` — where generated files land (a relative path is
148
+ anchored to the project root; `$TINYCLOUD_OUT` still wins).
149
+
150
+ Precedence is **CLI flags > project-local `.tinycloud/config.json` > global
151
+ config**; a more specific scope replaces (does not merge) a broader one.
152
+ Read-only mode always keeps the `read` and `bash` tools.
@@ -14,7 +14,8 @@ every verb. Regenerate doubts from it instead of trusting prose.
14
14
  | `ask` | cloud | yes | Grounded Q&A over one or more videos |
15
15
  | `clip` | local | no | Cuts, thumbs, audio, stitch, split, transcode, burn, explore |
16
16
  | `grab` | network | no | Download a remote video (YouTube, TikTok, Loom, direct) |
17
- | `library` | varies | no | Collections, connectors, local mirrors, sync |
17
+ | `face` | cloud | yes | Detect faces in a video, or match/search a query face (0.3.4+) |
18
+ | `library` | varies | no | Collections (incl. create/add/remove/delete), connectors, mirrors, sync |
18
19
  | `jobs` | network | yes | Poll/wait/forget tracked async jobs |
19
20
  | `workflow` | varies | no | Validate/plan/run workflow recipes |
20
21
  | `publish` | cloud | yes | Publish HTML/code artifacts as Cloudglue Sites; share videos |
@@ -23,6 +24,24 @@ every verb. Regenerate doubts from it instead of trusting prose.
23
24
  Cloud verbs run through the configured Cloudglue API key.
24
25
  `caption`/`library`/`workflow` vary by what they end up doing.
25
26
 
27
+ ## Global flags (0.3.3+)
28
+
29
+ Leading options (placed *before* the verb) and agent-level allowlists, separate
30
+ from the per-verb flags below. `--home`/`--profile` and the `profile` verb are
31
+ host concerns and are intentionally absent from `commands --json`.
32
+
33
+ - `--home <dir>` / `$TINYCLOUD_HOME` — run against an isolated state home
34
+ (config, sessions, cache, jobs, artifacts, skills) instead of `~/.tinycloud`.
35
+ - `--profile <name>` — use a named profile's home. Managed by
36
+ `tinycloud profile list|show|create|use|remove`
37
+ (`create <name> [--home <dir>] [--copy-from <name>] [--description <text>] [--default]`).
38
+ Unrelated to `watch --profile default|light|custom` (an analysis profile).
39
+ - `--skills <list>` (0.3.3+) / `--tools <list>` — comma-separated agent skill /
40
+ tool allowlists (omit = all); also settable per project via
41
+ `.tinycloud/config.json`.
42
+
43
+ Profiles, project-scoped sessions, and `.tinycloud/config.json`: [setup.md](setup.md).
44
+
26
45
  ## Per-verb flags
27
46
 
28
47
  Flags shared by most verbs are listed once at the bottom.
@@ -108,17 +127,84 @@ tinycloud clip cut --from-findings -o clips/ # cut timestamped findings p
108
127
  tinycloud grab <url> [-o <file-or-dir>] [--audio-only] [--format <yt-dlp-selector>]
109
128
  ```
110
129
 
130
+ ### face — detect & match faces (cloud, 0.3.4+)
131
+
132
+ ```bash
133
+ tinycloud face detect <source> [--fps <n>] [--start <t>] [--end <t>]
134
+ [--thumbnails] [--limit <n>] --json
135
+ tinycloud face match <image> <source> [--max-faces <n>] [--min-similarity <0-100>]
136
+ [--fps <n>] [--start <t>] [--end <t>] [--thumbnails] --json
137
+ tinycloud face list <source> --in collection:col_… [--limit <n>] [--offset <n>] --json
138
+ tinycloud face search <image> --in collection:col_… [col_…]
139
+ [--min-score <n>] [--group-by file] [--limit <n>] --json
140
+ ```
141
+
142
+ `detect` runs Cloudglue face detection over a video and returns every face as
143
+ a normalized 0–1 bounding box (`{top,left,width,height}`) plus a timestamp.
144
+ `match` takes a query image — a local file (downscaled and sent inline, **never
145
+ uploaded**) or an http(s) URL — and returns the closest faces ranked by a 0–100
146
+ `similarity`. Both upload the *video* first like `watch`/`extract`
147
+ (`needs_upload` without `--no-upload`) and cache by source + options, so re-runs
148
+ are free. `--fps`/`--start`/`--end` tune sampling and window;
149
+ `--max-faces`/`--min-similarity` bound `match`, `--limit` bounds `detect`,
150
+ `--thumbnails` adds per-face frame URLs.
151
+
152
+ `list` and `search` operate over a **face-analysis collection** (create one with
153
+ `library collections create --type face-analysis` and add videos with
154
+ `library collections add`): `list` reads a video's stored detections; `search`
155
+ finds the query face across one or more collections (`--min-score`,
156
+ `--group-by file`). `total` reports the server-available count across all modes
157
+ (never rewritten by client `--min-*`/`--limit` filters).
158
+
111
159
  ### library — collections and connectors
112
160
 
113
161
  ```bash
114
162
  tinycloud library collections list --json
115
- tinycloud library collections show <col_id> --json
163
+ tinycloud library collections show <col_id> --json # files[].status: pending|processing|completed (readiness)
116
164
  tinycloud library collections sync <col_id> --artifacts descriptions,transcripts,thumbnails,metadata --json
165
+ # Collection writes (0.3.4+) — the only write paths in library:
166
+ tinycloud library collections create <name> [--type media-descriptions|entities|rich-transcripts|face-analysis] [--description <text>] [--prompt <text> | --schema <file>] --json
167
+ tinycloud library collections add <source> --to <col_id> [--no-upload] [--no-download] --json
168
+ tinycloud library collections remove <source> --from <col_id> --json
169
+ tinycloud library collections delete <col_id> --json
170
+ tinycloud library collections entities <col_id> <source> [--limit <n>] [--offset <n>] --json # read a video's entities
117
171
  tinycloud library connectors list --json
118
172
  tinycloud library connectors files <connector-id> [--limit 25] [--page-token <t>] --json
119
173
  tinycloud library connectors sync [<connector-id>] <uri-share-link-or-public-url> --json
120
174
  ```
121
175
 
176
+ `collections create|add|remove|delete` are the only writes in an otherwise
177
+ read-only `library` (gated by the `library.collections.create.v1` /
178
+ `library.collections.mutate.v1` feature ids). `create` defaults to
179
+ `--type media-descriptions`; an `entities` collection also needs an extraction
180
+ spec — `--prompt <text>` or `--schema <file.json>` — or `create` errors. `add`
181
+ (`--to <col>`, or `--collection`) resolves the source like `watch`/`extract` —
182
+ a local file uploads first (or `needs_upload` with `--no-upload`) — and records
183
+ the file→collection mapping; `remove` (`--from <col>`) takes a Cloudglue file
184
+ id/uri; `delete` removes the whole collection (and cleans the local mirror).
185
+ Collection ids accept a bare uuid, a `col_…` slug, or `collection:<id>` /
186
+ `cloudglue://collections/<id>` forms, consistently across read and write paths.
187
+
188
+ **Readiness — always poll before querying.** `add` enriches each file
189
+ asynchronously and returns `pending`. Poll `collections show <col> --json` and
190
+ wait until every `files[].status` is `completed` (`pending → processing →
191
+ completed`; `failed` is terminal) — a query before then returns empty or errors.
192
+
193
+ The collection's `--type` decides which verb reads it (every type follows the
194
+ same `create → add → poll show → query → delete` lifecycle):
195
+
196
+ | `--type` | read with |
197
+ |---|---|
198
+ | `media-descriptions` (default) | `ask` / `probe` / `search` (`--in collection:<col>`) |
199
+ | `face-analysis` | `face list` / `face search` |
200
+ | `entities` (needs `--prompt`/`--schema`) | `library collections entities <col> <source>` |
201
+ | `rich-transcripts` | `collections sync --artifacts transcripts` |
202
+
203
+ `collections entities <col> <source>` returns a video's extracted entities
204
+ (video- and segment-level, `--limit`/`--offset`) from an `entities` collection.
205
+ For a one-off per-video pull without standing up a collection, `extract` returns
206
+ entities/facts directly (free-form query or `--schema`).
207
+
122
208
  `connectors sync` materializes its argument into a Cloudglue file without
123
209
  starting analysis (idempotent). The connector id is optional — with just a
124
210
  URI or link, sync routes through the matching connector type. Connector URIs
@@ -257,11 +343,16 @@ Output: `--json` (force JSONL envelopes), `--pretty` (one JSON array),
257
343
  `--data raw`, `--raw-output` (raw backend payload; disables pipe protocol),
258
344
  `--quiet`, `--verbose`.
259
345
 
260
- Cache/spend — on `watch`, `extract`, `caption`, and `workflow` only:
346
+ Cache — on `watch`, `extract`, `caption`, `face`, and `workflow` only:
261
347
  `--refresh` (recompute), `--no-cache` (no persistence), `--cached` (reuse
262
- exact-match history), `--no-upload` (refuse cloud upload `needs_upload`),
263
- `--no-download` (refuse local materialization → `needs_download`).
264
- `ask`/`probe` always call the cloud; use `search` for a free cached lookup.
348
+ exact-match history). `ask`/`probe` always call the cloud; use `search` for a
349
+ free cached lookup.
350
+
351
+ Upload/download refusal — on every verb that resolves a source:
352
+ `--no-upload` (refuse cloud upload → `needs_upload`) on `watch`/`extract`/
353
+ `caption`/`face`/`workflow`/`publish` and `library collections add`;
354
+ `--no-download` (refuse local materialization → `needs_download`) on the same
355
+ set minus `publish`.
265
356
 
266
357
  Source reuse (`watch`/`extract`/`caption`): `--source-id <id>`, `--result-id <id>`.
267
358
 
@@ -141,5 +141,9 @@ are not implemented in 0.3.x — treat `partial`/`paused` as terminal.
141
141
  The tinycloud agent picks it up on next start; from any shell it runs by
142
142
  path: `tinycloud workflow run ~/.tinycloud/skills/my-skill/my-skill.yaml demo.mp4 --allow-command --json`.
143
143
 
144
+ With `--home`/`--profile` (0.3.3+) the global skills dir moves under the active
145
+ home (`<home>/skills/`); a project-local `.tinycloud/config.json` can also pin
146
+ which skills load via a `preferences.skills` allowlist.
147
+
144
148
  To wrap a recipe as a skill for *this* host agent instead, see the
145
149
  `tinycloud-skill-creator` skill in this repo.
@@ -9,11 +9,11 @@ set -u
9
9
 
10
10
  # Mirror tinycloud-skill.json: min_version / supported_range upper bound
11
11
  # (CI diffs these against the manifest).
12
- MIN_VERSION="0.3.2"
12
+ MIN_VERSION="0.3.4"
13
13
  MAX_VERSION_EXCLUSIVE="0.4.0"
14
14
  INSTALL_CMD='curl -fsSL https://app.cloudglue.dev/tinycloud.sh | bash'
15
15
  # Kept in sync with ../tinycloud-skill.json required_features (CI diffs them).
16
- REQUIRED_FEATURES="envelope.v1 watch.v1 extract.v1 caption.v1 search.v1 probe.v1 ask.v1 clip.v1 grab.v1 jobs.v1 library.collections.v1 library.sync.url.v1 workflow.v1 publish.v1 publish.manage.v1 publish.video.v1 setup.v1"
16
+ REQUIRED_FEATURES="envelope.v1 watch.v1 extract.v1 caption.v1 search.v1 probe.v1 ask.v1 clip.v1 grab.v1 face.v1 jobs.v1 library.collections.v1 library.collections.create.v1 library.collections.mutate.v1 library.collections.entities.v1 library.sync.url.v1 workflow.v1 publish.v1 publish.manage.v1 publish.video.v1 setup.v1"
17
17
 
18
18
  # 1) Binary present and responsive?
19
19
  if ! command -v tinycloud >/dev/null 2>&1; then
@@ -1,8 +1,8 @@
1
1
  {
2
- "skill_version": "0.3.0",
2
+ "skill_version": "0.3.4",
3
3
  "tinycloud": {
4
- "min_version": "0.3.2",
5
- "supported_range": ">=0.3.2 <0.4.0",
4
+ "min_version": "0.3.4",
5
+ "supported_range": ">=0.3.4 <0.4.0",
6
6
  "required_features": [
7
7
  "envelope.v1",
8
8
  "watch.v1",
@@ -13,8 +13,12 @@
13
13
  "ask.v1",
14
14
  "clip.v1",
15
15
  "grab.v1",
16
+ "face.v1",
16
17
  "jobs.v1",
17
18
  "library.collections.v1",
19
+ "library.collections.create.v1",
20
+ "library.collections.mutate.v1",
21
+ "library.collections.entities.v1",
18
22
  "library.sync.url.v1",
19
23
  "workflow.v1",
20
24
  "publish.v1",
@@ -22,14 +22,16 @@ in order, skipping any that already pass.
22
22
  command -v tinycloud && tinycloud --version --json </dev/null
23
23
  ```
24
24
 
25
- If installed and the JSON reports `"version"` ≥ 0.3.0, go to step 2. If
26
- missing (or no machine-readable version), install it ask the user which
27
- they prefer:
25
+ If installed and the JSON reports `"version"` ≥ 0.3.4 (the floor the tinycloud
26
+ skill requires), go to step 2. If missing, older than 0.3.4, or no
27
+ machine-readable version, install or upgrade it — ask the user which they
28
+ prefer:
28
29
 
29
30
  ```bash
30
- npm install -g @cloudglue/tinycloud # canonical (Node >= 18)
31
+ npm install -g @cloudglue/tinycloud # canonical (Node >= 18); reinstall to upgrade
31
32
  # or
32
33
  curl -fsSL https://app.cloudglue.dev/tinycloud.sh | bash
34
+ tinycloud update # already installed but older → move to latest stable
33
35
  ```
34
36
 
35
37
  The first run downloads the platform distribution (~90 MB, one time). More
@@ -75,6 +77,9 @@ Report what's now working and point forward:
75
77
  uses the API key)
76
78
  - One-command workflows: `tinycloud workflow list --json` (sales-coaching,
77
79
  blog-post, ad-analysis, meeting-breakdown, youtube-publish, …)
80
+ - Multiple accounts or isolated installs (0.3.3+): `tinycloud profile create
81
+ <name> --default`, then `--profile <name>` (or `--home <dir>` /
82
+ `$TINYCLOUD_HOME`) on any command to switch state homes
78
83
  - If the general `tinycloud` skill is installed alongside this one, it
79
84
  documents the full CLI, envelope contract, and a glossary; its
80
85
  `scripts/preflight.sh` re-checks this setup any time.
@@ -44,6 +44,8 @@ Before writing anything, establish:
44
44
  `.claude/skills/<name>/`) whose SKILL.md runs the recipe **by path**.
45
45
  - *The tinycloud agent*: `~/.tinycloud/skills/<name>/` (global) or
46
46
  `.tinycloud/skills/<name>/` (project), picked up as `/skills:<name>`.
47
+ (With `--home`/`--profile`, 0.3.3+, the global dir is the active home's
48
+ `skills/`; a project's `.tinycloud/config.json` can allowlist skills.)
47
49
 
48
50
  ## 2. Scaffold
49
51