yaaw-se 0.3.9 → 0.3.10

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.
@@ -28,8 +28,8 @@
28
28
  },
29
29
  {
30
30
  "path": "payload.json",
31
- "sha256": "a0cc2f108e5025cb574d243489bdd7e0696c709261d8d17adbb0653f0f6ec142",
32
- "bytes": 56
31
+ "sha256": "2211e998c23c18c69a51c5084e54be0089740ba3a5fea80da0320a9764563064",
32
+ "bytes": 57
33
33
  },
34
34
  {
35
35
  "path": "skills/yaaw-create-spec/SKILL.md",
@@ -53,8 +53,8 @@
53
53
  },
54
54
  {
55
55
  "path": "skills/yaaw-orchestrator/SKILL.md",
56
- "sha256": "a87e912c81b5cdab3bc76aaa922f9f3092e53e689e31e1458d71dfc9f03da57e",
57
- "bytes": 666
56
+ "sha256": "981e912d418cd833e8281034f739e206894134a496766caacb97471b9577ec1c",
57
+ "bytes": 864
58
58
  },
59
59
  {
60
60
  "path": "skills/yaaw-planner/SKILL.md",
@@ -486,6 +486,11 @@
486
486
  "sha256": "27e76946abf1abde306a561621f5a7833a164f3af85db83f5db22b6809450ba6",
487
487
  "bytes": 4812
488
488
  },
489
+ {
490
+ "path": "yaaw-core/system/tools/frontmatter.mjs",
491
+ "sha256": "2c90097563a91c52e6f9c7748dac583e67967fd4a9748e393bf61d753594277e",
492
+ "bytes": 5474
493
+ },
489
494
  {
490
495
  "path": "yaaw-core/system/tools/orchestration-engine.mjs",
491
496
  "sha256": "5058465a85ef3f59d9da1159f7dd871f73930be95e7eaeeb11a1fa6cf4edd3c9",
@@ -493,8 +498,8 @@
493
498
  },
494
499
  {
495
500
  "path": "yaaw-core/system/tools/orchestration-runtime.mjs",
496
- "sha256": "25797671d0c84d2e08499854c7e6327d67e7628a786255e6c294d291c638659e",
497
- "bytes": 14566
501
+ "sha256": "e25923b994867d5dea2c6e9a7391ecd66371a042ee95a1ccc1553f963464efc7",
502
+ "bytes": 13540
498
503
  },
499
504
  {
500
505
  "path": "yaaw-core/system/tools/repository-identity.mjs",
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "schema": "yaaw.payload/v1",
3
- "version": "0.3.9"
3
+ "version": "0.3.10"
4
4
  }
@@ -11,4 +11,4 @@ INTENT: `CONTINUE`
11
11
  Resolve the YAAW workspace root, then invoke:
12
12
  `node .yaaw-core/system/tools/orchestration-runtime.mjs --workspace <WORKSPACE_ROOT> --invoke-skill yaaw-orchestrator`
13
13
 
