gamealgo-cli 0.3.5 → 0.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/docs.js +154 -1
- package/package.json +1 -1
- package/skills/gamealgo/SKILL.md +7 -5
package/README.md
CHANGED
package/dist/docs.js
CHANGED
|
@@ -257,8 +257,17 @@ export async function handleDocs(args, global) {
|
|
|
257
257
|
const flags = parseFlags(args);
|
|
258
258
|
const topicId = String(args.shift() || "").trim();
|
|
259
259
|
const apiQuery = topicId === "api" ? args.join(" ").trim() : "";
|
|
260
|
+
const listSections = flags.list === true || flags.toc === true;
|
|
261
|
+
const sectionId = optionalDocsSectionId(flags.section);
|
|
262
|
+
if (listSections && sectionId)
|
|
263
|
+
throw new Error("--list and --section cannot be used together");
|
|
264
|
+
if (flags.open && (listSections || sectionId))
|
|
265
|
+
throw new Error("--open cannot be combined with --list or --section");
|
|
266
|
+
if (topicId === "api" && (listSections || sectionId)) {
|
|
267
|
+
throw new Error("gamealgo docs api uses its own function list; --list and --section only apply to documentation topics");
|
|
268
|
+
}
|
|
260
269
|
if (topicId !== "api" && args.length) {
|
|
261
|
-
throw new Error("usage: gamealgo docs [topic] [--open] [--json] [--host <admin-url>]");
|
|
270
|
+
throw new Error("usage: gamealgo docs [topic] [--list | --section <section-id>] [--open] [--json] [--host <admin-url>]");
|
|
262
271
|
}
|
|
263
272
|
const saved = await loadConfig();
|
|
264
273
|
const host = normalizeHost(global.host || saved?.host || "");
|
|
@@ -307,6 +316,28 @@ export async function handleDocs(args, global) {
|
|
|
307
316
|
console.error("Warning: using cached documentation index because the Admin host could not be reached");
|
|
308
317
|
return;
|
|
309
318
|
}
|
|
319
|
+
if (listSections) {
|
|
320
|
+
if (!topic.sections.length)
|
|
321
|
+
throw new Error(`Documentation topic ${topic.id} does not publish addressable sections`);
|
|
322
|
+
if (global.json) {
|
|
323
|
+
await printResult({
|
|
324
|
+
ok: true,
|
|
325
|
+
version: manifest.version,
|
|
326
|
+
updatedAt: manifest.updatedAt,
|
|
327
|
+
topic: topic.id,
|
|
328
|
+
title: topic.title,
|
|
329
|
+
cached: loaded.cached,
|
|
330
|
+
webUrl,
|
|
331
|
+
rawUrl: docsRawUrl(host, topic.file),
|
|
332
|
+
sections: topic.sections,
|
|
333
|
+
}, global);
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
printDocsSectionIndex(topic, manifest, topic.sections);
|
|
337
|
+
if (loaded.cached)
|
|
338
|
+
console.error("Warning: using cached documentation index because the Admin host could not be reached");
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
310
341
|
const document = await loadDocsDocument(host, manifest, topic, loaded.cached);
|
|
311
342
|
if (topicId === "api") {
|
|
312
343
|
await printDocsApiResult({
|
|
@@ -321,6 +352,34 @@ export async function handleDocs(args, global) {
|
|
|
321
352
|
});
|
|
322
353
|
return;
|
|
323
354
|
}
|
|
355
|
+
if (sectionId) {
|
|
356
|
+
const section = topic.sections.find((item) => item.id === sectionId);
|
|
357
|
+
if (!section) {
|
|
358
|
+
const available = topic.sections.map((item) => item.id);
|
|
359
|
+
const suffix = available.length ? ` Available sections: ${available.join(", ")}` : "";
|
|
360
|
+
throw new Error(`Unknown documentation section: ${topic.id}/${sectionId}.${suffix}`);
|
|
361
|
+
}
|
|
362
|
+
const content = extractDocsSection(document.content, section);
|
|
363
|
+
if (global.json) {
|
|
364
|
+
await printResult({
|
|
365
|
+
ok: true,
|
|
366
|
+
version: manifest.version,
|
|
367
|
+
updatedAt: manifest.updatedAt,
|
|
368
|
+
topic: topic.id,
|
|
369
|
+
title: topic.title,
|
|
370
|
+
section,
|
|
371
|
+
cached: document.cached,
|
|
372
|
+
webUrl,
|
|
373
|
+
rawUrl: docsRawUrl(host, topic.file),
|
|
374
|
+
content,
|
|
375
|
+
}, global);
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
console.log(content.trimEnd());
|
|
379
|
+
if (document.cached)
|
|
380
|
+
console.error("Warning: using cached documentation because the Admin host could not be reached");
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
324
383
|
if (global.json) {
|
|
325
384
|
await printResult({
|
|
326
385
|
ok: true,
|
|
@@ -340,6 +399,62 @@ export async function handleDocs(args, global) {
|
|
|
340
399
|
if (document.cached)
|
|
341
400
|
console.error("Warning: using cached documentation because the Admin host could not be reached");
|
|
342
401
|
}
|
|
402
|
+
export function optionalDocsSectionId(value) {
|
|
403
|
+
if (value === undefined || value === false || value === "")
|
|
404
|
+
return undefined;
|
|
405
|
+
if (value === true)
|
|
406
|
+
throw new Error("--section requires a section id");
|
|
407
|
+
const sectionId = String(value).trim();
|
|
408
|
+
if (!/^[a-z][a-z0-9-]{0,63}$/.test(sectionId)) {
|
|
409
|
+
throw new Error("--section must use a stable lowercase section id");
|
|
410
|
+
}
|
|
411
|
+
return sectionId;
|
|
412
|
+
}
|
|
413
|
+
export function extractDocsSection(content, section) {
|
|
414
|
+
const lines = content.split(/\r?\n/);
|
|
415
|
+
const headingLines = markdownHeadingLines(lines);
|
|
416
|
+
const matches = headingLines.filter((item) => (item.level === section.level
|
|
417
|
+
&& normalizeDocsSectionTitle(item.title) === normalizeDocsSectionTitle(section.title)));
|
|
418
|
+
if (!matches.length)
|
|
419
|
+
throw new Error(`Documentation section heading not found: ${section.id} (${section.title})`);
|
|
420
|
+
if (matches.length > 1)
|
|
421
|
+
throw new Error(`Documentation section heading is ambiguous: ${section.id} (${section.title})`);
|
|
422
|
+
const startLine = matches[0].line;
|
|
423
|
+
const endLine = headingLines.find((item) => item.line > startLine && item.level <= section.level)?.line ?? lines.length;
|
|
424
|
+
return lines.slice(startLine, endLine).join("\n").trimEnd();
|
|
425
|
+
}
|
|
426
|
+
function normalizeDocsSectionTitle(value) {
|
|
427
|
+
return stripInlineMarkdown(value).replace(/\s+/g, " ").trim();
|
|
428
|
+
}
|
|
429
|
+
function markdownHeadingLines(lines) {
|
|
430
|
+
const headings = [];
|
|
431
|
+
let fence = "";
|
|
432
|
+
for (let line = 0; line < lines.length; line += 1) {
|
|
433
|
+
const fenceMatch = lines[line].match(/^\s{0,3}(```+|~~~+)/);
|
|
434
|
+
if (fenceMatch) {
|
|
435
|
+
const marker = fenceMatch[1][0];
|
|
436
|
+
if (!fence)
|
|
437
|
+
fence = marker;
|
|
438
|
+
else if (fence === marker)
|
|
439
|
+
fence = "";
|
|
440
|
+
continue;
|
|
441
|
+
}
|
|
442
|
+
if (fence)
|
|
443
|
+
continue;
|
|
444
|
+
const heading = lines[line].match(/^(#{1,6})[ \t]+(.+?)\s*$/);
|
|
445
|
+
if (heading)
|
|
446
|
+
headings.push({ line, level: heading[1].length, title: heading[2] });
|
|
447
|
+
}
|
|
448
|
+
return headings;
|
|
449
|
+
}
|
|
450
|
+
export function printDocsSectionIndex(topic, manifest, sections) {
|
|
451
|
+
console.log(`${topic.title}章节 ${manifest.version}${manifest.updatedAt ? ` (${manifest.updatedAt})` : ""}`);
|
|
452
|
+
for (const section of sections) {
|
|
453
|
+
const indent = " ".repeat(Math.max(1, section.level - 1));
|
|
454
|
+
console.log(`\n${indent}${section.id}\n${indent} ${section.title}${section.summary ? `:${section.summary}` : ""}`);
|
|
455
|
+
}
|
|
456
|
+
console.log(`\n读取章节: gamealgo docs ${topic.id} --section <section-id>`);
|
|
457
|
+
}
|
|
343
458
|
export function parseDocsApiPlatform(value) {
|
|
344
459
|
if (value === undefined || value === false || value === "")
|
|
345
460
|
return undefined;
|
|
@@ -556,6 +671,7 @@ export function parseDocsManifest(text) {
|
|
|
556
671
|
group: String(topic.group || "其他"),
|
|
557
672
|
file,
|
|
558
673
|
hidden: topic.hidden === true,
|
|
674
|
+
sections: parseDocsManifestSections(topic.sections, id),
|
|
559
675
|
};
|
|
560
676
|
});
|
|
561
677
|
const ids = new Set(topics.map((topic) => topic.id));
|
|
@@ -572,6 +688,43 @@ export function parseDocsManifest(text) {
|
|
|
572
688
|
topics,
|
|
573
689
|
};
|
|
574
690
|
}
|
|
691
|
+
function parseDocsManifestSections(value, topicId) {
|
|
692
|
+
if (value === undefined)
|
|
693
|
+
return [];
|
|
694
|
+
if (!Array.isArray(value))
|
|
695
|
+
throw new Error(`Documentation topic ${topicId} sections must be an array`);
|
|
696
|
+
const sections = [];
|
|
697
|
+
const parents = [];
|
|
698
|
+
const ids = new Set();
|
|
699
|
+
for (const item of value) {
|
|
700
|
+
const section = objectValue(item);
|
|
701
|
+
const id = optionalString(section?.id);
|
|
702
|
+
const title = optionalString(section?.title);
|
|
703
|
+
const summary = optionalString(section?.summary);
|
|
704
|
+
const level = Number(section?.level);
|
|
705
|
+
if (!id || !/^[a-z][a-z0-9-]{0,63}$/.test(id) || !title || !summary || !Number.isInteger(level) || level < 2 || level > 6) {
|
|
706
|
+
throw new Error(`Documentation topic ${topicId} contains an invalid section: ${id || "unknown"}`);
|
|
707
|
+
}
|
|
708
|
+
if (ids.has(id))
|
|
709
|
+
throw new Error(`Documentation topic ${topicId} contains duplicate section id: ${id}`);
|
|
710
|
+
ids.add(id);
|
|
711
|
+
while (parents.length && parents[parents.length - 1].level >= level)
|
|
712
|
+
parents.pop();
|
|
713
|
+
if (level > 2 && (!parents.length || level > parents[parents.length - 1].level + 1)) {
|
|
714
|
+
throw new Error(`Documentation topic ${topicId} section ${id} has no valid parent`);
|
|
715
|
+
}
|
|
716
|
+
const parsed = {
|
|
717
|
+
id,
|
|
718
|
+
title,
|
|
719
|
+
summary,
|
|
720
|
+
level,
|
|
721
|
+
...(parents.length ? { parentId: parents[parents.length - 1].id } : {}),
|
|
722
|
+
};
|
|
723
|
+
sections.push(parsed);
|
|
724
|
+
parents.push(parsed);
|
|
725
|
+
}
|
|
726
|
+
return sections;
|
|
727
|
+
}
|
|
575
728
|
export function docsWebUrl(host, topicId) {
|
|
576
729
|
const url = new URL(`${normalizeHost(host)}/dashboard/docs/`);
|
|
577
730
|
if (topicId)
|
package/package.json
CHANGED
package/skills/gamealgo/SKILL.md
CHANGED
|
@@ -22,7 +22,7 @@ When the user asks what GameAlgo can do, asks for ongoing iteration, or names a
|
|
|
22
22
|
1. Run `gamealgo optimize list --json` to load the complete current task catalog from the Server.
|
|
23
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
24
|
3. Once the user selects a task, or their request clearly identifies one, run `gamealgo optimize get <task-id> --json`.
|
|
25
|
-
4.
|
|
25
|
+
4. For each topic named in the task's `docs` field, run `gamealgo docs <topic> --list --json`, then read the sections relevant to the selected task with `gamealgo docs <topic> --section <section-id>` before implementation.
|
|
26
26
|
5. Follow the returned `agentSteps` and use `deliverables` as the completion checklist.
|
|
27
27
|
|
|
28
28
|
Do not run every catalog task in sequence. Complete the selected task and report unmet prerequisites instead of manufacturing missing data.
|
|
@@ -31,14 +31,16 @@ Do not run every catalog task in sequence. Complete the selected task and report
|
|
|
31
31
|
|
|
32
32
|
Classify the requested work by the actions it requires, not only by the user's task title. Before implementation or interpreting results, load every applicable source below from the current Server:
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
Use each topic's section list as the router. Read the whole topic only when the task genuinely spans most of it; when that is necessary, load one full topic per tool call. If any tool marks documentation output as truncated, treat it as incomplete, retrieve the missing sections, and do not infer that unseen guidance is absent or switch workflows based on the partial output.
|
|
35
|
+
|
|
36
|
+
- SDK installation, identity, lifecycle, telemetry, attribution, advertising, or purchase work: run `gamealgo integration get-plan --environment <domestic|overseas> --platform <ios|android|maker|rest> --json`, then list the `sdk-integration` sections and read the relevant shared and platform-specific sections.
|
|
35
37
|
- Calling or changing a concrete public SDK function: additionally run `gamealgo docs api <function> --platform <platform>` for each affected function. Never infer one platform's API from another.
|
|
36
38
|
- Adding or changing events: additionally run `gamealgo events list --days 30 --json` before defining fields or names.
|
|
37
|
-
-
|
|
38
|
-
- Creating or changing remote configuration, strategies, experiments, variants, objectives, rollout, or assessment:
|
|
39
|
+
- Querying an existing dashboard or report: read `gamealgo docs reports --section report-query` plus the section that defines the relevant metric or template. Creating, changing, previewing, or publishing Report Packs, charts, report SQL, or business metrics: list the `reports` sections and read every section affected by the change before interpreting data or editing definitions.
|
|
40
|
+
- Creating or changing remote configuration, strategies, experiments, variants, objectives, rollout, or assessment: list the `experiments` sections and read every section affected by the change; also load the integration plan and exact API documentation when client code changes.
|
|
39
41
|
- Adjust or marketing attribution work: load the selected optimization task when one applies, read every topic in its `docs`, and load the integration plan and exact identifier/attribution APIs for the target platform.
|
|
40
42
|
|
|
41
|
-
If work crosses domains, combine their requirements. For example, an advertising event used by an experiment report requires the integration plan, event inventory, SDK API documentation, report
|
|
43
|
+
If work crosses domains, combine their requirements. For example, an advertising event used by an experiment report requires the integration plan, event inventory, SDK API documentation, relevant report sections, and relevant experiment sections. Read a section once per turn unless the task expands or the Server documentation version changes.
|
|
42
44
|
|
|
43
45
|
For initial integration or business dashboard work, standard dashboards are only the baseline. During the initial integration, inspect the actual game loop and define a separate game-specific new-user flow dashboard with meaningful milestones and drop-off diagnostics. Add required milestone telemetry and event catalog fields in the same integration instead of waiting for production volume. Validate the pipeline with test data; label business conclusions as awaiting data when production samples are immature, but do not omit the dashboard design.
|
|
44
46
|
|