alp-code 0.9.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.
Files changed (204) hide show
  1. package/CHANGELOG.md +770 -0
  2. package/LICENSE +21 -0
  3. package/README.md +295 -0
  4. package/alp.config.yaml +5 -0
  5. package/dist/src/agents/agent-definition.js +28 -0
  6. package/dist/src/agents/capability-catalog.js +33 -0
  7. package/dist/src/agents/compaction.js +36 -0
  8. package/dist/src/agents/errors.js +12 -0
  9. package/dist/src/agents/librarian.js +38 -0
  10. package/dist/src/agents/main.js +37 -0
  11. package/dist/src/agents/memory-grant.js +29 -0
  12. package/dist/src/agents/model-context.js +70 -0
  13. package/dist/src/agents/modes.js +134 -0
  14. package/dist/src/agents/oracle.js +36 -0
  15. package/dist/src/agents/read-thread.js +38 -0
  16. package/dist/src/agents/registry.js +238 -0
  17. package/dist/src/agents/render-identity.js +38 -0
  18. package/dist/src/agents/review.js +37 -0
  19. package/dist/src/agents/search.js +37 -0
  20. package/dist/src/agents/shared/house-rules.js +33 -0
  21. package/dist/src/agents/shared/principal.js +18 -0
  22. package/dist/src/agents/shared/voice.js +29 -0
  23. package/dist/src/agents/titling.js +32 -0
  24. package/dist/src/agents/types.js +15 -0
  25. package/dist/src/backend/execution-backend.js +2 -0
  26. package/dist/src/backend/local-execution-store.js +144 -0
  27. package/dist/src/backend/local-process-backend.js +533 -0
  28. package/dist/src/backend/local-supervisor.js +104 -0
  29. package/dist/src/cli/alp.js +380 -0
  30. package/dist/src/cli/commands/context.js +203 -0
  31. package/dist/src/cli/commands/delegate.js +136 -0
  32. package/dist/src/cli/commands/identity-sync.js +31 -0
  33. package/dist/src/cli/commands/init.js +184 -0
  34. package/dist/src/cli/commands/mode.js +22 -0
  35. package/dist/src/cli/commands/principal.js +114 -0
  36. package/dist/src/cli/commands/run-main.js +90 -0
  37. package/dist/src/cli/commands/runtime.js +21 -0
  38. package/dist/src/cli/mode-preference-store.js +62 -0
  39. package/dist/src/cli/mode-selector.js +178 -0
  40. package/dist/src/cli/update-check.js +77 -0
  41. package/dist/src/context/checkpoint.js +134 -0
  42. package/dist/src/context/compact-journal.js +153 -0
  43. package/dist/src/context/compact-payload.js +121 -0
  44. package/dist/src/context/continuity.js +70 -0
  45. package/dist/src/context/types.js +2 -0
  46. package/dist/src/delegation/backend-registry.js +40 -0
  47. package/dist/src/delegation/delegation-service.js +300 -0
  48. package/dist/src/delegation/types.js +12 -0
  49. package/dist/src/execution/execution-policy.js +96 -0
  50. package/dist/src/execution/execution-service.js +115 -0
  51. package/dist/src/execution/execution-store.js +78 -0
  52. package/dist/src/execution/identity-capsule.js +65 -0
  53. package/dist/src/execution/types.js +12 -0
  54. package/dist/src/hooks/execution-bridge.js +84 -0
  55. package/dist/src/index.js +4 -0
  56. package/dist/src/memory/adapters/markdown-file-store.js +257 -0
  57. package/dist/src/memory/adapters/memory-api-client.js +2 -0
  58. package/dist/src/memory/adapters/memory-path-mapper.js +76 -0
  59. package/dist/src/memory/adapters/remote-api-store.js +25 -0
  60. package/dist/src/memory/context-ranker.js +21 -0
  61. package/dist/src/memory/errors.js +58 -0
  62. package/dist/src/memory/memory-service.js +149 -0
  63. package/dist/src/memory/memory-store.js +2 -0
  64. package/dist/src/memory/types.js +2 -0
  65. package/dist/src/policy/capability-policy.js +29 -0
  66. package/dist/src/policy/delegation-policy.js +25 -0
  67. package/dist/src/policy/errors.js +10 -0
  68. package/dist/src/policy/invariants.js +31 -0
  69. package/dist/src/policy/memory-policy.js +22 -0
  70. package/dist/src/policy/policy-engine.js +85 -0
  71. package/dist/src/policy/types.js +8 -0
  72. package/dist/src/policy/workspace-policy.js +77 -0
  73. package/dist/src/principal/principal-profile-store.js +89 -0
  74. package/dist/src/runtime/adapter-files.js +147 -0
  75. package/dist/src/runtime/claude-adapter.js +177 -0
  76. package/dist/src/runtime/codex-adapter.js +169 -0
  77. package/dist/src/runtime/permission-rules.js +156 -0
  78. package/dist/src/runtime/render-session-context.js +124 -0
  79. package/dist/src/runtime/render-task-input.js +33 -0
  80. package/dist/src/runtime/runtime-adapter.js +2 -0
  81. package/dist/src/runtime/runtime-preference-store.js +66 -0
  82. package/dist/src/runtime/runtime-selector.js +178 -0
  83. package/dist/src/runtime/types.js +2 -0
  84. package/dist/src/runtime/windows-shim.js +57 -0
  85. package/dist/src/state-paths.js +49 -0
  86. package/dist/src/workflow/output-validator.js +27 -0
  87. package/dist/src/workflow/repair-policy.js +8 -0
  88. package/dist/src/workflow/types.js +22 -0
  89. package/dist/src/workflow/workflow-runner.js +81 -0
  90. package/hooks/compact-record.cjs +109 -0
  91. package/hooks/session-boot.cjs +112 -0
  92. package/hooks/session-end.cjs +34 -0
  93. package/package.json +48 -0
  94. package/scaffold/memory/INDEX.md +27 -0
  95. package/scaffold/memory/README.md +76 -0
  96. package/scaffold/memory/projects/INDEX.md +22 -0
  97. package/scaffold/memory/projects/PROTOCOL.md +128 -0
  98. package/scaffold/memory/projects/_template/PROJECT.md +45 -0
  99. package/scripts/alp.cjs +126 -0
  100. package/scripts/alp.ps1 +4 -0
  101. package/scripts/alp.sh +3 -0
  102. package/scripts/bootstrap.cjs +144 -0
  103. package/scripts/checkout-release.cjs +30 -0
  104. package/scripts/delegate.cjs +19 -0
  105. package/scripts/doctor.cjs +158 -0
  106. package/scripts/doctor.sh +3 -0
  107. package/scripts/ensure-state.cjs +22 -0
  108. package/scripts/lib/cli-link.cjs +375 -0
  109. package/scripts/lib/codex-role.cjs +18 -0
  110. package/scripts/lib/delegation/command-runner.cjs +108 -0
  111. package/scripts/lib/delegation/config.cjs +81 -0
  112. package/scripts/lib/install-paths.cjs +154 -0
  113. package/scripts/lib/release-manifest.cjs +42 -0
  114. package/scripts/lib/semver-lite.cjs +20 -0
  115. package/scripts/lib/state.cjs +274 -0
  116. package/scripts/lib/uninstall.cjs +252 -0
  117. package/scripts/lib/update-check-worker.cjs +21 -0
  118. package/scripts/lib/update.cjs +395 -0
  119. package/scripts/run-role.cjs +42 -0
  120. package/scripts/run-role.ps1 +4 -0
  121. package/scripts/run-role.sh +3 -0
  122. package/scripts/sync-project-index.sh +167 -0
  123. package/skills/agent-memory/SKILL.md +109 -0
  124. package/skills/alp-debug/SKILL.md +90 -0
  125. package/skills/alp-debug/references/defense-in-depth.md +118 -0
  126. package/skills/alp-debug/references/investigation-methodology.md +106 -0
  127. package/skills/alp-debug/references/log-and-ci-analysis.md +96 -0
  128. package/skills/alp-debug/references/performance-diagnostics.md +112 -0
  129. package/skills/alp-debug/references/reporting-standards.md +120 -0
  130. package/skills/alp-debug/references/root-cause-tracing.md +134 -0
  131. package/skills/alp-debug/references/systematic-debugging.md +93 -0
  132. package/skills/alp-debug/references/verification.md +86 -0
  133. package/skills/alp-debug/scripts/find-polluter.sh +63 -0
  134. package/skills/alp-debug/scripts/find-polluter.test.md +102 -0
  135. package/skills/alp-plan/SKILL.md +128 -0
  136. package/skills/alp-plan/references/archive-workflow.md +77 -0
  137. package/skills/alp-plan/references/codebase-understanding.md +55 -0
  138. package/skills/alp-plan/references/output-standards.md +96 -0
  139. package/skills/alp-plan/references/plan-organization.md +129 -0
  140. package/skills/alp-plan/references/red-team-personas.md +76 -0
  141. package/skills/alp-plan/references/red-team-workflow.md +81 -0
  142. package/skills/alp-plan/references/research-phase.md +57 -0
  143. package/skills/alp-plan/references/scope-challenge.md +82 -0
  144. package/skills/alp-plan/references/solution-design.md +76 -0
  145. package/skills/alp-plan/references/validate-question-framework.md +89 -0
  146. package/skills/alp-plan/references/validate-workflow.md +83 -0
  147. package/skills/alp-predict/SKILL.md +98 -0
  148. package/skills/alp-scenario/SKILL.md +86 -0
  149. package/skills/code-review/SKILL.md +111 -0
  150. package/skills/code-review/references/code-review-reception.md +114 -0
  151. package/skills/code-review/references/edge-case-scouting.md +78 -0
  152. package/skills/code-review/references/verification-before-completion.md +117 -0
  153. package/skills/delegation/SKILL.md +46 -0
  154. package/skills/docs-seeker/.env.example +15 -0
  155. package/skills/docs-seeker/SKILL.md +87 -0
  156. package/skills/docs-seeker/package.json +25 -0
  157. package/skills/docs-seeker/references/advanced.md +82 -0
  158. package/skills/docs-seeker/references/context7-patterns.md +68 -0
  159. package/skills/docs-seeker/references/errors.md +72 -0
  160. package/skills/docs-seeker/scripts/analyze-llms-txt.js +211 -0
  161. package/skills/docs-seeker/scripts/detect-topic.js +172 -0
  162. package/skills/docs-seeker/scripts/fetch-docs.js +213 -0
  163. package/skills/docs-seeker/scripts/tests/run-tests.js +72 -0
  164. package/skills/docs-seeker/scripts/tests/test-analyze-llms.js +119 -0
  165. package/skills/docs-seeker/scripts/tests/test-detect-topic.js +112 -0
  166. package/skills/docs-seeker/scripts/tests/test-fetch-docs.js +84 -0
  167. package/skills/docs-seeker/scripts/utils/env-loader.js +94 -0
  168. package/skills/docs-seeker/workflows/library-search.md +73 -0
  169. package/skills/docs-seeker/workflows/repo-analysis.md +90 -0
  170. package/skills/docs-seeker/workflows/topic-search.md +69 -0
  171. package/skills/git/SKILL.md +121 -0
  172. package/skills/git/references/branch-management.md +90 -0
  173. package/skills/git/references/commit-standards.md +82 -0
  174. package/skills/git/references/gh-cli-guide.md +132 -0
  175. package/skills/git/references/safety-protocols.md +86 -0
  176. package/skills/git/references/workflow-commit.md +89 -0
  177. package/skills/git/references/workflow-merge.md +63 -0
  178. package/skills/git/references/workflow-pr.md +70 -0
  179. package/skills/git/references/workflow-push.md +62 -0
  180. package/skills/gkg/SKILL.md +87 -0
  181. package/skills/gkg/references/cli-commands.md +92 -0
  182. package/skills/gkg/references/http-api.md +99 -0
  183. package/skills/gkg/references/language-support.md +54 -0
  184. package/skills/problem-solving/SKILL.md +86 -0
  185. package/skills/problem-solving/references/attribution.md +48 -0
  186. package/skills/problem-solving/references/collision-zone-thinking.md +71 -0
  187. package/skills/problem-solving/references/inversion-exercise.md +88 -0
  188. package/skills/problem-solving/references/meta-pattern-recognition.md +80 -0
  189. package/skills/problem-solving/references/scale-game.md +82 -0
  190. package/skills/problem-solving/references/simplification-cascades.md +83 -0
  191. package/skills/problem-solving/references/when-stuck.md +76 -0
  192. package/skills/repomix/SKILL.md +94 -0
  193. package/skills/repomix/references/configuration.md +134 -0
  194. package/skills/repomix/references/usage-patterns.md +106 -0
  195. package/skills/repomix/scripts/.coverage +0 -0
  196. package/skills/repomix/scripts/README.md +179 -0
  197. package/skills/repomix/scripts/repomix_batch.py +455 -0
  198. package/skills/repomix/scripts/repos.example.json +15 -0
  199. package/skills/repomix/scripts/requirements.txt +15 -0
  200. package/skills/repomix/scripts/tests/test_repomix_batch.py +531 -0
  201. package/skills/research/SKILL.md +107 -0
  202. package/skills/security-scan/SKILL.md +101 -0
  203. package/skills/security-scan/references/secret-patterns.md +75 -0
  204. package/skills/security-scan/references/vulnerability-patterns.md +136 -0
