openxiangda 1.0.139 → 1.0.140
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 +1 -1
- package/lib/cli.js +45 -19
- package/lib/sdd.js +783 -101
- package/openxiangda-skills/SKILL.md +1 -1
- package/openxiangda-skills/skills/openxiangda-core/SKILL.md +2 -2
- package/package.json +1 -1
- package/templates/openxiangda-react-spa/.cursor/rules/openxiangda.mdc +1 -1
- package/templates/openxiangda-react-spa/.qoder/rules/openxiangda.md +1 -1
- package/templates/openxiangda-react-spa/AGENTS.md +1 -1
- package/templates/openxiangda-react-spa/app-workspace.config.ts +1 -0
- package/templates/sy-lowcode-app-workspace/.cursor/rules/openxiangda.mdc +1 -1
- package/templates/sy-lowcode-app-workspace/.qoder/rules/openxiangda.md +1 -1
- package/templates/sy-lowcode-app-workspace/AGENTS.md +3 -3
- package/templates/sy-lowcode-app-workspace/app-workspace.config.ts +1 -0
- package/templates/sy-lowcode-app-workspace/src/types/app-workspace.types.ts +1 -0
package/README.md
CHANGED
|
@@ -82,7 +82,7 @@ Domestic npm mirrors may lag and return an older OpenXiangda version. `openxiang
|
|
|
82
82
|
|
|
83
83
|
Architecture-class requests are plan-gated by default. For new apps, complex pages, login/register, public/no-login access, role/data-scope design, workflow/automation, App Function, connector, notification, and external integration work, AI agents must plan first and implement only after the user confirms the design.
|
|
84
84
|
|
|
85
|
-
High-risk implementation is also SDD-gated. New workspaces include `openspec/` by default. Before changing forms, pages, resources, functions, automations, workflows, JS_CODE nodes, runtime code, or `app-workspace.config.ts`, run `openxiangda sdd context --json`, create or continue a change with `openxiangda sdd propose <change>`, and wait for `openxiangda sdd approve <change>`.
|
|
85
|
+
High-risk implementation is also SDD-gated. New workspaces include OpenXiangda SDD v2 records under `openspec/` by default. Before changing forms, pages, resources, functions, automations, workflows, JS_CODE nodes, runtime code, or `app-workspace.config.ts`, run `openxiangda sdd context --json`, create or continue a change with `openxiangda sdd propose <change>`, and wait for `openxiangda sdd approve <change>`. `approve` only opens the implementation gate; release checks also require `coverage.json`, `release.json`, `evidence.md`, `tasks.md`, and delta specs to pass `openxiangda sdd verify <change> --changed`. Emergency releases can use `--sdd-bypass --reason "..."`, which writes `openspec/changes/bypass-log.md` and appends bypass evidence to the active approved change when one exists.
|
|
86
86
|
|
|
87
87
|
Before confirmation, agents may read, inspect, snapshot, dry-run, ask questions, and output/write the architecture document. They must not edit source files, mutate platform resources, publish, deploy, send notifications, or call live write/delete endpoints.
|
|
88
88
|
|
package/lib/cli.js
CHANGED
|
@@ -127,7 +127,7 @@ Usage:
|
|
|
127
127
|
openxiangda doctor [--profile name] [--app-type APP_XXX] [--release] [--json]
|
|
128
128
|
openxiangda contract list|explain <contract-name> [--json]
|
|
129
129
|
openxiangda design gates|template|review [--topic code] [--json]
|
|
130
|
-
openxiangda sdd init|propose|approve|status|context|verify|sync|archive [change] [--json]
|
|
130
|
+
openxiangda sdd init|migrate|propose|approve|status|context|verify|sync|archive [change] [--json]
|
|
131
131
|
openxiangda env [--profile name]
|
|
132
132
|
openxiangda workspace init [dir] [--name package-name] [--runtime legacy|react-spa] [--install] [--profile name --app-type APP_XXX]
|
|
133
133
|
openxiangda workspace init [dir] --profile <name> --app-name <app-name> [--runtime legacy|react-spa] [--install]
|
|
@@ -531,26 +531,28 @@ async function sdd(args) {
|
|
|
531
531
|
const { flags, positional } = parseArgs(rest);
|
|
532
532
|
if (wantsSubcommandHelp(subcommand, flags)) {
|
|
533
533
|
print([
|
|
534
|
-
'用法: openxiangda sdd init|propose|approve|status|context|verify|sync|archive [change] [--json]',
|
|
534
|
+
'用法: openxiangda sdd init|migrate|propose|approve|status|context|verify|sync|archive [change] [--json]',
|
|
535
535
|
'常用流程:',
|
|
536
|
-
' openxiangda sdd init',
|
|
537
|
-
' openxiangda sdd propose add-customer-list --title "客户列表" --forms customer --pages customer_list',
|
|
536
|
+
' openxiangda sdd init --upgrade',
|
|
537
|
+
' openxiangda sdd propose add-customer-list --title "客户列表" --forms customer --pages customer_list --resources',
|
|
538
538
|
' openxiangda sdd approve add-customer-list --summary "用户确认设计"',
|
|
539
539
|
' openxiangda sdd context --json',
|
|
540
540
|
' openxiangda sdd verify add-customer-list --changed --json',
|
|
541
541
|
' openxiangda sdd archive add-customer-list',
|
|
542
542
|
'说明:',
|
|
543
543
|
' - SDD 记录保存在 openspec/,不依赖官方 OpenSpec CLI。',
|
|
544
|
-
' -
|
|
544
|
+
' - 新工作区使用 openxiangda-sdd-v2:proposal/specs/design/tasks/evidence 给人读,coverage.json/release.json 给 CLI 校验。',
|
|
545
|
+
' - 高风险发布需要 approved change 的 coverage 覆盖本次 forms/pages/resources/runtime 目标和高风险文件。',
|
|
545
546
|
].join('\n'));
|
|
546
547
|
return;
|
|
547
548
|
}
|
|
548
549
|
|
|
549
|
-
if (subcommand === 'init') {
|
|
550
|
+
if (subcommand === 'init' || subcommand === 'migrate' || subcommand === 'upgrade') {
|
|
550
551
|
const result = initSddWorkspace({
|
|
551
552
|
cwd: process.cwd(),
|
|
552
553
|
path: flags.path,
|
|
553
554
|
force: Boolean(flags.force),
|
|
555
|
+
upgrade: Boolean(flags.upgrade || subcommand === 'migrate' || subcommand === 'upgrade'),
|
|
554
556
|
});
|
|
555
557
|
if (flags.json) return writeJson(result);
|
|
556
558
|
printSddInitReport(result);
|
|
@@ -586,13 +588,17 @@ async function sdd(args) {
|
|
|
586
588
|
}
|
|
587
589
|
|
|
588
590
|
if (subcommand === 'status' || subcommand === 'context') {
|
|
589
|
-
const status = buildSddStatus({ cwd: process.cwd(), configText: readWorkspaceConfigText() });
|
|
590
591
|
const releasePlan = buildWorkspaceReleasePlan({
|
|
591
592
|
changed: Boolean(flags.changed || flags['changed-only']),
|
|
592
593
|
since: flags.since,
|
|
593
594
|
profile: flags.profile || '<profile>',
|
|
594
595
|
skipSdd: true,
|
|
595
596
|
});
|
|
597
|
+
const status = buildSddStatus({
|
|
598
|
+
cwd: process.cwd(),
|
|
599
|
+
configText: readWorkspaceConfigText(),
|
|
600
|
+
releasePlan,
|
|
601
|
+
});
|
|
596
602
|
const result = {
|
|
597
603
|
...status,
|
|
598
604
|
releasePlan,
|
|
@@ -667,7 +673,7 @@ async function sdd(args) {
|
|
|
667
673
|
return;
|
|
668
674
|
}
|
|
669
675
|
|
|
670
|
-
fail('用法: openxiangda sdd init|propose|approve|status|context|verify|sync|archive [change] [--json]');
|
|
676
|
+
fail('用法: openxiangda sdd init|migrate|propose|approve|status|context|verify|sync|archive [change] [--json]');
|
|
671
677
|
}
|
|
672
678
|
|
|
673
679
|
function parseSddAffectedFlags(flags) {
|
|
@@ -681,6 +687,11 @@ function parseSddAffectedFlags(flags) {
|
|
|
681
687
|
return {
|
|
682
688
|
forms: split(flags.forms || flags.form),
|
|
683
689
|
pages: split(flags.pages || flags.page),
|
|
690
|
+
functions: split(flags.functions || flags.function),
|
|
691
|
+
automations: split(flags.automations || flags.automation),
|
|
692
|
+
workflows: split(flags.workflows || flags.workflow),
|
|
693
|
+
jsCodeNodes: split(flags['js-code-nodes'] || flags.jsCodeNodes || flags['js-code']),
|
|
694
|
+
files: split(flags.files || flags.file),
|
|
684
695
|
resources: Boolean(flags.resources),
|
|
685
696
|
runtime: Boolean(flags.runtime),
|
|
686
697
|
backend: Boolean(flags.backend),
|
|
@@ -689,7 +700,7 @@ function parseSddAffectedFlags(flags) {
|
|
|
689
700
|
}
|
|
690
701
|
|
|
691
702
|
function printSddInitReport(result) {
|
|
692
|
-
const lines = [`OpenXiangda SDD initialized: ${result.path}`];
|
|
703
|
+
const lines = [`OpenXiangda SDD initialized: ${result.path}`, `schemaVersion: ${result.schemaVersion || 'unknown'}`];
|
|
693
704
|
for (const op of result.operations) {
|
|
694
705
|
lines.push(`- ${op.action}: ${op.path}`);
|
|
695
706
|
}
|
|
@@ -700,12 +711,18 @@ function printSddStatusReport(result) {
|
|
|
700
711
|
const lines = [
|
|
701
712
|
`OpenXiangda SDD status: ${result.governance.initialized ? 'initialized' : 'not initialized'}`,
|
|
702
713
|
`path: ${result.governance.path}`,
|
|
714
|
+
`schemaVersion: ${result.schemaVersion || result.governance.schemaVersion || 'unknown'}`,
|
|
703
715
|
`strictHighRisk: ${result.governance.strictHighRisk ? 'true' : 'false'}`,
|
|
704
716
|
`active changes: ${result.activeChanges.length}`,
|
|
705
717
|
];
|
|
706
718
|
for (const change of result.activeChanges) {
|
|
707
719
|
lines.push(`- ${change.id}: ${change.status}${change.approvedAt ? ` approvedAt=${change.approvedAt}` : ''}`);
|
|
708
720
|
}
|
|
721
|
+
if (result.activeChange) lines.push(`activeChange: ${result.activeChange.id}`);
|
|
722
|
+
if (result.blockingErrors?.length) {
|
|
723
|
+
lines.push('blockingErrors:');
|
|
724
|
+
for (const error of result.blockingErrors) lines.push(`- ${error.name}: ${error.message}`);
|
|
725
|
+
}
|
|
709
726
|
if (result.bypassLog) lines.push(`bypass log: ${result.bypassLog}`);
|
|
710
727
|
print(lines.join('\n'));
|
|
711
728
|
}
|
|
@@ -714,6 +731,7 @@ function printSddVerifyReport(result) {
|
|
|
714
731
|
const lines = [
|
|
715
732
|
`OpenXiangda SDD verify: ${result.passed ? 'passed' : 'failed'}`,
|
|
716
733
|
`change: ${result.change.id}`,
|
|
734
|
+
`schemaVersion: ${result.schemaVersion || result.change.schemaVersion || 'unknown'}`,
|
|
717
735
|
];
|
|
718
736
|
if (result.errors.length > 0) {
|
|
719
737
|
lines.push('Errors:');
|
|
@@ -4629,7 +4647,10 @@ async function resource(args) {
|
|
|
4629
4647
|
assertSddReleaseGate(
|
|
4630
4648
|
buildExplicitSddReleasePlan(
|
|
4631
4649
|
{ forms: [], pages: [], resources: true, runtime: false, other: [] },
|
|
4632
|
-
{
|
|
4650
|
+
{
|
|
4651
|
+
files: getWorkspaceChangedFiles(),
|
|
4652
|
+
commands: [`openxiangda resource publish --profile ${profileName}`],
|
|
4653
|
+
}
|
|
4633
4654
|
),
|
|
4634
4655
|
flags,
|
|
4635
4656
|
'openxiangda resource publish'
|
|
@@ -4696,7 +4717,10 @@ async function runtime(args) {
|
|
|
4696
4717
|
assertSddReleaseGate(
|
|
4697
4718
|
buildExplicitSddReleasePlan(
|
|
4698
4719
|
{ forms: [], pages: [], resources: false, runtime: true, other: [] },
|
|
4699
|
-
{
|
|
4720
|
+
{
|
|
4721
|
+
files: getWorkspaceChangedFiles(),
|
|
4722
|
+
commands: [`openxiangda runtime deploy --profile ${target.profileName}`],
|
|
4723
|
+
}
|
|
4700
4724
|
),
|
|
4701
4725
|
flags,
|
|
4702
4726
|
'openxiangda runtime deploy'
|
|
@@ -5313,7 +5337,7 @@ async function commands(args) {
|
|
|
5313
5337
|
'doctor [--profile name] [--app-type APP_XXX] [--release]',
|
|
5314
5338
|
'contract list|explain <contract-name>',
|
|
5315
5339
|
'design gates|template|review [--topic code]',
|
|
5316
|
-
'sdd init|propose|approve|status|context|verify|sync|archive',
|
|
5340
|
+
'sdd init|migrate|propose|approve|status|context|verify|sync|archive',
|
|
5317
5341
|
'env',
|
|
5318
5342
|
'workspace init|bind|plan|check|publish [--app-name] [--changed|--since|--form|--page|--only|--dry-run|--force|--resources|--skip-resources|--prune]',
|
|
5319
5343
|
'app list|create|snapshot',
|
|
@@ -5347,7 +5371,7 @@ async function commands(args) {
|
|
|
5347
5371
|
'public-access is the new React SPA public policy resource for /view/:appType/public/* routes; page code should use PublicAccessGate, and Page SDK hooks must stay inside OpenXiangdaProvider + OpenXiangdaPageProvider.',
|
|
5348
5372
|
'settings public-access is legacy form public-access compatibility/repair and should not be used for new React SPA apps.',
|
|
5349
5373
|
'Direct live mutation commands should use --dry-run first and --write-manifest when the repository should remain source of truth.',
|
|
5350
|
-
'OpenXiangda native SDD uses openspec/ records; high-risk publishes require an approved change unless --sdd-bypass --reason is used.',
|
|
5374
|
+
'OpenXiangda native SDD uses openspec/ records; high-risk publishes require an approved change with coverage.json/release.json verification unless --sdd-bypass --reason is used.',
|
|
5351
5375
|
],
|
|
5352
5376
|
};
|
|
5353
5377
|
if (flags.json) return writeJson(manifest);
|
|
@@ -12367,6 +12391,7 @@ function buildWorkspaceReleasePlan(options = {}) {
|
|
|
12367
12391
|
? getWorkspaceChangedFiles({ since: options.since })
|
|
12368
12392
|
: getAllWorkspaceSourceFiles();
|
|
12369
12393
|
const targets = classifyWorkspaceFiles(files, runtimeMode);
|
|
12394
|
+
if (options.includeResources) targets.resources = true;
|
|
12370
12395
|
const issues = scanWorkspaceReliabilityIssues(files);
|
|
12371
12396
|
const errors = issues
|
|
12372
12397
|
.filter(issue => issue.severity === 'error')
|
|
@@ -12382,6 +12407,7 @@ function buildWorkspaceReleasePlan(options = {}) {
|
|
|
12382
12407
|
configText,
|
|
12383
12408
|
targets,
|
|
12384
12409
|
files,
|
|
12410
|
+
commands,
|
|
12385
12411
|
});
|
|
12386
12412
|
const sddErrors = sdd ? sdd.errors.map(error => ({ name: error.name, message: error.message })) : [];
|
|
12387
12413
|
const checks = [
|
|
@@ -12471,14 +12497,17 @@ function printWorkspaceReleasePlan(plan) {
|
|
|
12471
12497
|
|
|
12472
12498
|
function buildExplicitSddReleasePlan(targets, options = {}) {
|
|
12473
12499
|
const configText = readWorkspaceConfigText();
|
|
12500
|
+
const commands = options.commands || [];
|
|
12474
12501
|
const sdd = buildSddGovernanceReport({
|
|
12475
12502
|
cwd: process.cwd(),
|
|
12476
12503
|
configText,
|
|
12477
12504
|
targets,
|
|
12478
12505
|
files: options.files || [],
|
|
12506
|
+
commands,
|
|
12479
12507
|
});
|
|
12480
12508
|
return {
|
|
12481
12509
|
targets,
|
|
12510
|
+
commands,
|
|
12482
12511
|
sdd,
|
|
12483
12512
|
errors: sdd.errors.map(error => ({ name: error.name, message: error.message })),
|
|
12484
12513
|
};
|
|
@@ -12503,15 +12532,12 @@ function assertSddReleaseGate(plan, flags, command) {
|
|
|
12503
12532
|
|
|
12504
12533
|
function buildWorkspacePublishSddPlan(publishOptions, runtimeMode, profile) {
|
|
12505
12534
|
if (publishOptions.changed || (!publishOptions.targetForm && !publishOptions.targetPage && !publishOptions.only)) {
|
|
12506
|
-
|
|
12535
|
+
return buildWorkspaceReleasePlan({
|
|
12507
12536
|
changed: publishOptions.changed,
|
|
12508
12537
|
since: publishOptions.since,
|
|
12509
12538
|
profile,
|
|
12539
|
+
includeResources: publishOptions.includeResources,
|
|
12510
12540
|
});
|
|
12511
|
-
if (publishOptions.includeResources) {
|
|
12512
|
-
plan.targets.resources = true;
|
|
12513
|
-
}
|
|
12514
|
-
return plan;
|
|
12515
12541
|
}
|
|
12516
12542
|
const targets = {
|
|
12517
12543
|
forms: [],
|
|
@@ -12536,7 +12562,7 @@ function buildWorkspacePublishSddPlan(publishOptions, runtimeMode, profile) {
|
|
|
12536
12562
|
targets.forms = unique(targets.forms).sort();
|
|
12537
12563
|
targets.pages = unique(targets.pages).sort();
|
|
12538
12564
|
const commands = buildWorkspaceReleaseCommands(targets, runtimeMode, profile);
|
|
12539
|
-
const sddPlan = buildExplicitSddReleasePlan(targets, { files: [] });
|
|
12565
|
+
const sddPlan = buildExplicitSddReleasePlan(targets, { files: [], commands });
|
|
12540
12566
|
return {
|
|
12541
12567
|
workspace: {
|
|
12542
12568
|
cwd: process.cwd(),
|