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/LICENSE +21 -0
- package/README.md +129 -0
- package/dist/config.d.ts +750 -0
- package/dist/config.js +392 -0
- package/dist/ids.d.ts +67 -0
- package/dist/ids.js +199 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +51 -0
- package/dist/report.d.ts +108 -0
- package/dist/report.js +169 -0
- package/dist/state.d.ts +261 -0
- package/dist/state.js +132 -0
- package/package.json +40 -0
- package/schemas/config-layer.schema.json +997 -0
- package/schemas/config.schema.json +1002 -0
- package/schemas/map.schema.json +102 -0
- package/schemas/review-report.schema.json +312 -0
- package/schemas/work-item.schema.json +415 -0
package/dist/state.js
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { ACTIVITY_IDS, CUSTOM_MAP_ITEM_PATTERN, MAP_ITEM_IDS, SKILL_IDS } from "./ids.js";
|
|
3
|
+
// Project state lives in .peer-ai/: one map.json, plus one file per work item under
|
|
4
|
+
// .peer-ai/work/. Parallel sessions touch different files, so they no longer collide on a
|
|
5
|
+
// single state file. Which item a session is on follows from the git branch it is on.
|
|
6
|
+
const Path = z.string().min(1);
|
|
7
|
+
const Timestamp = z.iso.datetime({ offset: true });
|
|
8
|
+
export const MapItemIdSchema = z.union([
|
|
9
|
+
z.enum(MAP_ITEM_IDS),
|
|
10
|
+
z.string().regex(CUSTOM_MAP_ITEM_PATTERN, "custom map items start with x-"),
|
|
11
|
+
]);
|
|
12
|
+
const MapItem = z
|
|
13
|
+
.strictObject({
|
|
14
|
+
status: z.enum(["present", "partial", "missing", "not-applicable"]),
|
|
15
|
+
evidence: z.array(Path).optional().describe("Files or URLs that prove the status."),
|
|
16
|
+
inferred: z
|
|
17
|
+
.boolean()
|
|
18
|
+
.optional()
|
|
19
|
+
.describe("Found by reading the code, not a document, and not yet confirmed by a person."),
|
|
20
|
+
note: z.string().min(1).max(200).optional(),
|
|
21
|
+
checkedAt: Timestamp,
|
|
22
|
+
})
|
|
23
|
+
.superRefine((item, ctx) => {
|
|
24
|
+
if ((item.status === "present" || item.status === "partial") && (item.evidence ?? []).length === 0) {
|
|
25
|
+
ctx.addIssue({ code: "custom", path: ["evidence"], message: `a ${item.status} item needs evidence` });
|
|
26
|
+
}
|
|
27
|
+
if (item.status === "not-applicable" && item.note === undefined) {
|
|
28
|
+
ctx.addIssue({ code: "custom", path: ["note"], message: "say why this item does not apply" });
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
export const MapSchema = z
|
|
32
|
+
.strictObject({
|
|
33
|
+
$schema: z.string().optional(),
|
|
34
|
+
version: z.literal(1),
|
|
35
|
+
assessedAt: Timestamp,
|
|
36
|
+
items: z.partialRecord(MapItemIdSchema, MapItem),
|
|
37
|
+
})
|
|
38
|
+
.meta({
|
|
39
|
+
title: "Peer AI project map",
|
|
40
|
+
description: ".peer-ai/map.json: what the project has, what is partial, and what is missing, with evidence.",
|
|
41
|
+
});
|
|
42
|
+
const ActivityProgress = z
|
|
43
|
+
.strictObject({
|
|
44
|
+
id: z.enum(ACTIVITY_IDS),
|
|
45
|
+
status: z.enum(["pending", "in-progress", "done", "skipped"]),
|
|
46
|
+
reason: z.string().min(1).optional(),
|
|
47
|
+
})
|
|
48
|
+
.superRefine((activity, ctx) => {
|
|
49
|
+
if (activity.status === "skipped" && activity.reason === undefined) {
|
|
50
|
+
ctx.addIssue({ code: "custom", path: ["reason"], message: "a skipped activity needs a reason" });
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
export const WorkItemIdSchema = z
|
|
54
|
+
.string()
|
|
55
|
+
.regex(/^[A-Za-z0-9][A-Za-z0-9._-]*$/, "use letters, digits, dots, hyphens and underscores");
|
|
56
|
+
export const WorkItemSchema = z
|
|
57
|
+
.strictObject({
|
|
58
|
+
$schema: z.string().optional(),
|
|
59
|
+
version: z.literal(1),
|
|
60
|
+
id: WorkItemIdSchema.describe("Also the file name. Tracker keys work as they are, such as PROJ-14; a GitHub issue #42 becomes GH-42."),
|
|
61
|
+
title: z.string().min(1),
|
|
62
|
+
kind: z.enum(["feature", "bug", "refactor", "migration", "gap", "discovery", "chore"]),
|
|
63
|
+
stage: z.enum(["prepare", "build", "verify", "ship", "done", "cancelled"]),
|
|
64
|
+
track: z.string().min(1).optional(),
|
|
65
|
+
branch: z.string().min(1).optional(),
|
|
66
|
+
gap: MapItemIdSchema.optional().describe("For kind gap: the map item this work fills."),
|
|
67
|
+
goal: z
|
|
68
|
+
.string()
|
|
69
|
+
.min(1)
|
|
70
|
+
.max(500)
|
|
71
|
+
.optional()
|
|
72
|
+
.describe("What done means for this item, in a sentence or two (RFC 0005)."),
|
|
73
|
+
acceptance: z
|
|
74
|
+
.array(z.string().min(1).max(300))
|
|
75
|
+
.max(20)
|
|
76
|
+
.optional()
|
|
77
|
+
.describe("Criteria a tester could check: given a situation, when something happens, then a result anyone can see."),
|
|
78
|
+
sources: z
|
|
79
|
+
.array(Path)
|
|
80
|
+
.max(10)
|
|
81
|
+
.optional()
|
|
82
|
+
.describe("The spec, design, requirement or issue it implements: paths in the repository, or URLs."),
|
|
83
|
+
dependsOn: z.array(WorkItemIdSchema).max(20).optional().describe("Items that must reach ship before this one can."),
|
|
84
|
+
activities: z.array(ActivityProgress).optional(),
|
|
85
|
+
position: z
|
|
86
|
+
.strictObject({ activity: z.enum(ACTIVITY_IDS), step: z.number().int().positive() })
|
|
87
|
+
.optional()
|
|
88
|
+
.describe("Where work stopped, so the next session resumes exactly there."),
|
|
89
|
+
lastVerify: z.strictObject({ result: z.enum(["pass", "fail"]), at: Timestamp }).optional(),
|
|
90
|
+
reviews: z
|
|
91
|
+
.array(z.strictObject({
|
|
92
|
+
skill: z.enum(SKILL_IDS),
|
|
93
|
+
result: z.enum(["pass", "fail", "incomplete"]),
|
|
94
|
+
report: Path.optional().describe("The review report the result was worked out from."),
|
|
95
|
+
summary: z.string().min(1).max(500).optional(),
|
|
96
|
+
unproven: z
|
|
97
|
+
.boolean()
|
|
98
|
+
.optional()
|
|
99
|
+
.describe("Recorded without a report, so the result is the agent's word rather than proven."),
|
|
100
|
+
at: Timestamp,
|
|
101
|
+
}))
|
|
102
|
+
.optional(),
|
|
103
|
+
requiredReviews: z
|
|
104
|
+
.array(z.strictObject({ skill: z.enum(SKILL_IDS), reason: z.string().min(1) }))
|
|
105
|
+
.optional()
|
|
106
|
+
.describe("The reviews this work needs, worked out from what the change touched when it reached verify (RFC 0004)."),
|
|
107
|
+
reopened: z
|
|
108
|
+
.array(z.strictObject({ activity: z.enum(ACTIVITY_IDS), reason: z.string().min(1), at: Timestamp }))
|
|
109
|
+
.optional()
|
|
110
|
+
.describe("Earlier activities reopened for this item, such as a re-spec after a new decision."),
|
|
111
|
+
next: z
|
|
112
|
+
.string()
|
|
113
|
+
.min(1)
|
|
114
|
+
.max(200)
|
|
115
|
+
.describe("One line: the next action. The fuller story lives in the goal, acceptance criteria and sources."),
|
|
116
|
+
updatedAt: Timestamp,
|
|
117
|
+
})
|
|
118
|
+
.superRefine((item, ctx) => {
|
|
119
|
+
if (item.kind === "gap" && item.gap === undefined) {
|
|
120
|
+
ctx.addIssue({ code: "custom", path: ["gap"], message: "a gap work item names the map item it fills" });
|
|
121
|
+
}
|
|
122
|
+
if (item.kind !== "gap" && item.gap !== undefined) {
|
|
123
|
+
ctx.addIssue({ code: "custom", path: ["gap"], message: "only a gap work item names a map item" });
|
|
124
|
+
}
|
|
125
|
+
if (item.dependsOn?.includes(item.id) === true) {
|
|
126
|
+
ctx.addIssue({ code: "custom", path: ["dependsOn"], message: "a work item can't depend on itself" });
|
|
127
|
+
}
|
|
128
|
+
})
|
|
129
|
+
.meta({
|
|
130
|
+
title: "Peer AI work item",
|
|
131
|
+
description: ".peer-ai/work/<id>.json: one piece of work, where it stands, and what comes next.",
|
|
132
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "peer-ai-workflow",
|
|
3
|
+
"version": "1.0.0-next.0",
|
|
4
|
+
"description": "Peer AI's workflow vocabulary, and the schemas for peer-ai.config.json and project state",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"./schemas/*": "./schemas/*"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"zod": "4.6.5"
|
|
16
|
+
},
|
|
17
|
+
"engines": {
|
|
18
|
+
"node": ">=24"
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist",
|
|
22
|
+
"schemas"
|
|
23
|
+
],
|
|
24
|
+
"publishConfig": {
|
|
25
|
+
"access": "public"
|
|
26
|
+
},
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "git+https://github.com/AbuMahir980/peer-ai.git",
|
|
30
|
+
"directory": "packages/workflow"
|
|
31
|
+
},
|
|
32
|
+
"homepage": "https://github.com/AbuMahir980/peer-ai#readme",
|
|
33
|
+
"bugs": {
|
|
34
|
+
"url": "https://github.com/AbuMahir980/peer-ai/issues"
|
|
35
|
+
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"generate": "node scripts/generate-schemas.ts",
|
|
38
|
+
"build": "tsc -p tsconfig.build.json"
|
|
39
|
+
}
|
|
40
|
+
}
|