@anchorlock/cli 0.0.0-stage → 0.2.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/lib/demo.js ADDED
@@ -0,0 +1,48 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const os = require('os');
5
+ const path = require('path');
6
+
7
+ const CONTRACT = {
8
+ schemaVersion: '1',
9
+ units: [
10
+ {
11
+ id: 'order_no',
12
+ type: 'string',
13
+ aliases: ['orderId', 'order_sn', '订单编号', '单号'],
14
+ },
15
+ { id: 'trade_no', type: 'string' },
16
+ ],
17
+ };
18
+
19
+ /** init 落盘:空名单。字段从本次任务写,不要塞 demo 订单字段。 */
20
+ const STARTER = {
21
+ schemaVersion: '1',
22
+ units: [],
23
+ };
24
+
25
+ const DRIFT = 'export function row() {\n return { orderId: "A10086", order_sn: "S1" };\n}\n';
26
+ const ALIGNED = 'export function row() {\n return { order_no: "A10086", trade_no: "T1" };\n}\n';
27
+
28
+ /**
29
+ * @param {(opts: object) => Promise<object>} checkNames
30
+ */
31
+ async function runDemo(checkNames) {
32
+ const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'anchorlock-demo-'));
33
+ try {
34
+ const src = path.join(tmp, 'src');
35
+ fs.mkdirSync(src);
36
+ const contract = path.join(tmp, 'constraints.json');
37
+ fs.writeFileSync(contract, JSON.stringify(CONTRACT, null, 2) + '\n');
38
+ fs.writeFileSync(path.join(src, 'order.js'), DRIFT);
39
+ const red = await checkNames({ contract: contract, src: src, relRoot: tmp });
40
+ fs.writeFileSync(path.join(src, 'order.js'), ALIGNED);
41
+ const green = await checkNames({ contract: contract, src: src, relRoot: tmp });
42
+ return { red: red, green: green };
43
+ } finally {
44
+ fs.rmSync(tmp, { recursive: true, force: true });
45
+ }
46
+ }
47
+
48
+ module.exports = { runDemo, CONTRACT, STARTER, DRIFT, ALIGNED };
package/lib/engine.js ADDED
@@ -0,0 +1,36 @@
1
+ 'use strict';
2
+
3
+ const path = require('path');
4
+
5
+ function loadEngine() {
6
+ const tried = [];
7
+ const candidates = [
8
+ path.resolve(__dirname, '../../anchorlock-core'),
9
+ '@anchorlock/core',
10
+ ];
11
+ for (let i = 0; i < candidates.length; i++) {
12
+ const id = candidates[i];
13
+ try {
14
+ const spec = require(id);
15
+ if (
16
+ spec &&
17
+ typeof spec.checkNames === 'function' &&
18
+ typeof spec.draftIds === 'function' &&
19
+ typeof spec.writeConstraintsFromOpenApi === 'function' &&
20
+ typeof spec.draftConstraintsFromCode === 'function' &&
21
+ typeof spec.writePin === 'function'
22
+ ) {
23
+ return spec;
24
+ }
25
+ tried.push(id + ' (missing checkNames/draftIds/writeConstraintsFromOpenApi; need >= 0.5.17)');
26
+ } catch (e) {
27
+ tried.push(id + ' (' + (e && e.code ? e.code : e.message) + ')');
28
+ }
29
+ }
30
+ throw new Error(
31
+ 'Anchorlock needs @anchorlock/core >= 0.6.0 (checkNames / pin / constraints).\n tried: ' +
32
+ tried.join('\n tried: '),
33
+ );
34
+ }
35
+
36
+ module.exports = { loadEngine };
@@ -0,0 +1,46 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ function skillSourcePath() {
7
+ return path.join(__dirname, '..', 'skills', 'anchorlock', 'SKILL.md');
8
+ }
9
+
10
+ function readSkill() {
11
+ return fs.readFileSync(skillSourcePath(), 'utf8');
12
+ }
13
+
14
+ function installAgentFiles(root, opts) {
15
+ opts = opts || {};
16
+ const dests = [
17
+ path.join(root, '.cursor', 'skills', 'anchorlock', 'SKILL.md'),
18
+ path.join(root, '.claude', 'skills', 'anchorlock', 'SKILL.md'),
19
+ ];
20
+ const body = readSkill();
21
+ const written = [];
22
+ dests.forEach(function (dest) {
23
+ if (!opts.dryRun) {
24
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
25
+ fs.writeFileSync(dest, body, 'utf8');
26
+ }
27
+ written.push(dest);
28
+ });
29
+ const agents = path.join(root, 'AGENTS.md');
30
+ const stamp =
31
+ '\n## AnchorLock\n\n消费仓对照钉死的 OpenAPI(`contracts/*.pin.json`)收紧字段名:`npx anchor check --contract contracts/order.pin.json --src src --strict --require-hash`。没有说明书时,一处 `anchor init --from-code` 起草,**人确认**后再 check;不要从需求/PRD 编写名单。要用新字段等后端发新 tag,由人 `anchor repin --tag vX.Y.Z --change <需求号>` 整份换;不要为了绿灯 repin 或改 pin。多 Agent 共享同一份 pin,不是每人一份名单。抛锚是 SemanticAnchor 的 `bind` / `verify_usage`,不是这条 check。\n';
32
+ if (!opts.dryRun) {
33
+ if (!fs.existsSync(agents)) {
34
+ fs.writeFileSync(agents, '# Agent\n' + stamp, 'utf8');
35
+ } else {
36
+ const cur = fs.readFileSync(agents, 'utf8');
37
+ if (cur.indexOf('AnchorLock') === -1) {
38
+ fs.appendFileSync(agents, stamp, 'utf8');
39
+ }
40
+ }
41
+ }
42
+ written.push(agents);
43
+ return { skill: dests, agents: agents, written: written };
44
+ }
45
+
46
+ module.exports = { skillSourcePath, readSkill, installAgentFiles };
package/lib/term.js ADDED
@@ -0,0 +1,45 @@
1
+ 'use strict';
2
+
3
+ let forceOff = false;
4
+
5
+ function setNoColor(v) {
6
+ forceOff = !!v;
7
+ }
8
+
9
+ function supportsColor(stream) {
10
+ if (forceOff) return false;
11
+ if (process.env.NO_COLOR != null && process.env.NO_COLOR !== '') return false;
12
+ if (process.env.FORCE_COLOR === '0') return false;
13
+ if (process.env.FORCE_COLOR && process.env.FORCE_COLOR !== '0') return true;
14
+ const s = stream || process.stdout;
15
+ return !!(s && s.isTTY);
16
+ }
17
+
18
+ function paint(codes, text, stream) {
19
+ const s = String(text == null ? '' : text);
20
+ if (!supportsColor(stream)) return s;
21
+ const seq = Array.isArray(codes) ? codes.join(';') : String(codes);
22
+ return '\u001b[' + seq + 'm' + s + '\u001b[0m';
23
+ }
24
+
25
+ const bold = function (t, stream) { return paint(1, t, stream); };
26
+ const dim = function (t, stream) { return paint(2, t, stream); };
27
+ const green = function (t, stream) { return paint(32, t, stream); };
28
+ const red = function (t, stream) { return paint(31, t, stream); };
29
+ const cyan = function (t, stream) { return paint(36, t, stream); };
30
+
31
+ function quote(name) {
32
+ return '"' + name + '"';
33
+ }
34
+
35
+ module.exports = {
36
+ setNoColor,
37
+ supportsColor,
38
+ paint,
39
+ bold,
40
+ dim,
41
+ green,
42
+ red,
43
+ cyan,
44
+ quote,
45
+ };
package/package.json CHANGED
@@ -1,6 +1,46 @@
1
1
  {
2
2
  "name": "@anchorlock/cli",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.2.0",
4
+ "description": "Field-name gate: consumer repos vs a pinned OpenAPI.",
5
+ "bin": {
6
+ "anchor": "./bin/anchor.js"
7
+ },
8
+ "main": "lib/cli.js",
9
+ "files": [
10
+ "bin",
11
+ "lib",
12
+ "README.md",
13
+ "LICENSE",
14
+ "CHANGELOG.md",
15
+ "docs",
16
+ "public/index.html",
17
+ "ci",
18
+ "skills"
19
+ ],
20
+ "scripts": {
21
+ "test": "node scripts/run-tests.js",
22
+ "demo": "node bin/anchor.js demo",
23
+ "preview": "npx --yes serve public -l 4174",
24
+ "prepublishOnly": "npm test"
25
+ },
26
+ "engines": {
27
+ "node": ">=18"
28
+ },
29
+ "dependencies": {
30
+ "@anchorlock/core": "^0.6.0"
31
+ },
32
+ "homepage": "https://www.npmjs.com/package/@anchorlock/cli",
33
+ "publishConfig": {
34
+ "access": "public",
35
+ "registry": "https://registry.npmjs.org/"
36
+ },
37
+ "keywords": [
38
+ "ai",
39
+ "contract",
40
+ "field-names",
41
+ "lint",
42
+ "ci",
43
+ "openapi"
44
+ ],
45
+ "license": "MIT"
46
+ }