takomi 2.1.39 → 2.1.41

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.
Files changed (68) hide show
  1. package/.agents/plugins/marketplace.json +22 -22
  2. package/README.md +217 -422
  3. package/assets/.agent/skills/remotion/rules/assets/charts-bar-chart.tsx +173 -173
  4. package/assets/.agent/skills/remotion/rules/assets/text-animations-typewriter.tsx +100 -100
  5. package/assets/.agent/skills/remotion/rules/text-animations.md +20 -20
  6. package/assets/.agent/skills/remotion/rules/videos.md +171 -171
  7. package/assets/.agent/skills/remotion-real-ui-video/SKILL.md +264 -0
  8. package/assets/.agent/skills/remotion-real-ui-video/references/anti-patterns.md +114 -0
  9. package/assets/.agent/skills/remotion-real-ui-video/references/completion-checklist.md +75 -0
  10. package/assets/.agent/skills/remotion-real-ui-video/references/real-ui-bridge-example.md +93 -0
  11. package/assets/.agent/skills/takomi-flow/SKILL.md +244 -235
  12. package/docs/features/TakomiFlow_Portable_Plugin.md +82 -78
  13. package/docs/takomi-flow-onboarding.md +86 -76
  14. package/docs/takomi-flow-public-distribution.md +94 -94
  15. package/package.json +1 -1
  16. package/plugins/takomi-flow/.mcp.json +11 -11
  17. package/plugins/takomi-flow/assets/capabilities.json +203 -171
  18. package/plugins/takomi-flow/assets/collection.schema.json +29 -29
  19. package/plugins/takomi-flow/assets/examples.json +94 -94
  20. package/plugins/takomi-flow/assets/request.schema.json +91 -67
  21. package/plugins/takomi-flow/assets/result.schema.json +46 -45
  22. package/plugins/takomi-flow/assets/templates/image-request.json +17 -13
  23. package/plugins/takomi-flow/assets/templates/video-request.json +19 -15
  24. package/plugins/takomi-flow/package.json +38 -38
  25. package/plugins/takomi-flow/pnpm-lock.yaml +813 -813
  26. package/plugins/takomi-flow/references/flow-provider-contract.md +349 -326
  27. package/plugins/takomi-flow/scripts/lib/agent-plan.mjs +140 -130
  28. package/plugins/takomi-flow/scripts/lib/api.mjs +113 -113
  29. package/plugins/takomi-flow/scripts/lib/args.mjs +66 -57
  30. package/plugins/takomi-flow/scripts/lib/audit.mjs +65 -65
  31. package/plugins/takomi-flow/scripts/lib/browser.mjs +78 -39
  32. package/plugins/takomi-flow/scripts/lib/capabilities.mjs +11 -11
  33. package/plugins/takomi-flow/scripts/lib/collect.mjs +53 -53
  34. package/plugins/takomi-flow/scripts/lib/commands.mjs +192 -175
  35. package/plugins/takomi-flow/scripts/lib/doctor.mjs +77 -77
  36. package/plugins/takomi-flow/scripts/lib/examples.mjs +17 -17
  37. package/plugins/takomi-flow/scripts/lib/flow-media.mjs +39 -0
  38. package/plugins/takomi-flow/scripts/lib/flow-outcome.mjs +115 -90
  39. package/plugins/takomi-flow/scripts/lib/flow-project-session.mjs +153 -0
  40. package/plugins/takomi-flow/scripts/lib/flow-ui.mjs +204 -165
  41. package/plugins/takomi-flow/scripts/lib/generation.mjs +190 -90
  42. package/plugins/takomi-flow/scripts/lib/inspect.mjs +81 -81
  43. package/plugins/takomi-flow/scripts/lib/mcp-prompts.mjs +39 -39
  44. package/plugins/takomi-flow/scripts/lib/mcp-resources.mjs +16 -16
  45. package/plugins/takomi-flow/scripts/lib/mcp-tools.mjs +165 -150
  46. package/plugins/takomi-flow/scripts/lib/media.mjs +78 -78
  47. package/plugins/takomi-flow/scripts/lib/observe.mjs +54 -54
  48. package/plugins/takomi-flow/scripts/lib/paths.mjs +37 -37
  49. package/plugins/takomi-flow/scripts/lib/playwright-loader.mjs +23 -23
  50. package/plugins/takomi-flow/scripts/lib/prompts.mjs +72 -72
  51. package/plugins/takomi-flow/scripts/lib/report.mjs +141 -141
  52. package/plugins/takomi-flow/scripts/lib/request-validator.mjs +73 -58
  53. package/plugins/takomi-flow/scripts/lib/request.mjs +66 -55
  54. package/plugins/takomi-flow/scripts/lib/resource-files.mjs +69 -69
  55. package/plugins/takomi-flow/scripts/lib/result.mjs +31 -31
  56. package/plugins/takomi-flow/scripts/lib/review.mjs +44 -44
  57. package/plugins/takomi-flow/scripts/lib/selftest.mjs +153 -153
  58. package/plugins/takomi-flow/scripts/lib/settings-plan.mjs +44 -39
  59. package/plugins/takomi-flow/scripts/lib/templates.mjs +37 -37
  60. package/plugins/takomi-flow/scripts/lib/trusted-chrome.mjs +67 -67
  61. package/plugins/takomi-flow/scripts/lib/workflow.mjs +56 -56
  62. package/plugins/takomi-flow/scripts/mcp-server.mjs +18 -18
  63. package/plugins/takomi-flow/scripts/mcp-smoke.mjs +144 -142
  64. package/plugins/takomi-flow/scripts/takomi-flow.mjs +17 -17
  65. package/plugins/takomi-flow/skills/takomi-flow/SKILL.md +244 -235
  66. package/plugins/takomi-flow/skills/takomi-flow/agents/openai.yaml +7 -7
  67. package/scripts/install-takomi-flow.ps1 +85 -85
  68. package/src/skills-catalog.js +10 -9
