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.
Files changed (69) hide show
  1. package/README.ja.md +108 -0
  2. package/README.md +108 -0
  3. package/bin/takt-marp.mjs +24 -0
  4. package/fixtures/marp-slide-workflow/_workflow-smoke/README.md +23 -0
  5. package/fixtures/marp-slide-workflow/_workflow-smoke/brief.md +44 -0
  6. package/marp.config.mjs +3 -0
  7. package/package.json +56 -0
  8. package/scripts/lib/takt-marp-cli.mjs +199 -0
  9. package/scripts/lib/takt-marp-project-init.mjs +81 -0
  10. package/scripts/lib/takt-marp-project-templates.mjs +93 -0
  11. package/scripts/lib/takt-marp-runtime-context.mjs +24 -0
  12. package/scripts/lib/takt-marp-slide-workflow.mjs +453 -0
  13. package/scripts/takt-marp-approve-slide-workflow-state.mjs +37 -0
  14. package/scripts/takt-marp-build-slide-artifact.mjs +151 -0
  15. package/scripts/takt-marp-check-slide-workflow-state.mjs +41 -0
  16. package/scripts/takt-marp-render-slide-workflow-evidence.mjs +70 -0
  17. package/scripts/takt-marp-run-slide-workflow.mjs +435 -0
  18. package/scripts/takt-marp-sync-project-templates.mjs +125 -0
  19. package/scripts/takt-marp-validate-global-install.mjs +391 -0
  20. package/scripts/takt-marp-validate-package-boundary.mjs +276 -0
  21. package/scripts/takt-marp-validate-slide-workflow-foundation.mjs +571 -0
  22. package/scripts/takt-marp-validate-slide-workflow-smoke.mjs +1935 -0
  23. package/scripts/takt-marp-verify-delivery-artifacts.mjs +181 -0
  24. package/scripts/takt-marp-verify-render-evidence-metadata.mjs +133 -0
  25. package/templates/project/facets/instructions/takt-marp-ai-antipattern-fix.md +47 -0
  26. package/templates/project/facets/instructions/takt-marp-ai-antipattern-review.md +37 -0
  27. package/templates/project/facets/instructions/takt-marp-compose-fix.md +25 -0
  28. package/templates/project/facets/instructions/takt-marp-compose-review.md +30 -0
  29. package/templates/project/facets/instructions/takt-marp-compose-slides.md +35 -0
  30. package/templates/project/facets/instructions/takt-marp-compose-work-summary.md +23 -0
  31. package/templates/project/facets/instructions/takt-marp-deliver-build.md +30 -0
  32. package/templates/project/facets/instructions/takt-marp-deliver-fix.md +25 -0
  33. package/templates/project/facets/instructions/takt-marp-deliver-verify.md +25 -0
  34. package/templates/project/facets/instructions/takt-marp-design-system.md +37 -0
  35. package/templates/project/facets/instructions/takt-marp-intake.md +15 -0
  36. package/templates/project/facets/instructions/takt-marp-normalize-brief.md +24 -0
  37. package/templates/project/facets/instructions/takt-marp-plan-fix.md +26 -0
  38. package/templates/project/facets/instructions/takt-marp-plan-review.md +24 -0
  39. package/templates/project/facets/instructions/takt-marp-plan-work-summary.md +24 -0
  40. package/templates/project/facets/instructions/takt-marp-plan.md +26 -0
  41. package/templates/project/facets/instructions/takt-marp-polish-fix.md +25 -0
  42. package/templates/project/facets/instructions/takt-marp-polish-inspect.md +25 -0
  43. package/templates/project/facets/instructions/takt-marp-render-evidence.md +35 -0
  44. package/templates/project/facets/instructions/takt-marp-supervise-command.md +58 -0
  45. package/templates/project/facets/instructions/takt-marp-visual-generate.md +26 -0
  46. package/templates/project/facets/knowledge/takt-marp-repo-conventions.md +119 -0
  47. package/templates/project/facets/output-contracts/takt-marp-ai-antipattern-fix.md +48 -0
  48. package/templates/project/facets/output-contracts/takt-marp-ai-antipattern-review.md +43 -0
  49. package/templates/project/facets/output-contracts/takt-marp-command-fix.md +32 -0
  50. package/templates/project/facets/output-contracts/takt-marp-command-review.md +32 -0
  51. package/templates/project/facets/output-contracts/takt-marp-command-work.md +42 -0
  52. package/templates/project/facets/output-contracts/takt-marp-normalized-brief.md +31 -0
  53. package/templates/project/facets/output-contracts/takt-marp-slide-plan.md +30 -0
  54. package/templates/project/facets/output-contracts/takt-marp-supervision.md +45 -0
  55. package/templates/project/facets/personas/takt-marp-slide-planner.md +24 -0
  56. package/templates/project/facets/personas/takt-marp-slide-qa.md +23 -0
  57. package/templates/project/facets/personas/takt-marp-slide-reviewer.md +22 -0
  58. package/templates/project/facets/personas/takt-marp-slide-reviser.md +22 -0
  59. package/templates/project/facets/personas/takt-marp-slide-supervisor.md +24 -0
  60. package/templates/project/facets/personas/takt-marp-slide-writer.md +22 -0
  61. package/templates/project/facets/policies/takt-marp-general-slide-quality.md +91 -0
  62. package/templates/project/facets/policies/takt-marp-slide-quality.md +73 -0
  63. package/templates/project/facets/policies/takt-marp-svg-first-visual.md +66 -0
  64. package/templates/project/facets/policies/takt-marp-worker-boundary.md +32 -0
  65. package/templates/project/workflows/takt-marp-slide-ai-quality-gate.yaml +125 -0
  66. package/templates/project/workflows/takt-marp-slide-compose.yaml +209 -0
  67. package/templates/project/workflows/takt-marp-slide-deliver.yaml +164 -0
  68. package/templates/project/workflows/takt-marp-slide-plan.yaml +213 -0
  69. package/templates/project/workflows/takt-marp-slide-polish.yaml +158 -0
