forge-cc 1.0.0 → 1.0.2
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/cli.js +7 -1
- package/dist/cli.js.map +1 -1
- package/package.json +3 -2
- package/dist/gates/codex-gate.d.ts +0 -51
- package/dist/gates/codex-gate.js +0 -121
- package/dist/gates/codex-gate.js.map +0 -1
- package/dist/gates/prd-gate.d.ts +0 -7
- package/dist/gates/prd-gate.js +0 -193
- package/dist/gates/prd-gate.js.map +0 -1
- package/dist/gates/remediation.d.ts +0 -46
- package/dist/gates/remediation.js +0 -423
- package/dist/gates/remediation.js.map +0 -1
- package/dist/gates/review-gate.d.ts +0 -16
- package/dist/gates/review-gate.js +0 -479
- package/dist/gates/review-gate.js.map +0 -1
- package/dist/gates/runtime-gate.d.ts +0 -5
- package/dist/gates/runtime-gate.js +0 -99
- package/dist/gates/runtime-gate.js.map +0 -1
- package/dist/gates/test-analysis.d.ts +0 -21
- package/dist/gates/test-analysis.js +0 -394
- package/dist/gates/test-analysis.js.map +0 -1
- package/dist/gates/visual-capture.d.ts +0 -24
- package/dist/gates/visual-capture.js +0 -144
- package/dist/gates/visual-capture.js.map +0 -1
- package/dist/gates/visual-gate.d.ts +0 -18
- package/dist/gates/visual-gate.js +0 -234
- package/dist/gates/visual-gate.js.map +0 -1
- package/dist/gates/visual-reviewer.d.ts +0 -11
- package/dist/gates/visual-reviewer.js +0 -211
- package/dist/gates/visual-reviewer.js.map +0 -1
- package/dist/go/auto-chain.d.ts +0 -136
- package/dist/go/auto-chain.js +0 -389
- package/dist/go/auto-chain.js.map +0 -1
- package/dist/go/executor.d.ts +0 -137
- package/dist/go/executor.js +0 -447
- package/dist/go/executor.js.map +0 -1
- package/dist/go/finalize.d.ts +0 -108
- package/dist/go/finalize.js +0 -331
- package/dist/go/finalize.js.map +0 -1
- package/dist/go/linear-sync-cli.d.ts +0 -55
- package/dist/go/linear-sync-cli.js +0 -192
- package/dist/go/linear-sync-cli.js.map +0 -1
- package/dist/go/linear-sync.d.ts +0 -112
- package/dist/go/linear-sync.js +0 -375
- package/dist/go/linear-sync.js.map +0 -1
- package/dist/go/prd-queue.d.ts +0 -43
- package/dist/go/prd-queue.js +0 -67
- package/dist/go/prd-queue.js.map +0 -1
- package/dist/go/prd-selector.d.ts +0 -57
- package/dist/go/prd-selector.js +0 -101
- package/dist/go/prd-selector.js.map +0 -1
- package/dist/go/verify-loop.d.ts +0 -64
- package/dist/go/verify-loop.js +0 -327
- package/dist/go/verify-loop.js.map +0 -1
- package/dist/hooks/pre-commit.d.ts +0 -5
- package/dist/hooks/pre-commit.js +0 -75
- package/dist/hooks/pre-commit.js.map +0 -1
- package/dist/linear/issues.d.ts +0 -22
- package/dist/linear/issues.js +0 -51
- package/dist/linear/issues.js.map +0 -1
- package/dist/linear/milestones.d.ts +0 -11
- package/dist/linear/milestones.js +0 -32
- package/dist/linear/milestones.js.map +0 -1
- package/dist/linear/projects.d.ts +0 -16
- package/dist/linear/projects.js +0 -51
- package/dist/linear/projects.js.map +0 -1
- package/dist/reporter/human.d.ts +0 -7
- package/dist/reporter/human.js +0 -93
- package/dist/reporter/human.js.map +0 -1
- package/dist/reporter/json.d.ts +0 -2
- package/dist/reporter/json.js +0 -4
- package/dist/reporter/json.js.map +0 -1
- package/dist/setup/structural-templates.d.ts +0 -12
- package/dist/setup/structural-templates.js +0 -288
- package/dist/setup/structural-templates.js.map +0 -1
- package/dist/setup/templates.d.ts +0 -17
- package/dist/setup/templates.js +0 -109
- package/dist/setup/templates.js.map +0 -1
- package/dist/setup/test-planner.d.ts +0 -38
- package/dist/setup/test-planner.js +0 -91
- package/dist/setup/test-planner.js.map +0 -1
- package/dist/setup/test-scaffold.d.ts +0 -31
- package/dist/setup/test-scaffold.js +0 -209
- package/dist/setup/test-scaffold.js.map +0 -1
- package/dist/setup/test-templates.d.ts +0 -37
- package/dist/setup/test-templates.js +0 -313
- package/dist/setup/test-templates.js.map +0 -1
- package/dist/spec/generator.d.ts +0 -34
- package/dist/spec/generator.js +0 -227
- package/dist/spec/generator.js.map +0 -1
- package/dist/spec/interview.d.ts +0 -142
- package/dist/spec/interview.js +0 -287
- package/dist/spec/interview.js.map +0 -1
- package/dist/spec/linear-sync.d.ts +0 -48
- package/dist/spec/linear-sync.js +0 -125
- package/dist/spec/linear-sync.js.map +0 -1
- package/dist/spec/scanner.d.ts +0 -79
- package/dist/spec/scanner.js +0 -566
- package/dist/spec/scanner.js.map +0 -1
- package/dist/spec/templates.d.ts +0 -375
- package/dist/spec/templates.js +0 -95
- package/dist/spec/templates.js.map +0 -1
- package/dist/state/prd-status.d.ts +0 -62
- package/dist/state/prd-status.js +0 -122
- package/dist/state/prd-status.js.map +0 -1
- package/dist/state/reader.d.ts +0 -7
- package/dist/state/reader.js +0 -43
- package/dist/state/reader.js.map +0 -1
- package/dist/state/writer.d.ts +0 -21
- package/dist/state/writer.js +0 -106
- package/dist/state/writer.js.map +0 -1
- package/dist/team/consensus.d.ts +0 -28
- package/dist/team/consensus.js +0 -130
- package/dist/team/consensus.js.map +0 -1
- package/dist/team/index.d.ts +0 -4
- package/dist/team/index.js +0 -5
- package/dist/team/index.js.map +0 -1
- package/dist/team/lifecycle.d.ts +0 -37
- package/dist/team/lifecycle.js +0 -92
- package/dist/team/lifecycle.js.map +0 -1
- package/dist/team/reviewer.d.ts +0 -10
- package/dist/team/reviewer.js +0 -345
- package/dist/team/reviewer.js.map +0 -1
- package/dist/team/types.d.ts +0 -269
- package/dist/team/types.js +0 -70
- package/dist/team/types.js.map +0 -1
- package/dist/utils/browser.d.ts +0 -10
- package/dist/utils/browser.js +0 -96
- package/dist/utils/browser.js.map +0 -1
- package/dist/utils/platform.d.ts +0 -29
- package/dist/utils/platform.js +0 -90
- package/dist/utils/platform.js.map +0 -1
- package/dist/worktree/identity.d.ts +0 -9
- package/dist/worktree/identity.js +0 -32
- package/dist/worktree/identity.js.map +0 -1
- package/dist/worktree/parallel.d.ts +0 -87
- package/dist/worktree/parallel.js +0 -328
- package/dist/worktree/parallel.js.map +0 -1
- package/dist/worktree/session.d.ts +0 -67
- package/dist/worktree/session.js +0 -194
- package/dist/worktree/session.js.map +0 -1
- package/dist/worktree/state-merge.d.ts +0 -43
- package/dist/worktree/state-merge.js +0 -162
- package/dist/worktree/state-merge.js.map +0 -1
package/dist/linear/issues.js
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { LinearClientError } from "./client.js";
|
|
2
|
-
/** Valid issue states */
|
|
3
|
-
export const ISSUE_STATES = [
|
|
4
|
-
"Backlog",
|
|
5
|
-
"Todo",
|
|
6
|
-
"In Progress",
|
|
7
|
-
"In Review",
|
|
8
|
-
"Done",
|
|
9
|
-
"Canceled",
|
|
10
|
-
];
|
|
11
|
-
/** Create an issue under a project milestone */
|
|
12
|
-
export async function createMilestoneIssue(client, input) {
|
|
13
|
-
return client.createIssue({
|
|
14
|
-
title: input.title,
|
|
15
|
-
description: input.description,
|
|
16
|
-
teamId: input.teamId,
|
|
17
|
-
projectId: input.projectId,
|
|
18
|
-
milestoneId: input.milestoneId,
|
|
19
|
-
priority: input.priority,
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
/** Resolve a state name to its UUID for a given team */
|
|
23
|
-
export async function resolveStateId(client, teamId, stateName) {
|
|
24
|
-
const states = await client.listWorkflowStates(teamId);
|
|
25
|
-
const match = states.find((s) => s.name === stateName);
|
|
26
|
-
if (!match) {
|
|
27
|
-
throw new LinearClientError(`Workflow state "${stateName}" not found for team ${teamId}`);
|
|
28
|
-
}
|
|
29
|
-
return match.id;
|
|
30
|
-
}
|
|
31
|
-
/** Transition all issues in a milestone to a target state */
|
|
32
|
-
export async function transitionMilestoneIssues(client, projectId, milestoneId, targetState, teamId) {
|
|
33
|
-
// Resolve state name to UUID
|
|
34
|
-
const stateId = await resolveStateId(client, teamId, targetState);
|
|
35
|
-
const issues = await client.listIssues({ projectId, milestoneId });
|
|
36
|
-
const updatedIssues = [];
|
|
37
|
-
for (const issue of issues) {
|
|
38
|
-
if (issue.state !== targetState) {
|
|
39
|
-
const updated = await client.updateIssue(issue.id, {
|
|
40
|
-
stateId,
|
|
41
|
-
});
|
|
42
|
-
updatedIssues.push(updated);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
return { updated: updatedIssues.length, issues: updatedIssues };
|
|
46
|
-
}
|
|
47
|
-
/** Add a progress comment to an issue */
|
|
48
|
-
export async function addProgressComment(client, issueId, message) {
|
|
49
|
-
await client.createComment(issueId, message);
|
|
50
|
-
}
|
|
51
|
-
//# sourceMappingURL=issues.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"issues.js","sourceRoot":"","sources":["../../src/linear/issues.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,iBAAiB,EAAoB,MAAM,aAAa,CAAC;AAEhF,yBAAyB;AACzB,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,SAAS;IACT,MAAM;IACN,aAAa;IACb,WAAW;IACX,MAAM;IACN,UAAU;CACF,CAAC;AAGX,gDAAgD;AAChD,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,MAAoB,EACpB,KAOC;IAED,OAAO,MAAM,CAAC,WAAW,CAAC;QACxB,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,QAAQ,EAAE,KAAK,CAAC,QAAQ;KACzB,CAAC,CAAC;AACL,CAAC;AAED,wDAAwD;AACxD,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,MAAoB,EACpB,MAAc,EACd,SAAiB;IAEjB,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC;IACvD,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC;IACvD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,iBAAiB,CACzB,mBAAmB,SAAS,wBAAwB,MAAM,EAAE,CAC7D,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC,EAAE,CAAC;AAClB,CAAC;AAED,6DAA6D;AAC7D,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,MAAoB,EACpB,SAAiB,EACjB,WAAmB,EACnB,WAAmB,EACnB,MAAc;IAEd,6BAA6B;IAC7B,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;IAElE,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC,CAAC;IAEnE,MAAM,aAAa,GAAkB,EAAE,CAAC;IACxC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,KAAK,CAAC,KAAK,KAAK,WAAW,EAAE,CAAC;YAChC,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,EAAE;gBACjD,OAAO;aACR,CAAC,CAAC;YACH,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;AAClE,CAAC;AAED,yCAAyC;AACzC,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,MAAoB,EACpB,OAAe,EACf,OAAe;IAEf,MAAM,MAAM,CAAC,aAAa,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;AAC/C,CAAC"}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { LinearClient, type LinearMilestone } from "./client.js";
|
|
2
|
-
/** Create a milestone under a project */
|
|
3
|
-
export declare function createProjectMilestone(client: LinearClient, projectId: string, name: string, description?: string, targetDate?: string): Promise<LinearMilestone>;
|
|
4
|
-
/** Get milestone progress: total and completed issue counts */
|
|
5
|
-
export declare function getMilestoneProgress(client: LinearClient, projectId: string, milestoneName: string): Promise<{
|
|
6
|
-
milestone: LinearMilestone;
|
|
7
|
-
totalIssues: number;
|
|
8
|
-
completedIssues: number;
|
|
9
|
-
}>;
|
|
10
|
-
/** Find a milestone by name within a project */
|
|
11
|
-
export declare function findMilestoneByName(client: LinearClient, projectId: string, name: string): Promise<LinearMilestone | null>;
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
/** Create a milestone under a project */
|
|
2
|
-
export async function createProjectMilestone(client, projectId, name, description, targetDate) {
|
|
3
|
-
return client.createMilestone({
|
|
4
|
-
projectId,
|
|
5
|
-
name,
|
|
6
|
-
description,
|
|
7
|
-
targetDate,
|
|
8
|
-
});
|
|
9
|
-
}
|
|
10
|
-
/** Get milestone progress: total and completed issue counts */
|
|
11
|
-
export async function getMilestoneProgress(client, projectId, milestoneName) {
|
|
12
|
-
const milestone = await findMilestoneByName(client, projectId, milestoneName);
|
|
13
|
-
if (!milestone) {
|
|
14
|
-
throw new Error(`Milestone not found: "${milestoneName}" in project ${projectId}`);
|
|
15
|
-
}
|
|
16
|
-
const issues = await client.listIssues({
|
|
17
|
-
projectId,
|
|
18
|
-
milestoneId: milestone.id,
|
|
19
|
-
});
|
|
20
|
-
const completedIssues = issues.filter((i) => i.state === "Done" || i.state === "Canceled").length;
|
|
21
|
-
return {
|
|
22
|
-
milestone,
|
|
23
|
-
totalIssues: issues.length,
|
|
24
|
-
completedIssues,
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
/** Find a milestone by name within a project */
|
|
28
|
-
export async function findMilestoneByName(client, projectId, name) {
|
|
29
|
-
const milestones = await client.listMilestones(projectId);
|
|
30
|
-
return milestones.find((m) => m.name === name) ?? null;
|
|
31
|
-
}
|
|
32
|
-
//# sourceMappingURL=milestones.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"milestones.js","sourceRoot":"","sources":["../../src/linear/milestones.ts"],"names":[],"mappings":"AAEA,yCAAyC;AACzC,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,MAAoB,EACpB,SAAiB,EACjB,IAAY,EACZ,WAAoB,EACpB,UAAmB;IAEnB,OAAO,MAAM,CAAC,eAAe,CAAC;QAC5B,SAAS;QACT,IAAI;QACJ,WAAW;QACX,UAAU;KACX,CAAC,CAAC;AACL,CAAC;AAED,+DAA+D;AAC/D,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,MAAoB,EACpB,SAAiB,EACjB,aAAqB;IAMrB,MAAM,SAAS,GAAG,MAAM,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,aAAa,CAAC,CAAC;IAC9E,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CACb,yBAAyB,aAAa,gBAAgB,SAAS,EAAE,CAClE,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC;QACrC,SAAS;QACT,WAAW,EAAE,SAAS,CAAC,EAAE;KAC1B,CAAC,CAAC;IACH,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,CACnC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,MAAM,IAAI,CAAC,CAAC,KAAK,KAAK,UAAU,CACpD,CAAC,MAAM,CAAC;IAET,OAAO;QACL,SAAS;QACT,WAAW,EAAE,MAAM,CAAC,MAAM;QAC1B,eAAe;KAChB,CAAC;AACJ,CAAC;AAED,gDAAgD;AAChD,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,MAAoB,EACpB,SAAiB,EACjB,IAAY;IAEZ,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC;IAC1D,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,IAAI,CAAC;AACzD,CAAC"}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { LinearClient, type LinearProject } from "./client.js";
|
|
2
|
-
/** Valid project states in forward-only order */
|
|
3
|
-
export declare const PROJECT_STATES: readonly ["Backlog", "Planned", "In Progress", "In Review", "Done"];
|
|
4
|
-
export type ProjectState = (typeof PROJECT_STATES)[number];
|
|
5
|
-
/**
|
|
6
|
-
* Validate that a project state transition is forward-only.
|
|
7
|
-
* Backlog -> Planned -> In Progress -> In Review -> Done.
|
|
8
|
-
* Same-state transitions are not valid (no-op).
|
|
9
|
-
*/
|
|
10
|
-
export declare function isValidTransition(from: string, to: string): boolean;
|
|
11
|
-
/** Create a new project in Backlog state (used during triage) */
|
|
12
|
-
export declare function createTriageProject(client: LinearClient, name: string, description: string, teamIds: string[]): Promise<LinearProject>;
|
|
13
|
-
/** Transition a project to a target state with forward-only validation */
|
|
14
|
-
export declare function transitionProject(client: LinearClient, projectId: string, targetState: string): Promise<LinearProject>;
|
|
15
|
-
/** Find an existing project by exact name (for dedup during triage) */
|
|
16
|
-
export declare function findProjectByName(client: LinearClient, name: string): Promise<LinearProject | null>;
|
package/dist/linear/projects.js
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { LinearClientError } from "./client.js";
|
|
2
|
-
/** Valid project states in forward-only order */
|
|
3
|
-
export const PROJECT_STATES = [
|
|
4
|
-
"Backlog",
|
|
5
|
-
"Planned",
|
|
6
|
-
"In Progress",
|
|
7
|
-
"In Review",
|
|
8
|
-
"Done",
|
|
9
|
-
];
|
|
10
|
-
/** State index map for transition validation */
|
|
11
|
-
const stateIndex = new Map(PROJECT_STATES.map((s, i) => [s, i]));
|
|
12
|
-
/**
|
|
13
|
-
* Validate that a project state transition is forward-only.
|
|
14
|
-
* Backlog -> Planned -> In Progress -> In Review -> Done.
|
|
15
|
-
* Same-state transitions are not valid (no-op).
|
|
16
|
-
*/
|
|
17
|
-
export function isValidTransition(from, to) {
|
|
18
|
-
const fromIdx = stateIndex.get(from);
|
|
19
|
-
const toIdx = stateIndex.get(to);
|
|
20
|
-
if (fromIdx === undefined || toIdx === undefined)
|
|
21
|
-
return false;
|
|
22
|
-
return toIdx > fromIdx;
|
|
23
|
-
}
|
|
24
|
-
/** Create a new project in Backlog state (used during triage) */
|
|
25
|
-
export async function createTriageProject(client, name, description, teamIds) {
|
|
26
|
-
return client.createProject({
|
|
27
|
-
name,
|
|
28
|
-
description,
|
|
29
|
-
teamIds,
|
|
30
|
-
state: "Backlog",
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
/** Transition a project to a target state with forward-only validation */
|
|
34
|
-
export async function transitionProject(client, projectId, targetState) {
|
|
35
|
-
// Fetch current project to validate transition
|
|
36
|
-
const projects = await client.listProjects();
|
|
37
|
-
const project = projects.find((p) => p.id === projectId);
|
|
38
|
-
if (!project) {
|
|
39
|
-
throw new LinearClientError(`Project not found: ${projectId}`);
|
|
40
|
-
}
|
|
41
|
-
if (!isValidTransition(project.state, targetState)) {
|
|
42
|
-
throw new LinearClientError(`Invalid project transition: ${project.state} -> ${targetState}`);
|
|
43
|
-
}
|
|
44
|
-
return client.updateProject(projectId, { state: targetState });
|
|
45
|
-
}
|
|
46
|
-
/** Find an existing project by exact name (for dedup during triage) */
|
|
47
|
-
export async function findProjectByName(client, name) {
|
|
48
|
-
const projects = await client.listProjects({ query: name });
|
|
49
|
-
return projects.find((p) => p.name === name) ?? null;
|
|
50
|
-
}
|
|
51
|
-
//# sourceMappingURL=projects.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"projects.js","sourceRoot":"","sources":["../../src/linear/projects.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,iBAAiB,EAAsB,MAAM,aAAa,CAAC;AAElF,iDAAiD;AACjD,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,SAAS;IACT,SAAS;IACT,aAAa;IACb,WAAW;IACX,MAAM;CACE,CAAC;AAGX,gDAAgD;AAChD,MAAM,UAAU,GAAG,IAAI,GAAG,CACxB,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CACrC,CAAC;AAEF;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAY,EAAE,EAAU;IACxD,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACrC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACjC,IAAI,OAAO,KAAK,SAAS,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IAC/D,OAAO,KAAK,GAAG,OAAO,CAAC;AACzB,CAAC;AAED,iEAAiE;AACjE,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,MAAoB,EACpB,IAAY,EACZ,WAAmB,EACnB,OAAiB;IAEjB,OAAO,MAAM,CAAC,aAAa,CAAC;QAC1B,IAAI;QACJ,WAAW;QACX,OAAO;QACP,KAAK,EAAE,SAAS;KACjB,CAAC,CAAC;AACL,CAAC;AAED,0EAA0E;AAC1E,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,MAAoB,EACpB,SAAiB,EACjB,WAAmB;IAEnB,+CAA+C;IAC/C,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,YAAY,EAAE,CAAC;IAC7C,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC;IACzD,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,iBAAiB,CAAC,sBAAsB,SAAS,EAAE,CAAC,CAAC;IACjE,CAAC;IACD,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,KAAK,EAAE,WAAW,CAAC,EAAE,CAAC;QACnD,MAAM,IAAI,iBAAiB,CACzB,+BAA+B,OAAO,CAAC,KAAK,OAAO,WAAW,EAAE,CACjE,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC,aAAa,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC;AACjE,CAAC;AAED,uEAAuE;AACvE,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,MAAoB,EACpB,IAAY;IAEZ,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5D,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,IAAI,CAAC;AACvD,CAAC"}
|
package/dist/reporter/human.d.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import type { PipelineResult } from "../types.js";
|
|
2
|
-
import type { Session } from "../worktree/session.js";
|
|
3
|
-
export declare function formatHumanReport(result: PipelineResult): string;
|
|
4
|
-
/**
|
|
5
|
-
* Format a human-readable sessions report as a markdown table.
|
|
6
|
-
*/
|
|
7
|
-
export declare function formatSessionsReport(sessions: Session[]): string;
|
package/dist/reporter/human.js
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
export function formatHumanReport(result) {
|
|
2
|
-
const lines = [];
|
|
3
|
-
// Header
|
|
4
|
-
const status = result.passed ? "PASSED" : "FAILED";
|
|
5
|
-
lines.push("## Verification Report");
|
|
6
|
-
lines.push(`**Status:** ${status}`);
|
|
7
|
-
lines.push(`**Iterations:** ${result.iteration}/${result.maxIterations}`);
|
|
8
|
-
const totalMs = result.gates.reduce((sum, g) => sum + g.duration_ms, 0);
|
|
9
|
-
lines.push(`**Duration:** ${formatDuration(totalMs)}`);
|
|
10
|
-
lines.push("");
|
|
11
|
-
// Gate results
|
|
12
|
-
lines.push("### Gates");
|
|
13
|
-
for (const gate of result.gates) {
|
|
14
|
-
const icon = gate.passed ? "[x]" : "[ ]";
|
|
15
|
-
const statusText = gate.passed ? "PASS" : "FAIL";
|
|
16
|
-
const dur = formatDuration(gate.duration_ms);
|
|
17
|
-
let suffix = "";
|
|
18
|
-
if (!gate.passed && gate.errors.length > 0) {
|
|
19
|
-
suffix = ` — ${gate.errors.length} error${gate.errors.length === 1 ? "" : "s"}`;
|
|
20
|
-
}
|
|
21
|
-
else if (gate.passed && gate.warnings.length > 0) {
|
|
22
|
-
suffix = ` — ${gate.warnings.length} warning${gate.warnings.length === 1 ? "" : "s"}`;
|
|
23
|
-
}
|
|
24
|
-
lines.push(`- ${icon} ${gate.gate}: ${statusText} (${dur})${suffix}`);
|
|
25
|
-
}
|
|
26
|
-
lines.push("");
|
|
27
|
-
// Errors section
|
|
28
|
-
const gatesWithErrors = result.gates.filter((g) => g.errors.length > 0);
|
|
29
|
-
if (gatesWithErrors.length > 0) {
|
|
30
|
-
lines.push("### Errors");
|
|
31
|
-
for (const gate of gatesWithErrors) {
|
|
32
|
-
lines.push(`#### ${gate.gate}`);
|
|
33
|
-
for (const err of gate.errors) {
|
|
34
|
-
const loc = err.file
|
|
35
|
-
? `${err.file}${err.line ? `:${err.line}` : ""}`
|
|
36
|
-
: "";
|
|
37
|
-
const prefix = loc ? `${loc}: ` : "";
|
|
38
|
-
lines.push(`- ${prefix}${err.message}`);
|
|
39
|
-
if (err.remediation) {
|
|
40
|
-
lines.push(` > Fix: ${err.remediation}`);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
lines.push("");
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
// Warnings section
|
|
47
|
-
const gatesWithWarnings = result.gates.filter((g) => g.warnings.length > 0);
|
|
48
|
-
if (gatesWithWarnings.length > 0) {
|
|
49
|
-
lines.push("### Warnings");
|
|
50
|
-
for (const gate of gatesWithWarnings) {
|
|
51
|
-
lines.push(`#### ${gate.gate}`);
|
|
52
|
-
for (const warning of gate.warnings) {
|
|
53
|
-
lines.push(`- ${warning}`);
|
|
54
|
-
}
|
|
55
|
-
lines.push("");
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
return lines.join("\n");
|
|
59
|
-
}
|
|
60
|
-
function formatDuration(ms) {
|
|
61
|
-
return `${(ms / 1000).toFixed(1)}s`;
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Format a human-readable sessions report as a markdown table.
|
|
65
|
-
*/
|
|
66
|
-
export function formatSessionsReport(sessions) {
|
|
67
|
-
if (sessions.length === 0) {
|
|
68
|
-
return "No active sessions.";
|
|
69
|
-
}
|
|
70
|
-
const lines = [];
|
|
71
|
-
lines.push("### Active Sessions");
|
|
72
|
-
lines.push("| Session | User | Skill | Milestone | Branch | Status | Duration | Worktree |");
|
|
73
|
-
lines.push("|---------|------|-------|-----------|--------|--------|----------|----------|");
|
|
74
|
-
for (const s of sessions) {
|
|
75
|
-
const shortId = s.id.slice(0, 8);
|
|
76
|
-
const milestone = s.milestone ?? "\u2014";
|
|
77
|
-
const elapsed = Date.now() - new Date(s.startedAt).getTime();
|
|
78
|
-
const duration = formatSessionDuration(elapsed);
|
|
79
|
-
const statusLabel = s.status === "stale" ? "stale \u26A0" : s.status;
|
|
80
|
-
lines.push(`| ${shortId} | ${s.user} | ${s.skill} | ${milestone} | ${s.branch} | ${statusLabel} | ${duration} | ${s.worktreePath} |`);
|
|
81
|
-
}
|
|
82
|
-
return lines.join("\n");
|
|
83
|
-
}
|
|
84
|
-
function formatSessionDuration(ms) {
|
|
85
|
-
const totalMinutes = Math.round(ms / 60_000);
|
|
86
|
-
if (totalMinutes < 60) {
|
|
87
|
-
return `${totalMinutes}min`;
|
|
88
|
-
}
|
|
89
|
-
const hours = Math.floor(totalMinutes / 60);
|
|
90
|
-
const minutes = totalMinutes % 60;
|
|
91
|
-
return `${hours}h${minutes > 0 ? ` ${minutes}min` : ""}`;
|
|
92
|
-
}
|
|
93
|
-
//# sourceMappingURL=human.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"human.js","sourceRoot":"","sources":["../../src/reporter/human.ts"],"names":[],"mappings":"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"}
|
package/dist/reporter/json.d.ts
DELETED
package/dist/reporter/json.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"json.js","sourceRoot":"","sources":["../../src/reporter/json.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,gBAAgB,CAAC,MAAsB;IACrD,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AACzC,CAAC"}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
export interface StructuralTestOptions {
|
|
2
|
-
sourceDir: string;
|
|
3
|
-
testDir: string;
|
|
4
|
-
entryPoints?: string[];
|
|
5
|
-
}
|
|
6
|
-
export declare function circularImportTemplate(sourceDir: string): string;
|
|
7
|
-
export declare function fileNamingTemplate(sourceDir: string): string;
|
|
8
|
-
export declare function exportBoundaryTemplate(sourceDir: string, entryPoints: string[]): string;
|
|
9
|
-
export declare function generateStructuralTests(options: StructuralTestOptions): Array<{
|
|
10
|
-
path: string;
|
|
11
|
-
content: string;
|
|
12
|
-
}>;
|
|
@@ -1,288 +0,0 @@
|
|
|
1
|
-
// ── Structural Test Templates ───────────────────────────────────────
|
|
2
|
-
// Template functions that generate self-contained structural/architectural
|
|
3
|
-
// test files. Each template returns a string of valid TypeScript test code
|
|
4
|
-
// using describe/it/expect (vitest/jest compatible).
|
|
5
|
-
// ---------------------------------------------------------------------------
|
|
6
|
-
// Template: No Circular Imports
|
|
7
|
-
// ---------------------------------------------------------------------------
|
|
8
|
-
export function circularImportTemplate(sourceDir) {
|
|
9
|
-
return `import { describe, it, expect } from "vitest";
|
|
10
|
-
import * as fs from "node:fs";
|
|
11
|
-
import * as path from "node:path";
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Structural test: detect circular import dependencies.
|
|
15
|
-
* Scans all .ts/.tsx files under the source directory, builds an adjacency
|
|
16
|
-
* list of file-level imports, and fails if any cycle is found.
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
const SOURCE_DIR = ${JSON.stringify(sourceDir)};
|
|
20
|
-
|
|
21
|
-
function collectTsFiles(dir: string): string[] {
|
|
22
|
-
const results: string[] = [];
|
|
23
|
-
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
24
|
-
for (const entry of entries) {
|
|
25
|
-
const full = path.join(dir, entry.name);
|
|
26
|
-
if (entry.isDirectory() && entry.name !== "node_modules") {
|
|
27
|
-
results.push(...collectTsFiles(full));
|
|
28
|
-
} else if (/\\.tsx?$/.test(entry.name) && !entry.name.endsWith(".d.ts")) {
|
|
29
|
-
results.push(full);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
return results;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function parseImports(filePath: string): string[] {
|
|
36
|
-
const content = fs.readFileSync(filePath, "utf-8");
|
|
37
|
-
const importRegex = /(?:import|export)\\s.*?from\\s+["'](.+?)["']/g;
|
|
38
|
-
const imports: string[] = [];
|
|
39
|
-
for (const match of content.matchAll(importRegex)) {
|
|
40
|
-
const specifier = match[1];
|
|
41
|
-
// Only resolve relative imports
|
|
42
|
-
if (!specifier.startsWith(".")) continue;
|
|
43
|
-
const dir = path.dirname(filePath);
|
|
44
|
-
let resolved = path.resolve(dir, specifier);
|
|
45
|
-
// Strip .js extension to match .ts source files
|
|
46
|
-
resolved = resolved.replace(/\\.js$/, "");
|
|
47
|
-
// Try common extensions
|
|
48
|
-
for (const ext of ["", ".ts", ".tsx", "/index.ts", "/index.tsx"]) {
|
|
49
|
-
const candidate = resolved + ext;
|
|
50
|
-
if (fs.existsSync(candidate)) {
|
|
51
|
-
imports.push(candidate);
|
|
52
|
-
break;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
return imports;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function detectCycles(
|
|
60
|
-
graph: Map<string, string[]>,
|
|
61
|
-
): string[][] {
|
|
62
|
-
const visited = new Set<string>();
|
|
63
|
-
const inStack = new Set<string>();
|
|
64
|
-
const cycles: string[][] = [];
|
|
65
|
-
|
|
66
|
-
function dfs(node: string, pathSoFar: string[]): void {
|
|
67
|
-
if (inStack.has(node)) {
|
|
68
|
-
const cycleStart = pathSoFar.indexOf(node);
|
|
69
|
-
cycles.push(pathSoFar.slice(cycleStart).concat(node));
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
if (visited.has(node)) return;
|
|
73
|
-
visited.add(node);
|
|
74
|
-
inStack.add(node);
|
|
75
|
-
const neighbors = graph.get(node) ?? [];
|
|
76
|
-
for (const neighbor of neighbors) {
|
|
77
|
-
dfs(neighbor, [...pathSoFar, node]);
|
|
78
|
-
}
|
|
79
|
-
inStack.delete(node);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
for (const node of graph.keys()) {
|
|
83
|
-
dfs(node, []);
|
|
84
|
-
}
|
|
85
|
-
return cycles;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
describe("No Circular Imports", () => {
|
|
89
|
-
it("should have no circular import dependencies", () => {
|
|
90
|
-
const absSource = path.resolve(SOURCE_DIR);
|
|
91
|
-
const files = collectTsFiles(absSource);
|
|
92
|
-
const graph = new Map<string, string[]>();
|
|
93
|
-
|
|
94
|
-
for (const file of files) {
|
|
95
|
-
graph.set(file, parseImports(file));
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
const cycles = detectCycles(graph);
|
|
99
|
-
|
|
100
|
-
if (cycles.length > 0) {
|
|
101
|
-
const formatted = cycles
|
|
102
|
-
.map((c) => c.map((f) => path.relative(absSource, f)).join(" -> "))
|
|
103
|
-
.join("\\n ");
|
|
104
|
-
expect.fail(
|
|
105
|
-
\`Found \${cycles.length} circular import(s):\\n \${formatted}\`,
|
|
106
|
-
);
|
|
107
|
-
}
|
|
108
|
-
});
|
|
109
|
-
});
|
|
110
|
-
`;
|
|
111
|
-
}
|
|
112
|
-
// ---------------------------------------------------------------------------
|
|
113
|
-
// Template: Consistent File Naming
|
|
114
|
-
// ---------------------------------------------------------------------------
|
|
115
|
-
export function fileNamingTemplate(sourceDir) {
|
|
116
|
-
return `import { describe, it, expect } from "vitest";
|
|
117
|
-
import * as fs from "node:fs";
|
|
118
|
-
import * as path from "node:path";
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Structural test: enforce consistent kebab-case file naming.
|
|
122
|
-
* All .ts/.tsx source files must use kebab-case (e.g., my-component.ts).
|
|
123
|
-
* index.ts / index.tsx files are allowed.
|
|
124
|
-
*/
|
|
125
|
-
|
|
126
|
-
const SOURCE_DIR = ${JSON.stringify(sourceDir)};
|
|
127
|
-
|
|
128
|
-
function collectTsFiles(dir: string): string[] {
|
|
129
|
-
const results: string[] = [];
|
|
130
|
-
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
131
|
-
for (const entry of entries) {
|
|
132
|
-
const full = path.join(dir, entry.name);
|
|
133
|
-
if (entry.isDirectory() && entry.name !== "node_modules") {
|
|
134
|
-
results.push(...collectTsFiles(full));
|
|
135
|
-
} else if (/\\.tsx?$/.test(entry.name) && !entry.name.endsWith(".d.ts")) {
|
|
136
|
-
results.push(full);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
return results;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/** Check if a filename (without extension) is valid kebab-case */
|
|
143
|
-
function isKebabCase(name: string): boolean {
|
|
144
|
-
return /^[a-z0-9]+(-[a-z0-9]+)*$/.test(name);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
describe("Consistent File Naming", () => {
|
|
148
|
-
it("should use kebab-case for all source file names", () => {
|
|
149
|
-
const absSource = path.resolve(SOURCE_DIR);
|
|
150
|
-
const files = collectTsFiles(absSource);
|
|
151
|
-
const violations: string[] = [];
|
|
152
|
-
|
|
153
|
-
for (const file of files) {
|
|
154
|
-
const basename = path.basename(file);
|
|
155
|
-
const nameWithoutExt = basename.replace(/\\.tsx?$/, "");
|
|
156
|
-
|
|
157
|
-
// Allow index files
|
|
158
|
-
if (nameWithoutExt === "index") continue;
|
|
159
|
-
|
|
160
|
-
if (!isKebabCase(nameWithoutExt)) {
|
|
161
|
-
violations.push(path.relative(absSource, file));
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
if (violations.length > 0) {
|
|
166
|
-
expect.fail(
|
|
167
|
-
\`Found \${violations.length} file(s) not using kebab-case:\\n \${violations.join("\\n ")}\`,
|
|
168
|
-
);
|
|
169
|
-
}
|
|
170
|
-
});
|
|
171
|
-
});
|
|
172
|
-
`;
|
|
173
|
-
}
|
|
174
|
-
// ---------------------------------------------------------------------------
|
|
175
|
-
// Template: Export Boundary Validation
|
|
176
|
-
// ---------------------------------------------------------------------------
|
|
177
|
-
export function exportBoundaryTemplate(sourceDir, entryPoints) {
|
|
178
|
-
return `import { describe, it, expect } from "vitest";
|
|
179
|
-
import * as fs from "node:fs";
|
|
180
|
-
import * as path from "node:path";
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* Structural test: validate public API export boundaries.
|
|
184
|
-
* Checks that entry point files only re-export from expected local modules,
|
|
185
|
-
* and that no internal-only modules are accidentally exposed.
|
|
186
|
-
*/
|
|
187
|
-
|
|
188
|
-
const SOURCE_DIR = ${JSON.stringify(sourceDir)};
|
|
189
|
-
const ENTRY_POINTS: string[] = ${JSON.stringify(entryPoints)};
|
|
190
|
-
|
|
191
|
-
function parseExports(filePath: string): string[] {
|
|
192
|
-
const content = fs.readFileSync(filePath, "utf-8");
|
|
193
|
-
const exportRegex = /export\\s.*?from\\s+["'](.+?)["']/g;
|
|
194
|
-
const exports: string[] = [];
|
|
195
|
-
for (const match of content.matchAll(exportRegex)) {
|
|
196
|
-
exports.push(match[1]);
|
|
197
|
-
}
|
|
198
|
-
return exports;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
describe("Export Boundary Validation", () => {
|
|
202
|
-
it("entry point files should exist", () => {
|
|
203
|
-
const absSource = path.resolve(SOURCE_DIR);
|
|
204
|
-
for (const entry of ENTRY_POINTS) {
|
|
205
|
-
const fullPath = path.resolve(absSource, entry);
|
|
206
|
-
expect(
|
|
207
|
-
fs.existsSync(fullPath),
|
|
208
|
-
\`Entry point \${entry} does not exist at \${fullPath}\`,
|
|
209
|
-
).toBe(true);
|
|
210
|
-
}
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
it("entry points should only re-export from relative paths", () => {
|
|
214
|
-
const absSource = path.resolve(SOURCE_DIR);
|
|
215
|
-
const violations: string[] = [];
|
|
216
|
-
|
|
217
|
-
for (const entry of ENTRY_POINTS) {
|
|
218
|
-
const fullPath = path.resolve(absSource, entry);
|
|
219
|
-
if (!fs.existsSync(fullPath)) continue;
|
|
220
|
-
|
|
221
|
-
const exports = parseExports(fullPath);
|
|
222
|
-
for (const specifier of exports) {
|
|
223
|
-
if (!specifier.startsWith(".")) {
|
|
224
|
-
violations.push(\`\${entry}: re-exports from non-relative "\${specifier}"\`);
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
if (violations.length > 0) {
|
|
230
|
-
expect.fail(
|
|
231
|
-
\`Found \${violations.length} unexpected re-export(s):\\n \${violations.join("\\n ")}\`,
|
|
232
|
-
);
|
|
233
|
-
}
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
it("entry points should not export from deeply nested internal paths", () => {
|
|
237
|
-
const absSource = path.resolve(SOURCE_DIR);
|
|
238
|
-
const violations: string[] = [];
|
|
239
|
-
|
|
240
|
-
for (const entry of ENTRY_POINTS) {
|
|
241
|
-
const fullPath = path.resolve(absSource, entry);
|
|
242
|
-
if (!fs.existsSync(fullPath)) continue;
|
|
243
|
-
|
|
244
|
-
const exports = parseExports(fullPath);
|
|
245
|
-
for (const specifier of exports) {
|
|
246
|
-
if (!specifier.startsWith(".")) continue;
|
|
247
|
-
// Flag paths that go more than two levels deep (e.g., ./a/b/c/internal)
|
|
248
|
-
const depth = specifier.split("/").filter((s) => s !== "." && s !== "..").length;
|
|
249
|
-
if (depth > 2) {
|
|
250
|
-
violations.push(\`\${entry}: deep internal export "\${specifier}"\`);
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
if (violations.length > 0) {
|
|
256
|
-
expect.fail(
|
|
257
|
-
\`Found \${violations.length} deep internal export(s) that may expose internals:\\n \${violations.join("\\n ")}\`,
|
|
258
|
-
);
|
|
259
|
-
}
|
|
260
|
-
});
|
|
261
|
-
});
|
|
262
|
-
`;
|
|
263
|
-
}
|
|
264
|
-
// ---------------------------------------------------------------------------
|
|
265
|
-
// Main Generator
|
|
266
|
-
// ---------------------------------------------------------------------------
|
|
267
|
-
export function generateStructuralTests(options) {
|
|
268
|
-
const { sourceDir, testDir, entryPoints } = options;
|
|
269
|
-
const results = [];
|
|
270
|
-
// Always include circular import detection and file naming
|
|
271
|
-
results.push({
|
|
272
|
-
path: `${testDir}/structural/no-circular-imports.test.ts`,
|
|
273
|
-
content: circularImportTemplate(sourceDir),
|
|
274
|
-
});
|
|
275
|
-
results.push({
|
|
276
|
-
path: `${testDir}/structural/file-naming.test.ts`,
|
|
277
|
-
content: fileNamingTemplate(sourceDir),
|
|
278
|
-
});
|
|
279
|
-
// Include export boundary validation if entry points are specified
|
|
280
|
-
if (entryPoints && entryPoints.length > 0) {
|
|
281
|
-
results.push({
|
|
282
|
-
path: `${testDir}/structural/export-boundaries.test.ts`,
|
|
283
|
-
content: exportBoundaryTemplate(sourceDir, entryPoints),
|
|
284
|
-
});
|
|
285
|
-
}
|
|
286
|
-
return results;
|
|
287
|
-
}
|
|
288
|
-
//# sourceMappingURL=structural-templates.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"structural-templates.js","sourceRoot":"","sources":["../../src/setup/structural-templates.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,2EAA2E;AAC3E,2EAA2E;AAC3E,qDAAqD;AAYrD,8EAA8E;AAC9E,gCAAgC;AAChC,8EAA8E;AAE9E,MAAM,UAAU,sBAAsB,CAAC,SAAiB;IACtD,OAAO;;;;;;;;;;qBAUY,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2F7C,CAAC;AACF,CAAC;AAED,8EAA8E;AAC9E,mCAAmC;AACnC,8EAA8E;AAE9E,MAAM,UAAU,kBAAkB,CAAC,SAAiB;IAClD,OAAO;;;;;;;;;;qBAUY,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8C7C,CAAC;AACF,CAAC;AAED,8EAA8E;AAC9E,uCAAuC;AACvC,8EAA8E;AAE9E,MAAM,UAAU,sBAAsB,CACpC,SAAiB,EACjB,WAAqB;IAErB,OAAO;;;;;;;;;;qBAUY,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC;iCACb,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyE3D,CAAC;AACF,CAAC;AAED,8EAA8E;AAC9E,iBAAiB;AACjB,8EAA8E;AAE9E,MAAM,UAAU,uBAAuB,CACrC,OAA8B;IAE9B,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC;IACpD,MAAM,OAAO,GAA6C,EAAE,CAAC;IAE7D,2DAA2D;IAC3D,OAAO,CAAC,IAAI,CAAC;QACX,IAAI,EAAE,GAAG,OAAO,yCAAyC;QACzD,OAAO,EAAE,sBAAsB,CAAC,SAAS,CAAC;KAC3C,CAAC,CAAC;IAEH,OAAO,CAAC,IAAI,CAAC;QACX,IAAI,EAAE,GAAG,OAAO,iCAAiC;QACjD,OAAO,EAAE,kBAAkB,CAAC,SAAS,CAAC;KACvC,CAAC,CAAC;IAEH,mEAAmE;IACnE,IAAI,WAAW,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1C,OAAO,CAAC,IAAI,CAAC;YACX,IAAI,EAAE,GAAG,OAAO,uCAAuC;YACvD,OAAO,EAAE,sBAAsB,CAAC,SAAS,EAAE,WAAW,CAAC;SACxD,CAAC,CAAC;IACL,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import type { TestingConfig } from "../types.js";
|
|
2
|
-
export interface SetupContext {
|
|
3
|
-
projectName: string;
|
|
4
|
-
techStack: string;
|
|
5
|
-
description: string;
|
|
6
|
-
gates: string[];
|
|
7
|
-
date: string;
|
|
8
|
-
appDir?: string;
|
|
9
|
-
testing?: TestingConfig;
|
|
10
|
-
/** forge-cc version stamped into .forge.json during setup */
|
|
11
|
-
forgeVersion?: string;
|
|
12
|
-
}
|
|
13
|
-
export declare function forgeConfigTemplate(ctx: SetupContext): string;
|
|
14
|
-
export declare function claudeMdTemplate(ctx: SetupContext): string;
|
|
15
|
-
export declare function lessonsMdTemplate(ctx: SetupContext): string;
|
|
16
|
-
export declare function globalClaudeMdTemplate(): string;
|
|
17
|
-
export declare function gitignoreForgeLines(): string;
|