@cristochang/spec-core 0.1.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 OpenCode
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,107 @@
1
+ # @cristochang/spec-core
2
+
3
+ Spec data structures and validation library for OpenCode.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @cristochang/spec-core
9
+ ```
10
+
11
+ ## Overview
12
+
13
+ This package provides the core data structures and validation logic for OpenCode's Spec workflow. It includes:
14
+
15
+ - **Spec Schema** - Zod schemas for Spec data structures
16
+ - **Validator** - Business-level validation logic
17
+ - **Spec API** - Utility functions for creating and manipulating specs
18
+
19
+ ## Usage
20
+
21
+ ### Creating a Spec
22
+
23
+ ```typescript
24
+ import { Spec } from '@cristochang/spec-core'
25
+
26
+ const spec = Spec.create({
27
+ objective: "Add user authentication feature",
28
+ scope: {
29
+ inclusions: ["login page", "auth service"],
30
+ exclusions: ["social login"]
31
+ },
32
+ successCriteria: ["Users can log in with email/password", "Session management works"]
33
+ })
34
+
35
+ console.log(spec.id) // ULID
36
+ console.log(spec.status) // "pending"
37
+ console.log(spec.created) // ISO 8601 timestamp
38
+ ```
39
+
40
+ ### Validating a Spec
41
+
42
+ ```typescript
43
+ import { Spec } from '@cristochang/spec-core'
44
+
45
+ const result = Spec.validate(spec)
46
+
47
+ if (!result.valid) {
48
+ console.log("Missing fields:", result.missing)
49
+ console.log("Errors:", result.errors)
50
+ }
51
+ ```
52
+
53
+ ### Status Transitions
54
+
55
+ ```typescript
56
+ import { Spec } from '@cristochang/spec-core'
57
+
58
+ // Pending -> Approved
59
+ const approved = Spec.approve(spec)
60
+
61
+ // Approved -> Implemented
62
+ const implemented = Spec.markImplemented(approved, "All tests passing")
63
+
64
+ // Any status -> Cancelled
65
+ const cancelled = Spec.cancel(spec)
66
+ ```
67
+
68
+ ### Checking Execution Readiness
69
+
70
+ ```typescript
71
+ import { Spec } from '@cristochang/spec-core'
72
+
73
+ if (Spec.canExecute(spec)) {
74
+ console.log("Spec is ready for execution")
75
+ }
76
+ ```
77
+
78
+ ## Data Structure
79
+
80
+ ```typescript
81
+ interface SpecInfo {
82
+ // Meta (required)
83
+ id: string // ULID
84
+ created: string // ISO 8601 datetime
85
+ status: "pending" | "approved" | "implemented" | "cancelled"
86
+
87
+ // Core (required)
88
+ objective: string // Clear one-sentence objective
89
+ scope: {
90
+ inclusions: string[] // Items in scope
91
+ exclusions: string[] // Items out of scope
92
+ }
93
+ successCriteria: string[] // Verifiable success criteria
94
+
95
+ // Optional
96
+ constraints?: string[] // Technical/architectural constraints
97
+ implementationNotes?: string // Non-binding guidance
98
+
99
+ // Auto-filled
100
+ changes?: string[] // Files modified
101
+ verification?: string // Verification result
102
+ }
103
+ ```
104
+
105
+ ## License
106
+
107
+ MIT
@@ -0,0 +1,38 @@
1
+ import type { SpecInfo, SpecStatus } from "./schema";
2
+ import { Validator } from "./validator";
3
+ export { SpecInfo, SpecStatus } from "./schema";
4
+ export { Validator } from "./validator";
5
+ /**
6
+ * Spec namespace containing all spec-related utilities
7
+ */
8
+ export declare namespace Spec {
9
+ /**
10
+ * Create a new spec with default values
11
+ */
12
+ function create(input: Partial<SpecInfo> & Pick<SpecInfo, "objective" | "scope" | "successCriteria">): SpecInfo;
13
+ /**
14
+ * Validate a spec (convenience alias)
15
+ */
16
+ function validate(spec: SpecInfo): Validator.ValidationResult;
17
+ /**
18
+ * Check if a spec can be executed (convenience alias)
19
+ */
20
+ function canExecute(spec: SpecInfo): boolean;
21
+ /**
22
+ * Update spec status
23
+ */
24
+ function updateStatus(spec: SpecInfo, status: SpecStatus): SpecInfo;
25
+ /**
26
+ * Approve a spec
27
+ */
28
+ function approve(spec: SpecInfo): SpecInfo;
29
+ /**
30
+ * Mark a spec as implemented
31
+ */
32
+ function markImplemented(spec: SpecInfo, verification?: string): SpecInfo;
33
+ /**
34
+ * Cancel a spec
35
+ */
36
+ function cancel(spec: SpecInfo): SpecInfo;
37
+ }
38
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAEvC,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAEvC;;GAEG;AACH,yBAAiB,IAAI,CAAC;IACpB;;OAEG;IACH,SAAgB,MAAM,CACpB,KAAK,EAAE,OAAO,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,QAAQ,EAAE,WAAW,GAAG,OAAO,GAAG,iBAAiB,CAAC,GACnF,QAAQ,CAaV;IAED;;OAEG;IACH,SAAgB,QAAQ,CAAC,IAAI,EAAE,QAAQ,8BAEtC;IAED;;OAEG;IACH,SAAgB,UAAU,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAElD;IAED;;OAEG;IACH,SAAgB,YAAY,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,GAAG,QAAQ,CAEzE;IAED;;OAEG;IACH,SAAgB,OAAO,CAAC,IAAI,EAAE,QAAQ,GAAG,QAAQ,CAEhD;IAED;;OAEG;IACH,SAAgB,eAAe,CAAC,IAAI,EAAE,QAAQ,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,QAAQ,CAM/E;IAED;;OAEG;IACH,SAAgB,MAAM,CAAC,IAAI,EAAE,QAAQ,GAAG,QAAQ,CAE/C;CACF"}
package/dist/index.js ADDED
@@ -0,0 +1,101 @@
1
+ // src/index.ts
2
+ import { ulid } from "ulid";
3
+
4
+ // src/validator.ts
5
+ var Validator;
6
+ ((Validator2) => {
7
+ function validate(spec) {
8
+ const missing = [];
9
+ const errors = [];
10
+ if (!spec.objective?.trim()) missing.push("objective");
11
+ if (!spec.successCriteria?.length) missing.push("successCriteria");
12
+ return {
13
+ valid: missing.length === 0 && errors.length === 0,
14
+ missing,
15
+ errors
16
+ };
17
+ }
18
+ Validator2.validate = validate;
19
+ function canExecute(spec) {
20
+ return spec.status === "approved" && validate(spec).valid;
21
+ }
22
+ Validator2.canExecute = canExecute;
23
+ })(Validator || (Validator = {}));
24
+
25
+ // src/schema.ts
26
+ import z from "zod";
27
+ var SpecStatusSchema = z.enum(["pending", "approved", "implemented", "cancelled"]);
28
+ var SpecInfo = z.object({
29
+ // Meta (required)
30
+ id: z.string().describe("Unique identifier for the spec (ULID)"),
31
+ created: z.string().datetime().describe("ISO 8601 timestamp of creation (Zod validated)"),
32
+ status: SpecStatusSchema.describe("Current status of the spec"),
33
+ // Core (required)
34
+ objective: z.string().describe("Clear, one-sentence objective"),
35
+ scope: z.object({
36
+ inclusions: z.array(z.string()).min(1).describe("Items to include in scope (at least one)"),
37
+ exclusions: z.array(z.string()).min(0).describe("Items explicitly excluded from scope")
38
+ }).describe("Scope boundaries"),
39
+ successCriteria: z.array(z.string()).describe("Verifiable success criteria"),
40
+ // Optional
41
+ constraints: z.array(z.string()).optional().describe("Technical or architectural constraints"),
42
+ implementationNotes: z.string().optional().describe("Non-binding implementation guidance for AI; not a requirement"),
43
+ // Auto-filled (optional)
44
+ changes: z.array(z.string().min(1)).optional().describe("List of files modified (auto-filled, non-empty strings)"),
45
+ verification: z.string().optional().describe("Verification result (auto-filled)")
46
+ }).meta({
47
+ ref: "Spec"
48
+ });
49
+
50
+ // src/index.ts
51
+ var Spec;
52
+ ((Spec2) => {
53
+ function create(input) {
54
+ return {
55
+ id: input.id ?? ulid(),
56
+ created: input.created ?? (/* @__PURE__ */ new Date()).toISOString(),
57
+ status: input.status ?? "pending",
58
+ objective: input.objective,
59
+ scope: input.scope,
60
+ successCriteria: input.successCriteria,
61
+ constraints: input.constraints,
62
+ implementationNotes: input.implementationNotes,
63
+ changes: input.changes,
64
+ verification: input.verification
65
+ };
66
+ }
67
+ Spec2.create = create;
68
+ function validate(spec) {
69
+ return Validator.validate(spec);
70
+ }
71
+ Spec2.validate = validate;
72
+ function canExecute(spec) {
73
+ return Validator.canExecute(spec);
74
+ }
75
+ Spec2.canExecute = canExecute;
76
+ function updateStatus(spec, status) {
77
+ return { ...spec, status };
78
+ }
79
+ Spec2.updateStatus = updateStatus;
80
+ function approve(spec) {
81
+ return updateStatus(spec, "approved");
82
+ }
83
+ Spec2.approve = approve;
84
+ function markImplemented(spec, verification) {
85
+ return {
86
+ ...spec,
87
+ status: "implemented",
88
+ verification: verification ?? spec.verification
89
+ };
90
+ }
91
+ Spec2.markImplemented = markImplemented;
92
+ function cancel(spec) {
93
+ return updateStatus(spec, "cancelled");
94
+ }
95
+ Spec2.cancel = cancel;
96
+ })(Spec || (Spec = {}));
97
+ export {
98
+ Spec,
99
+ SpecInfo,
100
+ Validator
101
+ };
@@ -0,0 +1,12 @@
1
+ import z from "zod";
2
+ /**
3
+ * Spec status values
4
+ */
5
+ export declare const SpecStatusSchema: any;
6
+ export type SpecStatus = z.infer<typeof SpecStatusSchema>;
7
+ /**
8
+ * Main Spec data structure
9
+ */
10
+ export declare const SpecInfo: any;
11
+ export type SpecInfo = z.infer<typeof SpecInfo>;
12
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,MAAM,KAAK,CAAA;AAEnB;;GAEG;AACH,eAAO,MAAM,gBAAgB,KAA8D,CAAA;AAC3F,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAA;AAEzD;;GAEG;AACH,eAAO,MAAM,QAAQ,KAoCjB,CAAA;AAEJ,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,QAAQ,CAAC,CAAA"}
package/dist/schema.js ADDED
@@ -0,0 +1,42 @@
1
+ import z from "zod";
2
+ /**
3
+ * Spec status values
4
+ */
5
+ export const SpecStatusSchema = z.enum(["pending", "approved", "implemented", "cancelled"]);
6
+ /**
7
+ * Main Spec data structure
8
+ */
9
+ export const SpecInfo = z
10
+ .object({
11
+ // Meta (required)
12
+ id: z.string().describe("Unique identifier for the spec (ULID)"),
13
+ created: z
14
+ .string()
15
+ .datetime()
16
+ .describe("ISO 8601 timestamp of creation (Zod validated)"),
17
+ status: SpecStatusSchema.describe("Current status of the spec"),
18
+ // Core (required)
19
+ objective: z.string().describe("Clear, one-sentence objective"),
20
+ scope: z
21
+ .object({
22
+ inclusions: z.array(z.string()).min(1).describe("Items to include in scope (at least one)"),
23
+ exclusions: z.array(z.string()).min(0).describe("Items explicitly excluded from scope"),
24
+ })
25
+ .describe("Scope boundaries"),
26
+ successCriteria: z.array(z.string()).describe("Verifiable success criteria"),
27
+ // Optional
28
+ constraints: z.array(z.string()).optional().describe("Technical or architectural constraints"),
29
+ implementationNotes: z
30
+ .string()
31
+ .optional()
32
+ .describe("Non-binding implementation guidance for AI; not a requirement"),
33
+ // Auto-filled (optional)
34
+ changes: z
35
+ .array(z.string().min(1))
36
+ .optional()
37
+ .describe("List of files modified (auto-filled, non-empty strings)"),
38
+ verification: z.string().optional().describe("Verification result (auto-filled)"),
39
+ })
40
+ .meta({
41
+ ref: "Spec",
42
+ });
@@ -0,0 +1,21 @@
1
+ import type { SpecInfo } from "./schema";
2
+ export declare namespace Validator {
3
+ interface ValidationResult {
4
+ valid: boolean;
5
+ missing: string[];
6
+ errors: string[];
7
+ }
8
+ /**
9
+ * Validate Spec's business-level completeness
10
+ *
11
+ * Note: Basic data format validation is done by Zod schema during parse
12
+ * This only handles business logic related validation
13
+ */
14
+ function validate(spec: SpecInfo): ValidationResult;
15
+ /**
16
+ * Check if Spec can be executed
17
+ * Requires: status is "approved" and business-level validation passes
18
+ */
19
+ function canExecute(spec: SpecInfo): boolean;
20
+ }
21
+ //# sourceMappingURL=validator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../src/validator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAExC,yBAAiB,SAAS,CAAC;IACzB,UAAiB,gBAAgB;QAC/B,KAAK,EAAE,OAAO,CAAA;QACd,OAAO,EAAE,MAAM,EAAE,CAAA;QACjB,MAAM,EAAE,MAAM,EAAE,CAAA;KACjB;IAED;;;;;OAKG;IACH,SAAgB,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,gBAAgB,CAezD;IAED;;;OAGG;IACH,SAAgB,UAAU,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAElD;CACF"}
@@ -0,0 +1,33 @@
1
+ export var Validator;
2
+ (function (Validator) {
3
+ /**
4
+ * Validate Spec's business-level completeness
5
+ *
6
+ * Note: Basic data format validation is done by Zod schema during parse
7
+ * This only handles business logic related validation
8
+ */
9
+ function validate(spec) {
10
+ const missing = [];
11
+ const errors = [];
12
+ // Business-level checks: non-empty validation (Zod already ensures arrays exist, here we check content)
13
+ if (!spec.objective?.trim())
14
+ missing.push("objective");
15
+ if (!spec.successCriteria?.length)
16
+ missing.push("successCriteria");
17
+ // Zod already ensures status is a valid value, no need to re-validate
18
+ return {
19
+ valid: missing.length === 0 && errors.length === 0,
20
+ missing,
21
+ errors,
22
+ };
23
+ }
24
+ Validator.validate = validate;
25
+ /**
26
+ * Check if Spec can be executed
27
+ * Requires: status is "approved" and business-level validation passes
28
+ */
29
+ function canExecute(spec) {
30
+ return spec.status === "approved" && validate(spec).valid;
31
+ }
32
+ Validator.canExecute = canExecute;
33
+ })(Validator || (Validator = {}));
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@cristochang/spec-core",
3
+ "version": "0.1.0",
4
+ "description": "Spec data structures and validation for OpenCode",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ },
13
+ "./schema": {
14
+ "types": "./dist/schema.d.ts",
15
+ "import": "./dist/schema.js"
16
+ },
17
+ "./validator": {
18
+ "types": "./dist/validator.d.ts",
19
+ "import": "./dist/validator.js"
20
+ }
21
+ },
22
+ "files": [
23
+ "dist",
24
+ "README.md",
25
+ "LICENSE"
26
+ ],
27
+ "scripts": {
28
+ "build": "bun run build:js && bun run build:dts",
29
+ "build:js": "bunx esbuild src/index.ts --bundle --platform=node --format=esm --outfile=dist/index.js --external:zod --external:ulid",
30
+ "build:dts": "bunx tsc --emitDeclarationOnly",
31
+ "build:npm": "npx esbuild src/index.ts --bundle --platform=node --format=esm --outfile=dist/index.js --external:zod --external:ulid && npx typescript --emitDeclarationOnly",
32
+ "test": "bun test"
33
+ },
34
+ "keywords": [
35
+ "opencode",
36
+ "spec",
37
+ "validation",
38
+ "schema",
39
+ "workflow"
40
+ ],
41
+ "author": "OpenCode",
42
+ "license": "MIT",
43
+ "dependencies": {
44
+ "zod": "^3.0.0",
45
+ "ulid": "^2.3.0"
46
+ },
47
+ "devDependencies": {
48
+ "@types/node": "22.13.9",
49
+ "esbuild": "0.27.2",
50
+ "typescript": "^5.0.0"
51
+ },
52
+ "engines": {
53
+ "node": ">=18.0.0"
54
+ }
55
+ }