@open-agent-toolkit/control-plane 0.1.66 → 0.1.72

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.
@@ -1 +1 @@
1
- {"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACV,YAAY,EACZ,cAAc,EAGf,MAAM,SAAS,CAAC;AAEjB,wBAAsB,eAAe,CACnC,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,YAAY,CAAC,CA8DvB;AAED,wBAAsB,YAAY,CAChC,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,cAAc,EAAE,CAAC,CAmF3B"}
1
+ {"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EACV,YAAY,EACZ,cAAc,EAGf,MAAM,SAAS,CAAC;AAEjB,wBAAsB,eAAe,CACnC,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,YAAY,CAAC,CA6DvB;AAED,wBAAsB,YAAY,CAChC,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,cAAc,EAAE,CAAC,CAkF3B"}
package/dist/project.js CHANGED
@@ -3,7 +3,7 @@ import { basename, dirname, isAbsolute, join, relative } from 'node:path';
3
3
  import { recommendSkill } from './recommender/router.js';
4
4
  import { scanArtifacts } from './state/artifacts.js';
5
5
  import { parseStateFrontmatter } from './state/parser.js';
6
- import { parseReviewTable, scanUnprocessedReviews } from './state/reviews.js';
6
+ import { parseReviewArtifactIdentity, parseReviewTable, scanUnprocessedReviews, } from './state/reviews.js';
7
7
  import { parseTaskProgress } from './state/tasks.js';
8
8
  export async function getProjectState(projectPath) {
9
9
  const displayPath = await resolveProjectDisplayPath(projectPath);
@@ -15,8 +15,9 @@ export async function getProjectState(projectPath) {
15
15
  scanUnprocessedReviews(projectPath),
16
16
  ]);
17
17
  const parsedState = parseStateFrontmatter(stateContent);
18
- const activeReviewArtifacts = toActiveReviewArtifacts(reviewFiles, projectPath);
19
- const reviews = mergeReviews(parseReviewTable(planContent), activeReviewArtifacts);
18
+ const tableReviews = parseReviewTable(planContent);
19
+ const activeReviewArtifacts = await toActiveReviewArtifacts(reviewFiles, projectPath, tableReviews);
20
+ const reviews = mergeReviews(tableReviews, activeReviewArtifacts);
20
21
  const progress = parseTaskProgress(planContent, implementationContent);
21
22
  const projectStateWithoutRecommendation = {
22
23
  name: basename(projectPath),
@@ -71,8 +72,9 @@ export async function listProjects(projectsRoot) {
71
72
  scanUnprocessedReviews(projectDir),
72
73
  ]);
73
74
  const parsedState = parseStateFrontmatter(stateContent);
74
- const activeReviewArtifacts = toActiveReviewArtifacts(reviewFiles, projectDir);
75
- const reviews = mergeReviews(parseReviewTable(planContent), activeReviewArtifacts);
75
+ const tableReviews = parseReviewTable(planContent);
76
+ const activeReviewArtifacts = await toActiveReviewArtifacts(reviewFiles, projectDir, tableReviews);
77
+ const reviews = mergeReviews(tableReviews, activeReviewArtifacts);
76
78
  const progress = parseTaskProgress(planContent, implementationContent);
77
79
  const stateWithoutRecommendation = {
78
80
  name: basename(projectDir),
@@ -192,10 +194,19 @@ function mergeReviews(tableReviews, activeReviewArtifacts) {
192
194
  }
193
195
  return merged;
194
196
  }
