blank-agentic-cli 0.1.0__py3-none-any.whl

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.
@@ -0,0 +1,88 @@
1
+ Metadata-Version: 2.4
2
+ Name: blank-agentic-cli
3
+ Version: 0.1.0
4
+ Summary: CLI scaffolder for agentic AI research projects
5
+ Author: Shusuke Ioku
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/shusuke-ioku/blank
8
+ Project-URL: Repository, https://github.com/shusuke-ioku/blank
9
+ Project-URL: Issues, https://github.com/shusuke-ioku/blank/issues
10
+ Keywords: cli,scaffold,research,agentic-ai,typst
11
+ Classifier: Environment :: Console
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: Topic :: Software Development :: Code Generators
14
+ Classifier: Topic :: Utilities
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3 :: Only
17
+ Classifier: License :: OSI Approved :: MIT License
18
+ Classifier: Operating System :: OS Independent
19
+ Requires-Python: >=3.9
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Dynamic: license-file
23
+
24
+ # blank
25
+
26
+ `blank` is a CLI scaffolder for research projects driven by agentic AI workflows.
27
+
28
+ ## Install
29
+
30
+ From PyPI (after release):
31
+
32
+ ```bash
33
+ pipx install blank-agentic-cli
34
+ ```
35
+
36
+ From GitHub:
37
+
38
+ ```bash
39
+ pipx install git+https://github.com/shusuke-ioku/blank.git
40
+ ```
41
+
42
+ ## Usage
43
+
44
+ ```bash
45
+ blank init
46
+ blank init my-project
47
+ blank init my-project --project-name "My Project"
48
+ blank init my-project --dry-run
49
+ blank init my-project --force
50
+ blank init my-project --no-agents
51
+ blank init my-project --paper-template latex
52
+ blank init my-project --paper-template blank
53
+ ```
54
+
55
+ If `--paper-template` is omitted and you run in a terminal, `blank init` asks you to choose:
56
+ - `latex`: LaTeX-like Typst starter
57
+ - `blank`: minimal empty Typst file
58
+
59
+ ## Generated scaffold
60
+
61
+ - `analysis/scripts/`
62
+ - `analysis/data/`
63
+ - `analysis/output/`
64
+ - `paper/`
65
+ - `idea/`
66
+ - `.codex/` and `.claude/` by default
67
+
68
+ ## Development
69
+
70
+ ```bash
71
+ python3 -m venv .venv
72
+ source .venv/bin/activate
73
+ pip install -e . pytest
74
+ pytest
75
+ ```
76
+
77
+ ## Release (PyPI)
78
+
79
+ 1. Create PyPI project `blank-agentic-cli` and enable Trusted Publishing for this GitHub repo.
80
+ 2. (Optional) Run GitHub Action `publish` manually with `testpypi` to verify packaging.
81
+ 3. Tag a release:
82
+
83
+ ```bash
84
+ git tag v0.1.0
85
+ git push origin v0.1.0
86
+ ```
87
+
88
+ 4. The `publish` workflow builds and uploads to PyPI.
@@ -0,0 +1,27 @@
1
+ blank_agentic_cli-0.1.0.dist-info/licenses/LICENSE,sha256=MRBn9GxVpiQ-55qAQkGpU7p7Ljy8XgpXlJVUZxf3urk,1069
2
+ blank_cli/__init__.py,sha256=4t_crzhrLum--oyowUMxtjBTzUtWp7oRTF22ewEvJG4,49
3
+ blank_cli/__main__.py,sha256=MHKZ_ae3fSLGTLUUMOx15fWdeOnJSHhq-zslRP5F5Lc,79
4
+ blank_cli/cli.py,sha256=XekEayeZmmgYoy3OxMyuoidJY1AbKJ9uCmO7aGzxMIw,3686
5
+ blank_cli/scaffold.py,sha256=EWavwWZBSfREcBmG-Jru1SgtNcNfKU5spMYEn7-ONwo,6064
6
+ blank_cli/templates/.gitignore.tpl,sha256=rX3wTwZbiukecTU11CdnpbP6uGER3bYxUMTyv8DwymM,220
7
+ blank_cli/templates/.here.tpl,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ blank_cli/templates/README.md.tpl,sha256=nH_JARSx86LLxLTPvkKMiH3SM2jtyHUf9qwo53_NNGY,392
9
+ blank_cli/templates/.claude/settings.local.json.tpl,sha256=7m0QNJhb4G3eYAXAb4iQUxOoayhkUuh1apd6bN9V_Ls,43
10
+ blank_cli/templates/.codex/config.toml.tpl,sha256=1O8PWj3W7pyP07MWVOaTvHUVXcHmDK0Fs54GNJd5PKU,154
11
+ blank_cli/templates/.codex/project.md.tpl,sha256=PiVPviq4OH1nrvG2LDB98kFVdKJep2KXYuhFq_JfUro,382
12
+ blank_cli/templates/analysis/data/codebook.md.tpl,sha256=-Xk1AjYzRTXJ-4kL6WfLMuAmnokaQGoR33dVjSBM0uQ,214
13
+ blank_cli/templates/analysis/output/figures/.gitkeep.tpl,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
+ blank_cli/templates/analysis/output/results/.gitkeep.tpl,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
+ blank_cli/templates/analysis/output/tables/.gitkeep.tpl,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
+ blank_cli/templates/analysis/scripts/00_setup.R.tpl,sha256=r0gCODXKfVTpsOl0IamUWx7MpgNqE5MW75LoOkBvx7I,174
17
+ blank_cli/templates/analysis/scripts/20_data.R.tpl,sha256=-i_dgFWTCUmbIAuicdZhBjEt3ZQHhc_NoyYTZTka6yE,125
18
+ blank_cli/templates/analysis/scripts/30_results_main.R.tpl,sha256=Soa_wWAnI47oL8XP8zbh1jvCVgrIDHwSExqGiM1gYeA,191
19
+ blank_cli/templates/paper/aesthetics.typ.tpl,sha256=3GhiK02rsowbz72kJrjpJ9HwOV7n4iN15-Ss0qBRAlE,65
20
+ blank_cli/templates/paper/paper_blank.typ.tpl,sha256=cOWcOc6yL-R0b1G1WgCb2bceQacr4cjIECMIfHq3FK0,19
21
+ blank_cli/templates/paper/paper_latex.typ.tpl,sha256=4flbPQ3FhDISToXhohQ1i2i9acLa5Q7CXq0JOvnu1yk,300
22
+ blank_cli/templates/paper/ref.bib.tpl,sha256=oNfPSWJdTpuwQkCIVuiP_O4RoMgQ3NZbLqICIeu5hyk,133
23
+ blank_agentic_cli-0.1.0.dist-info/METADATA,sha256=2L_U7NeWP2xJ_YpTGDhHC1xXA-YNG__GsfWFMI3P8XU,2177
24
+ blank_agentic_cli-0.1.0.dist-info/WHEEL,sha256=YCfwYGOYMi5Jhw2fU4yNgwErybb2IX5PEwBKV4ZbdBo,91
25
+ blank_agentic_cli-0.1.0.dist-info/entry_points.txt,sha256=ivuRUixJGWOksbVr4a70y9W2HnY6wE9UHOejY8qHbWk,45
26
+ blank_agentic_cli-0.1.0.dist-info/top_level.txt,sha256=AMjS8r2y0yWcJKEo_QWbS8G4df-6z54w884ny1gXA6M,10
27
+ blank_agentic_cli-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ blank = blank_cli.cli:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Shusuke Ioku
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ blank_cli
blank_cli/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ __all__ = ["__version__"]
2
+
3
+ __version__ = "0.1.0"
blank_cli/__main__.py ADDED
@@ -0,0 +1,4 @@
1
+ from .cli import main
2
+
3
+ if __name__ == "__main__":
4
+ raise SystemExit(main())
blank_cli/cli.py ADDED
@@ -0,0 +1,116 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import sys
5
+ from importlib import resources
6
+ from pathlib import Path
7
+
8
+ from .scaffold import ScaffoldConflictError, apply_actions, plan_actions
9
+
10
+
11
+ def _build_parser() -> argparse.ArgumentParser:
12
+ parser = argparse.ArgumentParser(prog="blank", description="Scaffold research projects")
13
+ subparsers = parser.add_subparsers(dest="command", required=True)
14
+
15
+ init_parser = subparsers.add_parser("init", help="Initialize a research project scaffold")
16
+ init_parser.add_argument("target_dir", nargs="?", default=".", help="Target directory (default: current directory)")
17
+ init_parser.add_argument(
18
+ "--project-name",
19
+ help="Project name used in generated template files (default: target directory name)",
20
+ )
21
+ init_parser.add_argument(
22
+ "--force",
23
+ action="store_true",
24
+ help="Overwrite scaffold files when they already exist and differ",
25
+ )
26
+ init_parser.add_argument("--dry-run", action="store_true", help="Show planned actions without writing files")
27
+ init_parser.add_argument(
28
+ "--no-agents",
29
+ action="store_true",
30
+ help="Skip generating .codex/ and .claude/ files",
31
+ )
32
+ init_parser.add_argument(
33
+ "--paper-template",
34
+ choices=["latex", "blank"],
35
+ help="Paper template style for paper/paper.typ (latex or blank)",
36
+ )
37
+
38
+ return parser
39
+
40
+
41
+ def _prompt_paper_template() -> str:
42
+ print("Choose paper template for paper/paper.typ:")
43
+ print("1) latex (LaTeX-like layout) [recommended]")
44
+ print("2) blank (empty starter file)")
45
+ while True:
46
+ choice = input("Enter 1 or 2 [1]: ").strip()
47
+ if choice in ("", "1"):
48
+ return "latex"
49
+ if choice == "2":
50
+ return "blank"
51
+ print("Invalid choice. Please enter 1 or 2.")
52
+
53
+
54
+ def _command_init(args: argparse.Namespace) -> int:
55
+ target_dir = Path(args.target_dir).expanduser().resolve()
56
+ project_name = args.project_name or target_dir.name
57
+ include_agents = not args.no_agents
58
+ paper_template = args.paper_template
59
+ if paper_template is None:
60
+ if sys.stdin.isatty():
61
+ paper_template = _prompt_paper_template()
62
+ else:
63
+ paper_template = "latex"
64
+
65
+ templates_dir = resources.files("blank_cli") / "templates"
66
+ templates_path = Path(str(templates_dir))
67
+
68
+ target_dir.mkdir(parents=True, exist_ok=True)
69
+
70
+ try:
71
+ actions = plan_actions(
72
+ target_dir=target_dir,
73
+ templates_dir=templates_path,
74
+ project_name=project_name,
75
+ include_agents=include_agents,
76
+ paper_template=paper_template,
77
+ force=args.force,
78
+ )
79
+ except ScaffoldConflictError as exc:
80
+ print(str(exc), file=sys.stderr)
81
+ return 3
82
+
83
+ counts = apply_actions(
84
+ actions=actions,
85
+ target_dir=target_dir,
86
+ templates_dir=templates_path,
87
+ project_name=project_name,
88
+ include_agents=include_agents,
89
+ paper_template=paper_template,
90
+ dry_run=args.dry_run,
91
+ )
92
+
93
+ mode = "DRY RUN" if args.dry_run else "DONE"
94
+ print(f"[{mode}] blank init -> {target_dir}")
95
+ print(f"paper_template={paper_template}")
96
+ print(
97
+ f"created_dirs={counts['created_dirs']} created_files={counts['created_files']} "
98
+ f"replaced_files={counts['replaced_files']} skipped={counts['skipped']}"
99
+ )
100
+
101
+ return 0
102
+
103
+
104
+ def main(argv: list[str] | None = None) -> int:
105
+ parser = _build_parser()
106
+ args = parser.parse_args(argv)
107
+
108
+ if args.command == "init":
109
+ return _command_init(args)
110
+
111
+ parser.print_usage()
112
+ return 2
113
+
114
+
115
+ if __name__ == "__main__":
116
+ raise SystemExit(main())
blank_cli/scaffold.py ADDED
@@ -0,0 +1,191 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from datetime import date
5
+ from pathlib import Path
6
+ from typing import Dict, List
7
+
8
+
9
+ @dataclass(frozen=True)
10
+ class ScaffoldAction:
11
+ action: str
12
+ path: Path
13
+
14
+
15
+ class ScaffoldConflictError(RuntimeError):
16
+ pass
17
+
18
+
19
+ DIRECTORIES: List[str] = [
20
+ "analysis/scripts",
21
+ "analysis/data/base",
22
+ "analysis/data/covariates",
23
+ "analysis/data/rworg",
24
+ "analysis/data/ocr_tables",
25
+ "analysis/output/figures",
26
+ "analysis/output/tables",
27
+ "analysis/output/results",
28
+ "paper",
29
+ "idea",
30
+ ]
31
+
32
+ TEMPLATE_FILES: List[str] = [
33
+ "README.md.tpl",
34
+ ".gitignore.tpl",
35
+ ".here.tpl",
36
+ "analysis/scripts/00_setup.R.tpl",
37
+ "analysis/scripts/20_data.R.tpl",
38
+ "analysis/scripts/30_results_main.R.tpl",
39
+ "analysis/data/codebook.md.tpl",
40
+ "paper/ref.bib.tpl",
41
+ "paper/aesthetics.typ.tpl",
42
+ "analysis/output/figures/.gitkeep.tpl",
43
+ "analysis/output/tables/.gitkeep.tpl",
44
+ "analysis/output/results/.gitkeep.tpl",
45
+ ".codex/project.md.tpl",
46
+ ".codex/config.toml.tpl",
47
+ ".claude/settings.local.json.tpl",
48
+ ]
49
+
50
+ PAPER_TEMPLATE_MAP = {
51
+ "latex": ("paper/paper_latex.typ.tpl", "paper/paper.typ"),
52
+ "blank": ("paper/paper_blank.typ.tpl", "paper/paper.typ"),
53
+ }
54
+
55
+
56
+ def _render_template(content: str, variables: Dict[str, str]) -> str:
57
+ rendered = content
58
+ for key, value in variables.items():
59
+ rendered = rendered.replace(f"{{{{{key}}}}}", value)
60
+ return rendered
61
+
62
+
63
+ def _is_agent_file(template_path: str) -> bool:
64
+ return template_path.startswith(".codex/") or template_path.startswith(".claude/")
65
+
66
+
67
+ def _output_path_from_template(template_path: str) -> Path:
68
+ if not template_path.endswith(".tpl"):
69
+ raise ValueError(f"Template path must end with .tpl: {template_path}")
70
+ return Path(template_path[:-4])
71
+
72
+
73
+ def _effective_templates(paper_template: str) -> List[str]:
74
+ if paper_template not in PAPER_TEMPLATE_MAP:
75
+ raise ValueError(f"Unsupported paper template: {paper_template}")
76
+ _paper_src, paper_out = PAPER_TEMPLATE_MAP[paper_template]
77
+ return TEMPLATE_FILES + [f"{paper_out}.tpl"]
78
+
79
+
80
+ def plan_actions(
81
+ target_dir: Path,
82
+ templates_dir: Path,
83
+ project_name: str,
84
+ include_agents: bool,
85
+ paper_template: str,
86
+ force: bool,
87
+ ) -> List[ScaffoldAction]:
88
+ variables = {
89
+ "project_name": project_name,
90
+ "today": str(date.today()),
91
+ }
92
+ actions: List[ScaffoldAction] = []
93
+
94
+ for rel_dir in DIRECTORIES:
95
+ out_dir = target_dir / rel_dir
96
+ if out_dir.exists():
97
+ actions.append(ScaffoldAction("skip_dir", out_dir))
98
+ else:
99
+ actions.append(ScaffoldAction("create_dir", out_dir))
100
+
101
+ effective_templates = _effective_templates(paper_template)
102
+ paper_src, paper_out = PAPER_TEMPLATE_MAP[paper_template]
103
+ virtual_paths = {f"{paper_out}.tpl": paper_src}
104
+
105
+ for template_rel in effective_templates:
106
+ source_template_rel = virtual_paths.get(template_rel, template_rel)
107
+ if not include_agents and _is_agent_file(template_rel):
108
+ continue
109
+
110
+ template_file = templates_dir / source_template_rel
111
+ if not template_file.exists():
112
+ raise FileNotFoundError(f"Missing template file: {template_file}")
113
+
114
+ out_file = target_dir / _output_path_from_template(template_rel)
115
+ rendered_content = _render_template(template_file.read_text(encoding="utf-8"), variables)
116
+
117
+ if out_file.exists():
118
+ existing_content = out_file.read_text(encoding="utf-8")
119
+ if existing_content == rendered_content:
120
+ actions.append(ScaffoldAction("skip_file", out_file))
121
+ elif force:
122
+ actions.append(ScaffoldAction("replace_file", out_file))
123
+ else:
124
+ raise ScaffoldConflictError(
125
+ f"Refusing to overwrite existing file: {out_file}. Use --force to replace scaffold files."
126
+ )
127
+ else:
128
+ actions.append(ScaffoldAction("create_file", out_file))
129
+
130
+ return actions
131
+
132
+
133
+ def apply_actions(
134
+ actions: List[ScaffoldAction],
135
+ target_dir: Path,
136
+ templates_dir: Path,
137
+ project_name: str,
138
+ include_agents: bool,
139
+ paper_template: str,
140
+ dry_run: bool,
141
+ ) -> Dict[str, int]:
142
+ variables = {
143
+ "project_name": project_name,
144
+ "today": str(date.today()),
145
+ }
146
+ counts = {
147
+ "created_dirs": 0,
148
+ "created_files": 0,
149
+ "replaced_files": 0,
150
+ "skipped": 0,
151
+ }
152
+
153
+ effective_templates = _effective_templates(paper_template)
154
+ paper_src, paper_out = PAPER_TEMPLATE_MAP[paper_template]
155
+ virtual_paths = {f"{paper_out}.tpl": paper_src}
156
+ template_index = {}
157
+ for template_rel in effective_templates:
158
+ source_template_rel = virtual_paths.get(template_rel, template_rel)
159
+ if include_agents or not _is_agent_file(template_rel):
160
+ template_index[_output_path_from_template(template_rel)] = templates_dir / source_template_rel
161
+
162
+ for item in actions:
163
+ if item.action == "create_dir":
164
+ counts["created_dirs"] += 1
165
+ if not dry_run:
166
+ item.path.mkdir(parents=True, exist_ok=True)
167
+ continue
168
+
169
+ if item.action == "skip_dir" or item.action == "skip_file":
170
+ counts["skipped"] += 1
171
+ continue
172
+
173
+ output_rel = item.path.relative_to(target_dir)
174
+ template_path = template_index.get(output_rel)
175
+ if template_path is None:
176
+ raise RuntimeError(f"No template found for output file {output_rel}")
177
+
178
+ content = _render_template(template_path.read_text(encoding="utf-8"), variables)
179
+
180
+ if item.action == "create_file":
181
+ counts["created_files"] += 1
182
+ elif item.action == "replace_file":
183
+ counts["replaced_files"] += 1
184
+ else:
185
+ raise RuntimeError(f"Unsupported action: {item.action}")
186
+
187
+ if not dry_run:
188
+ item.path.parent.mkdir(parents=True, exist_ok=True)
189
+ item.path.write_text(content, encoding="utf-8")
190
+
191
+ return counts
@@ -0,0 +1,5 @@
1
+ {
2
+ "permissions": {
3
+ "allow": []
4
+ }
5
+ }
@@ -0,0 +1,8 @@
1
+ [mcp_servers.zotero]
2
+ command = "zotero-mcp"
3
+ args = []
4
+ env = { ZOTERO_LOCAL = "true" }
5
+
6
+ [mcp_servers.markitdown]
7
+ command = "uvx"
8
+ args = ["markitdown-mcp"]
@@ -0,0 +1,19 @@
1
+ # Project: {{project_name}}
2
+
3
+ ## Overview
4
+
5
+ Research workspace scaffolded by `blank` on {{today}}.
6
+
7
+ ## Agent Rules
8
+
9
+ - Ask before destructive file operations.
10
+ - Keep `analysis/data/codebook.md` synchronized with data changes.
11
+ - Regenerate outputs when analysis scripts change.
12
+
13
+ ## Directory Structure
14
+
15
+ - `analysis/scripts/`
16
+ - `analysis/data/`
17
+ - `analysis/output/`
18
+ - `paper/`
19
+ - `idea/`
@@ -0,0 +1,11 @@
1
+ .DS_Store
2
+ .venv/
3
+ .tmp/
4
+ __pycache__/
5
+ *.pyc
6
+ analysis/output/figures/*
7
+ analysis/output/tables/*
8
+ analysis/output/results/*
9
+ !analysis/output/figures/.gitkeep
10
+ !analysis/output/tables/.gitkeep
11
+ !analysis/output/results/.gitkeep
File without changes
@@ -0,0 +1,20 @@
1
+ # {{project_name}}
2
+
3
+ Research project scaffold initialized by `blank` on {{today}}.
4
+
5
+ ## Structure
6
+
7
+ - `analysis/`: data pipelines, scripts, generated outputs
8
+ - `paper/`: manuscript files
9
+ - `idea/`: brainstorming notes
10
+ - `.codex/`: agent workflow config
11
+
12
+ ## Quickstart
13
+
14
+ ```bash
15
+ # initialize from repo root
16
+ blank init .
17
+
18
+ # run the analysis skeleton
19
+ Rscript analysis/scripts/30_results_main.R
20
+ ```
@@ -0,0 +1,9 @@
1
+ # Codebook: {{project_name}}
2
+
3
+ Last scaffold update: {{today}}
4
+
5
+ ## Variables
6
+
7
+ | variable | type | source | description |
8
+ |---|---|---|---|
9
+ | example_var | numeric | placeholder | Replace with actual variable docs |
@@ -0,0 +1,9 @@
1
+ # 00_setup.R
2
+ # Shared setup for analysis scripts.
3
+
4
+ message("[setup] loading configuration")
5
+
6
+ cfg <- list(
7
+ project_name = "{{project_name}}",
8
+ generated_on = "{{today}}"
9
+ )
@@ -0,0 +1,6 @@
1
+ # 20_data.R
2
+ # Data preparation entrypoint.
3
+
4
+ source("analysis/scripts/00_setup.R")
5
+
6
+ message("[data] build data objects here")
@@ -0,0 +1,7 @@
1
+ # 30_results_main.R
2
+ # Main analysis entrypoint.
3
+
4
+ source("analysis/scripts/00_setup.R")
5
+ source("analysis/scripts/20_data.R")
6
+
7
+ message("[results] write analysis outputs into analysis/output/")
@@ -0,0 +1 @@
1
+ // Placeholder style helpers for Typst manuscript customization.
@@ -0,0 +1 @@
1
+ # {{project_name}}
@@ -0,0 +1,12 @@
1
+ #set page(margin: (x: 1in, y: 1in), numbering: "1")
2
+ #set text(font: "Libertinus Serif", size: 11pt)
3
+ #set par(justify: true, leading: 0.7em)
4
+ #set heading(numbering: "1.")
5
+
6
+ #heading(level: 1)[{{project_name}}]
7
+
8
+ #outline(title: [Contents])
9
+
10
+ #heading(level: 1)[Introduction]
11
+
12
+ Write your manuscript here.
@@ -0,0 +1,6 @@
1
+ @article{example2026,
2
+ title={Example Reference},
3
+ author={Doe, Jane},
4
+ journal={Journal of Placeholder Studies},
5
+ year={2026}
6
+ }