gooseworks 0.3.4 → 0.3.6

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.
@@ -0,0 +1,10 @@
1
+ import { Command } from 'commander';
2
+ /**
3
+ * `gooseworks doctor` — verify the local prerequisites for making VIDEO ads
4
+ * (the goose-video skill renders locally: Playwright records the mockup, ffmpeg
5
+ * stitches/mixes). Also checks auth + that the GooseWorks MCP server is wired,
6
+ * since the skill reads/writes the project over MCP. Exits non-zero if anything
7
+ * is missing so the agent's Phase-0 preflight can relay the fix and stop.
8
+ */
9
+ export declare const doctorCommand: Command;
10
+ //# sourceMappingURL=doctor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAmCpC;;;;;;GAMG;AACH,eAAO,MAAM,aAAa,SAiDtB,CAAC"}
@@ -0,0 +1,118 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.doctorCommand = void 0;
37
+ const commander_1 = require("commander");
38
+ const child_process_1 = require("child_process");
39
+ const credentials_1 = require("../auth/credentials");
40
+ const logger = __importStar(require("../utils/logger"));
41
+ /** True if `bin` resolves on PATH (cross-platform). */
42
+ function onPath(bin) {
43
+ const probe = process.platform === 'win32' ? 'where' : 'which';
44
+ try {
45
+ return (0, child_process_1.spawnSync)(probe, [bin], { stdio: 'ignore' }).status === 0;
46
+ }
47
+ catch {
48
+ return false;
49
+ }
50
+ }
51
+ /** Playwright is usable either as a global bin or via `npx playwright`. */
52
+ function hasPlaywright() {
53
+ if (onPath('playwright'))
54
+ return true;
55
+ try {
56
+ return ((0, child_process_1.spawnSync)('npx', ['--no-install', 'playwright', '--version'], {
57
+ stdio: 'ignore',
58
+ }).status === 0);
59
+ }
60
+ catch {
61
+ return false;
62
+ }
63
+ }
64
+ /**
65
+ * `gooseworks doctor` — verify the local prerequisites for making VIDEO ads
66
+ * (the goose-video skill renders locally: Playwright records the mockup, ffmpeg
67
+ * stitches/mixes). Also checks auth + that the GooseWorks MCP server is wired,
68
+ * since the skill reads/writes the project over MCP. Exits non-zero if anything
69
+ * is missing so the agent's Phase-0 preflight can relay the fix and stop.
70
+ */
71
+ exports.doctorCommand = new commander_1.Command('doctor')
72
+ .description('Check local prerequisites for video ad rendering (ffmpeg, Playwright) + auth/MCP')
73
+ .action(() => {
74
+ const creds = (0, credentials_1.getCredentials)();
75
+ const checks = [
76
+ { label: 'Logged in', ok: !!creds, fix: 'gooseworks login' },
77
+ {
78
+ label: 'GooseWorks MCP configured',
79
+ ok: !!creds?.mcp_server_url,
80
+ fix: 'gooseworks install --claude --mcp (then restart Claude Code)',
81
+ },
82
+ {
83
+ label: 'ffmpeg on PATH',
84
+ ok: onPath('ffmpeg'),
85
+ fix: 'brew install ffmpeg (macOS) / apt-get install ffmpeg (Linux)',
86
+ },
87
+ {
88
+ label: 'ffprobe on PATH',
89
+ ok: onPath('ffprobe'),
90
+ fix: 'bundled with ffmpeg — install ffmpeg',
91
+ },
92
+ {
93
+ label: 'Playwright (Chromium renderer)',
94
+ ok: hasPlaywright(),
95
+ fix: 'npx playwright install chromium',
96
+ },
97
+ ];
98
+ logger.info('GooseWorks doctor — prerequisites for local video ad rendering\n');
99
+ let allOk = true;
100
+ for (const c of checks) {
101
+ if (c.ok) {
102
+ logger.success(c.label);
103
+ }
104
+ else {
105
+ logger.error(`${c.label} → fix: ${c.fix}`);
106
+ allOk = false;
107
+ }
108
+ }
109
+ logger.info('');
110
+ if (allOk) {
111
+ logger.success('All set — you can make video ads locally (goose-video).');
112
+ }
113
+ else {
114
+ logger.warn('Some prerequisites are missing. Fix the items above, then re-run: gooseworks doctor');
115
+ process.exitCode = 1;
116
+ }
117
+ });
118
+ //# sourceMappingURL=doctor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,yCAAoC;AACpC,iDAA0C;AAC1C,qDAAqD;AACrD,wDAA0C;AAE1C,uDAAuD;AACvD,SAAS,MAAM,CAAC,GAAW;IACzB,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;IAC/D,IAAI,CAAC;QACH,OAAO,IAAA,yBAAS,EAAC,KAAK,EAAE,CAAC,GAAG,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC;IACnE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,2EAA2E;AAC3E,SAAS,aAAa;IACpB,IAAI,MAAM,CAAC,YAAY,CAAC;QAAE,OAAO,IAAI,CAAC;IACtC,IAAI,CAAC;QACH,OAAO,CACL,IAAA,yBAAS,EAAC,KAAK,EAAE,CAAC,cAAc,EAAE,YAAY,EAAE,WAAW,CAAC,EAAE;YAC5D,KAAK,EAAE,QAAQ;SAChB,CAAC,CAAC,MAAM,KAAK,CAAC,CAChB,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAQD;;;;;;GAMG;AACU,QAAA,aAAa,GAAG,IAAI,mBAAO,CAAC,QAAQ,CAAC;KAC/C,WAAW,CACV,kFAAkF,CACnF;KACA,MAAM,CAAC,GAAG,EAAE;IACX,MAAM,KAAK,GAAG,IAAA,4BAAc,GAAE,CAAC;IAC/B,MAAM,MAAM,GAAY;QACtB,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,kBAAkB,EAAE;QAC5D;YACE,KAAK,EAAE,2BAA2B;YAClC,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,cAAc;YAC3B,GAAG,EAAE,+DAA+D;SACrE;QACD;YACE,KAAK,EAAE,gBAAgB;YACvB,EAAE,EAAE,MAAM,CAAC,QAAQ,CAAC;YACpB,GAAG,EAAE,8DAA8D;SACpE;QACD;YACE,KAAK,EAAE,iBAAiB;YACxB,EAAE,EAAE,MAAM,CAAC,SAAS,CAAC;YACrB,GAAG,EAAE,sCAAsC;SAC5C;QACD;YACE,KAAK,EAAE,gCAAgC;YACvC,EAAE,EAAE,aAAa,EAAE;YACnB,GAAG,EAAE,iCAAiC;SACvC;KACF,CAAC;IAEF,MAAM,CAAC,IAAI,CAAC,kEAAkE,CAAC,CAAC;IAChF,IAAI,KAAK,GAAG,IAAI,CAAC;IACjB,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,IAAI,CAAC,CAAC,EAAE,EAAE,CAAC;YACT,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QAC1B,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,aAAa,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;YAC7C,KAAK,GAAG,KAAK,CAAC;QAChB,CAAC;IACH,CAAC;IACD,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAChB,IAAI,KAAK,EAAE,CAAC;QACV,MAAM,CAAC,OAAO,CAAC,yDAAyD,CAAC,CAAC;IAC5E,CAAC;SAAM,CAAC;QACN,MAAM,CAAC,IAAI,CACT,qFAAqF,CACtF,CAAC;QACF,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;IACvB,CAAC;AACH,CAAC,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA2BpC,eAAO,MAAM,YAAY,SAqBrB,CAAC;AAEL;;;GAGG;AACH,wBAAsB,cAAc,CAAC,OAAO,GAAE,MAAiB,sDAW9D"}
1
+ {"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA2CpC,eAAO,MAAM,YAAY,SAuBrB,CAAC;AAEL;;;GAGG;AACH,wBAAsB,cAAc,CAAC,OAAO,GAAE,MAAiB,sDAW9D"}
@@ -41,6 +41,7 @@ const oauth_server_1 = require("../auth/oauth-server");
41
41
  const installer_1 = require("../skills/installer");
