@jenga-ai/agent 1.2.3 → 1.3.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 (45) hide show
  1. package/README.md +4 -1
  2. package/agents/developer.md +18 -0
  3. package/agents/scrum-master.md +1 -0
  4. package/agents/tester.md +18 -0
  5. package/hooks/on_session_end.sh +27 -0
  6. package/lib/commands/init.js +23 -68
  7. package/lib/generate-copilot-instructions.js +142 -0
  8. package/package.json +18 -17
  9. package/scripts/postinstall.js +21 -0
  10. package/skills/commit/SKILL.md +11 -1
  11. package/skills/dev-done/SKILL.md +46 -0
  12. package/skills/dev-done/scripts/classify-commit-outcome.sh +114 -0
  13. package/skills/init/SKILL.md +7 -6
  14. package/skills/init/assets/scope-thresholds_template.json +7 -0
  15. package/skills/init/scripts/init.sh +6 -0
  16. package/skills/publish/SKILL.md +66 -0
  17. package/skills/publish/adapters/npm-ci.md +34 -0
  18. package/skills/publish/adapters/npm.md +18 -0
  19. package/skills/publish/assets/ci-contract.md +27 -0
  20. package/skills/publish/assets/publish.example.json +27 -0
  21. package/skills/publish/schemas/publish.schema.json +20 -0
  22. package/skills/publish/scripts/npm_ci_pipeline.sh +29 -0
  23. package/skills/publish/scripts/npm_stage_inspect.sh +829 -0
  24. package/skills/publish/scripts/npm_stage_pipeline.sh +427 -0
  25. package/skills/publish/scripts/publish_common.sh +16 -0
  26. package/skills/publish/scripts/show_history.sh +12 -5
  27. package/skills/publish/scripts/validate_npm_stage_env.sh +184 -0
  28. package/skills/publish/scripts/write_ledger_entry.sh +92 -2
  29. package/skills/reconcile/SKILL.md +121 -11
  30. package/skills/reconcile/assets/report_format.md +17 -0
  31. package/skills/reconcile/scripts/resolve-reconcile-scope.sh +489 -0
  32. package/skills/uncharted/SKILL.md +200 -21
  33. package/skills/uncharted/scripts/directory-triage.sh +342 -0
  34. package/skills/uncharted/scripts/elicitation-state.sh +457 -0
  35. package/templates/SCRUM_BOARD_SCHEMA.md +57 -0
  36. package/templates/agent-context.md.tpl +23 -11
  37. package/templates/copilot-instructions.md.tpl +21 -11
  38. package/mcp/router/README.md +0 -19
  39. package/mcp/router/embedder.js +0 -23
  40. package/mcp/router/index.js +0 -204
  41. package/mcp/router/matcher.js +0 -87
  42. package/mcp/router/package-lock.json +0 -1048
  43. package/mcp/router/package.json +0 -11
  44. package/mcp/router/skill-index.js +0 -104
  45. package/skills/route/SKILL.md +0 -180