195
- function toActiveReviewArtifacts(reviewFiles, projectPath) {
196
- return reviewFiles.map((reviewFile) => ({
197
- path: relative(projectPath, reviewFile).replace(/\\/g, '/'),
198
- archived: false,
199
- actionable: true,
197
+ async function toActiveReviewArtifacts(reviewFiles, projectPath, tableReviews) {
198
+ return Promise.all(reviewFiles.map(async (reviewFile) => {
199
+ const path = relative(projectPath, reviewFile).replace(/\\/g, '/');
200
+ const identity = parseReviewArtifactIdentity(await readOptionalFile(reviewFile));
201
+ const actionable = identity !== null &&
202
+ tableReviews.some((review) => review.scope.toLowerCase() === identity.scope.toLowerCase() &&
203
+ review.type.toLowerCase() === identity.type.toLowerCase() &&
204
+ review.artifact === path &&
205
+ review.status.toLowerCase() === 'received');
206
+ return {
207
+ path,
208
+ archived: false,
209
+ actionable,
210
+ };
200
211
  }));
201
212
  }
@@ -104,7 +104,9 @@ function getPostImplementationRecommendation(state) {
104
104
  reason: 'Unprocessed review feedback exists',
105
105
  };
106
106
  }
107
- const finalReview = state.reviews.find((review) => review.scope === 'final' && review.type === 'code');
107
+ const finalReview = [...state.reviews]
108
+ .reverse()
109
+ .find((review) => review.scope === 'final' && review.type === 'code');
108
110
  if (!finalReview || finalReview.status === 'pending') {
109
111
  return {
110
112
  skill: 'oat-project-review-provide',
@@ -148,7 +150,10 @@ function hasIncompleteRevisionPhase(state) {
148
150
  return state.progress.phases.some((phase) => phase.isRevision && phase.completed < phase.total);
149
151
  }
150
152
  function hasActionableReviewArtifacts(state) {
151
- return state.activeReviewArtifacts.some((reviewArtifact) => reviewArtifact.actionable && !reviewArtifact.archived);
153
+ return state.activeReviewArtifacts.some((reviewArtifact) => reviewArtifact.actionable &&
154
+ !reviewArtifact.archived &&
155
+ state.reviews.some((review) => review.artifact === reviewArtifact.path &&
156
+ review.status.toLowerCase() === 'received'));
152
157
  }
153
158
  function getCurrentArtifact(state) {
154
159
  if (state.phase === 'decomposition') {
@@ -1,4 +1,8 @@
1
1
  import type { ReviewStatus } from '../types.js';
2
2
  export declare function parseReviewTable(planContent: string): ReviewStatus[];
3
3
  export declare function scanUnprocessedReviews(projectPath: string): Promise<string[]>;
4
+ export declare function parseReviewArtifactIdentity(content: string): {
5
+ scope: string;
6
+ type: string;
7
+ } | null;
4
8
  //# sourceMappingURL=reviews.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"reviews.d.ts","sourceRoot":"","sources":["../../src/state/reviews.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAI7C,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,YAAY,EAAE,CAepE;AAED,wBAAsB,sBAAsB,CAC1C,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,MAAM,EAAE,CAAC,CAgBnB"}
1
+ {"version":3,"file":"reviews.d.ts","sourceRoot":"","sources":["../../src/state/reviews.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAI7C,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,YAAY,EAAE,CAepE;AAED,wBAAsB,sBAAsB,CAC1C,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,MAAM,EAAE,CAAC,CAgBnB;AAED,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,MAAM,GACd;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAYxC"}
@@ -1,6 +1,7 @@
1
1
  import { readdir } from 'node:fs/promises';
2
2
  import { join } from 'node:path';
3
3
  import { isMissingFileError } from '../shared/utils/errors.js';
4
+ import { parseFrontmatterRecord } from '../shared/utils/frontmatter.js';
4
5
  const REVIEWS_HEADING = '## Reviews';
5
6
  export function parseReviewTable(planContent) {
6
7
  const reviewsSection = extractReviewsSection(planContent);
@@ -32,13 +33,23 @@ export async function scanUnprocessedReviews(projectPath) {
32
33
  throw error;
33
34
  }
34
35
  }
36
+ export function parseReviewArtifactIdentity(content) {
37
+ const frontmatter = parseFrontmatterRecord(content);
38
+ const scope = typeof frontmatter.oat_review_scope === 'string'
39
+ ? frontmatter.oat_review_scope.trim()
40
+ : '';
41
+ const type = typeof frontmatter.oat_review_type === 'string'
42
+ ? frontmatter.oat_review_type.trim()
43
+ : '';
44
+ return scope && type ? { scope, type } : null;
45
+ }
35
46
  function extractReviewsSection(planContent) {
36
- const startIndex = planContent.indexOf(REVIEWS_HEADING);
37
- if (startIndex === -1) {
47
+ const reviewsHeading = new RegExp(`^${REVIEWS_HEADING}[ \\t]*\\r?$`, 'm').exec(planContent);
48
+ if (!reviewsHeading) {
38
49
  return null;
39
50
  }
40
- const remaining = planContent.slice(startIndex + REVIEWS_HEADING.length);
41
- const nextHeadingIndex = remaining.search(/\n## /);
51
+ const remaining = planContent.slice(reviewsHeading.index + reviewsHeading[0].length);
52
+ const nextHeadingIndex = remaining.search(/^##(?!#)[ \t]+\S.*\r?$/m);
42
53
  if (nextHeadingIndex === -1) {
43
54
  return remaining.trim();
44
55
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-agent-toolkit/control-plane",
3
- "version": "0.1.66",
3
+ "version": "0.1.72",
4
4
  "private": false,
5
5
  "description": "Read-only OAT control-plane library for structured project state",
6
6
  "homepage": "https://github.com/voxmedia/open-agent-toolkit/tree/main/packages/control-plane",