@impractical-ai/motion 0.1.14 → 0.1.15

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
@@ -1,16 +1,16 @@
1
1
  # Impractical Motion CLI
2
2
 
3
- <!-- ARRIVAL-DRIFT-STANDARD START -->
4
- ## Default motion: arrival and residual drift
3
+ <!-- MOTION-STANDARD START -->
4
+ ## Default motion: arrival, hold and exit
5
5
 
6
- Foreground motion defaults to an energetic arrival, strong deceleration, and small continuing travel through the reading hold. Compose a deterministic residual trajectory with the native entrance; do not stop and restart with a disconnected tween. “Settled” and “hold” mean readable and visually stable, with subtle nonzero travel unless an explicit freeze, exact alignment or reduced-motion mode requires stillness.
6
+ Each foreground element follows one continuous trajectory: it arrives with energy, decelerates strongly to near-zero speed for the reading hold, then accelerates into its exit or its next authored move. Shape this with the entrance and exit eases themselves; there is no separate drift layer, no constant velocity floor and no perpetual creep. Finish the visible travel quickly: during the hold the ease tail moves the subject less than about 0.5 output px in total, so “settled” and “hold” mean visually still and readable. Exits ease in (or in-out) from that near-zero state, so nothing starts from a dead stop. This applies to whole entities, not to every element inside them: a card, product window, headline block or logo lockup arrives, holds and exits as one unit, and its children (labels, icons, rows, words, buttons) ride with it. Don't recurse and give each child its own arrival, deceleration and exit; that reads as fidgeting and splits attention. Children may still have their own discrete actions inside the hold (text typing in, a toggle flipping, a row highlighting, a number counting), but not their own arrive/hold/exit motion.
7
7
 
8
- Use the Rox `ambientOffset(seconds, amount, phase)` pattern: native 960×540 product groups start around amount 3.2–4, text around 0.8–1. Keep a signed velocity floor with gentle curvature, not a pure sine bob. Move attached labels, controls, connectors and shadows together, avoid double drift, preserve 3D hierarchy, and verify reverse/retimed seeks and late-hold displacement.
8
+ Why no creep: exports render in headless Linux Chromium, which snaps slowly moving text and elements to whole pixels, so a slow drift of about 1 px/s shows up as a visible 1 px tick every ~0.9 s. Move attached labels, controls, connectors and shadows together with their subject, preserve 3D hierarchy, and verify reverse/retimed seeks.
9
9
 
10
- The host owns the background environment. Animate foreground objects only; never move or retime the background to simulate object motion. Reusable foregrounds should be transparent. Keep one primary focus and room around it; purposeful object camera moves remain welcome, and a generic card is not the default visual container.
10
+ The host owns the background environment. Animate foreground objects only; never move or retime the background to simulate object motion. Reusable foregrounds should be transparent. Make things breathe: only one focus at a time, with generous negative space and comfortable margins around it. Keep supporting context subordinate; sequence competing text and actions. Let the focal idea land and be read before handing attention to the next event. If crowded, remove or sequence content before shrinking it. Check entry, hold and exit as well as playback. Purposeful object camera moves remain welcome; a generic card is not the default visual container.
11
11
 
12
12
  Full specification: [motion standard](../docs/motion-standard.md).
13
- <!-- ARRIVAL-DRIFT-STANDARD END -->
13
+ <!-- MOTION-STANDARD END -->
14
14
 
15
15
  Connect Claude Code, Codex, Cursor, or OpenCode to Impractical Motion through
16
16
  its MCP server.
@@ -134,5 +134,11 @@ Opt-in flags on the MCP process, for testing quality interventions without touch
134
134
  - `IMPRACTICAL_MOTION_LIBRARY_FILE=<path>` — overlay locally generated motion-library
135
135
  guidance onto the live catalog.
136
136
 
137
+ With quality-loop checks active, text-density findings (`OVERLOADED`) from `get_motion`
138
+ are advisory and never refuse `export_video`: UI labels can exceed the density threshold
139
+ without making a beat unreadable. Review the findings and simplify crowded beats when needed.
140
+ Other shot-list and study-timing findings still refuse export once per draft revision in each
141
+ MCP session; a second attempt on that revision can pass that gate. Other export gates still apply.
142
+
137
143
  If authorization is rejected, rerun the setup command. If the configuration file is malformed,
138
144
  the CLI reports its exact path instead of silently ignoring it.
@@ -5,17 +5,17 @@ description: Create and refine studio-quality motion-graphics launch videos with
5
5
 
6
6
  # Impractical Motion
