aw-spec-cli 0.1.26 → 0.1.27
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/assets/.agents/skills/appweaver/SKILL.md +7 -47
- package/assets/.appweaver/ORCH-DIALECT.md +13 -9
- package/assets/.appweaver/RULE-PARTITION.md +3 -3
- package/assets/.appweaver/STACK-PROFILES.md +188 -101
- package/assets/.appweaver/domain.json +1 -1
- package/assets/.appweaver/orchestrator/AGENT-PROFILE.md +38 -10
- package/assets/.appweaver/orchestrator/LAUNCHER.md +88 -0
- package/assets/.appweaver/orchestrator/README.md +120 -53
- package/assets/.appweaver/orchestrator/adapters/claude_agent_sdk.py +1 -1
- package/assets/.appweaver/orchestrator/modules/domain-select.orch.yaml +6 -4
- package/assets/.appweaver/orchestrator/modules/foundation-lane.orch.yaml +237 -0
- package/assets/.appweaver/orchestrator/modules/foundation-pack.orch.yaml +349 -0
- package/assets/.appweaver/orchestrator/modules/foundation-spec.orch.yaml +381 -0
- package/assets/.appweaver/orchestrator/modules/lane-gate.orch.yaml +4 -4
- package/assets/.appweaver/orchestrator/modules/tech-select.orch.yaml +3 -3
- package/assets/.appweaver/orchestrator/orchestrator.py +162 -63
- package/assets/.appweaver/orchestrator/rev-templates/domain-constitution.skeleton.md +2 -2
- package/assets/.appweaver/orchestrator/rev-templates/domain-requirements.skeleton.md +2 -2
- package/assets/.appweaver/orchestrator/rev-templates/domain-rule-bindings.skeleton.md +5 -5
- package/assets/.appweaver/orchestrator/rev-templates/frontend-rule-file.skeleton.md +1 -1
- package/assets/.appweaver/orchestrator/rev-templates/tech-rule-bindings.skeleton.md +14 -14
- package/assets/.appweaver/orchestrator/rev-templates/technical-constitution.skeleton.md +2 -2
- package/assets/.appweaver/orchestrator/rev-templates/technical-rule-file.skeleton.md +1 -1
- package/assets/.appweaver/orchestrator/rev-templates/ui-constitution.skeleton.md +3 -3
- package/assets/.appweaver/orchestrator/workflows/_smoke.orch.yaml +41 -3
- package/assets/.appweaver/orchestrator/workflows/aw-spec-add-component.orch.yaml +1 -1
- package/assets/.appweaver/orchestrator/workflows/aw-spec-adopt.orch.yaml +13 -13
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice-delta.orch.yaml +3 -3
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice.orch.yaml +3 -3
- package/assets/.appweaver/orchestrator/workflows/aw-spec-build.orch.yaml +4 -4
- package/assets/.appweaver/orchestrator/workflows/aw-spec-characterize.orch.yaml +2 -2
- package/assets/.appweaver/orchestrator/workflows/aw-spec-constitution.orch.yaml +61 -12
- package/assets/.appweaver/orchestrator/workflows/aw-spec-foundation.orch.yaml +172 -293
- package/assets/.appweaver/orchestrator/workflows/aw-spec-implement.orch.yaml +29 -28
- package/assets/.appweaver/orchestrator/workflows/aw-spec-onboard.orch.yaml +53 -53
- package/assets/.appweaver/orchestrator/workflows/aw-spec-parity.orch.yaml +2 -2
- package/assets/.appweaver/orchestrator/workflows/aw-spec-plan.orch.yaml +3 -3
- package/assets/.appweaver/orchestrator/workflows/aw-spec-run.orch.yaml +4 -4
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-bounded-context.orch.yaml +26 -2
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-domain.orch.yaml +21 -15
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-stack.orch.yaml +14 -14
- package/assets/.appweaver/orchestrator/workflows/aw-spec-specify.orch.yaml +2 -2
- package/assets/.appweaver/orchestrator/workflows/aw-spec-ui-model.orch.yaml +3 -3
- package/assets/.appweaver/orchestrator/workflows/aw-spec-ui-prototype.orch.yaml +6 -6
- package/assets/.appweaver/scripts/bash/aw-spec-actions.sh +284 -128
- package/assets/.appweaver/scripts/powershell/aw-spec-actions.ps1 +224 -70
- package/assets/.appweaver/scripts/python/aw-foundation.py +809 -20
- package/assets/.appweaver/scripts/python/aw-inventory.py +28 -21
- package/assets/.appweaver/scripts/python/aw-plan-coverage.py +2 -2
- package/assets/.appweaver/scripts/python/aw-rev-constitution.py +6 -6
- package/assets/.appweaver/scripts/python/aw-stack.py +195 -89
- package/assets/.appweaver/stack-profiles/angular.json +2 -1
- package/assets/.appweaver/stack-profiles/blazor-wasm.json +2 -1
- package/assets/.appweaver/stack-profiles/dotnet-webapi.json +16 -3
- package/assets/.appweaver/stack-profiles/go-chi.json +6 -1
- package/assets/.appweaver/stack-profiles/java-spring.json +12 -5
- package/assets/.appweaver/stack-profiles/nextjs.json +2 -1
- package/assets/.appweaver/stack-profiles/node-express.json +8 -2
- package/assets/.appweaver/stack-profiles/python-fastapi.json +9 -2
- package/assets/.appweaver/stack-profiles/react-vite.json +2 -1
- package/assets/.appweaver/stack-profiles/sveltekit.json +2 -1
- package/assets/.appweaver/stack-profiles/vue-vite.json +2 -1
- package/assets/.appweaver/stacks.json +37 -1
- package/assets/.appweaver/templates/constitution-ui-template.md +2 -2
- package/assets/.claude/skills/appweaver/SKILL.md +7 -46
- package/assets/foundations/dotnet/ModularMonolith/ModularMonolith.Reference.rules.json +31 -0
- package/assets/foundations/dotnet/VerticalSlice/VerticalSlice.Reference.rules.json +16 -0
- package/assets/foundations/foundation.spec.template.txt +73 -0
- package/assets/foundations/java/Hexagonal/Hexagonal.pack.txt +1 -1
- package/assets/foundations/java/Hexagonal/unpack-foundation.sh +4 -5
- package/assets/foundations/node/NextAppRouter/NextAppRouter.spec.txt +68 -0
- package/assets/guardrails/README.md +31 -17
- package/assets/guardrails/{projects/dotnet-default-project → project-specific}/domain-rule-bindings.md +5 -5
- package/assets/guardrails/{projects/dotnet-default-project → project-specific}/tech-rule-bindings.md +151 -114
- package/assets/guardrails/technology/dotnet-webapi/PACKAGING.md +58 -4
- package/assets/guardrails/technology/dotnet-webapi/coding-standards.md +1 -1
- package/assets/guardrails/technology/dotnet-webapi/dependency-policy.md +1 -1
- package/assets/guardrails/technology/dotnet-webapi/folder-structure.md +1 -1
- package/assets/guardrails/technology/dotnet-webapi/framework-api-map.md +1 -1
- package/assets/guardrails/technology/dotnet-webapi/testing.md +1 -1
- package/assets/guardrails/technology/nextjs/api-client.md +1 -1
- package/assets/guardrails/technology/nextjs/component-architecture.md +1 -1
- package/assets/guardrails/technology/nextjs/component-conventions.md +2 -2
- package/assets/guardrails/technology/nextjs/design-system.md +1 -1
- package/assets/guardrails/technology/nextjs/frontend-testing.md +1 -1
- package/assets/guardrails/technology/nextjs/routing-navigation.md +1 -1
- package/assets/guardrails/technology/nextjs/ui-constitution.md +3 -3
- package/package.json +1 -1
- package/assets/.agents/skills/appweaver-demo/SKILL.md +0 -59
- package/assets/.appweaver/orchestrator/.smoke-out/smoke.md +0 -6
- package/assets/.appweaver/orchestrator/workflows/aw-spec-demo.orch.yaml +0 -100
- package/assets/.claude/skills/appweaver-demo/SKILL.md +0 -59
- package/assets/.kilocode/agent.json +0 -28
- package/assets/.kilocode/workflows/appweaver-demo.md +0 -59
- package/assets/.kilocode/workflows/appweaver.md +0 -55
- /package/assets/guardrails/{projects/dotnet-default-project → project-specific}/domain/domain-requirements.md +0 -0
- /package/assets/guardrails/{projects/dotnet-default-project → project-specific}/frontend/ui-requirements.md +0 -0
- /package/assets/guardrails/{projects/dotnet-default-project → project-specific}/service/technical-requirements.md +0 -0
|
@@ -16,7 +16,7 @@ into a single cross-platform python core, with two brownfield additions:
|
|
|
16
16
|
are SIGNALS, not decisions: the workflow's agent draws the real aggregate
|
|
17
17
|
boundaries. This is the "inventory following aggregate root" the pipeline
|
|
18
18
|
needs — one aggregate ≈ one candidate specs/<Feature>/ vertical slice.
|
|
19
|
-
* frontend_signals — candidate FRONTEND
|
|
19
|
+
* frontend_signals — candidate FRONTEND lane root folders derived from component /
|
|
20
20
|
view / stylesheet clustering around frontend build configs. Like the
|
|
21
21
|
aggregate signals these are SIGNALS, not decisions, and they are what lets
|
|
22
22
|
/appweaver onboard answer "does this repo have a UI, and where" from the
|
|
@@ -24,7 +24,7 @@ into a single cross-platform python core, with two brownfield additions:
|
|
|
24
24
|
* ui_attribution — which frontend files serve which aggregate, so a slice's OKF
|
|
25
25
|
concept carries its UI surface and not just its backend files. Needed because
|
|
26
26
|
the lane scoping below builds aggregate_signals from backend files ALONE.
|
|
27
|
-
Searches the FRONTEND
|
|
27
|
+
Searches the FRONTEND LANE ROOT FOLDER only (declared root, else the roots the scan
|
|
28
28
|
found) and does not run at all where the repo has no frontend — a backend repo's
|
|
29
29
|
email templates and docs stylesheets are not its UI. Signals again: files
|
|
30
30
|
matching no aggregate are reported unattributed rather than forced into one.
|
|
@@ -153,7 +153,7 @@ UI_COMPONENT_RE = re.compile(
|
|
|
153
153
|
UI_MARKUP_RE = re.compile(r"\.(html?|xhtml)$", re.IGNORECASE)
|
|
154
154
|
UI_STYLE_RE = re.compile(r"\.(css|scss|sass|less|styl|pcss)$", re.IGNORECASE)
|
|
155
155
|
# A build/tooling config only a frontend carries. The strongest separable-lane
|
|
156
|
-
# signal there is: the directory holding one IS the lane root.
|
|
156
|
+
# signal there is: the directory holding one IS the lane root folder.
|
|
157
157
|
UI_BUILD_CFG_RE = re.compile(
|
|
158
158
|
r"(^|/)(angular\.json|vite\.config\.[cm]?[jt]s|next\.config\.[cm]?[jt]s|"
|
|
159
159
|
r"nuxt\.config\.[cm]?[jt]s|svelte\.config\.[cm]?[jt]s|vue\.config\.[cm]?[jt]s|"
|
|
@@ -223,7 +223,7 @@ UI_STRONG_MIN = 5
|
|
|
223
223
|
|
|
224
224
|
|
|
225
225
|
def frontend_signals(files, churn_map):
|
|
226
|
-
"""Derive CANDIDATE frontend lane
|
|
226
|
+
"""Derive CANDIDATE frontend lane root folders — the deterministic answer to "does
|
|
227
227
|
this repo have a UI, and where". Each root reports its evidence, what KIND of
|
|
228
228
|
UI it is, and whether it is SEPARABLE (owns a subtree) or server-rendered
|
|
229
229
|
inside the service tree.
|
|
@@ -471,9 +471,16 @@ def is_excluded(path, config_excludes):
|
|
|
471
471
|
|
|
472
472
|
# The agent integrations the kit installs beside a project, and the agent-instruction
|
|
473
473
|
# files at the repository root. Configuration for an assistant, never evidence of how
|
|
474
|
-
# the project's code is built.
|
|
475
|
-
|
|
476
|
-
|
|
474
|
+
# the project's code is built. Each agent folder is excluded whole, the project's own
|
|
475
|
+
# assistant setup in it included. `.kilocode` is where older kit installs put Kilo.
|
|
476
|
+
# `.github/` also holds the project's CI, so only the kit's launcher there is excluded,
|
|
477
|
+
# by its exact path.
|
|
478
|
+
KIT_AGENT_DIRS = (
|
|
479
|
+
".claude", ".agents", ".kilo", ".kilocode", ".cursor", ".copilot", ".devin",
|
|
480
|
+
".cline", ".clinerules", ".roo", ".opencode", ".factory", ".augment",
|
|
481
|
+
".continue", ".kiro", ".codex", ".gemini", ".amp",
|
|
482
|
+
)
|
|
483
|
+
KIT_AGENT_FILES = ("CLAUDE.md", "AGENTS.md", ".github/prompts/appweaver.prompt.md")
|
|
477
484
|
|
|
478
485
|
|
|
479
486
|
def kit_excludes(root):
|
|
@@ -485,11 +492,11 @@ def kit_excludes(root):
|
|
|
485
492
|
`guardrails/` and `foundations/` are ordinary names a project may use for its own
|
|
486
493
|
code, so each is excluded only when it has the kit's shape: `guardrails/` holding
|
|
487
494
|
one of the kit's rule tiers, `foundations/` holding a scaffold pack (a directory with
|
|
488
|
-
a `*.pack.txt`). The agent directories and
|
|
495
|
+
a `*.pack.txt`). The agent directories and agent instruction files are excluded
|
|
489
496
|
whenever present."""
|
|
490
497
|
names = [d for d in KIT_AGENT_DIRS if os.path.isdir(os.path.join(root, d))]
|
|
491
498
|
g = os.path.join(root, "guardrails")
|
|
492
|
-
if any(os.path.isdir(os.path.join(g, t)) for t in ("
|
|
499
|
+
if any(os.path.isdir(os.path.join(g, t)) for t in ("project-specific", "technology", "domain")):
|
|
493
500
|
names.append("guardrails")
|
|
494
501
|
f = os.path.join(root, "foundations")
|
|
495
502
|
if os.path.isdir(f):
|
|
@@ -518,7 +525,7 @@ def _ignore_rules(dirpath, base):
|
|
|
518
525
|
"""The patterns one .gitignore contributes, as (dir_rel, pattern, dir_only, anchored).
|
|
519
526
|
|
|
520
527
|
Best-effort by design — negation, character classes and escaped spaces are not
|
|
521
|
-
honoured. It exists for the one case git cannot answer: a lane root the HOST
|
|
528
|
+
honoured. It exists for the one case git cannot answer: a lane root folder the HOST
|
|
522
529
|
repository ignores wholesale, whose own nested .gitignore files still describe
|
|
523
530
|
what inside it is build output."""
|
|
524
531
|
rules = []
|
|
@@ -589,11 +596,11 @@ def list_files(root, config_excludes, lane_roots=()):
|
|
|
589
596
|
same ignore rules git itself would, including nested .gitignore files, so build
|
|
590
597
|
output (obj/, bin/, node_modules/) stays out exactly as before.
|
|
591
598
|
|
|
592
|
-
A DECLARED LANE ROOT IS READ EVEN WHEN THE HOST REPO IGNORES IT. A codebase under
|
|
599
|
+
A DECLARED LANE ROOT FOLDER IS READ EVEN WHEN THE HOST REPO IGNORES IT. A codebase under
|
|
593
600
|
assessment is often nested inside a repository that deliberately keeps it out of
|
|
594
601
|
version control — a test bed, a vendored drop, a legacy tree copied in for analysis.
|
|
595
602
|
git then answers "nothing here" for the one subtree select-stack said IS the code.
|
|
596
|
-
The declaration outranks the host's ignore rule: an ignored lane root is walked from
|
|
603
|
+
The declaration outranks the host's ignore rule: an ignored lane root folder is walked from
|
|
597
604
|
disk, with build output pruned and the lane's OWN nested .gitignore files applied.
|
|
598
605
|
"""
|
|
599
606
|
if git(root, "rev-parse", "--git-dir"):
|
|
@@ -2941,7 +2948,7 @@ def attribute_ui_files(signals, fe_files, churn_map, ui_roots, feature_aggs=None
|
|
|
2941
2948
|
the architecture note instead of being forced into a slice.
|
|
2942
2949
|
|
|
2943
2950
|
SCOPED TO THE FRONTEND TREE, AND SKIPPED ENTIRELY WITHOUT ONE. `ui_roots` is the
|
|
2944
|
-
lane
|
|
2951
|
+
lane root folder — the declared one, or the roots the scan actually found. Two
|
|
2945
2952
|
guards, because neither is sufficient alone: with no UI root declared scope_to_lanes
|
|
2946
2953
|
hands back the WHOLE REPO as the frontend list, and the file-shape regexes match
|
|
2947
2954
|
plenty of things a backend repo legitimately owns — a server-side email template, a
|
|
@@ -3091,7 +3098,7 @@ def bundle_state(bundle_dir):
|
|
|
3091
3098
|
|
|
3092
3099
|
|
|
3093
3100
|
def _lane_prefix(p):
|
|
3094
|
-
"""A declared
|
|
3101
|
+
"""A declared lane root folder as a '/'-terminated repo-relative prefix, or ''."""
|
|
3095
3102
|
s = (p or "").strip().replace("\\", "/").lstrip("./").lstrip("/")
|
|
3096
3103
|
if s and not s.endswith("/"):
|
|
3097
3104
|
s += "/"
|
|
@@ -3177,7 +3184,7 @@ def cmd_scan(args):
|
|
|
3177
3184
|
|
|
3178
3185
|
# A DECLARED UI lane is a lane. The strength test below UI_STRONG_MIN exists to
|
|
3179
3186
|
# stop a scan INVENTING one from a wwwroot/ of loose scripts; it has no business
|
|
3180
|
-
# overruling a person who declared the lane and named its
|
|
3187
|
+
# overruling a person who declared the lane and named its lane root folder. What the
|
|
3181
3188
|
# evidence still decides is what goes IN the governance — and a declared root
|
|
3182
3189
|
# holding no recognisable UI files is reported as exactly that, so onboard can
|
|
3183
3190
|
# say so rather than quietly authoring a UI half out of nothing.
|
|
@@ -3193,7 +3200,7 @@ def cmd_scan(args):
|
|
|
3193
3200
|
"build_configs": [], "route_files": [], "store_files": []}]
|
|
3194
3201
|
|
|
3195
3202
|
# Give every aggregate candidate the UI that serves it — searching the FRONTEND
|
|
3196
|
-
#
|
|
3203
|
+
# LANE ROOT FOLDER and nothing else. The declared root is the authority; absent a
|
|
3197
3204
|
# declaration the roots the scan actually found stand in, which keeps a repo whose
|
|
3198
3205
|
# UI is server-rendered inside the service tree covered. With neither, this repo
|
|
3199
3206
|
# has no frontend and the pass does not run at all.
|
|
@@ -3295,7 +3302,7 @@ def cmd_scan(args):
|
|
|
3295
3302
|
"frontend_present": "true" if fsig["present"] else "false",
|
|
3296
3303
|
"frontend_separable": "true" if fsig["separable"] else "false",
|
|
3297
3304
|
"frontend_root_count": len(fsig["roots"]),
|
|
3298
|
-
# The PRIMARY lane root — the strongest candidate, and the single value a
|
|
3305
|
+
# The PRIMARY lane root folder — the strongest candidate, and the single value a
|
|
3299
3306
|
# lane declaration needs. Empty when no root reached lane strength.
|
|
3300
3307
|
"frontend_root": next((r["root"] for r in fsig["roots"]
|
|
3301
3308
|
if r["confidence"] == "strong"), ""),
|
|
@@ -4526,10 +4533,10 @@ def main():
|
|
|
4526
4533
|
ps.add_argument("--bundle", help="OKF bundle dir to check for existing curation (default: <root>/knowledge).")
|
|
4527
4534
|
ps.add_argument("--config", help="okf-config.yml whose exclude: list is honored.")
|
|
4528
4535
|
ps.add_argument("--service-root", dest="service_root", default="",
|
|
4529
|
-
help="The
|
|
4536
|
+
help="The declared lane root folder of the service lane. Backend signals are "
|
|
4530
4537
|
"derived from this subtree only.")
|
|
4531
4538
|
ps.add_argument("--ui-root", dest="ui_root", default="",
|
|
4532
|
-
help="The
|
|
4539
|
+
help="The declared lane root folder of the UI lane. Frontend signals are derived "
|
|
4533
4540
|
"from this subtree only, and it is excluded from the backend's — "
|
|
4534
4541
|
"so neither lane's substrate carries the other's files.")
|
|
4535
4542
|
ps.add_argument("--map", help="Machine map whose features attribute UI feature folders "
|
|
@@ -4541,9 +4548,9 @@ def main():
|
|
|
4541
4548
|
pp.add_argument("--root", help="Codebase to scan (default: CWD).")
|
|
4542
4549
|
pp.add_argument("--config", help="okf-config.yml whose exclude: list is honored.")
|
|
4543
4550
|
pp.add_argument("--service-root", dest="service_root", default="",
|
|
4544
|
-
help="The
|
|
4551
|
+
help="The declared lane root folder of the service lane.")
|
|
4545
4552
|
pp.add_argument("--ui-root", dest="ui_root", default="",
|
|
4546
|
-
help="The
|
|
4553
|
+
help="The declared lane root folder of the UI lane.")
|
|
4547
4554
|
pp.add_argument("--strategy", default="",
|
|
4548
4555
|
help="Pass A strategy: structure | reference | persistence. "
|
|
4549
4556
|
"Default: the highest-coverage AVAILABLE strategy, ties "
|
|
@@ -15,7 +15,7 @@ lane's code names it and whether the generating model reported it done.
|
|
|
15
15
|
TWO KINDS OF ELEMENT.
|
|
16
16
|
checked an identifier the code must contain — an entity, value object, domain
|
|
17
17
|
event or use-case (backend); a component (frontend); an identifier in a
|
|
18
|
-
delta plan's ADDED table. Searched for in the lane
|
|
18
|
+
delta plan's ADDED table. Searched for in the lane root folder, test
|
|
19
19
|
files excluded. Entities and value objects must appear as a whole word.
|
|
20
20
|
Every other checked name may carry a suffix, because stacks add one by
|
|
21
21
|
convention: `MedicationOrderPlaced` is realised as
|
|
@@ -246,7 +246,7 @@ def is_test_file(rel):
|
|
|
246
246
|
|
|
247
247
|
|
|
248
248
|
def scan(repo, source_path, test_path, names):
|
|
249
|
-
"""{name: [relative files naming it]} over the lane
|
|
249
|
+
"""{name: [relative files naming it]} over the lane root folder, tests excluded.
|
|
250
250
|
|
|
251
251
|
`names` holds (name, whole) keys: whole is True when the name must appear as a whole
|
|
252
252
|
word, False when an identifier that STARTS with it counts.
|
|
@@ -15,7 +15,7 @@ amends it additively from its own code. The standalone
|
|
|
15
15
|
steps without calling this core.
|
|
16
16
|
|
|
17
17
|
This core is the MODEL-FREE half: it gathers the project framing the OKF bundle holds
|
|
18
|
-
and scaffolds the `guardrails/
|
|
18
|
+
and scaffolds the `guardrails/brf-<domain>/` project directory; the calling
|
|
19
19
|
workflow's authoring agents fill its rule files + the split constitution. Governance
|
|
20
20
|
stance is AS-BUILT (the constitution reflects what the code does, not an aspiration).
|
|
21
21
|
|
|
@@ -36,7 +36,7 @@ STDERR so they never corrupt the JSON contract:
|
|
|
36
36
|
works; it answers with this framing and no longer lists features.
|
|
37
37
|
|
|
38
38
|
project-setup --name <domain> [--fallback <name>] [--root <dir>]
|
|
39
|
-
-> slugify + `brf-` prefix the domain, create guardrails
|
|
39
|
+
-> slugify + `brf-` prefix the domain, create guardrails/<slug>/ with its
|
|
40
40
|
service/ frontend/ domain/ subfolders when the project does not exist yet.
|
|
41
41
|
Without --name, an ACTIVE `brf-` project (.appweaver/domain.json) that already
|
|
42
42
|
holds rule files wins over the fallback name, so every later context amends the
|
|
@@ -196,7 +196,7 @@ def _active_brf_project(root):
|
|
|
196
196
|
return ""
|
|
197
197
|
if not name.startswith("brf-"):
|
|
198
198
|
return ""
|
|
199
|
-
d = os.path.join(root, "guardrails",
|
|
199
|
+
d = os.path.join(root, "guardrails", name)
|
|
200
200
|
return name if _md_files(d, root) else ""
|
|
201
201
|
|
|
202
202
|
|
|
@@ -214,7 +214,7 @@ def cmd_pack_setup(args):
|
|
|
214
214
|
slug = re.sub(r"^brf-+", "", slug)
|
|
215
215
|
domain_slug = "brf-" + slug
|
|
216
216
|
|
|
217
|
-
pack_dir = os.path.join(root, "guardrails",
|
|
217
|
+
pack_dir = os.path.join(root, "guardrails", domain_slug)
|
|
218
218
|
# A project that already holds .md files was authored by an earlier context: the
|
|
219
219
|
# calling workflow AMENDS it rather than authoring it again. The walk covers the
|
|
220
220
|
# whole subtree, because a project's rule files live in its service/ frontend/
|
|
@@ -248,7 +248,7 @@ def cmd_pack_setup(args):
|
|
|
248
248
|
"root": root,
|
|
249
249
|
}
|
|
250
250
|
print(
|
|
251
|
-
f"[govern-backward] project: guardrails/
|
|
251
|
+
f"[govern-backward] project: guardrails/{domain_slug}/ "
|
|
252
252
|
f"({'EXISTS — ' + ', '.join(f'{k} {len(v)}' for k, v in tiers.items()) if present else 'created'})",
|
|
253
253
|
file=sys.stderr,
|
|
254
254
|
)
|
|
@@ -273,7 +273,7 @@ def main(argv):
|
|
|
273
273
|
# the folder it creates is a PROJECT, so the primary name says so, and a caller
|
|
274
274
|
# written against either spelling reaches the same code.
|
|
275
275
|
for name in ("project-setup", "pack-setup"):
|
|
276
|
-
ps = sub.add_parser(name, help="create the guardrails/
|
|
276
|
+
ps = sub.add_parser(name, help="create the guardrails/brf-<domain>/ project dir")
|
|
277
277
|
ps.add_argument("--name", default="")
|
|
278
278
|
ps.add_argument("--fallback", default="")
|
|
279
279
|
ps.add_argument("--root", default="")
|