@thinkingai/ae-cli 6.1.19 → 6.1.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/README.md +95 -38
  2. package/README.zh.md +95 -38
  3. package/dist/{auth-QPEVVBKT.js → auth-FUM37MHF.js} +236 -127
  4. package/dist/{capability-ASI62I4A.js → capability-AXFQW5WM.js} +45 -32
  5. package/dist/{chunk-FR2EDAQM.js → chunk-4P355ZWB.js} +69 -1
  6. package/dist/chunk-6ZIQV5GW.js +26 -0
  7. package/dist/chunk-ATSM5XAW.js +623 -0
  8. package/dist/{chunk-6QK7SYYY.js → chunk-BBEFP4SB.js} +29 -37
  9. package/dist/{chunk-KEWVSO64.js → chunk-CZU3V3DQ.js} +4 -15
  10. package/dist/chunk-ECBLHAZO.js +15 -0
  11. package/dist/{chunk-B5HMQAOM.js → chunk-I4WQAEYB.js} +1 -1
  12. package/dist/{chunk-QL6R6TSK.js → chunk-LNZBEQXW.js} +6 -5
  13. package/dist/{chunk-GJXRF7Y6.js → chunk-QJQZH5GQ.js} +47 -78
  14. package/dist/{chunk-S5PONFBM.js → chunk-RSG4ONOI.js} +7 -7
  15. package/dist/{chunk-DKEXH6Q5.js → chunk-TAL6CZH6.js} +6 -6
  16. package/dist/{chunk-JYMGHGCE.js → chunk-VXNMYUXU.js} +24 -13
  17. package/dist/{chunk-KZ6ZHF5J.js → chunk-WZ6YVQSF.js} +14 -14
  18. package/dist/{community-report-client-6OL6GBQW.js → community-report-client-XXWGSBSD.js} +2 -4
  19. package/dist/{config-XNMACCKX.js → config-EXUGQN5T.js} +8 -9
  20. package/dist/{data-integration-4NZGZ45E.js → data-integration-4NZ53OMT.js} +17 -15
  21. package/dist/index.js +52 -79
  22. package/dist/{local-data-upload-client-EFHDLXFG.js → local-data-upload-client-KYOKVYZV.js} +2 -4
  23. package/dist/{memory-HBJQ2GC5.js → memory-ATNKZNW5.js} +4 -6
  24. package/dist/{metadata-62EWBEIG.js → metadata-VZVC7YMH.js} +7 -9
  25. package/dist/{model-BHLJ2QMT.js → model-E4JMQ4V2.js} +4 -6
  26. package/dist/{personal-semantic-preference-JBVMAI7L.js → personal-semantic-preference-7S2SQ3UV.js} +6 -8
  27. package/dist/{project-semantic-BQ4MPUS5.js → project-semantic-2SZP2OPO.js} +11 -13
  28. package/dist/{sync-MUWPRM5I.js → sync-YV3E66IF.js} +6 -8
  29. package/dist/{te-agent-WQZTMEK4.js → te-agent-JB5T3PO7.js} +393 -90
  30. package/dist/{te-analysis-MNCG65LU.js → te-analysis-3YJAAT2D.js} +99 -33
  31. package/dist/{te-community-BNE6KREL.js → te-community-UDBI672N.js} +9 -32
  32. package/dist/{te-dataops-TXLKM2W6.js → te-dataops-ZLYOCXZ4.js} +19 -20
  33. package/dist/{te-engage-TPKLRVTL.js → te-engage-4XG6OJML.js} +12 -14
  34. package/dist/{te-experiment-6BYKTCY7.js → te-experiment-VXUWPINJ.js} +6 -8
  35. package/dist/{te-kb-BUD6N3NI.js → te-kb-WYQWHFSC.js} +595 -85
  36. package/dist/{te-system-44LZJDTQ.js → te-system-7G6F2LJA.js} +6 -8
  37. package/dist/{te-team-XMP47YXE.js → te-team-E7FBBXMQ.js} +6 -8
  38. package/dist/{update-UODFOZ4P.js → update-D47BUG25.js} +7 -8
  39. package/package.json +12 -9
  40. package/skills/ae-agent/SKILL.md +30 -13
  41. package/skills/ae-agent/references/agent-distribution.md +94 -0
  42. package/skills/ae-agent/references/approval-request.md +4 -0
  43. package/skills/ae-agent/references/command_index.md +9 -2
  44. package/skills/ae-agent/references/create-automation.md +20 -2
  45. package/skills/ae-agent/references/get-agent-context.md +70 -0
  46. package/skills/ae-agent/references/list-automations.md +18 -3
  47. package/skills/ae-agent/references/list-mcp-credentials.md +1 -1
  48. package/skills/ae-agent/references/mcp-token.md +3 -3
  49. package/skills/ae-agent/references/set-mcp-credential.md +0 -1
  50. package/skills/ae-agent/references/update-automation.md +18 -0
  51. package/skills/ae-analysis/SKILL.md +11 -2
  52. package/skills/ae-analysis/references/adhoc_run.md +2 -0
  53. package/skills/ae-analysis/references/ai_models.md +23 -3
  54. package/skills/ae-analysis/references/analysis_gateway_assets.md +2 -2
  55. package/skills/ae-analysis/references/command_index.md +7 -7
  56. package/skills/ae-analysis/references/cross_source_config.md +84 -0
  57. package/skills/ae-analysis/references/dashboard_update.md +1 -1
  58. package/skills/ae-analysis/references/user_tag_create.md +23 -1
  59. package/skills/ae-analysis/references/user_tag_models.md +9 -1
  60. package/skills/ae-analysis/references/user_tag_update.md +14 -2
  61. package/skills/ae-data-integration/SKILL.md +2 -1
  62. package/skills/ae-data-integration/references/dimension-routing.md +36 -0
  63. package/skills/ae-data-integration/references/local-analysis.md +2 -0
  64. package/skills/ae-data-integration/references/source-inspect.md +1 -1
  65. package/skills/ae-data-integration/references/tracking-plan.md +1 -1
  66. package/skills/ae-data-integration/references/transform.md +1 -1
  67. package/skills/ae-data-integration/references/ue-mapping.md +5 -2
  68. package/skills/ae-data-integration/references/ue-routing.md +17 -1
  69. package/skills/ae-generate-tracking-plan/SKILL.md +25 -13
  70. package/skills/ae-generate-tracking-plan/references/business-dimension-mapping.md +1 -1
  71. package/skills/ae-kb/SKILL.md +107 -36
  72. package/skills/ae-kb/references/query-workflow.md +59 -29
  73. package/skills/ae-kb/references/versions.md +46 -0
  74. package/skills/ae-system/SKILL.md +1 -1
  75. package/skills/ae-use-agent/SKILL.md +42 -0
  76. package/skills/ae-use-agent/references/local-agent.md +114 -0
  77. package/dist/auth-UMJAA72O.js +0 -15
  78. package/dist/chunk-533JK3YX.js +0 -72
  79. package/dist/chunk-O6K2ODUE.js +0 -318
  80. package/dist/chunk-RWXIXOZG.js +0 -326
  81. package/dist/client-N7NGZUNI.js +0 -204
  82. package/skills/ae-agent/references/auto-provision-mcp-credentials.md +0 -57