@@ -1,65 +1,65 @@
1
- import fs from 'node:fs';
2
- import { runDoctor } from './doctor.mjs';
3
- import { loadCapabilities } from './capabilities.mjs';
4
- import { listRuns } from './inspect.mjs';
5
-
6
- export async function runAudit(args = {}) {
7
- const doctor = await runDoctor({
8
- profileDir: args.profileDir,
9
- outputDir: args.outputDir,
10
- });
11
- const capabilities = loadCapabilities();
12
- const history = listRuns(doctor.outputDir, args.limit || 5);
13
- const profileReady = fs.existsSync(doctor.profileDir);
14
- const browserReady = doctor.checks.every(check => (
15
- check.name === 'profileDir' || check.status !== 'failed'
16
- ));
17
- return {
18
- schemaVersion: 1,
19
- status: doctor.status === 'failed' ? 'failed' : 'ok',
20
- readiness: {
21
- browserAutomation: browserReady,
22
- flowProfile: profileReady,
23
- mediaReview: hasCheck(doctor, 'ffmpeg') && hasCheck(doctor, 'ffprobe'),
24
- mcpTools: capabilities.mcpTools.length,
25
- mcpResources: capabilities.mcpResources.length,
26
- },
27
- safeActions: [
28
- 'capabilities',
29
- 'examples',
30
- 'plan',
31
- 'template',
32
- 'workflow without submit',
33
- 'validate',
34
- 'runs',
35
- 'inspect',
36
- 'assets',
37
- 'review',
38
- 'collect',
39
- 'report',
40
- 'selftest',
41
- ],
42
- gatedActions: [
43
- 'bootstrap requires a headed browser and manual Google login',
44
- 'observe requires allowBrowser=true',
45
- 'generate requires allowBrowser=true and allowSpend=true',
46
- 'workflow submit requires allowBrowser=true and allowSpend=true',
47
- ],
48
- doctor,
49
- recentRuns: history.runs,
50
- nextActions: nextActions(doctor, profileReady, history.runs),
51
- };
52
- }
53
-
54
- function hasCheck(doctor, name) {
55
- return doctor.checks.some(check => check.name === name && check.status === 'ok');
56
- }
57
-
58
- function nextActions(doctor, profileReady, runs) {
59
- const actions = [...doctor.recommendations];
60
- if (!profileReady) actions.push('Run bootstrap in headed mode and log into Google Flow manually.');
61
- if (!runs.length) actions.push('Run workflow to prepare the first request, or smoke after login.');
62
- if (runs.some(run => run.status === 'downloaded')) actions.push('Run review on downloaded runs before reusing assets.');
63
- if (!actions.length) actions.push('TakomiFlow is ready for safe no-spend workflow preparation.');
64
- return [...new Set(actions)];
65
- }
1
+ import fs from 'node:fs';
2
+ import { runDoctor } from './doctor.mjs';
3
+ import { loadCapabilities } from './capabilities.mjs';
4
+ import { listRuns } from './inspect.mjs';
5
+
6
+ export async function runAudit(args = {}) {
7
+ const doctor = await runDoctor({
8
+ profileDir: args.profileDir,
9
+ outputDir: args.outputDir,
10
+ });
11
+ const capabilities = loadCapabilities();
12
+ const history = listRuns(doctor.outputDir, args.limit || 5);
13
+ const profileReady = fs.existsSync(doctor.profileDir);
14
+ const browserReady = doctor.checks.every(check => (
15
+ check.name === 'profileDir' || check.status !== 'failed'
16
+ ));
17
+ return {
18
+ schemaVersion: 1,
19
+ status: doctor.status === 'failed' ? 'failed' : 'ok',
20
+ readiness: {
21
+ browserAutomation: browserReady,
22
+ flowProfile: profileReady,
23
+ mediaReview: hasCheck(doctor, 'ffmpeg') && hasCheck(doctor, 'ffprobe'),
24
+ mcpTools: capabilities.mcpTools.length,
25
+ mcpResources: capabilities.mcpResources.length,
26
+ },
27
+ safeActions: [
28
+ 'capabilities',
29
+ 'examples',
30
+ 'plan',
31
+ 'template',
32
+ 'workflow without submit',
33
+ 'validate',
34
+ 'runs',
35
+ 'inspect',
36
+ 'assets',
37
+ 'review',
38
+ 'collect',
39
+ 'report',
40
+ 'selftest',
41
+ ],
42
+ gatedActions: [
43
+ 'bootstrap requires a headed browser and manual Google login',
44
+ 'observe requires allowBrowser=true',
45
+ 'generate requires allowBrowser=true and allowSpend=true',
46
+ 'workflow submit requires allowBrowser=true and allowSpend=true',
47
+ ],
48
+ doctor,
49
+ recentRuns: history.runs,
50
+ nextActions: nextActions(doctor, profileReady, history.runs),
51
+ };
52
+ }
53
+
54
+ function hasCheck(doctor, name) {
55
+ return doctor.checks.some(check => check.name === name && check.status === 'ok');
56
+ }
57
+
58
+ function nextActions(doctor, profileReady, runs) {
59
+ const actions = [...doctor.recommendations];
60
+ if (!profileReady) actions.push('Run bootstrap in headed mode and log into Google Flow manually.');
61
+ if (!runs.length) actions.push('Run workflow to prepare the first request, or smoke after login.');
62
+ if (runs.some(run => run.status === 'downloaded')) actions.push('Run review on downloaded runs before reusing assets.');
63
+ if (!actions.length) actions.push('TakomiFlow is ready for safe no-spend workflow preparation.');
64
+ return [...new Set(actions)];
65
+ }
@@ -1,39 +1,78 @@
1
- import path from 'node:path';
2
- import { ensureDir } from './paths.mjs';
3
- import { loadPlaywright } from './playwright-loader.mjs';
4
-
5
- export async function launchFlowBrowser(options = {}) {
6
- const { chromium } = await loadPlaywright();
7
- const profileDir = ensureDir(path.resolve(options.profileDir));
8
- const downloadsDir = ensureDir(path.resolve(options.downloadsDir));
9
- const headless = options.headless === true;
10
- if (options.cdpUrl) {
11
- const browser = await chromium.connectOverCDP(options.cdpUrl);
12
- const context = browser.contexts()[0] || await browser.newContext({ acceptDownloads: true });
13
- const page = context.pages()[0] || await context.newPage();
14
- return { browser, context, page, profileDir, downloadsDir, cdpUrl: options.cdpUrl, attached: true };
15
- }
16
- const channel = options.browserChannel || process.env.TAKOMI_FLOW_BROWSER_CHANNEL || defaultBrowserChannel();
17
- const context = await chromium.launchPersistentContext(profileDir, {
18
- headless,
19
- ...(channel ? { channel } : {}),
20
- acceptDownloads: true,
21
- downloadsPath: downloadsDir,
22
- viewport: { width: 1440, height: 960 },
23
- });
24
- const page = context.pages()[0] || await context.newPage();
25
- return { context, page, profileDir, downloadsDir, browserChannel: channel };
26
- }
27
-
28
- export async function closeFlowBrowser(browser) {
29
- if (browser.attached) {
30
- await browser.browser?.close?.().catch(() => {});
31
- return;
32
- }
33
- await browser.context.close().catch(() => {});
34
- }
35
-
36
- function defaultBrowserChannel() {
37
- if (process.platform === 'win32' || process.platform === 'darwin') return 'chrome';
38
- return undefined;
39
- }
1
+ import path from 'node:path';
2
+ import { ensureDir } from './paths.mjs';
3
+ import { loadPlaywright } from './playwright-loader.mjs';
4
+ import { launchTrustedChrome } from './trusted-chrome.mjs';
5
+
6
+ const DEFAULT_CDP_URL = 'http://127.0.0.1:9222';
7
+
8
+ export async function launchFlowBrowser(options = {}) {
9
+ const { chromium } = await loadPlaywright();
10
+ const profileDir = ensureDir(path.resolve(options.profileDir));
11
+ const downloadsDir = ensureDir(path.resolve(options.downloadsDir));
12
+ const headless = options.headless === true;
13
+ const trustedCdpUrl = await trustedChromeCdpUrl(options);
14
+ if (trustedCdpUrl) {
15
+ const browser = await chromium.connectOverCDP(trustedCdpUrl);
16
+ const context = browser.contexts()[0] || await browser.newContext({ acceptDownloads: true });
17
+ const page = context.pages()[0] || await context.newPage();
18
+ return { browser, context, page, profileDir, downloadsDir, cdpUrl: trustedCdpUrl, attached: true };
19
+ }
20
+ const channel = options.browserChannel || process.env.TAKOMI_FLOW_BROWSER_CHANNEL || defaultBrowserChannel();
21
+ const context = await chromium.launchPersistentContext(profileDir, {
22
+ headless,
23
+ ...(channel ? { channel } : {}),
24
+ acceptDownloads: true,
25
+ downloadsPath: downloadsDir,
26
+ viewport: { width: 1440, height: 960 },
27
+ });
28
+ const page = context.pages()[0] || await context.newPage();
29
+ return { context, page, profileDir, downloadsDir, browserChannel: channel };
30
+ }
31
+
32
+ export async function closeFlowBrowser(browser) {
33
+ if (browser.attached) {
34
+ if (typeof browser.browser?.disconnect === 'function') {
35
+ browser.browser.disconnect();
36
+ return;
37
+ }
38
+ await browser.browser?.close?.().catch(() => {});
39
+ return;
40
+ }
41
+ await browser.context.close().catch(() => {});
42
+ }
43
+
44
+ async function trustedChromeCdpUrl(options) {
45
+ if (options.cdpUrl) return options.cdpUrl;
46
+ if (options.headless === true || options.usePlaywrightBrowser === true) return null;
47
+ if (await canConnect(DEFAULT_CDP_URL)) return DEFAULT_CDP_URL;
48
+ try {
49
+ const trusted = launchTrustedChrome({ url: options.url });
50
+ return await waitForCdp(trusted.cdpUrl);
51
+ } catch {
52
+ return null;
53
+ }
54
+ }
55
+
56
+ async function waitForCdp(cdpUrl) {
57
+ const timeoutMs = 10000;
58
+ const started = Date.now();
59
+ while (Date.now() - started < timeoutMs) {
60
+ if (await canConnect(cdpUrl)) return cdpUrl;
61
+ await new Promise(resolve => setTimeout(resolve, 500));
62
+ }
63
+ return null;
64
+ }
65
+
66
+ async function canConnect(cdpUrl) {
67
+ try {
68
+ const response = await fetch(`${cdpUrl}/json/version`, { signal: AbortSignal.timeout(2000) });
69
+ return response.ok;
70
+ } catch {
71
+ return false;
72
+ }
73
+ }
74
+
75
+ function defaultBrowserChannel() {
76
+ if (process.platform === 'win32' || process.platform === 'darwin') return 'chrome';
77
+ return undefined;
78
+ }
@@ -1,11 +1,11 @@
1
- import fs from 'node:fs';
2
- import path from 'node:path';
3
- import { fileURLToPath } from 'node:url';
4
-
5
- const MODULE_DIR = path.dirname(fileURLToPath(import.meta.url));
6
- const PLUGIN_ROOT = path.resolve(MODULE_DIR, '..', '..');
7
- const CAPABILITIES_PATH = path.join(PLUGIN_ROOT, 'assets', 'capabilities.json');
8
-
9
- export function loadCapabilities() {
10
- return JSON.parse(fs.readFileSync(CAPABILITIES_PATH, 'utf8'));
11
- }
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+
5
+ const MODULE_DIR = path.dirname(fileURLToPath(import.meta.url));
6
+ const PLUGIN_ROOT = path.resolve(MODULE_DIR, '..', '..');
7
+ const CAPABILITIES_PATH = path.join(PLUGIN_ROOT, 'assets', 'capabilities.json');
8
+
9
+ export function loadCapabilities() {
10
+ return JSON.parse(fs.readFileSync(CAPABILITIES_PATH, 'utf8'));
11
+ }
@@ -1,53 +1,53 @@
1
- import fs from 'node:fs';
2
- import path from 'node:path';
3
- import { ensureDir, writeJson } from './paths.mjs';
4
- import { reviewRun } from './review.mjs';
5
-
6
- export async function collectRun(args = {}) {
7
- if (!args.run) throw new Error('collect requires --run <run.json|run-dir>');
8
- if (!args.targetDir) throw new Error('collect requires --target-dir <path>');
9
- const targetDir = ensureDir(path.resolve(args.targetDir));
10
- const review = await reviewRun({
11
- run: args.run,
12
- frames: args.frames || 0,
13
- reportPath: args.reportPath,
14
- });
15
- const copied = [];
16
- for (const asset of review.catalog.assets || []) {
17
- if (asset.exists) copied.push(copyFile(asset.path, path.join(targetDir, 'assets')));
18
- if (args.includeFrames) {
19
- for (const frame of asset.frames || []) copied.push(copyFile(frame, path.join(targetDir, 'frames')));
20
- }
21
- }
22
- if (review.reportPath && fs.existsSync(review.reportPath)) {
23
- copied.push(copyFile(review.reportPath, targetDir, 'report.md'));
24
- }
25
- const manifest = {
26
- schemaVersion: 1,
27
- status: copied.length ? 'collected' : 'empty',
28
- runId: review.runId,
29
- sourceRunDir: review.runDir,
30
- targetDir,
31
- copied,
32
- review,
33
- };
34
- manifest.manifestPath = writeJson(path.join(targetDir, 'takomi-flow-collection.json'), manifest);
35
- return manifest;
36
- }
37
-
38
- function copyFile(source, targetDir, name = path.basename(source)) {
39
- ensureDir(targetDir);
40
- const target = uniquePath(path.join(targetDir, name));
41
- fs.copyFileSync(source, target);
42
- return { source, target };
43
- }
44
-
45
- function uniquePath(filePath) {
46
- if (!fs.existsSync(filePath)) return filePath;
47
- const parsed = path.parse(filePath);
48
- for (let i = 1; i < 1000; i += 1) {
49
- const candidate = path.join(parsed.dir, `${parsed.name}-${i}${parsed.ext}`);
50
- if (!fs.existsSync(candidate)) return candidate;
51
- }
52
- throw new Error(`Could not create unique path for ${filePath}`);
53
- }
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { ensureDir, writeJson } from './paths.mjs';
4
+ import { reviewRun } from './review.mjs';
5
+
6
+ export async function collectRun(args = {}) {
7
+ if (!args.run) throw new Error('collect requires --run <run.json|run-dir>');
8
+ if (!args.targetDir) throw new Error('collect requires --target-dir <path>');
9
+ const targetDir = ensureDir(path.resolve(args.targetDir));
10
+ const review = await reviewRun({
11
+ run: args.run,
12
+ frames: args.frames || 0,
13
+ reportPath: args.reportPath,
14
+ });
15
+ const copied = [];
16
+ for (const asset of review.catalog.assets || []) {
17
+ if (asset.exists) copied.push(copyFile(asset.path, path.join(targetDir, 'assets')));
18
+ if (args.includeFrames) {
19
+ for (const frame of asset.frames || []) copied.push(copyFile(frame, path.join(targetDir, 'frames')));
20
+ }
21
+ }
22
+ if (review.reportPath && fs.existsSync(review.reportPath)) {
23
+ copied.push(copyFile(review.reportPath, targetDir, 'report.md'));
24
+ }
25
+ const manifest = {
26
+ schemaVersion: 1,
27
+ status: copied.length ? 'collected' : 'empty',
28
+ runId: review.runId,
29
+ sourceRunDir: review.runDir,
30
+ targetDir,
31
+ copied,
32
+ review,
33
+ };
34
+ manifest.manifestPath = writeJson(path.join(targetDir, 'takomi-flow-collection.json'), manifest);
35
+ return manifest;
36
+ }
37
+
38
+ function copyFile(source, targetDir, name = path.basename(source)) {
39
+ ensureDir(targetDir);
40
+ const target = uniquePath(path.join(targetDir, name));
41
+ fs.copyFileSync(source, target);
42
+ return { source, target };
43
+ }
44
+
45
+ function uniquePath(filePath) {
46
+ if (!fs.existsSync(filePath)) return filePath;
47
+ const parsed = path.parse(filePath);
48
+ for (let i = 1; i < 1000; i += 1) {
49
+ const candidate = path.join(parsed.dir, `${parsed.name}-${i}${parsed.ext}`);
50
+ if (!fs.existsSync(candidate)) return candidate;
51
+ }
52
+ throw new Error(`Could not create unique path for ${filePath}`);
53
+ }