@thebackstoryis/engineering-with-ai 0.2.9
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/Docs/README.md +50 -0
- package/Docs/adoption/consultancy-and-multi-project-rollout.md +135 -0
- package/Docs/adoption/non-technical-team-guide.md +126 -0
- package/Docs/archaeology-technology-and-hosting-discovery.md +212 -0
- package/Docs/blast-radius-and-impact-routing-guide.md +325 -0
- package/Docs/blueprints/internal-blueprint-catalogue.md +146 -0
- package/Docs/blueprints/maintaining-organisation-blueprints.md +154 -0
- package/Docs/blueprints/validation-and-troubleshooting.md +168 -0
- package/Docs/cli-reference.md +113 -0
- package/Docs/completed-phase-evidence-amendments.md +74 -0
- package/Docs/consultancy-network-rollout-control-plane-guide.md +202 -0
- package/Docs/context-aware-delivery-companion-guide.md +198 -0
- package/Docs/context-aware-delivery-companion-user-guide.md +184 -0
- package/Docs/context-management-and-token-efficiency.md +113 -0
- package/Docs/design-systems/design-system-implementation-guide.md +85 -0
- package/Docs/design-systems/design-system-pack-authoring-guide.md +95 -0
- package/Docs/design-systems/design-system-review-guide.md +51 -0
- package/Docs/design-systems/design-system-user-guide.md +96 -0
- package/Docs/design-systems/product-owner-guide.md +49 -0
- package/Docs/designing-organisation-blueprint-packs.md +384 -0
- package/Docs/developer-delivery-guide.md +224 -0
- package/Docs/error-reporting-guide.md +110 -0
- package/Docs/error-reporting-provider-guide.md +49 -0
- package/Docs/examples/error-report-adapter.md +70 -0
- package/Docs/examples/meeting-review.md +76 -0
- package/Docs/examples/minimal-design-system.md +67 -0
- package/Docs/examples/prototype-review-inputs.md +175 -0
- package/Docs/examples/reproducible-archaeology-depth-example.md +144 -0
- package/Docs/examples/test-scenario-input.md +68 -0
- package/Docs/examples/worked-examples.md +147 -0
- package/Docs/existing-project-onboarding-guide.md +214 -0
- package/Docs/explanation/core-concepts.md +26 -0
- package/Docs/explanation/delivery-workflow.md +48 -0
- package/Docs/governance/governance-team-guide.md +139 -0
- package/Docs/governed-starter-project-materialisation-guide.md +284 -0
- package/Docs/guide-catalogue.md +117 -0
- package/Docs/guided-discovery-facilitator-guide.md +172 -0
- package/Docs/guided-intent-workspace-guide.md +109 -0
- package/Docs/guided-phase-evidence-drafting-guide.md +119 -0
- package/Docs/human-approval-and-assurance-guide.md +146 -0
- package/Docs/knowledge-proposals-implementer-guide.md +106 -0
- package/Docs/knowledge-proposals-user-guide.md +247 -0
- package/Docs/maintainers/context-benchmarks.md +29 -0
- package/Docs/maintainers/contributing.md +58 -0
- package/Docs/maintainers/evidence-depth-acceptance.md +72 -0
- package/Docs/maintainers/verification-walkthroughs.md +104 -0
- package/Docs/meeting-evidence-implementer-guide.md +132 -0
- package/Docs/meeting-evidence-user-guide.md +200 -0
- package/Docs/operations/dashboard-and-delivery-state.md +153 -0
- package/Docs/operations/dashboard-configuration.md +87 -0
- package/Docs/operations/installation-updating-and-entitlements.md +135 -0
- package/Docs/operations/premium-personas-setup.md +76 -0
- package/Docs/operations/troubleshooting-and-recovery.md +205 -0
- package/Docs/organisation-rollout-guide.md +142 -0
- package/Docs/persona-entitlement-provider-guide.md +199 -0
- package/Docs/persona-guided-prototype-iteration.md +129 -0
- package/Docs/personas/organisation-specific-personas.md +103 -0
- package/Docs/personas/persona-authoring-cookbook.md +176 -0
- package/Docs/personas/persona-engagement-ui.md +133 -0
- package/Docs/personas/persona-governance.md +118 -0
- package/Docs/platform-export-analysis-guide.md +336 -0
- package/Docs/policies/governance-owner-guide.md +36 -0
- package/Docs/policies/implementation-guide.md +42 -0
- package/Docs/policies/organisation-policy-design-gates.md +58 -0
- package/Docs/policies/policy-pack-authoring-guide.md +108 -0
- package/Docs/policies/product-owner-guide.md +43 -0
- package/Docs/policies/technical-owner-guide.md +37 -0
- package/Docs/product-owner-guide.md +327 -0
- package/Docs/project-portfolio-orchestration-guide.md +199 -0
- package/Docs/quality/manual-qa-and-acceptance.md +162 -0
- package/Docs/quality/persona-driven-test-scenarios.md +172 -0
- package/Docs/quality/reproducible-archaeology-depth-review-checklist.md +89 -0
- package/Docs/reference/capabilities-and-project-layout.md +678 -0
- package/Docs/reference/cli-and-configuration.md +398 -0
- package/Docs/reference/contributions-api.md +23 -0
- package/Docs/reference/security-adapter-authoring.md +81 -0
- package/Docs/reference/starter-adapter-authoring.md +74 -0
- package/Docs/repository-source-map-guide.md +381 -0
- package/Docs/reproducible-archaeology-and-discovery-depth.md +292 -0
- package/Docs/screen-prototype-creation-guide.md +324 -0
- package/Docs/security-validation-guide.md +353 -0
- package/Docs/solution-readiness-review-guide.md +123 -0
- package/Docs/standards/project-standards-authoring.md +157 -0
- package/Docs/team-hub-guide.md +162 -0
- package/Docs/team-hub-resource-registry-guide.md +167 -0
- package/Docs/tutorials/first-delivery.md +83 -0
- package/Docs/tutorials/first-session.md +62 -0
- package/Docs/using-lifecycle-hooks.md +381 -0
- package/Docs/working-with-personas.md +274 -0
- package/LICENSE +165 -0
- package/README.md +96 -0
- package/agents-src/claude/ewai-security-reviewer.md +15 -0
- package/bin/ewai +5 -0
- package/config/archaeology-record-families.yaml +59 -0
- package/config/delivery-artifacts.yaml +121 -0
- package/config/delivery-stages.yaml +77 -0
- package/config/design-system.schema.json +46 -0
- package/config/error-reporting.schema.json +79 -0
- package/config/evidence-depth.schema.json +53 -0
- package/config/intent.schema.json +90 -0
- package/config/knowledge-proposals-proposal.schema.json +34 -0
- package/config/lifecycle-event.schema.json +68 -0
- package/config/lifecycle-handler.schema.json +45 -0
- package/config/lifecycle-hook-ack.schema.json +19 -0
- package/config/meeting-evidence-candidate.schema.json +102 -0
- package/config/organisation-policy.schema.json +137 -0
- package/config/pack.schema.json +250 -0
- package/config/persona-pack.schema.json +21 -0
- package/config/persona.schema.json +17 -0
- package/config/policy-evaluation.schema.json +77 -0
- package/config/policy-facts.schema.json +140 -0
- package/config/portfolio.schema.json +68 -0
- package/config/project.schema.json +313 -0
- package/config/prototype-iteration.schema.json +128 -0
- package/config/rollout.schema.json +87 -0
- package/config/security-adapter.schema.json +31 -0
- package/config/security-scan-request.schema.json +64 -0
- package/config/security-scan-response.schema.json +52 -0
- package/config/security-validation-policy.schema.json +74 -0
- package/config/starter-source-acknowledgement.schema.json +13 -0
- package/config/starter-source-adapter.schema.json +38 -0
- package/config/starter-source-request.schema.json +61 -0
- package/package.json +77 -0
- package/packs/core/pack.yaml +7 -0
- package/packs/design-systems/default/experience-promise.md +9 -0
- package/packs/design-systems/default/intentional-review.md +10 -0
- package/packs/design-systems/default/interaction-and-entry.md +9 -0
- package/packs/design-systems/default/meaningful-content-and-states.md +9 -0
- package/packs/design-systems/default/pack.yaml +44 -0
- package/packs/design-systems/default/principles.md +10 -0
- package/packs/personas/core/pack.yaml +7 -0
- package/packs/personas/core/personas/archaeologist.md +37 -0
- package/packs/personas/core/personas/end-user.md +17 -0
- package/packs/personas/core/personas/maintainer.md +17 -0
- package/packs/personas/core/personas/operator.md +17 -0
- package/packs/personas/core/personas/specs-knowledge-curator.md +35 -0
- package/packs/technologies/laravel/pack.yaml +30 -0
- package/packs/technologies/laravel-nuxt/pack.yaml +30 -0
- package/packs/technologies/nuxt/pack.yaml +30 -0
- package/packs/technologies/power-platform/pack.yaml +31 -0
- package/packs/technologies/salesforce/pack.yaml +25 -0
- package/public/app.js +4896 -0
- package/public/apple-touch-icon.png +0 -0
- package/public/assets/backstory-icon.png +0 -0
- package/public/dashboard-navigation.js +98 -0
- package/public/favicon-16.png +0 -0
- package/public/favicon-32.png +0 -0
- package/public/favicon.ico +0 -0
- package/public/index.html +789 -0
- package/public/styles.css +2693 -0
- package/public/team-hub/app.js +202 -0
- package/public/team-hub/index.html +79 -0
- package/public/team-hub/styles.css +90 -0
- package/scripts/publication-check.mjs +140 -0
- package/scripts/setup.mjs +21 -0
- package/skills-src/ewai-archaeology/SKILL.md +334 -0
- package/skills-src/ewai-archaeology/agents/openai.yaml +4 -0
- package/skills-src/ewai-archaeology/references/archaeology-contract.md +201 -0
- package/skills-src/ewai-archaeology/references/lifecycle-reconstruction.md +177 -0
- package/skills-src/ewai-archaeology/references/maximum-detail-reconstruction.md +97 -0
- package/skills-src/ewai-archaeology/references/model-routing.md +26 -0
- package/skills-src/ewai-architecture/SKILL.md +108 -0
- package/skills-src/ewai-architecture/agents/openai.yaml +4 -0
- package/skills-src/ewai-architecture/references/architecture-contract.md +176 -0
- package/skills-src/ewai-context/SKILL.md +68 -0
- package/skills-src/ewai-context/agents/openai.yaml +4 -0
- package/skills-src/ewai-context-import/SKILL.md +118 -0
- package/skills-src/ewai-context-import/agents/openai.yaml +4 -0
- package/skills-src/ewai-context-import/references/context-import-contract.md +106 -0
- package/skills-src/ewai-dashboard-configuration/SKILL.md +20 -0
- package/skills-src/ewai-deliver/SKILL.md +122 -0
- package/skills-src/ewai-deliver/references/delivery-evidence.md +92 -0
- package/skills-src/ewai-deliver/references/phase-routing.md +31 -0
- package/skills-src/ewai-design-system-apply/SKILL.md +27 -0
- package/skills-src/ewai-design-system-apply/agents/openai.yaml +4 -0
- package/skills-src/ewai-design-system-apply/references/application-contract.md +36 -0
- package/skills-src/ewai-design-system-author/SKILL.md +28 -0
- package/skills-src/ewai-design-system-author/agents/openai.yaml +4 -0
- package/skills-src/ewai-design-system-author/references/authoring-contract.md +38 -0
- package/skills-src/ewai-design-system-review/SKILL.md +26 -0
- package/skills-src/ewai-design-system-review/agents/openai.yaml +4 -0
- package/skills-src/ewai-design-system-review/references/review-contract.md +40 -0
- package/skills-src/ewai-error-reporting/SKILL.md +46 -0
- package/skills-src/ewai-error-reporting/agents/openai.yaml +4 -0
- package/skills-src/ewai-error-reporting/references/provider-contract.md +74 -0
- package/skills-src/ewai-evidence-depth/SKILL.md +72 -0
- package/skills-src/ewai-evidence-depth/agents/openai.yaml +4 -0
- package/skills-src/ewai-evidence-depth/references/evidence-depth-contract.md +127 -0
- package/skills-src/ewai-intent/SKILL.md +68 -0
- package/skills-src/ewai-intent/agents/openai.yaml +4 -0
- package/skills-src/ewai-intent/references/intent-contract.md +42 -0
- package/skills-src/ewai-knowledge-proposals/SKILL.md +105 -0
- package/skills-src/ewai-knowledge-proposals/agents/openai.yaml +4 -0
- package/skills-src/ewai-knowledge-proposals/references/proposal-contract.md +59 -0
- package/skills-src/ewai-meeting-evidence/SKILL.md +106 -0
- package/skills-src/ewai-meeting-evidence/agents/openai.yaml +4 -0
- package/skills-src/ewai-meeting-evidence/references/candidate-contract.md +64 -0
- package/skills-src/ewai-organisation-policy/SKILL.md +62 -0
- package/skills-src/ewai-organisation-policy/agents/openai.yaml +4 -0
- package/skills-src/ewai-organisation-policy/references/policy-contract.md +94 -0
- package/skills-src/ewai-palace-housekeeping/SKILL.md +55 -0
- package/skills-src/ewai-palace-housekeeping/agents/openai.yaml +4 -0
- package/skills-src/ewai-persona-entitlement/SKILL.md +60 -0
- package/skills-src/ewai-persona-entitlement/agents/openai.yaml +4 -0
- package/skills-src/ewai-phase-evidence/SKILL.md +79 -0
- package/skills-src/ewai-phase-evidence/agents/openai.yaml +4 -0
- package/skills-src/ewai-pipeline/SKILL.md +130 -0
- package/skills-src/ewai-pipeline/agents/openai.yaml +4 -0
- package/skills-src/ewai-pipeline/references/cli.md +86 -0
- package/skills-src/ewai-pipeline/references/specs-contract.md +16 -0
- package/skills-src/ewai-portfolio/SKILL.md +70 -0
- package/skills-src/ewai-portfolio/agents/openai.yaml +4 -0
- package/skills-src/ewai-portfolio/references/portfolio-contract.md +67 -0
- package/skills-src/ewai-project-discovery/SKILL.md +95 -0
- package/skills-src/ewai-project-discovery/agents/openai.yaml +4 -0
- package/skills-src/ewai-project-discovery/references/discovery-contract.md +34 -0
- package/skills-src/ewai-prototype-iteration/SKILL.md +30 -0
- package/skills-src/ewai-prototype-iteration/agents/openai.yaml +4 -0
- package/skills-src/ewai-prototype-iteration/references/review-contract.md +49 -0
- package/skills-src/ewai-retro/SKILL.md +48 -0
- package/skills-src/ewai-retro/agents/openai.yaml +4 -0
- package/skills-src/ewai-retro/references/asset-routing.md +14 -0
- package/skills-src/ewai-rollout/SKILL.md +74 -0
- package/skills-src/ewai-rollout/agents/openai.yaml +4 -0
- package/skills-src/ewai-rollout/references/rollout-contract.md +74 -0
- package/skills-src/ewai-shape-intents/SKILL.md +84 -0
- package/skills-src/ewai-shape-intents/agents/openai.yaml +4 -0
- package/skills-src/ewai-shape-intents/references/intent-mapping-contract.md +109 -0
- package/skills-src/ewai-solution-readiness/SKILL.md +55 -0
- package/skills-src/ewai-solution-readiness/agents/openai.yaml +4 -0
- package/skills-src/ewai-standards-check/SKILL.md +93 -0
- package/skills-src/ewai-standards-check/agents/openai.yaml +4 -0
- package/skills-src/ewai-standards-check/references/report-contract.md +116 -0
- package/skills-src/ewai-test-scenarios/SKILL.md +94 -0
- package/skills-src/ewai-test-scenarios/agents/openai.yaml +4 -0
- package/skills-src/ewai-test-scenarios/references/scenario-contract.md +88 -0
- package/src/afk-worker.mjs +16 -0
- package/src/archaeology.mjs +1333 -0
- package/src/checkin.mjs +261 -0
- package/src/cli.mjs +2427 -0
- package/src/companion-guidance.mjs +257 -0
- package/src/companion-opening.mjs +62 -0
- package/src/companion.mjs +256 -0
- package/src/context.mjs +210 -0
- package/src/dashboard-preferences.mjs +80 -0
- package/src/delivery-artifacts.mjs +204 -0
- package/src/delivery-documents.mjs +248 -0
- package/src/delivery-gates.mjs +317 -0
- package/src/delivery.mjs +1433 -0
- package/src/design-system-application.mjs +291 -0
- package/src/design-system-authoring.mjs +101 -0
- package/src/design-systems.mjs +466 -0
- package/src/discovery.mjs +1314 -0
- package/src/error-reporting.mjs +323 -0
- package/src/evidence-depth.mjs +543 -0
- package/src/execution-state.mjs +243 -0
- package/src/install.mjs +166 -0
- package/src/intent-dependencies.mjs +117 -0
- package/src/intent-maps.mjs +402 -0
- package/src/intents.mjs +747 -0
- package/src/knowledge-proposals.mjs +717 -0
- package/src/launcher.mjs +51 -0
- package/src/meeting-evidence.mjs +703 -0
- package/src/network-rollout.mjs +386 -0
- package/src/organisation-blueprints.mjs +438 -0
- package/src/organisation-policies.mjs +448 -0
- package/src/packs.mjs +44 -0
- package/src/paths.mjs +62 -0
- package/src/persona-entitlements.mjs +438 -0
- package/src/persona-licence-config.mjs +98 -0
- package/src/persona-website-provider.mjs +134 -0
- package/src/persona-zip.mjs +87 -0
- package/src/personas.mjs +159 -0
- package/src/platform-metadata-analysis.mjs +314 -0
- package/src/policy-design-gates.mjs +623 -0
- package/src/policy-gate-integration.mjs +318 -0
- package/src/portfolio.mjs +509 -0
- package/src/power-platform-source-map.mjs +190 -0
- package/src/project.mjs +449 -0
- package/src/prototype-iterations.mjs +730 -0
- package/src/repository-source-map.mjs +603 -0
- package/src/runtime/afk-conductor.mjs +973 -0
- package/src/runtime/context-assembly.mjs +457 -0
- package/src/runtime/context-benchmarks.mjs +115 -0
- package/src/runtime/dashboard-actions.mjs +109 -0
- package/src/runtime/dashboard-handoffs.mjs +149 -0
- package/src/runtime/dashboard-server.mjs +1272 -0
- package/src/runtime/dashboard.mjs +197 -0
- package/src/runtime/database.mjs +789 -0
- package/src/runtime/error-reporting.mjs +581 -0
- package/src/runtime/evidence-depth-workspace.mjs +412 -0
- package/src/runtime/execution-leases.mjs +299 -0
- package/src/runtime/guided-discovery.mjs +350 -0
- package/src/runtime/guided-intents.mjs +517 -0
- package/src/runtime/impact-analysis.mjs +535 -0
- package/src/runtime/intents.mjs +222 -0
- package/src/runtime/knowledge.mjs +86 -0
- package/src/runtime/lifecycle-hooks.mjs +1239 -0
- package/src/runtime/mcp-config.mjs +110 -0
- package/src/runtime/mcp-server.mjs +1885 -0
- package/src/runtime/palace.mjs +362 -0
- package/src/runtime/paths.mjs +58 -0
- package/src/runtime/persona-engagement.mjs +255 -0
- package/src/runtime/phase-contributions.mjs +594 -0
- package/src/runtime/policy-workspace.mjs +170 -0
- package/src/runtime/prototype-iterations.mjs +235 -0
- package/src/runtime/provider-adapters.mjs +163 -0
- package/src/runtime/repository-index.mjs +838 -0
- package/src/runtime/runs.mjs +185 -0
- package/src/runtime/security-validation.mjs +1230 -0
- package/src/runtime/starter-materialisation.mjs +1155 -0
- package/src/runtime/team-hub-client.mjs +479 -0
- package/src/runtime/team-hub-database.mjs +288 -0
- package/src/runtime/team-hub-server.mjs +191 -0
- package/src/runtime/team-hub.mjs +110 -0
- package/src/runtime/tree-sitter-index.mjs +390 -0
- package/src/runtime/version.mjs +1 -0
- package/src/runtime/work.mjs +633 -0
- package/src/salesforce-source-map.mjs +212 -0
- package/src/security-validation-config.mjs +224 -0
- package/src/solution-readiness.mjs +620 -0
- package/src/starter-materialisation-contract.mjs +407 -0
- package/src/task-graph.mjs +544 -0
- package/src/team-hub-resources.mjs +239 -0
- package/src/team-hub.mjs +242 -0
- package/src/test-scenarios.mjs +622 -0
- package/src/validation-config.mjs +289 -0
- package/templates/SPECS/1.Scope/personas/registry.yaml +12 -0
- package/templates/SPECS/5.Strategy/patterns/context-packet.md +119 -0
- package/templates/SPECS/6.Build/_tracker-template.md +16 -0
- package/templates/SPECS/pipeline.yaml +62 -0
- package/templates/discovery-answers.yaml +86 -0
- package/templates/intent-body.md +21 -0
- package/tests/fixtures/context-benchmarks.json +9 -0
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { basename } from 'node:path';
|
|
2
|
+
import {
|
|
3
|
+
PlatformAnalysisError,
|
|
4
|
+
createPlatformFactCollector,
|
|
5
|
+
normalisePlatformIdentifier,
|
|
6
|
+
normalisePlatformPathIdentifier,
|
|
7
|
+
parseSafePlatformXml,
|
|
8
|
+
parseSafePlatformYaml,
|
|
9
|
+
platformFactsFile,
|
|
10
|
+
xmlChildText,
|
|
11
|
+
xmlDescendants
|
|
12
|
+
} from './platform-metadata-analysis.mjs';
|
|
13
|
+
|
|
14
|
+
const parser = 'ewai-power-platform-metadata/v1';
|
|
15
|
+
|
|
16
|
+
function normalPath(path) {
|
|
17
|
+
return String(path ?? '').replaceAll('\\', '/').replace(/^\.\//, '');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function solutionFromPath(path) {
|
|
21
|
+
return normalPath(path).match(/(?:^|\/)solutions\/([^/]+)\/(?:solution|solutioncomponents|rootcomponents|missingdependencies)\.ya?ml$/i)?.[1] ?? '';
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function addSolution(collector, solution) {
|
|
25
|
+
if (collector.addSymbol('power_solution', solution)) return solution;
|
|
26
|
+
return '';
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function addComponent(collector, solution, name, componentType = '') {
|
|
30
|
+
if (!collector.addSymbol('power_component', name, componentType ? { component_type: componentType } : {})) return;
|
|
31
|
+
if (solution) collector.addRelationship('power_solution', solution, 'contains', 'power_component', name);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function analyseLegacySolution(path, content) {
|
|
35
|
+
const root = parseSafePlatformXml(content);
|
|
36
|
+
if (root.name.toLowerCase() !== 'importexportxml') throw new PlatformAnalysisError('unsupported-platform-layout');
|
|
37
|
+
const collector = createPlatformFactCollector({ platform: 'power-platform', layout: 'legacy-solution-xml' });
|
|
38
|
+
const manifest = xmlDescendants(root, 'SolutionManifest')[0];
|
|
39
|
+
const solution = addSolution(collector, normalisePlatformIdentifier(xmlChildText(manifest, 'UniqueName')));
|
|
40
|
+
if (!solution) collector.markPartial();
|
|
41
|
+
for (const component of xmlDescendants(manifest, 'RootComponent')) {
|
|
42
|
+
const name = normalisePlatformIdentifier(component.attributes.schemaName ?? component.attributes.id);
|
|
43
|
+
const type = normalisePlatformIdentifier(component.attributes.type);
|
|
44
|
+
if (name) addComponent(collector, solution, name, type);
|
|
45
|
+
else collector.markPartial(1);
|
|
46
|
+
}
|
|
47
|
+
return platformFactsFile(collector.finish(), { language: 'xml', parser, content });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function analyseLegacyCustomisations(path, content) {
|
|
51
|
+
const root = parseSafePlatformXml(content);
|
|
52
|
+
const collector = createPlatformFactCollector({ platform: 'power-platform', layout: 'legacy-customisations-xml' });
|
|
53
|
+
let recognised = 0;
|
|
54
|
+
for (const entity of xmlDescendants(root, 'Entity')) {
|
|
55
|
+
const name = normalisePlatformIdentifier(
|
|
56
|
+
entity.attributes.Name ?? entity.attributes.name ?? xmlChildText(entity, 'Name')
|
|
57
|
+
);
|
|
58
|
+
if (!name) continue;
|
|
59
|
+
recognised += 1;
|
|
60
|
+
collector.addSymbol('power_entity', name);
|
|
61
|
+
for (const attribute of xmlDescendants(entity, 'attribute')) {
|
|
62
|
+
const attributeName = normalisePlatformIdentifier(
|
|
63
|
+
attribute.attributes.PhysicalName ?? attribute.attributes.Name ?? xmlChildText(attribute, 'Name')
|
|
64
|
+
);
|
|
65
|
+
if (!attributeName) continue;
|
|
66
|
+
collector.addSymbol('power_attribute', `${name}.${attributeName}`);
|
|
67
|
+
collector.addRelationship('power_entity', name, 'contains', 'power_attribute', `${name}.${attributeName}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (!recognised) collector.markPartial();
|
|
71
|
+
return platformFactsFile(collector.finish(), { language: 'xml', parser, content });
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function pathEntries(value, state = { values: [], omitted: 0 }) {
|
|
75
|
+
if (Array.isArray(value)) {
|
|
76
|
+
for (const entry of value) pathEntries(entry, state);
|
|
77
|
+
return state;
|
|
78
|
+
}
|
|
79
|
+
if (!value || typeof value !== 'object') return state;
|
|
80
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
81
|
+
if (key.toLowerCase() === 'path') {
|
|
82
|
+
const path = normalisePlatformPathIdentifier(entry);
|
|
83
|
+
if (path) state.values.push(path);
|
|
84
|
+
else state.omitted += 1;
|
|
85
|
+
} else if (entry && typeof entry === 'object') pathEntries(entry, state);
|
|
86
|
+
}
|
|
87
|
+
return state;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function componentEntries(value, found = []) {
|
|
91
|
+
if (Array.isArray(value)) {
|
|
92
|
+
for (const entry of value) componentEntries(entry, found);
|
|
93
|
+
return found;
|
|
94
|
+
}
|
|
95
|
+
if (!value || typeof value !== 'object') return found;
|
|
96
|
+
const name = normalisePlatformIdentifier(
|
|
97
|
+
value.SchemaName ?? value.schemaName ?? value.UniqueName ?? value.uniqueName ?? value.Id ?? value.id
|
|
98
|
+
);
|
|
99
|
+
const type = normalisePlatformIdentifier(value.Type ?? value.type);
|
|
100
|
+
if (name) found.push({ name, type });
|
|
101
|
+
for (const entry of Object.values(value)) {
|
|
102
|
+
if (entry && typeof entry === 'object') componentEntries(entry, found);
|
|
103
|
+
}
|
|
104
|
+
return found;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function analyseSolutionYaml(path, content) {
|
|
108
|
+
const value = parseSafePlatformYaml(content);
|
|
109
|
+
const file = basename(path).toLowerCase();
|
|
110
|
+
const layout = `solution-${file.replace(/\.ya?ml$/, '')}-yaml`;
|
|
111
|
+
const collector = createPlatformFactCollector({ platform: 'power-platform', layout });
|
|
112
|
+
const solution = addSolution(collector, normalisePlatformIdentifier(solutionFromPath(path)));
|
|
113
|
+
if (!solution) collector.markPartial();
|
|
114
|
+
if (file === 'solutioncomponents.yml') {
|
|
115
|
+
const { values: components, omitted } = pathEntries(value);
|
|
116
|
+
for (const component of components) addComponent(collector, solution, component);
|
|
117
|
+
if (omitted) collector.markPartial(omitted);
|
|
118
|
+
if (!components.length && !omitted) collector.markPartial();
|
|
119
|
+
} else if (file === 'rootcomponents.yml') {
|
|
120
|
+
const components = componentEntries(value);
|
|
121
|
+
for (const component of components) addComponent(collector, solution, component.name, component.type);
|
|
122
|
+
if (!components.length) collector.markPartial();
|
|
123
|
+
} else if (file === 'missingdependencies.yml') {
|
|
124
|
+
const { values: required, omitted } = pathEntries(value?.Required ?? value?.required ?? value?.MissingDependencies ?? value);
|
|
125
|
+
for (const component of required) {
|
|
126
|
+
collector.addSymbol('power_component', component);
|
|
127
|
+
if (solution) collector.addRelationship('power_solution', solution, 'requires', 'power_component', component);
|
|
128
|
+
}
|
|
129
|
+
if (omitted) collector.markPartial(omitted);
|
|
130
|
+
if (!required.length && !omitted) collector.markPartial();
|
|
131
|
+
}
|
|
132
|
+
return platformFactsFile(collector.finish(), { language: 'yaml', parser, content });
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function canvasChildren(value, parent, collector, depth = 0) {
|
|
136
|
+
if (depth > 96 || !value) return;
|
|
137
|
+
const entries = Array.isArray(value) ? value : [value];
|
|
138
|
+
for (const entry of entries) {
|
|
139
|
+
if (!entry || typeof entry !== 'object') continue;
|
|
140
|
+
for (const [name, definition] of Object.entries(entry)) {
|
|
141
|
+
const control = normalisePlatformIdentifier(name);
|
|
142
|
+
if (!control || ['Properties', 'Control', 'Variant'].includes(name)) continue;
|
|
143
|
+
collector.addSymbol('power_control', control);
|
|
144
|
+
collector.addRelationship(parent.kind, parent.name, 'contains', 'power_control', control);
|
|
145
|
+
canvasChildren(definition?.Children, { kind: 'power_control', name: control }, collector, depth + 1);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function analyseCanvas(path, content) {
|
|
151
|
+
const value = parseSafePlatformYaml(content);
|
|
152
|
+
const collector = createPlatformFactCollector({ platform: 'power-platform', layout: 'canvas-pa-yaml' });
|
|
153
|
+
const filename = basename(path).replace(/\.pa\.ya?ml$/i, '');
|
|
154
|
+
if (value?.App && filename.toLowerCase() === 'app') collector.addSymbol('power_app', filename);
|
|
155
|
+
let recognised = value?.App ? 1 : 0;
|
|
156
|
+
for (const [name, screen] of Object.entries(value?.Screens ?? {})) {
|
|
157
|
+
const screenName = normalisePlatformIdentifier(name);
|
|
158
|
+
if (!screenName) continue;
|
|
159
|
+
recognised += 1;
|
|
160
|
+
collector.addSymbol('power_screen', screenName);
|
|
161
|
+
canvasChildren(screen?.Children, { kind: 'power_screen', name: screenName }, collector);
|
|
162
|
+
}
|
|
163
|
+
for (const [name, component] of Object.entries(value?.Components ?? {})) {
|
|
164
|
+
const componentName = normalisePlatformIdentifier(name);
|
|
165
|
+
if (!componentName) continue;
|
|
166
|
+
recognised += 1;
|
|
167
|
+
collector.addSymbol('power_named_component', componentName);
|
|
168
|
+
canvasChildren(component?.Children, { kind: 'power_named_component', name: componentName }, collector);
|
|
169
|
+
}
|
|
170
|
+
if (!recognised) collector.markPartial();
|
|
171
|
+
return platformFactsFile(collector.finish(), { language: 'yaml', parser, content });
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export function powerPlatformLayout(path) {
|
|
175
|
+
const value = normalPath(path).toLowerCase();
|
|
176
|
+
if (/(?:^|\/)other\/solution\.xml$/.test(value)) return 'legacy-solution-xml';
|
|
177
|
+
if (/(?:^|\/)other\/customi[sz]ations\.xml$/.test(value)) return 'legacy-customisations-xml';
|
|
178
|
+
if (/(?:^|\/)solutions\/[^/]+\/(?:solution|solutioncomponents|rootcomponents|missingdependencies)\.ya?ml$/.test(value)) return 'solution-yaml';
|
|
179
|
+
if (/(?:^|\/)src\/[^/]+\.pa\.ya?ml$/.test(value) || /(?:^|\/)src\/component\/[^/]+\.pa\.ya?ml$/.test(value)) return 'canvas-pa-yaml';
|
|
180
|
+
return '';
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export function analysePowerPlatformMetadata(path, content) {
|
|
184
|
+
const layout = powerPlatformLayout(path);
|
|
185
|
+
if (layout === 'legacy-solution-xml') return analyseLegacySolution(path, content);
|
|
186
|
+
if (layout === 'legacy-customisations-xml') return analyseLegacyCustomisations(path, content);
|
|
187
|
+
if (layout === 'solution-yaml') return analyseSolutionYaml(path, content);
|
|
188
|
+
if (layout === 'canvas-pa-yaml') return analyseCanvas(path, content);
|
|
189
|
+
throw new PlatformAnalysisError('unsupported-platform-layout');
|
|
190
|
+
}
|
package/src/project.mjs
ADDED
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { basename, dirname, extname, relative, resolve } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import YAML from 'yaml';
|
|
6
|
+
import {normaliseDashboardPreferences} from './dashboard-preferences.mjs';
|
|
7
|
+
import { projectPaths } from './paths.mjs';
|
|
8
|
+
import { premiumPersonaLearningAction } from './companion-opening.mjs';
|
|
9
|
+
import { initializeRuntime } from './runtime/database.mjs';
|
|
10
|
+
import { configureProjectMcp } from './runtime/mcp-config.mjs';
|
|
11
|
+
import {
|
|
12
|
+
VALIDATION_PROVIDERS,
|
|
13
|
+
effectiveValidationConfig,
|
|
14
|
+
normaliseValidationConfig,
|
|
15
|
+
setValidationCheckpoint,
|
|
16
|
+
setValidationProvider,
|
|
17
|
+
} from './validation-config.mjs';
|
|
18
|
+
|
|
19
|
+
const moduleDir = dirname(fileURLToPath(import.meta.url));
|
|
20
|
+
const installRoot = resolve(moduleDir, '..');
|
|
21
|
+
const templateRoot = resolve(installRoot, 'templates/SPECS');
|
|
22
|
+
const codeManifestNames = new Set([
|
|
23
|
+
'package.json',
|
|
24
|
+
'composer.json',
|
|
25
|
+
'pyproject.toml',
|
|
26
|
+
'requirements.txt',
|
|
27
|
+
'go.mod',
|
|
28
|
+
'Cargo.toml',
|
|
29
|
+
'pom.xml',
|
|
30
|
+
'build.gradle',
|
|
31
|
+
'build.gradle.kts',
|
|
32
|
+
'Gemfile',
|
|
33
|
+
'mix.exs'
|
|
34
|
+
]);
|
|
35
|
+
const codeExtensions = new Set([
|
|
36
|
+
'.c', '.cc', '.cjs', '.cpp', '.cs', '.css', '.cts', '.dart', '.ex', '.exs', '.fs',
|
|
37
|
+
'.fsx', '.go', '.h', '.hpp', '.html', '.java', '.js', '.jsx', '.kt', '.kts', '.lua',
|
|
38
|
+
'.mjs', '.mts', '.php', '.py', '.r', '.rb', '.rs', '.scala', '.sh', '.swift', '.ts',
|
|
39
|
+
'.tsx', '.vue'
|
|
40
|
+
]);
|
|
41
|
+
const ignoredCodeDirectories = new Set([
|
|
42
|
+
'.git', '.ewai-pipeline', '.idea', '.vscode', 'SPECS', 'build', 'coverage', 'dist',
|
|
43
|
+
'node_modules', 'target', 'vendor'
|
|
44
|
+
]);
|
|
45
|
+
|
|
46
|
+
const requiredDirectories = [
|
|
47
|
+
'1.Scope/domain',
|
|
48
|
+
'1.Scope/personas/core',
|
|
49
|
+
'1.Scope/personas/project',
|
|
50
|
+
'1.Scope/personas/overlays',
|
|
51
|
+
'1.Scope/api',
|
|
52
|
+
'1.Scope/research',
|
|
53
|
+
'1.Scope/handoffs',
|
|
54
|
+
'1.Scope/templates',
|
|
55
|
+
'2.Purpose/intents',
|
|
56
|
+
'2.Purpose/journeys',
|
|
57
|
+
'2.Purpose/explorations',
|
|
58
|
+
'2.Purpose/discussions',
|
|
59
|
+
'2.Purpose/requirements/pending',
|
|
60
|
+
'2.Purpose/requirements/deferred',
|
|
61
|
+
'2.Purpose/requirements/satisfied',
|
|
62
|
+
'3.Evidence/retros',
|
|
63
|
+
'3.Evidence/postmortems',
|
|
64
|
+
'3.Evidence/gates',
|
|
65
|
+
'3.Evidence/iteration-logs',
|
|
66
|
+
'3.Evidence/archaeology',
|
|
67
|
+
'3.Evidence/context-imports',
|
|
68
|
+
'3.Evidence/risk',
|
|
69
|
+
'4.Constraints/compliance',
|
|
70
|
+
'5.Strategy/architecture/schema',
|
|
71
|
+
'5.Strategy/architecture/patterns',
|
|
72
|
+
'5.Strategy/architecture/checklists',
|
|
73
|
+
'5.Strategy/patterns',
|
|
74
|
+
'5.Strategy/decisions',
|
|
75
|
+
'5.Strategy/options',
|
|
76
|
+
'5.Strategy/sops',
|
|
77
|
+
'5.Strategy/runbooks',
|
|
78
|
+
'5.Strategy/capsules',
|
|
79
|
+
'6.Build'
|
|
80
|
+
];
|
|
81
|
+
|
|
82
|
+
const templateFiles = [
|
|
83
|
+
['pipeline.yaml', 'pipeline.yaml'],
|
|
84
|
+
['1.Scope/personas/registry.yaml', '1.Scope/personas/registry.yaml'],
|
|
85
|
+
['5.Strategy/patterns/context-packet.md', '5.Strategy/patterns/context-packet.md'],
|
|
86
|
+
['6.Build/_tracker-template.md', '6.Build/_tracker-template.md']
|
|
87
|
+
];
|
|
88
|
+
|
|
89
|
+
const checkinInstructionFiles = ['AGENTS.md', 'CLAUDE.md'];
|
|
90
|
+
const checkinInstruction = `<!-- EWAI-CHECKIN:START -->
|
|
91
|
+
## EWAI conversation check-in
|
|
92
|
+
|
|
93
|
+
Keep the complete workflow, but present it cleanly: four routine status lines plus the returned menu at a decision point. Preserve warnings, blockers and unknown checks with short reasons; detailed diagnostics are on request. Progress is one sentence of at most 24 words per meaningful checkpoint. Do not narrate commands, file reads, JSON parsing or internal reasoning, and do not repeat the menu during a selected action. Never shorten briefing, purpose alignment, consent, standards or approval gates to meet an output limit. Use the guarded dashboard password form by default for licence setup; a private terminal prompt is an alternative only when a genuine interactive terminal is available. Never emulate hidden terminal entry through chat.
|
|
94
|
+
|
|
95
|
+
When \`.ewai-pipeline/project.json\` exists, perform one EWAI check-in at the start of each new agent conversation before substantive project work. That locator identifies the configured SPECS root; do not assume it is \`./SPECS\`. Claude may receive its check-in JSON from the managed SessionStart hook; interpret that result instead of running it twice. Otherwise run \`ewai checkin --project . --json\`. This starts or reuses the project-local pipeline dashboard and refreshes its SQLite projection. Always interpret and report the EWAI version/update status, premium entitlement and installed-library status, dashboard URL, state-integrity result, mandatory standards status, configured external validators, and any configured-versus-installed CLI mismatch. Show unknown checks with their reason rather than omitting them.
|
|
96
|
+
|
|
97
|
+
The harness is installed and updated only through npm. Offer only its returned npm update action; never inspect, fetch, or pull a source repository to update EWAI.
|
|
98
|
+
|
|
99
|
+
For returning sessions, or after onboarding is complete or explicitly deferred by the owner, read recent work and render the structured \`companion\` opening returned by EWAI. **This response format is mandatory at that decision point.** A narrative-only session opening is invalid. Report the check-in status first, then render the returned heading and every returned action in order as \`[id] label\`. Do not replace the numbered menu with prose, recommendations, or a generic question. End with the exact returned closing prompt: **What's on your mind?** Do not finish that session opening until both the status and menu are visible. During first-run onboarding, continue the known startup path instead: settle chosen persona setup, human briefing and optional context import, then offer Archaeology for existing code, followed by Discovery and Doctor. Archaeology is optional: always offer it for existing code, explain that it can reconstruct missing documentation, and wait for acceptance before running it. Review and curate its findings only if accepted; if declined, continue to Discovery without reconstruction and leave Archaeology available later. Show the dashboard link early, but do not interrupt onboarding with the session menu or call the project ready before the chosen steps finish. Only the owner can defer onboarding.
|
|
100
|
+
|
|
101
|
+
When intent or delivery work exists, the menu must include **[6] Continue a piece of work**. If the user chooses it, use \`$ewai-deliver\` and the guarded EWAI continue/resume operations. Do not use the host AI's generic plan mode as a substitute for EWAI Plan.
|
|
102
|
+
|
|
103
|
+
Include **[7] Read about premium personas** and **[8] Set up premium personas** only when returned by check-in. Both are absent when premium access is available and the installed pack is verified. Learning opens https://www.conversationalcoding.dev/personas/ without purchasing, activating or downloading content. When \`companion.personaSetup\` is present, ask its exact optional question after the menu and before the closing prompt. Offer dashboard setup, reading first, or continuing with core personas; respect a decline. Use \`$ewai-persona-entitlement\` only when setup is chosen, and never ask for a key in chat. Do not substitute this setup question for an expired, invalid or unavailable configured licence.
|
|
104
|
+
|
|
105
|
+
For chosen premium setup, use the guarded dashboard setup form by default, or a hidden private terminal only when a genuine interactive terminal is available. Explain that key submission verifies and immediately installs the pack; no second sync confirmation is needed for that submitted action. Confirm the installed version, refresh the persona index and resolve chosen setup before Archaeology. On failure offer retry or explicit core-only continuation; preserve briefing and purpose alignment. Normal check-in and learning never download. Licence management remains reachable in dashboard Configuration even when promotional actions are hidden.
|
|
106
|
+
|
|
107
|
+
Always render the returned **[9] Configure the dashboard** action. Use \`$ewai-dashboard-configuration\` to explain and save explicit project-local view choices. Optional views are off by default. Hiding a view never disables required checks, hooks, policies, standards or approvals.
|
|
108
|
+
|
|
109
|
+
Immediately after successful first initialisation, offer the returned \`onboarding.personaLearning\` question before deeper analysis or Archaeology, for both new and existing code. Respect a decline and preserve the human briefing and purpose-alignment checkpoints. Do not repeat first-run onboarding in subsequent sessions; follow the returned action list.
|
|
110
|
+
|
|
111
|
+
Any request to plan, build, implement, deliver, resume, or move an intent forward must use \`$ewai-deliver\`. Before substantive repository claims, check the EWAI Tree-sitter index and refresh it when missing or stale. Never mutate a raw phase/status: phase progress must go through the canonical gate ledger, deterministic checker, and guarded start/complete operations. Build requires the explicit durable human approval gate.
|
|
112
|
+
|
|
113
|
+
External validation must follow the resolved checkpoint policy captured under the configured SPECS root at \`6.Build/<slug>/delivery-state.json\`: never use the orchestrator as its own independent reviewer, never exceed the configured cycle limit, and preserve each review and fix cycle as recorded evidence. The standards sweep remains mandatory even when no independent external CLI is configured.
|
|
114
|
+
|
|
115
|
+
When delivering an intent, use the EWAI MCP active-work tools to register the start, material progress events, human questions, blockers, handoffs, decisions, and finish. Do not emit heartbeat events with no material change.
|
|
116
|
+
|
|
117
|
+
Never download or update premium personas during the check-in. If the result offers an install or update, ask the user first. Only after an explicit yes, run \`ewai persona premium sync --project . --yes\`. Every session must check persona entitlement/version, even when the dashboard is reused. Website-purchased keys use the guarded dashboard password form by default; \`ewai persona premium configure --project .\` is an alternative only when a genuine private interactive terminal is available; never ask for a key in chat or write it into project YAML/SPECS. A confirmed matching team expiry removes only its managed premium cache; individual expiry keeps installed personas and stops updates. Unknown/invalid responses never delete content.
|
|
118
|
+
<!-- EWAI-CHECKIN:END -->`;
|
|
119
|
+
|
|
120
|
+
function renderTemplate(source, variables) {
|
|
121
|
+
return Object.entries(variables).reduce(
|
|
122
|
+
(content, [name, value]) => content.replaceAll(`{{${name}}}`, value),
|
|
123
|
+
source
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function writeIfMissing(path, content, force, result) {
|
|
128
|
+
if (existsSync(path) && !force) {
|
|
129
|
+
result.skipped.push(path);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
133
|
+
writeFileSync(path, content, 'utf8');
|
|
134
|
+
result.created.push(path);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function ensureCheckinInstructions(projectRoot, result) {
|
|
138
|
+
for (const name of checkinInstructionFiles) {
|
|
139
|
+
const path = resolve(projectRoot, name);
|
|
140
|
+
if (!existsSync(path)) {
|
|
141
|
+
writeFileSync(path, `${checkinInstruction}\n`, 'utf8');
|
|
142
|
+
result.created.push(path);
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
const content = readFileSync(path, 'utf8');
|
|
146
|
+
if (content.includes('<!-- EWAI-CHECKIN:START -->')) {
|
|
147
|
+
const start = content.indexOf('<!-- EWAI-CHECKIN:START -->');
|
|
148
|
+
const endMarker = '<!-- EWAI-CHECKIN:END -->';
|
|
149
|
+
const end = content.indexOf(endMarker, start);
|
|
150
|
+
if (end === -1) throw new Error(`Incomplete EWAI check-in instruction block: ${path}`);
|
|
151
|
+
const updated = `${content.slice(0, start)}${checkinInstruction}${content.slice(end + endMarker.length)}`;
|
|
152
|
+
if (updated === content) result.skipped.push(path);
|
|
153
|
+
else {
|
|
154
|
+
writeFileSync(path, updated, 'utf8');
|
|
155
|
+
result.created.push(path);
|
|
156
|
+
}
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
writeFileSync(path, `${content.trimEnd()}\n\n${checkinInstruction}\n`, 'utf8');
|
|
160
|
+
result.created.push(path);
|
|
161
|
+
}
|
|
162
|
+
removeLegacyGeminiCheckin(projectRoot, result);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function removeLegacyGeminiCheckin(projectRoot, result) {
|
|
166
|
+
const path = resolve(projectRoot, 'GEMINI.md');
|
|
167
|
+
if (!existsSync(path)) return;
|
|
168
|
+
const content = readFileSync(path, 'utf8');
|
|
169
|
+
const startMarker = '<!-- EWAI-CHECKIN:START -->';
|
|
170
|
+
const endMarker = '<!-- EWAI-CHECKIN:END -->';
|
|
171
|
+
const start = content.indexOf(startMarker);
|
|
172
|
+
if (start === -1) return;
|
|
173
|
+
const end = content.indexOf(endMarker, start);
|
|
174
|
+
if (end === -1) throw new Error(`Incomplete EWAI check-in instruction block: ${path}`);
|
|
175
|
+
const updated = `${content.slice(0, start)}${content.slice(end + endMarker.length)}`.trim();
|
|
176
|
+
writeFileSync(path, updated ? `${updated}\n` : '', 'utf8');
|
|
177
|
+
result.created.push(path);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function refreshCheckinInstructions(projectRoot) {
|
|
181
|
+
const result = { created: [], skipped: [] };
|
|
182
|
+
const root = resolve(projectRoot);
|
|
183
|
+
const paths = projectPaths(root);
|
|
184
|
+
ensureCheckinInstructions(root, result);
|
|
185
|
+
writeIfMissing(
|
|
186
|
+
resolve(paths.specsRoot, '5.Strategy/patterns/context-packet.md'),
|
|
187
|
+
readFileSync(resolve(templateRoot, '5.Strategy/patterns/context-packet.md'), 'utf8'),
|
|
188
|
+
false,
|
|
189
|
+
result
|
|
190
|
+
);
|
|
191
|
+
result.mcp = configureProjectMcp(root);
|
|
192
|
+
return result;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export function detectExistingCodebase(projectRoot) {
|
|
196
|
+
const root = resolve(projectRoot);
|
|
197
|
+
const evidence = [];
|
|
198
|
+
const queue = [{ path: root, depth: 0 }];
|
|
199
|
+
|
|
200
|
+
while (queue.length && evidence.length < 20) {
|
|
201
|
+
const current = queue.shift();
|
|
202
|
+
for (const entry of readdirSync(current.path, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
203
|
+
if (entry.isDirectory()) {
|
|
204
|
+
if (current.depth < 3 && !ignoredCodeDirectories.has(entry.name) && !entry.name.startsWith('.')) {
|
|
205
|
+
queue.push({ path: resolve(current.path, entry.name), depth: current.depth + 1 });
|
|
206
|
+
}
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
if (!entry.isFile()) continue;
|
|
210
|
+
|
|
211
|
+
const path = resolve(current.path, entry.name);
|
|
212
|
+
const projectRelative = relative(root, path);
|
|
213
|
+
if (
|
|
214
|
+
codeManifestNames.has(entry.name)
|
|
215
|
+
|| entry.name.endsWith('.sln')
|
|
216
|
+
|| entry.name.endsWith('.csproj')
|
|
217
|
+
|| codeExtensions.has(extname(entry.name).toLowerCase())
|
|
218
|
+
) {
|
|
219
|
+
evidence.push(projectRelative);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return { detected: evidence.length > 0, evidence };
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export function initProject(projectRoot, options = {}) {
|
|
228
|
+
const root = resolve(projectRoot);
|
|
229
|
+
if (!existsSync(root)) throw new Error(`Project directory does not exist: ${root}`);
|
|
230
|
+
|
|
231
|
+
const existingPaths = projectPaths(root);
|
|
232
|
+
const requestedPaths = projectPaths(root, { specsRoot: options.specsRoot });
|
|
233
|
+
if (options.specsRoot && existsSync(existingPaths.runtimeProjectPath) && existingPaths.specsRoot !== requestedPaths.specsRoot) {
|
|
234
|
+
throw new Error(`EWAI is already bound to ${existingPaths.specsRelative}. Relocating an existing SPECS contract requires an explicit migration, not init --specs.`);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const existingCodebase = detectExistingCodebase(root);
|
|
238
|
+
const paths = requestedPaths;
|
|
239
|
+
const discoveryCommand = `ewai discover --project ${JSON.stringify(root)}`;
|
|
240
|
+
const briefingPrompt = `Use $ewai-project-discovery to capture and confirm why ${JSON.stringify(root)} exists, who it serves, and what outcomes matter before Archaeology begins.`;
|
|
241
|
+
const archaeologyPrompt = `Offer optional Archaeology to reconstruct missing project documentation from the existing code. Ask whether the user wants it; only if the user accepts, use $ewai-archaeology to compare the confirmed project briefing with repository evidence at ${JSON.stringify(root)}, resolve material discrepancies, and reconstruct deeper project knowledge. If declined, continue to Discovery without reconstruction; Archaeology remains available later.`;
|
|
242
|
+
const result = {
|
|
243
|
+
projectRoot: root,
|
|
244
|
+
specsRoot: paths.specsRoot,
|
|
245
|
+
specsRelative: paths.specsRelative,
|
|
246
|
+
created: [],
|
|
247
|
+
skipped: [],
|
|
248
|
+
nextCommand: existingCodebase.detected ? null : discoveryCommand,
|
|
249
|
+
onboarding: existingCodebase.detected
|
|
250
|
+
? {
|
|
251
|
+
existingCodebase: true,
|
|
252
|
+
evidence: existingCodebase.evidence,
|
|
253
|
+
recommendedNextStep: 'project-briefing',
|
|
254
|
+
skill: 'ewai-project-discovery',
|
|
255
|
+
prompt: briefingPrompt,
|
|
256
|
+
afterBrief: archaeologyPrompt,
|
|
257
|
+
archaeology: { optional: true, question: 'Would you like us to analyse the existing code and reconstruct its missing project documentation?', declineCommand: discoveryCommand },
|
|
258
|
+
afterReview: discoveryCommand
|
|
259
|
+
}
|
|
260
|
+
: {
|
|
261
|
+
existingCodebase: false,
|
|
262
|
+
evidence: [],
|
|
263
|
+
recommendedNextStep: 'discovery',
|
|
264
|
+
command: discoveryCommand
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
result.onboarding.personaLearning = existsSync(existingPaths.runtimeProjectPath) ? null : {
|
|
268
|
+
question: 'Would you like to see how specialist personas could help us analyse this project?',
|
|
269
|
+
action: premiumPersonaLearningAction(),
|
|
270
|
+
optional: true,
|
|
271
|
+
before: 'deeper-analysis',
|
|
272
|
+
decline: 'Continue normal onboarding with installed core, personal and project personas.'
|
|
273
|
+
};
|
|
274
|
+
const variables = {
|
|
275
|
+
PROJECT_NAME: JSON.stringify(options.name || basename(root)),
|
|
276
|
+
SPECS_ROOT: JSON.stringify(paths.specsRelative),
|
|
277
|
+
PRIMARY_REPOSITORY_NAME: paths.specsRelative === 'SPECS' ? 'application' : 'knowledge',
|
|
278
|
+
PRIMARY_REPOSITORY_PATH: JSON.stringify(paths.specsRelative === 'SPECS' ? '.' : dirname(paths.specsRelative)),
|
|
279
|
+
PRIMARY_REPOSITORY_ROLE: paths.specsRelative === 'SPECS' ? 'application' : 'knowledge-and-delivery',
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
for (const directory of requiredDirectories) {
|
|
283
|
+
mkdirSync(resolve(paths.specsRoot, directory), { recursive: true });
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
for (const [sourceRelative, destinationRelative] of templateFiles) {
|
|
287
|
+
const source = readFileSync(resolve(templateRoot, sourceRelative), 'utf8');
|
|
288
|
+
writeIfMissing(
|
|
289
|
+
resolve(paths.specsRoot, destinationRelative),
|
|
290
|
+
renderTemplate(source, variables),
|
|
291
|
+
Boolean(options.force),
|
|
292
|
+
result
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (options.initSpecsRepository) {
|
|
297
|
+
const repositoryRoot = resolve(root, dirname(paths.specsRelative));
|
|
298
|
+
if (paths.specsRelative === 'SPECS' || repositoryRoot === root) {
|
|
299
|
+
throw new Error('--init-specs-repo requires a dedicated path such as project-knowledge/SPECS');
|
|
300
|
+
}
|
|
301
|
+
if (!existsSync(resolve(repositoryRoot, '.git'))) {
|
|
302
|
+
execFileSync('git', ['init', '-b', 'main'], { cwd: repositoryRoot, stdio: 'ignore' });
|
|
303
|
+
result.specsRepositoryInitialized = repositoryRoot;
|
|
304
|
+
} else {
|
|
305
|
+
result.specsRepositoryInitialized = null;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
ensureCheckinInstructions(root, result);
|
|
310
|
+
|
|
311
|
+
if (options.validators?.length) {
|
|
312
|
+
const config = YAML.parse(readFileSync(paths.configPath, 'utf8'));
|
|
313
|
+
for (const validator of options.validators) {
|
|
314
|
+
setValidationProvider(config, validator, 'available', true);
|
|
315
|
+
}
|
|
316
|
+
writeFileSync(paths.configPath, YAML.stringify(config, { lineWidth: 0 }), 'utf8');
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
mkdirSync(paths.runtimeRoot, { recursive: true });
|
|
320
|
+
writeIfMissing(resolve(paths.runtimeRoot, '.gitignore'), '*\n!.gitignore\n', false, result);
|
|
321
|
+
writeIfMissing(
|
|
322
|
+
paths.runtimeProjectPath,
|
|
323
|
+
`${JSON.stringify({ schema: 'ewai.runtime-project/v1', projectRoot: root, specsRoot: paths.specsRelative }, null, 2)}\n`,
|
|
324
|
+
Boolean(options.force),
|
|
325
|
+
result
|
|
326
|
+
);
|
|
327
|
+
|
|
328
|
+
result.runtime = initializeRuntime(root);
|
|
329
|
+
result.mcp = configureProjectMcp(root);
|
|
330
|
+
|
|
331
|
+
return result;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
export function configureExternalValidation(projectRoot, validator, state, enabled = undefined) {
|
|
335
|
+
const { config, paths } = loadProjectConfig(projectRoot);
|
|
336
|
+
setValidationProvider(config, validator, state, enabled);
|
|
337
|
+
writeFileSync(paths.configPath, YAML.stringify(config, { lineWidth: 0 }), 'utf8');
|
|
338
|
+
return externalValidationStatus(config);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
export function configureValidationCheckpoint(projectRoot, checkpoint, update) {
|
|
342
|
+
const { config, paths } = loadProjectConfig(projectRoot);
|
|
343
|
+
setValidationCheckpoint(config, checkpoint, update);
|
|
344
|
+
writeFileSync(paths.configPath, YAML.stringify(config, { lineWidth: 0 }), 'utf8');
|
|
345
|
+
return validationStatus(config);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
export function externalValidationStatus(config, orchestrator = 'manual') {
|
|
349
|
+
const effective = effectiveValidationConfig(config, orchestrator);
|
|
350
|
+
const selected = new Set(
|
|
351
|
+
Object.values(effective.checkpoints).flatMap((checkpoint) => checkpoint.validators),
|
|
352
|
+
);
|
|
353
|
+
return VALIDATION_PROVIDERS.map((validator) => ({
|
|
354
|
+
validator,
|
|
355
|
+
state: effective.providers[validator].state,
|
|
356
|
+
enabled: effective.providers[validator].enabled,
|
|
357
|
+
selected: selected.has(validator),
|
|
358
|
+
excluded_as_orchestrator: validator === effective.orchestrator,
|
|
359
|
+
}));
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
export function validationStatus(config, orchestrator = 'manual') {
|
|
363
|
+
return effectiveValidationConfig(config, orchestrator);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
export function migrateProjectValidationConfig(projectRoot) {
|
|
367
|
+
const paths = projectPaths(projectRoot);
|
|
368
|
+
if (!existsSync(paths.configPath)) {
|
|
369
|
+
throw new Error(`Missing EWAI project configuration: ${paths.configPath}`);
|
|
370
|
+
}
|
|
371
|
+
const config = YAML.parse(readFileSync(paths.configPath, 'utf8'));
|
|
372
|
+
const validation = normaliseValidationConfig(config.validation);
|
|
373
|
+
const changed = JSON.stringify(config.validation ?? null) !== JSON.stringify(validation);
|
|
374
|
+
if (changed) {
|
|
375
|
+
config.validation = validation;
|
|
376
|
+
writeFileSync(paths.configPath, YAML.stringify(config, { lineWidth: 0 }), 'utf8');
|
|
377
|
+
}
|
|
378
|
+
return { changed, path: paths.configPath, validation };
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
export function loadProjectConfig(projectRoot) {
|
|
382
|
+
const paths = projectPaths(projectRoot);
|
|
383
|
+
if (!existsSync(paths.configPath)) {
|
|
384
|
+
throw new Error(`Missing EWAI project configuration: ${paths.configPath}`);
|
|
385
|
+
}
|
|
386
|
+
const config = YAML.parse(readFileSync(paths.configPath, 'utf8'));
|
|
387
|
+
if (config?.schema !== 'ewai.project/v1') {
|
|
388
|
+
throw new Error(`Unsupported EWAI project schema in ${paths.configPath}`);
|
|
389
|
+
}
|
|
390
|
+
if (String(config?.specs?.root ?? '') !== paths.specsRelative) {
|
|
391
|
+
throw new Error(`SPECS locator mismatch: runtime points to ${paths.specsRelative}, but pipeline.yaml declares ${config?.specs?.root ?? '<missing>'}`);
|
|
392
|
+
}
|
|
393
|
+
config.validation = normaliseValidationConfig(config.validation);
|
|
394
|
+
config.dashboard = normaliseDashboardPreferences(config.dashboard);
|
|
395
|
+
return { config, paths };
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
export function doctorProject(projectRoot) {
|
|
399
|
+
const checks = [];
|
|
400
|
+
let loaded;
|
|
401
|
+
|
|
402
|
+
try {
|
|
403
|
+
loaded = loadProjectConfig(projectRoot);
|
|
404
|
+
checks.push({ name: 'project-config', status: 'pass', path: loaded.paths.configPath });
|
|
405
|
+
} catch (error) {
|
|
406
|
+
checks.push({ name: 'project-config', status: 'fail', message: error.message });
|
|
407
|
+
return checks;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
const required = [
|
|
411
|
+
loaded.paths.personaRoot,
|
|
412
|
+
loaded.paths.buildRoot,
|
|
413
|
+
resolve(loaded.paths.specsRoot, '5.Strategy/architecture/patterns'),
|
|
414
|
+
resolve(loaded.paths.personaRoot, 'registry.yaml'),
|
|
415
|
+
resolve(loaded.paths.runtimeRoot, 'data/pipeline.sqlite'),
|
|
416
|
+
resolve(loaded.paths.projectRoot, '.mcp.json'),
|
|
417
|
+
resolve(loaded.paths.projectRoot, '.claude/settings.json'),
|
|
418
|
+
resolve(loaded.paths.projectRoot, '.codex/config.toml'),
|
|
419
|
+
resolve(loaded.paths.projectRoot, '.agents/mcp_config.json')
|
|
420
|
+
];
|
|
421
|
+
|
|
422
|
+
for (const path of required) {
|
|
423
|
+
checks.push({
|
|
424
|
+
name: `path:${path.slice(loaded.paths.projectRoot.length + 1)}`,
|
|
425
|
+
status: existsSync(path) ? 'pass' : 'fail',
|
|
426
|
+
path
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
const repositories = loaded.config.repositories ?? [];
|
|
431
|
+
for (const repository of repositories) {
|
|
432
|
+
const path = resolve(loaded.paths.projectRoot, repository.path);
|
|
433
|
+
checks.push({
|
|
434
|
+
name: `repository:${repository.name}`,
|
|
435
|
+
status: existsSync(path) ? 'pass' : 'fail',
|
|
436
|
+
path
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
for (const provider of externalValidationStatus(loaded.config)) {
|
|
441
|
+
checks.push({
|
|
442
|
+
name: `external-validation:${provider.validator}`,
|
|
443
|
+
status: 'pass',
|
|
444
|
+
availability: provider.state
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
return checks;
|
|
449
|
+
}
|