@llm4ts/flow 0.2.2 → 0.3.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/dist/Package.d.ts +2 -0
- package/dist/Package.d.ts.map +1 -1
- package/dist/Package.js +6 -0
- package/dist/Package.js.map +1 -1
- package/dist/Patterns.d.ts +38 -0
- package/dist/Patterns.d.ts.map +1 -0
- package/dist/Patterns.js +80 -0
- package/dist/Patterns.js.map +1 -0
- package/dist/Wall.d.ts +30 -0
- package/dist/Wall.d.ts.map +1 -0
- package/dist/Wall.js +33 -0
- package/dist/Wall.js.map +1 -0
- package/package.json +4 -2
- package/src/Package.ts +12 -0
- package/src/Patterns.ts +91 -0
- package/src/Wall.ts +42 -0
package/dist/Package.d.ts
CHANGED
package/dist/Package.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Package.d.ts","sourceRoot":"","sources":["../src/Package.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Package.d.ts","sourceRoot":"","sources":["../src/Package.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,WAAW,iBAAiB,CAAA;AAEzC,8EAA8E;AAC9E,eAAO,MAAM,cAAc,EAAE,MAAyB,CAAA"}
|
package/dist/Package.js
CHANGED
|
@@ -1,2 +1,8 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import * as Schema from "effect/Schema";
|
|
3
|
+
const Manifest = Schema.Struct({ version: Schema.String });
|
|
4
|
+
const manifest = Schema.decodeUnknownSync(Manifest)(createRequire(import.meta.url)("../package.json"));
|
|
1
5
|
export const packageName = "@llm4ts/flow";
|
|
6
|
+
/** The published `@llm4ts/flow` version, recorded in provenance manifests. */
|
|
7
|
+
export const packageVersion = manifest.version;
|
|
2
8
|
//# sourceMappingURL=Package.js.map
|
package/dist/Package.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Package.js","sourceRoot":"","sources":["../src/Package.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,WAAW,GAAG,cAAc,CAAA"}
|
|
1
|
+
{"version":3,"file":"Package.js","sourceRoot":"","sources":["../src/Package.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAEvC,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAA;AAE1D,MAAM,QAAQ,GAAG,MAAM,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CACjD,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,iBAAiB,CAAC,CAClD,CAAA;AAED,MAAM,CAAC,MAAM,WAAW,GAAG,cAAc,CAAA;AAEzC,8EAA8E;AAC9E,MAAM,CAAC,MAAM,cAAc,GAAW,QAAQ,CAAC,OAAO,CAAA"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Schema from "effect/Schema";
|
|
3
|
+
import type { WorkspaceError, WorkspaceShape } from "./Workspace.ts";
|
|
4
|
+
declare const PatternCard_base: Schema.Class<PatternCard, Schema.Struct<{
|
|
5
|
+
readonly id: Schema.String;
|
|
6
|
+
readonly matches: Schema.String;
|
|
7
|
+
readonly body: Schema.String;
|
|
8
|
+
}>, {}>;
|
|
9
|
+
/**
|
|
10
|
+
* One reusable modernization pattern: a legacy idiom, its target translation,
|
|
11
|
+
* and the known traps — knowledge as data, like packs and lessons, but
|
|
12
|
+
* universal and curated rather than estate-local and run-accumulated.
|
|
13
|
+
* `matches` is a regex over legacy source: extraction tags each program's
|
|
14
|
+
* traceability fragment with the ids of the cards that hit, and implementation
|
|
15
|
+
* injects only the cited cards into the coder's brief — deterministic
|
|
16
|
+
* selection, bounded context.
|
|
17
|
+
*/
|
|
18
|
+
export declare class PatternCard extends PatternCard_base {
|
|
19
|
+
}
|
|
20
|
+
export declare const parsePatternCard: (id: string, text: string) => PatternCard;
|
|
21
|
+
/**
|
|
22
|
+
* All `*.md` cards under `directory`, sorted by id (the filename stem). An
|
|
23
|
+
* absent directory is an empty set — patterns are optional everywhere.
|
|
24
|
+
*/
|
|
25
|
+
export declare const loadPatternCards: (workspace: WorkspaceShape, directory: string) => Effect.Effect<readonly PatternCard[], WorkspaceError, never>;
|
|
26
|
+
/**
|
|
27
|
+
* The cards whose `matches` regex hits anywhere in `source` — how extraction
|
|
28
|
+
* decides which patterns a program embodies. A card with no regex never
|
|
29
|
+
* matches; an invalid regex is skipped rather than failing the run.
|
|
30
|
+
*/
|
|
31
|
+
export declare const matchingPatternCards: (source: string, cards: ReadonlyArray<PatternCard>) => ReadonlyArray<PatternCard>;
|
|
32
|
+
/**
|
|
33
|
+
* Every `PAT-…` id cited in a spec text, in order of first appearance — how
|
|
34
|
+
* implementation finds the cards its current task's specs were tagged with.
|
|
35
|
+
*/
|
|
36
|
+
export declare const taggedPatternIds: (spec: string) => ReadonlyArray<string>;
|
|
37
|
+
export {};
|
|
38
|
+
//# sourceMappingURL=Patterns.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Patterns.d.ts","sourceRoot":"","sources":["../src/Patterns.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;;;;;;AAEpE;;;;;;;;GAQG;AACH,qBAAa,WAAY,SAAQ,gBAI/B;CAAG;AAYL,eAAO,MAAM,gBAAgB,GAAI,IAAI,MAAM,EAAE,MAAM,MAAM,KAAG,WAe3D,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,gBAAgB,gHAY3B,CAAA;AAEF;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAC/B,QAAQ,MAAM,EACd,OAAO,aAAa,CAAC,WAAW,CAAC,KAChC,aAAa,CAAC,WAAW,CAUxB,CAAA;AAEJ;;;GAGG;AACH,eAAO,MAAM,gBAAgB,GAAI,MAAM,MAAM,KAAG,aAAa,CAAC,MAAM,CAEnE,CAAA"}
|
package/dist/Patterns.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Schema from "effect/Schema";
|
|
3
|
+
/**
|
|
4
|
+
* One reusable modernization pattern: a legacy idiom, its target translation,
|
|
5
|
+
* and the known traps — knowledge as data, like packs and lessons, but
|
|
6
|
+
* universal and curated rather than estate-local and run-accumulated.
|
|
7
|
+
* `matches` is a regex over legacy source: extraction tags each program's
|
|
8
|
+
* traceability fragment with the ids of the cards that hit, and implementation
|
|
9
|
+
* injects only the cited cards into the coder's brief — deterministic
|
|
10
|
+
* selection, bounded context.
|
|
11
|
+
*/
|
|
12
|
+
export class PatternCard extends Schema.Class("PatternCard")({
|
|
13
|
+
id: Schema.String,
|
|
14
|
+
matches: Schema.String,
|
|
15
|
+
body: Schema.String
|
|
16
|
+
}) {
|
|
17
|
+
}
|
|
18
|
+
const frontmatterField = (front, key) => {
|
|
19
|
+
for (const line of front.split(/\r?\n/)) {
|
|
20
|
+
const index = line.indexOf(":");
|
|
21
|
+
if (index > 0 && line.slice(0, index).trim() === key) {
|
|
22
|
+
return line.slice(index + 1).trim();
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return undefined;
|
|
26
|
+
};
|
|
27
|
+
export const parsePatternCard = (id, text) => {
|
|
28
|
+
const stripped = text.trim();
|
|
29
|
+
if (!stripped.startsWith("---")) {
|
|
30
|
+
return PatternCard.make({ id, matches: "", body: stripped });
|
|
31
|
+
}
|
|
32
|
+
const rest = stripped.slice(3);
|
|
33
|
+
const end = /^---[ \t]*$/m.exec(rest);
|
|
34
|
+
if (end === null) {
|
|
35
|
+
return PatternCard.make({ id, matches: "", body: stripped });
|
|
36
|
+
}
|
|
37
|
+
return PatternCard.make({
|
|
38
|
+
id,
|
|
39
|
+
matches: frontmatterField(rest.slice(0, end.index), "match") ?? "",
|
|
40
|
+
body: rest.slice(end.index + end[0].length).trim()
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* All `*.md` cards under `directory`, sorted by id (the filename stem). An
|
|
45
|
+
* absent directory is an empty set — patterns are optional everywhere.
|
|
46
|
+
*/
|
|
47
|
+
export const loadPatternCards = Effect.fn("@llm4ts/flow/Patterns.load")(function* (workspace, directory) {
|
|
48
|
+
const paths = yield* workspace.discover(`${directory}/*.md`).pipe(Effect.orElseSucceed(() => []));
|
|
49
|
+
const cards = [];
|
|
50
|
+
for (const path of [...paths].sort()) {
|
|
51
|
+
const file = path.split("/").at(-1) ?? path;
|
|
52
|
+
const text = yield* workspace.read(path).pipe(Effect.orElseSucceed(() => ""));
|
|
53
|
+
cards.push(parsePatternCard(file.replace(/\.md$/, ""), text));
|
|
54
|
+
}
|
|
55
|
+
return cards.sort((left, right) => left.id.localeCompare(right.id));
|
|
56
|
+
});
|
|
57
|
+
/**
|
|
58
|
+
* The cards whose `matches` regex hits anywhere in `source` — how extraction
|
|
59
|
+
* decides which patterns a program embodies. A card with no regex never
|
|
60
|
+
* matches; an invalid regex is skipped rather than failing the run.
|
|
61
|
+
*/
|
|
62
|
+
export const matchingPatternCards = (source, cards) => cards.filter((card) => {
|
|
63
|
+
if (card.matches.length === 0) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
return new RegExp(card.matches, "m").test(source);
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
/**
|
|
74
|
+
* Every `PAT-…` id cited in a spec text, in order of first appearance — how
|
|
75
|
+
* implementation finds the cards its current task's specs were tagged with.
|
|
76
|
+
*/
|
|
77
|
+
export const taggedPatternIds = (spec) => [
|
|
78
|
+
...new Set(spec.match(/PAT-[A-Za-z0-9-]+/g) ?? [])
|
|
79
|
+
];
|
|
80
|
+
//# sourceMappingURL=Patterns.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Patterns.js","sourceRoot":"","sources":["../src/Patterns.ts"],"names":[],"mappings":"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"}
|
package/dist/Wall.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Schema from "effect/Schema";
|
|
3
|
+
import type { WorkspaceError, WorkspaceShape } from "./Workspace.ts";
|
|
4
|
+
declare const WallClean_base: Schema.Class<WallClean, Schema.TaggedStruct<"Clean", {}>, {}>;
|
|
5
|
+
/**
|
|
6
|
+
* The enforced clean-room wall: target-phase flows (implement, verify, review)
|
|
7
|
+
* refuse to run when the target workspace contains anything matching the pack's
|
|
8
|
+
* legacy `sources:` regex, so the coder provably never has legacy source in
|
|
9
|
+
* reach. A breach is a recoverable, typed outcome — the caller decides to
|
|
10
|
+
* abort; only I/O failure fails the effect.
|
|
11
|
+
*/
|
|
12
|
+
export declare class WallClean extends WallClean_base {
|
|
13
|
+
}
|
|
14
|
+
declare const WallBreached_base: Schema.Class<WallBreached, Schema.TaggedStruct<"Breached", {
|
|
15
|
+
readonly paths: Schema.$Array<Schema.String>;
|
|
16
|
+
}>, {}>;
|
|
17
|
+
export declare class WallBreached extends WallBreached_base {
|
|
18
|
+
}
|
|
19
|
+
export declare const WallResult: Schema.Union<readonly [typeof WallClean, typeof WallBreached]>;
|
|
20
|
+
export type WallResult = typeof WallResult.Type;
|
|
21
|
+
/**
|
|
22
|
+
* Scans the working tree for files matching the pack's legacy `sourcesRegex`.
|
|
23
|
+
* `.git` is exempt — the wall is about the working tree, not repository
|
|
24
|
+
* internals.
|
|
25
|
+
*/
|
|
26
|
+
export declare const checkWall: (workspace: WorkspaceShape, sourcesRegex: string) => Effect.Effect<WallClean | WallBreached, WorkspaceError, never>;
|
|
27
|
+
/** The one-line breach message every target-phase flow reports, capped at ten paths. */
|
|
28
|
+
export declare const wallBreachMessage: (breach: WallBreached, consequence: string) => string;
|
|
29
|
+
export {};
|
|
30
|
+
//# sourceMappingURL=Wall.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Wall.d.ts","sourceRoot":"","sources":["../src/Wall.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAEvC,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;;AAEpE;;;;;;GAMG;AACH,qBAAa,SAAU,SAAQ,cAA4C;CAAG;;;;AAE9E,qBAAa,YAAa,SAAQ,iBAEhC;CAAG;AAEL,eAAO,MAAM,UAAU,gEAA0C,CAAA;AACjE,MAAM,MAAM,UAAU,GAAG,OAAO,UAAU,CAAC,IAAI,CAAA;AAE/C;;;;GAIG;AACH,eAAO,MAAM,SAAS,qHAQpB,CAAA;AAEF,wFAAwF;AACxF,eAAO,MAAM,iBAAiB,GAAI,QAAQ,YAAY,EAAE,aAAa,MAAM,KAAG,MAI7E,CAAA"}
|
package/dist/Wall.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Schema from "effect/Schema";
|
|
3
|
+
import { matchingFiles } from "./SpecChecks.js";
|
|
4
|
+
/**
|
|
5
|
+
* The enforced clean-room wall: target-phase flows (implement, verify, review)
|
|
6
|
+
* refuse to run when the target workspace contains anything matching the pack's
|
|
7
|
+
* legacy `sources:` regex, so the coder provably never has legacy source in
|
|
8
|
+
* reach. A breach is a recoverable, typed outcome — the caller decides to
|
|
9
|
+
* abort; only I/O failure fails the effect.
|
|
10
|
+
*/
|
|
11
|
+
export class WallClean extends Schema.TaggedClass()("Clean", {}) {
|
|
12
|
+
}
|
|
13
|
+
export class WallBreached extends Schema.TaggedClass()("Breached", {
|
|
14
|
+
paths: Schema.Array(Schema.String)
|
|
15
|
+
}) {
|
|
16
|
+
}
|
|
17
|
+
export const WallResult = Schema.Union([WallClean, WallBreached]);
|
|
18
|
+
/**
|
|
19
|
+
* Scans the working tree for files matching the pack's legacy `sourcesRegex`.
|
|
20
|
+
* `.git` is exempt — the wall is about the working tree, not repository
|
|
21
|
+
* internals.
|
|
22
|
+
*/
|
|
23
|
+
export const checkWall = Effect.fn("@llm4ts/flow/Wall.check")(function* (workspace, sourcesRegex) {
|
|
24
|
+
const matches = (yield* matchingFiles(workspace, sourcesRegex)).filter((path) => !path.startsWith(".git/"));
|
|
25
|
+
return matches.length === 0 ? new WallClean() : new WallBreached({ paths: matches });
|
|
26
|
+
});
|
|
27
|
+
/** The one-line breach message every target-phase flow reports, capped at ten paths. */
|
|
28
|
+
export const wallBreachMessage = (breach, consequence) => {
|
|
29
|
+
const shown = breach.paths.slice(0, 10).join(", ");
|
|
30
|
+
const more = breach.paths.length > 10 ? ` (+${breach.paths.length - 10} more)` : "";
|
|
31
|
+
return `clean-room wall breached — legacy source inside the target workspace: ${shown}${more}. ${consequence}`;
|
|
32
|
+
};
|
|
33
|
+
//# sourceMappingURL=Wall.js.map
|
package/dist/Wall.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Wall.js","sourceRoot":"","sources":["../src/Wall.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAG/C;;;;;;GAMG;AACH,MAAM,OAAO,SAAU,SAAQ,MAAM,CAAC,WAAW,EAAa,CAAC,OAAO,EAAE,EAAE,CAAC;CAAG;AAE9E,MAAM,OAAO,YAAa,SAAQ,MAAM,CAAC,WAAW,EAAgB,CAAC,UAAU,EAAE;IAC/E,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC;CACnC,CAAC;CAAG;AAEL,MAAM,CAAC,MAAM,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC,CAAA;AAGjE;;;;GAIG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,MAAM,CAAC,EAAE,CAAC,yBAAyB,CAAC,CAAC,QAAQ,CAAC,EACrE,SAAyB,EACzB,YAAoB;IAEpB,MAAM,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC,aAAa,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC,CAAC,MAAM,CACpE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CACpC,CAAA;IACD,OAAO,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,SAAS,EAAE,CAAC,CAAC,CAAC,IAAI,YAAY,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAA;AACtF,CAAC,CAAC,CAAA;AAEF,wFAAwF;AACxF,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,MAAoB,EAAE,WAAmB,EAAU,EAAE;IACrF,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAClD,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,MAAM,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAA;IACnF,OAAO,yEAAyE,KAAK,GAAG,IAAI,KAAK,WAAW,EAAE,CAAA;AAChH,CAAC,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@llm4ts/flow",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Effect-native LLM workflow, persistence, review, and repository automation",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"./Mermaid": "./dist/Mermaid.js",
|
|
37
37
|
"./Pack": "./dist/Pack.js",
|
|
38
38
|
"./Package": "./dist/Package.js",
|
|
39
|
+
"./Patterns": "./dist/Patterns.js",
|
|
39
40
|
"./Persistence": "./dist/Persistence.js",
|
|
40
41
|
"./Plan": "./dist/Plan.js",
|
|
41
42
|
"./PlanExecution": "./dist/PlanExecution.js",
|
|
@@ -50,6 +51,7 @@
|
|
|
50
51
|
"./SpecChecks": "./dist/SpecChecks.js",
|
|
51
52
|
"./Survey": "./dist/Survey.js",
|
|
52
53
|
"./TransientRetry": "./dist/TransientRetry.js",
|
|
54
|
+
"./Wall": "./dist/Wall.js",
|
|
53
55
|
"./Workspace": "./dist/Workspace.js",
|
|
54
56
|
"./WorkspaceLayout": "./dist/WorkspaceLayout.js",
|
|
55
57
|
"./WorkspaceTools": "./dist/WorkspaceTools.js"
|
|
@@ -76,7 +78,7 @@
|
|
|
76
78
|
"typescript"
|
|
77
79
|
],
|
|
78
80
|
"dependencies": {
|
|
79
|
-
"@llm4ts/core": "0.
|
|
81
|
+
"@llm4ts/core": "0.3.0"
|
|
80
82
|
},
|
|
81
83
|
"peerDependencies": {
|
|
82
84
|
"effect": "^4.0.0-beta.102"
|
package/src/Package.ts
CHANGED
|
@@ -1 +1,13 @@
|
|
|
1
|
+
import { createRequire } from "node:module"
|
|
2
|
+
import * as Schema from "effect/Schema"
|
|
3
|
+
|
|
4
|
+
const Manifest = Schema.Struct({ version: Schema.String })
|
|
5
|
+
|
|
6
|
+
const manifest = Schema.decodeUnknownSync(Manifest)(
|
|
7
|
+
createRequire(import.meta.url)("../package.json")
|
|
8
|
+
)
|
|
9
|
+
|
|
1
10
|
export const packageName = "@llm4ts/flow"
|
|
11
|
+
|
|
12
|
+
/** The published `@llm4ts/flow` version, recorded in provenance manifests. */
|
|
13
|
+
export const packageVersion: string = manifest.version
|
package/src/Patterns.ts
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect"
|
|
2
|
+
import * as Schema from "effect/Schema"
|
|
3
|
+
import type { WorkspaceError, WorkspaceShape } from "./Workspace.ts"
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* One reusable modernization pattern: a legacy idiom, its target translation,
|
|
7
|
+
* and the known traps — knowledge as data, like packs and lessons, but
|
|
8
|
+
* universal and curated rather than estate-local and run-accumulated.
|
|
9
|
+
* `matches` is a regex over legacy source: extraction tags each program's
|
|
10
|
+
* traceability fragment with the ids of the cards that hit, and implementation
|
|
11
|
+
* injects only the cited cards into the coder's brief — deterministic
|
|
12
|
+
* selection, bounded context.
|
|
13
|
+
*/
|
|
14
|
+
export class PatternCard extends Schema.Class<PatternCard>("PatternCard")({
|
|
15
|
+
id: Schema.String,
|
|
16
|
+
matches: Schema.String,
|
|
17
|
+
body: Schema.String
|
|
18
|
+
}) {}
|
|
19
|
+
|
|
20
|
+
const frontmatterField = (front: string, key: string): string | undefined => {
|
|
21
|
+
for (const line of front.split(/\r?\n/)) {
|
|
22
|
+
const index = line.indexOf(":")
|
|
23
|
+
if (index > 0 && line.slice(0, index).trim() === key) {
|
|
24
|
+
return line.slice(index + 1).trim()
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return undefined
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const parsePatternCard = (id: string, text: string): PatternCard => {
|
|
31
|
+
const stripped = text.trim()
|
|
32
|
+
if (!stripped.startsWith("---")) {
|
|
33
|
+
return PatternCard.make({ id, matches: "", body: stripped })
|
|
34
|
+
}
|
|
35
|
+
const rest = stripped.slice(3)
|
|
36
|
+
const end = /^---[ \t]*$/m.exec(rest)
|
|
37
|
+
if (end === null) {
|
|
38
|
+
return PatternCard.make({ id, matches: "", body: stripped })
|
|
39
|
+
}
|
|
40
|
+
return PatternCard.make({
|
|
41
|
+
id,
|
|
42
|
+
matches: frontmatterField(rest.slice(0, end.index), "match") ?? "",
|
|
43
|
+
body: rest.slice(end.index + end[0].length).trim()
|
|
44
|
+
})
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* All `*.md` cards under `directory`, sorted by id (the filename stem). An
|
|
49
|
+
* absent directory is an empty set — patterns are optional everywhere.
|
|
50
|
+
*/
|
|
51
|
+
export const loadPatternCards = Effect.fn("@llm4ts/flow/Patterns.load")(function* (
|
|
52
|
+
workspace: WorkspaceShape,
|
|
53
|
+
directory: string
|
|
54
|
+
): Effect.fn.Return<ReadonlyArray<PatternCard>, WorkspaceError> {
|
|
55
|
+
const paths = yield* workspace.discover(`${directory}/*.md`).pipe(Effect.orElseSucceed(() => []))
|
|
56
|
+
const cards: Array<PatternCard> = []
|
|
57
|
+
for (const path of [...paths].sort()) {
|
|
58
|
+
const file = path.split("/").at(-1) ?? path
|
|
59
|
+
const text = yield* workspace.read(path).pipe(Effect.orElseSucceed(() => ""))
|
|
60
|
+
cards.push(parsePatternCard(file.replace(/\.md$/, ""), text))
|
|
61
|
+
}
|
|
62
|
+
return cards.sort((left, right) => left.id.localeCompare(right.id))
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* The cards whose `matches` regex hits anywhere in `source` — how extraction
|
|
67
|
+
* decides which patterns a program embodies. A card with no regex never
|
|
68
|
+
* matches; an invalid regex is skipped rather than failing the run.
|
|
69
|
+
*/
|
|
70
|
+
export const matchingPatternCards = (
|
|
71
|
+
source: string,
|
|
72
|
+
cards: ReadonlyArray<PatternCard>
|
|
73
|
+
): ReadonlyArray<PatternCard> =>
|
|
74
|
+
cards.filter((card) => {
|
|
75
|
+
if (card.matches.length === 0) {
|
|
76
|
+
return false
|
|
77
|
+
}
|
|
78
|
+
try {
|
|
79
|
+
return new RegExp(card.matches, "m").test(source)
|
|
80
|
+
} catch {
|
|
81
|
+
return false
|
|
82
|
+
}
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Every `PAT-…` id cited in a spec text, in order of first appearance — how
|
|
87
|
+
* implementation finds the cards its current task's specs were tagged with.
|
|
88
|
+
*/
|
|
89
|
+
export const taggedPatternIds = (spec: string): ReadonlyArray<string> => [
|
|
90
|
+
...new Set(spec.match(/PAT-[A-Za-z0-9-]+/g) ?? [])
|
|
91
|
+
]
|
package/src/Wall.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect"
|
|
2
|
+
import * as Schema from "effect/Schema"
|
|
3
|
+
import { matchingFiles } from "./SpecChecks.ts"
|
|
4
|
+
import type { WorkspaceError, WorkspaceShape } from "./Workspace.ts"
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The enforced clean-room wall: target-phase flows (implement, verify, review)
|
|
8
|
+
* refuse to run when the target workspace contains anything matching the pack's
|
|
9
|
+
* legacy `sources:` regex, so the coder provably never has legacy source in
|
|
10
|
+
* reach. A breach is a recoverable, typed outcome — the caller decides to
|
|
11
|
+
* abort; only I/O failure fails the effect.
|
|
12
|
+
*/
|
|
13
|
+
export class WallClean extends Schema.TaggedClass<WallClean>()("Clean", {}) {}
|
|
14
|
+
|
|
15
|
+
export class WallBreached extends Schema.TaggedClass<WallBreached>()("Breached", {
|
|
16
|
+
paths: Schema.Array(Schema.String)
|
|
17
|
+
}) {}
|
|
18
|
+
|
|
19
|
+
export const WallResult = Schema.Union([WallClean, WallBreached])
|
|
20
|
+
export type WallResult = typeof WallResult.Type
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Scans the working tree for files matching the pack's legacy `sourcesRegex`.
|
|
24
|
+
* `.git` is exempt — the wall is about the working tree, not repository
|
|
25
|
+
* internals.
|
|
26
|
+
*/
|
|
27
|
+
export const checkWall = Effect.fn("@llm4ts/flow/Wall.check")(function* (
|
|
28
|
+
workspace: WorkspaceShape,
|
|
29
|
+
sourcesRegex: string
|
|
30
|
+
): Effect.fn.Return<WallResult, WorkspaceError> {
|
|
31
|
+
const matches = (yield* matchingFiles(workspace, sourcesRegex)).filter(
|
|
32
|
+
(path) => !path.startsWith(".git/")
|
|
33
|
+
)
|
|
34
|
+
return matches.length === 0 ? new WallClean() : new WallBreached({ paths: matches })
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
/** The one-line breach message every target-phase flow reports, capped at ten paths. */
|
|
38
|
+
export const wallBreachMessage = (breach: WallBreached, consequence: string): string => {
|
|
39
|
+
const shown = breach.paths.slice(0, 10).join(", ")
|
|
40
|
+
const more = breach.paths.length > 10 ? ` (+${breach.paths.length - 10} more)` : ""
|
|
41
|
+
return `clean-room wall breached — legacy source inside the target workspace: ${shown}${more}. ${consequence}`
|
|
42
|
+
}
|