@mison/ling 1.0.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/.agents/.shared/ui-ux-pro-max/data/charts.csv +26 -0
- package/.agents/.shared/ui-ux-pro-max/data/colors.csv +97 -0
- package/.agents/.shared/ui-ux-pro-max/data/icons.csv +101 -0
- package/.agents/.shared/ui-ux-pro-max/data/landing.csv +31 -0
- package/.agents/.shared/ui-ux-pro-max/data/products.csv +97 -0
- package/.agents/.shared/ui-ux-pro-max/data/prompts.csv +24 -0
- package/.agents/.shared/ui-ux-pro-max/data/react-performance.csv +45 -0
- package/.agents/.shared/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/.agents/.shared/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- package/.agents/.shared/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
- package/.agents/.shared/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/.agents/.shared/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
- package/.agents/.shared/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
- package/.agents/.shared/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/.agents/.shared/ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/.agents/.shared/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
- package/.agents/.shared/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/.agents/.shared/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/.agents/.shared/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/.agents/.shared/ui-ux-pro-max/data/styles.csv +59 -0
- package/.agents/.shared/ui-ux-pro-max/data/typography.csv +58 -0
- package/.agents/.shared/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
- package/.agents/.shared/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/.agents/.shared/ui-ux-pro-max/data/web-interface.csv +31 -0
- package/.agents/.shared/ui-ux-pro-max/scripts/core.py +258 -0
- package/.agents/.shared/ui-ux-pro-max/scripts/design_system.py +1067 -0
- package/.agents/.shared/ui-ux-pro-max/scripts/search.py +106 -0
- package/.agents/ARCHITECTURE.md +285 -0
- package/.agents/agents/backend-specialist.md +268 -0
- package/.agents/agents/code-archaeologist.md +106 -0
- package/.agents/agents/database-architect.md +225 -0
- package/.agents/agents/debugger.md +225 -0
- package/.agents/agents/devops-engineer.md +242 -0
- package/.agents/agents/documentation-writer.md +104 -0
- package/.agents/agents/explorer-agent.md +73 -0
- package/.agents/agents/frontend-specialist.md +618 -0
- package/.agents/agents/game-developer.md +162 -0
- package/.agents/agents/mobile-developer.md +382 -0
- package/.agents/agents/orchestrator.md +436 -0
- package/.agents/agents/penetration-tester.md +188 -0
- package/.agents/agents/performance-optimizer.md +187 -0
- package/.agents/agents/product-manager.md +112 -0
- package/.agents/agents/product-owner.md +95 -0
- package/.agents/agents/project-planner.md +405 -0
- package/.agents/agents/qa-automation-engineer.md +103 -0
- package/.agents/agents/security-auditor.md +170 -0
- package/.agents/agents/seo-specialist.md +111 -0
- package/.agents/agents/test-engineer.md +158 -0
- package/.agents/mcp_config.json +22 -0
- package/.agents/rules/GEMINI.md +273 -0
- package/.agents/scripts/auto_preview.py +148 -0
- package/.agents/scripts/checklist.py +217 -0
- package/.agents/scripts/session_manager.py +120 -0
- package/.agents/scripts/verify_all.py +327 -0
- package/.agents/skills/api-patterns/SKILL.md +84 -0
- package/.agents/skills/api-patterns/api-style.md +42 -0
- package/.agents/skills/api-patterns/auth.md +24 -0
- package/.agents/skills/api-patterns/documentation.md +26 -0
- package/.agents/skills/api-patterns/graphql.md +41 -0
- package/.agents/skills/api-patterns/rate-limiting.md +31 -0
- package/.agents/skills/api-patterns/response.md +37 -0
- package/.agents/skills/api-patterns/rest.md +40 -0
- package/.agents/skills/api-patterns/scripts/api_validator.py +211 -0
- package/.agents/skills/api-patterns/security-testing.md +122 -0
- package/.agents/skills/api-patterns/trpc.md +41 -0
- package/.agents/skills/api-patterns/versioning.md +22 -0
- package/.agents/skills/app-builder/SKILL.md +75 -0
- package/.agents/skills/app-builder/agent-coordination.md +74 -0
- package/.agents/skills/app-builder/feature-building.md +53 -0
- package/.agents/skills/app-builder/project-detection.md +34 -0
- package/.agents/skills/app-builder/scaffolding.md +118 -0
- package/.agents/skills/app-builder/tech-stack.md +40 -0
- package/.agents/skills/app-builder/templates/SKILL.md +39 -0
- package/.agents/skills/app-builder/templates/astro-static/TEMPLATE.md +76 -0
- package/.agents/skills/app-builder/templates/chrome-extension/TEMPLATE.md +92 -0
- package/.agents/skills/app-builder/templates/cli-tool/TEMPLATE.md +88 -0
- package/.agents/skills/app-builder/templates/electron-desktop/TEMPLATE.md +88 -0
- package/.agents/skills/app-builder/templates/express-api/TEMPLATE.md +83 -0
- package/.agents/skills/app-builder/templates/flutter-app/TEMPLATE.md +90 -0
- package/.agents/skills/app-builder/templates/monorepo-turborepo/TEMPLATE.md +90 -0
- package/.agents/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +122 -0
- package/.agents/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +122 -0
- package/.agents/skills/app-builder/templates/nextjs-static/TEMPLATE.md +169 -0
- package/.agents/skills/app-builder/templates/nuxt-app/TEMPLATE.md +134 -0
- package/.agents/skills/app-builder/templates/python-fastapi/TEMPLATE.md +83 -0
- package/.agents/skills/app-builder/templates/react-native-app/TEMPLATE.md +119 -0
- package/.agents/skills/architecture/SKILL.md +57 -0
- package/.agents/skills/architecture/context-discovery.md +43 -0
- package/.agents/skills/architecture/examples.md +94 -0
- package/.agents/skills/architecture/pattern-selection.md +68 -0
- package/.agents/skills/architecture/patterns-reference.md +50 -0
- package/.agents/skills/architecture/trade-off-analysis.md +77 -0
- package/.agents/skills/bash-linux/SKILL.md +201 -0
- package/.agents/skills/behavioral-modes/SKILL.md +264 -0
- package/.agents/skills/brainstorming/SKILL.md +164 -0
- package/.agents/skills/brainstorming/dynamic-questioning.md +359 -0
- package/.agents/skills/clean-code/SKILL.md +200 -0
- package/.agents/skills/code-review-checklist/SKILL.md +125 -0
- package/.agents/skills/database-design/SKILL.md +54 -0
- package/.agents/skills/database-design/database-selection.md +43 -0
- package/.agents/skills/database-design/indexing.md +39 -0
- package/.agents/skills/database-design/migrations.md +50 -0
- package/.agents/skills/database-design/optimization.md +36 -0
- package/.agents/skills/database-design/orm-selection.md +30 -0
- package/.agents/skills/database-design/schema-design.md +56 -0
- package/.agents/skills/database-design/scripts/schema_validator.py +172 -0
- package/.agents/skills/deployment-procedures/SKILL.md +241 -0
- package/.agents/skills/doc.md +177 -0
- package/.agents/skills/documentation-templates/SKILL.md +194 -0
- package/.agents/skills/frontend-design/SKILL.md +418 -0
- package/.agents/skills/frontend-design/animation-guide.md +331 -0
- package/.agents/skills/frontend-design/color-system.md +307 -0
- package/.agents/skills/frontend-design/decision-trees.md +418 -0
- package/.agents/skills/frontend-design/motion-graphics.md +306 -0
- package/.agents/skills/frontend-design/scripts/accessibility_checker.py +183 -0
- package/.agents/skills/frontend-design/scripts/ux_audit.py +727 -0
- package/.agents/skills/frontend-design/typography-system.md +345 -0
- package/.agents/skills/frontend-design/ux-psychology.md +1118 -0
- package/.agents/skills/frontend-design/visual-effects.md +383 -0
- package/.agents/skills/game-development/2d-games/SKILL.md +119 -0
- package/.agents/skills/game-development/3d-games/SKILL.md +135 -0
- package/.agents/skills/game-development/SKILL.md +167 -0
- package/.agents/skills/game-development/game-art/SKILL.md +185 -0
- package/.agents/skills/game-development/game-audio/SKILL.md +190 -0
- package/.agents/skills/game-development/game-design/SKILL.md +129 -0
- package/.agents/skills/game-development/mobile-games/SKILL.md +108 -0
- package/.agents/skills/game-development/multiplayer/SKILL.md +132 -0
- package/.agents/skills/game-development/pc-games/SKILL.md +144 -0
- package/.agents/skills/game-development/vr-ar/SKILL.md +123 -0
- package/.agents/skills/game-development/web-games/SKILL.md +150 -0
- package/.agents/skills/geo-fundamentals/SKILL.md +155 -0
- package/.agents/skills/geo-fundamentals/scripts/geo_checker.py +289 -0
- package/.agents/skills/i18n-localization/SKILL.md +154 -0
- package/.agents/skills/i18n-localization/scripts/i18n_checker.py +241 -0
- package/.agents/skills/intelligent-routing/SKILL.md +335 -0
- package/.agents/skills/lint-and-validate/SKILL.md +44 -0
- package/.agents/skills/lint-and-validate/scripts/lint_runner.py +184 -0
- package/.agents/skills/lint-and-validate/scripts/type_coverage.py +173 -0
- package/.agents/skills/mcp-builder/SKILL.md +176 -0
- package/.agents/skills/mobile-design/SKILL.md +394 -0
- package/.agents/skills/mobile-design/decision-trees.md +516 -0
- package/.agents/skills/mobile-design/mobile-backend.md +491 -0
- package/.agents/skills/mobile-design/mobile-color-system.md +420 -0
- package/.agents/skills/mobile-design/mobile-debugging.md +122 -0
- package/.agents/skills/mobile-design/mobile-design-thinking.md +355 -0
- package/.agents/skills/mobile-design/mobile-navigation.md +458 -0
- package/.agents/skills/mobile-design/mobile-performance.md +767 -0
- package/.agents/skills/mobile-design/mobile-testing.md +356 -0
- package/.agents/skills/mobile-design/mobile-typography.md +432 -0
- package/.agents/skills/mobile-design/platform-android.md +666 -0
- package/.agents/skills/mobile-design/platform-ios.md +561 -0
- package/.agents/skills/mobile-design/scripts/mobile_audit.py +670 -0
- package/.agents/skills/mobile-design/touch-psychology.md +537 -0
- package/.agents/skills/nextjs-react-expert/1-async-eliminating-waterfalls.md +311 -0
- package/.agents/skills/nextjs-react-expert/2-bundle-bundle-size-optimization.md +241 -0
- package/.agents/skills/nextjs-react-expert/3-server-server-side-performance.md +489 -0
- package/.agents/skills/nextjs-react-expert/4-client-client-side-data-fetching.md +263 -0
- package/.agents/skills/nextjs-react-expert/5-rerender-re-render-optimization.md +581 -0
- package/.agents/skills/nextjs-react-expert/6-rendering-rendering-performance.md +431 -0
- package/.agents/skills/nextjs-react-expert/7-js-javascript-performance.md +683 -0
- package/.agents/skills/nextjs-react-expert/8-advanced-advanced-patterns.md +149 -0
- package/.agents/skills/nextjs-react-expert/SKILL.md +286 -0
- package/.agents/skills/nextjs-react-expert/scripts/convert_rules.py +222 -0
- package/.agents/skills/nextjs-react-expert/scripts/react_performance_checker.py +252 -0
- package/.agents/skills/nodejs-best-practices/SKILL.md +333 -0
- package/.agents/skills/parallel-agents/SKILL.md +193 -0
- package/.agents/skills/performance-profiling/SKILL.md +149 -0
- package/.agents/skills/performance-profiling/scripts/lighthouse_audit.py +120 -0
- package/.agents/skills/plan-writing/SKILL.md +152 -0
- package/.agents/skills/powershell-windows/SKILL.md +166 -0
- package/.agents/skills/python-patterns/SKILL.md +441 -0
- package/.agents/skills/red-team-tactics/SKILL.md +203 -0
- package/.agents/skills/refactoring-patterns/SKILL.md +43 -0
- package/.agents/skills/rust-pro/SKILL.md +190 -0
- package/.agents/skills/seo-fundamentals/SKILL.md +135 -0
- package/.agents/skills/seo-fundamentals/scripts/seo_checker.py +215 -0
- package/.agents/skills/server-management/SKILL.md +161 -0
- package/.agents/skills/systematic-debugging/SKILL.md +114 -0
- package/.agents/skills/tailwind-patterns/SKILL.md +269 -0
- package/.agents/skills/tdd-workflow/SKILL.md +149 -0
- package/.agents/skills/testing-patterns/SKILL.md +178 -0
- package/.agents/skills/testing-patterns/scripts/test_runner.py +219 -0
- package/.agents/skills/vulnerability-scanner/SKILL.md +276 -0
- package/.agents/skills/vulnerability-scanner/checklists.md +131 -0
- package/.agents/skills/vulnerability-scanner/scripts/__pycache__/security_scan.cpython-310.pyc +0 -0
- package/.agents/skills/vulnerability-scanner/scripts/security_scan.py +524 -0
- package/.agents/skills/web-design-guidelines/SKILL.md +57 -0
- package/.agents/skills/webapp-testing/SKILL.md +187 -0
- package/.agents/skills/webapp-testing/scripts/playwright_runner.py +173 -0
- package/.agents/workflows/brainstorm.md +113 -0
- package/.agents/workflows/create.md +59 -0
- package/.agents/workflows/debug.md +103 -0
- package/.agents/workflows/deploy.md +176 -0
- package/.agents/workflows/enhance.md +63 -0
- package/.agents/workflows/orchestrate.md +242 -0
- package/.agents/workflows/plan.md +89 -0
- package/.agents/workflows/preview.md +80 -0
- package/.agents/workflows/restore-localize-compat.md +525 -0
- package/.agents/workflows/status.md +86 -0
- package/.agents/workflows/test.md +144 -0
- package/.agents/workflows/ui-ux-pro-max.md +295 -0
- package/.spec/profiles/codex/AGENTS.spec.md +7 -0
- package/.spec/profiles/codex/ling.spec.rules.md +4 -0
- package/.spec/profiles/gemini/GEMINI.spec.md +5 -0
- package/.spec/references/README.md +36 -0
- package/.spec/references/cse-quickstart.md +96 -0
- package/.spec/references/gda-framework.md +394 -0
- package/.spec/references/harness-engineering-digest.md +93 -0
- package/.spec/skills/cybernetic-systems-engineering/SKILL.md +792 -0
- package/.spec/skills/cybernetic-systems-engineering/agents/openai.yaml +5 -0
- package/.spec/skills/cybernetic-systems-engineering/assets/quickstart.md +96 -0
- package/.spec/skills/cybernetic-systems-engineering/references/README.md +36 -0
- package/.spec/skills/cybernetic-systems-engineering/references/gda-framework.md +394 -0
- package/.spec/skills/cybernetic-systems-engineering/scripts/issues.csv +20 -0
- package/.spec/skills/harness-engineering/SKILL.md +100 -0
- package/.spec/skills/harness-engineering/agents/openai.yaml +4 -0
- package/.spec/skills/harness-engineering/references/harness-engineering-digest.md +93 -0
- package/.spec/templates/driver-prompt.md +7 -0
- package/.spec/templates/handoff.md +9 -0
- package/.spec/templates/issues.template.csv +2 -0
- package/.spec/templates/phase-acceptance.md +9 -0
- package/.spec/templates/review-report.md +9 -0
- package/AGENT_FLOW.md +609 -0
- package/CHANGELOG.md +43 -0
- package/LICENSE +21 -0
- package/README.md +359 -0
- package/bin/adapters/base.js +63 -0
- package/bin/adapters/codex.js +421 -0
- package/bin/adapters/gemini.js +157 -0
- package/bin/ag-kit.js +2266 -0
- package/bin/core/builder.js +80 -0
- package/bin/core/generator.js +59 -0
- package/bin/core/resource-loader.js +64 -0
- package/bin/core/transformer.js +208 -0
- package/bin/interactive.js +65 -0
- package/bin/ling.js +3 -0
- package/bin/utils/atomic-writer.js +97 -0
- package/bin/utils/git-helper.js +68 -0
- package/bin/utils/managed-block.js +65 -0
- package/bin/utils/manifest.js +244 -0
- package/bin/utils.js +89 -0
- package/docs/PLAN.md +54 -0
- package/docs/TECH.md +191 -0
- package/package.json +56 -0
- package/scripts/ci-verify.js +110 -0
- package/scripts/clean.js +123 -0
- package/scripts/health-check.js +143 -0
- package/scripts/health-check.sh +6 -0
- package/scripts/postinstall-check.js +112 -0
- package/scripts/run-tests.js +49 -0
- package/tests/atomic-writer.test.js +47 -0
- package/tests/clean-script.test.js +77 -0
- package/tests/cli-smoke.test.js +479 -0
- package/tests/codex-adapter.test.js +132 -0
- package/tests/doctor.test.js +94 -0
- package/tests/gemini-adapter.test.js +30 -0
- package/tests/generator.test.js +48 -0
- package/tests/git-helper.test.js +53 -0
- package/tests/global-sync.test.js +133 -0
- package/tests/health-check-script.test.js +34 -0
- package/tests/managed-block.test.js +41 -0
- package/tests/manifest.test.js +97 -0
- package/tests/package-tarball.test.js +33 -0
- package/tests/phase-c.test.js +107 -0
- package/tests/spec-profile.test.js +86 -0
- package/tests/standards-compliance.test.js +303 -0
- package/tests/transformer.test.js +74 -0
- package/tests/versioning.test.js +51 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
|
|
4
|
+
function escapeRegex(input) {
|
|
5
|
+
return input.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function detectLineEnding(content) {
|
|
9
|
+
return content.includes("\r\n") ? "\r\n" : "\n";
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function buildMarkers(blockId) {
|
|
13
|
+
const id = String(blockId || "default").trim();
|
|
14
|
+
return {
|
|
15
|
+
begin: `<!-- BEGIN AG-KIT MANAGED BLOCK: ${id} -->`,
|
|
16
|
+
end: `<!-- END AG-KIT MANAGED BLOCK: ${id} -->`,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function buildManagedBlock(blockId, body, lineEnding = "\n") {
|
|
21
|
+
const markers = buildMarkers(blockId);
|
|
22
|
+
const normalizedBody = String(body || "").replace(/\r?\n/g, lineEnding).trimEnd();
|
|
23
|
+
return `${markers.begin}${lineEnding}${normalizedBody}${lineEnding}${markers.end}`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function upsertManagedBlock(filePath, blockId, body, options = {}) {
|
|
27
|
+
const dryRun = Boolean(options.dryRun);
|
|
28
|
+
const original = fs.existsSync(filePath) ? fs.readFileSync(filePath, "utf8") : "";
|
|
29
|
+
const lineEnding = detectLineEnding(original);
|
|
30
|
+
const managedBlock = buildManagedBlock(blockId, body, lineEnding);
|
|
31
|
+
const markers = buildMarkers(blockId);
|
|
32
|
+
const blockRegex = new RegExp(
|
|
33
|
+
`${escapeRegex(markers.begin)}[\\s\\S]*?${escapeRegex(markers.end)}`,
|
|
34
|
+
"m",
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
let next = "";
|
|
38
|
+
let action = "unchanged";
|
|
39
|
+
|
|
40
|
+
if (!original) {
|
|
41
|
+
next = `${managedBlock}${lineEnding}`;
|
|
42
|
+
action = "created";
|
|
43
|
+
} else if (blockRegex.test(original)) {
|
|
44
|
+
next = original.replace(blockRegex, managedBlock);
|
|
45
|
+
action = next === original ? "unchanged" : "updated";
|
|
46
|
+
if (next && !/\r?\n$/.test(next)) {
|
|
47
|
+
next += lineEnding;
|
|
48
|
+
}
|
|
49
|
+
} else {
|
|
50
|
+
next = `${original.replace(/\r?\n?$/, "")}${lineEnding}${lineEnding}${managedBlock}${lineEnding}`;
|
|
51
|
+
action = "appended";
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (!dryRun && action !== "unchanged") {
|
|
55
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
56
|
+
fs.writeFileSync(filePath, next, "utf8");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return { action };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
module.exports = {
|
|
63
|
+
buildManagedBlock,
|
|
64
|
+
upsertManagedBlock,
|
|
65
|
+
};
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const crypto = require("crypto");
|
|
4
|
+
|
|
5
|
+
class ManifestManager {
|
|
6
|
+
constructor(manifestPath, options = {}) {
|
|
7
|
+
this.manifestPath = manifestPath;
|
|
8
|
+
this.options = options;
|
|
9
|
+
this.manifest = {
|
|
10
|
+
version: 2,
|
|
11
|
+
target: typeof options.target === "string" ? options.target : "",
|
|
12
|
+
kitVersion: typeof options.kitVersion === "string" ? options.kitVersion : "",
|
|
13
|
+
updatedAt: "",
|
|
14
|
+
files: {},
|
|
15
|
+
};
|
|
16
|
+
this.lastLoadError = null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Compute SHA-256 hash of a string or buffer
|
|
21
|
+
* @param {string|Buffer} content
|
|
22
|
+
* @returns {string} sha256 hash
|
|
23
|
+
*/
|
|
24
|
+
static computeHash(content) {
|
|
25
|
+
return crypto.createHash("sha256").update(content).digest("hex");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
static normalizeFileEntry(entry) {
|
|
29
|
+
if (typeof entry === "string") {
|
|
30
|
+
return { hash: entry, source: "" };
|
|
31
|
+
}
|
|
32
|
+
if (!entry || typeof entry !== "object") {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
const hash = typeof entry.hash === "string" ? entry.hash : "";
|
|
36
|
+
const source = typeof entry.source === "string" ? entry.source : "";
|
|
37
|
+
if (!hash) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
return { hash, source };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
static extractHash(entry) {
|
|
44
|
+
if (typeof entry === "string") {
|
|
45
|
+
return entry;
|
|
46
|
+
}
|
|
47
|
+
if (entry && typeof entry === "object" && typeof entry.hash === "string") {
|
|
48
|
+
return entry.hash;
|
|
49
|
+
}
|
|
50
|
+
return "";
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
static normalizeManifestShape(input) {
|
|
54
|
+
const output = {
|
|
55
|
+
version: typeof input.version === "number" ? input.version : 2,
|
|
56
|
+
target: typeof input.target === "string" ? input.target : "",
|
|
57
|
+
kitVersion: typeof input.kitVersion === "string" ? input.kitVersion : "",
|
|
58
|
+
updatedAt: typeof input.updatedAt === "string" ? input.updatedAt : "",
|
|
59
|
+
files: {},
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
if (!input.files || typeof input.files !== "object") {
|
|
63
|
+
return output;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
for (const [relPath, entry] of Object.entries(input.files)) {
|
|
67
|
+
const normalized = ManifestManager.normalizeFileEntry(entry);
|
|
68
|
+
if (!normalized) {
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
output.files[relPath] = normalized;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return output;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Load manifest from disk
|
|
79
|
+
*/
|
|
80
|
+
load() {
|
|
81
|
+
this.lastLoadError = null;
|
|
82
|
+
if (fs.existsSync(this.manifestPath)) {
|
|
83
|
+
try {
|
|
84
|
+
const raw = fs.readFileSync(this.manifestPath, "utf8");
|
|
85
|
+
this.manifest = ManifestManager.normalizeManifestShape(JSON.parse(raw));
|
|
86
|
+
} catch (err) {
|
|
87
|
+
this.lastLoadError = err;
|
|
88
|
+
this.manifest = {
|
|
89
|
+
version: 2,
|
|
90
|
+
target: typeof this.options.target === "string" ? this.options.target : "",
|
|
91
|
+
kitVersion: typeof this.options.kitVersion === "string" ? this.options.kitVersion : "",
|
|
92
|
+
updatedAt: "",
|
|
93
|
+
files: {},
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return this.manifest;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Save manifest to disk
|
|
102
|
+
*/
|
|
103
|
+
save() {
|
|
104
|
+
this.manifest.updatedAt = new Date().toISOString();
|
|
105
|
+
if (!this.manifest.target && this.options.target) {
|
|
106
|
+
this.manifest.target = this.options.target;
|
|
107
|
+
}
|
|
108
|
+
if (!this.manifest.kitVersion && this.options.kitVersion) {
|
|
109
|
+
this.manifest.kitVersion = this.options.kitVersion;
|
|
110
|
+
}
|
|
111
|
+
const dir = path.dirname(this.manifestPath);
|
|
112
|
+
if (!fs.existsSync(dir)) {
|
|
113
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
114
|
+
}
|
|
115
|
+
fs.writeFileSync(this.manifestPath, `${JSON.stringify(this.manifest, null, 2)}\n`, "utf8");
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Generate manifest entries from a directory recursively
|
|
120
|
+
* @param {string} rootDir Directory to scan
|
|
121
|
+
* @param {string} [baseDir] Base directory for relative paths (default: rootDir)
|
|
122
|
+
* @returns {object} { file: hash } map
|
|
123
|
+
*/
|
|
124
|
+
static generateFromDir(rootDir, baseDir = rootDir) {
|
|
125
|
+
const entries = ManifestManager.generateFileEntriesFromDir(rootDir, {
|
|
126
|
+
baseDir,
|
|
127
|
+
});
|
|
128
|
+
const hashes = {};
|
|
129
|
+
for (const [relPath, entry] of Object.entries(entries)) {
|
|
130
|
+
hashes[relPath] = entry.hash;
|
|
131
|
+
}
|
|
132
|
+
return hashes;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Generate managed file entries from a directory recursively
|
|
137
|
+
* @param {string} rootDir Directory to scan
|
|
138
|
+
* @param {object} options
|
|
139
|
+
* @param {string} [options.baseDir=rootDir] Base directory for relative paths
|
|
140
|
+
* @param {string} [options.sourcePrefix=""] Prefix recorded in `source` field
|
|
141
|
+
* @returns {object} { file: { hash, source } } map
|
|
142
|
+
*/
|
|
143
|
+
static generateFileEntriesFromDir(rootDir, options = {}) {
|
|
144
|
+
const baseDir = options.baseDir || rootDir;
|
|
145
|
+
const sourcePrefix = typeof options.sourcePrefix === "string" ? options.sourcePrefix.trim() : "";
|
|
146
|
+
const files = {};
|
|
147
|
+
|
|
148
|
+
function scan(dir) {
|
|
149
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
150
|
+
for (const entry of entries) {
|
|
151
|
+
const fullPath = path.join(dir, entry.name);
|
|
152
|
+
if (entry.isDirectory()) {
|
|
153
|
+
scan(fullPath);
|
|
154
|
+
} else if (entry.isFile()) {
|
|
155
|
+
const content = fs.readFileSync(fullPath);
|
|
156
|
+
const hash = ManifestManager.computeHash(content);
|
|
157
|
+
const relPath = path.relative(baseDir, fullPath).split(path.sep).join("/");
|
|
158
|
+
files[relPath] = {
|
|
159
|
+
hash,
|
|
160
|
+
source: sourcePrefix ? `${sourcePrefix}/${relPath}` : relPath,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (fs.existsSync(rootDir)) {
|
|
167
|
+
scan(rootDir);
|
|
168
|
+
}
|
|
169
|
+
return files;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Compare existing manifest with current file system state to detect user changes
|
|
174
|
+
* @param {string} projectRoot
|
|
175
|
+
* @returns {object} { modified: [], missing: [], unknown: [] }
|
|
176
|
+
*/
|
|
177
|
+
checkDrift(projectRoot) {
|
|
178
|
+
// This checks if the files listed in the manifest (which should match what we installed)
|
|
179
|
+
// still match what is currently on disk.
|
|
180
|
+
// Useful for doctor or update pre-check.
|
|
181
|
+
const drift = {
|
|
182
|
+
modified: [], // Hash mismatch
|
|
183
|
+
missing: [], // File in manifest but not on disk
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
for (const [relPath, entry] of Object.entries(this.manifest.files || {})) {
|
|
187
|
+
const expectedHash = ManifestManager.extractHash(entry);
|
|
188
|
+
if (!expectedHash) {
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
const absPath = path.join(projectRoot, relPath);
|
|
192
|
+
if (!fs.existsSync(absPath)) {
|
|
193
|
+
drift.missing.push(relPath);
|
|
194
|
+
} else {
|
|
195
|
+
const content = fs.readFileSync(absPath);
|
|
196
|
+
const currentHash = ManifestManager.computeHash(content);
|
|
197
|
+
if (currentHash !== expectedHash) {
|
|
198
|
+
drift.modified.push(relPath);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return drift;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Select user-modified files to backup during overwrite:
|
|
207
|
+
* currentHash != manifestHash AND currentHash != incomingHash
|
|
208
|
+
* @param {string} projectRoot
|
|
209
|
+
* @param {object} incomingFiles Next manifest files map
|
|
210
|
+
* @returns {string[]} relative file paths
|
|
211
|
+
*/
|
|
212
|
+
collectSmartOverwriteConflicts(projectRoot, incomingFiles = {}) {
|
|
213
|
+
const modified = [];
|
|
214
|
+
const existingFiles = this.manifest.files || {};
|
|
215
|
+
|
|
216
|
+
for (const [relPath, manifestEntry] of Object.entries(existingFiles)) {
|
|
217
|
+
const expectedHash = ManifestManager.extractHash(manifestEntry);
|
|
218
|
+
if (!expectedHash) {
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const absPath = path.join(projectRoot, relPath);
|
|
223
|
+
if (!fs.existsSync(absPath)) {
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const currentHash = ManifestManager.computeHash(fs.readFileSync(absPath));
|
|
228
|
+
if (currentHash === expectedHash) {
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const incomingHash = ManifestManager.extractHash(incomingFiles[relPath]);
|
|
233
|
+
if (incomingHash && currentHash === incomingHash) {
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
modified.push(relPath);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return modified;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
module.exports = ManifestManager;
|
package/bin/utils.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
const { execSync } = require("child_process");
|
|
2
|
+
const fs = require("fs");
|
|
3
|
+
const path = require("path");
|
|
4
|
+
const os = require("os");
|
|
5
|
+
|
|
6
|
+
const REPO_URL = "https://github.com/MisonL/Ling.git";
|
|
7
|
+
|
|
8
|
+
function parseJsonSafe(raw) {
|
|
9
|
+
try {
|
|
10
|
+
return JSON.parse(raw);
|
|
11
|
+
} catch (err) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function readGlobalNpmDependencies() {
|
|
17
|
+
const cmd = "npm ls --global --depth=0 --json --silent";
|
|
18
|
+
let output = "";
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
output = execSync(cmd, {
|
|
22
|
+
encoding: "utf8",
|
|
23
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
24
|
+
});
|
|
25
|
+
} catch (err) {
|
|
26
|
+
output = typeof err.stdout === "string" ? err.stdout : "";
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (!output || output.trim() === "") {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const data = parseJsonSafe(output);
|
|
34
|
+
if (!data || typeof data !== "object") {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const deps = data.dependencies;
|
|
39
|
+
if (!deps || typeof deps !== "object") {
|
|
40
|
+
return {};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return deps;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function cloneBranchAgentDir(branch, options) {
|
|
47
|
+
const safeBranch = branch.trim();
|
|
48
|
+
if (!/^[A-Za-z0-9._/-]+$/.test(safeBranch)) {
|
|
49
|
+
throw new Error(`非法分支名: ${branch}`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "ag-kit-"));
|
|
53
|
+
const logFn = options && options.logger ? options.logger : console.log;
|
|
54
|
+
|
|
55
|
+
if (!options.quiet) logFn(`[download] 正在从 ${REPO_URL} 拉取分支 ${safeBranch} ...`);
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
execSync(`git clone --depth 1 --branch ${safeBranch} ${REPO_URL} "${tempDir}"`, {
|
|
59
|
+
stdio: options.quiet ? "ignore" : "pipe",
|
|
60
|
+
});
|
|
61
|
+
} catch (err) {
|
|
62
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
63
|
+
throw new Error(`无法拉取分支 ${safeBranch},请确认分支存在且网络可用`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const clonedAgentsDir = path.join(tempDir, ".agents");
|
|
67
|
+
const clonedAgentDir = path.join(tempDir, ".agent");
|
|
68
|
+
let templateDir = "";
|
|
69
|
+
|
|
70
|
+
if (fs.existsSync(clonedAgentsDir)) {
|
|
71
|
+
templateDir = clonedAgentsDir;
|
|
72
|
+
} else if (fs.existsSync(clonedAgentDir)) {
|
|
73
|
+
templateDir = clonedAgentDir;
|
|
74
|
+
} else {
|
|
75
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
76
|
+
throw new Error(`分支 ${safeBranch} 中未找到 .agents 或 .agent 目录`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
agentDir: templateDir,
|
|
81
|
+
cleanup: () => fs.rmSync(tempDir, { recursive: true, force: true }),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
module.exports = {
|
|
86
|
+
parseJsonSafe,
|
|
87
|
+
readGlobalNpmDependencies,
|
|
88
|
+
cloneBranchAgentDir
|
|
89
|
+
};
|
package/docs/PLAN.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# 灵轨规划(PLAN)
|
|
2
|
+
|
|
3
|
+
## 一句话
|
|
4
|
+
灵轨只做一件事:把仓库内统一的 `.agents/` 模板,优雅地投影到不同平台(Gemini/Antigravity 与 Codex),并提供安全、可回滚的更新机制。
|
|
5
|
+
|
|
6
|
+
## 设计原则
|
|
7
|
+
- 命令面少而强:用 `sync` 统一“首次安装 + 更新”,避免双心智。
|
|
8
|
+
- 默认安全:不做全局清理、不自动写入高风险全局规则。
|
|
9
|
+
- 模板源单一:仓库 Canonical 为 `.agents/`;旧 `.agent/` 只作为输入兼容,不作为主路径。
|
|
10
|
+
|
|
11
|
+
## 能力边界(项目 vs 全局)
|
|
12
|
+
### 项目安装(功能最完整)
|
|
13
|
+
- 命令:`ling init` / `ling update` / `ling status` / `ling doctor`
|
|
14
|
+
- 目标输出:
|
|
15
|
+
- `gemini` -> 项目内 `.agent/`(兼容 Gemini/Antigravity 工作区规范)
|
|
16
|
+
- `codex` -> 项目内 `.agents/`(受管目录)+ 注入工作区 `AGENTS.md` / `ling.rules` 托管区块
|
|
17
|
+
|
|
18
|
+
### 全局安装(跨项目复用 Skills)
|
|
19
|
+
- 命令:`ling global sync` / `ling global status`
|
|
20
|
+
- 默认行为:`ling global sync` 未指定 `--target/--targets` 时,同步 `codex + gemini`
|
|
21
|
+
- 目标路径:
|
|
22
|
+
- `codex` -> `$HOME/.codex/skills/`
|
|
23
|
+
- `gemini` -> 同时写入 `$HOME/.gemini/skills/` 与 `$HOME/.gemini/antigravity/skills/`
|
|
24
|
+
- 安全边界:全局只同步 Skills,不写入全局 Rules/Agents/Workflows。
|
|
25
|
+
|
|
26
|
+
## 覆盖与回滚(全局同步)
|
|
27
|
+
- 覆盖单位:每个 Skill 目录。
|
|
28
|
+
- 覆盖策略:只覆盖同名 Skill;不清理用户已有的其他 Skill。
|
|
29
|
+
- 覆盖前备份:每次覆盖同名 Skill 前备份到 `$HOME/.ling/backups/global/<timestamp>/...`。
|
|
30
|
+
|
|
31
|
+
## Spec Profile(可选进阶层)
|
|
32
|
+
### 当前阶段已落地
|
|
33
|
+
- 命令:`ling spec enable` / `ling spec disable` / `ling spec status`
|
|
34
|
+
- 默认关闭,必须显式启用
|
|
35
|
+
- 当前只开放全局层,不进入项目级注入
|
|
36
|
+
- 目标资源:
|
|
37
|
+
- Skills:`harness-engineering`、`cybernetic-systems-engineering`
|
|
38
|
+
- Templates:`$HOME/.ling/spec/templates/`
|
|
39
|
+
- References:`$HOME/.ling/spec/references/`
|
|
40
|
+
- 回退原则:启用前先备份同名资源;停用时优先恢复原资源
|
|
41
|
+
|
|
42
|
+
### 后续阶段
|
|
43
|
+
- 项目级 `spec init / remove / doctor`
|
|
44
|
+
- `.spec/profiles/` 的项目投影与可逆注入
|
|
45
|
+
|
|
46
|
+
## 兼容策略
|
|
47
|
+
- Gemini/Antigravity:输出 `.agent/`,保持与官方工作区机制一致。
|
|
48
|
+
- Codex:受管目录为 `.agents/`,并使用 `manifest.json` 做完整性与漂移检测;识别并迁移遗留 `.codex/`。
|
|
49
|
+
- 全局同步遵循真实消费端目录,而不是仓库模板源目录;仓库内仍以 `.agents/` 作为唯一 Canonical。
|
|
50
|
+
|
|
51
|
+
## 成功标准
|
|
52
|
+
- `ling global sync` 一条命令即可完成全局 Skills 安装/更新(默认 codex + gemini,其中 gemini 同步到 gemini-cli 与 antigravity)。
|
|
53
|
+
- 覆盖可回滚:每次覆盖同名 Skill 都有可用备份。
|
|
54
|
+
- 跨平台 CI(Linux/macOS/Windows)验证主链路通过。
|
package/docs/TECH.md
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# 灵轨技术说明(TECH)
|
|
2
|
+
|
|
3
|
+
## 快速验证(维护者)
|
|
4
|
+
```bash
|
|
5
|
+
bun install
|
|
6
|
+
bun run test
|
|
7
|
+
bun run ci:verify
|
|
8
|
+
bun run health-check
|
|
9
|
+
cd web && bun install && bun run lint
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## 核心目录与职责
|
|
13
|
+
- `.agents/`:仓库模板源(Canonical)
|
|
14
|
+
- `bin/ling.js`:CLI 入口与命令分发
|
|
15
|
+
- `bin/adapters/`:目标差异(`gemini` / `codex`)
|
|
16
|
+
- `bin/core/`:构建/转换(将 Workflows 投影为 Codex Skills 等)
|
|
17
|
+
- `bin/utils/`:原子写入、manifest、托管区块等通用能力
|
|
18
|
+
|
|
19
|
+
## 路径映射(最重要)
|
|
20
|
+
### 项目级(功能最完整)
|
|
21
|
+
- `gemini`:项目根目录 `.agent/`
|
|
22
|
+
- `codex`:项目根目录 `.agents/`(受管)+ `.agents-backup/`(漂移覆盖备份)
|
|
23
|
+
|
|
24
|
+
### 全局级(仅同步 Skills)
|
|
25
|
+
- `codex`:`$HOME/.codex/skills/`
|
|
26
|
+
- `gemini-cli`:`$HOME/.gemini/skills/`
|
|
27
|
+
- `antigravity`:`$HOME/.gemini/antigravity/skills/`
|
|
28
|
+
|
|
29
|
+
> 说明:仓库内 Skills 源路径为 `.agents/skills/`,全局同步会将其投影到真实工具读取的全局目录;仓库 Canonical 仍是 `.agents/`。
|
|
30
|
+
|
|
31
|
+
## 端到端链路(简述)
|
|
32
|
+
### 项目安装 / 更新
|
|
33
|
+
- `init`:选择目标 -> 适配器 `install()` -> 落盘目标目录(Gemini: `.agent/`;Codex: `.agents/`)->(Codex)注入托管区块到工作区 `AGENTS.md` 与 `ling.rules`
|
|
34
|
+
- `update`:自动检测已安装目标(或通过 `--target/--targets` 指定)-> 适配器 `update()` ->(Codex)漂移检测与备份 -> 原子替换
|
|
35
|
+
- `doctor`:检查完整性;`--fix` 尝试修复(Codex 支持迁移 `.codex/` 与重写托管区块)
|
|
36
|
+
|
|
37
|
+
### Codex 构建(Workflow -> Skill)
|
|
38
|
+
- 输入:`.agents/skills/` 与 `.agents/workflows/`
|
|
39
|
+
- 规则:每个 Workflow `<name>.md` 会转换为一个 Skill:`workflow-<name>/SKILL.md`
|
|
40
|
+
- 输出(受管目录 `.agents/` 内):`skills/`、`codex.json`、`AGENTS.md`、`ling.rules`、`manifest.json`
|
|
41
|
+
|
|
42
|
+
## 全局同步:`ling global sync/status`
|
|
43
|
+
### 默认目标
|
|
44
|
+
- 未指定 `--target/--targets`:默认同步 `codex + gemini`
|
|
45
|
+
- `--target gemini` / `--targets codex,gemini` 中的 `gemini` 会同时写入 gemini-cli 与 antigravity 两个消费端目录
|
|
46
|
+
|
|
47
|
+
### 来源与覆盖策略
|
|
48
|
+
- 来源:默认使用本包内置 `.agents/`;也可用 `--branch <name>` 从远端分支拉取模板源
|
|
49
|
+
- 覆盖单位:每个 Skill 目录
|
|
50
|
+
- 覆盖策略:只覆盖同名 Skill,不清理其他 Skill
|
|
51
|
+
- 原子替换:按 Skill 目录原子替换,避免半写状态
|
|
52
|
+
- 覆盖前备份:覆盖同名 Skill 前备份到 `$HOME/.ling/backups/global/<timestamp>/<consumer>/<skill>/...`
|
|
53
|
+
- `consumer` 可能是 `codex`、`gemini-cli`、`antigravity`
|
|
54
|
+
|
|
55
|
+
### 测试隔离
|
|
56
|
+
- `LING_GLOBAL_ROOT`:替代 `$HOME`(用于测试与 CI,避免污染真实用户目录)
|
|
57
|
+
|
|
58
|
+
## Spec Profile:`ling spec enable/disable/status`
|
|
59
|
+
### 当前范围
|
|
60
|
+
- 当前只实现全局层:
|
|
61
|
+
- `ling spec enable [--target codex|gemini] [--dry-run] [--quiet]`
|
|
62
|
+
- `ling spec disable [--target codex|gemini] [--dry-run] [--quiet]`
|
|
63
|
+
- `ling spec status [--quiet]`
|
|
64
|
+
- 默认目标:未指定 `--target/--targets` 时启用 `codex + gemini`
|
|
65
|
+
- 当前 Spec 源目录:`.spec/`
|
|
66
|
+
|
|
67
|
+
### 落盘与状态
|
|
68
|
+
- Spec 状态文件:`$HOME/.ling/spec/state.json`
|
|
69
|
+
- Spec templates:`$HOME/.ling/spec/templates/`
|
|
70
|
+
- Spec references:`$HOME/.ling/spec/references/`
|
|
71
|
+
- Spec 备份目录:`$HOME/.ling/backups/spec/<timestamp>/before/...`
|
|
72
|
+
|
|
73
|
+
### 当前安装内容
|
|
74
|
+
- 全局 Skills:
|
|
75
|
+
- `harness-engineering`
|
|
76
|
+
- `cybernetic-systems-engineering`
|
|
77
|
+
- Templates:
|
|
78
|
+
- `issues.template.csv`
|
|
79
|
+
- `driver-prompt.md`
|
|
80
|
+
- `review-report.md`
|
|
81
|
+
- `phase-acceptance.md`
|
|
82
|
+
- `handoff.md`
|
|
83
|
+
- References:
|
|
84
|
+
- `harness-engineering-digest.md`
|
|
85
|
+
- `gda-framework.md`
|
|
86
|
+
- 相关 quickstart / README
|
|
87
|
+
|
|
88
|
+
### 状态契约
|
|
89
|
+
- `ling spec status --quiet` 输出:
|
|
90
|
+
- `installed`
|
|
91
|
+
- `broken`
|
|
92
|
+
- `missing`
|
|
93
|
+
- 退出码沿用统一约定:
|
|
94
|
+
- `0` = `installed`
|
|
95
|
+
- `1` = `broken`
|
|
96
|
+
- `2` = `missing`
|
|
97
|
+
|
|
98
|
+
### 回退语义
|
|
99
|
+
- `spec enable`:
|
|
100
|
+
- 若目标位置已存在同名 Skill,会先备份再覆盖
|
|
101
|
+
- 若 `templates/` 或 `references/` 已存在,也会先备份
|
|
102
|
+
- `spec disable`:
|
|
103
|
+
- 若存在备份,恢复启用前快照
|
|
104
|
+
- 若启用前不存在资源,则删除由 Spec 安装的目录
|
|
105
|
+
- 当前尚未实现项目级 `spec init / remove / doctor`
|
|
106
|
+
|
|
107
|
+
## 状态契约(自动化)
|
|
108
|
+
- `ling status --quiet` / `ling global status --quiet` 只输出三态:
|
|
109
|
+
- `installed`:检测到目标且完整性正常
|
|
110
|
+
- `broken`:检测到目标但存在残缺、漂移或结构异常
|
|
111
|
+
- `missing`:未检测到任何已安装目标
|
|
112
|
+
- 退出码固定为:
|
|
113
|
+
- `0` = `installed`
|
|
114
|
+
- `1` = `broken`
|
|
115
|
+
- `2` = `missing`
|
|
116
|
+
- 若需要问题明细,使用 `ling doctor`;`status` 负责健康状态,`doctor` 负责诊断细节。
|
|
117
|
+
|
|
118
|
+
## 手动回滚(全局 Skills)
|
|
119
|
+
1. 找到备份目录:`$HOME/.ling/backups/global/<timestamp>/...`
|
|
120
|
+
2. 按 Skill 回滚(推荐一次只处理一个 Skill 目录):
|
|
121
|
+
- Codex 目标:恢复到 `$HOME/.codex/skills/<skill>/`
|
|
122
|
+
- Gemini CLI:恢复到 `$HOME/.gemini/skills/<skill>/`
|
|
123
|
+
- Antigravity:恢复到 `$HOME/.gemini/antigravity/skills/<skill>/`
|
|
124
|
+
|
|
125
|
+
macOS / Linux 示例(把某个 Skill 回滚为备份版本):
|
|
126
|
+
```bash
|
|
127
|
+
ts="2026-03-12T12-00-00-000Z"
|
|
128
|
+
skill="clean-code"
|
|
129
|
+
rm -rf "$HOME/.codex/skills/$skill"
|
|
130
|
+
cp -a "$HOME/.ling/backups/global/$ts/codex/$skill" "$HOME/.codex/skills/$skill"
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Windows PowerShell 示例:
|
|
134
|
+
```powershell
|
|
135
|
+
$ts = "2026-03-12T12-00-00-000Z"
|
|
136
|
+
$skill = "clean-code"
|
|
137
|
+
Remove-Item "$HOME\\.codex\\skills\\$skill" -Recurse -Force -ErrorAction SilentlyContinue
|
|
138
|
+
Copy-Item "$HOME\\.ling\\backups\\global\\$ts\\codex\\$skill" "$HOME\\.codex\\skills\\$skill" -Recurse -Force
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Gemini CLI 回滚示例:
|
|
142
|
+
```bash
|
|
143
|
+
ts="2026-03-12T12-00-00-000Z"
|
|
144
|
+
skill="clean-code"
|
|
145
|
+
rm -rf "$HOME/.gemini/skills/$skill"
|
|
146
|
+
cp -a "$HOME/.ling/backups/global/$ts/gemini-cli/$skill" "$HOME/.gemini/skills/$skill"
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Antigravity 回滚示例:
|
|
150
|
+
```bash
|
|
151
|
+
ts="2026-03-12T12-00-00-000Z"
|
|
152
|
+
skill="clean-code"
|
|
153
|
+
rm -rf "$HOME/.gemini/antigravity/skills/$skill"
|
|
154
|
+
cp -a "$HOME/.ling/backups/global/$ts/antigravity/$skill" "$HOME/.gemini/antigravity/skills/$skill"
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## 环境变量
|
|
158
|
+
- `LING_INDEX_PATH`:工作区索引文件路径(默认 `~/.ling/workspaces.json`)
|
|
159
|
+
- `LING_GLOBAL_ROOT`:全局目录根(替代 `$HOME`)
|
|
160
|
+
- `LING_SKIP_UPSTREAM_CHECK`:跳过上游同名包安装提示(测试用)
|
|
161
|
+
|
|
162
|
+
## 安装提示机制
|
|
163
|
+
- npm 全局安装:`postinstall` 会尽力检测并提示上游英文版 `@vudovn/ag-kit` 冲突。
|
|
164
|
+
- Bun 全局安装:Bun 默认会阻止本包 `postinstall`;因此冲突提示以内置 CLI 运行期检查为准,会在 `init` / `update` / `update-all` / `global sync` 时提示。
|
|
165
|
+
- 冲突提示只负责提醒,不会自动修改当前安装状态;如需清理可执行 `npm uninstall -g @vudovn/ag-kit`。
|
|
166
|
+
|
|
167
|
+
## 常见故障
|
|
168
|
+
- 更新中断:原子替换保证不会出现半写状态;重新运行 `update`/`global sync` 即可。
|
|
169
|
+
- Windows `EPERM/EBUSY`:通常是目录被占用;关闭占用 `.agents/` 或目标 Skill 目录的进程后重试。
|
|
170
|
+
- 漂移覆盖:Codex 若检测到用户修改受管文件,会在覆盖前写入 `.agents-backup/<timestamp>/`。
|
|
171
|
+
|
|
172
|
+
## 跨平台与文本编码约束
|
|
173
|
+
- 编码与换行:仓库内分发的文本与模板资源使用 UTF-8 与 LF(避免 CRLF 引发的解析与 diff 噪声)。
|
|
174
|
+
- 终端可读性:模板文本与脚本输出避免使用 Emoji 或装饰性 Unicode 字符,统一采用纯 ASCII 标记(例如 `[OK]`、`[FAIL]`),以提升 Windows/WSL/Linux/macOS 终端与编辑器的显示一致性。
|
|
175
|
+
- Web 文档站快捷键提示:搜索弹窗在 macOS 显示 `Cmd + K`,在其他平台显示 `Ctrl + K`。
|
|
176
|
+
- 安全基线检查:安全扫描脚本会检查 Web 站点的基础安全响应头配置;当前实现位于 `web/next.config.ts`。
|
|
177
|
+
|
|
178
|
+
## Codex 官方 `.rules`(手动配置)
|
|
179
|
+
灵轨不会自动写入全局 `~/.codex/rules/default.rules`,避免引入不可预期的全局副作用。若你需要启用 Codex 官方命令审批策略(如 `prefix_rule()`),可按需手动创建:
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
# default.rules
|
|
183
|
+
load("builtin://rules/rules.star", "prefix_rule")
|
|
184
|
+
|
|
185
|
+
rules = [
|
|
186
|
+
prefix_rule(["ls"], action="allow"),
|
|
187
|
+
prefix_rule(["cat"], action="allow"),
|
|
188
|
+
prefix_rule(["rg"], action="allow"),
|
|
189
|
+
prefix_rule(["git", "status"], action="allow"),
|
|
190
|
+
]
|
|
191
|
+
```
|