@renkosky/lark-fe-skills 0.1.2 → 0.1.3

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
@@ -24,10 +24,10 @@ The first packaged workflow includes `prd-fe-task` for task breakdowns and `prd-
24
24
  npm install -g @renkosky/lark-fe-skills
25
25
  ```
26
26
 
27
- 2. Install the skill:
27
+ 2. Install the Codex skills:
28
28
 
29
29
  ```bash
30
- lark-fe-skills install prd-fe-task
30
+ lark-fe-skills install
31
31
  ```
32
32
 
33
33
  3. Restart Codex or open a new Codex session.
@@ -35,7 +35,7 @@ lark-fe-skills install prd-fe-task
35
35
  PATH fallback:
36
36
 
37
37
  ```bash
38
- npm exec --package=@renkosky/lark-fe-skills -- lark-fe-skills install prd-fe-task
38
+ npm exec --package=@renkosky/lark-fe-skills -- lark-fe-skills install
39
39
  ```
40
40
 
41
41
  Use it in Codex:
@@ -63,12 +63,11 @@ Official lark-cli documentation: <https://github.com/larksuite/cli/tree/main>
63
63
  ```bash
64
64
  lark-fe-skills list
65
65
  lark-fe-skills path
66
- lark-fe-skills path prd-fe-task
67
- lark-fe-skills path prd-fe-schedule
68
- lark-fe-skills install prd-fe-task
69
- lark-fe-skills install prd-fe-schedule
70
- lark-fe-skills uninstall prd-fe-task
71
- lark-fe-skills uninstall prd-fe-schedule
66
+ lark-fe-skills path <skill-name>
67
+ lark-fe-skills install
68
+ lark-fe-skills install <skill-name>
69
+ lark-fe-skills uninstall
70
+ lark-fe-skills uninstall <skill-name>
72
71
  lark-fe-skills uninstall lark-fe-task
73
72
  ```
74
73
 
@@ -83,7 +82,7 @@ lark-fe-skills uninstall lark-fe-task
83
82
 
84
83
  - npm installation and Codex skill installation are separate steps.
85
84
  - This package installs skills to `~/.codex/skills`.
86
- - Use `lark-fe-skills uninstall prd-fe-task` to reset local Codex skill installation for first-run testing.
85
+ - Use `lark-fe-skills uninstall` to reset local Codex skill installation for first-run testing.
87
86
  - Use `lark-fe-skills uninstall lark-fe-task` to remove the legacy install directory.
88
87
  - Existing Lark skills may live under `~/.agents/skills`; that is a different source.
89
88
 
@@ -103,6 +102,7 @@ lark-fe-skills uninstall lark-fe-task
103
102
  - [x] Configure and remember task classification rules with `+config-types`.
104
103
  - [x] Read custom task status/type options from the Base instead of inventing tags.
105
104
  - [x] Support dry-run previews from a profile without modifying Lark.
105
+ - [x] Reduce token usage by printing compact dry-run summaries and writing full previews to local temp files.
106
106
  - [x] Add an explicit `+create` helper that requires confirmation/`--yes` before writing records.
107
107
  - [ ] Phase 3: Implement frontend tasks from generated Markdown.
108
108
  - [ ] Default to implementing one task at a time to keep human review small and manageable.
@@ -138,10 +138,10 @@ lark-fe-skills uninstall lark-fe-task
138
138
  npm install -g @renkosky/lark-fe-skills
139
139
  ```
140
140
 
141
- 2. 安装 skill
141
+ 2. 安装 Codex skills
142
142
 
143
143
  ```bash
144
- lark-fe-skills install prd-fe-task
144
+ lark-fe-skills install
145
145
  ```
146
146
 
147
147
  3. 重启 Codex 或新开一个 Codex 会话。
@@ -149,7 +149,7 @@ lark-fe-skills install prd-fe-task
149
149
  PATH fallback:
150
150
 
151
151
  ```bash
152
- npm exec --package=@renkosky/lark-fe-skills -- lark-fe-skills install prd-fe-task
152
+ npm exec --package=@renkosky/lark-fe-skills -- lark-fe-skills install
153
153
  ```
154
154
 
155
155
  在 Codex 中使用:
@@ -177,12 +177,11 @@ lark-cli auth login --recommend
177
177
  ```bash