@@ -36,6 +36,7 @@ Use this skill when the user wants to bring a **local data file** (CSV/TSV/TXT/J
36
36
  | Generate / upload a project-level tracking plan (source material is PRD / chat / template / code; deliverable is a real platform tracking plan) | ae-generate-tracking-plan |
37
37
  | Upload documents / URLs to a knowledge base | ae-kb |
38
38
  | Reports / dashboards / queries / governance on data already in AE | ae-analysis |
39
+ | Dimension / dictionary data (a stable-entity lookup — city / product / device) to load as a dimension table bound to a property | ae-metadata |
39
40
 
40
41
  This skill also produces a tracking-plan draft (`source_type: data`) as a governance prerequisite; that draft is an input to ae-generate-tracking-plan, not a substitute for its five-phase platform plan.
41
42
 
@@ -43,7 +44,7 @@ This skill also produces a tracking-plan draft (`source_type: data`) as a govern
43
44
 
44
45
  Walk the four submodules in order. Each submodule is its own reference; follow it and come back here for the next step.
45
46
 
46
- 1. **Source — business identification.** Read [references/source-inspect.md](references/source-inspect.md). Profile every file fully, infer its business meaning using business-doc / user-prompt priors, then pick a branch via [references/ue-routing.md](references/ue-routing.md).
47
+ 1. **Source — business identification.** Read [references/source-inspect.md](references/source-inspect.md). Profile every file fully, infer its business meaning using business-doc / user-prompt priors, then pick a branch via [references/ue-routing.md](references/ue-routing.md): UE ingestion, dimension routing ([references/dimension-routing.md](references/dimension-routing.md)), or local analysis.
47
48
  2. **Reuse check.** If the profile is `ue_eligible`, read [references/reuse.md](references/reuse.md) and match the recommended mapping against the handoff index. `reuse` searches the current directory's `.ae-cli/data-integration/` upward, then `~/.ae-cli/data-integration/`, so a package written elsewhere is still found. A match proposes a frozen package; after one explicit confirmation, run the returned `transform.mjs` command and jump to Sink (step 5). No match → continue.
48
49
  3. **Tracking plan.** Read [references/tracking-plan.md](references/tracking-plan.md). The plan is generated from the mapping (`plan --mapping`), so confirm the recommended mapping's key system fields with the user first — `mode`, `#account_id`/`#distinct_id`, `#time` + timezone, `#event_name`, `#ip`/`#uuid` (see [references/transform.md](references/transform.md) steps 1–5) — then generate the event/property plan and get a single explicit confirmation from the user before touching data. The plan is a separate, required deliverable from the transform mapping: a user who supplies a column→field mapping directly has **not** completed this step, so build the plan from the confirmed mapping anyway. `user_set` still requires a plan (no events; every property becomes a user property). This step runs for **every** file: a second or later file merges its new events and properties into the existing project plan (tracking-plan.md step 4) — an existing plan is never a reason to skip it.
49
50
  4. **Transform.** Read [references/transform.md](references/transform.md). Map columns to AE system fields and properties, convert, and quarantine dirty rows per [references/ue-mapping.md](references/ue-mapping.md).
@@ -0,0 +1,36 @@
1
+ # Dimension routing
2
+
3
+ Use this reference after [ue-routing.md](ue-routing.md) has classified the file as dimension data — a stable-entity lookup with no row-level identity or event time. It covers the handoff to ae-metadata; this skill does not ingest dimension data itself.
4
+
5
+ ## What dimension data is
6
+
7
+ Judge by content, never by file extension — CSV / TSV / TXT / JSON / JSONL / XLS / XLSX can all be dimension data. The classification signals live in [ue-routing.md](ue-routing.md): no row-level identity, no row-level event time, finite entity enumeration, and a join key shared with event data.
8
+
9
+ Dimension data is not the only file that fails UE prerequisites. Aggregates, pivot tables, and cumulative snapshots also lack identity/time, but those are local-analysis material, not dictionaries. The tell is the entity shape and the join key: a dimension table maps one entity code to its attributes (`city_code` → name / level), while an aggregate summarizes many rows into one measure.
10
+
11
+ Low confidence is a proposal, never a silent decision — ask the user instead of routing automatically.
12
+
13
+ ## Handoff to ae-metadata
14
+
15
+ Extract the dimension sheet / file to CSV, then hand the following commands to ae-metadata. The sequence lists entry points only; full flags live in ae-metadata's references.
16
+
17
+ Bind prerequisite (confirm before creating the table):
18
+
19
+ - The table binds to an existing property (`property_name` + `property_scope` = user or event). That property usually appears once event/user data is uploaded first (e.g. events carrying `city_code`), so dimension binding is a second-phase action after data lands.
20
+ - If the target property does not exist yet, create it first via ae-analysis metadata or a tracking plan. Never invent a property name.
21
+
22
+ Entry command sequence:
23
+
24
+ ```bash
25
+ # 1. Extract the dimension sheet/file to CSV (metadata upload accepts CSV only,
26
+ # purpose data_table.csv)
27
+ ae-cli analysis input-file upload --project-id <id> --purpose data_table.csv --file <dim.csv>
28
+ # 2. Create + bind in one step (or split into csv-write + bind-existing)
29
+ ae-cli metadata property create-and-bind-csv-dimension-table --project-id <id> \
30
+ --property-name <p> --property-scope user|event --input-file-id ifile_xxx
31
+ # 3. Later dictionary changes (add/update/delete):
32
+ ae-cli metadata data-table csv-write --operation incremental_update|replace_update \
33
+ --data-table-id <id> --input-file-id ifile_xxx
34
+ ```
35
+
36
+ Binding model: AE attaches a dimension table to a user/event property, turning it into a dict property whose values join through the table's key column to expand `--dict-columns`. See ae-metadata's dimension-table reference for the full flags.
@@ -1,5 +1,7 @@
1
1
  # Local analysis
2
2
 
3
+ Dimension / dictionary data (a stable-entity lookup with a join key) is not local-analysis material — it routes to ae-metadata as a dimension table. See [ue-routing.md](ue-routing.md) and [dimension-routing.md](dimension-routing.md).
4
+
3
5
  Keep the source on the local machine. Generated scripts and reports belong under `.ae-cli/data-integration/runs/<run-id>/` with restrictive permissions.
4
6
  Set the directory to `0700` and generated scripts/reports to `0600`.
5
7
 
@@ -63,7 +63,7 @@ Summarize both — report the shape of the distribution and the names of the val
63
63
  - **Excel date cells** — a cell whose number format is a date or date+time is read as the wall-clock timestamp shown in Excel, not as the Excel serial number stored behind it, so the column infers as `datetime` and can serve as the time field. Inspect lists every such column in a warning. Treat that warning as a question to the user, not as a note: the same column profiled as `number` before this behavior existed, so if any part of this file was already sent to AE, the property may have been received as a number and its type is now locked — it cannot be changed to datetime, and the column has to be re-sent under a new property name. Ask whether the column was uploaded before, and only map it once the user answers. Elapsed-duration formats (`[h]:mm:ss` and the equivalent built-ins) are durations rather than points in time and stay `number`.
64
64
  - **Excel formula cells** — a spreadsheet stores a formula and, next to it, the result Excel last computed. That cached result is the value: it is read normally, including a result of `0` or `""`, which are real values and not blanks. This tool never evaluates a formula and never guesses a result, so a cell holding a formula the file never computed has nothing to upload; it is read as missing and counted, as is an Excel error value (`#N/A`, `#DIV/0!`, …). Inspect reports the counts per column in a warning and `convert` repeats them in `manifest.output.unreadable_cells`. Report them: the rows are kept and the record count is unchanged, so this is the only explanation for a column that is empty in AE while the spreadsheet looks full. When a column that matters reads as missing this way, ask the user to recalculate and re-export in Excel, or to export values instead of formulas, before uploading. This covers XLSX; a legacy `.xls` workbook goes through a different parser and is not counted here.
65
65
  - **Merged cells, hidden rows, and hidden columns** — a sheet maintained by hand merges a label down the rows it covers (`区域` spanning one region's block). Excel keeps that value on the block's first row only and stores every row below it as an empty cell, so a column that looks full on screen arrives mostly missing, and the AE property built from it would be empty for most events. The same worksheet may also hide a row inside a data block or hide a whole column. None of this travels with a row, so inspect scans the worksheet structure separately and reports it under `xlsx_structure`: `merged_ranges` with `merged_range_samples` (references such as `A3:A5`, never cell text), `merged_covered_cells` per column, `hidden_rows` with `hidden_row_samples` (source row numbers as Excel numbers them), and `hidden_columns` by header name. The default read is unchanged, so the report is a question for the user, and each answer is a flag: `--fill-merged-cells` copies each block's value into the cells its own range covers — bounded to the range, never overwriting a value that is there and never inventing one when the block's own cell is empty, so it is not a forward fill; `--exclude-hidden-rows` leaves hidden rows out. Neither is on by default: those cells really are empty in the file, and a row hidden inside a data block may still be real data — unlike a hidden *worksheet* (below), which is excluded by default. Hidden columns have no flag at all; when the user confirms one is not data, list it in the mapping's `exclude_columns`. Both flags are carried into the recommended mapping as `fill_merged_cells` / `exclude_hidden_rows`, which is what makes `convert` read the rows inspect profiled — `convert` has no read flags of its own — and `convert` repeats the findings in `manifest.output.xlsx_structure`, the only record of a layout the converted rows no longer show. This covers XLSX; a legacy `.xls` workbook is not scanned, so ask the user about merged labels and hidden rows there instead of trusting silence.
66
- - **Hidden worksheets** — a worksheet hidden in the workbook is left out of the `--data-set` candidates and out of `--merge-sheets`, because a sheet the file does not show is usually scratch space, a lookup table, or a superseded draft rather than rows anyone meant to upload. Inspect lists each one under `excluded_sheets` (with `reason: hidden`); report those names to the user, since they are the only explanation for a row count lower than the workbook appears to hold. Their headers are also left out of `header_consistency`, so a stale hidden draft cannot make a mergeable workbook look ragged. A hidden sheet stays readable when the user names it in `--data-set` — the command then warns on stderr that the selected sheet is hidden. Only pass a hidden sheet after the user says that is what they want. When *every* worksheet is hidden there is no candidate left, and inspect fails with `LOCAL_DATA_ALL_DATA_SETS_HIDDEN` whose hint lists the hidden sheets; treat that as a question about which sheet holds the real data, not as an unreadable file. This detection covers XLSX only: a legacy `.xls` workbook's sheet list is unfiltered, so a hidden sheet there still appears as a candidate and is still merged — for `.xls`, ask the user to confirm the sheet list instead of trusting it.
66
+ - **Hidden worksheets** — a worksheet hidden in the workbook is left out of the `--data-set` candidates and out of `--merge-sheets`, because a sheet the file does not show is usually scratch space or a superseded draft rather than rows anyone meant to upload, but a hidden sheet can also be a dimension / dictionary table that is meant to be loaded, and that goes through dimension routing instead of being dismissed as scratch. Inspect lists each one under `excluded_sheets` (with `reason: hidden`); report those names to the user, since they are the only explanation for a row count lower than the workbook appears to hold. Their headers are also left out of `header_consistency`, so a stale hidden draft cannot make a mergeable workbook look ragged. A hidden sheet stays readable when the user names it in `--data-set` — the command then warns on stderr that the selected sheet is hidden. Only pass a hidden sheet after the user says that is what they want. When *every* worksheet is hidden there is no candidate left, and inspect fails with `LOCAL_DATA_ALL_DATA_SETS_HIDDEN` whose hint lists the hidden sheets; treat that as a question about which sheet holds the real data, not as an unreadable file. This detection covers XLSX only: a legacy `.xls` workbook's sheet list is unfiltered, so a hidden sheet there still appears as a candidate and is still merged — for `.xls`, ask the user to confirm the sheet list instead of trusting it.
67
67
  - **Excel sheets** — `--merge-sheets` streams every visible worksheet in file order instead of a single selected sheet; otherwise ask which sheet/`--data-set` to use. Inspect also reports `header_consistency` (`all_same` or `different`) across a workbook's sheets, with `header_details` listing each sheet's header row when they differ; prefer `--merge-sheets` only when headers match. Matching headers establish a shared structure, not disjoint rows: a detail sheet and a summary sheet, or `1月` and `1月修订版`, usually carry identical headers and would be merged and reported twice over. Before merging, confirm with the user that the sources are mutually exclusive partitions (one month per sheet, no overlap) rather than overlapping, revised, or derived views of the same rows, and show each sheet's row count and time coverage range in that confirmation so an overlap is visible. The same rule applies to repeated `--input-file`.
68
68
  - **Multi-file type conflicts** — when the same column has different inferred types across files, present each conflict and resolve with `--type-resolutions` on `convert` (see [transform](transform.md)).
69
69
 
@@ -10,7 +10,7 @@ Generate and confirm the event/property plan **before** any transform or upload.
10
10
  ## Sub-steps
11
11
 
12
12
  1. **Event-model decision** — reuse the UE routing result: single-table single-event `track`, single-table multi-event (event-name column), `user_set`, or `mixed`. The agent may propose splitting one table into several events (for example an ad table into `ad_show`/`ad_click` by `campaign_type`); that proposal must be confirmed by the user in the confirmation gate.
13
- 2. **Column → property draft** — confirm the recommended mapping's key system fields with the user **before** drafting: `mode` (`#type`), `#account_id`/`#distinct_id` (ask together; at least one is required — a `user_id` column can be either an anonymous or a login ID and only the user knows), `#time` + source timezone + `#zone_offset`, `#event_name` (track only; the event column or a reviewed `default_event_name`), and `#ip`/`#uuid` when the data has such a column. The exact questions and the never-infer-from-a-column-name-alone rule are [transform.md](transform.md) steps 1–5; run them here. The plan is generated from this mapping, so never draft from an unconfirmed mapping. With the system fields settled, map the remaining columns to event and/or user properties (the mapping `mode` decides). Common event properties — project-level super properties attached to every event — are defined by `ae-generate-tracking-plan`, not this import path. Name events and properties in snake_case and fill **every** `display_name`, `desc`, and `event_tag` (events also carry `event_desc`). Infer all three from field names, value distribution, samples, and business-doc / prompt priors — never leave them empty: `desc`/`event_desc` state what the item means in plain language (language follows the user), and `event_tag` picks the closest category from the canonical tag list (see the `event_tag` appendix in `../../ae-generate-tracking-plan/references/business-dimension-mapping.md`). When you cannot infer a `desc` or `event_tag`, mark it pending and ask the user for it inside the confirmation gate. Infer types (`number` / `bool` / `datetime` / enum) the same way; CSV defaults to `string`. Columns that stay uncertain or conflicting are marked pending and asked only inside the confirmation gate.
13
+ 2. **Column → property draft** — confirm the recommended mapping's key system fields with the user **before** drafting: `mode` (`#type`), `#account_id`/`#distinct_id` (ask together; at least one is required — a `user_id` column can be either an anonymous or a login ID and only the user knows), `#time` + source timezone + `#zone_offset`, `#event_name` (track only; the event column or a reviewed `default_event_name`), and `#ip`/`#uuid` when the data has such a column. The exact questions and the never-infer-from-a-column-name-alone rule are [transform.md](transform.md) steps 1–5; run them here. The plan is generated from this mapping, so never draft from an unconfirmed mapping. With the system fields settled, map the remaining columns to event and/or user properties (the mapping `mode` decides). Common event properties — project-level super properties attached to every event — are defined by `ae-generate-tracking-plan`, not this import path. Name events and properties in snake_case by default (keep an uppercase event name only when the user asks to preserve it; property names stay lowercase-only) and fill **every** `display_name`, `desc`, and `event_tag` (events also carry `event_desc`). Infer all three from field names, value distribution, samples, and business-doc / prompt priors — never leave them empty: `desc`/`event_desc` state what the item means in plain language (language follows the user), and `event_tag` picks the closest category from the canonical tag list (see the `event_tag` appendix in `../../ae-generate-tracking-plan/references/business-dimension-mapping.md`). When you cannot infer a `desc` or `event_tag`, mark it pending and ask the user for it inside the confirmation gate. Infer types (`number` / `bool` / `datetime` / enum) the same way; CSV defaults to `string`. Columns that stay uncertain or conflicting are marked pending and asked only inside the confirmation gate.
14
14
  3. **Confirmation gate (single, one pass)** — present the concrete plan, never a counts-only summary: the confirmed key system-field mapping (`mode`, `#account_id`/`#distinct_id`, `#time` + source timezone, `#event_name`; `#ip`/`#uuid` when present), then a full event table (one row per event: `event_name` + `event_tag`/`event_desc` + the properties attached to it), then a full property table (one row per property: source column → target AE name → type → `display_name`/`desc`, uncertain types highlighted; a kept-whole `object`/`array_row` lists its `parent.child` sub-properties next to the parent), plus field scope (default: plan fields only, with a full-import switch) and unrecognized/dirty-data handling. For a multi-sheet workbook, group the property table by sheet so each sheet's source columns are visible. The user answers once with ok or edits (renames, types, identity/time/event columns, add/drop columns).
15
15
  4. **Merge with the existing plan** — fetch the project's current tracking plan; same-name property type conflicts are severe, same-name events are advisory; decide append vs replace. This runs for **every** file, not just the first: when the project already has a plan (an earlier file or run), diff this file's events and properties against it and put the additions — new events, new properties, new object sub-properties from flattening — in the confirmation gate. An existing plan is never a reason to skip this step; only when every addition is already present may you skip the merge, and even then state and confirm that fact with the user.
16
16
  5. **Persist the plan** — `.ae-cli/data-integration/draft.json` → `.ae-cli/data-integration/draft.xlsx` → upload with `sdk_integration_mode=none`.
@@ -17,7 +17,7 @@ Confirm the system fields with the user before touching properties. These are th
17
17
  4. **`#event_name` (track only).** Confirm the event-name column, or a reviewed `default_event_name` when no column exists.
18
18
  5. **`#ip` / `#uuid` (optional).** Ask only when the data has an IP- or UUID-like column; map it via `ip_field`/`uuid_field`, otherwise skip. `#ip` is event data only and must be a valid IPv4/IPv6 address (a private/LAN IP is kept but reported — AE cannot geolocate it); `#uuid` must be a standard 36-character UUID. A value that violates the spec is dropped from that row only (`INVALID_IP` / `INVALID_UUID`) — the row itself is kept. The program never auto-generates a `#uuid`.
19
19
 
20
- When an `#event_name`, `#account_id`, or `#distinct_id` column's values do not satisfy AE naming rules (pure Chinese, uppercase, spaces), do not stop — scan the distinct values, list them to the user, and ask for one AE-name replacement each; record the pairs in `value_mapping` (see [UE mapping](ue-mapping.md)). A value with no matching key keeps its original text and fails validation, so confirm every distinct value is covered or excluded. The same mechanism applies to a property column via that entry's own `value_mapping`.
20
+ When an `#event_name`, `#account_id`, or `#distinct_id` column's values do not satisfy AE naming rules (pure Chinese, spaces), do not stop — scan the distinct values, list them to the user, and ask for one AE-name replacement each; record the pairs in `value_mapping` (see [UE mapping](ue-mapping.md)). Event names are lowercased by default; keep an uppercase event name only when the user asks to preserve it — leave a legal uppercase value unmapped so it passes through, or map an illegal value to an uppercase target (e.g. `购买` → `Purchase`); see the casing rules in [UE mapping](ue-mapping.md). A value with no matching key keeps its original text and fails validation, so confirm every distinct value is covered or excluded. The same mechanism applies to a property column via that entry's own `value_mapping` — property names stay lowercase-only, so uppercase property values still need `value_mapping`.
21
21
 
22
22
  Then confirm the property set with the user before saving the mapping. Present every property as a readable table — one row per property with source column, target AE name, and type — never dump the raw mapping JSON at the user. Whether the rows are event or user properties follows `mode`: `track` → **event properties**, `user_set` (or another profile mode) → **user properties**, `mixed` → the same set applies to both event and user rows.
23
23
 
@@ -66,12 +66,14 @@ Each mapped system field carries a value spec, enforced at both inspect (warning
66
66
  | Field | Value spec | On violation (convert) |
67
67
  | --- | --- | --- |
68
68
  | `#account_id` / `#distinct_id` | Non-empty string, at most 128 characters | Row error `MISSING_USER_ID` (absent) / `USER_ID_TOO_LONG` (>128) |
69
- | `#event_name` | `^[a-z][a-z0-9_]{0,49}$` (lowercase snake_case, letter-leading, ≤50 chars) | Row error `INVALID_EVENT_NAME` |
69
+ | `#event_name` | `^[A-Za-z][A-Za-z0-9_]{0,49}$` (letter-leading letters/digits/underscore, ≤50 chars; lowercase by default, uppercase kept only on user request) | Row error `INVALID_EVENT_NAME` |
70
70
  | `#time` | One of the supported formats, within 3 years back / 3 days forward | Row error `INVALID_TIME` / `TIME_OUT_OF_RANGE` |
71
71
  | `#ip` | Valid IPv4 or IPv6. Event data only | Field skip `INVALID_IP`; a private/LAN IP is kept and reported — AE cannot geolocate it |
72
72
  | `#zone_offset` | Integer -12..14 (or an IANA name for `zone_offset_value`). Event data only | Row error `INVALID_ZONE_OFFSET` |
73
73
  | `#uuid` | Standard 36-character UUID (`xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`). Both data kinds | Field skip `INVALID_UUID` |
74
74
 
75
+ Event-name case is decided entirely by the mapping, never by the CLI — the CLI performs no case conversion and has no flag for it. The name AE receives is the source `event_name_field` value (or `default_event_name` when there is no event column), optionally replaced by a `value_mapping.event_name` target; `data-integration plan --event-name` passes each name through unchanged. Lowercase is the default: lowercase the name when writing the mapping (a `value_mapping.event_name` entry such as `Purchase` → `purchase`, or a lowercase `default_event_name`). To keep uppercase, skip that lowercasing: leave a legal uppercase source value unmapped so it passes through, write an uppercase `value_mapping.event_name` target only when the source value is not itself a legal name (e.g. `购买` → `Purchase`), or write an uppercase `default_event_name` / `--event-name`.
76
+
75
77
  A field skip is not a row failure: the row is kept with its other fields, and the count is reported in `manifest.output.skipped_fields` so the agent tells the user at the end. `#uuid` is never auto-generated — it comes only from a mapped source column.
76
78
 
77
79
  Mapping keys: `account_id_field`, `distinct_id_field`, `time.field`, `event_name_field` (or a reviewed `default_event_name`), `ip_field`, `uuid_field`. Inspect surfaces every identity-shaped column in `identity_candidates` (name, `account`/`distinct` kind, unique and missing ratios) so the agent can present all candidates for confirmation. When the required identity column is absent, use an explicit `account_id_value`/`distinct_id_value` placeholder or `random_pool` — a user decision, never invented.
@@ -147,7 +149,8 @@ Values with an explicit offset or `Z` are parsed by the JavaScript `Date` constr
147
149
  - Identity values remain strings and are at most 128 characters.
148
150
  - Source timezone is an IANA name derived from user/project context.
149
151
  - `#zone_offset`, when set, is a whole-hour integer in -12..14 (or an IANA name/column that resolves to one) and is emitted inside `properties`, never at the top level.
150
- - Event/property names are lowercase snake_case, begin with a letter, and are at most 50 characters.
152
+ - Event names begin with a letter and are at most 50 characters; lowercase is the default, uppercase is kept only when the user asks to preserve it.
153
+ - Property names are lowercase snake_case (letter-leading, at most 50 characters).
151
154
  - Target property names are unique and do not collide with UE system fields.
152
155
  - Types are one of `string`, `number`, `boolean`, `datetime`, `list`, `object`, or `array_row`.
153
156
  - Text is at most 2 KB; numbers stay within -9E15..9E15.
@@ -20,7 +20,7 @@ Classification order:
20
20
  5. Rows that mix track and user-profile facts in one file use `mixed` with a `record_type_field`; require explicit review.
21
21
  6. Low-confidence output is a proposal, never silent approval.
22
22
 
23
- Aggregated metrics, pivot tables, cross-tabs, model outputs, free-form documents, and records without real identity/time should normally use local analysis.
23
+ Aggregated metrics, pivot tables, cross-tabs, model outputs, and free-form documents should normally use local analysis. Records without real identity/time are checked against dimension routing next and fall to local analysis only if they are not a stable-entity lookup.
24
24
 
25
25
  ### Time coverage is not native granularity
26
26
 
@@ -44,6 +44,22 @@ point, and do not proceed until they answer:
44
44
  An unanswered question, a cumulative snapshot, or overlapping periods route to local analysis
45
45
  instead.
46
46
 
47
+ ## Route to dimension table
48
+
49
+ A dimension / dictionary table describes stable entities (city, product, device): a lookup that maps an entity code to its attributes. It has no row-level identity or event time, so it fails the UE prerequisites above, but it is not local-analysis material either — it belongs in AE as a dimension table bound to a property.
50
+
51
+ Classification order (UE first, dimension second, local analysis last):
52
+
53
+ 1. Satisfy the UE must-holds above → UE ingestion wins; never route an identity/time-bearing file here.
54
+ 2. Fail the UE prerequisites **and** match most of these dimension signals → dimension routing:
55
+ - No row-level identity: no `account_id` / `distinct_id` column. A `code` / `id` / `no` key is an entity code, not a user identity.
56
+ - No row-level event time: no `#time` column. If time exists, it is an effective / expiry interval, not an event occurrence.
57
+ - Finite enumeration: few rows, each describing one entity's attributes (code → name / level), not facts accumulating over time.
58
+ - A join key: a column shared with event data (`city_code`, `sku_id`, `device_id`) whose values are descriptive attributes, not measures.
59
+ 3. Otherwise → local analysis.
60
+
61
+ See [references/dimension-routing.md](references/dimension-routing.md) for the handoff.
62
+
47
63
  ## Route to local analysis
48
64
 
49
65
  Choose local analysis when:
@@ -79,7 +79,7 @@ Phase 0 → 1 → 2 → 3 → 4, do not skip steps.
79
79
  > CLI messages, and auto-track/i18n-owned labels must come from `src/tracking/i18n` via `AE_LANG=<user_lang>` and `draft.meta.lang`.
80
80
  > When a localized label is needed, inspect `src/tracking/i18n` and use the existing resource key/value; do not invent translations from the model.
81
81
  > If template business text needs localization and the CLI/i18n resources do not provide it, preserve the imported text and ask the user before rewriting business semantics.
82
- > Only identifier fields like `event_name`, `prop_name` remain in English snake_case (canonical format).
82
+ > Only identifier fields like `event_name`, `prop_name` remain in English. Property names are `snake_case`; event names are lowercase by default, uppercase only when the user asks to preserve it.
83
83
  > This skill only cares about command behavior, not internal implementation.
84
84
 
85
85
  ---
@@ -111,7 +111,7 @@ If **not** in a sandbox environment, ask exactly:
111
111
  ```text
112
112
  Choose your source material (up to 2):
113
113
 
114
- 1 - Product document (local path, image file, or folder) — Extract events and properties from product docs; supports md/pdf/docx/URL/images (png/jpg/jpeg/webp)
114
+ 1 - Product document (local path, image file, or folder) — Extract events and properties from product docs; supports md/pdf/docx/xlsx/pptx/URL/images (png/jpg/jpeg/webp)
115
115
  2 - Detailed description (conversational) — Describe app business flow, core features, user behaviors, monetization model, etc.
116
116
  3 - Codebase (local project path; hidden in sandbox) — Analyze source code to extract events and properties
117
117
  4 - Pre-built template (built-in industry and game genre templates) — Select a built-in template
@@ -126,7 +126,7 @@ If in a sandbox environment, ask exactly:
126
126
  ```text
127
127
  Choose your source material (up to 2):
128
128
 
129
- 1 - Product document (sandbox workspace path, uploaded attachment, URL, image file, or folder) — Extract events and properties from product docs; supports md/pdf/docx/URL/images (png/jpg/jpeg/webp). You can attach/upload relevant files here.
129
+ 1 - Product document (sandbox workspace path, uploaded attachment, URL, image file, or folder) — Extract events and properties from product docs; supports md/pdf/docx/xlsx/pptx/URL/images (png/jpg/jpeg/webp). You can attach/upload relevant files here.
130
130
  2 - Detailed description (conversational) — Describe app business flow, core features, user behaviors, monetization model, etc.
131
131
  3 - Pre-built template (built-in industry and game genre templates) — Select a built-in template
132
132
  4 - Modify existing tracking plan (sandbox workspace path) — Import an existing tracking plan xlsx as baseline for modification; can be combined with Product doc / Description, but NOT with Pre-built template
@@ -142,7 +142,7 @@ User can multi-select (max 2). Interpret numbers by the **visible list shown to
142
142
 
143
143
  Canonical source material options (non-sandbox numbering):
144
144
 
145
- 1. **Product document** ****(****local path, sandbox workspace path, uploaded attachment, URL, image file, or folder) — Extract events and properties from product docs; supports md/pdf/docx/URL/images (png/jpg/jpeg/webp)
145
+ 1. **Product document** ****(****local path, sandbox workspace path, uploaded attachment, URL, image file, or folder) — Extract events and properties from product docs; supports md/pdf/docx/xlsx/pptx/URL/images (png/jpg/jpeg/webp)
146
146
  2. **Detailed description** (conversational) — Describe app business flow, core features, user behaviors, monetization model, etc.
147
147
  3. **Codebase** (local project path; hidden in sandbox) — Analyze source code to extract events and properties
148
148
  4. **Pre-built template** (built-in industry and game genre templates) — Select a built-in template (run `AE_LANG=<user_lang> ae-cli tracking plan list-templates --json` to see available templates)
@@ -210,7 +210,7 @@ Based on user selection, determine source material type and record to `meta.sour
210
210
  | Severity | Examples | Handling | User Action |
211
211
  |---|---|---|---|
212
212
  | 🔧 **Minor** (auto-fixable) | `display_name` duplicate, `array_row` sub-property inconsistency, event name duplicate | `--fix` auto-fixes, writes to `draft.json`. Inform user of what was fixed. | None (informed) |
213
- | ⚠️ **Medium** (needs confirmation) | snake_case violation, property name duplicate, invalid property type, nested property parent is not a composite type | List each issue with current value → suggested fix. User confirms item by item before writing to `draft.json`. | Confirm each fix |
213
+ | ⚠️ **Medium** (needs confirmation) | property name snake_case violation, property name duplicate, invalid property type, nested property parent is not a composite type | List each issue with current value → suggested fix. User confirms item by item before writing to `draft.json`. | Confirm each fix |
214
214
  | 🛑 **Severe** | File cannot be parsed, or events array is empty after import | Reject. Tell user the specific issue. User fixes **original file** and re-imports. | Fix original file |
215
215
 
216
216
  **Medium issue confirmation format**:
@@ -219,7 +219,7 @@ Based on user selection, determine source material type and record to `meta.sour
219
219
 
220
220
  | # | Issue | Location | Current | Suggested |
221
221
  |---|-------|----------|---------|------------|
222
- | 1 | snake_case | event_name | UserLogin | user_login |
222
+ | 1 | snake_case | event_name | UserLogin | user_login (keep UserLogin only if the user asked to preserve case) |
223
223
  | 2 | snake_case | prop_name | vipLevel | vip_level |
224
224
  | 3 | invalid type | property "level" | integer | number |
225
225
 
@@ -242,7 +242,7 @@ Based on user selection, determine source material type and record to `meta.sour
242
242
  - **Events**: Player interaction actions (click/swipe/trigger), scene transitions, game state changes (start/pause/end), business flow nodes (purchase/upgrade/unlock)
243
243
  - **Event Properties**: Action parameters (bullet type/enemy level/item ID), state values (score/HP/coins), context (level ID/difficulty/mode)
244
244
  - **User Properties**: Persistent state (level/experience/VIP/cumulative spend)
245
- 5. Map extracted results to AE naming conventions (`snake_case` event names + `display_name` in user's language)
245
+ 5. Map extracted results to AE naming conventions (event names lowercase by default, uppercase only on request; property names `snake_case`; + `display_name` in user's language)
246
246
  6. Confirm extracted results with user, supplement missing items
247
247
 
248
248
  **Business Dimension Confirmation**:
@@ -386,7 +386,7 @@ When the user selects the **Data sample / file profile** option (`source_type =
386
386
  3. **Column → property mapping draft**:
387
387
  - Identify system columns first: time field, `distinct_id` / `account_id`, event-name column, user-property-name column.
388
388
  - Map the remaining columns to event properties / user properties / super properties.
389
- - Naming: `snake_case` event/property names + `display_name` + `desc` + `event_tag` (language follows the user's input).
389
+ - Naming: `snake_case` property names, event names lowercase by default (uppercase only on request) + `display_name` + `desc` + `event_tag` (language follows the user's input).
390
390
  - Type inference: CSV columns default to `string`; infer `number` / `bool` / `datetime` / enum from field name + value distribution + business doc/prompt priors. **Uncertain or conflicting columns are marked "to-confirm" and asked only inside the gate** (do not ask column-by-column beforehand).
391
391
  4. **Single confirmation gate** (replaces Phase 2, see below).
392
392
  5. **Merge with existing plan** (reuse Phase 4.1/4.2 conflict detection).
@@ -656,7 +656,7 @@ Draft
656
656
  **Property types** (enum): `string` / `number` / `bool` / `datetime` /
657
657
  `object` (single object, with sub-properties) / `array_row` (object array, supports `parent.child` nesting) / `array_string` (string array)
658
658
 
659
- **Naming rules**: Event names / property names must be `snake_case`; use `display_name` field for human-readable names.
659
+ **Naming rules**: Property names must be `snake_case`; event names are lowercase by default, and uppercase is kept only when the user asks to preserve it. Use `display_name` for human-readable names.
660
660
 
661
661
  ### 1.2 Merge Source Materials
662
662
 
@@ -674,9 +674,20 @@ Earlier sources take precedence — same-name events keep the earlier version, l
674
674
  - ⚠️ **No model-invented translations for template labels**: When replacing or explaining a localized template-owned label, use the exact value from `src/tracking/i18n` resources. If no corresponding resource exists, preserve the template text and ask the user before changing semantics.
675
675
  - ⚠️ **event_tag is not free-form model translation**: Do not manually map `业务事件`/`系统事件` to another language. Preserve template tags, or rely on `src/tracking/i18n` and autotrack generation for system labels when the CLI owns them.
676
676
  - **codebase**: Scan project source directory, extract events/properties from business logic; **same-name events merge prop_names without overwriting existing fields**; new items `source: "codebase"`
677
- - **prd**: Read all user-provided product documents (md / pdf / docx / URL / images), extract events and properties from each file; **same-name events merge prop_names without overwriting existing fields**; image files analyzed via multimodal interpretation of UI elements and interaction flows; all new items `source: "prd"`
677
+ - **prd**: Read all user-provided product documents (md / pdf / docx / xlsx / pptx / URL / images), extract events and properties from each file; **same-name events merge prop_names without overwriting existing fields**; image files analyzed via multimodal interpretation of UI elements and interaction flows; all new items `source: "prd"`
678
+ - **Read each format with the table below.** Preferred tool first; when it is missing, fall back rather than fail the read.
679
+ | Format | Read via |
680
+ |---|---|
681
+ | md | read directly |
682
+ | docx | `pandoc -t markdown <file>`; fallback `markitdown <file>` (`pip install markitdown` if missing); last resort `unzip -p <file> word/document.xml` and read the text |
683
+ | pdf (text) | extract text (native Read or a PDF text extractor) |
684
+ | pdf (scanned) | render pages to images, then read with vision |
685
+ | xlsx | read rows/columns with a structure-preserving reader (`openpyxl` / `pandas`, `pip install` if missing); a tracking table's row/column layout carries meaning — do NOT rely on a flattened markdown dump |
686
+ | pptx | `markitdown <file>` (`pip install markitdown` if missing) |
687
+ | png/jpg/jpeg/webp | multimodal interpretation, analyze UI elements and interaction flows |
688
+ - **xlsx is a third source-material path, distinct from the two existing xlsx flows.** A human-readable tracking table (event / property / type rows) is read row-by-row here. An **AE-format tracking-plan xlsx** goes through `import-template`; a **CSV/Excel data sample** goes through the data path (`ae-cli data-integration inspect`). Never route one into another's flow.
678
689
  - **prd path is a folder**: Recursively scan all files in the directory:
679
- - md/pdf/docx → read text content, extract events/properties
690
+ - md/pdf/docx/xlsx/pptx → read per the table above, extract events/properties
680
691
  - png/jpg/jpeg/webp → multimodal interpretation, analyze UI elements and interaction flows
681
692
  - subdirectories → recurse
682
693
  - other files → skip
@@ -817,7 +828,8 @@ AE_LANG=<user_lang> ae-cli tracking plan draft --in .ae-cli/draft.json --out .ae
817
828
  |---|---|---|
818
829
  | Display name uniqueness | Within same property pool, `display_name` must not repeat | ✅ Add distinguishing prefix |
819
830
  | Object array consistency | Same `array_row` across different events must have identical sub-properties | ✅ Fill missing sub-properties |
820
- | snake_case | Event/property names must match `^[a-z][a-z0-9_]*$` | ❌ Manual fix needed |
831
+ | Property name snake_case | Property names must match `^[a-z][a-z0-9_]*$` | ❌ Manual fix needed |
832
+ | Event name format | Event names match `^[A-Za-z][A-Za-z0-9_]*$`; lowercase by default, uppercase kept on request | ❌ Manual fix needed |
821
833
  | Property name uniqueness | Property names must not repeat | ❌ Manual fix needed |
822
834
  | Event name uniqueness | Event names must not repeat | ✅ Remove later duplicates |
823
835
 
@@ -1255,7 +1267,7 @@ Continue append (without fixing) / Modify draft / Switch to replace?
1255
1267
 
1256
1268
  #### Conflict Type B: Same-name events (advisory)
1257
1269
 
1258
- AE merge-by-name: Same-name events are not overwritten; new-name events are added.
1270
+ AE merge-by-name: Same-name events are not overwritten; new-name events are added. Event names are case-sensitive (`Purchase` ≠ `purchase`) — changing only the case creates a new event, not a rename.
1259
1271
 
1260
1272
  **Detection logic**:
1261
1273
 
@@ -434,7 +434,7 @@
434
434
 
435
435
  - 必须有 ≥2 个自定义属性
436
436
  - 必须标注事件标签(模块归属)和采集端
437
- - 命名符合 snake_case
437
+ - 命名符合 snake_case(事件名默认小写,用户要求保留大写时按用户要求)
438
438
 
439
439
  ---
440
440