14
- Follow `orchestration.route` until `CONTINUE_UNTIL_STOP` is satisfied or a human-input, BLOCKED, or framework stop occurs. Do not execute orchestrator semantics directly from this wrapper; every semantic execution requires the exact runtime handoff.
14
+ Follow `orchestration.route` until `CONTINUE_UNTIL_STOP` is satisfied or a human-input, BLOCKED, or framework stop occurs. A `FRAMEWORK_STOP` is terminal for the current invocation: report it once and do not re-run the unchanged runtime unless the installation version, manifest, or managed framework bytes have changed. Do not execute orchestrator semantics directly from this wrapper; every semantic execution requires the exact runtime handoff.
@@ -0,0 +1,173 @@
1
+ function indentOf(line) {
2
+ const match = String(line).match(/^ */);
3
+ return match ? match[0].length : 0;
4
+ }
5
+
6
+ function unquote(raw) {
7
+ if (raw.length >= 2 && raw.startsWith("\"") && raw.endsWith("\"")) {
8
+ try { return JSON.parse(raw); } catch { return raw.slice(1, -1); }
9
+ }
10
+ if (raw.length >= 2 && raw.startsWith("'") && raw.endsWith("'")) {
11
+ return raw.slice(1, -1).replace(/''/g, "'");
12
+ }
13
+ return raw;
14
+ }
15
+
16
+ function splitInline(raw) {
17
+ const out = [];
18
+ let start = 0, quote = null, depth = 0;
19
+ for (let i = 0; i < raw.length; i += 1) {
20
+ const ch = raw[i];
21
+ if (quote) {
22
+ if (ch === quote && raw[i - 1] !== "\\") quote = null;
23
+ continue;
24
+ }
25
+ if (ch === "\"" || ch === "'") { quote = ch; continue; }
26
+ if (ch === "[" || ch === "{") depth += 1;
27
+ else if (ch === "]" || ch === "}") depth -= 1;
28
+ else if (ch === "," && depth === 0) {
29
+ out.push(raw.slice(start, i));
30
+ start = i + 1;
31
+ }
32
+ }
33
+ out.push(raw.slice(start));
34
+ return out;
35
+ }
36
+
37
+ export function parseScalar(value) {
38
+ const raw = String(value ?? "").trim();
39
+ if (raw === "") return "";
40
+ if (raw === "null" || raw === "~") return null;
41
+ if (raw === "true") return true;
42
+ if (raw === "false") return false;
43
+ if (/^-?\d+$/.test(raw)) return Number(raw);
44
+ if (raw.startsWith("[") && raw.endsWith("]")) {
45
+ try { return JSON.parse(raw); } catch {}
46
+ const body = raw.slice(1, -1).trim();
47
+ if (!body) return [];
48
+ return splitInline(body).map(item => parseScalar(item));
49
+ }
50
+ if (raw.startsWith("{") && raw.endsWith("}")) {
51
+ try { return JSON.parse(raw); } catch {}
52
+ }
53
+ return unquote(raw);
54
+ }
55
+
56
+ function nextContent(lines, index, end) {
57
+ for (let i = index; i < end; i += 1) {
58
+ const trimmed = lines[i].trim();
59
+ if (trimmed && !trimmed.startsWith("#")) return i;
60
+ }
61
+ return end;
62
+ }
63
+
64
+ function keyValue(text) {
65
+ const index = text.indexOf(":");
66
+ if (index <= 0) return null;
67
+ return [text.slice(0, index).trim(), text.slice(index + 1).trim()];
68
+ }
69
+
70
+ function parseMapping(lines, start, end, indent) {
71
+ const out = {};
72
+ let i = start;
73
+ while (i < end) {
74
+ i = nextContent(lines, i, end);
75
+ if (i >= end) break;
76
+ const raw = lines[i], current = indentOf(raw), trimmed = raw.trim();
77
+ if (current < indent) break;
78
+ if (current > indent) throw new Error(`unexpected indentation at frontmatter line ${i + 1}`);
79
+ if (trimmed.startsWith("-")) break;
80
+ const pair = keyValue(trimmed);
81
+ if (!pair) throw new Error(`invalid frontmatter mapping at line ${i + 1}`);
82
+ const [key, rest] = pair;
83
+ if (rest !== "") {
84
+ out[key] = parseScalar(rest);
85
+ i += 1;
86
+ continue;
87
+ }
88
+ const next = nextContent(lines, i + 1, end);
89
+ if (next >= end || indentOf(lines[next]) <= indent) {
90
+ out[key] = {};
91
+ i += 1;
92
+ continue;
93
+ }
94
+ const [child, after] = parseNode(lines, next, end, indentOf(lines[next]));
95
+ out[key] = child;
96
+ i = after;
97
+ }
98
+ return [out, i];
99
+ }
100
+
101
+ function parseSequence(lines, start, end, indent) {
102
+ const out = [];
103
+ let i = start;
104
+ while (i < end) {
105
+ i = nextContent(lines, i, end);
106
+ if (i >= end) break;
107
+ const raw = lines[i], current = indentOf(raw), trimmed = raw.trim();
108
+ if (current < indent) break;
109
+ if (current > indent) throw new Error(`unexpected indentation at frontmatter line ${i + 1}`);
110
+ if (!trimmed.startsWith("-")) break;
111
+ const body = trimmed.slice(1).trim();
112
+ if (body === "") {
113
+ const next = nextContent(lines, i + 1, end);
114
+ if (next >= end || indentOf(lines[next]) <= indent) {
115
+ out.push(null);
116
+ i += 1;
117
+ continue;
118
+ }
119
+ const [child, after] = parseNode(lines, next, end, indentOf(lines[next]));
120
+ out.push(child);
121
+ i = after;
122
+ continue;
123
+ }
124
+ const pair = keyValue(body);
125
+ if (pair) {
126
+ const item = {};
127
+ const [key, rest] = pair;
128
+ item[key] = rest === "" ? {} : parseScalar(rest);
129
+ const next = nextContent(lines, i + 1, end);
130
+ if (next < end && indentOf(lines[next]) > indent) {
131
+ const childIndent = indentOf(lines[next]);
132
+ if (rest === "") {
133
+ const [child, after] = parseNode(lines, next, end, childIndent);
134
+ item[key] = child;
135
+ i = after;
136
+ } else {
137
+ const [extra, after] = parseMapping(lines, next, end, childIndent);
138
+ Object.assign(item, extra);
139
+ i = after;
140
+ }
141
+ } else {
142
+ i += 1;
143
+ }
144
+ out.push(item);
145
+ continue;
146
+ }
147
+ out.push(parseScalar(body));
148
+ i += 1;
149
+ }
150
+ return [out, i];
151
+ }
152
+
153
+ function parseNode(lines, start, end, indent) {
154
+ const index = nextContent(lines, start, end);
155
+ if (index >= end) return [{}, end];
156
+ return lines[index].trim().startsWith("-")
157
+ ? parseSequence(lines, index, end, indent)
158
+ : parseMapping(lines, index, end, indent);
159
+ }
160
+
161
+ export function parseFrontmatter(text) {
162
+ const lines = String(text).split(/\r?\n/);
163
+ if (lines[0]?.trim() !== "---") throw new Error("missing opening frontmatter");
164
+ const relativeEnd = lines.slice(1).findIndex(line => line.trim() === "---");
165
+ if (relativeEnd < 0) throw new Error("missing closing frontmatter");
166
+ const end = relativeEnd + 1;
167
+ const start = nextContent(lines, 1, end);
168
+ if (start >= end) return {};
169
+ const [value, after] = parseMapping(lines, start, end, indentOf(lines[start]));
170
+ const trailing = nextContent(lines, after, end);
171
+ if (trailing < end) throw new Error(`unsupported frontmatter syntax at line ${trailing + 1}`);
172
+ return value;
173
+ }
@@ -3,22 +3,21 @@ import {spawnSync} from "node:child_process";
3
3
  import {mkdir,readFile,readdir,rm,writeFile} from "node:fs/promises";