7
7
 
8
- <!-- ARRIVAL-DRIFT-STANDARD START -->
9
- ## Default motion: arrival and residual drift
8
+ <!-- MOTION-STANDARD START -->
9
+ ## Default motion: arrival, hold and exit
10
10
 
11
- Foreground motion defaults to an energetic arrival, strong deceleration, and small continuing travel through the reading hold. Compose a deterministic residual trajectory with the native entrance; do not stop and restart with a disconnected tween. “Settled” and “hold” mean readable and visually stable, with subtle nonzero travel unless an explicit freeze, exact alignment or reduced-motion mode requires stillness.
11
+ Each foreground element follows one continuous trajectory: it arrives with energy, decelerates strongly to near-zero speed for the reading hold, then accelerates into its exit or its next authored move. Shape this with the entrance and exit eases themselves; there is no separate drift layer, no constant velocity floor and no perpetual creep. Finish the visible travel quickly: during the hold the ease tail moves the subject less than about 0.5 output px in total, so “settled” and “hold” mean visually still and readable. Exits ease in (or in-out) from that near-zero state, so nothing starts from a dead stop. This applies to whole entities, not to every element inside them: a card, product window, headline block or logo lockup arrives, holds and exits as one unit, and its children (labels, icons, rows, words, buttons) ride with it. Don't recurse and give each child its own arrival, deceleration and exit; that reads as fidgeting and splits attention. Children may still have their own discrete actions inside the hold (text typing in, a toggle flipping, a row highlighting, a number counting), but not their own arrive/hold/exit motion.
12
12
 
13
- Use the Rox `ambientOffset(seconds, amount, phase)` pattern: native 960×540 product groups start around amount 3.2–4, text around 0.8–1. Keep a signed velocity floor with gentle curvature, not a pure sine bob. Move attached labels, controls, connectors and shadows together, avoid double drift, preserve 3D hierarchy, and verify reverse/retimed seeks and late-hold displacement.
13
+ Why no creep: exports render in headless Linux Chromium, which snaps slowly moving text and elements to whole pixels, so a slow drift of about 1 px/s shows up as a visible 1 px tick every ~0.9 s. Move attached labels, controls, connectors and shadows together with their subject, preserve 3D hierarchy, and verify reverse/retimed seeks.
14
14
 
15
- The host owns the background environment. Animate foreground objects only; never move or retime the background to simulate object motion. Reusable foregrounds should be transparent. Keep one primary focus and room around it; purposeful object camera moves remain welcome, and a generic card is not the default visual container.
15
+ The host owns the background environment. Animate foreground objects only; never move or retime the background to simulate object motion. Reusable foregrounds should be transparent. Make things breathe: only one focus at a time, with generous negative space and comfortable margins around it. Keep supporting context subordinate; sequence competing text and actions. Let the focal idea land and be read before handing attention to the next event. If crowded, remove or sequence content before shrinking it. Check entry, hold and exit as well as playback. Purposeful object camera moves remain welcome; a generic card is not the default visual container.
16
16
 
17
- For the complete trajectory and review checks, read `motion_context` → `guidance.director` before authoring.
18
- <!-- ARRIVAL-DRIFT-STANDARD END -->
17
+ For the complete standard and review checks, read `motion_context` → `guidance.director` before authoring.
18
+ <!-- MOTION-STANDARD END -->
19
19
 
20
20
  You direct the video yourself. Impractical provides the engine, the preview
21
21
  build, the render, and the studio the user tweaks in — you provide the
@@ -15,8 +15,8 @@ import {
15
15
  waitForJobs,
16
16
  withAdmissionReceipt,
17
17
  withMeteredCall
18
- } from "./chunk-O3BUN66P.js";
19
- import "./chunk-7SNCVSTA.js";
18
+ } from "./chunk-2VIBS7AS.js";
19
+ import "./chunk-52D5QRLI.js";
20
20
  import "./chunk-77HVPD4G.js";
21
21
  export {
22
22
  ApiContractError,
@@ -2,9 +2,9 @@
2
2
  import {
3
3
  PRODUCTION_APP_URL,
4
4
  PRODUCTION_SUPABASE_URL
5
- } from "./chunk-7SNCVSTA.js";
5
+ } from "./chunk-52D5QRLI.js";
6
6
 