178
178
  lark-fe-skills list
179
179
  lark-fe-skills path
180
- lark-fe-skills path prd-fe-task
181
- lark-fe-skills path prd-fe-schedule
182
- lark-fe-skills install prd-fe-task
183
- lark-fe-skills install prd-fe-schedule
184
- lark-fe-skills uninstall prd-fe-task
185
- lark-fe-skills uninstall prd-fe-schedule
180
+ lark-fe-skills path <skill-name>
181
+ lark-fe-skills install
182
+ lark-fe-skills install <skill-name>
183
+ lark-fe-skills uninstall
184
+ lark-fe-skills uninstall <skill-name>
186
185
  lark-fe-skills uninstall lark-fe-task
187
186
  ```
188
187
 
@@ -197,7 +196,7 @@ lark-fe-skills uninstall lark-fe-task
197
196
 
198
197
  - npm 包安装和 Codex skill 安装是两步。
199
198
  - 本包默认安装到 `~/.codex/skills`。
200
- - 可以用 `lark-fe-skills uninstall prd-fe-task` 清理本地 Codex skill,方便测试首次安装流程。
199
+ - 可以用 `lark-fe-skills uninstall` 清理本地 Codex skills,方便测试首次安装流程。
201
200
  - 可以用 `lark-fe-skills uninstall lark-fe-task` 清理旧版安装目录。
202
201
  - 已有 Lark skills 可能位于 `~/.agents/skills`,这是另一类来源。
203
202
 
@@ -217,6 +216,7 @@ lark-fe-skills uninstall lark-fe-task
217
216
  - [x] 支持通过 `+config-types` 配置并记住任务分类规则。
218
217
  - [x] 从 Base 读取自定义任务状态/类型选项,不臆造 tag。
219
218
  - [x] 支持基于 profile 的 dry-run 预览,不修改 Lark。
219
+ - [x] 默认输出精简 dry-run 摘要,并把完整预览写入本地临时文件,降低 token 消耗。
220
220
  - [x] 新增显式 `+create` helper,要求确认/`--yes` 后才写入记录。
221
221
  - [ ] Phase 3:根据已生成的 Markdown 实际开发前端任务。
222
222
  - [ ] 默认一次只实现一个 Task,控制代码改动粒度,方便人工 review。
@@ -26,16 +26,14 @@ Usage:
26
26
  Commands:
27
27
  list List packaged skills.
28
28
  path Print the package skills directory or one skill path.
29
- install Copy a packaged skill to $HOME/.codex/skills.
30
- uninstall Remove an installed skill from $HOME/.codex/skills.
29
+ install Copy all packaged skills, or one named skill, to $HOME/.codex/skills.
30
+ uninstall Remove all packaged skills, or one named skill, from $HOME/.codex/skills.
31
31
 
32
32
  Examples:
33
33
  lark-fe-skills list
34
34
  lark-fe-skills path prd-fe-task
35
- lark-fe-skills path prd-fe-schedule
36
- lark-fe-skills install prd-fe-task
37
- lark-fe-skills install prd-fe-schedule
38
- lark-fe-skills uninstall prd-fe-task
35
+ lark-fe-skills install
36
+ lark-fe-skills uninstall
39
37
  lark-fe-skills uninstall lark-fe-task`)
40
38
  }
41
39
 
@@ -63,7 +61,7 @@ function printPath(skillName) {
63
61
  console.log(skillsRoot)
64
62
  }
65
63
 
