openxiangda-skill-kit 2.0.0-alpha.100
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 +10 -0
- package/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +35 -0
- package/dist/bin.js.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +309 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/skill-installer.d.ts +8 -0
- package/dist/internal/skill-installer.d.ts.map +1 -0
- package/dist/internal/skill-installer.js +51 -0
- package/dist/internal/skill-installer.js.map +1 -0
- package/package.json +36 -0
- package/skills/manifest.json +10 -0
- package/skills/openxiangda-v2/SKILL.md +67 -0
- package/skills/openxiangda-v2/agents/openai.yaml +4 -0
- package/skills/openxiangda-v2/references/appspec.md +67 -0
- package/skills/openxiangda-v2/references/architecture.md +7 -0
- package/skills/openxiangda-v2/references/backend.md +268 -0
- package/skills/openxiangda-v2/references/commands.md +21 -0
- package/skills/openxiangda-v2/references/data-authz.md +270 -0
- package/skills/openxiangda-v2/references/delivery.md +42 -0
- package/skills/openxiangda-v2/references/discovery.md +15 -0
- package/skills/openxiangda-v2/references/frontend.md +131 -0
- package/skills/openxiangda-v2/references/testing.md +66 -0
- package/skills/openxiangda-v2/references/workflow-events.md +180 -0
- package/skills/openxiangda-v2/references/workspace.md +26 -0
package/README.md
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# openxiangda-skill-kit
|
|
2
|
+
|
|
3
|
+
Validates OpenXiangda 2.0 skill metadata, links, command references, version boundaries, and deterministic distribution manifests.
|
|
4
|
+
|
|
5
|
+
The package exposes a library for release validation and controlled installation.
|
|
6
|
+
It contains exactly one installable Skill, `openxiangda-v2`; architecture,
|
|
7
|
+
frontend, backend, authorization and delivery guidance live as nested references
|
|
8
|
+
inside that Skill. It publishes no executable and no Workflow/Event Skill. The
|
|
9
|
+
installer only replaces that exact Skill and never recognizes or migrates an
|
|
10
|
+
older 2.0 Skill layout.
|
package/dist/bin.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bin.d.ts","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":""}
|
package/dist/bin.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { installSkills, resolveDefaultSkillsRoot, validateSkills, writeSkillManifest, } from './index.js';
|
|
5
|
+
const args = process.argv.slice(2);
|
|
6
|
+
const command = args[0] === 'install' || args[0] === 'validate' ? args.shift() : 'validate';
|
|
7
|
+
const writeManifest = args.includes('--write-manifest');
|
|
8
|
+
const force = args.includes('--force');
|
|
9
|
+
const optionValue = (name) => {
|
|
10
|
+
const index = args.indexOf(name);
|
|
11
|
+
return index >= 0 ? args[index + 1] : undefined;
|
|
12
|
+
};
|
|
13
|
+
const defaultSkillsRoot = resolveDefaultSkillsRoot();
|
|
14
|
+
if (command === 'install') {
|
|
15
|
+
const skillsRoot = resolve(optionValue('--source') || defaultSkillsRoot);
|
|
16
|
+
const destination = resolve(optionValue('--dest') || process.env.CODEX_HOME || resolve(homedir(), '.codex'), optionValue('--dest') ? '' : 'skills');
|
|
17
|
+
const result = await installSkills({ skillsRoot, destination, force });
|
|
18
|
+
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
const rootArgument = args.find(argument => !argument.startsWith('-'));
|
|
22
|
+
const skillsRoot = resolve(process.cwd(), rootArgument || defaultSkillsRoot);
|
|
23
|
+
const issues = await validateSkills(skillsRoot);
|
|
24
|
+
if (issues.length) {
|
|
25
|
+
for (const issue of issues)
|
|
26
|
+
process.stderr.write(`${issue.skill}: ${issue.message} (${issue.file})\n`);
|
|
27
|
+
process.exitCode = 1;
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
if (writeManifest)
|
|
31
|
+
await writeSkillManifest(skillsRoot);
|
|
32
|
+
process.stdout.write(`Validated ${skillsRoot}${writeManifest ? ' and wrote manifest' : ''}.\n`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=bin.js.map
|
package/dist/bin.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EACL,aAAa,EACb,wBAAwB,EACxB,cAAc,EACd,kBAAkB,GACnB,MAAM,YAAY,CAAC;AAEpB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACnC,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,KAAK,SAAS,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC;AAC5F,MAAM,aAAa,GAAG,IAAI,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAC;AACxD,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;AACvC,MAAM,WAAW,GAAG,CAAC,IAAY,EAAE,EAAE;IACnC,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAClD,CAAC,CAAC;AACF,MAAM,iBAAiB,GAAG,wBAAwB,EAAE,CAAC;AAErD,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;IAC1B,MAAM,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,UAAU,CAAC,IAAI,iBAAiB,CAAC,CAAC;IACzE,MAAM,WAAW,GAAG,OAAO,CACzB,WAAW,CAAC,QAAQ,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,OAAO,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,EAC/E,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CACtC,CAAC;IACF,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,EAAE,UAAU,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,CAAC;IACvE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;AAC/D,CAAC;KAAM,CAAC;IACN,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;IACtE,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,YAAY,IAAI,iBAAiB,CAAC,CAAC;IAC7E,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,UAAU,CAAC,CAAC;IAChD,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAClB,KAAK,MAAM,KAAK,IAAI,MAAM;YAAE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,KAAK,KAAK,KAAK,CAAC,OAAO,KAAK,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC;QACvG,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;IACvB,CAAC;SAAM,CAAC;QACN,IAAI,aAAa;YAAE,MAAM,kBAAkB,CAAC,UAAU,CAAC,CAAC;QACxD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa,UAAU,GAAG,aAAa,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IAClG,CAAC;AACH,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export interface SkillValidationIssue {
|
|
2
|
+
skill: string;
|
|
3
|
+
file: string;
|
|
4
|
+
message: string;
|
|
5
|
+
}
|
|
6
|
+
export interface SkillManifestEntry {
|
|
7
|
+
name: string;
|
|
8
|
+
description: string;
|
|
9
|
+
sha256: string;
|
|
10
|
+
}
|
|
11
|
+
export interface SkillManifest {
|
|
12
|
+
schemaVersion: 1;
|
|
13
|
+
skills: SkillManifestEntry[];
|
|
14
|
+
}
|
|
15
|
+
export interface InstallSkillsInput {
|
|
16
|
+
skillsRoot: string;
|
|
17
|
+
destination: string;
|
|
18
|
+
force?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export declare const OPENXIANGDA_SKILL_NAME = "openxiangda-v2";
|
|
21
|
+
export declare const RETIRED_OPENXIANGDA_V2_SKILL_NAMES: readonly ["openxiangda-v2-architecture", "openxiangda-v2-backend", "openxiangda-v2-data-authz", "openxiangda-v2-delivery", "openxiangda-v2-frontend", "openxiangda-v2-workflow-events"];
|
|
22
|
+
export declare function resolveDefaultSkillsRoot(): string;
|
|
23
|
+
export declare function validateSkills(skillsRoot: string): Promise<SkillValidationIssue[]>;
|
|
24
|
+
export declare function createSkillManifest(skillsRoot: string): Promise<SkillManifest>;
|
|
25
|
+
export declare function writeSkillManifest(skillsRoot: string, output?: string): Promise<SkillManifest>;
|
|
26
|
+
export declare function installSkills(input: InstallSkillsInput): Promise<{
|
|
27
|
+
destination: string;
|
|
28
|
+
installed: string[];
|
|
29
|
+
retired: string[];
|
|
30
|
+
references: string;
|
|
31
|
+
}>;
|
|
32
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAeA,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,aAAa,EAAE,CAAC,CAAC;IACjB,MAAM,EAAE,kBAAkB,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,eAAO,MAAM,sBAAsB,mBAAmB,CAAC;AACvD,eAAO,MAAM,kCAAkC,yLAOrC,CAAC;AACX,wBAAgB,wBAAwB,WAMvC;AAmCD,wBAAsB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAgGxF;AAED,wBAAsB,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAepF;AAuBD,wBAAsB,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,SAAuC,0BAIzG;AAED,wBAAsB,aAAa,CAAC,KAAK,EAAE,kBAAkB;;;;;GAuB5D"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { readdir, readFile, stat, writeFile, } from 'node:fs/promises';
|
|
4
|
+
import { dirname, relative, resolve } from 'node:path';
|
|
5
|
+
import { DEVKIT_COMMANDS, renderDevkitCommandReference, } from 'openxiangda-devkit-core';
|
|
6
|
+
import { installSkillTransaction } from './internal/skill-installer.js';
|
|
7
|
+
export const OPENXIANGDA_SKILL_NAME = 'openxiangda-v2';
|
|
8
|
+
export const RETIRED_OPENXIANGDA_V2_SKILL_NAMES = [
|
|
9
|
+
'openxiangda-v2-architecture',
|
|
10
|
+
'openxiangda-v2-backend',
|
|
11
|
+
'openxiangda-v2-data-authz',
|
|
12
|
+
'openxiangda-v2-delivery',
|
|
13
|
+
'openxiangda-v2-frontend',
|
|
14
|
+
'openxiangda-v2-workflow-events',
|
|
15
|
+
];
|
|
16
|
+
export function resolveDefaultSkillsRoot() {
|
|
17
|
+
const explicit = String(process.env.OPENXIANGDA_SKILLS_ROOT || '').trim();
|
|
18
|
+
if (explicit)
|
|
19
|
+
return resolve(explicit);
|
|
20
|
+
const packaged = resolve(import.meta.dirname, '../skills');
|
|
21
|
+
const development = resolve(import.meta.dirname, '../../../skills');
|
|
22
|
+
return existsSync(packaged) ? packaged : development;
|
|
23
|
+
}
|
|
24
|
+
const FORBIDDEN_V2_TERMS = [
|
|
25
|
+
/\bApp Function\b/i,
|
|
26
|
+
/\bJS_CODE\b/i,
|
|
27
|
+
/\bresource publish\b/i,
|
|
28
|
+
/\bworkflow v3\b/i,
|
|
29
|
+
/\bRoleSession\b/i,
|
|
30
|
+
/\bcreate-openxiangda\b/i,
|
|
31
|
+
/\bopenxiangda-(?:admin|compiler|field-kit|testing|user|workflow)\b/i,
|
|
32
|
+
];
|
|
33
|
+
const knownCommandIds = new Set(DEVKIT_COMMANDS.map(command => command.id));
|
|
34
|
+
const REQUIRED_REFERENCES = [
|
|
35
|
+
'appspec.md',
|
|
36
|
+
'architecture.md',
|
|
37
|
+
'backend.md',
|
|
38
|
+
'commands.md',
|
|
39
|
+
'data-authz.md',
|
|
40
|
+
'delivery.md',
|
|
41
|
+
'discovery.md',
|
|
42
|
+
'frontend.md',
|
|
43
|
+
'testing.md',
|
|
44
|
+
'workflow-events.md',
|
|
45
|
+
'workspace.md',
|
|
46
|
+
];
|
|
47
|
+
export async function validateSkills(skillsRoot) {
|
|
48
|
+
const skills = await loadSkills(skillsRoot);
|
|
49
|
+
const knownSkills = new Set(skills.map(skill => skill.name));
|
|
50
|
+
const issues = [];
|
|
51
|
+
if (skills.length !== 1 || skills[0]?.name !== OPENXIANGDA_SKILL_NAME) {
|
|
52
|
+
issues.push({
|
|
53
|
+
skill: OPENXIANGDA_SKILL_NAME,
|
|
54
|
+
file: skillsRoot,
|
|
55
|
+
message: `Skill distribution must contain only ${OPENXIANGDA_SKILL_NAME}.`,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
for (const skill of skills) {
|
|
59
|
+
const add = (message, file = skill.file) => issues.push({ skill: skill.name || skill.directory, file, message });
|
|
60
|
+
if (!skill.name)
|
|
61
|
+
add('SKILL.md frontmatter must declare name.');
|
|
62
|
+
if (skill.name !== skill.directory)
|
|
63
|
+
add('Skill directory and frontmatter name must match.');
|
|
64
|
+
if (!skill.description || !/\bUse (when|for)\b/i.test(skill.description)) {
|
|
65
|
+
add('Description must explain when the skill should be used.');
|
|
66
|
+
}
|
|
67
|
+
const referencesRoot = resolve(skillsRoot, skill.directory, 'references');
|
|
68
|
+
for (const name of REQUIRED_REFERENCES) {
|
|
69
|
+
const referenceFile = resolve(referencesRoot, name);
|
|
70
|
+
if (!(await isFile(referenceFile))) {
|
|
71
|
+
add(`Missing required nested reference: references/${name}`, referenceFile);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const markdownFiles = [skill.file, ...(await markdownFilesBelow(referencesRoot))];
|
|
75
|
+
for (const markdownFile of markdownFiles) {
|
|
76
|
+
const source = await readFile(markdownFile, 'utf8');
|
|
77
|
+
if (markdownFile !== skill.file && /^---\n/.test(source)) {
|
|
78
|
+
add('Nested reference must not declare Skill frontmatter.', markdownFile);
|
|
79
|
+
}
|
|
80
|
+
if (/\bTODO\b/.test(source)) {
|
|
81
|
+
add('Skill contains unfinished TODO text.', markdownFile);
|
|
82
|
+
}
|
|
83
|
+
for (const forbidden of FORBIDDEN_V2_TERMS) {
|
|
84
|
+
if (forbidden.test(source)) {
|
|
85
|
+
add(`2.0 skill contains forbidden 1.x term: ${forbidden.source}`, markdownFile);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
for (const linkedSkill of extractSkillMentions(source)) {
|
|
89
|
+
if (!knownSkills.has(linkedSkill)) {
|
|
90
|
+
add(`Unknown skill reference: $${linkedSkill}`, markdownFile);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
for (const command of extractCommands(source)) {
|
|
94
|
+
const commandId = toCommandId(command);
|
|
95
|
+
if (!commandId || !knownCommandIds.has(commandId)) {
|
|
96
|
+
add(`Unknown 2.0 CLI command reference: openxiangda ${command}`, markdownFile);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
for (const reference of extractLocalMarkdownLinks(source)) {
|
|
100
|
+
const target = resolve(dirname(markdownFile), reference);
|
|
101
|
+
if (!(await isFile(target))) {
|
|
102
|
+
add(`Broken local reference: ${reference}`, markdownFile);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
const commandReference = resolve(referencesRoot, 'commands.md');
|
|
107
|
+
if ((await isFile(commandReference)) &&
|
|
108
|
+
(await readFile(commandReference, 'utf8')) !==
|
|
109
|
+
renderDevkitCommandReference({
|
|
110
|
+
title: 'OpenXiangda 2.0 Commands',
|
|
111
|
+
executable: 'pnpm openxiangda',
|
|
112
|
+
})) {
|
|
113
|
+
add('Generated command reference drifted from DEVKIT_COMMANDS.', commandReference);
|
|
114
|
+
}
|
|
115
|
+
const agentFile = resolve(skillsRoot, skill.directory, 'agents/openai.yaml');
|
|
116
|
+
if (!(await isFile(agentFile))) {
|
|
117
|
+
add('Missing agents/openai.yaml.', agentFile);
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
const agentSource = await readFile(agentFile, 'utf8');
|
|
121
|
+
if (!agentSource.includes(`$${skill.name}`)) {
|
|
122
|
+
add('Agent default_prompt must explicitly mention the skill name.', agentFile);
|
|
123
|
+
}
|
|
124
|
+
if (/\bTODO\b/.test(agentSource))
|
|
125
|
+
add('Agent metadata contains unfinished TODO text.', agentFile);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return issues.sort((left, right) => `${left.skill}:${left.file}:${left.message}`.localeCompare(`${right.skill}:${right.file}:${right.message}`));
|
|
129
|
+
}
|
|
130
|
+
export async function createSkillManifest(skillsRoot) {
|
|
131
|
+
const skills = await loadSkills(skillsRoot);
|
|
132
|
+
assertCanonicalSkillSet(skills);
|
|
133
|
+
const entries = await Promise.all(skills.map(async (skill) => {
|
|
134
|
+
return {
|
|
135
|
+
name: skill.name,
|
|
136
|
+
description: skill.description,
|
|
137
|
+
sha256: await skillDirectoryDigest(resolve(skillsRoot, skill.directory)),
|
|
138
|
+
};
|
|
139
|
+
}));
|
|
140
|
+
return { schemaVersion: 1, skills: entries.sort((left, right) => left.name.localeCompare(right.name)) };
|
|
141
|
+
}
|
|
142
|
+
async function skillDirectoryDigest(root) {
|
|
143
|
+
const files = [];
|
|
144
|
+
const visit = async (directory) => {
|
|
145
|
+
for (const entry of await readdir(directory, { withFileTypes: true })) {
|
|
146
|
+
const path = resolve(directory, entry.name);
|
|
147
|
+
if (entry.isDirectory())
|
|
148
|
+
await visit(path);
|
|
149
|
+
else if (entry.isFile())
|
|
150
|
+
files.push(path);
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
await visit(root);
|
|
154
|
+
const hash = createHash('sha256');
|
|
155
|
+
for (const file of files.sort()) {
|
|
156
|
+
hash
|
|
157
|
+
.update(relative(root, file).replaceAll('\\', '/'))
|
|
158
|
+
.update('\0')
|
|
159
|
+
.update(await readFile(file))
|
|
160
|
+
.update('\0');
|
|
161
|
+
}
|
|
162
|
+
return hash.digest('hex');
|
|
163
|
+
}
|
|
164
|
+
export async function writeSkillManifest(skillsRoot, output = resolve(skillsRoot, 'manifest.json')) {
|
|
165
|
+
const manifest = await createSkillManifest(skillsRoot);
|
|
166
|
+
await writeFile(output, `${JSON.stringify(manifest, null, 2)}\n`, 'utf8');
|
|
167
|
+
return manifest;
|
|
168
|
+
}
|
|
169
|
+
export async function installSkills(input) {
|
|
170
|
+
const skills = await loadSkills(input.skillsRoot);
|
|
171
|
+
assertCanonicalSkillSet(skills);
|
|
172
|
+
const skill = skills[0];
|
|
173
|
+
const target = resolve(input.destination, OPENXIANGDA_SKILL_NAME);
|
|
174
|
+
if (!input.force && (await pathExists(target))) {
|
|
175
|
+
throw new Error(`SKILL_ALREADY_EXISTS: ${target}`);
|
|
176
|
+
}
|
|
177
|
+
const retired = await managedRetiredSkillNames(input.destination);
|
|
178
|
+
await installSkillTransaction({
|
|
179
|
+
source: resolve(input.skillsRoot, skill.directory),
|
|
180
|
+
destination: input.destination,
|
|
181
|
+
name: OPENXIANGDA_SKILL_NAME,
|
|
182
|
+
retiredNames: retired,
|
|
183
|
+
});
|
|
184
|
+
return {
|
|
185
|
+
destination: input.destination,
|
|
186
|
+
installed: [OPENXIANGDA_SKILL_NAME],
|
|
187
|
+
retired,
|
|
188
|
+
references: resolve(target, 'references'),
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
async function managedRetiredSkillNames(destination) {
|
|
192
|
+
const retired = [];
|
|
193
|
+
for (const name of RETIRED_OPENXIANGDA_V2_SKILL_NAMES) {
|
|
194
|
+
const metadata = resolve(destination, name, '.openxiangda-skill-install.json');
|
|
195
|
+
if (!(await isFile(metadata)))
|
|
196
|
+
continue;
|
|
197
|
+
try {
|
|
198
|
+
const parsed = JSON.parse(await readFile(metadata, 'utf8'));
|
|
199
|
+
if (parsed?.manager === 'openxiangda' &&
|
|
200
|
+
/(?:^|\/)v2\/skills\/openxiangda-v2-/.test(String(parsed?.sourceRelativePath || ''))) {
|
|
201
|
+
retired.push(name);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
catch {
|
|
205
|
+
// An unreadable or user-owned directory is never removed automatically.
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return retired.sort();
|
|
209
|
+
}
|
|
210
|
+
async function loadSkills(skillsRoot) {
|
|
211
|
+
const entries = await readdir(skillsRoot, { withFileTypes: true });
|
|
212
|
+
const directories = entries
|
|
213
|
+
.filter(entry => entry.isDirectory() && entry.name.startsWith('openxiangda-'))
|
|
214
|
+
.map(entry => entry.name)
|
|
215
|
+
.sort();
|
|
216
|
+
return Promise.all(directories.map(async (directory) => {
|
|
217
|
+
const file = resolve(skillsRoot, directory, 'SKILL.md');
|
|
218
|
+
const source = await readFile(file, 'utf8');
|
|
219
|
+
const frontmatter = parseFrontmatter(source);
|
|
220
|
+
return {
|
|
221
|
+
directory,
|
|
222
|
+
file,
|
|
223
|
+
source,
|
|
224
|
+
name: frontmatter.name || '',
|
|
225
|
+
description: frontmatter.description || '',
|
|
226
|
+
};
|
|
227
|
+
}));
|
|
228
|
+
}
|
|
229
|
+
function assertCanonicalSkillSet(skills) {
|
|
230
|
+
if (skills.length !== 1 || skills[0]?.name !== OPENXIANGDA_SKILL_NAME) {
|
|
231
|
+
throw new Error(`SKILL_SET_INVALID: expected only ${OPENXIANGDA_SKILL_NAME}`);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
function parseFrontmatter(source) {
|
|
235
|
+
const match = source.match(/^---\n([\s\S]*?)\n---\n/);
|
|
236
|
+
if (!match?.[1])
|
|
237
|
+
return {};
|
|
238
|
+
return Object.fromEntries(match[1]
|
|
239
|
+
.split('\n')
|
|
240
|
+
.map(line => line.match(/^([a-z_]+):\s*(.+)$/))
|
|
241
|
+
.filter((line) => Boolean(line))
|
|
242
|
+
.map(line => [line[1], line[2].replace(/^['"]|['"]$/g, '')]));
|
|
243
|
+
}
|
|
244
|
+
function extractCommands(source) {
|
|
245
|
+
return [
|
|
246
|
+
...source.matchAll(/\bopenxiangda(?:@[^\s`]+)?\s+([a-z][a-z0-9:-]*(?:\s+[a-z][a-z0-9:-]*)?)/gi),
|
|
247
|
+
].map(match => match[1].trim().toLowerCase());
|
|
248
|
+
}
|
|
249
|
+
function extractSkillMentions(source) {
|
|
250
|
+
return [...source.matchAll(/\$([a-z0-9-]+)/g)].map(match => match[1]);
|
|
251
|
+
}
|
|
252
|
+
function extractLocalMarkdownLinks(source) {
|
|
253
|
+
return [...source.matchAll(/\]\(([^)]+)\)/g)]
|
|
254
|
+
.map(match => match[1])
|
|
255
|
+
.filter(reference => !/^(?:https?:|#)/.test(reference));
|
|
256
|
+
}
|
|
257
|
+
function toCommandId(command) {
|
|
258
|
+
const tokens = command.split(/\s+/).filter(token => token && !token.startsWith('-'));
|
|
259
|
+
if (!tokens[0])
|
|
260
|
+
return undefined;
|
|
261
|
+
for (let length = Math.min(tokens.length, 4); length >= 1; length -= 1) {
|
|
262
|
+
const candidate = tokens.slice(0, length).join(':');
|
|
263
|
+
if (knownCommandIds.has(candidate))
|
|
264
|
+
return candidate;
|
|
265
|
+
}
|
|
266
|
+
return undefined;
|
|
267
|
+
}
|
|
268
|
+
async function isFile(file) {
|
|
269
|
+
try {
|
|
270
|
+
return (await stat(file)).isFile();
|
|
271
|
+
}
|
|
272
|
+
catch {
|
|
273
|
+
return false;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
async function isDirectory(path) {
|
|
277
|
+
try {
|
|
278
|
+
return (await stat(path)).isDirectory();
|
|
279
|
+
}
|
|
280
|
+
catch {
|
|
281
|
+
return false;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
async function markdownFilesBelow(root) {
|
|
285
|
+
if (!(await isDirectory(root)))
|
|
286
|
+
return [];
|
|
287
|
+
const files = [];
|
|
288
|
+
const visit = async (directory) => {
|
|
289
|
+
for (const entry of await readdir(directory, { withFileTypes: true })) {
|
|
290
|
+
const path = resolve(directory, entry.name);
|
|
291
|
+
if (entry.isDirectory())
|
|
292
|
+
await visit(path);
|
|
293
|
+
else if (entry.isFile() && entry.name.endsWith('.md'))
|
|
294
|
+
files.push(path);
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
await visit(root);
|
|
298
|
+
return files.sort();
|
|
299
|
+
}
|
|
300
|
+
async function pathExists(path) {
|
|
301
|
+
try {
|
|
302
|
+
await stat(path);
|
|
303
|
+
return true;
|
|
304
|
+
}
|
|
305
|
+
catch {
|
|
306
|
+
return false;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface SkillInstallTransactionInput {
|
|
2
|
+
source: string;
|
|
3
|
+
destination: string;
|
|
4
|
+
name: string;
|
|
5
|
+
retiredNames?: string[];
|
|
6
|
+
}
|
|
7
|
+
export declare function installSkillTransaction(input: SkillInstallTransactionInput): Promise<string>;
|
|
8
|
+
//# sourceMappingURL=skill-installer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-installer.d.ts","sourceRoot":"","sources":["../../src/internal/skill-installer.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,4BAA4B;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,wBAAsB,uBAAuB,CAAC,KAAK,EAAE,4BAA4B,mBAqChF"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { cp, mkdir, mkdtemp, rename, rm, stat } from 'node:fs/promises';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
export async function installSkillTransaction(input) {
|
|
4
|
+
await mkdir(input.destination, { recursive: true });
|
|
5
|
+
const target = resolve(input.destination, input.name);
|
|
6
|
+
const staging = await mkdtemp(resolve(input.destination, '.openxiangda-v2-install-'));
|
|
7
|
+
const stagedSkill = resolve(staging, input.name);
|
|
8
|
+
const backupRoot = resolve(staging, 'previous');
|
|
9
|
+
const backedUp = [];
|
|
10
|
+
let installedNewSkill = false;
|
|
11
|
+
try {
|
|
12
|
+
await cp(input.source, stagedSkill, { recursive: true });
|
|
13
|
+
await mkdir(backupRoot, { recursive: true });
|
|
14
|
+
for (const name of [
|
|
15
|
+
input.name,
|
|
16
|
+
...new Set(input.retiredNames || []),
|
|
17
|
+
]) {
|
|
18
|
+
const source = resolve(input.destination, name);
|
|
19
|
+
if (!(await pathExists(source)))
|
|
20
|
+
continue;
|
|
21
|
+
const backup = resolve(backupRoot, name);
|
|
22
|
+
await rename(source, backup);
|
|
23
|
+
backedUp.push({ source, backup });
|
|
24
|
+
}
|
|
25
|
+
await rename(stagedSkill, target);
|
|
26
|
+
installedNewSkill = true;
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
if (installedNewSkill || (await pathExists(target))) {
|
|
30
|
+
await rm(target, { recursive: true, force: true });
|
|
31
|
+
}
|
|
32
|
+
for (const entry of backedUp.reverse()) {
|
|
33
|
+
await rename(entry.backup, entry.source);
|
|
34
|
+
}
|
|
35
|
+
throw error;
|
|
36
|
+
}
|
|
37
|
+
finally {
|
|
38
|
+
await rm(staging, { recursive: true, force: true });
|
|
39
|
+
}
|
|
40
|
+
return target;
|
|
41
|
+
}
|
|
42
|
+
async function pathExists(path) {
|
|
43
|
+
try {
|
|
44
|
+
await stat(path);
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=skill-installer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-installer.js","sourceRoot":"","sources":["../../src/internal/skill-installer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACxE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AASpC,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,KAAmC;IAC/E,MAAM,KAAK,CAAC,KAAK,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACpD,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,WAAW,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;IACtD,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,WAAW,EAAE,0BAA0B,CAAC,CAAC,CAAC;IACtF,MAAM,WAAW,GAAG,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;IACjD,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;IAChD,MAAM,QAAQ,GAA8C,EAAE,CAAC;IAC/D,IAAI,iBAAiB,GAAG,KAAK,CAAC;IAE9B,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACzD,MAAM,KAAK,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7C,KAAK,MAAM,IAAI,IAAI;YACjB,KAAK,CAAC,IAAI;YACV,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,YAAY,IAAI,EAAE,CAAC;SACrC,EAAE,CAAC;YACF,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;YAChD,IAAI,CAAC,CAAC,MAAM,UAAU,CAAC,MAAM,CAAC,CAAC;gBAAE,SAAS;YAC1C,MAAM,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;YACzC,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YAC7B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QACpC,CAAC;QACD,MAAM,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;QAClC,iBAAiB,GAAG,IAAI,CAAC;IAC3B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,iBAAiB,IAAI,CAAC,MAAM,UAAU,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;YACpD,MAAM,EAAE,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACrD,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC;YACvC,MAAM,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;QAC3C,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;YAAS,CAAC;QACT,MAAM,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACtD,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,KAAK,UAAU,UAAU,CAAC,IAAY;IACpC,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;QACjB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "openxiangda-skill-kit",
|
|
3
|
+
"version": "2.0.0-alpha.100",
|
|
4
|
+
"description": "Validation and deterministic packaging for OpenXiangda 2.0 AI skills.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist/",
|
|
16
|
+
"skills/",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"openxiangda-devkit-core": "2.0.0-alpha.79"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"tsx": "4.23.12",
|
|
24
|
+
"typescript": "5.9.3"
|
|
25
|
+
},
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=20"
|
|
28
|
+
},
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "tsc -p tsconfig.json",
|
|
32
|
+
"build:release": "node ../../scripts/prune-package-dist.mjs && pnpm build",
|
|
33
|
+
"check": "tsc -p tsconfig.json --noEmit",
|
|
34
|
+
"test": "tsx --test test/*.test.ts"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"skills": [
|
|
4
|
+
{
|
|
5
|
+
"name": "openxiangda-v2",
|
|
6
|
+
"description": "Use when researching, designing, building, testing, or delivering a complete OpenXiangda 2.0 application.",
|
|
7
|
+
"sha256": "58f0524847353f8e8fd0b583e2a3566cac1b646e6c19fc5f62473c801e91a8dc"
|
|
8
|
+
}
|
|
9
|
+
]
|
|
10
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: openxiangda-v2
|
|
3
|
+
description: Use when researching, designing, building, testing, or delivering a complete OpenXiangda 2.0 application.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# OpenXiangda 2.0
|
|
7
|
+
|
|
8
|
+
Treat requirement discovery, architecture, authorization, development, testing and delivery as one evidence-backed application lifecycle. OpenXiangda 2.0 has no compatibility surface: replace an incorrect alpha contract directly and never add aliases, migration branches, 1.x SDD, Umi, ProComponents, Function CRUD, application-owned identity switching or another identity path.
|
|
9
|
+
|
|
10
|
+
Before a workspace exists, use this Skill's exact npm version:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pnpm dlx openxiangda@__OPENXIANGDA_VERSION__ login --base-url <platform>
|
|
14
|
+
pnpm dlx openxiangda@__OPENXIANGDA_VERSION__ create <directory>
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Inside an application, use only its locked executable through `pnpm openxiangda`. Never invoke a bare global `openxiangda`, because that executable may belong to stable 1.x. Moving tags such as `latest` and `alpha` are forbidden. To install or refresh this same Skill from the package, run:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pnpm dlx openxiangda@__OPENXIANGDA_VERSION__ skill install --force
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Use this order:
|
|
24
|
+
|
|
25
|
+
1. Read [Commands](references/commands.md) when selecting a CLI operation. It is generated from the executable registry. `create` owns application binding and initialization, while `check`, `deploy` and `status` expose the current workspace and deployment facts.
|
|
26
|
+
2. Read [Discovery](references/discovery.md), turn the request into actors, scenarios, data, permissions, constraints and acceptance evidence, and identify every unresolved choice.
|
|
27
|
+
3. If the workspace contains `appspec/`, the user asks to maintain requirements, or the task changes observable behavior, read [AppSpec](references/appspec.md), load the bounded index with `pnpm openxiangda spec context --json`, then select only the relevant stable ID. AppSpec is optional and never a release gate; L0 changes create no record.
|
|
28
|
+
4. Read [Architecture](references/architecture.md), assign one owner to each capability and keep ordinary CRUD on platform Data API.
|
|
29
|
+
5. For every resource or permission change, read [Data and authorization](references/data-authz.md) before editing `openxiangda.config.ts`.
|
|
30
|
+
6. Read [Frontend](references/frontend.md) for pages or fields and [Backend actions](references/backend.md) only for a real business action.
|
|
31
|
+
7. When the application explicitly enables standard approval, application events or notifications, read [Workflow, Events and Notification Hub](references/workflow-events.md). Keep them optional and out of ordinary CRUD.
|
|
32
|
+
8. Run the connected loop with `pnpm openxiangda dev`, then follow [Testing](references/testing.md) and `pnpm openxiangda check`; check performs the target platform's read-only Native compatibility preflight before application checks or builds.
|
|
33
|
+
9. Follow [Delivery](references/delivery.md): deploy preproduction first, inspect status/logs, then promote the exact successful version to production.
|
|
34
|
+
|
|
35
|
+
For an AI-native client, start the workspace protocol through the same pinned executable:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pnpm exec openxiangda --mcp-stdio --cwd <workspace>
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Read `openxiangda://workspace/contracts` or call `contract_describe`. For first-time menu authoring, copy `data.adminNavigationAuthoring.suggestion.expression` once into `frontend.admin.navigation` with its listed `openxiangda/config` imports, then edit that application-owned declaration; never treat the proposal as runtime discovery. When AppSpec is enabled, read `openxiangda://workspace/appspec` or call `appspec_context`; treat it as advisory context, never as deployment authority. Require `aiCatalog` and `aiCatalogDigest` to match the normal compiler output. Never create an application MCP server, Catalog file, preview store or AI authorization path.
|
|
42
|
+
|
|
43
|
+
The browser uses the current logged-in user's complete application-role union. The platform is authoritative for capabilities, fields, rows and RLS. A declared Perspective is only a read projection over that union: it may narrow pages, rows and readable fields, but never create/update/delete, workflow or custom-action authorization. Omitted Perspective means the complete union. Application code does not replace the authenticated identity, persist a platform token, construct an authorization result or keep a second permission state.
|
|
44
|
+
|
|
45
|
+
Applications own one explicit typed admin navigation declaration. The compiler
|
|
46
|
+
owns page/route generation, but it never turns every generated resource or
|
|
47
|
+
Workflow into a menu item; the Shell renders only declared navigation and then
|
|
48
|
+
filters it by authorization. Keep page existence, route reachability and menu
|
|
49
|
+
visibility separate. Use resource mutation ownership and per-operation
|
|
50
|
+
generated surface flags, and give only self-contained `standalone` Workflow
|
|
51
|
+
launch pages a menu entry.
|
|
52
|
+
|
|
53
|
+
Read only the references needed for the current change, except the mandatory discovery and Data/authorization rules above:
|
|
54
|
+
|
|
55
|
+
- [Discovery](references/discovery.md)
|
|
56
|
+
- [AppSpec](references/appspec.md)
|
|
57
|
+
- [Commands](references/commands.md)
|
|
58
|
+
- [Architecture](references/architecture.md)
|
|
59
|
+
- [Workspace agent contract](references/workspace.md)
|
|
60
|
+
- [Backend actions](references/backend.md)
|
|
61
|
+
- [Data and authorization](references/data-authz.md)
|
|
62
|
+
- [Frontend](references/frontend.md)
|
|
63
|
+
- [Workflow, Events and Notification Hub](references/workflow-events.md)
|
|
64
|
+
- [Testing](references/testing.md)
|
|
65
|
+
- [Delivery](references/delivery.md)
|
|
66
|
+
|
|
67
|
+
Workflow and Notification Hub are optional 2.0 modules outside the CRUD golden path. Never add their packages, routes, declarations or runtime workers to an application that does not enable them.
|