@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,154 @@
1
+ """
2
+ reporter.py — Surface training results to terminal, summary.md, and results.json.
3
+
4
+ Usage:
5
+ from skills.train.assets.results_parsers.reporter import surface_results
6
+ surface_results(job_dir, job_type)
7
+ """
8
+ import importlib
9
+ import json
10
+ import sys
11
+ from datetime import datetime, timezone
12
+ from pathlib import Path
13
+
14
+ _PARSER_PKG = Path(__file__).resolve().parent
15
+ sys.path.insert(0, str(_PARSER_PKG.parent.parent.parent)) # ensure skills/ is on path
16
+
17
+
18
+ def _load_parser(job_type: str):
19
+ """Dynamically import the type-specific parser module."""
20
+ try:
21
+ spec_path = _PARSER_PKG / f"{job_type}.py"
22
+ import importlib.util
23
+ spec = importlib.util.spec_from_file_location(job_type, spec_path)
24
+ mod = importlib.util.module_from_spec(spec)
25
+ spec.loader.exec_module(mod)
26
+ return mod
27
+ except Exception as e:
28
+ return None
29
+
30
+
31
+ def _format_value(v) -> str:
32
+ if isinstance(v, float):
33
+ return f"{v:.4f}"
34
+ return str(v)
35
+
36
+
37
+ def _print_terminal_summary(job_dir: Path, job_type: str, metrics: dict):
38
+ """Print a box-formatted summary to stdout."""
39
+ job_name = job_dir.name
40
+ width = 54
41
+ border = "─" * width
42
+ print(f"\n┌{border}┐")
43
+ print(f"│ 📊 Training Summary — {job_name:<{width - 25}}│")
44
+ print(f"│ Type: {job_type:<{width - 9}}│")
45
+ print(f"├{border}┤")
46
+ if metrics:
47
+ for k, v in metrics.items():
48
+ label = k.replace("_", " ").title()
49
+ value = _format_value(v)
50
+ line = f" {label}: {value}"
51
+ print(f"│{line:<{width + 1}}│")
52
+ else:
53
+ print(f"│ (no metrics found — check training-results/ or results.json) │")
54
+ print(f"└{border}┘\n")
55
+
56
+
57
+ def _write_summary_md(job_dir: Path, job_type: str, metrics: dict):
58
+ """Write a Markdown summary file to the job directory."""
59
+ job_name = job_dir.name
60
+ timestamp = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M UTC")
61
+ lines = [
62
+ f"# Training Summary — {job_name}",
63
+ f"",
64
+ f"**Type:** {job_type} ",
65
+ f"**Generated:** {timestamp} ",
66
+ f"",
67
+ f"## Metrics",
68
+ f"",
69
+ f"| Metric | Value |",
70
+ f"|--------|-------|",
71
+ ]
72
+ if metrics:
73
+ for k, v in metrics.items():
74
+ label = k.replace("_", " ").title()
75
+ lines.append(f"| {label} | {_format_value(v)} |")
76
+ else:
77
+ lines.append("| — | No metrics found |")
78
+
79
+ summary_path = job_dir / "summary.md"
80
+ summary_path.write_text("\n".join(lines) + "\n")
81
+ return summary_path
82
+
83
+
84
+ def _write_results_json(job_dir: Path, job_type: str, metrics: dict):
85
+ """Write or update results.json in the job directory."""
86
+ results_path = job_dir / "results.json"
87
+
88
+ # Merge with existing results.json if present
89
+ existing = {}
90
+ if results_path.exists():
91
+ try:
92
+ existing = json.loads(results_path.read_text())
93
+ except Exception:
94
+ pass
95
+
96
+ existing.update({
97
+ "job_name": job_dir.name,
98
+ "job_type": job_type,
99
+ "generated_at": datetime.now(timezone.utc).isoformat(),
100
+ "metrics": metrics,
101
+ })
102
+
103
+ results_path.write_text(json.dumps(existing, indent=2))
104
+ return results_path
105
+
106
+
107
+ def surface_results(job_dir, job_type: str):
108
+ """
109
+ Parse training results for the given job and surface them:
110
+ 1. Print terminal summary
111
+ 2. Write summary.md to job directory
112
+ 3. Write/update results.json in job directory
113
+
114
+ Args:
115
+ job_dir: Path (or str) to the job directory
116
+ job_type: One of 'classifiers', 'transformers', 'nlp'
117
+ """
118
+ job_dir = Path(job_dir)
119
+
120
+ parser = _load_parser(job_type)
121
+ if parser is None:
122
+ print(f"⚠️ No parser found for type '{job_type}' — skipping result surfacing.")
123
+ return {}
124
+
125
+ try:
126
+ metrics = parser.parse(job_dir) or {}
127
+ except Exception as e:
128
+ print(f"⚠️ Parser error for '{job_type}': {e}")
129
+ metrics = {}
130
+
131
+ _print_terminal_summary(job_dir, job_type, metrics)
132
+
133
+ try:
134
+ summary_path = _write_summary_md(job_dir, job_type, metrics)
135
+ print(f"📄 summary.md written to {summary_path}")
136
+ except Exception as e:
137
+ print(f"⚠️ Could not write summary.md: {e}")
138
+
139
+ try:
140
+ results_path = _write_results_json(job_dir, job_type, metrics)
141
+ print(f"📄 results.json written to {results_path}")
142
+ except Exception as e:
143
+ print(f"⚠️ Could not write results.json: {e}")
144
+
145
+ return metrics
146
+
147
+
148
+ if __name__ == "__main__":
149
+ import argparse
150
+ p = argparse.ArgumentParser(description="Surface training results for a job directory.")
151
+ p.add_argument("job_dir", help="Path to the job directory")
152
+ p.add_argument("job_type", choices=["classifiers", "transformers", "nlp"])
153
+ ns = p.parse_args()
154
+ surface_results(ns.job_dir, ns.job_type)
@@ -0,0 +1,120 @@
1
+ """
2
+ transformers.py — Results parser for HuggingFace Transformers fine-tuning jobs.
3
+
4
+ Extracts: perplexity, eval_loss, train_loss, epochs, model_name.
5
+ Reads from results.json or scans training-results/ and trainer_state.json.
6
+ """
7
+ import json
8
+ import math
9
+ from pathlib import Path
10
+
11
+
12
+ def parse(job_dir: Path) -> dict:
13
+ """
14
+ Parse training results for a transformers job.
15
+
16
+ Returns a dict with keys:
17
+ perplexity, eval_loss, train_loss, epochs, model_name
18
+ Returns empty dict if no results can be found; never raises.
19
+ """
20
+ job_dir = Path(job_dir)
21
+ metrics = {}
22
+
23
+ # 1. Try results.json at root
24
+ results_json = job_dir / "results.json"
25
+ if results_json.exists():
26
+ try:
27
+ data = json.loads(results_json.read_text())
28
+ metrics = _extract_from_dict(data)
29
+ if metrics:
30
+ return metrics
31
+ except Exception:
32
+ pass
33
+
34
+ # 2. Try trainer_state.json (HuggingFace Trainer output)
35
+ for trainer_state in sorted(job_dir.rglob("trainer_state.json")):
36
+ try:
37
+ data = json.loads(trainer_state.read_text())
38
+ metrics = _extract_from_trainer_state(data)
39
+ if metrics:
40
+ return metrics
41
+ except Exception:
42
+ continue
43
+
44
+ # 3. Scan training-results/ for JSON files
45
+ training_results_dir = job_dir / "training-results"
46
+ if training_results_dir.exists():
47
+ for json_file in sorted(training_results_dir.rglob("*.json")):
48
+ try:
49
+ data = json.loads(json_file.read_text())
50
+ metrics = _extract_from_dict(data)
51
+ if metrics:
52
+ return metrics
53
+ except Exception:
54
+ continue
55
+
56
+ return metrics
57
+
58
+
59
+ def _extract_from_dict(data: dict) -> dict:
60
+ """Extract transformer-relevant keys from a parsed dict."""
61
+ metrics = {}
62
+ if not isinstance(data, dict):
63
+ return metrics
64
+
65
+ field_map = {
66
+ "eval_loss": ["eval_loss", "validation_loss", "val_loss"],
67
+ "train_loss": ["train_loss", "training_loss", "loss"],
68
+ "epochs": ["epochs", "num_train_epochs", "epoch"],
69
+ "model_name": ["model_name", "model", "base_model", "pretrained_model_name_or_path"],
70
+ "perplexity": ["perplexity", "eval_perplexity"],
71
+ }
72
+
73
+ for target_key, candidates in field_map.items():
74
+ for candidate in candidates:
75
+ value = data.get(candidate)
76
+ if value is not None:
77
+ metrics[target_key] = value
78
+ break
79
+
80
+ # Derive perplexity from eval_loss if not already present
81
+ if "perplexity" not in metrics and "eval_loss" in metrics:
82
+ try:
83
+ metrics["perplexity"] = round(math.exp(float(metrics["eval_loss"])), 4)
84
+ except (ValueError, OverflowError):
85
+ pass
86
+
87
+ return metrics
88
+
89
+
90
+ def _extract_from_trainer_state(data: dict) -> dict:
91
+ """Extract metrics from HuggingFace Trainer's trainer_state.json."""
92
+ metrics = {}
93
+ if not isinstance(data, dict):
94
+ return metrics
95
+
96
+ # Best metrics
97
+ best_metric = data.get("best_metric")
98
+ if best_metric is not None:
99
+ metrics["eval_loss"] = best_metric
100
+
101
+ # Epoch count
102
+ epoch = data.get("epoch")
103
+ if epoch is not None:
104
+ metrics["epochs"] = epoch
105
+
106
+ # Last eval from log history
107
+ log_history = data.get("log_history", [])
108
+ for entry in reversed(log_history):
109
+ if "eval_loss" in entry:
110
+ metrics["eval_loss"] = entry["eval_loss"]
111
+ break
112
+
113
+ # Derive perplexity
114
+ if "perplexity" not in metrics and "eval_loss" in metrics:
115
+ try:
116
+ metrics["perplexity"] = round(math.exp(float(metrics["eval_loss"])), 4)
117
+ except (ValueError, OverflowError):
118
+ pass
119
+
120
+ return metrics