freeharness 0.4.1 → 0.4.2
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 +8 -7
- package/canonical/HARVEST.yaml +25 -14
- package/canonical/PROVENANCE.yaml +1 -1
- package/canonical/agents/README.md +77 -36
- package/canonical/agents/agentic-architect.md +2 -1
- package/canonical/agents/database-reviewer.md +6 -8
- package/canonical/agents/explorer-ai-engineer-analyst.md +2 -6
- package/canonical/agents/implementer.md +30 -0
- package/canonical/agents/pr-reviewer.md +9 -11
- package/canonical/agents/python-reviewer.md +7 -9
- package/canonical/agents/report-formatter.md +7 -3
- package/canonical/agents/security-reviewer.md +7 -8
- package/canonical/agents/test-writer.md +5 -2
- package/canonical/hooks/cost-prices.json +11 -9
- package/canonical/hooks/freeharness-config-guard.mjs +8072 -87
- package/canonical/hooks/freeharness-context-monitor.mjs +88 -54
- package/canonical/hooks/freeharness-converge.mjs +78 -23
- package/canonical/hooks/freeharness-cost-tracker.mjs +1129 -218
- package/canonical/hooks/freeharness-memory-guard.mjs +20 -23
- package/canonical/hooks/freeharness-precompact.mjs +217 -87
- package/canonical/hooks/freeharness-quota-tap.mjs +173 -0
- package/canonical/hooks/freeharness-reload-context.mjs +197 -0
- package/canonical/hooks/freeharness-repeat-tool.mjs +1 -1
- package/canonical/hooks/freeharness-sdd-context.mjs +970 -0
- package/canonical/hooks/freeharness-session-start +297 -59
- package/canonical/hooks/hooks.claude-code.json +1 -1
- package/canonical/hooks/hooks.config-guard.claude-code.json +3 -2
- package/canonical/hooks/hooks.context-monitor.claude-code.json +1 -1
- package/canonical/hooks/hooks.converge.claude-code.json +1 -1
- package/canonical/hooks/hooks.memory-guard.claude-code.json +3 -2
- package/canonical/hooks/hooks.reload-context.claude-code.json +15 -0
- package/canonical/openspec/schema.spec-driven.yaml +29 -8
- package/canonical/openspec/templates/follow-ups.md +3 -2
- package/canonical/openspec/templates/proposal.md +1 -1
- package/canonical/openspec/templates/tasks.md +8 -0
- package/canonical/rules/CLAUDE.md +5 -4
- package/canonical/rules/routing.md +10 -9
- package/canonical/skills/agent-evaluation/SKILL.md +17 -6
- package/canonical/skills/agent-evaluation/evals/cases.yaml +38 -23
- package/canonical/skills/agentic-architecture/SKILL.md +4 -0
- package/canonical/skills/agentic-architecture/evals/cases.yaml +27 -0
- package/canonical/skills/code-review/SKILL.md +9 -2
- package/canonical/skills/code-review/evals/cases.yaml +1118 -27
- package/canonical/skills/git-workflow/SKILL.md +2 -0
- package/canonical/skills/local-code/SKILL.md +2 -2
- package/canonical/skills/local-code/evals/cases.yaml +1 -1
- package/canonical/skills/report-formatting/SKILL.md +10 -2
- package/canonical/skills/report-formatting/reference/ARCHITECTURE.md +3 -3
- package/canonical/skills/report-formatting/reference/HELPERS.md +10 -5
- package/canonical/skills/report-formatting/reference/RELEASE.md +1 -2
- package/canonical/skills/report-formatting/reference/TEMPLATES.md +3 -2
- package/canonical/skills/report-formatting/reference/authoring.md +25 -37
- package/canonical/skills/report-formatting/scripts/build-architecture.js +1 -1
- package/canonical/skills/report-formatting/scripts/build-md-claro.js +5 -8
- package/canonical/skills/report-formatting/scripts/check-env.js +0 -4
- package/canonical/skills/report-formatting/scripts/docmodel.js +3 -11
- package/canonical/skills/report-formatting/scripts/example-build.js +8 -10
- package/canonical/skills/report-formatting/scripts/example_build_ArchitectureReview_docx.js +8 -10
- package/canonical/skills/report-formatting/scripts/example_build_ExecutiveReport_docx.js +8 -10
- package/canonical/skills/report-formatting/scripts/theme-claro.js +39 -10
- package/canonical/skills/subagent-driven-development/SKILL.md +35 -28
- package/canonical/skills/subagent-driven-development/evals/cases.yaml +3496 -19
- package/canonical/skills/subagent-driven-development/reference/brief.md +185 -35
- package/canonical/skills/subagent-driven-development/reference/handoff.md +82 -2
- package/canonical/skills/subagent-driven-development/reference/loop.md +57 -0
- package/canonical/skills/subagent-driven-development/reference/trees.md +40 -0
- package/canonical/skills/subagent-driven-development/reference/workspace.md +31 -0
- package/canonical/skills/subagent-driven-development/scripts/validate-handoff.mjs +4771 -326
- package/dist/adapters/harness/claude-code.d.ts +19 -3
- package/dist/adapters/harness/claude-code.js +204 -39
- package/dist/adapters/harness/opencode.js +16 -2
- package/dist/adapters/harness/plugin.js +28 -17
- package/dist/adapters/memory/doctor.d.ts +174 -29
- package/dist/adapters/memory/doctor.js +758 -134
- package/dist/adapters/memory/migrate.d.ts +95 -0
- package/dist/adapters/memory/migrate.js +497 -0
- package/dist/adapters/memory/paths.d.ts +22 -0
- package/dist/adapters/memory/paths.js +85 -0
- package/dist/adapters/memory/vendored.d.ts +56 -29
- package/dist/adapters/memory/vendored.js +191 -154
- package/dist/adapters/sync/diff.js +4 -1
- package/dist/cli/cleanup-prompt.d.ts +35 -0
- package/dist/cli/cleanup-prompt.js +68 -0
- package/dist/cli/cleanup.d.ts +22 -24
- package/dist/cli/cleanup.js +182 -68
- package/dist/cli/cost-quota.d.ts +158 -0
- package/dist/cli/cost-quota.js +645 -0
- package/dist/cli/cost.d.ts +408 -46
- package/dist/cli/cost.js +1731 -57
- package/dist/cli/doctor.d.ts +52 -13
- package/dist/cli/doctor.js +193 -44
- package/dist/cli/hard-cleanup.d.ts +93 -0
- package/dist/cli/hard-cleanup.js +552 -0
- package/dist/cli/install-facts.d.ts +28 -0
- package/dist/cli/install-facts.js +66 -0
- package/dist/cli/json-key-edit.d.ts +18 -0
- package/dist/cli/json-key-edit.js +118 -0
- package/dist/cli/memory-import.d.ts +74 -0
- package/dist/cli/memory-import.js +469 -0
- package/dist/cli/plugin-validate.d.ts +12 -0
- package/dist/cli/plugin-validate.js +43 -0
- package/dist/cli/uninstall.d.ts +1 -1
- package/dist/cli/uninstall.js +2 -2
- package/dist/cli/upstream-launchers.d.ts +49 -0
- package/dist/cli/upstream-launchers.js +287 -0
- package/dist/index.js +247 -70
- package/dist/infra/fsutil.d.ts +31 -3
- package/dist/infra/fsutil.js +69 -6
- package/dist/infra/manifest.d.ts +2 -1
- package/dist/infra/manifest.js +3 -2
- package/dist/infra/prices.d.ts +18 -0
- package/dist/infra/prices.js +64 -0
- package/node_modules/@freeharness/core/dist/tiers.d.ts +10 -1
- package/node_modules/@freeharness/core/dist/tiers.js +24 -2
- package/node_modules/@freeharness/core/package.json +1 -1
- package/node_modules/@freeharness/sdd/PROVENANCE.yaml +279 -2
- package/node_modules/@freeharness/sdd/dist/src/bin/openspec.js +34 -8
- package/node_modules/@freeharness/sdd/dist/src/shim/archive.d.ts +7 -6
- package/node_modules/@freeharness/sdd/dist/src/shim/archive.js +72 -64
- package/node_modules/@freeharness/sdd/dist/src/shim/follow-up.d.ts +48 -16
- package/node_modules/@freeharness/sdd/dist/src/shim/follow-up.js +168 -70
- package/node_modules/@freeharness/sdd/dist/src/shim/instructions.d.ts +36 -6
- package/node_modules/@freeharness/sdd/dist/src/shim/instructions.js +110 -49
- package/node_modules/@freeharness/sdd/dist/src/shim/list.d.ts +1 -1
- package/node_modules/@freeharness/sdd/dist/src/shim/list.js +57 -14
- package/node_modules/@freeharness/sdd/dist/src/shim/nested-change.d.ts +7 -0
- package/node_modules/@freeharness/sdd/dist/src/shim/nested-change.js +118 -0
- package/node_modules/@freeharness/sdd/dist/src/shim/new-change.js +3 -1
- package/node_modules/@freeharness/sdd/dist/src/shim/proposal-capabilities.d.ts +12 -0
- package/node_modules/@freeharness/sdd/dist/src/shim/proposal-capabilities.js +94 -0
- package/node_modules/@freeharness/sdd/dist/src/shim/shared.d.ts +17 -0
- package/node_modules/@freeharness/sdd/dist/src/shim/shared.js +182 -1
- package/node_modules/@freeharness/sdd/dist/src/shim/status.d.ts +2 -2
- package/node_modules/@freeharness/sdd/dist/src/shim/status.js +34 -5
- package/node_modules/@freeharness/sdd/dist/src/shim/validate.d.ts +9 -0
- package/node_modules/@freeharness/sdd/dist/src/shim/validate.js +49 -41
- package/node_modules/@freeharness/sdd/dist/src/shim/warnings.d.ts +21 -0
- package/node_modules/@freeharness/sdd/dist/src/shim/warnings.js +66 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/apply/line-endings.d.ts +4 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/apply/line-endings.js +17 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/apply/specs-apply.js +95 -22
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/change-status-policy.d.ts +6 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/change-status-policy.js +14 -6
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/index.d.ts +1 -1
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/index.js +1 -1
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/outputs.d.ts +3 -3
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/outputs.js +24 -4
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/task-progress.js +4 -7
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/types.js +13 -1
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/apply-change.js +21 -6
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/archive-change.js +80 -19
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/explore.js +42 -22
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/project-root.d.ts +32 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/project-root.js +39 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/propose.js +16 -9
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/sync-specs.js +36 -1
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/update-change.js +65 -30
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/constants.d.ts +1 -1
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/constants.js +1 -1
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/change-parser.d.ts +8 -13
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/change-parser.js +79 -102
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/markdown-parser.js +4 -9
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/requirement-blocks.d.ts +50 -10
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/requirement-blocks.js +165 -37
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/requirement-text.d.ts +24 -3
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/requirement-text.js +55 -8
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/spec-structure.js +4 -1
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/purpose-placeholder.js +19 -9
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/spec-discovery.d.ts +10 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/spec-discovery.js +115 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/task-lines.d.ts +3 -3
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/task-lines.js +20 -20
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/validator.d.ts +6 -0
- package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/validator.js +75 -14
- package/node_modules/@freeharness/sdd/package.json +1 -1
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/schema.upstream.yaml +47 -7
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-apply-change/SKILL.md +25 -7
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-archive-change/SKILL.md +50 -11
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-bulk-archive-change/SKILL.md +48 -10
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-continue-change/SKILL.md +27 -7
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-explore/SKILL.md +25 -12
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-ff-change/SKILL.md +17 -5
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-new-change/SKILL.md +12 -1
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-onboard/SKILL.md +47 -33
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-propose/SKILL.md +17 -5
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-sync-specs/SKILL.md +29 -1
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-update-change/SKILL.md +31 -13
- package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-verify-change/SKILL.md +75 -27
- package/package.json +6 -4
- package/provenance/canonical.PROVENANCE.yaml +1 -1
- package/provenance/memory.PROVENANCE.yaml +458 -6
- package/provenance/sdd.PROVENANCE.yaml +279 -2
- package/canonical/hooks/freeharness-auto-memory.mjs +0 -158
- package/canonical/hooks/hooks.auto-memory.claude-code.json +0 -16
|
@@ -20,6 +20,7 @@ Git operations are deterministic and reversible. The orchestrator runs them dire
|
|
|
20
20
|
<push_and_pr>
|
|
21
21
|
1. Never commit directly on `main`/`master`: create `type/short-topic` from the current branch first.
|
|
22
22
|
2. `git push -u origin <branch>`. Never `--force` or `--force-with-lease` on a shared branch; if history must change, ask the user and explain the consequence.
|
|
23
|
+
The config guard hook denies history-destroying git commands (e.g. `reset --hard`, `--force` push, `branch -D`, `clean -f`, discarding `checkout`/`restore`, `stash drop`, `worktree remove --force` outside the temp root) from the model, also when a same-line variable, `xargs ... sh -c` or interpreter code that spawns processes runs them; the user runs an intended one with the `!` prompt prefix. Git text inside a quoted argument, a `sed`/`grep`/`awk` script or interpreter code without a process-spawning call passes. The hook lets `--force-with-lease` through, so the shared-branch ban on it rests on the rule above.
|
|
23
24
|
3. Open the PR with `gh pr create`: title = the commit summary (or the change id for spec-driven work), body = summary of what and why, the OpenSpec change reference, the verification evidence (test/type/lint commands and results), and a checklist of review items. Draft when verification is incomplete.
|
|
24
25
|
4. Report the PR URL.
|
|
25
26
|
</push_and_pr>
|
|
@@ -34,6 +35,7 @@ Local branches whose remote is gone: `git fetch --prune`, list `git branch -vv |
|
|
|
34
35
|
- Never offer "discard" as an option for uncommitted work: the choices are commit, stash (`git stash push -u` inside that worktree) or leave it.
|
|
35
36
|
- Never operate on another worktree's branch: no `git switch`/`git checkout` to a branch checked out elsewhere, no `gh pr checkout` in the current tree unless the user asks; inspect PRs with `gh pr view`, `gh pr diff`, `git show <ref>:<path>` against fetched refs.
|
|
36
37
|
- Rebase conflicts: resolve only in files this branch modified; a conflict in a file it did not touch means abort and ask.
|
|
38
|
+
- SDD task trees under `.worktrees/<change>/<task>` are created, landed and removed by the subagent-driven-development tree rule (`validate-handoff.mjs tree` and `commit --tree`), never by hand.
|
|
37
39
|
</worktrees>
|
|
38
40
|
|
|
39
41
|
<output_format>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: local-code
|
|
3
|
-
description: Use when a
|
|
3
|
+
description: Use when a log or stack trace is large, or a bulk multi-file scan or AST summary is needed before reading: offloads ingestion to llama-server on localhost:8080 and returns a compressed digest. Falls back to Grep/Glob when the server is down.
|
|
4
4
|
tools: Read, Glob, Grep, Bash
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -14,7 +14,7 @@ tools: Read, Glob, Grep, Bash
|
|
|
14
14
|
|
|
15
15
|
<workflow>
|
|
16
16
|
1. Identify Files:
|
|
17
|
-
Identify candidate files using `Glob` or `Grep`.
|
|
17
|
+
Identify candidate files using `Glob` or `Grep`. Read source files directly with `Read` line ranges; delegate large logs, stack traces, bulk multi-file scans, or an AST or call-graph summary the caller asks for, to the local model.
|
|
18
18
|
|
|
19
19
|
2. Execute Local Extraction:
|
|
20
20
|
Call the local extractor pointing to the skill script path:
|
|
@@ -75,7 +75,7 @@ cases:
|
|
|
75
75
|
dogfood: 2026-09-06 STT
|
|
76
76
|
provisional: true
|
|
77
77
|
input: "Read src/stt/config.py (40 lines) and tell me the default sample rate."
|
|
78
|
-
expected: "A 40-line file is read directly; the local extractor is not invoked
|
|
78
|
+
expected: "A 40-line file is read directly; the local extractor is not invoked for a single small source file."
|
|
79
79
|
graders:
|
|
80
80
|
- type: tool_used
|
|
81
81
|
expected: "Read:src/stt/config.py"
|
|
@@ -54,9 +54,17 @@ Do not author a standalone JS script.
|
|
|
54
54
|
</pipeline_routing>
|
|
55
55
|
|
|
56
56
|
<editorial_invariants>
|
|
57
|
+
- Page identity (every .docx, categories A and B): the layout of the reference release
|
|
58
|
+
document (stt-whisperx — Release 0.4.2), as encoded in `scripts/theme-claro.js` — A4,
|
|
59
|
+
`compact` geometry (1 cm left/right, top 1.35 cm, bottom 1.03 cm, header 0.31 cm, footer
|
|
60
|
+
0.29 cm), header `<app> — <document type>` italic grey 9 pt with the Claro logo right and
|
|
61
|
+
a bottom rule, footer `<author> — <role> | <area>` left and `Confidencial – Uso Interno |
|
|
62
|
+
Página X de Y` right over a top rule, `ELABORAÇÃO` block at the end. Use the theme
|
|
63
|
+
defaults (`T.headerLine`, `T.footerAuthorLine`, `T.HOUSE`); never hand-set margins,
|
|
64
|
+
header or footer. Details: `reference/authoring.md`.
|
|
57
65
|
- Typography: Arial for prose, Consolas for code/identifiers. Body justified at 1.15 line spacing.
|
|
58
66
|
- Pagination integrity: Heading lines enforced with `keepNext`; short tables and code blocks kept on single page; zero orphaned lines.
|
|
59
67
|
- Semaphore colors: Red = Critical/Action, Amber = Attention/Hypothesis, Green = Healthy/Target, Blue = Insight.
|
|
60
|
-
- File Lock Safety: If `~$<name>.docx` exists, Microsoft Word has the file locked.
|
|
61
|
-
- Deliverable:
|
|
68
|
+
- File Lock Safety: If `~$<name>.docx` exists, Microsoft Word has the file locked. Never overwrite it: the category A engines write `<name>-new.docx` automatically; a category B build writes `<name>-new.docx` the same way.
|
|
69
|
+
- Deliverable: `.docx` or `.pptx` only. No PDF is generated, not even as a check: the `.docx` is the print master (printed from Word).
|
|
62
70
|
</editorial_invariants>
|
|
@@ -12,7 +12,7 @@ build fails on an unknown section, a missing mandatory section or a malformed ta
|
|
|
12
12
|
node scripts/build-architecture.js <architecture.md> <out.docx>
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
Deliver only the `.docx` (
|
|
15
|
+
Deliver only the `.docx` (print master). No PDF is generated.
|
|
16
16
|
Same page identity as every house document: Claro logo, compact layout (1 cm lateral
|
|
17
17
|
margins), justified 1.15 text, two fonts, blocks flush with the text. Cover page +
|
|
18
18
|
static table of contents (titles only, no field update prompt; Word's navigation pane
|
|
@@ -77,8 +77,8 @@ sections as H1 (numbered), `###` as numbered H2 (N.k), `####` as H3; ```mermaid
|
|
|
77
77
|
rendered and embedded (scaled to the text width and at most ~half a page; `<!-- width:
|
|
78
78
|
70 -->` before a diagram scales it); "Figura N – …" / "Tabela N – …" lines become
|
|
79
79
|
captions; `> **Nota:** …` callouts; pipe tables (`<!-- cols: 30 70 -->` fixes widths);
|
|
80
|
-
`ELABORAÇÃO` block at the end; header "
|
|
81
|
-
|
|
80
|
+
`ELABORAÇÃO` block at the end; header "<app> — Arquitetura de Software" (house
|
|
81
|
+
identity, reference/authoring.md).
|
|
82
82
|
|
|
83
83
|
## Diagram set (minimum)
|
|
84
84
|
|
|
@@ -14,9 +14,10 @@ Import: `const T = require("./theme-claro");`
|
|
|
14
14
|
- Lead-in sentences before a code block or table take `{ keepNext: true }` so the
|
|
15
15
|
sentence and the block never separate.
|
|
16
16
|
- Page geometry by layout — select it with `T.configure({ layout })` **before building
|
|
17
|
-
any block**, because the usable width derives from it: `
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
any block**, because the usable width derives from it: `compact` (default, the house
|
|
18
|
+
page of every .docx: 1 cm left/right, top 1.35 cm, bottom 1.03 cm, header 0.31 cm,
|
|
19
|
+
footer 0.29 cm from the page edge) and `report` (opt-out: 2 cm all round,
|
|
20
|
+
header/footer at 1.25 cm).
|
|
20
21
|
`T.configure({ page: { left, right, top, bottom, header, footer } })` overrides in DXA
|
|
21
22
|
("personalizado"). `T.contentWidth()` is the usable width; tables, code blocks,
|
|
22
23
|
callouts, stat cards and images span exactly that width and are centered, so their
|
|
@@ -94,9 +95,13 @@ Import: `const T = require("./theme-claro");`
|
|
|
94
95
|
(spread: `children.push(...T.coverBlock({...}))`). `rule: false` for the compact
|
|
95
96
|
release front page (title + metaTable, no cover rule).
|
|
96
97
|
- `T.authorBlock({ heading, name, role, area, project })` -> array; heading defaults to
|
|
97
|
-
"ELABORAÇÃO
|
|
98
|
+
"ELABORAÇÃO", name/role to the house author (`T.HOUSE`).
|
|
98
99
|
- `T.buildDocument({ children, headerText, footerAuthor, footerNote, layout, page })` ->
|
|
99
|
-
Document for `Packer.toBuffer`. `
|
|
100
|
+
Document for `Packer.toBuffer`. `footerAuthor` defaults to `T.footerAuthorLine()`,
|
|
101
|
+
`footerNote` to "Confidencial – Uso Interno".
|
|
102
|
+
- `T.headerLine(app, docType)` -> `"<app> — <document type>"`; `T.footerAuthorLine({ author, role, area })`
|
|
103
|
+
-> `"<author> — <role> | <area>"` (missing fields keep the house value). Page identity:
|
|
104
|
+
reference/authoring.md.
|
|
100
105
|
Prefer `T.configure({ layout })` at the top of the script; passing `layout`/`page` here
|
|
101
106
|
applies them to the page only and warns (blocks were already sized).
|
|
102
107
|
|
|
@@ -15,8 +15,7 @@ unknown change category — the structure is the model, not a suggestion.
|
|
|
15
15
|
node scripts/build-release.js <release.md> <out.docx>
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
Deliver only the `.docx
|
|
19
|
-
check in a scratch directory; it is never written next to the deliverable.
|
|
18
|
+
Deliver only the `.docx` (print master). No PDF is generated.
|
|
20
19
|
|
|
21
20
|
## Where the structure comes from (2024-2026 practice)
|
|
22
21
|
|
|
@@ -36,8 +36,9 @@ project data, clear + rebuild a slide. Preferred over rebuilding by hand; see PP
|
|
|
36
36
|
|
|
37
37
|
All .docx templates share the house typography (reference/HELPERS.md, "House
|
|
38
38
|
typography"): two fonts, justified 1.15 text, headings kept with their text, tables and
|
|
39
|
-
code blocks never cut by a page break
|
|
40
|
-
|
|
39
|
+
code blocks never cut by a page break — and the house page identity of the release
|
|
40
|
+
document (compact geometry, header `<app> — <document type>`, footer author + page
|
|
41
|
+
count; reference/authoring.md). `.docx` is the only output; no PDF is generated.
|
|
41
42
|
|
|
42
43
|
## Adding a new template
|
|
43
44
|
|
|
@@ -4,50 +4,38 @@ Defaults for the authorship block and footer. Override if the user names another
|
|
|
4
4
|
author/project. These are house defaults, not hardcoded — pass different values
|
|
5
5
|
to the helpers when needed.
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## House page identity (every .docx)
|
|
8
8
|
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
- Area: DADOS & IA | Tech Dados & IA — Aquisição e Transformação de Dados & IA
|
|
12
|
-
- Project: configurable per document (e.g. "Projeto STT — Claro")
|
|
9
|
+
Measured from the reference release document (stt-whisperx — Release 0.4.2) and encoded
|
|
10
|
+
as the defaults of `scripts/theme-claro.js`; categories A and B use it unchanged.
|
|
13
11
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
12
|
+
| Element | Value |
|
|
13
|
+
|---|---|
|
|
14
|
+
| Page | A4 portrait (11906 x 16838 DXA) |
|
|
15
|
+
| Layout | `compact` (default): left/right 1 cm (567), top 1.35 cm (767), bottom 1.03 cm (586), header 0.31 cm (177), footer 0.29 cm (162) from the edge |
|
|
16
|
+
| Header content | `<app> — <document type>` (`T.headerLine(app, type)`), e.g. `stt-whisperx — Release` |
|
|
17
|
+
| Header format | Arial italic 9 pt grey `666666`; Claro logo 24 px on a right tab at the text width; bottom rule 3/8 pt `E5D3D3`; 5 pt after |
|
|
18
|
+
| Footer content | left `<author> — <role> \| <area>` (`T.footerAuthorLine()`); right `Confidencial – Uso Interno \| Página X de Y` (fields) |
|
|
19
|
+
| Footer format | Arial 9 pt grey `666666`; top rule 3/8 pt `E5D3D3`; 5 pt before; right tab at the text width |
|
|
20
|
+
| Closing block | `ELABORAÇÃO` (default heading of `T.authorBlock`) with name, role, project |
|
|
22
21
|
|
|
23
|
-
|
|
22
|
+
House author (`T.HOUSE`): André Negrelli, Tech Lead AI Data Engineer, DADOS & IA. Override
|
|
23
|
+
only when the user names another author/project; pass the fields, not a new format.
|
|
24
24
|
|
|
25
25
|
```js
|
|
26
|
-
T.
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
footerAuthor: "André Negrelli — DADOS & IA",
|
|
30
|
-
});
|
|
26
|
+
T.configure({ layout: "compact" }); // first line of the build (already the default)
|
|
27
|
+
T.authorBlock({ project: "Speech-to-text Claro / stt-whisperx" });
|
|
28
|
+
T.buildDocument({ children, headerText: T.headerLine("stt-whisperx", "Release") }); // footer = T.footerAuthorLine()
|
|
31
29
|
```
|
|
32
30
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
(stt-whisperx — Release 0.4.2): header `"<app> — Release"`, footer author
|
|
37
|
-
`"André Negrelli — Tech Lead AI Data Engineer | DADOS & IA"`, authorship heading
|
|
38
|
-
`"ELABORAÇÃO"` with name, role and `"<Project> — Claro / <app>"`, `T.configure({ layout: "compact" })`.
|
|
39
|
-
|
|
40
|
-
```js
|
|
41
|
-
T.authorBlock({ heading: "ELABORAÇÃO", name: "André Negrelli", role: "Tech Lead AI Data Engineer", project: "Speech-to-text Claro / stt-whisperx" });
|
|
42
|
-
T.configure({ layout: "compact" }); // first line of the build
|
|
43
|
-
T.buildDocument({ children, headerText: "stt-whisperx — Release", footerAuthor: "André Negrelli — Tech Lead AI Data Engineer | DADOS & IA" });
|
|
44
|
-
```
|
|
31
|
+
Document types in the header: `Release`, `Arquitetura de Software`, `Relatório Técnico`,
|
|
32
|
+
`Relatório Executivo`, `Revisão Arquitetural`. `layout: "report"` (2 cm all round) is an
|
|
33
|
+
opt-out used only when the user asks for it.
|
|
45
34
|
|
|
46
35
|
## Document identity (Claro theme)
|
|
47
36
|
|
|
48
|
-
- A4 page, Arial for prose + Consolas for code (no third font); geometry
|
|
49
|
-
|
|
50
|
-
top/bottom, header/footer close to the edge). Blocks span the text width, centered.
|
|
37
|
+
- A4 page, Arial for prose + Consolas for code (no third font); `compact` geometry for
|
|
38
|
+
every document (table above). Blocks span the text width, centered.
|
|
51
39
|
- Body text justified, 1.15 line spacing; headings keep with the next paragraph;
|
|
52
40
|
tables, code blocks and callouts are never cut by a page break.
|
|
53
41
|
- Claro institutional palette (red anchors, gray hierarchy): red headings, red
|
|
@@ -57,9 +45,9 @@ T.buildDocument({ children, headerText: "stt-whisperx — Release", footerAuthor
|
|
|
57
45
|
coverBlock) and small in the top-right of every page header (via buildDocument).
|
|
58
46
|
Both degrade cleanly if the asset is missing. Use `T.logoRun({height})` to place
|
|
59
47
|
it elsewhere.
|
|
60
|
-
-
|
|
61
|
-
|
|
62
|
-
-
|
|
48
|
+
- Header and footer exactly as the table above.
|
|
49
|
+
- Centered "ELABORAÇÃO" authorship block at the end.
|
|
50
|
+
- Deliverable is the .docx (print master); no PDF is generated.
|
|
63
51
|
- Output language: PT-BR by default (client-facing); change per request.
|
|
64
52
|
|
|
65
53
|
The logo asset is shared by both formats at scripts/assets/claro-logo.png (pptx uses
|
|
@@ -21,7 +21,7 @@ const model = {
|
|
|
21
21
|
tagline: (m) => "System Design",
|
|
22
22
|
org: (m) => m.org || m.project,
|
|
23
23
|
title: (m) => m.title,
|
|
24
|
-
headerText: (m) =>
|
|
24
|
+
headerText: (m) => `${m.app} — Arquitetura de Software`, // house header "<app> — <document type>"
|
|
25
25
|
requiredMeta: ["app", "title", "version", "date", "status"],
|
|
26
26
|
meta: [
|
|
27
27
|
["app", "Aplicação"], ["doc_type", "Tipo de documento"], ["version", "Versão do documento"],
|
|
@@ -210,15 +210,12 @@ while (i < md.length) {
|
|
|
210
210
|
}
|
|
211
211
|
|
|
212
212
|
// ---- assemble -------------------------------------------------------------
|
|
213
|
-
const
|
|
213
|
+
const title = opt.title || path.basename(inputPath, ".md");
|
|
214
|
+
const front = T.coverBlock({ title, subtitle: opt.subtitle || undefined, rule: false }); // compact front page, as the release
|
|
214
215
|
if (opt.meta) {
|
|
215
216
|
const rows = opt.meta.split(";").map((pair) => pair.split("=")).filter((r) => r.length === 2).map(([k, v]) => [k.trim(), v.trim()]);
|
|
216
217
|
if (rows.length) front.push(T.metaTable(rows), T.spacer(120));
|
|
217
218
|
}
|
|
218
|
-
const tail =
|
|
219
|
-
const doc = T.buildDocument({ children: [...front, T.spacer(160), ...children, ...tail], headerText: opt.header, footerAuthor:
|
|
220
|
-
|
|
221
|
-
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
|
222
|
-
fs.writeFileSync(outputPath, buf);
|
|
223
|
-
console.log("Gerado:", outputPath, `(${buf.length} bytes)`, `diagramas: ${diagramIndex}`);
|
|
224
|
-
});
|
|
219
|
+
const tail = T.authorBlock({ name: opt.author || undefined, role: opt.role || undefined, area: T.HOUSE.area, project: opt.project || undefined });
|
|
220
|
+
const doc = T.buildDocument({ children: [...front, T.spacer(160), ...children, ...tail], headerText: opt.header || title, footerAuthor: T.footerAuthorLine({ author: opt.author, role: opt.role }) });
|
|
221
|
+
T.saveDocx(doc, outputPath).then(() => console.log(`diagramas: ${diagramIndex}`));
|
|
@@ -49,10 +49,6 @@ function hasChrome() {
|
|
|
49
49
|
if (hasChrome()) ok("Chrome/Chromium para renderizar Mermaid");
|
|
50
50
|
else info("Chrome ausente (só p/ diagramas) → npx puppeteer browsers install chrome-headless-shell");
|
|
51
51
|
|
|
52
|
-
// validação opcional (soffice)
|
|
53
|
-
try { execSync("soffice --version", { stdio: "pipe" }); ok("LibreOffice (soffice) — validação visual opcional"); }
|
|
54
|
-
catch { info("LibreOffice ausente (opcional, só p/ gerar PDF de conferência)"); }
|
|
55
|
-
|
|
56
52
|
console.log("");
|
|
57
53
|
if (fail) { console.log(`\x1b[31m${fail} pré-requisito(s) obrigatório(s) faltando.\x1b[0m\n`); process.exit(1); }
|
|
58
54
|
console.log("\x1b[32mPronto para gerar relatórios.\x1b[0m\n");
|
|
@@ -259,18 +259,10 @@ async function build(model, inputPath, outputPath) {
|
|
|
259
259
|
children.push(...renderBlocks(body, ctx));
|
|
260
260
|
}
|
|
261
261
|
|
|
262
|
-
children.push(...T.authorBlock({ heading: model.authorHeading || "ELABORAÇÃO", name: meta.author ||
|
|
263
|
-
const doc = T.buildDocument({ children, headerText: model.headerText(meta), footerAuthor:
|
|
262
|
+
children.push(...T.authorBlock({ heading: model.authorHeading || "ELABORAÇÃO", name: meta.author || T.HOUSE.author, role: meta.role || T.HOUSE.role, area: meta.area, project: meta.project || meta.app }));
|
|
263
|
+
const doc = T.buildDocument({ children, headerText: model.headerText(meta), footerAuthor: T.footerAuthorLine({ author: meta.author, role: meta.role, area: meta.area }), updateFields: false });
|
|
264
264
|
|
|
265
|
-
|
|
266
|
-
const outAbs = path.resolve(outputPath);
|
|
267
|
-
const lock = path.join(path.dirname(outAbs), "~$" + path.basename(outAbs).slice(2));
|
|
268
|
-
const target = fs.existsSync(lock) ? outAbs.replace(/\.docx$/i, "-new.docx") : outAbs;
|
|
269
|
-
if (target !== outAbs) console.warn(`${model.name} model: ${path.basename(outAbs)} is open in Word; writing ${path.basename(target)} instead — close Word and rename.`);
|
|
270
|
-
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
271
|
-
const buf = await Packer.toBuffer(doc);
|
|
272
|
-
fs.writeFileSync(target, buf);
|
|
273
|
-
console.log("Generated:", target, `(${buf.length} bytes)`);
|
|
265
|
+
await T.saveDocx(doc, outputPath); // Word lock guard lives in the theme (one implementation)
|
|
274
266
|
}
|
|
275
267
|
|
|
276
268
|
/** CLI wrapper: `node build-<model>.js <content.md> <out.docx>`. */
|
|
@@ -20,6 +20,8 @@ const path = require("path");
|
|
|
20
20
|
const { Packer, AlignmentType } = require("docx");
|
|
21
21
|
const T = require("./theme-claro");
|
|
22
22
|
|
|
23
|
+
T.configure({ layout: "compact" }); // house page (release geometry); set before any block
|
|
24
|
+
|
|
23
25
|
const children = [];
|
|
24
26
|
|
|
25
27
|
// ---- CAPA ----
|
|
@@ -98,22 +100,18 @@ children.push(T.codeBlock([
|
|
|
98
100
|
|
|
99
101
|
// ---- AUTORIA ----
|
|
100
102
|
children.push(...T.authorBlock({
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
103
|
+
heading: "ELABORAÇÃO",
|
|
104
|
+
name: T.HOUSE.author,
|
|
105
|
+
role: T.HOUSE.role,
|
|
104
106
|
project: "Projeto STT — Claro · 19/06/2026",
|
|
105
107
|
}));
|
|
106
108
|
|
|
107
109
|
// ---- MONTAGEM ----
|
|
108
110
|
const doc = T.buildDocument({
|
|
109
111
|
children,
|
|
110
|
-
headerText: "
|
|
111
|
-
footerAuthor: "André Negrelli — DADOS & IA"
|
|
112
|
+
headerText: T.headerLine("<app>", "Relatório Técnico"), // house header "<app> — <document type>"
|
|
113
|
+
footerAuthor: T.footerAuthorLine(), // "André Negrelli — Tech Lead AI Data Engineer | DADOS & IA"
|
|
112
114
|
});
|
|
113
115
|
|
|
114
116
|
const outPath = process.argv[2] || path.join(process.cwd(), "out", "Exemplo_Tema_Claro.docx");
|
|
115
|
-
|
|
116
|
-
Packer.toBuffer(doc).then((buf) => {
|
|
117
|
-
fs.writeFileSync(outPath, buf);
|
|
118
|
-
console.log("Gerado:", outPath, `(${buf.length} bytes)`);
|
|
119
|
-
});
|
|
117
|
+
T.saveDocx(doc, outPath); // writes <name>-new.docx when Word holds the file open
|
|
@@ -24,6 +24,8 @@ const path = require("path");
|
|
|
24
24
|
const { Packer, AlignmentType } = require("docx");
|
|
25
25
|
const T = require("./theme-claro");
|
|
26
26
|
|
|
27
|
+
T.configure({ layout: "compact" }); // house page (release geometry); set before any block
|
|
28
|
+
|
|
27
29
|
const children = [];
|
|
28
30
|
|
|
29
31
|
/** Severity scale shared by the findings table and the summary cards. */
|
|
@@ -175,21 +177,17 @@ children.push(T.callout([
|
|
|
175
177
|
|
|
176
178
|
// ---- AUTHORSHIP ----
|
|
177
179
|
children.push(...T.authorBlock({
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
180
|
+
heading: "ELABORAÇÃO",
|
|
181
|
+
name: T.HOUSE.author,
|
|
182
|
+
role: T.HOUSE.role,
|
|
181
183
|
project: "<Projeto> — Claro · DD/MM/AAAA",
|
|
182
184
|
}));
|
|
183
185
|
|
|
184
186
|
const doc = T.buildDocument({
|
|
185
187
|
children,
|
|
186
|
-
headerText: "Revisão Arquitetural
|
|
187
|
-
footerAuthor: "André Negrelli — DADOS & IA"
|
|
188
|
+
headerText: T.headerLine("<app>", "Revisão Arquitetural"), // house header "<app> — <document type>"
|
|
189
|
+
footerAuthor: T.footerAuthorLine(), // "André Negrelli — Tech Lead AI Data Engineer | DADOS & IA"
|
|
188
190
|
});
|
|
189
191
|
|
|
190
192
|
const outPath = process.argv[2] || path.join(process.cwd(), "out", "Revisao_Arquitetural.docx");
|
|
191
|
-
|
|
192
|
-
Packer.toBuffer(doc).then((buf) => {
|
|
193
|
-
fs.writeFileSync(outPath, buf);
|
|
194
|
-
console.log("Gerado:", outPath, `(${buf.length} bytes)`);
|
|
195
|
-
});
|
|
193
|
+
T.saveDocx(doc, outPath); // writes <name>-new.docx when Word holds the file open
|
|
@@ -19,6 +19,8 @@ const path = require("path");
|
|
|
19
19
|
const { Packer, AlignmentType } = require("docx");
|
|
20
20
|
const T = require("./theme-claro");
|
|
21
21
|
|
|
22
|
+
T.configure({ layout: "compact" }); // house page (release geometry); set before any block
|
|
23
|
+
|
|
22
24
|
const children = [];
|
|
23
25
|
|
|
24
26
|
// ---- COVER (rich) ----
|
|
@@ -94,21 +96,17 @@ children.push(T.callout([
|
|
|
94
96
|
|
|
95
97
|
// ---- AUTHORSHIP ----
|
|
96
98
|
children.push(...T.authorBlock({
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
99
|
+
heading: "ELABORAÇÃO",
|
|
100
|
+
name: T.HOUSE.author,
|
|
101
|
+
role: T.HOUSE.role,
|
|
100
102
|
project: "Projeto STT — Claro · 22/06/2026",
|
|
101
103
|
}));
|
|
102
104
|
|
|
103
105
|
const doc = T.buildDocument({
|
|
104
106
|
children,
|
|
105
|
-
headerText: "Relatório Executivo
|
|
106
|
-
footerAuthor: "André Negrelli — DADOS & IA"
|
|
107
|
+
headerText: T.headerLine("<app>", "Relatório Executivo"), // house header "<app> — <document type>"
|
|
108
|
+
footerAuthor: T.footerAuthorLine(), // "André Negrelli — Tech Lead AI Data Engineer | DADOS & IA"
|
|
107
109
|
});
|
|
108
110
|
|
|
109
111
|
const outPath = process.argv[2] || path.join(process.cwd(), "out", "Relatorio_Executivo.docx");
|
|
110
|
-
|
|
111
|
-
Packer.toBuffer(doc).then((buf) => {
|
|
112
|
-
fs.writeFileSync(outPath, buf);
|
|
113
|
-
console.log("Gerado:", outPath, `(${buf.length} bytes)`);
|
|
114
|
-
});
|
|
112
|
+
T.saveDocx(doc, outPath); // writes <name>-new.docx when Word holds the file open
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
const fs = require("fs");
|
|
13
13
|
const path = require("path");
|
|
14
14
|
const {
|
|
15
|
-
Document, Paragraph, TextRun, Table, TableRow, TableCell,
|
|
15
|
+
Document, Packer, Paragraph, TextRun, Table, TableRow, TableCell,
|
|
16
16
|
Header, Footer, AlignmentType, BorderStyle, WidthType, ShadingType,
|
|
17
17
|
VerticalAlign, PageNumber, ImageRun, PageBreak,
|
|
18
18
|
} = require("docx");
|
|
@@ -31,9 +31,10 @@ const C = {
|
|
|
31
31
|
noteFill: "FFF2CC", dangerFill: "FFE0E0", successFill: "E2EFDA", infoFill: "FBEDED",
|
|
32
32
|
rule: "E2000F", codeFill: "F5F5F5", codeText: "1F1F1F", codeBar: "555555",
|
|
33
33
|
};
|
|
34
|
-
// A4 portrait. Geometry per layout (DXA): "
|
|
35
|
-
//
|
|
36
|
-
//
|
|
34
|
+
// A4 portrait. Geometry per layout (DXA): "compact" is the house page, measured from
|
|
35
|
+
// the reference release document (stt-whisperx — Release 0.4.2) and the default for
|
|
36
|
+
// every .docx — 1 cm lateral margins, top 1.35 cm, bottom 1.03 cm, header 0.31 cm and
|
|
37
|
+
// footer 0.29 cm from the page edge; "report" (2 cm all round) is an explicit opt-out. Blocks (tables, code, callouts,
|
|
37
38
|
// images) span exactly the text width and are centered, so their edges meet the
|
|
38
39
|
// justified text. Select the layout with T.configure({ layout }) BEFORE building
|
|
39
40
|
// blocks: the usable width depends on it.
|
|
@@ -42,6 +43,15 @@ const LAYOUTS = {
|
|
|
42
43
|
report: { top: 1134, bottom: 1134, left: 1134, right: 1134, header: 708, footer: 708 },
|
|
43
44
|
compact: { top: 767, bottom: 586, left: 567, right: 567, header: 177, footer: 162 },
|
|
44
45
|
};
|
|
46
|
+
// House identity (same reference document): header "<app> — <document type>" in italic
|
|
47
|
+
// grey 9 pt with the Claro logo (24 px) right-aligned over a bottom rule; footer over a top
|
|
48
|
+
// rule with "<author> — <role> | <area>" on the left and "<note> | Página X de Y" on the
|
|
49
|
+
// right (9 pt grey). The .docx is the print master: no PDF is produced.
|
|
50
|
+
const HOUSE = { author: "André Negrelli", role: "Tech Lead AI Data Engineer", area: "DADOS & IA", footerNote: "Confidencial – Uso Interno" };
|
|
51
|
+
/** Footer author line "<author> — <role> | <area>"; o overrides any field (falsy keeps the house value). */
|
|
52
|
+
const footerAuthorLine = (o = {}) => `${o.author || HOUSE.author} — ${o.role || HOUSE.role} | ${o.area || HOUSE.area}`;
|
|
53
|
+
/** Header line "<app> — <document type>" (e.g. "stt-whisperx — Release"). */
|
|
54
|
+
const headerLine = (app, docType) => [app, docType].filter(Boolean).join(" — ");
|
|
45
55
|
const CONTENT_W = 9640; // legacy constant (report layout); prefer contentWidth()
|
|
46
56
|
const EMU_PER_PX = 9525;
|
|
47
57
|
// Font sizes in half-points: body 11 pt, code 9 pt, table cell 10 pt, cell code 8.5 pt.
|
|
@@ -51,7 +61,7 @@ const LOGO_PATH = path.join(__dirname, "assets", "claro-logo.png");
|
|
|
51
61
|
|
|
52
62
|
// Mutable house defaults; T.configure({ justify: false }) opts out for a build.
|
|
53
63
|
// Justified text needs automatic hyphenation to avoid rivers of white space.
|
|
54
|
-
const DEFAULTS = { justify: true, hyphenate: true, line: 276, lang: "pt-BR", layout: "
|
|
64
|
+
const DEFAULTS = { justify: true, hyphenate: true, line: 276, lang: "pt-BR", layout: "compact", page: {} };
|
|
55
65
|
/** Override the house defaults (layout, page overrides, justify, hyphenate, line, lang) for the current build. */
|
|
56
66
|
function configure(o = {}) { Object.assign(DEFAULTS, o); }
|
|
57
67
|
/** Page geometry of the configured layout, with DEFAULTS.page overrides applied. */
|
|
@@ -324,8 +334,8 @@ function coverBlock({ title, subtitle, tagline, org, metaLines = [], rule = true
|
|
|
324
334
|
}
|
|
325
335
|
|
|
326
336
|
// ---- AUTHORSHIP BLOCK ----
|
|
327
|
-
/** Authorship block closing the document; heading
|
|
328
|
-
function authorBlock({ heading: hd = "ELABORAÇÃO
|
|
337
|
+
/** Authorship block closing the document; heading "ELABORAÇÃO", name/role default to the house author. */
|
|
338
|
+
function authorBlock({ heading: hd = "ELABORAÇÃO", name = HOUSE.author, role = HOUSE.role, area, project } = {}) {
|
|
329
339
|
const out = [];
|
|
330
340
|
const line = (text, o) => new Paragraph({ alignment: AlignmentType.CENTER, keepNext: true, keepLines: true, spacing: o.spacing, children: [t(text, o.run)] });
|
|
331
341
|
out.push(spacer(240));
|
|
@@ -339,8 +349,8 @@ function authorBlock({ heading: hd = "ELABORAÇÃO E ANÁLISE", name, role, area
|
|
|
339
349
|
}
|
|
340
350
|
|
|
341
351
|
// ---- DOCUMENT ----
|
|
342
|
-
/** Final Document: A4, header/footer, lists, house defaults; layout "
|
|
343
|
-
function buildDocument({ children, headerText = "", footerAuthor =
|
|
352
|
+
/** Final Document: A4, house header/footer, lists, house defaults; layout "compact" (default) | "report", page = DXA overrides. */
|
|
353
|
+
function buildDocument({ children, headerText = "", footerAuthor = footerAuthorLine(), footerNote = HOUSE.footerNote, layout, page, updateFields = false }) {
|
|
344
354
|
if ((layout && layout !== DEFAULTS.layout) || page) {
|
|
345
355
|
console.warn("theme-claro: pass layout/page to T.configure() before building blocks; buildDocument() applies them to the page only");
|
|
346
356
|
configure({ layout: layout || DEFAULTS.layout, page: page || DEFAULTS.page });
|
|
@@ -387,9 +397,28 @@ function buildDocument({ children, headerText = "", footerAuthor = "", footerNot
|
|
|
387
397
|
});
|
|
388
398
|
}
|
|
389
399
|
|
|
400
|
+
/**
|
|
401
|
+
* Write the Document to outPath, never over a file Word holds open: with a lock
|
|
402
|
+
* "~$<name>" beside it, writes "<name>-new.docx" and warns (Word reloads images lazily
|
|
403
|
+
* and would save broken links). Resolves to the path actually written.
|
|
404
|
+
*/
|
|
405
|
+
async function saveDocx(doc, outPath) {
|
|
406
|
+
const outAbs = path.resolve(outPath);
|
|
407
|
+
const lock = path.join(path.dirname(outAbs), "~$" + path.basename(outAbs).slice(2));
|
|
408
|
+
const target = fs.existsSync(lock) ? outAbs.replace(/\.docx$/i, "-new.docx") : outAbs;
|
|
409
|
+
if (target !== outAbs) console.warn(`${path.basename(outAbs)} is open in Word; writing ${path.basename(target)} instead — close Word and rename.`);
|
|
410
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
411
|
+
const buf = await Packer.toBuffer(doc);
|
|
412
|
+
fs.writeFileSync(target, buf);
|
|
413
|
+
console.log("Generated:", target, `(${buf.length} bytes)`);
|
|
414
|
+
return target;
|
|
415
|
+
}
|
|
416
|
+
|
|
390
417
|
module.exports = {
|
|
391
418
|
// tokens
|
|
392
419
|
C, FONT, MONO, SZ, CONTENT_W, PAGE, LAYOUTS, DEFAULTS, configure, contentWidth, geometry, fitCols,
|
|
420
|
+
// house identity
|
|
421
|
+
HOUSE, footerAuthorLine, headerLine,
|
|
393
422
|
// runs
|
|
394
423
|
t, mono, cap, rich, rc,
|
|
395
424
|
// blocks
|
|
@@ -399,5 +428,5 @@ module.exports = {
|
|
|
399
428
|
// highlights
|
|
400
429
|
callout, calloutLine, codeBlock, statCards,
|
|
401
430
|
// media / structure
|
|
402
|
-
image, logoRun, coverBlock, authorBlock, buildDocument, LOGO_PATH,
|
|
431
|
+
image, logoRun, coverBlock, authorBlock, buildDocument, saveDocx, LOGO_PATH,
|
|
403
432
|
};
|