@jenga-ai/agent 1.2.2 → 1.2.4

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 CHANGED
@@ -1,4 +1,4 @@
1
- # JengaAgent
1
+ # Jenga AI
2
2
 
3
3
  **A structured multi-agent development workflow that works with any AI agent or AI-native IDE.** Three specialised AI agents — Scrum Master, Developer, and Tester — collaborate through a shared scrum board, an event-driven trigger queue, and 28 slash-command skills to take a project from idea to verified, committed code — across as many sessions as it takes.
4
4
 
@@ -25,16 +25,18 @@ npm install @jenga-ai/agent
25
25
  | Agent | Skills & Agents | Root Context File |
26
26
  |---|---|---|
27
27
  | **Claude Code** | `.claude/` — mirrored automatically on install | `CLAUDE.md` — generated by `/init` today |
28
- | **GitHub Copilot** | `.agents/` — mirrored automatically on install | `.github/copilot-instructions.md` — generated by `/init` today |
28
+ | **GitHub Copilot** | `.agents/` — mirrored automatically on install | `.github/copilot-instructions.md` — bootstrapped at `npm install` time, refined by `jenga init` |
29
29
  | **Codex** | `.agents/` — mirrored automatically on install | `AGENTS.md` — generated by `/init` today |
30
30
 
31
31
  `CLAUDE.md` and `AGENTS.md` are generated unconditionally by `/init` — every agent gets a real, populated root-level context file out of the box, not just a pointer. If either file already exists as a genuine pre-existing user file, Jenga writes its own copy as `J-CLAUDE.md` / `J-AGENTS.md` instead and inserts a short reference into the existing file, leaving it otherwise untouched.
32
32
 
33
+ `.github/copilot-instructions.md` follows a different path: `scripts/postinstall.js` writes it unconditionally and non-interactively the moment `npm install` finishes, so Copilot has working `/skill-name` routing instructions even if a consumer's very first action is a Copilot slash command, before the separate `jenga init` CLI wizard has ever run. Running `jenga init` afterward refines the same file using the user's actual chosen skills path — it is never generated by the `/init` skill itself.
34
+
33
35
  ---
34
36
 
35
37
  ## The Problem It Solves
36
38
 
37
- Without a framework like JengaAgent, AI-assisted development has serious structural weaknesses:
39
+ Without a framework like Jenga AI, AI-assisted development has serious structural weaknesses:
38
40
 
39
41
  | Problem | Reality |
40
42
  |---|---|
@@ -45,7 +47,7 @@ Without a framework like JengaAgent, AI-assisted development has serious structu
45
47
  | **No audit trail** | You can't replay *why* something was built, by which agent, based on which task |
46
48
  | **Sessions just end** | Work-in-progress, unresolved problems, and incomplete stories silently vanish |
47
49
 
48
- JengaAgent solves each of these with structure: persistent board state, strict agent roles, typed inter-agent contracts, and session-end hooks that preserve context between sessions.
50
+ Jenga AI solves each of these with structure: persistent board state, strict agent roles, typed inter-agent contracts, and session-end hooks that preserve context between sessions.
49
51
 
50
52
  ---
51
53
 
@@ -53,7 +55,7 @@ JengaAgent solves each of these with structure: persistent board state, strict a
53
55
 
54
56
  ### Before / After
55
57
 
56
- **Without JengaAgent:**
58
+ **Without Jenga AI:**
57
59
  ```
58
60
  You: "Add user authentication"
59
61
  AI agent: [writes auth code, declares it works, session ends]
@@ -63,7 +65,7 @@ You: "What's the status of auth?"
63
65
  AI agent: "I don't have context from the previous session."
64
66
  ```
65
67
 
66
- **With JengaAgent:**
68
+ **With Jenga AI:**
67
69
  ```
68
70
  /todo → "Add user authentication" → linked to E01_S02
69
71
  /do → Developer creates worktree E01_S02_T01-auth
@@ -81,7 +83,7 @@ Next session:
81
83
 
82
84
  ### Real-World Scenario: Building a Feature Across Sessions
83
85
 
84
- Imagine you're building a REST API with auth, rate limiting, and an admin dashboard. Each is a separate Epic. Here's how JengaAgent handles that across multiple days:
86
+ Imagine you're building a REST API with auth, rate limiting, and an admin dashboard. Each is a separate Epic. Here's how Jenga AI handles that across multiple days:
85
87
 
86
88
  **Planning**
87
89
  ```
@@ -187,7 +189,7 @@ Run `/status` at any time to see where the project stands.
187
189
 
188
190
  ## Supported Platforms
189
191
 
190
- JengaAgent has been tested with:
192
+ Jenga AI has been tested with:
191
193
 
192
194
  | Platform | Type |
193
195
  |---|---|
@@ -261,7 +263,7 @@ Skills live in `.agents/skills/<name>/SKILL.md`. Invoke with `/<name>` in your A
261
263
 
262
264
  ## Distributing the Workflow
263
265
 
264
- JengaAgent can propagate its workflow files to other projects on your machine via `/distribute`.
266
+ Jenga AI can propagate its workflow files to other projects on your machine via `/distribute`.
265
267
 