@@ -0,0 +1,126 @@
1
+ #!/usr/bin/env node
2
+ // Stable CommonJS bootstrap. All parsing, policy, identity, and runtime selection live
3
+ // in the compiled TypeScript CLI; this file only ensures that entrypoint exists.
4
+
5
+ const fs = require("fs");
6
+ const path = require("path");
7
+ const { spawnSync } = require("child_process");
8
+ const { spawnSyncCommand } = require("./lib/delegation/command-runner.cjs");
9
+
10
+ const repoRoot = path.resolve(__dirname, "..");
11
+ const maintenance = process.argv[2];
12
+
13
+ // Ba lệnh dưới đây cố ý chạy thẳng, không qua dist/: chúng phải dùng được cả khi build hỏng
14
+ // — doctor để chẩn đoán, update để sửa, uninstall để gỡ một bản cài đã hỏng. Đánh đổi: chúng
15
+ // không đi qua main() nên không hiện thông báo có bản mới. `help` thì không có lý do đó, nên
16
+ // nó đi qua CLI đã compile để chỉ có một nguồn help duy nhất (helpText() trong cli/alp.ts).
17
+
18
+ if (maintenance === "doctor") {
19
+ const result = spawnSync(process.execPath, [path.join(repoRoot, "scripts", "doctor.cjs"), ...process.argv.slice(3)], { cwd: repoRoot, stdio: "inherit" });
20
+ process.exit(result.status ?? 2);
21
+ }
22
+
23
+ if (maintenance === "update") {
24
+ const { updateInstallation } = require("./lib/update.cjs");
25
+ const options = process.argv.slice(3);
26
+ const unknown = options.find((value) => value !== "--verbose");
27
+ if (unknown) {
28
+ console.error(`ERROR unknown update option \`${unknown}\``);
29
+ process.exit(2);
30
+ }
31
+ // Mặc định im lặng: `alp update` là một việc vặt, người dùng cần biết đi từ bản nào sang bản
32
+ // nào chứ không cần đọc git fetch với npm ci. `--verbose` trả lại toàn bộ output, cần khi
33
+ // build hỏng và phải nhìn lỗi thật.
34
+ const verbose = options.includes("--verbose");
35
+ // updateInstallation là async (resolve tag release cần gọi mạng). Gọi đồng bộ rồi đọc
36
+ // `result.ok` trên một Promise sẽ luôn ra undefined — v0.1.0/v0.1.1 in "ERROR undefined"
37
+ // và không update gì cả.
38
+ updateInstallation(repoRoot, {
39
+ verbose,
40
+ // Tải hoặc cài đặt là bước lâu nhất, nên nó phải có một dòng báo — im lặng vài chục giây
41
+ // trông như treo. Không dùng `\r` để vẽ lại dòng: `alp update` hay bị chạy qua log,
42
+ // qua CI hay qua pane không phải TTY, chỗ đó `\r` chỉ thành rác.
43
+ onCheckout({ from, tag }) {
44
+ console.log(`${from ? `v${from} → ` : ""}${tag}`);
45
+ console.log("Đang cập nhật…");
46
+ },
47
+ }).then(
48
+ (result) => {
49
+ if (!result.ok) {
50
+ console.error(`✗ ${result.message}`);
51
+ process.exit(1);
52
+ }
53
+ if (result.unchanged) console.log(`✓ alp-code ${result.tag} — đã là bản mới nhất`);
54
+ else console.log(`✓ alp-code ${result.tag} — memory, runtime và backend preferences giữ nguyên (~/.alp)`);
55
+ process.exit(0);
56
+ },
57
+ (error) => {
58
+ console.error(`✗ ${error && error.message ? error.message : String(error)}`);
59
+ process.exit(1);
60
+ },
61
+ );
62
+ return;
63
+ }
64
+
65
+ if (maintenance === "uninstall") {
66
+ const args = new Set(process.argv.slice(3));
67
+ for (const arg of args) if (!["--purge-memory", "--force"].includes(arg)) {
68
+ console.error(`ERROR unknown uninstall option \`${arg}\``);
69
+ process.exit(2);
70
+ }
71
+ const { uninstall } = require("./lib/uninstall.cjs");
72
+ try {
73
+ const result = uninstall(repoRoot, { purgeMemory: args.has("--purge-memory"), force: args.has("--force") });
74
+ for (const item of result.log) console.log(`${item.level.padEnd(9)} ${item.text}`);
75
+ process.exit(0);
76
+ } catch (error) {
77
+ console.error(`ERROR ${error.message}`);
78
+ process.exit(1);
79
+ }
80
+ }
81
+ // Bản phát hành đã có sẵn `dist/`; chỉ dev clone mới có `src/` để build. Thiếu `dist/` trong
82
+ // một bản cài phát hành nghĩa là artifact hỏng hoặc bị xoá dở — chạy `npm run build` ở đó chỉ
83
+ // đổi một lỗi rõ ràng lấy một lỗi tsc khó hiểu, vì `src/` và devDependencies đều không có.
84
+ const entry = path.join(repoRoot, "dist", "src", "cli", "alp.js");
85
+ if (!fs.existsSync(entry)) {
86
+ if (!fs.existsSync(path.join(repoRoot, "src"))) {
87
+ console.error(`ERROR bản cài tại ${repoRoot} thiếu dist/ — cài lại bằng \`npm i -g alp-code\` hoặc installer`);
88
+ process.exit(2);
89
+ }
90
+ const built = spawnSyncCommand("npm", ["run", "build"], {
91
+ cwd: repoRoot,
92
+ stdio: "inherit",
93
+ });
94
+ if (built.error || built.status !== 0) {
95
+ console.error(`ERROR cannot build ALP TypeScript CLI${built.error ? `: ${built.error.message}` : ""}`);
96
+ process.exit(built.status || 2);
97
+ }
98
+ }
99
+
100
+ // `npm i -g alp-code` không chạy bootstrap, và `--ignore-scripts` là mặc định ở nhiều nơi.
101
+ // Nên điều kiện tiên quyết (`~/.alp` và hook forwarder) phải tự dựng ở lần dùng đầu tiên. Đọc
102
+ // một file JSON nhỏ để biết có cần làm gì không: bình thường thì đây chỉ là một stat thừa.
103
+ ensureStateIfStale();
104
+ process.env.ALP_REPO_ROOT = repoRoot;
105
+ require(entry).main(process.argv.slice(2)).then(
106
+ (code) => { process.exitCode = code; },
107
+ (error) => {
108
+ console.error(`ERROR ${error && error.message ? error.message : String(error)}`);
109
+ process.exitCode = 2;
110
+ },
111
+ );
112
+
113
+ function ensureStateIfStale() {
114
+ try {
115
+ const P = require("./lib/install-paths.cjs");
116
+ const version = JSON.parse(fs.readFileSync(path.join(repoRoot, "package.json"), "utf8")).version || null;
117
+ const record = P.readInstallRecord();
118
+ if (record && record.root === repoRoot && record.version === version) return;
119
+ const { ensureState } = require("./lib/state.cjs");
120
+ ensureState({ root: repoRoot, version });
121
+ } catch (error) {
122
+ // Không chặn lệnh người dùng đang gõ: nếu thật sự thiếu state thì lệnh đó sẽ hỏng với
123
+ // thông báo của chính nó, còn `alp doctor` mới là chỗ nói ra chuyện này cho tử tế.
124
+ console.error(`WARN không dựng được ~/.alp: ${error && error.message ? error.message : String(error)}`);
125
+ }
126
+ }
@@ -0,0 +1,4 @@
1
+ $ErrorActionPreference = "Stop"
2
+ $ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
3
+ & node (Join-Path $ScriptDir "alp.cjs") @args
4
+ exit $LASTEXITCODE
package/scripts/alp.sh ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env bash
2
+ # Wrapper giữ nguyên CLI. Logic ở alp.cjs.
3
+ exec node "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/alp.cjs" "$@"
@@ -0,0 +1,144 @@
1
+ #!/usr/bin/env node
2
+ // bootstrap.cjs — bước 2 của installer: dựng state, kiểm tra bản cài rồi đưa `alp` vào PATH.
3
+ // bootstrap.cjs --no-path tạo lệnh `alp` nhưng không sửa shell profile/User PATH
4
+ //
5
+ // VÌ SAO TÁCH KHỎI install.sh: install.sh/install.ps1 chạy khi chưa có gì trên máy nên buộc
6
+ // phải viết bằng shell. Từ lúc code đã nằm trên đĩa trở đi, ba OS dùng chung một
7
+ // implementation Node — đúng luật của repo này: .sh/.ps1 là wrapper, .cjs là bản thật duy nhất.
8
+ //
9
+ // Từ v0.9.0 chỉ dev clone mới build. Bản npm và bản tarball tới máy người dùng đã có sẵn
10
+ // `dist/`, nên ở đó bootstrap không gọi `npm ci` hay `tsc` — nó chỉ kiểm tra rằng artifact
11
+ // đầy đủ. Đó chính là điều làm việc cài nhanh và không cần toolchain.
12
+
13
+ const fs = require("fs");
14
+ const path = require("path");
15
+ const { spawnSync } = require("child_process");
16
+ const { spawnSyncCommand } = require("./lib/delegation/command-runner.cjs");
17
+ const CLI = require("./lib/cli-link.cjs");
18
+ const P = require("./lib/install-paths.cjs");
19
+ const { ensureState } = require("./lib/state.cjs");
20
+
21
+ const repoRoot = path.resolve(__dirname, "..");
22
+ if (!fs.existsSync(path.join(repoRoot, "package.json"))) die("không tìm thấy package.json của alp-code");
23
+
24
+ const args = process.argv.slice(2);
25
+ if (args.includes("-h") || args.includes("--help")) usage(0);
26
+ const skipPath = args.includes("--no-path") || process.env.ALP_NO_PATH === "1";
27
+ for (const a of args)
28
+ if (a.startsWith("-") && !["--no-path", "-h", "--help"].includes(a))
29
+ die(`tham số lạ: ${a}`);
30
+
31
+ const channel = P.detectChannel(repoRoot);
32
+
33
+ // 1. Build — chỉ ở dev clone. Bản phát hành đã compile sẵn trên máy maintainer.
34
+ console.log("---");
35
+ if (channel === "dev") {
36
+ mustNpm(["ci", "--include=dev"]);
37
+ mustNpm(["run", "build"]);
38
+ writeBuildHash();
39
+ } else {
40
+ console.log(`OK bản cài ${channel} — dùng dist/ dựng sẵn, không build trên máy này`);
41
+ }
42
+
43
+ // 2. State cục bộ ở `~/.alp`. Không bao giờ đè memory hay preferences.
44
+ console.log("---");
45
+ let state;
46
+ try { state = ensureState({ root: repoRoot }); }
47
+ catch (error) { die(error.message); }
48
+ for (const entry of state.log) console.log(`${entry.level.padEnd(8)} ${entry.text}`);
49
+
50
+ // 3. Validate the compiled registry and both runtime adapters without launching them.
51
+ validateCodeNative();
52
+
53
+ // 4. Khám. doctor exit 1 = có finding (DRIFT, TEMPLATE-LEFT…) — đó là thông tin, không
54
+ // phải lỗi cài đặt. Chỉ exit 2 mới là doctor tự gãy.
55
+ console.log("---");
56
+ const health = run("doctor.cjs", []);
57
+ if (health === 2) die("doctor.cjs gãy — bản cài có thể thiếu file");
58
+
59
+ // 5. `alp` vào PATH. Không có bước này thì mọi lệnh trong README đều phải gõ đường dẫn
60
+ // tuyệt đối tới thư mục cài — tức là vẫn đúng cái phiền mà `alp init` sinh ra để xoá bỏ.
61
+ // Trừ bản npm: ở đó `alp` đã là bin do npm tạo trong global bin dir của chính nó. Tạo
62
+ // thêm một shim thứ hai trong `~/.local/bin` chỉ dựng lên hai lệnh `alp` tranh nhau PATH,
63
+ // và cái do ta tạo sẽ trỏ vào một thư mục npm có toàn quyền xoá ở lần cài kế tiếp.
64
+ console.log("---");
65
+ if (channel === "npm") {
66
+ console.log("OK lệnh `alp` do npm cài — installer không tạo thêm shim nào");
67
+ } else {
68
+ for (const entry of CLI.installCli(repoRoot, { skipPath }))
69
+ console.log(`${entry.level.padEnd(8)} ${entry.text}`);
70
+ }
71
+
72
+ console.log("---");
73
+ console.log(`READY code-native alp-code tại ${repoRoot} (${channel})`);
74
+ console.log(` memory và state: ${state.stateHome}`);
75
+ if (health !== 0) console.log("CHECK doctor còn cảnh báo ở trên — cài đặt vẫn dùng được, xử lý sau cũng kịp");
76
+ console.log("");
77
+ console.log(" cd <project-bất-kỳ> && alp init");
78
+ console.log(" alp # launch main agent");
79
+ console.log("");
80
+ console.log("Cập nhật về sau: `alp update` — thay thư mục cài, giữ nguyên mọi thứ trong ~/.alp.");
81
+
82
+ function mustNpm(extra) {
83
+ // spawnSync("npm.cmd", ...) trực tiếp trên Windows ăn EINVAL từ bản Node vá
84
+ // CVE-2024-27980 (chặn spawn .cmd/.bat không qua shell). spawnSyncCommand đã giải
85
+ // quyết đúng việc này ở chỗ khác trong repo — dùng lại thay vì viết version yếu hơn.
86
+ const r = spawnSyncCommand("npm", extra, { stdio: "inherit", cwd: repoRoot });
87
+ if (r.error || r.status !== 0)
88
+ die(`\`npm ${extra.join(" ")}\` thất bại${r.error ? `: ${r.error.message}` : ` (exit ${r.status})`}`);
89
+ }
90
+
91
+ function validateCodeNative() {
92
+ try {
93
+ const { agentRegistry } = require(path.join(repoRoot, "dist", "src", "agents", "registry.js"));
94
+ const { ClaudeRuntimeAdapter } = require(path.join(repoRoot, "dist", "src", "runtime", "claude-adapter.js"));
95
+ const { CodexRuntimeAdapter } = require(path.join(repoRoot, "dist", "src", "runtime", "codex-adapter.js"));
96
+ const agents = agentRegistry.list();
97
+ if (!agents.length || !agentRegistry.has("main")) throw new Error("registry thiếu main agent");
98
+ if (new ClaudeRuntimeAdapter().name !== "claude" || new CodexRuntimeAdapter().name !== "codex")
99
+ throw new Error("runtime adapter name không hợp lệ");
100
+ console.log(`OK AgentRegistry ${agents.length} agents; runtime adapters claude,codex`);
101
+ } catch (error) {
102
+ die(channel === "dev"
103
+ ? `code-native validation thất bại: ${error.message}`
104
+ : `bản cài ${channel} tại ${repoRoot} không dùng được: ${error.message}\n Artifact hỏng hoặc thiếu dependency — cài lại thay vì build tại chỗ.`);
105
+ }
106
+ }
107
+
108
+ function writeBuildHash() {
109
+ const crypto = require("crypto");
110
+ const files = [];
111
+ collectTypeScript(path.join(repoRoot, "src"), files);
112
+ const hash = crypto.createHash("sha256");
113
+ for (const file of files.sort()) hash.update(path.relative(repoRoot, file)).update("\0").update(fs.readFileSync(file));
114
+ fs.mkdirSync(path.join(repoRoot, "dist"), { recursive: true });
115
+ fs.writeFileSync(path.join(repoRoot, "dist", ".alp-source-hash"), `${hash.digest("hex")}\n`);
116
+ }
117
+
118
+ function collectTypeScript(directory, files) {
119
+ for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
120
+ const file = path.join(directory, entry.name);
121
+ if (entry.isDirectory()) collectTypeScript(file, files);
122
+ else if (entry.isFile() && entry.name.endsWith(".ts")) files.push(file);
123
+ }
124
+ }
125
+
126
+ // ---------------------------------------------------------------- tiện ích
127
+
128
+ function run(script, extra) {
129
+ const file = path.join(repoRoot, "scripts", script);
130
+ const r = spawnSync(process.execPath, [file, ...extra], { stdio: "inherit", cwd: repoRoot });
131
+ if (r.error) die(`không chạy được ${script}: ${r.error.message}`);
132
+ return r.status ?? 1;
133
+ }
134
+
135
+ function usage(code) {
136
+ console.log("bootstrap.cjs [--no-path] — dựng ~/.alp, validate bản cài, doctor, cài lệnh `alp`");
137
+ console.log(" (dev clone: npm ci + build trước)");
138
+ process.exit(code);
139
+ }
140
+
141
+ function die(m) {
142
+ console.error(`ERROR ${m}`);
143
+ process.exit(1);
144
+ }
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env node
2
+ // checkout-release.cjs — installers call this right after cloning (and `alp update` runs the
3
+ // same logic through scripts/lib/update.cjs) to land the working tree on the latest GitHub
4
+ // Release tag, or on --version <tag> when pinned.
5
+
6
+ const path = require("path");
7
+ const { checkoutLatestRelease } = require("./lib/update.cjs");
8
+
9
+ const repoRoot = path.resolve(__dirname, "..");
10
+ const args = process.argv.slice(2);
11
+ let pinTag;
12
+ for (let index = 0; index < args.length; index += 1) {
13
+ if (args[index] === "--version") {
14
+ pinTag = args[++index];
15
+ if (!pinTag) { console.error("ERROR --version requires a tag"); process.exit(1); }
16
+ }
17
+ }
18
+
19
+ checkoutLatestRelease(repoRoot, {
20
+ env: process.env,
21
+ stdio: "inherit",
22
+ pinTag,
23
+ log(level, message) { console.log(`${level.padEnd(9)}${message}`); },
24
+ }).then(
25
+ (result) => {
26
+ if (!result.ok) { console.error(`ERROR ${result.message}`); process.exit(1); }
27
+ console.log(`READY checked out ${result.tag}`);
28
+ },
29
+ (error) => { console.error(`ERROR ${error.message}`); process.exit(1); },
30
+ );
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+ // Compatibility wrapper for the code-native TypeScript CLI.
3
+
4
+ const fs = require("fs");
5
+ const path = require("path");
6
+ const { spawnSyncCommand } = require("./lib/delegation/command-runner.cjs");
7
+ const repoRoot = path.resolve(__dirname, "..");
8
+ const entry = path.join(repoRoot, "dist", "src", "cli", "alp.js");
9
+ if (!fs.existsSync(entry)) {
10
+ const built = spawnSyncCommand("npm", ["run", "build"], { cwd: repoRoot, stdio: "inherit" });
11
+ if (built.error || built.status !== 0) process.exit(built.status || 2);
12
+ }
13
+ process.env.ALP_REPO_ROOT = repoRoot;
14
+ const argv = process.argv.slice(2);
15
+ const command = argv[0] === "delegate" ? argv : ["delegation", ...argv];
16
+ require(entry).main(command).then(
17
+ (code) => { process.exitCode = code; },
18
+ (error) => { console.error(`ERROR ${error && error.message ? error.message : String(error)}`); process.exitCode = 2; },
19
+ );
@@ -0,0 +1,158 @@
1
+ #!/usr/bin/env node
2
+ // Code-native ALP health checks. Exit 0 healthy, 1 actionable findings, 2 doctor failure.
3
+ const crypto = require("crypto");
4
+ const fs = require("fs");
5
+ const path = require("path");
6
+ const D = require("./lib/delegation/config.cjs");
7
+ const P = require("./lib/install-paths.cjs");
8
+
9
+ const quiet = process.argv.includes("--quiet");
10
+ const repoRoot = path.resolve(__dirname, "..");
11
+ const channel = P.detectChannel(repoRoot);
12
+ // Cách sửa một bản cài khác hẳn cách sửa một dev clone: bản phát hành không có `src/` lẫn
13
+ // devDependencies, nên bảo người dùng chạy `npm run build` ở đó là chỉ đường vào ngõ cụt.
14
+ const REBUILD = channel === "dev" ? "node scripts/bootstrap.cjs --no-path" : "alp update";
15
+ const signals = [];
16
+ const observations = [];
17
+ const signal = (tag, msg, fix) => signals.push({ tag, msg, fix });
18
+ const observe = (tag, msg) => observations.push({ tag, msg });
19
+ const compiled = (file) => path.join(repoRoot, "dist", "src", ...file.split("/")) + ".js";
20
+
21
+ function checkAgentRegistry() {
22
+ try {
23
+ const { agentRegistry } = require(compiled("agents/registry"));
24
+ const agents = agentRegistry.list();
25
+ if (!agents.length || !agentRegistry.has("main")) throw new Error("missing main agent");
26
+ observe("AGENT-REGISTRY", `${agents.length} agents valid`);
27
+ } catch (error) { signal("AGENT-REGISTRY", error.message, REBUILD); }
28
+ }
29
+
30
+ async function checkRuntimes() {
31
+ const runtimes = [
32
+ ["RUNTIME-CLAUDE", "ClaudeRuntimeAdapter", "runtime/claude-adapter", "Install Claude Code and ensure claude is on PATH."],
33
+ ["RUNTIME-CODEX", "CodexRuntimeAdapter", "runtime/codex-adapter", "Install Codex CLI and ensure codex is on PATH."],
34
+ ];
35
+ for (const [tag, exportName, moduleName, fallback] of runtimes) {
36
+ try {
37
+ const Adapter = require(compiled(moduleName))[exportName];
38
+ const health = await new Adapter().probe();
39
+ if (health.ok) observe(tag, health.message);
40
+ else signal(tag, health.message, health.remediation || fallback);
41
+ } catch (error) { signal(tag, error.message, REBUILD); }
42
+ }
43
+ }
44
+
45
+ async function checkMemory() {
46
+ const root = P.memoryRoot();
47
+ try {
48
+ const { MarkdownFileStore } = require(compiled("memory/adapters/markdown-file-store"));
49
+ fs.mkdirSync(root, { recursive: true, mode: 0o700 });
50
+ fs.accessSync(root, fs.constants.R_OK | fs.constants.W_OK);
51
+ const store = new MarkdownFileStore({ root });
52
+ await store.search({ scope: "shared", text: "", limit: 1 });
53
+ observe("MEMORY-ADAPTER", `${root} readable and writable`);
54
+ } catch (error) { signal("MEMORY-ADAPTER", error.message, REBUILD); }
55
+ }
56
+
57
+ function checkExecutionState() {
58
+ let roots;
59
+ try { roots = [P.executionsDir()]; }
60
+ catch (error) { return signal("EXECUTION-STATE", error.message, "set HOME rồi chạy lại alp doctor"); }
61
+ try { roots.push(D.loadDelegationConfig(repoRoot).stateDir); }
62
+ catch (error) { signal("EXECUTION-STATE", `delegation config invalid: ${error.message}`, "sửa alp.config.yaml"); }
63
+ for (const root of new Set(roots)) {
64
+ try {
65
+ fs.mkdirSync(root, { recursive: true, mode: 0o700 });
66
+ const mode = fs.statSync(root).mode & 0o777;
67
+ if (process.platform !== "win32" && (mode & 0o077))
68
+ signal("EXECUTION-STATE", `${root} permissions are ${mode.toString(8)}`, `chmod 700 ${JSON.stringify(root)}`);
69
+ for (const entry of fs.readdirSync(root)) if (/^\..+\.tmp$/.test(entry))
70
+ signal("ORPHAN-EXECUTION", path.join(root, entry), `rm -rf ${JSON.stringify(path.join(root, entry))}`);
71
+ observe("EXECUTION-STATE", `${root} accessible`);
72
+ } catch (error) { signal("EXECUTION-STATE", `${root}: ${error.message}`, REBUILD); }
73
+ }
74
+ }
75
+
76
+ function collect(directory, files) {
77
+ for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
78
+ const file = path.join(directory, entry.name);
79
+ if (entry.isDirectory()) collect(file, files);
80
+ else if (entry.isFile() && entry.name.endsWith(".ts")) files.push(file);
81
+ }
82
+ }
83
+
84
+ function sourceHash() {
85
+ const files = [];
86
+ collect(path.join(repoRoot, "src"), files);
87
+ const hash = crypto.createHash("sha256");
88
+ for (const file of files.sort()) hash.update(path.relative(repoRoot, file)).update("\0").update(fs.readFileSync(file));
89
+ return hash.digest("hex");
90
+ }
91
+
92
+ /**
93
+ * Dev clone: `dist/` có khớp `src/` không.
94
+ *
95
+ * Chỉ có ý nghĩa ở dev clone. Bản phát hành không mang theo `src/` — nó được compile trên máy
96
+ * maintainer — nên ở đó câu hỏi đúng không phải "build có cũ không" mà là "artifact có đủ
97
+ * file không", và đó là việc của checkArtifact().
98
+ */
99
+ function checkBuildDrift() {
100
+ if (channel !== "dev") return;
101
+ const entry = compiled("cli/alp");
102
+ const stamp = path.join(repoRoot, "dist", ".alp-source-hash");
103
+ try {
104
+ if (!fs.existsSync(entry) || !fs.existsSync(stamp)) throw new Error("compiled CLI or source hash missing");
105
+ if (fs.readFileSync(stamp, "utf8").trim() !== sourceHash()) throw new Error("TypeScript source differs from compiled build stamp");
106
+ observe("BUILD-DRIFT", "compiled artifacts match source hash");
107
+ } catch (error) { signal("BUILD-DRIFT", error.message, REBUILD); }
108
+ }
109
+
110
+ /** Bản phát hành: những file mà thiếu là hỏng câm, không phải hỏng ồn. */
111
+ function checkArtifact() {
112
+ if (channel === "dev") return;
113
+ const required = ["dist/src/cli/alp.js", "hooks/session-boot.cjs", "scaffold/memory/INDEX.md", "alp.config.yaml"];
114
+ const missing = required.filter((file) => !fs.existsSync(path.join(repoRoot, ...file.split("/"))));
115
+ if (missing.length) signal("ARTIFACT", `bản cài ${channel} thiếu ${missing.join(", ")}`, REBUILD);
116
+ else observe("ARTIFACT", `bản cài ${channel} tại ${repoRoot} đầy đủ`);
117
+ }
118
+
119
+ /**
120
+ * `~/.alp/hooks/*.cjs` và `~/.alp/install.json` có trỏ đúng bản cài đang chạy không.
121
+ *
122
+ * Đây là chỗ hỏng câm nguy hiểm nhất của mô hình mới: `<project>/.claude/settings.local.json`
123
+ * do `alp init` ghi trỏ vào forwarder, forwarder đọc install.json để tìm thư mục cài. Lệch
124
+ * một mắt xích thì phiên `claude` mở tay vẫn chạy — chỉ là không còn identity nào cả, và
125
+ * không có thông báo lỗi nào xuất hiện.
126
+ */
127
+ function checkInstallRecord() {
128
+ const record = P.readInstallRecord();
129
+ if (!record) return signal("INSTALL-RECORD", `thiếu ${P.installRecordPath()}`, "node scripts/ensure-state.cjs");
130
+ if (path.resolve(record.root) !== repoRoot)
131
+ signal("INSTALL-RECORD", `install.json trỏ tới ${record.root}, không phải ${repoRoot}`, "node scripts/ensure-state.cjs");
132
+
133
+ const forwarder = P.hookForwarderPath("session-boot");
134
+ if (!fs.existsSync(forwarder))
135
+ return signal("HOOK-FORWARDER", `thiếu ${forwarder} — hook SessionStart trong project sẽ im lặng không chạy`, "node scripts/ensure-state.cjs");
136
+ observe("HOOK-FORWARDER", `${forwarder} → ${record.root}`);
137
+ }
138
+
139
+ const render = ({ tag, msg, fix }) => `${tag.padEnd(20)} ${msg}\n${" ".repeat(20)} → fix: ${fix}`;
140
+
141
+ async function main() {
142
+ checkAgentRegistry();
143
+ await checkRuntimes();
144
+ await checkMemory();
145
+ checkExecutionState();
146
+ checkBuildDrift();
147
+ checkArtifact();
148
+ checkInstallRecord();
149
+ if (!quiet) for (const item of observations) console.log(`${item.tag.padEnd(20)} ${item.msg}`);
150
+ for (const item of signals) console.log(render(item));
151
+ if (!signals.length && !quiet) console.log("OK code-native alp-code healthy");
152
+ process.exitCode = signals.length ? 1 : 0;
153
+ }
154
+
155
+ main().catch((error) => {
156
+ console.error(`ERROR doctor failed: ${error.message}`);
157
+ process.exitCode = 2;
158
+ });
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env bash
2
+ # Stable shell wrapper; health-check logic lives in doctor.cjs.
3
+ exec node "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/doctor.cjs" "$@"
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env node
2
+ // ensure-state.cjs — dựng `~/.alp` cho thư mục cài đang chứa file này.
3
+ //
4
+ // Installer gọi sau khi giải nén, và `alp update` gọi lại bằng CODE MỚI sau khi thay xong thư
5
+ // mục cài: tiến trình `alp` đang chạy đã nạp bản cũ vào bộ nhớ và thư mục dưới chân nó vừa bị
6
+ // thay, nên mọi thứ cần chạy sau đó phải là một tiến trình mới đọc file mới.
7
+
8
+ "use strict";
9
+
10
+ const path = require("path");
11
+ const { ensureState } = require("./lib/state.cjs");
12
+
13
+ const root = path.resolve(__dirname, "..");
14
+ const quiet = process.argv.includes("--quiet");
15
+
16
+ try {
17
+ const result = ensureState({ root });
18
+ if (!quiet) for (const entry of result.log) console.log(`${entry.level.padEnd(9)}${entry.text}`);
19
+ } catch (error) {
20
+ console.error(`ERROR không dựng được state tại ~/.alp: ${error.message}`);
21
+ process.exit(1);
22
+ }