peer-ai-workflow 1.0.0-next.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/dist/config.js ADDED
@@ -0,0 +1,392 @@
1
+ import { z } from "zod";
2
+ import { ACTIVITY_IDS, SKILL_IDS, TOOL_IDS, TRAITS } from "./ids.js";
3
+ // peer-ai.config.json: everything a project used to get by editing or patching the
4
+ // playbook's files. Objects are strict, so a misspelt key is an error, not a silent no-op.
5
+ // Only `version`, `project.name` and `tracks` are required. Everything else has a default,
6
+ // so an informal project's config can be ten lines.
7
+ const Path = z.string().min(1).describe("A path relative to the project root, or a URL.");
8
+ const Slug = z.string().regex(/^[a-z][a-z0-9]*(-[a-z0-9]+)*$/, "use lowercase letters, digits and hyphens");
9
+ const Note = z.string().min(1);
10
+ const RuleId = z
11
+ .string()
12
+ .regex(/^[A-Z][A-Z0-9]*-\d{2,}$/, "use a rule id such as SEC-07 or REACT-03")
13
+ .describe("A standards rule id, such as SEC-07.");
14
+ const AddOn = z
15
+ .string()
16
+ .regex(/^(\/[a-z0-9-]+|[a-z0-9-]+:[a-z0-9-]+)$/, "use plugin:skill for a plugin skill, or /command for a tool's built-in command")
17
+ .describe("An extra skill that feeds Peer AI's own: plugin:skill, or /command for a tool's built-in command.");
18
+ const Project = z.strictObject({
19
+ name: z.string().min(1),
20
+ description: z.string().min(1).optional(),
21
+ stage: z
22
+ .enum(["prototype", "mvp", "production"])
23
+ .optional()
24
+ .describe("How strict the gates are: prototype asks for the essentials only, production for everything. Defaults to mvp."),
25
+ origin: z
26
+ .enum(["new", "existing"])
27
+ .optional()
28
+ .describe("new: nothing is built yet. existing: code or docs already exist; Peer AI reads them first and never overwrites them. Detected when omitted."),
29
+ team: z
30
+ .enum(["solo", "team"])
31
+ .optional()
32
+ .describe("solo: nobody else reviews, so no second reviewer is asked for. Defaults to solo."),
33
+ traits: z
34
+ .array(z.enum(TRAITS))
35
+ .optional()
36
+ .describe("What the product is or does that switches on extra rules: money, safety-critical data, several apps or tenants on one backend, offline use, live connections, uploads, AI features."),
37
+ });
38
+ const Design = z.strictObject({
39
+ status: z.enum(["exists", "to-be-produced", "none"]),
40
+ reference: Path.optional(),
41
+ tokens: Path.optional(),
42
+ authoritative: z
43
+ .boolean()
44
+ .optional()
45
+ .describe("When true, layouts, spacing, type and colour are implemented exactly; nothing is invented."),
46
+ stopAfterSpecify: z
47
+ .boolean()
48
+ .optional()
49
+ .describe("Designs are made outside the session from the specs; work waits after Specify until they land."),
50
+ });
51
+ const Track = z.strictObject({
52
+ id: Slug,
53
+ kind: z.enum([
54
+ "web",
55
+ "mobile",
56
+ "desktop",
57
+ "backend",
58
+ "infrastructure",
59
+ "library",
60
+ "cli",
61
+ "data",
62
+ "extension",
63
+ "embedded",
64
+ "other",
65
+ ]),
66
+ path: Path.optional(),
67
+ repo: z
68
+ .string()
69
+ .min(1)
70
+ .optional()
71
+ .describe("For an external track: the repository it lives in, such as acme/platform-api."),
72
+ stack: z.array(z.string().min(1)).optional(),
73
+ architecture: Slug.optional().describe("The architecture style, such as layered, modular-monolith, microservice, hexagonal, feature-first or mvvm. The project's architecture decisions are the source of truth; this label tells reviews and stack profiles what shape to expect."),
74
+ targets: z
75
+ .array(Slug)
76
+ .optional()
77
+ .describe("Platforms this track ships to, such as ios, android, web, macos or chrome."),
78
+ uses: z
79
+ .array(Slug)
80
+ .optional()
81
+ .describe("Tracks whose code this track shares, such as a core library. A change to one verifies both."),
82
+ consumes: z.array(Slug).optional().describe("Ids of the APIs this track calls."),
83
+ branch: z
84
+ .string()
85
+ .min(1)
86
+ .optional()
87
+ .describe("The track's long-lived integration branch, when it has one. Work item branches start from it."),
88
+ deploy: z
89
+ .strictObject({
90
+ target: Slug.describe("Where it runs, such as vercel, fly, aws-ecs, app-store or play-store."),
91
+ environments: z.array(Slug).optional(),
92
+ })
93
+ .optional(),
94
+ status: z
95
+ .enum(["active", "dormant", "frozen", "retiring", "external"])
96
+ .describe("active: being built or changed. dormant: not started; its activities do not run. frozen: it exists and is documented, not redesigned. retiring: being replaced; its behaviour is the reference until then. external: it lives in another repository and is read here, never changed."),
97
+ replacedBy: z.array(Slug).min(1).optional().describe("For a retiring track: the tracks replacing it."),
98
+ note: Note.optional(),
99
+ });
100
+ const Api = z.strictObject({
101
+ id: Slug,
102
+ kind: z
103
+ .enum(["http", "graphql", "rpc", "websocket", "events", "in-process", "package", "cli"])
104
+ .describe("in-process: an interface inside the app, such as a repository over local storage. package and cli: the public surface of a library or a command-line tool."),
105
+ providedBy: Slug.optional().describe("The track that provides this API. Omit it for a third-party or hosted service."),
106
+ contract: z
107
+ .strictObject({
108
+ source: z
109
+ .enum(["handwritten", "openapi", "asyncapi", "graphql-schema", "protobuf", "types", "docs"])
110
+ .describe("Where the contract comes from. A generated source is the truth; any contract document is derived from it."),
111
+ location: Path.optional(),
112
+ checkDrift: z
113
+ .boolean()
114
+ .optional()
115
+ .describe("Fail CI when the derived contract document no longer matches its source."),
116
+ })
117
+ .optional(),
118
+ note: Note.optional(),
119
+ });
120
+ const Environment = z.strictObject({
121
+ id: Slug,
122
+ production: z.boolean().optional(),
123
+ url: z.string().min(1).optional(),
124
+ });
125
+ const Repo = z.strictObject({
126
+ host: z.enum(["github", "gitlab", "bitbucket", "azure-devops", "other"]).optional(),
127
+ remote: z
128
+ .string()
129
+ .min(1)
130
+ .nullable()
131
+ .optional()
132
+ .describe("The git remote, such as origin, or null for a local-only repo."),
133
+ defaultBranch: z.string().min(1).optional(),
134
+ branchNaming: z.string().min(1).optional().describe("A pattern such as feature/{ticket}-{slug}."),
135
+ commits: z.enum(["conventional", "ticket-prefix", "free"]).optional(),
136
+ mergePolicy: z
137
+ .enum(["pull-request", "local-merge"])
138
+ .optional()
139
+ .describe("pull-request is required once branch protection is on. local-merge suits only a solo, unprotected repo."),
140
+ pushEachCommit: z
141
+ .boolean()
142
+ .optional()
143
+ .describe("Push every commit as it lands, for repos whose remote is the backup."),
144
+ });
145
+ const Tracker = z.strictObject({
146
+ kind: z.enum(["none", "github", "gitlab", "linear", "jira", "other"]),
147
+ ticketPrefix: z.string().min(1).optional(),
148
+ project: z.string().min(1).optional().describe("The repo slug, project key or URL."),
149
+ });
150
+ const Commands = z.strictObject({
151
+ verify: z
152
+ .string()
153
+ .min(1)
154
+ .nullable()
155
+ .optional()
156
+ .describe("Run before any work item is called done. null or omitted means none yet; creating one is the first build item."),
157
+ test: z.string().min(1).optional(),
158
+ lint: z.string().min(1).optional(),
159
+ build: z.string().min(1).optional(),
160
+ });
161
+ const Delivery = z.strictObject({
162
+ ci: z
163
+ .enum(["none", "existing"])
164
+ .describe("existing: extend the project's pipeline. A second, parallel pipeline is never added."),
165
+ pipeline: Path.optional(),
166
+ });
167
+ const StandardsDocument = z.strictObject({
168
+ path: Path,
169
+ role: z.enum(["standard", "addendum", "checklist"]),
170
+ scope: z.array(Slug).optional().describe("Track ids this document governs. Omit for the whole project."),
171
+ });
172
+ const Standards = z.strictObject({
173
+ core: z.boolean().optional().describe("Apply Peer AI's core principles. Defaults to true."),
174
+ profiles: z.array(Slug).optional().describe("Stack profiles, such as react, express or python-fastapi."),
175
+ documents: z.array(StandardsDocument).optional(),
176
+ precedence: z
177
+ .enum(["project", "core"])
178
+ .optional()
179
+ .describe("Which side wins a conflict. Defaults to project: the project's own documents win."),
180
+ onExisting: z
181
+ .enum(["map", "revise"])
182
+ .optional()
183
+ .describe("What the Standards activity does with existing documents: map each rule to its enforcer, or revise the documents."),
184
+ overrides: z
185
+ .record(RuleId, z.strictObject({
186
+ value: z
187
+ .union([z.number(), z.string().min(1)])
188
+ .describe("The project's value in place of the profile's default."),
189
+ reason: Note,
190
+ }))
191
+ .optional()
192
+ .describe("A rule's default changed for this project, such as a larger component size limit, with the reason."),
193
+ exceptions: z
194
+ .array(z.strictObject({
195
+ rule: RuleId,
196
+ reason: Note,
197
+ decidedBy: Note.describe("The person who decided to set the rule aside."),
198
+ until: z.iso.date().optional().describe("When the exception ends, if it's temporary."),
199
+ }))
200
+ .optional()
201
+ .describe("Rules this project sets aside, each with a reason and who decided. peer-ai doctor lists them all."),
202
+ });
203
+ const Compliance = z.strictObject({
204
+ jurisdictions: z
205
+ .array(z
206
+ .string()
207
+ .regex(/^([A-Z]{2}(-[A-Z0-9]{1,3})?|[a-z][a-z0-9]*(-[a-z0-9]+)*)$/, "use an ISO 3166 code such as NG or US-CA, or a lowercase zone id such as eu or difc"))
208
+ .optional()
209
+ .describe("Where the product operates or has users."),
210
+ industries: z.array(Slug).optional().describe("Such as payments, health, food or maritime."),
211
+ packs: z
212
+ .array(Slug)
213
+ .optional()
214
+ .describe("Rule packs to apply: laws, industry standards, religious or cultural standards, labelling rules, platform policies. peer-ai assess suggests them from the jurisdictions, industries and data it finds."),
215
+ dataInventory: Path.optional().describe("Where the inventory of personal and sensitive data fields lives."),
216
+ });
217
+ const Capability = z.strictObject({
218
+ also: z.array(AddOn).optional(),
219
+ checklists: z.array(Path).optional().describe("Project checklists the skill works through as well as its own rules."),
220
+ notes: z.array(Note).optional(),
221
+ });
222
+ const ReviewChoices = z
223
+ .strictObject({
224
+ require: z
225
+ .array(z.enum(SKILL_IDS))
226
+ .optional()
227
+ .describe("Reviews every change needs, beyond the ones Peer AI works out from what it touched."),
228
+ skip: z
229
+ .array(z.strictObject({ skill: z.enum(SKILL_IDS), reason: Note }))
230
+ .optional()
231
+ .describe("Reviews this project doesn't need, each with the reason."),
232
+ })
233
+ .describe("For verify: add to or remove from the reviews each change needs (RFC 0004).");
234
+ const Activity = z.strictObject({
235
+ inputs: z.array(Path).optional().describe("Read these before anything else, and before asking the user anything."),
236
+ notes: z.array(Note).optional().describe("Project-specific instructions for this activity."),
237
+ reviews: ReviewChoices.optional(),
238
+ });
239
+ const Models = z
240
+ .strictObject({
241
+ policy: z
242
+ .enum(["none", "tiers", "pinned"])
243
+ .describe("none: no model guidance. tiers: Peer AI's tier per activity. pinned: the models named here."),
244
+ default: z.string().min(1).optional(),
245
+ byActivity: z.partialRecord(z.enum(ACTIVITY_IDS), z.string().min(1)).optional(),
246
+ ifUnavailable: z
247
+ .enum(["most-capable", "ask"])
248
+ .optional()
249
+ .describe("What to do when a pinned model is not offered. Never a weaker model for a gate."),
250
+ })
251
+ .superRefine((models, ctx) => {
252
+ if (models.policy === "pinned" && models.default === undefined) {
253
+ ctx.addIssue({ code: "custom", path: ["default"], message: "a pinned policy needs a default model" });
254
+ }
255
+ if (models.policy !== "pinned" && (models.default !== undefined || models.byActivity !== undefined)) {
256
+ ctx.addIssue({ code: "custom", path: ["policy"], message: "model names are only used with the pinned policy" });
257
+ }
258
+ });
259
+ const Skills = z.strictObject({
260
+ commit: z
261
+ .boolean()
262
+ .optional()
263
+ .describe("Commit the skills peer-ai render writes, for AI tools that can't run a setup step first. Defaults to false: they're rebuilt from the installed version and left out of git, and each tool's setup step writes them."),
264
+ });
265
+ const Gates = z.strictObject({
266
+ blockOn: z
267
+ .enum(["critical", "high", "medium"])
268
+ .optional()
269
+ .describe("The lowest open finding severity that blocks a merge. Defaults to critical."),
270
+ });
271
+ const Docs = z.strictObject({
272
+ dir: Path.optional(),
273
+ preserveStructure: z.boolean().optional().describe("Add to the existing docs; never reorganise them."),
274
+ backlog: Path.optional().describe("Where ideas outside the agreed scope go, instead of becoming work items."),
275
+ });
276
+ const configShape = {
277
+ $schema: z.string().optional(),
278
+ extends: z
279
+ .string()
280
+ .min(1)
281
+ .optional()
282
+ .describe("A shared base config to inherit, as a path or package name. The project's values win; objects merge key by key and lists are replaced."),
283
+ version: z.literal(1),
284
+ project: Project,
285
+ tools: z.array(z.enum(TOOL_IDS)).min(1).optional(),
286
+ skills: Skills.optional(),
287
+ design: Design.optional(),
288
+ tracks: z.array(Track).min(1),
289
+ apis: z.array(Api).optional(),
290
+ environments: z.array(Environment).optional(),
291
+ repo: Repo.optional(),
292
+ tracker: Tracker.optional(),
293
+ commands: Commands.optional(),
294
+ delivery: Delivery.optional(),
295
+ standards: Standards.optional(),
296
+ compliance: Compliance.optional(),
297
+ rules: z
298
+ .array(z.strictObject({ path: Path, description: Note.optional() }))
299
+ .optional()
300
+ .describe("Project rules every activity respects, wherever they are written, such as a contributing guide."),
301
+ models: Models.optional(),
302
+ gates: Gates.optional(),
303
+ capabilities: z.partialRecord(z.enum(SKILL_IDS), Capability).optional(),
304
+ activities: z.partialRecord(z.enum(ACTIVITY_IDS), Activity).optional(),
305
+ docs: Docs.optional(),
306
+ };
307
+ function checkUnique(ids, path, kind, report) {
308
+ ids.forEach((id, i) => {
309
+ if (ids.indexOf(id) !== i)
310
+ report({ path: [...path, i, "id"], message: `duplicate ${kind} id "${id}"` });
311
+ });
312
+ }
313
+ function checkRefs(refs, known, path, kind, report) {
314
+ refs?.forEach((ref, i) => {
315
+ if (!known.includes(ref))
316
+ report({ path: [...path, i], message: `"${ref}" is not ${kind}` });
317
+ });
318
+ }
319
+ function checkConfig(config, report) {
320
+ const trackIds = config.tracks.map((track) => track.id);
321
+ const apiIds = (config.apis ?? []).map((api) => api.id);
322
+ const environmentIds = (config.environments ?? []).map((environment) => environment.id);
323
+ checkUnique(trackIds, ["tracks"], "track", report);
324
+ checkUnique(apiIds, ["apis"], "api", report);
325
+ checkUnique(environmentIds, ["environments"], "environment", report);
326
+ config.tracks.forEach((track, i) => {
327
+ const at = ["tracks", i];
328
+ checkRefs(track.uses, trackIds, [...at, "uses"], "a track id", report);
329
+ checkRefs(track.replacedBy, trackIds, [...at, "replacedBy"], "a track id", report);
330
+ checkRefs(track.consumes, apiIds, [...at, "consumes"], "an api id", report);
331
+ checkRefs(track.deploy?.environments, environmentIds, [...at, "deploy", "environments"], "an environment id", report);
332
+ if (track.uses?.includes(track.id) === true || track.replacedBy?.includes(track.id) === true) {
333
+ report({ path: at, message: "a track cannot refer to itself" });
334
+ }
335
+ if (track.status === "external" && track.repo === undefined) {
336
+ report({ path: [...at, "repo"], message: "an external track names the repository it lives in" });
337
+ }
338
+ if (track.status !== "external" && track.repo !== undefined) {
339
+ report({ path: [...at, "repo"], message: "only an external track lives in another repository" });
340
+ }
341
+ if (track.status === "retiring" && track.replacedBy === undefined) {
342
+ report({ path: [...at, "replacedBy"], message: "a retiring track names the tracks replacing it" });
343
+ }
344
+ if (track.status !== "retiring" && track.replacedBy !== undefined) {
345
+ report({ path: [...at, "replacedBy"], message: "only a retiring track is replaced" });
346
+ }
347
+ });
348
+ config.apis?.forEach((api, i) => {
349
+ if (api.providedBy !== undefined && !trackIds.includes(api.providedBy)) {
350
+ report({ path: ["apis", i, "providedBy"], message: `"${api.providedBy}" is not a track id` });
351
+ }
352
+ });
353
+ config.standards?.documents?.forEach((doc, i) => {
354
+ checkRefs(doc.scope, trackIds, ["standards", "documents", i, "scope"], "a track id", report);
355
+ });
356
+ }
357
+ export const ConfigSchema = z
358
+ .strictObject(configShape)
359
+ .superRefine((config, ctx) => {
360
+ checkConfig(config, (issue) => {
361
+ ctx.addIssue({ code: "custom", ...issue });
362
+ });
363
+ })
364
+ .meta({
365
+ title: "Peer AI project config",
366
+ description: "peer-ai.config.json: a project's settings, shape, standards, rule packs and customisations.",
367
+ });
368
+ export const ConfigLayerSchema = z.strictObject(configShape).partial().meta({
369
+ title: "Peer AI shared base config",
370
+ description: "A base config that projects inherit with extends. Every key is optional.",
371
+ });
372
+ function isPlainObject(value) {
373
+ return typeof value === "object" && value !== null && !Array.isArray(value);
374
+ }
375
+ /** Merges a project's config over a base: objects merge key by key, lists and values are replaced. */
376
+ export function mergeConfigs(base, own) {
377
+ if (!isPlainObject(base) || !isPlainObject(own))
378
+ return own === undefined ? base : own;
379
+ const merged = { ...base };
380
+ for (const [key, value] of Object.entries(own))
381
+ merged[key] = mergeConfigs(base[key], value);
382
+ return merged;
383
+ }
384
+ /** Resolves a chain of configs, base first, into one config ready to validate. */
385
+ export function resolveConfig(layers) {
386
+ const resolved = layers.reduce((base, own) => mergeConfigs(base, own), {});
387
+ if (!isPlainObject(resolved))
388
+ return resolved;
389
+ const withoutExtends = { ...resolved };
390
+ delete withoutExtends.extends;
391
+ return withoutExtends;
392
+ }
package/dist/ids.d.ts ADDED
@@ -0,0 +1,67 @@
1
+ /** The activities a work item can call. They replace v0's fixed line of phases. */
2
+ export declare const ACTIVITY_IDS: readonly ["understand", "architect", "specify", "contract", "standards", "delivery-setup", "plan", "build", "verify", "test", "ship", "operate", "document", "retro"];
3
+ export type ActivityId = (typeof ACTIVITY_IDS)[number];
4
+ /** Peer AI's own skills. Each one is also a capability that config can extend with add-ons. */
5
+ export declare const SKILL_IDS: readonly ["requirements-analysis", "product-spec", "architecture", "system-design", "api-design", "data-modelling", "threat-model", "design-system", "issue-planning", "implement-ticket", "code-review", "security-review", "contract-check", "accessibility-review", "design-review", "performance-review", "reliability-review", "data-migration-review", "dependency-review", "compliance-review", "test-strategy", "qa-acceptance", "ai-feature-review", "release-readiness", "infrastructure-review", "observability-review", "incident-response", "documentation", "tech-debt-triage"];
6
+ export type SkillId = (typeof SKILL_IDS)[number];
7
+ /** What a skill produces (RFC 0004): a review report, a document, or changes to work items. */
8
+ export type SkillKind = "review" | "document" | "work";
9
+ export declare const SKILL_KINDS: Record<SkillId, SkillKind>;
10
+ /** The tools Peer AI's MCP server offers, in the order it lists them. Skills name them. */
11
+ export declare const MCP_TOOL_IDS: readonly ["project_map", "next_work", "standards_for_file", "create_work_item", "update_work_item", "run_verify", "record_review", "check_document", "advance_work_item", "draft_feedback"];
12
+ export type McpToolId = (typeof MCP_TOOL_IDS)[number];
13
+ /** The peer-ai commands. Skills name them. */
14
+ export declare const CLI_COMMAND_IDS: readonly ["init", "assess", "render", "doctor", "check", "check-report", "check-document", "feedback", "mcp"];
15
+ export type CliCommandId = (typeof CLI_COMMAND_IDS)[number];
16
+ /** AI tools Peer AI renders instructions for. */
17
+ export declare const TOOL_IDS: readonly ["claude-code", "codex", "cursor", "copilot", "gemini-cli", "other"];
18
+ export type ToolId = (typeof TOOL_IDS)[number];
19
+ /**
20
+ * What a product is or does that switches on rules only some products need (RFC 0003). A
21
+ * project lists its traits in its config; `peer-ai assess` suggests them from what it finds.
22
+ */
23
+ export declare const TRAITS: readonly ["money", "safety-critical", "several-audiences", "offline", "real-time", "uploads", "ai-features"];
24
+ export type Trait = (typeof TRAITS)[number];
25
+ /**
26
+ * The standards' domains, each with the prefix its rule ids use (RFC 0003). The last two are
27
+ * sets that apply only to a product with the trait of the same name.
28
+ */
29
+ export declare const DOMAINS: {
30
+ readonly requirements: "REQ";
31
+ readonly architecture: "ARC";
32
+ readonly "system-design": "SYS";
33
+ readonly "api-design": "API";
34
+ readonly frontend: "FE";
35
+ readonly mobile: "MOB";
36
+ readonly "design-accessibility": "DES";
37
+ readonly backend: "BE";
38
+ readonly data: "DATA";
39
+ readonly performance: "PERF";
40
+ readonly reliability: "REL";
41
+ readonly security: "SEC";
42
+ readonly "privacy-compliance": "PRIV";
43
+ readonly testing: "TEST";
44
+ readonly delivery: "DEL";
45
+ readonly operations: "OPS";
46
+ readonly "ai-features": "AI";
47
+ readonly "code-quality": "CODE";
48
+ readonly money: "MONEY";
49
+ readonly "safety-critical": "SAFE";
50
+ };
51
+ export type DomainId = keyof typeof DOMAINS;
52
+ export declare const DOMAIN_IDS: DomainId[];
53
+ /** How a rule is checked (RFC 0003): a tool, Peer AI's review with evidence, or a person's decision. */
54
+ export declare const CHECKS: readonly ["auto", "ai-review", "person"];
55
+ export type CheckKind = (typeof CHECKS)[number];
56
+ /**
57
+ * What `peer-ai assess` records on the project map. A project can add its own items
58
+ * with an `x-` prefix, such as `x-legal-review`.
59
+ */
60
+ export declare const MAP_ITEM_IDS: readonly ["requirements", "architecture", "threat-model", "specs", "api-contract", "data-model", "data-inventory", "dpia", "design", "standards", "ci", "environments", "tests", "load-testing", "infrastructure", "observability", "slos", "runbooks", "docs"];
61
+ export type KnownMapItemId = (typeof MAP_ITEM_IDS)[number];
62
+ /**
63
+ * The skills that fill each map item, in order (RFC 0004). standards, ci and environments have
64
+ * none: next_work says what to set up for them instead.
65
+ */
66
+ export declare const MAP_ITEM_SKILLS: Partial<Record<KnownMapItemId, SkillId[]>>;
67
+ export declare const CUSTOM_MAP_ITEM_PATTERN: RegExp;
package/dist/ids.js ADDED
@@ -0,0 +1,199 @@
1
+ // The fixed vocabularies the workflow is built on. Config and state are checked against
2
+ // these lists, so a misspelt activity or skill fails validation instead of being ignored.
3
+ /** The activities a work item can call. They replace v0's fixed line of phases. */
4
+ export const ACTIVITY_IDS = [
5
+ "understand",
6
+ "architect",
7
+ "specify",
8
+ "contract",
9
+ "standards",
10
+ "delivery-setup",
11
+ "plan",
12
+ "build",
13
+ "verify",
14
+ "test",
15
+ "ship",
16
+ "operate",
17
+ "document",
18
+ "retro",
19
+ ];
20
+ /** Peer AI's own skills. Each one is also a capability that config can extend with add-ons. */
21
+ export const SKILL_IDS = [
22
+ "requirements-analysis",
23
+ "product-spec",
24
+ "architecture",
25
+ "system-design",
26
+ "api-design",
27
+ "data-modelling",
28
+ "threat-model",
29
+ "design-system",
30
+ "issue-planning",
31
+ "implement-ticket",
32
+ "code-review",
33
+ "security-review",
34
+ "contract-check",
35
+ "accessibility-review",
36
+ "design-review",
37
+ "performance-review",
38
+ "reliability-review",
39
+ "data-migration-review",
40
+ "dependency-review",
41
+ "compliance-review",
42
+ "test-strategy",
43
+ "qa-acceptance",
44
+ "ai-feature-review",
45
+ "release-readiness",
46
+ "infrastructure-review",
47
+ "observability-review",
48
+ "incident-response",
49
+ "documentation",
50
+ "tech-debt-triage",
51
+ ];
52
+ export const SKILL_KINDS = {
53
+ "requirements-analysis": "document",
54
+ "product-spec": "document",
55
+ architecture: "document",
56
+ "system-design": "document",
57
+ "api-design": "document",
58
+ "data-modelling": "document",
59
+ "threat-model": "document",
60
+ "design-system": "document",
61
+ "issue-planning": "work",
62
+ "implement-ticket": "work",
63
+ "code-review": "review",
64
+ "security-review": "review",
65
+ "contract-check": "review",
66
+ "accessibility-review": "review",
67
+ "design-review": "review",
68
+ "performance-review": "review",
69
+ "reliability-review": "review",
70
+ "data-migration-review": "review",
71
+ "dependency-review": "review",
72
+ "compliance-review": "review",
73
+ "test-strategy": "document",
74
+ "qa-acceptance": "review",
75
+ "ai-feature-review": "review",
76
+ "release-readiness": "review",
77
+ "infrastructure-review": "review",
78
+ "observability-review": "review",
79
+ "incident-response": "work",
80
+ documentation: "document",
81
+ "tech-debt-triage": "work",
82
+ };
83
+ /** The tools Peer AI's MCP server offers, in the order it lists them. Skills name them. */
84
+ export const MCP_TOOL_IDS = [
85
+ "project_map",
86
+ "next_work",
87
+ "standards_for_file",
88
+ "create_work_item",
89
+ "update_work_item",
90
+ "run_verify",
91
+ "record_review",
92
+ "check_document",
93
+ "advance_work_item",
94
+ "draft_feedback",
95
+ ];
96
+ /** The peer-ai commands. Skills name them. */
97
+ export const CLI_COMMAND_IDS = [
98
+ "init",
99
+ "assess",
100
+ "render",
101
+ "doctor",
102
+ "check",
103
+ "check-report",
104
+ "check-document",
105
+ "feedback",
106
+ "mcp",
107
+ ];
108
+ /** AI tools Peer AI renders instructions for. */
109
+ export const TOOL_IDS = ["claude-code", "codex", "cursor", "copilot", "gemini-cli", "other"];
110
+ /**
111
+ * What a product is or does that switches on rules only some products need (RFC 0003). A
112
+ * project lists its traits in its config; `peer-ai assess` suggests them from what it finds.
113
+ */
114
+ export const TRAITS = [
115
+ "money",
116
+ "safety-critical",
117
+ "several-audiences",
118
+ "offline",
119
+ "real-time",
120
+ "uploads",
121
+ "ai-features",
122
+ ];
123
+ /**
124
+ * The standards' domains, each with the prefix its rule ids use (RFC 0003). The last two are
125
+ * sets that apply only to a product with the trait of the same name.
126
+ */
127
+ export const DOMAINS = {
128
+ requirements: "REQ",
129
+ architecture: "ARC",
130
+ "system-design": "SYS",
131
+ "api-design": "API",
132
+ frontend: "FE",
133
+ mobile: "MOB",
134
+ "design-accessibility": "DES",
135
+ backend: "BE",
136
+ data: "DATA",
137
+ performance: "PERF",
138
+ reliability: "REL",
139
+ security: "SEC",
140
+ "privacy-compliance": "PRIV",
141
+ testing: "TEST",
142
+ delivery: "DEL",
143
+ operations: "OPS",
144
+ "ai-features": "AI",
145
+ "code-quality": "CODE",
146
+ money: "MONEY",
147
+ "safety-critical": "SAFE",
148
+ };
149
+ export const DOMAIN_IDS = Object.keys(DOMAINS);
150
+ /** How a rule is checked (RFC 0003): a tool, Peer AI's review with evidence, or a person's decision. */
151
+ export const CHECKS = ["auto", "ai-review", "person"];
152
+ /**
153
+ * What `peer-ai assess` records on the project map. A project can add its own items
154
+ * with an `x-` prefix, such as `x-legal-review`.
155
+ */
156
+ export const MAP_ITEM_IDS = [
157
+ "requirements",
158
+ "architecture",
159
+ "threat-model",
160
+ "specs",
161
+ "api-contract",
162
+ "data-model",
163
+ "data-inventory",
164
+ "dpia",
165
+ "design",
166
+ "standards",
167
+ "ci",
168
+ "environments",
169
+ "tests",
170
+ "load-testing",
171
+ "infrastructure",
172
+ "observability",
173
+ "slos",
174
+ "runbooks",
175
+ "docs",
176
+ ];
177
+ /**
178
+ * The skills that fill each map item, in order (RFC 0004). standards, ci and environments have
179
+ * none: next_work says what to set up for them instead.
180
+ */
181
+ export const MAP_ITEM_SKILLS = {
182
+ requirements: ["requirements-analysis"],
183
+ architecture: ["architecture"],
184
+ "threat-model": ["threat-model"],
185
+ specs: ["product-spec", "system-design"],
186
+ "api-contract": ["api-design"],
187
+ "data-model": ["data-modelling"],
188
+ "data-inventory": ["compliance-review"],
189
+ dpia: ["compliance-review"],
190
+ design: ["design-system"],
191
+ tests: ["test-strategy"],
192
+ "load-testing": ["performance-review"],
193
+ infrastructure: ["infrastructure-review"],
194
+ observability: ["observability-review"],
195
+ slos: ["observability-review"],
196
+ runbooks: ["incident-response"],
197
+ docs: ["documentation"],
198
+ };
199
+ export const CUSTOM_MAP_ITEM_PATTERN = /^x-[a-z0-9]+(-[a-z0-9]+)*$/;