66
- function installSkill(skillName = 'prd-fe-task') {
64
+ function installOneSkill(skillName) {
67
65
  assertSkillName(skillName)
68
66
 
69
67
  const source = join(skillsRoot, skillName)
@@ -96,7 +94,18 @@ function installSkill(skillName = 'prd-fe-task') {
96
94
  console.log(`Installed ${skillName} to ${target}`)
97
95
  }
98
96
 
99
- function uninstallSkill(skillName = 'prd-fe-task') {
97
+ function installSkill(skillName) {
98
+ if (skillName) {
99
+ installOneSkill(skillName)
100
+ return
101
+ }
102
+
103
+ for (const packagedSkill of packagedSkills) {
104
+ installOneSkill(packagedSkill)
105
+ }
106
+ }
107
+
108
+ function uninstallOneSkill(skillName) {
100
109
  assertSkillName(skillName, uninstallableSkills)
101
110
 
102
111
  const target = join(homedir(), '.codex', 'skills', skillName)
@@ -110,6 +119,17 @@ function uninstallSkill(skillName = 'prd-fe-task') {
110
119
  console.log(`Uninstalled ${skillName} from ${target}`)
111
120
  }
112
121
 
122
+ function uninstallSkill(skillName) {
123
+ if (skillName) {
124
+ uninstallOneSkill(skillName)
125
+ return
126
+ }
127
+
128
+ for (const packagedSkill of packagedSkills) {
129
+ uninstallOneSkill(packagedSkill)
130
+ }
131
+ }
132
+
113
133
  const [command, skillName] = process.argv.slice(2)
114
134
 
115
135
  switch (command) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@renkosky/lark-fe-skills",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Codex skills for Lark-based frontend requirement planning workflows.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -50,9 +50,11 @@ View or update task classification rules:
50
50
  Preview records:
51
51
 
52
52
  ```text
53
- [$prd-fe-schedule](path/to/SKILL.md) +dry-run [--profile <profile.yaml>] <task-md-path-or-name>
53
+ [$prd-fe-schedule](path/to/SKILL.md) +dry-run [--profile <profile.yaml>] [--verbose] [--full] <task-md-path-or-name>
54
54
  ```
55
55
 
56
+ By default, dry-run prints a compact summary and writes the complete preview to a local temp Markdown file. Use `--full` only when you need every row printed in the conversation.
57
+
56
58
  Create records after preview and confirmation:
57
59
 
58
60
  ```text
@@ -66,7 +68,7 @@ Underlying helpers:
66
68
  ```bash
67
69
  "$HOME/.codex/skills/prd-fe-schedule/scripts/config-schedule.sh" "<base-url-or-title>" "[table-id-or-name]" "[profile-path]"
68
70
  "$HOME/.codex/skills/prd-fe-schedule/scripts/config-types.sh" "[--profile <profile.yaml>]" "[<type-option>:perTask]" "[<type-option>:once]"
69
- "$HOME/.codex/skills/prd-fe-schedule/scripts/dry-run-schedule.sh" "[--profile <profile.yaml>]" "<task-md-path-or-name>"
71
+ "$HOME/.codex/skills/prd-fe-schedule/scripts/dry-run-schedule.sh" "[--profile <profile.yaml>]" "[--verbose]" "[--full]" "<task-md-path-or-name>"
70
72
  "$HOME/.codex/skills/prd-fe-schedule/scripts/create-schedule.sh" --yes "[--profile <profile.yaml>]" "<task-md-path-or-name>"
71
73
  ```
72
74
 
@@ -79,17 +81,20 @@ Underlying helpers:
79
81
 
80
82
  ## Output
81
83
 
82
- The dry-run preview includes:
84
+ The compact dry-run preview includes:
83
85
 
84
86
  - task Markdown source
85
87
  - target Base and table
86
88
  - active profile
87
- - record rule preview
88
- - field mapping table
89
- - preview rows generated from profile rules
89
+ - parsed task count and preview record count
90
+ - mapped fields summary
91
+ - one record sample by default, or two with `--verbose`
92
+ - full preview file path
90
93
  - unmapped fields and pending questions
91
94
  - a safety note that no Lark data was modified
92
95
 
96
+ The full preview file and `--full` output include record rules, the field mapping table, and all preview rows generated from profile rules.
97
+
93
98
  ## Safety
94
99
 
95
100
  - `+dry-run` is read-only.
@@ -146,9 +151,11 @@ profile 阶段会展示识别到的字段和可用任务类型。创建记录前
146
151
  预览记录:
147
152
 
148
153
  ```text
149
- [$prd-fe-schedule](path/to/SKILL.md) +dry-run [--profile <profile.yaml>] <任务Markdown路径或名称>
154
+ [$prd-fe-schedule](path/to/SKILL.md) +dry-run [--profile <profile.yaml>] [--verbose] [--full] <任务Markdown路径或名称>
150
155
  ```
151
156
 
157
+ 默认 dry-run 只在对话里输出精简摘要,并把完整预览写入本地临时 Markdown 文件。只有需要把每一行都打印到对话里时,再使用 `--full`。
158
+
152
159
  预览确认后创建记录:
153
160
 
154
161
  ```text
@@ -162,7 +169,7 @@ profile 阶段会展示识别到的字段和可用任务类型。创建记录前
162
169
  ```bash
163
170
  "$HOME/.codex/skills/prd-fe-schedule/scripts/config-schedule.sh" "<Base链接或名称>" "[表ID或表名]" "[profile路径]"
164
171
  "$HOME/.codex/skills/prd-fe-schedule/scripts/config-types.sh" "[--profile <profile.yaml>]" "[<类型选项>:perTask]" "[<类型选项>:once]"
165
- "$HOME/.codex/skills/prd-fe-schedule/scripts/dry-run-schedule.sh" "[--profile <profile.yaml>]" "<任务Markdown路径或名称>"
172
+ "$HOME/.codex/skills/prd-fe-schedule/scripts/dry-run-schedule.sh" "[--profile <profile.yaml>]" "[--verbose]" "[--full]" "<任务Markdown路径或名称>"
166
173
  "$HOME/.codex/skills/prd-fe-schedule/scripts/create-schedule.sh" --yes "[--profile <profile.yaml>]" "<任务Markdown路径或名称>"
167
174
  ```
168
175
 
@@ -175,17 +182,20 @@ profile 阶段会展示识别到的字段和可用任务类型。创建记录前
175
182
 
176
183
  ## 输出
177
184
 
178
- dry-run 预览包含:
185
+ 精简 dry-run 预览包含:
179
186
 
180
187
  - 任务 Markdown 来源
181
188
  - 目标 Base 和数据表
182
189
  - 当前 profile
183
- - 记录生成规则预览
184
- - 字段映射表
185
- - profile 规则生成的预览记录
190
+ - 解析到的任务数量和预览记录数量
191
+ - 字段映射摘要
192
+ - 默认 1 条记录样例,`--verbose` 输出 2 条
193
+ - 完整预览文件路径
186
194
  - 未映射字段和待确认问题
187
195
  - 未修改 Lark 数据的安全提示
188
196
 
197
+ 完整预览文件和 `--full` 输出包含记录生成规则、字段映射表,以及按 profile 规则生成的所有预览记录。
198
+
189
199
  ## 安全规则
190
200
 
191
201
  - `+dry-run` 只读。
@@ -42,7 +42,7 @@ When the user says something like "schedule these tasks", "帮我按照刚刚拆
42
42
  - which Base type options should be `once`
43
43
  - whether any type should be omitted
44
44
  4. Save the answer with `+config-types`.
45
- 5. Run `+dry-run` and show the preview.
45
+ 5. Run `+dry-run` and show the compact preview summary.
46
46
  6. Ask whether to execute scheduling with the current settings.
47
47
  7. Only run `+create` after the user explicitly confirms.
48
48
 
@@ -107,7 +107,7 @@ Use `+dry-run` when the user wants to preview Lark Base schedule records from a
107
107
  Accepted Codex invocation:
108
108
 
109
109
  ```text
110
- +dry-run [--profile <profile.yaml>] <task-md-path-or-name>
110
+ +dry-run [--profile <profile.yaml>] [--verbose] [--full] <task-md-path-or-name>
111
111
  ```
112
112
 
113
113
  Examples:
@@ -115,14 +115,17 @@ Examples:
115
115
  ```text
116
116
  +dry-run docs/frontend-tasks/2026-05-18-pr-00000.md
117
117
  +dry-run --profile ./schedule-profile.yaml pr-00000
118
+ +dry-run --full --profile ./schedule-profile.yaml pr-00000
118
119
  ```
119
120
 
120
121
  Recommended invocation:
121
122
 
122
123
  ```bash
123
- "$HOME/.codex/skills/prd-fe-schedule/scripts/dry-run-schedule.sh" "[--profile <profile.yaml>]" "<task-md-path-or-name>"
124
+ "$HOME/.codex/skills/prd-fe-schedule/scripts/dry-run-schedule.sh" "[--profile <profile.yaml>]" "[--verbose]" "[--full]" "<task-md-path-or-name>"
124
125
  ```
125
126
 
127
+ Default dry-run output is intentionally compact to reduce Codex token usage. It prints counts, key mappings, pending questions, one record sample, and a local full-preview file path. Use `--verbose` for two samples, or `--full` only when the full table must be printed in the conversation.
128
+
126
129
  ### +create
127
130
 
128
131
  Use `+create` only when the user explicitly asks to create Lark Base records. Always run or show the same preview first. The helper script requires `--yes` for actual writes.
@@ -165,21 +168,26 @@ Recommended invocation:
165
168
  - If `fields.type` is removed from the profile, do not write task type values.
166
169
  - If `defaults.assignee` is `currentUser` and a writable assignee user field exists, default owner to the current `lark-cli` user.
167
170
  - Leave start time, completion time, and work days empty unless the profile/template explicitly provides values.
171
+ - Print compact output by default and write the complete Markdown preview to a local temp file.
168
172
  - Print unmapped or ambiguous fields under pending questions.
169
173
  - `+dry-run` must not call `record-batch-create`, `record-upsert`, or any Lark write command.
170
174
 
171
175
  ## Output Rules
172
176
 
173
- The dry-run output must include:
177
+ The default dry-run output must include:
174
178
 
175
179
  - Task Markdown source.
176
180
  - Target Base token and table.
177
- - Field mapping table.
178
- - Record preview, one row per parsed frontend task.
179
- - Record-rule preview from the active profile.
181
+ - Active profile path.
182
+ - Parsed task count and preview record count.
183
+ - Mapped fields summary.
184
+ - One record sample by default, or two with `--verbose`.
185
+ - Full preview file path.
180
186
  - Unmapped fields and pending questions.
181
187
  - A clear reminder that no Lark data was modified.
182
188
 
189
+ The full preview file and `--full` output must include field mapping, record-rule preview, and every generated preview row.
190
+
183
191
  ## Safety
184
192
 
185
193
  - `+dry-run` is read-only against Lark Base.
@@ -190,4 +198,4 @@ The dry-run output must include:
190
198
  ## TODO / Roadmap
191
199
 
192
200
  - Phase 2.1: keep improving profile editing and validation hints.
193
- - Phase 2.3: optionally export dry-run preview to a local Markdown or JSON file.
201
+ - Phase 2.3: optionally add JSON preview export for integrations.
@@ -570,9 +570,31 @@ function fieldMappingRows(profile, mappedFields, rows) {
570
570
  })
571
571
  }
572
572
 
573
- function printPreview(context) {
573
+ function previewHeaders(context) {
574
+ const fields = context.profile.fields || {}
575
+ const headers = ['#', 'recordKey', 'mode', 'title']
576
+ if (fields.status) headers.push('status')
577
+ if (fields.type) headers.push('type')
578
+ if (fields.requirementId) headers.push('requirementId')
579
+ if (fields.assignee) headers.push('assignee')
580
+ if (fields.description) headers.push('description')
581
+ return headers
582
+ }
583
+
584
+ function previewRowValues(headers, row, index) {
585
+ return headers.map((header) => {
586
+ if (header === '#') return index + 1
587
+ if (header === 'recordKey') return row.recordKey
588
+ if (header === 'mode') return row.recordMode
589
+ if (header === 'assignee') return row.assigneeLabel
590
+ return row[header]
591
+ })
592
+ }
593
+
594
+ function renderFullPreview(context, previewFile) {
574
595
  const createCommand = `+create ${context.taskFile}`
575
- console.log(`# PRD FE Schedule Dry-Run
596
+ const out = []
597
+ out.push(`# PRD FE Schedule Dry-Run
576
598
 
577
599
  ## Source
578
600
 
@@ -580,6 +602,7 @@ function printPreview(context) {
580
602
  - Parsed tasks: ${context.tasks.length}
581
603
  - Profile: ${context.profilePath}
582
604
  - Preview records: ${context.rows.length}
605
+ ${previewFile ? `- Full preview file: ${previewFile}\n` : ''}
583
606
 
584
607
  ## Target
585
608
 
@@ -591,43 +614,30 @@ function printPreview(context) {
591
614
  | Key | Mode | Type | Title Template |
592
615
  | --- | --- | --- | --- |`)
593
616
  for (const record of context.profile.records) {
594
- console.log(`| ${cell(record.key)} | ${cell(record.mode)} | ${cell(record.type)} | ${cell(record.title)} |`)
617
+ out.push(`| ${cell(record.key)} | ${cell(record.mode)} | ${cell(record.type)} | ${cell(record.title)} |`)
595
618
  }
596
- console.log(`
619
+ out.push(`
597
620
  ## Field Mapping
598
621
 
599
622
  | Logical Field | Matched Base Field | Source | Example Value |
600
623
  | --- | --- | --- | --- |`)
601
624
  for (const [logical, matched, source, value] of fieldMappingRows(context.profile, context.mappedFields, context.rows)) {
602
- console.log(`| ${cell(logical)} | ${cell(matched)} | ${cell(source)} | ${cell(value)} |`)
625
+ out.push(`| ${cell(logical)} | ${cell(matched)} | ${cell(source)} | ${cell(value)} |`)
603
626
  }
604
- console.log(`
627
+ out.push(`
605
628
  ## Record Preview`)
606
- const fields = context.profile.fields || {}
607
- const headers = ['#', 'recordKey', 'mode', 'title']
608
- if (fields.status) headers.push('status')
609
- if (fields.type) headers.push('type')
610
- if (fields.requirementId) headers.push('requirementId')
611
- if (fields.assignee) headers.push('assignee')
612
- if (fields.description) headers.push('description')
613
- console.log(`| ${headers.join(' | ')} |`)
614
- console.log(`| ${headers.map(() => '---').join(' | ')} |`)
629
+ const headers = previewHeaders(context)
630
+ out.push(`| ${headers.join(' | ')} |`)
631
+ out.push(`| ${headers.map(() => '---').join(' | ')} |`)
615
632
  context.rows.forEach((row, index) => {
616
- const values = headers.map((header) => {
617
- if (header === '#') return index + 1
618
- if (header === 'recordKey') return row.recordKey
619
- if (header === 'mode') return row.recordMode
620
- if (header === 'assignee') return row.assigneeLabel
621
- return row[header]
622
- })
623
- console.log(`| ${values.map(cell).join(' | ')} |`)
633
+ out.push(`| ${previewRowValues(headers, row, index).map(cell).join(' | ')} |`)
624
634
  })
625
- console.log(`
635
+ out.push(`
626
636
  ## Pending Questions
627
637
  `)
628
- if (context.pending.length === 0) console.log('- None.')
629
- else [...new Set(context.pending)].forEach((item) => console.log(`- ${item}`))
630
- console.log(`
638
+ if (context.pending.length === 0) out.push('- None.')
639
+ else [...new Set(context.pending)].forEach((item) => out.push(`- ${item}`))
640
+ out.push(`
631
641
  ## Safety
632
642
 
633
643
  Dry-run only. No Lark Base records were created or updated.
@@ -639,6 +649,83 @@ Review the field mapping, record rules, and preview rows above.
639
649
  - If the task type layout is not right, run \`+config-types\` with the desired rules, then run dry-run again.
640
650
  - If everything looks right and the user explicitly confirms, run \`${createCommand}\`.
641
651
  - Do not create records without explicit confirmation.`)
652
+ return out.join('\n')
653
+ }
654
+
655
+ function writePreviewFile(context) {
656
+ const fileName = `prd-fe-schedule-preview-${Date.now()}.md`
657
+ const previewFile = join(tmpdir(), fileName)
658
+ writeFileSync(previewFile, renderFullPreview(context, previewFile))
659
+ return previewFile
660
+ }
661
+
662
+ function recordSummary(row, index) {
663
+ const parts = [`${index + 1}. ${row.title || '(untitled)'}`]
664
+ if (row.type) parts.push(`type=${row.type}`)
665
+ if (row.status) parts.push(`status=${row.status}`)
666
+ if (row.assigneeLabel) parts.push(`assignee=${row.assigneeLabel}`)
667
+ return parts.join(' | ')
668
+ }
669
+
670
+ function printCompactPreview(context, previewFile, options = {}) {
671
+ const createCommand = `+create ${context.taskFile}`
672
+ const typeValues = [...new Set(context.rows.map((row) => row.type).filter(Boolean))]
673
+ const mapped = Object.entries(context.profile.fields || {})
674
+ .filter(([logical]) => context.mappedFields[logical])
675
+ .map(([logical, actual]) => `${logical}=${actual}`)
676
+ const pending = [...new Set(context.pending)]
677
+ console.log(`# PRD FE Schedule Dry-Run Summary
678
+
679
+ ## Source
680
+
681
+ - Task Markdown: ${context.taskFile}
682
+ - Parsed tasks: ${context.tasks.length}
683
+ - Profile: ${context.profilePath}
684
+ - Preview records: ${context.rows.length}
685
+ - Full preview file: ${previewFile}
686
+
687
+ ## Target
688
+
689
+ - Base: ${context.baseToken}
690
+ - Table: ${context.tableName} (${context.tableId})
691
+
692
+ ## Summary
693
+
694
+ - Record rules: ${context.profile.records.map((record) => `${record.key}:${record.mode}${record.type ? `/${record.type}` : ''}`).join(', ')}
695
+ - Mapped fields: ${mapped.length ? mapped.join(', ') : '(none)'}
696
+ - Task types: ${typeValues.length ? typeValues.join(', ') : '(none)'}
697
+ - Assignee: ${context.rows.find((row) => row.assigneeLabel)?.assigneeLabel || '(none)'}
698
+ - Pending questions: ${pending.length}
699
+ `)
700
+
701
+ const sampleLimit = options.verbose ? Math.min(2, context.rows.length) : 1
702
+ console.log('## Record Samples')
703
+ context.rows.slice(0, sampleLimit).forEach((row, index) => console.log(`- ${recordSummary(row, index)}`))
704
+ if (context.rows.length > sampleLimit) console.log(`- ... ${context.rows.length - sampleLimit} more records hidden. Use --full to print all rows.`)
705
+
706
+ console.log('\n## Pending Questions')
707
+ if (pending.length === 0) console.log('- None.')
708
+ else pending.forEach((item) => console.log(`- ${item}`))
709
+
710
+ console.log(`
711
+ ## Safety
712
+
713
+ Dry-run only. No Lark Base records were created or updated.
714
+
715
+ ## Next Step
716
+
717
+ Review the summary above. Open the full preview file when you need every field and row.
718
+
719
+ - If the task type layout is not right, run \`+config-types\` with the desired rules, then run dry-run again.
720
+ - If everything looks right and the user explicitly confirms, run \`${createCommand}\`.
721
+ - Use \`--full\` only when you need to print the entire preview in the conversation.
722
+ - Do not create records without explicit confirmation.`)
723
+ }
724
+
725
+ function printPreview(context, options = {}) {
726
+ const previewFile = writePreviewFile(context)
727
+ if (options.full) console.log(renderFullPreview(context, previewFile))
728
+ else printCompactPreview(context, previewFile, options)
642
729
  }
643
730
 
644
731
  function buildContext(taskInput, profilePath) {
@@ -745,7 +832,7 @@ function commandConfigSchedule() {
745
832
  }
746
833
 
747
834
  function parseRunArgs(args) {
748
- const options = { profile: defaultProfile, yes: false }
835
+ const options = { profile: defaultProfile, yes: false, full: false, verbose: false }
749
836
  const positional = []
750
837
  for (let index = 0; index < args.length; index += 1) {
751
838
  if (args[index] === '--profile') {
@@ -753,6 +840,10 @@ function parseRunArgs(args) {
753
840
  index += 1
754
841
  } else if (args[index] === '--yes') {
755
842
  options.yes = true
843
+ } else if (args[index] === '--full') {
844
+ options.full = true
845
+ } else if (args[index] === '--verbose') {
846
+ options.verbose = true
756
847
  } else {
757
848
  positional.push(args[index])
758
849
  }
@@ -781,10 +872,10 @@ function commandDryRun() {
781
872
  writeProfile(tmp, profile, availableTypeOptions)
782
873
  options.profile = tmp
783
874
  }
784
- if (!taskInput) die('Usage: dry-run-schedule.sh [--profile <profile.yaml>] <task-md-path-or-name>', 2)
875
+ if (!taskInput) die('Usage: dry-run-schedule.sh [--profile <profile.yaml>] [--verbose] [--full] <task-md-path-or-name>', 2)
785
876
  if (!existsSync(options.profile)) die(`Profile not found: ${options.profile}. Run +config-schedule first or pass --profile <path>.`)
786
877
  requireLarkCli()
787
- printPreview(buildContext(taskInput, options.profile))
878
+ printPreview(buildContext(taskInput, options.profile), options)
788
879
  }
789
880
 
790
881
  function parseTypeArgs(args) {