@llm4ts/shell 0.5.0 → 0.6.1
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.d.ts +2 -2
- package/dist/Cli.d.ts.map +1 -1
- package/dist/Cli.js +8 -4
- package/dist/Cli.js.map +1 -1
- package/dist/FlowCatalog.d.ts.map +1 -1
- package/dist/FlowCatalog.js +6 -2
- package/dist/FlowCatalog.js.map +1 -1
- package/flows/fixtures/scaffolds/kafka-streams-service/README.md +12 -0
- package/flows/fixtures/scaffolds/kafka-streams-service/pom.xml +49 -0
- package/flows/fixtures/scaffolds/kafka-streams-service/scripts/replay.sh +10 -0
- package/flows/fixtures/scaffolds/kafka-streams-service/src/main/java/com/meridian/scaffold/Application.java +29 -0
- package/flows/fixtures/scaffolds/kafka-streams-service/src/test/java/com/meridian/scaffold/ApplicationTests.java +21 -0
- package/flows/fixtures/scaffolds/kafka-streams-service/src/test/resources/features/.gitkeep +0 -0
- package/flows/fixtures/scaffolds/nextjs-spa/README.md +6 -0
- package/flows/fixtures/scaffolds/nextjs-spa/app/layout.js +11 -0
- package/flows/fixtures/scaffolds/nextjs-spa/app/page.js +7 -0
- package/flows/fixtures/scaffolds/nextjs-spa/docs/specs/.gitkeep +0 -0
- package/flows/fixtures/scaffolds/nextjs-spa/features/.gitkeep +0 -0
- package/flows/fixtures/scaffolds/nextjs-spa/next.config.js +7 -0
- package/flows/fixtures/scaffolds/nextjs-spa/package.json +15 -0
- package/flows/fixtures/scaffolds/nextjs-spa/tests/smoke.test.js +6 -0
- package/flows/fixtures/scaffolds/spring-bff/README.md +9 -0
- package/flows/fixtures/scaffolds/spring-bff/frontend/app/layout.js +11 -0
- package/flows/fixtures/scaffolds/spring-bff/frontend/app/page.js +7 -0
- package/flows/fixtures/scaffolds/spring-bff/frontend/next.config.js +7 -0
- package/flows/fixtures/scaffolds/spring-bff/frontend/package.json +15 -0
- package/flows/fixtures/scaffolds/spring-bff/frontend/tests/smoke.test.js +6 -0
- package/flows/fixtures/scaffolds/spring-bff/pom.xml +57 -0
- package/flows/fixtures/scaffolds/spring-bff/scripts/test.sh +6 -0
- package/flows/fixtures/scaffolds/spring-bff/src/main/java/com/meridian/bff/Application.java +12 -0
- package/flows/fixtures/scaffolds/spring-bff/src/main/resources/application.yaml +12 -0
- package/flows/fixtures/scaffolds/spring-bff/src/test/java/com/meridian/bff/ApplicationTests.java +12 -0
- package/flows/fixtures/scaffolds/spring-bff/src/test/resources/features/.gitkeep +0 -0
- package/flows/fixtures/scaffolds/spring-boot-service/README.md +7 -0
- package/flows/fixtures/scaffolds/spring-boot-service/pom.xml +53 -0
- package/flows/fixtures/scaffolds/spring-boot-service/scripts/replay.sh +9 -0
- package/flows/fixtures/scaffolds/spring-boot-service/src/main/java/com/meridian/scaffold/Application.java +12 -0
- package/flows/fixtures/scaffolds/spring-boot-service/src/main/resources/application.yaml +12 -0
- package/flows/fixtures/scaffolds/spring-boot-service/src/test/java/com/meridian/scaffold/ApplicationTests.java +12 -0
- package/flows/fixtures/scaffolds/spring-boot-service/src/test/resources/features/.gitkeep +0 -0
- package/flows/implement.js +29 -0
- package/flows/issue-pr.js +71 -0
- package/flows/judge-suite.js +48 -0
- package/flows/local.js +40 -0
- package/flows/modernize-bench.js +263 -0
- package/flows/modernize-extract.js +454 -0
- package/flows/modernize-implement.js +249 -0
- package/flows/modernize-review.js +237 -0
- package/flows/modernize-seed.js +189 -0
- package/flows/modernize-survey.js +0 -0
- package/flows/modernize-verify.js +435 -0
- package/flows/packs/ace-integration/lessons.md +6 -0
- package/flows/packs/ace-integration/pack.md +29 -0
- package/flows/packs/ace-integration/prompts/analysis.md +21 -0
- package/flows/packs/ace-integration/prompts/bdd.md +13 -0
- package/flows/packs/ace-integration/prompts/implement.md +17 -0
- package/flows/packs/ace-integration/prompts/plan.md +12 -0
- package/flows/packs/ace-integration/prompts/review.md +17 -0
- package/flows/packs/ace-integration/prompts/spec.md +29 -0
- package/flows/packs/ace-integration/reviewers/routing-fidelity.md +19 -0
- package/flows/packs/ace-integration/reviewers/traceability.md +13 -0
- package/flows/packs/ace-kafka/lessons.md +6 -0
- package/flows/packs/ace-kafka/pack.md +36 -0
- package/flows/packs/ace-kafka/prompts/analysis.md +21 -0
- package/flows/packs/ace-kafka/prompts/bdd.md +13 -0
- package/flows/packs/ace-kafka/prompts/implement.md +34 -0
- package/flows/packs/ace-kafka/prompts/plan.md +12 -0
- package/flows/packs/ace-kafka/prompts/review.md +17 -0
- package/flows/packs/ace-kafka/prompts/spec.md +31 -0
- package/flows/packs/ace-kafka/prompts/vectors.md +21 -0
- package/flows/packs/ace-kafka/reviewers/event-contracts.md +9 -0
- package/flows/packs/ace-kafka/reviewers/routing-fidelity.md +19 -0
- package/flows/packs/ace-kafka/reviewers/traceability.md +13 -0
- package/flows/packs/cobol-kafka/lessons.md +3 -0
- package/flows/packs/cobol-kafka/pack.md +51 -0
- package/flows/packs/cobol-kafka/patterns/PAT-STREAM-001-record-to-event.md +7 -0
- package/flows/packs/cobol-kafka/patterns/PAT-STREAM-002-job-step-topology.md +6 -0
- package/flows/packs/cobol-kafka/patterns/PAT-STREAM-003-checkpoint-idempotence.md +7 -0
- package/flows/packs/cobol-kafka/patterns/PAT-STREAM-004-eod-totals-window.md +7 -0
- package/flows/packs/cobol-kafka/patterns/PAT-STREAM-005-reject-topic.md +6 -0
- package/flows/packs/cobol-kafka/patterns/PAT-STREAM-006-keyed-state-store.md +7 -0
- package/flows/packs/cobol-kafka/patterns/PAT-STREAM-007-outbox.md +7 -0
- package/flows/packs/cobol-kafka/patterns/PAT-STREAM-008-partition-key.md +7 -0
- package/flows/packs/cobol-kafka/patterns/PAT-STREAM-009-batch-window-cutoff.md +7 -0
- package/flows/packs/cobol-kafka/patterns/PAT-STREAM-010-replay-backfill.md +7 -0
- package/flows/packs/cobol-kafka/prompts/analysis.md +23 -0
- package/flows/packs/cobol-kafka/prompts/bdd.md +15 -0
- package/flows/packs/cobol-kafka/prompts/implement.md +47 -0
- package/flows/packs/cobol-kafka/prompts/plan.md +13 -0
- package/flows/packs/cobol-kafka/prompts/review.md +17 -0
- package/flows/packs/cobol-kafka/prompts/spec.md +28 -0
- package/flows/packs/cobol-kafka/prompts/vectors.md +21 -0
- package/flows/packs/cobol-kafka/reviewers/cobol-fidelity.md +16 -0
- package/flows/packs/cobol-kafka/reviewers/pattern-conformance.md +10 -0
- package/flows/packs/cobol-kafka/reviewers/traceability.md +13 -0
- package/flows/packs/cobol-springboot/lessons.md +3 -0
- package/flows/packs/cobol-springboot/pack.md +51 -0
- package/flows/packs/cobol-springboot/prompts/analysis.md +23 -0
- package/flows/packs/cobol-springboot/prompts/bdd.md +15 -0
- package/flows/packs/cobol-springboot/prompts/implement.md +31 -0
- package/flows/packs/cobol-springboot/prompts/plan.md +13 -0
- package/flows/packs/cobol-springboot/prompts/review.md +17 -0
- package/flows/packs/cobol-springboot/prompts/spec.md +28 -0
- package/flows/packs/cobol-springboot/prompts/vectors.md +22 -0
- package/flows/packs/cobol-springboot/reviewers/cobol-fidelity.md +16 -0
- package/flows/packs/cobol-springboot/reviewers/pattern-conformance.md +10 -0
- package/flows/packs/cobol-springboot/reviewers/traceability.md +13 -0
- package/flows/packs/jsp-bff-nextjs/lessons.md +6 -0
- package/flows/packs/jsp-bff-nextjs/pack.md +30 -0
- package/flows/packs/jsp-bff-nextjs/prompts/analysis.md +20 -0
- package/flows/packs/jsp-bff-nextjs/prompts/bdd.md +10 -0
- package/flows/packs/jsp-bff-nextjs/prompts/implement.md +17 -0
- package/flows/packs/jsp-bff-nextjs/prompts/plan.md +12 -0
- package/flows/packs/jsp-bff-nextjs/prompts/review.md +16 -0
- package/flows/packs/jsp-bff-nextjs/prompts/spec.md +27 -0
- package/flows/packs/jsp-bff-nextjs/reviewers/bff-boundary.md +18 -0
- package/flows/packs/jsp-bff-nextjs/reviewers/traceability.md +13 -0
- package/flows/packs/jsp-nextjs/lessons.md +6 -0
- package/flows/packs/jsp-nextjs/pack.md +29 -0
- package/flows/packs/jsp-nextjs/prompts/analysis.md +21 -0
- package/flows/packs/jsp-nextjs/prompts/bdd.md +12 -0
- package/flows/packs/jsp-nextjs/prompts/implement.md +17 -0
- package/flows/packs/jsp-nextjs/prompts/plan.md +14 -0
- package/flows/packs/jsp-nextjs/prompts/review.md +16 -0
- package/flows/packs/jsp-nextjs/prompts/spec.md +26 -0
- package/flows/packs/jsp-nextjs/reviewers/traceability.md +13 -0
- package/flows/packs/jsp-nextjs/reviewers/ux-fidelity.md +16 -0
- package/flows/patterns/PAT-COBOL-001-comp3-money.md +7 -0
- package/flows/patterns/PAT-COBOL-002-redefines.md +7 -0
- package/flows/patterns/PAT-COBOL-003-88-levels.md +7 -0
- package/flows/patterns/PAT-COBOL-004-perform-thru.md +7 -0
- package/flows/patterns/PAT-COBOL-005-file-status.md +6 -0
- package/flows/patterns/PAT-COBOL-006-sqlcode.md +7 -0
- package/flows/patterns/PAT-COBOL-007-validation-order.md +7 -0
- package/flows/patterns/PAT-COBOL-008-zoned-decimal.md +6 -0
- package/flows/patterns/PAT-COBOL-009-occurs-table.md +6 -0
- package/flows/patterns/PAT-COBOL-010-copybook-record.md +7 -0
- package/flows/patterns/PAT-COBOL-011-batch-counters.md +6 -0
- package/flows/patterns/PAT-COBOL-012-commit-frequency.md +7 -0
- package/flows/patterns/PAT-COBOL-013-abend.md +6 -0
- package/flows/patterns/PAT-COBOL-014-reject-file.md +6 -0
- package/flows/patterns/PAT-COBOL-015-accept-date.md +6 -0
- package/flows/patterns/PAT-COBOL-016-string-building.md +6 -0
- package/flows/patterns/PAT-COBOL-017-cursor-loop.md +6 -0
- package/flows/patterns/PAT-COBOL-018-yn-switches.md +6 -0
- package/flows/patterns/PAT-COBOL-019-shared-accumulators.md +6 -0
- package/flows/patterns/PAT-COBOL-020-tiered-fees.md +7 -0
- package/flows/patterns/PAT-COBOL-021-overdraft-floor.md +7 -0
- package/flows/patterns/PAT-COBOL-022-jcl-job-net.md +6 -0
- package/flows/patterns/PAT-COBOL-023-sort-step.md +6 -0
- package/flows/patterns/PAT-COBOL-024-fixed-width-records.md +6 -0
- package/flows/patterns/PAT-COBOL-025-audit-rows.md +6 -0
- package/flows/sdd.js +114 -0
- package/package.json +5 -4
- package/src/Cli.ts +13 -4
- package/src/FlowCatalog.ts +8 -2
- package/flows/implement.ts +0 -38
- package/flows/issue-pr.ts +0 -108
- package/flows/judge-suite.ts +0 -63
- package/flows/local.ts +0 -59
- package/flows/modernize-bench.ts +0 -362
- package/flows/modernize-extract.ts +0 -633
- package/flows/modernize-implement.ts +0 -333
- package/flows/modernize-review.ts +0 -343
- package/flows/modernize-seed.ts +0 -259
- package/flows/modernize-survey.ts +0 -0
- package/flows/modernize-verify.ts +0 -611
- package/flows/sdd.ts +0 -181
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Write Gherkin .feature files encoding the specs as executable user journeys — these
|
|
2
|
+
become the acceptance tests of the new SPA.
|
|
3
|
+
|
|
4
|
+
- One feature per screen/flow.
|
|
5
|
+
- User vocabulary, not servlet vocabulary: "When she submits a transfer of 3000.00,
|
|
6
|
+
Then she is asked to confirm", not "doPost forwards to confirm.jsp".
|
|
7
|
+
- Error messages are asserted VERBATIM — they are contract.
|
|
8
|
+
- Concrete values everywhere: real account numbers, amounts, statuses.
|
|
9
|
+
- Cover: each screen's happy path, EVERY validation rule and its message, threshold
|
|
10
|
+
boundaries (a rule at 2500.00 gets scenarios at 2500.00 and 2500.01 per its
|
|
11
|
+
inclusive/exclusive semantics), confirmation flows, and the state that must
|
|
12
|
+
survive navigation (what hidden fields carried).
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
You implement one task at a time in a client-only Next.js (App Router, static
|
|
2
|
+
export) SPA that replaces a J2EE/JSP application. The committed specs under
|
|
3
|
+
docs/specs/ and the .feature files under features/ are the contract; the acceptance
|
|
4
|
+
tests encode them — make them pass without weakening them.
|
|
5
|
+
|
|
6
|
+
Non-negotiables when porting JSP semantics:
|
|
7
|
+
|
|
8
|
+
- Validation error messages match the spec VERBATIM — character for character.
|
|
9
|
+
- Validation runs client-side in the same order the spec numbers the rules
|
|
10
|
+
(first-failure-wins), and thresholds keep their inclusive/exclusive semantics.
|
|
11
|
+
- Session attributes and hidden-field flows become explicit client state — never
|
|
12
|
+
re-derive a value the legacy app carried through a hidden field.
|
|
13
|
+
- Every server interaction goes through the API client module; components never
|
|
14
|
+
call fetch directly (the tests mock that seam).
|
|
15
|
+
- Money renders and compares as exact decimals — no floating-point arithmetic on
|
|
16
|
+
amounts.
|
|
17
|
+
- Follow the scaffold's structure; tests stay runnable with `node --test` alone.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
Derive the implementation task list for the client-only Next.js SPA from the spec
|
|
2
|
+
pack. Constraints:
|
|
3
|
+
|
|
4
|
+
- The FIRST task must be exactly: encode the BDD scenarios as failing acceptance
|
|
5
|
+
tests under tests/ using node:test, driving the screen components/logic with the
|
|
6
|
+
expected API mocked at the fetch boundary (no production code).
|
|
7
|
+
- Early tasks: the typed API client module implementing the spec's "Expected API
|
|
8
|
+
contract" (all calls go through it — it is the seam the tests mock), and shared
|
|
9
|
+
state that replaces session/hidden-field flows.
|
|
10
|
+
- Then one task per screen/flow: page component, validation with VERBATIM message
|
|
11
|
+
texts, navigation, confirmation steps.
|
|
12
|
+
- Each task names the spec rules (R1, ...) and scenarios it covers.
|
|
13
|
+
- No server-side code: the scaffold exports statically (S3); everything dynamic
|
|
14
|
+
goes through the API client.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
You review a finished modernization increment: a client-only Next.js SPA built from
|
|
2
|
+
specs reverse-engineered out of a J2EE/JSP app. Judge the implementation against the
|
|
3
|
+
committed spec pack, not your own taste.
|
|
4
|
+
|
|
5
|
+
Look specifically for:
|
|
6
|
+
|
|
7
|
+
- Message drift: validation texts that differ from the spec's verbatim messages.
|
|
8
|
+
- Rule reordering or boundary drift (2500.00 inclusive vs exclusive).
|
|
9
|
+
- Session/hidden-field state that silently disappeared — flows that "work" only
|
|
10
|
+
because a value is re-fetched or guessed.
|
|
11
|
+
- Direct fetch calls bypassing the API client seam, or API shapes that diverge from
|
|
12
|
+
the spec's expected contract.
|
|
13
|
+
- Weakened tests: scenarios deleted, messages loosened to regex, boundaries moved.
|
|
14
|
+
|
|
15
|
+
Classify each finding as FIX (violates the spec) or IMPROVEMENT (compliant but
|
|
16
|
+
worth a follow-up).
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
Write one behavioural spec per screen/servlet cluster as Markdown with exactly these
|
|
2
|
+
sections:
|
|
3
|
+
|
|
4
|
+
# <Screen or flow> — <one-line purpose>
|
|
5
|
+
|
|
6
|
+
## Overview
|
|
7
|
+
What the user does here, entry url-pattern(s), where it navigates.
|
|
8
|
+
|
|
9
|
+
## Screens
|
|
10
|
+
What each page shows (fields, per-row rendering, status label mappings), forms and
|
|
11
|
+
their fields (including hidden fields), links.
|
|
12
|
+
|
|
13
|
+
## Business rules
|
|
14
|
+
Numbered (R1, R2, ...), in the order the servlet applies them: validation rules with
|
|
15
|
+
their EXACT error message texts, thresholds (with boundary semantics), confirmation
|
|
16
|
+
steps, and what each successful action writes (table, columns, values).
|
|
17
|
+
|
|
18
|
+
## Session & state
|
|
19
|
+
What lives in the session, what rides in hidden fields, timeout behaviour.
|
|
20
|
+
|
|
21
|
+
## Expected API contract
|
|
22
|
+
The endpoints a client-only SPA needs to replace the servlet behaviour (method,
|
|
23
|
+
path, request/response shape) — derived from the data access, not invented UX.
|
|
24
|
+
|
|
25
|
+
## Open questions
|
|
26
|
+
Anything ambiguous in the source. Empty if none.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
You are the traceability reviewer for a spec-driven modernization. Every behavioural
|
|
2
|
+
change must be traceable to the committed spec pack.
|
|
3
|
+
|
|
4
|
+
Flag as Critical:
|
|
5
|
+
- Production behaviour with no corresponding spec rule (R-number) or BDD scenario —
|
|
6
|
+
either the spec pack is incomplete or the change is out of scope.
|
|
7
|
+
- Changes to .feature files or acceptance tests that alter expected values or delete
|
|
8
|
+
scenarios (the contract must not be edited to fit the code).
|
|
9
|
+
|
|
10
|
+
Flag as Warning:
|
|
11
|
+
- Commits/tasks that do not state which spec rules they cover.
|
|
12
|
+
|
|
13
|
+
Ignore refactors with no behavioural surface.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
files: .*\.(js|jsx|ts|tsx)
|
|
3
|
+
---
|
|
4
|
+
You are the UX-fidelity reviewer for a JSP-to-SPA port. Your single concern: does
|
|
5
|
+
the SPA preserve the exact user-observable behaviour the specs carried over?
|
|
6
|
+
|
|
7
|
+
Flag as Critical:
|
|
8
|
+
- Validation error messages differing from the spec text in ANY character.
|
|
9
|
+
- Validation rules running in a different order than the spec's numbering, or
|
|
10
|
+
threshold boundaries drifting (>= where the spec says >).
|
|
11
|
+
- Confirmation steps skipped, auto-submitted, or gated on different amounts.
|
|
12
|
+
- Legacy session/hidden-field state not represented in explicit client state.
|
|
13
|
+
- Floating-point arithmetic on money.
|
|
14
|
+
|
|
15
|
+
Ignore styling, component structure, and framework idioms — other reviewers own
|
|
16
|
+
those.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
match: COMP-3|COMPUTE .* ROUNDED
|
|
3
|
+
---
|
|
4
|
+
COBOL packed-decimal money (COMP-3) maps to BigDecimal with scale 2; COMPUTE ROUNDED
|
|
5
|
+
is RoundingMode.HALF_UP. Never float/double for amounts, rates, or balances; scale is
|
|
6
|
+
part of the contract (S9(5)V99 means exactly 2 decimals). Trap: intermediate COMPUTE
|
|
7
|
+
results keep COBOL truncation rules — round only where the source says ROUNDED.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
match: REDEFINES
|
|
3
|
+
---
|
|
4
|
+
A REDEFINES clause is two interpretations of one buffer. Map to a sealed interface
|
|
5
|
+
with one variant per interpretation and an explicit discriminator — never two mutable
|
|
6
|
+
views of shared state. Trap: the discriminator is often implicit in which paragraph
|
|
7
|
+
reads the field; the spec must name it.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
match: ^ {10,}88
|
|
3
|
+
---
|
|
4
|
+
Level-88 condition names are the domain vocabulary: map each group to a Java enum
|
|
5
|
+
(ACCT-STATUS 88s → AccountStatus.ACTIVE/FROZEN/CLOSED/DORMANT) and keep the raw code
|
|
6
|
+
as the enum field. Trap: several 88s can overlap one value range — model predicates,
|
|
7
|
+
not just constants, when ranges appear.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
match: PERFORM .* THRU|GO TO .*-EXIT
|
|
3
|
+
---
|
|
4
|
+
PERFORM…THRU with GO TO <para>-EXIT is early-exit control flow: map to a method with
|
|
5
|
+
early returns (or a validation chain), one method per performed range. Trap: a GO TO
|
|
6
|
+
that escapes its PERFORM range is a latent fall-through bug in the original — flag it
|
|
7
|
+
in the spec instead of reproducing it.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
---
|
|
2
|
+
match: FILE STATUS
|
|
3
|
+
---
|
|
4
|
+
FILE STATUS codes are typed I/O outcomes: 00 ok, 10 EOF, others errors. Map the
|
|
5
|
+
handled codes to the value channel (sealed result), unexpected ones to exceptions.
|
|
6
|
+
Trap: an unchecked OPEN status in the source is a silent-failure path — surface it.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
match: SQLCODE
|
|
3
|
+
---
|
|
4
|
+
The SQLCODE ladder (0 / +100 / other) maps to Optional-plus-typed-error: +100 is
|
|
5
|
+
Optional.empty (not found), 0 is a value, anything else is an exception. Trap:
|
|
6
|
+
WHENEVER clauses and per-statement EVALUATEs can differ per call site — port each
|
|
7
|
+
site as it is, not one global policy.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
match: GO TO .*-EXIT|EVALUATE TRUE
|
|
3
|
+
---
|
|
4
|
+
First-failure-wins validation ladders are contract: port as an ordered chain where
|
|
5
|
+
the first failing rule short-circuits with its exact reason code. Property to keep:
|
|
6
|
+
at most ONE reject reason per input, and its code is the first violated rule in
|
|
7
|
+
source order.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
---
|
|
2
|
+
match: PIC S9\([0-9]+\)V99(?! +COMP)
|
|
3
|
+
---
|
|
4
|
+
Zoned-decimal DISPLAY amounts (overpunched sign) are a wire format: parse at the
|
|
5
|
+
boundary into BigDecimal, serialize back only at the edge. Trap: sign overpunch
|
|
6
|
+
conventions differ (EBCDIC { A-I / } J-R); never let the raw bytes leak inland.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
match: ^ {6,}COPY *[A-Z]
|
|
3
|
+
---
|
|
4
|
+
One copybook = one record type shared across programs: generate a single Java record
|
|
5
|
+
per copybook and reuse it, mirroring COPY REPLACING prefixes as field-identical
|
|
6
|
+
wrapper types. Trap: two programs copying the same book may honor different subsets
|
|
7
|
+
of fields — the SPEC, not the layout, says which fields are behaviour.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
---
|
|
2
|
+
match: ADD 1 TO WS-.*(COUNT|CNT)
|
|
3
|
+
---
|
|
4
|
+
Run counters and totals (read/posted/rejected/fees) are the batch run report: map to
|
|
5
|
+
an immutable RunSummary the use case returns, not logging. The summary is observable
|
|
6
|
+
behaviour when the source DISPLAYs it — keep field-for-field parity.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
match: COMMIT
|
|
3
|
+
---
|
|
4
|
+
COMMIT every N records is restart-safety, not business logic: in the target, one
|
|
5
|
+
transaction per unit of work (per transfer) replaces chunked commits, and the spec
|
|
6
|
+
notes the difference. Trap: never reproduce partial-batch visibility as behaviour —
|
|
7
|
+
it was an artifact of the mainframe transaction monitor.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
---
|
|
2
|
+
match: ABEND|ILBOABN0|RETURN-CODE
|
|
3
|
+
---
|
|
4
|
+
ABEND U-codes and RETURN-CODE are process-exit contract with the scheduler: map to
|
|
5
|
+
typed exceptions caught at the entrypoint that set the process exit status. Keep the
|
|
6
|
+
distinction: expected rejects are data, ABENDs are crashes.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
---
|
|
2
|
+
match: REJECT
|
|
3
|
+
---
|
|
4
|
+
A reject file with code+reason is a first-class output: map to a reject record
|
|
5
|
+
entity (code, reason, original payload) written through a port, with codes verbatim.
|
|
6
|
+
The reject set is closed — port it as an enum and cover every code with a test.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
---
|
|
2
|
+
match: DECLARE .* CURSOR|FETCH
|
|
3
|
+
---
|
|
4
|
+
DB2 cursor loops map to pageable repository queries or streams; the loop body is a
|
|
5
|
+
per-row use case. Trap: cursors see uncommitted own-writes under the same unit of
|
|
6
|
+
work — if the source relies on that, the spec must say so explicitly.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
---
|
|
2
|
+
match: PIC X\(0?1\).*VALUE '[YN]'
|
|
3
|
+
---
|
|
4
|
+
X(1) Y/N switches are booleans with domain names: WS-OD-TRIGGERED → boolean
|
|
5
|
+
odFeeDue. Keep the original flag name in a comment or mapping doc for traceability.
|
|
6
|
+
Trap: some are tri-state in practice (space = unknown) — check every writer first.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
---
|
|
2
|
+
match: DAILY.*ACCUM|ACCUM.*DAILY
|
|
3
|
+
---
|
|
4
|
+
Accumulators maintained across programs (daily transfer totals reset by another job)
|
|
5
|
+
are shared domain state: model as an explicit entity with named reset semantics, and
|
|
6
|
+
document WHICH process resets it — the invariant lives between programs.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
match: FEE
|
|
3
|
+
---
|
|
4
|
+
Threshold fee ladders (flat under X, percent capped over) are a policy value object:
|
|
5
|
+
one pure function amount→fee, boundary semantics exactly as the source (< vs <=),
|
|
6
|
+
tested at each boundary and cap. Trap: waivers (same-customer) are part of the
|
|
7
|
+
policy, not the caller.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
match: OD-FLOOR|OVERDRAFT
|
|
3
|
+
---
|
|
4
|
+
Overdraft floors including fees are guard clauses with exact arithmetic: compute the
|
|
5
|
+
post-transaction balance INCLUDING all fees the source includes before comparing to
|
|
6
|
+
the floor. Trap: the OD fee itself may count against the floor — read the COMPUTE
|
|
7
|
+
order, not the comment.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
---
|
|
2
|
+
match: ^//[A-Z0-9]+ +EXEC
|
|
3
|
+
---
|
|
4
|
+
A JCL job is an orchestration: each EXEC step maps to a use-case invocation, COND
|
|
5
|
+
codes to conditional continuation, DD statements to the step's ports. The job net
|
|
6
|
+
belongs in the orchestrator (scheduler config), never hidden inside one service.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
---
|
|
2
|
+
match: SORT
|
|
3
|
+
---
|
|
4
|
+
DFSORT steps are contract about processing ORDER: map to repository ORDER BY (or an
|
|
5
|
+
explicit sort at the boundary) and record WHY the order matters — usually a control
|
|
6
|
+
break downstream. Trap: sort keys include type/sign quirks of the raw record.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
---
|
|
2
|
+
match: RECORD CONTAINS|RECORDING MODE
|
|
3
|
+
---
|
|
4
|
+
Fixed-width record layouts (LRECL) are wire formats: one explicit parser/serializer
|
|
5
|
+
per layout at the boundary, tested against byte-exact golden records; inland types
|
|
6
|
+
are domain records. Never index into strings mid-service.
|
package/flows/sdd.js
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
// Spec-driven development: write a specification, encode it as red tests, implement to green.
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import * as Effect from "effect/Effect";
|
|
4
|
+
import { makeChat } from "@llm4ts/flow/Chat";
|
|
5
|
+
import { FlowAborted } from "@llm4ts/flow/FlowError";
|
|
6
|
+
import { Plan, defaultPlanPath } from "@llm4ts/flow/Plan";
|
|
7
|
+
import { implementTaskLoop, stage } from "@llm4ts/flow/PlanExecution";
|
|
8
|
+
import { defaultPlanInstructions, planFrom, writeBrief } from "@llm4ts/flow/Planner";
|
|
9
|
+
import { makePlanStore } from "@llm4ts/flow/Persistence";
|
|
10
|
+
import { lintCommand, minimalReviewers, reviewAndFixLoop } from "@llm4ts/flow/Review";
|
|
11
|
+
import { asReadOnly, coderFromEnv, gemini, withModel } from "@llm4ts/runner/Connectors";
|
|
12
|
+
import { resolveFlowInput } from "@llm4ts/runner/FlowArgs";
|
|
13
|
+
import { runFlowMain, runNode } from "@llm4ts/runner/FlowRunner";
|
|
14
|
+
import { nodePlainFileStore } from "@llm4ts/runner/NodePlainFileStore";
|
|
15
|
+
import { nodeProcessExecutor } from "@llm4ts/runner/NodeProcessExecutor";
|
|
16
|
+
const proModel = process.env.LLM4TS_REASONING_MODEL ?? "gemini-3-pro-preview";
|
|
17
|
+
const flashModel = process.env.LLM4TS_CODER_MODEL ?? "gemini-2.5-flash";
|
|
18
|
+
const specInstructions = [
|
|
19
|
+
"Turn the change request into a precise repository specification with context, goals,",
|
|
20
|
+
"non-goals, and a numbered list of testable Given/When/Then acceptance criteria.",
|
|
21
|
+
"Explore the repository as needed. Return Markdown prose, not a task list."
|
|
22
|
+
].join("\n");
|
|
23
|
+
const planInstructions = [
|
|
24
|
+
defaultPlanInstructions,
|
|
25
|
+
"",
|
|
26
|
+
"The request below is a specification with numbered acceptance criteria.",
|
|
27
|
+
"The first task must encode those criteria as tests without changing production code.",
|
|
28
|
+
"Every later task implements production behavior toward making those tests pass."
|
|
29
|
+
].join("\n");
|
|
30
|
+
const verificationFailure = (result) => FlowAborted.make({
|
|
31
|
+
message: [
|
|
32
|
+
"acceptance criteria not met:",
|
|
33
|
+
...result.issues.map((issue) => `${issue.title}\n${issue.description}`)
|
|
34
|
+
].join("\n\n")
|
|
35
|
+
});
|
|
36
|
+
const program = Effect.gen(function* () {
|
|
37
|
+
const input = yield* resolveFlowInput("Add due dates, mark overdue items in list output, and show items due today.");
|
|
38
|
+
const explicitCoder = process.env.LLM4TS_CODER?.trim();
|
|
39
|
+
const coder = explicitCoder === undefined || explicitCoder.length === 0
|
|
40
|
+
? withModel(gemini, flashModel)
|
|
41
|
+
: coderFromEnv(process.env);
|
|
42
|
+
const reasoning = explicitCoder === undefined || explicitCoder.length === 0
|
|
43
|
+
? asReadOnly(withModel(gemini, proModel))
|
|
44
|
+
: asReadOnly(coder);
|
|
45
|
+
const reviewer = explicitCoder === undefined || explicitCoder.length === 0
|
|
46
|
+
? asReadOnly(withModel(gemini, flashModel))
|
|
47
|
+
: asReadOnly(coder);
|
|
48
|
+
const store = makePlanStore(nodePlainFileStore);
|
|
49
|
+
const planPath = join(input.workDir, defaultPlanPath(input.prompt));
|
|
50
|
+
yield* runNode({
|
|
51
|
+
workDir: input.workDir,
|
|
52
|
+
workspace: input.workspace,
|
|
53
|
+
userPrompt: input.prompt,
|
|
54
|
+
coder,
|
|
55
|
+
reasoning,
|
|
56
|
+
reviewers: [reviewer],
|
|
57
|
+
environment: process.env
|
|
58
|
+
}, (context) => Effect.gen(function* () {
|
|
59
|
+
const plan = yield* stage(context.events, "specification and plan", store.recoverOrCreate(planPath, Effect.gen(function* () {
|
|
60
|
+
const spec = yield* writeBrief(context.reasoning, input.prompt, specInstructions);
|
|
61
|
+
const planned = yield* planFrom(context.reasoning, spec, planInstructions);
|
|
62
|
+
return Plan.make({ ...planned, brief: spec });
|
|
63
|
+
})));
|
|
64
|
+
const spec = plan.brief === undefined || plan.brief.length === 0
|
|
65
|
+
? yield* writeBrief(context.reasoning, input.prompt, specInstructions)
|
|
66
|
+
: plan.brief;
|
|
67
|
+
const planWithSpec = plan.brief === undefined || plan.brief.length === 0
|
|
68
|
+
? Plan.make({ ...plan, brief: spec })
|
|
69
|
+
: plan;
|
|
70
|
+
if (planWithSpec !== plan) {
|
|
71
|
+
yield* store.save(planPath, planWithSpec);
|
|
72
|
+
}
|
|
73
|
+
yield* stage(context.events, "branch", context.git.checkoutOrCreate(planWithSpec.epicId));
|
|
74
|
+
const specPath = join(input.workDir, `specs/${planWithSpec.epicId}.md`);
|
|
75
|
+
yield* stage(context.events, "commit specification", nodePlainFileStore
|
|
76
|
+
.read(specPath)
|
|
77
|
+
.pipe(Effect.flatMap((existing) => existing === undefined
|
|
78
|
+
? nodePlainFileStore
|
|
79
|
+
.writeAtomic(specPath, `${spec}\n`)
|
|
80
|
+
.pipe(Effect.andThen(context.git.commitAll(`${planWithSpec.epicId}: specification`)), Effect.asVoid)
|
|
81
|
+
: Effect.void)));
|
|
82
|
+
const coderChat = yield* makeChat(context.coder, {
|
|
83
|
+
system: "Implement one task at a time. The committed specification is the contract; do not weaken its tests."
|
|
84
|
+
});
|
|
85
|
+
const testGate = lintCommand(nodeProcessExecutor, context.events, ["mvn", "-q", "test"], input.workDir);
|
|
86
|
+
const compileGate = lintCommand(nodeProcessExecutor, context.events, ["mvn", "-q", "test-compile"], input.workDir);
|
|
87
|
+
const firstTitle = planWithSpec.tasks[0]?.title;
|
|
88
|
+
yield* implementTaskLoop(store, context.events, planPath, planWithSpec, (task) => Effect.gen(function* () {
|
|
89
|
+
const testsTask = task.title === firstTitle;
|
|
90
|
+
yield* coderChat.ask(planWithSpec.taskPrompt(task));
|
|
91
|
+
yield* reviewAndFixLoop({
|
|
92
|
+
reviewers: minimalReviewers,
|
|
93
|
+
reviewerService: context.reviewers[0] ?? context.reasoning,
|
|
94
|
+
coder: coderChat,
|
|
95
|
+
taskTitle: task.title,
|
|
96
|
+
currentDiff: context.git.diffAll,
|
|
97
|
+
events: context.events,
|
|
98
|
+
lint: testsTask ? compileGate : testGate,
|
|
99
|
+
parallelism: 1
|
|
100
|
+
});
|
|
101
|
+
if (testsTask) {
|
|
102
|
+
const red = yield* testGate;
|
|
103
|
+
if (red.isClean) {
|
|
104
|
+
return yield* FlowAborted.make({
|
|
105
|
+
message: "the new tests pass before implementation; the specification is not encoded by a red test"
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
yield* context.git.commitAll(`${planWithSpec.epicId}: ${task.title}`);
|
|
110
|
+
}));
|
|
111
|
+
yield* stage(context.events, "verify acceptance criteria", Effect.flatMap(testGate, (result) => result.isClean ? Effect.void : Effect.fail(verificationFailure(result))));
|
|
112
|
+
}));
|
|
113
|
+
});
|
|
114
|
+
runFlowMain(program);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@llm4ts/shell",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "Interactive shell and CLI for llm4ts: flow discovery, run-a-flow, and view",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -49,9 +49,10 @@
|
|
|
49
49
|
],
|
|
50
50
|
"dependencies": {
|
|
51
51
|
"@effect/platform-node": "^4.0.0-beta.102",
|
|
52
|
-
"@llm4ts/
|
|
53
|
-
"@llm4ts/
|
|
54
|
-
"@llm4ts/
|
|
52
|
+
"@llm4ts/flow": "0.6.1",
|
|
53
|
+
"@llm4ts/runner": "0.6.1",
|
|
54
|
+
"@llm4ts/modernize": "0.6.1",
|
|
55
|
+
"@llm4ts/core": "0.6.1"
|
|
55
56
|
},
|
|
56
57
|
"peerDependencies": {
|
|
57
58
|
"effect": "^4.0.0-beta.102"
|
package/src/Cli.ts
CHANGED
|
@@ -76,14 +76,14 @@ export const renderFlowList = (
|
|
|
76
76
|
|
|
77
77
|
/**
|
|
78
78
|
* Resolves a flow argument to a runnable script path: an explicit path (a
|
|
79
|
-
* value containing a separator or ending in `.ts`) is used as-is,
|
|
80
|
-
* else is looked up by name in the discovery listing.
|
|
79
|
+
* value containing a separator or ending in `.ts`/`.js`) is used as-is,
|
|
80
|
+
* anything else is looked up by name in the discovery listing.
|
|
81
81
|
*/
|
|
82
82
|
export const resolveFlow = Effect.fn("@llm4ts/shell/Cli.resolveFlow")(function* (
|
|
83
83
|
reference: string,
|
|
84
84
|
tiers: FlowTierPaths
|
|
85
85
|
) {
|
|
86
|
-
if (reference.includes("/") || reference.endsWith(".ts")) {
|
|
86
|
+
if (reference.includes("/") || reference.endsWith(".ts") || reference.endsWith(".js")) {
|
|
87
87
|
const fs = yield* FileSystem
|
|
88
88
|
const exists = yield* fs.exists(reference).pipe(Effect.orElseSucceed(() => false))
|
|
89
89
|
if (!exists) {
|
|
@@ -115,6 +115,12 @@ const runCommand = Command.make(
|
|
|
115
115
|
Argument.variadic(),
|
|
116
116
|
Argument.withDescription("Task text passed to the flow")
|
|
117
117
|
),
|
|
118
|
+
repo: Flag.string("repo").pipe(
|
|
119
|
+
Flag.optional,
|
|
120
|
+
Flag.withDescription(
|
|
121
|
+
"Repository to run against, forwarded to the flow as --repo (defaults to the current directory)"
|
|
122
|
+
)
|
|
123
|
+
),
|
|
118
124
|
verbose: Flag.boolean("verbose").pipe(Flag.withDescription("Stream verbose flow output"))
|
|
119
125
|
},
|
|
120
126
|
(config) =>
|
|
@@ -127,7 +133,10 @@ const runCommand = Command.make(
|
|
|
127
133
|
}
|
|
128
134
|
const exitCode = yield* launchFlow({
|
|
129
135
|
flowPath,
|
|
130
|
-
taskArgs:
|
|
136
|
+
taskArgs: [
|
|
137
|
+
...(config.repo._tag === "Some" ? ["--repo", config.repo.value] : []),
|
|
138
|
+
...config.task
|
|
139
|
+
],
|
|
131
140
|
environment
|
|
132
141
|
})
|
|
133
142
|
yield* Effect.sync(() => {
|
package/src/FlowCatalog.ts
CHANGED
|
@@ -62,6 +62,11 @@ export const defaultTierPaths = (options: {
|
|
|
62
62
|
|
|
63
63
|
const tierOrder: ReadonlyArray<FlowTier> = ["project", "global", "builtin"]
|
|
64
64
|
|
|
65
|
+
// User-authored flows are .ts (launched with type stripping); the built-in
|
|
66
|
+
// tier ships .js because Node refuses to strip types under node_modules.
|
|
67
|
+
const flowExtension = (entry: string): string | undefined =>
|
|
68
|
+
entry.endsWith(".ts") ? ".ts" : entry.endsWith(".js") ? ".js" : undefined
|
|
69
|
+
|
|
65
70
|
const listTier = Effect.fn("@llm4ts/shell/FlowCatalog.listTier")(function* (
|
|
66
71
|
tier: FlowTier,
|
|
67
72
|
directory: string
|
|
@@ -70,14 +75,15 @@ const listTier = Effect.fn("@llm4ts/shell/FlowCatalog.listTier")(function* (
|
|
|
70
75
|
const entries = yield* fs.readDirectory(directory).pipe(Effect.orElseSucceed(() => []))
|
|
71
76
|
const flows: Array<Omit<DiscoveredFlow, "shadows">> = []
|
|
72
77
|
for (const entry of [...entries].sort()) {
|
|
73
|
-
|
|
78
|
+
const extension = flowExtension(entry)
|
|
79
|
+
if (extension === undefined) {
|
|
74
80
|
continue
|
|
75
81
|
}
|
|
76
82
|
const path = join(directory, entry)
|
|
77
83
|
const source = yield* fs.readFileString(path).pipe(Effect.orElseSucceed(() => ""))
|
|
78
84
|
const description = parseFlowDescription(source)
|
|
79
85
|
flows.push({
|
|
80
|
-
name: entry.slice(0, -
|
|
86
|
+
name: entry.slice(0, -extension.length),
|
|
81
87
|
path,
|
|
82
88
|
tier,
|
|
83
89
|
...(description === undefined ? {} : { description })
|
package/flows/implement.ts
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
// Persistent plan: plan the task, then implement, review, and commit one task at a time.
|
|
2
|
-
import { join } from "node:path"
|
|
3
|
-
import * as Effect from "effect/Effect"
|
|
4
|
-
import { implementPlanFlow } from "@llm4ts/flow/Flow"
|
|
5
|
-
import { defaultPlanPath } from "@llm4ts/flow/Plan"
|
|
6
|
-
import { planFrom } from "@llm4ts/flow/Planner"
|
|
7
|
-
import { makePlanStore } from "@llm4ts/flow/Persistence"
|
|
8
|
-
import { coderFromEnv } from "@llm4ts/runner/Connectors"
|
|
9
|
-
import { resolveFlowInput } from "@llm4ts/runner/FlowArgs"
|
|
10
|
-
import { runFlowMain, runNode } from "@llm4ts/runner/FlowRunner"
|
|
11
|
-
import { nodePlainFileStore } from "@llm4ts/runner/NodePlainFileStore"
|
|
12
|
-
|
|
13
|
-
const program = Effect.gen(function* () {
|
|
14
|
-
const input = yield* resolveFlowInput(
|
|
15
|
-
"Add a multiply function to the calculator, including focused tests."
|
|
16
|
-
)
|
|
17
|
-
const planPath = join(input.workDir, defaultPlanPath(input.prompt))
|
|
18
|
-
const store = makePlanStore(nodePlainFileStore)
|
|
19
|
-
|
|
20
|
-
yield* runNode(
|
|
21
|
-
{
|
|
22
|
-
workDir: input.workDir,
|
|
23
|
-
workspace: input.workspace,
|
|
24
|
-
userPrompt: input.prompt,
|
|
25
|
-
coder: coderFromEnv(process.env),
|
|
26
|
-
environment: process.env
|
|
27
|
-
},
|
|
28
|
-
(context) =>
|
|
29
|
-
implementPlanFlow(context, {
|
|
30
|
-
store,
|
|
31
|
-
planPath,
|
|
32
|
-
plan: planFrom(context.reasoning, input.prompt),
|
|
33
|
-
system: "Implement one task at a time in the current repository."
|
|
34
|
-
})
|
|
35
|
-
)
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
runFlowMain(program)
|