7
- // src/config.ts
7
+ // cli/src/config.ts
8
8
  import {
9
9
  chmodSync,
10
10
  existsSync,
@@ -106,7 +106,7 @@ function requireConfig() {
106
106
  return config;
107
107
  }
108
108
 
109
- // src/api.ts
109
+ // cli/src/api.ts
110
110
  import { AsyncLocalStorage } from "node:async_hooks";
111
111
  import { randomUUID } from "node:crypto";
112
112
  var admissionContext = new AsyncLocalStorage();
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- // package.json
3
+ // cli/package.json
4
4
  var package_default = {
5
5
  name: "@impractical-ai/motion",
6
- version: "0.1.14",
6
+ version: "0.1.15",
7
7
  private: false,
8
8
  description: "Create and refine Impractical Motion videos from coding agents and the terminal.",
9
9
  homepage: "https://github.com/Openpod/impractical-motion#readme",
@@ -49,7 +49,7 @@ var package_default = {
49
49
  }
50
50
  };
51
51
 
52
- // src/constants.ts
52
+ // cli/src/constants.ts
53
53
  if (package_default.name !== "@impractical-ai/motion" || typeof package_default.version !== "string" || !package_default.version) {
54
54
  throw new Error("The packaged CLI manifest is missing valid release metadata");
55
55
  }
@@ -57,7 +57,7 @@ var PRODUCTION_APP_URL = "https://motion.impractical.ai";
57
57
  var PRODUCTION_SUPABASE_URL = "https://uaxjweednprtbhbceify.supabase.co";
58
58
  var PACKAGE_NAME = "@impractical-ai/motion";
59
59
  var PACKAGE_VERSION = package_default.version;
60
- var BUILD_COMMIT = "e734492" ? "e734492" : "dev";
60
+ var BUILD_COMMIT = "e3a238b6" ? "e3a238b6" : "dev";
61
61
  var BUILD_ID = `${PACKAGE_VERSION}+${BUILD_COMMIT}`;
62
62
  var PACKAGE_COMMAND = `npx --yes ${PACKAGE_NAME}`;
63
63
 
@@ -7,16 +7,16 @@ import {
7
7
  loadConfig,
8
8
  recoverCorruptConfig,
9
9
  saveConfig
10
- } from "./chunk-O3BUN66P.js";
10
+ } from "./chunk-2VIBS7AS.js";
11
11
  import {
12
12
  PACKAGE_COMMAND,
13
13
  PACKAGE_NAME,
14
14
  PACKAGE_VERSION,
15
15
  PRODUCTION_APP_URL,
16
16
  PRODUCTION_SUPABASE_URL
17
- } from "./chunk-7SNCVSTA.js";
17
+ } from "./chunk-52D5QRLI.js";
18
18
 
19
- // src/onboarding.ts
19
+ // cli/src/onboarding.ts
20
20
  import {
21
21
  chmodSync,
22
22
  existsSync,
@@ -33,7 +33,7 @@ import { fileURLToPath } from "node:url";
33
33
  import { spawnSync } from "node:child_process";
34
34
  import { createRequire } from "node:module";
35
35
 
36
- // src/workspaces.ts
36
+ // cli/src/workspaces.ts
37
37
  var UNSUPPORTED = {
38
38
  supported: false,
39
39
  keyScope: null,
@@ -73,7 +73,7 @@ function grantScope(grant) {
73
73
  return grant.scope === "user" ? "user" : "org";
74
74
  }
75
75
 
76
- // src/onboarding.ts
76
+ // cli/src/onboarding.ts
77
77
  var CLAUDE_MARKETPLACE = "Openpod/impractical-agent-plugins";
78
78
  var CLAUDE_PLUGIN = "impractical-motion@impractical";
79
79
  var CLAUDE_MARKETPLACE_NAME = "impractical";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  setup
4
- } from "./chunk-L5ERED5A.js";
4
+ } from "./chunk-TPFRXA7C.js";
5
5
  import {
6
6
  invoke,
7
7
  invokeAt,
@@ -11,15 +11,15 @@ import {
11
11
  saveConfig,
12
12
  studioLink,
13
13
  waitForJobs
14
- } from "./chunk-O3BUN66P.js";
15
- import "./chunk-7SNCVSTA.js";
14
+ } from "./chunk-2VIBS7AS.js";
15
+ import "./chunk-52D5QRLI.js";
16
16
  import "./chunk-77HVPD4G.js";
17
17
 
18
- // src/commands.ts
18
+ // cli/src/commands.ts
19
19
  import { readFileSync as readFileSync2, writeFileSync } from "node:fs";
20
20
  import { randomBytes } from "node:crypto";
21
21
 
22
- // src/media.ts
22
+ // cli/src/media.ts
23
23
  import { readFileSync, statSync } from "node:fs";