42
42
  const master_skill_1 = require("../skills/master-skill");
43
43
  const claude_1 = require("../agents/claude");
44
+ const claude_mcp_1 = require("../agents/claude-mcp");
44
45
  const detect_1 = require("../agents/detect");
45
46
  const logger = __importStar(require("../utils/logger"));
46
47
  const config_1 = require("../config");
@@ -63,6 +64,20 @@ function refreshEntrySkillsOnLogin() {
63
64
  if ((0, detect_1.isAgentInstalled)('claude'))
64
65
  (0, claude_1.configureClaude)();
65
66
  }
67
+ /**
68
+ * Re-point the `gooseworks` MCP registration at the backend we just logged into
69
+ * (from `creds.mcp_server_url`). `install`/`update` already do this, but plain
70
+ * `login` didn't — so switching backends (e.g. prod → local dev) left the MCP
71
+ * tools pointed at the OLD backend even though the CLI creds were correct. That
72
+ * mismatch reads as "project not found" / wrong org on every `mcp__gooseworks__*`
73
+ * call, while `doctor` (creds-only) still passes — a confusing trap.
74
+ */
75
+ function syncMcpRegistration() {
76
+ if ((0, claude_mcp_1.configureClaudeMcp)()) {
77
+ const creds = (0, credentials_1.getCredentials)();
78
+ logger.info(`Synced the gooseworks MCP → ${creds?.mcp_server_url ?? 'the configured server'}`);
79
+ }
80
+ }
66
81
  exports.loginCommand = new commander_1.Command('login')
67
82
  .description('Sign in to GooseWorks with Google')
68
83
  .option('--api-base <url>', 'API base URL', config_1.API_BASE)
@@ -71,6 +86,7 @@ exports.loginCommand = new commander_1.Command('login')
71
86
  if (existing) {
72
87
  logger.success(`Already logged in as ${existing.email}`);
73
88
  refreshEntrySkillsOnLogin();
89
+ syncMcpRegistration();
74
90
  logger.info('Run "gooseworks logout" first to switch accounts.');
75
91
  return;
76
92
  }
@@ -78,6 +94,7 @@ exports.loginCommand = new commander_1.Command('login')
78
94
  const result = await (0, oauth_server_1.runOAuthFlow)(opts.apiBase);
79
95
  logger.success(`Logged in as ${result.email}`);
80
96
  refreshEntrySkillsOnLogin();
97
+ syncMcpRegistration();
81
98
  }
82
99
  catch (err) {
83
100
  const message = err instanceof Error ? err.message : 'Login failed';
@@ -1 +1 @@
1
- {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsDA,wCAWC;AAjED,yCAAoC;AACpC,qDAAqD;AACrD,uDAAoD;AACpD,mDAAoF;AACpF,yDAAwD;AACxD,6CAAmD;AACnD,6CAAoD;AACpD,wDAA0C;AAC1C,sCAAqC;AAErC;;;;;;GAMG;AACH,SAAS,yBAAyB;IAChC,IAAI,CAAC,IAAA,8BAAkB,GAAE,CAAC,QAAQ,CAAC,YAAY,CAAC;QAAE,OAAO;IACzD,MAAM,OAAO,GAAG,IAAA,qCAAyB,EAAC,IAAA,6BAAc,GAAE,CAAC;SACxD,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,WAAW,CAAC;SACvC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACtB,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IACjC,MAAM,CAAC,OAAO,CAAC,qBAAqB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC1D,IAAI,IAAA,yBAAgB,EAAC,QAAQ,CAAC;QAAE,IAAA,wBAAe,GAAE,CAAC;AACpD,CAAC;AAEY,QAAA,YAAY,GAAG,IAAI,mBAAO,CAAC,OAAO,CAAC;KAC7C,WAAW,CAAC,mCAAmC,CAAC;KAChD,MAAM,CAAC,kBAAkB,EAAE,cAAc,EAAE,iBAAQ,CAAC;KACpD,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;IACrB,MAAM,QAAQ,GAAG,IAAA,4BAAc,GAAE,CAAC;IAClC,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,CAAC,OAAO,CAAC,wBAAwB,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;QACzD,yBAAyB,EAAE,CAAC;QAC5B,MAAM,CAAC,IAAI,CAAC,mDAAmD,CAAC,CAAC;QACjE,OAAO;IACT,CAAC;IAED,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,IAAA,2BAAY,EAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAChD,MAAM,CAAC,OAAO,CAAC,gBAAgB,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;QAC/C,yBAAyB,EAAE,CAAC;IAC9B,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,cAAc,CAAC;QACpE,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACtB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC;AAEL;;;GAGG;AACI,KAAK,UAAU,cAAc,CAAC,UAAkB,iBAAQ;IAC7D,MAAM,QAAQ,GAAG,IAAA,4BAAc,GAAE,CAAC;IAClC,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAE9B,MAAM,MAAM,GAAG,MAAM,IAAA,2BAAY,EAAC,OAAO,CAAC,CAAC;IAC3C,MAAM,KAAK,GAAG,IAAA,4BAAc,GAAE,CAAC;IAC/B,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,CAAC,KAAK,CAAC,wCAAwC,CAAC,CAAC;QACvD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"logout.d.ts","sourceRoot":"","sources":["../../src/commands/logout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,eAAO,MAAM,aAAa,SAWtB,CAAC"}
1
+ {"version":3,"file":"logout.d.ts","sourceRoot":"","sources":["../../src/commands/logout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKpC,eAAO,MAAM,aAAa,SAgBtB,CAAC"}
@@ -36,6 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.logoutCommand = void 0;
37
37
  const commander_1 = require("commander");
38
38
  const credentials_1 = require("../auth/credentials");
39
+ const claude_mcp_1 = require("../agents/claude-mcp");
39
40
  const logger = __importStar(require("../utils/logger"));
40
41
  exports.logoutCommand = new commander_1.Command('logout')
41
42
  .description('Sign out and clear saved credentials')
@@ -46,6 +47,11 @@ exports.logoutCommand = new commander_1.Command('logout')
46
47
  return;
47
48
  }
48
49
  (0, credentials_1.clearCredentials)();
49
- logger.success(`Logged out (was ${existing.email})`);
50
+ // Also drop the `gooseworks` MCP registration from ~/.claude.json. Otherwise
51
+ // it lingers pointing at the logged-out backend with a now-dead token — the
52
+ // classic "project not found" / wrong-org trap when you later log into a
53
+ // different backend (e.g. local dev) without re-registering.
54
+ (0, claude_mcp_1.removeClaudeMcp)();
55
+ logger.success(`Logged out (was ${existing.email}) — cleared credentials + MCP registration`);
50
56
  });
