gamealgo-cli 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -0
- package/dist/client.js +9 -2
- package/dist/commands/marketing.js +13 -5
- package/dist/commands/optimize.js +35 -0
- package/dist/commands/skill.js +57 -0
- package/dist/main.js +10 -0
- package/package.json +2 -1
- package/skills/gamealgo/SKILL.md +50 -0
- package/skills/gamealgo/agents/openai.yaml +7 -0
package/README.md
CHANGED
|
@@ -35,6 +35,22 @@ gamealgo docs api GameAlgo.Init --platform lua
|
|
|
35
35
|
|
|
36
36
|
Help and documentation are fetched from the configured Server on every invocation. The CLI intentionally does not keep a documentation cache.
|
|
37
37
|
|
|
38
|
+
## Agent skill
|
|
39
|
+
|
|
40
|
+
Install or update the bundled GameAlgo skill for the current agent:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
gamealgo skill install --agent codex
|
|
44
|
+
gamealgo skill install --agent claude
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The skill uses the project-bound CLI and live Server content. It can discover the complete iteration catalog without copied prompts:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
gamealgo optimize list --json
|
|
51
|
+
gamealgo optimize get ad-monetization --json
|
|
52
|
+
```
|
|
53
|
+
|
|
38
54
|
Adjust S2S event forwarding can be configured with an explicit event allowlist:
|
|
39
55
|
|
|
40
56
|
```bash
|
package/dist/client.js
CHANGED
|
@@ -177,8 +177,15 @@ export class GameAlgoAdminClient {
|
|
|
177
177
|
const params = new URLSearchParams({ environment, platform });
|
|
178
178
|
return await this.get(`/admin/v1/games/${encodeURIComponent(await this.gameId())}/integration/plan?${params.toString()}`);
|
|
179
179
|
}
|
|
180
|
-
async
|
|
181
|
-
return await this.get(`/admin/v1/games/${encodeURIComponent(await this.gameId())}/
|
|
180
|
+
async listOptimizationTasks() {
|
|
181
|
+
return await this.get(`/admin/v1/games/${encodeURIComponent(await this.gameId())}/optimization/tasks`);
|
|
182
|
+
}
|
|
183
|
+
async getOptimizationTask(taskId) {
|
|
184
|
+
return await this.get(`/admin/v1/games/${encodeURIComponent(await this.gameId())}/optimization/tasks/${encodeURIComponent(taskId)}`);
|
|
185
|
+
}
|
|
186
|
+
async getAdjustMarketing(platform) {
|
|
187
|
+
const query = platform ? `?platform=${encodeURIComponent(platform)}` : "";
|
|
188
|
+
return await this.get(`/admin/v1/games/${encodeURIComponent(await this.gameId())}/marketing/adjust${query}`);
|
|
182
189
|
}
|
|
183
190
|
async putAdjustMarketing(body) {
|
|
184
191
|
return await this.put(`/admin/v1/games/${encodeURIComponent(await this.gameId())}/marketing/adjust`, body);
|
|
@@ -10,19 +10,23 @@ export async function handleMarketing(client, args, global) {
|
|
|
10
10
|
throw new Error("usage: gamealgo marketing adjust <get|configure|sync>");
|
|
11
11
|
const sub = args.shift();
|
|
12
12
|
if (sub === "get" || sub === "status") {
|
|
13
|
-
|
|
13
|
+
const flags = parseFlags(args);
|
|
14
|
+
const platform = optionalString(flags.platform);
|
|
15
|
+
if (platform && platform !== "ios" && platform !== "android")
|
|
16
|
+
throw new Error("--platform must be ios or android");
|
|
17
|
+
await printResult(await client.getAdjustMarketing(platform), global);
|
|
14
18
|
return;
|
|
15
19
|
}
|
|
16
20
|
if (sub === "configure" || sub === "set") {
|
|
17
21
|
const flags = parseFlags(args);
|
|
18
|
-
const
|
|
22
|
+
const platform = optionalString(flags.platform);
|
|
23
|
+
if (platform !== "ios" && platform !== "android")
|
|
24
|
+
throw new Error("--platform is required and must be ios or android");
|
|
25
|
+
const current = await client.getAdjustMarketing(platform);
|
|
19
26
|
const existing = current.integration || {};
|
|
20
27
|
const appToken = optionalString(flags["app-token"] || flags.appToken || existing.appToken);
|
|
21
|
-
const platform = optionalString(flags.platform || existing.platform) || "ios";
|
|
22
28
|
if (!appToken)
|
|
23
29
|
throw new Error("--app-token is required");
|
|
24
|
-
if (platform !== "ios" && platform !== "android")
|
|
25
|
-
throw new Error("--platform must be ios or android");
|
|
26
30
|
const body = {
|
|
27
31
|
appToken,
|
|
28
32
|
appName: optionalString(flags["app-name"] || flags.appName || existing.appName) || "",
|
|
@@ -50,7 +54,11 @@ export async function handleMarketing(client, args, global) {
|
|
|
50
54
|
const stopProgress = startProgress("Syncing Adjust marketing spend", timeoutMs);
|
|
51
55
|
let response;
|
|
52
56
|
try {
|
|
57
|
+
const platform = optionalString(flags.platform);
|
|
58
|
+
if (platform && platform !== "ios" && platform !== "android")
|
|
59
|
+
throw new Error("--platform must be ios or android");
|
|
53
60
|
response = await client.syncAdjustMarketing({
|
|
61
|
+
...(platform ? { platform } : {}),
|
|
54
62
|
startDate: optionalString(flags.from || flags.start || flags.startDate),
|
|
55
63
|
endDate: optionalString(flags.to || flags.end || flags.endDate),
|
|
56
64
|
}, { timeoutMs });
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import YAML from "yaml";
|
|
2
|
+
import { parseFlags } from "../args.js";
|
|
3
|
+
import { printResult } from "../output.js";
|
|
4
|
+
import { writeTextFile } from "../resources.js";
|
|
5
|
+
export async function handleOptimize(client, args, global) {
|
|
6
|
+
const sub = args.shift();
|
|
7
|
+
const flags = parseFlags(args);
|
|
8
|
+
if (sub === "list") {
|
|
9
|
+
if (args.length)
|
|
10
|
+
throw new Error("usage: gamealgo optimize list [--out tasks.json]");
|
|
11
|
+
await writeOptimizationOutput(await client.listOptimizationTasks(), flags, global);
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
if (sub === "get") {
|
|
15
|
+
const taskId = args.shift();
|
|
16
|
+
if (!taskId || args.length)
|
|
17
|
+
throw new Error("usage: gamealgo optimize get <task-id> [--out task.json]");
|
|
18
|
+
await writeOptimizationOutput(await client.getOptimizationTask(taskId), flags, global);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
throw new Error("usage: gamealgo optimize <list|get>");
|
|
22
|
+
}
|
|
23
|
+
async function writeOptimizationOutput(value, flags, global) {
|
|
24
|
+
if (flags.out) {
|
|
25
|
+
if (typeof flags.out !== "string")
|
|
26
|
+
throw new Error("--out requires a file path");
|
|
27
|
+
const content = flags.out.toLowerCase().endsWith(".json")
|
|
28
|
+
? `${JSON.stringify(value, null, 2)}\n`
|
|
29
|
+
: YAML.stringify(value);
|
|
30
|
+
await writeTextFile(flags.out, content);
|
|
31
|
+
await printResult({ ok: true, out: flags.out }, global);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
await printResult(value, global);
|
|
35
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { cp, mkdir, stat } from "node:fs/promises";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { dirname, join, resolve } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { parseFlags } from "../args.js";
|
|
6
|
+
import { printResult } from "../output.js";
|
|
7
|
+
const SKILL_NAME = "gamealgo";
|
|
8
|
+
export async function handleSkill(args, global) {
|
|
9
|
+
const sub = args.shift();
|
|
10
|
+
const flags = parseFlags(args);
|
|
11
|
+
if (sub !== "install" || args.length) {
|
|
12
|
+
throw new Error("usage: gamealgo skill install --agent <codex|claude> [--dest <skill-directory>]");
|
|
13
|
+
}
|
|
14
|
+
const destination = skillDestination(flags);
|
|
15
|
+
const updated = await pathExists(destination);
|
|
16
|
+
await installBundledGameAlgoSkill(destination);
|
|
17
|
+
await printResult({
|
|
18
|
+
ok: true,
|
|
19
|
+
skill: SKILL_NAME,
|
|
20
|
+
destination,
|
|
21
|
+
action: updated ? "updated" : "installed",
|
|
22
|
+
note: "The skill is available to new agent turns. Restart the agent if it does not appear.",
|
|
23
|
+
}, global);
|
|
24
|
+
}
|
|
25
|
+
export async function installBundledGameAlgoSkill(destination) {
|
|
26
|
+
const source = bundledSkillDirectory();
|
|
27
|
+
await mkdir(dirname(destination), { recursive: true });
|
|
28
|
+
await cp(source, destination, { recursive: true, force: true });
|
|
29
|
+
}
|
|
30
|
+
export function skillDestination(flags) {
|
|
31
|
+
if (typeof flags.dest === "string")
|
|
32
|
+
return resolve(flags.dest);
|
|
33
|
+
if (flags.dest === true)
|
|
34
|
+
throw new Error("--dest requires a directory path");
|
|
35
|
+
const agent = typeof flags.agent === "string" ? flags.agent.toLowerCase() : "";
|
|
36
|
+
if (agent === "codex") {
|
|
37
|
+
return join(process.env.CODEX_HOME || join(homedir(), ".codex"), "skills", SKILL_NAME);
|
|
38
|
+
}
|
|
39
|
+
if (agent === "claude") {
|
|
40
|
+
return join(process.env.CLAUDE_CONFIG_DIR || join(homedir(), ".claude"), "skills", SKILL_NAME);
|
|
41
|
+
}
|
|
42
|
+
throw new Error("--agent must be codex or claude when --dest is not provided");
|
|
43
|
+
}
|
|
44
|
+
function bundledSkillDirectory() {
|
|
45
|
+
return resolve(dirname(fileURLToPath(import.meta.url)), "../../skills", SKILL_NAME);
|
|
46
|
+
}
|
|
47
|
+
async function pathExists(path) {
|
|
48
|
+
try {
|
|
49
|
+
await stat(path);
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
if (error.code === "ENOENT")
|
|
54
|
+
return false;
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
}
|
package/dist/main.js
CHANGED
|
@@ -6,8 +6,10 @@ import { handleExperiment } from "./commands/experiment.js";
|
|
|
6
6
|
import { handleIntegration } from "./commands/integration.js";
|
|
7
7
|
import { handleGameKey } from "./commands/keys.js";
|
|
8
8
|
import { handleMarketing } from "./commands/marketing.js";
|
|
9
|
+
import { handleOptimize } from "./commands/optimize.js";
|
|
9
10
|
import { handleReport } from "./commands/report.js";
|
|
10
11
|
import { handleFileResource } from "./commands/resources.js";
|
|
12
|
+
import { handleSkill } from "./commands/skill.js";
|
|
11
13
|
import { loadPackageMetadata } from "./config.js";
|
|
12
14
|
import { handleDocs, handleHelp } from "./docs.js";
|
|
13
15
|
import { printResult } from "./output.js";
|
|
@@ -57,6 +59,10 @@ export async function main() {
|
|
|
57
59
|
await handleAdminKey(args, global);
|
|
58
60
|
return;
|
|
59
61
|
}
|
|
62
|
+
if (command === "skill") {
|
|
63
|
+
await handleSkill(args, global);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
60
66
|
const client = await createClient(global);
|
|
61
67
|
if (command === "experiment") {
|
|
62
68
|
await handleExperiment(client, args, global);
|
|
@@ -90,6 +96,10 @@ export async function main() {
|
|
|
90
96
|
await handleMarketing(client, args, global);
|
|
91
97
|
return;
|
|
92
98
|
}
|
|
99
|
+
if (command === "optimize") {
|
|
100
|
+
await handleOptimize(client, args, global);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
93
103
|
throw new Error(`Unknown command: ${command}`);
|
|
94
104
|
}
|
|
95
105
|
catch (error) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gamealgo-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "GameAlgo command-line client for AI agents and game developers",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
},
|
|
17
17
|
"files": [
|
|
18
18
|
"dist/",
|
|
19
|
+
"skills/",
|
|
19
20
|
"README.md",
|
|
20
21
|
"LICENSE"
|
|
21
22
|
],
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: gamealgo
|
|
3
|
+
description: Integrate and improve games with GameAlgo using the project-bound CLI, live Server documentation, and the Server-maintained optimization task catalog. Use when working on GameAlgo SDK setup, telemetry events, report packs, Adjust attribution, remote strategies, experiments, or when discovering supported GameAlgo iteration tasks for a game.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# GameAlgo
|
|
7
|
+
|
|
8
|
+
Use the GameAlgo CLI as the source of current project identity and Server-maintained instructions. Do not rely on remembered SDK signatures, copied prompts, or stale local documentation.
|
|
9
|
+
|
|
10
|
+
## Start In The Game Repository
|
|
11
|
+
|
|
12
|
+
Run commands from the game repository that owns the integration. Before any game-level operation:
|
|
13
|
+
|
|
14
|
+
1. Run `gamealgo version`. If unavailable or rejected by the Server, install the version named by the CLI or Server error.
|
|
15
|
+
2. Run `gamealgo whoami --json` and confirm the project-bound `gameId`. If the project is not logged in, ask the developer for that game's Game Admin Key and run the displayed login command from the repository root.
|
|
16
|
+
3. Never put `ga_admin_*` credentials in client code, logs, generated reports, commits, or chat summaries.
|
|
17
|
+
|
|
18
|
+
## Discover Supported Work
|
|
19
|
+
|
|
20
|
+
When the user asks what GameAlgo can do, asks for ongoing iteration, or names a broad optimization goal:
|
|
21
|
+
|
|
22
|
+
1. Run `gamealgo optimize list --json` to load the complete current task catalog from the Server.
|
|
23
|
+
2. Treat the result as an unranked capability catalog. Do not claim that a task is recommended or ready until its prerequisites have been checked against the current game.
|
|
24
|
+
3. Once the user selects a task, or their request clearly identifies one, run `gamealgo optimize get <task-id> --json`.
|
|
25
|
+
4. Read each topic named in the task's `docs` field with `gamealgo docs <topic>` before implementation.
|
|
26
|
+
5. Follow the returned `agentSteps` and use `deliverables` as the completion checklist.
|
|
27
|
+
|
|
28
|
+
Do not run every catalog task in sequence. Complete the selected task and report unmet prerequisites instead of manufacturing missing data.
|
|
29
|
+
|
|
30
|
+
## Integration Work
|
|
31
|
+
|
|
32
|
+
For SDK integration or telemetry changes, determine the actual deployment environment and runtime platform, then run:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
gamealgo integration get-plan --environment <domestic|overseas> --platform <ios|android|maker|rest> --json
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Inspect the existing project before editing. Reuse existing SDK wrappers, user identity, lifecycle, event, advertising, purchase, and attribution flows. Use `gamealgo docs api <function> --platform <platform>` for exact public APIs; do not invent methods or infer support from another platform.
|
|
39
|
+
|
|
40
|
+
Before adding events, run `gamealgo events list --days 30 --json`. Reuse events with the same business meaning, register new fields with their real JSON types, and verify non-debug context and business events with real runtime traffic.
|
|
41
|
+
|
|
42
|
+
## Changes And Verification
|
|
43
|
+
|
|
44
|
+
- Make repository changes when the selected task requires them; keep unrelated files unchanged.
|
|
45
|
+
- Preserve local fallback behavior when adding remote configuration, scripts, or experiments.
|
|
46
|
+
- Validate Report Packs before preview or publication and compare results with real event counts.
|
|
47
|
+
- Show the relevant diff and impact before publishing reports, remote configuration, scripts, or experiments unless the user has already explicitly authorized that exact operation.
|
|
48
|
+
- Do not push directly to a protected primary branch. Follow the repository's branch and merge-request workflow.
|
|
49
|
+
|
|
50
|
+
Finish with the changed files, checks executed, observed data evidence, unmet prerequisites, and any operation still awaiting developer approval. Only call initial integration activated after real configuration and event acceptance checks pass.
|