@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 +20 -20
- package/bin/lark-fe-skills.js +28 -8
- package/package.json +1 -1
- package/skills/prd-fe-schedule/README.md +22 -12
- package/skills/prd-fe-schedule/SKILL.md +16 -8
- package/skills/prd-fe-schedule/scripts/schedule-core.js +121 -30
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
|
|
27
|
+
2. Install the Codex skills:
|
|
28
28
|
|
|
29
29
|
```bash
|
|
30
|
-
lark-fe-skills install
|
|
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
|
|
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
|
|
67
|
-
lark-fe-skills
|
|
68
|
-
lark-fe-skills install
|
|
69
|
-
lark-fe-skills
|
|
70
|
-
lark-fe-skills uninstall
|
|
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
|
|
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. 安装
|
|
141
|
+
2. 安装 Codex skills:
|
|
142
142
|
|
|
143
143
|
```bash
|
|
144
|
-
lark-fe-skills install
|
|
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
|
|
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
|
|
181
|
-
lark-fe-skills
|
|
182
|
-
lark-fe-skills install
|
|
183
|
-
lark-fe-skills
|
|
184
|
-
lark-fe-skills uninstall
|
|
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
|
|
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。
|
package/bin/lark-fe-skills.js
CHANGED
|
@@ -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
|
|
30
|
-
uninstall Remove
|
|
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
|
|
36
|
-
lark-fe-skills
|
|
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
|
|
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
|
|
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
|
@@ -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
|
-
-
|
|
88
|
-
-
|
|
89
|
-
-
|
|
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
|
-
-
|
|
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
|
-
-
|
|
178
|
-
-
|
|
179
|
-
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
617
|
+
out.push(`| ${cell(record.key)} | ${cell(record.mode)} | ${cell(record.type)} | ${cell(record.title)} |`)
|
|
595
618
|
}
|
|
596
|
-
|
|
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
|
-
|
|
625
|
+
out.push(`| ${cell(logical)} | ${cell(matched)} | ${cell(source)} | ${cell(value)} |`)
|
|
603
626
|
}
|
|
604
|
-
|
|
627
|
+
out.push(`
|
|
605
628
|
## Record Preview`)
|
|
606
|
-
const
|
|
607
|
-
|
|
608
|
-
|
|
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
|
-
|
|
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
|
-
|
|
635
|
+
out.push(`
|
|
626
636
|
## Pending Questions
|
|
627
637
|
`)
|
|
628
|
-
if (context.pending.length === 0)
|
|
629
|
-
else [...new Set(context.pending)].forEach((item) =>
|
|
630
|
-
|
|
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) {
|