51
57
  //# sourceMappingURL=logout.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"logout.js","sourceRoot":"","sources":["../../src/commands/logout.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,yCAAoC;AACpC,qDAAuE;AACvE,wDAA0C;AAE7B,QAAA,aAAa,GAAG,IAAI,mBAAO,CAAC,QAAQ,CAAC;KAC/C,WAAW,CAAC,sCAAsC,CAAC;KACnD,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,MAAM,QAAQ,GAAG,IAAA,4BAAc,GAAE,CAAC;IAClC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;QACxC,OAAO;IACT,CAAC;IAED,IAAA,8BAAgB,GAAE,CAAC;IACnB,MAAM,CAAC,OAAO,CAAC,mBAAmB,QAAQ,CAAC,KAAK,GAAG,CAAC,CAAC;AACvD,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"logout.js","sourceRoot":"","sources":["../../src/commands/logout.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,yCAAoC;AACpC,qDAAuE;AACvE,qDAAuD;AACvD,wDAA0C;AAE7B,QAAA,aAAa,GAAG,IAAI,mBAAO,CAAC,QAAQ,CAAC;KAC/C,WAAW,CAAC,sCAAsC,CAAC;KACnD,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,MAAM,QAAQ,GAAG,IAAA,4BAAc,GAAE,CAAC;IAClC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;QACxC,OAAO;IACT,CAAC;IAED,IAAA,8BAAgB,GAAE,CAAC;IACnB,6EAA6E;IAC7E,4EAA4E;IAC5E,yEAAyE;IACzE,6DAA6D;IAC7D,IAAA,4BAAe,GAAE,CAAC;IAClB,MAAM,CAAC,OAAO,CAAC,mBAAmB,QAAQ,CAAC,KAAK,4CAA4C,CAAC,CAAC;AAChG,CAAC,CAAC,CAAC"}
package/dist/index.js CHANGED
@@ -14,6 +14,7 @@ const call_1 = require("./commands/call");
14
14
  const orthogonal_1 = require("./commands/orthogonal");
15
15
  const styles_1 = require("./commands/styles");
16
16
  const formats_1 = require("./commands/formats");
17
+ const doctor_1 = require("./commands/doctor");
17
18
  const version_1 = require("./version");
18
19
  const program = new commander_1.Command();
19
20
  program
@@ -32,5 +33,6 @@ program.addCommand(call_1.callCommand);
32
33
  program.addCommand(orthogonal_1.orthogonalCommand);
33
34
  program.addCommand(styles_1.stylesCommand);
34
35
  program.addCommand(formats_1.formatsCommand);
36
+ program.addCommand(doctor_1.doctorCommand);
35
37
  program.parse();
36
38
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AACA,yCAAoC;AACpC,gDAAoD;AACpD,4CAAgD;AAChD,8CAAkD;AAClD,8CAAkD;AAClD,gDAAoD;AACpD,8CAAkD;AAClD,4CAAgD;AAChD,wCAA4C;AAC5C,0CAA8C;AAC9C,sDAA0D;AAC1D,8CAAkD;AAClD,gDAAoD;AACpD,uCAAuC;AAEvC,MAAM,OAAO,GAAG,IAAI,mBAAO,EAAE,CAAC;AAC9B,OAAO;KACJ,IAAI,CAAC,YAAY,CAAC;KAClB,WAAW,CAAC,yDAAyD,CAAC;KACtE,OAAO,CAAC,IAAA,oBAAU,GAAE,CAAC,CAAC;AAEzB,OAAO,CAAC,UAAU,CAAC,wBAAc,CAAC,CAAC;AACnC,OAAO,CAAC,UAAU,CAAC,oBAAY,CAAC,CAAC;AACjC,OAAO,CAAC,UAAU,CAAC,sBAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,sBAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,wBAAc,CAAC,CAAC;AACnC,OAAO,CAAC,UAAU,CAAC,sBAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,oBAAY,CAAC,CAAC;AACjC,OAAO,CAAC,UAAU,CAAC,gBAAU,CAAC,CAAC;AAC/B,OAAO,CAAC,UAAU,CAAC,kBAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,8BAAiB,CAAC,CAAC;AACtC,OAAO,CAAC,UAAU,CAAC,sBAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,wBAAc,CAAC,CAAC;AAEnC,OAAO,CAAC,KAAK,EAAE,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AACA,yCAAoC;AACpC,gDAAoD;AACpD,4CAAgD;AAChD,8CAAkD;AAClD,8CAAkD;AAClD,gDAAoD;AACpD,8CAAkD;AAClD,4CAAgD;AAChD,wCAA4C;AAC5C,0CAA8C;AAC9C,sDAA0D;AAC1D,8CAAkD;AAClD,gDAAoD;AACpD,8CAAkD;AAClD,uCAAuC;AAEvC,MAAM,OAAO,GAAG,IAAI,mBAAO,EAAE,CAAC;AAC9B,OAAO;KACJ,IAAI,CAAC,YAAY,CAAC;KAClB,WAAW,CAAC,yDAAyD,CAAC;KACtE,OAAO,CAAC,IAAA,oBAAU,GAAE,CAAC,CAAC;AAEzB,OAAO,CAAC,UAAU,CAAC,wBAAc,CAAC,CAAC;AACnC,OAAO,CAAC,UAAU,CAAC,oBAAY,CAAC,CAAC;AACjC,OAAO,CAAC,UAAU,CAAC,sBAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,sBAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,wBAAc,CAAC,CAAC;AACnC,OAAO,CAAC,UAAU,CAAC,sBAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,oBAAY,CAAC,CAAC;AACjC,OAAO,CAAC,UAAU,CAAC,gBAAU,CAAC,CAAC;AAC/B,OAAO,CAAC,UAAU,CAAC,kBAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,8BAAiB,CAAC,CAAC;AACtC,OAAO,CAAC,UAAU,CAAC,sBAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,wBAAc,CAAC,CAAC;AACnC,OAAO,CAAC,UAAU,CAAC,sBAAa,CAAC,CAAC;AAElC,OAAO,CAAC,KAAK,EAAE,CAAC"}
@@ -11,7 +11,9 @@
11
11
  * Sibling domain skills NOT vendored here (fetched live from goose-skills):
