@mison/ling 1.1.1 → 1.2.2

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.
@@ -47,7 +47,7 @@ describe("Spec Profile", () => {
47
47
  const enableResult = runCli(["spec", "enable", "--target", "codex", "--quiet"], { env });
48
48
  assert.strictEqual(enableResult.status, 0, enableResult.stderr || enableResult.stdout);
49
49
 
50
- const codexSkill = path.join(tempRoot, ".codex", "skills", "harness-engineering", "SKILL.md");
50
+ const codexSkill = path.join(tempRoot, ".agents", "skills", "harness-engineering", "SKILL.md");
51
51
  const stateFile = path.join(tempRoot, ".ling", "spec", "state.json");
52
52
  const templatesDir = path.join(tempRoot, ".ling", "spec", "templates");
53
53
  const referencesDir = path.join(tempRoot, ".ling", "spec", "references");
@@ -66,7 +66,7 @@ describe("Spec Profile", () => {
66
66
 
67
67
  const disableResult = runCli(["spec", "disable", "--target", "codex", "--quiet"], { env });
68
68
  assert.strictEqual(disableResult.status, 0, disableResult.stderr || disableResult.stdout);
69
- assert.ok(!fs.existsSync(path.join(tempRoot, ".codex", "skills", "harness-engineering")), "spec skill should be removed");
69
+ assert.ok(!fs.existsSync(path.join(tempRoot, ".agents", "skills", "harness-engineering")), "spec skill should be removed");
70
70
  assert.ok(!fs.existsSync(stateFile), "spec state should be removed after final disable");
71
71
  assert.ok(!fs.existsSync(templatesDir), "spec templates should be removed after final disable");
72
72
  assert.ok(!fs.existsSync(referencesDir), "spec references should be removed after final disable");
@@ -75,7 +75,7 @@ describe("Spec Profile", () => {
75
75
 
76
76
  test("spec disable should restore pre-existing skill backup", () => {
77
77
  const env = { LING_GLOBAL_ROOT: tempRoot };
78
- const skillDir = path.join(tempRoot, ".codex", "skills", "harness-engineering");
78
+ const skillDir = path.join(tempRoot, ".agents", "skills", "harness-engineering");
79
79
  fs.mkdirSync(skillDir, { recursive: true });
80
80
  fs.writeFileSync(path.join(skillDir, "SKILL.md"), "legacy skill", "utf8");
81
81
 
@@ -95,7 +95,7 @@ describe("Spec Profile", () => {
95
95
  assert.strictEqual(enableResult.status, 0, enableResult.stderr || enableResult.stdout);
96
96
 
97
97
  const templatesDir = path.join(tempRoot, ".ling", "spec", "templates");
98
- const codexSkillDir = path.join(tempRoot, ".codex", "skills", "harness-engineering");
98
+ const codexSkillDir = path.join(tempRoot, ".agents", "skills", "harness-engineering");
99
99
  fs.rmSync(templatesDir, { recursive: true, force: true });
100
100
  fs.rmSync(codexSkillDir, { recursive: true, force: true });
101
101
 
@@ -114,6 +114,69 @@ describe("Spec Profile", () => {
114
114
  assert.ok(fs.existsSync(path.join(codexSkillDir, "SKILL.md")), "spec skill should be repaired");
115
115
  });
116
116
 
117
+ test("spec enable should install antigravity skills independently", () => {
118
+ const env = { LING_GLOBAL_ROOT: tempRoot };
119
+
120
+ const enableResult = runCli(["spec", "enable", "--target", "antigravity", "--quiet"], { env });
121
+ assert.strictEqual(enableResult.status, 0, enableResult.stderr || enableResult.stdout);
122
+
123
+ const antigravitySkill = path.join(tempRoot, ".gemini", "antigravity", "skills", "harness-engineering", "SKILL.md");
124
+ const geminiSkill = path.join(tempRoot, ".gemini", "skills", "harness-engineering", "SKILL.md");
125
+ assert.ok(fs.existsSync(antigravitySkill), "missing installed antigravity spec skill");
126
+ assert.ok(!fs.existsSync(geminiSkill), "antigravity spec enable should not install gemini skill");
127
+
128
+ const statusResult = runCli(["spec", "status", "--quiet"], { env });
129
+ assert.strictEqual(statusResult.status, 0);
130
+ assert.strictEqual((statusResult.stdout || "").trim(), "installed");
131
+ });
132
+
133
+ test("spec enable should install gemini skills independently", () => {
134
+ const env = { LING_GLOBAL_ROOT: tempRoot };
135
+
136
+ const enableResult = runCli(["spec", "enable", "--target", "gemini", "--quiet"], { env });
137
+ assert.strictEqual(enableResult.status, 0, enableResult.stderr || enableResult.stdout);
138
+
139
+ const geminiSkill = path.join(tempRoot, ".gemini", "skills", "harness-engineering", "SKILL.md");
140
+ const antigravitySkill = path.join(tempRoot, ".gemini", "antigravity", "skills", "harness-engineering", "SKILL.md");
141
+ assert.ok(fs.existsSync(geminiSkill), "missing installed gemini spec skill");
142
+ assert.ok(!fs.existsSync(antigravitySkill), "gemini spec enable should not install antigravity skill");
143
+ });
144
+
145
+ test("spec enable should migrate legacy codex global skill path to ~/.agents/skills", () => {
146
+ const env = { LING_GLOBAL_ROOT: tempRoot };
147
+ const stateDir = path.join(tempRoot, ".ling", "spec");
148
+ fs.mkdirSync(stateDir, { recursive: true });
149
+ fs.writeFileSync(path.join(stateDir, "state.json"), JSON.stringify({
150
+ version: 1,
151
+ updatedAt: new Date().toISOString(),
152
+ targets: {
153
+ codex: {
154
+ enabledAt: new Date().toISOString(),
155
+ consumers: {
156
+ codex: {
157
+ skills: [
158
+ {
159
+ name: "harness-engineering",
160
+ destPath: path.join(tempRoot, ".codex", "skills", "harness-engineering"),
161
+ backupPath: "",
162
+ mode: "created",
163
+ },
164
+ ],
165
+ },
166
+ },
167
+ },
168
+ },
169
+ assets: {},
170
+ }, null, 2), "utf8");
171
+
172
+ const enableResult = runCli(["spec", "enable", "--target", "codex", "--quiet"], { env });
173
+ assert.strictEqual(enableResult.status, 0, enableResult.stderr || enableResult.stdout);
174
+
175
+ const state = JSON.parse(fs.readFileSync(path.join(stateDir, "state.json"), "utf8"));
176
+ const skillState = state.targets.codex.consumers.codex.skills.find((skill) => skill.name === "harness-engineering");
177
+ assert.ok(skillState.destPath.includes(`${path.sep}.agents${path.sep}skills${path.sep}harness-engineering`), "legacy codex path should be migrated to .agents/skills");
178
+ });
179
+
117
180
  test("spec status should report broken when an asset file is missing", () => {
118
181
  const env = { LING_GLOBAL_ROOT: tempRoot };
119
182
 
@@ -137,11 +137,42 @@ describe('Standards Compliance', () => {
137
137
  const file = path.resolve('docs/TECH.md');
138
138
  const content = fs.readFileSync(file, 'utf8');
139
139
 
140
- assert.ok(content.includes('$HOME/.codex/skills/'), 'missing global skill path: $HOME/.codex/skills/');
140
+ assert.ok(content.includes('$HOME/.agents/skills/'), 'missing global skill path: $HOME/.agents/skills/');
141
141
  assert.ok(content.includes('$HOME/.gemini/skills/'), 'missing global skill path: $HOME/.gemini/skills/');
142
142
  assert.ok(content.includes('$HOME/.gemini/antigravity/skills/'), 'missing global skill path: $HOME/.gemini/antigravity/skills/');
143
143
  assert.ok(content.includes('.agents/skills'), 'missing repo skill path: .agents/skills');
144
- assert.ok(!content.includes('$HOME/.agents/skills/'), 'should not contain deprecated global path: $HOME/.agents/skills/');
144
+ assert.ok(!content.includes('$HOME/.codex/skills/'), 'should not contain deprecated global path: $HOME/.codex/skills/');
145
+ });
146
+
147
+ test('README spec section should reflect implemented spec commands', () => {
148
+ const file = path.resolve('README.md');
149
+ const content = fs.readFileSync(file, 'utf8');
150
+
151
+ assert.ok(content.includes('ling spec init'), 'README should mention ling spec init');
152
+ assert.ok(content.includes('ling spec doctor'), 'README should mention ling spec doctor');
153
+ assert.ok(content.includes('~/.ling/spec/profiles/'), 'README should mention spec profiles path');
154
+ assert.ok(!content.includes('本版本尚未开放'), 'README should not claim spec project commands are unavailable');
155
+ assert.ok(!content.includes('spec init/remove/doctor'), 'README should not mention unsupported spec remove subcommand');
156
+ });
157
+
158
+ test('docs should describe gemini and antigravity as separate command targets', () => {
159
+ const readme = fs.readFileSync(path.resolve('README.md'), 'utf8');
160
+ const tech = fs.readFileSync(path.resolve('docs/TECH.md'), 'utf8');
161
+
162
+ assert.ok(readme.includes('ling init --target antigravity'), 'README should document project antigravity target');
163
+ assert.ok(readme.includes('ling global sync --target antigravity'), 'README should document global antigravity target');
164
+ assert.ok(tech.includes('`--target gemini` 只写入 gemini-cli;`--target antigravity` 只写入 antigravity'), 'TECH should describe split global targets');
165
+ assert.ok(!tech.includes('gemini 会同时写入 gemini-cli 与 antigravity'), 'TECH should not describe gemini as bundled antigravity target');
166
+ });
167
+
168
+ test('gitignore should whitelist curated reference materials only', () => {
169
+ const rootIgnore = fs.readFileSync(path.resolve('.gitignore'), 'utf8');
170
+ const referenceIgnore = fs.readFileSync(path.resolve('reference/.gitignore'), 'utf8');
171
+
172
+ assert.ok(rootIgnore.includes('!reference/official/**'), 'root .gitignore should keep reference/official tracked');
173
+ assert.ok(rootIgnore.includes('!reference/docs-archive/**'), 'root .gitignore should keep reference/docs-archive tracked');
174
+ assert.ok(referenceIgnore.includes('!official/**'), 'reference/.gitignore should keep official subtree tracked');
175
+ assert.ok(referenceIgnore.includes('!docs-archive/**'), 'reference/.gitignore should keep docs-archive subtree tracked');
145
176
  });
146
177
 
147
178
  test('.agents script files should stay identical to reference snapshot', { skip: !HAS_REF_SCRIPTS_ROOT }, () => {