@@ -1,11 +0,0 @@
1
- {
2
- "name": "jenga-router",
3
- "version": "1.0.0",
4
- "type": "module",
5
- "main": "index.js",
6
- "description": "Jenga Router — stdio MCP server for skill-based prompt routing",
7
- "dependencies": {
8
- "@modelcontextprotocol/sdk": "^1.11.1",
9
- "zod": "^3.24.4"
10
- }
11
- }
@@ -1,104 +0,0 @@
1
- import { readdirSync, readFileSync, statSync, existsSync } from "fs";
2
- import { join } from "path";
3
-
4
- /**
5
- * Parses YAML frontmatter from a markdown file.
6
- * Returns an object with the parsed fields, or {} if no frontmatter found.
7
- */
8
- function parseFrontmatter(content) {
9
- const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
10
- if (!match) return {};
11
- try {
12
- const lines = match[1].split("\n");
13
- const obj = {};
14
- let currentKey = null;
15
- let arrayMode = false;
16
- for (const line of lines) {
17
- const arrayItem = line.match(/^\s+-\s+(.*)/);
18
- if (arrayItem && arrayMode && currentKey) {
19
- obj[currentKey].push(arrayItem[1].trim().replace(/^["']|["']$/g, ""));
20
- continue;
21
- }
22
- arrayMode = false;
23
- const kv = line.match(/^(\w+):\s*(.*)/);
24
- if (!kv) continue;
25
- const [, key, val] = kv;
26
- if (val.trim() === "[]") {
27
- obj[key] = [];
28
- currentKey = key;
29
- arrayMode = false;
30
- } else if (val.trim() === "") {
31
- obj[key] = [];
32
- currentKey = key;
33
- arrayMode = true;
34
- } else {
35
- obj[key] = val.trim().replace(/^["']|["']$/g, "");
36
- currentKey = key;
37
- }
38
- }
39
- return obj;
40
- } catch {
41
- return {};
42
- }
43
- }
44
-
45
- /**
46
- * Recursively finds all SKILL.md files under a directory.
47
- */
48
- function findSkillFiles(dir) {
49
- if (!existsSync(dir)) return [];
50
- const results = [];
51
- try {
52
- for (const entry of readdirSync(dir)) {
53
- const full = join(dir, entry);
54
- try {
55
- if (statSync(full).isDirectory()) {
56
- results.push(...findSkillFiles(full));
57
- } else if (entry === "SKILL.md") {
58
- results.push(full);
59
- }
60
- } catch {}
61
- }
62
- } catch {}
63
- return results;
64
- }
65
-
66
- /**
67
- * Builds the in-memory skill index from the skills directory.
68
- * Optionally accepts an embedder ({ embed(text) }) to pre-compute skill embeddings.
69
- * Returns an array of skill records.
70
- */
71
- export async function buildSkillIndex(skillsPath, embedder = null) {
72
- const start = Date.now();
73
- const files = findSkillFiles(skillsPath);
74
- const skills = [];
75
-
76
- for (const filePath of files) {
77
- try {
78
- const content = readFileSync(filePath, "utf8");
79
- const fm = parseFrontmatter(content);
80
- if (!fm.name) {
81
- process.stderr.write(`Warning: ${filePath} has no 'name' in frontmatter — skipped\n`);
82
- continue;
83
- }
84
- const skill = {
85
- name: fm.name,
86
- description: fm.description || "",
87
- keywords: Array.isArray(fm.keywords) ? fm.keywords : [],
88
- examples: Array.isArray(fm.examples) ? fm.examples : [],
89
- path: filePath,
90
- embedding: null,
91
- };
92
- if (embedder) {
93
- skill.embedding = await embedder.embed(skill.description);
94
- }
95
- skills.push(skill);
96
- } catch (e) {
97
- process.stderr.write(`Warning: failed to parse ${filePath}: ${e.message}\n`);
98
- }
99
- }
100
-
101
- const elapsed = Date.now() - start;
102
- process.stderr.write(`[jenga-router] Indexed ${skills.length} skills in ${elapsed}ms\n`);
103
- return skills;
104
- }
@@ -1,180 +0,0 @@
1
- ---
2
- name: route
3
- description: Intelligently routes a prompt to the best-matching skill in the project. Reads available skills, matches the input semantically and by keyword, enriches the prompt with board context and documentation references, then invokes the matched skill with the full context prepended.
4
- keywords:
5
- - "route"
6
- - "dispatch"
7
- - "auto skill"
8
- - "smart prompt"
9
- - "find skill"
10
- examples:
11
- - "route: I want to plan a new feature"
12
- - "route: something is broken in the auth module"
13
- - "route: let's review what's done so far"
14
- - "route: analyze this approach in depth before we commit"
15
- - "route: add a task to capture this idea"
16
- ---
17
-
18
- # Route — Intelligent Skill Dispatcher
19
-
20
- ## Purpose
21
-
22
- `/route <prompt>` takes any natural-language input, finds the best-matching skill in the project, enriches it with live board context and documentation references, and invokes the matched skill — all in a single, fully-loaded message.
23
-
24
- ---
25
-
26
- ## Instructions
27
-
28
- ### Step 1 — Discover Available Skills
29
-
30
- Scan both skill registries (they may differ slightly — check both):
31
-
32
- ```
33
- .agents/skills/<name>/SKILL.md
34
- .claude/skills/<name>/SKILL.md (if the directory exists)
35
- skills/<name>/SKILL.md (if the directory exists)
36
- ```
37
-
38
- For each skill found, extract the following fields from its YAML front-matter:
39
-
40
- | Field | Purpose |
41
- |---|---|
42
- | `name` | Canonical identifier |
43
- | `description` | Primary match signal |
44
- | `keywords` | Exact/partial keyword match |
45
- | `examples` | Semantic match reference |
46
- | `metadata.prefered_agent` | Which agent to delegate to |
47
-
48
- Build an in-memory skill registry from this data before proceeding.
49
-
50
- ---
51
-
52
- ### Step 2 — Match the Prompt to a Skill
53
-
54
- Run matching in three passes (stop at first confident match):
55
-
56
- #### Pass 1 — Keyword Match
57
- Check whether any phrase from a skill's `keywords` list appears verbatim (case-insensitive) in the prompt.
58
-
59
- #### Pass 2 — Example Similarity
60
- Compare the prompt against each skill's `examples` list. Treat this as a semantic similarity check: pick the skill whose examples most closely reflect the intent of the prompt.
61
-
62
- #### Pass 3 — Description Match
63
- If no clear winner has emerged, compare the prompt against each skill's `description` field. Pick the skill whose description best captures what the user is trying to do.
64
-
65
- **If two or more skills score equally**, present the top candidates and ask the user to choose:
66
-
67
- ```
68
- More than one skill matches your prompt. Which should I apply?
69
- 1. /<skill-a> — <one-line description>
70
- 2. /<skill-b> — <one-line description>
71
- 3. Neither — describe what you need
72
- ```
73
-
74
- **If no skill matches at all**, inform the user and offer:
75
-
76
- ```
77
- No matching skill found for: "<prompt>"
78
- Would you like to:
79
- 1. Browse all available skills (/help)
80
- 2. Create a new skill for this use case (/btw)
81
- 3. Proceed without a skill (raw prompt)
82
- ```
83
-
84
- ---
85
-
86
- ### Step 3 — Check the Board for Related Context
87
-
88
- Read `project/configs/workflow.json` for board paths (default: `project/board/`).
89
-
90
- Scan the board for items that are topically related to the prompt:
91
-
92
- 1. **Epics** — `project/board/epics/` — check `title` and `description` fields
93
- 2. **Stories** — `project/board/stories/` — check `title` and `description` fields
94
- 3. **Tasks** — `project/board/tasks/` — check `title` and `description` fields
95
-
96
- Extract any items whose title or description contains keywords from the prompt. Limit to the **top 5 most relevant** results. Do not include items whose status is `Archived` or `Cancelled`.
97
-
98
- ---
99
-
100
- ### Step 4 — Check Documentation
101
-
102
- Scan the following directories for relevant documentation files:
103
-
104
- ```
105
- project/documentation/plans/
106
- project/documentation/summaries/
107
- project/documentation/examples/
108
- docs/
109
- ```
110
-
111
- Match filenames and headings against the prompt keywords. Collect the **top 3 most relevant** file paths.
112
-
113
- ---
114
-
115
- ### Step 5 — Construct the Enriched Prompt
116
-
117
- Assemble the following composite message in **exactly this order**:
118
-
119
- #### Part A — Matched Skill (full content)
120
- Inline the **full body** of the matched skill's `SKILL.md` (everything after the YAML front-matter). This is placed first so the agent reads the skill instructions before anything else.
121
-
122
- ```
123
- <!-- SKILL: /<matched-skill-name> -->
124
- <full skill instructions>
125
- <!-- END SKILL -->
126
- ```
127
-
128
- #### Part B — Board & Documentation Context
129
- Immediately after the skill, append a context block:
130
-
131
- ```markdown
132
- ---
133
- ## 📋 Relevant Board Context
134
-
135
- <list each matched board item as:>
136
- - [<status>] **<ID>** — <title> (`project/board/<type>/<filename>`)
137
-
138
- ## 📄 Relevant Documentation
139
-
140
- <list each matched doc as:>
141
- - `<file-path>` — <one-line summary of the doc's purpose>
142
- ---
143
- ```
144
-
145
- If no board items or docs were found, omit the corresponding section rather than leaving it empty.
146
-
147
- #### Part C — Original Prompt
148
- Finally, append the user's original unmodified prompt:
149
-
150
- ```
151
- ## 🗣 Original Prompt
152
-
153
- > <original prompt verbatim>
154
- ```
155
-
156
- ---
157
-
158
- ### Step 6 — Invoke the Matched Skill
159
-
160
- Deliver the enriched composite message to the appropriate agent:
161
-
162
- - If the matched skill specifies `metadata.prefered_agent`, load that agent's definition from `agents/<prefered_agent>.md` and pass the full composite message to it.
163
- - If no `prefered_agent` is specified, execute the skill instructions directly using the composite message as the working input.
164
-
165
- Do **not** summarise or restate the composite message — deliver it as-is.
166
-
167
- ---
168
-
169
- ### Step 7 — Report Routing Decision
170
-
171
- Before invoking the skill, briefly inform the user:
172
-
173
- ```
174
- Routing to: /<matched-skill-name>
175
- Reason: <one sentence explaining why this skill was chosen>
176
- Board items found: <count>
177
- Docs found: <count>
178
- ```
179
-
180
- Then proceed immediately — do not wait for user confirmation unless the match was ambiguous (Step 2 tie-break already handled that).