@refactco/refact-os 1.5.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 (61) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/LICENSE +21 -0
  3. package/README.md +162 -0
  4. package/bin/refact-os.js +154 -0
  5. package/lib/adapters.js +302 -0
  6. package/lib/company.js +76 -0
  7. package/lib/frontmatter.js +30 -0
  8. package/lib/migrate.js +116 -0
  9. package/lib/project-utils.js +179 -0
  10. package/lib/refact-config.js +324 -0
  11. package/lib/scaffold.js +329 -0
  12. package/lib/validate.js +145 -0
  13. package/package.json +46 -0
  14. package/templates/base/AGENTS.md +9 -0
  15. package/templates/base/CLAUDE.md +3 -0
  16. package/templates/base/README.md +54 -0
  17. package/templates/base/agent/AGENTS.md +60 -0
  18. package/templates/base/agent/CLAUDE.md +7 -0
  19. package/templates/base/agent/claude-hooks.json +32 -0
  20. package/templates/base/agent/hooks/claude-sync-transcript.py +236 -0
  21. package/templates/base/agent/hooks/preflight-metadata.mjs +202 -0
  22. package/templates/base/agent/hooks/send-transcript-to-remote-server.py +238 -0
  23. package/templates/base/agent/hooks/sync-chat-transcript.py +188 -0
  24. package/templates/base/agent/hooks.json +29 -0
  25. package/templates/base/agent/scripts/import-project-chat-history.py +196 -0
  26. package/templates/base/agent/scripts/sync-asana.mjs +408 -0
  27. package/templates/base/agent/skills/adopt/SKILL.md +46 -0
  28. package/templates/base/agent/skills/close-ticket/SKILL.md +31 -0
  29. package/templates/base/agent/skills/extract-learnings/SKILL.md +90 -0
  30. package/templates/base/agent/skills/git-it/SKILL.md +138 -0
  31. package/templates/base/agent/skills/import-chat-history/SKILL.md +85 -0
  32. package/templates/base/agent/skills/ingest-input/SKILL.md +43 -0
  33. package/templates/base/agent/skills/open-ticket/SKILL.md +36 -0
  34. package/templates/base/agent/skills/process-docs/SKILL.md +69 -0
  35. package/templates/base/agent/skills/project-status/SKILL.md +35 -0
  36. package/templates/base/agent/skills/project-status/scripts/scan-status.mjs +153 -0
  37. package/templates/base/agent/skills/refact/SKILL.md +139 -0
  38. package/templates/base/agent/skills/setup-project/SKILL.md +140 -0
  39. package/templates/base/agent/skills/sync-asana/SKILL.md +106 -0
  40. package/templates/base/agent/skills/update-canonical-record/SKILL.md +28 -0
  41. package/templates/base/agent/skills/update-package/SKILL.md +51 -0
  42. package/templates/base/docs/context/project.md +30 -0
  43. package/templates/base/docs/decisions.md +22 -0
  44. package/templates/base/docs/index.md +31 -0
  45. package/templates/base/docs/sources/raw/.gitkeep +0 -0
  46. package/templates/base/docs/task/.gitkeep +0 -0
  47. package/templates/base/env.example +14 -0
  48. package/templates/base/gitignore +34 -0
  49. package/templates/overlays/client/agent/skills/create-deliverable/SKILL.md +29 -0
  50. package/templates/overlays/client/docs/deliverables/.gitkeep +0 -0
  51. package/templates/overlays/code/agent/skills/add-codebase/SKILL.md +239 -0
  52. package/templates/overlays/code/agent/skills/code-development/SKILL.md +58 -0
  53. package/templates/overlays/code/agent/skills/code-development/references/gitflow.md +144 -0
  54. package/templates/overlays/nextjs/agent/skills/nextjs-dev/SKILL.md +93 -0
  55. package/templates/overlays/nextjs/agent/skills/setup-netlify-deploy/SKILL.md +143 -0
  56. package/templates/overlays/nextjs/agent/skills/setup-nextjs-app/SKILL.md +118 -0
  57. package/templates/overlays/nextjs/agent/skills/setup-vercel-deploy/SKILL.md +116 -0
  58. package/templates/overlays/wordpress/agent/skills/install-wp-skills/SKILL.md +130 -0
  59. package/templates/overlays/wordpress/agent/skills/setup-kinsta-deploy/SKILL.md +201 -0
  60. package/templates/overlays/wordpress/agent/skills/wp-env/SKILL.md +478 -0
  61. package/templates/overlays/wordpress/wp-cli.yml.example +46 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,60 @@