266
268
  1. **Register consumer projects** — add each consuming project to `distribute.config.json` at the repo root (or pass a path directly: `/distribute /path/to/project`).
267
269
  2. **Run `/distribute`** — choose `major`, `minor`, `patch`, or `amend` release type; the skill handles versioning, dry-run preview, file copy, and a version bump commit.
@@ -375,7 +377,7 @@ All agents log every incoming sender object to `project/logs/events.json` as the
375
377
 
376
378
  ---
377
379
 
378
- ## When to Use JengaAgent
380
+ ## When to Use Jenga AI
379
381
 
380
382
  **Use it when:**
381
383
  - You're building a non-trivial project across multiple sessions
@@ -1,10 +1,11 @@
1
1
  import { createInterface } from "readline";
2
- import { readFileSync, writeFileSync, existsSync, appendFileSync, mkdirSync, readdirSync } from "fs";
2
+ import { readFileSync, writeFileSync, existsSync, appendFileSync } from "fs";
3
3
  import { join, dirname } from "path";
4
4
  import { fileURLToPath } from "url";
5
5
  import { validateConfig } from "../config-schema.js";
6
6
  import { injectSettings } from "../inject-settings.js";
7
7
  import { generateAgentContext } from "../generate-agent-context.js";
8
+ import { generateCopilotInstructions } from "../generate-copilot-instructions.js";
8
9
 
9
10
  const CONFIG_FILE = "jenga.cli.json";
10
11
 
@@ -125,74 +126,28 @@ export async function runInit(args, projectRoot = process.cwd()) {
125
126
  console.warn("You can register it manually later by running jenga init again.");
126
127
  }
127
128
 
128
- // Generate .github/copilot-instructions.md from template.
129
- // Templates ship inside the jenga-agent package (node_modules/jenga-agent/templates/).
130
- // Fall back to a bare `templates/` at the project root only for the dev-repo case.
129
+ // Generate .github/copilot-instructions.md from template, via the shared generator
130
+ // (lib/generate-copilot-instructions.js) also used unconditionally by scripts/postinstall.js
131
+ // at install time (E46_S03_T01). Templates ship inside the jenga-agent package
132
+ // (node_modules/jenga-agent/templates/), with a bare `templates/` at the project root as a
133
+ // fallback for the dev-repo case — both handled inside the shared generator.
134
+ //
135
+ // skillsPath may be a string or an array (multi-target installs); pick the first existing
136
+ // directory — mirrored copies hold identical content. This pass uses the user's actual
137
+ // chosen skillsPath from the wizard, which may differ from postinstall's `.agents/skills`
138
+ // default and refines whatever postinstall already bootstrapped. The shared generator's
139
+ // idempotent marker-replace logic means running it again here never duplicates the JENGA
140
+ // block or corrupts content outside the markers.
131
141
  try {
132
- const tplCandidates = [
133
- join(PACKAGE_ROOT, "templates", "copilot-instructions.md.tpl"),
134
- join(projectRoot, "templates", "copilot-instructions.md.tpl"),
135
- ];
136
- const tplPath = tplCandidates.find(existsSync);
137
- if (tplPath) {
138
- const tpl = readFileSync(tplPath, "utf8");
139
-
140
- // Build skill list from skillsPath. skillsPath may be a string or an
141
- // array (multi-target installs); pick the first existing directory —
142
- // mirrored copies hold identical content.
143
- const skillsPathList = Array.isArray(config.skillsPath) ? config.skillsPath : [config.skillsPath];
144
- const skillsDir = skillsPathList
145
- .map(p => join(projectRoot, p))
146
- .find(existsSync);
147
- let skillLines = [];
148
- if (skillsDir) {
149
- for (const entry of readdirSync(skillsDir, { withFileTypes: true })) {
150
- if (!entry.isDirectory()) continue;
151
- const skillMdPath = join(skillsDir, entry.name, "SKILL.md");
152
- let description = "";
153
- if (existsSync(skillMdPath)) {
154
- const content = readFileSync(skillMdPath, "utf8");
155
- const match = content.match(/^description:\s*(.+)$/m);
156
- if (match) description = match[1].trim();
157
- }
158
- skillLines.push(`- **${entry.name}**${description ? `: ${description}` : ""}`);
159
- }
160
- }
161
- const skillList = skillLines.length > 0 ? skillLines.join("\n") : "_No skills found._";
162
- const rendered = tpl.replace("{{SKILL_LIST}}", skillList);
163
-
164
- const githubDir = join(projectRoot, ".github");
165
- const copilotInstructionsPath = join(githubDir, "copilot-instructions.md");
166
-
167
- if (!existsSync(githubDir)) mkdirSync(githubDir, { recursive: true });
168
-
169
- if (!existsSync(copilotInstructionsPath)) {
170
- writeFileSync(copilotInstructionsPath, rendered, "utf8");
171
- } else {
172
- // Replace only the JENGA block; preserve content outside the markers
173
- const existing = readFileSync(copilotInstructionsPath, "utf8");
174
- const startMarker = "<!-- JENGA:START -->";
175
- const endMarker = "<!-- JENGA:END -->";
176
- const startIdx = existing.indexOf(startMarker);
177
- const endIdx = existing.indexOf(endMarker);
178
-
179
- // Extract the new JENGA block from rendered template
180
- const renderedStart = rendered.indexOf(startMarker);
181
- const renderedEnd = rendered.indexOf(endMarker);
182
- const newBlock = rendered.slice(renderedStart, renderedEnd + endMarker.length);
183
-
184
- let updated;
185
- if (startIdx !== -1 && endIdx !== -1 && startIdx < endIdx) {
186
- updated =
187
- existing.slice(0, startIdx) +
188
- newBlock +
189
- existing.slice(endIdx + endMarker.length);
190
- } else {
191
- // No existing markers — append the block
192
- updated = existing + (existing.endsWith("\n") ? "" : "\n") + newBlock + "\n";
193
- }
194
- writeFileSync(copilotInstructionsPath, updated, "utf8");
195
- }
142
+ const skillsPathList = Array.isArray(config.skillsPath) ? config.skillsPath : [config.skillsPath];
143
+ const skillsDir = skillsPathList
144
+ .map(p => join(projectRoot, p))
145
+ .find(existsSync);
146
+
147
+ const result = generateCopilotInstructions(projectRoot, PACKAGE_ROOT, skillsDir);
148
+ if (result.skipped) {
149
+ console.warn("Warning: templates/copilot-instructions.md.tpl not found — skipped .github/copilot-instructions.md generation.");
150
+ } else {
196
151
  console.log("✓ .github/copilot-instructions.md written");
197
152
  }
198
153
  } catch (e) {
@@ -26,7 +26,7 @@
26
26
  * ESM, Node built-ins only — mirrors lib/mirror.js and lib/inject-settings.js.
27
27
  */
28
28
 
29
- import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync } from "fs";
29
+ import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync, realpathSync } from "fs";
30
30
  import { join, dirname } from "path";
