verity-framework 0.1.0 → 0.2.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/README.md +69 -5
- package/commands/verity/architect.md +24 -4
- package/package.json +1 -1
- package/verity/bin/lib/deployment.cjs +230 -0
- package/verity/bin/lib/install.cjs +20 -4
- package/verity/bin/verity.cjs +4 -0
- package/verity/templates/deploy-access-pointer.md.tmpl +13 -0
- package/verity/templates/deployment-methods.md.tmpl +32 -0
- package/verity/templates/gitignore.tmpl +3 -0
package/README.md
CHANGED
|
@@ -6,25 +6,89 @@ Verity is a CI/CD-native, GitHub-native, production-lifecycle AI software delive
|
|
|
6
6
|
|
|
7
7
|
Most AI coding tools stop when the code is written. Verity keeps going until the software is tested, deployed, and **proven working in front of a user**. It runs a project as a sequence of specialized AI roles (architect, builder, reviewer, release operator, verifier…) that hand work off through clear contracts, with GitHub as the source of truth.
|
|
8
8
|
|
|
9
|
-
> **Status
|
|
9
|
+
> **Status:** published — [`verity-framework@0.2.0`](https://www.npmjs.com/package/verity-framework) on npm. 13 role commands, the full Relay/Ledger/Gate/Shipyard/Verify engine, adapters for Claude Code + OpenCode, and the deployment-methods catalog (see below).
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm i -g verity-framework
|
|
15
|
+
verity install --claude # or: --opencode
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
**Prerequisites:** a GitHub account · Node ≥16 · `git` and the GitHub CLI (`gh`) installed **and signed in** (`gh auth login` — installing `gh` is not the same as being authenticated). Preflight:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
node -v && git --version && gh auth status
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Then, in your AI assistant, start a project with `/verity:vision`.
|
|
25
|
+
|
|
26
|
+
## Deployment methods
|
|
27
|
+
|
|
28
|
+
**What it is.** A catalog of the places *you* can deploy to (an AWS box, a server on your LAN, a managed host…), plus a per-app record of where each project actually runs. Verity reads it when the **Architect** designs an app, so deployment becomes a *deliberate choice you make* — not an accident.
|
|
29
|
+
|
|
30
|
+
**Why it exists.** Without it, an AI agent picks a host by whatever happens to be lying around — e.g. suggesting Render.com simply because a Render tool is installed in its environment. That's not a decision; it's a coincidence. The catalog makes the Architect ask *"here's what you can deploy to — which one for this app?"*, and record the answer as an [ADR](docs/commands.md). It also keeps host and credential details **out of git** while still letting a team share them safely.
|
|
31
|
+
|
|
32
|
+
**Two files, two scopes:**
|
|
33
|
+
|
|
34
|
+
| | Global catalog | Per-app access |
|
|
35
|
+
|---|---|---|
|
|
36
|
+
| **Path** | `~/.verity/deployment-methods.md` | `.verity/deploy-access.md` |
|
|
37
|
+
| **Scope** | You, across *every* project | One app |
|
|
38
|
+
| **Holds** | The deploy targets you *can* use | How to reach *this* app's host |
|
|
39
|
+
| **Created** | At `verity install` (seeded once, never overwritten) | Written by the Architect, per app |
|
|
40
|
+
| **In git?** | No — lives in your home dir | **No — gitignored.** A committed, secret-free pointer (`.verity/deploy-access.README.md`) tells teammates who lack it to ask the project admin |
|
|
41
|
+
|
|
42
|
+
> 🔒 **Both files reference credential *locations*, never secrets.** Point at a key file (`~/.ssh/prod.pem`), an SSO profile, or a secret-store entry — never paste an actual key, password, or token. The real per-app file is shared out-of-band (not through the repo), so nothing sensitive ever lands in git history. (Same rule as `STATUS.md`.)
|
|
43
|
+
|
|
44
|
+
**How to use it.** The global catalog is seeded at install with two worked examples (AWS EC2 over SSH, and a local-network server) — edit it to describe *your* real targets:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
verity deployment list # what targets you have (the Architect reads this)
|
|
48
|
+
verity deployment show aws-ec2 # one method's details
|
|
49
|
+
verity deployment path # where the catalog file lives
|
|
50
|
+
verity deployment edit # open it in $EDITOR to add/adjust targets
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
When the Architect designs an app it runs `verity deployment list`, helps you **pick a target** (recording it as an ADR), and — if nothing real is configured yet — **asks how you want to deploy and offers suggestions**. It then sets up the per-app access file:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
verity deployment init-access # gitignore the real file + commit the "ask the admin" pointer
|
|
57
|
+
verity deployment access # is the access file present on this machine? if not, who to ask
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
…and writes `.verity/deploy-access.md` with how to reach this app's host. The **Release/Deploy Operator** (`/verity:ship`) consumes that target when it builds the deploy step.
|
|
61
|
+
|
|
62
|
+
## Guides (interactive, beginner-friendly)
|
|
63
|
+
|
|
64
|
+
Self-contained HTML — clone/download the repo and open them in any browser (no server or internet needed):
|
|
65
|
+
|
|
66
|
+
- [`docs/verity-overview.html`](docs/verity-overview.html) — **Overview**: what Verity is, the mental model, how it works (no jargon assumed)
|
|
67
|
+
- [`docs/verity-usage.html`](docs/verity-usage.html) — **Usage**: install + command-by-command recipes + pro tips (Claude Code / OpenCode toggle)
|
|
68
|
+
- [`docs/verity-flows.html`](docs/verity-flows.html) — **Flows**: start-from-scratch vs add-to-an-existing-project, side by side
|
|
69
|
+
- [`docs/verity-flows.drawio`](docs/verity-flows.drawio) — the flow diagram as an editable draw.io / diagrams.net file
|
|
70
|
+
- [`docs/commands.md`](docs/commands.md) — **Command reference**: all 13 `/verity:*` role commands and what each one does
|
|
71
|
+
- [`docs/explainer-kit.md`](docs/explainer-kit.md) — **Explainer kit**: a briefing for an AI to describe Verity to humans (podcast/deck/talk) — story, diagrams, soundbites, fact sheet
|
|
10
72
|
|
|
11
73
|
## Subsystems
|
|
12
74
|
- **Relay** — role orchestration + the stream loop + dependency engine
|
|
13
|
-
- **Shipyard** — CI/CD spine + Release/Deploy Operator + runtime truth (`STATUS.md`)
|
|
75
|
+
- **Shipyard** — CI/CD spine + Release/Deploy Operator + deployment-methods catalog + runtime truth (`STATUS.md`)
|
|
14
76
|
- **Ledger** — GitHub-derived state engine (no stale files)
|
|
15
77
|
- **Gate** — review + security + the quality gates
|
|
16
78
|
- **Verify** — live "observably-works" verification
|
|
17
79
|
|
|
18
|
-
##
|
|
80
|
+
## Design docs
|
|
19
81
|
- [`docs/framework-spec.md`](docs/framework-spec.md) — the build-ready architecture spec
|
|
20
|
-
- [`docs/roles-spec.md`](docs/roles-spec.md) — working log + full rationale (all
|
|
82
|
+
- [`docs/roles-spec.md`](docs/roles-spec.md) — working log + full rationale (all roles)
|
|
21
83
|
- [`docs/interview-findings.md`](docs/interview-findings.md) — forensic interview of the real build that drove the design
|
|
22
84
|
- [`docs/brand.md`](docs/brand.md) — naming / positioning
|
|
23
|
-
- [`docs/features/helper-bot.md`](docs/features/helper-bot.md) — drop-in feature #1
|
|
24
85
|
- [`docs/walking-skeleton-plan.md`](docs/walking-skeleton-plan.md) — the first implementation slice
|
|
25
86
|
|
|
26
87
|
## Package
|
|
27
88
|
`verity-framework` (npm) · CLI binary: `verity` · Node ≥16 · host deps: `git`, `gh`
|
|
28
89
|
|
|
90
|
+
## Contributing
|
|
91
|
+
See [`CONTRIBUTING.md`](CONTRIBUTING.md) — local setup, the test/lint checks, project layout, and conventions.
|
|
92
|
+
|
|
29
93
|
## License
|
|
30
94
|
MIT
|
|
@@ -34,24 +34,44 @@ and the walking-skeleton definition handed to /verity:plan.
|
|
|
34
34
|
```
|
|
35
35
|
Then fill in Context / Decision / Alternatives considered / Consequences.
|
|
36
36
|
|
|
37
|
-
3. **
|
|
37
|
+
3. **Choose the deployment target.** Read the user's global catalog of where they can
|
|
38
|
+
deploy, and pick a target for THIS app — never assume one (don't reach for a host
|
|
39
|
+
just because its MCP/CLI happens to be installed):
|
|
40
|
+
```bash
|
|
41
|
+
verity deployment list # ~/.verity/deployment-methods.md (locations, not secrets)
|
|
42
|
+
```
|
|
43
|
+
- Work with the user to choose a method; record the choice as an ADR.
|
|
44
|
+
- If `hasConfigured` is false (only the shipped examples remain), ASK how they want
|
|
45
|
+
to deploy and offer suggestions (managed PaaS, a VM over SSH, a local/LAN server…),
|
|
46
|
+
then help them add it: `verity deployment edit`.
|
|
47
|
+
- Set up the per-app access file (committed pointer + gitignore — no secrets in git):
|
|
48
|
+
```bash
|
|
49
|
+
verity deployment init-access
|
|
50
|
+
```
|
|
51
|
+
Then WRITE `.verity/deploy-access.md` with how to reach this app's host —
|
|
52
|
+
credential **locations** only (key files, SSO profiles, secret-store entries),
|
|
53
|
+
never raw secrets. That file is gitignored and shared out-of-band; teammates who
|
|
54
|
+
lack it are pointed at the project admin. The Release/Deploy Operator (`/verity:ship`)
|
|
55
|
+
consumes this target when it builds `deploy.sh`.
|
|
56
|
+
|
|
57
|
+
4. **Freeze the core contracts** (wire/JWT/schema between components) — additive-only
|
|
38
58
|
thereafter; a breaking change is a NEW contract, never an edit:
|
|
39
59
|
```bash
|
|
40
60
|
verity contract new <seam-name>
|
|
41
61
|
```
|
|
42
62
|
|
|
43
|
-
|
|
63
|
+
5. Offer the **drop-in feature catalog**. For each feature the user wants, note that
|
|
44
64
|
its stages will fold into the plan:
|
|
45
65
|
```bash
|
|
46
66
|
verity feature list
|
|
47
67
|
verity feature show <id>
|
|
48
68
|
```
|
|
49
69
|
|
|
50
|
-
|
|
70
|
+
6. Define the **walking skeleton** (Stage 0): the thinnest end-to-end slice that
|
|
51
71
|
compiles, runs, passes one real test, goes green in CI, and deploys. This blocks
|
|
52
72
|
all feature stages and proves the spine.
|
|
53
73
|
|
|
54
|
-
|
|
74
|
+
7. Hand off to **/verity:plan** (Intake/Planner) to decompose the design + accepted
|
|
55
75
|
features into the initial thin backlog of stages.
|
|
56
76
|
|
|
57
77
|
Runtime note: if `verity` is not on PATH, use `node "$HOME/.claude/verity/bin/verity.cjs" ...`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "verity-framework",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Verity — a CI/CD-native, GitHub-native, production-lifecycle AI software delivery framework.",
|
|
5
5
|
"keywords": ["verity", "ai", "ci-cd", "github", "devops", "agent", "framework"],
|
|
6
6
|
"author": "Sean Mahoney",
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
// Verity deployment methods (framework-spec.md — deployment-target feature).
|
|
2
|
+
//
|
|
3
|
+
// TWO scopes, deliberately separate:
|
|
4
|
+
//
|
|
5
|
+
// 1. GLOBAL catalog — `~/.verity/deployment-methods.md`. Your private inventory
|
|
6
|
+
// of where you CAN deploy (one per machine/user, reused across every project).
|
|
7
|
+
// Seeded at `verity install`, edited with `verity deployment edit`, read by the
|
|
8
|
+
// Architect with `verity deployment list`. Holds credential LOCATIONS, never
|
|
9
|
+
// secret values. NOT in any repo.
|
|
10
|
+
//
|
|
11
|
+
// 2. PER-PROJECT access file — `.verity/deploy-access.md`. Written by the Architect
|
|
12
|
+
// for ONE app: how to reach THAT app's host. Gitignored + shared out-of-band; a
|
|
13
|
+
// secret-free pointer (`.verity/deploy-access.README.md`) IS committed so a
|
|
14
|
+
// teammate without the file is told to get it from the project admin.
|
|
15
|
+
const fs = require('node:fs');
|
|
16
|
+
const os = require('node:os');
|
|
17
|
+
const path = require('node:path');
|
|
18
|
+
const { spawnSync } = require('node:child_process');
|
|
19
|
+
|
|
20
|
+
const identity = require('./identity.cjs');
|
|
21
|
+
const { render } = require('./core.cjs');
|
|
22
|
+
|
|
23
|
+
const TEMPLATES_DIR = path.join(__dirname, '..', '..', 'templates');
|
|
24
|
+
|
|
25
|
+
const ACCESS_FILE = '.verity/deploy-access.md'; // gitignored real file
|
|
26
|
+
const ACCESS_POINTER = '.verity/deploy-access.README.md'; // committed pointer
|
|
27
|
+
|
|
28
|
+
function readTemplate(name) {
|
|
29
|
+
return fs.readFileSync(path.join(TEMPLATES_DIR, name), 'utf8');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// --- GLOBAL catalog -------------------------------------------------------
|
|
33
|
+
|
|
34
|
+
// User-global, harness-independent, and OUTSIDE the install dir (which `verity
|
|
35
|
+
// install` clobbers on every reinstall). VERITY_HOME / opts.home override for tests.
|
|
36
|
+
function homeDir(opts = {}) {
|
|
37
|
+
return opts.home || process.env.VERITY_HOME || path.join(os.homedir(), '.verity');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function globalPath(opts = {}) {
|
|
41
|
+
return path.join(homeDir(opts), 'deployment-methods.md');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Seed the catalog only if absent — NEVER clobber the user's edits.
|
|
45
|
+
function ensure(opts = {}) {
|
|
46
|
+
const p = globalPath(opts);
|
|
47
|
+
if (fs.existsSync(p)) {
|
|
48
|
+
return { created: false, path: p };
|
|
49
|
+
}
|
|
50
|
+
fs.mkdirSync(path.dirname(p), { recursive: true });
|
|
51
|
+
fs.writeFileSync(p, readTemplate('deployment-methods.md.tmpl'));
|
|
52
|
+
return { created: true, path: p };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Each method is a `## <id> — <Title>` section; `- **status:** <x>` tags it.
|
|
56
|
+
// status defaults to `active` when a user adds a method without one; the shipped
|
|
57
|
+
// samples are `example` so the Architect can tell "real option" from "placeholder".
|
|
58
|
+
function parseMethods(text) {
|
|
59
|
+
const methods = [];
|
|
60
|
+
let cur = null;
|
|
61
|
+
for (const line of String(text).split('\n')) {
|
|
62
|
+
const head = line.match(/^##\s+([a-z0-9][a-z0-9-]*)\s+—\s+(.+)$/);
|
|
63
|
+
if (head) {
|
|
64
|
+
if (cur) {
|
|
65
|
+
methods.push(cur);
|
|
66
|
+
}
|
|
67
|
+
cur = { id: head[1], title: head[2].trim(), status: 'active', content: line };
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
if (cur) {
|
|
71
|
+
cur.content += `\n${line}`;
|
|
72
|
+
const st = line.match(/^-\s+\*\*status:\*\*\s*(.+)$/i);
|
|
73
|
+
if (st) {
|
|
74
|
+
cur.status = st[1].trim().toLowerCase();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (cur) {
|
|
79
|
+
methods.push(cur);
|
|
80
|
+
}
|
|
81
|
+
return methods;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function list(opts = {}) {
|
|
85
|
+
const { path: p } = ensure(opts);
|
|
86
|
+
const methods = parseMethods(fs.readFileSync(p, 'utf8')).map((m) => ({
|
|
87
|
+
id: m.id,
|
|
88
|
+
title: m.title,
|
|
89
|
+
status: m.status,
|
|
90
|
+
}));
|
|
91
|
+
const configured = methods.filter((m) => m.status !== 'example');
|
|
92
|
+
return { path: p, methods, configured, hasConfigured: configured.length > 0 };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function show(id, opts = {}) {
|
|
96
|
+
if (!id) {
|
|
97
|
+
throw new Error('show requires a method id');
|
|
98
|
+
}
|
|
99
|
+
const { path: p } = ensure(opts);
|
|
100
|
+
const method = parseMethods(fs.readFileSync(p, 'utf8')).find((m) => m.id === id);
|
|
101
|
+
if (!method) {
|
|
102
|
+
throw new Error(`no such deployment method: ${id}`);
|
|
103
|
+
}
|
|
104
|
+
return method;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Open the catalog in $EDITOR when run interactively; otherwise just report the
|
|
108
|
+
// path (the agent edits via its own Write tool, not an interactive editor).
|
|
109
|
+
function edit(opts = {}) {
|
|
110
|
+
const { path: p } = ensure(opts);
|
|
111
|
+
const editor = opts.editor || process.env.VISUAL || process.env.EDITOR || 'nano';
|
|
112
|
+
const isTTY = opts.isTTY !== undefined ? opts.isTTY : Boolean(process.stdout.isTTY);
|
|
113
|
+
if (!isTTY) {
|
|
114
|
+
return { path: p, editor, opened: false, hint: `open it: ${editor} ${p}` };
|
|
115
|
+
}
|
|
116
|
+
const run = opts.spawn || ((cmd, a) => spawnSync(cmd, a, { stdio: 'inherit' }));
|
|
117
|
+
run(editor, [p]);
|
|
118
|
+
return { path: p, editor, opened: true };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// --- PER-PROJECT access ---------------------------------------------------
|
|
122
|
+
|
|
123
|
+
function adminFor(cwd) {
|
|
124
|
+
try {
|
|
125
|
+
const { manifest } = identity.get(cwd);
|
|
126
|
+
return manifest.owner || manifest.name || manifest.slug || '(project admin)';
|
|
127
|
+
} catch (_e) {
|
|
128
|
+
return '(project admin — lock identity first)';
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Add a line to the project .gitignore if not already present. Returns whether the
|
|
133
|
+
// file changed (idempotent).
|
|
134
|
+
function ensureIgnored(cwd, line) {
|
|
135
|
+
const gi = path.join(cwd, '.gitignore');
|
|
136
|
+
const text = fs.existsSync(gi) ? fs.readFileSync(gi, 'utf8') : '';
|
|
137
|
+
if (text.split('\n').some((l) => l.trim() === line)) {
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
const prefix = text.length && !text.endsWith('\n') ? '\n' : '';
|
|
141
|
+
fs.writeFileSync(gi, `${text}${prefix}${line}\n`);
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Set up the committed pointer + gitignore for a project's access file. Does NOT
|
|
146
|
+
// write the real access file — the Architect writes that with real host detail.
|
|
147
|
+
function initAccess(cwd) {
|
|
148
|
+
fs.mkdirSync(path.join(cwd, '.verity'), { recursive: true });
|
|
149
|
+
const admin = adminFor(cwd);
|
|
150
|
+
const ignored = ensureIgnored(cwd, ACCESS_FILE);
|
|
151
|
+
|
|
152
|
+
const pointer = path.join(cwd, ACCESS_POINTER);
|
|
153
|
+
let pointerCreated = false;
|
|
154
|
+
if (!fs.existsSync(pointer)) {
|
|
155
|
+
fs.writeFileSync(pointer, render(readTemplate('deploy-access-pointer.md.tmpl'), { admin }));
|
|
156
|
+
pointerCreated = true;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return {
|
|
160
|
+
accessFile: ACCESS_FILE,
|
|
161
|
+
accessPath: path.join(cwd, ACCESS_FILE),
|
|
162
|
+
pointer: ACCESS_POINTER,
|
|
163
|
+
pointerCreated,
|
|
164
|
+
gitignoreUpdated: ignored,
|
|
165
|
+
admin,
|
|
166
|
+
next: `Write ${ACCESS_FILE} with how to reach this app's host — credential LOCATIONS only.`,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Report whether THIS machine has the (gitignored) access file; if not, name the
|
|
171
|
+
// admin to request it from.
|
|
172
|
+
function accessStatus(cwd) {
|
|
173
|
+
const present = fs.existsSync(path.join(cwd, ACCESS_FILE));
|
|
174
|
+
const admin = adminFor(cwd);
|
|
175
|
+
return {
|
|
176
|
+
present,
|
|
177
|
+
accessFile: ACCESS_FILE,
|
|
178
|
+
pointerPresent: fs.existsSync(path.join(cwd, ACCESS_POINTER)),
|
|
179
|
+
admin,
|
|
180
|
+
message: present
|
|
181
|
+
? `${ACCESS_FILE} is present.`
|
|
182
|
+
: `${ACCESS_FILE} not found — request it from the project admin: ${admin}.`,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function dispatch(args, flags) {
|
|
187
|
+
const verb = args[0];
|
|
188
|
+
const cwd = flags.cwd || process.cwd();
|
|
189
|
+
const opts = { home: flags.home };
|
|
190
|
+
if (verb === 'list') {
|
|
191
|
+
return list(opts);
|
|
192
|
+
}
|
|
193
|
+
if (verb === 'show') {
|
|
194
|
+
return show(args[1], opts);
|
|
195
|
+
}
|
|
196
|
+
if (verb === 'path') {
|
|
197
|
+
const p = globalPath(opts);
|
|
198
|
+
return { path: p, raw: p };
|
|
199
|
+
}
|
|
200
|
+
if (verb === 'ensure') {
|
|
201
|
+
return ensure(opts);
|
|
202
|
+
}
|
|
203
|
+
if (verb === 'edit') {
|
|
204
|
+
return edit(opts);
|
|
205
|
+
}
|
|
206
|
+
if (verb === 'init-access') {
|
|
207
|
+
return initAccess(cwd);
|
|
208
|
+
}
|
|
209
|
+
if (verb === 'access') {
|
|
210
|
+
return accessStatus(cwd);
|
|
211
|
+
}
|
|
212
|
+
throw new Error(
|
|
213
|
+
`unknown deployment verb: ${verb || '(none)'} — use list|show|path|edit|ensure|init-access|access`,
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
module.exports = {
|
|
218
|
+
homeDir,
|
|
219
|
+
globalPath,
|
|
220
|
+
ensure,
|
|
221
|
+
parseMethods,
|
|
222
|
+
list,
|
|
223
|
+
show,
|
|
224
|
+
edit,
|
|
225
|
+
initAccess,
|
|
226
|
+
accessStatus,
|
|
227
|
+
dispatch,
|
|
228
|
+
ACCESS_FILE,
|
|
229
|
+
ACCESS_POINTER,
|
|
230
|
+
};
|
|
@@ -7,8 +7,17 @@ const fs = require('node:fs');
|
|
|
7
7
|
const os = require('node:os');
|
|
8
8
|
const path = require('node:path');
|
|
9
9
|
|
|
10
|
+
const deployment = require('./deployment.cjs');
|
|
11
|
+
|
|
10
12
|
const PKG_ROOT = path.join(__dirname, '..', '..', '..');
|
|
11
13
|
|
|
14
|
+
// Part of setup: seed the user-global deployment-methods catalog (NEVER clobbered).
|
|
15
|
+
// It lives in the user's home (~/.verity), independent of the harness target dir.
|
|
16
|
+
function seedDeploymentMethods(opts) {
|
|
17
|
+
const seed = deployment.ensure({ home: opts.home });
|
|
18
|
+
return { ...seed, label: `${seed.path}${seed.created ? '' : ' (existing)'}` };
|
|
19
|
+
}
|
|
20
|
+
|
|
12
21
|
function commandFiles(srcCommands) {
|
|
13
22
|
return fs.readdirSync(srcCommands).filter((n) => n.endsWith('.md'));
|
|
14
23
|
}
|
|
@@ -38,7 +47,11 @@ function installClaude(opts = {}) {
|
|
|
38
47
|
copyInternals(target);
|
|
39
48
|
installed.push('verity/');
|
|
40
49
|
|
|
41
|
-
|
|
50
|
+
// 3. Seed the global deployment-methods catalog (setup step).
|
|
51
|
+
const deploymentMethods = seedDeploymentMethods(opts);
|
|
52
|
+
installed.push(deploymentMethods.label);
|
|
53
|
+
|
|
54
|
+
return { harness: 'claude', target, installed, deploymentMethods };
|
|
42
55
|
}
|
|
43
56
|
|
|
44
57
|
// --- OpenCode adapter ---
|
|
@@ -85,17 +98,20 @@ function installOpenCode(opts = {}) {
|
|
|
85
98
|
copyInternals(target);
|
|
86
99
|
installed.push('verity/');
|
|
87
100
|
|
|
88
|
-
|
|
101
|
+
const deploymentMethods = seedDeploymentMethods(opts);
|
|
102
|
+
installed.push(deploymentMethods.label);
|
|
103
|
+
|
|
104
|
+
return { harness: 'opencode', target, installed, deploymentMethods };
|
|
89
105
|
}
|
|
90
106
|
|
|
91
107
|
function dispatch(_args, flags) {
|
|
92
108
|
if (flags.opencode) {
|
|
93
|
-
return installOpenCode({ target: flags.target });
|
|
109
|
+
return installOpenCode({ target: flags.target, home: flags.home });
|
|
94
110
|
}
|
|
95
111
|
if (flags.codex || flags.gemini) {
|
|
96
112
|
throw new Error('only the claude and opencode adapters are implemented so far');
|
|
97
113
|
}
|
|
98
|
-
return installClaude({ target: flags.target });
|
|
114
|
+
return installClaude({ target: flags.target, home: flags.home });
|
|
99
115
|
}
|
|
100
116
|
|
|
101
117
|
module.exports = {
|
package/verity/bin/verity.cjs
CHANGED
|
@@ -26,6 +26,7 @@ const map = require('./lib/map.cjs');
|
|
|
26
26
|
const recovery = require('./lib/recovery.cjs');
|
|
27
27
|
const golive = require('./lib/golive.cjs');
|
|
28
28
|
const smoke = require('./lib/smoke.cjs');
|
|
29
|
+
const deployment = require('./lib/deployment.cjs');
|
|
29
30
|
|
|
30
31
|
function parseArgs(argv) {
|
|
31
32
|
const positional = [];
|
|
@@ -154,6 +155,9 @@ const COMMANDS = {
|
|
|
154
155
|
smoke(rest, flags) {
|
|
155
156
|
return smoke.dispatch(rest, flags);
|
|
156
157
|
},
|
|
158
|
+
deployment(rest, flags) {
|
|
159
|
+
return deployment.dispatch(rest, flags);
|
|
160
|
+
},
|
|
157
161
|
};
|
|
158
162
|
|
|
159
163
|
function main() {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Deployment access — obtain from the project admin
|
|
2
|
+
|
|
3
|
+
This project deploys to a specific host. The access details live in
|
|
4
|
+
`.verity/deploy-access.md`, which is **intentionally gitignored** — it is never
|
|
5
|
+
committed, so no host or credential detail ever lands in git history.
|
|
6
|
+
|
|
7
|
+
**If you do not have `.verity/deploy-access.md` on your machine, request it from the
|
|
8
|
+
project admin: {{admin}}.** They maintain it and share it out-of-band (not through
|
|
9
|
+
this repo).
|
|
10
|
+
|
|
11
|
+
Maintainers: write or update the real file with `/verity:architect`, or by hand.
|
|
12
|
+
Keep it to credential *locations* (key files, SSO profiles, secret-store entries) —
|
|
13
|
+
never raw secrets.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Your Verity deployment methods
|
|
2
|
+
|
|
3
|
+
> **Global to YOU, across every project.** This is your private inventory of the
|
|
4
|
+
> places you *can* deploy to. The Architect (`/verity:architect`) reads it with
|
|
5
|
+
> `verity deployment list` and works with you to choose a target for each app.
|
|
6
|
+
>
|
|
7
|
+
> ⚠️ **Reference credential LOCATIONS, never paste secrets.** Point at a key file,
|
|
8
|
+
> an SSO profile, or a secret-store entry — never an actual key, password, or token.
|
|
9
|
+
> This file lives at `~/.verity/deployment-methods.md`; it is NOT in any repo, but
|
|
10
|
+
> treat it as sensitive anyway.
|
|
11
|
+
>
|
|
12
|
+
> Edit it any time with `verity deployment edit` (or open the path printed by
|
|
13
|
+
> `verity deployment path`). Add one `## <id> — <Title>` section per method, and set
|
|
14
|
+
> `- **status:** active` once it is real (the two below are `example` — replace or
|
|
15
|
+
> delete them).
|
|
16
|
+
|
|
17
|
+
## aws-ec2 — AWS EC2 over SSH (example — edit or delete)
|
|
18
|
+
- **status:** example
|
|
19
|
+
- **provider:** AWS
|
|
20
|
+
- **host:** `<ec2-public-dns-or-elastic-ip>`
|
|
21
|
+
- **user:** `ubuntu`
|
|
22
|
+
- **access:** `ssh -i ~/.ssh/<your-key>.pem ubuntu@<host>` — key *location* only
|
|
23
|
+
- **notes:** region `<e.g. us-east-1>`; the security group must allow your IP on
|
|
24
|
+
ports 22 (SSH) and 443 (HTTPS).
|
|
25
|
+
|
|
26
|
+
## local-server — Local network server (example — edit or delete)
|
|
27
|
+
- **status:** example
|
|
28
|
+
- **provider:** self-hosted (LAN)
|
|
29
|
+
- **host:** `<hostname-or-10.0.0.x>`
|
|
30
|
+
- **user:** `<you>`
|
|
31
|
+
- **access:** `ssh <user>@<host>` over the local network / VPN
|
|
32
|
+
- **notes:** only reachable on the LAN or while connected to the VPN — not public.
|
|
@@ -7,3 +7,6 @@ dist/
|
|
|
7
7
|
!.env.*.example
|
|
8
8
|
# Verity's derived state cache — never authoritative (framework-spec §5)
|
|
9
9
|
.verity-cache/
|
|
10
|
+
# Per-app deployment access (host + credential locations) — shared out-of-band,
|
|
11
|
+
# never committed. The committed pointer (.verity/deploy-access.README.md) stays.
|
|
12
|
+
.verity/deploy-access.md
|