create-byan-agent 2.19.1 → 2.20.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 (52) hide show
  1. package/CHANGELOG.md +188 -0
  2. package/README.md +4 -4
  3. package/install/src/byan-v2/generation/templates/default-agent.md +1 -1
  4. package/install/templates/.claude/CLAUDE.md +1 -1
  5. package/install/templates/.claude/hooks/fd-phase-guard.js +2 -2
  6. package/install/templates/.claude/hooks/mantra-validate.js +16 -8
  7. package/install/templates/.claude/hooks/strict-scope-guard.js +25 -7
  8. package/install/templates/.claude/rules/native-workflows.md +32 -0
  9. package/install/templates/.claude/skills/byan-byan/SKILL.md +5 -5
  10. package/install/templates/.claude/skills/byan-mantra-audit/SKILL.md +53 -0
  11. package/install/templates/.claude/skills/byan-merise-agile/SKILL.md +2 -2
  12. package/install/templates/.claude/skills/byan-native-dev-story/SKILL.md +83 -0
  13. package/install/templates/.claude/workflows/INDEX.md +35 -0
  14. package/install/templates/.claude/workflows/check-implementation-readiness.js +280 -0
  15. package/install/templates/.claude/workflows/code-review.js +179 -0
  16. package/install/templates/.claude/workflows/create-excalidraw-dataflow.js +214 -0
  17. package/install/templates/.claude/workflows/create-excalidraw-diagram.js +188 -0
  18. package/install/templates/.claude/workflows/create-excalidraw-flowchart.js +225 -0
  19. package/install/templates/.claude/workflows/create-excalidraw-wireframe.js +192 -0
  20. package/install/templates/.claude/workflows/create-story.js +216 -0
  21. package/install/templates/.claude/workflows/dev-story.js +100 -0
  22. package/install/templates/.claude/workflows/document-project.js +455 -0
  23. package/install/templates/.claude/workflows/qa-automate.js +169 -0
  24. package/install/templates/.claude/workflows/quick-dev.js +273 -0
  25. package/install/templates/.claude/workflows/sprint-planning.js +261 -0
  26. package/install/templates/.claude/workflows/testarch-atdd.js +287 -0
  27. package/install/templates/.claude/workflows/testarch-automate.js +229 -0
  28. package/install/templates/.claude/workflows/testarch-ci.js +184 -0
  29. package/install/templates/.claude/workflows/testarch-framework.js +267 -0
  30. package/install/templates/.claude/workflows/testarch-nfr.js +316 -0
  31. package/install/templates/.claude/workflows/testarch-test-design.js +293 -0
  32. package/install/templates/.claude/workflows/testarch-test-review.js +321 -0
  33. package/install/templates/.claude/workflows/testarch-trace.js +316 -0
  34. package/install/templates/.githooks/pre-commit +49 -15
  35. package/install/templates/_byan/config.yaml +15 -5
  36. package/install/templates/_byan/mcp/byan-mcp-server/bin/byan-build-workflows.js +20 -0
  37. package/install/templates/_byan/mcp/byan-mcp-server/bin/byan-lint-workflows.js +57 -0
  38. package/install/templates/_byan/mcp/byan-mcp-server/lib/native-loop.js +39 -0
  39. package/install/templates/_byan/mcp/byan-mcp-server/lib/workflows-generator.js +149 -0
  40. package/install/templates/_byan/mcp/byan-mcp-server/lib/workflows-lint.js +113 -0
  41. package/install/templates/_byan/workflow/simple/byan/feature-workflow.md +14 -11
  42. package/install/templates/docs/native-workflows-contract.md +84 -0
  43. package/package.json +2 -2
  44. package/src/byan-v2/data/agent-scopes.json +46 -0
  45. package/src/byan-v2/data/mantras.json +194 -8
  46. package/src/byan-v2/generation/mantra-audit.js +147 -0
  47. package/src/byan-v2/generation/mantra-validator.js +56 -6
  48. package/src/byan-v2/generation/scope-resolver.js +102 -0
  49. package/src/byan-v2/generation/templates/default-agent.md +1 -1
  50. package/update-byan-agent/bin/update-byan-agent.js +67 -72
  51. package/update-byan-agent/lib/apply-update.js +202 -0
  52. package/update-byan-agent/package.json +1 -1