24
24
  import { basename, extname } from "node:path";
25
25
  var MAX_UPLOAD_BYTES = 3e8;
@@ -79,7 +79,7 @@ async function uploadAsset(input) {
79
79
  return registered.assetId;
80
80
  }
81
81
 
82
- // ../src/studio/sampleLogos.ts
82
+ // src/studio/sampleLogos.ts
83
83
  var SAMPLE_LOGOS = [
84
84
  {
85
85
  id: "bolt",
@@ -117,7 +117,7 @@ function sampleLogo(id) {
117
117
  return SAMPLE_LOGOS.find((s) => s.id === id);
118
118
  }
119
119
 
120
- // ../src/studio/templates.ts
120
+ // src/studio/templates.ts
121
121
  var esc = (s) => s.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
122
122
  var escTemplate = (s) => s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
123
123
  var num = (v, fallback) => {
@@ -1091,7 +1091,7 @@ var TEMPLATES = [
1091
1091
  blank
1092
1092
  ];
1093
1093
 
1094
- // ../src/studio/parseFiles.ts
1094
+ // src/studio/parseFiles.ts
1095
1095
  var FENCE = /```([^\n`]*)\n([\s\S]*?)```/g;
1096
1096
  var DROP = /* @__PURE__ */ new Set([
1097
1097
  "index.tsx",
@@ -1143,7 +1143,7 @@ function parseSceneFiles(pasted) {
1143
1143
  return files;
1144
1144
  }
1145
1145
 
1146
- // ../src/shared/local-project.ts
1146
+ // src/shared/local-project.ts
1147
1147
  var LOCAL_PROJECT_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
1148
1148
  var LOCAL_SCENE_NAME_RE = /^[a-z0-9][a-z0-9-]{0,63}$/;
1149
1149
  function isLocalProjectId(value) {
@@ -1163,7 +1163,7 @@ function localProjectMarker(input) {
1163
1163
  `;
1164
1164
  }
1165
1165
 
1166
- // ../src/shared/initial-files.ts
1166
+ // src/shared/initial-files.ts
1167
1167
  var TITLE_TEMPLATE = TEMPLATES.find((t) => t.id === "title-card");
1168
1168
  var BLANK_DIRECTION = TEMPLATES.find((t) => t.id === "blank").generate({});
1169
1169
  var defaultParams = (t) => Object.fromEntries(t.params.map((p) => [p.key, p.default]));
@@ -1201,10 +1201,10 @@ function buildInitialFiles(input) {
1201
1201
  };
1202
1202
  }
1203
1203
 
1204
- // ../src/shared/video-models.ts
1204
+ // src/shared/video-models.ts
1205
1205
  var DEFAULT_OPENROUTER_VIDEO_MODEL = "anthropic/claude-opus-5";
1206
1206
 
1207
- // ../src/shared/video-clone-command.ts
1207
+ // src/shared/video-clone-command.ts
1208
1208
  var TOKEN = /^[A-Za-z0-9_-]{43}$/;
1209
1209
  var SHARE_HOSTS = /* @__PURE__ */ new Set(["impractical.ai", "www.impractical.ai", "impractical-video-shares.vercel.app"]);
