autosnippet 1.5.7 → 1.5.8
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 +18 -2
- package/bin/asd +1 -0
- package/bin/asnip.js +26 -0
- package/checksums.json +1 -1
- package/package.json +1 -1
- package/resources/asd-entry/main.swift +41 -43
- package/scripts/build-asd-entry.js +2 -2
package/README.md
CHANGED
|
@@ -114,7 +114,7 @@ asd install:full --lancedb # 仅安装 LanceDB(向量检索更快)
|
|
|
114
114
|
| **Recipe** | `Knowledge/recipes/` 下的 Markdown 知识(配方):含代码块 + 使用说明,供 AI 检索、Guard、搜索 |
|
|
115
115
|
| **Snippet** | Xcode 代码片段,通过 trigger(默认 `@`)补全,可与 Recipe 关联 |
|
|
116
116
|
| **Candidate(候选)** | 待审核入库的项;来自 `as:create`、MCP 提交、`asd ais` 扫描等,经 Dashboard 审核后保存为 Recipe/Snippet |
|
|
117
|
-
| **Knowledge** | 项目知识库目录,包含 `recipes/`、`.autosnippet/`(索引、candidates、guard 配置等);Snippet 配置在 root spec 的 list
|
|
117
|
+
| **Knowledge** | 项目知识库目录,包含 `recipes/`、`.autosnippet/`(索引、candidates、guard 配置等);Snippet 配置在 root spec 的 list 中。其下各路径与 Git 的关系见 [Knowledge 目录与 Git](#knowledge-目录与-git)。 |
|
|
118
118
|
| **Dashboard** | Web 管理后台(`asd ui` 启动),含 Recipes、Candidates、Guard、Snippets 等页面 |
|
|
119
119
|
| **watch** | 文件监听进程(`asd ui` 或 `asd watch` 启动),保存时触发 `as:create`、`as:guard`、`as:search` |
|
|
120
120
|
| **Guard** | 按 Recipe 知识库对代码做 AI 审查;`// as:guard` 触发 |
|
|
@@ -125,7 +125,23 @@ asd install:full --lancedb # 仅安装 LanceDB(向量检索更快)
|
|
|
125
125
|
| **项目根** | 含 `AutoSnippetRoot.boxspec.json` 的目录 |
|
|
126
126
|
| **Target** | SPM 模块/编译单元;`asd ais <Target>` 扫描该 Target 下的源码提取候选 |
|
|
127
127
|
|
|
128
|
-
**详细介绍**:启动 `asd ui` 后访问 Dashboard → **使用说明** 页;或参阅 [使用文档](docs/使用文档.md)(含 Skills 一览、AI 配置、闭环详解等)。
|
|
128
|
+
**详细介绍**:启动 `asd ui` 后访问 Dashboard → **使用说明** 页;或参阅 [使用文档](docs/使用文档.md)(含 Skills 一览、AI 配置、闭环详解等)。
|
|
129
|
+
|
|
130
|
+
## Knowledge 目录与 Git
|
|
131
|
+
|
|
132
|
+
Knowledge 下各路径与版本控制的关系建议如下(可按项目需要调整):
|
|
133
|
+
|
|
134
|
+
| 路径 | 说明 | 建议 |
|
|
135
|
+
|------|------|------|
|
|
136
|
+
| **Knowledge/recipes/** | Recipe 的 Markdown 文件 | **Git 子仓库**:单独建远程仓库并 `git submodule add <url> Knowledge/recipes`,用于权限拦截(仅能 push 子仓库的人可保存/上传 Recipe)。详见 [权限设置说明](docs/权限设置说明.md) 中「只把 Knowledge/recipes 作为子仓库」。 |
|
|
137
|
+
| **Knowledge/.autosnippet/** | Guard 规则、违反记录、candidates、recipe-stats、context 配置等 | **跟随主仓库 Git**:规则与配置建议提交到主仓库,便于团队共享。 |
|
|
138
|
+
| **Knowledge/.autosnippet/context/index/** | 语义向量索引(embed 生成) | **不跟随 Git**:体积大、机器相关,建议加入 `.gitignore`(如 `Knowledge/.autosnippet/context/index/` 或其下 `lancedb/`、`vector_index.json`)。 |
|
|
139
|
+
| **Knowledge/.autosnippet/candidates/**(若存在) | 候选数据等 | 视需要:若仅本地缓存可不提交;若团队共享可跟随主仓库或单独子仓库。 |
|
|
140
|
+
| **Knowledge/AutoSnippet.spmmap.json**(若存在) | SPM 依赖映射 | **跟随主仓库 Git**:便于依赖关系图一致。 |
|
|
141
|
+
|
|
142
|
+
- **跟随主仓库 Git**:由主项目 `git add/commit/push` 管理,所有人按主仓库权限读写。
|
|
143
|
+
- **Git 子仓库**:`Knowledge/recipes` 为单独仓库(submodule),Recipe 上传(git push)由 Git 服务端权限拦截。配合 `.env` 中 `ASD_RECIPES_WRITE_DIR=Knowledge/recipes` 是为了保证管理员(有 push 权限者)能够正确提交 Recipe:探针目录与 Recipe 写入目录一致,保存后可正常推送。
|
|
144
|
+
- **不跟随 Git**:在 `.gitignore` 中忽略,不提交、不推送。
|
|
129
145
|
|
|
130
146
|
---
|
|
131
147
|
|
package/bin/asd
CHANGED
|
@@ -13,6 +13,7 @@ DIR="$(cd "$(dirname "$SCRIPT")" && pwd)"
|
|
|
13
13
|
# 将调用时的当前目录传给 Node,供查找项目根(dev:link 等场景下 process.cwd() 可能不是用户所在目录)
|
|
14
14
|
ASD_CWD="$(pwd)"
|
|
15
15
|
export ASD_CWD
|
|
16
|
+
# 优先 Swift 入口(完整性校验,仅 macOS),否则回退 node
|
|
16
17
|
if [ -x "$DIR/asd-verify" ]; then
|
|
17
18
|
exec "$DIR/asd-verify" "$@"
|
|
18
19
|
else
|
package/bin/asnip.js
CHANGED
|
@@ -22,6 +22,21 @@
|
|
|
22
22
|
|
|
23
23
|
const fs = require('fs');
|
|
24
24
|
const path = require('path');
|
|
25
|
+
|
|
26
|
+
// 入口校验:包内存在 checksums.json 且未经过 asd-verify(无 ASD_VERIFIED)时,可拒跑或警告,避免绕过完整性校验直接运行 node bin/asnip.js
|
|
27
|
+
const pkgRoot = path.join(__dirname, '..');
|
|
28
|
+
const checksumsPath = path.join(pkgRoot, 'checksums.json');
|
|
29
|
+
if (fs.existsSync(checksumsPath) && process.env.ASD_VERIFIED !== '1') {
|
|
30
|
+
const msg = 'asd: 未经过完整性校验入口(请使用 asd 命令,勿直接运行 node bin/asnip.js)。开发/调试可设 ASD_SKIP_ENTRY_CHECK=1 跳过。';
|
|
31
|
+
if (process.env.ASD_STRICT_ENTRY === '1') {
|
|
32
|
+
console.error(msg);
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
if (process.env.ASD_SKIP_ENTRY_CHECK !== '1') {
|
|
36
|
+
console.warn('⚠️ ' + msg);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
25
40
|
// 读取输入命令
|
|
26
41
|
const inquirer = require('inquirer');
|
|
27
42
|
// 命令行工具
|
|
@@ -1023,6 +1038,17 @@ commander
|
|
|
1023
1038
|
console.log(`${fail} .env: 不存在,请从 .env.example 复制并填写 API Key`);
|
|
1024
1039
|
}
|
|
1025
1040
|
|
|
1041
|
+
// 2.5 写权限探针(可选)
|
|
1042
|
+
try {
|
|
1043
|
+
const writeGuard = require('../lib/writeGuard');
|
|
1044
|
+
const probeDir = writeGuard.getProbeDir(projectRoot);
|
|
1045
|
+
if (probeDir) {
|
|
1046
|
+
const probePath = path.join(projectRoot, probeDir);
|
|
1047
|
+
const exists = fs.existsSync(probePath) && fs.statSync(probePath).isDirectory();
|
|
1048
|
+
console.log(`${exists ? ok : fail} 写权限探针: 已配置 (${probeDir})${exists ? '' : ',目录不存在'}`);
|
|
1049
|
+
}
|
|
1050
|
+
} catch (_) {}
|
|
1051
|
+
|
|
1026
1052
|
// 3. 语义索引(JsonAdapter: context/index/vector_index.json,LanceDB: context/index/lancedb/,manifest 由 embed 写入)
|
|
1027
1053
|
const paths = require('../lib/infra/paths');
|
|
1028
1054
|
const indexPath = paths.getContextIndexPath(projectRoot);
|
package/checksums.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"bin/asnip.js": "
|
|
2
|
+
"bin/asnip.js": "9c8fdca74a0522836c48a61a85db65b065dc8f72e026696040d280d6714edeb9",
|
|
3
3
|
"bin/ui.js": "e1b52cb75b6d404edcb9298f6e3dcae0bf7e2bd95c7df2c38ff020c8111bf96f",
|
|
4
4
|
"lib/writeGuard.js": "f6574f09668d854b871415e96570359859b0ecd79d46d609af90b2d2833bd817"
|
|
5
5
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
*
|
|
1
|
+
#!/usr/bin/env swift
|
|
2
|
+
/*
|
|
3
|
+
* AutoSnippet 完整性校验入口(Swift,仅 macOS)
|
|
4
|
+
* 读 checksums.json,校验关键文件 SHA-256,通过则设置 ASD_VERIFIED=1 并 spawn node bin/asnip.js。
|
|
5
|
+
* 构建:node scripts/build-asd-entry.js,产物 bin/asd-verify。
|
|
4
6
|
*/
|
|
5
7
|
|
|
6
8
|
import Foundation
|
|
@@ -8,66 +10,58 @@ import CryptoKit
|
|
|
8
10
|
|
|
9
11
|
func fail(_ msg: String) -> Never {
|
|
10
12
|
fputs(msg + "\n", stderr)
|
|
11
|
-
fflush(stderr)
|
|
12
13
|
exit(1)
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
///
|
|
16
|
+
/// 从可执行路径解析包根目录(bin/asd-verify 的上级的上级)
|
|
16
17
|
func getPackageRoot() -> String? {
|
|
17
|
-
let
|
|
18
|
-
let
|
|
19
|
-
var
|
|
20
|
-
if
|
|
21
|
-
|
|
18
|
+
let argv0 = CommandLine.arguments[0]
|
|
19
|
+
let path = (argv0 as NSString).standardizingPath
|
|
20
|
+
var url = URL(fileURLWithPath: path)
|
|
21
|
+
if path != (path as NSString).resolvingSymlinksInPath {
|
|
22
|
+
url = URL(fileURLWithPath: (path as NSString).resolvingSymlinksInPath)
|
|
22
23
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
let rootPath = root.path
|
|
27
|
-
let asnipPath = (rootPath as NSString).appendingPathComponent("bin/asnip.js")
|
|
28
|
-
guard FileManager.default.fileExists(atPath: asnipPath) else {
|
|
29
|
-
return nil
|
|
30
|
-
}
|
|
31
|
-
return rootPath
|
|
24
|
+
var dir = url.deletingLastPathComponent().path // bin
|
|
25
|
+
dir = (dir as NSString).deletingLastPathComponent // package root
|
|
26
|
+
return dir
|
|
32
27
|
}
|
|
33
28
|
|
|
34
|
-
///
|
|
35
|
-
func sha256Hex(
|
|
36
|
-
guard let data = try? Data(contentsOf:
|
|
37
|
-
let
|
|
38
|
-
return
|
|
29
|
+
/// 文件内容 SHA-256 小写 hex
|
|
30
|
+
func sha256Hex(filePath: String) -> String? {
|
|
31
|
+
guard let data = try? Data(contentsOf: URL(fileURLWithPath: filePath)) else { return nil }
|
|
32
|
+
let hash = SHA256.hash(data: data)
|
|
33
|
+
return hash.map { String(format: "%02x", $0) }.joined()
|
|
39
34
|
}
|
|
40
35
|
|
|
41
|
-
/// 校验 checksums.json
|
|
36
|
+
/// 校验 checksums.json 中列出的文件。拒绝 relPath 含 ".." 或为绝对路径。
|
|
42
37
|
func verifyIntegrity(root: String, checksumsPath: String) -> Bool {
|
|
43
38
|
guard let data = try? Data(contentsOf: URL(fileURLWithPath: checksumsPath)) else {
|
|
44
39
|
fputs("asd: 无法读取 checksums.json\n", stderr)
|
|
45
40
|
return false
|
|
46
41
|
}
|
|
47
|
-
guard let json = try? JSONSerialization.jsonObject(with: data)
|
|
42
|
+
guard let json = try? JSONSerialization.jsonObject(with: data),
|
|
43
|
+
let entries = json as? [String: String] else {
|
|
48
44
|
fputs("asd: checksums.json 格式无效\n", stderr)
|
|
49
45
|
return false
|
|
50
46
|
}
|
|
51
|
-
let
|
|
52
|
-
|
|
53
|
-
if rootNorm.hasSuffix("/") { rootNorm = String(rootNorm.dropLast()) }
|
|
54
|
-
for (relPath, expectedHex) in json {
|
|
47
|
+
let rootNorm = (root as NSString).standardizingPath
|
|
48
|
+
for (relPath, expectedHex) in entries {
|
|
55
49
|
if relPath.hasPrefix("/") || relPath.contains("..") {
|
|
56
50
|
fputs("asd: 校验拒绝非法路径: \(relPath)\n", stderr)
|
|
57
51
|
return false
|
|
58
52
|
}
|
|
59
|
-
let
|
|
60
|
-
let
|
|
61
|
-
|
|
53
|
+
let fullPath = (root as NSString).appendingPathComponent(relPath)
|
|
54
|
+
let fullNorm = (fullPath as NSString).standardizingPath
|
|
55
|
+
let rootSlash = rootNorm.hasSuffix("/") ? rootNorm : rootNorm + "/"
|
|
56
|
+
guard fullNorm == rootNorm || fullNorm.hasPrefix(rootSlash) else {
|
|
62
57
|
fputs("asd: 校验拒绝路径逃逸: \(relPath)\n", stderr)
|
|
63
58
|
return false
|
|
64
59
|
}
|
|
65
|
-
guard
|
|
66
|
-
|
|
67
|
-
fputs("asd: 校验失败(无法读取): \(relPath)\n", stderr)
|
|
60
|
+
guard let actualHex = sha256Hex(filePath: fullPath) else {
|
|
61
|
+
fputs("asd: 完整性校验失败: \(relPath)\n", stderr)
|
|
68
62
|
return false
|
|
69
63
|
}
|
|
70
|
-
if actualHex != expectedHex {
|
|
64
|
+
if actualHex.lowercased() != expectedHex.lowercased() {
|
|
71
65
|
fputs("asd: 完整性校验失败: \(relPath)\n", stderr)
|
|
72
66
|
return false
|
|
73
67
|
}
|
|
@@ -75,19 +69,22 @@ func verifyIntegrity(root: String, checksumsPath: String) -> Bool {
|
|
|
75
69
|
return true
|
|
76
70
|
}
|
|
77
71
|
|
|
78
|
-
/// 执行 node bin/asnip.js [args...]
|
|
79
|
-
func spawnNode(root: String) -> Int32 {
|
|
72
|
+
/// 执行 node bin/asnip.js [args...],将调用时的 cwd 传入 ASD_CWD,校验通过则设 ASD_VERIFIED=1。
|
|
73
|
+
func spawnNode(root: String, integrityVerified: Bool) -> Int32 {
|
|
80
74
|
let asnipPath = (root as NSString).appendingPathComponent("bin/asnip.js")
|
|
81
75
|
guard FileManager.default.fileExists(atPath: asnipPath) else {
|
|
82
76
|
fail("asd: 未找到 bin/asnip.js")
|
|
83
77
|
}
|
|
84
|
-
let nodeArgs = ["node", asnipPath] + CommandLine.arguments.dropFirst()
|
|
78
|
+
let nodeArgs = ["node", asnipPath] + Array(CommandLine.arguments.dropFirst())
|
|
85
79
|
let process = Process()
|
|
86
80
|
process.executableURL = URL(fileURLWithPath: "/usr/bin/env")
|
|
87
|
-
process.arguments =
|
|
81
|
+
process.arguments = nodeArgs
|
|
88
82
|
process.currentDirectoryURL = URL(fileURLWithPath: root)
|
|
89
83
|
var env = ProcessInfo.processInfo.environment
|
|
90
84
|
env["ASD_CWD"] = FileManager.default.currentDirectoryPath
|
|
85
|
+
if integrityVerified {
|
|
86
|
+
env["ASD_VERIFIED"] = "1"
|
|
87
|
+
}
|
|
91
88
|
process.environment = env
|
|
92
89
|
process.standardInput = FileHandle.standardInput
|
|
93
90
|
process.standardOutput = FileHandle.standardOutput
|
|
@@ -108,11 +105,12 @@ guard let root = getPackageRoot() else {
|
|
|
108
105
|
}
|
|
109
106
|
|
|
110
107
|
let checksumsPath = (root as NSString).appendingPathComponent("checksums.json")
|
|
111
|
-
|
|
108
|
+
var integrityVerified = false
|
|
112
109
|
if FileManager.default.fileExists(atPath: checksumsPath) {
|
|
113
110
|
if !verifyIntegrity(root: root, checksumsPath: checksumsPath) {
|
|
114
111
|
exit(1)
|
|
115
112
|
}
|
|
113
|
+
integrityVerified = true
|
|
116
114
|
}
|
|
117
115
|
|
|
118
|
-
exit(spawnNode(root: root))
|
|
116
|
+
exit(spawnNode(root: root, integrityVerified: integrityVerified))
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* 构建 asd
|
|
5
|
-
*
|
|
4
|
+
* 构建 asd 完整性校验入口(Swift,仅 macOS)。产物:bin/asd-verify。
|
|
5
|
+
* 若不存在或构建失败,bin/asd 将回退到 node bin/asnip.js。
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
if (process.platform !== 'darwin') process.exit(0);
|