1
+ # Changelog
2
+
3
+ Notable changes to the refact-os standard and scaffolder.
4
+
5
+ **Staying current:** a consumer repo updates with `npm run refact:update` (or `npx github:refactco/refact-os init`). That refreshes the package-managed `agent/` payload, **prunes** skills this package has removed or renamed (safe — only skills the package previously shipped, never your own), regenerates `.cursor/` + `.claude/`, and flags `agent/AGENTS.md` drift for a manual merge. The version it last applied is recorded in `.refact-os.json` under `_scaffold.version`, and `init` prints the `old → new` transition so you can scan the entries below for anything needing manual action. Run `npm run refact:validate` afterward.
6
+
7
+ ## 1.5.0
8
+
9
+ ### Changed
10
+ - **Published to npm as the scoped public package [`@refactco/refact-os`](https://www.npmjs.com/package/@refactco/refact-os).** Install/run with `npx @refactco/refact-os init` (no more `github:` specifier required). Added the publish metadata (`license: MIT`, `repository`, `keywords`, `engines: node >=18`, `publishConfig.access: public`) and an MIT `LICENSE`.
11
+ - **Removed the `postinstall` auto-scaffold.** Scaffolding is now always explicit via `init`. A postinstall that writes to the install directory is a trust/security red flag for a public package (and breaks under `--ignore-scripts`); dropping it also removes the double-scaffold that ran on `npx … init`. `bin/postinstall.js` is gone; the package-root + `isAllowedTarget` guards live in `bin/refact-os.js`.
12
+
13
+ ## 1.4.4
14
+
15
+ ### Fixed
16
+ - **`sync` no longer deletes foreign (non-refact-os) Cursor/Claude skills.** Adapter generation used to wipe `.cursor/skills/` and `.claude/skills/` wholesale and rebuild them from `agent/skills/`, which destroyed any skill pack placed there by another tool (e.g. a third-party WordPress skills bundle with no `agent/` source). It now *reconciles* the skills dir: it mirrors each `agent/` skill, prunes only skills refact-os itself previously generated (tracked in a per-surface `.refact-os-owned.json` manifest), and leaves foreign skills untouched. Other adapter subdirs (`hooks/`, `scripts/`, `references/`) are still fully owned and replaced.
17
+
18
+ ## 1.4.3
19
+
20
+ ### Changed
21
+ - **`refact-os init` mentions now show a runnable form too.** Follow-up to 1.4.2: the remaining bare `refact-os init …` directives in the `adopt`, `setup-project`, and `refact` skills now use `npx github:refactco/refact-os init …` (bootstrap/greenfield, where nothing is installed yet) or `npx refact-os init …` (inside an already-scaffolded repo). The conceptual "don't run `refact-os init|migrate|sync`" rule in `adopt` is left as-is.
22
+
23
+ ## 1.4.2
24
+
25
+ ### Changed
26
+ - **Docs/skills now show the runnable command form.** Scaffolded `README.md`, `agent/AGENTS.md`, `agent/CLAUDE.md`, and skills (`adopt`, `update-package`, `setup-nextjs-app`) — plus `validate` remediation messages and generated-file headers — say `npm run refact:sync` / `npm run refact:validate` (and `npx refact-os sync company`) instead of the bare `refact-os …`. The bare binary isn't on a shell's PATH, so an agent pasting it got `command not found` and hand-mirrored the payload; the npm-script form runs as-is. Added a short "Tooling" note to `AGENTS.md`/`README.md` explaining the `refact-os` devDependency and the `npm install` / `npx` fallback.
27
+
28
+ ### Fixed
29
+ - **Brownfield repos can now opt into overlays.** An explicit `--overlay <name>` (e.g. `--overlay code`) is applied even on a brownfield/adopted repo — copying only the overlay's `agent/` payload (skills/hooks), never its convention/structure files (`wp-cli.yml.example`) or directories. Previously brownfield skipped overlays entirely, so an adopted repo had no clean way to add a skill-pack without `--seed`. Type-derived overlays still apply on greenfield only.
30
+
31
+ ## 1.4.1
32
+
33
+ ### Changed
34
+ - **Brownfield `init` is leaner.** It now also skips `.env.example` (a convention file, like the `docs/` seed) — an existing repo keeps its own env conventions, and `/adopt` adds what's actually needed. Greenfield still gets `.env.example`.
35
+
36
+ ## 1.4.0
37
+
38
+ ### Added
39
+ - **Default Claude permission allowlist.** Scaffolded repos now ship a shared, committed `.claude/settings.json` with `permissions.allow` for `git commit`, `git push`, and `gh pr create`, so agents don't re-prompt for the common commit/push/PR flow. It's generated from `agent/claude-hooks.json` and **merged** on every sync — the managed allow rules are unioned in, and any allow/deny/ask rules you add are preserved. Personal overrides belong in `.claude/settings.local.json` (now gitignored).
40
+ - Note: this is Claude Code only. Cursor's command allowlist (`~/.cursor/permissions.json` → `terminalAllowlist`) is **global per-user with no per-project override**, so it can't be scaffolded into a repo — set it once on your machine.
41
+ - Posture: pair this with branch protection on `main` (the agent already won't push to `main` per the `AGENTS.md` hard rule). Claude's permission allowlist is a prompt convenience, not a security boundary.
42
+
43
+ ## 1.3.0
44
+
45
+ ### Added
46
+ - **Team-sync upgrades.** Scaffold now stamps `_scaffold.version` and `_scaffold.shippedSkills` into `.refact-os.json`. On the next update, skills the package shipped before but no longer ships (removed or renamed) are pruned from the consumer's `agent/skills/`; user-authored skills are never in the manifest, so they're never touched. The CLI reports the version transition and any pruned skills.
47
+ - **Brownfield `init`.** An existing (non-empty) repo now receives only the agent layer (base skills incl. `adopt`, root pointers, config, adapters) — no `docs/` seed, no `apps/`, no type overlays imposed over your structure. Run `/adopt` to plan the transition. The decision is recorded (`_scaffold.brownfield`) and sticky; `--seed` forces the full scaffold.
48
+ - **Standard (`docs/agent-first-repo-best-practices.md`).** New sections: *Latent vs. Deterministic Work*, *Keep The Harness Narrow*, the *process-not-content* skill-reuse test, the resolver doctrine (frontmatter is the routing table), and the *diarization* (`synthesize-subject-brief`) earned pattern.
49
+ - **Enforcement.** `project-status` moved its counting into `scripts/scan-status.mjs` (run-and-interpret); the generated skill index now carries the full resolver slice (`when_not_to_use`, `next_skills`, …) and is drift-checked on the full slice; warn-level frontmatter-quality lint (missing `next_skills` key, near-empty/paragraph-length descriptions).
50
+
51
+ ### Changed
52
+ - **Project type `other` renamed to `blank`** (legacy `other` is still accepted on input and migrated to `blank`, so existing configs keep working).
53
+ - **Type resolution.** Detection is now only a *suggestion* for the prompt default — never silently committed. An explicit (`--type`) or interactive choice is authoritative and **replaces** the stack (so picking a type can downgrade away from a detected one).
54
+
55
+ ### Fixed
56
+ - `init` no longer silently commits a detected type (e.g. from a stray `wp-content/` dir) and applies its overlay before the user is asked.
57
+
58
+ ## 1.2.0 and earlier
59
+
60
+ See git history (`git log`) — versioned changelog entries start at 1.3.0.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Refact
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,162 @@
1
+ # refact-os
2
+
3
+ `refact-os` sets up a project so AI coding agents (Cursor, Claude Code) can work in it reliably. It drops in a fixed folder layout (the **substrate**) plus a set of agent **skills** (the **move set**) — all generated from one source of truth.
4
+
5
+ > The full standard is written up in [`docs/agent-first-repo-best-practices.md`](./docs/agent-first-repo-best-practices.md). Read it before changing what gets scaffolded.
6
+
7
+ You only ever edit one canonical folder, `agent/`. The tool generates the tool-specific folders (`.cursor/`, `.claude/`) from it — you never edit those by hand.
8
+
9
+ ## Get started
10
+
11
+ > refact-os is **not** on the public npm registry. You install it straight from GitHub.
12
+
13
+ Create a project and scaffold it:
14
+
15
+ ```bash
16
+ mkdir my-project && cd my-project
17
+ npx github:refactco/refact-os init
18
+ ```
19
+
20
+ Choose the project type up front (skips the prompt):
21
+
22
+ ```bash
23
+ npx github:refactco/refact-os init --type nextjs # or: wordpress, blank
24
+ npx github:refactco/refact-os init --type wordpress,nextjs # hybrid: apply both overlays
25
+ ```
26
+
27
+ `init` adapts to the repo. An **empty** repo gets the full scaffold (substrate seed + skills + overlays). An **existing** repo gets **only the agent layer** (skills, including `adopt`) so it never imposes folders over your structure — then `/adopt` plans the transition. Force the full scaffold on a non-empty repo with `--seed`. See the **Bringing an existing repo** note under [Commands](#commands) below.
28
+
29
+ ## Run it inside your project (npm)
30
+
31
+ After `init`, your `package.json` gets a `refact-os` dev-dependency and ready-made scripts. From then on you use plain **npm** — no more typing the long `npx github:…`:
32
+
33
+ ```jsonc
34
+ "scripts": {
35
+ "refact:sync": "refact-os sync", // rebuild .cursor/ + .claude/ after editing agent/
36
+ "refact:validate": "refact-os validate", // check the project follows the standard
37
+ "refact:migrate": "refact-os migrate", // move an old layout to the current one
38
+ "refact:update": "refact-os init" // pull the latest refact-os files
39
+ }
40
+ ```
41
+
42
+ Install once, then run the scripts:
43
+
44
+ ```bash
45
+ npm install
46
+ npm run refact:sync
47
+ npm run refact:update # when you want to refresh the agent/ files
48
+ ```
49
+
50
+ A normal `npm install` is **safe** — it will not overwrite your files. Refreshing only happens when you explicitly run `npm run refact:update`.
51
+
52
+ **Staying current as the standard evolves.** `npm run refact:update` (= `refact-os init`) refreshes the package-managed `agent/` payload (new + edited skills/hooks/scripts), **prunes** skills the package has since removed or renamed (only ones it previously shipped — never your own), regenerates `.cursor/` + `.claude/`, and prints the version transition (`old → new`). The version last applied is recorded in `.refact-os.json` (`_scaffold.version`); scan [CHANGELOG.md](./CHANGELOG.md) for anything needing manual action, then run `npm run refact:validate`. Your `agent/AGENTS.md` is never overwritten — if the upstream contract template changed, the update flags it so you can merge by hand.
53
+
54
+ ## Project types
55
+
56
+ | Type | Command | Extra skills it adds |
57
+ |---|---|---|
58
+ | Next.js | `init --type nextjs` | `setup-nextjs-app`, `nextjs-dev`, `setup-vercel-deploy`, `setup-netlify-deploy` |
59
+ | WordPress | `init --type wordpress` | `wp-env`, `install-wp-skills`, `setup-kinsta-deploy` |
60
+ | Both (hybrid) | `init --type wordpress,nextjs` | both overlay skill sets |
61
+ | Blank | `init --type blank` | base skills only — the catch-all for repos that start small and grow |
62
+
63
+ Each runnable app lives in its own folder under `apps/` (e.g. `apps/web/`, `apps/wordpress/`). After scaffolding, ask the agent for the flow you need:
64
+
65
+ ```txt
66
+ /refact setup nextjs app
67
+ /refact wp-env setup
68
+ /refact setup vercel deploy
69
+ ```
70
+
71
+ The detailed steps live in the overlay skills, so you don't have to remember them.
72
+
73
+ ## Commands
74
+
75
+ ```bash
76
+ refact-os init [--target <dir>] [--type <types>] [--overlay <name>] [--no-force]
77
+ refact-os sync [--target <dir>] # regenerate .cursor/ + .claude/ from agent/
78
+ refact-os sync company [--target <dir>] # pull company context into docs/context/company/
79
+ refact-os validate [--target <dir>] # check structure + skills against the spec
80
+ refact-os migrate [--target <dir>] # move an older layout into the current one
81
+ ```
82
+
83
+ - **`init`** — scaffold a project. An **empty** repo gets the thin seed + base `agent/` skills + type overlays + adapters; an **existing** repo gets only the agent layer (then run `/adopt`). `--seed` forces the full scaffold on a non-empty repo; `--no-force` never overwrites existing files.
84
+ - **`sync`** — after editing `agent/`, regenerate `.cursor/` and `.claude/`. Never edit those by hand.
85
+ - **`validate`** — checks the required folders/files exist, every skill declares its resolver frontmatter (the fields the agent routes on — `name`, `pattern`, `description`, `when_to_use`, `when_not_to_use`, `next_skills`), the generated skill index isn't stale, and the adapters are in sync.
86
+ - **`migrate`** — moves an old-layout repo into the current one and tops up the `package.json` scripts. Safe: it never overwrites an existing file, and merges old chat folders into the new location.
87
+ - **`sync company`** — pulls Refact's shared company context (brand, team, pitch) from `refactco/refact-operation` into `docs/context/company/`. Re-syncable, so it never drifts permanently. Source + SHA are recorded in `.refact-os.json`.
88
+
89
+ **Bringing an existing repo to the standard:** just run `npx github:refactco/refact-os init`. It detects that the repo isn't empty and adds **only the agent layer** — the skills (including `adopt`), root pointers, config, and adapters — *without* dropping a `docs/` seed or `apps/` over your existing structure, and without applying type overlays. Then run the **`adopt`** skill (`/adopt`): it surveys the repo, writes a phased transition plan to `docs/task/open/`, and reconciles your structure with the standard — asking before anything ambiguous — until `refact-os validate` is green. (Pass `--seed` if you actually want the full greenfield scaffold on a non-empty repo.)
90
+
91
+ ## What gets generated
92
+
93
+ ```
94
+ <project-root>/
95
+ ├── AGENTS.md CLAUDE.md ← thin pointers to agent/
96
+ ├── README.md package.json .env.example .gitignore .refact-os.json
97
+ ├── agent/ ← CANONICAL (the source of truth — you edit here)
98
+ │ ├── AGENTS.md ← the engagement contract (kept if it already exists)
99
+ │ ├── CLAUDE.md
100
+ │ ├── hooks.json hooks/ scripts/ references/
101
+ │ ├── agent-surface-map.json ← generated map of canonical → adapters
102
+ │ └── skills/ ← the skill catalog (ingest-input, open-ticket, …)
103
+ ├── docs/ ← your working docs
104
+ │ ├── index.md decisions.md
105
+ │ ├── sources/raw/{email,call-transcripts,agent-transcripts}/ ← inbound material
106
+ │ ├── context/{project,people,open-decisions,learnings}.md ← curated knowledge
107
+ │ ├── task/{open,closed}/ ← work tracking
108
+ │ └── deliverables/ ← finished output
109
+ ├── apps/ ← your product code (apps/web, apps/wordpress, …)
110
+ ├── .cursor/ ← GENERATED from agent/ (do not edit)
111
+ └── .claude/ ← GENERATED from agent/ (do not edit)
112
+ ```
113
+
114
+ `agent/` is the source of truth. `.cursor/` and `.claude/` are throwaway copies — change `agent/` and run `npm run refact:sync`.
115
+
116
+ The generated `.claude/settings.json` ships a shared, team-wide **permission allowlist** (`git commit`, `git push`, `gh pr create`) so agents don't re-prompt for the common commit/push/PR flow — edit the source in `agent/claude-hooks.json`, and put personal tweaks in the gitignored `.claude/settings.local.json`. (Claude Code only — Cursor's allowlist is global per-user at `~/.cursor/permissions.json` with no per-repo option.)
117
+
118
+ ## Skills
119
+
120
+ Skills live in `agent/skills/<name>/SKILL.md`. They are loaded lazily: the agent reads each skill's short resolver frontmatter (`name`, `pattern`, `description`, `when_to_use`, `when_not_to_use`, `next_skills`) to decide what to use, and only opens the full skill when it picks it.
121
+
122
+ The base is deliberately thin — only what *every* repo needs (an internal ops or research repo with no codebase and no customer deliverables still wants all of it):
123
+
124
+ | Skill | What it does |
125
+ |---|---|
126
+ | `ingest-input` | Classify + save any inbound material to `docs/sources/raw/`. The entry point for new input. |
127
+ | `open-ticket` / `close-ticket` | Track work in `docs/task/`. |
128
+ | `update-canonical-record` | Make a careful, cited edit to the project's main truth file. |
129
+ | `extract-learnings` | Capture non-obvious learnings into `docs/context/`. |
130
+ | `adopt` | Bring an existing, non-conformant repo up to the standard (survey → phased plan → reconcile). |
131
+ | `refact` | The `/refact …` command router that dispatches to the operational skills. |
132
+
133
+ Refact operational skills, also in base (run via `/refact <action>`, or picked on their own): `setup-project`, `update-package`, `import-chat-history`, `process-docs`, `project-status`, `git-it`, `sync-asana`.
134
+
135
+ Everything else ships as an **overlay**, applied only when the project needs it — so a repo with no code and no deliverables never sees code or delivery skills:
136
+
137
+ - **`code`** — `code-development`, `add-codebase`. Auto-applied for `wordpress`/`nextjs` (they always have code); opt in elsewhere with `--overlay code`.
138
+ - **`client`** — `create-deliverable` + the `docs/deliverables/` folder. Opt in with `--overlay client` on client-facing engagements.
139
+ - **`wordpress`** (`--type wordpress`) — `wp-env`, `install-wp-skills`, `setup-kinsta-deploy`.
140
+ - **`nextjs`** (`--type nextjs`) — `setup-nextjs-app`, `nextjs-dev`, `setup-vercel-deploy`, `setup-netlify-deploy`.
141
+
142
+ Engagement-shaped skills (`draft-quote`, `draft-proposal`, `draft-email`, `design-asset`, …) are **not** pre-shipped — add them when a real engagement needs them.
143
+
144
+ ## Repository layout (this package)
145
+
146
+ ```
147
+ bin/refact-os.js # CLI: init | sync | validate | migrate (no postinstall — scaffolding is always explicit)
148
+ lib/
149
+ scaffold.js # copies templates/base into the target, then generates adapters
150
+ adapters.js # generate .cursor/ + .claude/ from agent/
151
+ validate.js # check structure + skill frontmatter
152
+ migrate.js # old layout → current layout
153
+ project-utils.js # project-type detection; package.json scripts + dev-dependency
154
+ refact-config.js # .refact-os.json (stack: types/hosting/runtime/environments, Asana id)
155
+ templates/base/ # the payload copied into every project (universal + Refact tooling)
156
+ templates/overlays/ # overlays applied on top of base: code/, client/, wordpress/, nextjs/
157
+ docs/agent-first-repo-best-practices.md # the spec this tool implements
158
+ ```
159
+
160
+ Published package contents: `bin/`, `lib/`, `templates/`, `README.md` (see `package.json` `files`).
161
+
162
+ For this scaffolder's own agent map, see [`AGENTS.md`](./AGENTS.md).
@@ -0,0 +1,154 @@
1
+ #!/usr/bin/env node
2
+ const os = require("os");
3
+ const path = require("path");
4
+ const { scaffoldProject } = require("../lib/scaffold");
5
+ const { generateAdapters } = require("../lib/adapters");
6
+ const { validateRepo } = require("../lib/validate");
7
+ const { migrateRepo } = require("../lib/migrate");
8
+ const { syncCompany } = require("../lib/company");
9
+ const { ensurePackageScripts } = require("../lib/project-utils");
10
+
11
+ const COMMANDS = ["init", "sync", "validate", "migrate"];
12
+
13
+ // The refact-os package's own root. Commands must never operate on it — running
14
+ // `npm run init` / `npx refact-os init` inside this repo would otherwise scaffold
15
+ // the scaffolder into itself.
16
+ const PACKAGE_ROOT = path.resolve(__dirname, "..");
17
+
18
+ function isAllowedTarget(target) {
19
+ if (!target) return false;
20
+ if (target === "/") return false;
21
+ const home = os.homedir();
22
+ if (target === home) return false;
23
+ if (target === path.dirname(home)) return false;
24
+ if (target === PACKAGE_ROOT) return false;
25
+ return true;
26
+ }
27
+
28
+ function parseArgs(argv) {
29
+ const args = { command: "init", sub: undefined, target: process.cwd(), projectType: undefined, overlays: [], force: true, seed: false };
30
+ const raw = [...argv];
31
+ if (raw[0] && !raw[0].startsWith("-")) args.command = raw.shift();
32
+ if (raw[0] && !raw[0].startsWith("-")) args.sub = raw.shift();
33
+ while (raw.length > 0) {
34
+ const token = raw.shift();
35
+ if (token === "--type") args.projectType = raw.shift();
36
+ else if (token === "--overlay") args.overlays.push(raw.shift());
37
+ else if (token === "--target") args.target = raw.shift();
38
+ else if (token === "--no-force") args.force = false;
39
+ else if (token === "--seed") args.seed = true;
40
+ }
41
+ return args;
42
+ }
43
+
44
+ async function runScaffold(args, { force }) {
45
+ const result = await scaffoldProject(args.target, {
46
+ projectType: args.projectType,
47
+ overlays: args.overlays,
48
+ force,
49
+ seed: args.seed,
50
+ interactive: process.stdin.isTTY,
51
+ });
52
+ console.log(`Scaffolded ${result.filesWritten} files in ${result.target}`);
53
+ console.log(`Stack types: ${(result.projectTypes && result.projectTypes.length ? result.projectTypes : [result.projectType]).join(", ")}`);
54
+ console.log(`Adapters generated: ${result.adapters.tools.join(", ") || "none"}`);
55
+ console.log(`Config: ${result.configPath}`);
56
+ console.log(`.gitignore: ${result.gitignoreStatus}`);
57
+ if (result.prevVersion && result.prevVersion !== result.version) {
58
+ console.log(`Updated refact-os ${result.prevVersion} → ${result.version} — see the package CHANGELOG.md for what changed.`);
59
+ } else {
60
+ console.log(`refact-os version: ${result.version}`);
61
+ }
62
+ if (result.prunedSkills && result.prunedSkills.length > 0) {
63
+ console.log(`Pruned ${result.prunedSkills.length} skill(s) removed upstream: ${result.prunedSkills.join(", ")}`);
64
+ }
65
+ if (result.brownfield) {
66
+ console.log("");
67
+ console.log("Existing repo detected — added the agent layer only (no docs/ seed or apps/, no type overlays).");
68
+ console.log("Next: run the `adopt` skill (/adopt) to plan the transition, or re-run with `--seed` to scaffold the full structure anyway.");
69
+ }
70
+ if (result.missingFields && result.missingFields.length > 0) {
71
+ console.log(`Missing metadata (re-asked on next /refact run): ${result.missingFields.join(", ")}`);
72
+ }
73
+ if (result.contractChanged) {
74
+ console.log("");
75
+ console.log("⚠ agent/AGENTS.md template changed upstream. Your copy was preserved (skipIfExists).");
76
+ console.log(" Review templates/base/agent/AGENTS.md in the installed package and merge by hand.");
77
+ }
78
+ if (result.packageStatus && result.packageStatus.scriptsAdded.length > 0) {
79
+ console.log(`Added npm scripts: ${result.packageStatus.scriptsAdded.join(", ")}`);
80
+ }
81
+ if (result.packageStatus && result.packageStatus.depsAdded && result.packageStatus.depsAdded.length > 0) {
82
+ console.log(`Added devDependencies: ${result.packageStatus.depsAdded.join(", ")}`);
83
+ }
84
+ }
85
+
86
+ function runValidate(target) {
87
+ const report = validateRepo(target);
88
+ for (const w of report.warnings) console.log(` warn: ${w}`);
89
+ for (const e of report.errors) console.error(` error: ${e}`);
90
+ if (report.ok) {
91
+ console.log(`✓ valid — ${report.skillCount} skill(s), ${report.warnings.length} warning(s).`);
92
+ return 0;
93
+ }
94
+ console.error(`✗ ${report.errors.length} error(s), ${report.warnings.length} warning(s).`);
95
+ return 1;
96
+ }
97
+
98
+ async function main() {
99
+ const args = parseArgs(process.argv.slice(2));
100
+ if (!COMMANDS.includes(args.command)) {
101
+ console.error(`Unknown command "${args.command}". Use one of: ${COMMANDS.join(" | ")}`);
102
+ process.exit(1);
103
+ }
104
+ const target = path.resolve(args.target);
105
+ if (!isAllowedTarget(target)) {
106
+ console.error(`Refusing to operate on '${target}'. Pass --target <dir> or cd into a project directory first.`);
107
+ process.exit(1);
108
+ }
109
+
110
+ switch (args.command) {
111
+ case "init":
112
+ await runScaffold({ ...args, target }, { force: args.force });
113
+ break;
114
+ case "sync": {
115
+ if (args.sub === "company") {
116
+ const r = syncCompany(target, {});
117
+ console.log(`Synced ${r.fileCount} company file(s) to ${r.dest}/ from ${r.source} (${r.sourcePath}) @ ${r.sha.slice(0, 8)}`);
118
+ } else if (args.sub && args.sub !== "adapters") {
119
+ console.error(`Unknown sync target "${args.sub}". Use: refact-os sync [adapters|company]`);
120
+ process.exit(1);
121
+ } else {
122
+ const result = generateAdapters(target, {});
123
+ console.log(`Adapters synced from agent/: ${result.tools.join(", ") || "none"}`);
124
+ }
125
+ break;
126
+ }
127
+ case "migrate": {
128
+ const result = migrateRepo(target);
129
+ for (const m of result.moved) console.log(`moved: ${m.from} -> ${m.to}${m.merged ? ` (merged ${m.merged} file${m.merged === 1 ? "" : "s"})` : ""}`);
130
+ for (const s of result.skipped) console.log(`skipped: ${s.from} (${s.skipped})`);
131
+ const pkg = ensurePackageScripts(target);
132
+ if (pkg.scriptsAdded && pkg.scriptsAdded.length > 0) console.log(`package.json scripts added: ${pkg.scriptsAdded.join(", ")}`);
133
+ if (pkg.depsAdded && pkg.depsAdded.length > 0) console.log(`package.json devDependencies added: ${pkg.depsAdded.join(", ")}`);
134
+ console.log(`Migrated ${result.moved.length} path(s). Run \`refact-os init\` to lay down the agent/ payload, then \`refact-os validate\`.`);
135
+ break;
136
+ }
137
+ case "validate":
138
+ process.exit(runValidate(target));
139
+ break;
140
+ default:
141
+ break;
142
+ }
143
+ }
144
+
145
+ // Only run when invoked directly (e.g. `node bin/refact-os.js` or via the bin
146
+ // shim) — never on `require()`, so importing this file can't trigger a scaffold.
147
+ if (require.main === module) {
148
+ main().catch((err) => {
149
+ console.error(err instanceof Error ? err.message : String(err));
150
+ process.exit(1);
151
+ });
152
+ }
153
+
154
+ module.exports = { main, parseArgs };