@leejungkiin/awkit 1.6.6 → 1.6.8

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 (34) hide show
  1. package/bin/awk.js +111 -8
  2. package/package.json +5 -3
  3. package/schemas/onboarding-screen.schema.json +108 -0
  4. package/scripts/__pycache__/openrouter_image_gen.cpython-311.pyc +0 -0
  5. package/scripts/cockpit-quota.js +93 -0
  6. package/scripts/openrouter_image_gen.py +772 -0
  7. package/scripts/video-analyzer.js +172 -0
  8. package/skills/CATALOG.md +2 -1
  9. package/skills/ai-sprite-maker/SKILL.md +27 -6
  10. package/skills/ai-sprite-maker/scripts/__pycache__/remove_chroma_key.cpython-311.pyc +0 -0
  11. package/skills/ai-sprite-maker/scripts/remove_chroma_key.py +440 -0
  12. package/skills/awf-caveman/SKILL.md +65 -0
  13. package/skills/expo-build-optimizer/SKILL.md +33 -0
  14. package/skills/ios-app-store-audit/SKILL.md +48 -0
  15. package/skills/ios-expert-coder/SKILL.md +45 -0
  16. package/skills/mascot-designer/SKILL.md +66 -0
  17. package/skills/mascot-designer/examples/witny-case-study.md +35 -0
  18. package/skills/orchestrator/SKILL.md +20 -0
  19. package/skills/short-maker/scripts/google-flow-cli/README.md +227 -115
  20. package/skills/short-maker/scripts/google-flow-cli/gflow/api/client.py +32 -3
  21. package/skills/short-maker/scripts/google-flow-cli/gflow/api/models.py +4 -2
  22. package/skills/short-maker/scripts/google-flow-cli/gflow/cli/main.py +33 -6
  23. package/skills/short-maker/scripts/google-flow-cli/pyproject.toml +1 -1
  24. package/skills/verification-gate/SKILL.md +4 -0
  25. package/templates/help.html +21 -0
  26. package/templates/project-identity/android.json +24 -0
  27. package/templates/project-identity/backend-nestjs.json +24 -0
  28. package/templates/project-identity/expo.json +24 -0
  29. package/templates/project-identity/ios.json +24 -0
  30. package/templates/project-identity/web-nextjs.json +24 -0
  31. package/templates/specs/design-template.md +71 -161
  32. package/templates/specs/requirements-template.md +133 -65
  33. package/workflows/ui/create-spec-architect.md +80 -50
  34. package/workflows/ui/image-gen.md +118 -0