@@ -0,0 +1,276 @@
1
+ #!/usr/bin/env node
2
+ import { spawn } from "node:child_process";
3
+ import { accessSync, constants } from "node:fs";
4
+ import { readFile, readdir } from "node:fs/promises";
5
+ import path from "node:path";
6
+ import {
7
+ PROHIBITED_TEMPLATE_PATTERNS,
8
+ TEMPLATE_DOMAINS,
9
+ assertNoProhibitedEntries,
10
+ listTemplateEntries,
11
+ templateRootPath,
12
+ } from "./lib/takt-marp-project-templates.mjs";
13
+ import { resolveRuntimeContext } from "./lib/takt-marp-runtime-context.mjs";
14
+ import { SlideWorkflowError, formatError } from "./lib/takt-marp-slide-workflow.mjs";
15
+
16
+ const GROUPS = ["template tree", "pack contents", "metadata"];
17
+
18
+ const ALLOWED_PACK_PREFIXES = ["bin/", "scripts/", "templates/", "fixtures/marp-slide-workflow/"];
19
+ const ALLOWED_PACK_FILES = ["marp.config.mjs", "package.json"];
20
+ const ALLOWED_PACK_FILE_PATTERNS = [/^readme($|\.)/i, /^licen[cs]e($|\.)/i];
21
+
22
+ const FORBIDDEN_PACK_PREFIXES = [
23
+ ".kiro/",
24
+ ".takt/",
25
+ ".claude/",
26
+ ".agents/",
27
+ "slides/",
28
+ "docs/",
29
+ "dist/",
30
+ "node_modules/",
31
+ ];
32
+
33
+ // Local helper files under scripts/ (e.g. run-claude-*.sh kept out of git via
34
+ // .git/info/exclude) are still picked up by `npm pack` because `files` lists the
35
+ // directory. Only the canonical script naming is allowed in the pack.
36
+ const EXPECTED_SCRIPT_PATTERNS = [/^scripts\/takt-marp-[^/]+\.mjs$/, /^scripts\/lib\/takt-marp-[^/]+\.mjs$/];
37
+
38
+ const REQUIRED_PACK_FILES = [
39
+ "bin/takt-marp.mjs",
40
+ "marp.config.mjs",
41
+ "scripts/takt-marp-run-slide-workflow.mjs",
42
+ "scripts/takt-marp-validate-slide-workflow-smoke.mjs",
43
+ "scripts/lib/takt-marp-cli.mjs",
44
+ "scripts/lib/takt-marp-project-init.mjs",
45
+ "scripts/lib/takt-marp-project-templates.mjs",
46
+ "scripts/lib/takt-marp-runtime-context.mjs",
47
+ "scripts/lib/takt-marp-slide-workflow.mjs",
48
+ ];
49
+ const SMOKE_FIXTURE_PREFIX = "fixtures/marp-slide-workflow/_workflow-smoke/";
50
+
51
+ const EXPECTED_FILES_ALLOWLIST = ["bin/", "scripts/", "templates/", "fixtures/marp-slide-workflow/", "marp.config.mjs"];
52
+ const EXPECTED_BIN_NAME = "takt-marp";
53
+ const EXPECTED_BIN_TARGET = "bin/takt-marp.mjs";
54
+ const EXPECTED_ENGINES_NODE = ">=24";
55
+ const REQUIRED_DEPENDENCIES = ["takt", "@marp-team/marp-cli"];
56
+
57
+ function runNpmPackDryRun(packageRoot) {
58
+ const npmCli = process.platform === "win32" ? "npm.cmd" : "npm";
59
+ return new Promise((resolve, reject) => {
60
+ const child = spawn(npmCli, ["pack", "--dry-run", "--json"], {
61
+ cwd: packageRoot,
62
+ shell: process.platform === "win32",
63
+ stdio: ["ignore", "pipe", "pipe"],
64
+ });
65
+ let stdout = "";
66
+ let stderr = "";
67
+ child.stdout.on("data", (chunk) => {
68
+ stdout += chunk;
69
+ });
70
+ child.stderr.on("data", (chunk) => {
71
+ stderr += chunk;
72
+ });
73
+ child.on("error", reject);
74
+ child.on("close", (code) => {
75
+ if (code !== 0) {
76
+ reject(new SlideWorkflowError(`npm pack --dry-run failed with exit code ${code}.\n${stderr}`, "NPM_PACK_FAILED"));
77
+ return;
78
+ }
79
+ resolve(stdout);
80
+ });
81
+ });
82
+ }
83
+
84
+ function parsePackOutput(stdout) {
85
+ const text = stdout.trim();
86
+ try {
87
+ return JSON.parse(text);
88
+ } catch {
89
+ // npm can prepend non-JSON noise (lifecycle/banner output); the JSON report
90
+ // is the last array in stdout, so retry from the last line that opens one.
91
+ const start = text.lastIndexOf("\n[");
92
+ if (start !== -1) {
93
+ try {
94
+ return JSON.parse(text.slice(start + 1));
95
+ } catch {
96
+ // fall through to the error below
97
+ }
98
+ }
99
+ }
100
+ throw new SlideWorkflowError(`Unable to parse JSON output of npm pack --dry-run.\n${text}`, "NPM_PACK_FAILED");
101
+ }
102
+
103
+ function packedPaths(packReport) {
104
+ if (!Array.isArray(packReport) || packReport.length === 0 || !Array.isArray(packReport[0]?.files)) {
105
+ throw new SlideWorkflowError(
106
+ `npm pack --dry-run --json returned an unexpected shape (expected [{ files: [...] }]).`,
107
+ "NPM_PACK_FAILED",
108
+ );
109
+ }
110
+ return packReport[0].files.map((file) => file.path.split(path.sep).join("/")).sort();
111
+ }
112
+
113
+ async function checkTemplateTree(addViolation) {
114
+ const templateRoot = templateRootPath();
115
+ const entries = await listTemplateEntries();
116
+
117
+ if (entries.length === 0) {
118
+ addViolation("template tree", `template canon is empty: no files under ${templateRoot}/{${TEMPLATE_DOMAINS.join(",")}}`);
119
+ }
120
+
121
+ let rootEntries = [];
122
+ try {
123
+ rootEntries = await readdir(templateRoot, { withFileTypes: true });
124
+ } catch (error) {
125
+ if (error.code === "ENOENT" || error.code === "ENOTDIR") {
126
+ addViolation("template tree", `template root is missing: ${templateRoot}`);
127
+ } else {
128
+ throw error;
129
+ }
130
+ }
131
+ for (const dirent of rootEntries) {
132
+ if (!TEMPLATE_DOMAINS.includes(dirent.name) || !dirent.isDirectory()) {
133
+ addViolation("template tree", `unexpected entry outside {${TEMPLATE_DOMAINS.join(",")}} domains: ${dirent.name}`);
134
+ }
135
+ }
136
+
137
+ try {
138
+ assertNoProhibitedEntries(entries);
139
+ } catch (error) {
140
+ if (error instanceof SlideWorkflowError && error.code === "PACKAGE_BOUNDARY_VIOLATION") {
141
+ addViolation("template tree", error.message);
142
+ } else {
143
+ throw error;
144
+ }
145
+ }
146
+
147
+ return entries;
148
+ }
149
+
150
+ function isAllowedPackPath(packedPath) {
151
+ return (
152
+ ALLOWED_PACK_PREFIXES.some((prefix) => packedPath.startsWith(prefix)) ||
153
+ ALLOWED_PACK_FILES.includes(packedPath) ||
154
+ ALLOWED_PACK_FILE_PATTERNS.some((pattern) => pattern.test(packedPath))
155
+ );
156
+ }
157
+
158
+ function checkPackContents(paths, templateEntries, addViolation) {
159
+ const packed = new Set(paths);
160
+
161
+ for (const packedPath of paths) {
162
+ const forbiddenPrefix = FORBIDDEN_PACK_PREFIXES.find((prefix) => packedPath.startsWith(prefix));
163
+ if (forbiddenPrefix) {
164
+ addViolation("pack contents", `forbidden path (matches denied prefix '${forbiddenPrefix}'): ${packedPath}`);
165
+ } else if (!isAllowedPackPath(packedPath)) {
166
+ addViolation("pack contents", `path outside the files allowlist: ${packedPath}`);
167
+ }
168
+
169
+ const prohibited = PROHIBITED_TEMPLATE_PATTERNS.find((pattern) => pattern.test(packedPath));
170
+ if (prohibited) {
171
+ addViolation("pack contents", `path matches prohibited pattern ${prohibited}: ${packedPath}`);
172
+ }
173
+
174
+ if (packedPath.startsWith("scripts/") && !EXPECTED_SCRIPT_PATTERNS.some((pattern) => pattern.test(packedPath))) {
175
+ addViolation(
176
+ "pack contents",
177
+ `unexpected scripts/ file (expected scripts/takt-marp-*.mjs or scripts/lib/takt-marp-*.mjs): ${packedPath}`,
178
+ );
179
+ }
180
+ }
181
+
182
+ for (const requiredPath of REQUIRED_PACK_FILES) {
183
+ if (!packed.has(requiredPath)) {
184
+ addViolation("pack contents", `required file missing from pack: ${requiredPath}`);
185
+ }
186
+ }
187
+ if (!paths.some((packedPath) => packedPath.startsWith(SMOKE_FIXTURE_PREFIX))) {
188
+ addViolation("pack contents", `required smoke fixture missing from pack: no file under ${SMOKE_FIXTURE_PREFIX}`);
189
+ }
190
+ for (const entry of templateEntries) {
191
+ const expectedPath = `templates/project/${entry.relativePath}`;
192
+ if (!packed.has(expectedPath)) {
193
+ addViolation("pack contents", `template entry missing from pack: ${expectedPath}`);
194
+ }
195
+ }
196
+ }
197
+
198
+ async function checkMetadata(packageRoot, addViolation) {
199
+ const manifest = JSON.parse(await readFile(path.join(packageRoot, "package.json"), "utf8"));
200
+
201
+ const binTarget = manifest.bin?.[EXPECTED_BIN_NAME];
202
+ if (binTarget !== EXPECTED_BIN_TARGET) {
203
+ addViolation(
204
+ "metadata",
205
+ `bin["${EXPECTED_BIN_NAME}"] must be "${EXPECTED_BIN_TARGET}" (found: ${JSON.stringify(binTarget)})`,
206
+ );
207
+ } else {
208
+ try {
209
+ accessSync(path.join(packageRoot, EXPECTED_BIN_TARGET), constants.F_OK | constants.X_OK);
210
+ } catch {
211
+ addViolation("metadata", `bin entrypoint is missing or not executable: ${EXPECTED_BIN_TARGET}`);
212
+ }
213
+ }
214
+
215
+ const files = Array.isArray(manifest.files) ? manifest.files : [];
216
+ for (const expected of EXPECTED_FILES_ALLOWLIST) {
217
+ if (!files.includes(expected)) {
218
+ addViolation("metadata", `files allowlist is missing "${expected}"`);
219
+ }
220
+ }
221
+ for (const declared of files) {
222
+ if (!EXPECTED_FILES_ALLOWLIST.includes(declared)) {
223
+ addViolation("metadata", `files allowlist declares an entry outside the design boundary: "${declared}"`);
224
+ }
225
+ }
226
+
227
+ if (manifest.engines?.node !== EXPECTED_ENGINES_NODE) {
228
+ addViolation(
229
+ "metadata",
230
+ `engines.node must be "${EXPECTED_ENGINES_NODE}" (found: ${JSON.stringify(manifest.engines?.node)})`,
231
+ );
232
+ }
233
+
234
+ for (const dependency of REQUIRED_DEPENDENCIES) {
235
+ if (!manifest.dependencies?.[dependency]) {
236
+ addViolation("metadata", `runtime dependency missing from dependencies: ${dependency}`);
237
+ }
238
+ }
239
+ }
240
+
241
+ async function main() {
242
+ const packageRoot = resolveRuntimeContext().packageRoot;
243
+ const violations = [];
244
+ const addViolation = (group, detail) => violations.push({ group, detail });
245
+
246
+ const templateEntries = await checkTemplateTree(addViolation);
247
+ const paths = packedPaths(parsePackOutput(await runNpmPackDryRun(packageRoot)));
248
+ checkPackContents(paths, templateEntries, addViolation);
249
+ await checkMetadata(packageRoot, addViolation);
250
+
251
+ if (violations.length > 0) {
252
+ for (const group of GROUPS) {
253
+ const grouped = violations.filter((violation) => violation.group === group);
254
+ if (grouped.length === 0) {
255
+ continue;
256
+ }
257
+ console.error(`${group} violations (${grouped.length}):`);
258
+ for (const violation of grouped) {
259
+ console.error(` - ${violation.detail}`);
260
+ }
261
+ }
262
+ throw new SlideWorkflowError(
263
+ `package boundary check failed with ${violations.length} violation(s) across ${new Set(violations.map((violation) => violation.group)).size} group(s). See the list above.`,
264
+ "PACKAGE_BOUNDARY_VIOLATION",
265
+ );
266
+ }
267
+
268
+ console.log(
269
+ `package boundary check ok: ${GROUPS.length} groups checked, ${paths.length} packed files, ${templateEntries.length} template entries.`,
270
+ );
271
+ }
272
+
273
+ main().catch((error) => {
274
+ console.error(formatError(error));
275
+ process.exit(1);
276
+ });