@tailor-platform/erp-kit 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/README.md +158 -62
- package/dist/cli.js +1010 -270
- package/package.json +11 -8
- package/schemas/module/command.yml +1 -0
- package/schemas/module/model.yml +14 -0
- package/schemas/module/query.yml +53 -0
- package/skills/{app-compose-1-requirement-analysis → erp-kit-app-1-requirements}/SKILL.md +2 -2
- package/skills/{app-compose-2-requirements-breakdown → erp-kit-app-2-breakdown}/SKILL.md +3 -3
- package/skills/{app-compose-3-doc-review → erp-kit-app-3-doc-review}/SKILL.md +2 -2
- package/skills/{app-compose-4-design-mock → erp-kit-app-4-design}/SKILL.md +3 -3
- package/skills/{app-compose-5-design-mock-review → erp-kit-app-5-design-review}/SKILL.md +4 -4
- package/skills/{app-compose-6-implementation-spec → erp-kit-app-6-impl-spec}/SKILL.md +3 -3
- package/skills/{mock-scenario → erp-kit-mock-scenario}/SKILL.md +1 -1
- package/skills/{1-module-docs → erp-kit-module-1-docs}/SKILL.md +2 -2
- package/skills/{2-module-feature-breakdown → erp-kit-module-2-feature-breakdown}/SKILL.md +13 -9
- package/skills/erp-kit-module-2-feature-breakdown/references/naming.md +59 -0
- package/skills/{3-module-doc-review → erp-kit-module-3-doc-review}/SKILL.md +83 -25
- package/skills/erp-kit-module-4-tdd/SKILL.md +94 -0
- package/skills/erp-kit-module-4-tdd/references/cross-module-dependency.md +133 -0
- package/skills/{4-module-tdd-implementation → erp-kit-module-4-tdd}/references/db-relations.md +5 -1
- package/skills/{4-module-tdd-implementation → erp-kit-module-4-tdd}/references/exports.md +1 -1
- package/skills/erp-kit-module-4-tdd/references/generated-code.md +32 -0
- package/skills/{5-module-implementation-review → erp-kit-module-5-impl-review}/SKILL.md +46 -44
- package/skills/erp-kit-module-5-impl-review/references/commands.md +62 -0
- package/skills/erp-kit-module-5-impl-review/references/errors.md +10 -0
- package/skills/{5-module-implementation-review → erp-kit-module-5-impl-review}/references/testing.md +1 -1
- package/skills/erp-kit-module-shared/SKILL.md +16 -0
- package/skills/erp-kit-module-shared/references/commands.md +203 -0
- package/skills/erp-kit-module-shared/references/errors.md +35 -0
- package/skills/erp-kit-module-shared/references/queries.md +168 -0
- package/skills/erp-kit-module-shared/references/structure.md +36 -0
- package/skills/{3-module-doc-review → erp-kit-module-shared}/references/testing.md +4 -3
- package/skills/erp-kit-update/SKILL.md +64 -0
- package/src/cli.doc.test.ts +65 -0
- package/src/cli.ts +3 -117
- package/src/commands/app/index.ts +74 -0
- package/src/commands/check.test.ts +3 -2
- package/src/commands/check.ts +3 -2
- package/src/commands/index.ts +73 -0
- package/src/commands/init.test.ts +22 -5
- package/src/commands/init.ts +25 -16
- package/src/commands/license.ts +193 -0
- package/src/commands/mock/index.ts +2 -2
- package/src/commands/mock/start.ts +1 -1
- package/src/commands/mock/validate.test.ts +1 -1
- package/src/commands/module/generate.ts +35 -0
- package/src/commands/module/index.ts +87 -0
- package/src/commands/module/list.test.ts +57 -0
- package/src/commands/module/list.ts +64 -0
- package/src/commands/scaffold-templates.ts +65 -0
- package/src/commands/scaffold.test.ts +97 -2
- package/src/commands/scaffold.ts +24 -3
- package/src/commands/sync-check.test.ts +88 -1
- package/src/commands/sync-check.ts +41 -2
- package/src/generator/generate-code.test.ts +200 -0
- package/src/generator/generate-code.ts +260 -0
- package/src/generator/parse-command-doc.test.ts +159 -0
- package/src/generator/parse-command-doc.ts +116 -0
- package/src/integration.test.ts +6 -8
- package/src/module.ts +10 -9
- package/src/modules/item-management/README.md +38 -0
- package/src/modules/item-management/command/activateItem.generated.ts +6 -0
- package/src/modules/item-management/command/activateItem.test.ts +76 -0
- package/src/modules/item-management/command/activateItem.ts +42 -0
- package/src/modules/item-management/command/assignItemToTaxonomy.generated.ts +6 -0
- package/src/modules/item-management/command/assignItemToTaxonomy.test.ts +88 -0
- package/src/modules/item-management/command/assignItemToTaxonomy.ts +63 -0
- package/src/modules/item-management/command/createItem.generated.ts +6 -0
- package/src/modules/item-management/command/createItem.test.ts +152 -0
- package/src/modules/item-management/command/createItem.ts +72 -0
- package/src/modules/item-management/command/createTaxonomyNode.generated.ts +6 -0
- package/src/modules/item-management/command/createTaxonomyNode.test.ts +126 -0
- package/src/modules/item-management/command/createTaxonomyNode.ts +70 -0
- package/src/modules/item-management/command/deactivateItem.generated.ts +6 -0
- package/src/modules/item-management/command/deactivateItem.test.ts +76 -0
- package/src/modules/item-management/command/deactivateItem.ts +42 -0
- package/src/modules/item-management/command/deleteItem.generated.ts +6 -0
- package/src/modules/item-management/command/deleteItem.test.ts +61 -0
- package/src/modules/item-management/command/deleteItem.ts +38 -0
- package/src/modules/item-management/command/deleteTaxonomyNode.generated.ts +6 -0
- package/src/modules/item-management/command/deleteTaxonomyNode.test.ts +73 -0
- package/src/modules/item-management/command/deleteTaxonomyNode.ts +50 -0
- package/src/modules/item-management/command/moveTaxonomyNode.generated.ts +6 -0
- package/src/modules/item-management/command/moveTaxonomyNode.test.ts +136 -0
- package/src/modules/item-management/command/moveTaxonomyNode.ts +85 -0
- package/src/modules/item-management/command/reactivateItem.generated.ts +6 -0
- package/src/modules/item-management/command/reactivateItem.test.ts +76 -0
- package/src/modules/item-management/command/reactivateItem.ts +42 -0
- package/src/modules/item-management/command/removeItemFromTaxonomy.generated.ts +6 -0
- package/src/modules/item-management/command/removeItemFromTaxonomy.test.ts +43 -0
- package/src/modules/item-management/command/removeItemFromTaxonomy.ts +30 -0
- package/src/modules/item-management/command/updateItem.generated.ts +6 -0
- package/src/modules/item-management/command/updateItem.test.ts +178 -0
- package/src/modules/item-management/command/updateItem.ts +103 -0
- package/src/modules/item-management/command/updateTaxonomyNode.generated.ts +6 -0
- package/src/modules/item-management/command/updateTaxonomyNode.test.ts +88 -0
- package/src/modules/item-management/command/updateTaxonomyNode.ts +62 -0
- package/src/modules/item-management/db/item.ts +47 -0
- package/src/modules/item-management/db/itemTaxonomyAssignment.ts +49 -0
- package/src/modules/item-management/db/taxonomyNode.ts +34 -0
- package/src/modules/item-management/docs/commands/ActivateItem.md +32 -0
- package/src/modules/item-management/docs/commands/AssignItemToTaxonomy.md +38 -0
- package/src/modules/item-management/docs/commands/CreateItem.md +44 -0
- package/src/modules/item-management/docs/commands/CreateTaxonomyNode.md +44 -0
- package/src/modules/item-management/docs/commands/DeactivateItem.md +34 -0
- package/src/modules/item-management/docs/commands/DeleteItem.md +35 -0
- package/src/modules/item-management/docs/commands/DeleteTaxonomyNode.md +39 -0
- package/src/modules/item-management/docs/commands/MoveTaxonomyNode.md +45 -0
- package/src/modules/item-management/docs/commands/ReactivateItem.md +34 -0
- package/src/modules/item-management/docs/commands/RemoveItemFromTaxonomy.md +30 -0
- package/src/modules/item-management/docs/commands/UpdateItem.md +55 -0
- package/src/modules/item-management/docs/commands/UpdateTaxonomyNode.md +36 -0
- package/src/modules/item-management/docs/features/item-lifecycle.md +60 -0
- package/src/modules/item-management/docs/features/item-taxonomy.md +65 -0
- package/src/modules/item-management/docs/models/ItemTaxonomyAssignment.md +36 -0
- package/src/modules/item-management/docs/models/TaxonomyNode.md +47 -0
- package/src/modules/item-management/docs/models/item.md +59 -0
- package/src/modules/item-management/docs/queries/CalculateNodeDepth.md +36 -0
- package/src/modules/item-management/docs/queries/CalculateSubtreeDepth.md +40 -0
- package/src/modules/item-management/docs/queries/DetectCircularReference.md +41 -0
- package/src/modules/item-management/docs/queries/GetItem.md +38 -0
- package/src/modules/item-management/docs/queries/GetItemTaxonomyAssignment.md +29 -0
- package/src/modules/item-management/docs/queries/GetTaxonomyNode.md +35 -0
- package/src/modules/item-management/docs/queries/GetTaxonomyNodeAssignments.md +29 -0
- package/src/modules/item-management/docs/queries/GetTaxonomyNodeChildren.md +29 -0
- package/src/modules/item-management/generated/enums.ts +9 -0
- package/src/modules/item-management/generated/kysely-tailordb.ts +62 -0
- package/src/modules/item-management/index.ts +53 -0
- package/src/modules/item-management/lib/_db_deps.ts +13 -0
- package/src/modules/item-management/lib/errors.generated.ts +117 -0
- package/src/modules/item-management/lib/permissions.generated.ts +17 -0
- package/src/modules/item-management/lib/types.ts +19 -0
- package/src/modules/item-management/module.ts +97 -0
- package/src/modules/item-management/query/calculateNodeDepth.generated.ts +5 -0
- package/src/modules/item-management/query/calculateNodeDepth.test.ts +56 -0
- package/src/modules/item-management/query/calculateNodeDepth.ts +28 -0
- package/src/modules/item-management/query/calculateSubtreeDepth.generated.ts +5 -0
- package/src/modules/item-management/query/calculateSubtreeDepth.test.ts +75 -0
- package/src/modules/item-management/query/calculateSubtreeDepth.ts +29 -0
- package/src/modules/item-management/query/detectCircularReference.generated.ts +5 -0
- package/src/modules/item-management/query/detectCircularReference.test.ts +61 -0
- package/src/modules/item-management/query/detectCircularReference.ts +32 -0
- package/src/modules/item-management/query/getItem.generated.ts +5 -0
- package/src/modules/item-management/query/getItem.test.ts +67 -0
- package/src/modules/item-management/query/getItem.ts +20 -0
- package/src/modules/item-management/query/getItemTaxonomyAssignment.generated.ts +5 -0
- package/src/modules/item-management/query/getItemTaxonomyAssignment.test.ts +25 -0
- package/src/modules/item-management/query/getItemTaxonomyAssignment.ts +18 -0
- package/src/modules/item-management/query/getTaxonomyNode.generated.ts +5 -0
- package/src/modules/item-management/query/getTaxonomyNode.test.ts +47 -0
- package/src/modules/item-management/query/getTaxonomyNode.ts +18 -0
- package/src/modules/item-management/query/getTaxonomyNodeAssignments.generated.ts +5 -0
- package/src/modules/item-management/query/getTaxonomyNodeAssignments.test.ts +25 -0
- package/src/modules/item-management/query/getTaxonomyNodeAssignments.ts +16 -0
- package/src/modules/item-management/query/getTaxonomyNodeChildren.generated.ts +5 -0
- package/src/modules/item-management/query/getTaxonomyNodeChildren.test.ts +34 -0
- package/src/modules/item-management/query/getTaxonomyNodeChildren.ts +16 -0
- package/src/modules/item-management/tailor.config.ts +11 -0
- package/src/modules/item-management/testing/fixtures.ts +81 -0
- package/src/modules/primitives/command/activateCategory.generated.ts +6 -0
- package/src/modules/primitives/command/activateCategory.test.ts +11 -29
- package/src/modules/primitives/command/activateCategory.ts +27 -34
- package/src/modules/primitives/command/activateCurrency.generated.ts +6 -0
- package/src/modules/primitives/command/activateCurrency.test.ts +11 -29
- package/src/modules/primitives/command/activateCurrency.ts +27 -34
- package/src/modules/primitives/command/activateUnit.generated.ts +6 -0
- package/src/modules/primitives/command/activateUnit.test.ts +11 -15
- package/src/modules/primitives/command/activateUnit.ts +27 -34
- package/src/modules/primitives/command/createCategory.generated.ts +6 -0
- package/src/modules/primitives/command/createCategory.test.ts +27 -39
- package/src/modules/primitives/command/createCategory.ts +53 -62
- package/src/modules/primitives/command/createCurrency.generated.ts +6 -0
- package/src/modules/primitives/command/createCurrency.test.ts +78 -71
- package/src/modules/primitives/command/createCurrency.ts +43 -48
- package/src/modules/primitives/command/createExchangeRate.generated.ts +6 -0
- package/src/modules/primitives/command/createExchangeRate.test.ts +101 -100
- package/src/modules/primitives/command/createExchangeRate.ts +50 -59
- package/src/modules/primitives/command/createUnit.generated.ts +6 -0
- package/src/modules/primitives/command/createUnit.test.ts +92 -95
- package/src/modules/primitives/command/createUnit.ts +54 -57
- package/src/modules/primitives/command/deactivateCategory.generated.ts +6 -0
- package/src/modules/primitives/command/deactivateCategory.test.ts +27 -28
- package/src/modules/primitives/command/deactivateCategory.ts +43 -50
- package/src/modules/primitives/command/deactivateCurrency.generated.ts +6 -0
- package/src/modules/primitives/command/deactivateCurrency.test.ts +23 -38
- package/src/modules/primitives/command/deactivateCurrency.ts +31 -38
- package/src/modules/primitives/command/deactivateUnit.generated.ts +6 -0
- package/src/modules/primitives/command/deactivateUnit.test.ts +27 -23
- package/src/modules/primitives/command/deactivateUnit.ts +39 -49
- package/src/modules/primitives/command/setBaseCurrency.generated.ts +6 -0
- package/src/modules/primitives/command/setBaseCurrency.test.ts +40 -33
- package/src/modules/primitives/command/setBaseCurrency.ts +43 -50
- package/src/modules/primitives/command/setReferenceUnit.generated.ts +6 -0
- package/src/modules/primitives/command/setReferenceUnit.test.ts +39 -35
- package/src/modules/primitives/command/setReferenceUnit.ts +46 -59
- package/src/modules/primitives/db/unit.ts +13 -3
- package/src/modules/primitives/docs/commands/ActivateCategory.md +1 -2
- package/src/modules/primitives/docs/commands/ActivateCurrency.md +1 -2
- package/src/modules/primitives/docs/commands/ActivateUnit.md +1 -2
- package/src/modules/primitives/docs/commands/CreateCategory.md +1 -4
- package/src/modules/primitives/docs/commands/CreateCurrency.md +3 -4
- package/src/modules/primitives/docs/commands/CreateExchangeRate.md +4 -5
- package/src/modules/primitives/docs/commands/CreateUnit.md +5 -5
- package/src/modules/primitives/docs/commands/DeactivateCategory.md +2 -3
- package/src/modules/primitives/docs/commands/DeactivateCurrency.md +2 -3
- package/src/modules/primitives/docs/commands/DeactivateUnit.md +2 -3
- package/src/modules/primitives/docs/commands/SetBaseCurrency.md +2 -3
- package/src/modules/primitives/docs/commands/SetReferenceUnit.md +2 -3
- package/src/modules/primitives/docs/models/Currency.md +4 -0
- package/src/modules/primitives/docs/models/ExchangeRate.md +4 -1
- package/src/modules/primitives/docs/models/Unit.md +4 -1
- package/src/modules/primitives/docs/models/UoMCategory.md +2 -0
- package/src/modules/primitives/docs/{commands → queries}/ConvertAmount.md +3 -5
- package/src/modules/primitives/docs/{commands → queries}/ConvertQuantity.md +3 -5
- package/src/modules/primitives/docs/queries/GetBaseCurrency.md +32 -0
- package/src/modules/primitives/docs/queries/GetCurrency.md +36 -0
- package/src/modules/primitives/docs/queries/GetUnit.md +36 -0
- package/src/modules/primitives/docs/queries/GetUoMCategory.md +36 -0
- package/src/modules/primitives/docs/queries/ListUnitsByCategory.md +26 -0
- package/src/modules/primitives/generated/kysely-tailordb.ts +24 -45
- package/src/modules/primitives/index.ts +17 -6
- package/src/modules/primitives/lib/errors.generated.ts +112 -0
- package/src/modules/primitives/{permissions.ts → lib/permissions.generated.ts} +9 -10
- package/src/modules/primitives/module.ts +39 -27
- package/src/modules/primitives/query/convertAmount.generated.ts +5 -0
- package/src/modules/primitives/{command → query}/convertAmount.test.ts +4 -21
- package/src/modules/primitives/query/convertAmount.ts +121 -0
- package/src/modules/primitives/query/convertQuantity.generated.ts +5 -0
- package/src/modules/primitives/{command → query}/convertQuantity.test.ts +8 -15
- package/src/modules/primitives/query/convertQuantity.ts +63 -0
- package/src/modules/primitives/query/getBaseCurrency.generated.ts +5 -0
- package/src/modules/primitives/query/getBaseCurrency.test.ts +28 -0
- package/src/modules/primitives/query/getBaseCurrency.ts +16 -0
- package/src/modules/primitives/query/getCurrency.generated.ts +5 -0
- package/src/modules/primitives/query/getCurrency.test.ts +47 -0
- package/src/modules/primitives/query/getCurrency.ts +18 -0
- package/src/modules/primitives/query/getUnit.generated.ts +5 -0
- package/src/modules/primitives/query/getUnit.test.ts +47 -0
- package/src/modules/primitives/query/getUnit.ts +18 -0
- package/src/modules/primitives/query/getUoMCategory.generated.ts +5 -0
- package/src/modules/primitives/query/getUoMCategory.test.ts +47 -0
- package/src/modules/primitives/query/getUoMCategory.ts +18 -0
- package/src/modules/primitives/query/listUnitsByCategory.generated.ts +5 -0
- package/src/modules/primitives/query/listUnitsByCategory.ts +16 -0
- package/src/modules/primitives/tailor.config.ts +3 -3
- package/src/modules/shared/defineCommand.test.ts +23 -10
- package/src/modules/shared/defineCommand.ts +23 -10
- package/src/modules/shared/defineQuery.test.ts +28 -0
- package/src/modules/shared/defineQuery.ts +16 -0
- package/src/modules/shared/internal.ts +3 -1
- package/src/modules/shared/requirePermission.test.ts +22 -21
- package/src/modules/shared/requirePermission.ts +8 -2
- package/src/modules/shared/result.ts +12 -0
- package/src/modules/shared/types.ts +8 -0
- package/src/modules/testing/index.ts +36 -11
- package/src/modules/user-management/command/activateUser.generated.ts +6 -0
- package/src/modules/user-management/command/activateUser.test.ts +27 -27
- package/src/modules/user-management/command/activateUser.ts +40 -48
- package/src/modules/user-management/command/assignPermissionToRole.generated.ts +6 -0
- package/src/modules/user-management/command/assignPermissionToRole.test.ts +42 -43
- package/src/modules/user-management/command/assignPermissionToRole.ts +59 -62
- package/src/modules/user-management/command/assignRoleToUser.generated.ts +6 -0
- package/src/modules/user-management/command/assignRoleToUser.test.ts +70 -63
- package/src/modules/user-management/command/assignRoleToUser.ts +63 -66
- package/src/modules/user-management/command/createPermission.generated.ts +6 -0
- package/src/modules/user-management/command/createPermission.test.ts +45 -38
- package/src/modules/user-management/command/createPermission.ts +42 -46
- package/src/modules/user-management/command/createRole.generated.ts +6 -0
- package/src/modules/user-management/command/createRole.test.ts +30 -29
- package/src/modules/user-management/command/createRole.ts +33 -33
- package/src/modules/user-management/command/createUser.generated.ts +6 -0
- package/src/modules/user-management/command/createUser.test.ts +64 -42
- package/src/modules/user-management/command/createUser.ts +54 -56
- package/src/modules/user-management/command/deactivateUser.generated.ts +6 -0
- package/src/modules/user-management/command/deactivateUser.test.ts +27 -27
- package/src/modules/user-management/command/deactivateUser.ts +40 -48
- package/src/modules/user-management/command/logAuditEvent.generated.ts +6 -0
- package/src/modules/user-management/command/logAuditEvent.test.ts +50 -42
- package/src/modules/user-management/command/logAuditEvent.ts +25 -28
- package/src/modules/user-management/command/reactivateUser.generated.ts +6 -0
- package/src/modules/user-management/command/reactivateUser.test.ts +31 -27
- package/src/modules/user-management/command/reactivateUser.ts +40 -48
- package/src/modules/user-management/command/revokePermissionFromRole.generated.ts +6 -0
- package/src/modules/user-management/command/revokePermissionFromRole.test.ts +52 -51
- package/src/modules/user-management/command/revokePermissionFromRole.ts +60 -57
- package/src/modules/user-management/command/revokeRoleFromUser.generated.ts +6 -0
- package/src/modules/user-management/command/revokeRoleFromUser.test.ts +53 -48
- package/src/modules/user-management/command/revokeRoleFromUser.ts +58 -57
- package/src/modules/user-management/docs/commands/CreatePermission.md +2 -2
- package/src/modules/user-management/docs/commands/CreateRole.md +1 -1
- package/src/modules/user-management/docs/models/AuditEvent.md +2 -0
- package/src/modules/user-management/docs/models/Permission.md +2 -0
- package/src/modules/user-management/docs/models/Role.md +2 -0
- package/src/modules/user-management/docs/models/RolePermission.md +2 -0
- package/src/modules/user-management/docs/models/User.md +2 -0
- package/src/modules/user-management/docs/models/UserRole.md +2 -0
- package/src/modules/user-management/generated/enums.ts +11 -11
- package/src/modules/user-management/generated/kysely-tailordb.ts +27 -56
- package/src/modules/user-management/index.ts +2 -2
- package/src/modules/user-management/lib/errors.generated.ts +67 -0
- package/src/modules/user-management/{permissions.ts → lib/permissions.generated.ts} +8 -7
- package/src/modules/user-management/module.ts +22 -22
- package/src/modules/user-management/tailor.config.ts +3 -3
- package/src/schemas.ts +2 -1
- package/skills/1-module-docs/references/structure.md +0 -22
- package/skills/2-module-feature-breakdown/references/commands.md +0 -48
- package/skills/2-module-feature-breakdown/references/structure.md +0 -22
- package/skills/3-module-doc-review/references/commands.md +0 -54
- package/skills/3-module-doc-review/references/models.md +0 -29
- package/skills/4-module-tdd-implementation/SKILL.md +0 -74
- package/skills/4-module-tdd-implementation/references/commands.md +0 -45
- package/skills/4-module-tdd-implementation/references/errors.md +0 -7
- package/skills/4-module-tdd-implementation/references/models.md +0 -30
- package/skills/4-module-tdd-implementation/references/structure.md +0 -22
- package/skills/4-module-tdd-implementation/references/testing.md +0 -37
- package/skills/5-module-implementation-review/references/commands.md +0 -45
- package/skills/5-module-implementation-review/references/errors.md +0 -7
- package/skills/5-module-implementation-review/references/exports.md +0 -8
- package/skills/5-module-implementation-review/references/models.md +0 -30
- package/src/modules/primitives/command/convertAmount.ts +0 -126
- package/src/modules/primitives/command/convertQuantity.ts +0 -73
- package/src/modules/primitives/lib/errors.ts +0 -138
- package/src/modules/user-management/lib/errors.ts +0 -81
- /package/skills/{app-compose-1-requirement-analysis → erp-kit-app-1-requirements}/references/structure.md +0 -0
- /package/skills/{app-compose-2-requirements-breakdown → erp-kit-app-2-breakdown}/references/screen-detailview.md +0 -0
- /package/skills/{app-compose-2-requirements-breakdown → erp-kit-app-2-breakdown}/references/screen-form.md +0 -0
- /package/skills/{app-compose-2-requirements-breakdown → erp-kit-app-2-breakdown}/references/screen-listview.md +0 -0
- /package/skills/{app-compose-2-requirements-breakdown → erp-kit-app-2-breakdown}/references/structure.md +0 -0
- /package/skills/{app-compose-3-doc-review → erp-kit-app-3-doc-review}/references/structure.md +0 -0
- /package/skills/{app-compose-4-design-mock → erp-kit-app-4-design}/references/component.md +0 -0
- /package/skills/{app-compose-4-design-mock → erp-kit-app-4-design}/references/screen-detailview.md +0 -0
- /package/skills/{app-compose-4-design-mock → erp-kit-app-4-design}/references/screen-form.md +0 -0
- /package/skills/{app-compose-4-design-mock → erp-kit-app-4-design}/references/screen-listview.md +0 -0
- /package/skills/{app-compose-4-design-mock → erp-kit-app-4-design}/references/structure.md +0 -0
- /package/skills/{app-compose-5-design-mock-review → erp-kit-app-5-design-review}/references/component.md +0 -0
- /package/skills/{app-compose-5-design-mock-review → erp-kit-app-5-design-review}/references/screen-detailview.md +0 -0
- /package/skills/{app-compose-5-design-mock-review → erp-kit-app-5-design-review}/references/screen-form.md +0 -0
- /package/skills/{app-compose-5-design-mock-review → erp-kit-app-5-design-review}/references/screen-listview.md +0 -0
- /package/skills/{app-compose-6-implementation-spec → erp-kit-app-6-impl-spec}/references/auth.md +0 -0
- /package/skills/{app-compose-6-implementation-spec → erp-kit-app-6-impl-spec}/references/structure.md +0 -0
- /package/skills/{2-module-feature-breakdown → erp-kit-module-4-tdd}/references/models.md +0 -0
package/dist/cli.js
CHANGED
|
@@ -1,30 +1,310 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
|
+
import { runMain } from "politty";
|
|
5
|
+
|
|
6
|
+
// src/commands/index.ts
|
|
7
|
+
import { z as z5 } from "zod";
|
|
8
|
+
import { defineCommand as defineCommand5, arg as arg5 } from "politty";
|
|
9
|
+
|
|
10
|
+
// src/commands/init.ts
|
|
11
|
+
import fs from "fs";
|
|
12
|
+
import path2 from "path";
|
|
13
|
+
import chalk from "chalk";
|
|
14
|
+
|
|
15
|
+
// src/util.ts
|
|
16
|
+
import path from "path";
|
|
17
|
+
var PACKAGE_ROOT = path.resolve(import.meta.dirname, "..");
|
|
18
|
+
|
|
19
|
+
// src/commands/init.ts
|
|
20
|
+
var SKILLS_SRC = path2.join(PACKAGE_ROOT, "skills");
|
|
21
|
+
function discoverFrameworkSkills() {
|
|
22
|
+
if (!fs.existsSync(SKILLS_SRC)) return [];
|
|
23
|
+
return fs.readdirSync(SKILLS_SRC, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name.startsWith("erp-kit-")).map((entry) => entry.name);
|
|
24
|
+
}
|
|
25
|
+
function copyDirectoryRecursive(srcDir, destDir, force) {
|
|
26
|
+
let copied = 0;
|
|
27
|
+
let skipped = 0;
|
|
28
|
+
for (const entry of fs.readdirSync(srcDir, { withFileTypes: true })) {
|
|
29
|
+
const srcPath = path2.join(srcDir, entry.name);
|
|
30
|
+
const destPath = path2.join(destDir, entry.name);
|
|
31
|
+
if (entry.isDirectory()) {
|
|
32
|
+
const sub = copyDirectoryRecursive(srcPath, destPath, force);
|
|
33
|
+
copied += sub.copied;
|
|
34
|
+
skipped += sub.skipped;
|
|
35
|
+
} else {
|
|
36
|
+
if (!force && fs.existsSync(destPath)) {
|
|
37
|
+
skipped++;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
41
|
+
fs.copyFileSync(srcPath, destPath);
|
|
42
|
+
copied++;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return { copied, skipped };
|
|
46
|
+
}
|
|
47
|
+
function runInit(cwd5, force) {
|
|
48
|
+
console.log(chalk.bold("erp-kit init\n"));
|
|
49
|
+
const skillsDest = path2.join(cwd5, ".agents", "skills");
|
|
50
|
+
if (force && fs.existsSync(skillsDest)) {
|
|
51
|
+
let removedCount = 0;
|
|
52
|
+
for (const entry of fs.readdirSync(skillsDest)) {
|
|
53
|
+
if (entry.startsWith("erp-kit-")) {
|
|
54
|
+
fs.rmSync(path2.join(skillsDest, entry), { recursive: true, force: true });
|
|
55
|
+
removedCount++;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (removedCount > 0) {
|
|
59
|
+
console.log(chalk.green(` Removed ${removedCount} existing erp-kit-* skills`));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
let copiedCount = 0;
|
|
63
|
+
let skippedCount = 0;
|
|
64
|
+
const frameworkSkills = discoverFrameworkSkills();
|
|
65
|
+
for (const skill of frameworkSkills) {
|
|
66
|
+
const srcSkillDir = path2.join(SKILLS_SRC, skill);
|
|
67
|
+
if (!fs.existsSync(srcSkillDir)) continue;
|
|
68
|
+
const destDir = path2.join(skillsDest, skill);
|
|
69
|
+
const result = copyDirectoryRecursive(srcSkillDir, destDir, force);
|
|
70
|
+
copiedCount += result.copied;
|
|
71
|
+
if (result.skipped > 0) {
|
|
72
|
+
console.log(chalk.yellow(` Skipped ${skill}/ (${result.skipped} existing files)`));
|
|
73
|
+
skippedCount += result.skipped;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
console.log(chalk.green(` Copied ${copiedCount} skill files to .agents/skills/`));
|
|
77
|
+
if (skippedCount > 0) {
|
|
78
|
+
console.log(
|
|
79
|
+
chalk.yellow(` Skipped ${skippedCount} existing files (use --force to overwrite)`)
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
const claudeSkills = path2.join(cwd5, ".claude", "skills");
|
|
83
|
+
const relTarget = path2.relative(path2.dirname(claudeSkills), skillsDest);
|
|
84
|
+
const claudeSkillsExists = (() => {
|
|
85
|
+
try {
|
|
86
|
+
fs.lstatSync(claudeSkills);
|
|
87
|
+
return true;
|
|
88
|
+
} catch {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
})();
|
|
92
|
+
if (claudeSkillsExists) {
|
|
93
|
+
const stat2 = fs.lstatSync(claudeSkills);
|
|
94
|
+
if (stat2.isSymbolicLink()) {
|
|
95
|
+
const current = fs.readlinkSync(claudeSkills);
|
|
96
|
+
if (current === relTarget) {
|
|
97
|
+
console.log(chalk.green(" .claude/skills -> .agents/skills/ (already linked)"));
|
|
98
|
+
} else if (force) {
|
|
99
|
+
fs.unlinkSync(claudeSkills);
|
|
100
|
+
fs.symlinkSync(relTarget, claudeSkills);
|
|
101
|
+
console.log(chalk.green(" .claude/skills -> .agents/skills/ (relinked)"));
|
|
102
|
+
} else {
|
|
103
|
+
console.log(
|
|
104
|
+
chalk.yellow(` Skipped .claude/skills (symlink exists -> ${current}, use --force)`)
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
} else {
|
|
108
|
+
console.log(chalk.yellow(" Skipped .claude/skills (directory exists, not a symlink)"));
|
|
109
|
+
}
|
|
110
|
+
} else {
|
|
111
|
+
fs.mkdirSync(path2.dirname(claudeSkills), { recursive: true });
|
|
112
|
+
fs.symlinkSync(relTarget, claudeSkills);
|
|
113
|
+
console.log(chalk.green(" .claude/skills -> .agents/skills/ (linked)"));
|
|
114
|
+
}
|
|
115
|
+
console.log(chalk.bold.green("\nDone! Run `erp-kit check` to validate your docs."));
|
|
116
|
+
return 0;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// src/commands/license.ts
|
|
120
|
+
import fs2 from "fs";
|
|
121
|
+
import { execSync } from "child_process";
|
|
122
|
+
var licenseGroups = {
|
|
123
|
+
// https://github.com/google/licenseclassifier/blob/e6a9bb99b5a6f71d5a34336b8245e305f5430f99/license_type.go#L225
|
|
124
|
+
reciprocal: [
|
|
125
|
+
"APSL-1.0",
|
|
126
|
+
"APSL-1.1",
|
|
127
|
+
"APSL-1.2",
|
|
128
|
+
"APSL-2.0",
|
|
129
|
+
"CDDL-1.0",
|
|
130
|
+
"CDDL-1.1",
|
|
131
|
+
"CPL-1.0",
|
|
132
|
+
"EPL-1.0",
|
|
133
|
+
"EPL-2.0",
|
|
134
|
+
"FreeImage",
|
|
135
|
+
"IPL-1.0",
|
|
136
|
+
"MPL-1.0",
|
|
137
|
+
"MPL-1.1",
|
|
138
|
+
"MPL-2.0",
|
|
139
|
+
"Ruby"
|
|
140
|
+
],
|
|
141
|
+
// https://github.com/google/licenseclassifier/blob/e6a9bb99b5a6f71d5a34336b8245e305f5430f99/license_type.go#L249
|
|
142
|
+
notice: [
|
|
143
|
+
"AFL-1.1",
|
|
144
|
+
"AFL-1.2",
|
|
145
|
+
"AFL-2.0",
|
|
146
|
+
"AFL-2.1",
|
|
147
|
+
"AFL-3.0",
|
|
148
|
+
"Apache-1.0",
|
|
149
|
+
"Apache-1.1",
|
|
150
|
+
"Apache-2.0",
|
|
151
|
+
"Artistic-1.0-cl8",
|
|
152
|
+
"Artistic-1.0-Perl",
|
|
153
|
+
"Artistic-1.0",
|
|
154
|
+
"Artistic-2.0",
|
|
155
|
+
"BSL-1.0",
|
|
156
|
+
"BSD-2-Clause-FreeBSD",
|
|
157
|
+
"BSD-2-Clause-NetBSD",
|
|
158
|
+
"BSD-2-Clause",
|
|
159
|
+
"BSD-3-Clause-Attribution",
|
|
160
|
+
"BSD-3-Clause-Clear",
|
|
161
|
+
"BSD-3-Clause-LBNL",
|
|
162
|
+
"BSD-3-Clause",
|
|
163
|
+
"BSD-4-Clause",
|
|
164
|
+
"BSD-4-Clause-UC",
|
|
165
|
+
"BSD-Protection",
|
|
166
|
+
"CC-BY-1.0",
|
|
167
|
+
"CC-BY-2.0",
|
|
168
|
+
"CC-BY-2.5",
|
|
169
|
+
"CC-BY-3.0",
|
|
170
|
+
"CC-BY-4.0",
|
|
171
|
+
"FTL",
|
|
172
|
+
"ISC",
|
|
173
|
+
"ImageMagick",
|
|
174
|
+
"Libpng",
|
|
175
|
+
"Lil-1.0",
|
|
176
|
+
"Linux-OpenIB",
|
|
177
|
+
"LPL-1.02",
|
|
178
|
+
"LPL-1.0",
|
|
179
|
+
"MS-PL",
|
|
180
|
+
"MIT",
|
|
181
|
+
"NCSA",
|
|
182
|
+
"OpenSSL",
|
|
183
|
+
"PHP-3.01",
|
|
184
|
+
"PHP-3.0",
|
|
185
|
+
"PIL",
|
|
186
|
+
"Python-2.0",
|
|
187
|
+
"Python-2.0-complete",
|
|
188
|
+
"PostgreSQL",
|
|
189
|
+
"SGI-B-1.0",
|
|
190
|
+
"SGI-B-1.1",
|
|
191
|
+
"SGI-B-2.0",
|
|
192
|
+
"Unicode-DFS-2015",
|
|
193
|
+
"Unicode-DFS-2016",
|
|
194
|
+
"Unicode-TOU",
|
|
195
|
+
"UPL-1.0",
|
|
196
|
+
"W3C-19980720",
|
|
197
|
+
"W3C-20150513",
|
|
198
|
+
"W3C",
|
|
199
|
+
"X11",
|
|
200
|
+
"Xnet",
|
|
201
|
+
"Zend-2.0",
|
|
202
|
+
"zlib-acknowledgement",
|
|
203
|
+
"Zlib",
|
|
204
|
+
"ZPL-1.1",
|
|
205
|
+
"ZPL-2.0",
|
|
206
|
+
"ZPL-2.1"
|
|
207
|
+
],
|
|
208
|
+
// https://github.com/google/licenseclassifier/blob/e6a9bb99b5a6f71d5a34336b8245e305f5430f99/license_type.go#L324
|
|
209
|
+
unencumbered: ["CC0-1.0", "Unlicense", "0BSD"]
|
|
210
|
+
};
|
|
211
|
+
function buildAllowSet(config) {
|
|
212
|
+
const set = /* @__PURE__ */ new Set();
|
|
213
|
+
for (const group of config.groups) {
|
|
214
|
+
for (const license of licenseGroups[group]) {
|
|
215
|
+
set.add(license);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
if (config.allow) {
|
|
219
|
+
for (const license of config.allow) {
|
|
220
|
+
set.add(license);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (config.deny) {
|
|
224
|
+
for (const license of config.deny) {
|
|
225
|
+
set.delete(license);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return set;
|
|
229
|
+
}
|
|
230
|
+
function isLicenseAllowed(licenseString, allowSet) {
|
|
231
|
+
if (/\s+(?:OR|AND)\s+/i.test(licenseString)) {
|
|
232
|
+
const licenses = licenseString.replace(/[()]/g, "").trim().split(/\s+(?:OR|AND)\s+/i).map((l) => l.trim()).filter((l) => l.length > 0);
|
|
233
|
+
return licenses.every((l) => allowSet.has(l));
|
|
234
|
+
}
|
|
235
|
+
return allowSet.has(licenseString);
|
|
236
|
+
}
|
|
237
|
+
function runLicenseList() {
|
|
238
|
+
for (const [group, licenses] of Object.entries(licenseGroups)) {
|
|
239
|
+
console.log(`${group} (${licenses.length} licenses):`);
|
|
240
|
+
for (const license of licenses) {
|
|
241
|
+
console.log(` ${license}`);
|
|
242
|
+
}
|
|
243
|
+
console.log();
|
|
244
|
+
}
|
|
245
|
+
return 0;
|
|
246
|
+
}
|
|
247
|
+
function runLicenseCheck(configPath) {
|
|
248
|
+
const raw = fs2.readFileSync(configPath, "utf-8");
|
|
249
|
+
const config = JSON.parse(raw);
|
|
250
|
+
const validGroups = Object.keys(licenseGroups);
|
|
251
|
+
for (const group of config.groups) {
|
|
252
|
+
if (!validGroups.includes(group)) {
|
|
253
|
+
console.error(`Unknown license group: "${group}". Valid groups: ${validGroups.join(", ")}`);
|
|
254
|
+
return 2;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
const allowSet = buildAllowSet(config);
|
|
258
|
+
console.log("Checking licenses...\n");
|
|
259
|
+
execSync("pnpm licenses list", { stdio: "inherit" });
|
|
260
|
+
const output = execSync("pnpm licenses list --json");
|
|
261
|
+
const licensesJson = JSON.parse(output.toString());
|
|
262
|
+
const violations = [];
|
|
263
|
+
for (const [license, packages] of Object.entries(licensesJson)) {
|
|
264
|
+
if (!isLicenseAllowed(license, allowSet)) {
|
|
265
|
+
for (const pkg of packages) {
|
|
266
|
+
violations.push({ package: pkg.name, license });
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
if (violations.length === 0) {
|
|
271
|
+
console.log("All licenses are allowed.");
|
|
272
|
+
return 0;
|
|
273
|
+
}
|
|
274
|
+
console.error("Found dependencies with disallowed licenses:\n");
|
|
275
|
+
for (const violation of violations) {
|
|
276
|
+
console.error(` - ${violation.package}`);
|
|
277
|
+
console.error(` License: ${violation.license}
|
|
278
|
+
`);
|
|
279
|
+
}
|
|
280
|
+
return 1;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// src/commands/module/index.ts
|
|
4
284
|
import { z as z2 } from "zod";
|
|
5
|
-
import { defineCommand as defineCommand2,
|
|
285
|
+
import { defineCommand as defineCommand2, arg as arg2 } from "politty";
|
|
6
286
|
|
|
7
287
|
// src/mdschema.ts
|
|
8
|
-
import
|
|
9
|
-
import
|
|
288
|
+
import path3 from "path";
|
|
289
|
+
import fs3 from "fs";
|
|
10
290
|
import { execFile } from "child_process";
|
|
11
291
|
import { createRequire } from "module";
|
|
12
292
|
var require2 = createRequire(import.meta.url);
|
|
13
293
|
function getMdschemaBin() {
|
|
14
294
|
const pkgPath = require2.resolve("@jackchuka/mdschema/package.json");
|
|
15
|
-
const pkg = JSON.parse(
|
|
295
|
+
const pkg = JSON.parse(fs3.readFileSync(pkgPath, "utf-8"));
|
|
16
296
|
const bin = typeof pkg.bin === "string" ? pkg.bin : pkg.bin?.mdschema;
|
|
17
297
|
if (!bin) {
|
|
18
298
|
throw new Error("Could not resolve mdschema binary from package.json bin field");
|
|
19
299
|
}
|
|
20
|
-
return
|
|
300
|
+
return path3.join(path3.dirname(pkgPath), bin);
|
|
21
301
|
}
|
|
22
|
-
function runMdschema(args,
|
|
302
|
+
function runMdschema(args, cwd5) {
|
|
23
303
|
return new Promise((resolve4) => {
|
|
24
304
|
execFile(
|
|
25
305
|
getMdschemaBin(),
|
|
26
306
|
args,
|
|
27
|
-
{ encoding: "utf-8", cwd:
|
|
307
|
+
{ encoding: "utf-8", cwd: cwd5, timeout: 3e4 },
|
|
28
308
|
(error, stdout, stderr) => {
|
|
29
309
|
if (error) {
|
|
30
310
|
const execError = error;
|
|
@@ -42,27 +322,22 @@ function runMdschema(args, cwd2) {
|
|
|
42
322
|
}
|
|
43
323
|
|
|
44
324
|
// src/schemas.ts
|
|
45
|
-
import
|
|
46
|
-
|
|
47
|
-
// src/util.ts
|
|
48
|
-
import path2 from "path";
|
|
49
|
-
var PACKAGE_ROOT = path2.resolve(import.meta.dirname, "..");
|
|
50
|
-
|
|
51
|
-
// src/schemas.ts
|
|
52
|
-
var SCHEMAS_ROOT = path3.join(PACKAGE_ROOT, "schemas");
|
|
325
|
+
import path4 from "path";
|
|
326
|
+
var SCHEMAS_ROOT = path4.join(PACKAGE_ROOT, "schemas");
|
|
53
327
|
var MODULE_SCHEMAS = {
|
|
54
|
-
module:
|
|
55
|
-
command:
|
|
56
|
-
model:
|
|
57
|
-
feature:
|
|
328
|
+
module: path4.join(SCHEMAS_ROOT, "module", "module.yml"),
|
|
329
|
+
command: path4.join(SCHEMAS_ROOT, "module", "command.yml"),
|
|
330
|
+
model: path4.join(SCHEMAS_ROOT, "module", "model.yml"),
|
|
331
|
+
feature: path4.join(SCHEMAS_ROOT, "module", "feature.yml"),
|
|
332
|
+
query: path4.join(SCHEMAS_ROOT, "module", "query.yml")
|
|
58
333
|
};
|
|
59
334
|
var APP_COMPOSE_SCHEMAS = {
|
|
60
|
-
requirements:
|
|
61
|
-
actors:
|
|
62
|
-
"business-flow":
|
|
63
|
-
story:
|
|
64
|
-
screen:
|
|
65
|
-
resolver:
|
|
335
|
+
requirements: path4.join(SCHEMAS_ROOT, "app-compose", "requirements.yml"),
|
|
336
|
+
actors: path4.join(SCHEMAS_ROOT, "app-compose", "actors.yml"),
|
|
337
|
+
"business-flow": path4.join(SCHEMAS_ROOT, "app-compose", "business-flow.yml"),
|
|
338
|
+
story: path4.join(SCHEMAS_ROOT, "app-compose", "story.yml"),
|
|
339
|
+
screen: path4.join(SCHEMAS_ROOT, "app-compose", "screen.yml"),
|
|
340
|
+
resolver: path4.join(SCHEMAS_ROOT, "app-compose", "resolver.yml")
|
|
66
341
|
};
|
|
67
342
|
var ALL_SCHEMAS = {
|
|
68
343
|
...MODULE_SCHEMAS,
|
|
@@ -78,6 +353,7 @@ function buildCheckTargets(config) {
|
|
|
78
353
|
{ glob: `${m}/[a-zA-Z]*/docs/features/*.md`, schemaKey: "feature" },
|
|
79
354
|
{ glob: `${m}/[a-zA-Z]*/docs/commands/*.md`, schemaKey: "command" },
|
|
80
355
|
{ glob: `${m}/[a-zA-Z]*/docs/models/*.md`, schemaKey: "model" },
|
|
356
|
+
{ glob: `${m}/[a-zA-Z]*/docs/queries/*.md`, schemaKey: "query" },
|
|
81
357
|
{ glob: `${m}/[a-zA-Z]*/README.md`, schemaKey: "module" }
|
|
82
358
|
);
|
|
83
359
|
}
|
|
@@ -94,7 +370,7 @@ function buildCheckTargets(config) {
|
|
|
94
370
|
}
|
|
95
371
|
return targets;
|
|
96
372
|
}
|
|
97
|
-
async function runCheck(config,
|
|
373
|
+
async function runCheck(config, cwd5) {
|
|
98
374
|
const targets = buildCheckTargets(config);
|
|
99
375
|
const results = await Promise.all(
|
|
100
376
|
targets.map(async (target) => {
|
|
@@ -105,7 +381,7 @@ async function runCheck(config, cwd2) {
|
|
|
105
381
|
}
|
|
106
382
|
const { exitCode, stdout, stderr } = await runMdschema(
|
|
107
383
|
["check", target.glob, "--schema", schemaPath],
|
|
108
|
-
|
|
384
|
+
cwd5
|
|
109
385
|
);
|
|
110
386
|
if (stdout.trim()) console.log(stdout);
|
|
111
387
|
if (stderr.trim()) console.error(stderr);
|
|
@@ -117,22 +393,28 @@ async function runCheck(config, cwd2) {
|
|
|
117
393
|
}
|
|
118
394
|
|
|
119
395
|
// src/commands/sync-check.ts
|
|
120
|
-
import
|
|
396
|
+
import path5 from "path";
|
|
121
397
|
import fg from "fast-glob";
|
|
122
|
-
import
|
|
398
|
+
import chalk2 from "chalk";
|
|
123
399
|
function moduleCategories(root) {
|
|
124
400
|
return [
|
|
125
401
|
{
|
|
126
402
|
name: "command",
|
|
127
403
|
sourcePattern: `${root}/*/command/*.ts`,
|
|
128
404
|
docPattern: `${root}/*/docs/commands/*.md`,
|
|
129
|
-
exclusions: [/\.test\.ts$/]
|
|
405
|
+
exclusions: [/\.test\.ts$/, /\.generated\.ts$/]
|
|
130
406
|
},
|
|
131
407
|
{
|
|
132
408
|
name: "model",
|
|
133
409
|
sourcePattern: `${root}/*/db/*.ts`,
|
|
134
410
|
docPattern: `${root}/*/docs/models/*.md`,
|
|
135
411
|
exclusions: [/\.test\.ts$/, /^index\.ts$/]
|
|
412
|
+
},
|
|
413
|
+
{
|
|
414
|
+
name: "query",
|
|
415
|
+
sourcePattern: `${root}/*/query/*.ts`,
|
|
416
|
+
docPattern: `${root}/*/docs/queries/*.md`,
|
|
417
|
+
exclusions: [/\.test\.ts$/]
|
|
136
418
|
}
|
|
137
419
|
];
|
|
138
420
|
}
|
|
@@ -149,7 +431,7 @@ function appComposeCategories(root) {
|
|
|
149
431
|
function shouldExclude(fileName, exclusions) {
|
|
150
432
|
return exclusions.some((pattern) => pattern.test(fileName));
|
|
151
433
|
}
|
|
152
|
-
async function runSyncCheck(config,
|
|
434
|
+
async function runSyncCheck(config, cwd5) {
|
|
153
435
|
const errors = [];
|
|
154
436
|
let totalSources = 0;
|
|
155
437
|
let totalDocs = 0;
|
|
@@ -161,18 +443,21 @@ async function runSyncCheck(config, cwd2) {
|
|
|
161
443
|
allCategories.push(...appComposeCategories(config.appRoot));
|
|
162
444
|
}
|
|
163
445
|
for (const category of allCategories) {
|
|
164
|
-
const sources = await fg(category.sourcePattern, { cwd:
|
|
165
|
-
const docs = await fg(category.docPattern, { cwd:
|
|
446
|
+
const sources = await fg(category.sourcePattern, { cwd: cwd5 });
|
|
447
|
+
const docs = await fg(category.docPattern, { cwd: cwd5 });
|
|
166
448
|
const sourceBasenames = /* @__PURE__ */ new Map();
|
|
167
449
|
const docBasenames = /* @__PURE__ */ new Map();
|
|
168
450
|
for (const sourcePath of sources) {
|
|
169
|
-
const fileName =
|
|
451
|
+
const fileName = path5.basename(sourcePath);
|
|
170
452
|
if (shouldExclude(fileName, category.exclusions)) continue;
|
|
171
|
-
|
|
453
|
+
let basename2 = path5.basename(sourcePath, path5.extname(sourcePath));
|
|
454
|
+
if (basename2.endsWith(".generated")) {
|
|
455
|
+
basename2 = basename2.slice(0, -".generated".length);
|
|
456
|
+
}
|
|
172
457
|
sourceBasenames.set(basename2.toLowerCase(), sourcePath);
|
|
173
458
|
}
|
|
174
459
|
for (const docPath of docs) {
|
|
175
|
-
const basename2 =
|
|
460
|
+
const basename2 = path5.basename(docPath, path5.extname(docPath));
|
|
176
461
|
docBasenames.set(basename2.toLowerCase(), docPath);
|
|
177
462
|
}
|
|
178
463
|
for (const [basename2, sourcePath] of sourceBasenames) {
|
|
@@ -210,9 +495,9 @@ async function runSyncCheck(config, cwd2) {
|
|
|
210
495
|
}
|
|
211
496
|
function formatSyncCheckReport(result) {
|
|
212
497
|
const lines = [];
|
|
213
|
-
lines.push(
|
|
498
|
+
lines.push(chalk2.bold("docs-sync-check: Checking source-documentation correspondence...\n"));
|
|
214
499
|
if (result.errors.length > 0) {
|
|
215
|
-
lines.push(
|
|
500
|
+
lines.push(chalk2.red.bold("Errors:\n"));
|
|
216
501
|
const byCategory = /* @__PURE__ */ new Map();
|
|
217
502
|
for (const error of result.errors) {
|
|
218
503
|
const existing = byCategory.get(error.category) ?? [];
|
|
@@ -220,45 +505,105 @@ function formatSyncCheckReport(result) {
|
|
|
220
505
|
byCategory.set(error.category, existing);
|
|
221
506
|
}
|
|
222
507
|
for (const [category, categoryErrors] of byCategory) {
|
|
223
|
-
lines.push(
|
|
508
|
+
lines.push(chalk2.cyan(` Category: ${category}
|
|
224
509
|
`));
|
|
225
510
|
for (const error of categoryErrors) {
|
|
226
511
|
if (error.type === "missing-doc") {
|
|
227
|
-
lines.push(` ${
|
|
228
|
-
lines.push(` ${
|
|
512
|
+
lines.push(` ${chalk2.red(error.sourcePath)}`);
|
|
513
|
+
lines.push(` ${chalk2.yellow("Missing documentation for:")} ${error.expectedBasename}`);
|
|
229
514
|
} else {
|
|
230
|
-
lines.push(` ${
|
|
515
|
+
lines.push(` ${chalk2.red(error.docPath)}`);
|
|
231
516
|
lines.push(
|
|
232
|
-
` ${
|
|
517
|
+
` ${chalk2.yellow("Orphaned documentation:")} no source file for ${error.expectedBasename}`
|
|
233
518
|
);
|
|
234
519
|
}
|
|
235
520
|
lines.push("");
|
|
236
521
|
}
|
|
237
522
|
}
|
|
238
523
|
} else {
|
|
239
|
-
lines.push(
|
|
524
|
+
lines.push(chalk2.green("All source files have corresponding documentation.\n"));
|
|
240
525
|
}
|
|
241
|
-
lines.push(
|
|
526
|
+
lines.push(chalk2.bold("Summary:"));
|
|
242
527
|
lines.push(` Categories checked: ${result.summary.categoriesChecked}`);
|
|
243
528
|
lines.push(
|
|
244
529
|
` Source files: ${result.summary.totalSources}, Doc files: ${result.summary.totalDocs}`
|
|
245
530
|
);
|
|
246
531
|
if (result.errors.length > 0) {
|
|
247
|
-
lines.push(
|
|
532
|
+
lines.push(chalk2.red(` Errors: ${result.errors.length}`));
|
|
248
533
|
lines.push("");
|
|
249
|
-
lines.push(
|
|
534
|
+
lines.push(chalk2.red.bold(`docs-sync-check failed with ${result.errors.length} error(s).`));
|
|
250
535
|
} else {
|
|
251
|
-
lines.push(
|
|
536
|
+
lines.push(chalk2.green(" Errors: 0"));
|
|
252
537
|
lines.push("");
|
|
253
|
-
lines.push(
|
|
538
|
+
lines.push(chalk2.green.bold("docs-sync-check passed."));
|
|
254
539
|
}
|
|
255
540
|
return lines.join("\n");
|
|
256
541
|
}
|
|
257
542
|
|
|
258
543
|
// src/commands/scaffold.ts
|
|
259
|
-
import
|
|
260
|
-
import
|
|
261
|
-
|
|
544
|
+
import path6 from "path";
|
|
545
|
+
import fs4 from "fs";
|
|
546
|
+
|
|
547
|
+
// src/commands/scaffold-templates.ts
|
|
548
|
+
function getModuleSrcTemplates() {
|
|
549
|
+
return [
|
|
550
|
+
{ type: "dir", path: "db" },
|
|
551
|
+
{ type: "dir", path: "command" },
|
|
552
|
+
{ type: "dir", path: "executor" },
|
|
553
|
+
{ type: "dir", path: "generated" },
|
|
554
|
+
{ type: "dir", path: "query" },
|
|
555
|
+
{ type: "file", path: "module.ts", content: () => MODULE_TEMPLATE },
|
|
556
|
+
{ type: "file", path: "index.ts", content: () => INDEX_TEMPLATE },
|
|
557
|
+
{ type: "file", path: "permissions.ts", content: (n) => permissionsTemplate(n) },
|
|
558
|
+
{ type: "file", path: "tailor.config.ts", content: (n) => tailorConfigTemplate(n) },
|
|
559
|
+
{ type: "file", path: "lib/errors.ts", content: () => ERRORS_TEMPLATE },
|
|
560
|
+
{ type: "file", path: "lib/types.ts", content: () => TYPES_TEMPLATE },
|
|
561
|
+
{ type: "file", path: "testing/fixtures.ts", content: () => FIXTURES_TEMPLATE }
|
|
562
|
+
];
|
|
563
|
+
}
|
|
564
|
+
var MODULE_TEMPLATE = `export const defineModule = () => {
|
|
565
|
+
return {
|
|
566
|
+
db: {},
|
|
567
|
+
commands: {},
|
|
568
|
+
queries: {},
|
|
569
|
+
};
|
|
570
|
+
};
|
|
571
|
+
`;
|
|
572
|
+
var INDEX_TEMPLATE = `export { defineModule } from "./module";
|
|
573
|
+
export { permissions, own, all } from "./permissions";
|
|
574
|
+
`;
|
|
575
|
+
var ERRORS_TEMPLATE = `import { createDomainError } from "../../shared/internal";
|
|
576
|
+
`;
|
|
577
|
+
var TYPES_TEMPLATE = `import type { InferSchema, Selectable, Insertable, Updateable } from "../../shared/internal";
|
|
578
|
+
import type { DB } from "../generated/kysely-tailordb";
|
|
579
|
+
|
|
580
|
+
export type Schema = InferSchema<DB>;
|
|
581
|
+
`;
|
|
582
|
+
var FIXTURES_TEMPLATE = `// Add test fixtures here
|
|
583
|
+
`;
|
|
584
|
+
function permissionsTemplate(moduleName) {
|
|
585
|
+
return `import { definePermissions } from "../shared/internal";
|
|
586
|
+
|
|
587
|
+
export const { permissions, own, all } = definePermissions("${moduleName}", [] as const);
|
|
588
|
+
`;
|
|
589
|
+
}
|
|
590
|
+
function tailorConfigTemplate(moduleName) {
|
|
591
|
+
return `import { defineConfig, defineGenerators } from "@tailor-platform/sdk";
|
|
592
|
+
|
|
593
|
+
export default defineConfig({
|
|
594
|
+
name: "${moduleName}",
|
|
595
|
+
db: { "main-db": { files: [\`./db/*.ts\`] } },
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
export const generators = defineGenerators(
|
|
599
|
+
["@tailor-platform/kysely-type", { distPath: \`./generated/kysely-tailordb.ts\` }],
|
|
600
|
+
["@tailor-platform/enum-constants", { distPath: "./generated/enums.ts" }],
|
|
601
|
+
);
|
|
602
|
+
`;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
// src/commands/scaffold.ts
|
|
606
|
+
var MODULE_TYPES = ["module", "feature", "command", "model", "query"];
|
|
262
607
|
var APP_TYPES = [
|
|
263
608
|
"requirements",
|
|
264
609
|
"actors",
|
|
@@ -271,7 +616,8 @@ var ALL_TYPES = [...MODULE_TYPES, ...APP_TYPES];
|
|
|
271
616
|
var MODULE_DIR_MAP = {
|
|
272
617
|
feature: "docs/features",
|
|
273
618
|
command: "docs/commands",
|
|
274
|
-
model: "docs/models"
|
|
619
|
+
model: "docs/models",
|
|
620
|
+
query: "docs/queries"
|
|
275
621
|
};
|
|
276
622
|
var APP_DIR_MAP = {
|
|
277
623
|
actors: "docs/actors",
|
|
@@ -279,18 +625,15 @@ var APP_DIR_MAP = {
|
|
|
279
625
|
screen: "docs/screen",
|
|
280
626
|
resolver: "docs/resolver"
|
|
281
627
|
};
|
|
282
|
-
function isModuleType(type) {
|
|
283
|
-
return MODULE_TYPES.includes(type);
|
|
284
|
-
}
|
|
285
628
|
function resolveScaffoldPath(type, parentName, name, root) {
|
|
286
629
|
if (type === "module" || type === "requirements") {
|
|
287
|
-
return
|
|
630
|
+
return path6.join(root, parentName, "README.md");
|
|
288
631
|
}
|
|
289
632
|
if (!name) {
|
|
290
633
|
throw new Error(`Name is required for scaffold type "${type}"`);
|
|
291
634
|
}
|
|
292
635
|
if (type === "business-flow") {
|
|
293
|
-
return
|
|
636
|
+
return path6.join(root, parentName, "docs/business-flow", name, "README.md");
|
|
294
637
|
}
|
|
295
638
|
if (type === "story") {
|
|
296
639
|
const parts = name.split("/");
|
|
@@ -299,20 +642,20 @@ function resolveScaffoldPath(type, parentName, name, root) {
|
|
|
299
642
|
`Story name must be "<flow>/<story>" (e.g., "onboarding/admin--create-user")`
|
|
300
643
|
);
|
|
301
644
|
}
|
|
302
|
-
return
|
|
645
|
+
return path6.join(root, parentName, "docs/business-flow", parts[0], "story", `${parts[1]}.md`);
|
|
303
646
|
}
|
|
304
647
|
if (MODULE_DIR_MAP[type]) {
|
|
305
|
-
return
|
|
648
|
+
return path6.join(root, parentName, MODULE_DIR_MAP[type], `${name}.md`);
|
|
306
649
|
}
|
|
307
650
|
if (APP_DIR_MAP[type]) {
|
|
308
|
-
return
|
|
651
|
+
return path6.join(root, parentName, APP_DIR_MAP[type], `${name}.md`);
|
|
309
652
|
}
|
|
310
653
|
throw new Error(`Unknown scaffold type: ${type}`);
|
|
311
654
|
}
|
|
312
|
-
async function runScaffold(type, parentName, name, root,
|
|
655
|
+
async function runScaffold(type, parentName, name, root, cwd5) {
|
|
313
656
|
const outputPath = resolveScaffoldPath(type, parentName, name, root);
|
|
314
|
-
const absoluteOutput =
|
|
315
|
-
if (
|
|
657
|
+
const absoluteOutput = path6.resolve(cwd5, outputPath);
|
|
658
|
+
if (fs4.existsSync(absoluteOutput)) {
|
|
316
659
|
console.error(`File already exists: ${outputPath}`);
|
|
317
660
|
return 1;
|
|
318
661
|
}
|
|
@@ -322,7 +665,7 @@ async function runScaffold(type, parentName, name, root, cwd2) {
|
|
|
322
665
|
return 2;
|
|
323
666
|
}
|
|
324
667
|
try {
|
|
325
|
-
|
|
668
|
+
fs4.mkdirSync(path6.dirname(absoluteOutput), { recursive: true });
|
|
326
669
|
} catch (err) {
|
|
327
670
|
console.error(
|
|
328
671
|
`Failed to create directory: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -331,122 +674,562 @@ async function runScaffold(type, parentName, name, root, cwd2) {
|
|
|
331
674
|
}
|
|
332
675
|
const { exitCode, stdout, stderr } = await runMdschema(
|
|
333
676
|
["generate", "--schema", schemaPath, "--output", absoluteOutput],
|
|
334
|
-
|
|
677
|
+
cwd5
|
|
335
678
|
);
|
|
336
679
|
if (stdout.trim()) console.log(stdout);
|
|
337
680
|
if (stderr.trim()) console.error(stderr);
|
|
681
|
+
if (exitCode !== 0) return exitCode;
|
|
682
|
+
if (type === "module") {
|
|
683
|
+
scaffoldModuleSrc(path6.dirname(absoluteOutput), parentName);
|
|
684
|
+
}
|
|
338
685
|
return exitCode;
|
|
339
686
|
}
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
var FRAMEWORK_SKILLS = [
|
|
347
|
-
"1-module-docs",
|
|
348
|
-
"2-module-feature-breakdown",
|
|
349
|
-
"3-module-doc-review",
|
|
350
|
-
"4-module-tdd-implementation",
|
|
351
|
-
"5-module-implementation-review",
|
|
352
|
-
"app-compose-1-requirement-analysis",
|
|
353
|
-
"app-compose-2-requirements-breakdown",
|
|
354
|
-
"app-compose-3-doc-review",
|
|
355
|
-
"app-compose-4-design-mock",
|
|
356
|
-
"app-compose-5-design-mock-review",
|
|
357
|
-
"app-compose-6-implementation-spec",
|
|
358
|
-
"mock-scenario"
|
|
359
|
-
];
|
|
360
|
-
function copyDirectoryRecursive(srcDir, destDir, force) {
|
|
361
|
-
let copied = 0;
|
|
362
|
-
let skipped = 0;
|
|
363
|
-
for (const entry of fs3.readdirSync(srcDir, { withFileTypes: true })) {
|
|
364
|
-
const srcPath = path6.join(srcDir, entry.name);
|
|
365
|
-
const destPath = path6.join(destDir, entry.name);
|
|
366
|
-
if (entry.isDirectory()) {
|
|
367
|
-
const sub = copyDirectoryRecursive(srcPath, destPath, force);
|
|
368
|
-
copied += sub.copied;
|
|
369
|
-
skipped += sub.skipped;
|
|
687
|
+
function scaffoldModuleSrc(moduleDir, moduleName) {
|
|
688
|
+
for (const entry of getModuleSrcTemplates()) {
|
|
689
|
+
const fullPath = path6.join(moduleDir, entry.path);
|
|
690
|
+
if (entry.type === "dir") {
|
|
691
|
+
fs4.mkdirSync(fullPath, { recursive: true });
|
|
692
|
+
fs4.writeFileSync(path6.join(fullPath, ".gitkeep"), "");
|
|
370
693
|
} else {
|
|
371
|
-
|
|
372
|
-
|
|
694
|
+
fs4.mkdirSync(path6.dirname(fullPath), { recursive: true });
|
|
695
|
+
fs4.writeFileSync(fullPath, entry.content(moduleName));
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
// src/commands/module/list.ts
|
|
701
|
+
import { readdirSync, existsSync } from "fs";
|
|
702
|
+
import { join } from "path";
|
|
703
|
+
import chalk3 from "chalk";
|
|
704
|
+
var MODULES_DIR = join(PACKAGE_ROOT, "src", "modules");
|
|
705
|
+
var EXCLUDED_DIRS = /* @__PURE__ */ new Set(["shared", "testing"]);
|
|
706
|
+
function countFiles(dir, pattern, exclusions) {
|
|
707
|
+
if (!existsSync(dir)) return 0;
|
|
708
|
+
return readdirSync(dir).filter((f) => pattern.test(f) && !exclusions.some((p) => p.test(f))).length;
|
|
709
|
+
}
|
|
710
|
+
function listModules() {
|
|
711
|
+
if (!existsSync(MODULES_DIR)) return [];
|
|
712
|
+
return readdirSync(MODULES_DIR, { withFileTypes: true }).filter((d) => d.isDirectory() && !EXCLUDED_DIRS.has(d.name)).map((d) => {
|
|
713
|
+
const modDir = join(MODULES_DIR, d.name);
|
|
714
|
+
return {
|
|
715
|
+
name: d.name,
|
|
716
|
+
commands: countFiles(join(modDir, "command"), /\.ts$/, [/\.test\.ts$/]),
|
|
717
|
+
models: countFiles(join(modDir, "db"), /\.ts$/, [/\.test\.ts$/, /^index\.ts$/]),
|
|
718
|
+
features: countFiles(join(modDir, "docs", "features"), /\.md$/, [])
|
|
719
|
+
};
|
|
720
|
+
}).sort((a, b) => a.name.localeCompare(b.name));
|
|
721
|
+
}
|
|
722
|
+
function formatModuleList(modules) {
|
|
723
|
+
if (modules.length === 0) return "No modules found.";
|
|
724
|
+
const lines = [];
|
|
725
|
+
lines.push(chalk3.bold("Modules:\n"));
|
|
726
|
+
const nameWidth = Math.max(...modules.map((m) => m.name.length), 4);
|
|
727
|
+
for (const mod of modules) {
|
|
728
|
+
const counts = [
|
|
729
|
+
`${mod.commands} commands`,
|
|
730
|
+
`${mod.models} models`,
|
|
731
|
+
`${mod.features} features`
|
|
732
|
+
].join(", ");
|
|
733
|
+
lines.push(` ${mod.name.padEnd(nameWidth)} ${counts}`);
|
|
734
|
+
}
|
|
735
|
+
lines.push("");
|
|
736
|
+
lines.push(`${modules.length} modules`);
|
|
737
|
+
return lines.join("\n");
|
|
738
|
+
}
|
|
739
|
+
function runModuleList() {
|
|
740
|
+
const modules = listModules();
|
|
741
|
+
console.log(formatModuleList(modules));
|
|
742
|
+
return 0;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
// src/commands/module/generate.ts
|
|
746
|
+
import path8 from "path";
|
|
747
|
+
import { defineCommand, arg } from "politty";
|
|
748
|
+
import { z } from "zod";
|
|
749
|
+
|
|
750
|
+
// src/generator/generate-code.ts
|
|
751
|
+
import fs5 from "fs";
|
|
752
|
+
import path7 from "path";
|
|
753
|
+
|
|
754
|
+
// src/generator/parse-command-doc.ts
|
|
755
|
+
import { fromMarkdown } from "mdast-util-from-markdown";
|
|
756
|
+
import { toString } from "mdast-util-to-string";
|
|
757
|
+
function parseCommandDoc(fileName, markdown) {
|
|
758
|
+
const commandName = fileName.charAt(0).toLowerCase() + fileName.slice(1);
|
|
759
|
+
const tree = fromMarkdown(markdown);
|
|
760
|
+
return {
|
|
761
|
+
commandName,
|
|
762
|
+
errors: parseErrorScenarios(tree),
|
|
763
|
+
externalDependencies: parseExternalDependencies(tree)
|
|
764
|
+
};
|
|
765
|
+
}
|
|
766
|
+
function errorCodeToClassName(code) {
|
|
767
|
+
const pascal = code.toLowerCase().split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
|
|
768
|
+
return pascal + "Error";
|
|
769
|
+
}
|
|
770
|
+
function isHeading(node) {
|
|
771
|
+
return node.type === "heading";
|
|
772
|
+
}
|
|
773
|
+
function isList(node) {
|
|
774
|
+
return node.type === "list";
|
|
775
|
+
}
|
|
776
|
+
function getNodesUnderHeading(tree, headingText) {
|
|
777
|
+
const nodes = [];
|
|
778
|
+
let collecting = false;
|
|
779
|
+
for (const node of tree.children) {
|
|
780
|
+
if (isHeading(node)) {
|
|
781
|
+
if (collecting) break;
|
|
782
|
+
if (node.depth === 2 && toString(node) === headingText) {
|
|
783
|
+
collecting = true;
|
|
373
784
|
continue;
|
|
374
785
|
}
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
786
|
+
}
|
|
787
|
+
if (collecting) {
|
|
788
|
+
nodes.push(node);
|
|
378
789
|
}
|
|
379
790
|
}
|
|
380
|
-
return
|
|
791
|
+
return nodes;
|
|
381
792
|
}
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
const
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
const
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
console.log(chalk2.yellow(` Skipped ${skill}/ (${result.skipped} existing files)`));
|
|
395
|
-
skippedCount += result.skipped;
|
|
793
|
+
var ERROR_PATTERN = /^([A-Z_]+):\s*(.+)$/;
|
|
794
|
+
function parseErrorScenarios(tree) {
|
|
795
|
+
const nodes = getNodesUnderHeading(tree, "Error Scenarios");
|
|
796
|
+
const errors = [];
|
|
797
|
+
for (const node of nodes) {
|
|
798
|
+
if (!isList(node)) continue;
|
|
799
|
+
for (const item of node.children) {
|
|
800
|
+
const text = toString(item);
|
|
801
|
+
const match = ERROR_PATTERN.exec(text);
|
|
802
|
+
if (match) {
|
|
803
|
+
errors.push({ code: match[1], description: match[2].trim() });
|
|
804
|
+
}
|
|
396
805
|
}
|
|
397
806
|
}
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
807
|
+
return errors;
|
|
808
|
+
}
|
|
809
|
+
var DEPENDENCY_PATTERN = /^([^:]+)::(.+)$/;
|
|
810
|
+
function parseExternalDependencies(tree) {
|
|
811
|
+
const nodes = getNodesUnderHeading(tree, "External Dependencies");
|
|
812
|
+
const deps = [];
|
|
813
|
+
for (const node of nodes) {
|
|
814
|
+
if (!isList(node)) continue;
|
|
815
|
+
for (const item of node.children) {
|
|
816
|
+
const firstChild = item.children[0];
|
|
817
|
+
if (firstChild?.type !== "paragraph") continue;
|
|
818
|
+
for (const inline of firstChild.children) {
|
|
819
|
+
if (inline.type === "link" || inline.type === "linkReference") {
|
|
820
|
+
const linkText = toString(inline);
|
|
821
|
+
const match = DEPENDENCY_PATTERN.exec(linkText);
|
|
822
|
+
if (match) {
|
|
823
|
+
deps.push({ module: match[1], entity: match[2] });
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
}
|
|
403
828
|
}
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
829
|
+
return deps;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
// src/generator/generate-code.ts
|
|
833
|
+
function generateErrors(docs) {
|
|
834
|
+
const seen = /* @__PURE__ */ new Set();
|
|
835
|
+
const lines = [];
|
|
836
|
+
for (const doc of docs) {
|
|
837
|
+
for (const error of doc.errors) {
|
|
838
|
+
if (seen.has(error.code)) continue;
|
|
839
|
+
seen.add(error.code);
|
|
840
|
+
const className = errorCodeToClassName(error.code);
|
|
841
|
+
lines.push(`export const ${className} = createDomainError(`);
|
|
842
|
+
lines.push(` "${className}", "${error.code}",`);
|
|
843
|
+
const escapedDesc = error.description.replace(/`/g, "'");
|
|
844
|
+
lines.push(` (identifier: string) => \`${escapedDesc}: \${identifier}\`,`);
|
|
845
|
+
lines.push(`);`);
|
|
846
|
+
lines.push(``);
|
|
412
847
|
}
|
|
413
|
-
}
|
|
414
|
-
if (
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
848
|
+
}
|
|
849
|
+
if (lines.length === 0) return "";
|
|
850
|
+
return `// @generated \u2014 do not edit
|
|
851
|
+
import { createDomainError } from "../../shared/internal";
|
|
852
|
+
|
|
853
|
+
${lines.join("\n")}`;
|
|
854
|
+
}
|
|
855
|
+
function generateCommandStub(doc) {
|
|
856
|
+
const pascal = doc.commandName.charAt(0).toUpperCase() + doc.commandName.slice(1);
|
|
857
|
+
const inputType = `${pascal}Input`;
|
|
858
|
+
return `import { ok, type CommandContext } from "../../shared/internal";
|
|
859
|
+
import type { DB } from "../generated/kysely-tailordb";
|
|
860
|
+
|
|
861
|
+
export interface ${inputType} {
|
|
862
|
+
// TODO: define input fields
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
export async function run(db: DB, input: ${inputType}, ctx: CommandContext) {
|
|
866
|
+
// TODO: implement
|
|
867
|
+
return ok({});
|
|
868
|
+
}
|
|
869
|
+
`;
|
|
870
|
+
}
|
|
871
|
+
function generateTestStub(doc) {
|
|
872
|
+
const pascal = doc.commandName.charAt(0).toUpperCase() + doc.commandName.slice(1);
|
|
873
|
+
return `import { describe, expect, it } from "vitest";
|
|
874
|
+
import { createMockDb } from "../../testing/index";
|
|
875
|
+
import type { CommandContext } from "../../shared/internal";
|
|
876
|
+
import type { DB } from "../generated/kysely-tailordb";
|
|
877
|
+
import { run, ${pascal}Input } from "./${doc.commandName}";
|
|
878
|
+
|
|
879
|
+
describe("${doc.commandName} - test scenario", () => {
|
|
880
|
+
const ctx: CommandContext = {
|
|
881
|
+
actorId: "test-actor",
|
|
882
|
+
permissions: ["TODO:${doc.commandName}"],
|
|
883
|
+
};
|
|
884
|
+
|
|
885
|
+
it("should be implemented", async () => {
|
|
886
|
+
const { db } = createMockDb<DB>();
|
|
887
|
+
const result = await run(db, {} as ${pascal}Input, ctx);
|
|
888
|
+
expect(result.ok).toBe(true);
|
|
889
|
+
});
|
|
890
|
+
});
|
|
891
|
+
`;
|
|
892
|
+
}
|
|
893
|
+
function generateCommandShell(doc) {
|
|
894
|
+
const lines = [
|
|
895
|
+
`// @generated \u2014 do not edit`,
|
|
896
|
+
`import { defineCommand } from "../../shared/internal";`,
|
|
897
|
+
`import { permissions } from "../lib/permissions.generated";`,
|
|
898
|
+
`import { run } from "./${doc.commandName}";`,
|
|
899
|
+
``,
|
|
900
|
+
`export const ${doc.commandName} = defineCommand(permissions.${doc.commandName}, run);`,
|
|
901
|
+
``
|
|
902
|
+
];
|
|
903
|
+
return lines.join("\n");
|
|
904
|
+
}
|
|
905
|
+
function generateQueryShell(doc) {
|
|
906
|
+
const lines = [
|
|
907
|
+
`// @generated \u2014 do not edit`,
|
|
908
|
+
`import { defineQuery } from "../../shared/internal";`,
|
|
909
|
+
`import { run } from "./${doc.commandName}";`,
|
|
910
|
+
``,
|
|
911
|
+
`export const ${doc.commandName} = defineQuery(run);`,
|
|
912
|
+
``
|
|
913
|
+
];
|
|
914
|
+
return lines.join("\n");
|
|
915
|
+
}
|
|
916
|
+
function generateQueryStub(doc) {
|
|
917
|
+
const pascal = doc.commandName.charAt(0).toUpperCase() + doc.commandName.slice(1);
|
|
918
|
+
const inputType = `${pascal}Input`;
|
|
919
|
+
return `import type { ReadonlyDB } from "../../shared/internal";
|
|
920
|
+
import type { DB } from "../generated/kysely-tailordb";
|
|
921
|
+
|
|
922
|
+
export interface ${inputType} {
|
|
923
|
+
// TODO: define input fields
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
export async function run(db: ReadonlyDB<DB>, input: ${inputType}) {
|
|
927
|
+
// TODO: implement
|
|
928
|
+
return {};
|
|
929
|
+
}
|
|
930
|
+
`;
|
|
931
|
+
}
|
|
932
|
+
function generateQueryTestStub(doc) {
|
|
933
|
+
const pascal = doc.commandName.charAt(0).toUpperCase() + doc.commandName.slice(1);
|
|
934
|
+
const inputType = `${pascal}Input`;
|
|
935
|
+
return `import { describe, expect, it } from "vitest";
|
|
936
|
+
import { createMockDb } from "../../testing/index";
|
|
937
|
+
import type { DB } from "../generated/kysely-tailordb";
|
|
938
|
+
import { run, type ${inputType} } from "./${doc.commandName}";
|
|
939
|
+
|
|
940
|
+
describe("${doc.commandName}", () => {
|
|
941
|
+
it("should be implemented", async () => {
|
|
942
|
+
const { db } = createMockDb<DB>();
|
|
943
|
+
const result = await run(db, {} as ${inputType});
|
|
944
|
+
expect(result).toBeDefined();
|
|
945
|
+
});
|
|
946
|
+
});
|
|
947
|
+
`;
|
|
948
|
+
}
|
|
949
|
+
function generatePermissions(moduleName, commandNames) {
|
|
950
|
+
const sorted = [...commandNames].sort();
|
|
951
|
+
const entries = sorted.map((name) => ` "${name}",`).join("\n");
|
|
952
|
+
return `// @generated \u2014 do not edit
|
|
953
|
+
import { definePermissions } from "../../shared/internal";
|
|
954
|
+
|
|
955
|
+
export const { permissions, own, all } = definePermissions("${moduleName}", [
|
|
956
|
+
${entries}
|
|
957
|
+
] as const);
|
|
958
|
+
`;
|
|
959
|
+
}
|
|
960
|
+
function runGenerateCode(modulePath, moduleName) {
|
|
961
|
+
const docsDir = path7.join(modulePath, "docs", "commands");
|
|
962
|
+
const libDir = path7.join(modulePath, "lib");
|
|
963
|
+
const commandDir = path7.join(modulePath, "command");
|
|
964
|
+
if (!fs5.existsSync(docsDir)) {
|
|
965
|
+
console.error(`No docs/commands/ directory found at ${docsDir}`);
|
|
966
|
+
return 1;
|
|
967
|
+
}
|
|
968
|
+
const mdFiles = fs5.readdirSync(docsDir).filter((f) => f.endsWith(".md"));
|
|
969
|
+
if (mdFiles.length === 0) {
|
|
970
|
+
console.error(`No command docs found in ${docsDir}`);
|
|
971
|
+
return 1;
|
|
972
|
+
}
|
|
973
|
+
const parsedDocs = [];
|
|
974
|
+
for (const file of mdFiles) {
|
|
975
|
+
const content = fs5.readFileSync(path7.join(docsDir, file), "utf-8");
|
|
976
|
+
const name = path7.basename(file, ".md");
|
|
977
|
+
parsedDocs.push(parseCommandDoc(name, content));
|
|
978
|
+
}
|
|
979
|
+
const queryDocsDir = path7.join(modulePath, "docs", "queries");
|
|
980
|
+
const allDocsForErrors = [...parsedDocs];
|
|
981
|
+
if (fs5.existsSync(queryDocsDir)) {
|
|
982
|
+
const queryFiles = fs5.readdirSync(queryDocsDir).filter((f) => f.endsWith(".md"));
|
|
983
|
+
for (const file of queryFiles) {
|
|
984
|
+
const content = fs5.readFileSync(path7.join(queryDocsDir, file), "utf-8");
|
|
985
|
+
const name = path7.basename(file, ".md");
|
|
986
|
+
allDocsForErrors.push(parseCommandDoc(name, content));
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
let generated = 0;
|
|
990
|
+
const errorsContent = generateErrors(allDocsForErrors);
|
|
991
|
+
if (errorsContent) {
|
|
992
|
+
fs5.mkdirSync(libDir, { recursive: true });
|
|
993
|
+
const errorsFile = path7.join(libDir, "errors.generated.ts");
|
|
994
|
+
fs5.writeFileSync(errorsFile, errorsContent);
|
|
995
|
+
console.log(` wrote ${path7.relative(modulePath, errorsFile)}`);
|
|
996
|
+
generated++;
|
|
997
|
+
}
|
|
998
|
+
const commandNames = parsedDocs.map((d) => d.commandName);
|
|
999
|
+
const permissionsContent = generatePermissions(moduleName, commandNames);
|
|
1000
|
+
const permissionsFile = path7.join(libDir, "permissions.generated.ts");
|
|
1001
|
+
fs5.writeFileSync(permissionsFile, permissionsContent);
|
|
1002
|
+
console.log(` wrote ${path7.relative(modulePath, permissionsFile)}`);
|
|
1003
|
+
generated++;
|
|
1004
|
+
fs5.mkdirSync(commandDir, { recursive: true });
|
|
1005
|
+
for (const doc of parsedDocs) {
|
|
1006
|
+
const implFile = path7.join(commandDir, `${doc.commandName}.ts`);
|
|
1007
|
+
const shellContent = generateCommandShell(doc);
|
|
1008
|
+
const shellFile = path7.join(commandDir, `${doc.commandName}.generated.ts`);
|
|
1009
|
+
fs5.writeFileSync(shellFile, shellContent);
|
|
1010
|
+
console.log(` wrote ${path7.relative(modulePath, shellFile)}`);
|
|
1011
|
+
generated++;
|
|
1012
|
+
if (!fs5.existsSync(implFile)) {
|
|
1013
|
+
fs5.writeFileSync(implFile, generateCommandStub(doc));
|
|
1014
|
+
console.log(` scaffolded ${path7.relative(modulePath, implFile)}`);
|
|
1015
|
+
}
|
|
1016
|
+
const testFile = path7.join(commandDir, `${doc.commandName}.test.ts`);
|
|
1017
|
+
if (!fs5.existsSync(testFile)) {
|
|
1018
|
+
fs5.writeFileSync(testFile, generateTestStub(doc));
|
|
1019
|
+
console.log(` scaffolded ${path7.relative(modulePath, testFile)}`);
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
if (fs5.existsSync(queryDocsDir)) {
|
|
1023
|
+
const queryFiles = fs5.readdirSync(queryDocsDir).filter((f) => f.endsWith(".md"));
|
|
1024
|
+
if (queryFiles.length > 0) {
|
|
1025
|
+
const queryDir = path7.join(modulePath, "query");
|
|
1026
|
+
fs5.mkdirSync(queryDir, { recursive: true });
|
|
1027
|
+
for (const file of queryFiles) {
|
|
1028
|
+
const content = fs5.readFileSync(path7.join(queryDocsDir, file), "utf-8");
|
|
1029
|
+
const name = path7.basename(file, ".md");
|
|
1030
|
+
const doc = parseCommandDoc(name, content);
|
|
1031
|
+
const shellFile = path7.join(queryDir, `${doc.commandName}.generated.ts`);
|
|
1032
|
+
fs5.writeFileSync(shellFile, generateQueryShell(doc));
|
|
1033
|
+
console.log(` wrote ${path7.relative(modulePath, shellFile)}`);
|
|
1034
|
+
generated++;
|
|
1035
|
+
const implFile = path7.join(queryDir, `${doc.commandName}.ts`);
|
|
1036
|
+
if (!fs5.existsSync(implFile)) {
|
|
1037
|
+
fs5.writeFileSync(implFile, generateQueryStub(doc));
|
|
1038
|
+
console.log(` scaffolded ${path7.relative(modulePath, implFile)}`);
|
|
1039
|
+
}
|
|
1040
|
+
const testFile = path7.join(queryDir, `${doc.commandName}.test.ts`);
|
|
1041
|
+
if (!fs5.existsSync(testFile)) {
|
|
1042
|
+
fs5.writeFileSync(testFile, generateQueryTestStub(doc));
|
|
1043
|
+
console.log(` scaffolded ${path7.relative(modulePath, testFile)}`);
|
|
1044
|
+
}
|
|
428
1045
|
}
|
|
429
|
-
} else {
|
|
430
|
-
console.log(chalk2.yellow(" Skipped .claude/skills (directory exists, not a symlink)"));
|
|
431
1046
|
}
|
|
432
|
-
} else {
|
|
433
|
-
fs3.mkdirSync(path6.dirname(claudeSkills), { recursive: true });
|
|
434
|
-
fs3.symlinkSync(relTarget, claudeSkills);
|
|
435
|
-
console.log(chalk2.green(" .claude/skills -> .agents/skills/ (linked)"));
|
|
436
1047
|
}
|
|
437
|
-
console.log(
|
|
1048
|
+
console.log(`Generated ${generated} file(s) for ${moduleName}`);
|
|
438
1049
|
return 0;
|
|
439
1050
|
}
|
|
440
1051
|
|
|
1052
|
+
// src/commands/module/generate.ts
|
|
1053
|
+
var cwd = process.cwd();
|
|
1054
|
+
var codeCommand = defineCommand({
|
|
1055
|
+
name: "code",
|
|
1056
|
+
description: "Generate errors, permissions, command shells, and query shells from docs",
|
|
1057
|
+
args: z.object({
|
|
1058
|
+
root: arg(z.string(), {
|
|
1059
|
+
alias: "r",
|
|
1060
|
+
description: "Path to modules directory"
|
|
1061
|
+
}),
|
|
1062
|
+
module: arg(z.string(), {
|
|
1063
|
+
positional: true,
|
|
1064
|
+
description: "Module name (e.g., primitives, item-management)"
|
|
1065
|
+
})
|
|
1066
|
+
}),
|
|
1067
|
+
run: (args) => {
|
|
1068
|
+
const modulePath = path8.resolve(cwd, args.root, args.module);
|
|
1069
|
+
console.log(`Generating code for ${args.module}...`);
|
|
1070
|
+
const exitCode = runGenerateCode(modulePath, args.module);
|
|
1071
|
+
process.exit(exitCode);
|
|
1072
|
+
}
|
|
1073
|
+
});
|
|
1074
|
+
var generateCommand = defineCommand({
|
|
1075
|
+
name: "generate",
|
|
1076
|
+
description: "Generate code from model definitions and docs",
|
|
1077
|
+
subCommands: {
|
|
1078
|
+
code: codeCommand
|
|
1079
|
+
}
|
|
1080
|
+
});
|
|
1081
|
+
|
|
1082
|
+
// src/commands/module/index.ts
|
|
1083
|
+
var cwd2 = process.cwd();
|
|
1084
|
+
var rootArgs = z2.object({
|
|
1085
|
+
root: arg2(z2.string(), {
|
|
1086
|
+
alias: "r",
|
|
1087
|
+
description: "Path to modules directory"
|
|
1088
|
+
})
|
|
1089
|
+
});
|
|
1090
|
+
var listCommand = defineCommand2({
|
|
1091
|
+
name: "list",
|
|
1092
|
+
description: "List available modules",
|
|
1093
|
+
run: () => {
|
|
1094
|
+
const exitCode = runModuleList();
|
|
1095
|
+
process.exit(exitCode);
|
|
1096
|
+
}
|
|
1097
|
+
});
|
|
1098
|
+
var checkCommand = defineCommand2({
|
|
1099
|
+
name: "check",
|
|
1100
|
+
description: "Validate module docs against schemas",
|
|
1101
|
+
args: rootArgs,
|
|
1102
|
+
run: async (args) => {
|
|
1103
|
+
const exitCode = await runCheck({ modulesRoot: args.root }, cwd2);
|
|
1104
|
+
process.exit(exitCode);
|
|
1105
|
+
}
|
|
1106
|
+
});
|
|
1107
|
+
var syncCheckCommand = defineCommand2({
|
|
1108
|
+
name: "sync-check",
|
|
1109
|
+
description: "Validate source <-> doc correspondence",
|
|
1110
|
+
args: rootArgs,
|
|
1111
|
+
run: async (args) => {
|
|
1112
|
+
const result = await runSyncCheck({ modulesRoot: args.root }, cwd2);
|
|
1113
|
+
console.log(formatSyncCheckReport(result));
|
|
1114
|
+
process.exit(result.exitCode);
|
|
1115
|
+
}
|
|
1116
|
+
});
|
|
1117
|
+
var scaffoldCommand = defineCommand2({
|
|
1118
|
+
name: "scaffold",
|
|
1119
|
+
description: "Generate module doc from schema template",
|
|
1120
|
+
args: rootArgs.extend({
|
|
1121
|
+
type: arg2(z2.enum(MODULE_TYPES), {
|
|
1122
|
+
positional: true,
|
|
1123
|
+
description: `Scaffold type (${MODULE_TYPES.join(", ")})`
|
|
1124
|
+
}),
|
|
1125
|
+
parent: arg2(z2.string(), {
|
|
1126
|
+
positional: true,
|
|
1127
|
+
description: "Module name"
|
|
1128
|
+
}),
|
|
1129
|
+
name: arg2(z2.string().optional(), {
|
|
1130
|
+
positional: true,
|
|
1131
|
+
description: "Item name (required for feature, command, model)"
|
|
1132
|
+
})
|
|
1133
|
+
}),
|
|
1134
|
+
run: async (args) => {
|
|
1135
|
+
const exitCode = await runScaffold(
|
|
1136
|
+
args.type,
|
|
1137
|
+
args.parent,
|
|
1138
|
+
args.name,
|
|
1139
|
+
args.root,
|
|
1140
|
+
cwd2
|
|
1141
|
+
);
|
|
1142
|
+
process.exit(exitCode);
|
|
1143
|
+
}
|
|
1144
|
+
});
|
|
1145
|
+
var moduleCommand = defineCommand2({
|
|
1146
|
+
name: "module",
|
|
1147
|
+
description: "Module management",
|
|
1148
|
+
subCommands: {
|
|
1149
|
+
list: listCommand,
|
|
1150
|
+
check: checkCommand,
|
|
1151
|
+
"sync-check": syncCheckCommand,
|
|
1152
|
+
scaffold: scaffoldCommand,
|
|
1153
|
+
generate: generateCommand
|
|
1154
|
+
}
|
|
1155
|
+
});
|
|
1156
|
+
|
|
1157
|
+
// src/commands/app/index.ts
|
|
1158
|
+
import { z as z3 } from "zod";
|
|
1159
|
+
import { defineCommand as defineCommand3, arg as arg3 } from "politty";
|
|
1160
|
+
var cwd3 = process.cwd();
|
|
1161
|
+
var rootArgs2 = z3.object({
|
|
1162
|
+
root: arg3(z3.string(), {
|
|
1163
|
+
alias: "r",
|
|
1164
|
+
description: "Path to app-compose directory"
|
|
1165
|
+
})
|
|
1166
|
+
});
|
|
1167
|
+
var checkCommand2 = defineCommand3({
|
|
1168
|
+
name: "check",
|
|
1169
|
+
description: "Validate app docs against schemas",
|
|
1170
|
+
args: rootArgs2,
|
|
1171
|
+
run: async (args) => {
|
|
1172
|
+
const exitCode = await runCheck({ appRoot: args.root }, cwd3);
|
|
1173
|
+
process.exit(exitCode);
|
|
1174
|
+
}
|
|
1175
|
+
});
|
|
1176
|
+
var syncCheckCommand2 = defineCommand3({
|
|
1177
|
+
name: "sync-check",
|
|
1178
|
+
description: "Validate source <-> doc correspondence",
|
|
1179
|
+
args: rootArgs2,
|
|
1180
|
+
run: async (args) => {
|
|
1181
|
+
const result = await runSyncCheck({ appRoot: args.root }, cwd3);
|
|
1182
|
+
console.log(formatSyncCheckReport(result));
|
|
1183
|
+
process.exit(result.exitCode);
|
|
1184
|
+
}
|
|
1185
|
+
});
|
|
1186
|
+
var scaffoldCommand2 = defineCommand3({
|
|
1187
|
+
name: "scaffold",
|
|
1188
|
+
description: "Generate app doc from schema template",
|
|
1189
|
+
args: rootArgs2.extend({
|
|
1190
|
+
type: arg3(z3.enum(APP_TYPES), {
|
|
1191
|
+
positional: true,
|
|
1192
|
+
description: `Scaffold type (${APP_TYPES.join(", ")})`
|
|
1193
|
+
}),
|
|
1194
|
+
parent: arg3(z3.string(), {
|
|
1195
|
+
positional: true,
|
|
1196
|
+
description: "App name"
|
|
1197
|
+
}),
|
|
1198
|
+
name: arg3(z3.string().optional(), {
|
|
1199
|
+
positional: true,
|
|
1200
|
+
description: "Item name (required for most types)"
|
|
1201
|
+
})
|
|
1202
|
+
}),
|
|
1203
|
+
run: async (args) => {
|
|
1204
|
+
const exitCode = await runScaffold(
|
|
1205
|
+
args.type,
|
|
1206
|
+
args.parent,
|
|
1207
|
+
args.name,
|
|
1208
|
+
args.root,
|
|
1209
|
+
cwd3
|
|
1210
|
+
);
|
|
1211
|
+
process.exit(exitCode);
|
|
1212
|
+
}
|
|
1213
|
+
});
|
|
1214
|
+
var appCommand = defineCommand3({
|
|
1215
|
+
name: "app",
|
|
1216
|
+
description: "App-compose management",
|
|
1217
|
+
subCommands: {
|
|
1218
|
+
check: checkCommand2,
|
|
1219
|
+
"sync-check": syncCheckCommand2,
|
|
1220
|
+
scaffold: scaffoldCommand2
|
|
1221
|
+
}
|
|
1222
|
+
});
|
|
1223
|
+
|
|
441
1224
|
// src/commands/mock/index.ts
|
|
442
|
-
import { z } from "zod";
|
|
443
|
-
import { defineCommand, arg } from "politty";
|
|
1225
|
+
import { z as z4 } from "zod";
|
|
1226
|
+
import { defineCommand as defineCommand4, arg as arg4 } from "politty";
|
|
444
1227
|
|
|
445
1228
|
// src/commands/mock/start.ts
|
|
446
1229
|
import { createServer, request as httpRequest } from "http";
|
|
447
1230
|
import { createServer as createNetServer } from "net";
|
|
448
|
-
import { existsSync, readdirSync } from "fs";
|
|
449
|
-
import { resolve as resolve2, relative, join } from "path";
|
|
1231
|
+
import { existsSync as existsSync2, readdirSync as readdirSync2 } from "fs";
|
|
1232
|
+
import { resolve as resolve2, relative, join as join2 } from "path";
|
|
450
1233
|
|
|
451
1234
|
// src/mockServer.ts
|
|
452
1235
|
import { readFileSync } from "fs";
|
|
@@ -489,7 +1272,7 @@ async function createMockServer(mockJsonPath, port) {
|
|
|
489
1272
|
// src/commands/mock/start.ts
|
|
490
1273
|
function readdirSafe(dir) {
|
|
491
1274
|
try {
|
|
492
|
-
return
|
|
1275
|
+
return readdirSync2(dir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
493
1276
|
} catch {
|
|
494
1277
|
return [];
|
|
495
1278
|
}
|
|
@@ -497,10 +1280,10 @@ function readdirSafe(dir) {
|
|
|
497
1280
|
function discoverMocks(mocksDir, filters) {
|
|
498
1281
|
const mocks = [];
|
|
499
1282
|
for (const provider of readdirSafe(mocksDir)) {
|
|
500
|
-
const providerDir =
|
|
1283
|
+
const providerDir = join2(mocksDir, provider);
|
|
501
1284
|
for (const scenario of readdirSafe(providerDir)) {
|
|
502
|
-
const mockPath =
|
|
503
|
-
if (!
|
|
1285
|
+
const mockPath = join2(providerDir, scenario, "mock.json");
|
|
1286
|
+
if (!existsSync2(mockPath)) continue;
|
|
504
1287
|
mocks.push({ provider, scenario, mockPath });
|
|
505
1288
|
}
|
|
506
1289
|
}
|
|
@@ -619,20 +1402,20 @@ Reverse proxy listening on http://localhost:${port}`);
|
|
|
619
1402
|
|
|
620
1403
|
// src/commands/mock/validate.ts
|
|
621
1404
|
import { readdir, readFile, stat } from "fs/promises";
|
|
622
|
-
import { join as
|
|
623
|
-
import
|
|
1405
|
+
import { join as join3, resolve as resolve3, relative as relative2, dirname as dirname2, basename } from "path";
|
|
1406
|
+
import chalk4 from "chalk";
|
|
624
1407
|
function pass(msg) {
|
|
625
|
-
console.log(
|
|
1408
|
+
console.log(chalk4.green(`\u2713 ${msg}`));
|
|
626
1409
|
}
|
|
627
1410
|
function fail(ctx, msg) {
|
|
628
|
-
console.log(
|
|
1411
|
+
console.log(chalk4.red(`\u2717 ${msg}`));
|
|
629
1412
|
ctx.failures++;
|
|
630
1413
|
}
|
|
631
1414
|
async function subdirs(dir) {
|
|
632
1415
|
const entries = await readdir(dir);
|
|
633
1416
|
const dirs = [];
|
|
634
1417
|
for (const entry of entries) {
|
|
635
|
-
const full =
|
|
1418
|
+
const full = join3(dir, entry);
|
|
636
1419
|
const s = await stat(full);
|
|
637
1420
|
if (s.isDirectory()) dirs.push(entry);
|
|
638
1421
|
}
|
|
@@ -702,21 +1485,21 @@ async function discoverAllScenarios(mocksDir) {
|
|
|
702
1485
|
const scenarios = [];
|
|
703
1486
|
const providers = await subdirs(mocksDir);
|
|
704
1487
|
for (const provider of providers) {
|
|
705
|
-
const providerDir =
|
|
1488
|
+
const providerDir = join3(mocksDir, provider);
|
|
706
1489
|
for (const scenario of await subdirs(providerDir)) {
|
|
707
1490
|
scenarios.push(`${provider}/${scenario}`);
|
|
708
1491
|
}
|
|
709
1492
|
}
|
|
710
1493
|
return scenarios;
|
|
711
1494
|
}
|
|
712
|
-
function resolveScenarioDir(
|
|
713
|
-
const abs = resolve3(
|
|
1495
|
+
function resolveScenarioDir(arg6) {
|
|
1496
|
+
const abs = resolve3(arg6);
|
|
714
1497
|
if (basename(abs) === "mock.json") return dirname2(abs);
|
|
715
1498
|
return abs;
|
|
716
1499
|
}
|
|
717
1500
|
async function validateScenario(ctx, scenarioDir, mocksDir) {
|
|
718
1501
|
const label = relative2(mocksDir, scenarioDir);
|
|
719
|
-
console.log(
|
|
1502
|
+
console.log(chalk4.bold(`
|
|
720
1503
|
\u2500\u2500 ${label} \u2500\u2500`));
|
|
721
1504
|
let entries;
|
|
722
1505
|
try {
|
|
@@ -727,7 +1510,7 @@ async function validateScenario(ctx, scenarioDir, mocksDir) {
|
|
|
727
1510
|
}
|
|
728
1511
|
const hasMock = checkStructure(ctx, entries, label);
|
|
729
1512
|
if (!hasMock) return;
|
|
730
|
-
const mockPath =
|
|
1513
|
+
const mockPath = join3(scenarioDir, "mock.json");
|
|
731
1514
|
let data;
|
|
732
1515
|
try {
|
|
733
1516
|
data = JSON.parse(await readFile(mockPath, "utf-8"));
|
|
@@ -743,37 +1526,37 @@ async function validateScenario(ctx, scenarioDir, mocksDir) {
|
|
|
743
1526
|
async function runMockValidate(mocksRoot, paths) {
|
|
744
1527
|
const ctx = { failures: 0 };
|
|
745
1528
|
const mocksDir = resolve3(mocksRoot);
|
|
746
|
-
const targets = paths.length > 0 ? paths.map(resolveScenarioDir) : (await discoverAllScenarios(mocksDir)).map((s) =>
|
|
1529
|
+
const targets = paths.length > 0 ? paths.map(resolveScenarioDir) : (await discoverAllScenarios(mocksDir)).map((s) => join3(mocksDir, s));
|
|
747
1530
|
if (targets.length === 0) {
|
|
748
1531
|
fail(ctx, "No scenarios found under mocks/");
|
|
749
1532
|
return 1;
|
|
750
1533
|
}
|
|
751
|
-
console.log(
|
|
1534
|
+
console.log(chalk4.bold("\nValidating mock configs...\n"));
|
|
752
1535
|
for (const target of targets) {
|
|
753
1536
|
await validateScenario(ctx, target, mocksDir);
|
|
754
1537
|
}
|
|
755
|
-
console.log(
|
|
1538
|
+
console.log(chalk4.bold("\n\u2500\u2500 summary \u2500\u2500"));
|
|
756
1539
|
if (ctx.failures === 0) {
|
|
757
|
-
console.log(
|
|
1540
|
+
console.log(chalk4.green("\u2713 All checks passed"));
|
|
758
1541
|
} else {
|
|
759
|
-
console.log(
|
|
1542
|
+
console.log(chalk4.red(`\u2717 ${ctx.failures} check(s) failed`));
|
|
760
1543
|
}
|
|
761
1544
|
return ctx.failures === 0 ? 0 : 1;
|
|
762
1545
|
}
|
|
763
1546
|
|
|
764
1547
|
// src/commands/mock/index.ts
|
|
765
|
-
var startCommand =
|
|
1548
|
+
var startCommand = defineCommand4({
|
|
766
1549
|
name: "start",
|
|
767
1550
|
description: "Start mock API servers with reverse proxy",
|
|
768
|
-
args:
|
|
769
|
-
mocksRoot:
|
|
1551
|
+
args: z4.object({
|
|
1552
|
+
mocksRoot: arg4(z4.string().default("./mocks"), {
|
|
770
1553
|
description: "Path to mocks directory"
|
|
771
1554
|
}),
|
|
772
|
-
port:
|
|
1555
|
+
port: arg4(z4.coerce.number().default(3e3), {
|
|
773
1556
|
alias: "p",
|
|
774
1557
|
description: "Reverse proxy port"
|
|
775
1558
|
}),
|
|
776
|
-
filter:
|
|
1559
|
+
filter: arg4(z4.array(z4.string()).default([]), {
|
|
777
1560
|
positional: true,
|
|
778
1561
|
description: "Filter by provider or provider/scenario"
|
|
779
1562
|
})
|
|
@@ -783,14 +1566,14 @@ var startCommand = defineCommand({
|
|
|
783
1566
|
process.exit(exitCode);
|
|
784
1567
|
}
|
|
785
1568
|
});
|
|
786
|
-
var validateCommand =
|
|
1569
|
+
var validateCommand = defineCommand4({
|
|
787
1570
|
name: "validate",
|
|
788
1571
|
description: "Validate mock scenario configs",
|
|
789
|
-
args:
|
|
790
|
-
mocksRoot:
|
|
1572
|
+
args: z4.object({
|
|
1573
|
+
mocksRoot: arg4(z4.string().default("./mocks"), {
|
|
791
1574
|
description: "Path to mocks directory"
|
|
792
1575
|
}),
|
|
793
|
-
paths:
|
|
1576
|
+
paths: arg4(z4.array(z4.string()).default([]), {
|
|
794
1577
|
positional: true,
|
|
795
1578
|
description: "Specific scenario paths to validate"
|
|
796
1579
|
})
|
|
@@ -800,7 +1583,7 @@ var validateCommand = defineCommand({
|
|
|
800
1583
|
process.exit(exitCode);
|
|
801
1584
|
}
|
|
802
1585
|
});
|
|
803
|
-
var mockCommand =
|
|
1586
|
+
var mockCommand = defineCommand4({
|
|
804
1587
|
name: "mock",
|
|
805
1588
|
description: "Mock API server management",
|
|
806
1589
|
subCommands: {
|
|
@@ -809,106 +1592,63 @@ var mockCommand = defineCommand({
|
|
|
809
1592
|
}
|
|
810
1593
|
});
|
|
811
1594
|
|
|
812
|
-
// src/
|
|
813
|
-
var
|
|
814
|
-
var
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
1595
|
+
// src/commands/index.ts
|
|
1596
|
+
var cwd4 = process.cwd();
|
|
1597
|
+
var initCommand = defineCommand5({
|
|
1598
|
+
name: "init",
|
|
1599
|
+
description: "Set up consumer repo with framework skills",
|
|
1600
|
+
args: z5.object({
|
|
1601
|
+
force: arg5(z5.boolean().default(false), {
|
|
1602
|
+
alias: "f",
|
|
1603
|
+
description: "Overwrite existing skills"
|
|
1604
|
+
})
|
|
818
1605
|
}),
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
description: "Path to app-compose directory (apps/ or examples/)"
|
|
822
|
-
})
|
|
823
|
-
});
|
|
824
|
-
function requireRoot(args) {
|
|
825
|
-
const paths = { modulesRoot: args.modulesRoot, appRoot: args.appRoot };
|
|
826
|
-
if (!paths.modulesRoot && !paths.appRoot) {
|
|
827
|
-
console.error("At least one of --modules-root or --app-root is required.");
|
|
828
|
-
process.exit(2);
|
|
829
|
-
}
|
|
830
|
-
return paths;
|
|
831
|
-
}
|
|
832
|
-
var checkCommand = defineCommand2({
|
|
833
|
-
name: "check",
|
|
834
|
-
description: "Validate docs against schemas",
|
|
835
|
-
args: rootArgs,
|
|
836
|
-
run: async (args) => {
|
|
837
|
-
const paths = requireRoot(args);
|
|
838
|
-
const exitCode = await runCheck(paths, cwd);
|
|
1606
|
+
run: (args) => {
|
|
1607
|
+
const exitCode = runInit(cwd4, args.force);
|
|
839
1608
|
process.exit(exitCode);
|
|
840
1609
|
}
|
|
841
1610
|
});
|
|
842
|
-
var
|
|
843
|
-
name: "
|
|
844
|
-
description: "
|
|
845
|
-
args:
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
console.log(formatSyncCheckReport(result));
|
|
850
|
-
process.exit(result.exitCode);
|
|
851
|
-
}
|
|
852
|
-
});
|
|
853
|
-
var scaffoldCommand = defineCommand2({
|
|
854
|
-
name: "scaffold",
|
|
855
|
-
description: "Generate doc file from schema template",
|
|
856
|
-
args: rootArgs.extend({
|
|
857
|
-
type: arg2(z2.enum(ALL_TYPES), {
|
|
858
|
-
positional: true,
|
|
859
|
-
description: `Scaffold type (${ALL_TYPES.join(", ")})`
|
|
860
|
-
}),
|
|
861
|
-
parent: arg2(z2.string(), {
|
|
862
|
-
positional: true,
|
|
863
|
-
description: "Parent name (module or app name)"
|
|
864
|
-
}),
|
|
865
|
-
name: arg2(z2.string().optional(), {
|
|
866
|
-
positional: true,
|
|
867
|
-
description: "Item name (required for most types)"
|
|
1611
|
+
var licenseCheckCommand = defineCommand5({
|
|
1612
|
+
name: "check",
|
|
1613
|
+
description: "Check dependency licenses against allowlist",
|
|
1614
|
+
args: z5.object({
|
|
1615
|
+
config: arg5(z5.string(), {
|
|
1616
|
+
alias: "c",
|
|
1617
|
+
description: "Path to license config JSON file"
|
|
868
1618
|
})
|
|
869
1619
|
}),
|
|
870
|
-
run:
|
|
871
|
-
const
|
|
872
|
-
const root = isModuleType(args.type) ? paths.modulesRoot : paths.appRoot;
|
|
873
|
-
if (!root) {
|
|
874
|
-
console.error(
|
|
875
|
-
`--${isModuleType(args.type) ? "modules-root" : "app-root"} is required for scaffold type "${args.type}".`
|
|
876
|
-
);
|
|
877
|
-
process.exit(2);
|
|
878
|
-
}
|
|
879
|
-
const exitCode = await runScaffold(
|
|
880
|
-
args.type,
|
|
881
|
-
args.parent,
|
|
882
|
-
args.name,
|
|
883
|
-
root,
|
|
884
|
-
cwd
|
|
885
|
-
);
|
|
1620
|
+
run: (args) => {
|
|
1621
|
+
const exitCode = runLicenseCheck(args.config);
|
|
886
1622
|
process.exit(exitCode);
|
|
887
1623
|
}
|
|
888
1624
|
});
|
|
889
|
-
var
|
|
890
|
-
name: "
|
|
891
|
-
description: "
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
alias: "f",
|
|
895
|
-
description: "Overwrite existing skills"
|
|
896
|
-
})
|
|
897
|
-
}),
|
|
898
|
-
run: (args) => {
|
|
899
|
-
const exitCode = runInit(cwd, args.force);
|
|
1625
|
+
var licenseListCommand = defineCommand5({
|
|
1626
|
+
name: "list",
|
|
1627
|
+
description: "List available license groups",
|
|
1628
|
+
run: () => {
|
|
1629
|
+
const exitCode = runLicenseList();
|
|
900
1630
|
process.exit(exitCode);
|
|
901
1631
|
}
|
|
902
1632
|
});
|
|
903
|
-
var
|
|
1633
|
+
var licenseCommand = defineCommand5({
|
|
1634
|
+
name: "license",
|
|
1635
|
+
description: "License management",
|
|
1636
|
+
subCommands: {
|
|
1637
|
+
check: licenseCheckCommand,
|
|
1638
|
+
list: licenseListCommand
|
|
1639
|
+
}
|
|
1640
|
+
});
|
|
1641
|
+
var mainCommand = defineCommand5({
|
|
904
1642
|
name: "erp-kit",
|
|
905
|
-
description: "
|
|
1643
|
+
description: "ERP module framework CLI",
|
|
906
1644
|
subCommands: {
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
1645
|
+
module: moduleCommand,
|
|
1646
|
+
app: appCommand,
|
|
1647
|
+
mock: mockCommand,
|
|
910
1648
|
init: initCommand,
|
|
911
|
-
|
|
1649
|
+
license: licenseCommand
|
|
912
1650
|
}
|
|
913
1651
|
});
|
|
914
|
-
|
|
1652
|
+
|
|
1653
|
+
// src/cli.ts
|
|
1654
|
+
void runMain(mainCommand);
|