12
12
  * - `goose-graphics` — charts/slides/infographics/branded visuals. Installed via
13
13
  * `gooseworks install --with goose-graphics` or fetched on demand.
14
- * - `goose-video` — ad/UGC/talking-head video. Coming soon.
14
+ * - `goose-video` — video ad remix: fetches the per-format recipe by slug,
15
+ * renders LOCALLY (Playwright + ffmpeg + media proxies), mirrors a script for
16
+ * in-app review, saves the MP4 back over MCP (getGooseVideoSkillContent).
15
17
  *
16
18
  * Recipe skills (remix-graphic-ad-from-reference, brand-research, meta-ads-analyzer,
17
19
  * …) are NOT vendored here — they live in goose-skills and are fetched live on
@@ -50,4 +52,17 @@ export declare function getMasterSkillContent(): string;
50
52
  * ad, research a brand for ads, or analyze ad performance.
51
53
  */
52
54
  export declare function getGooseAdsSkillContent(): string;
55
+ /**
56
+ * Returns the goose-video entry SKILL.md content (the `goose-video` entry skill).
57
+ *
58
+ * Unlike `goose-ads` (static images, generated 100% server-side via the remix
59
+ * batch tools), VIDEO ads render LOCALLY in the user's own Claude Code: the app
60
+ * pre-creates the project and hands the user a paste prompt; this skill fetches
61
+ * the per-format recipe by slug from goose-skills, renders on the user's machine
62
+ * (Playwright + ffmpeg + the GooseWorks media proxies), mirrors the script for a
63
+ * free in-app review, then saves the finished MP4 back over MCP. The app is the
64
+ * viewer + review surface. The `gooseworks` parent router hands video here; Claude
65
+ * also loads it by description.
66
+ */
67
+ export declare function getGooseVideoSkillContent(): string;
53
68
  //# sourceMappingURL=master-skill.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"master-skill.d.ts","sourceRoot":"","sources":["../../src/skills/master-skill.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,UAAU;IACzB,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,oDAAoD;AACpD,wBAAgB,cAAc,IAAI,UAAU,EAAE,CAK7C;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CA8K9C;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,uBAAuB,IAAI,MAAM,CAsLhD"}