4
4
  import {basename,join,resolve} from "node:path";
5
5
  import {applyReconciliation,handoffBasis,planNext} from "./orchestration-engine.mjs";
6
+ import {parseFrontmatter} from "./frontmatter.mjs";
6
7
 
7
8
  const argv=process.argv.slice(2);let workspaceArg=".",mode="prepare",invokeSkill=null;
8
9
  for(let i=0;i<argv.length;i++){if(argv[i]==="--workspace"&&argv[i+1])workspaceArg=argv[++i];else if(argv[i]==="--inspect-only")mode="inspect";else if(argv[i]==="--check-handoff")mode="check";else if(argv[i]==="--reconcile-one")mode="reconcile";else if(argv[i]==="--invoke-skill"&&argv[i+1])invokeSkill=argv[++i];else{process.stderr.write("usage: orchestration-runtime.mjs [--workspace <path>] [--invoke-skill <id>] [--inspect-only|--check-handoff|--reconcile-one]\n");process.exit(2);}}
9
10
  const workspace=resolve(workspaceArg),systemRoot=join(workspace,".yaaw-core","system"),projectRoot=join(workspace,".yaaw-core","project"),runtimeRoot=join(workspace,".yaaw-core","runtime");
10
11
  const observedPath=join(runtimeRoot,"observed-state.json"),handoffPath=join(runtimeRoot,"handoff.json"),intentPath=join(runtimeRoot,"intent.json"),statePath=join(projectRoot,"state.json");
11
12
  const json=async p=>JSON.parse(await readFile(p,"utf8"));const maybe=async p=>{try{return await json(p)}catch{return null}};const uniq=a=>[...new Set(a.filter(Boolean))];