package/bin/awk.js CHANGED
@@ -41,6 +41,8 @@ const { generateClaudeRules, generateClaudeSkills } = require('./claude-generato
41
41
  const { generateCursorRules, generateCursorSkills } = require('./cursor-generators');
42
42
  const { cmdGate } = require('../scripts/automation-gate');
43
43
  const { cmdObsidian } = require('../scripts/obsidian-sync');
44
+ const { cmdQuota } = require('../scripts/cockpit-quota');
45
+ const { cmdVideo } = require('../scripts/video-analyzer');
44
46
 
45
47
  // ─── Platform Definitions ──────────────────────────────────────────────────
46
48
 
@@ -55,6 +57,7 @@ const PLATFORMS = {
55
57
  skills: 'skills',
56
58
  schemas: 'schemas',
57
59
  templates: 'templates',
60
+ scripts: 'scripts',
58
61
  },
59
62
  supportsCustomModes: false,
60
63
  supportsSubagents: false,
@@ -736,6 +739,15 @@ function cmdInstall(args = []) {
736
739
  ok(`${tmplCount} templates installed`);
737
740
  }
738
741
 
742
+ // 8.5 Copy scripts (always overwrite)
743
+ if (plat.dirs.scripts) {
744
+ info('Installing scripts...');
745
+ const scriptsSrc = path.join(AWK_ROOT, 'scripts');
746
+ const scriptsDest = path.join(target, plat.dirs.scripts);
747
+ const scriptsCount = copyDirRecursive(scriptsSrc, scriptsDest);
748
+ ok(`${scriptsCount} scripts installed`);
749
+ }
750
+
739
751
  // 9. Save version
740
752
  fs.writeFileSync(plat.versionFile, AWK_VERSION);
741
753
  ok(`Version ${AWK_VERSION} saved`);
@@ -2019,6 +2031,9 @@ function cmdHelp() {
2019
2031
  log(` ${C.green}update${C.reset} Pull latest + reinstall`);
2020
2032
  log(` ${C.green}lint${C.reset} Run skill & workflow guards (check length, frontmatter)`);
2021
2033
  log(` ${C.green}doctor${C.reset} Check installation health`);
2034
+ log(` ${C.green}credentials list${C.reset} List stored API keys`);
2035
+ log(` ${C.green}credentials set${C.reset} <k> <v> Set API key (e.g., gemini_api_key, openrouter_api_key)`);
2036
+ log(` ${C.green}set-openrouter${C.reset} <key> Shorthand for setting OpenRouter API Key`);
2022
2037
  log('');
2023
2038
 
2024
2039
  // Project Init
@@ -2034,6 +2049,13 @@ function cmdHelp() {
2034
2049
  log(`${C.bold}🧹 Maintenance${C.reset}`);
2035
2050
  log(line);
2036
2051
  log(` ${C.green}serve${C.reset} [dir] [-p <port>] Start local HTTP server for assets in CWD`);
2052
+ log(` ${C.green}video${C.reset} <file.mp4> [pmt] Phân tích video/screen record bằng Gemini API`);
2053
+ log(` ${C.gray} --pro${C.reset} Dùng Gemini 3.1 Pro (Chất lượng cao nhất)`);
2054
+ log(` ${C.gray} --flash${C.reset} Dùng Gemini 3 Flash (Nhanh & Tiết kiệm)`);
2055
+ log(` ${C.gray} --debug${C.reset} Dùng prompt tìm lỗi, crash, log`);
2056
+ log(` ${C.gray} --uiux${C.reset} Dùng prompt đánh giá giao diện, animation`);
2057
+ log(` ${C.gray} --clone${C.reset} Dùng prompt bóc băng cấu trúc app đối thủ`);
2058
+ log(` ${C.green}quota${C.reset} Check AI Model Quota from Cockpit extension`);
2037
2059
  log(` ${C.green}browser clean${C.reset} Clean browser recordings`);
2038
2060
  log(` ${C.gray} --days <N>${C.reset} Keep recordings from last N days (default: 7)`);
2039
2061
  log(` ${C.gray} --all${C.reset} Delete all recordings`);
@@ -2349,6 +2371,24 @@ function buildProjectIdentity(projectName, projectType, cwd, date) {
2349
2371
  git: {
2350
2372
  autoCommit: true,
2351
2373
  autoPush: true
2374
+ },
2375
+ obsidian: {
2376
+ enabled: false,
2377
+ path: "",
2378
+ autoSync: false
2379
+ },
2380
+ mcp: {
2381
+ "pixel-mcp": {
2382
+ enabled: false
2383
+ }
2384
+ }
2385
+ },
2386
+ modelPolicy: {
2387
+ mode: "auto",
2388
+ defaultTier: "STANDARD",
2389
+ tierOverrides: {
2390
+ "*.plist|*.json|*.env": "LIGHT",
2391
+ "docs/*": "LIGHT"
2352
2392
  }
2353
2393
  },
2354
2394
  projectStage: 'development',
@@ -2574,16 +2614,51 @@ async function cmdInit(forceFlag = false) {
2574
2614
  git: {
2575
2615
  autoCommit: true,
2576
2616
  autoPush: true
2617
+ },
2618
+ obsidian: {
2619
+ enabled: false,
2620
+ path: "",
2621
+ autoSync: false
2622
+ },
2623
+ mcp: {
2624
+ "pixel-mcp": {
2625
+ enabled: false
2626
+ }
2577
2627
  }
2578
2628
  };
2579
2629
  fs.writeFileSync(identityPath, JSON.stringify(currentIdentity, null, 2) + '\n');
2580
2630
  ok('Added Automation config placeholder to .project-identity');
2581
- } else if (currentIdentity.automation.autoQA === undefined) {
2582
- // Update existing automation block with new QA fields
2583
- currentIdentity.automation.autoQA = true;
2584
- currentIdentity.automation.maxSelfCorrectionLoops = 3;
2585
- fs.writeFileSync(identityPath, JSON.stringify(currentIdentity, null, 2) + '\n');
2586
- ok('Updated Automation config with autoQA defaults in .project-identity');
2631
+ } else {
2632
+ let changed = false;
2633
+ if (currentIdentity.automation.autoQA === undefined) {
2634
+ // Update existing automation block with new QA fields
2635
+ currentIdentity.automation.autoQA = true;
2636
+ currentIdentity.automation.maxSelfCorrectionLoops = 3;
2637
+ changed = true;
2638
+ }
2639
+ if (!currentIdentity.automation.obsidian) {
2640
+ currentIdentity.automation.obsidian = { enabled: false, path: "", autoSync: false };
2641
+ changed = true;
2642
+ }
2643
+ if (!currentIdentity.automation.mcp) {
2644
+ currentIdentity.automation.mcp = { "pixel-mcp": { enabled: false } };
2645
+ changed = true;
2646
+ }
2647
+ if (!currentIdentity.modelPolicy) {
2648
+ currentIdentity.modelPolicy = {
2649
+ mode: "auto",
2650
+ defaultTier: "STANDARD",
2651
+ tierOverrides: {
2652
+ "*.plist|*.json|*.env": "LIGHT",
2653
+ "docs/*": "LIGHT"
2654
+ }
2655
+ };
2656
+ changed = true;
2657
+ }
2658
+ if (changed) {
2659
+ fs.writeFileSync(identityPath, JSON.stringify(currentIdentity, null, 2) + '\n');
2660
+ ok('Updated Automation/ModelPolicy config with defaults in .project-identity');
2661
+ }
2587
2662
  }
2588
2663
  } catch (_) { /* ignore */ }
2589
2664
 
@@ -3042,7 +3117,7 @@ function credentialsHelp() {
3042
3117
  log(` ${C.green}awkit credentials remove${C.reset} <key> Remove a credential`);
3043
3118
  log(` ${C.green}awkit credentials setup${C.reset} Interactive setup wizard`);
3044
3119
  log('');
3045
- log(` ${C.gray}Known keys: gemini_api_key, lucylab_bearer${C.reset}`);
3120
+ log(` ${C.gray}Known keys: gemini_api_key, openrouter_api_key, lucylab_bearer${C.reset}`);
3046
3121
  log(` ${C.gray}Config: ${CREDENTIALS_CONFIG_PATH}${C.reset}`);
3047
3122
  log('');
3048
3123
  }
@@ -3089,6 +3164,20 @@ async function credentialsSetup() {
3089
3164
  dim('Kept existing LucyLab Bearer');
3090
3165
  }
3091
3166
 
3167
+ log('');
3168
+
3169
+ // OpenRouter API Key
3170
+ log(`${C.gray} OpenRouter API Key for Premium Image Generation (GPT-5.4)${C.reset}`);
3171
+ log(`${C.gray} Get your key at: https://openrouter.ai/keys${C.reset}`);
3172
+ const openrouterKey = sanitize(await question(` ${C.yellow}OpenRouter API Key${config.openrouter_api_key ? ` [${config.openrouter_api_key.slice(0, 8)}...]` : ''}: ${C.reset}`));
3173
+ if (openrouterKey) {
3174
+ config.openrouter_api_key = openrouterKey;
3175
+ ok('OpenRouter API Key saved');
3176
+ } else if (config.openrouter_api_key) {
3177
+ dim('Kept existing OpenRouter API Key');
3178
+ }
3179
+
3180
+ log('');
3092
3181
  credentialsSave(config);
3093
3182
  log('');
3094
3183
  ok(`Credentials saved to ${CREDENTIALS_CONFIG_PATH}`);
@@ -3918,6 +4007,15 @@ const [, , command, ...args] = process.argv;
3918
4007
  case 'creds':
3919
4008
  cmdCredentials(args);
3920
4009
  break;
4010
+ case 'set-openrouter': {
4011
+ const key = args[0];
4012
+ if (!key) {
4013
+ err('Usage: awkit set-openrouter <api_key>');
4014
+ return;
4015
+ }
4016
+ cmdCredentials(['set', 'openrouter_api_key', key]);
4017
+ break;
4018
+ }
3921
4019
  case 'serve':
3922
4020
  cmdServe(args);
3923
4021
  break;
@@ -3928,13 +4026,18 @@ const [, , command, ...args] = process.argv;
3928
4026
  case 'gate':
3929
4027
  cmdGate(args);
3930
4028
  break;
3931
- case 'obsidian':
3932
4029
  case 'obs':
3933
4030
  cmdObsidian(args);
3934
4031
  break;
4032
+ case 'quota':
4033
+ cmdQuota(args);
4034
+ break;
3935
4035
  case 'admin':
3936
4036
  cmdAdmin();
3937
4037
  break;
4038
+ case 'video':
4039
+ await cmdVideo(args);
4040
+ break;
3938
4041
  case 'restart':
3939
4042
  await cmdRestart();
3940
4043
  break;
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "@leejungkiin/awkit",
3
- "version": "1.6.6",
3
+ "version": "1.6.8",
4
4
  "description": "Antigravity Workflow Kit v1.6 Unified AI agent orchestration system with Mindful Checkpoints.",
5
5
  "main": "bin/awk.js",
6
+ "private": false,
6
7
  "bin": {
7
8
  "awkit": "bin/awk.js",
8
9
  "ag": "bin/awk.js"
@@ -38,7 +39,8 @@
38
39
  "CHANGELOG.md"
39
40
  ],
40
41
  "dependencies": {
41
- "@leejungkiin/awkit-symphony": "^0.1.0",
42
- "@duytransipher/gitnexus": "latest"
42
+ "@duytransipher/gitnexus": "latest",
43
+ "@google/genai": "^1.50.1",
44
+ "@leejungkiin/awkit-symphony": "^0.1.0"
43
45
  }
44
46
  }