1
+ {"version":3,"file":"master-skill.d.ts","sourceRoot":"","sources":["../../src/skills/master-skill.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,WAAW,UAAU;IACzB,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,oDAAoD;AACpD,wBAAgB,cAAc,IAAI,UAAU,EAAE,CAM7C;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CA8K9C;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,uBAAuB,IAAI,MAAM,CAoOhD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,CA+MlD"}
@@ -3,11 +3,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getEntrySkills = getEntrySkills;
4
4
  exports.getMasterSkillContent = getMasterSkillContent;
5
5
  exports.getGooseAdsSkillContent = getGooseAdsSkillContent;
6
+ exports.getGooseVideoSkillContent = getGooseVideoSkillContent;
6
7
  /** Every entry skill the CLI vendors + installs. */
7
8
  function getEntrySkills() {
8
9
  return [
9
10
  { name: 'gooseworks', content: getMasterSkillContent() },
10
11
  { name: 'goose-ads', content: getGooseAdsSkillContent() },
12
+ { name: 'goose-video', content: getGooseVideoSkillContent() },
11
13
  ];
12
14
  }
13
15
  /**
@@ -50,7 +52,7 @@ Before anything else, check whether the request belongs to a specialized domain.
50
52
  | --- | --- | --- |
51
53
  | Remix/make an ad, research a brand for ads, OR analyze ad performance — Meta/Google ad campaigns, creative fatigue, CAC/lead quality, competitor ad intel, ad angles & hooks | **\`goose-ads\`** | Installed locally as an entry skill. Just use it. If unavailable, run \`gooseworks install --claude\`. |
52
54
  | Charts, infographics, slides, social graphics, branded visual designs from a style/format | **\`goose-graphics\`** | If installed locally, use it. Otherwise \`gooseworks fetch goose-graphics\` (or \`gooseworks install --claude --with goose-graphics\`). |
53
- | Ad/UGC/talking-head **video** | **\`goose-video\`** | Coming soon. Until it ships, search the catalog (\`gooseworks search "ugc video"\`) and \`gooseworks fetch\` the matching recipe. |
55
+ | Make a **video** ad — remix a video ad template (e.g. iMessage chat-reveal), or "make the video for project <id>" | **\`goose-video\`** | Installed locally as an entry skill. Just use it. If unavailable, run \`gooseworks install --claude\`. |
54
56
  | Anything else — scraping, research, lead gen, enrichment, any data lookup | (stay here) | Follow "How to Use" below. |
55
57
 
56
58
  Examples — all of these route to \`goose-ads\`, not the data flow: "remix this ad with project id 123", "make an ad for my product", "research my brand", "why is my Meta campaign underperforming", "which creatives should I cut".
@@ -224,7 +226,7 @@ description: >
224
226
  app uses) — credits are reserved and billed server-side. Analytics recipes are fetched from
225
227
  goose-skills on demand.
226
228
  category: ads
227
- version: 2.0.0
229
+ version: 2.1.0
228
230
  author: GooseWorks
229
231
  tags: [gooseworks, ads, remix, static-ad, brand, creative, image, analytics, meta-ads, performance]
230
232
  ---
@@ -267,8 +269,10 @@ what they'd get in the UI. **Pass these explicitly:**
267
269
  - \`ratios\`: **["4:5"]** (Meta feed vertical)
268
270
  - \`engine\`: **"gpt_image_2"**
269
271
  - \`quality\`: **"medium"**
270
- - \`preserve_source_styling\`: **true** (keep the template's own colours/fonts; only restyle to
271
- the brand palette if the user explicitly asks to "match my brand colours")
272
+ - \`preserve_source_styling\`: **ASK the user** — "Keep original" (the template's own
273
+ colours/fonts \`preserve_source_styling: true\`) vs "Match brand" (restyle to the brand
274
+ palette/fonts → \`preserve_source_styling: false\`). This mirrors the app's Styling control.
275
+ **The default is "Keep original"** — if the user doesn't answer or doesn't care, send \`true\`.
272
276
 
273
277
  If the user asks for something the app exposes (more variants, a different ratio like 1:1 or
274
278
  9:16, a faster engine, higher quality), pass that instead. Omitting a field lets backend policy
@@ -294,6 +298,12 @@ decide — fine, but prefer sending the app defaults for predictable parity.
294
298
  \`status\` is \`"failed"\` — never assume a stall and re-submit, that double-bills.
295
299
  - \`list_brand_creatives { brand_id, limit?, offset? }\` — the brand's gallery feed (newest
296
300
  first) + \`brand_url\`. Alternative poll target; also use to show everything made for a brand.
301
+ - \`surprise_me_templates { brand_id, count? }\` — the **"Surprise me" recommender**. Picks
302
+ brand-relevant templates (SAME logic as the web /create "Surprise me" button — templates
303
+ whose category overlaps the brand float to the top, bucketed + shuffled so picks stay fresh).
304
+ Returns the picked templates (id, slug, title, image, ratio) AND a ready-to-open \`create_url\`
305
+ (the /create page with \`cli=true\` and the picks pre-selected). This is how you recommend
306
+ templates — do NOT hand-pick from the raw catalog yourself (see "Picking templates" below).
297
307
  - \`regenerate_creative { project_id, mode?, prompt?, source_render_id?, ... }\` — **edit / re-roll
298
308
  one existing creative** through the same pipeline. \`mode: "variation"\` (default) re-rolls from
299
309
  the template; \`"edit"\` makes a targeted change to a specific render (\`prompt\` + \`source_render_id\`
@@ -317,21 +327,53 @@ decide — fine, but prefer sending the app defaults for predictable parity.
317
327
  image as a private template, then remix it like any other.
318
328
  - \`get_ad_project\` / \`append_project_message\` — inspect a creative / leave a note on its thread.
319
329
 
330
+ ## Picking templates — ASK the user; don't freelance from the catalog
331
+
332
+ When the user wants to make ads but has NOT named a specific template (id/slug/Community
333
+ ad/upload), do NOT silently browse the raw catalog and hand-pick for them. Instead run this
334
+ short ask flow — it mirrors the web app and keeps the human in the loop:
335
+
336
+ 1. **Ask what kind of ads they want** — the angle/offer/theme/season, the vibe, and which
337
+ product from the brand kit to feature. This shapes both the template choice and your steering
338
+ \`prompt\`. Keep it to one or two quick questions.
339
+ 2. **Ask how to pick templates: "Choose explicitly" or "Surprise me".**
340
+ - **Surprise me** (they want you/the app to pick) → call
341
+ \`surprise_me_templates { brand_id, count }\` and hand the user the returned \`create_url\`.
342
+ It opens /create in **CLI mode** with the picks pre-selected, a preview modal, and the
343
+ **copyable remix prompt at the bottom** (in place of the Generate input). They can swap
344
+ picks and copy that prompt. If they'd rather you "just make them" without reviewing in the
345
+ app, you MAY submit the \`surprise_me_templates\` picks directly (skip to submit).
346
+ - **Choose explicitly** (they want to browse and select) → hand the user this URL, with the
347
+ active brand's slug filled in:
348
+ \`https://make.gooseworks.ai/create?brand=<brand-slug>&cli=true\`
349
+ In CLI mode the app shows the copyable remix prompt at the bottom (dismissable / switchable
350
+ back to the UI composer). They browse, select templates, and copy the prompt.
351
+ 3. **Ask the styling** — "Keep original" (default) vs "Match brand" — per the Defaults section.
352
+ 4. **Close the loop.** When the user **pastes back the copyable remix prompt** from the app
353
+ (it names the brand + the templates they chose), THAT is your cue to generate: resolve the
354
+ named template(s), then \`submit_remix_batch\` with the app defaults + the styling they chose.
355
+
356
+ If the user already named a template (id/slug), a Community ad, or an upload, skip the ask flow
357
+ for template choice — they've chosen — but still confirm the styling default and steer the prompt.
358
+
320
359
  ## Workflow — make ads from a template
321
360
 
322
361
  1. **Resolve the brand.** \`list_ad_brands\` by name/site → \`get_brand_kit { brand_id }\`. If the
323
362
  kit's \`researchStatus\` isn't \`complete\`, you can still submit (the batch queues and runs when
324
363
  research finishes) — just tell the user. Use the kit to pick \`product_name\` (a real entry from
325
364
  \`products[]\`, not a guess) and, if the user supplied product photos, \`reference_image_urls\`.
326
- 2. **Resolve the template(s).** \`get_static_ad_template { template_id }\` for each. For a Community
327
- ad, \`remix_community_ad\` first; for an uploaded image, \`create_user_ad_template\` first.
365
+ 2. **Pick the template(s) via the ask flow above** (kind of ads → Choose explicitly vs Surprise
366
+ me styling). Once you have concrete ids: \`get_static_ad_template { template_id }\` for each.
367
+ For a Community ad, \`remix_community_ad\` first; for an uploaded image, \`create_user_ad_template\`
368
+ first.
328
369
  3. **(Optional) Craft the steering prompt.** The \`prompt\` is OPTIONAL — this is where the skill
329
- adds value: turn the user's intent into a concise steering note (e.g. tone, season, emphasis).
330
- Don't over-specify; the backend pipeline + brand kit handle palette, fonts, product swap.
370
+ adds value: turn the user's intent (from step 1) into a concise steering note (e.g. tone,
371
+ season, emphasis). Don't over-specify; the backend pipeline + brand kit handle palette, fonts,
372
+ product swap.
331
373
  4. **(Optional) Quote the cost.** \`estimate_remix_batch { items, engine, quality }\` → tell the user.
332
374
  5. **Submit ONE batch.** \`submit_remix_batch { brand_id, items, prompt?, product_name?, engine,
333
- quality, preserve_source_styling }\` using the app defaults above. Keep the returned \`batch_id\`
334
- and \`links\`.
375
+ quality, preserve_source_styling }\` using the app defaults above and the styling the user chose.
376
+ Keep the returned \`batch_id\` and \`links\`.
335
377
  6. **Poll until done.** \`get_remix_batch { batch_id }\` (or \`list_brand_creatives\`) every ~20-30s
336
378
  until every creative's \`pending\` is 0. Most images finish in a few minutes; text-heavy templates
337
379
  and \`quality: high\` take longer. Read each render's \`elapsed_seconds\` rather than guessing — a
@@ -388,8 +430,234 @@ run through the \`gooseworks\` CLI (\`gooseworks fetch\` / \`gooseworks call\`),
388
430
  each creative's \`app_url\`), copied verbatim. Never end on just "done" or a file path.
389
431
  - **Quote cost before generating** when it's non-trivial (use \`estimate_remix_batch\`), and
390
432
  relay \`insufficient_credits\` plainly if the submit is rejected — don't retry blindly.
433
+ - **Don't hand-pick templates silently.** If the user didn't name a template, run the ask flow
434
+ (kind of ads → Choose explicitly vs Surprise me → styling). "Surprise me" goes through
435
+ \`surprise_me_templates\`; "Choose explicitly" sends them to \`/create?brand=<slug>&cli=true\`.
436
+ Generate when they paste the app's copyable remix prompt back (or submit the surprise picks
437
+ directly if they'd rather not review).
438
+ - **Ask the styling** — Keep original (default) vs Match brand — before you submit.
391
439
  - **Don't busy-loop** — poll \`get_remix_batch\` on a sensible interval (~20-30s); a \`queued\`
392
440
  batch is waiting on research and will start on its own.
393
441
  `;
394
442
  }
443
+ /**
444
+ * Returns the goose-video entry SKILL.md content (the `goose-video` entry skill).
445
+ *
446
+ * Unlike `goose-ads` (static images, generated 100% server-side via the remix
447
+ * batch tools), VIDEO ads render LOCALLY in the user's own Claude Code: the app
448
+ * pre-creates the project and hands the user a paste prompt; this skill fetches
449
+ * the per-format recipe by slug from goose-skills, renders on the user's machine
450
+ * (Playwright + ffmpeg + the GooseWorks media proxies), mirrors the script for a
451
+ * free in-app review, then saves the finished MP4 back over MCP. The app is the
452
+ * viewer + review surface. The `gooseworks` parent router hands video here; Claude
453
+ * also loads it by description.
454
+ */
455
+ function getGooseVideoSkillContent() {
456
+ return `---
457
+ name: goose-video
458
+ slug: goose-video
459
+ description: >
460
+ GooseWorks video ads — remix a video ad template (iMessage chat-reveal, more coming) into a
461
+ branded video ad for the user's product. Renders LOCALLY on the user's machine (Playwright +
462
+ ffmpeg + GooseWorks media proxies) and saves the finished MP4 back to the project over MCP.
463
+ Use when the user says "make the video for project <id>", references a video ad project or
464
+ template, or asks to remix a video ad. Unlike goose-ads (static images, generated server-side),
465
+ video renders locally and reports progress + the result back through the gooseworks MCP tools.
466
+ category: ads
467
+ version: 0.1.0
468
+ author: GooseWorks
469
+ tags: [gooseworks, ads, video, remix, imessage, local-render, byoa]
470
+ ---
471
+
472
+ # GooseWorks Video Ads — local remix runtime
473
+
474
+ You produce **video** ad creative on the user's OWN machine and sync the result back to the
475
+ GooseWorks app over MCP. This document is the **runtime contract** (auth, credits, the media
476
+ proxies, data I/O, the review gate). A separate **recipe skill** — fetched per format — tells
477
+ you *what to make*; read both, and this doc wins on any conflict about the environment.
478
+
479
+ You run inside the user's own Claude Code session (they pasted an instruction with a project
480
+ id). The app NEVER runs you — it is the viewer + review surface; you are the renderer.
481
+
482
+ ## Prerequisite — MCP + a local toolchain (Phase 0 preflight)
483
+
484
+ - The \`mcp__gooseworks__*\` tools are REQUIRED. If they're unavailable, stop and tell the user
485
+ to run \`gooseworks install --claude --mcp\` and restart Claude Code. There is no REST fallback.
486
+ - This is a LOCAL render. Run \`gooseworks doctor\` FIRST — it checks login, the MCP server,
487
+ **ffmpeg** + **ffprobe**, and **Playwright Chromium** in one shot. If it reports any ✗, relay
488
+ the exact fix it prints (e.g. \`brew install ffmpeg\`, \`npx playwright install chromium\`) and
489
+ stop — don't half-render.
490
+
491
+ ## Identity, token, credits
492
+
493
+ - Read \`~/.gooseworks/credentials.json\` → \`api_key\` (your agent token), \`api_base\`, \`agent_id\`.
494
+ Never print the token.
495
+ - **CRITICAL — target the org-default Ads agent on EVERY file op.** The app serves project files
496
+ (the render-file route) from the org's DEFAULT agent, but MCP file writes default to your
497
+ token's pinned agent — which can be a DIFFERENT agent, so a render written with the default
498
+ scope is **invisible in the app**. First resolve the Ads agent: \`list_accessible_scopes\` → the
499
+ scope with \`is_org_default: true\` → its \`agent_id\` is \`ADS_AGENT\`. Then pass
500
+ \`target: { type: "agent", agent_id: ADS_AGENT }\` on EVERY \`get_upload_url\` / \`get_download_url\`
501
+ / \`write_file\` / \`list_directory\` / \`read_file\` — NEVER omit \`target\`.
502
+ - Media generation (FAL / ElevenLabs) is billed to the agent through the GooseWorks proxies.
503
+ \`submit_render { kind: "full" }\` debits **1 ad credit at row creation** — so sequence it LAST
504
+ (render + verify a good MP4 first), and never re-submit on a guess (that double-bills). Call
505
+ \`get_ad_credits\` first; the user can check \`gooseworks credits\`.
506
+
507
+ ## Step 1 — resolve the project, source, brand
508
+
509
+ 1. \`get_ad_project { project_id }\` → keep \`brand_id\`, \`source_sample_id\`, \`name\`, \`status\`.
510
+ 2. \`get_ad_template { template_id: source_sample_id }\` → the source video: \`media_url\`,
511
+ \`recipe\`, \`format\` (e.g. "imessage"), \`extracted_script\`, \`how_to\`, \`remix_spec\`.
512
+ 3. Brand gate: \`get_brand_kit { brand_id }\`. If \`researchStatus\` is \`complete\`, REUSE it —
513
+ never re-research. If not, run brand research first (\`gooseworks fetch brand-research\`,
514
+ follow it, then \`finalize_brand_research { brand_id }\`) before continuing.
515
+
516
+ ## Step 2 — fetch the recipe + its pack skills for the format
517
+
518
+ The video-ad format skills live in the goose-skills **\`video-ad-formats\`** pack. Map the project's
519
+ \`format\` to its recipe slug:
520
+
521
+ | format | recipe slug | renderer + atoms it drives |
522
+ | --- | --- | --- |
523
+ | \`imessage\` | \`remix-imessage-ad-from-sample\` | create-imessage-video-ad, create-imessage-mockup, stitch-videos-ffmpeg, mix-master, watch |
524
+ | \`chatgpt\` | \`remix-chatgpt-ad-from-sample\` | create-chatgpt-video-ad, create-chatgpt-mockup, render-ios-keyboard, stitch-videos-ffmpeg, watch |
525
+ | \`apple-notes\` | \`remix-apple-notes-ad-from-sample\` | create-apple-notes-video-ad, create-apple-notes-mockup, stitch-videos-ffmpeg, watch |
526
+
527
+ (photo-grid + music-video coming.) Pack skills are fetchable individually by slug but do NOT
528
+ auto-resolve dependencies (no \`dependencySkills\`), so \`gooseworks fetch\` the recipe **and** each
529
+ skill in its row — e.g. for iMessage:
530
+
531
+ \`\`\`bash
532
+ for s in remix-imessage-ad-from-sample create-imessage-video-ad create-imessage-mockup \\
533
+ stitch-videos-ffmpeg mix-master watch; do gooseworks fetch "$s"; done
534
+ \`\`\`
535
+
536
+ Each prints \`{ content, scripts, files }\`. Save each skill's scripts + files under
537
+ \`/tmp/gooseworks-scripts/<slug>/\` and FOLLOW the recipe's SKILL.md — it orchestrates the others.
538
+ The renderer (the \`create-*-mockup\` atom for the format) is a Node package —
539
+ \`npm install\` in its folder so its \`generate.js\` + Playwright resolve, and point the recorder's
540
+ \`NODE_PATH\` at it.
541
+
542
+ ## Step 3 — prepare ALL the ingredients, then review ONCE (always, before any paid render)
543
+
544
+ This is a **review-once** flow: prepare every ingredient the video needs, show the whole set to
545
+ the user in the app, get ONE approval, then render. Never render before approval, and don't drip
546
+ ingredients out one at a time.
547
+
548
+ 1. **Generate every ingredient the format needs — not just the script.** For an iMessage video
549
+ that's typically: the **script** (the bubble thread), the **image(s)** shown in the conversation
550
+ (one or more), and the **end card**. Richer templates add more (hook frame, background, product
551
+ shots, music bed…). Read the recipe for the exact ingredient list. Generate the visuals NOW
552
+ (media proxies / recipe), and \`get_upload_url\` each preview asset to \`working/review/<name>\`.
553
+ You may ask the user a couple of clarifying questions about the generation first if the recipe
554
+ calls for it (angle, which product, offer/code) — batch them, then prepare everything.
555
+ 2. **Mirror the whole ingredient set for review** — \`update_ad_project_script { project_id,
556
+ script_drafts, script }\`. \`script_drafts\` is a structured payload of **container-tagged
557
+ ingredients** so the app renders each piece the right way:
558
+ \`{ format, scenes?, ingredients: [{ container, label, subtitle?, path?, text? }] }\`. Each
559
+ ingredient's \`container\` tells the app HOW to show it:
560
+ - \`image\` (a frame shown in the video), \`endcard\` (the end card), \`avatar\` (a character
561
+ headshot), \`background\` → rendered as an image tile.
562
+ - \`voice\` (a voiceover clip — put the voice NAME in \`subtitle\`), \`music\` (the bed),
563
+ \`audio\` → rendered as an audio player.
564
+ - \`video\` (a clip) → a video player. \`text\` (a copy line like the CTA) → a text tile.
565
+ - \`script\` / \`thread\` / \`note\` / \`conversation\` → the written script (or set \`scenes[]\`
566
+ for the podcast shape, or pass the readable \`script\` string).
567
+ \`path\` = \`working/review/<name>\` (upload the preview asset first via \`get_upload_url\`); \`url\`
568
+ works too. **Label every ingredient** ("Hook image", "End card", "Voiceover", "Background
569
+ music", "HER"). This writes NO render and costs NO credits — it populates the review panel.
570
+ 3. **STOP and ask the user to approve the ingredients in THIS Claude Code session.** Do not render
571
+ until they say go. If they want changes, regenerate the affected ingredient, call
572
+ \`update_ad_project_script\` again, and re-ask. Only AFTER approval do Step 4.
573
+
574
+ ## Step 4 — render locally, report stages, publish
575
+
576
+ 1. Render per the recipe (Playwright record → ffmpeg stitch → \`mix-master\` audio). Generate any
577
+ hook / background / end-card assets through the media proxies (below).
578
+ 2. Open the row LAST: \`submit_render { project_id, kind: "full" }\` → keep \`render_id\`, then
579
+ \`update_render_status { render_id, status: "running" }\`. The render row tracks status only
580
+ (queued / running / complete / failed) — narrate fine-grained progress with
581
+ \`append_project_message\` instead.
582
+ 3. QC by watching: run the \`watch\` skill on the master — verify bubble/beat order + SFX, that
583
+ the brand's product (not the source's) is shown, the end card has the real wordmark + code,
584
+ and the duration is within ~20% of the source.
585
+ 4. Publish: \`get_upload_url { target: { type: "agent", agent_id: ADS_AGENT } }\` → PUT the master
586
+ to \`working/final.mp4\` and a poster to \`working/final-thumb.jpg\`. **Always target ADS_AGENT**
587
+ (see Identity — a file on your token's own agent is invisible to the app). Verify servable:
588
+ \`get_download_url { target: ADS_AGENT, path: "working/final.mp4" }\` must return a non-empty URL.
589
+ Then \`update_render_status { render_id, status: "complete", output_url, thumbnail_url }\` where
590
+ **output_url MUST be the durable render-file URL**
591
+ \`/api/ads/projects/<project_id>/render-file?path=working/final.mp4\` (the app re-presigns it on
592
+ every view) — NEVER a raw proxy/CDN URL (those expire). Same for \`thumbnail_url\`.
593
+ 5. \`set_final_render { project_id, render_id }\` to pin it, then return the \`app_url\` +
594
+ \`brand_url\` (from the project/links) verbatim. Never end on just "done" or a file path.
595
+
596
+ Narrate each long step in one line via \`append_project_message { project_id, role: "agent",
597
+ content }\` — never sit silent on a queue > 90s.
598
+
599
+ ## Media generation — the GooseWorks proxies (queue loop)
600
+
601
+ Media APIs go through GooseWorks proxies with your agent token; do NOT use an SDK's default host
602
+ (your token isn't a FAL/ElevenLabs token → 401). Base = \`<api_base>/api/internal/<proxy>\`; pass
603
+ \`?token=<api_key>&agent_id=<agent_id>\` (agent_id bills the Ads agent). FAL = \`fal-proxy\` (+
604
+ \`fal-storage-proxy\` to host a local image and get a CDN URL); ElevenLabs = \`elevenlabs-proxy\`
605
+ (VO / music bed).
606
+
607
+ **FAL queue gotcha** (#1 waste of generations): submit returns \`status_url\`/\`response_url\` on
608
+ \`queue.fal.run\` (the real host, not the proxy). Polling those 401s forever — rewrite their host
609
+ to the proxy base (keep the path), re-add \`?token=&agent_id=\`. Only the final \`*.fal.media\`
610
+ image is a real public URL. Helper:
611
+
612
+ \`\`\`python
613
+ import json, os, pathlib, time, requests
614
+ from urllib.parse import urlparse
615
+
616
+ def _cfg():
617
+ c = json.loads(pathlib.Path(os.path.expanduser("~/.gooseworks/credentials.json")).read_text())
618
+ return c["api_base"].rstrip("/"), c["api_key"], c.get("agent_id")
619
+
620
+ def _params(tok, agent):
621
+ p = {"token": tok}
622
+ if agent: p["agent_id"] = agent
623
+ return p
624
+
625
+ def fal_generate(model_path, payload, timeout_s=180, poll_s=3):
626
+ """model_path e.g. 'fal-ai/nano-banana-2/edit' (the recipe names the model).
627
+ Returns the result image URL (a public *.fal.media CDN URL)."""
628
+ api_base, tok, agent = _cfg()
629
+ base = api_base + "/api/internal/fal-proxy"
630
+ sub = requests.post(f"{base}/{model_path}", params=_params(tok, agent), json=payload).json()
631
+ to_proxy = lambda u: base + urlparse(u).path
632
+ status_url, response_url = to_proxy(sub["status_url"]), to_proxy(sub["response_url"])
633
+ deadline = time.time() + timeout_s
634
+ while time.time() < deadline:
635
+ st = requests.get(status_url, params=_params(tok, agent)).json()
636
+ if st.get("status") == "COMPLETED":
637
+ return requests.get(response_url, params=_params(tok, agent)).json()["images"][0]["url"]
638
+ if st.get("status") in ("FAILED", "ERROR"):
639
+ raise RuntimeError(f"FAL failed: {st}")
640
+ time.sleep(poll_s)
641
+ raise TimeoutError("FAL polling exceeded timeout")
642
+ \`\`\`
643
+
644
+ ElevenLabs (VO / music) is the same shape against \`<api_base>/api/internal/elevenlabs-proxy\`
645
+ with \`?token=&agent_id=\`. Feed FAL a local image by storing it (\`get_upload_url\`) and passing its
646
+ \`get_download_url\` presigned URL as an \`image_urls\` entry, or POST the bytes to \`fal-storage-proxy\`.
647
+
648
+ ## Rules
649
+
650
+ - **MCP + ffmpeg + Playwright required** — run \`gooseworks doctor\` in Phase 0; stop with the
651
+ exact fix it prints if anything is ✗.
652
+ - **Prepare ALL ingredients first** (script + every visual: image(s) + end card + whatever else
653
+ the template needs), mirror the whole set with \`update_ad_project_script\`, and get the user's
654
+ approval in-session BEFORE rendering — always (review-once).
655
+ - **submit_render LAST**; \`output_url\` = the durable render-file URL, never a CDN URL.
656
+ - **Verify a real, non-empty MP4** (watch it) before marking the render complete.
657
+ - **Reuse the brand** when its research is complete; never re-research.
658
+ - On a hard error (auth/quota/model/timeout) set the render \`failed\` with a short
659
+ \`error_message\` and stop — don't ship the source unchanged.
660
+ - Always end a successful run with \`app_url\` + \`brand_url\`, verbatim.
661
+ `;
662
+ }
395
663
  //# sourceMappingURL=master-skill.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"master-skill.js","sourceRoot":"","sources":["../../src/skills/master-skill.ts"],"names":[],"mappings":";;AA0BA,wCAKC;AAWD,sDA8KC;AAiBD,0DAsLC;AAtYD,oDAAoD;AACpD,SAAgB,cAAc;IAC5B,OAAO;QACL,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,qBAAqB,EAAE,EAAE;QACxD,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,uBAAuB,EAAE,EAAE;KAC1D,CAAC;AACJ,CAAC;AAED;;;;;;;;GAQG;AACH,SAAgB,qBAAqB;IACnC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4KR,CAAC;AACF,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,SAAgB,uBAAuB;IACrC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoLR,CAAC;AACF,CAAC"}
1
+ {"version":3,"file":"master-skill.js","sourceRoot":"","sources":["../../src/skills/master-skill.ts"],"names":[],"mappings":";;AA4BA,wCAMC;AAWD,sDA8KC;AAiBD,0DAoOC;AAcD,8DA+MC;AAlpBD,oDAAoD;AACpD,SAAgB,cAAc;IAC5B,OAAO;QACL,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,qBAAqB,EAAE,EAAE;QACxD,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,uBAAuB,EAAE,EAAE;QACzD,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,yBAAyB,EAAE,EAAE;KAC9D,CAAC;AACJ,CAAC;AAED;;;;;;;;GAQG;AACH,SAAgB,qBAAqB;IACnC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4KR,CAAC;AACF,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,SAAgB,uBAAuB;IACrC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkOR,CAAC;AACF,CAAC;AAED;;;;;;;;;;;GAWG;AACH,SAAgB,yBAAyB;IACvC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6MR,CAAC;AACF,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gooseworks",
3
- "version": "0.3.4",
3
+ "version": "0.3.6",
4
4
  "description": "GooseWorks CLI — give your coding agent real data tools",
5
5
  "bin": {
6
6
  "gooseworks": "./dist/index.js"