agentic-dev 0.2.11 → 0.2.13
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/README.md +72 -54
- package/bin/agentic-dev.mjs +162 -11
- package/lib/github.mjs +246 -0
- package/lib/orchestration-assets.mjs +249 -0
- package/lib/scaffold.mjs +89 -0
- package/package.json +8 -19
- package/.dockerignore +0 -8
- package/.env.example +0 -50
- package/.gitignore +0 -16
- package/AGENTS.md +0 -86
- package/SDD_SKILL.md +0 -589
- package/compose.yml +0 -206
- package/infra/compose/.env.dev.example +0 -28
- package/infra/compose/.env.prod.example +0 -29
- package/infra/compose/README.md +0 -35
- package/infra/compose/dev.yml +0 -125
- package/infra/compose/prod.yml +0 -126
- package/infra/terraform/README.md +0 -34
- package/infra/terraform/aws/data/.terraform.lock.hcl +0 -25
- package/infra/terraform/aws/data/README.md +0 -18
- package/infra/terraform/aws/data/main.tf +0 -147
- package/infra/terraform/aws/data/outputs.tf +0 -14
- package/infra/terraform/aws/data/variables.tf +0 -57
- package/infra/terraform/aws/data/versions.tf +0 -10
- package/infra/terraform/aws/domain/.terraform.lock.hcl +0 -25
- package/infra/terraform/aws/domain/README.md +0 -20
- package/infra/terraform/aws/domain/env/dev.tfvars.example +0 -6
- package/infra/terraform/aws/domain/env/prod.tfvars.example +0 -7
- package/infra/terraform/aws/domain/main.tf +0 -149
- package/infra/terraform/aws/domain/outputs.tf +0 -29
- package/infra/terraform/aws/domain/variables.tf +0 -58
- package/infra/terraform/aws/domain/versions.tf +0 -10
- package/infra/terraform/openstack/README.md +0 -38
- package/infra/terraform/openstack/dev/.terraform.lock.hcl +0 -24
- package/infra/terraform/openstack/dev/README.md +0 -18
- package/infra/terraform/openstack/dev/main.tf +0 -49
- package/infra/terraform/openstack/dev/providers.tf +0 -15
- package/infra/terraform/openstack/dev/terraform.tfvars.example +0 -54
- package/infra/terraform/openstack/dev/variables.tf +0 -210
- package/infra/terraform/openstack/dev/versions.tf +0 -10
- package/infra/terraform/openstack/modules/environment_host/main.tf +0 -143
- package/infra/terraform/openstack/modules/environment_host/outputs.tf +0 -25
- package/infra/terraform/openstack/modules/environment_host/templates/docker-host-user-data.sh.tftpl +0 -40
- package/infra/terraform/openstack/modules/environment_host/variables.tf +0 -145
- package/infra/terraform/openstack/modules/environment_host/versions.tf +0 -7
- package/infra/terraform/openstack/prod/.terraform.lock.hcl +0 -24
- package/infra/terraform/openstack/prod/README.md +0 -18
- package/infra/terraform/openstack/prod/main.tf +0 -49
- package/infra/terraform/openstack/prod/providers.tf +0 -15
- package/infra/terraform/openstack/prod/terraform.tfvars.example +0 -55
- package/infra/terraform/openstack/prod/variables.tf +0 -210
- package/infra/terraform/openstack/prod/versions.tf +0 -10
- package/infra/terraform/openstack/server/.terraform.lock.hcl +0 -45
- package/infra/terraform/openstack/server/README.md +0 -47
- package/infra/terraform/openstack/server/main.tf +0 -161
- package/infra/terraform/openstack/server/outputs.tf +0 -30
- package/infra/terraform/openstack/server/providers.tf +0 -30
- package/infra/terraform/openstack/server/templates/server-user-data.sh.tftpl +0 -50
- package/infra/terraform/openstack/server/variables.tf +0 -233
- package/infra/terraform/openstack/server/zz_aspace.auto.tfvars.example.json +0 -29
- package/pnpm-workspace.yaml +0 -2
- package/scripts/dev/audit_sdd_build_ast.py +0 -277
- package/sdd/01_planning/01_feature/INDEX.md +0 -16
- package/sdd/01_planning/01_feature/README.md +0 -76
- package/sdd/01_planning/01_feature/alerts_feature_spec.md +0 -55
- package/sdd/01_planning/01_feature/auth_feature_spec.md +0 -57
- package/sdd/01_planning/01_feature/catalog_feature_spec.md +0 -61
- package/sdd/01_planning/01_feature/fulfillment_feature_spec.md +0 -58
- package/sdd/01_planning/01_feature/health_feature_spec.md +0 -52
- package/sdd/01_planning/01_feature/inventory_feature_spec.md +0 -60
- package/sdd/01_planning/01_feature/order_feature_spec.md +0 -63
- package/sdd/01_planning/01_feature/shipping_feature_spec.md +0 -55
- package/sdd/01_planning/01_feature/support_feature_spec.md +0 -53
- package/sdd/01_planning/01_feature/user_feature_spec.md +0 -54
- package/sdd/01_planning/02_screen/INDEX.md +0 -13
- package/sdd/01_planning/02_screen/README.md +0 -41
- package/sdd/01_planning/02_screen/admin_screen_spec.pdf +0 -0
- package/sdd/01_planning/02_screen/assets/README.md +0 -16
- package/sdd/01_planning/02_screen/assets/example/README.md +0 -13
- package/sdd/01_planning/02_screen/landing_screen_spec.pdf +0 -0
- package/sdd/01_planning/02_screen/mobile_screen_spec.pdf +0 -0
- package/sdd/01_planning/02_screen/web_screen_spec.pdf +0 -0
- package/sdd/01_planning/03_architecture/INDEX.md +0 -9
- package/sdd/01_planning/03_architecture/README.md +0 -25
- package/sdd/01_planning/03_architecture/architecture_document_structure.md +0 -77
- package/sdd/01_planning/03_architecture/backend/README.md +0 -10
- package/sdd/01_planning/03_architecture/frontend/README.md +0 -12
- package/sdd/01_planning/03_architecture/infra/README.md +0 -10
- package/sdd/01_planning/03_architecture/tech-research/README.md +0 -4
- package/sdd/01_planning/03_architecture/templates_system_architecture.md +0 -84
- package/sdd/01_planning/04_data/INDEX.md +0 -4
- package/sdd/01_planning/04_data/README.md +0 -10
- package/sdd/01_planning/04_data/templates_data_modeling.md +0 -119
- package/sdd/01_planning/05_api/README.md +0 -12
- package/sdd/01_planning/05_api/templates_api_contract.md +0 -90
- package/sdd/01_planning/06_iac/README.md +0 -11
- package/sdd/01_planning/06_iac/templates_runtime_and_cicd_baseline.md +0 -46
- package/sdd/01_planning/07_integration/README.md +0 -11
- package/sdd/01_planning/07_integration/templates_frontend_api_integration.md +0 -46
- package/sdd/01_planning/08_nonfunctional/README.md +0 -7
- package/sdd/01_planning/09_security/README.md +0 -7
- package/sdd/01_planning/10_test/README.md +0 -12
- package/sdd/01_planning/10_test/templates_test_strategy.md +0 -60
- package/sdd/01_planning/INDEX.md +0 -19
- package/sdd/01_planning/README.md +0 -17
- package/sdd/02_plan/01_feature/README.md +0 -34
- package/sdd/02_plan/01_feature/_feature_todo_template.md +0 -29
- package/sdd/02_plan/02_screen/INDEX.md +0 -19
- package/sdd/02_plan/02_screen/README.md +0 -39
- package/sdd/02_plan/02_screen/_screen_todo_template.md +0 -60
- package/sdd/02_plan/03_architecture/README.md +0 -23
- package/sdd/02_plan/03_architecture/architecture_document_governance.md +0 -40
- package/sdd/02_plan/03_architecture/build_ast_runtime_tree_governance.md +0 -53
- package/sdd/02_plan/03_architecture/repository_governance.md +0 -39
- package/sdd/02_plan/03_architecture/runtime_and_structure_governance.md +0 -38
- package/sdd/02_plan/03_architecture/templates-hexagonal-template-architecture.md +0 -9
- package/sdd/02_plan/03_architecture/toolchain_governance.md +0 -98
- package/sdd/02_plan/04_data/README.md +0 -5
- package/sdd/02_plan/05_api/README.md +0 -5
- package/sdd/02_plan/06_iac/README.md +0 -11
- package/sdd/02_plan/06_iac/dev_runtime_delivery.md +0 -36
- package/sdd/02_plan/06_iac/template_runtime_delivery.md +0 -50
- package/sdd/02_plan/07_integration/README.md +0 -5
- package/sdd/02_plan/07_integration/frontend_live_integration.md +0 -31
- package/sdd/02_plan/08_nonfunctional/README.md +0 -5
- package/sdd/02_plan/08_nonfunctional/repository_hygiene.md +0 -26
- package/sdd/02_plan/09_security/README.md +0 -5
- package/sdd/02_plan/10_test/README.md +0 -11
- package/sdd/02_plan/10_test/regression_verification.md +0 -39
- package/sdd/02_plan/10_test/templates/README.md +0 -8
- package/sdd/02_plan/10_test/templates/ui_parity_web_contract.template.yaml +0 -23
- package/sdd/02_plan/10_test/verification_strategy.md +0 -43
- package/sdd/02_plan/99_generated/from_planning/ui_parity/.gitkeep +0 -1
- package/sdd/02_plan/README.md +0 -40
- package/sdd/03_build/01_feature/README.md +0 -20
- package/sdd/03_build/01_feature/domain/README.md +0 -3
- package/sdd/03_build/01_feature/domain/account_and_access.md +0 -20
- package/sdd/03_build/01_feature/domain/catalog_and_inventory.md +0 -20
- package/sdd/03_build/01_feature/domain/ordering_and_fulfillment.md +0 -21
- package/sdd/03_build/01_feature/domain/support_and_observability.md +0 -21
- package/sdd/03_build/01_feature/domain_surfaces.md +0 -28
- package/sdd/03_build/01_feature/service/README.md +0 -3
- package/sdd/03_build/01_feature/service/admin_surface.md +0 -15
- package/sdd/03_build/01_feature/service/landing_surface.md +0 -13
- package/sdd/03_build/01_feature/service/mobile_surface.md +0 -14
- package/sdd/03_build/01_feature/service/web_surface.md +0 -14
- package/sdd/03_build/02_screen/README.md +0 -25
- package/sdd/03_build/02_screen/_screen_build_template.md +0 -26
- package/sdd/03_build/02_screen/admin/README.md +0 -5
- package/sdd/03_build/02_screen/landing/README.md +0 -5
- package/sdd/03_build/02_screen/mobile/README.md +0 -5
- package/sdd/03_build/02_screen/web/README.md +0 -5
- package/sdd/03_build/03_architecture/README.md +0 -10
- package/sdd/03_build/03_architecture/architecture_document_governance.md +0 -30
- package/sdd/03_build/03_architecture/build_ast_runtime_tree_governance.md +0 -24
- package/sdd/03_build/03_architecture/repository_governance.md +0 -18
- package/sdd/03_build/03_architecture/toolchain_governance.md +0 -36
- package/sdd/03_build/06_iac/README.md +0 -3
- package/sdd/03_build/06_iac/dev_runtime_delivery.md +0 -10
- package/sdd/03_build/06_iac/template_runtime_delivery.md +0 -49
- package/sdd/03_build/07_integration/README.md +0 -3
- package/sdd/03_build/07_integration/frontend_live_integration.md +0 -11
- package/sdd/03_build/08_nonfunctional/README.md +0 -3
- package/sdd/03_build/08_nonfunctional/repository_hygiene.md +0 -10
- package/sdd/03_build/10_test/README.md +0 -9
- package/sdd/03_build/10_test/regression_verification.md +0 -16
- package/sdd/03_build/10_test/verification_harness.md +0 -11
- package/sdd/03_build/README.md +0 -35
- package/sdd/03_verify/01_feature/README.md +0 -5
- package/sdd/03_verify/01_feature/domain_verification.md +0 -14
- package/sdd/03_verify/01_feature/service_verification.md +0 -22
- package/sdd/03_verify/02_screen/README.md +0 -6
- package/sdd/03_verify/02_screen/_screen_verify_template.md +0 -20
- package/sdd/03_verify/02_screen/admin/README.md +0 -4
- package/sdd/03_verify/02_screen/landing/README.md +0 -4
- package/sdd/03_verify/02_screen/mobile/README.md +0 -4
- package/sdd/03_verify/02_screen/web/README.md +0 -4
- package/sdd/03_verify/03_architecture/README.md +0 -10
- package/sdd/03_verify/03_architecture/architecture_document_governance.md +0 -15
- package/sdd/03_verify/03_architecture/build_ast_runtime_tree_governance.md +0 -28
- package/sdd/03_verify/03_architecture/repository_governance.md +0 -16
- package/sdd/03_verify/03_architecture/toolchain_governance.md +0 -58
- package/sdd/03_verify/06_iac/README.md +0 -3
- package/sdd/03_verify/06_iac/dev_runtime_delivery.md +0 -10
- package/sdd/03_verify/06_iac/template_runtime_delivery.md +0 -42
- package/sdd/03_verify/07_integration/README.md +0 -3
- package/sdd/03_verify/07_integration/frontend_live_integration.md +0 -16
- package/sdd/03_verify/08_nonfunctional/README.md +0 -3
- package/sdd/03_verify/08_nonfunctional/repository_hygiene.md +0 -14
- package/sdd/03_verify/10_test/README.md +0 -9
- package/sdd/03_verify/10_test/regression_verification.md +0 -16
- package/sdd/03_verify/10_test/ui_parity/README.md +0 -4
- package/sdd/03_verify/10_test/ui_parity/loop_runs/.gitkeep +0 -0
- package/sdd/03_verify/10_test/ui_parity/reference/.gitkeep +0 -0
- package/sdd/03_verify/10_test/ui_parity/staged_runs/.gitkeep +0 -0
- package/sdd/03_verify/10_test/verification_harness.md +0 -17
- package/sdd/03_verify/README.md +0 -22
- package/sdd/05_operate/01_runbooks/.gitkeep +0 -1
- package/sdd/05_operate/01_runbooks/README.md +0 -4
- package/sdd/05_operate/02_delivery_status/README.md +0 -4
- package/sdd/05_operate/02_delivery_status/service_status.md +0 -16
- package/sdd/05_operate/README.md +0 -12
- package/sdd/99_toolchain/01_automation/.gitkeep +0 -1
- package/sdd/99_toolchain/01_automation/README.md +0 -76
- package/sdd/99_toolchain/01_automation/agentic-dev/analyze_proof_results.py +0 -132
- package/sdd/99_toolchain/01_automation/agentic-dev/analyze_route_gap.py +0 -85
- package/sdd/99_toolchain/01_automation/agentic-dev/assets/repo-contract.template.json +0 -75
- package/sdd/99_toolchain/01_automation/agentic-dev/bootstrap_frontend_parity.sh +0 -84
- package/sdd/99_toolchain/01_automation/agentic-dev/init_frontend_parity.sh +0 -33
- package/sdd/99_toolchain/01_automation/agentic-dev/init_repo_contract.sh +0 -51
- package/sdd/99_toolchain/01_automation/agentic-dev/repo-contract.json +0 -76
- package/sdd/99_toolchain/01_automation/agentic-dev/resolve_frontend_target.py +0 -52
- package/sdd/99_toolchain/01_automation/agentic-dev/resolve_repo_contract.py +0 -56
- package/sdd/99_toolchain/01_automation/agentic-dev/run_frontend_target.sh +0 -100
- package/sdd/99_toolchain/01_automation/agentic-dev/run_repo_phase.sh +0 -140
- package/sdd/99_toolchain/01_automation/agentic-dev/validate_json_schema.py +0 -39
- package/sdd/99_toolchain/01_automation/agentic-parity-harness-design.md +0 -291
- package/sdd/99_toolchain/01_automation/assets/admin_screen_capture/dashboard.png +0 -0
- package/sdd/99_toolchain/01_automation/assets/admin_screen_capture/login.png +0 -0
- package/sdd/99_toolchain/01_automation/assets/admin_screen_capture/queue.png +0 -0
- package/sdd/99_toolchain/01_automation/assets/admin_screen_capture/support.png +0 -0
- package/sdd/99_toolchain/01_automation/assets/landing_screen_capture/home.png +0 -0
- package/sdd/99_toolchain/01_automation/assets/landing_screen_capture/login.png +0 -0
- package/sdd/99_toolchain/01_automation/assets/landing_screen_capture/workspace.png +0 -0
- package/sdd/99_toolchain/01_automation/assets/mobile_screen_capture/dashboard.png +0 -0
- package/sdd/99_toolchain/01_automation/assets/mobile_screen_capture/fulfillment.png +0 -0
- package/sdd/99_toolchain/01_automation/assets/mobile_screen_capture/login.png +0 -0
- package/sdd/99_toolchain/01_automation/assets/web_screen_capture/dashboard.png +0 -0
- package/sdd/99_toolchain/01_automation/assets/web_screen_capture/login.png +0 -0
- package/sdd/99_toolchain/01_automation/assets/web_screen_capture/orders.png +0 -0
- package/sdd/99_toolchain/01_automation/build_asset_recipes.py +0 -10
- package/sdd/99_toolchain/01_automation/build_screen_spec_pdf.py +0 -427
- package/sdd/99_toolchain/01_automation/capture_screen_assets.mjs +0 -148
- package/sdd/99_toolchain/01_automation/harness-layout.md +0 -34
- package/sdd/99_toolchain/01_automation/parity-execution-tooling-design.md +0 -319
- package/sdd/99_toolchain/01_automation/playwright_exactness_manifest.py +0 -21
- package/sdd/99_toolchain/01_automation/run_playwright_exactness.py +0 -87
- package/sdd/99_toolchain/01_automation/screen_spec_manifest.py +0 -321
- package/sdd/99_toolchain/01_automation/spec_asset_builder.py +0 -274
- package/sdd/99_toolchain/01_automation/ui-contract-projection.md +0 -79
- package/sdd/99_toolchain/01_automation/ui-parity/README.md +0 -60
- package/sdd/99_toolchain/01_automation/ui-parity/cli/extract-reference-pages.mjs +0 -2
- package/sdd/99_toolchain/01_automation/ui-parity/cli/materialize-reference-assets.mjs +0 -58
- package/sdd/99_toolchain/01_automation/ui-parity/cli/normalize-reference-assets.mjs +0 -2
- package/sdd/99_toolchain/01_automation/ui-parity/cli/route-gap-report.mjs +0 -187
- package/sdd/99_toolchain/01_automation/ui-parity/cli/run-proof.mjs +0 -50
- package/sdd/99_toolchain/01_automation/ui-parity/cli/scaffold-contract.mjs +0 -62
- package/sdd/99_toolchain/01_automation/ui-parity/cli/upload-parity1.mjs +0 -2
- package/sdd/99_toolchain/01_automation/ui-parity/contracts/collector-metadata.schema.json +0 -33
- package/sdd/99_toolchain/01_automation/ui-parity/contracts/proof-result.schema.json +0 -76
- package/sdd/99_toolchain/01_automation/ui-parity/contracts/route-gap-report.schema.json +0 -95
- package/sdd/99_toolchain/01_automation/ui-parity/core/capture-runner.mjs +0 -55
- package/sdd/99_toolchain/01_automation/ui-parity/core/load-adapter.mjs +0 -25
- package/sdd/99_toolchain/01_automation/ui-parity/core/load-contract.mjs +0 -81
- package/sdd/99_toolchain/01_automation/ui-parity/core/paths.mjs +0 -23
- package/sdd/99_toolchain/01_automation/ui-parity/core/proof-runner.mjs +0 -255
- package/sdd/99_toolchain/01_automation/ui-parity/interfaces/ui-parity-artifact-layout.md +0 -23
- package/sdd/99_toolchain/01_automation/ui-parity/interfaces/ui-parity-proof-interface.md +0 -60
- package/sdd/99_toolchain/01_automation/ui-parity/interfaces/ui-parity-route-gap-interface.md +0 -82
- package/sdd/99_toolchain/01_automation/ui-parity/runtime/playwright-runtime.mjs +0 -16
- package/sdd/99_toolchain/01_automation/ui-parity/runtime/static-runtime.mjs +0 -6
- package/sdd/99_toolchain/02_policies/.gitkeep +0 -1
- package/sdd/99_toolchain/02_policies/build-ast-governance-policy.md +0 -22
- package/sdd/99_toolchain/02_policies/compose-runtime-baseline-policy.md +0 -24
- package/sdd/99_toolchain/02_policies/convention-storage-policy.md +0 -26
- package/sdd/99_toolchain/02_policies/main-push-before-dev-deploy-policy.md +0 -27
- package/sdd/99_toolchain/02_policies/regression-verification-policy.md +0 -22
- package/sdd/99_toolchain/03_templates/.gitkeep +0 -1
- package/sdd/99_toolchain/03_templates/asset_recipe_manifest.example.py +0 -38
- package/sdd/99_toolchain/03_templates/generated_assets/README.md +0 -11
- package/sdd/99_toolchain/03_templates/generated_assets/example-brand-lockup.svg +0 -3
- package/sdd/99_toolchain/03_templates/generated_assets/example-brand-mark.svg +0 -3
- package/sdd/99_toolchain/03_templates/generated_assets/example-brand-wordmark.svg +0 -3
- package/sdd/99_toolchain/03_templates/playwright_exactness_manifest.example.py +0 -21
- package/sdd/99_toolchain/README.md +0 -23
- package/sdd/README.md +0 -21
package/lib/github.mjs
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import process from "node:process";
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
|
|
4
|
+
function ghToken() {
|
|
5
|
+
const envToken =
|
|
6
|
+
process.env.GH_TOKEN || process.env.GITHUB_TOKEN || process.env.AGENTIC_GITHUB_TOKEN || "";
|
|
7
|
+
if (envToken) {
|
|
8
|
+
return envToken;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const result = spawnSync("gh", ["auth", "token"], {
|
|
12
|
+
encoding: "utf-8",
|
|
13
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
14
|
+
});
|
|
15
|
+
if (result.status === 0) {
|
|
16
|
+
return (result.stdout || "").trim();
|
|
17
|
+
}
|
|
18
|
+
return "";
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function githubRequest(url, { method = "GET", body } = {}) {
|
|
22
|
+
const token = ghToken();
|
|
23
|
+
if (!token) {
|
|
24
|
+
throw new Error("GitHub authentication is required. Set GH_TOKEN, GITHUB_TOKEN, or AGENTIC_GITHUB_TOKEN.");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const response = await fetch(url, {
|
|
28
|
+
method,
|
|
29
|
+
headers: {
|
|
30
|
+
Accept: "application/vnd.github+json",
|
|
31
|
+
"Content-Type": "application/json",
|
|
32
|
+
"User-Agent": "agentic-dev",
|
|
33
|
+
Authorization: `Bearer ${token}`,
|
|
34
|
+
},
|
|
35
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
if (!response.ok) {
|
|
39
|
+
const payload = await response.text();
|
|
40
|
+
throw new Error(`GitHub API request failed (${response.status}): ${payload}`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (response.status === 204) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
return response.json();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function githubGraphql(query, variables = {}) {
|
|
50
|
+
const token = ghToken();
|
|
51
|
+
if (!token) {
|
|
52
|
+
throw new Error("GitHub authentication is required. Set GH_TOKEN, GITHUB_TOKEN, or AGENTIC_GITHUB_TOKEN.");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const response = await fetch("https://api.github.com/graphql", {
|
|
56
|
+
method: "POST",
|
|
57
|
+
headers: {
|
|
58
|
+
Accept: "application/vnd.github+json",
|
|
59
|
+
"Content-Type": "application/json",
|
|
60
|
+
"User-Agent": "agentic-dev",
|
|
61
|
+
Authorization: `Bearer ${token}`,
|
|
62
|
+
},
|
|
63
|
+
body: JSON.stringify({ query, variables }),
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const payload = await response.json();
|
|
67
|
+
if (!response.ok || payload.errors) {
|
|
68
|
+
throw new Error(`GitHub GraphQL request failed: ${JSON.stringify(payload.errors || payload)}`);
|
|
69
|
+
}
|
|
70
|
+
return payload.data;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function normalizeRepoInput(input, { fallbackOwner = "say828", fallbackName = "agentic-service" } = {}) {
|
|
74
|
+
const normalized = (input || "").trim();
|
|
75
|
+
if (!normalized) {
|
|
76
|
+
return {
|
|
77
|
+
owner: fallbackOwner,
|
|
78
|
+
name: fallbackName,
|
|
79
|
+
slug: `${fallbackOwner}/${fallbackName}`,
|
|
80
|
+
cloneUrl: `https://github.com/${fallbackOwner}/${fallbackName}.git`,
|
|
81
|
+
htmlUrl: `https://github.com/${fallbackOwner}/${fallbackName}`,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const withoutGit = normalized.replace(/\.git$/, "");
|
|
86
|
+
const httpsMatch = withoutGit.match(/^https:\/\/github\.com\/([^/]+)\/([^/]+)$/);
|
|
87
|
+
if (httpsMatch) {
|
|
88
|
+
const [, owner, name] = httpsMatch;
|
|
89
|
+
return {
|
|
90
|
+
owner,
|
|
91
|
+
name,
|
|
92
|
+
slug: `${owner}/${name}`,
|
|
93
|
+
cloneUrl: `https://github.com/${owner}/${name}.git`,
|
|
94
|
+
htmlUrl: `https://github.com/${owner}/${name}`,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const slugMatch = withoutGit.match(/^([^/]+)\/([^/]+)$/);
|
|
99
|
+
if (slugMatch) {
|
|
100
|
+
const [, owner, name] = slugMatch;
|
|
101
|
+
return {
|
|
102
|
+
owner,
|
|
103
|
+
name,
|
|
104
|
+
slug: `${owner}/${name}`,
|
|
105
|
+
cloneUrl: `https://github.com/${owner}/${name}.git`,
|
|
106
|
+
htmlUrl: `https://github.com/${owner}/${name}`,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
owner: fallbackOwner,
|
|
112
|
+
name: withoutGit,
|
|
113
|
+
slug: `${fallbackOwner}/${withoutGit}`,
|
|
114
|
+
cloneUrl: `https://github.com/${fallbackOwner}/${withoutGit}.git`,
|
|
115
|
+
htmlUrl: `https://github.com/${fallbackOwner}/${withoutGit}`,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export async function getAuthenticatedViewer() {
|
|
120
|
+
const data = await githubGraphql(`
|
|
121
|
+
query ViewerIdentity {
|
|
122
|
+
viewer {
|
|
123
|
+
login
|
|
124
|
+
id
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
`);
|
|
128
|
+
return data.viewer;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export async function ensureGitHubRepository(repoInput, { visibility = "private" } = {}) {
|
|
132
|
+
const viewer = await getAuthenticatedViewer();
|
|
133
|
+
const target = normalizeRepoInput(repoInput, {
|
|
134
|
+
fallbackOwner: viewer.login,
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
try {
|
|
138
|
+
const existing = await githubRequest(`https://api.github.com/repos/${target.owner}/${target.name}`);
|
|
139
|
+
return {
|
|
140
|
+
owner: existing.owner.login,
|
|
141
|
+
name: existing.name,
|
|
142
|
+
slug: existing.full_name,
|
|
143
|
+
cloneUrl: existing.clone_url,
|
|
144
|
+
htmlUrl: existing.html_url,
|
|
145
|
+
created: false,
|
|
146
|
+
};
|
|
147
|
+
} catch (error) {
|
|
148
|
+
const message = String(error.message || error);
|
|
149
|
+
if (!message.includes("(404)")) {
|
|
150
|
+
throw error;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (target.owner !== viewer.login) {
|
|
155
|
+
throw new Error(
|
|
156
|
+
`Repository ${target.slug} does not exist. Automatic creation currently supports only the authenticated user namespace (${viewer.login}).`,
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const created = await githubRequest("https://api.github.com/user/repos", {
|
|
161
|
+
method: "POST",
|
|
162
|
+
body: {
|
|
163
|
+
name: target.name,
|
|
164
|
+
private: visibility !== "public",
|
|
165
|
+
auto_init: false,
|
|
166
|
+
},
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
return {
|
|
170
|
+
owner: created.owner.login,
|
|
171
|
+
name: created.name,
|
|
172
|
+
slug: created.full_name,
|
|
173
|
+
cloneUrl: created.clone_url,
|
|
174
|
+
htmlUrl: created.html_url,
|
|
175
|
+
created: true,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export async function ensureGitHubProject({ ownerLogin, title, mode = "create-if-missing" }) {
|
|
180
|
+
const data = await githubGraphql(
|
|
181
|
+
`
|
|
182
|
+
query OwnerProjects($login: String!) {
|
|
183
|
+
user(login: $login) {
|
|
184
|
+
id
|
|
185
|
+
projectsV2(first: 50) {
|
|
186
|
+
nodes {
|
|
187
|
+
id
|
|
188
|
+
title
|
|
189
|
+
number
|
|
190
|
+
url
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
`,
|
|
196
|
+
{ login: ownerLogin },
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
const owner = data.user;
|
|
200
|
+
if (!owner) {
|
|
201
|
+
throw new Error(`Unable to load GitHub projects for ${ownerLogin}.`);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const existing = owner.projectsV2.nodes.find((project) => project.title === title);
|
|
205
|
+
if (existing) {
|
|
206
|
+
return {
|
|
207
|
+
id: existing.id,
|
|
208
|
+
title: existing.title,
|
|
209
|
+
number: existing.number,
|
|
210
|
+
url: existing.url,
|
|
211
|
+
created: false,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (mode !== "create-if-missing") {
|
|
216
|
+
throw new Error(`GitHub project not found: ${title}`);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const created = await githubGraphql(
|
|
220
|
+
`
|
|
221
|
+
mutation CreateProject($ownerId: ID!, $title: String!) {
|
|
222
|
+
createProjectV2(input: { ownerId: $ownerId, title: $title }) {
|
|
223
|
+
projectV2 {
|
|
224
|
+
id
|
|
225
|
+
title
|
|
226
|
+
number
|
|
227
|
+
url
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
`,
|
|
232
|
+
{
|
|
233
|
+
ownerId: owner.id,
|
|
234
|
+
title,
|
|
235
|
+
},
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
const project = created.createProjectV2.projectV2;
|
|
239
|
+
return {
|
|
240
|
+
id: project.id,
|
|
241
|
+
title: project.title,
|
|
242
|
+
number: project.number,
|
|
243
|
+
url: project.url,
|
|
244
|
+
created: true,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
const SPECIALIZED_AGENTS = [
|
|
5
|
+
{ id: "architecture", description: "Drive boundaries, system shape, and migration decisions." },
|
|
6
|
+
{ id: "specs", description: "Translate SDD planning artifacts into actionable task structure." },
|
|
7
|
+
{ id: "runtime", description: "Own application/runtime implementation work." },
|
|
8
|
+
{ id: "ui", description: "Own screen and UI delivery tasks." },
|
|
9
|
+
{ id: "api", description: "Own contract and API work." },
|
|
10
|
+
{ id: "quality", description: "Run verification and regression closure." },
|
|
11
|
+
{ id: "gitops", description: "Own GitHub Projects, workflow automation, and delivery closure." },
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
function repoRootDir() {
|
|
15
|
+
return path.resolve(new URL("..", import.meta.url).pathname);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function copyRecursive(sourceRoot, destinationRoot) {
|
|
19
|
+
fs.mkdirSync(destinationRoot, { recursive: true });
|
|
20
|
+
for (const entry of fs.readdirSync(sourceRoot, { withFileTypes: true })) {
|
|
21
|
+
const sourcePath = path.join(sourceRoot, entry.name);
|
|
22
|
+
const destinationPath = path.join(destinationRoot, entry.name);
|
|
23
|
+
if (entry.isDirectory()) {
|
|
24
|
+
copyRecursive(sourcePath, destinationPath);
|
|
25
|
+
} else {
|
|
26
|
+
fs.copyFileSync(sourcePath, destinationPath);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function writeFile(filePath, content) {
|
|
32
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
33
|
+
fs.writeFileSync(filePath, content);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function workflowYaml() {
|
|
37
|
+
return `name: Agentic Orchestration
|
|
38
|
+
|
|
39
|
+
on:
|
|
40
|
+
push:
|
|
41
|
+
paths:
|
|
42
|
+
- "sdd/02_plan/**"
|
|
43
|
+
- ".agentic-dev/orchestration.json"
|
|
44
|
+
- ".agentic-dev/runtime/**"
|
|
45
|
+
workflow_dispatch:
|
|
46
|
+
|
|
47
|
+
jobs:
|
|
48
|
+
orchestrate:
|
|
49
|
+
runs-on: ubuntu-latest
|
|
50
|
+
permissions:
|
|
51
|
+
contents: read
|
|
52
|
+
issues: write
|
|
53
|
+
repository-projects: write
|
|
54
|
+
steps:
|
|
55
|
+
- name: Checkout
|
|
56
|
+
uses: actions/checkout@v4
|
|
57
|
+
|
|
58
|
+
- name: Setup Node
|
|
59
|
+
uses: actions/setup-node@v4
|
|
60
|
+
with:
|
|
61
|
+
node-version: "20"
|
|
62
|
+
|
|
63
|
+
- name: Build task IR from SDD planning
|
|
64
|
+
run: node .agentic-dev/runtime/sdd_to_ir.mjs
|
|
65
|
+
|
|
66
|
+
- name: Sync GitHub project tasks
|
|
67
|
+
env:
|
|
68
|
+
GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }}
|
|
69
|
+
run: node .agentic-dev/runtime/sync_project_tasks.mjs
|
|
70
|
+
|
|
71
|
+
- name: Plan multi-agent queue
|
|
72
|
+
env:
|
|
73
|
+
GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }}
|
|
74
|
+
run: node .agentic-dev/runtime/run_multi_agent_queue.mjs
|
|
75
|
+
|
|
76
|
+
- name: Close completed tasks
|
|
77
|
+
env:
|
|
78
|
+
GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }}
|
|
79
|
+
run: node .agentic-dev/runtime/close_completed_tasks.mjs
|
|
80
|
+
`;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function sddToIrScript() {
|
|
84
|
+
return `#!/usr/bin/env node
|
|
85
|
+
import fs from "node:fs";
|
|
86
|
+
import path from "node:path";
|
|
87
|
+
|
|
88
|
+
function walk(root) {
|
|
89
|
+
if (!fs.existsSync(root)) return [];
|
|
90
|
+
const files = [];
|
|
91
|
+
for (const entry of fs.readdirSync(root, { withFileTypes: true })) {
|
|
92
|
+
const full = path.join(root, entry.name);
|
|
93
|
+
if (entry.isDirectory()) files.push(...walk(full));
|
|
94
|
+
else if (entry.name.endsWith(".md")) files.push(full);
|
|
95
|
+
}
|
|
96
|
+
return files;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function parseChecklist(markdown, source) {
|
|
100
|
+
const tasks = [];
|
|
101
|
+
const lines = markdown.split(/\\r?\\n/);
|
|
102
|
+
for (const [index, line] of lines.entries()) {
|
|
103
|
+
const match = line.match(/^- \\[( |x)\\] (.+)$/);
|
|
104
|
+
if (!match) continue;
|
|
105
|
+
const status = match[1] === "x" ? "closed" : "open";
|
|
106
|
+
const title = match[2].trim();
|
|
107
|
+
const id = \`\${path.basename(source, ".md")}:\${index + 1}\`;
|
|
108
|
+
tasks.push({ id, title, status, source });
|
|
109
|
+
}
|
|
110
|
+
return tasks;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const planRoot = path.resolve("sdd/02_plan");
|
|
114
|
+
const files = walk(planRoot);
|
|
115
|
+
const tasks = files.flatMap((file) => parseChecklist(fs.readFileSync(file, "utf-8"), path.relative(process.cwd(), file)));
|
|
116
|
+
const outputDir = path.resolve(".agentic-dev/generated");
|
|
117
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
118
|
+
const outputPath = path.join(outputDir, "task-ir.json");
|
|
119
|
+
fs.writeFileSync(outputPath, JSON.stringify({ generated_at: new Date().toISOString(), tasks }, null, 2) + "\\n");
|
|
120
|
+
console.log(\`task_ir=\${outputPath}\`);
|
|
121
|
+
console.log(\`tasks=\${tasks.length}\`);
|
|
122
|
+
`;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function syncProjectTasksScript() {
|
|
126
|
+
return `#!/usr/bin/env node
|
|
127
|
+
import fs from "node:fs";
|
|
128
|
+
import { execFileSync } from "node:child_process";
|
|
129
|
+
|
|
130
|
+
const orchestration = JSON.parse(fs.readFileSync(".agentic-dev/orchestration.json", "utf-8"));
|
|
131
|
+
const taskIr = JSON.parse(fs.readFileSync(".agentic-dev/generated/task-ir.json", "utf-8"));
|
|
132
|
+
|
|
133
|
+
function ghJson(args) {
|
|
134
|
+
return JSON.parse(execFileSync("gh", args, { encoding: "utf-8" }));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function gh(args) {
|
|
138
|
+
return execFileSync("gh", args, { encoding: "utf-8" }).trim();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const existingIssues = ghJson(["issue", "list", "--repo", orchestration.github.repository.slug, "--state", "all", "--limit", "200", "--json", "number,title,state"]);
|
|
142
|
+
|
|
143
|
+
for (const task of taskIr.tasks) {
|
|
144
|
+
const issueTitle = \`[agentic-task] \${task.id} \${task.title}\`;
|
|
145
|
+
const found = existingIssues.find((issue) => issue.title.startsWith(\`[agentic-task] \${task.id} \`));
|
|
146
|
+
if (!found && task.status === "open") {
|
|
147
|
+
gh(["issue", "create", "--repo", orchestration.github.repository.slug, "--title", issueTitle, "--body", \`SDD source: \${task.source}\\n\\nManaged by agentic-dev orchestration.\`, "--label", "agentic-task"]);
|
|
148
|
+
}
|
|
149
|
+
if (found && task.status === "closed" && found.state !== "CLOSED") {
|
|
150
|
+
gh(["issue", "close", String(found.number), "--repo", orchestration.github.repository.slug, "--comment", "Closed from SDD task IR."]);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
console.log(\`synced_tasks=\${taskIr.tasks.length}\`);
|
|
155
|
+
`;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function runQueueScript() {
|
|
159
|
+
return `#!/usr/bin/env node
|
|
160
|
+
import fs from "node:fs";
|
|
161
|
+
|
|
162
|
+
const orchestration = JSON.parse(fs.readFileSync(".agentic-dev/orchestration.json", "utf-8"));
|
|
163
|
+
const taskIr = JSON.parse(fs.readFileSync(".agentic-dev/generated/task-ir.json", "utf-8"));
|
|
164
|
+
|
|
165
|
+
function classifyAgent(title) {
|
|
166
|
+
const lower = title.toLowerCase();
|
|
167
|
+
if (lower.includes("api") || lower.includes("contract")) return "api";
|
|
168
|
+
if (lower.includes("screen") || lower.includes("ui")) return "ui";
|
|
169
|
+
if (lower.includes("verify") || lower.includes("test") || lower.includes("proof")) return "quality";
|
|
170
|
+
if (lower.includes("workflow") || lower.includes("project") || lower.includes("github")) return "gitops";
|
|
171
|
+
if (lower.includes("arch") || lower.includes("boundary") || lower.includes("structure")) return "architecture";
|
|
172
|
+
return "runtime";
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const openTasks = taskIr.tasks.filter((task) => task.status === "open");
|
|
176
|
+
const queue = openTasks.map((task) => ({
|
|
177
|
+
...task,
|
|
178
|
+
assigned_agent: classifyAgent(task.title),
|
|
179
|
+
}));
|
|
180
|
+
|
|
181
|
+
const outputPath = ".agentic-dev/generated/agent-queue.json";
|
|
182
|
+
fs.writeFileSync(outputPath, JSON.stringify({ providers: orchestration.providers, queue }, null, 2) + "\\n");
|
|
183
|
+
console.log(\`agent_queue=\${outputPath}\`);
|
|
184
|
+
console.log(\`queued=\${queue.length}\`);
|
|
185
|
+
`;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function closeTasksScript() {
|
|
189
|
+
return `#!/usr/bin/env node
|
|
190
|
+
import fs from "node:fs";
|
|
191
|
+
import { execFileSync } from "node:child_process";
|
|
192
|
+
|
|
193
|
+
const orchestration = JSON.parse(fs.readFileSync(".agentic-dev/orchestration.json", "utf-8"));
|
|
194
|
+
const taskIr = JSON.parse(fs.readFileSync(".agentic-dev/generated/task-ir.json", "utf-8"));
|
|
195
|
+
|
|
196
|
+
const closedIds = new Set(taskIr.tasks.filter((task) => task.status === "closed").map((task) => task.id));
|
|
197
|
+
const issues = JSON.parse(execFileSync("gh", ["issue", "list", "--repo", orchestration.github.repository.slug, "--state", "open", "--limit", "200", "--json", "number,title"], { encoding: "utf-8" }));
|
|
198
|
+
|
|
199
|
+
for (const issue of issues) {
|
|
200
|
+
const match = issue.title.match(/^\\[agentic-task\\] ([^ ]+) /);
|
|
201
|
+
if (!match) continue;
|
|
202
|
+
if (!closedIds.has(match[1])) continue;
|
|
203
|
+
execFileSync("gh", ["issue", "close", String(issue.number), "--repo", orchestration.github.repository.slug, "--comment", "Closed from SDD orchestration close pass."], { encoding: "utf-8" });
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
console.log(\`closed_candidates=\${closedIds.size}\`);
|
|
207
|
+
`;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export function installSharedAgentAssets(destinationRoot) {
|
|
211
|
+
const root = repoRootDir();
|
|
212
|
+
copyRecursive(path.join(root, ".agent"), path.join(destinationRoot, ".agent"));
|
|
213
|
+
copyRecursive(path.join(root, ".claude"), path.join(destinationRoot, ".claude"));
|
|
214
|
+
copyRecursive(path.join(root, ".codex"), path.join(destinationRoot, ".codex"));
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export function installOrchestrationAssets(destinationRoot) {
|
|
218
|
+
writeFile(path.join(destinationRoot, ".github/workflows/agentic-orchestration.yml"), workflowYaml());
|
|
219
|
+
writeFile(path.join(destinationRoot, ".agentic-dev/runtime/sdd_to_ir.mjs"), sddToIrScript());
|
|
220
|
+
writeFile(path.join(destinationRoot, ".agentic-dev/runtime/sync_project_tasks.mjs"), syncProjectTasksScript());
|
|
221
|
+
writeFile(path.join(destinationRoot, ".agentic-dev/runtime/run_multi_agent_queue.mjs"), runQueueScript());
|
|
222
|
+
writeFile(path.join(destinationRoot, ".agentic-dev/runtime/close_completed_tasks.mjs"), closeTasksScript());
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export function buildOrchestrationConfig(setupSelections = {}) {
|
|
226
|
+
return {
|
|
227
|
+
project_name: setupSelections.projectName || "",
|
|
228
|
+
specialized_agents: SPECIALIZED_AGENTS,
|
|
229
|
+
providers: Array.isArray(setupSelections.providerProfiles) ? setupSelections.providerProfiles : [],
|
|
230
|
+
github: {
|
|
231
|
+
repository: setupSelections.githubRepository || {},
|
|
232
|
+
project: setupSelections.githubProject || {},
|
|
233
|
+
project_mode: setupSelections.githubProjectMode || "create-if-missing",
|
|
234
|
+
},
|
|
235
|
+
workflow: {
|
|
236
|
+
ir_output: ".agentic-dev/generated/task-ir.json",
|
|
237
|
+
queue_output: ".agentic-dev/generated/agent-queue.json",
|
|
238
|
+
workflow_file: ".github/workflows/agentic-orchestration.yml",
|
|
239
|
+
},
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export function writeOrchestrationConfig(destinationRoot, setupSelections = {}) {
|
|
244
|
+
const config = buildOrchestrationConfig(setupSelections);
|
|
245
|
+
writeFile(
|
|
246
|
+
path.join(destinationRoot, ".agentic-dev/orchestration.json"),
|
|
247
|
+
`${JSON.stringify(config, null, 2)}\n`,
|
|
248
|
+
);
|
|
249
|
+
}
|
package/lib/scaffold.mjs
CHANGED
|
@@ -3,6 +3,12 @@ import os from "node:os";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import process from "node:process";
|
|
5
5
|
import { spawnSync } from "node:child_process";
|
|
6
|
+
import { ensureGitHubProject, ensureGitHubRepository } from "./github.mjs";
|
|
7
|
+
import {
|
|
8
|
+
installOrchestrationAssets,
|
|
9
|
+
installSharedAgentAssets,
|
|
10
|
+
writeOrchestrationConfig,
|
|
11
|
+
} from "./orchestration-assets.mjs";
|
|
6
12
|
|
|
7
13
|
export const DEFAULT_TEMPLATE_OWNER = "say828";
|
|
8
14
|
|
|
@@ -64,6 +70,10 @@ export function parseArgs(argv) {
|
|
|
64
70
|
owner: DEFAULT_TEMPLATE_OWNER,
|
|
65
71
|
appMode: "",
|
|
66
72
|
aiProviders: [],
|
|
73
|
+
providerProfiles: [],
|
|
74
|
+
githubRepositoryInput: "",
|
|
75
|
+
githubProjectMode: "",
|
|
76
|
+
githubProjectTitle: "",
|
|
67
77
|
githubAuthMode: "",
|
|
68
78
|
githubPat: "",
|
|
69
79
|
force: false,
|
|
@@ -100,6 +110,23 @@ export function parseArgs(argv) {
|
|
|
100
110
|
.filter(Boolean);
|
|
101
111
|
break;
|
|
102
112
|
}
|
|
113
|
+
case "--provider-profiles": {
|
|
114
|
+
const value = args.shift() ?? "";
|
|
115
|
+
options.providerProfiles = value
|
|
116
|
+
.split(",")
|
|
117
|
+
.map((entry) => entry.trim())
|
|
118
|
+
.filter(Boolean);
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
case "--github-repo":
|
|
122
|
+
options.githubRepositoryInput = args.shift() ?? "";
|
|
123
|
+
break;
|
|
124
|
+
case "--github-project-mode":
|
|
125
|
+
options.githubProjectMode = args.shift() ?? "";
|
|
126
|
+
break;
|
|
127
|
+
case "--github-project-title":
|
|
128
|
+
options.githubProjectTitle = args.shift() ?? "";
|
|
129
|
+
break;
|
|
103
130
|
case "--github-auth":
|
|
104
131
|
options.githubAuthMode = args.shift() ?? "";
|
|
105
132
|
break;
|
|
@@ -143,6 +170,7 @@ export function usage() {
|
|
|
143
170
|
" npx agentic-dev init my-app",
|
|
144
171
|
" npx agentic-dev init my-app --template template-web --yes",
|
|
145
172
|
" npx agentic-dev init my-app --template template-web --project-name my-app --providers codex,claude",
|
|
173
|
+
" npx agentic-dev init my-app --github-repo say828/my-app --github-project-title 'My App Delivery' --provider-profiles codex-subscription,openai-api",
|
|
146
174
|
"",
|
|
147
175
|
"Options:",
|
|
148
176
|
" --template, -t Template repo name or suffix to use",
|
|
@@ -150,6 +178,10 @@ export function usage() {
|
|
|
150
178
|
" --owner GitHub owner for template-* repos (default: say828)",
|
|
151
179
|
" --app-mode App mode: fullstack, frontend, backend",
|
|
152
180
|
" --providers Comma-separated AI providers: codex, claude, ollama",
|
|
181
|
+
" --provider-profiles Provider/runtime list such as codex-subscription, claude-api, openai-api",
|
|
182
|
+
" --github-repo GitHub repo slug or URL to create/use",
|
|
183
|
+
" --github-project-mode create-if-missing or use-existing",
|
|
184
|
+
" --github-project-title GitHub Project title to bind orchestration to",
|
|
153
185
|
" --github-auth GitHub auth mode: public, env, pat",
|
|
154
186
|
" --github-pat GitHub PAT for this run only",
|
|
155
187
|
" --force Allow scaffolding into a non-empty directory",
|
|
@@ -174,6 +206,33 @@ export function ensureTargetDir(targetDir, { force = false } = {}) {
|
|
|
174
206
|
return resolved;
|
|
175
207
|
}
|
|
176
208
|
|
|
209
|
+
function initializeGitRepository(destinationRoot, githubRepository) {
|
|
210
|
+
const gitDir = path.join(destinationRoot, ".git");
|
|
211
|
+
if (!fs.existsSync(gitDir)) {
|
|
212
|
+
runCommand("git", ["init", "-b", "main"], {
|
|
213
|
+
cwd: destinationRoot,
|
|
214
|
+
label: "Initializing git repository",
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const remotes = spawnSync("git", ["remote"], {
|
|
219
|
+
cwd: destinationRoot,
|
|
220
|
+
encoding: "utf-8",
|
|
221
|
+
});
|
|
222
|
+
const existingRemotes = new Set((remotes.stdout || "").split(/\r?\n/).filter(Boolean));
|
|
223
|
+
if (existingRemotes.has("origin")) {
|
|
224
|
+
runCommand("git", ["remote", "set-url", "origin", githubRepository.cloneUrl], {
|
|
225
|
+
cwd: destinationRoot,
|
|
226
|
+
label: "Updating origin remote",
|
|
227
|
+
});
|
|
228
|
+
} else {
|
|
229
|
+
runCommand("git", ["remote", "add", "origin", githubRepository.cloneUrl], {
|
|
230
|
+
cwd: destinationRoot,
|
|
231
|
+
label: "Adding origin remote",
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
177
236
|
export async function fetchTemplateRepos({ owner = DEFAULT_TEMPLATE_OWNER } = {}) {
|
|
178
237
|
const repos = [];
|
|
179
238
|
let page = 1;
|
|
@@ -383,7 +442,12 @@ function writeSetupConfig(destinationRoot, setupSelections = {}) {
|
|
|
383
442
|
template_owner: setupSelections.owner || DEFAULT_TEMPLATE_OWNER,
|
|
384
443
|
app_mode: setupSelections.appMode || "fullstack",
|
|
385
444
|
ai_providers: Array.isArray(setupSelections.aiProviders) ? setupSelections.aiProviders : [],
|
|
445
|
+
provider_profiles: Array.isArray(setupSelections.providerProfiles)
|
|
446
|
+
? setupSelections.providerProfiles
|
|
447
|
+
: [],
|
|
386
448
|
github_auth_mode: setupSelections.githubAuthMode || "public",
|
|
449
|
+
github_repository: setupSelections.githubRepository || null,
|
|
450
|
+
github_project: setupSelections.githubProject || null,
|
|
387
451
|
github_pat_supplied: Boolean(setupSelections.githubPat),
|
|
388
452
|
skip_bootstrap: Boolean(setupSelections.skipBootstrap),
|
|
389
453
|
};
|
|
@@ -492,8 +556,11 @@ export function installTemplateRepo({
|
|
|
492
556
|
skipBootstrap = false,
|
|
493
557
|
}) {
|
|
494
558
|
scaffoldFromTemplateRepo({ destinationRoot, templateRepo });
|
|
559
|
+
installSharedAgentAssets(destinationRoot);
|
|
560
|
+
installOrchestrationAssets(destinationRoot);
|
|
495
561
|
applyProviderSelections(destinationRoot, setupSelections.aiProviders);
|
|
496
562
|
applyProjectMetadata(destinationRoot, setupSelections);
|
|
563
|
+
writeOrchestrationConfig(destinationRoot, setupSelections);
|
|
497
564
|
writeSetupConfig(destinationRoot, {
|
|
498
565
|
...setupSelections,
|
|
499
566
|
templateRepo: templateRepo.name,
|
|
@@ -518,3 +585,25 @@ export function installTemplateRepo({
|
|
|
518
585
|
nextSteps: effectiveSkipBootstrap ? [bootstrapHint] : [],
|
|
519
586
|
};
|
|
520
587
|
}
|
|
588
|
+
|
|
589
|
+
export async function resolveGitHubOrchestration(setupSelections = {}) {
|
|
590
|
+
const repository = await ensureGitHubRepository(
|
|
591
|
+
setupSelections.githubRepositoryInput || setupSelections.projectName,
|
|
592
|
+
);
|
|
593
|
+
const project = await ensureGitHubProject({
|
|
594
|
+
ownerLogin: repository.owner,
|
|
595
|
+
title: setupSelections.githubProjectTitle || `${setupSelections.projectName} Delivery`,
|
|
596
|
+
mode: setupSelections.githubProjectMode || "create-if-missing",
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
return {
|
|
600
|
+
githubRepository: repository,
|
|
601
|
+
githubProject: project,
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
export function finalizeRepositoryGit(destinationRoot, setupSelections = {}) {
|
|
606
|
+
if (setupSelections.githubRepository) {
|
|
607
|
+
initializeGitRepository(destinationRoot, setupSelections.githubRepository);
|
|
608
|
+
}
|
|
609
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentic-dev",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.2.13",
|
|
4
|
+
"description": "GitHub Project based installer and multi-agent orchestration CLI for public say828/template-* repos.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "pnpm@10.32.0",
|
|
7
7
|
"bin": {
|
|
@@ -12,30 +12,19 @@
|
|
|
12
12
|
".claude",
|
|
13
13
|
".codex",
|
|
14
14
|
"bin",
|
|
15
|
-
"client",
|
|
16
|
-
"infra",
|
|
17
15
|
"lib",
|
|
18
|
-
"
|
|
19
|
-
"sdd",
|
|
20
|
-
"server",
|
|
21
|
-
".dockerignore",
|
|
22
|
-
".env.example",
|
|
23
|
-
".gitignore",
|
|
24
|
-
"AGENTS.md",
|
|
25
|
-
"README.md",
|
|
26
|
-
"SDD_SKILL.md",
|
|
27
|
-
"compose.yml",
|
|
28
|
-
"pnpm-workspace.yaml"
|
|
16
|
+
"README.md"
|
|
29
17
|
],
|
|
30
18
|
"scripts": {
|
|
31
|
-
"smoke:init": "node ./bin/agentic-dev.mjs init ./.tmp-agentic-smoke --template template-
|
|
19
|
+
"smoke:init": "node ./bin/agentic-dev.mjs init ./.tmp-agentic-smoke --template template-web --app-mode backend --providers codex --yes --force"
|
|
32
20
|
},
|
|
33
21
|
"keywords": [
|
|
34
22
|
"agentic",
|
|
35
|
-
"template",
|
|
36
23
|
"scaffold",
|
|
37
|
-
"
|
|
38
|
-
"
|
|
24
|
+
"cli",
|
|
25
|
+
"github",
|
|
26
|
+
"template",
|
|
27
|
+
"orchestration"
|
|
39
28
|
],
|
|
40
29
|
"license": "UNLICENSED"
|
|
41
30
|
}
|