neo - Neo Compose项目源CLI
neo 将git友好的Neo Compose项目工作副本与
网页编辑器。工作拷贝格式4是Neo-native: .neo 定义项目
模型和撰写值; .neoflow 定义对话流和托管映像
音频文件以普通二进制文件的形式保存。
当前代理编写指南已导入
cli/skills/neocompose-cli/SKILL.md.
功能规格
neo-compose-specs/new-features
文档设计意图和边缘情况;已发布的编译器、契约和
测试是可执行的事实来源。
源架构
CLI源代码将创作、文档投影和同步分开:
project-source/ parse, lower, and emit .neo/.neoflow plus managed binaries
project-manifest/ semantic bridge between authored source and project documents
project-sync/ diff, merge, conflict, migration, and CAS state handling
流程是 project source -> ProjectSchemaManifest -> project documents -> sync. ProjectSchemaManifest 携带独立版本的序列化文件
契约使得源语法和服务器文档可以在不耦合的情况下发展
它们的版本号。当前的工作区格式仍然是格式4;这个
清单的数字契约版本不是第二个工作副本格式。
依赖项向下指向: project-source 可以使用两个较低的层,而
project-sync 永远不要导入源代码编写或清单代码。
运行
npm i -g @neocompose/cli # published package; installs `neo` on PATH
node cli/bin/neo.mjs <command> # repository development
在自动化中设置一个带有显式id的工作副本:
neo login --profile editor
neo init --project <id> --version <id> --dir neo
交互式终端可以选择项目、版本和目录。Neo
不安装、不发现或不需要。net。 neo doctor 验证
format-4浏览器安全编译器,VS Code分析合同,跟踪源代码,和
托管的文件的能力。中的畸形记录也会失败
文档的时间。
Format-4工作副本
neo/
neo.json # formatVersion: 4
Project.neo # project defaults/settings
Root.neo # protected root shape; editable root values
Classes/{System/, ...}
Interfaces/{System/, ...}
Enums/{System/, ...}
Relations.neo # generic project relations, when present
Templates/
Localization.neo
LocalizationStatuses/
Files/
Images.neo # ImageRegistry Images
AudioClips.neo # AudioClipRegistry AudioClips
Images/ # tracked image bytes
AudioClips/ # tracked audio bytes
DialogueGroups/
Dialogues/*.neoflow
Migrations/*.neo
**/*.spec.neo # colocated local-only NeoScript tests
.neo/
state.json # private merge/CAS bases; never hand-edit
conflicts/
test-build/ # local test compilation metadata
生产 .neo, .neoflow,并跟踪受支持的托管二进制文件。
托管的 .spec.neo 文件仅用于测试:它们从不输入状态、源
标识,拉/复位,或推。 .neo/ 被忽略的私有状态。没有图式 .csprojc#编写
树,Roslyn编译器,生成的创作DLL,每个函数的sidecar树,或
签入对话JSON。
只有 formatVersion: 4 被接受。格式4是一个彻底的突破;事实并非如此
读取或转换格式为3的c#签出。与老年人一起推动预期的变化
首先,保留您需要的任何本地源代码,然后从
权威服务器:
neo init --project <id> --version <id> --dir neo
对于现有的format-4工作区, neo pull --reset 再生规范
源代码和托管二进制文件。正常的 neo pull 执行一个稳定的id感知
三向合并,尽可能保留源位置,并使用持久的
事务游标,仅获取自上次同步以来更改的记录。
撤销/重置元数据将使该游标失效,并安全地退回到满的状态
快照。
在Unity项目中, neo.json 可以使用 unityConfigPath 而不是存储
projectId 和 versionId. 被引用的 NeoComposeConfig.asset 然后拥有
这些id并接收分支/版本切换。 neo export 写进去
统一项目;没有 unityConfigPath,它提供了链接配置资产
对于这个项目,它可以在Unity项目中找到工作副本。
验证源代码降低,提交准备,动画验证和Unity
的共享文件缓存中 .neo/build/. 一个新的CLI进程可以
重用;保持 neo dev 不需要运行。准确的输入和依赖关系
检查使受影响的结果无效。独立对话散文变化保存
结构性工作,包括提交这些更改的拉操作。结构性来源
编辑仍然需要更广泛的分析和准备。
Export检查输出字节并恢复丢失或修改的生成文件
在不重写未更改的文件的情况下,从其缓存中获取。 neo dev 使用相同的
每次保存后的准备和导出规则。
拉、推和导出拒绝其所拉项目或版本的工作副本
不同于配置,当资产在
CLI。运行 neo branch switch <versionId> 重置和拉配置
的版本。
在当地组织资源
移动 .neo 和 .neoflow 文件放入任何项目文件夹,并组声明
在您喜欢的文件中。保留现有的id。拉,用力,复位
在应用服务器状态之前的当前位置。新的服务器声明使用
默认文件夹。系统声明使用 System 子文件夹,例如
Classes/System, Enums/System,或者 Interfaces/System. 所有权来自于
保留的系统标识,因此项目类型保持其正常的默认值。
受管理的图像和音频也可以移动。更新他们键入的注册表路径。
将大型根或静态初始化器提取到类型化声明中,而不需要导入:
// Configuration/ProjectSortingLayers.neo
List<NeoSortingLayer> SORTING_LAYERS = [
new NeoSortingLayer() { Name = "Default" }
];
// Inside the Root Assets initializer
SortingLayers = SORTING_LAYERS
现有的嵌套 @id 注释保留在提取的初始化器中。这个
消费成员保留其值ID;使用时不需要身份证。一个聚合
有一个归属赋值。其他参考资料使用 Reference(NAME) 哪里
成员支持引用。独立副本需要新的嵌套id;
编辑器的重复操作将删除旧的id。标量可能有好几个
的主人。如果网络编辑使这些值不同,则内联不同的用途。
共享语言服务为选定的完整文本提供提取到文件的功能
初始化器,内联用于单独使用,并重复。这些声明是局部的
项目设置,不创建运行时全局变量。将它们作为完整值使用
初始化;在可执行表达式中使用它们的名称,例如
BASE + 1 将被拒绝。初始化式循环和歧义
在同步之前聚合所有权验证失败。修复源错误
在使用提取的初始化器重置签出之前,以便它们的布局可以
恢复安全。
移民也可以移动。保持他们的 /// @migration 页眉和 /// @order N
指令,或保留用于排序的数字文件名前缀。
本土新作家
类、接口和枚举都是顶级声明。模式功能, getter和setter是内联的,并作为NeoScript编译;他们没有 分离源文件。
@id("enum-item-rarity-id")
enum ItemRarity {
@id("rarity-common-id")
Common = "Common text",
}
@id("class-inventory-item-id")
@storage(allowed: .Immutable)
abstract class InventoryItem<
@id("inventory-context-generic-id")
TContext extends SomeClass
> {
@id("item-name-id")
@settings(searchBy: .MemberKey)
virtual string Name = "";
@id("item-rarity-id")
abstract ItemRarity Rarity;
@id("item-stack-size-id")
@settings(min: 1, max: 999)
int StackSize = 1;
@id("item-display-name-id")
string DisplayName {
get {
return $"{Name} ({Rarity})";
}
}
@id("item-use-id")
abstract void Use(TContext context);
}
源标识符是持久化的名称。 @settings 和 @storage 是
由它们的声明上下文键入;无效字段和枚举情况为
诊断。Swift-like .EnumCase 语法适用于任何期望的枚举
类型是已知的。集中的注释,例如 @id, @visibility,
@relations 进行横切行为。从来没有作者 @system;它是
为平台拥有的系统记录保留。 @visibility(.Hidden) 标志着
类的成员选择器和类树省略;缺席意味着可见。
列表可以重复 @index 和 @column 属性上命名字段的注释
entry类。索引需要一个非本地化的 string 或者单次选择
enum;列也可以针对保留的 __other__ Key,它携带
子类添加的每个字段的默认布局。
@id("outpost-list-id")
@settings(kind: .Unordered)
@index(member: Slug, kind: .Unique)
@column(member: Slug, width: 158, pin: .Leading)
@column(member: Notes, visibility: .Hidden)
public List<Outpost> Outposts = [];
稳定id在重命名、重新排序和文件移动中保持身份。省略一个
@id 只是为了创造一些东西。成功的真实推送会分配ID和
重写来源;试运行、验证失败、上传失败和CAS失败
保持源字节对字节不变。集合项标识使用相同的
本机注释:
Tags = [
@id("story-tag-item-id")
"story",
];
根值和类拥有的值
Root.neo 包含编译器拥有的 Root root = new() { ... } 信封。
它的成员名、类型、id、锁注释和存储设置是
受保护,而 root.Assets, root.Save, root.Session,
root.User 值初始化式是可编辑的。
可重用值存储在普通项目类的静态成员中。有
没有 ValueRegistry 或生成 Values 名称空间。
@id("assets-class-id")
@storage(allowed: .Immutable)
class Assets {
@id("capitol-member-id")
static Outpost Capitol = new {
Name = "Capitol",
Image = Images.Capitol.Slice(0),
};
@id("home-getter-id")
static Outpost Home {
get {
return Assets.Capitol;
}
}
}
成员ID锚定存储的绑定;不添加第二个ID到静态 初始化器。嵌套的类和集合行保持独立标识。
默认值在创建值时实现一次。现有实例不需要
与以后的类默认值更改保持链接。逻辑查找/对话引用
使用本机类型的内部变量。对值类型的引用,例如
Reference<int>,指定成员路径:
Reference(Assets.Capitol)
Reference(root.Assets.Cosmetics.Pants)
Reference(root.Player.Gold)
Reference<PantsAsset>(key: "pants.long")
Reference<NeoDialogue>(id: "dialogue-id")
Project.neo,输入纹理/音频模板,本地化配置和
状态、优先组/选项、对话组和直接关系使用
相同的顶级类型声明语法。编译器派生出结构
行标识、存储位置、通用戳记、本地化文本链接以及
其他不可创作源的持久性细节。
文件
图像和音频在类型注册表中声明并直接引用:
ImageRegistry Images = new() {
@id("sword-image-file-id")
@settings(template: PixelArt)
NeoImage Sword = new("Files/Images/Sword.png");
}
AudioClipRegistry AudioClips = new() {
@id("sword-hit-audio-file-id")
@settings(template: SoundEffect)
NeoAudioClip SwordHit = new("Files/AudioClips/SwordHit.wav");
}
使用 Images.Sword.Slice(0) 来一杯雪碧和 AudioClips.SwordHit 对于音频
成员。将支持的二进制文件放入 Files/Images/ 或者
Files/AudioClips/ 创建一个临时符号;状态/差异报告它,和一个
成功的push写入显式声明和分配的ID。 neo files add 可以在push之前创建挂起的声明。
Pull和push比较服务器验证的SHA-256摘要。不同的本地/远程
字节保存本地文件,并将经过验证的远程端置于
.neo/conflicts/files/<file-id>/ 直到显式解决。
NeoFlow对话编写
每一个 .neoflow 文件直接包含一个密封的非泛型类
来源于系统 NeoDialogue 输入和一个所需的触发器
覆盖。对话函数和词法值绑定是普通类型的
新代码。主要语言环境散文是内联的;其他区域设置和工作流元数据
保留在本地化文本记录中。
图构造函数使用仅限neoflow的尾体。 => Node 最后
return Node; 是相等的目的地;没有可授权的 to:
论点。终端文本/动作节点可以省略目的地,而触发器,
选择和结果都需要一个。
@id("capitol-dialogue-id")
@settings(name: "Capitol: cold boot", saveOptionChoices: true)
sealed class CapitolColdBoot : NeoDialogue {
@primary Outpost capitol = Assets.Capitol;
@id("can-start-function-id")
bool CanStart() {
return capitol.Name == "Capitol";
}
@id("capitol-trigger-id")
override Trigger Trigger = new(
group: CapitolDialogues.High,
when: [
@id("can-start-condition-use-id")
CanStart,
]
) => Welcome;
@id("welcome-node-id")
Text Welcome = new(name: "Welcome!") {
"""
Welcome to {capitol.Name}.
"""
@id("continue-option-id")
Option Continue = new() {
"""
Tell me more.
"""
return Finish;
}
}
@id("finish-node-id")
Text Finish = new() {
"""
Until next time.
"""
}
}
持久化条件使用、操作调用、突变、暂停、选项等
结果各有各的稳定 @id. 通过词汇进行引用
类的绑定和传递函数调用派生相同的链接值
网页编辑器。运行 neo dialogue dryrun <ref> 对话后编辑遍历
选项路径针对新的保存并捕获运行时存储所有权错误。
同步工作流程
neo pull
# edit .neo, .neoflow, and managed binaries
neo status
neo diff
neo dialogue dryrun <ref>
neo push --dry-run
neo push
拉紧跟着状态或推干跑必须是语义的 无操作。如果它报告创作更改,那么停止并报告一个往返错误 推动。
Pull按稳定标识合并源概念和集合,而不是按
索引。冲突是显式的,并阻塞编译。编辑源文件
期望的结果,或使用 neo resolve --mine|--theirs 作为整体
方便。没有强制cas旁路。接受所需的兼容性
只有在检查后才会碰撞 neo push --accept-bump.
一个push在本地准备提交:它重新编译改变了的NeoScript和
受模式影响的调用者,物化值,并验证整个图
对着被拉的头。服务器检查头部,每个更改的CAS库,
授权和行形状,然后自动提交语义记录更改,
本地化的主区域设置编辑和分级文件。它只接受来自
一个CLI在它自己的NeoScript编译器修订版。 neo push --dry-run 运行
同样的准备加上完整的从源到唱片的排练。
通过编辑它们的跟踪来创建类、存储值、文件声明和对话框 .neo 或者 .neoflow 直接来源。下添加托管二进制文件 Files/Images/ 或者 Files/AudioClips/; neo push 发现它们并将它们与源事务放在一起。CLI故意没有并行支架或低级内容写入命令。
NeoScript单元测试
的地方 *.spec.neo 文件在项目源代码旁边的任何地方。他们看到了完整的
本地准备的生产图——包括未推入的声明、值和
函数体-加上仅测试类型的前奏。生产源从未见过
测试全局。
describe("Player.TakeDamage", () => {
test("rejects negative damage", () => {
var player = new Player();
expect(() => { player.TakeDamage(-1); }).toThrow("negative");
});
});
运行所有测试,按文件/目录缩小范围,或使用。过滤完整测试名称 JavaScript正则表达式:
neo test
neo test Classes/Player.spec.neo
neo test -t "TakeDamage"
neo test --seed=17
neo test --maxWorkers=4
neo test --runInBand
neo test --reporter=json --outputFile=.neo/test-results.json
--seed 接受一个带符号的安全十进制整数,取2^32的约模。JSON
报告记录有效种子。没有它,项目指纹选择
种子。注册和每个隔离测试重新开始;beforeEach,身体,
和after共享测试的流。工作线程计数和过滤器不会改变
开始的水流。 neo script eval --seed 17 种子单一评价
服务器快照。
本地候选人评估保留待处理的身份 .neo/export.json
因此,随机存储的结构在测试、试运行、导出和推送中都是一致的。
一个只有身份的文件并不意味着Unity导出成功。缓存的候选人
恢复丢失的保留和重建冲突。推钩消耗
家长准备的候选人;不完整的钩子元数据显式失败。
默认情况下,规范文件与最多四个可用CPU工作线程并行运行。
集 test.maxWorkers 在 neo.json 或者用 --maxWorkers <positive integer>. --runInBand 覆盖两者并串行运行。单个选择
文件在主进程中运行。每个文件中的钩子和测试保持有序;
fixture和mock保持隔离。报告、保存文件和测试注册命令。
JSON报告器使用 schemaVersion: 2. 失败帧包含一个成员名
和可选的 file, line, column. 没有文件的帧包括
coordinateSpace 区分身体相对位置;未知位置是
省略。从版本1升级的消费者必须允许这些可选位置。
JSON输出文件是自动写入的。报告区分已经过的启动
以及从总结的测试工作中执行,这可能会超过并行运行的时间
运行。JSON timings.workerStartupMs 测量worker初始化和
timings.executeMs 度量经过的执行。
测试编译并验证本地候选程序一次。他们省略了额外的来源
连载和重降排练使用的 neo push --dry-run;生产
语法、类型检查和整个图验证仍然是必需的。规范编制
跨缓存丢失共享一个不可变的编译器上下文和模式索引。
测试编译元数据和经过实验验证的准备项目缓存
住在 .neo/test-build/;它们和测试文件都不是服务器记录。
生产源,管理文件,配置,工作空间状态,CLI版本,
和编译器修订使准备的项目无效,而只编辑一个
.spec.neo File重用它。丢失、损坏或校验和不匹配的缓存
重新构建条目。工作空间必须初始化或拉一次
.neo/state.json 包含离线项目事实,但正在运行测试
不执行登录或网络请求。
本机函数不能在本地求值器中执行,因此会失败
除非被嘲笑,否则关闭。使用 mock.native(Member), mock.function(Member),或者
spyOn(Member),然后配置 returns,
returnsOnce, throws, throwsOnce, doesNothing,或者 implementation.
模拟和调用历史记录在每个测试中是隔离的。
要进行门推送,请将其中一个或两个命令添加到 neo.json:
{
"prePushHook": "neo test",
"prePushDryRunHook": "neo test --reporter=json --outputFile=.neo/test-results.json"
}
real-push钩子故意不被用于演练:任意钩子
命令可能有副作用。每个适用的钩子只在local之后运行
候选人验证和认证前/网络活动;一个非零
退出中止。 neo push --no-verify 是显式本地旁路,而不是服务器
授权边界。
命令
| 命令 | 目的 |
|---|---|
--version | 打印安装的CLI包版本。 |
login [--api <url>] | 验证选定的概要文件。 |
whoami [--api <url>] | 检查选定的概要文件。 |
logout [--api <url>] | 删除一个API源的存储凭据并撤销其会话。 |
init --project <id> [--version <id>] | 创建一个format-4工作副本并执行其第一次重置拉取。 |
doctor | 验证格式/编译器/编辑器/源/文件契约;否。净的发现。 |
pull [--force|--reset] [--regenerate-source-names] | 合并远程更改或重新生成规范源代码和二进制文件。 |
status [--json] | 编译源代码和汇总语义记录和上传意图。 |
diff [--json] | 显示语义记录、跨度、摘要和上传意图。 |
push [--dry-run] [--accept-bump] [--no-verify] | 验证或提交一个原子比较-交换事务。 |
test [file-or-dir ...] [-t pattern] | 编译未推送的本地候选程序并运行 *.spec.neo 测试。 |
dev [--unity-project <dir>] | 运行 neo export 每次局部变化之后;不要拉或推。 |
resolve --mine|--theirs | 用一方解决当前所有的源/二进制冲突。 |
script check|compile|eval | 编译或预览带有项目上下文的NeoScript;永远不要提交写。 |
dialogue dryrun <ref> | 模拟本地编写的对话候选人。 |
loc | 检查或编辑未在主语言环境源中表示的本地化数据。 |
migrate new|list|check|run|prune | 编写和管理跟踪的NeoScript迁移。 |
branch | 管理写时复制分支。 |
merge | 合并分支变更。 |
release | 剪切、发布、存档或恢复发布。 |
channel | 管理发布渠道。 |
history | 检查并有意改写保留下来的历史。 |
export [--unity-project <dir>] | 将工作副本(包括本地编辑)写入Unity项目中。 |
除了…之外 neo loc,内容创作通过
追踪原生来源和 neo push. 本地化命令可能会发生变化
权威本地化记录: .neo 不能完全代表;运行
neo pull 随后,在进一步的源代码编辑或推送之前。
login, whoami, logout 使用当前的工作区 apiBaseUrl 当
在下面跑 neo.json. 一个显式的 --api 需要优先。外
工作空间,它们使用CLI构建的默认API源。Login打印
在请求或存储凭据之前选择的来源。
令牌在可用时使用操作系统凭证存储和受保护的本地文件
只能作为退路。CI可以使用 NEO_COMPOSE_TOKEN 或者 --token-stdin。这个
编辑器配置文件无法发布版本;服务器授权仍然是
安全边界。
重新生成对话源名称
每次拉扯,包括 --force 和 --reset,保存对话源名称
服务器存储。来替换生成的默认值,例如 Value_1a2b3c4d5e6f,
NeoFunction_…,或者 Conditions1 使用从编辑器派生的名称,运行:
neo pull --regenerate-source-names
neo status
neo push --dry-run
您选择的名字将被保留。当绑定被重命名时,引用它的代码也被重命名
重写以匹配。自定义链接值首选非空字符串成员
Name在对话范围内使用PascalCase,在节点内使用camelCase。
拉动将重新生成的名称作为局部更改留下。id不变,和
之前不会向服务器写入任何内容 neo push 成功。
包装
node cli/build.mjs 捆绑了Node编排器和浏览器安全的Neo
语言/编译器/编辑资产。npm包中没有c# SDK, Roslyn
应用程序、DLL或。NET运行时元数据。包检查拒绝那些
构件并验证是否存在共享的编译器资产和技能。
开发时的有用检查:
npm run typecheck
npx vitest run cli
npm --prefix cli run test:package
在发布、打包和发布之前运行存储库的完整验证套件
练习VS Code扩展,验证Hello World和live neowyn
格式-4无操作工作流,并运行 npm run doctor 最后的。
源格式
CLI和NeoScript VS Code扩展使用相同的格式化器。把项目
偏好, .editorconfig,包括在父目录 neo/:
[*.{neo,neoflow}]
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
嵌套的EditorConfig节覆盖父节。没有明确的
按照惯例,CLI再生使用两个空格、LF和最后一个换行符。VS代码
扩展使用编辑器设置作为回退并禁用缩进
默认是猜测。要在同步和编辑器保存中使用四个空格或制表符,
将首选项设置为 .editorconfig.
当发出的令牌不变时,Push保留已授权的文本,包括
普通注释和空白行。Pull保留文件而不改变语义。
仅格式化的主体和源位置的差异不会创建稍后的推送,
甚至与其他编辑一起。本地测试使用当前文件位置;
neo push --force-recompile 显式刷新存储位置。内部的变化
字符串字面值仍然很重要。
必须重新生成的文件使用共享格式化策略。网络编辑使用
共享的两个空格默认值,并将包装器缩进排除在存储体之外。