@@ -0,0 +1,108 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "title": "Onboarding Quiz Funnel Configuration",
4
+ "description": "Schema for defining a highly-converting, behaviorally-driven onboarding quiz funnel.",
5
+ "type": "object",
6
+ "required": ["funnelId", "screens"],
7
+ "properties": {
8
+ "funnelId": {
9
+ "type": "string",
10
+ "description": "Unique identifier for this onboarding funnel."
11
+ },
12
+ "defaultMascotId": {
13
+ "type": "string",
14
+ "description": "ID of the default mascot to use if a screen doesn't override it."
15
+ },
16
+ "screens": {
17
+ "type": "array",
18
+ "description": "An ordered array of screens the user will navigate through.",
19
+ "items": {
20
+ "$ref": "#/definitions/Screen"
21
+ }
22
+ }
23
+ },
24
+ "definitions": {
25
+ "Screen": {
26
+ "type": "object",
27
+ "required": ["id", "phase", "type", "title"],
28
+ "properties": {
29
+ "id": {
30
+ "type": "string",
31
+ "description": "Unique identifier for the screen."
32
+ },
33
+ "phase": {
34
+ "type": "string",
35
+ "enum": ["hook", "therapy", "objection", "labor_illusion", "reveal", "paywall"],
36
+ "description": "The psychological phase this screen belongs to."
37
+ },
38
+ "type": {
39
+ "type": "string",
40
+ "enum": ["intro", "single_choice", "multi_choice", "slider", "loading_simulation", "summary", "paywall"],
41
+ "description": "The functional UI type of the screen."
42
+ },
43
+ "title": {
44
+ "type": "string",
45
+ "description": "Main heading or question displayed to the user."
46
+ },
47
+ "subtitle": {
48
+ "type": "string",
49
+ "description": "Secondary text providing context or reassurance."
50
+ },
51
+ "mascotState": {
52
+ "type": "string",
53
+ "enum": ["none", "greeting", "thinking", "encouraging", "empathizing", "analyzing", "presenting", "celebrating", "idle"],
54
+ "description": "The emotional state of the virtual coach to display."
55
+ },
56
+ "progressSpeed": {
57
+ "type": "string",
58
+ "enum": ["fast", "normal", "slow", "jump"],
59
+ "description": "Manipulative progress bar speed for this screen."
60
+ },
61
+ "options": {
62
+ "type": "array",
63
+ "description": "Choices presented to the user (for single_choice or multi_choice types).",
64
+ "items": {
65
+ "$ref": "#/definitions/Option"
66
+ }
67
+ },
68
+ "loadingConfiguration": {
69
+ "type": "object",
70
+ "description": "Configuration specifically for labor_illusion screens.",
71
+ "properties": {
72
+ "durationMs": { "type": "integer" },
73
+ "fakeSteps": {
74
+ "type": "array",
75
+ "items": { "type": "string" }
76
+ }
77
+ }
78
+ }
79
+ }
80
+ },
81
+ "Option": {
82
+ "type": "object",
83
+ "required": ["id", "label"],
84
+ "properties": {
85
+ "id": {
86
+ "type": "string"
87
+ },
88
+ "label": {
89
+ "type": "string",
90
+ "description": "The text displayed on the button."
91
+ },
92
+ "icon": {
93
+ "type": "string",
94
+ "description": "Optional icon identifier for the option."
95
+ },
96
+ "tagsToApply": {
97
+ "type": "array",
98
+ "description": "List of tags to assign to the user if they select this option.",
99
+ "items": { "type": "string" }
100
+ },
101
+ "reinforcementMessage": {
102
+ "type": "string",
103
+ "description": "An intermittent reinforcement message to show after selecting this option (e.g., '70% of our users feel the same way.')."
104
+ }
105
+ }
106
+ }
107
+ }
108
+ }
@@ -0,0 +1,93 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const os = require('os');
4
+
5
+ const C = {
6
+ reset: '\x1b[0m',
7
+ green: '\x1b[32m',
8
+ yellow: '\x1b[33m',
9
+ red: '\x1b[31m',
10
+ cyan: '\x1b[36m',
11
+ bold: '\x1b[1m',
12
+ gray: '\x1b[90m',
13
+ };
14
+
15
+ function cmdQuota(args) {
16
+ const quotaDir = path.join(os.homedir(), '.antigravity_cockpit', 'cache', 'quota_api_v1_plugin', 'authorized');
17
+
18
+ if (!fs.existsSync(quotaDir)) {
19
+ console.log(`${C.yellow}⚠️ Cockpit quota cache not found at ${quotaDir}${C.reset}`);
20
+ return;
21
+ }
22
+
23
+ const files = fs.readdirSync(quotaDir).filter(f => f.endsWith('.json'));
24
+ if (files.length === 0) {
25
+ console.log(`${C.yellow}⚠️ No authorized quota files found. Make sure Cockpit extension is logged in.${C.reset}`);
26
+ return;
27
+ }
28
+
29
+ let allAccounts = [];
30
+
31
+ for (const file of files) {
32
+ try {
33
+ const data = JSON.parse(fs.readFileSync(path.join(quotaDir, file), 'utf8'));
34
+ if (data && data.email && data.payload && data.payload.models) {
35
+ allAccounts.push(data);
36
+ }
37
+ } catch (e) {
38
+ // ignore parse error
39
+ }
40
+ }
41
+
42
+ if (allAccounts.length === 0) {
43
+ console.log(`${C.yellow}⚠️ Quota files unreadable.${C.reset}`);
44
+ return;
45
+ }
46
+
47
+ // Sort by most recently updated
48
+ allAccounts.sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0));
49
+
50
+ console.log(`${C.cyan}${C.bold}🚀 AI Model Quota Status (Antigravity Cockpit)${C.reset}\n`);
51
+
52
+ for (const account of allAccounts) {
53
+ const timeAgoMs = Date.now() - (account.updatedAt || Date.now());
54
+ const timeAgoMins = Math.round(Math.max(0, timeAgoMs) / 60000);
55
+ console.log(`${C.bold}📧 Account: ${account.email}${C.reset} ${C.gray}(Updated ${timeAgoMins} mins ago)${C.reset}`);
56
+
57
+ const models = Object.values(account.payload.models).filter(m => m.quotaInfo && m.quotaInfo.remainingFraction !== undefined);
58
+
59
+ if (models.length === 0) {
60
+ console.log(` ${C.gray}No models found with quota info.${C.reset}\n`);
61
+ continue;
62
+ }
63
+
64
+ // Sort by displayName length to pad
65
+ const maxLen = Math.max(...models.map(m => (m.displayName || m.model).length));
66
+
67
+ // Sort models by displayName for consistent viewing
68
+ models.sort((a, b) => (a.displayName || a.model).localeCompare(b.displayName || b.model));
69
+
70
+ for (const m of models) {
71
+ const name = (m.displayName || m.model).padEnd(maxLen);
72
+ const fraction = m.quotaInfo.remainingFraction;
73
+ const pct = (fraction * 100).toFixed(2).padStart(6, ' ') + '%';
74
+
75
+ let color = C.green;
76
+ if (fraction <= 0.2) color = C.red;
77
+ else if (fraction < 0.5) color = C.yellow;
78
+
79
+ let resetTimeStr = '';
80
+ if (m.quotaInfo.resetTime) {
81
+ const resetDate = new Date(m.quotaInfo.resetTime);
82
+ if (resetDate.getTime() > Date.now()) {
83
+ resetTimeStr = ` (Resets: ${resetDate.toLocaleTimeString()})`;
84
+ }
85
+ }
86
+
87
+ console.log(` ${name} : ${color}${pct}${C.reset}${C.gray}${resetTimeStr}${C.reset}`);
88
+ }
89
+ console.log('');
90
+ }
91
+ }
92
+
93
+ module.exports = { cmdQuota };