@abarcenas/ai-workflow-template 1.49.0 → 2.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.
- package/.agents/instructions/knowledgebase.instructions.md +2 -2
- package/.agents/instructions/learned-knowledge.instructions.md +47 -2
- package/.agents/instructions/memory-schema.instructions.md +1 -1
- package/.github/workflows/npm-publish.yml +69 -1
- package/.opencode/plugins/graphify.js +22 -0
- package/LICENSE +15 -0
- package/README.md +56 -22
- package/memory-bank/.vocabulary.json +3 -0
- package/opencode.mcp.example.json +13 -0
- package/package.json +20 -10
- package/scripts/bump-version.js +18 -2
- package/scripts/mcp/playwright-mcp-launcher.js +45 -2
- package/scripts/bump-version.test.js +0 -238
- package/scripts/knowledgebase-cli.test.js +0 -576
- package/scripts/knowledgebase-index.test.js +0 -759
- package/scripts/mcp/playwright-mcp-launcher.test.js +0 -217
- package/scripts/mcp-knowledgebase-server.test.js +0 -745
- package/scripts/mcp-memory-server.test.js +0 -398
- package/scripts/memory-cli.test.js +0 -297
- package/scripts/memory-index.test.js +0 -1173
- package/scripts/normalize-memory.test.js +0 -753
- package/scripts/setup/discover.test.js +0 -530
- package/scripts/setup/hooks.test.js +0 -722
- package/scripts/setup/husky-init.test.js +0 -311
- package/scripts/setup/index.test.js +0 -1021
- package/scripts/setup/knowledgebase.test.js +0 -352
- package/scripts/setup/prepare.test.js +0 -718
- package/scripts/setup/skills-phase.test.js +0 -1274
- package/scripts/setup/sync-phase.test.js +0 -373
- package/scripts/setup/ui.test.js +0 -579
- package/scripts/setup/utils.test.js +0 -586
- package/scripts/sync-unit.test.js +0 -1881
- package/scripts/sync.test.js +0 -472
- package/scripts/validate-memory-schema.test.js +0 -328
- package/scripts/vocab-sync.test.js +0 -902
|
@@ -65,7 +65,7 @@ Before debugging from scratch, search for similar errors and their known resolut
|
|
|
65
65
|
Example queries:
|
|
66
66
|
- "spawnScript child process hanging silent output"
|
|
67
67
|
- "process.exit kills parent import module"
|
|
68
|
-
- "npm
|
|
68
|
+
- "npm dependency lifecycle scripts blocked in v12"
|
|
69
69
|
|
|
70
70
|
### 3. Making Architectural Decisions
|
|
71
71
|
|
|
@@ -117,7 +117,7 @@ knowledgebase_search({
|
|
|
117
117
|
**Finding specific error resolutions:**
|
|
118
118
|
```
|
|
119
119
|
knowledgebase_search({
|
|
120
|
-
query: "npm
|
|
120
|
+
query: "npm dependency lifecycle script execution blocked in v12",
|
|
121
121
|
topK: 3
|
|
122
122
|
})
|
|
123
123
|
```
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
**TDD Iterations:** 0 (feature pipeline, not TDD)
|
|
29
29
|
|
|
30
30
|
**New knowledge:**
|
|
31
|
-
- **npm v12 blocks
|
|
31
|
+
- **npm v12 blocks dependency lifecycle scripts** — this is a critical ecosystem shift that affects ALL npm library distribution. Install-time auto-sync is no longer viable; explicit setup commands are required
|
|
32
32
|
- **npx-based setup command pattern** (following Playwright, Prisma, Cypress convention) is the future-proof distribution mechanism for CLI tools that need to configure consumer projects
|
|
33
33
|
- **Husky v9 programmatic API** is dead simple: `import husky from 'husky'; husky(consumerRoot)` — no need to shell out or manually manage shim files
|
|
34
34
|
- **`child_process.spawn` for scripts with `process.exit()`** — importing sync.js (which calls `process.exit()`) directly into the setup process would kill the parent. Spawn as a child process to isolate exit behavior
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
- **Duplicate-marker trap** — constants.js `TEMPLATE_HOOKS.content` must stay in sync with actual `.husky/` hook files. During T12 (post-merge marker), a mismatch was discovered between the template content in constants.js (which already had the marker via `buildHookContent()`) and the actual disk file. Solution: update constants.js template content whenever hook files are modified
|
|
38
38
|
|
|
39
39
|
**Agent tuning notes:**
|
|
40
|
-
- **researcher**: Should explicitly investigate npm ecosystem changes (version-specific behavior, deprecations) — the npm v12
|
|
40
|
+
- **researcher**: Should explicitly investigate npm ecosystem changes (version-specific behavior, deprecations) — the npm v12 lifecycle-script discovery was the single most important finding that drove the architecture
|
|
41
41
|
- **architect**: Modular design with clear interface contracts (Context object shape, return types) paid off — all 5 Batch B modules could be implemented in parallel without coordination issues
|
|
42
42
|
- **implementer**: The parallel batch plan with explicit dependency edges (Batch A → B → C, D+E → F) was critical for the 6 sub-agent coder execution. Without this structure, 6 concurrent coders would have had race conditions on shared files
|
|
43
43
|
- **coder (parallel batches)**: Batch ordering discipline worked — foundation first, then core, then orchestrator. The truly independent batches (D: markers, F: tests) could run alongside later batches. Key lesson: define interfaces (Context shape, return types) before splitting work across parallel coders
|
|
@@ -595,3 +595,48 @@
|
|
|
595
595
|
- **reviewer (two-round shape)**: round 1 caught missing user-facing coverage; round 2 blocked the *new* mechanism for being wrong in both directions. A fix that introduces a heuristic deserves a dedicated review pass — reviewing it as part of the original change under-weights it.
|
|
596
596
|
- **coder (docs for honest gaps)**: protect-but-don't-honour (`.agents-sync-ignore`) must be stated plainly in the README and in code comments. Shipping the storage without the semantics is acceptable only when it is impossible to mistake for working.
|
|
597
597
|
- **all agents (knowledge retrieval)**: `memory_search` executed (cwd-scoped to this repo); `knowledgebase_search` **NOT executed** (PG vector unavailable — `DATABASE_URL` not configured). Neither retried nor treated as a task failure.
|
|
598
|
+
|
|
599
|
+
## Session: 2026-11-01 — MCP `environment` Block Shadowing (`{env:X}` injects an empty string)
|
|
600
|
+
|
|
601
|
+
**Pipeline:** coder (read-only investigation) → coder (fix + A/B shell verification) → tracker
|
|
602
|
+
**Coverage:** N/A (one config-block deletion in `opencode.json`; no test or `scripts/` surface changed, so the Vitest suite is unaffected)
|
|
603
|
+
**TDD Iterations:** 0 (support-driven bug fix, not TDD)
|
|
604
|
+
|
|
605
|
+
**New knowledge:**
|
|
606
|
+
- **`"environment": { "X": "{env:X}" }` in an OpenCode MCP server config injects an EMPTY STRING when the parent process lacks `X`.** It does not omit the key, pass `undefined`, or leave inheritance intact — it sets the variable to `""` in the child. Prefer **omitting the block entirely**: MCP children inherit the parent environment by default, so the block is redundant when the variable is set and actively harmful when it is not. **Universal pattern:** an "explicit passthrough" of a parent variable is not a no-op — it converts "absent" (inheritable/fallback-eligible) into "set but empty" (a real value that defeats every fallback).
|
|
607
|
+
- **An injected empty string silently defeats a `dotenv` fallback, because dotenv never overwrites an existing key.** `import 'dotenv/config'` at the top of `scripts/mcp-knowledgebase-server.js` is powerless once `process.env.DATABASE_URL === ''`: dotenv's loader skips keys already present, so a valid `.env` three feet away is never consulted and the server reports "DATABASE_URL is not configured". **Universal pattern:** `dotenv` is a *default provider*, not a *repair mechanism* — it cannot distinguish "legitimately set" from "set to garbage by a wrapper", so upstream config must not pre-set keys it does not actually have values for. This is the same family as the established "script reads `process.env` without loading `.env`" gotcha, but inverted: here the variable exists and is still unusable.
|
|
608
|
+
- **"Empty" and "unset" must be distinguished on both sides.** Diagnostic evidence that mattered: the MCP children showed `DATABASE_URL: <present but EMPTY>` while the parent showed it absent. A presence-only check (`if (process.env.DATABASE_URL)`) happens to work because `''` is falsy, but a presence-based diagnostic (`Object.hasOwn`, `env | grep`) reads "present" and sends you hunting in the wrong layer. Record emptiness, not just presence, when inspecting a child's environment.
|
|
609
|
+
- **`.zshrc` exports never reach a daemon started outside an interactive shell.** `~/.zshrc` had an unconditional top-level `export DATABASE_URL=...`; `~/.zshenv` and `~/.zprofile` did not. `.zshrc` is sourced only for **interactive** shells, so `zsh -c '…'` → unset while `zsh -ic '…'` → set, and `opencode serve` launched from a non-interactive context never saw the value. **Universal pattern:** for any long-lived service/daemon that needs env vars, the correct homes are `~/.zshenv` (every zsh) or a launchd/systemd unit — never `.zshrc`. Use `zsh -c` vs `zsh -ic` as a two-line A/B test to prove whether an export is interactive-only.
|
|
610
|
+
- **The A/B shell test is the decisive evidence, because it isolates the config from the shell.** `zsh -ic 'npm run kb:stats'` → connected (4 projects, 56 chunks, last sync 2026-10-31); `zsh -c 'npm run kb:stats'` → "DATABASE_URL not set". The delta is exactly `.zshrc` sourcing, which both confirms the diagnosis and proves the config fix (post-fix, connectivity is now conditioned only on how the host was started). **Universal pattern:** when a "missing env var" bug could be in the config OR in the launching environment, run the same command under an interactive and a non-interactive shell — the difference localizes the fault without touching either layer.
|
|
611
|
+
- **One-entry-point-fixed, sibling-missed recurs again — audit the whole config for the same pattern.** The `github` server still carries `GITHUB_PERSONAL_ACCESS_TOKEN: "{env:GITHUB_PERSONAL_ACCESS_TOKEN}"` (`opencode.json:22-23`) with its export at `~/.zshrc:6`, so it is very likely failing identically today. Deliberately left out of scope to keep verification clean, and recorded as an explicit follow-up rather than silently expanded. This is now the fourth appearance of this bug class in this project (dotenv imports, threshold defaults, `registerProject`, and now `{env:…}` passthrough).
|
|
612
|
+
- **A "recommended fix" can become the defect — spikes assert state and go stale.** `docs/spike-kb-mcp-database-url-investigation.md` still claims the server has no env block and no dotenv import (both untrue today) and recommends adding the `environment` block that is now the root cause. A spike is a snapshot, not a living contract. **Universal pattern:** when a spike's recommendation is implemented and later reversed, the spike must be corrected or explicitly superseded — a stale recommendation is worse than no recommendation, because the next agent will implement it again.
|
|
613
|
+
- **Long-lived MCP children do not reload config — a restart is part of the fix, not an afterthought.** The three live children (pids 50430, 55767, 58594 under `opencode serve` 26325) kept the injected `""` after the config edit, so the tools stayed broken while the files on disk were correct. This restates the established MCP-restart rule with a new cause: here it is a *config* change, not a code change, and it fails identically.
|
|
614
|
+
- **A tracked-secret blob in history outlives the file.** Commit `d788f68` retains the previously tracked `.env` (a live-looking credential) in git history, and rotation is still outstanding. Removing the file from the tree does not remove the exposure; only rotation (and optionally a history purge) does — and rotation is a user-owned, non-automatable step.
|
|
615
|
+
|
|
616
|
+
**Agent tuning notes:**
|
|
617
|
+
- **coder (investigation)**: For "can process A see variable X?" questions, gather three independent evidence layers before theorizing — (1) the tool's own reported behavior, (2) the config file that wires the env, (3) the actual child-process environment. Read the *loader's* behavior in `node_modules` when a fallback is claimed to work; the dotenv no-overwrite rule was verified at source rather than assumed. Keep such investigations strictly read-only so the diagnosis can be reviewed before any change.
|
|
618
|
+
- **coder (fix)**: Fix at the config layer, not by teaching the app to treat `""` as absent — the latter hides a defect class that affects every `{env:…}` entry. Validation for a JSON config edit should be concrete and cheap: `JSON.parse`, the entry's key set, all servers still present, and the removed key genuinely gone.
|
|
619
|
+
- **reviewer / future auditors**: When reviewing MCP config, treat every `{env:…}` passthrough as a suspect, not a best practice — ask "what happens when the parent lacks this?" before approving. Check `environment` blocks against where the variable is actually exported (`.zshrc` vs `.zshenv`).
|
|
620
|
+
- **tracker**: Record the durable rule (`"environment": { "X": "{env:X}" }` injects `""` when the parent lacks `X`; omit the block) as a config-level universal pattern, and pair it with the `.zshrc`-is-interactive-only fact — together they explain a failure mode that looks like an app bug, a dotenv bug, and a shell bug simultaneously. Never record the secret value, a prefix, or a full connection string; scheme/port/database are sufficient to make the record useful.
|
|
621
|
+
- **all agents (knowledge retrieval)**: `memory_search` executed — its top hit was the stale spike document itself, which independently corroborated the "spike is stale" finding. `knowledgebase_search` **NOT executed** (PG vector unavailable — the live MCP child still holds the injected empty string, so this very bug made the cross-project layer unreachable). Neither retried nor treated as a task failure.
|
|
622
|
+
|
|
623
|
+
## Session: 2026-11-01 — `github` MCP `{env:…}` Removal + Service Restart Topology (verify the premise)
|
|
624
|
+
|
|
625
|
+
**Pipeline:** coder (config fix + live process probe) → tracker
|
|
626
|
+
**Coverage:** N/A (one config-block deletion in `opencode.json`; no test or `scripts/` surface changed, so the Vitest suite is unaffected)
|
|
627
|
+
**TDD Iterations:** 0 (support-driven bug fix, not TDD)
|
|
628
|
+
|
|
629
|
+
**New knowledge:**
|
|
630
|
+
- **Verify the premise before fixing — and before REPORTING — a latent defect.** Pipeline 16 predicted the `github` server was "likely failing identically" because it carried the same `environment: { GITHUB_PERSONAL_ACCESS_TOKEN: "{env:…}" }` block. Live process probing of the parent `opencode serve` process showed the variable **was present** (non-empty, length matching the shell-exported value, so the child inherited a real value) and that the server was connecting successfully. The `{env:X}` pattern only degrades to an injected `""` when the parent **lacks** `X`; where the parent has it, the block is a redundant no-op. Removing it was still correct — it eliminates the trap — but the defect was **latent, not active**, and the earlier "likely failing" prediction has been explicitly superseded in the record. **Universal pattern:** a config pattern that is *capable* of failing is not evidence that it *is* failing. Before reporting a defect as active, prove the failing precondition (here: probe the actual parent environment) — and when a later probe contradicts an earlier prediction, supersede it in writing rather than leaving a misleading claim standing.
|
|
631
|
+
- **OpenCode service restart topology: `opencode serve --service` owns the config, the MCP spawning, and the environment.** The relevant chain was `-bash` → `opencode` client → `opencode serve --service` (its own process group, listening on a loopback port), which spawned the MCP children; several additional clients were attached to that same service. There is **no launchd job** — the service is started on demand by the **first** client and inherits **that** client's environment, then keeps it for the lifetime of the process. Durable consequences: (1) quitting the TUI, or any individual client, changes nothing — the service survives every client; (2) `opencode reload` re-reads configuration but **cannot change a running process's environment**, so it does not fix a missing env var; (3) a restart must originate from a process whose environment is already correct — `opencode service restart` from a fresh interactive shell, or better, move the exports to `~/.zshenv` so any on-demand start inherits them. **Universal pattern:** for any on-demand-supervised service, "restart the client" is not "restart the environment holder" — identify which process actually owns config + env, confirm how it was started (launcher/supervisor vs first-client inheritance), and restart *that* from a correctly-populated shell.
|
|
632
|
+
- **A redundant `{env:…}` passthrough is only harmless while the parent has the value — that is exactly why it survives undetected.** The `knowledgebase` entry in the same file was *actually* broken (its variable was genuinely absent from the parent) while the `github` entry was *quietly* fine. Two entries, one identical pattern, opposite outcomes. **Universal pattern:** identical code/config defects do not have identical activations; a "same defect class, same failure" assumption will be wrong whenever the preconditions differ — check each instance's precondition separately.
|
|
633
|
+
- **`headers` interpolation on a REMOTE MCP server is a different mechanism and is not the same defect.** The one `{env:…}` left in `opencode.json` is `context7` → `headers.CONTEXT7_API_KEY: "{env:CONTEXT7_TOKEN}"`. A remote server has no parent-environment inheritance and no `dotenv` fallback; request headers are its authentication path. A blanket "every `{env:…}` is suspect" sweep would have wrongly removed it. **Universal pattern:** distinguish *child-process environment passthrough* (local servers — omit the block) from *request-header interpolation* (remote servers — keep it); the defect class is scoped to the former.
|
|
634
|
+
- **The consumer scaffold is where this defect class ships.** `opencode.mcp.example.json` still carries the same pattern for `github`, `playwright`, and `playwright-firefox`. The local fix corrects one repo; the scaffold propagates the trap to every adopting project. **Universal pattern:** when a defect is fixed in a live config, grep the shipped/example/scaffold configs for the same pattern — fixing the instance without fixing the distribution leaves the defect in the product.
|
|
635
|
+
- **A diagnostic echo can put a live token in a session transcript.** One echo during this pipeline expanded a token variable into the transcript; it reached no file and no summary. **Universal pattern:** when probing a secret to prove presence, print presence/length only (`[ -n "$VAR" ]`, `${#VAR}`), never the expanded value — and if it does leak into a transcript, record the fact and recommend rotation rather than reproducing it in the permanent record.
|
|
636
|
+
|
|
637
|
+
**Agent tuning notes:**
|
|
638
|
+
- **coder (config fix)**: Confirm the entry's full key set after every edit (`type`, `command`, `enabled`), re-validate the JSON, and assert zero `environment` keys remain in the block plus all servers still present. When a sibling entry is fixed for the same pattern, do not assume this one is broken in the same way — probe the parent environment for the specific variable first and report latent vs active explicitly.
|
|
639
|
+
- **coder (process probing)**: Never print a secret value; use presence tests and length only, and note that an older/stale child process may carry a different value than the current service (stale per-session MCP children coexist with fresh ones — probe the service, not a random child).
|
|
640
|
+
- **reviewer / future auditors**: Ask "does the parent actually lack this variable?" before accepting a `{env:…}` entry as an active bug; accept config changes that remove a latent trap, but require the record to say latent rather than active. Distinguish remote-header interpolation from local-env passthrough, and check the example/scaffold config separately from the live one.
|
|
641
|
+
- **tracker**: Record the corrected assumption as a **supersede** of the earlier prediction (name the earlier pipeline and the claim) so the log does not carry a contradiction; keep the restart topology as durable operational knowledge, not a one-off note; and for any transcript exposure record only the fact plus the rotation recommendation — never the value, prefix, fragment, or connection detail.
|
|
642
|
+
- **all agents (knowledge retrieval)**: `memory_search` executed (its top hits surfaced the companion Pipeline 16 entries for both the `knowledgebase` and `github` fixes). `knowledgebase_search` **NOT executed** (PG vector unavailable — `DATABASE_URL` is still absent from the parent service until it is restarted). Neither retried nor treated as a task failure.
|
|
@@ -57,7 +57,7 @@ Docs files do not require `category`, `entities`, or strict heading structure. H
|
|
|
57
57
|
|
|
58
58
|
## Auto-Suggest on Normalization
|
|
59
59
|
|
|
60
|
-
When `scripts/normalize-memory.js` runs
|
|
60
|
+
When `scripts/normalize-memory.js` runs — via `npx ai-workflow-setup`, `npm run memory:normalize`, or directly — it:
|
|
61
61
|
- Adds missing frontmatter using filename, H1 title, and git modification date
|
|
62
62
|
- Auto-suggests `tags` by scanning content for vocabulary keywords
|
|
63
63
|
- Auto-suggests `entities` using regex patterns from `.vocabulary.json`
|
|
@@ -1,16 +1,52 @@
|
|
|
1
|
+
# Publishes @abarcenas/ai-workflow-template to npm when work lands on main.
|
|
2
|
+
#
|
|
3
|
+
# - Trigger: push to main (i.e. a merged PR), plus a manual workflow_dispatch for
|
|
4
|
+
# retrying a run that failed after the merge. Tags play no part in publishing.
|
|
5
|
+
# - Version: read from package.json. Nothing in CI derives it from a git ref.
|
|
6
|
+
# - Bumping: handled locally by the Husky pre-commit hook (scripts/bump-version.js),
|
|
7
|
+
# which bumps the minor version on every commit. Whatever version is on main when
|
|
8
|
+
# the PR merges is the version that gets published.
|
|
9
|
+
# - Safety: a preflight step hard-fails if that exact version is already on the
|
|
10
|
+
# registry, so a duplicate merge fails loudly instead of silently no-op'ing.
|
|
11
|
+
|
|
1
12
|
name: Publish to npm
|
|
2
13
|
|
|
3
14
|
on:
|
|
4
15
|
push:
|
|
5
16
|
branches:
|
|
6
17
|
- main
|
|
18
|
+
workflow_dispatch:
|
|
19
|
+
|
|
20
|
+
permissions:
|
|
21
|
+
contents: read
|
|
22
|
+
id-token: write
|
|
23
|
+
|
|
24
|
+
# Serialize publishes: two quick merges must not both clear the preflight guard
|
|
25
|
+
# and race each other at the registry. Never cancel — an in-flight publish should
|
|
26
|
+
# be allowed to finish rather than be killed mid-upload.
|
|
27
|
+
concurrency:
|
|
28
|
+
group: npm-publish-main
|
|
29
|
+
cancel-in-progress: false
|
|
7
30
|
|
|
8
31
|
jobs:
|
|
9
32
|
publish:
|
|
33
|
+
# This file is distributed to consumers, who may adapt it for their own
|
|
34
|
+
# package. Keep it inert anywhere but the upstream repo.
|
|
35
|
+
if: github.repository == 'abarcenas29/ai-workflow-template'
|
|
10
36
|
runs-on: ubuntu-latest
|
|
37
|
+
timeout-minutes: 20
|
|
11
38
|
steps:
|
|
12
39
|
- uses: actions/checkout@v4
|
|
13
40
|
|
|
41
|
+
# A manual run may be started against any ref, and it would publish that
|
|
42
|
+
# ref's tree with the real NPM_TOKEN and a provenance attestation. Only
|
|
43
|
+
# main is releasable.
|
|
44
|
+
- name: Require main for manual runs
|
|
45
|
+
if: github.event_name == 'workflow_dispatch' && github.ref != 'refs/heads/main'
|
|
46
|
+
run: |
|
|
47
|
+
echo "::error::Manual publishes must run against refs/heads/main (got ${GITHUB_REF})."
|
|
48
|
+
exit 1
|
|
49
|
+
|
|
14
50
|
- uses: actions/setup-node@v4
|
|
15
51
|
with:
|
|
16
52
|
node-version: '22'
|
|
@@ -32,7 +68,39 @@ jobs:
|
|
|
32
68
|
# The config defaults to headed mode; CI runners have no display.
|
|
33
69
|
HEADLESS: 'true'
|
|
34
70
|
|
|
71
|
+
# Refuse to publish a version that already exists. Without this, merging a PR
|
|
72
|
+
# that forgot to bump package.json fails deep inside `npm publish` with an
|
|
73
|
+
# opaque 403, or (worse) is retried by hand against the wrong version.
|
|
74
|
+
- name: Ensure version is unpublished
|
|
75
|
+
run: |
|
|
76
|
+
NAME="$(node -p "require('./package.json').name")"
|
|
77
|
+
VERSION="$(node -p "require('./package.json').version")"
|
|
78
|
+
|
|
79
|
+
# The version must be a plain X.Y.Z: no ranges, no pre-release tags.
|
|
80
|
+
case "$VERSION" in
|
|
81
|
+
*[!0-9.]*|*..*|.*|*.) echo "::error::package.json version '${VERSION}' is not a plain X.Y.Z"; exit 1 ;;
|
|
82
|
+
esac
|
|
83
|
+
|
|
84
|
+
echo "Checking ${NAME}@${VERSION} on the registry..."
|
|
85
|
+
# One deterministic read of the published version list. Its exit status
|
|
86
|
+
# — not the text of an error message — decides whether the registry
|
|
87
|
+
# answered, so an auth/network/registry failure fails closed.
|
|
88
|
+
VERSIONS="$(npm view "${NAME}" versions --json)" || {
|
|
89
|
+
echo "::error::Registry read failed for ${NAME}. Refusing to publish."
|
|
90
|
+
exit 1
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
# `-F` keeps the dots in the version literal instead of regex wildcards,
|
|
94
|
+
# and the surrounding quotes anchor the match to a whole JSON string, so
|
|
95
|
+
# "2.0.0" cannot match inside "2.0.0-rc.1" or "12.0.0".
|
|
96
|
+
if printf '%s' "$VERSIONS" | grep -qF "\"${VERSION}\""; then
|
|
97
|
+
echo "::error::Version ${VERSION} is already published to npm. Bump package.json before merging to main."
|
|
98
|
+
exit 1
|
|
99
|
+
fi
|
|
100
|
+
|
|
101
|
+
echo "Version ${VERSION} is unpublished — proceeding."
|
|
102
|
+
|
|
35
103
|
- name: Publish to npm
|
|
36
|
-
run: npm publish --access public
|
|
104
|
+
run: npm publish --access public --provenance
|
|
37
105
|
env:
|
|
38
106
|
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// graphify OpenCode plugin
|
|
2
|
+
// Injects a knowledge graph reminder before bash tool calls when the graph exists.
|
|
3
|
+
import { existsSync } from "fs";
|
|
4
|
+
import { join } from "path";
|
|
5
|
+
|
|
6
|
+
export const GraphifyPlugin = async ({ directory }) => {
|
|
7
|
+
let reminded = false;
|
|
8
|
+
|
|
9
|
+
return {
|
|
10
|
+
"tool.execute.before": async (input, output) => {
|
|
11
|
+
if (reminded) return;
|
|
12
|
+
if (!existsSync(join(directory, "graphify-out", "graph.json"))) return;
|
|
13
|
+
|
|
14
|
+
if (input.tool === "bash") {
|
|
15
|
+
output.args.command =
|
|
16
|
+
'echo "[graphify] knowledge graph at graphify-out/. For focused questions, run \`graphify query \"<question>\"\` (scoped subgraph, usually much smaller than GRAPH_REPORT.md) instead of grepping raw files. Read GRAPH_REPORT.md only for broad architecture context." && ' +
|
|
17
|
+
output.args.command;
|
|
18
|
+
reminded = true;
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
};
|
package/LICENSE
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 abarcenas29
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
10
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
11
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
12
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
13
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
14
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
15
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,22 +1,29 @@
|
|
|
1
1
|
# AI Workflow Template
|
|
2
2
|
|
|
3
|
-
Publishable AI workflow starter pack for OpenCode —
|
|
3
|
+
Publishable AI workflow starter pack for OpenCode — explicitly-synced, version-managed, npm-distributed agent configuration suite with vector search memory.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npm install @abarcenas/ai-workflow-template
|
|
8
|
+
npm install --save-dev @abarcenas/ai-workflow-template
|
|
9
|
+
npx ai-workflow-setup
|
|
9
10
|
```
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
This package is a **CLI/scaffold, not a runtime library** — it has no `require`/`import` entry point, so importing it is not supported by design. Installing it copies nothing into your project and ships no install/`postinstall` hook; `npx ai-workflow-setup` is the only setup path and does all file syncing, memory-bank scaffolding, and hook installation.
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+
### Upgrading to 2.0.0
|
|
15
|
+
|
|
16
|
+
2.0.0 removed the install-time `postinstall` sync, so upgrading no longer copies anything into your project. After installing the new version, run the setup command once:
|
|
14
17
|
|
|
15
18
|
```bash
|
|
16
|
-
|
|
17
|
-
|
|
19
|
+
npx ai-workflow-setup
|
|
20
|
+
```
|
|
18
21
|
|
|
19
|
-
|
|
22
|
+
Node 20 support was also dropped (`engines.node` is now `>=22`), and the package no longer declares a `main` entry point — it is a scaffold CLI, not an importable library.
|
|
23
|
+
|
|
24
|
+
## Git Hook Setup
|
|
25
|
+
|
|
26
|
+
```bash
|
|
20
27
|
npx ai-workflow-setup
|
|
21
28
|
```
|
|
22
29
|
|
|
@@ -29,11 +36,13 @@ npx ai-workflow-setup
|
|
|
29
36
|
| Prepare | Merges `"husky"` into your `package.json` prepare script |
|
|
30
37
|
| Husky Init | Initializes husky and sets `core.hooksPath` |
|
|
31
38
|
| Sync | Copies agent configurations, skills, and memory bank scaffolding |
|
|
39
|
+
| Skills | Refreshes pinned skills from upstream (skipped with `--skip-sync`) |
|
|
40
|
+
| Knowledgebase | Registers the project with the centralized knowledgebase (skipped with `--skip-knowledgebase`) |
|
|
32
41
|
|
|
33
42
|
### Options
|
|
34
43
|
|
|
35
44
|
```bash
|
|
36
|
-
npx ai-workflow-setup --dry-run # Preview
|
|
45
|
+
npx ai-workflow-setup --dry-run # Preview hooks/prepare/skills, skip sync
|
|
37
46
|
npx ai-workflow-setup --force # Overwrite existing hooks
|
|
38
47
|
npx ai-workflow-setup --skip-hooks # Skip hook installation
|
|
39
48
|
npx ai-workflow-setup --skip-sync # Skip file sync
|
|
@@ -48,7 +57,7 @@ npx ai-workflow-setup --help # Show all options
|
|
|
48
57
|
- **Complex hooks** trigger a warning with manual merge instructions
|
|
49
58
|
- **CI environments** auto-skip hook configuration
|
|
50
59
|
|
|
51
|
-
> **
|
|
60
|
+
> **No install-time scripts:** npm 12+, pnpm 10+, and Yarn 4 block dependency lifecycle scripts by default. This package ships no `postinstall`; everything runs when you explicitly invoke `npx ai-workflow-setup`.
|
|
52
61
|
|
|
53
62
|
## What This Package Does
|
|
54
63
|
|
|
@@ -59,10 +68,12 @@ npx ai-workflow-setup --help # Show all options
|
|
|
59
68
|
- **Memory bank** — persistent project context with sqlite-vec semantic search
|
|
60
69
|
- **Caveman mode** — ultra-compressed communication for token efficiency
|
|
61
70
|
- **Playwright + Vitest testing** — E2E browser tests, unit tests with v8 coverage (≥90%)
|
|
62
|
-
- **Version auto-bumping + auto-publishing** — Husky-managed
|
|
71
|
+
- **Version auto-bumping + auto-publishing** — Husky-managed; npm publish on merge to `main`
|
|
63
72
|
- **graphify knowledge graph** — queryable code-level relationship mapping
|
|
64
73
|
|
|
65
|
-
##
|
|
74
|
+
## Maintainer sync (in this repo)
|
|
75
|
+
|
|
76
|
+
`npm run sync` is a maintainer command defined in this repository's own `package.json`. It is not shipped to consumers — consumer projects sync by running `npx ai-workflow-setup`, which invokes the same sync engine.
|
|
66
77
|
|
|
67
78
|
```bash
|
|
68
79
|
npm run sync -- --dry-run # Preview changes without applying
|
|
@@ -78,7 +89,7 @@ npm run sync # Normal sync
|
|
|
78
89
|
- Project-owned files are never overwritten or pruned after creation, and `--force` cannot replace them
|
|
79
90
|
- GitHub Actions workflows are excluded from consumer sync (`.github/workflows/`)
|
|
80
91
|
|
|
81
|
-
>
|
|
92
|
+
> `npx ai-workflow-setup --dry-run` previews the hooks, prepare, and skills phases but skips sync, so no files are copied or pruned and no scaffold files are created. For a preview of sync itself, run `npm run sync -- --dry-run` in this repository; consumers sync with `npx ai-workflow-setup`.
|
|
82
93
|
|
|
83
94
|
### Project-owned files
|
|
84
95
|
|
|
@@ -107,11 +118,18 @@ Post-install, your project receives this structure:
|
|
|
107
118
|
├── instructions/ # Framework, workflow, and security guidance
|
|
108
119
|
├── skills/ # 34 specialized AI skills
|
|
109
120
|
└── prompts/ # Reusable prompt templates
|
|
110
|
-
.opencode/
|
|
111
|
-
|
|
112
|
-
|
|
121
|
+
.opencode/
|
|
122
|
+
├── agents/ # Orchestrator + specialized sub-agents
|
|
123
|
+
├── commands/ # Slash commands
|
|
124
|
+
└── plugins/ # graphify knowledge-graph plugin
|
|
125
|
+
memory-bank/ # Persistent project context (+ tasks/)
|
|
126
|
+
scripts/ # Sync, memory, and knowledgebase CLI helpers
|
|
113
127
|
AGENTS.md # Master instruction file with agent guidance
|
|
114
128
|
AGENTS.project.md # Project-owned rules (recreated if deleted, never overwritten)
|
|
129
|
+
opencode.json # OpenCode config (project-owned after creation)
|
|
130
|
+
.prettierignore # Formatter coverage for managed files (project-owned)
|
|
131
|
+
skills-lock.json # Pinned skill versions
|
|
132
|
+
.github/workflows/ # Repo-only — never synced to consumers
|
|
115
133
|
```
|
|
116
134
|
|
|
117
135
|
## Multi-Agent Orchestrator
|
|
@@ -149,7 +167,7 @@ Invoke the orchestrator with `@orchestrator` followed by your task:
|
|
|
149
167
|
|
|
150
168
|
### MCP Tooling
|
|
151
169
|
|
|
152
|
-
Some agents require MCP servers configured in `opencode.mcp
|
|
170
|
+
Some agents require MCP servers configured in `opencode.json` (template: `opencode.mcp.example.json`):
|
|
153
171
|
|
|
154
172
|
| Agent | Required MCP | Purpose |
|
|
155
173
|
| --- | --- | --- |
|
|
@@ -158,6 +176,9 @@ Some agents require MCP servers configured in `opencode.mcp`:
|
|
|
158
176
|
| Reviewer | GitHub | PR and issue management |
|
|
159
177
|
| Deployer | GitHub | Releases, Actions |
|
|
160
178
|
|
|
179
|
+
Playwright MCP is configured twice for browser parity: `playwright` (Google Chrome — the `chrome` channel, which `@playwright/mcp` uses by default, so Google Chrome must be installed) and `playwright-firefox` (Firefox, `--browser firefox --isolated`). Each server needs its own permission entry (`playwright_*`, `playwright-firefox_*`), and Firefox must be installed first with `npx playwright install firefox` — see [`docs/playwright-mcp-configuration.md`](https://github.com/abarcenas29/ai-workflow-template/blob/main/docs/playwright-mcp-configuration.md#browser-selection). Existing `opencode.json` files are never rewritten by sync, so upgrading projects must add the `playwright-firefox` block and its permission by hand.
|
|
180
|
+
|
|
181
|
+
|
|
161
182
|
## TDD Orchestrator
|
|
162
183
|
|
|
163
184
|
Strict Test-Driven Development through the RED → GREEN → REFACTOR/VERIFY cycle:
|
|
@@ -219,7 +240,7 @@ projectbrief.md → productContext.md / systemPatterns.md / techContext.md → a
|
|
|
219
240
|
|
|
220
241
|
### Document Schema
|
|
221
242
|
|
|
222
|
-
All memory-bank files use YAML frontmatter (`id`, `title`, `updated`, `tags`, `entities`, `category`) for search indexing; see `memory-bank/.vocabulary.json` for the controlled vocabulary. `
|
|
243
|
+
All memory-bank files use YAML frontmatter (`id`, `title`, `updated`, `tags`, `entities`, `category`) for search indexing; see `memory-bank/.vocabulary.json` for the controlled vocabulary. `npx ai-workflow-setup` runs `normalize-memory.js` to add frontmatter; the Husky pre-commit hook rejects invalid frontmatter. Task commands: `add task`, `update task [ID]`, `show tasks [filter]`.
|
|
223
244
|
|
|
224
245
|
### Token Efficiency
|
|
225
246
|
|
|
@@ -258,7 +279,20 @@ Husky auto-bumps versions on every commit: minor by default (`1.2.0 → 1.3.0`),
|
|
|
258
279
|
|
|
259
280
|
## Publish Flow
|
|
260
281
|
|
|
261
|
-
GitHub Actions
|
|
282
|
+
GitHub Actions publishes to npm when work lands on `main` — that is, when a PR is merged. The version published is whatever `package.json` says at merge time (the Husky pre-commit hook already bumped the minor version):
|
|
283
|
+
|
|
284
|
+
```bash
|
|
285
|
+
git push origin feat/my-change
|
|
286
|
+
gh pr merge --merge # merging to main publishes that version
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
A version already on the registry fails the run instead of republishing, so bump `package.json` rather than re-running against the same version. To retry a failed publish, dispatch against `main` — there are no inputs:
|
|
290
|
+
|
|
291
|
+
```bash
|
|
292
|
+
gh workflow run npm-publish.yml --ref main
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
Pushing a `v*` tag does not publish anything.
|
|
262
296
|
|
|
263
297
|
Add `NPM_TOKEN` to GitHub repository secrets:
|
|
264
298
|
|
|
@@ -283,7 +317,7 @@ npm run test:unit:watch # Watch mode for unit tests
|
|
|
283
317
|
npm run memory:rebuild # Build the vector index (one-time, ~8 min)
|
|
284
318
|
npm run memory:update # Incremental index update (seconds)
|
|
285
319
|
npm run memory:search "tdd orchestrator status"
|
|
286
|
-
npm run memory:normalize # Normalize frontmatter (runs
|
|
320
|
+
npm run memory:normalize # Normalize frontmatter (also runs during npx ai-workflow-setup)
|
|
287
321
|
```
|
|
288
322
|
|
|
289
323
|
### Testing
|
|
@@ -301,8 +335,8 @@ npm run memory:normalize # Normalize frontmatter (runs automatically on instal
|
|
|
301
335
|
1. Edit files in `.agents/`, `docs/`, or `scripts/`
|
|
302
336
|
2. Test changes locally
|
|
303
337
|
3. Commit (triggers auto-version bump)
|
|
304
|
-
4. Merge to `main` (triggers npm publish)
|
|
305
|
-
5. Consumers run `
|
|
338
|
+
4. Merge the PR to `main` (triggers npm publish)
|
|
339
|
+
5. Consumers install the new version, then re-run `npx ai-workflow-setup` to sync
|
|
306
340
|
|
|
307
341
|
## Important Notes
|
|
308
342
|
|
|
@@ -312,5 +346,5 @@ npm run memory:normalize # Normalize frontmatter (runs automatically on instal
|
|
|
312
346
|
- Do not add `AGENTS.project.md` or `opencode.json` to `package.json` `files`: both are scaffolded create-if-absent and must never ship from the package
|
|
313
347
|
- `AGENTS.md` is the main instruction file agents should read
|
|
314
348
|
- Package assumes the public npm registry
|
|
315
|
-
- Playwright MCP integration is documented in `docs
|
|
349
|
+
- Playwright MCP integration is documented in [`docs/playwright-mcp-configuration.md`](https://github.com/abarcenas29/ai-workflow-template/blob/main/docs/playwright-mcp-configuration.md)
|
|
316
350
|
- Run `npx ai-workflow-setup` after install to configure git hooks (npm v12+ compatible)
|
|
@@ -56,6 +56,7 @@
|
|
|
56
56
|
"dotenv",
|
|
57
57
|
"embeddings",
|
|
58
58
|
"env-remediation",
|
|
59
|
+
"env-var-shadowing",
|
|
59
60
|
"feature-vocab-sync",
|
|
60
61
|
"fix-kb-registerproject",
|
|
61
62
|
"float32array",
|
|
@@ -65,11 +66,13 @@
|
|
|
65
66
|
"knowledgebase",
|
|
66
67
|
"learned-knowledge",
|
|
67
68
|
"mcp",
|
|
69
|
+
"mcp-config",
|
|
68
70
|
"package-structure",
|
|
69
71
|
"pgvector",
|
|
70
72
|
"pipeline-follow-up",
|
|
71
73
|
"pre-commit",
|
|
72
74
|
"readme",
|
|
75
|
+
"restart",
|
|
73
76
|
"security",
|
|
74
77
|
"test-hardening",
|
|
75
78
|
"vocabulary"
|
|
@@ -44,6 +44,18 @@
|
|
|
44
44
|
"timeout": 120000,
|
|
45
45
|
"enabled": true
|
|
46
46
|
},
|
|
47
|
+
"playwright-firefox": {
|
|
48
|
+
"type": "local",
|
|
49
|
+
"command": ["node", "scripts/mcp/playwright-mcp-launcher.js"],
|
|
50
|
+
"environment": {
|
|
51
|
+
"BROWSER": "firefox",
|
|
52
|
+
"ISOLATED": "true",
|
|
53
|
+
"HEADLESS": "{env:HEADLESS}",
|
|
54
|
+
"VIEWPORT": "{env:VIEWPORT}"
|
|
55
|
+
},
|
|
56
|
+
"timeout": 120000,
|
|
57
|
+
"enabled": true
|
|
58
|
+
},
|
|
47
59
|
"example-server": {
|
|
48
60
|
"type": "local",
|
|
49
61
|
"command": ["npx", "-y", "your-mcp-package"],
|
|
@@ -66,6 +78,7 @@
|
|
|
66
78
|
"mcp-everything_*": "allow",
|
|
67
79
|
"angular-cli_*": "allow",
|
|
68
80
|
"playwright_*": "allow",
|
|
81
|
+
"playwright-firefox_*": "allow",
|
|
69
82
|
"context7_*": "allow",
|
|
70
83
|
"memory-bank_*": "allow"
|
|
71
84
|
}
|
package/package.json
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@abarcenas/ai-workflow-template",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
5
|
-
"main": "index.js",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "OpenCode scaffolding CLI for AI-assisted development workflows — syncs agent configurations, skills, memory bank, and reusable prompts into any project.",
|
|
6
5
|
"bin": {
|
|
7
6
|
"ai-workflow-setup": "./bin/setup.js"
|
|
8
7
|
},
|
|
@@ -17,7 +16,6 @@
|
|
|
17
16
|
"test:unit:watch": "vitest",
|
|
18
17
|
"prepare": "husky",
|
|
19
18
|
"sync": "node ./scripts/sync.js",
|
|
20
|
-
"postinstall": "node ./scripts/sync.js && node ./scripts/normalize-memory.js && echo '' && echo '⚠️ IMPORTANT: Run npx ai-workflow-setup to configure git hooks' && echo ' (npm postinstall scripts will be blocked by npm v12+)'",
|
|
21
19
|
"memory:search": "node ./scripts/memory-cli.js search",
|
|
22
20
|
"memory:rebuild": "node ./scripts/memory-cli.js rebuild",
|
|
23
21
|
"memory:update": "node ./scripts/memory-cli.js update",
|
|
@@ -30,31 +28,43 @@
|
|
|
30
28
|
"type": "git",
|
|
31
29
|
"url": "git+https://github.com/abarcenas29/ai-workflow-template.git"
|
|
32
30
|
},
|
|
33
|
-
"keywords": [
|
|
34
|
-
|
|
31
|
+
"keywords": [
|
|
32
|
+
"opencode",
|
|
33
|
+
"ai-agents",
|
|
34
|
+
"scaffold",
|
|
35
|
+
"mcp",
|
|
36
|
+
"memory-bank",
|
|
37
|
+
"developer-tools"
|
|
38
|
+
],
|
|
39
|
+
"author": "Aldrich Allen Barcenas <aldrich.barcenas@gmail.com>",
|
|
35
40
|
"license": "ISC",
|
|
36
41
|
"type": "module",
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">=22"
|
|
44
|
+
},
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"access": "public",
|
|
47
|
+
"provenance": true
|
|
48
|
+
},
|
|
37
49
|
"bugs": {
|
|
38
50
|
"url": "https://github.com/abarcenas29/ai-workflow-template/issues"
|
|
39
51
|
},
|
|
40
52
|
"homepage": "https://github.com/abarcenas29/ai-workflow-template#readme",
|
|
41
|
-
"//": "Never add AGENTS.project.md or opencode.json to files: both are scaffolded create-if-absent at the consumer root and must not ship from the package.",
|
|
42
53
|
"files": [
|
|
43
54
|
"bin/",
|
|
44
55
|
".agents/",
|
|
45
56
|
".github/workflows/",
|
|
46
57
|
"scripts/",
|
|
47
|
-
"scripts/setup/",
|
|
48
58
|
".opencode/agents/",
|
|
49
59
|
".opencode/commands/",
|
|
60
|
+
".opencode/plugins/",
|
|
50
61
|
".husky/",
|
|
51
62
|
"memory-bank/.vocabulary.json",
|
|
52
63
|
"AGENTS.md",
|
|
53
64
|
".prettierignore",
|
|
54
65
|
"opencode.mcp.example.json",
|
|
55
66
|
"skills-lock.json",
|
|
56
|
-
"
|
|
57
|
-
".husky/post-commit"
|
|
67
|
+
"LICENSE"
|
|
58
68
|
],
|
|
59
69
|
"devDependencies": {
|
|
60
70
|
"@playwright/test": "^1.59.1",
|
package/scripts/bump-version.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync } from 'fs'
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from 'fs'
|
|
2
2
|
import { execSync } from 'child_process'
|
|
3
3
|
import { resolve, dirname } from 'path'
|
|
4
4
|
import { fileURLToPath } from 'url'
|
|
5
5
|
|
|
6
6
|
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
7
7
|
const pkgPath = resolve(__dirname, '../package.json')
|
|
8
|
+
const lockPath = resolve(__dirname, '../package-lock.json')
|
|
8
9
|
|
|
9
10
|
// Opt-out: SKIP_BUMP=1 keeps the current version (e.g. when a batch of commits
|
|
10
11
|
// is one logical change set and should yield a single bump).
|
|
@@ -32,5 +33,20 @@ const newVersion =
|
|
|
32
33
|
|
|
33
34
|
pkg.version = newVersion
|
|
34
35
|
writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n')
|
|
35
|
-
|
|
36
|
+
|
|
37
|
+
// Keep the lockfile's root version in sync so `npm ci` does not report a
|
|
38
|
+
// mismatch after the hook stages the commit.
|
|
39
|
+
if (existsSync(lockPath)) {
|
|
40
|
+
const lock = JSON.parse(readFileSync(lockPath, 'utf8'))
|
|
41
|
+
lock.version = newVersion
|
|
42
|
+
if (lock.packages && lock.packages['']) {
|
|
43
|
+
lock.packages[''].version = newVersion
|
|
44
|
+
}
|
|
45
|
+
writeFileSync(lockPath, JSON.stringify(lock, null, 2) + '\n')
|
|
46
|
+
execSync('git add package.json')
|
|
47
|
+
execSync('git add package-lock.json')
|
|
48
|
+
} else {
|
|
49
|
+
execSync('git add package.json')
|
|
50
|
+
}
|
|
51
|
+
|
|
36
52
|
console.log(`Version bumped: ${current} → ${newVersion}`)
|