@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.
Files changed (35) hide show
  1. package/.agents/instructions/knowledgebase.instructions.md +2 -2
  2. package/.agents/instructions/learned-knowledge.instructions.md +47 -2
  3. package/.agents/instructions/memory-schema.instructions.md +1 -1
  4. package/.github/workflows/npm-publish.yml +69 -1
  5. package/.opencode/plugins/graphify.js +22 -0
  6. package/LICENSE +15 -0
  7. package/README.md +56 -22
  8. package/memory-bank/.vocabulary.json +3 -0
  9. package/opencode.mcp.example.json +13 -0
  10. package/package.json +20 -10
  11. package/scripts/bump-version.js +18 -2
  12. package/scripts/mcp/playwright-mcp-launcher.js +45 -2
  13. package/scripts/bump-version.test.js +0 -238
  14. package/scripts/knowledgebase-cli.test.js +0 -576
  15. package/scripts/knowledgebase-index.test.js +0 -759
  16. package/scripts/mcp/playwright-mcp-launcher.test.js +0 -217
  17. package/scripts/mcp-knowledgebase-server.test.js +0 -745
  18. package/scripts/mcp-memory-server.test.js +0 -398
  19. package/scripts/memory-cli.test.js +0 -297
  20. package/scripts/memory-index.test.js +0 -1173
  21. package/scripts/normalize-memory.test.js +0 -753
  22. package/scripts/setup/discover.test.js +0 -530
  23. package/scripts/setup/hooks.test.js +0 -722
  24. package/scripts/setup/husky-init.test.js +0 -311
  25. package/scripts/setup/index.test.js +0 -1021
  26. package/scripts/setup/knowledgebase.test.js +0 -352
  27. package/scripts/setup/prepare.test.js +0 -718
  28. package/scripts/setup/skills-phase.test.js +0 -1274
  29. package/scripts/setup/sync-phase.test.js +0 -373
  30. package/scripts/setup/ui.test.js +0 -579
  31. package/scripts/setup/utils.test.js +0 -586
  32. package/scripts/sync-unit.test.js +0 -1881
  33. package/scripts/sync.test.js +0 -472
  34. package/scripts/validate-memory-schema.test.js +0 -328
  35. 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 postinstall blocked in v12"
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 postinstall execution blocked in v12 dependency lifecycle",
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 postinstall scripts from dependencies** — this is a critical ecosystem shift that affects ALL npm library distribution. Postinstall auto-sync is no longer viable; explicit setup commands are required
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 postinstall discovery was the single most important finding that drove the architecture
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 (postinstall or manual), it:
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 — auto-syncing, version-managed, npm-distributed agent configuration suite with vector search memory.
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
- The package runs `postinstall` to sync files; for full setup including git hooks, use the setup command below.
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
- ## Git Hook Setup
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
- # One-step setup (fetches from npm registry automatically):
17
- npx @abarcenas/ai-workflow-template
19
+ npx ai-workflow-setup
20
+ ```
18
21
 
19
- # Or if the package is already installed:
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 without changes
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
- > **npm v12 note:** Future npm versions block automatic `postinstall` scripts. The explicit `npx setup` command is the recommended, future-proof approach.
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, npm publish on merge to `main`
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
- ## Sync Behavior
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
- > `--dry-run` previews copies and prunes, but missing scaffold files (memory-bank stubs, `opencode.json`, `AGENTS.project.md`, `.prettierignore`) are still created. This matches the pre-existing scaffold behaviour.
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/agents/ # Orchestrator + specialized sub-agents
111
- .github/workflows/ # CI/CD automation (excluded from sync)
112
- docs/ # Guides (e.g. playwright-mcp-configuration.md)
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. `npm install` 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]`.
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 automatically publishes to npm when merging to `main`.
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 automatically on install)
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 `npm install` to get updates
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": "1.49.0",
4
- "description": "A comprehensive template repository for AI-assisted development workflows using GitHub Copilot. This template provides a structured approach to leveraging AI assistance through pre-configured skills, prompts, agents, and coding standards.",
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
- "author": "",
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
- ".husky/post-merge",
57
- ".husky/post-commit"
67
+ "LICENSE"
58
68
  ],
59
69
  "devDependencies": {
60
70
  "@playwright/test": "^1.59.1",
@@ -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
- execSync('git add package.json')
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}`)