31
31
  import { fileURLToPath } from "url";
32
32
 
@@ -220,7 +220,16 @@ export function generateAgentContext(projectRoot = process.cwd(), packageRoot =
220
220
  // used by skills/init/scripts/init.sh (this repo's own board-scaffolding
221
221
  // flow, where node is guaranteed available). The published npm CLI path
222
222
  // (lib/commands/init.js) imports generateAgentContext() directly instead.
223
- if (process.argv[1] === fileURLToPath(import.meta.url)) {
223
+ //
224
+ // process.argv[1] is compared via realpath, not as a raw string: Node
225
+ // resolves import.meta.url through symlinks when loading an ES module, but
226
+ // leaves process.argv[1] exactly as the shell passed it. On macOS, TMPDIR
227
+ // and /tmp are themselves symlinks into /private, so any invocation from a
228
+ // path under either (a very ordinary occurrence — every consumer install
229
+ // under a symlinked, mapped, or `npm link`-ed directory hits this) made the
230
+ // two sides disagree and silently skipped generation with no error.
231
+ const invokedPath = process.argv[1] ? realpathSync(process.argv[1]) : null;
232
+ if (invokedPath === fileURLToPath(import.meta.url)) {
224
233
  const projectRoot = process.argv[2] || process.cwd();
225
234
  const result = generateAgentContext(projectRoot);
226
235
  if (result.skipped) {
@@ -0,0 +1,142 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * lib/generate-copilot-instructions.js — .github/copilot-instructions.md generation
4
+ *
5
+ * Single source of truth for scaffolding the Copilot routing-instructions file from
6
+ * templates/copilot-instructions.md.tpl (extracted from lib/commands/init.js per E46_S03_T01).
7
+ * Used by:
8
+ * - scripts/postinstall.js (runs unconditionally, non-interactively, on every `npm install`)
9
+ * - lib/commands/init.js (the interactive `jenga init` CLI wizard, using the user's chosen
10
+ * skillsPath — may differ from postinstall's default)
11
+ *
12
+ * Root-cause context: `.github/copilot-instructions.md` teaches Copilot the `/skill-name` →
13
+ * `.agents/skills/<name>/SKILL.md` routing convention. It was previously written only by the
14
+ * `jenga init` CLI wizard, never by postinstall.js — so a consumer whose very first action was
15
+ * typing `/init` inside Copilot (before ever running `jenga init`) got no routing at all.
16
+ * Extracting this into a shared, parameterized function lets postinstall.js bootstrap a working
17
+ * routing file immediately, with `jenga init` free to refine it later using the user's actual
18
+ * chosen skills path.
19
+ *
20
+ * Collision behaviour (preserved verbatim from the original inline implementation — do not
21
+ * rewrite this algorithm, only relocate it):
22
+ * - If `.github/copilot-instructions.md` does not exist, write the rendered template in full.
23
+ * - If it exists, replace only the content between `<!-- JENGA:START -->` and
24
+ * `<!-- JENGA:END -->`, preserving everything outside the markers.
25
+ * - If it exists but has no JENGA markers, append the block to the end of the file.
26
+ * This makes repeat runs (postinstall run twice, or postinstall followed by `jenga init`)
27
+ * idempotent: the JENGA block is never duplicated and content outside the markers is never
28
+ * touched.
29
+ *
30
+ * ESM, Node built-ins only — mirrors lib/generate-agent-context.js and lib/mirror.js.
31
+ */
32
+
33
+ import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync } from "fs";
34
+ import { join, dirname } from "path";
35
+ import { fileURLToPath } from "url";
36
+
37
+ // This file lives at <package>/lib/generate-copilot-instructions.js — one level up is the
38
+ // installed jenga-agent package root, which holds templates/.
39
+ const __dirname = dirname(fileURLToPath(import.meta.url));
40
+ const DEFAULT_PACKAGE_ROOT = join(__dirname, "..");
41
+
42
+ const START_MARKER = "<!-- JENGA:START -->";
43
+ const END_MARKER = "<!-- JENGA:END -->";
44
+
45
+ function resolveTemplatePath(projectRoot, packageRoot) {
46
+ const candidates = [
47
+ join(packageRoot, "templates", "copilot-instructions.md.tpl"),
48
+ join(projectRoot, "templates", "copilot-instructions.md.tpl"),
49
+ ];
50
+ return candidates.find(existsSync);
51
+ }
52
+
53
+ /**
54
+ * Build the {{SKILL_LIST}} markdown block by scanning `skillsDir` for subdirectories
55
+ * containing a SKILL.md, extracting each one's `description:` frontmatter line.
56
+ */
57
+ function buildSkillList(skillsDir) {
58
+ if (!skillsDir || !existsSync(skillsDir)) return "_No skills found._";
59
+
60
+ const skillLines = [];
61
+ for (const entry of readdirSync(skillsDir, { withFileTypes: true })) {
62
+ if (!entry.isDirectory()) continue;
63
+ const skillMdPath = join(skillsDir, entry.name, "SKILL.md");
64
+ let description = "";
65
+ if (existsSync(skillMdPath)) {
66
+ const content = readFileSync(skillMdPath, "utf8");
67
+ const match = content.match(/^description:\s*(.+)$/m);
68
+ if (match) description = match[1].trim();
69
+ }
70
+ skillLines.push(`- **${entry.name}**${description ? `: ${description}` : ""}`);
71
+ }
72
+ return skillLines.length > 0 ? skillLines.join("\n") : "_No skills found._";
73
+ }
74
+
75
+ /**
76
+ * Replace only the JENGA:START..JENGA:END block in an existing copilot-instructions.md with the
77
+ * freshly rendered one, preserving everything outside the markers. If no existing file, write
78
+ * the rendered template in full. If the existing file has no markers, append the block.
79
+ */
80
+ function writeCopilotInstructions(path, rendered) {
81
+ if (!existsSync(path)) {
82
+ writeFileSync(path, rendered, "utf8");
83
+ return;
84
+ }
85
+
86
+ const existing = readFileSync(path, "utf8");
87
+ const startIdx = existing.indexOf(START_MARKER);
88
+ const endIdx = existing.indexOf(END_MARKER);
89
+
90
+ const renderedStart = rendered.indexOf(START_MARKER);
91
+ const renderedEnd = rendered.indexOf(END_MARKER);
92
+ const newBlock = rendered.slice(renderedStart, renderedEnd + END_MARKER.length);
93
+
94
+ let updated;
95
+ if (startIdx !== -1 && endIdx !== -1 && startIdx < endIdx) {
96
+ updated = existing.slice(0, startIdx) + newBlock + existing.slice(endIdx + END_MARKER.length);
97
+ } else {
98
+ // No existing markers — append the block.
99
+ updated = existing + (existing.endsWith("\n") ? "" : "\n") + newBlock + "\n";
100
+ }
101
+ writeFileSync(path, updated, "utf8");
102
+ }
103
+
104
+ /**
105
+ * Generate (or idempotently refresh) `.github/copilot-instructions.md` at projectRoot from the
106
+ * shared template.
107
+ *
108
+ * @param {string} projectRoot - project root directory (default: cwd)
109
+ * @param {string} packageRoot - installed jenga-agent package root (default: derived from this
110
+ * file's own location)
111
+ * @param {string} skillsDir - path to the skills directory, resolved relative to projectRoot if
112
+ * not already absolute (e.g. ".agents/skills" from postinstall, or the user's chosen
113
+ * skillsPath from the `jenga init` wizard)
114
+ * @returns {{written: boolean, path?: string, skipped?: boolean}}
115
+ */
116
+ export function generateCopilotInstructions(
117
+ projectRoot = process.cwd(),
118
+ packageRoot = DEFAULT_PACKAGE_ROOT,
119
+ skillsDir
120
+ ) {
121
+ const tplPath = resolveTemplatePath(projectRoot, packageRoot);
122
+ if (!tplPath) {
123
+ return { written: false, skipped: true };
124
+ }
125
+
126
+ const tpl = readFileSync(tplPath, "utf8");
127
+
128
+ const resolvedSkillsDir = skillsDir
129
+ ? (skillsDir.startsWith("/") ? skillsDir : join(projectRoot, skillsDir))
130
+ : undefined;
131
+ const skillList = buildSkillList(resolvedSkillsDir);
132
+ const rendered = tpl.replace("{{SKILL_LIST}}", skillList);
133
+
134
+ const githubDir = join(projectRoot, ".github");
135
+ const copilotInstructionsPath = join(githubDir, "copilot-instructions.md");
136
+
137
+ if (!existsSync(githubDir)) mkdirSync(githubDir, { recursive: true });
138
+
139
+ writeCopilotInstructions(copilotInstructionsPath, rendered);
140
+
141
+ return { written: true, path: copilotInstructionsPath };
142
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jenga-ai/agent",
3
- "version": "1.2.2",
3
+ "version": "1.2.4",
4
4
  "description": "Structured multi-agent development workflow for AI coding agents — scrum board, role-bounded scrum master / developer / tester agents, and 30+ slash-command skills. Works with Claude Code, GitHub Copilot, and Codex.",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -14,6 +14,7 @@
14
14
  },
15
15
  "scripts": {
16
16
  "postinstall": "node scripts/postinstall.js",
17
+ "test": "bats tests/*.bats",
17
18
  "validate:npm-metadata": "bash scripts/validate_npm_metadata.sh",
18
19
  "ui:dev": "npm run ui:dev --prefix project/app",
19
20
  "ui:build": "npm run ui:build --prefix project/app",
@@ -74,5 +75,8 @@
74
75
  },
75
76
  "comments": {
76
77
  "audit": "sharp <0.35.0 and adm-zip <0.6.0 are transitive deps of @huggingface/transformers with no upstream fix available. Not exploitable in this context: only text feature-extraction pipeline is used — no image processing or ZIP handling at application boundary. Review when @huggingface/transformers ships a patched release."
78
+ },
79
+ "devDependencies": {
80
+ "bats": "^1.13.0"
77
81
  }
78
82
  }
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * postinstall.js — JengaAgent consumer installation hook
3
+ * postinstall.js — Jenga AI consumer installation hook
4
4
  *
5
5
  * Runs automatically when a consumer project installs jenga-agent via npm.
6
6
  * Copies the discovery-bound dirs — `skills/` and `agents/` — into BOTH
@@ -32,6 +32,7 @@ import path from 'node:path';
32
32
  import { fileURLToPath } from 'node:url';
33
33
 
34
34
  import { mirror } from '../lib/mirror.js';
35
+ import { generateCopilotInstructions } from '../lib/generate-copilot-instructions.js';
35
36
 
36
37
  // ESM equivalent of __dirname
37
38
  const __filename = fileURLToPath(import.meta.url);
@@ -65,7 +66,7 @@ function main() {
65
66
 
66
67
  // Avoid running during development (when INIT_CWD === packageRoot itself)
67
68
  if (path.resolve(consumerRoot) === path.resolve(packageRoot)) {
68
- console.log('\n ℹ JengaAgent postinstall: running inside the package itself — skipping copy.\n');
69
+ console.log('\n ℹ Jenga AI postinstall: running inside the package itself — skipping copy.\n');
69
70
  return;
70
71
  }
71
72
 
@@ -93,7 +94,7 @@ function main() {
93
94
  const shouldCopy = isFirstInstall || isUpgrade;
94
95
 
95
96
  console.log('\n╔══════════════════════════════════════════════════════╗');
96
- console.log('║ JengaAgent — postinstall hook ║');
97
+ console.log('║ Jenga AI — postinstall hook ║');
97
98
  console.log('╚══════════════════════════════════════════════════════╝\n');
98
99
  console.log(` Package version : ${packageVersion}`);
99
100
  console.log(` Installed version: ${installedVersion ?? '(none — first install)'}`);
@@ -153,6 +154,26 @@ function main() {
153
154
  totalSkipped += result.skipped.length;
154
155
  }
155
156
 
157
+ // Bootstrap .github/copilot-instructions.md unconditionally, right after the mirror step —
158
+ // no interactive prompts, since postinstall runs unattended during `npm install`. Without
159
+ // this, a consumer whose very first action is a Copilot slash command (before ever running
160
+ // the separate `jenga init` CLI wizard) gets no `/skill-name` routing instructions at all
161
+ // (E46_S03_T01). `.agents/skills` is passed as the skills directory because that's the
162
+ // directory the mirror step above just populated, regardless of which agentTarget the user
163
+ // eventually picks in `jenga init`. The shared generator's idempotent marker-replace logic
164
+ // (lib/generate-copilot-instructions.js) means a later `jenga init` run — using the user's
165
+ // actual chosen skillsPath — safely refines this file without duplicating the JENGA block.
166
+ try {
167
+ const result = generateCopilotInstructions(consumerRoot, packageRoot, '.agents/skills');
168
+ if (result.skipped) {
169
+ console.log(' ⚠ templates/copilot-instructions.md.tpl not found — skipped .github/copilot-instructions.md bootstrap');
170
+ } else {
171
+ console.log(' ✓ .github/copilot-instructions.md bootstrapped');
172
+ }
173
+ } catch (e) {
174
+ console.log(` ⚠ Could not bootstrap .github/copilot-instructions.md — ${e.message}`);
175
+ }
176
+
156
177
  // Write .jenga-version to record the installed version at consumer root
157
178
  fs.writeFileSync(versionFile, packageVersion + '\n', 'utf8');
158
179
 
@@ -7,7 +7,7 @@
7
7
  # (CLAUDE.md), this is a script rather than an agent instruction so that
8
8
  # future drift in package.json's publish-facing fields (e.g. keywords
9
9
  # accidentally cleared, or repository.url silently re-pointed at the
10
- # private samwelmunga/JengaAgent repo instead of the public jenga-npm
10
+ # private samwelmunga/Jenga AI repo instead of the public jenga-npm
11
11
  # mirror) is caught deterministically instead of relying on an agent
12
12
  # noticing during review.
13
13
  #
@@ -109,7 +109,7 @@ if (pkg.repository != null) {
109
109
  if (!repoUrl || repoUrl.trim().length === 0) {
110
110
  fail("repository is present but has no resolvable url.");
111
111
  } else if (!repoUrl.includes(PUBLIC_REPO_MARKER)) {
112
- fail("repository.url (\"" + repoUrl + "\") does not point at the public " + PUBLIC_REPO_MARKER + " repo — it must not reference a private repo (e.g. samwelmunga/JengaAgent) or any other location.");
112
+ fail("repository.url (\"" + repoUrl + "\") does not point at the public " + PUBLIC_REPO_MARKER + " repo — it must not reference a private repo (e.g. samwelmunga/Jenga AI) or any other location.");
113
113
  } else {
114
114
  pass("repository.url points at the public " + PUBLIC_REPO_MARKER + " repo.");
115
115
  }
@@ -6,7 +6,7 @@ This document is the canonical reference for the `jenga.config.json` file writte
6
6
 
7
7
  ## Purpose
8
8
 
9
- `jenga.config.json` lives at the root of a consuming project and tracks which version of the JengaAgent framework is currently installed there, where the framework files were placed, and when the last distribution occurred. It is read by the distribution script on subsequent runs to determine the target directory and detect whether an upgrade is needed.
9
+ `jenga.config.json` lives at the root of a consuming project and tracks which version of the Jenga AI framework is currently installed there, where the framework files were placed, and when the last distribution occurred. It is read by the distribution script on subsequent runs to determine the target directory and detect whether an upgrade is needed.
10
10
 
11
11
  ---
12
12
 
@@ -40,17 +40,17 @@ This document is the canonical reference for the `jenga.config.json` file writte
40
40
  |---|---|---|---|---|
41
41
  | `project_name` | string | yes | — | Human-readable identifier for the consuming project. Must match the `name` field of the corresponding entry in the monorepo's `distribute.config.json`. |
42
42
  | `target_dir` | string | yes | `.agents` | The directory under the project root where framework files are copied. `distribute-changes.sh` reads this field to resolve the destination path on every run. Change this only if the consuming project uses a non-standard layout. |
43
- | `version` | string | yes | — | The JengaAgent semantic version currently installed in this project (e.g. `"2.3.1"`). Compared against the `version` field in the monorepo's `package.json` to determine whether an upgrade is required. |
43
+ | `version` | string | yes | — | The Jenga AI semantic version currently installed in this project (e.g. `"2.3.1"`). Compared against the `version` field in the monorepo's `package.json` to determine whether an upgrade is required. |
44
44
  | `updated_at` | string (ISO 8601 date) | yes | — | Date of the last successful distribution, in `YYYY-MM-DD` format. Does **not** include a time component. |
45
45
  | `last_distributed` | string (ISO 8601 datetime) | yes | — | Full UTC timestamp of the last successful distribution, in `YYYY-MM-DDTHH:MM:SSZ` format. Provides more precision than `updated_at` and is useful for audit and ordering purposes. |
46
46
  | `source` | string | yes | `"private"` | Distribution channel. Always `"private"` for projects that receive updates via the filesystem distribution mechanism. Distinguishes these projects from any future npm-installed consumers. Do not change this value manually. |
47
- | `project_files_visibility` | string (enum) | no | `"visible"` | How JengaAgent's own working files appear in the consuming project. Exactly one of `visible` or `ignored` — no other value is accepted. Written by `/init`, not by distribution. See [Project files visibility](#project-files-visibility) below. |
47
+ | `project_files_visibility` | string (enum) | no | `"visible"` | How Jenga AI's own working files appear in the consuming project. Exactly one of `visible` or `ignored` — no other value is accepted. Written by `/init`, not by distribution. See [Project files visibility](#project-files-visibility) below. |
48
48
 
49
49
  ---
50
50
 
51
51
  ## Project files visibility
52
52
 
53
- `project_files_visibility` controls how JengaAgent's own working files — the `project/` tree containing the scrum board, `todo.md`, `queue/`, `rapports/`, and `logs/` — appear in a consuming project.
53
+ `project_files_visibility` controls how Jenga AI's own working files — the `project/` tree containing the scrum board, `todo.md`, `queue/`, `rapports/`, and `logs/` — appear in a consuming project.
54
54
 
55
55
  This is distinct from `target_dir`. `target_dir` governs where the **distributed framework files** (skill and agent definitions) land; `project_files_visibility` governs the **working tree** that accumulates as the framework is used.
56
56
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: distribute
3
- description: Distribute JengaAgent framework files from this private monorepo to one or more consuming projects via the local filesystem. Manages release type selection, version bumping, dry-run preview, per-target file copy, and a post-distribution git commit.
3
+ description: Distribute Jenga AI framework files from this private monorepo to one or more consuming projects via the local filesystem. Manages release type selection, version bumping, dry-run preview, per-target file copy, and a post-distribution git commit.
4
4
  keywords:
5
5
  - distribute
6
6
  - private distribution
@@ -16,7 +16,7 @@ examples:
16
16
 
17
17
  # Distribute
18
18
 
19
- Copies JengaAgent framework files from this monorepo to all active consuming projects registered in `distribute.config.json`. Manages the full version lifecycle: release type selection, `package.json` version bump, dry-run preview, per-target file copy, and a final git commit of the version bump.
19
+ Copies Jenga AI framework files from this monorepo to all active consuming projects registered in `distribute.config.json`. Manages the full version lifecycle: release type selection, `package.json` version bump, dry-run preview, per-target file copy, and a final git commit of the version bump.
20
20
 
21
21
  Distinct from `/self-sync` (which mirrors root → in-repo `.claude/.agents/`) and `/mirror-public` (which syncs to the public GitHub repo). Do not call either of those skills from within this flow.
22
22
 
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env bash
2
- # distribute-changes.sh — Copy JengaAgent framework files into a consuming project
2
+ # distribute-changes.sh — Copy Jenga AI framework files into a consuming project
3
3
  #
4
4
  # Usage:
5
5
  # distribute-changes.sh <project_path> [--dry-run]
@@ -39,7 +39,7 @@ After target resolution, all generation must operate on a synthesis context obje
39
39
  ```yaml
40
40
  target_path: README.md
41
41
  objective: project overview
42
- project_name: JengaAgent
42
+ project_name: Jenga AI
43
43
  project_description: Structured multi-agent development workflow
44
44
  features: []
45
45
  getting_started: []
@@ -67,11 +67,11 @@ changes nothing on disk, so it is the only safe default.
67
67
  Never treat `existing-codebase` as if it were `empty`, and never skip straight to step 2
68
68
  on that verdict without the user (or the non-interactive default) choosing to.
69
69
 
70
- ### 2. Ask how JengaAgent's working files should appear
70
+ ### 2. Ask how Jenga AI's working files should appear
71
71
 
72
72
  Ask the user this question, verbatim, before running any script:
73
73
 
74
- How should JengaAgent's own working files (project/ — the scrum board, todo.md,
74
+ How should Jenga AI's own working files (project/ — the scrum board, todo.md,
75
75
  queue/, rapports/, and logs/) appear in this project?
76
76
  1. Visible — keep them at `project/`, tracked and visible in directory listings
77
77
  2. Ignored — keep them at `project/` but add them to `.gitignore` so they are never committed
@@ -96,10 +96,26 @@ of the mechanical work.
96
96
 
97
97
  ### 3. Run the scaffold script
98
98
 
99
- Execute the init script from the project root, passing the choice from step 2:
99
+ `init.sh` is not guaranteed to live at a single fixed path: in a project that
100
+ installed Jenga via npm, it was mirrored to `.claude/skills/init/scripts/`
101
+ (Claude Code) and `.agents/skills/init/scripts/` (Copilot/other agents) by
102
+ `postinstall.js`, and neither of those exists yet in this framework's own
103
+ source checkout, where it lives at the bare `skills/init/scripts/` path
104
+ instead. This step runs before `CLAUDE.md`/`AGENTS.md` exist, so it cannot
105
+ rely on either file's routing instructions to resolve the path — it must
106
+ locate its own script directly. Execute the init script from the project
107
+ root, passing the choice from step 2:
100
108
 
101
109
  ```bash
102
- chmod +x ./scripts/init.sh && ./scripts/init.sh --visibility <visible|ignored>
110
+ INIT_SCRIPT=""
111
+ for candidate in .claude/skills/init/scripts/init.sh .agents/skills/init/scripts/init.sh skills/init/scripts/init.sh; do
112
+ [[ -f "$candidate" ]] && { INIT_SCRIPT="$candidate"; break; }
113
+ done
114
+ if [[ -z "$INIT_SCRIPT" ]]; then
115
+ echo "Error: could not locate init.sh under .claude/skills/, .agents/skills/, or skills/" >&2
116
+ exit 1
117
+ fi
118
+ chmod +x "$INIT_SCRIPT" && "$INIT_SCRIPT" --visibility <visible|ignored>
103
119
  ```
104
120
 
105
121
  Omitting `--visibility` falls back to the `JENGA_PROJECT_FILES_VISIBILITY`
@@ -41,7 +41,7 @@ usage() {
41
41
  exit 1
42
42
  }
43
43
 
44
- # Every JengaAgent working file named in E31_S05 — the scrum board, todo.md,
44
+ # Every Jenga AI working file named in E31_S05 — the scrum board, todo.md,
45
45
  # queue/, rapports/ and logs/ — nests under this single root, so one entry
46
46
  # covers them all. `ignored` consumes this list.
47
47
  JENGA_WORKING_PATHS=("project")
@@ -5,6 +5,23 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
5
  ASSETS_DIR="$SCRIPT_DIR/../assets"
6
6
  VISIBILITY_SCRIPT="$SCRIPT_DIR/apply-project-visibility.sh"
7
7
 
8
+ # ─── Resolve the package root that owns templates/ and lib/ ──────────────────
9
+ # postinstall.js mirrors only skills/ and agents/ into .claude/ and .agents/ —
10
+ # templates/ and lib/ are never copied there, so a script running from a
11
+ # mirrored copy (.claude/skills/init/scripts/ or .agents/skills/init/scripts/)
12
+ # cannot reach its siblings via a fixed ../../../ climb the way it can in this
13
+ # monorepo checkout, where init.sh actually lives at skills/init/scripts/ with
14
+ # templates/ and lib/ three levels up. Consumers instead have them inside the
15
+ # installed npm package.
16
+ if [[ -d "$SCRIPT_DIR/../../../templates" ]]; then
17
+ PKG_ROOT="$SCRIPT_DIR/../../.."
18
+ elif [[ -d "$PWD/node_modules/@jenga-ai/agent/templates" ]]; then
19
+ PKG_ROOT="$PWD/node_modules/@jenga-ai/agent"
20
+ else
21
+ echo "Error: could not locate the jenga-agent package root (templates/ not found via monorepo checkout or node_modules/@jenga-ai/agent)." >&2
22
+ exit 1
23
+ fi
24
+
8
25
  # ─── 0. Resolve project_files_visibility ─────────────────────────────────────
9
26
  # Defaults to `visible` — the only value that touches nothing on disk — so an
10
27
  # unattended run can never silently relocate directories or edit .gitignore.
@@ -65,7 +82,7 @@ cp "$ASSETS_DIR/strategy_stub_template.md" docs/STRATEGY.md
65
82
 
66
83
  # ─── 10. Create CHANGELOG.md ──────────────────────────────────────────────────
67
84
  echo "→ Creating CHANGELOG.md from template..."
68
- cp "$SCRIPT_DIR/../../../templates/CHANGELOG_TEMPLATE.md" CHANGELOG.md
85
+ cp "$PKG_ROOT/templates/CHANGELOG_TEMPLATE.md" CHANGELOG.md
69
86
 
70
87
  # ─── 11. Apply project_files_visibility ──────────────────────────────────────
71
88
  # Runs before the commit so the .gitignore entry (ignored) is captured in the
@@ -79,7 +96,7 @@ bash "$VISIBILITY_SCRIPT" "$VISIBILITY" "$PWD"
79
96
  # lib/generate-agent-context.js (shared with the published `jenga init` CLI).
80
97
  echo "→ Generating CLAUDE.md / AGENTS.md..."
81
98
  if command -v node >/dev/null 2>&1; then
82
- node "$SCRIPT_DIR/../../../lib/generate-agent-context.js" "$PWD"
99
+ node "$PKG_ROOT/lib/generate-agent-context.js" "$PWD"
83
100
  else
84
101
  echo " Warning: node not found — skipped CLAUDE.md/AGENTS.md generation." >&2
85
102
  fi
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env bash
2
- # npm_pipeline.sh — Execute the npm publish pipeline for the JengaAgent repo.
2
+ # npm_pipeline.sh — Execute the npm publish pipeline for the Jenga AI repo.
3
3
  #
4
- # Runs from the JengaAgent repo root (where package.json lives) and invokes
4
+ # Runs from the Jenga AI repo root (where package.json lives) and invokes
5
5
  # `npm publish --tag <dist_tag>` (or `--dry-run` when requested).
6
6
  #
7
7
  # Exit codes:
@@ -45,10 +45,10 @@
45
45
  # itself. The epic cap — and the surfacing of anything it drops — is
46
46
  # applied by a separate layer on top of this script's output.
47
47
  # --include-framework Do not exclude Jenga scaffolding. Needed to point the script at the
48
- # JengaAgent repo itself, where the scaffolding IS the application.
48
+ # Jenga AI repo itself, where the scaffolding IS the application.
49
49
  # --include-training-scaffolding
50
50
  # Do not exclude .training/ job-template scaffolding. Needed to point the
51
- # script at JengaAgent's own /train subsystem, or at a project that
51
+ # script at Jenga AI's own /train subsystem, or at a project that
52
52
  # genuinely wants job templates catalogued as a candidate.
53
53
  # --with-dependencies Additionally compute the "cohesion" signal by calling
54
54
  # detect-dependencies.sh once per candidate. Off by default: it is an