1210
1210
  function videoShareToken(source) {
@@ -1218,7 +1218,7 @@ function videoShareToken(source) {
1218
1218
  throw new Error("Use an Impractical video share URL: https://impractical.ai/v/<token>");
1219
1219
  }
1220
1220
 
1221
- // src/clone-video-project.ts
1221
+ // cli/src/clone-video-project.ts
1222
1222
  async function cloneVideoProject(source, json = false) {
1223
1223
  const token = videoShareToken(source);
1224
1224
  const result = await invoke("video-shares", { action: "clone", token });
@@ -1234,7 +1234,7 @@ async function cloneVideoProject(source, json = false) {
1234
1234
  console.log(`Use Impractical to open project ${result.projectId} and help me iterate on this video.`);
1235
1235
  }
1236
1236
 
1237
- // src/video-sharing.ts
1237
+ // cli/src/video-sharing.ts
1238
1238
  import { openAsBlob } from "node:fs";
1239
1239
  import { readFile, stat } from "node:fs/promises";
1240
1240
  import { basename as basename2, extname as extname2 } from "node:path";
@@ -1335,7 +1335,7 @@ async function shareVideo(rest, flags) {
1335
1335
  }
1336
1336
  }
1337
1337
 
1338
- // src/commands.ts
1338
+ // cli/src/commands.ts
1339
1339
  var flagString = (flags, name) => {
1340
1340
  const value = flags[name];
1341
1341
  if (Array.isArray(value)) return value[value.length - 1];
@@ -1438,9 +1438,14 @@ async function generate(flags) {
1438
1438
  }
1439
1439
  const projectId = flagString(flags, "project");
1440
1440
  const feedback = flagString(flags, "feedback");
1441
- const append = Boolean(projectId && feedback);
1441
+ if (projectId && feedback) {
1442
+ throw new Error(
1443
+ "Projects have one draft now. To revise this video, edit its draft with the MCP tools edit_draft_file / write_draft_files; every save is kept as a version."
1444
+ );
1445
+ }
1446
+ const append = false;
1442
1447
  const slug = flagString(flags, "slug") ?? `${slugify(valueProp)}-${randomBytes(2).toString("hex")}`;
1443
- const directorIds = (flagString(flags, "directors") ?? "d1,d2").split(",").map((d) => d.trim());
1448
+ const directorIds = (flagString(flags, "directors") ?? "d1").split(",").map((d) => d.trim()).slice(0, 1);
1444
1449
  const files = buildInitialFiles({ valueProp, appCode, brand: brand2 });
1445
1450
  const prepared = await invoke("prepare-generation", {
1446
1451
  ...projectId ? { projectId } : {},
package/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  PACKAGE_VERSION
4
- } from "./chunk-7SNCVSTA.js";
4
+ } from "./chunk-52D5QRLI.js";
5
5
  import "./chunk-77HVPD4G.js";
6
6
 
7
- // src/cli-args.ts
7
+ // cli/src/cli-args.ts
8
8
  import { parseArgs } from "node:util";
9
9
  var CLI_OPTIONS = {
10
10
  key: { type: "string" },
@@ -48,7 +48,7 @@ function parseCliArgs(args) {
48
48
  });
49
49
  }
50
50
 
51
- // src/cli-runtime.ts
51
+ // cli/src/cli-runtime.ts
52
52
  var HELP = `impractical \u2014 Impractical Motion from your terminal (and your coding agent)
53
53
 
54
54
  Setup
@@ -65,7 +65,7 @@ Agents
65
65
  Make videos
66
66
  generate --value-prop "..." [--directors d1,d2] [--vibe confident]
67
67
  [--code-file app.md] [--brand <slug>] [--asset file ...]
68
- [--project <id> --feedback "..."] [--model <id>] [--wait]
68
+ [--project <id>] [--model <id>] [--wait]
69
69
  status --project <id> | --jobs <id,id> [--wait]
70
70
  export [draftId] [--wait] [--out video.mp4]
71
71
 
@@ -157,14 +157,14 @@ async function runCli(args, deps) {
157
157
  throw new Error(`Unknown command "${[command, ...rest].join(" ")}"`);
158
158
  }
159
159
 
160
- // src/index.ts
160
+ // cli/src/index.ts
161
161
  runCli(process.argv.slice(2), {
162
162
  writeLine: (line) => console.log(line),
163
- loadCommands: () => import("./commands-3PIMUGYP.js"),
164
- loadApi: () => import("./api-VLQHOCLI.js"),
165
- loadMcp: () => import("./stdio-M2YRHGFJ.js")
163
+ loadCommands: () => import("./commands-MJSGITIZ.js"),
164
+ loadApi: () => import("./api-UXWCLV7P.js"),
165
+ loadMcp: () => import("./stdio-LWBKGIWN.js")
166
166
  }).catch(async (error) => {
167
- const { ApiError } = await import("./api-VLQHOCLI.js");
167
+ const { ApiError } = await import("./api-UXWCLV7P.js");
168
168
  if (error instanceof ApiError) {
169
169
  console.error(`Error (${error.status} from ${error.fn}): ${error.message}`);
170
170
  if (error.status === 429) console.error("Weekly MCP allowance reached. Upgrade or wait for Monday UTC.");
@@ -4,9 +4,9 @@ import {
4
4
  __require
5
5
  } from "./chunk-77HVPD4G.js";
6
6
 
7
- // node_modules/esbuild/lib/main.js
7
+ // cli/node_modules/esbuild/lib/main.js
8
8
  var require_main = __commonJS({
9
- "node_modules/esbuild/lib/main.js"(exports, module) {
9
+ "cli/node_modules/esbuild/lib/main.js"(exports, module) {
10
10
  var __defProp = Object.defineProperty;
11
11
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
12
12
  var __getOwnPropNames = Object.getOwnPropertyNames;