12
- function scalar(s){s=s.trim();if(s==="null")return null;if(s==="true")return true;if(s==="false")return false;if(/^-?\d+$/.test(s))return Number(s);if(s.startsWith("[")||s.startsWith("{"))try{return JSON.parse(s)}catch{}return s.replace(/^["']|["']$/g,"")}
13
- async function fm(path){try{const l=(await readFile(path,"utf8")).split(/\r?\n/);if(l[0]?.trim()!=="---")return null;const n=l.slice(1).findIndex(x=>x.trim()==="---");if(n<0)return null;const d={};for(const x of l.slice(1,n+1)){if(!x.trim()||/^\s/.test(x)||!x.includes(":"))continue;const i=x.indexOf(":");d[x.slice(0,i).trim()]=scalar(x.slice(i+1));}return d}catch{return null}}
14
- async function review(path){try{const l=(await readFile(path,"utf8")).split(/\r?\n/);const n=l.slice(1).findIndex(x=>x.trim()==="---");if(l[0]?.trim()!=="---"||n<0)return null;const d={repository:{}};let repo=false;for(const x of l.slice(1,n+1)){if(/^repository:\s*$/.test(x)){repo=true;continue}const m=x.match(/^\s{2}([A-Za-z0-9_]+):\s*(.*)$/);if(repo&&m){d.repository[m[1]]=scalar(m[2]);continue}if(!/^\S/.test(x))continue;repo=false;const t=x.match(/^([A-Za-z0-9_]+):\s*(.*)$/);if(t)d[t[1]]=scalar(t[2]);}return d}catch{return null}}
13
+ async function fm(path){try{return parseFrontmatter(await readFile(path,"utf8"))}catch{return null}}
15
14
  async function list(dir,re){try{return(await readdir(dir,{withFileTypes:true})).filter(x=>x.isFile()&&re.test(x.name)).map(x=>join(dir,x.name)).sort()}catch{return[]}}
16
15
  function run(tool,args){const r=spawnSync(process.execPath,[tool,...args],{cwd:workspace,encoding:"utf8",windowsHide:true,maxBuffer:64*1024*1024});try{return JSON.parse(r.stdout||"{}")}catch{throw new Error((r.stderr||"").trim()||"invalid JSON from "+basename(tool))}}
17
16
  function framework(raw,status=raw?.status??"UNKNOWN"){return{yaaw_version:String(raw?.package_version??"unknown"),system_schema:Number(raw?.system_schema??1),installation_schema:Number(raw?.installation_schema??1),project_schema:Number(raw?.project_schema??1),manifest_digest:String(raw?.manifest_digest??"sha256:unknown"),integrity_status:status,modified:Array.isArray(raw?.modified)?raw.modified:[],missing:Array.isArray(raw?.missing)?raw.missing:[],local_overrides:Array.isArray(raw?.local_overrides)?raw.local_overrides:[],repair_required:status!=="HEALTHY"}}
18
17
  function repo(raw){return{schema:"yaaw.repository-identity/v2",algorithm:raw?.algorithm??null,status:raw?.status??"UNAVAILABLE",workspace_scope:String(raw?.workspace_scope??"."),git_root_relation:raw?.git_root_relation??"unknown",head_commit:raw?.head_commit??null,dirty:typeof raw?.dirty==="boolean"?raw.dirty:null,worktree_digest:raw?.worktree_digest??null,components:raw?.components??null,changed_paths:Array.isArray(raw?.changed_paths)?raw.changed_paths:[],error:raw?.error??null}}
19
18
  async function contracts(){const names=["workflows","execution-policy","role-io","routing-policy","handoff-policy","artifacts","skills","transitions","reconciliation-policy"];const vals=await Promise.all(names.map(n=>json(join(systemRoot,"registries",n+".json"))));const c=Object.fromEntries(names.map((n,i)=>[n.replaceAll("-","_"),vals[i]])),errors=[];for(const[w,p]of Object.entries(c.handoff_policy.workflows??{})){const reg=c.workflows[w],ex=c.execution_policy.workflows?.[w],io=c.role_io.roles?.[reg?.role];if(!reg)errors.push("unknown workflow "+w);if(!ex)errors.push("no execution policy "+w);if(!io)errors.push("unknown role "+reg?.role);if(io){const br=(p.reads??[]).filter(x=>!io.reads.includes(x)),bw=(p.writes??[]).filter(x=>!io.writes.includes(x));if(br.length)errors.push(w+" bad reads "+br);if(bw.length)errors.push(w+" bad writes "+bw)}}return{...c,errors}}
20
19
  const rel=p=>p.replace(workspace+"/","").replaceAll("\\","/"),specId=v=>String(v??"").match(/SPEC-[0-9]+/)?.[0]??null;
21
- async function inspect(state){const pp=join(projectRoot,"product.md"),ep=join(projectRoot,"engineering.md"),specs={},tickets={},reviews=[],evidence=[];for(const p of await list(join(projectRoot,"specs"),/^SPEC-[0-9]+\.md$/)){const m=await fm(p);if(m?.id)specs[m.id]={path:rel(p),meta:m}}for(const p of await list(join(projectRoot,"tickets"),/^TASK-[0-9]+\.md$/)){const m=await fm(p);if(m?.id)tickets[m.id]={path:rel(p),meta:m}}for(const p of await list(join(projectRoot,"reviews"),/^TASK-[0-9]+-R[0-9]+\.md$/)){const v=await review(p);if(v)reviews.push({path:rel(p),value:v})}for(const p of await list(join(projectRoot,"evidence"),/\.json$/)){try{evidence.push({path:rel(p),value:await json(p)})}catch{}}return{product:{path:rel(pp),meta:await fm(pp)},engineering:{path:rel(ep),meta:await fm(ep)},specs,tickets,reviews,evidence,research:(await list(join(projectRoot,"research"),/^RSH-.*\.md$/)).map(rel),rules:(await list(join(projectRoot,"rules"),/\.md$/)).map(rel),activeSpecId:specId(state?.planning?.active_spec)}}
20
+ async function inspect(state){const pp=join(projectRoot,"product.md"),ep=join(projectRoot,"engineering.md"),specs={},tickets={},reviews=[],evidence=[];for(const p of await list(join(projectRoot,"specs"),/^SPEC-[0-9]+\.md$/)){const m=await fm(p);if(m?.id)specs[m.id]={path:rel(p),meta:m}}for(const p of await list(join(projectRoot,"tickets"),/^TASK-[0-9]+\.md$/)){const m=await fm(p);if(m?.id)tickets[m.id]={path:rel(p),meta:m}}for(const p of await list(join(projectRoot,"reviews"),/^TASK-[0-9]+-R[0-9]+\.md$/)){const v=await fm(p);if(v)reviews.push({path:rel(p),value:v})}for(const p of await list(join(projectRoot,"evidence"),/\.json$/)){try{evidence.push({path:rel(p),value:await json(p)})}catch{}}return{product:{path:rel(pp),meta:await fm(pp)},engineering:{path:rel(ep),meta:await fm(ep)},specs,tickets,reviews,evidence,research:(await list(join(projectRoot,"research"),/^RSH-.*\.md$/)).map(rel),rules:(await list(join(projectRoot,"rules"),/\.md$/)).map(rel),activeSpecId:specId(state?.planning?.active_spec)}}
22
21
  function summary(a){return{product:a.product.meta?a.product:null,engineering:a.engineering.meta?a.engineering:null,specs:a.specs,tickets:a.tickets,reviews:a.reviews.map(x=>x.path),evidence:a.evidence.map(x=>x.path)}}
23
22
  function requirement(r,repository){return r==="NONE"||r==="INSPECT"&&["READY","UNVERSIONED"].includes(repository.status)||r==="IDENTITY"&&repository.status==="READY"}
24
23
  function refs(policy,a,ticket,state){const out=[];for(const x of policy.reads??[]){if(x==="product"&&a.product.meta)out.push(a.product.path);else if(x==="engineering"&&a.engineering.meta)out.push(a.engineering.path);else if(x==="spec"){const id=specId(state?.planning?.active_spec);if(id&&a.specs[id])out.push(a.specs[id].path)}else if(x==="ticket"&&ticket&&a.tickets[ticket])out.push(a.tickets[ticket].path);else if(x==="evidence")out.push(...a.evidence.map(v=>v.path));else if(x==="review")out.push(...a.reviews.map(v=>v.path));else if(x==="engineering_research")out.push(...a.research);else if(x==="project_rule")out.push(...a.rules);else if(x==="state")out.push(".yaaw-core/project/state.json")}return uniq(out)}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yaaw-se",
3
- "version": "0.3.9",
3
+ "version": "0.3.10",
4
4
  "description": "Artifact-first autonomous software engineering workflow installer.",
5
5
  "type": "module",
6
6
  "bin": {