@capagents/forgeqe 0.0.0-stage → 1.0.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/HOW_TO_USE.md +1639 -0
- package/README.md +338 -2
- package/bin/forgeqe.d.ts +3 -0
- package/bin/forgeqe.js +188 -0
- package/docs/ARCHITECTURE.md +408 -0
- package/docs/security-data-flow.html +1870 -0
- package/docs/user-guide.html +2094 -0
- package/evals/domain/access-role-change.yaml +37 -0
- package/evals/domain/banking-payment-transfer.yaml +41 -0
- package/evals/domain/gdpr-account-erasure.yaml +30 -0
- package/evals/domain/insurance-claim-fnol.yaml +30 -0
- package/evals/domain/insurance-policy-renewal.yaml +37 -0
- package/evals/domain/lending-loan-application.yaml +36 -0
- package/forgeqe-architecture.html +718 -0
- package/forgeqe.example.yaml +203 -0
- package/knowledge/README.md +18 -0
- package/knowledge/business-rules.md +10 -0
- package/knowledge/compliance.md +16 -0
- package/knowledge/environments.md +5 -0
- package/knowledge/glossary.md +5 -0
- package/knowledge/product.md +16 -0
- package/knowledge/roles.md +9 -0
- package/knowledge/test-data.md +7 -0
- package/llms.example.json +51 -0
- package/package.json +63 -4
- package/prompts/README.md +34 -0
- package/prompts/api-scenarios.md +21 -0
- package/prompts/api-tests.md +39 -0
- package/prompts/brain.md +166 -0
- package/prompts/browser-capture.md +7 -0
- package/prompts/browser-case-report.md +7 -0
- package/prompts/browser-checks.md +14 -0
- package/prompts/browser-efficiency.md +7 -0
- package/prompts/browser-error-recovery.md +15 -0
- package/prompts/browser-extra.md +5 -0
- package/prompts/browser-goal-verification.md +13 -0
- package/prompts/browser-skill-select.md +6 -0
- package/prompts/domain-rule.md +13 -0
- package/prompts/eval-judge.md +19 -0
- package/prompts/explore-charters.md +27 -0
- package/prompts/find-tests.md +28 -0
- package/prompts/heal.md +40 -0
- package/prompts/pr-review.md +29 -0
- package/prompts/skill-rule.md +6 -0
- package/prompts/test-agent.md +34 -0
- package/prompts/test-reviewer.md +30 -0
- package/prompts/test-rules.md +28 -0
- package/py/forgeqe_engine/__init__.py +3 -0
- package/py/forgeqe_engine/__main__.py +3 -0
- package/py/forgeqe_engine/brand.py +453 -0
- package/py/forgeqe_engine/brand_assets.py +6 -0
- package/py/forgeqe_engine/browser.py +196 -0
- package/py/forgeqe_engine/capture.py +538 -0
- package/py/forgeqe_engine/checks.py +98 -0
- package/py/forgeqe_engine/cli.py +188 -0
- package/py/forgeqe_engine/diagnostics.py +609 -0
- package/py/forgeqe_engine/llm.py +195 -0
- package/py/forgeqe_engine/logins.py +251 -0
- package/py/forgeqe_engine/loops.py +224 -0
- package/py/forgeqe_engine/memory.py +1004 -0
- package/py/forgeqe_engine/network.py +244 -0
- package/py/forgeqe_engine/prompts.py +20 -0
- package/py/forgeqe_engine/replay.py +240 -0
- package/py/forgeqe_engine/report.py +173 -0
- package/py/forgeqe_engine/service.py +1335 -0
- package/py/forgeqe_engine/settings.py +93 -0
- package/py/forgeqe_engine/skills.py +88 -0
- package/py/forgeqe_engine/summary.py +239 -0
- package/py/forgeqe_engine/urls.py +81 -0
- package/py/forgeqe_engine/workflow_yaml.py +689 -0
- package/skills/dotnet-nunit-playwright/SKILL.md +34 -0
- package/skills/dynamics-365/SKILL.md +21 -0
- package/skills/playwright-typescript/SKILL.md +35 -0
- package/skills/writing-skills/SKILL.md +30 -0
- package/src/api/plan.ts +246 -0
- package/src/api/run.ts +204 -0
- package/src/api/scenario.ts +502 -0
- package/src/api/schema.ts +170 -0
- package/src/api/spec.ts +624 -0
- package/src/api/store.ts +232 -0
- package/src/api/traffic.ts +281 -0
- package/src/api/types.ts +163 -0
- package/src/api/write.ts +272 -0
- package/src/auth/signin.ts +494 -0
- package/src/auth/store.ts +170 -0
- package/src/brain/index.ts +189 -0
- package/src/brain/prompt.ts +19 -0
- package/src/capabilities/docs.ts +101 -0
- package/src/capabilities/guide.ts +455 -0
- package/src/capabilities/registry.ts +343 -0
- package/src/capabilities/render.ts +106 -0
- package/src/capabilities/status.ts +137 -0
- package/src/card.ts +11 -0
- package/src/cases/ado.ts +325 -0
- package/src/cases/api.ts +117 -0
- package/src/cases/bugs.ts +223 -0
- package/src/cases/confluence.ts +111 -0
- package/src/cases/context.ts +223 -0
- package/src/cases/dashboards.ts +137 -0
- package/src/cases/failure.ts +97 -0
- package/src/cases/files.ts +295 -0
- package/src/cases/goal.ts +53 -0
- package/src/cases/http.ts +111 -0
- package/src/cases/index.ts +131 -0
- package/src/cases/jira.ts +122 -0
- package/src/cases/qtest.ts +108 -0
- package/src/cases/quality.ts +190 -0
- package/src/cases/rag.ts +100 -0
- package/src/cases/readiness.ts +284 -0
- package/src/cases/save.ts +149 -0
- package/src/cases/sprints.ts +148 -0
- package/src/cases/testrail.ts +176 -0
- package/src/cases/types.ts +107 -0
- package/src/cases/wiki.ts +105 -0
- package/src/cases/xray.ts +206 -0
- package/src/cases/zephyr.ts +153 -0
- package/src/childEnv.ts +86 -0
- package/src/cli/args.ts +114 -0
- package/src/cli/help.ts +167 -0
- package/src/cli/index.ts +1142 -0
- package/src/cli/terminal.ts +187 -0
- package/src/code/areas.ts +337 -0
- package/src/code/attention.ts +214 -0
- package/src/code/find.ts +540 -0
- package/src/code/handoff.ts +541 -0
- package/src/code/history.ts +170 -0
- package/src/code/impact.ts +231 -0
- package/src/code/index.ts +34 -0
- package/src/code/livebrowser.ts +67 -0
- package/src/code/loop.ts +603 -0
- package/src/code/notes.ts +75 -0
- package/src/code/profile.ts +226 -0
- package/src/code/prompt.ts +303 -0
- package/src/code/prreview.ts +649 -0
- package/src/code/prtests.ts +103 -0
- package/src/code/replay.ts +772 -0
- package/src/code/review.ts +202 -0
- package/src/code/runtime.ts +757 -0
- package/src/code/slices.ts +159 -0
- package/src/code/testrun.ts +187 -0
- package/src/code/traceability.ts +100 -0
- package/src/code/worktree.ts +153 -0
- package/src/commands.ts +101 -0
- package/src/config.ts +970 -0
- package/src/detail.ts +65 -0
- package/src/domain.ts +19 -0
- package/src/engine/bridge.ts +359 -0
- package/src/engine/mock.ts +102 -0
- package/src/engine/setup.ts +298 -0
- package/src/entra.ts +154 -0
- package/src/env.ts +24 -0
- package/src/evals/domain.ts +338 -0
- package/src/explore/charters.ts +151 -0
- package/src/explore/run.ts +280 -0
- package/src/explore/story.ts +106 -0
- package/src/guard.ts +51 -0
- package/src/heal/loop.ts +357 -0
- package/src/heal/prompt.ts +143 -0
- package/src/heal/report.ts +95 -0
- package/src/index.ts +12 -0
- package/src/init.ts +102 -0
- package/src/knowledge.ts +114 -0
- package/src/legacyEnv.ts +9 -0
- package/src/llm.ts +61 -0
- package/src/locale.ts +25 -0
- package/src/logins.ts +148 -0
- package/src/managed.ts +101 -0
- package/src/mcp/apitools.ts +335 -0
- package/src/mcp/captools.ts +25 -0
- package/src/mcp/casetools.ts +223 -0
- package/src/mcp/commands.ts +421 -0
- package/src/mcp/exploretools.ts +129 -0
- package/src/mcp/external.ts +172 -0
- package/src/mcp/git.ts +78 -0
- package/src/mcp/notifytools.ts +49 -0
- package/src/mcp/prtools.ts +261 -0
- package/src/mcp/qualitytools.ts +154 -0
- package/src/mcp/repotests.ts +286 -0
- package/src/mcp/server.ts +234 -0
- package/src/mcp/tools.ts +810 -0
- package/src/memory.ts +250 -0
- package/src/money.ts +39 -0
- package/src/monitor/alerts.ts +94 -0
- package/src/monitor/background.ts +68 -0
- package/src/monitor/run.ts +263 -0
- package/src/monitor/state.ts +105 -0
- package/src/notify/events.ts +144 -0
- package/src/notify/notice.ts +177 -0
- package/src/prices.ts +175 -0
- package/src/print.ts +54 -0
- package/src/privacy.ts +35 -0
- package/src/prompts.ts +165 -0
- package/src/quality/budgets.ts +249 -0
- package/src/quality/history.ts +149 -0
- package/src/quality/visual.ts +201 -0
- package/src/report/data.ts +475 -0
- package/src/report/html.ts +106 -0
- package/src/report/template.html +84 -0
- package/src/run.ts +243 -0
- package/src/sessionLog.ts +216 -0
- package/src/sessionLogins.ts +94 -0
- package/src/skills/registry.ts +217 -0
- package/src/skills/scaffold.ts +65 -0
- package/src/skills/select.ts +37 -0
- package/src/skills/view.ts +44 -0
- package/src/suite/report.ts +135 -0
- package/src/suite/run.ts +303 -0
- package/src/summary.ts +188 -0
- package/src/tools.ts +130 -0
- package/src/tui/App.tsx +983 -0
- package/src/tui/clipboard.ts +36 -0
- package/src/tui/components.tsx +263 -0
- package/src/tui/dialogs.ts +65 -0
- package/src/tui/main.tsx +20 -0
- package/src/tui/text.ts +28 -0
- package/src/tui/theme.ts +104 -0
- package/src/tui/transcript.tsx +344 -0
- package/src/types.ts +136 -0
- package/src/update.ts +311 -0
- package/src/usage.ts +158 -0
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What ForgeQE can do: the one source its agent, the shell's /features, `forgeqe features`, and the Agent map in
|
|
3
|
+
* HOW_TO_USE.md are built from. Every tool belongs to exactly one capability, and every docs reference names a real
|
|
4
|
+
* heading; tests/capabilities.test.ts fails when either drifts, so a new tool cannot ship without saying what it is for.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/** A system ForgeQE reads from or writes to, as configured in forgeqe.yaml (or signed in with /auth). */
|
|
8
|
+
export type System = "azure_devops" | "jira" | "xray" | "testrail" | "zephyr" | "qtest" | "confluence"
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* What a capability needs before it works. Each kind is checked against the loaded config and this machine by
|
|
12
|
+
* src/capabilities/status.ts; `optional` ones only widen what it does (a capability without them still works).
|
|
13
|
+
*/
|
|
14
|
+
export type Requirement =
|
|
15
|
+
/** A live llms.json profile for the browser agent (not none or the offline mock). */
|
|
16
|
+
| { kind: "live_model"; optional?: boolean }
|
|
17
|
+
/** ForgeQE's coding agent, Scribe: a live profile (or code.model) and the agent runtime. */
|
|
18
|
+
| { kind: "coding_agent"; optional?: boolean }
|
|
19
|
+
/** At least one of these systems is set up. */
|
|
20
|
+
| { kind: "system"; anyOf: System[]; optional?: boolean; why?: string }
|
|
21
|
+
/** sources.bugs in forgeqe.yaml. */
|
|
22
|
+
| { kind: "bugs"; optional?: boolean }
|
|
23
|
+
/** monitor.alerts (Teams or email) in forgeqe.yaml. */
|
|
24
|
+
| { kind: "monitor_alerts"; optional?: boolean }
|
|
25
|
+
/** domain: (industry and regulations) in forgeqe.yaml. */
|
|
26
|
+
| { kind: "domain"; optional?: boolean }
|
|
27
|
+
/** notify.channels (Teams or email) in forgeqe.yaml. */
|
|
28
|
+
| { kind: "notify_channels"; optional?: boolean }
|
|
29
|
+
/** A command-line tool on PATH. */
|
|
30
|
+
| { kind: "command"; command: "gh" | "az" | "git" | "npx"; optional?: boolean; why?: string }
|
|
31
|
+
/** Something only the request can supply (a repository, a pull request); stated, never checked. */
|
|
32
|
+
| { kind: "input"; what: string }
|
|
33
|
+
/** Alternatives: met when any one of them is (GitHub through gh, or Azure DevOps). */
|
|
34
|
+
| { kind: "either"; of: Requirement[]; optional?: boolean }
|
|
35
|
+
|
|
36
|
+
/** A heading in README.md or HOW_TO_USE.md, by its exact text (### headings without their parent). */
|
|
37
|
+
export type DocRef = { doc: "README.md" | "HOW_TO_USE.md"; heading: string }
|
|
38
|
+
|
|
39
|
+
export type Area = "Browser" | "Test cases" | "Automated tests" | "Pull requests" | "API" | "Quality" | "Release" | "Operations" | "Systems" | "Setup"
|
|
40
|
+
|
|
41
|
+
export type Capability = {
|
|
42
|
+
/** Stable id the agent asks for details by. */
|
|
43
|
+
id: string
|
|
44
|
+
area: Area
|
|
45
|
+
title: string
|
|
46
|
+
/** What it does, for a user who has never seen ForgeQE: one or two sentences. */
|
|
47
|
+
summary: string
|
|
48
|
+
/** The tools that carry it out (forgeqe_ prefix left off). */
|
|
49
|
+
tools: string[]
|
|
50
|
+
/** How a user might ask for it, in their words. */
|
|
51
|
+
examples: string[]
|
|
52
|
+
requires: Requirement[]
|
|
53
|
+
/** What it does not do, so the agent never promises it. */
|
|
54
|
+
limits?: string[]
|
|
55
|
+
docs: DocRef[]
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export const CAPABILITIES: Capability[] = [
|
|
59
|
+
{
|
|
60
|
+
id: "browser-goals",
|
|
61
|
+
area: "Browser",
|
|
62
|
+
title: "Run any goal in a real browser",
|
|
63
|
+
summary:
|
|
64
|
+
"Carries out a plain-language goal in Chrome one step at a time and verifies the outcome, with screenshots, a report, and the steps it took. Page memory replays steps that already worked on a site, so repeat runs are faster and cheaper.",
|
|
65
|
+
tools: ["browser_run", "run_details", "page_memory"],
|
|
66
|
+
examples: ["Go to booking.com and search hotels in Mumbai for next weekend", "What does ForgeQE know about the checkout page?"],
|
|
67
|
+
requires: [{ kind: "live_model" }],
|
|
68
|
+
limits: ["Web applications in Chrome or Chromium only: not native mobile or desktop applications."],
|
|
69
|
+
docs: [
|
|
70
|
+
{ doc: "HOW_TO_USE.md", heading: "4. Without the shell" },
|
|
71
|
+
{ doc: "HOW_TO_USE.md", heading: "5. Page memory" },
|
|
72
|
+
],
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
id: "logins",
|
|
76
|
+
area: "Browser",
|
|
77
|
+
title: "Saved logins for the application under test",
|
|
78
|
+
summary: "Signs the browser agent in with saved credentials (including TOTP) and reuses the session; a user can also sign in by hand once in a browser.",
|
|
79
|
+
tools: ["logins"],
|
|
80
|
+
examples: ["Sign in to the admin site and keep the session", "Forget the saved session for shop.example.com"],
|
|
81
|
+
requires: [],
|
|
82
|
+
docs: [{ doc: "HOW_TO_USE.md", heading: "Signing in" }],
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
id: "run-test-cases",
|
|
86
|
+
area: "Test cases",
|
|
87
|
+
title: "Run manual test cases in browsers",
|
|
88
|
+
summary:
|
|
89
|
+
"Reads test cases from Azure DevOps, Jira, Xray, TestRail, Zephyr Scale, qTest, a file, or pasted text, runs each in a real browser with a verdict per step, writes report.html and junit.xml, posts results back, and files or updates bugs for failures.",
|
|
90
|
+
tools: ["find_test_cases", "run_test_cases"],
|
|
91
|
+
examples: ["Run SHOP-12 and SHOP-14 from Jira, two browsers at once", "Run test plan 12 suite 34 from Azure DevOps and don't post results"],
|
|
92
|
+
requires: [
|
|
93
|
+
{ kind: "live_model" },
|
|
94
|
+
{ kind: "system", anyOf: ["azure_devops", "jira", "xray", "testrail", "zephyr", "qtest"], optional: true, why: "to read cases from a test management system and post results back (files and pasted text work without one)" },
|
|
95
|
+
{ kind: "bugs", optional: true },
|
|
96
|
+
],
|
|
97
|
+
docs: [
|
|
98
|
+
{ doc: "HOW_TO_USE.md", heading: "7. Test cases and suites" },
|
|
99
|
+
{ doc: "HOW_TO_USE.md", heading: "Where cases come from" },
|
|
100
|
+
{ doc: "HOW_TO_USE.md", heading: "Running" },
|
|
101
|
+
{ doc: "HOW_TO_USE.md", heading: "Results back in the source" },
|
|
102
|
+
{ doc: "HOW_TO_USE.md", heading: "Bugs for failures" },
|
|
103
|
+
],
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
id: "test-case-quality",
|
|
107
|
+
area: "Test cases",
|
|
108
|
+
title: "Check and improve test cases",
|
|
109
|
+
summary:
|
|
110
|
+
"Checks test cases before they run (vague or missing expected results, missing test data, placeholders, duplicates) against the story, feature, and epic they test, then rewrites Azure DevOps cases once the user agrees.",
|
|
111
|
+
tools: ["check_test_cases", "update_test_case"],
|
|
112
|
+
examples: ["Are the cases in test plan 12 good enough to run?", "Fix the expected results of case 4512"],
|
|
113
|
+
requires: [{ kind: "system", anyOf: ["azure_devops"], optional: true, why: "to rewrite cases in place" }],
|
|
114
|
+
docs: [{ doc: "HOW_TO_USE.md", heading: "Checking test cases before they run" }],
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
id: "write-test-cases",
|
|
118
|
+
area: "Test cases",
|
|
119
|
+
title: "Write test cases from a story",
|
|
120
|
+
summary: "Writes test cases from a story's acceptance criteria and saves them in Azure DevOps or Jira, linked to the story.",
|
|
121
|
+
tools: ["save_test_cases"],
|
|
122
|
+
examples: ["Write test cases for story 1234 and save them"],
|
|
123
|
+
requires: [{ kind: "system", anyOf: ["azure_devops", "jira"] }],
|
|
124
|
+
docs: [{ doc: "HOW_TO_USE.md", heading: "Where cases come from" }],
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
id: "domain-compliance",
|
|
128
|
+
area: "Test cases",
|
|
129
|
+
title: "Domain and regulatory test design",
|
|
130
|
+
summary:
|
|
131
|
+
"Every agent works as an expert in the product's industry would, automatically: the risks the industry is known for and the obligations of its regulations and standards (for example DORA, PSD2, GDPR, ISO 27001), declared in forgeqe.yaml or worked out from the story and the application. It applies to analysing requirements, writing and checking test cases, exploration, writing, reviewing, and healing test code, API tests, finding tests, and pull request reviews, labelled so coverage can be traced and citing only the control ids in the team's compliance file. forgeqe eval domain measures how well the agent does it.",
|
|
132
|
+
tools: [],
|
|
133
|
+
examples: ["Write test cases for story 1234, covering our DORA and PSD2 obligations", "Which GDPR obligations do these cases miss?"],
|
|
134
|
+
requires: [{ kind: "domain", optional: true }],
|
|
135
|
+
limits: ["Designs and checks tests for regulatory obligations; it does not certify compliance or replace an audit.", "Cites control or article ids only from the team's compliance file, never from memory."],
|
|
136
|
+
docs: [{ doc: "HOW_TO_USE.md", heading: "16. Domain and regulations" }],
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
id: "exploratory-testing",
|
|
140
|
+
area: "Test cases",
|
|
141
|
+
title: "Exploratory testing of a story",
|
|
142
|
+
summary:
|
|
143
|
+
"Explores a story in real browsers beyond its scripted cases (edge cases, invalid input, boundaries, roles, state), confirms each defect by reproducing it, proposes test cases, and files bugs once the user agrees.",
|
|
144
|
+
tools: ["explore_story"],
|
|
145
|
+
examples: ["Explore story 1234 and find bugs", "Test this feature beyond the test cases"],
|
|
146
|
+
requires: [{ kind: "live_model" }, { kind: "system", anyOf: ["azure_devops", "jira"], optional: true, why: "to read the story and file bugs" }],
|
|
147
|
+
docs: [{ doc: "HOW_TO_USE.md", heading: "Exploratory testing from a story" }],
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
id: "write-tests",
|
|
151
|
+
area: "Automated tests",
|
|
152
|
+
title: "Turn a passed run into an automated test",
|
|
153
|
+
summary:
|
|
154
|
+
"Scribe, the coding agent, writes a test for a passed browser run in the repository's own framework, page objects, and helpers; ForgeQE runs it until it passes and a reviewer approves it.",
|
|
155
|
+
tools: ["write_test", "run_test"],
|
|
156
|
+
examples: ["Write a Playwright test for that run in ../app"],
|
|
157
|
+
requires: [{ kind: "coding_agent" }, { kind: "input", what: "a repository to write the test in (default: the working directory)" }],
|
|
158
|
+
docs: [{ doc: "HOW_TO_USE.md", heading: "6. Tests" }],
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
id: "heal-tests",
|
|
162
|
+
area: "Automated tests",
|
|
163
|
+
title: "Heal failing tests",
|
|
164
|
+
summary: "Runs a repository's tests and updates the ones the application has outgrown against the live app, or reports a real application bug instead.",
|
|
165
|
+
tools: ["heal_tests"],
|
|
166
|
+
examples: ["Heal the failing tests in ../app"],
|
|
167
|
+
requires: [{ kind: "coding_agent" }, { kind: "input", what: "a repository with tests" }],
|
|
168
|
+
docs: [{ doc: "HOW_TO_USE.md", heading: "Fixing broken tests" }],
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
id: "find-run-tests",
|
|
172
|
+
area: "Automated tests",
|
|
173
|
+
title: "Find and run existing tests",
|
|
174
|
+
summary: "Finds the automated tests that match a feature, tags, ids, a folder, or a sprint, confirms the selection, and runs it once the user agrees, with a verdict per test.",
|
|
175
|
+
tools: ["find_tests", "run_tests", "list_sprints"],
|
|
176
|
+
examples: ["Run the checkout tests", "Run the tests added in sprint 12"],
|
|
177
|
+
requires: [
|
|
178
|
+
{ kind: "coding_agent" },
|
|
179
|
+
{ kind: "input", what: "a repository with tests" },
|
|
180
|
+
{ kind: "system", anyOf: ["azure_devops", "jira"], optional: true, why: "to pick tests by sprint" },
|
|
181
|
+
],
|
|
182
|
+
docs: [{ doc: "HOW_TO_USE.md", heading: "Finding and running existing tests" }],
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
id: "flaky-tests",
|
|
186
|
+
area: "Automated tests",
|
|
187
|
+
title: "Flaky test detection and quarantine",
|
|
188
|
+
summary: "Finds tests that flip between passing and failing in the repository's run history, with their likely cause, and quarantines them once the user agrees.",
|
|
189
|
+
tools: ["flaky_tests", "quarantine_tests"],
|
|
190
|
+
examples: ["Which tests are flaky?", "Quarantine the flaky login test"],
|
|
191
|
+
requires: [{ kind: "input", what: "a repository whose tests ForgeQE has run" }],
|
|
192
|
+
docs: [{ doc: "HOW_TO_USE.md", heading: "Flaky tests" }],
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
id: "pr-review",
|
|
196
|
+
area: "Pull requests",
|
|
197
|
+
title: "Review pull requests",
|
|
198
|
+
summary: "Reviews an Azure DevOps or GitHub pull request from the local clone for bugs, security, requirements, and tests, and posts the review once the user agrees.",
|
|
199
|
+
tools: ["review_pull_request", "post_pr_review"],
|
|
200
|
+
examples: ["Review PR 42 and focus on error handling"],
|
|
201
|
+
requires: [
|
|
202
|
+
{ kind: "coding_agent" },
|
|
203
|
+
{ kind: "input", what: "a local clone of the repository" },
|
|
204
|
+
{ kind: "either", of: [{ kind: "system", anyOf: ["azure_devops"], why: "for Azure DevOps pull requests" }, { kind: "command", command: "gh", why: "for GitHub pull requests" }] },
|
|
205
|
+
],
|
|
206
|
+
docs: [{ doc: "HOW_TO_USE.md", heading: "Reviewing pull requests" }],
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
id: "pr-impact",
|
|
210
|
+
area: "Pull requests",
|
|
211
|
+
title: "Pull request impact and test selection",
|
|
212
|
+
summary:
|
|
213
|
+
"Works out which product areas, pages, requirements, and tests a pull request touches, picks the tests it can break, runs them, and posts the impact or results on the pull request once the user agrees.",
|
|
214
|
+
tools: ["analyze_pr_impact", "select_pr_tests", "post_pr_test_results", "comment_on_pr"],
|
|
215
|
+
examples: ["What does PR 42 impact?", "Run the tests PR 42 can break"],
|
|
216
|
+
requires: [
|
|
217
|
+
{ kind: "input", what: "a local clone of the repository" },
|
|
218
|
+
{ kind: "either", of: [{ kind: "system", anyOf: ["azure_devops"], why: "for Azure DevOps pull requests and their linked work items" }, { kind: "command", command: "gh", why: "for GitHub pull requests" }] },
|
|
219
|
+
],
|
|
220
|
+
docs: [
|
|
221
|
+
{ doc: "HOW_TO_USE.md", heading: "What a pull request impacts in the product" },
|
|
222
|
+
{ doc: "HOW_TO_USE.md", heading: "Tests for a pull request" },
|
|
223
|
+
],
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
id: "api-testing",
|
|
227
|
+
area: "API",
|
|
228
|
+
title: "API and contract testing",
|
|
229
|
+
summary:
|
|
230
|
+
"Builds API contracts (status, schema, key fields) from the calls a browser run saw or from an API description (OpenAPI, Swagger, WSDL, GraphQL, .proto, gRPC reflection), and end-to-end API scenarios from runs, descriptions, test cases, or plain English. Runs them straight against the API without a browser, or writes them as tests in the repository. REST, GraphQL, SOAP, and gRPC.",
|
|
231
|
+
tools: ["api_contracts", "run_api_contracts", "api_scenarios", "run_api_scenarios", "write_api_tests"],
|
|
232
|
+
examples: ["Turn the last run's API calls into tests", "Test the API in openapi.yaml", "Write API scenario tests for the order lifecycle"],
|
|
233
|
+
requires: [{ kind: "coding_agent", optional: true }],
|
|
234
|
+
limits: ["Functional API testing, not load or stress testing."],
|
|
235
|
+
docs: [
|
|
236
|
+
{ doc: "HOW_TO_USE.md", heading: "API and contract tests" },
|
|
237
|
+
{ doc: "HOW_TO_USE.md", heading: "End-to-end API scenarios" },
|
|
238
|
+
],
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
id: "accessibility-performance",
|
|
242
|
+
area: "Quality",
|
|
243
|
+
title: "Accessibility and page performance",
|
|
244
|
+
summary: "Checks every page a run visits against WCAG rules (axe-core) and measures its Core Web Vitals, against budgets, with trends over time per page.",
|
|
245
|
+
tools: ["quality_report", "quality_trends"],
|
|
246
|
+
examples: ["Is the checkout page accessible?", "Did page performance get worse this week?"],
|
|
247
|
+
requires: [{ kind: "input", what: "a browser run (from browser_run or run_test_cases); new runs need a live model" }],
|
|
248
|
+
limits: ["Per-page Core Web Vitals, not load testing under many users."],
|
|
249
|
+
docs: [{ doc: "HOW_TO_USE.md", heading: "Accessibility and performance budgets" }],
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
id: "visual-comparison",
|
|
253
|
+
area: "Quality",
|
|
254
|
+
title: "Visual comparison with baselines",
|
|
255
|
+
summary: "Compares each page a run captures with its approved baseline and shows what changed; intended changes become the new baseline once the user agrees.",
|
|
256
|
+
tools: ["visual_compare", "visual_approve"],
|
|
257
|
+
examples: ["What changed visually since the last run?", "Approve the new header"],
|
|
258
|
+
requires: [{ kind: "input", what: "a browser run (from browser_run or run_test_cases); new runs need a live model" }],
|
|
259
|
+
docs: [{ doc: "HOW_TO_USE.md", heading: "Visual comparison" }],
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
id: "release-readiness",
|
|
263
|
+
area: "Release",
|
|
264
|
+
title: "Requirement coverage and release readiness",
|
|
265
|
+
summary: "Gives requirement coverage and a GO / GO WITH RISKS / NO-GO verdict for a sprint, area, epic, or stories, from test cases, results, bugs, and flaky tests; can publish it to the wiki.",
|
|
266
|
+
tools: ["release_readiness"],
|
|
267
|
+
examples: ["Are we ready to release?", "Coverage of sprint 12"],
|
|
268
|
+
requires: [{ kind: "system", anyOf: ["azure_devops", "jira"] }],
|
|
269
|
+
docs: [{ doc: "HOW_TO_USE.md", heading: "Requirement coverage and release readiness" }],
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
id: "monitoring",
|
|
273
|
+
area: "Operations",
|
|
274
|
+
title: "Scheduled monitoring with alerts",
|
|
275
|
+
summary: "Runs checks on a schedule and alerts Teams or email when one keeps failing, and again when it recovers.",
|
|
276
|
+
tools: ["monitor"],
|
|
277
|
+
examples: ["Check the login flow every 15 minutes and alert Teams"],
|
|
278
|
+
requires: [{ kind: "live_model" }, { kind: "monitor_alerts", optional: true }],
|
|
279
|
+
docs: [{ doc: "HOW_TO_USE.md", heading: "9. Monitoring" }],
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
id: "notifications",
|
|
283
|
+
area: "Operations",
|
|
284
|
+
title: "Teams and email notifications",
|
|
285
|
+
summary:
|
|
286
|
+
"Tells the team in Microsoft Teams channels or by email when a suite fails or passes, when a release readiness verdict is in, and when the domain evaluation falls below its gate, with the failing cases, their bugs, and links to the report and the CI build. Each channel picks its events; the agent also posts a summary to a channel when asked.",
|
|
287
|
+
tools: ["notify"],
|
|
288
|
+
examples: ["Post the results to the QA channel", "Tell the release channel the verdict is NO-GO"],
|
|
289
|
+
requires: [{ kind: "notify_channels" }],
|
|
290
|
+
limits: ["Automatic notifications come from runs nobody is watching (forgeqe suite, forgeqe run, pipelines, the MCP server), not from the interactive shell, where the agent posts only when asked."],
|
|
291
|
+
docs: [{ doc: "HOW_TO_USE.md", heading: "17. Notifications" }],
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
id: "pipelines",
|
|
295
|
+
area: "Operations",
|
|
296
|
+
title: "CI pipelines",
|
|
297
|
+
summary: "Writes a GitHub Actions or Azure Pipelines workflow that runs ForgeQE, stores the secrets it needs, pushes a branch, and makes the pipeline green.",
|
|
298
|
+
tools: ["push_branch", "set_ci_secret"],
|
|
299
|
+
examples: ["Add a GitHub Actions workflow that runs tests/cases.yaml on every PR, and make it green"],
|
|
300
|
+
requires: [
|
|
301
|
+
{ kind: "command", command: "git" },
|
|
302
|
+
{ kind: "either", of: [{ kind: "command", command: "gh", why: "for GitHub Actions" }, { kind: "command", command: "az", why: "for Azure Pipelines" }] },
|
|
303
|
+
],
|
|
304
|
+
docs: [{ doc: "HOW_TO_USE.md", heading: "8. Pipelines" }],
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
id: "systems",
|
|
308
|
+
area: "Systems",
|
|
309
|
+
title: "Work with Azure DevOps, Jira, and Confluence",
|
|
310
|
+
summary: "Reads anything in the configured systems (work items, issues, queries, boards, pages), makes changes the user asks for, and builds Azure DevOps dashboards and wiki pages.",
|
|
311
|
+
tools: ["query_api", "read_confluence", "create_ado_dashboard", "write_ado_wiki_page"],
|
|
312
|
+
examples: ["Show the open bugs in this sprint", "Read the checkout requirements from Confluence", "Make a dashboard of this sprint's test results"],
|
|
313
|
+
requires: [{ kind: "system", anyOf: ["azure_devops", "jira", "xray", "confluence"] }],
|
|
314
|
+
docs: [{ doc: "README.md", heading: "Azure DevOps, Jira, and other systems" }],
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
id: "setup-help",
|
|
318
|
+
area: "Setup",
|
|
319
|
+
title: "Setup, skills, and documentation",
|
|
320
|
+
summary: "Shows the model profiles and MCP servers, installs or repairs the browser engine, creates a forgeqe.yaml, manages skills, and answers questions about ForgeQE from its documentation and this list.",
|
|
321
|
+
tools: ["forgeqe_setup", "capabilities"],
|
|
322
|
+
examples: ["What can you do?", "Which model am I using?", "How do I set up email alerts?"],
|
|
323
|
+
requires: [],
|
|
324
|
+
docs: [
|
|
325
|
+
{ doc: "HOW_TO_USE.md", heading: "2. Scaffold config" },
|
|
326
|
+
{ doc: "HOW_TO_USE.md", heading: "11. Skills" },
|
|
327
|
+
],
|
|
328
|
+
},
|
|
329
|
+
]
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* What ForgeQE does not do, so a "can you…?" gets a plain no with the nearest thing it does, instead of a guess.
|
|
333
|
+
* Only add what is certain.
|
|
334
|
+
*/
|
|
335
|
+
export const OUT_OF_SCOPE: { what: string; instead?: string }[] = [
|
|
336
|
+
{ what: "Native mobile apps (iOS, Android) and desktop applications", instead: "web applications in Chrome or Chromium" },
|
|
337
|
+
{ what: "Load, stress, or soak testing with many simultaneous users", instead: "per-page Core Web Vitals with budgets" },
|
|
338
|
+
{ what: "Penetration testing or dynamic security scanning", instead: "security findings in pull request reviews" },
|
|
339
|
+
]
|
|
340
|
+
|
|
341
|
+
export function capability(id: string): Capability | undefined {
|
|
342
|
+
return CAPABILITIES.find((c) => c.id === id)
|
|
343
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import type { AppConfig } from "../config.ts"
|
|
2
|
+
import { anchor, resolveDoc } from "./docs.ts"
|
|
3
|
+
import { CAPABILITIES, OUT_OF_SCOPE, type Area, type Capability } from "./registry.ts"
|
|
4
|
+
import { allStatuses, capabilityStatus, MACHINE, type CapabilityStatus, type Probes } from "./status.ts"
|
|
5
|
+
|
|
6
|
+
const STATE: Record<CapabilityStatus["state"], string> = { ready: "ready", partial: "ready, some parts need setup", needs_setup: "needs setup" }
|
|
7
|
+
|
|
8
|
+
function byArea(): [Area, Capability[]][] {
|
|
9
|
+
const areas = new Map<Area, Capability[]>()
|
|
10
|
+
for (const c of CAPABILITIES) areas.set(c.area, [...(areas.get(c.area) || []), c])
|
|
11
|
+
return [...areas]
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** For the agent's session notes: every capability on one line, with its state in this setup. */
|
|
15
|
+
export function capabilityIndex(cfg: AppConfig, probes: Probes = MACHINE): string {
|
|
16
|
+
const status = allStatuses(cfg, probes)
|
|
17
|
+
return [
|
|
18
|
+
"What ForgeQE can do (the capabilities tool has each one's details, setup, and docs; anything not listed here or under its out-of-scope list, ForgeQE does not do):",
|
|
19
|
+
...CAPABILITIES.map((c) => {
|
|
20
|
+
const s = status.get(c.id)!
|
|
21
|
+
const missing = s.gaps.filter((g) => !g.optional).map((g) => g.missing)
|
|
22
|
+
return `- ${c.id}: ${c.title} [${STATE[s.state]}${missing.length ? `: needs ${missing.join("; ")}` : ""}]`
|
|
23
|
+
}),
|
|
24
|
+
`Out of scope: ${OUT_OF_SCOPE.map((o) => o.what).join("; ")}.`,
|
|
25
|
+
].join("\n")
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** The capabilities tool's list: areas with each capability's summary and state. */
|
|
29
|
+
export function capabilityList(cfg: AppConfig, probes: Probes = MACHINE) {
|
|
30
|
+
const status = allStatuses(cfg, probes)
|
|
31
|
+
return {
|
|
32
|
+
areas: byArea().map(([area, caps]) => ({
|
|
33
|
+
area,
|
|
34
|
+
capabilities: caps.map((c) => ({ id: c.id, title: c.title, summary: c.summary, state: status.get(c.id)!.state })),
|
|
35
|
+
})),
|
|
36
|
+
out_of_scope: OUT_OF_SCOPE,
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** The capabilities tool's details for one capability: what it does, how to ask, what it needs here, and its docs. */
|
|
41
|
+
export function capabilityDetail(c: Capability, cfg: AppConfig, probes: Probes = MACHINE) {
|
|
42
|
+
const s = capabilityStatus(c, cfg, probes)
|
|
43
|
+
return {
|
|
44
|
+
id: c.id,
|
|
45
|
+
title: c.title,
|
|
46
|
+
summary: c.summary,
|
|
47
|
+
state: s.state,
|
|
48
|
+
tools: c.tools,
|
|
49
|
+
examples: c.examples,
|
|
50
|
+
...(s.gaps.length ? { setup_needed: s.gaps.map((g) => ({ missing: g.missing, optional: g.optional, fix: g.fix })) } : {}),
|
|
51
|
+
...(s.inputs.length ? { needs_from_request: s.inputs } : {}),
|
|
52
|
+
...(c.limits?.length ? { limits: c.limits } : {}),
|
|
53
|
+
docs: c.docs.map((d) => {
|
|
54
|
+
const section = resolveDoc(d)
|
|
55
|
+
return { id: section?.id || `${d.doc}#${anchor(d.heading)}`, heading: d.heading, text: section?.text || "" }
|
|
56
|
+
}),
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** /features and `forgeqe features`: what ForgeQE can do here, by area. */
|
|
61
|
+
export function featuresText(cfg: AppConfig, probes: Probes = MACHINE): string {
|
|
62
|
+
const status = allStatuses(cfg, probes)
|
|
63
|
+
const mark = { ready: "✓", partial: "◐", needs_setup: "○" } as const
|
|
64
|
+
const lines: string[] = ["What ForgeQE can do (✓ ready ◐ works, some parts need setup ○ needs setup)", ""]
|
|
65
|
+
for (const [area, caps] of byArea()) {
|
|
66
|
+
lines.push(area)
|
|
67
|
+
for (const c of caps) {
|
|
68
|
+
const s = status.get(c.id)!
|
|
69
|
+
lines.push(` ${mark[s.state]} ${c.title}`)
|
|
70
|
+
lines.push(` e.g. ${c.examples[0]}`)
|
|
71
|
+
for (const g of s.gaps.filter((g) => !g.optional)) lines.push(` needs ${g.missing}: ${g.fix}`)
|
|
72
|
+
}
|
|
73
|
+
lines.push("")
|
|
74
|
+
}
|
|
75
|
+
lines.push(`Not supported: ${OUT_OF_SCOPE.map((o) => o.what).join("; ")}.`)
|
|
76
|
+
lines.push("Ask in the shell for details about any of these, e.g. \"how do I set up API testing?\"")
|
|
77
|
+
return lines.join("\n")
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export const AGENT_MAP_START = "<!-- capabilities:start (generated from src/capabilities/registry.ts by bun scripts/gen-capabilities.ts; do not edit) -->"
|
|
81
|
+
export const AGENT_MAP_END = "<!-- capabilities:end -->"
|
|
82
|
+
|
|
83
|
+
/** The Capabilities part of HOW_TO_USE.md's Agent map: every capability with its tools and a link to its docs. */
|
|
84
|
+
export function agentMapMarkdown(): string {
|
|
85
|
+
const link = (c: Capability) => {
|
|
86
|
+
const d = c.docs[0]!
|
|
87
|
+
return d.doc === "HOW_TO_USE.md" ? `[docs](#${anchor(d.heading)})` : `[docs](${d.doc}#${anchor(d.heading)})`
|
|
88
|
+
}
|
|
89
|
+
return [
|
|
90
|
+
AGENT_MAP_START,
|
|
91
|
+
"| Area | Capability | Tools | |",
|
|
92
|
+
"|------|------------|-------|-|",
|
|
93
|
+
...CAPABILITIES.map((c) => `| ${c.area} | **${c.title}**: ${c.summary} | ${c.tools.map((t) => `\`${t}\``).join(", ")} | ${link(c)} |`),
|
|
94
|
+
"",
|
|
95
|
+
`Not supported: ${OUT_OF_SCOPE.map((o) => `${o.what}${o.instead ? ` (instead: ${o.instead})` : ""}`).join("; ")}.`,
|
|
96
|
+
AGENT_MAP_END,
|
|
97
|
+
].join("\n")
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** HOW_TO_USE.md with its generated block (from its start marker, whatever follows the marker's name, to its end marker) replaced by the current one. */
|
|
101
|
+
export function withAgentMap(markdown: string): string {
|
|
102
|
+
const start = markdown.indexOf("<!-- capabilities:start")
|
|
103
|
+
const end = markdown.indexOf(AGENT_MAP_END)
|
|
104
|
+
if (start === -1 || end < start) throw new Error("HOW_TO_USE.md has no capabilities block (its start and end markers).")
|
|
105
|
+
return markdown.slice(0, start) + agentMapMarkdown() + markdown.slice(end + AGENT_MAP_END.length)
|
|
106
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import type { AppConfig } from "../config.ts"
|
|
2
|
+
import { findOpencode } from "../code/runtime.ts"
|
|
3
|
+
import { noAgentModel } from "../llm.ts"
|
|
4
|
+
import { CAPABILITIES, type Capability, type Requirement, type System } from "./registry.ts"
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Whether each capability works in this setup, and what is missing when it does not: every requirement kind is
|
|
8
|
+
* checked against the loaded config and this machine, never guessed from text.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export type Probes = {
|
|
12
|
+
/** A command on PATH. */
|
|
13
|
+
which(command: string): boolean
|
|
14
|
+
/** The agent runtime Scribe runs on is installed. */
|
|
15
|
+
runtime(cfg: AppConfig): boolean
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const MACHINE: Probes = {
|
|
19
|
+
which: (command) => Boolean(Bun.which(command)),
|
|
20
|
+
runtime: (cfg) => Boolean(findOpencode(cfg.code.opencode)),
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const SYSTEM_LABEL: Record<System, string> = {
|
|
24
|
+
azure_devops: "Azure DevOps",
|
|
25
|
+
jira: "Jira",
|
|
26
|
+
xray: "Xray",
|
|
27
|
+
testrail: "TestRail",
|
|
28
|
+
zephyr: "Zephyr Scale",
|
|
29
|
+
qtest: "qTest",
|
|
30
|
+
confluence: "Confluence",
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** The systems forgeqe.yaml (or a sign-in) sets up. */
|
|
34
|
+
export function configuredSystems(cfg: AppConfig): Set<System> {
|
|
35
|
+
const s = cfg.sources
|
|
36
|
+
const on: [System, unknown][] = [
|
|
37
|
+
["azure_devops", s.ado],
|
|
38
|
+
["jira", s.jira],
|
|
39
|
+
["xray", (s.xray.url && process.env[s.xray.clientIdEnv]) || (s.jira && !s.jira.cloud)],
|
|
40
|
+
["testrail", s.testrail],
|
|
41
|
+
["zephyr", s.zephyr],
|
|
42
|
+
["qtest", s.qtest],
|
|
43
|
+
["confluence", s.confluence],
|
|
44
|
+
]
|
|
45
|
+
return new Set(on.filter(([, v]) => Boolean(v)).map(([k]) => k))
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export type Gap = { requirement: Requirement; optional: boolean; missing: string; fix: string }
|
|
49
|
+
|
|
50
|
+
export type CapabilityStatus = {
|
|
51
|
+
id: string
|
|
52
|
+
/** ready: everything is set up. partial: it works, but something optional is missing. needs_setup: it does not work yet. */
|
|
53
|
+
state: "ready" | "partial" | "needs_setup"
|
|
54
|
+
gaps: Gap[]
|
|
55
|
+
/** What only the request can supply (a repository, a pull request). */
|
|
56
|
+
inputs: string[]
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function gap(r: Requirement, cfg: AppConfig, probes: Probes): Omit<Gap, "requirement" | "optional"> | null {
|
|
60
|
+
switch (r.kind) {
|
|
61
|
+
case "live_model":
|
|
62
|
+
return cfg.llm.provider === "none" || cfg.llm.provider === "mock"
|
|
63
|
+
? { missing: "a live model profile", fix: "add a profile with your provider to llms.json (forgeqe init writes one), then pick it with llm.active or Tab" }
|
|
64
|
+
: null
|
|
65
|
+
case "coding_agent": {
|
|
66
|
+
const why = noAgentModel(cfg.llm, cfg.code.model, "Scribe")
|
|
67
|
+
if (why) return { missing: "a live model for the coding agent", fix: "pick a live llms.json profile, or set code.model" }
|
|
68
|
+
return probes.runtime(cfg) ? null : { missing: "the agent runtime the coding agent runs on", fix: "run forgeqe setup, or set code.runtime to its binary" }
|
|
69
|
+
}
|
|
70
|
+
case "system": {
|
|
71
|
+
const have = configuredSystems(cfg)
|
|
72
|
+
if (r.anyOf.some((s) => have.has(s))) return null
|
|
73
|
+
const names = r.anyOf.map((s) => SYSTEM_LABEL[s])
|
|
74
|
+
return {
|
|
75
|
+
missing: `${names.length > 1 ? `one of ${names.join(", ")}` : names[0]}${r.why ? ` (${r.why})` : ""}`,
|
|
76
|
+
fix: `set it up under sources: in forgeqe.yaml, or sign in with /auth ${r.anyOf[0]}`,
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
case "bugs":
|
|
80
|
+
return cfg.sources.bugs ? null : { missing: "bug filing for failures", fix: "set sources.bugs in forgeqe.yaml (Jira or Azure DevOps)" }
|
|
81
|
+
case "monitor_alerts":
|
|
82
|
+
return cfg.monitor.alerts.teams || cfg.monitor.alerts.email ? null : { missing: "alert channels", fix: "set monitor.alerts.teams or monitor.alerts.email in forgeqe.yaml" }
|
|
83
|
+
case "notify_channels":
|
|
84
|
+
return cfg.notify?.channels.length ? null : { missing: "a notification channel", fix: "add notify.channels to forgeqe.yaml: a Teams Workflows webhook (webhook_env) or email for each" }
|
|
85
|
+
case "domain":
|
|
86
|
+
return cfg.domain ? null : { missing: "the product's industry and regulations", fix: "set domain: in forgeqe.yaml, and list your applicable controls in .forgeqe/knowledge/compliance.md" }
|
|
87
|
+
case "command":
|
|
88
|
+
return probes.which(r.command) ? null : { missing: `${r.command} on PATH${r.why ? ` (${r.why})` : ""}`, fix: `install ${r.command}` }
|
|
89
|
+
case "input":
|
|
90
|
+
return null
|
|
91
|
+
case "either": {
|
|
92
|
+
const gaps = r.of.map((alt) => gap(alt, cfg, probes))
|
|
93
|
+
if (gaps.some((g) => !g)) return null
|
|
94
|
+
return { missing: gaps.map((g) => g!.missing).join(", or "), fix: gaps.map((g) => g!.fix).join("; or ") }
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function capabilityStatus(c: Capability, cfg: AppConfig, probes: Probes = MACHINE): CapabilityStatus {
|
|
100
|
+
const gaps: Gap[] = []
|
|
101
|
+
for (const r of c.requires) {
|
|
102
|
+
const g = gap(r, cfg, probes)
|
|
103
|
+
if (g) gaps.push({ requirement: r, optional: Boolean("optional" in r && r.optional), ...g })
|
|
104
|
+
}
|
|
105
|
+
const state = gaps.some((g) => !g.optional) ? "needs_setup" : gaps.length ? "partial" : "ready"
|
|
106
|
+
return { id: c.id, state, gaps, inputs: c.requires.flatMap((r) => (r.kind === "input" ? [r.what] : [])) }
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function allStatuses(cfg: AppConfig, probes: Probes = MACHINE): Map<string, CapabilityStatus> {
|
|
110
|
+
return new Map(CAPABILITIES.map((c) => [c.id, capabilityStatus(c, cfg, probes)]))
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** A requirement in words, for documentation: what it is and where it is set up (not whether this machine has it). */
|
|
114
|
+
export function describeRequirement(r: Requirement): string {
|
|
115
|
+
switch (r.kind) {
|
|
116
|
+
case "live_model":
|
|
117
|
+
return "A live model profile in llms.json (an API key or Microsoft Entra ID sign-in)"
|
|
118
|
+
case "coding_agent":
|
|
119
|
+
return "The coding agent, Scribe: a live model profile (or code.model) and the agent runtime, which forgeqe setup installs"
|
|
120
|
+
case "system":
|
|
121
|
+
return `${r.anyOf.map((s) => SYSTEM_LABEL[s]).join(" or ")}${r.why ? `, ${r.why}` : ""}: set up under sources: in forgeqe.yaml, or sign in with /auth`
|
|
122
|
+
case "bugs":
|
|
123
|
+
return "Bug filing: sources.bugs in forgeqe.yaml (Jira or Azure DevOps)"
|
|
124
|
+
case "monitor_alerts":
|
|
125
|
+
return "Alert channels: monitor.alerts.teams or monitor.alerts.email in forgeqe.yaml"
|
|
126
|
+
case "notify_channels":
|
|
127
|
+
return "A notification channel: notify.channels in forgeqe.yaml, each a Teams Workflows webhook (in an environment variable) or email"
|
|
128
|
+
case "domain":
|
|
129
|
+
return "The product's industry and regulations: domain: in forgeqe.yaml, with your applicable controls in .forgeqe/knowledge/compliance.md"
|
|
130
|
+
case "command":
|
|
131
|
+
return `${r.command} on PATH${r.why ? `, ${r.why}` : ""}`
|
|
132
|
+
case "input":
|
|
133
|
+
return `From your request: ${r.what}`
|
|
134
|
+
case "either":
|
|
135
|
+
return r.of.map(describeRequirement).join("; or ")
|
|
136
|
+
}
|
|
137
|
+
}
|
package/src/card.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** A tool's result as a card: the shell renders it as a panel, plain output prints it as text. */
|
|
2
|
+
export type CardStatus = "ok" | "fail" | "stopped" | "info"
|
|
3
|
+
|
|
4
|
+
export type Card = { status: CardStatus; title: string; stats?: string; body: string[] }
|
|
5
|
+
|
|
6
|
+
export const CARD_MARK: Record<CardStatus, string> = { ok: "✓", fail: "✗", stopped: "■", info: "▸" }
|
|
7
|
+
|
|
8
|
+
export function cardText(c: Card): string {
|
|
9
|
+
const head = `${CARD_MARK[c.status]} ${c.title}${c.stats ? ` · ${c.stats}` : ""}`
|
|
10
|
+
return c.body.length ? [head, "", ...c.body].join("\n") : head
|
|
11
|
+
}
|