@jenga-ai/agent 1.0.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 (177) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +340 -0
  3. package/agents/ai_engineer.md +113 -0
  4. package/agents/developer.md +236 -0
  5. package/agents/scrum-master.md +349 -0
  6. package/agents/scrutiny-agent.md +137 -0
  7. package/agents/solution-assessor.md +185 -0
  8. package/agents/tester.md +339 -0
  9. package/bin/jenga.js +70 -0
  10. package/hooks/copilot_session_end.sh +29 -0
  11. package/hooks/on_session_end.sh +238 -0
  12. package/hooks/prompt_router.sh +11 -0
  13. package/hooks/prompt_router_helper.js +52 -0
  14. package/hooks/session_end_helper.js +29 -0
  15. package/hooks/session_end_watcher.sh +24 -0
  16. package/lib/commands/attach.js +47 -0
  17. package/lib/commands/init.js +207 -0
  18. package/lib/commands/start.js +16 -0
  19. package/lib/commands/status.js +53 -0
  20. package/lib/config-schema.js +72 -0
  21. package/lib/inject-settings.js +61 -0
  22. package/lib/mirror.js +244 -0
  23. package/lib/resolve-project-dir.sh +47 -0
  24. package/mcp/execute-ticket/index.js +10 -0
  25. package/mcp/execute-ticket/package.json +5 -0
  26. package/mcp/help/index.js +79 -0
  27. package/mcp/help/package.json +14 -0
  28. package/mcp/router/README.md +19 -0
  29. package/mcp/router/embedder.js +23 -0
  30. package/mcp/router/index.js +204 -0
  31. package/mcp/router/matcher.js +87 -0
  32. package/mcp/router/package-lock.json +1048 -0
  33. package/mcp/router/package.json +11 -0
  34. package/mcp/router/skill-index.js +104 -0
  35. package/package.json +47 -0
  36. package/scripts/board_resolver.sh +46 -0
  37. package/scripts/e25_s01_extract_board_graph.py +292 -0
  38. package/scripts/e25_s01_generate_synthetic_board.py +90 -0
  39. package/scripts/measurement-10x.json +50 -0
  40. package/scripts/measurement-10x.txt +4 -0
  41. package/scripts/measurement-real.json +50 -0
  42. package/scripts/measurement-real.txt +4 -0
  43. package/scripts/postinstall.js +165 -0
  44. package/scripts/todo_cleanup.sh +22 -0
  45. package/scripts/todo_manager.sh +86 -0
  46. package/scripts/validate-board.sh +190 -0
  47. package/scripts/validate-story-format.sh +53 -0
  48. package/skills/brainstorm/SKILL.md +47 -0
  49. package/skills/btw/SKILL.md +42 -0
  50. package/skills/commit/SKILL.md +29 -0
  51. package/skills/commit/assets/user_instructions_template.md +22 -0
  52. package/skills/continue/SKILL.md +29 -0
  53. package/skills/convert/SKILL.md +124 -0
  54. package/skills/convert/convert_cli.py +235 -0
  55. package/skills/convert/tests/sample.csv +4 -0
  56. package/skills/convert/tests/sample.json +5 -0
  57. package/skills/convert/tests/sample.jsonl +3 -0
  58. package/skills/convert/tests/sample.yaml +18 -0
  59. package/skills/convert/tests/sample_obj.csv +2 -0
  60. package/skills/convert/tests/sample_obj.json +9 -0
  61. package/skills/deep-dive/SKILL.md +167 -0
  62. package/skills/do/SKILL.md +88 -0
  63. package/skills/do/assets/sender_template.json +12 -0
  64. package/skills/doc/SKILL.md +314 -0
  65. package/skills/doc/assets/path-objectives.yaml +38 -0
  66. package/skills/doc-sync/SKILL.md +167 -0
  67. package/skills/doc-sync/assets/default_excludes.txt +21 -0
  68. package/skills/doc-sync/assets/doc_targets.md +14 -0
  69. package/skills/dooo/SKILL.md +60 -0
  70. package/skills/error/SKILL.md +29 -0
  71. package/skills/evaluate/SKILL.md +45 -0
  72. package/skills/evaluate/assets/evaluation_invokation_template.yml +3 -0
  73. package/skills/evaluate/assets/evaluation_rapport_template.md +24 -0
  74. package/skills/examplify/SKILL.md +42 -0
  75. package/skills/help/SKILL.md +36 -0
  76. package/skills/improve/SKILL.md +55 -0
  77. package/skills/index/scripts/board-index +4 -0
  78. package/skills/index/scripts/board_index.py +615 -0
  79. package/skills/index/scripts/smoke_test.sh +86 -0
  80. package/skills/init/SKILL.md +44 -0
  81. package/skills/init/assets/.gitignore_template +15 -0
  82. package/skills/init/assets/PROJECT_SUMMARY_template.md +13 -0
  83. package/skills/init/assets/directory_structure.txt +13 -0
  84. package/skills/init/assets/test-config_template.json +4 -0
  85. package/skills/init/assets/workflow_template.json +30 -0
  86. package/skills/init/scripts/init.sh +48 -0
  87. package/skills/jbp/SKILL.md +25 -0
  88. package/skills/jenga/SKILL.md +68 -0
  89. package/skills/lgtm/SKILL.md +21 -0
  90. package/skills/mirror-public/SKILL.md +237 -0
  91. package/skills/mirror-public/assets/config.json +5 -0
  92. package/skills/mirror-public/scripts/mirror.sh +374 -0
  93. package/skills/pi-plan/SKILL.md +62 -0
  94. package/skills/pi-plan/assets/epic.json +7 -0
  95. package/skills/pi-plan/assets/story_template.md +18 -0
  96. package/skills/proceed/SKILL.md +29 -0
  97. package/skills/publish/SKILL.md +351 -0
  98. package/skills/publish/adapters/droplet.md +200 -0
  99. package/skills/publish/adapters/mobile-ios.md +114 -0
  100. package/skills/publish/adapters/npm-ci.md +223 -0
  101. package/skills/publish/adapters/npm.md +121 -0
  102. package/skills/publish/assets/ExportOptions.plist.template +19 -0
  103. package/skills/publish/assets/ci-contract.md +111 -0
  104. package/skills/publish/assets/ownership-matrix.md +17 -0
  105. package/skills/publish/assets/publish.example.json +85 -0
  106. package/skills/publish/assets/publish.example.npm-ci.json +40 -0
  107. package/skills/publish/assets/publish.example.npm.json +41 -0
  108. package/skills/publish/assets/secrets-guide.md +104 -0
  109. package/skills/publish/schemas/fixtures/npm-ci-minimal.json +17 -0
  110. package/skills/publish/schemas/fixtures/npm-ci-with-empty-secrets.json +18 -0
  111. package/skills/publish/schemas/fixtures/npm-ci-with-workflow-path.json +18 -0
  112. package/skills/publish/schemas/publish.schema.json +428 -0
  113. package/skills/publish/scripts/check_target_config.sh +96 -0
  114. package/skills/publish/scripts/droplet_pipeline.sh +208 -0
  115. package/skills/publish/scripts/generate_release_notes.sh +200 -0
  116. package/skills/publish/scripts/ios_pipeline.sh +486 -0
  117. package/skills/publish/scripts/npm_ci_pipeline.sh +225 -0
  118. package/skills/publish/scripts/npm_pipeline.sh +249 -0
  119. package/skills/publish/scripts/publish_common.sh +253 -0
  120. package/skills/publish/scripts/publish_deploy.sh +538 -0
  121. package/skills/publish/scripts/reconcile_tags.sh +135 -0
  122. package/skills/publish/scripts/run_gates.sh +616 -0
  123. package/skills/publish/scripts/setup_wizard.sh +394 -0
  124. package/skills/publish/scripts/show_history.sh +95 -0
  125. package/skills/publish/scripts/suggest_semver_bump.sh +105 -0
  126. package/skills/publish/scripts/validate_config.sh +163 -0
  127. package/skills/publish/scripts/validate_droplet_env.sh +45 -0
  128. package/skills/publish/scripts/validate_ios_env.sh +68 -0
  129. package/skills/publish/scripts/validate_npm_ci_env.sh +71 -0
  130. package/skills/publish/scripts/validate_npm_env.sh +22 -0
  131. package/skills/publish/scripts/write_ledger_entry.sh +126 -0
  132. package/skills/publish/wizards/droplet.md +275 -0
  133. package/skills/publish/wizards/mobile-ios.md +157 -0
  134. package/skills/publish/wizards/npm-ci.md +240 -0
  135. package/skills/publish/wizards/npm.md +224 -0
  136. package/skills/reconcile/SKILL.md +93 -0
  137. package/skills/reconcile/assets/report_format.md +44 -0
  138. package/skills/reconcile-origin/SKILL.md +75 -0
  139. package/skills/reconcile-origin/scripts/reconcile-origin.sh +372 -0
  140. package/skills/redo/SKILL.md +70 -0
  141. package/skills/route/SKILL.md +180 -0
  142. package/skills/self-sync/SKILL.md +73 -0
  143. package/skills/self-sync/scripts/run.js +136 -0
  144. package/skills/skillify/SKILL.md +68 -0
  145. package/skills/skillify/assets/init-new/SKILL.md +35 -0
  146. package/skills/skillify/assets/init-new/assets/.gitignore_template +15 -0
  147. package/skills/skillify/assets/init-new/assets/PROJECT_SUMMARY_template.md +13 -0
  148. package/skills/skillify/assets/init-new/assets/directory_structure.txt +10 -0
  149. package/skills/skillify/assets/init-new/assets/test-config_template.json +4 -0
  150. package/skills/skillify/assets/init-new/assets/workflow_template.json +17 -0
  151. package/skills/skillify/assets/init-new/scripts/init.sh +48 -0
  152. package/skills/skillify/assets/init-old/SKILL.md +124 -0
  153. package/skills/spinoff/SKILL.md +48 -0
  154. package/skills/status/SKILL.md +33 -0
  155. package/skills/status/assets/output_format.md +41 -0
  156. package/skills/todo/SKILL.md +46 -0
  157. package/skills/todo/assets/todo_handoff_template.md +22 -0
  158. package/skills/todo/assets/todo_template.md +3 -0
  159. package/skills/train/SKILL.md +116 -0
  160. package/skills/train/assets/dashboard-templates/classifiers.html +106 -0
  161. package/skills/train/assets/dashboard-templates/nlp.html +102 -0
  162. package/skills/train/assets/dashboard-templates/transformers.html +98 -0
  163. package/skills/train/assets/results-parsers/__init__.py +9 -0
  164. package/skills/train/assets/results-parsers/classifiers.py +84 -0
  165. package/skills/train/assets/results-parsers/nlp.py +88 -0
  166. package/skills/train/assets/results-parsers/reporter.py +154 -0
  167. package/skills/train/assets/results-parsers/transformers.py +120 -0
  168. package/skills/train/train_cli.py +786 -0
  169. package/templates/EXECUTION_PLAN_TEMPLATE.md +43 -0
  170. package/templates/EXECUTION_SUMMARY_TEMPLATE.md +50 -0
  171. package/templates/JENGA_CONFIG_TEMPLATE.json +23 -0
  172. package/templates/PROBLEM_RAPPORT_TEMPLATE.md +88 -0
  173. package/templates/SCRUM_BOARD_SCHEMA.md +311 -0
  174. package/templates/SKILL.md +16 -0
  175. package/templates/SKILL_TEMPLATE.md +28 -0
  176. package/templates/USER_INSTRUCTIONS_TEMPLATE.md +22 -0
  177. package/templates/copilot-instructions.md.tpl +55 -0
