gooseworks 0.3.7 → 0.3.9
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/dist/agents/claude.d.ts.map +1 -1
- package/dist/agents/claude.js +4 -58
- package/dist/agents/claude.js.map +1 -1
- package/dist/agents/codex.d.ts.map +1 -1
- package/dist/agents/codex.js +4 -58
- package/dist/agents/codex.js.map +1 -1
- package/dist/agents/skill-links.d.ts +18 -0
- package/dist/agents/skill-links.d.ts.map +1 -0
- package/dist/agents/skill-links.js +192 -0
- package/dist/agents/skill-links.js.map +1 -0
- package/dist/commands/install.d.ts.map +1 -1
- package/dist/commands/install.js +16 -2
- package/dist/commands/install.js.map +1 -1
- package/dist/skills/master-skill.d.ts.map +1 -1
- package/dist/skills/master-skill.js +171 -49
- package/dist/skills/master-skill.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../../src/agents/claude.ts"],"names":[],"mappings":"AAQA,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED,wBAAgB,eAAe,IAAI,MAAM,
|
|
1
|
+
{"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../../src/agents/claude.ts"],"names":[],"mappings":"AAQA,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED,wBAAgB,eAAe,IAAI,MAAM,CAOxC;AAED,wBAAgB,YAAY,IAAI,IAAI,CAEnC"}
|
package/dist/agents/claude.js
CHANGED
|
@@ -40,7 +40,7 @@ const fs = __importStar(require("fs"));
|
|
|
40
40
|
const path = __importStar(require("path"));
|
|
41
41
|
const os = __importStar(require("os"));
|
|
42
42
|
const installer_1 = require("../skills/installer");
|
|
43
|
-
const
|
|
43
|
+
const skill_links_1 = require("./skill-links");
|
|
44
44
|
const CLAUDE_SKILLS_DIR = path.join(os.homedir(), '.claude', 'skills');
|
|
45
45
|
function getClaudeSkillsDir() {
|
|
46
46
|
return CLAUDE_SKILLS_DIR;
|
|
@@ -50,64 +50,10 @@ function configureClaude() {
|
|
|
50
50
|
if (!fs.existsSync(skillsBase)) {
|
|
51
51
|
return 0;
|
|
52
52
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
try {
|
|
56
|
-
const existing = fs.readdirSync(CLAUDE_SKILLS_DIR);
|
|
57
|
-
for (const entry of existing) {
|
|
58
|
-
if (!(0, names_1.isManagedGooseworksSkill)(entry))
|
|
59
|
-
continue;
|
|
60
|
-
const target = path.join(CLAUDE_SKILLS_DIR, entry);
|
|
61
|
-
try {
|
|
62
|
-
const stat = fs.lstatSync(target);
|
|
63
|
-
if (stat.isSymbolicLink()) {
|
|
64
|
-
fs.unlinkSync(target);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
catch {
|
|
68
|
-
// Ignore
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
catch {
|
|
73
|
-
// Directory might not exist yet
|
|
74
|
-
}
|
|
75
|
-
// Symlink only what's currently in ~/.agents/skills/
|
|
76
|
-
const skillDirs = fs.readdirSync(skillsBase)
|
|
77
|
-
.filter(names_1.isManagedGooseworksSkill);
|
|
78
|
-
let linked = 0;
|
|
79
|
-
for (const skillDir of skillDirs) {
|
|
80
|
-
const source = path.join(skillsBase, skillDir);
|
|
81
|
-
const target = path.join(CLAUDE_SKILLS_DIR, skillDir);
|
|
82
|
-
try {
|
|
83
|
-
fs.symlinkSync(source, target, 'dir');
|
|
84
|
-
}
|
|
85
|
-
catch {
|
|
86
|
-
// If it somehow still exists, remove and retry
|
|
87
|
-
fs.rmSync(target, { recursive: true, force: true });
|
|
88
|
-
fs.symlinkSync(source, target, 'dir');
|
|
89
|
-
}
|
|
90
|
-
linked++;
|
|
91
|
-
}
|
|
92
|
-
return linked;
|
|
53
|
+
// Cross-platform, privilege-free, resilient linking (GOOSE-2418).
|
|
54
|
+
return (0, skill_links_1.syncManagedSkillLinks)(skillsBase, CLAUDE_SKILLS_DIR).linked;
|
|
93
55
|
}
|
|
94
56
|
function removeClaude() {
|
|
95
|
-
|
|
96
|
-
return;
|
|
97
|
-
const entries = fs.readdirSync(CLAUDE_SKILLS_DIR);
|
|
98
|
-
for (const entry of entries) {
|
|
99
|
-
if (!(0, names_1.isManagedGooseworksSkill)(entry))
|
|
100
|
-
continue;
|
|
101
|
-
const target = path.join(CLAUDE_SKILLS_DIR, entry);
|
|
102
|
-
try {
|
|
103
|
-
const stat = fs.lstatSync(target);
|
|
104
|
-
if (stat.isSymbolicLink()) {
|
|
105
|
-
fs.unlinkSync(target);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
catch {
|
|
109
|
-
// Ignore
|
|
110
|
-
}
|
|
111
|
-
}
|
|
57
|
+
(0, skill_links_1.removeManagedSkillLinks)(CLAUDE_SKILLS_DIR);
|
|
112
58
|
}
|
|
113
59
|
//# sourceMappingURL=claude.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude.js","sourceRoot":"","sources":["../../src/agents/claude.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAQA,gDAEC;AAED,
|
|
1
|
+
{"version":3,"file":"claude.js","sourceRoot":"","sources":["../../src/agents/claude.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAQA,gDAEC;AAED,0CAOC;AAED,oCAEC;AAvBD,uCAAyB;AACzB,2CAA6B;AAC7B,uCAAyB;AACzB,mDAAwD;AACxD,+CAA+E;AAE/E,MAAM,iBAAiB,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;AAEvE,SAAgB,kBAAkB;IAChC,OAAO,iBAAiB,CAAC;AAC3B,CAAC;AAED,SAAgB,eAAe;IAC7B,MAAM,UAAU,GAAG,IAAA,6BAAiB,GAAE,CAAC;IACvC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,OAAO,CAAC,CAAC;IACX,CAAC;IACD,kEAAkE;IAClE,OAAO,IAAA,mCAAqB,EAAC,UAAU,EAAE,iBAAiB,CAAC,CAAC,MAAM,CAAC;AACrE,CAAC;AAED,SAAgB,YAAY;IAC1B,IAAA,qCAAuB,EAAC,iBAAiB,CAAC,CAAC;AAC7C,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codex.d.ts","sourceRoot":"","sources":["../../src/agents/codex.ts"],"names":[],"mappings":"AAUA,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED,wBAAgB,cAAc,IAAI,MAAM,
|
|
1
|
+
{"version":3,"file":"codex.d.ts","sourceRoot":"","sources":["../../src/agents/codex.ts"],"names":[],"mappings":"AAUA,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED,wBAAgB,cAAc,IAAI,MAAM,CAOvC;AAED,wBAAgB,WAAW,IAAI,IAAI,CAElC;AAwCD;;;;GAIG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAiB3C"}
|
package/dist/agents/codex.js
CHANGED
|
@@ -42,7 +42,7 @@ const path = __importStar(require("path"));
|
|
|
42
42
|
const os = __importStar(require("os"));
|
|
43
43
|
const credentials_1 = require("../auth/credentials");
|
|
44
44
|
const installer_1 = require("../skills/installer");
|
|
45
|
-
const
|
|
45
|
+
const skill_links_1 = require("./skill-links");
|
|
46
46
|
const CODEX_SKILLS_DIR = path.join(os.homedir(), '.codex', 'skills');
|
|
47
47
|
const CODEX_CONFIG_PATH = path.join(os.homedir(), '.codex', 'config.toml');
|
|
48
48
|
function getCodexSkillsDir() {
|
|
@@ -53,65 +53,11 @@ function configureCodex() {
|
|
|
53
53
|
if (!fs.existsSync(skillsBase)) {
|
|
54
54
|
return 0;
|
|
55
55
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
try {
|
|
59
|
-
const existing = fs.readdirSync(CODEX_SKILLS_DIR);
|
|
60
|
-
for (const entry of existing) {
|
|
61
|
-
if (!(0, names_1.isManagedGooseworksSkill)(entry))
|
|
62
|
-
continue;
|
|
63
|
-
const target = path.join(CODEX_SKILLS_DIR, entry);
|
|
64
|
-
try {
|
|
65
|
-
const stat = fs.lstatSync(target);
|
|
66
|
-
if (stat.isSymbolicLink()) {
|
|
67
|
-
fs.unlinkSync(target);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
catch {
|
|
71
|
-
// Ignore
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
catch {
|
|
76
|
-
// Directory might not exist yet
|
|
77
|
-
}
|
|
78
|
-
// Symlink only what's currently in ~/.agents/skills/
|
|
79
|
-
const skillDirs = fs.readdirSync(skillsBase)
|
|
80
|
-
.filter(names_1.isManagedGooseworksSkill);
|
|
81
|
-
let linked = 0;
|
|
82
|
-
for (const skillDir of skillDirs) {
|
|
83
|
-
const source = path.join(skillsBase, skillDir);
|
|
84
|
-
const target = path.join(CODEX_SKILLS_DIR, skillDir);
|
|
85
|
-
try {
|
|
86
|
-
fs.symlinkSync(source, target, 'dir');
|
|
87
|
-
}
|
|
88
|
-
catch {
|
|
89
|
-
// If it somehow still exists, remove and retry
|
|
90
|
-
fs.rmSync(target, { recursive: true, force: true });
|
|
91
|
-
fs.symlinkSync(source, target, 'dir');
|
|
92
|
-
}
|
|
93
|
-
linked++;
|
|
94
|
-
}
|
|
95
|
-
return linked;
|
|
56
|
+
// Cross-platform, privilege-free, resilient linking (GOOSE-2418).
|
|
57
|
+
return (0, skill_links_1.syncManagedSkillLinks)(skillsBase, CODEX_SKILLS_DIR).linked;
|
|
96
58
|
}
|
|
97
59
|
function removeCodex() {
|
|
98
|
-
|
|
99
|
-
return;
|
|
100
|
-
const entries = fs.readdirSync(CODEX_SKILLS_DIR);
|
|
101
|
-
for (const entry of entries) {
|
|
102
|
-
if (!(0, names_1.isManagedGooseworksSkill)(entry))
|
|
103
|
-
continue;
|
|
104
|
-
const target = path.join(CODEX_SKILLS_DIR, entry);
|
|
105
|
-
try {
|
|
106
|
-
const stat = fs.lstatSync(target);
|
|
107
|
-
if (stat.isSymbolicLink()) {
|
|
108
|
-
fs.unlinkSync(target);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
catch {
|
|
112
|
-
// Ignore
|
|
113
|
-
}
|
|
114
|
-
}
|
|
60
|
+
(0, skill_links_1.removeManagedSkillLinks)(CODEX_SKILLS_DIR);
|
|
115
61
|
}
|
|
116
62
|
function normalizeMcpUrl(base) {
|
|
117
63
|
const stripped = base.replace(/\/$/, '');
|
package/dist/agents/codex.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codex.js","sourceRoot":"","sources":["../../src/agents/codex.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAUA,8CAEC;AAED,
|
|
1
|
+
{"version":3,"file":"codex.js","sourceRoot":"","sources":["../../src/agents/codex.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAUA,8CAEC;AAED,wCAOC;AAED,kCAEC;AA6CD,8CAiBC;AAvFD,uCAAyB;AACzB,2CAA6B;AAC7B,uCAAyB;AACzB,qDAAqD;AACrD,mDAAwD;AACxD,+CAA+E;AAE/E,MAAM,gBAAgB,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;AACrE,MAAM,iBAAiB,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC;AAE3E,SAAgB,iBAAiB;IAC/B,OAAO,gBAAgB,CAAC;AAC1B,CAAC;AAED,SAAgB,cAAc;IAC5B,MAAM,UAAU,GAAG,IAAA,6BAAiB,GAAE,CAAC;IACvC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,OAAO,CAAC,CAAC;IACX,CAAC;IACD,kEAAkE;IAClE,OAAO,IAAA,mCAAqB,EAAC,UAAU,EAAE,gBAAgB,CAAC,CAAC,MAAM,CAAC;AACpE,CAAC;AAED,SAAgB,WAAW;IACzB,IAAA,qCAAuB,EAAC,gBAAgB,CAAC,CAAC;AAC5C,CAAC;AAED,SAAS,eAAe,CAAC,IAAY;IACnC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACzC,OAAO,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,MAAM,CAAC;AAClE,CAAC;AAED,SAAS,UAAU,CAAC,KAAa;IAC/B,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;AAC/B,CAAC;AAED,SAAS,eAAe;IACtB,IAAI,CAAC;QACH,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,iBAAiB,CAAC;YAAE,OAAO,EAAE,CAAC;QACjD,OAAO,EAAE,CAAC,YAAY,CAAC,iBAAiB,EAAE,OAAO,CAAC,CAAC;IACrD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,SAAS,cAAc,CAAC,GAAW,EAAE,SAAiB;IACpD,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACjC,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,IAAI,QAAQ,GAAG,KAAK,CAAC;IAErB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;QAClD,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YAC9B,QAAQ,GAAG,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,SAAS,GAAG,CAAC,CAAC;QACpE,CAAC;QAED,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,OAAO,EAAE,CAAC;AAC9D,CAAC;AAED;;;;GAIG;AACH,SAAgB,iBAAiB;IAC/B,MAAM,KAAK,GAAG,IAAA,4BAAc,GAAE,CAAC;IAC/B,IAAI,CAAC,KAAK,EAAE,cAAc,IAAI,CAAC,KAAK,CAAC,OAAO;QAAE,OAAO,KAAK,CAAC;IAE3D,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAClD,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE7C,MAAM,QAAQ,GAAG,cAAc,CAAC,eAAe,EAAE,EAAE,wBAAwB,CAAC,CAAC;IAC7E,MAAM,KAAK,GAAG;QACZ,0BAA0B;QAC1B,SAAS,UAAU,CAAC,eAAe,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,EAAE;QAC5D,oCAAoC,UAAU,CAAC,UAAU,KAAK,CAAC,OAAO,EAAE,CAAC,IAAI;KAC9E,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACb,MAAM,IAAI,GAAG,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,IAAI,CAAC;IAE9D,EAAE,CAAC,aAAa,CAAC,iBAAiB,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC3D,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Drop every managed link under `targetDir` (leaving real directories alone). */
|
|
2
|
+
export declare function removeManagedSkillLinks(targetDir: string): void;
|
|
3
|
+
/**
|
|
4
|
+
* Link every managed skill under `skillsBase` into `targetDir`.
|
|
5
|
+
*
|
|
6
|
+
* - win32 → directory JUNCTIONS (no admin / Developer Mode needed).
|
|
7
|
+
* - a link already resolving to the wanted source is PRESERVED, so a manually
|
|
8
|
+
* repaired junction survives repeated `install` / `update` runs.
|
|
9
|
+
* - a per-skill failure is warned (with an actionable Windows hint) and
|
|
10
|
+
* skipped, never thrown — the caller must still reach MCP registration.
|
|
11
|
+
*
|
|
12
|
+
* Returns { linked, failed }; never throws.
|
|
13
|
+
*/
|
|
14
|
+
export declare function syncManagedSkillLinks(skillsBase: string, targetDir: string): {
|
|
15
|
+
linked: number;
|
|
16
|
+
failed: number;
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=skill-links.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-links.d.ts","sourceRoot":"","sources":["../../src/agents/skill-links.ts"],"names":[],"mappings":"AAwEA,kFAAkF;AAClF,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAa/D;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,GAChB;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAkDpC"}
|
|
@@ -0,0 +1,192 @@
|
|
|
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.removeManagedSkillLinks = removeManagedSkillLinks;
|
|
37
|
+
exports.syncManagedSkillLinks = syncManagedSkillLinks;
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const path = __importStar(require("path"));
|
|
40
|
+
const names_1 = require("../skills/names");
|
|
41
|
+
const logger = __importStar(require("../utils/logger"));
|
|
42
|
+
/**
|
|
43
|
+
* Cross-platform skill linking (GOOSE-2418).
|
|
44
|
+
*
|
|
45
|
+
* On Windows, `fs.symlinkSync(src, dst, 'dir')` requires Administrator rights or
|
|
46
|
+
* Developer Mode — a fresh, non-elevated user hits EPERM, the exception aborts
|
|
47
|
+
* `install`, and the GooseWorks MCP server never gets registered. Directory
|
|
48
|
+
* JUNCTIONS need no privileges and Claude Code / Codex follow them identically,
|
|
49
|
+
* so we use junctions on win32 and dir symlinks everywhere else. Every operation
|
|
50
|
+
* here is best-effort and NEVER throws to its caller — a single bad link must not
|
|
51
|
+
* take down MCP registration.
|
|
52
|
+
*/
|
|
53
|
+
const LINK_TYPE = process.platform === 'win32' ? 'junction' : 'dir';
|
|
54
|
+
/**
|
|
55
|
+
* True when `target` is one of OUR links (a POSIX symlink or a Windows junction),
|
|
56
|
+
* not a real directory. `readlinkSync` succeeds on both link kinds and throws on
|
|
57
|
+
* a regular file/dir — unlike `lstat().isSymbolicLink()`, which reports `false`
|
|
58
|
+
* for junctions and so can't be trusted on Windows.
|
|
59
|
+
*/
|
|
60
|
+
function isLink(target) {
|
|
61
|
+
try {
|
|
62
|
+
fs.readlinkSync(target);
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Does `target` already resolve to `source`? A real directory resolves to
|
|
71
|
+
* itself, so this is false for a non-link — safe to use as a "keep this link"
|
|
72
|
+
* check without risking a match on a user's own folder.
|
|
73
|
+
*/
|
|
74
|
+
function resolvesTo(target, source) {
|
|
75
|
+
try {
|
|
76
|
+
return path.resolve(fs.realpathSync(target)) === path.resolve(source);
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Remove a link/junction WITHOUT ever deleting its target contents or a real
|
|
84
|
+
* directory. Callers gate on `isLink()` first. `unlinkSync` handles POSIX
|
|
85
|
+
* symlinks; a Windows junction is a directory reparse point, removed with
|
|
86
|
+
* `rmdirSync` (which drops the junction, not the linked folder).
|
|
87
|
+
*/
|
|
88
|
+
function removeLink(target) {
|
|
89
|
+
try {
|
|
90
|
+
fs.unlinkSync(target);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
// not a plain symlink (e.g. a win32 junction) — fall through
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
fs.rmdirSync(target);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
// last resort
|
|
102
|
+
}
|
|
103
|
+
try {
|
|
104
|
+
fs.rmSync(target, { recursive: false, force: true });
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
// ignore — nothing more we can safely do
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/** Drop every managed link under `targetDir` (leaving real directories alone). */
|
|
111
|
+
function removeManagedSkillLinks(targetDir) {
|
|
112
|
+
if (!fs.existsSync(targetDir))
|
|
113
|
+
return;
|
|
114
|
+
let entries = [];
|
|
115
|
+
try {
|
|
116
|
+
entries = fs.readdirSync(targetDir);
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
for (const entry of entries) {
|
|
122
|
+
if (!(0, names_1.isManagedGooseworksSkill)(entry))
|
|
123
|
+
continue;
|
|
124
|
+
const target = path.join(targetDir, entry);
|
|
125
|
+
if (isLink(target))
|
|
126
|
+
removeLink(target);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Link every managed skill under `skillsBase` into `targetDir`.
|
|
131
|
+
*
|
|
132
|
+
* - win32 → directory JUNCTIONS (no admin / Developer Mode needed).
|
|
133
|
+
* - a link already resolving to the wanted source is PRESERVED, so a manually
|
|
134
|
+
* repaired junction survives repeated `install` / `update` runs.
|
|
135
|
+
* - a per-skill failure is warned (with an actionable Windows hint) and
|
|
136
|
+
* skipped, never thrown — the caller must still reach MCP registration.
|
|
137
|
+
*
|
|
138
|
+
* Returns { linked, failed }; never throws.
|
|
139
|
+
*/
|
|
140
|
+
function syncManagedSkillLinks(skillsBase, targetDir) {
|
|
141
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
142
|
+
let sources = [];
|
|
143
|
+
try {
|
|
144
|
+
sources = fs.readdirSync(skillsBase).filter(names_1.isManagedGooseworksSkill);
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
return { linked: 0, failed: 0 };
|
|
148
|
+
}
|
|
149
|
+
const wanted = new Map(sources.map((s) => [s, path.join(skillsBase, s)]));
|
|
150
|
+
// Drop stale managed links, but KEEP any that already point at a wanted source
|
|
151
|
+
// (so a working junction a Windows user repaired by hand isn't destroyed).
|
|
152
|
+
try {
|
|
153
|
+
for (const entry of fs.readdirSync(targetDir)) {
|
|
154
|
+
if (!(0, names_1.isManagedGooseworksSkill)(entry))
|
|
155
|
+
continue;
|
|
156
|
+
const target = path.join(targetDir, entry);
|
|
157
|
+
const source = wanted.get(entry);
|
|
158
|
+
if (source && resolvesTo(target, source))
|
|
159
|
+
continue;
|
|
160
|
+
if (isLink(target))
|
|
161
|
+
removeLink(target);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
// targetDir race — links are (re)created below regardless
|
|
166
|
+
}
|
|
167
|
+
let linked = 0;
|
|
168
|
+
let failed = 0;
|
|
169
|
+
for (const [skillDir, source] of wanted) {
|
|
170
|
+
const target = path.join(targetDir, skillDir);
|
|
171
|
+
if (resolvesTo(target, source)) {
|
|
172
|
+
linked++; // already correct — preserved, no privilege needed
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
try {
|
|
176
|
+
if (isLink(target))
|
|
177
|
+
removeLink(target);
|
|
178
|
+
fs.symlinkSync(source, target, LINK_TYPE);
|
|
179
|
+
linked++;
|
|
180
|
+
}
|
|
181
|
+
catch (err) {
|
|
182
|
+
failed++;
|
|
183
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
184
|
+
logger.warn(`Could not link skill "${skillDir}" into ${targetDir}: ${msg}` +
|
|
185
|
+
(process.platform === 'win32'
|
|
186
|
+
? ' — enable Windows Developer Mode or run the terminal as Administrator, then re-run `gooseworks install`.'
|
|
187
|
+
: ''));
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return { linked, failed };
|
|
191
|
+
}
|
|
192
|
+
//# sourceMappingURL=skill-links.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-links.js","sourceRoot":"","sources":["../../src/agents/skill-links.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyEA,0DAaC;AAaD,sDAqDC;AAxJD,uCAAyB;AACzB,2CAA6B;AAC7B,2CAA2D;AAC3D,wDAA0C;AAE1C;;;;;;;;;;GAUG;AACH,MAAM,SAAS,GAAuB,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC;AAExF;;;;;GAKG;AACH,SAAS,MAAM,CAAC,MAAc;IAC5B,IAAI,CAAC;QACH,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;QACxB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,SAAS,UAAU,CAAC,MAAc,EAAE,MAAc;IAChD,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACxE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,SAAS,UAAU,CAAC,MAAc;IAChC,IAAI,CAAC;QACH,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;QACtB,OAAO;IACT,CAAC;IAAC,MAAM,CAAC;QACP,6DAA6D;IAC/D,CAAC;IACD,IAAI,CAAC;QACH,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QACrB,OAAO;IACT,CAAC;IAAC,MAAM,CAAC;QACP,cAAc;IAChB,CAAC;IACD,IAAI,CAAC;QACH,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACvD,CAAC;IAAC,MAAM,CAAC;QACP,yCAAyC;IAC3C,CAAC;AACH,CAAC;AAED,kFAAkF;AAClF,SAAgB,uBAAuB,CAAC,SAAiB;IACvD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC;QAAE,OAAO;IACtC,IAAI,OAAO,GAAa,EAAE,CAAC;IAC3B,IAAI,CAAC;QACH,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO;IACT,CAAC;IACD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,CAAC,IAAA,gCAAwB,EAAC,KAAK,CAAC;YAAE,SAAS;QAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;QAC3C,IAAI,MAAM,CAAC,MAAM,CAAC;YAAE,UAAU,CAAC,MAAM,CAAC,CAAC;IACzC,CAAC;AACH,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAgB,qBAAqB,CACnC,UAAkB,EAClB,SAAiB;IAEjB,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE7C,IAAI,OAAO,GAAa,EAAE,CAAC;IAC3B,IAAI,CAAC;QACH,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,gCAAwB,CAAC,CAAC;IACxE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;IAClC,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAE1E,+EAA+E;IAC/E,2EAA2E;IAC3E,IAAI,CAAC;QACH,KAAK,MAAM,KAAK,IAAI,EAAE,CAAC,WAAW,CAAC,SAAS,CAAC,EAAE,CAAC;YAC9C,IAAI,CAAC,IAAA,gCAAwB,EAAC,KAAK,CAAC;gBAAE,SAAS;YAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;YAC3C,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YACjC,IAAI,MAAM,IAAI,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC;gBAAE,SAAS;YACnD,IAAI,MAAM,CAAC,MAAM,CAAC;gBAAE,UAAU,CAAC,MAAM,CAAC,CAAC;QACzC,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,0DAA0D;IAC5D,CAAC;IAED,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,KAAK,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACxC,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;QAC9C,IAAI,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;YAC/B,MAAM,EAAE,CAAC,CAAC,mDAAmD;YAC7D,SAAS;QACX,CAAC;QACD,IAAI,CAAC;YACH,IAAI,MAAM,CAAC,MAAM,CAAC;gBAAE,UAAU,CAAC,MAAM,CAAC,CAAC;YACvC,EAAE,CAAC,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;YAC1C,MAAM,EAAE,CAAC;QACX,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,EAAE,CAAC;YACT,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC7D,MAAM,CAAC,IAAI,CACT,yBAAyB,QAAQ,UAAU,SAAS,KAAK,GAAG,EAAE;gBAC5D,CAAC,OAAO,CAAC,QAAQ,KAAK,OAAO;oBAC3B,CAAC,CAAC,0GAA0G;oBAC5G,CAAC,CAAC,EAAE,CAAC,CACV,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AAC5B,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../src/commands/install.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAuBpC,wBAAgB,oBAAoB,IAAI,OAAO,
|
|
1
|
+
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../src/commands/install.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAuBpC,wBAAgB,oBAAoB,IAAI,OAAO,CAoK9C;AAED,eAAO,MAAM,cAAc,SAAyB,CAAC"}
|
package/dist/commands/install.js
CHANGED
|
@@ -105,7 +105,15 @@ Examples:
|
|
|
105
105
|
for (const agent of targetAgents) {
|
|
106
106
|
if (agent === 'claude') {
|
|
107
107
|
logger.info('Creating symlinks in ~/.claude/skills/');
|
|
108
|
-
|
|
108
|
+
// Skill linking must never abort MCP registration — on Windows a symlink
|
|
109
|
+
// privilege error used to throw here and skip MCP entirely (GOOSE-2418).
|
|
110
|
+
try {
|
|
111
|
+
(0, claude_1.configureClaude)();
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
logger.warn(`Skill linking failed: ${err instanceof Error ? err.message : String(err)}. ` +
|
|
115
|
+
'Continuing — the MCP server will still be registered.');
|
|
116
|
+
}
|
|
109
117
|
if (wantMcp) {
|
|
110
118
|
if ((0, claude_mcp_1.configureClaudeMcp)()) {
|
|
111
119
|
logger.success("Registered 'gooseworks' MCP server in ~/.claude.json");
|
|
@@ -127,7 +135,13 @@ Examples:
|
|
|
127
135
|
}
|
|
128
136
|
if (agent === 'codex') {
|
|
129
137
|
logger.info('Creating symlinks in ~/.codex/skills/');
|
|
130
|
-
|
|
138
|
+
try {
|
|
139
|
+
(0, codex_1.configureCodex)();
|
|
140
|
+
}
|
|
141
|
+
catch (err) {
|
|
142
|
+
logger.warn(`Skill linking failed: ${err instanceof Error ? err.message : String(err)}. ` +
|
|
143
|
+
'Continuing — the MCP server will still be registered.');
|
|
144
|
+
}
|
|
131
145
|
if (wantMcp) {
|
|
132
146
|
if ((0, codex_1.configureCodexMcp)()) {
|
|
133
147
|
logger.success("Registered 'gooseworks' MCP server in ~/.codex/config.toml");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/commands/install.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuBA,
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/commands/install.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
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,
|
|
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,CAsUhD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,CAyQlD"}
|
|
@@ -226,7 +226,7 @@ description: >
|
|
|
226
226
|
app uses) — credits are reserved and billed server-side. Analytics recipes are fetched from
|
|
227
227
|
goose-skills on demand.
|
|
228
228
|
category: ads
|
|
229
|
-
version: 2.
|
|
229
|
+
version: 2.2.0
|
|
230
230
|
author: GooseWorks
|
|
231
231
|
tags: [gooseworks, ads, remix, static-ad, brand, creative, image, analytics, meta-ads, performance]
|
|
232
232
|
---
|
|
@@ -309,6 +309,29 @@ decide — fine, but prefer sending the app defaults for predictable parity.
|
|
|
309
309
|
the template; \`"edit"\` makes a targeted change to a specific render (\`prompt\` + \`source_render_id\`
|
|
310
310
|
required); \`"exact"\` runs \`prompt\` verbatim against that render's references. Returns a
|
|
311
311
|
single-item batch — poll it with \`get_remix_batch\`.
|
|
312
|
+
- \`set_creative_feedback { render_id, rating?, comment?, reasons? }\` — record the user's reaction
|
|
313
|
+
to a generated image (the SAME happy/neutral/sad + comment + reason chips the app captures). Use
|
|
314
|
+
it whenever the user reacts ("love this one" / "the logo is wrong"). \`render_id\` is a RENDER id
|
|
315
|
+
from \`get_remix_batch\` / \`list_brand_creatives\`, not a project/batch id. \`reasons\` are quick
|
|
316
|
+
chips (wrong_product, brand_or_logo_wrong, off_brand, text_garbled, weak_copy, ai_or_distorted).
|
|
317
|
+
|
|
318
|
+
### Plan mode — review the plan BEFORE generating (optional)
|
|
319
|
+
|
|
320
|
+
For users who want to approve each ad's plan before spending credits (the app's "Plan it" flow):
|
|
321
|
+
|
|
322
|
+
- \`submit_remix_batch { ..., requires_approval: true }\` — composes each creative's plan and PAUSES.
|
|
323
|
+
**No credits are reserved and no image renders** until you approve.
|
|
324
|
+
- \`list_ad_approvals { brand_id? }\` — poll this; returns \`{ items, counts }\`. While a creative is
|
|
325
|
+
\`composing\`, wait; once \`awaiting_approval\`, show its \`plan\` (composed prompt + refs + quality)
|
|
326
|
+
to the user.
|
|
327
|
+
- \`revise_ad_plan { project_id, message?, variant_label? }\` — recompose from a chat steer, still
|
|
328
|
+
free. Poll \`list_ad_approvals\` until it's \`awaiting_approval\` again.
|
|
329
|
+
- \`approve_ad_plan { project_id | batch_id }\` — approve ONE creative (\`project_id\`) or the whole
|
|
330
|
+
batch (\`batch_id\`). **This is the step that reserves credits and renders.** Then poll
|
|
331
|
+
\`get_remix_batch\` and hand back links as usual.
|
|
332
|
+
|
|
333
|
+
Only offer plan mode when the user asks to review/approve first — the default path generates
|
|
334
|
+
immediately.
|
|
312
335
|
|
|
313
336
|
## Reading the brand & picking inputs (still MCP, read-only)
|
|
314
337
|
|
|
@@ -327,6 +350,33 @@ decide — fine, but prefer sending the app defaults for predictable parity.
|
|
|
327
350
|
image as a private template, then remix it like any other.
|
|
328
351
|
- \`get_ad_project\` / \`append_project_message\` — inspect a creative / leave a note on its thread.
|
|
329
352
|
|
|
353
|
+
## Keep the brand kit in sync — reconcile, then update (ASK first)
|
|
354
|
+
|
|
355
|
+
The brand kit is the source of truth every generation reads. During ANY task, when the user
|
|
356
|
+
**tells you something about the brand or asks to change something brand-level** — a different
|
|
357
|
+
tagline, audience, voice, a product's name/price/description, "our logo is X", "we don't sell Y
|
|
358
|
+
anymore", a new product photo — treat it as a possible kit update, don't just use it for this one
|
|
359
|
+
ad and forget it:
|
|
360
|
+
|
|
361
|
+
1. **Check it against the kit.** \`get_brand_kit { brand_id }\` and see whether what the user said
|
|
362
|
+
matches, is missing from, or contradicts the kit.
|
|
363
|
+
2. **If it's already in the kit and matches** — nothing to do; proceed.
|
|
364
|
+
3. **If it's new or different — ASK before writing.** Confirm in one line: *"Want me to update
|
|
365
|
+
the brand kit so this sticks for future ads?"* Only persist on a yes (or when the user clearly
|
|
366
|
+
asked you to change the brand). Don't silently mutate the kit, and don't nag on trivia.
|
|
367
|
+
4. **Persist with the write tools** (partial — only the fields you pass are touched; each edit is
|
|
368
|
+
recorded as a user override that later re-research won't clobber):
|
|
369
|
+
- \`update_brand_kit { brand_id, description?, audience?, voice?, instructions?, brand_type?,
|
|
370
|
+
value_props?, primary_color?, accent_color? }\` — the structured kit fields.
|
|
371
|
+
- \`upsert_brand_product { brand_id, ... }\` / \`delete_brand_product\` — manage products.
|
|
372
|
+
- \`add_brand_product_image { brand_id, ... }\` / \`remove_brand_reference_image\` — product /
|
|
373
|
+
reference photos.
|
|
374
|
+
5. **Confirm what changed** and continue the task. (Logo, colors, and fonts are owned by the
|
|
375
|
+
backend research pass — prefer \`update_ad_brand\` / the research flow for those, not free text.)
|
|
376
|
+
|
|
377
|
+
This is the parity gap the app closes in-product: a brand fact the user gives mid-task should be
|
|
378
|
+
able to flow back into the kit — with their ok — instead of being lost.
|
|
379
|
+
|
|
330
380
|
## Picking templates — ASK the user; don't freelance from the catalog
|
|
331
381
|
|
|
332
382
|
When the user wants to make ads but has NOT named a specific template (id/slug/Community
|
|
@@ -393,10 +443,49 @@ Then poll with \`get_remix_batch\` and hand back the links, same as above.
|
|
|
393
443
|
|
|
394
444
|
## Brand research
|
|
395
445
|
|
|
396
|
-
|
|
397
|
-
\`
|
|
398
|
-
|
|
399
|
-
|
|
446
|
+
Prefer the backend's result: \`get_brand_kit { brand_id }\`. If \`researchStatus\` is
|
|
447
|
+
\`complete\`, REUSE it — never re-research.
|
|
448
|
+
|
|
449
|
+
**The split — backend owns visuals, you own the qualitative depth:**
|
|
450
|
+
|
|
451
|
+
- **Backend LIGHT pass (automatic).** \`create_ad_brand\` with a \`website_url\` kicks off the same
|
|
452
|
+
backend research the web app uses, in \`mode: "light"\`: it resolves the **authoritative logo,
|
|
453
|
+
colors, and fonts** (Brandfetch + context.dev) plus a baseline kit, then flips
|
|
454
|
+
\`research_status\` to \`complete\` — usually under a minute. You can't reproduce those visual
|
|
455
|
+
signals locally, so **never re-derive logo/colors/fonts.** (Web onboarding via \`/api/ads/onboard\`
|
|
456
|
+
runs the full thing; nothing to do but read it.)
|
|
457
|
+
- **Your DEEP pass (local, agentic).** You add the qualitative depth the light pass leaves thin —
|
|
458
|
+
positioning, audience segments, voice, brandType, value props, proof points, products — grounded
|
|
459
|
+
on the actual site.
|
|
460
|
+
|
|
461
|
+
**CLI brand-research flow:**
|
|
462
|
+
|
|
463
|
+
1. \`create_ad_brand { name, website_url }\` → keep \`brand_id\` + \`slug\`. The brand comes back with
|
|
464
|
+
\`research_status: "pending"\` (light pass in flight).
|
|
465
|
+
2. **Wait for the backend light pass:** poll \`get_brand_kit { brand_id }\` until \`researchStatus\`
|
|
466
|
+
is \`complete\` (usually <60s). Now the kit has authoritative logo/colors/fonts + a baseline.
|
|
467
|
+
At this point generation is already unblocked — but do the deep pass to make it good.
|
|
468
|
+
3. **Deep research locally:** \`gooseworks fetch brand-research\` and follow its phases. **Ground
|
|
469
|
+
every fact on the fetched site** — if the site can't be read, say so and ask the user; never
|
|
470
|
+
guess a category from the brand name alone.
|
|
471
|
+
4. **Write the pack** with \`write_file\` under \`agent-config/brands/<slug>/\`:
|
|
472
|
+
- the \`brand-research/*.md\` docs + \`brand-assets/manifest.json\` (human-readable pack), AND
|
|
473
|
+
- \`brand-research/kit-patch.json\` — the STRUCTURED fields the web UI renders. Field-for-field
|
|
474
|
+
contract; only what you put here reaches the kit. Shape:
|
|
475
|
+
\`{ positioning?: string, audience?: string, voice?: string, brandType?: string, tagline?: string, valueProps?: string[], proofPoints?: string[], products?: [{ name, description?, link?, pricing?, imageUrls?: string[] }] }\`
|
|
476
|
+
(\`brandType\` ∈ product | saas | service | agency | restaurant | fashion | beauty | fitness |
|
|
477
|
+
finance | education | health). Only URLs already in our storage for product images.
|
|
478
|
+
- **Do NOT set logo / colors / fonts here** — the backend light pass already owns those.
|
|
479
|
+
5. **Persist it:** \`finalize_brand_research { brand_id }\` merges \`kit-patch.json\` into the kit
|
|
480
|
+
NON-CLOBBERINGLY (it will NOT overwrite the backend's visuals or any user edit), then re-confirms
|
|
481
|
+
\`research_status: complete\`.
|
|
482
|
+
6. **Verify:** \`get_brand_kit { brand_id }\` — confirm the qualitative fields you wrote are present
|
|
483
|
+
before generating.
|
|
484
|
+
|
|
485
|
+
**If the brand has NO website**, the backend light pass can't run (nothing to fetch) — do the whole
|
|
486
|
+
thing locally (steps 3–6) and finalize; an un-finalized brand has no kit for generation and leaves
|
|
487
|
+
no artifact to debug a wrong run (this is how a bad local classification, e.g. mislabelling a SaaS
|
|
488
|
+
as a "drink company", used to vanish without a trace).
|
|
400
489
|
|
|
401
490
|
## Analyze / intelligence (fetched recipes — NOT generation)
|
|
402
491
|
|
|
@@ -436,6 +525,15 @@ run through the \`gooseworks\` CLI (\`gooseworks fetch\` / \`gooseworks call\`),
|
|
|
436
525
|
Generate when they paste the app's copyable remix prompt back (or submit the surprise picks
|
|
437
526
|
directly if they'd rather not review).
|
|
438
527
|
- **Ask the styling** — Keep original (default) vs Match brand — before you submit.
|
|
528
|
+
- **Reconcile brand facts into the kit** — when the user states or changes something brand-level
|
|
529
|
+
mid-task, check it against \`get_brand_kit\` and, with their ok, persist it via \`update_brand_kit\`
|
|
530
|
+
/ \`upsert_brand_product\` / \`add_brand_product_image\` so it sticks for future ads. Ask first;
|
|
531
|
+
never silently mutate the kit.
|
|
532
|
+
- **Record feedback** — when the user reacts to a generated image, capture it with
|
|
533
|
+
\`set_creative_feedback { render_id, rating/comment/reasons }\` so the quality loop learns.
|
|
534
|
+
- **Plan mode is opt-in** — only compose-and-pause (\`requires_approval: true\` → \`list_ad_approvals\`
|
|
535
|
+
→ \`approve_ad_plan\`) when the user wants to review before spending credits; otherwise generate
|
|
536
|
+
immediately.
|
|
439
537
|
- **Don't busy-loop** — poll \`get_remix_batch\` on a sensible interval (~20-30s); a \`queued\`
|
|
440
538
|
batch is waiting on research and will start on its own.
|
|
441
539
|
`;
|
|
@@ -464,7 +562,7 @@ description: >
|
|
|
464
562
|
template, or asks to remix a video ad. Unlike goose-ads (static images, generated server-side),
|
|
465
563
|
video renders locally and reports progress + the result back through the gooseworks MCP tools.
|
|
466
564
|
category: ads
|
|
467
|
-
version: 0.
|
|
565
|
+
version: 0.2.0
|
|
468
566
|
author: GooseWorks
|
|
469
567
|
tags: [gooseworks, ads, video, remix, imessage, local-render, byoa]
|
|
470
568
|
---
|
|
@@ -528,38 +626,34 @@ id). The app NEVER runs you — it is the viewer + review surface; you are the r
|
|
|
528
626
|
never re-research. If not, run brand research first (\`gooseworks fetch brand-research\`,
|
|
529
627
|
follow it, then \`finalize_brand_research { brand_id }\`) before continuing.
|
|
530
628
|
|
|
531
|
-
## Step 2 —
|
|
629
|
+
## Step 2 — read the template's recipe (it carries everything; NO hardcoded format map)
|
|
532
630
|
|
|
533
|
-
The
|
|
534
|
-
|
|
535
|
-
|
|
631
|
+
The ad format is a **template (data) in the ad_sample DB**, not a per-format skill.
|
|
632
|
+
\`get_ad_template(source_sample_id)\` returns the template's \`recipe\` — a self-contained brief you
|
|
633
|
+
read and execute. **Do NOT map \`format\` to a hardcoded recipe slug** (there is no such table):
|
|
536
634
|
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
| \`ugc-street-testimonial\` | \`remix-ugc-street-testimonial-from-sample\` | create-ugc-street-testimonial-video-from-refs, create-video-seedance-2-fal, create-image-gpt-image-fal, watch |
|
|
548
|
-
|
|
549
|
-
(photo-grid + music-video coming.) Pack skills are fetchable individually by slug but do NOT
|
|
550
|
-
auto-resolve dependencies (no \`dependencySkills\`), so \`gooseworks fetch\` the recipe **and** each
|
|
551
|
-
skill in its row — e.g. for iMessage:
|
|
635
|
+
- \`recipe.format\` — the format label (e.g. \`vignette\`), for display only.
|
|
636
|
+
- \`recipe.atoms\` — the **capabilities** this template composes (e.g. \`create-video-seedance-2-fal\`,
|
|
637
|
+
\`create-image-gpt-image-fal\`, \`review-ugc-render\`, \`watch\`). \`gooseworks fetch <name>\` each — they
|
|
638
|
+
live in \`skills/ads/capabilities/\` and are reused across templates (so they cache).
|
|
639
|
+
- \`recipe.instructions\` — the **playbook** to follow: \`instructions.inline\` prose, or
|
|
640
|
+
\`instructions.doc_url\` (an S3 markdown doc — fetch it).
|
|
641
|
+
- \`recipe.config\` — every param (prompts, layout, timings, palette, model choices).
|
|
642
|
+
- \`recipe.inputs\` — the brand-asset contract (which product / logo / offer this template needs).
|
|
643
|
+
- \`recipe.assets\` — reference material as S3 links (reference render, style guide, example frames) —
|
|
644
|
+
fetch as needed.
|
|
552
645
|
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
646
|
+
Runtime: **read the recipe → \`gooseworks fetch\` each capability in \`recipe.atoms\` → follow
|
|
647
|
+
\`recipe.instructions\` with \`recipe.config\` + the brand's bound \`inputs\`.** The template IS the recipe;
|
|
648
|
+
there is no \`format → recipe-slug\` table and no per-format skill to fetch.
|
|
649
|
+
|
|
650
|
+
Save each fetched capability's scripts + files under \`/tmp/gooseworks-scripts/<name>/\`. If a capability
|
|
651
|
+
is a Node package (a phone-mockup renderer), \`npm install\` in its folder so its \`generate.js\` +
|
|
652
|
+
Playwright resolve, and point the recorder's \`NODE_PATH\` at it.
|
|
557
653
|
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
\`npm install\` in its folder so its \`generate.js\` + Playwright resolve, and point the recorder's
|
|
562
|
-
\`NODE_PATH\` at it.
|
|
654
|
+
> **Migration note:** older phone-mockup formats (\`imessage\` / \`chatgpt\` / \`apple-notes\`) whose DB
|
|
655
|
+
> recipe does not yet carry \`atoms\` / \`instructions\` still hold the legacy \`recipe.thread\` payload;
|
|
656
|
+
> migrate them to this shape (capabilities + instructions in the DB) — do not reintroduce a CLI map.
|
|
563
657
|
|
|
564
658
|
## Step 3 — prepare ALL the ingredients, then review ONCE (always, before any paid render)
|
|
565
659
|
|
|
@@ -604,13 +698,35 @@ ingredients out one at a time.
|
|
|
604
698
|
\`update_render_status { render_id, status: "running" }\`. The render row tracks status only
|
|
605
699
|
(queued / running / complete / failed) — narrate fine-grained progress with
|
|
606
700
|
\`append_project_message\` instead.
|
|
607
|
-
3.
|
|
608
|
-
the
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
701
|
+
3. **MANDATORY final-video review gate — review EVERY finished master before \`set_final_render\`,
|
|
702
|
+
whatever the format (UGC or not).** The render credit is already spent (\`submit_render\` in 4.2);
|
|
703
|
+
this gate stands between a rendered master and PINNING/publishing it, so a bad render never gets
|
|
704
|
+
set as final. A master that looks fine on a still can still have a mis-voiced word, a caption
|
|
705
|
+
drifting off its line, a beat out of order, or a deformation — review the actual VIDEO, not
|
|
706
|
+
stills. Run the passes that APPLY to this format:
|
|
707
|
+
- **Audio ↔ script** — any master with SPEECH (VO or native/Seedance voice); **skip for
|
|
708
|
+
music-only / no-speech formats.** \`review-ugc-render\` is format-agnostic despite the name —
|
|
709
|
+
a deterministic Whisper transcript-vs-script diff, not UGC-specific: persist the approved
|
|
710
|
+
spoken lines to \`working/approved-script.txt\`, then \`gooseworks fetch review-ugc-render\` and
|
|
711
|
+
run \`review_render.py --video <master>.mp4 --script-file working/approved-script.txt --json
|
|
712
|
+
working/review-verdict.json\` (exit 0 PASS / 2 FAIL / 3 ERROR). It blocks a mis-voiced word
|
|
713
|
+
(approved "human-vetted" → "human witted"), a dropped phrase, or silence. It routes Whisper
|
|
714
|
+
through the gooseworks proxy when \`OPENAI_BASE_URL\` is set; with no backend at all, run
|
|
715
|
+
\`fal-ai/whisper\` via \`fal-proxy\` (upload the audio, pass its \`get_download_url\` as \`audio_url\`)
|
|
716
|
+
and diff the transcript yourself.
|
|
717
|
+
- **Captions / subtitles** — ANY captioned format (the most common non-UGC defect); **skip for
|
|
718
|
+
UGC/Seedance masters, which carry no subtitle track.** Concrete check: diff the caption file
|
|
719
|
+
you burned (SRT/ASS) against the SAME Whisper transcript + word timings from the audio pass —
|
|
720
|
+
every caption line must match the heard/scripted words and sit within ~0.3s of when they're
|
|
721
|
+
spoken; then in the visual pass below, OCR-read the burned caption off 4–5 sampled frames to
|
|
722
|
+
confirm it's actually on screen at that time and not colliding with a hyperframe or the end
|
|
723
|
+
card. Mismatched text or >0.3s drift fails the gate.
|
|
724
|
+
- **Visual + structure** — always: run the \`watch\` skill on the master — beat/scene order + SFX,
|
|
725
|
+
the brand's product (not the source's) is shown, the end card has the real wordmark + code, no
|
|
726
|
+
deformation/artifact, duration within ~20% of the source.
|
|
727
|
+
If ANY applicable pass fails, FIX it (regenerate/stitch the offending window, rebuild captions)
|
|
728
|
+
and re-review — only a clean pass proceeds to \`set_final_render\`. **This gate is universal: it
|
|
729
|
+
runs from the master skill for every format, so a recipe never has to opt in.**
|
|
614
730
|
4. Publish: \`get_upload_url { target: { type: "agent", agent_id: ADS_AGENT } }\` → PUT the master
|
|
615
731
|
and poster **under the project folder** (see Identity's path-prefix rule) — to
|
|
616
732
|
\`agent-config/brands/<brand_slug>/projects/<project_id>/working/final.mp4\` and
|
|
@@ -632,8 +748,10 @@ content }\` — never sit silent on a queue > 90s.
|
|
|
632
748
|
|
|
633
749
|
Media APIs go through GooseWorks proxies with your agent token; do NOT use an SDK's default host
|
|
634
750
|
(your token isn't a FAL/ElevenLabs token → 401). Base = \`<api_base>/api/internal/<proxy>\`; pass
|
|
635
|
-
\`?token=<api_key>&agent_id=<agent_id>\` (agent_id bills the Ads agent
|
|
636
|
-
\`
|
|
751
|
+
\`?token=<api_key>&agent_id=<agent_id>&project_id=<project_id>\` (agent_id bills the Ads agent;
|
|
752
|
+
\`project_id\` = the id of the project you're rendering — it attributes this generation's credits to
|
|
753
|
+
that ad project so the user sees per-project spend in the app. ALWAYS pass it). FAL = \`fal-proxy\`
|
|
754
|
+
(+ \`fal-storage-proxy\` to host a local image and get a CDN URL); ElevenLabs = \`elevenlabs-proxy\`
|
|
637
755
|
(VO / music bed).
|
|
638
756
|
|
|
639
757
|
**FAL queue gotcha** (#1 waste of generations): submit returns \`status_url\`/\`response_url\` on
|
|
@@ -649,24 +767,26 @@ def _cfg():
|
|
|
649
767
|
c = json.loads(pathlib.Path(os.path.expanduser("~/.gooseworks/credentials.json")).read_text())
|
|
650
768
|
return c["api_base"].rstrip("/"), c["api_key"], c.get("agent_id")
|
|
651
769
|
|
|
652
|
-
def _params(tok, agent):
|
|
770
|
+
def _params(tok, agent, project_id=None):
|
|
653
771
|
p = {"token": tok}
|
|
654
772
|
if agent: p["agent_id"] = agent
|
|
773
|
+
if project_id: p["project_id"] = project_id # attributes the spend to this ad project
|
|
655
774
|
return p
|
|
656
775
|
|
|
657
|
-
def fal_generate(model_path, payload, timeout_s=180, poll_s=3):
|
|
776
|
+
def fal_generate(model_path, payload, project_id=None, timeout_s=180, poll_s=3):
|
|
658
777
|
"""model_path e.g. 'fal-ai/nano-banana-2/edit' (the recipe names the model).
|
|
778
|
+
Pass project_id = the ad project you're rendering so credits attribute to it.
|
|
659
779
|
Returns the result image URL (a public *.fal.media CDN URL)."""
|
|
660
780
|
api_base, tok, agent = _cfg()
|
|
661
781
|
base = api_base + "/api/internal/fal-proxy"
|
|
662
|
-
sub = requests.post(f"{base}/{model_path}", params=_params(tok, agent), json=payload).json()
|
|
782
|
+
sub = requests.post(f"{base}/{model_path}", params=_params(tok, agent, project_id), json=payload).json()
|
|
663
783
|
to_proxy = lambda u: base + urlparse(u).path
|
|
664
784
|
status_url, response_url = to_proxy(sub["status_url"]), to_proxy(sub["response_url"])
|
|
665
785
|
deadline = time.time() + timeout_s
|
|
666
786
|
while time.time() < deadline:
|
|
667
|
-
st = requests.get(status_url, params=_params(tok, agent)).json()
|
|
787
|
+
st = requests.get(status_url, params=_params(tok, agent, project_id)).json()
|
|
668
788
|
if st.get("status") == "COMPLETED":
|
|
669
|
-
return requests.get(response_url, params=_params(tok, agent)).json()["images"][0]["url"]
|
|
789
|
+
return requests.get(response_url, params=_params(tok, agent, project_id)).json()["images"][0]["url"]
|
|
670
790
|
if st.get("status") in ("FAILED", "ERROR"):
|
|
671
791
|
raise RuntimeError(f"FAL failed: {st}")
|
|
672
792
|
time.sleep(poll_s)
|
|
@@ -674,7 +794,7 @@ def fal_generate(model_path, payload, timeout_s=180, poll_s=3):
|
|
|
674
794
|
\`\`\`
|
|
675
795
|
|
|
676
796
|
ElevenLabs (VO / music) is the same shape against \`<api_base>/api/internal/elevenlabs-proxy\`
|
|
677
|
-
with \`?token=&agent_id=\`. Feed FAL a local image by storing it (\`get_upload_url\`) and passing its
|
|
797
|
+
with \`?token=&agent_id=&project_id=\`. Feed FAL a local image by storing it (\`get_upload_url\`) and passing its
|
|
678
798
|
\`get_download_url\` presigned URL as an \`image_urls\` / \`audio_url\` entry — this is the reliable
|
|
679
799
|
path. (\`fal-storage-proxy\` may 404 depending on the install; don't block on it — prefer the
|
|
680
800
|
\`get_download_url\` presigned URL.)
|
|
@@ -687,6 +807,8 @@ path. (\`fal-storage-proxy\` may 404 depending on the install; don't block on it
|
|
|
687
807
|
the template needs), mirror the whole set with \`update_ad_project_script\`, and get the user's
|
|
688
808
|
approval in-session BEFORE rendering — always (review-once).
|
|
689
809
|
- **submit_render LAST**; \`output_url\` = the durable render-file URL, never a CDN URL.
|
|
810
|
+
- **Always pass \`project_id\` on media-proxy calls** (fal / ElevenLabs) so the credits attribute
|
|
811
|
+
to this ad project — that's what lets the user see per-project spend in the app.
|
|
690
812
|
- **Verify a real, non-empty MP4** (watch it) before marking the render complete.
|
|
691
813
|
- **Reuse the brand** when its research is complete; never re-research.
|
|
692
814
|
- On a hard error (auth/quota/model/timeout) set the render \`failed\` with a short
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"master-skill.js","sourceRoot":"","sources":["../../src/skills/master-skill.ts"],"names":[],"mappings":";;AA4BA,wCAMC;AAWD,sDA8KC;AAiBD,
|
|
1
|
+
{"version":3,"file":"master-skill.js","sourceRoot":"","sources":["../../src/skills/master-skill.ts"],"names":[],"mappings":";;AA4BA,wCAMC;AAWD,sDA8KC;AAiBD,0DAsUC;AAcD,8DAyQC;AA9yBD,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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoUR,CAAC;AACF,CAAC;AAED;;;;;;;;;;;GAWG;AACH,SAAgB,yBAAyB;IACvC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuQR,CAAC;AACF,CAAC"}
|