@@ -0,0 +1,202 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Core of `update-byan-agent update`: rebuild the installed _byan tree and the
5
+ * Copilot stubs from the create-byan-agent template, without a network install
6
+ * and without ever leaving _byan deleted on failure.
7
+ *
8
+ * Design constraints (field-reported bugs this module fixes):
9
+ * - BUG3 : the updater already runs from the @latest package (npx -p ...), so
10
+ * the template lives next to the running bin. Resolve it locally instead of
11
+ * re-running `npm install create-byan-agent@latest` into the user project.
12
+ * - BUG2 : validate the replacement source on disk BEFORE touching the live
13
+ * _byan, then swap via rename (stage -> rename), so a failure never leaves
14
+ * the project without a _byan.
15
+ * - BUG1 : a usable-template failure reports whether the package could not be
16
+ * resolved at all vs. the template dir being present but empty, with the
17
+ * probed paths, instead of a flat "not found in npm package".
18
+ *
19
+ * Pure module: no commander, no inquirer, no spinners, no network. The bin
20
+ * wires UI/version/backup/customization-preservation around it.
21
+ */
22
+
23
+ const fs = require('fs');
24
+ const path = require('path');
25
+
26
+ /** True when p is a directory that contains at least one entry. */
27
+ function isNonEmptyDir(p) {
28
+ try {
29
+ if (!fs.statSync(p).isDirectory()) return false;
30
+ return fs.readdirSync(p).length > 0;
31
+ } catch {
32
+ return false;
33
+ }
34
+ }
35
+
36
+ /** Recursively copy a directory tree (files + dirs), creating dest as needed. */
37
+ function copyRecursive(src, dest) {
38
+ fs.mkdirSync(dest, { recursive: true });
39
+ for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
40
+ const srcPath = path.join(src, entry.name);
41
+ const destPath = path.join(dest, entry.name);
42
+ if (entry.isDirectory()) {
43
+ copyRecursive(srcPath, destPath);
44
+ } else if (entry.isFile()) {
45
+ fs.copyFileSync(srcPath, destPath);
46
+ }
47
+ // symlinks / specials are intentionally skipped (template ships none)
48
+ }
49
+ }
50
+
51
+ function rmrf(p) {
52
+ fs.rmSync(p, { recursive: true, force: true });
53
+ }
54
+
55
+ /**
56
+ * Resolve the create-byan-agent package root that holds install/templates.
57
+ * Preference order:
58
+ * 1. the running package itself (the bin lives at <pkg>/update-byan-agent/bin)
59
+ * 2. node_modules/create-byan-agent under the install path (legacy fallback)
60
+ *
61
+ * @param {object} opts
62
+ * @param {string} opts.installPath project root being updated
63
+ * @param {string} opts.binDir __dirname of the running bin
64
+ * @returns {{ pkgRoot: string, source: 'running-package'|'node_modules' }}
65
+ * @throws {Error} BUG1 diagnostic when no package root carries a template dir
66
+ */
67
+ function resolvePackageRoot({ installPath, binDir }) {
68
+ const runningPkg = path.resolve(binDir, '..', '..');
69
+ const nodeModulesPkg = path.join(installPath, 'node_modules', 'create-byan-agent');
70
+ const candidates = [
71
+ { pkgRoot: runningPkg, source: 'running-package' },
72
+ { pkgRoot: nodeModulesPkg, source: 'node_modules' },
73
+ ];
74
+
75
+ const probed = [];
76
+ for (const c of candidates) {
77
+ const tplDir = path.join(c.pkgRoot, 'install', 'templates');
78
+ const byanTpl = path.join(tplDir, '_byan');
79
+ probed.push(byanTpl);
80
+ if (isNonEmptyDir(byanTpl)) return c;
81
+ // Distinguish "present but empty" (real package defect) from "absent".
82
+ if (fs.existsSync(byanTpl)) {
83
+ throw new Error(
84
+ `create-byan-agent template is present but EMPTY at ${byanTpl}. ` +
85
+ `The package is malformed for this version; do not delete _byan. ` +
86
+ `Reinstall the package or republish.`
87
+ );
88
+ }
89
+ }
90
+
91
+ throw new Error(
92
+ `Could not resolve the create-byan-agent package template. Probed:\n` +
93
+ probed.map((p) => ` - ${p}`).join('\n') +
94
+ `\nThis means the running package layout is unexpected (not a template ` +
95
+ `absence in a healthy package). Re-run via ` +
96
+ `'npx -p create-byan-agent@latest update-byan-agent update'.`
97
+ );
98
+ }
99
+
100
+ /**
101
+ * Replace a destination dir with a freshly staged copy of `src`, never leaving
102
+ * the destination missing for more than two atomic renames. The replacement
103
+ * source is fully staged (and validated non-empty) before the live dir is moved
104
+ * aside, so an interrupted/failed stage cannot destroy the existing content.
105
+ *
106
+ * @param {string} src validated, non-empty source dir
107
+ * @param {string} dest live dir to replace
108
+ * @param {string} label for error messages
109
+ * @returns {number} number of top-level entries staged
110
+ */
111
+ function stageAndSwap(src, dest, label) {
112
+ if (!isNonEmptyDir(src)) {
113
+ throw new Error(`Refusing to replace ${label}: source ${src} is missing or empty.`);
114
+ }
115
+
116
+ const staging = `${dest}.staging`;
117
+ const prev = `${dest}.prev`;
118
+ // Clean any residue from a prior interrupted run. The replacement is always
119
+ // rebuilt from the validated `src`, so a stale `${dest}.prev` is debris, not
120
+ // data to recover (the bin's separate _byan.backup is the crash net).
121
+ rmrf(staging);
122
+ rmrf(prev);
123
+
124
+ // 1. Stage the new content beside the destination. If this throws, the live
125
+ // dest is still untouched.
126
+ copyRecursive(src, staging);
127
+ if (!isNonEmptyDir(staging)) {
128
+ rmrf(staging);
129
+ throw new Error(`Staging ${label} produced no files (source ${src}).`);
130
+ }
131
+
132
+ // 2. Swap. Move the live dir aside (atomic), move staging into place
133
+ // (atomic), then drop the old one. On a swap failure, restore and leave no
134
+ // .staging debris behind.
135
+ const destExists = fs.existsSync(dest);
136
+ try {
137
+ if (destExists) fs.renameSync(dest, prev);
138
+ fs.renameSync(staging, dest);
139
+ } catch (err) {
140
+ if (destExists && !fs.existsSync(dest) && fs.existsSync(prev)) {
141
+ try { fs.renameSync(prev, dest); } catch { /* leave prev for manual recovery */ }
142
+ }
143
+ rmrf(staging);
144
+ throw err;
145
+ }
146
+
147
+ // The swap is committed. A failure to drop the old copy must NOT fail the
148
+ // update (e.g. transient EBUSY on a locked file); a stale .prev is cleaned by
149
+ // the next run's top-of-function rmrf.
150
+ try { rmrf(prev); } catch { /* best-effort */ }
151
+
152
+ return fs.readdirSync(dest).length;
153
+ }
154
+
155
+ /**
156
+ * Apply the file-system part of an update: rebuild _byan and refresh the
157
+ * Copilot stubs from the resolved package template.
158
+ *
159
+ * Caller is responsible for preserving/restoring user customizations around
160
+ * this call (config.yaml, memory, _byan-output, etc.).
161
+ *
162
+ * @param {object} opts
163
+ * @param {string} opts.installPath project root being updated
164
+ * @param {string} opts.pkgRoot resolved create-byan-agent package root
165
+ * @returns {{ byanEntries: number, githubAgentsEntries: number|null, templateRoot: string }}
166
+ */
167
+ function applyUpdate({ installPath, pkgRoot }) {
168
+ const templateRoot = path.join(pkgRoot, 'install', 'templates');
169
+ const byanSrc = path.join(templateRoot, '_byan');
170
+
171
+ // Validate BEFORE any destruction (BUG2). resolvePackageRoot already vetted
172
+ // this, but applyUpdate may be called directly, so re-check.
173
+ if (!isNonEmptyDir(byanSrc)) {
174
+ throw new Error(
175
+ `Template _byan is missing or empty at ${byanSrc}. ` +
176
+ `Aborting update without deleting the existing _byan.`
177
+ );
178
+ }
179
+
180
+ const byanDir = path.join(installPath, '_byan');
181
+ const byanEntries = stageAndSwap(byanSrc, byanDir, '_byan');
182
+
183
+ // Refresh Copilot stubs (.github/agents) from the template, same discipline.
184
+ // Optional: only when the template ships them.
185
+ let githubAgentsEntries = null;
186
+ const ghSrc = path.join(templateRoot, '.github', 'agents');
187
+ if (isNonEmptyDir(ghSrc)) {
188
+ const ghDst = path.join(installPath, '.github', 'agents');
189
+ fs.mkdirSync(path.dirname(ghDst), { recursive: true });
190
+ githubAgentsEntries = stageAndSwap(ghSrc, ghDst, '.github/agents');
191
+ }
192
+
193
+ return { byanEntries, githubAgentsEntries, templateRoot };
194
+ }
195
+
196
+ module.exports = {
197
+ applyUpdate,
198
+ resolvePackageRoot,
199
+ stageAndSwap,
200
+ isNonEmptyDir,
201
+ copyRecursive,
202
+ };
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "update-byan-agent",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "CLI tool for managing BYAN updates with intelligent conflict detection and customization preservation",
5
5
  "bin": {
6
6
  "update-byan-agent": "bin/update-byan-agent.js"