takt-marp 0.1.0
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.ja.md +108 -0
- package/README.md +108 -0
- package/bin/takt-marp.mjs +24 -0
- package/fixtures/marp-slide-workflow/_workflow-smoke/README.md +23 -0
- package/fixtures/marp-slide-workflow/_workflow-smoke/brief.md +44 -0
- package/marp.config.mjs +3 -0
- package/package.json +56 -0
- package/scripts/lib/takt-marp-cli.mjs +199 -0
- package/scripts/lib/takt-marp-project-init.mjs +81 -0
- package/scripts/lib/takt-marp-project-templates.mjs +93 -0
- package/scripts/lib/takt-marp-runtime-context.mjs +24 -0
- package/scripts/lib/takt-marp-slide-workflow.mjs +453 -0
- package/scripts/takt-marp-approve-slide-workflow-state.mjs +37 -0
- package/scripts/takt-marp-build-slide-artifact.mjs +151 -0
- package/scripts/takt-marp-check-slide-workflow-state.mjs +41 -0
- package/scripts/takt-marp-render-slide-workflow-evidence.mjs +70 -0
- package/scripts/takt-marp-run-slide-workflow.mjs +435 -0
- package/scripts/takt-marp-sync-project-templates.mjs +125 -0
- package/scripts/takt-marp-validate-global-install.mjs +391 -0
- package/scripts/takt-marp-validate-package-boundary.mjs +276 -0
- package/scripts/takt-marp-validate-slide-workflow-foundation.mjs +571 -0
- package/scripts/takt-marp-validate-slide-workflow-smoke.mjs +1935 -0
- package/scripts/takt-marp-verify-delivery-artifacts.mjs +181 -0
- package/scripts/takt-marp-verify-render-evidence-metadata.mjs +133 -0
- package/templates/project/facets/instructions/takt-marp-ai-antipattern-fix.md +47 -0
- package/templates/project/facets/instructions/takt-marp-ai-antipattern-review.md +37 -0
- package/templates/project/facets/instructions/takt-marp-compose-fix.md +25 -0
- package/templates/project/facets/instructions/takt-marp-compose-review.md +30 -0
- package/templates/project/facets/instructions/takt-marp-compose-slides.md +35 -0
- package/templates/project/facets/instructions/takt-marp-compose-work-summary.md +23 -0
- package/templates/project/facets/instructions/takt-marp-deliver-build.md +30 -0
- package/templates/project/facets/instructions/takt-marp-deliver-fix.md +25 -0
- package/templates/project/facets/instructions/takt-marp-deliver-verify.md +25 -0
- package/templates/project/facets/instructions/takt-marp-design-system.md +37 -0
- package/templates/project/facets/instructions/takt-marp-intake.md +15 -0
- package/templates/project/facets/instructions/takt-marp-normalize-brief.md +24 -0
- package/templates/project/facets/instructions/takt-marp-plan-fix.md +26 -0
- package/templates/project/facets/instructions/takt-marp-plan-review.md +24 -0
- package/templates/project/facets/instructions/takt-marp-plan-work-summary.md +24 -0
- package/templates/project/facets/instructions/takt-marp-plan.md +26 -0
- package/templates/project/facets/instructions/takt-marp-polish-fix.md +25 -0
- package/templates/project/facets/instructions/takt-marp-polish-inspect.md +25 -0
- package/templates/project/facets/instructions/takt-marp-render-evidence.md +35 -0
- package/templates/project/facets/instructions/takt-marp-supervise-command.md +58 -0
- package/templates/project/facets/instructions/takt-marp-visual-generate.md +26 -0
- package/templates/project/facets/knowledge/takt-marp-repo-conventions.md +119 -0
- package/templates/project/facets/output-contracts/takt-marp-ai-antipattern-fix.md +48 -0
- package/templates/project/facets/output-contracts/takt-marp-ai-antipattern-review.md +43 -0
- package/templates/project/facets/output-contracts/takt-marp-command-fix.md +32 -0
- package/templates/project/facets/output-contracts/takt-marp-command-review.md +32 -0
- package/templates/project/facets/output-contracts/takt-marp-command-work.md +42 -0
- package/templates/project/facets/output-contracts/takt-marp-normalized-brief.md +31 -0
- package/templates/project/facets/output-contracts/takt-marp-slide-plan.md +30 -0
- package/templates/project/facets/output-contracts/takt-marp-supervision.md +45 -0
- package/templates/project/facets/personas/takt-marp-slide-planner.md +24 -0
- package/templates/project/facets/personas/takt-marp-slide-qa.md +23 -0
- package/templates/project/facets/personas/takt-marp-slide-reviewer.md +22 -0
- package/templates/project/facets/personas/takt-marp-slide-reviser.md +22 -0
- package/templates/project/facets/personas/takt-marp-slide-supervisor.md +24 -0
- package/templates/project/facets/personas/takt-marp-slide-writer.md +22 -0
- package/templates/project/facets/policies/takt-marp-general-slide-quality.md +91 -0
- package/templates/project/facets/policies/takt-marp-slide-quality.md +73 -0
- package/templates/project/facets/policies/takt-marp-svg-first-visual.md +66 -0
- package/templates/project/facets/policies/takt-marp-worker-boundary.md +32 -0
- package/templates/project/workflows/takt-marp-slide-ai-quality-gate.yaml +125 -0
- package/templates/project/workflows/takt-marp-slide-compose.yaml +209 -0
- package/templates/project/workflows/takt-marp-slide-deliver.yaml +164 -0
- package/templates/project/workflows/takt-marp-slide-plan.yaml +213 -0
- package/templates/project/workflows/takt-marp-slide-polish.yaml +158 -0
package/README.ja.md
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# takt-marp
|
|
2
|
+
|
|
3
|
+
[English](README.md)
|
|
4
|
+
|
|
5
|
+
Marpスライドデッキと、半自動でデッキを生成するためのTAKT workflowを管理するリポジトリです。
|
|
6
|
+
|
|
7
|
+
## TAKT Marp workflow
|
|
8
|
+
|
|
9
|
+
このworkflowは `slides/<deck>/brief.md` を起点に、`plan`、`compose`、`polish`、`deliver` の状態へ進めます。
|
|
10
|
+
|
|
11
|
+
詳細なworkflow contract: [docs/marp-slide-workflow.md](docs/marp-slide-workflow.md)
|
|
12
|
+
|
|
13
|
+
### 1. briefを作成する
|
|
14
|
+
|
|
15
|
+
`slides/<deck>/brief.md` を作成します。コマンドに渡すtargetは `brief.md` ではなく、deck directory の `slides/<deck>` です。
|
|
16
|
+
|
|
17
|
+
最小構成:
|
|
18
|
+
|
|
19
|
+
- `Goal`
|
|
20
|
+
- `Core Message`
|
|
21
|
+
- `Audience Context`
|
|
22
|
+
- `Output Requirements`
|
|
23
|
+
|
|
24
|
+
Output Requirementsの例:
|
|
25
|
+
|
|
26
|
+
```md
|
|
27
|
+
## Output Requirements
|
|
28
|
+
- Format: Marp
|
|
29
|
+
- Language: Japanese
|
|
30
|
+
- Target slide count: 5
|
|
31
|
+
- Deliverables: html, pdf
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### 2. workflowを実行する
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npm run slide:plan -- "slides/<deck>"
|
|
38
|
+
npm run slide:approve -- "slides/<deck>" plan --by <name>
|
|
39
|
+
npm run slide:compose -- "slides/<deck>"
|
|
40
|
+
npm run slide:approve -- "slides/<deck>" compose --by <name>
|
|
41
|
+
npm run slide:polish -- "slides/<deck>"
|
|
42
|
+
npm run slide:deliver -- "slides/<deck>"
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
targetは `slides/<deck>` を指定します。
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
npm run slide:plan -- "slides/<deck>"
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
人間承認は `plan` と `compose` に対してのみ `slide:approve` で記録します。`review`、`revise`、`qa`、`build-qa` はworkflow内部の責務であり、トップレベルコマンドではありません。
|
|
52
|
+
|
|
53
|
+
### 3. 生成されるファイル
|
|
54
|
+
|
|
55
|
+
```text
|
|
56
|
+
slides/<deck>/
|
|
57
|
+
brief.normalized.md
|
|
58
|
+
plan.md
|
|
59
|
+
design-system.md
|
|
60
|
+
SLIDES.md
|
|
61
|
+
images/*.svg
|
|
62
|
+
review/*.md
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
`design-system.md` はデッキ単位のtypography、spacing、layout、visual、color、QA tokenを定義します。`SLIDES.md` はスライドごとの個別style調整ではなく、Marp class経由でそれらのtokenを使う前提です。
|
|
66
|
+
|
|
67
|
+
### 4. polishとdeliverの範囲
|
|
68
|
+
|
|
69
|
+
`polish` は見た目の検査と修正loopを担当します。
|
|
70
|
+
|
|
71
|
+
- SVG参照とXML妥当性
|
|
72
|
+
- スライド枠への収まり、文字の収まり、図の大きさ、ページ番号との干渉
|
|
73
|
+
- layout選択と段組比率
|
|
74
|
+
- typography consistency: 文字間、行間、サイズ階層
|
|
75
|
+
- spatial balance: 上寄り、左寄り、大きな意図しない余白、視覚重心
|
|
76
|
+
- design-system usage: token化されたCSS、スライドごとのstyle drift防止
|
|
77
|
+
|
|
78
|
+
`deliver` は要求された成果物を生成します。PDF生成は対象deckの `SLIDES.md` だけをbuildします。
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
npm run build:pdf -- <deck>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### 5. 検証
|
|
85
|
+
|
|
86
|
+
軽量なローカル確認には foundation validation を使います。
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
npm test
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
workflow routing、state gate、render evidence、delivery verification、approval handling を変更した場合は smoke validation を実行します。
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
npm run slide:smoke -- --keep
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
smoke validation は fixture から一時的な `_workflow-smoke` deck を作成し、invalid target、approval failure path、`plan` -> `compose` -> `polish` -> `deliver` の一連の実行、render evidence metadata、delivery artifact、rerun/force behavior を検証します。`--keep` を付けると、生成された deck と report を `slides/_workflow-smoke/` に残して確認できます。
|
|
99
|
+
|
|
100
|
+
### Smoke fixture
|
|
101
|
+
|
|
102
|
+
小さな入力fixtureがあります。
|
|
103
|
+
|
|
104
|
+
```text
|
|
105
|
+
fixtures/marp-slide-workflow/_workflow-smoke/
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
新しいbriefを作らずにworkflow全体を試したい場合は、`slides/` 配下へコピーして使います。
|
package/README.md
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# takt-marp
|
|
2
|
+
|
|
3
|
+
[日本語](README.ja.md)
|
|
4
|
+
|
|
5
|
+
Marp slide decks and a TAKT workflow for semi-automated deck generation.
|
|
6
|
+
|
|
7
|
+
## TAKT Marp workflow
|
|
8
|
+
|
|
9
|
+
The workflow starts from `slides/<deck>/brief.md` and moves through `plan`, `compose`, `polish`, and `deliver`.
|
|
10
|
+
|
|
11
|
+
Detailed workflow contract: [docs/marp-slide-workflow.md](docs/marp-slide-workflow.md)
|
|
12
|
+
|
|
13
|
+
### 1. Create a brief
|
|
14
|
+
|
|
15
|
+
Create `slides/<deck>/brief.md`. Commands take the deck directory `slides/<deck>`, not the `brief.md` file path.
|
|
16
|
+
|
|
17
|
+
Minimum sections:
|
|
18
|
+
|
|
19
|
+
- `Goal`
|
|
20
|
+
- `Core Message`
|
|
21
|
+
- `Audience Context`
|
|
22
|
+
- `Output Requirements`
|
|
23
|
+
|
|
24
|
+
Example output requirement:
|
|
25
|
+
|
|
26
|
+
```md
|
|
27
|
+
## Output Requirements
|
|
28
|
+
- Format: Marp
|
|
29
|
+
- Language: Japanese
|
|
30
|
+
- Target slide count: 5
|
|
31
|
+
- Deliverables: html, pdf
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### 2. Run the workflows
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npm run slide:plan -- "slides/<deck>"
|
|
38
|
+
npm run slide:approve -- "slides/<deck>" plan --by <name>
|
|
39
|
+
npm run slide:compose -- "slides/<deck>"
|
|
40
|
+
npm run slide:approve -- "slides/<deck>" compose --by <name>
|
|
41
|
+
npm run slide:polish -- "slides/<deck>"
|
|
42
|
+
npm run slide:deliver -- "slides/<deck>"
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Use `slides/<deck>` as the target:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
npm run slide:plan -- "slides/<deck>"
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Human approval is recorded by `slide:approve` for `plan` and `compose` only. `review`, `revise`, `qa`, and `build-qa` are internal workflow responsibilities, not top-level commands.
|
|
52
|
+
|
|
53
|
+
### 3. Generated files
|
|
54
|
+
|
|
55
|
+
```text
|
|
56
|
+
slides/<deck>/
|
|
57
|
+
brief.normalized.md
|
|
58
|
+
plan.md
|
|
59
|
+
design-system.md
|
|
60
|
+
SLIDES.md
|
|
61
|
+
images/*.svg
|
|
62
|
+
review/*.md
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
`design-system.md` defines deck-local typography, spacing, layout, visual, color, and QA tokens. `SLIDES.md` should use those tokens through Marp classes instead of per-slide style tweaks.
|
|
66
|
+
|
|
67
|
+
### 4. Polish and delivery scope
|
|
68
|
+
|
|
69
|
+
`polish` is responsible for visual inspection and repair loops:
|
|
70
|
+
|
|
71
|
+
- SVG references and XML validity
|
|
72
|
+
- slide frame fit, text fit, figure size, page number interference
|
|
73
|
+
- layout choice and split ratios
|
|
74
|
+
- typography consistency: letter spacing, line height, size hierarchy
|
|
75
|
+
- spatial balance: top/left bias, large unintended blank areas, visual center of gravity
|
|
76
|
+
- design-system usage: tokenized CSS, no per-slide style drift
|
|
77
|
+
|
|
78
|
+
`deliver` is responsible for requested artifacts. PDF generation builds only the target deck's `SLIDES.md`:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
npm run build:pdf -- <deck>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### 5. Validation
|
|
85
|
+
|
|
86
|
+
Use the foundation validation for fast local checks:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
npm test
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Use the smoke validation when changing workflow routing, state gates, render evidence, delivery verification, or approval handling:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
npm run slide:smoke -- --keep
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
The smoke validation creates a temporary `_workflow-smoke` deck from the fixture, exercises invalid target and approval failure paths, runs the `plan` -> `compose` -> `polish` -> `deliver` sequence, verifies render evidence metadata, checks delivery artifacts, and covers rerun/force behavior. `--keep` leaves the generated deck and reports under `slides/_workflow-smoke/` for inspection.
|
|
99
|
+
|
|
100
|
+
### Smoke fixture
|
|
101
|
+
|
|
102
|
+
A small input fixture is available at:
|
|
103
|
+
|
|
104
|
+
```text
|
|
105
|
+
fixtures/marp-slide-workflow/_workflow-smoke/
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Copy it under `slides/` when you want to run the full workflow without creating a new brief.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// bin entry: Node version guard, then dispatcher startup. No other logic lives here.
|
|
3
|
+
// Keep the syntax in this file conservative so the guard is reached (instead of a
|
|
4
|
+
// syntax error) on unsupported Node versions; the dispatcher is loaded dynamically
|
|
5
|
+
// only after the guard passes.
|
|
6
|
+
var nodeMajor = Number(process.versions.node.split(".")[0]);
|
|
7
|
+
if (!(nodeMajor >= 24)) {
|
|
8
|
+
process.stderr.write(
|
|
9
|
+
"NODE_VERSION_UNSUPPORTED: takt-marp requires Node.js >= 24 (current: v" + process.versions.node + ")\n",
|
|
10
|
+
);
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
import("../scripts/lib/takt-marp-cli.mjs")
|
|
15
|
+
.then(function (cli) {
|
|
16
|
+
return cli.runCli(process.argv.slice(2));
|
|
17
|
+
})
|
|
18
|
+
.then(function (exitCode) {
|
|
19
|
+
process.exitCode = exitCode;
|
|
20
|
+
})
|
|
21
|
+
.catch(function (error) {
|
|
22
|
+
process.stderr.write(String((error && error.stack) || error) + "\n");
|
|
23
|
+
process.exitCode = 1;
|
|
24
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Workflow Smoke Deck
|
|
2
|
+
|
|
3
|
+
このfixtureは Marp slide TAKT workflow の実行確認用である。
|
|
4
|
+
通常の `npm run build` が `slides/` 配下のMarkdownをすべて変換するため、fixtureは `slides/` 外に置く。
|
|
5
|
+
|
|
6
|
+
## 実行手順
|
|
7
|
+
|
|
8
|
+
実行確認時は、fixture の source artifact を smoke deck directory にコピーして使う。
|
|
9
|
+
コマンド target は deck directory の `slides/<deck>` であり、`brief.md` file path ではない。
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
mkdir -p slides/_workflow-smoke
|
|
13
|
+
cp fixtures/marp-slide-workflow/_workflow-smoke/brief.md slides/_workflow-smoke/brief.md
|
|
14
|
+
npm run slide:plan -- "slides/_workflow-smoke"
|
|
15
|
+
npm run slide:approve -- "slides/_workflow-smoke" plan --by <name>
|
|
16
|
+
npm run slide:compose -- "slides/_workflow-smoke"
|
|
17
|
+
npm run slide:approve -- "slides/_workflow-smoke" compose --by <name>
|
|
18
|
+
npm run slide:polish -- "slides/_workflow-smoke"
|
|
19
|
+
npm run slide:deliver -- "slides/_workflow-smoke"
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
`slides/_workflow-smoke/brief.md` は fixture から再現する source artifact である。
|
|
23
|
+
workflow が生成する `slides/_workflow-smoke/review/` や `.takt/render/_workflow-smoke/` は generated evidence であり、`dist/_workflow-smoke/` は delivery artifact である。
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Brief
|
|
2
|
+
|
|
3
|
+
## Event
|
|
4
|
+
- Name: Workflow smoke test
|
|
5
|
+
- Date:
|
|
6
|
+
- Duration: 5 minutes
|
|
7
|
+
- Venue: local
|
|
8
|
+
- Audience: maintainer
|
|
9
|
+
|
|
10
|
+
## Goal
|
|
11
|
+
Marp slide TAKT workflow が brief から plan、draft、SVG、review、build QA まで接続できることを確認する。
|
|
12
|
+
|
|
13
|
+
## Core Message
|
|
14
|
+
半自動のMarp生成workflowは、入力、構成、visual、レビュー、QAを分離すると安定する。
|
|
15
|
+
|
|
16
|
+
## Audience Context
|
|
17
|
+
聴衆はこのリポジトリのメンテナであり、TAKTとMarpの基本を理解している。
|
|
18
|
+
|
|
19
|
+
## Required Topics
|
|
20
|
+
- briefを入力の正にする
|
|
21
|
+
- SVG-firstで図を管理する
|
|
22
|
+
- plan後とdraft後に人間確認を挟む
|
|
23
|
+
- build QAまでworkflowに含める
|
|
24
|
+
|
|
25
|
+
## Optional Topics
|
|
26
|
+
- appendixは必要な場合だけ作る
|
|
27
|
+
|
|
28
|
+
## Avoid
|
|
29
|
+
- Web画像の自動取得
|
|
30
|
+
- 他deck画像の自動流用
|
|
31
|
+
- 長い本文をスライドに詰め込むこと
|
|
32
|
+
|
|
33
|
+
## Source Materials
|
|
34
|
+
- docs/marp-slide-workflow.md
|
|
35
|
+
|
|
36
|
+
## Speaker Notes
|
|
37
|
+
短い確認用deckなので、各スライドのnotesは2-3文でよい。
|
|
38
|
+
|
|
39
|
+
## Output Requirements
|
|
40
|
+
- Format: Marp
|
|
41
|
+
- Language: Japanese
|
|
42
|
+
- Target slide count: 5
|
|
43
|
+
- Deliverables: html, pdf
|
|
44
|
+
- Visual scope: use exactly one SVG for the workflow overview slide. For paired discipline slides, use text-only split columns with Visual: none; do not plan unused SVG placeholders.
|
package/marp.config.mjs
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "takt-marp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "takt-marp",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/j5ik2o/takt-marp.git"
|
|
8
|
+
},
|
|
9
|
+
"bin": {
|
|
10
|
+
"takt-marp": "bin/takt-marp.mjs"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"bin/",
|
|
14
|
+
"scripts/",
|
|
15
|
+
"templates/",
|
|
16
|
+
"fixtures/marp-slide-workflow/",
|
|
17
|
+
"marp.config.mjs"
|
|
18
|
+
],
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=24"
|
|
21
|
+
},
|
|
22
|
+
"scripts": {
|
|
23
|
+
"slide:plan": "node scripts/takt-marp-run-slide-workflow.mjs plan",
|
|
24
|
+
"slide:compose": "node scripts/takt-marp-run-slide-workflow.mjs compose",
|
|
25
|
+
"slide:polish": "node scripts/takt-marp-run-slide-workflow.mjs polish",
|
|
26
|
+
"slide:deliver": "node scripts/takt-marp-run-slide-workflow.mjs deliver",
|
|
27
|
+
"slide:check-state": "node scripts/takt-marp-check-slide-workflow-state.mjs",
|
|
28
|
+
"slide:approve": "node scripts/takt-marp-approve-slide-workflow-state.mjs",
|
|
29
|
+
"slide:render-evidence": "node scripts/takt-marp-render-slide-workflow-evidence.mjs",
|
|
30
|
+
"slide:validate-foundation": "node scripts/takt-marp-validate-slide-workflow-foundation.mjs",
|
|
31
|
+
"slide:smoke": "node scripts/takt-marp-validate-slide-workflow-smoke.mjs",
|
|
32
|
+
"test": "npm run slide:validate-foundation",
|
|
33
|
+
"build": "npm run build:html && npm run build:pdf",
|
|
34
|
+
"build:html": "marp slides/takt-sdd/SLIDES.md --html --allow-local-files -o dist/takt-sdd/SLIDES.html",
|
|
35
|
+
"build:pptx": "marp slides/takt-sdd/SLIDES.md --html --allow-local-files --pptx -o dist/takt-sdd/SLIDES.pptx",
|
|
36
|
+
"build:pdf": "marp slides/takt-sdd/SLIDES.md --html --allow-local-files --pdf -o dist/takt-sdd/SLIDES.pdf",
|
|
37
|
+
"preview": "marp -s . --html",
|
|
38
|
+
"installer:sync-templates": "node scripts/takt-marp-sync-project-templates.mjs --write",
|
|
39
|
+
"installer:check-templates": "node scripts/takt-marp-sync-project-templates.mjs",
|
|
40
|
+
"installer:check-package": "node scripts/takt-marp-validate-package-boundary.mjs",
|
|
41
|
+
"installer:validate": "node scripts/takt-marp-validate-global-install.mjs",
|
|
42
|
+
"release": "release-it"
|
|
43
|
+
},
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"@fontsource/noto-sans-jp": "^5.2.5",
|
|
46
|
+
"@marp-team/marp-cli": "^4.4.0",
|
|
47
|
+
"takt": "^0.44.0"
|
|
48
|
+
},
|
|
49
|
+
"overrides": {
|
|
50
|
+
"yargs": "^18.0.0"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@release-it/conventional-changelog": "^11.0.1",
|
|
54
|
+
"release-it": "^20.2.0"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { statSync } from "node:fs";
|
|
3
|
+
import { mkdtemp } from "node:fs/promises";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { parseArgs } from "node:util";
|
|
7
|
+
import { initializeProject } from "./takt-marp-project-init.mjs";
|
|
8
|
+
import { packageScriptPath } from "./takt-marp-runtime-context.mjs";
|
|
9
|
+
import { formatError, SlideWorkflowError } from "./takt-marp-slide-workflow.mjs";
|
|
10
|
+
|
|
11
|
+
const WORKFLOW_COMMANDS = ["plan", "compose", "polish", "deliver"];
|
|
12
|
+
const VALID_COMMANDS = ["init", ...WORKFLOW_COMMANDS, "smoke"];
|
|
13
|
+
const RUNNER_SCRIPT = "scripts/takt-marp-run-slide-workflow.mjs";
|
|
14
|
+
const SMOKE_SCRIPT = "scripts/takt-marp-validate-slide-workflow-smoke.mjs";
|
|
15
|
+
const REQUIRED_PROJECT_DIRS = [".takt/workflows", ".takt/facets"];
|
|
16
|
+
|
|
17
|
+
function usage() {
|
|
18
|
+
return [
|
|
19
|
+
"Usage: takt-marp <command> [arguments]",
|
|
20
|
+
"",
|
|
21
|
+
"Commands:",
|
|
22
|
+
" init [dir] [--force|--overwrite] Install .takt/workflows and .takt/facets templates into <dir> (default: .)",
|
|
23
|
+
" plan <slides/deck> [options] Run the plan workflow for a deck in the current project",
|
|
24
|
+
" compose <slides/deck> [options] Run the compose workflow for a deck in the current project",
|
|
25
|
+
" polish <slides/deck> [options] Run the polish workflow for a deck in the current project",
|
|
26
|
+
" deliver <slides/deck> [options] Run the deliver workflow for a deck in the current project",
|
|
27
|
+
" smoke [--provider <name>] Run smoke validation in a temporary project (default provider: mock)",
|
|
28
|
+
"",
|
|
29
|
+
"Workflow options (passed through to the workflow runner unchanged):",
|
|
30
|
+
" --force Invalidate an already-successful state and rerun",
|
|
31
|
+
" --provider <name> Run the workflow with the specified TAKT provider",
|
|
32
|
+
].join("\n");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function initUsage() {
|
|
36
|
+
return [
|
|
37
|
+
"Usage: takt-marp init [dir] [--force|--overwrite]",
|
|
38
|
+
"",
|
|
39
|
+
"Installs .takt/workflows/** and .takt/facets/** templates into <dir> (default: current directory).",
|
|
40
|
+
"",
|
|
41
|
+
"Options:",
|
|
42
|
+
" --force Overwrite existing template-owned files",
|
|
43
|
+
" --overwrite Alias of --force",
|
|
44
|
+
" --help Show this message",
|
|
45
|
+
].join("\n");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function isDirectory(candidatePath) {
|
|
49
|
+
const stats = statSync(candidatePath, { throwIfNoEntry: false });
|
|
50
|
+
return stats !== undefined && stats.isDirectory();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function assertProjectInitialized() {
|
|
54
|
+
const cwd = process.cwd();
|
|
55
|
+
const missing = REQUIRED_PROJECT_DIRS.filter(
|
|
56
|
+
(relative) => !isDirectory(path.join(cwd, ...relative.split("/"))),
|
|
57
|
+
);
|
|
58
|
+
if (missing.length > 0) {
|
|
59
|
+
throw new SlideWorkflowError(
|
|
60
|
+
`Target project is not initialized: missing ${missing.join(" and ")} in ${cwd}. Run 'takt-marp init .' first.`,
|
|
61
|
+
"PROJECT_NOT_INITIALIZED",
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function runPackageScript(relativeScriptPath, args, options = {}) {
|
|
67
|
+
return new Promise((resolve, reject) => {
|
|
68
|
+
const child = spawn(process.execPath, [packageScriptPath(relativeScriptPath), ...args], {
|
|
69
|
+
cwd: options.cwd ?? process.cwd(),
|
|
70
|
+
stdio: "inherit",
|
|
71
|
+
});
|
|
72
|
+
child.on("error", reject);
|
|
73
|
+
child.on("close", (code) => {
|
|
74
|
+
resolve(code ?? 1);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function runWorkflowCommand(command, args) {
|
|
80
|
+
assertProjectInitialized();
|
|
81
|
+
return runPackageScript(RUNNER_SCRIPT, [command, ...args]);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function runInit(args) {
|
|
85
|
+
let parsed;
|
|
86
|
+
try {
|
|
87
|
+
parsed = parseArgs({
|
|
88
|
+
args,
|
|
89
|
+
allowPositionals: true,
|
|
90
|
+
options: {
|
|
91
|
+
force: { type: "boolean", default: false },
|
|
92
|
+
overwrite: { type: "boolean", default: false },
|
|
93
|
+
help: { type: "boolean", short: "h", default: false },
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
} catch (error) {
|
|
97
|
+
throw new SlideWorkflowError(
|
|
98
|
+
`Invalid init arguments: ${error.message}. Run 'takt-marp init --help' for usage.`,
|
|
99
|
+
"INVALID_ARGS",
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
if (parsed.values.help) {
|
|
103
|
+
console.log(initUsage());
|
|
104
|
+
return 0;
|
|
105
|
+
}
|
|
106
|
+
if (parsed.positionals.length > 1) {
|
|
107
|
+
throw new SlideWorkflowError(
|
|
108
|
+
`Unexpected extra init arguments: ${parsed.positionals.slice(1).join(" ")}. Run 'takt-marp init --help' for usage.`,
|
|
109
|
+
"INVALID_ARGS",
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const targetDir = path.resolve(process.cwd(), parsed.positionals[0] ?? ".");
|
|
114
|
+
const force = parsed.values.force || parsed.values.overwrite;
|
|
115
|
+
const { created, overwritten } = await initializeProject({ targetDir, force });
|
|
116
|
+
|
|
117
|
+
const lines = [`Initialized takt-marp templates in ${targetDir}`];
|
|
118
|
+
lines.push(`Created ${created.length} file(s)${created.length > 0 ? ":" : "."}`);
|
|
119
|
+
for (const relativePath of created) {
|
|
120
|
+
lines.push(` ${relativePath}`);
|
|
121
|
+
}
|
|
122
|
+
if (overwritten.length > 0) {
|
|
123
|
+
lines.push(`Overwrote ${overwritten.length} file(s):`);
|
|
124
|
+
for (const relativePath of overwritten) {
|
|
125
|
+
lines.push(` ${relativePath}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
lines.push(
|
|
129
|
+
"",
|
|
130
|
+
"Next steps:",
|
|
131
|
+
" - Provider configuration stays under your ownership: takt-marp does not create or modify",
|
|
132
|
+
" TAKT provider settings, API keys, or credentials.",
|
|
133
|
+
" - Configure your TAKT provider before running workflows.",
|
|
134
|
+
` - Run a workflow from the project root, e.g.: takt-marp plan slides/<deck>`,
|
|
135
|
+
);
|
|
136
|
+
console.log(lines.join("\n"));
|
|
137
|
+
return 0;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async function runSmoke(args) {
|
|
141
|
+
// Smoke runs in a freshly created temporary project so the user's current
|
|
142
|
+
// project is never touched. The temp project is retained after the run as
|
|
143
|
+
// the home of the provider-specific smoke summaries.
|
|
144
|
+
let tempProject;
|
|
145
|
+
try {
|
|
146
|
+
tempProject = await mkdtemp(path.join(os.tmpdir(), "takt-marp-smoke-"));
|
|
147
|
+
} catch (error) {
|
|
148
|
+
throw new SlideWorkflowError(
|
|
149
|
+
`Failed to create a temporary smoke project under ${os.tmpdir()}: ${error.message}`,
|
|
150
|
+
"SMOKE_PREPARE_FAILED",
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
await initializeProject({ targetDir: tempProject, force: false });
|
|
154
|
+
console.log(`Temporary smoke project: ${tempProject}`);
|
|
155
|
+
|
|
156
|
+
// Pass argv through unchanged: provider selection (--provider) and the
|
|
157
|
+
// mock default, mock/real summary generation, and surfacing real-provider
|
|
158
|
+
// misconfiguration as failures are all owned by the smoke script. The CLI
|
|
159
|
+
// never creates or modifies TAKT provider settings.
|
|
160
|
+
const exitCode = await runPackageScript(SMOKE_SCRIPT, args, { cwd: tempProject });
|
|
161
|
+
|
|
162
|
+
console.log(
|
|
163
|
+
[
|
|
164
|
+
"",
|
|
165
|
+
`Smoke validation finished with exit code ${exitCode}.`,
|
|
166
|
+
`Temporary smoke project (retained for inspection): ${tempProject}`,
|
|
167
|
+
"Provider-specific smoke summaries are written under the smoke deck's review directory",
|
|
168
|
+
`inside that project (default: ${path.join(tempProject, "slides", "_workflow-smoke", "review")}).`,
|
|
169
|
+
].join("\n"),
|
|
170
|
+
);
|
|
171
|
+
return exitCode;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export async function runCli(argv) {
|
|
175
|
+
const [command, ...rest] = argv;
|
|
176
|
+
if (command === undefined || command === "--help" || command === "-h" || command === "help") {
|
|
177
|
+
console.log(usage());
|
|
178
|
+
return 0;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
try {
|
|
182
|
+
if (!VALID_COMMANDS.includes(command)) {
|
|
183
|
+
throw new SlideWorkflowError(
|
|
184
|
+
`'${command}' is not a takt-marp command. Valid commands: ${VALID_COMMANDS.join(", ")}. Run 'takt-marp --help' for usage.`,
|
|
185
|
+
"UNKNOWN_COMMAND",
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
if (command === "init") {
|
|
189
|
+
return await runInit(rest);
|
|
190
|
+
}
|
|
191
|
+
if (command === "smoke") {
|
|
192
|
+
return await runSmoke(rest);
|
|
193
|
+
}
|
|
194
|
+
return await runWorkflowCommand(command, rest);
|
|
195
|
+
} catch (error) {
|
|
196
|
+
console.error(formatError(error));
|
|
197
|
+
return 1;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { copyFile, mkdir, stat } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { listTemplateEntries } from "./takt-marp-project-templates.mjs";
|
|
4
|
+
import { SlideWorkflowError } from "./takt-marp-slide-workflow.mjs";
|
|
5
|
+
|
|
6
|
+
const TAKT_DIR = ".takt";
|
|
7
|
+
|
|
8
|
+
async function assertTargetDirectory(targetDir) {
|
|
9
|
+
if (!path.isAbsolute(targetDir)) {
|
|
10
|
+
throw new SlideWorkflowError(
|
|
11
|
+
`Target directory must be an absolute path: ${targetDir}`,
|
|
12
|
+
"TARGET_DIR_NOT_FOUND",
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
let stats;
|
|
16
|
+
try {
|
|
17
|
+
stats = await stat(targetDir);
|
|
18
|
+
} catch (error) {
|
|
19
|
+
if (error.code === "ENOENT" || error.code === "ENOTDIR") {
|
|
20
|
+
throw new SlideWorkflowError(
|
|
21
|
+
`Target directory does not exist: ${targetDir}`,
|
|
22
|
+
"TARGET_DIR_NOT_FOUND",
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
throw error;
|
|
26
|
+
}
|
|
27
|
+
if (!stats.isDirectory()) {
|
|
28
|
+
throw new SlideWorkflowError(
|
|
29
|
+
`Target path is not a directory: ${targetDir}`,
|
|
30
|
+
"TARGET_DIR_NOT_FOUND",
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function destinationExists(destinationPath) {
|
|
36
|
+
try {
|
|
37
|
+
await stat(destinationPath);
|
|
38
|
+
return true;
|
|
39
|
+
} catch (error) {
|
|
40
|
+
if (error.code === "ENOENT" || error.code === "ENOTDIR") {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
throw error;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export async function initializeProject(options) {
|
|
48
|
+
const { targetDir, force } = options;
|
|
49
|
+
await assertTargetDirectory(targetDir);
|
|
50
|
+
|
|
51
|
+
const entries = await listTemplateEntries();
|
|
52
|
+
const plan = [];
|
|
53
|
+
for (const entry of entries) {
|
|
54
|
+
const destinationRelativePath = `${TAKT_DIR}/${entry.relativePath}`;
|
|
55
|
+
const destinationPath = path.join(targetDir, TAKT_DIR, ...entry.relativePath.split("/"));
|
|
56
|
+
const exists = await destinationExists(destinationPath);
|
|
57
|
+
plan.push({ entry, destinationRelativePath, destinationPath, exists });
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const conflicts = plan.filter((item) => item.exists).map((item) => item.destinationRelativePath);
|
|
61
|
+
if (!force && conflicts.length > 0) {
|
|
62
|
+
throw new SlideWorkflowError(
|
|
63
|
+
`Init conflict: existing template files would be overwritten. Conflicting paths:\n${conflicts.join("\n")}\nRe-run with --force (or --overwrite) to overwrite template-owned paths.`,
|
|
64
|
+
"INIT_CONFLICT",
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const created = [];
|
|
69
|
+
const overwritten = [];
|
|
70
|
+
for (const item of plan) {
|
|
71
|
+
await mkdir(path.dirname(item.destinationPath), { recursive: true });
|
|
72
|
+
await copyFile(item.entry.sourcePath, item.destinationPath);
|
|
73
|
+
if (item.exists) {
|
|
74
|
+
overwritten.push(item.destinationRelativePath);
|
|
75
|
+
} else {
|
|
76
|
+
created.push(item.destinationRelativePath);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return { created, overwritten };
|
|
81
|
+
}
|