peer-ai 1.0.0-next.0 → 1.0.0-next.1
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 +45 -1
- package/dist/cli.js +16 -3
- package/dist/doctor.js +5 -3
- package/dist/init.d.ts +3 -2
- package/dist/init.js +3 -3
- package/dist/migrate.d.ts +95 -0
- package/dist/migrate.js +787 -0
- package/dist/render.d.ts +1 -1
- package/dist/render.js +59 -0
- package/dist/v0-fingerprints.d.ts +5 -0
- package/dist/v0-fingerprints.js +393 -0
- package/dist/v0.d.ts +210 -0
- package/dist/v0.js +682 -0
- package/dist/work.d.ts +2 -0
- package/dist/work.js +8 -7
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -27,6 +27,7 @@ npm install --save-dev peer-ai
|
|
|
27
27
|
| Command | What it does |
|
|
28
28
|
|---------|--------------|
|
|
29
29
|
| [`peer-ai init`](#peer-ai-init) | Sets up Peer AI in a repository: one config file, from what it detects |
|
|
30
|
+
| [`peer-ai migrate`](#peer-ai-migrate) | Moves a project from its copy of v0 onto Peer AI 1.0 |
|
|
30
31
|
| [`peer-ai assess`](#peer-ai-assess) | Maps what the project has and what its stage still needs |
|
|
31
32
|
| [`peer-ai render`](#peer-ai-render) | Connects each AI tool: instructions, the MCP server and the skills |
|
|
32
33
|
| [`peer-ai doctor`](#peer-ai-doctor) | Checks the setup and says how to fix what isn't right |
|
|
@@ -76,6 +77,40 @@ It never overwrites an existing `peer-ai.config.json`, and it never assumes anyt
|
|
|
76
77
|
|
|
77
78
|
`0` success, `1` refused (a config already exists) or cancelled, `2` a usage error.
|
|
78
79
|
|
|
80
|
+
## `peer-ai migrate`
|
|
81
|
+
|
|
82
|
+
Moves a project from v0, when Peer AI was a playbook copied into a `peer-ai/` folder, onto the package ([RFC 0008](../../rfcs/0008-moving-a-v0-project-onto-1-0.md)).
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
npx peer-ai migrate --dry-run
|
|
86
|
+
npx peer-ai migrate
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
It converts what it can read with certainty:
|
|
90
|
+
|
|
91
|
+
| From v0 | Into |
|
|
92
|
+
|---------|------|
|
|
93
|
+
| The Project settings table in the workflow driver | `commands.verify`, `tracker`, `repo.branchNaming`, `repo.mergePolicy` and `design.reference` |
|
|
94
|
+
| `phase-config.json` | `models`, each skill's add-ons and notes, and each activity's notes. A phase that says a part is dormant marks it `dormant`. |
|
|
95
|
+
| Markdown files in `docs/standards/` | `standards.documents`, as standards or an addendum, each scoped to its part |
|
|
96
|
+
| `.peer-ai-state.json` | A work item for each ticket in progress. The tracker keeps the rest. |
|
|
97
|
+
|
|
98
|
+
Then it deletes the `peer-ai/` folder and the state file, takes v0's text out of `CLAUDE.md`, `AGENTS.md` and the other instruction files, maps the project and sets up the AI tools, as `assess` and `render` do.
|
|
99
|
+
|
|
100
|
+
Nothing is dropped silently. Whatever needs judgement, it copies word for word into `docs/peer-ai-migration.md`: the files the project edited in `peer-ai/`, the text it took out of the instruction files, the settings it couldn't place, and anything it left alone that still names v0. A work item, `migrate-v0`, points there, so your AI tool brings each decision up in the next session.
|
|
101
|
+
|
|
102
|
+
It tells the files the project changed from v0's own by their fingerprints: a list of every file in every v0 version, which ships with the package.
|
|
103
|
+
|
|
104
|
+
It never commits. It won't start with uncommitted changes, so the migration is a change of its own: review it, commit it on a branch, and open a pull request. `git restore . && git clean -fd` undoes it all.
|
|
105
|
+
|
|
106
|
+
### Options
|
|
107
|
+
|
|
108
|
+
The same as `init`. With `--dry-run`, it prints what it would convert, rewrite, delete and leave for a decision, and changes nothing.
|
|
109
|
+
|
|
110
|
+
### Exit codes
|
|
111
|
+
|
|
112
|
+
`0` success, `1` refused (a config already exists, there is no v0 copy, the project isn't in git or has uncommitted changes) or cancelled, `2` a usage error.
|
|
113
|
+
|
|
79
114
|
## `peer-ai assess`
|
|
80
115
|
|
|
81
116
|
Maps what a project already has, and what its stage still needs. Run it on any project, at any point: a brief with no code, a prototype, or a product in production with no documents at all.
|
|
@@ -165,7 +200,7 @@ npx peer-ai render
|
|
|
165
200
|
|
|
166
201
|
`AGENTS.md` gets the block whenever a tool other than Claude Code is listed, when it already exists, or when `CLAUDE.md` imports it.
|
|
167
202
|
|
|
168
|
-
The instructions are short: how to work through the MCP server, the project's parts, its commands, its compliance packs and its own rules. The server serves the detail when it's needed, rather than every rule on every turn.
|
|
203
|
+
The instructions are short: how to work through the MCP server, the project's parts, its commands, its compliance packs and its own rules, and the project's settings for models, skills and activities when it has any: the models to use, the add-ons, checklists and notes for each skill, and the files to read and notes for each activity. The server serves the detail when it's needed, rather than every rule on every turn.
|
|
169
204
|
|
|
170
205
|
### Skills
|
|
171
206
|
|
|
@@ -308,6 +343,15 @@ Run it after the project's own checks:
|
|
|
308
343
|
- run: npx peer-ai check
|
|
309
344
|
```
|
|
310
345
|
|
|
346
|
+
That uses the version in the project's `package.json`. A project without one, such as a Python or Flutter project, needs Node 24 on the runner and the version `render` pinned:
|
|
347
|
+
|
|
348
|
+
```yaml
|
|
349
|
+
- uses: actions/setup-node@v4
|
|
350
|
+
with:
|
|
351
|
+
node-version: 24
|
|
352
|
+
- run: npx -y peer-ai@<version> check
|
|
353
|
+
```
|
|
354
|
+
|
|
311
355
|
### Options
|
|
312
356
|
|
|
313
357
|
| Option | What it does |
|
package/dist/cli.js
CHANGED
|
@@ -13,6 +13,7 @@ import { runDoctor } from "./doctor.js";
|
|
|
13
13
|
import { runFeedback } from "./feedback.js";
|
|
14
14
|
import { serveStdio } from "./mcp.js";
|
|
15
15
|
import { runInit } from "./init.js";
|
|
16
|
+
import { runMigrate } from "./migrate.js";
|
|
16
17
|
import { VERSION } from "./package-info.js";
|
|
17
18
|
import { createTerminalPrompter } from "./prompter.js";
|
|
18
19
|
import { formatReport } from "./report.js";
|
|
@@ -23,6 +24,7 @@ const HELP = `peer-ai: from a brief to a shipped product, with any AI tool
|
|
|
23
24
|
|
|
24
25
|
Usage:
|
|
25
26
|
peer-ai init [options] Set up Peer AI in this repository
|
|
27
|
+
peer-ai migrate [options] Move a project from its copy of v0 onto Peer AI 1.0
|
|
26
28
|
peer-ai assess [options] Map what the project has and what its stage still needs
|
|
27
29
|
peer-ai render [options] Set up each AI tool in the config: instructions and the MCP server
|
|
28
30
|
peer-ai doctor [options] Check that Peer AI is set up correctly, and how to fix it
|
|
@@ -47,6 +49,9 @@ Options for init:
|
|
|
47
49
|
--tool <tool> An AI tool you use; repeat for several
|
|
48
50
|
(${TOOL_IDS.join(", ")})
|
|
49
51
|
|
|
52
|
+
Options for migrate: the same as init. It never commits, and it won't start on
|
|
53
|
+
uncommitted changes. --dry-run prints what it would do, changing nothing.
|
|
54
|
+
|
|
50
55
|
Options for assess:
|
|
51
56
|
--target <stage> Assess against a stage other than the project's own,
|
|
52
57
|
for example production before a launch
|
|
@@ -79,7 +84,8 @@ function oneOf(value, allowed, flag) {
|
|
|
79
84
|
return value;
|
|
80
85
|
throw new Error(`${flag} must be one of: ${allowed.join(", ")}`);
|
|
81
86
|
}
|
|
82
|
-
|
|
87
|
+
/** The options init and migrate share. */
|
|
88
|
+
function initOptions(args, io) {
|
|
83
89
|
const { values } = parseArgs({
|
|
84
90
|
args,
|
|
85
91
|
strict: true,
|
|
@@ -95,7 +101,7 @@ async function init(args, io) {
|
|
|
95
101
|
const stage = values.stage === undefined ? undefined : oneOf(values.stage, STAGES, "--stage");
|
|
96
102
|
const team = values.team === undefined ? undefined : oneOf(values.team, ["solo", "team"], "--team");
|
|
97
103
|
const tools = values.tool?.map((tool) => oneOf(tool, TOOL_IDS, "--tool"));
|
|
98
|
-
return
|
|
104
|
+
return {
|
|
99
105
|
cwd: io.cwd,
|
|
100
106
|
yes: values.yes === true,
|
|
101
107
|
dryRun: values["dry-run"] === true,
|
|
@@ -103,7 +109,13 @@ async function init(args, io) {
|
|
|
103
109
|
...(stage === undefined ? {} : { stage }),
|
|
104
110
|
...(team === undefined ? {} : { team }),
|
|
105
111
|
...(tools === undefined ? {} : { tools }),
|
|
106
|
-
}
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function init(args, io) {
|
|
115
|
+
return runInit(initOptions(args, io), io.prompter, io.out);
|
|
116
|
+
}
|
|
117
|
+
function migrate(args, io) {
|
|
118
|
+
return runMigrate(initOptions(args, io), io.prompter, io.out);
|
|
107
119
|
}
|
|
108
120
|
function assess(args, io) {
|
|
109
121
|
const { values } = parseArgs({
|
|
@@ -175,6 +187,7 @@ async function mcp(args, io) {
|
|
|
175
187
|
}
|
|
176
188
|
const COMMANDS = {
|
|
177
189
|
init,
|
|
190
|
+
migrate,
|
|
178
191
|
assess,
|
|
179
192
|
render,
|
|
180
193
|
doctor,
|
package/dist/doctor.js
CHANGED
|
@@ -240,9 +240,11 @@ function checkGit(root) {
|
|
|
240
240
|
function checkLegacy(root) {
|
|
241
241
|
if (!LEGACY_MARKERS.some((marker) => existsSync(join(root, marker))))
|
|
242
242
|
return [];
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
243
|
+
// migrate builds the config itself, so it only runs before there is one.
|
|
244
|
+
const fix = existsSync(join(root, CONFIG_FILE))
|
|
245
|
+
? `Move any changes your project made to it into ${CONFIG_FILE}, then remove it with: git rm -r peer-ai`
|
|
246
|
+
: "Run npx peer-ai migrate. It moves what your project changed into the config, and removes the folder.";
|
|
247
|
+
return [warn("legacy", "The peer-ai/ folder is a copy of the v0 playbook, which Peer AI 1.0 doesn't read.", fix)];
|
|
246
248
|
}
|
|
247
249
|
export function diagnose(root, nodeVersion = process.versions.node, today = new Date(), options = {}) {
|
|
248
250
|
const { check: configCheck, config } = checkConfig(root);
|
package/dist/init.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ export interface Output {
|
|
|
17
17
|
log: (line: string) => void;
|
|
18
18
|
error: (line: string) => void;
|
|
19
19
|
}
|
|
20
|
-
interface Answers {
|
|
20
|
+
export interface Answers {
|
|
21
21
|
name: string;
|
|
22
22
|
description: string;
|
|
23
23
|
tracks: DetectedTrack[];
|
|
@@ -26,6 +26,7 @@ interface Answers {
|
|
|
26
26
|
tools: ToolId[];
|
|
27
27
|
}
|
|
28
28
|
export declare function describeTrack(track: DetectedTrack): string;
|
|
29
|
+
export declare function ask(detected: Detected, prompter: Prompter, title?: string): Promise<Answers>;
|
|
30
|
+
export declare function defaults(detected: Detected, options: InitOptions): Answers;
|
|
29
31
|
export declare function buildConfig(detected: Detected, answers: Answers): Record<string, unknown>;
|
|
30
32
|
export declare function runInit(options: InitOptions, prompter: Prompter | undefined, out: Output): Promise<number>;
|
|
31
|
-
export {};
|
package/dist/init.js
CHANGED
|
@@ -42,8 +42,8 @@ function parseStack(text) {
|
|
|
42
42
|
.map((part) => part.trim().toLowerCase())
|
|
43
43
|
.filter((part) => part !== "");
|
|
44
44
|
}
|
|
45
|
-
async function ask(detected, prompter) {
|
|
46
|
-
prompter.intro(
|
|
45
|
+
export async function ask(detected, prompter, title = "peer-ai init") {
|
|
46
|
+
prompter.intro(title);
|
|
47
47
|
if (detected.tracks.length > 0) {
|
|
48
48
|
prompter.note(detected.tracks.map(describeTrack).join("\n"), "Found in this repository");
|
|
49
49
|
}
|
|
@@ -75,7 +75,7 @@ async function ask(detected, prompter) {
|
|
|
75
75
|
}
|
|
76
76
|
return { name, description, tracks, team, stage, tools };
|
|
77
77
|
}
|
|
78
|
-
function defaults(detected, options) {
|
|
78
|
+
export function defaults(detected, options) {
|
|
79
79
|
return {
|
|
80
80
|
name: options.name ?? detected.name,
|
|
81
81
|
description: detected.description ?? "",
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { type ActivityId } from "peer-ai-workflow";
|
|
2
|
+
import { type Detected } from "./detect.ts";
|
|
3
|
+
import { type Answers, type InitOptions, type Output } from "./init.ts";
|
|
4
|
+
import { type Prompter } from "./prompter.ts";
|
|
5
|
+
import { type Converted, type Copy, type Fingerprints } from "./v0.ts";
|
|
6
|
+
export declare const NOTES_FILE = "docs/peer-ai-migration.md";
|
|
7
|
+
export declare const MIGRATION_ITEM = "migrate-v0";
|
|
8
|
+
export interface MigrateOptions extends InitOptions {
|
|
9
|
+
now?: Date;
|
|
10
|
+
/** The v0 fingerprints to compare against. Tests pass their own. */
|
|
11
|
+
fingerprints?: Fingerprints;
|
|
12
|
+
}
|
|
13
|
+
/** What git knows about the project, read once before anything changes. */
|
|
14
|
+
export interface RepoState {
|
|
15
|
+
/** The copy's files git tracks, which it can bring back after they're deleted. */
|
|
16
|
+
tracked: string[];
|
|
17
|
+
/** The copy's files git ignores, which migrate leaves where they are. */
|
|
18
|
+
ignored: string[];
|
|
19
|
+
/** The commit before the migration, so the notes can point at the files as they were. */
|
|
20
|
+
base?: string;
|
|
21
|
+
}
|
|
22
|
+
interface Decision {
|
|
23
|
+
title: string;
|
|
24
|
+
body: string[];
|
|
25
|
+
}
|
|
26
|
+
interface PlannedItem {
|
|
27
|
+
id?: string;
|
|
28
|
+
title: string;
|
|
29
|
+
stage: "prepare" | "build" | "verify";
|
|
30
|
+
track?: string;
|
|
31
|
+
position?: {
|
|
32
|
+
activity: ActivityId;
|
|
33
|
+
step: number;
|
|
34
|
+
};
|
|
35
|
+
next: string;
|
|
36
|
+
}
|
|
37
|
+
export interface Plan {
|
|
38
|
+
config: Record<string, unknown>;
|
|
39
|
+
copy: Copy;
|
|
40
|
+
converted: Converted[];
|
|
41
|
+
/** Files rewritten: instructions without v0's text, package.json without v0's scripts. */
|
|
42
|
+
edits: {
|
|
43
|
+
path: string;
|
|
44
|
+
text: string;
|
|
45
|
+
}[];
|
|
46
|
+
/** Files deleted outside the copy, each quoted in the notes or rebuilt from git. */
|
|
47
|
+
removals: string[];
|
|
48
|
+
/** The copy's tracked files, deleted one by one; its ignored files stay. */
|
|
49
|
+
copyFiles: string[];
|
|
50
|
+
decisions: Decision[];
|
|
51
|
+
/** Whole files kept in the notes as v0 had them, for reference. */
|
|
52
|
+
kept: {
|
|
53
|
+
path: string;
|
|
54
|
+
text: string;
|
|
55
|
+
info: string;
|
|
56
|
+
}[];
|
|
57
|
+
items: PlannedItem[];
|
|
58
|
+
base?: string;
|
|
59
|
+
}
|
|
60
|
+
/** A fenced block that holds any text, with a fence longer than any inside it. */
|
|
61
|
+
export declare function fenced(text: string, info?: string): string[];
|
|
62
|
+
/** Inline code that holds any text, even text with backticks of its own. */
|
|
63
|
+
export declare function code(text: string): string;
|
|
64
|
+
/** A work item id for a v0 ticket: PROJ-14 stays, #12 becomes PREFIX-12 or ITEM-12. */
|
|
65
|
+
export declare function ticketId(ticket: string, prefix: string | undefined): string | undefined;
|
|
66
|
+
export interface Scripts {
|
|
67
|
+
text: string;
|
|
68
|
+
removed: {
|
|
69
|
+
name: string;
|
|
70
|
+
command: string;
|
|
71
|
+
}[];
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* package.json without the scripts that only run v0's files. A script that does anything else as
|
|
75
|
+
* well stays as it is, and is listed for a person.
|
|
76
|
+
*/
|
|
77
|
+
export declare function packageScripts(text: string): {
|
|
78
|
+
edit?: Scripts;
|
|
79
|
+
mentions: {
|
|
80
|
+
name: string;
|
|
81
|
+
command: string;
|
|
82
|
+
}[];
|
|
83
|
+
};
|
|
84
|
+
/** Works out everything migrate will do, without changing anything. */
|
|
85
|
+
export declare function planMigration(root: string, detected: Detected, answers: Answers, fingerprints: Fingerprints, repo: RepoState): Plan;
|
|
86
|
+
/** docs/peer-ai-migration.md: what migrate did, and every decision it left. */
|
|
87
|
+
export declare function notesDocument(plan: Plan, today: string): string;
|
|
88
|
+
/** A path that leads outside the project, through a symbolic link on the way or at the end. */
|
|
89
|
+
export declare function escapes(root: string, path: string): boolean;
|
|
90
|
+
/**
|
|
91
|
+
* Exit code 0 when migrated, or with --dry-run; 1 when it refuses, or when the migration was applied
|
|
92
|
+
* but a step after it didn't finish; 2 on a usage error.
|
|
93
|
+
*/
|
|
94
|
+
export declare function runMigrate(options: MigrateOptions, prompter: Prompter | undefined, out: Output): Promise<number>;
|
|
95
|
+
export {};
|