@@ -0,0 +1,29 @@
1
+ ---
2
+ name: commit
3
+ description: Commit implemented epic, story, or task work using the EST naming convention. Also handles user-action prerequisites and new-epic boundaries. Use after completing any EST work item.
4
+ keywords:
5
+ - commit
6
+ - save
7
+ - git commit
8
+ - done
9
+ - push
10
+ examples:
11
+ - "commit this work"
12
+ - "save my changes"
13
+ ---
14
+
15
+ # Commit — Commit Completed Work
16
+
17
+ ## Instructions
18
+
19
+ If no epic, task, or story has been implemented, exit with the message: "No implementation to commit."
20
+
21
+ 1. **Verify user-action prerequisites** — Check whether an `_INSTRUCTIONS.md` file exists for this task at `project/board/tasks/<E##_S##_T##>_INSTRUCTIONS.md`. If the task has out-of-scope prerequisites but no instructions file was created, create one now using `assets/user_instructions_template.md`. If one already exists, surface it to the user as a reminder. (The developer should have created this file during task intake — this is a final safety check.)
22
+
23
+ 2. **Commit** using the following format:
24
+ - **Epic:** `epic(<Epic Title>): <MAX_50_CHAR_SUMMARY>`
25
+ - **Task/Story:** `story(<Epic Title>_<Story Title>): <MAX_50_CHAR_SUMMARY>`
26
+
27
+ **Fallback: Group changes logically** — prefer one commit per coherent unit of work, but don't force splits. When in doubt, keep it together.
28
+
29
+ 3. **Check for next epic** — If a new epic is to be started, inform the user that a new conversation should be initiated. If there are no subsequent epics left, show the message: "All Done! 🎉"
@@ -0,0 +1,22 @@
1
+ # <Action Name> — Setup Instructions
2
+
3
+ **Epic**: E## — <epic title>
4
+ **Required before**: <story or task that depends on this>
5
+
6
+ ## Overview
7
+
8
+ <Brief description of what the user needs to set up and why.>
9
+
10
+ ## Steps
11
+
12
+ 1. <Step one>
13
+ 2. <Step two>
14
+ 3. <Step three>
15
+
16
+ ## Verification
17
+
18
+ <How the user can confirm the setup is complete and working.>
19
+
20
+ ## Notes
21
+
22
+ <Any caveats, links to external docs, or environment-specific instructions.>
@@ -0,0 +1,29 @@
1
+ ---
2
+ name: continue
3
+ description: Check project status across PROJECT_SUMMARY.md, epics, and stories to determine what should be done next. Reports "All done!" if everything is complete.
4
+ keywords:
5
+ - continue
6
+ - next
7
+ - proceed
8
+ - what's next
9
+ - status
10
+ examples:
11
+ - "what should I do next?"
12
+ - "continue with the project"
13
+ ---
14
+
15
+ # Continue — Pick Up the Next Work Item
16
+
17
+ ## Instructions
18
+
19
+ 1. **Check `project/PROJECT_SUMMARY.md`** — Determine if there is outstanding work at the project level.
20
+
21
+ 2. **Check `project/epics/`** — If the project summary is done, check if any epics have remaining work.
22
+
23
+ 3. **Check `project/stories/`** — If epics are done, check if any stories have remaining work.
24
+
25
+ **Important:** Always check story status within an epic even if the epic itself is marked as done.
26
+
27
+ 4. **If everything is complete** — Respond with: "All done! 🎉"
28
+
29
+ 5. **Otherwise** — Begin work on the next incomplete item.
@@ -0,0 +1,124 @@
1
+ ---
2
+ name: convert
3
+ description: Convert JSON, JSONL, YAML, or YML dataset files to CSV format. Pass-through for files already in CSV. Flattens nested structures using dot-notation.
4
+ keywords:
5
+ - convert
6
+ - csv
7
+ - json to csv
8
+ - yaml to csv
9
+ - dataset format
10
+ examples:
11
+ - "convert this JSON file to CSV"
12
+ - "convert my dataset to CSV format"
13
+ ---
14
+
15
+ # Convert — Dataset File Converter
16
+
17
+ ## What It Does
18
+
19
+ `/convert` takes a dataset file in JSON, JSONL, YAML, or YML format and outputs a clean CSV file ready for use in `/train` jobs.
20
+
21
+ - **Auto-detects** the input format from the file extension
22
+ - **Flattens** nested objects using dot-notation (e.g. `{"user": {"age": 30}}` → column `user.age`)
23
+ - **Pass-through**: if the input is already `.csv`, it is returned unchanged
24
+ - **Warns** if the top-level structure is an object `{}` instead of an array `[]`, and asks for confirmation before converting
25
+
26
+ Output is written to the **same directory** as the input file, with a `.csv` extension.
27
+
28
+ ---
29
+
30
+ ## Invocation
31
+
32
+ ```bash
33
+ python skills/convert/convert_cli.py <path-to-file>
34
+ ```
35
+
36
+ Or, when invoked through the Copilot CLI:
37
+
38
+ ```
39
+ /convert <path-to-file>
40
+ ```
41
+
42
+ ---
43
+
44
+ ## Supported Formats
45
+
46
+ | Extension | Description |
47
+ |---------------|--------------------------------------------------|
48
+ | `.json` | JSON array `[{...}, ...]` or object `{...}` |
49
+ | `.jsonl` | JSON Lines — one JSON object per line |
50
+ | `.yaml`/`.yml`| YAML array of records or mapping |
51
+ | `.csv` | Already CSV — returned as-is (no conversion) |
52
+
53
+ ---
54
+
55
+ ## Examples
56
+
57
+ ### JSON array → CSV
58
+
59
+ ```bash
60
+ python skills/convert/convert_cli.py data/train.json
61
+ # Output: data/train.csv
62
+ ```
63
+
64
+ ### JSONL with nested structures → CSV
65
+
66
+ Input `data/train.jsonl`:
67
+ ```
68
+ {"id": 1, "user": {"name": "Alice", "age": 30}, "label": "pos"}
69
+ {"id": 2, "user": {"name": "Bob", "age": 25}, "label": "neg"}
70
+ ```
71
+
72
+ Output `data/train.csv`:
73
+ ```
74
+ id,user.name,user.age,label
75
+ 1,Alice,30,pos
76
+ 2,Bob,25,neg
77
+ ```
78
+
79
+ ### YAML → CSV
80
+
81
+ ```bash
82
+ python skills/convert/convert_cli.py data/train.yaml
83
+ # Output: data/train.csv
84
+ ```
85
+
86
+ ### CSV pass-through
87
+
88
+ ```bash
89
+ python skills/convert/convert_cli.py data/train.csv
90
+ # ✅ Input is already CSV — no conversion needed: /path/to/data/train.csv
91
+ ```
92
+
93
+ ### JSON object (top-level `{}`)
94
+
95
+ When the file's top-level structure is an object rather than an array, the tool warns:
96
+
97
+ ```
98
+ ⚠️ Warning: 'config.json' has a top-level object ({...}), not an array ([...]).
99
+ The tool will create a single-row CSV where each top-level key becomes a column.
100
+ Proceed? [y/N]
101
+ ```
102
+
103
+ ---
104
+
105
+ ## Dot-Notation Flattening
106
+
107
+ Nested dicts are flattened recursively:
108
+
109
+ | Input JSON | CSV column | Value |
110
+ |-----------------------------------------|----------------|-------|
111
+ | `{"user": {"name": "Alice"}}` | `user.name` | Alice |
112
+ | `{"user": {"address": {"city": "NYC"}}}` | `user.address.city` | NYC |
113
+ | `{"tags": ["ml", "nlp"]}` | `tags` | `["ml", "nlp"]` (JSON string) |
114
+
115
+ Lists nested within records are preserved as JSON strings.
116
+
117
+ ---
118
+
119
+ ## Notes
120
+
121
+ - Requires `pyyaml` for YAML/YML support: `pip install pyyaml`
122
+ - Standard library only for JSON and JSONL (no extra dependencies)
123
+ - Columns are ordered by first appearance across all records
124
+ - Records missing a key will have an empty cell in that column
@@ -0,0 +1,235 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ convert_cli.py — Dataset file converter for ML training jobs.
4
+
5
+ Usage:
6
+ python convert_cli.py <path-to-file>
7
+
8
+ Supported input formats:
9
+ .json — JSON array of records (or object, with confirmation)
10
+ .jsonl — JSON Lines (one record per line)
11
+ .yaml — YAML array of records (or object, with confirmation)
12
+ .yml — same as .yaml
13
+ .csv — pass-through (returned as-is, no conversion)
14
+
15
+ Output:
16
+ Written to the same directory as the input file with a .csv extension.
17
+ e.g. data/train.json → data/train.csv
18
+ """
19
+ import csv
20
+ import json
21
+ import sys
22
+ from pathlib import Path
23
+
24
+ try:
25
+ import yaml
26
+ _YAML_AVAILABLE = True
27
+ except ImportError:
28
+ _YAML_AVAILABLE = False
29
+
30
+ SUPPORTED_EXTENSIONS = {".json", ".jsonl", ".yaml", ".yml", ".csv"}
31
+
32
+
33
+ # ---------------------------------------------------------------------------
34
+ # Flattening
35
+ # ---------------------------------------------------------------------------
36
+
37
+ def _flatten(record, prefix=""):
38
+ """
39
+ Recursively flatten a nested dict using dot-notation keys.
40
+ Non-dict values (including lists) are kept as-is (lists become JSON strings).
41
+ """
42
+ out = {}
43
+ for k, v in record.items():
44
+ full_key = f"{prefix}.{k}" if prefix else k
45
+ if isinstance(v, dict):
46
+ out.update(_flatten(v, full_key))
47
+ elif isinstance(v, list):
48
+ out[full_key] = json.dumps(v)
49
+ else:
50
+ out[full_key] = v
51
+ return out
52
+
53
+
54
+ def flatten_records(records):
55
+ """Flatten a list of dicts. Returns a list of flat dicts."""
56
+ return [_flatten(r) if isinstance(r, dict) else {"value": r} for r in records]
57
+
58
+
59
+ # ---------------------------------------------------------------------------
60
+ # Loaders
61
+ # ---------------------------------------------------------------------------
62
+
63
+ def load_json(path):
64
+ with open(path, encoding="utf-8") as f:
65
+ data = json.load(f)
66
+ return data
67
+
68
+
69
+ def load_jsonl(path):
70
+ records = []
71
+ with open(path, encoding="utf-8") as f:
72
+ for lineno, line in enumerate(f, 1):
73
+ line = line.strip()
74
+ if not line:
75
+ continue
76
+ try:
77
+ records.append(json.loads(line))
78
+ except json.JSONDecodeError as e:
79
+ print(f"⚠️ Skipping line {lineno} — JSON parse error: {e}")
80
+ return records
81
+
82
+
83
+ def load_yaml(path):
84
+ if not _YAML_AVAILABLE:
85
+ print("❌ PyYAML is not installed. Install it with: pip install pyyaml")
86
+ sys.exit(1)
87
+ with open(path, encoding="utf-8") as f:
88
+ data = yaml.safe_load(f)
89
+ return data
90
+
91
+
92
+ # ---------------------------------------------------------------------------
93
+ # Top-level structure sniff + normalisation
94
+ # ---------------------------------------------------------------------------
95
+
96
+ def _confirm_object_conversion(path, yes=False):
97
+ """
98
+ Warn the user that the top-level structure is a dict (not a list),
99
+ and ask whether to proceed by treating each top-level key as a column.
100
+ Returns True to proceed, False to abort.
101
+ When yes=True, auto-confirms without prompting (for non-interactive use).
102
+ """
103
+ print(f"\n⚠️ Warning: '{path.name}' has a top-level object ({{...}}), not an array ([...]).")
104
+ print(" The tool will create a single-row CSV where each top-level key becomes a column.")
105
+ if yes:
106
+ print(" Auto-confirming (--yes flag set).")
107
+ return True
108
+ try:
109
+ answer = input(" Proceed? [y/N] ").strip().lower()
110
+ except EOFError:
111
+ answer = "n"
112
+ return answer in ("y", "yes")
113
+
114
+
115
+ def normalise_to_records(data, path, yes=False):
116
+ """
117
+ Ensure data is a list of dicts.
118
+ - list → used directly
119
+ - dict → warn + confirm, then wrap as [data]
120
+ - other → error
121
+ When yes=True, dict top-level is auto-confirmed.
122
+ """
123
+ if isinstance(data, list):
124
+ return data
125
+ if isinstance(data, dict):
126
+ if not _confirm_object_conversion(path, yes=yes):
127
+ print("❌ Conversion aborted by user.")
128
+ sys.exit(0)
129
+ return [data]
130
+ print(f"❌ Unsupported top-level type '{type(data).__name__}'. Expected a list or dict.")
131
+ sys.exit(1)
132
+
133
+
134
+ # ---------------------------------------------------------------------------
135
+ # Writer
136
+ # ---------------------------------------------------------------------------
137
+
138
+ def write_csv(records, output_path):
139
+ """Write a list of flat dicts to a CSV file."""
140
+ if not records:
141
+ print(f"⚠️ No records to write — creating empty CSV at {output_path}")
142
+ output_path.write_text("")
143
+ return
144
+
145
+ # Build a unified ordered fieldset (preserving first-seen order)
146
+ fieldnames = list(dict.fromkeys(k for r in records for k in r))
147
+
148
+ with open(output_path, "w", newline="", encoding="utf-8") as f:
149
+ writer = csv.DictWriter(f, fieldnames=fieldnames, extrasaction="ignore")
150
+ writer.writeheader()
151
+ writer.writerows(records)
152
+
153
+ print(f"✅ Converted → {output_path} ({len(records)} row{'s' if len(records) != 1 else ''})")
154
+
155
+
156
+ # ---------------------------------------------------------------------------
157
+ # Main conversion entry point
158
+ # ---------------------------------------------------------------------------
159
+
160
+ def convert(input_path_str, yes=False):
161
+ """
162
+ Convert the given file to CSV.
163
+ Returns the output path (str), or the input path for .csv pass-through.
164
+ When yes=True, object top-level confirmation is auto-accepted.
165
+ """
166
+ path = Path(input_path_str).resolve()
167
+
168
+ if not path.exists():
169
+ print(f"❌ File not found: {path}")
170
+ sys.exit(1)
171
+
172
+ ext = path.suffix.lower()
173
+
174
+ if ext not in SUPPORTED_EXTENSIONS:
175
+ print(f"❌ Unsupported file extension '{ext}'.")
176
+ print(f" Supported: {', '.join(sorted(SUPPORTED_EXTENSIONS))}")
177
+ sys.exit(1)
178
+
179
+ # Pass-through for CSV
180
+ if ext == ".csv":
181
+ print(f"✅ Input is already CSV — no conversion needed: {path}")
182
+ return str(path)
183
+
184
+ # Load data based on format
185
+ if ext == ".json":
186
+ data = load_json(path)
187
+ elif ext == ".jsonl":
188
+ data = load_jsonl(path)
189
+ # JSONL is already a list of records
190
+ records = flatten_records(normalise_to_records(data, path, yes=yes))
191
+ output_path = path.with_suffix(".csv")
192
+ write_csv(records, output_path)
193
+ return str(output_path)
194
+ elif ext in (".yaml", ".yml"):
195
+ data = load_yaml(path)
196
+ else:
197
+ print(f"❌ Extension '{ext}' reached conversion without a handler — this is a bug.")
198
+ sys.exit(1)
199
+
200
+ records = flatten_records(normalise_to_records(data, path, yes=yes))
201
+ output_path = path.with_suffix(".csv")
202
+ write_csv(records, output_path)
203
+ return str(output_path)
204
+
205
+
206
+ # ---------------------------------------------------------------------------
207
+ # CLI entry point
208
+ # ---------------------------------------------------------------------------
209
+
210
+ def main():
211
+ args = [a for a in sys.argv[1:] if a not in ("-h", "--help")]
212
+ help_requested = "-h" in sys.argv or "--help" in sys.argv
213
+ yes = "--yes" in args or "--no-confirm" in args
214
+ file_args = [a for a in args if not a.startswith("--")]
215
+
216
+ if help_requested or len(file_args) != 1:
217
+ print("Usage: python convert_cli.py <path-to-file> [--yes]")
218
+ print()
219
+ print("Supported formats: .json, .jsonl, .yaml, .yml, .csv")
220
+ print()
221
+ print("Options:")
222
+ print(" --yes, --no-confirm Auto-confirm object-to-CSV conversion (non-interactive)")
223
+ print()
224
+ print("Examples:")
225
+ print(" python convert_cli.py data/train.json")
226
+ print(" python convert_cli.py data/train.jsonl --yes")
227
+ print(" python convert_cli.py data/train.yaml")
228
+ print(" python convert_cli.py data/train.csv # pass-through")
229
+ sys.exit(0 if help_requested else 1)
230
+
231
+ convert(file_args[0], yes=yes)
232
+
233
+
234
+ if __name__ == "__main__":
235
+ main()
@@ -0,0 +1,4 @@
1
+ id,text,meta.source,meta.lang,label
2
+ 1,The movie was great,twitter,en,positive
3
+ 2,Terrible experience,reddit,en,negative
4
+ 3,Pretty average overall,twitter,en,neutral
@@ -0,0 +1,5 @@
1
+ [
2
+ {"id": 1, "user": {"name": "Alice", "age": 30}, "label": "positive", "tags": ["ml", "nlp"]},
3
+ {"id": 2, "user": {"name": "Bob", "age": 25}, "label": "negative", "tags": ["cv"]},
4
+ {"id": 3, "user": {"name": "Carol", "age": 35}, "label": "positive", "tags": []}
5
+ ]
@@ -0,0 +1,3 @@
1
+ {"id": 1, "user": {"name": "Alice", "age": 30}, "score": 0.95, "label": "pos"}
2
+ {"id": 2, "user": {"name": "Bob", "age": 25}, "score": 0.72, "label": "neg"}
3
+ {"id": 3, "user": {"name": "Carol", "age": 35}, "score": 0.88, "label": "pos", "extra": {"source": "web"}}
@@ -0,0 +1,18 @@
1
+ - id: 1
2
+ text: "The movie was great"
3
+ meta:
4
+ source: twitter
5
+ lang: en
6
+ label: positive
7
+ - id: 2
8
+ text: "Terrible experience"
9
+ meta:
10
+ source: reddit
11
+ lang: en
12
+ label: negative
13
+ - id: 3
14
+ text: "Pretty average overall"
15
+ meta:
16
+ source: twitter
17
+ lang: en
18
+ label: neutral
@@ -0,0 +1,2 @@
1
+ name,age,address.city,address.zip,active
2
+ Alice,30,New York,10001,True
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "Alice",
3
+ "age": 30,
4
+ "address": {
5
+ "city": "New York",
6
+ "zip": "10001"
7
+ },
8
+ "active": true
9
+ }
@@ -0,0 +1,167 @@
1
+ ---
2
+ name: deep-dive
3
+ description: >
4
+ Multi-phase investigation workflow. The scrum-master orchestrates information
5
+ gathering, interactive brainstorming, critical scrutiny, and solution assessment
6
+ to produce a refined, well-considered output document. Use when a request needs
7
+ thorough analysis before committing to a plan. Triggers on phrases like
8
+ "deep dive", "investigate thoroughly", "think this through properly",
9
+ "analyze this in depth", or "I want a thorough analysis of".
10
+ metadata:
11
+ prefered_agent: scrum_master
12
+ keywords:
13
+ - "deep dive"
14
+ - "investigate"
15
+ - "analyze thoroughly"
16
+ - "think through"
17
+ examples:
18
+ - "deep dive on this idea"
19
+ - "let's think through this properly"
20
+ - "I want a thorough analysis of this feature"
21
+ - "investigate this proposal in depth"
22
+ ---
23
+
24
+ # /deep-dive — Deep Investigation Workflow
25
+
26
+ ## Overview
27
+
28
+ A multi-phase workflow that takes a user's request through structured investigation,
29
+ collaborative brainstorming, critical scrutiny, and optional solution assessment —
30
+ producing a refined, saved output document.
31
+
32
+ ### Modes
33
+
34
+ | Mode | Pipeline | When to use |
35
+ |------|----------|-------------|
36
+ | `explore` | Info gathering → Brainstorm → Save | Idea is clear, just needs structure |
37
+ | `challenge` | + Scrutiny review → Amend → Save | Default — most requests |
38
+ | `resolve` | + Solution assessment → Amend → Save | Known problems need concrete solutions |
39
+
40
+ **Default mode: `challenge`**
41
+
42
+ ---
43
+
44
+ ## Instructions
45
+
46
+ ### Step 1 — Receive the Request
47
+
48
+ Accept the user's mission, issue, or idea as the **request**. Note it clearly before proceeding.
49
+
50
+ ---
51
+
52
+ ### Step 2 — Select Mode
53
+
54
+ If the user has not specified a mode, ask:
55
+
56
+ > "How deep should we go?"
57
+ > 1. **Explore** — Brainstorm only
58
+ > 2. **Challenge** — Brainstorm + critical scrutiny *(default)*
59
+ > 3. **Resolve** — Full pipeline with solution assessment
60
+
61
+ If the user is in a hurry or it's obvious from context, default to `challenge` without asking.
62
+
63
+ ---
64
+
65
+ ### Step 3 — Information Gathering (Judgment Call)
66
+
67
+ Before brainstorming, assess whether sufficient context exists to proceed productively.
68
+
69
+ - Check `project/documentation/examples/` for related prior work or descriptions
70
+ - Consider invoking `/examplify` if a concept in the request needs clarification
71
+ - Ask the user directly if a critical piece of context is missing
72
+
73
+ **Default: proceed.** Do not over-investigate. The brainstorm step will surface gaps naturally. Move on unless something fundamental is clearly missing.
74
+
75
+ ---
76
+
77
+ ### Step 4 — Interactive Brainstorm
78
+
79
+ Invoke `/brainstorm` with the request and any context gathered in Step 3.
80
+
81
+ This is an **interactive session** — the user participates actively. Ask focused questions,
82
+ challenge assumptions, and explore the design space together.
83
+
84
+ When the user signals they are satisfied with the brainstorm output, **capture the full brainstorm result** as the working document and proceed.
85
+
86
+ ---
87
+
88
+ ### Step 5 — Scrutiny Review (`challenge` and `resolve` modes)
89
+
90
+ Summon the `scrutiny-agent` (`agents/scrutiny-agent.md`) with the brainstorm output as input.
91
+
92
+ The scrutiny agent will:
93
+ - Produce a structured critical assessment (assumptions, key questions, risks, strengths)
94
+ - Save it to `./scrutiny-<slug>.md`
95
+ - Return the assessment to the scrum-master
96
+
97
+ **Escalation threshold is low.** After reading the scrutiny output, escalate to Step 6 if any of the following are true:
98
+ - Any risk is rated **Med** or **High** severity
99
+ - Any key question remains unanswered and is not trivial
100
+ - Any core assumption is directly challenged without a clear resolution
101
+
102
+ If none of the above apply and the mode is `challenge`, skip Step 6 and proceed to Step 7.
103
+
104
+ ---
105
+
106
+ ### Step 6 — Solution Assessment (`resolve` mode, or escalated from `challenge`)
107
+
108
+ Summon the `solution-assessor` (`agents/solution-assessor.md`) in **Mode B (chained)** —
109
+ pass the full scrutiny output as input.
110
+
111
+ The solution assessor will:
112
+ - Derive all problems from the scrutiny output
113
+ - Assess candidate solutions with effort, risk, and viability
114
+ - Save the assessment to `./solution-assessment-<slug>.md`
115
+ - Return the full assessment to the scrum-master
116
+
117
+ ---
118
+
119
+ ### Step 7 — Amend the Brainstorm Output
120
+
121
+ The scrum-master **amends** the captured brainstorm document — does not replace it.
122
+
123
+ Append a `## 🔍 Deep Dive Synthesis` section at the end of the brainstorm document containing:
124
+
125
+ ```markdown
126
+ ## 🔍 Deep Dive Synthesis
127
+
128
+ ### Scrutiny Findings
129
+ [Brief summary of critical concerns, key questions, and risks identified]
130
+ → Full assessment: `./scrutiny-<slug>.md`
131
+
132
+ ### Solution Paths *(if applicable)*
133
+ [Brief summary of recommended solution paths and estimated effort]
134
+ → Full assessment: `./solution-assessment-<slug>.md`
135
+
136
+ ### Open Decisions
137
+ [Any unresolved questions or choices the user must make before proceeding]
138
+ ```
139
+
140
+ Keep each section **concise** — these are signposts to the full documents, not rewrites of them.
141
+
142
+ ---
143
+
144
+ ### Step 8 — Save Output
145
+
146
+ Determine the correct output directory based on content:
147
+
148
+ | Directory | When to use |
149
+ |-----------|-------------|
150
+ | `project/documentation/plans/` | Result can directly seed one or more Epics and/or Stories |
151
+ | `project/documentation/examples/` | Result is primarily descriptive or explanatory in nature |
152
+ | `project/documentation/summaries/` | Everything else |
153
+
154
+ Save the amended brainstorm document as `<slug>.md` in the chosen directory.
155
+ Also move any scrutiny/solution-assessment files from the working directory into the same directory for co-location.
156
+
157
+ Inform the user of all saved file paths.
158
+
159
+ ---
160
+
161
+ ### Step 9 — Offer Next Steps
162
+
163
+ Ask the user:
164
+
165
+ > "Would you like to commit this to the board?"
166
+ > 1. **Yes** — proceed with `/todo` to create board items from the output
167
+ > 2. **No** — close the deep-dive session