gamealgo-cli 0.3.4 → 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 CHANGED
@@ -29,6 +29,8 @@ gamealgo help
29
29
  gamealgo help report publish
30
30
  gamealgo docs
31
31
  gamealgo docs sdk-integration
32
+ gamealgo docs reports --list --json
33
+ gamealgo docs reports --section report-query
32
34
  gamealgo listapi
33
35
  gamealgo docs api GameAlgo.Init --platform lua
34
36
  ```
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gamealgo-cli",
3
- "version": "0.3.4",
3
+ "version": "0.3.6",
4
4
  "description": "GameAlgo command-line client for AI agents and game developers",
5
5
  "repository": {
6
6
  "type": "git",
@@ -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. Read each topic named in the task's `docs` field with `gamealgo docs <topic>` before implementation.
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
- - 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 read `gamealgo docs sdk-integration`.
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
- - Reading, creating, changing, previewing, or publishing Report Packs, charts, report SQL, or business metrics: read `gamealgo docs reports` before interpreting data or editing definitions.
38
- - Creating or changing remote configuration, strategies, experiments, variants, objectives, rollout, or assessment: read `gamealgo docs experiments`; also load the integration plan and exact API documentation when client code changes.
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 documentation, and experiment documentation. Read a topic once per turn unless the task expands or the Server documentation version changes.
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
 
@@ -67,6 +69,26 @@ Reuse events with the same business meaning, register new fields with their real
67
69
 
68
70
  For every game, prefer stable, human-readable Chinese values for business dimensions that will appear directly in report legends, axes, selectors, or tables. This includes values such as level or stage type, game mode, placement, item name, and business outcome or failure reason. Keep event names, field names, IDs, SQL aliases, and protocol-defined enums in their required technical format. Do not rename values already used by a live game solely to translate them, because that would split one historical dimension into multiple report values.
69
71
 
72
+ ## Initialize At The Earliest Safe Lifecycle Point
73
+
74
+ Initialize GameAlgo at the beginning of the earliest runtime lifecycle hook where its minimal required dependencies are available. Do not place it after save loading, scene creation, asset loading, UI construction, ad initialization, or a broad game-module bootstrap merely for organizational convenience. Any unhandled failure before the call would otherwise exclude an opened user from telemetry, and an SDK initialization watchdog cannot report a failure when the SDK was never started.
75
+
76
+ Wait only for dependencies GameAlgo actually needs, such as the platform runtime, client key, or a required stable identity. If richer gameplay context becomes available later, separate early SDK initialization from later context binding and business-event emission instead of delaying the SDK. Keep initialization non-blocking, isolate and log its synchronous exceptions without blocking the game, and do not silently discard an outer protected-call error.
77
+
78
+ Before delivery, inspect the complete startup path rather than only the SDK wrapper. Verify that deliberate failures in later save, resource, scene, or UI initialization still occur after GameAlgo initialization has started and can produce either a successful initialization log or the configured initialization-failure diagnostic.
79
+
80
+ ## Always Track Maker Ad Close Callbacks
81
+
82
+ For TapTap Maker `ShowRewardVideoAd`, entering the registered `onClose` callback is the platform's effective-exposure boundary. Call `TrackAd` unconditionally and exactly once whenever `onClose` runs.
83
+
84
+ - Never gate `TrackAd` on `result.success`, any return or error code, `result.msg` / `result.message`, a custom failure classification, or whether a `trackId` was extracted.
85
+ - Use `result.success` only to decide whether the game grants the reward. Other result fields may enrich diagnostics but must not suppress `TrackAd`.
86
+ - If `trackId` is missing, still call `TrackAd` and also emit a missing-track diagnostic when available.
87
+ - Do not infer that an `onClose` result was a pre-display failure. Only a thrown or rejected ad request, or a distinct failure callback that occurs without entering `onClose`, may skip `TrackAd`.
88
+ - Keep a per-request guard so duplicate `onClose` delivery cannot produce duplicate `ad_view` events.
89
+
90
+ Before delivery, inspect every Maker ad wrapper and test the `success=true`, `success=false`, non-success return-code, missing-trackId, and duplicate-callback cases. Each `onClose` case must produce exactly one `TrackAd`; only reward granting varies with `success`.
91
+
70
92
  ## Keep Experiment Identity Out Of Game Logic
71
93
 
72
94
  When client code implements an experiment or remote Strategy, define a stable, typed parameter contract first, including each parameter's meaning, type, valid values or unit, and local fallback. Drive game behavior only from those config parameters or from a validated script payload. Variants provide different parameter values; they are not feature flags.