sanook-cli 0.4.0 → 0.5.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/.env.example +19 -0
- package/CHANGELOG.md +144 -0
- package/README.md +153 -20
- package/README.th.md +136 -0
- package/dist/agentContext.js +4 -0
- package/dist/approval.js +6 -0
- package/dist/bin.js +394 -51
- package/dist/brain.js +92 -59
- package/dist/brand.js +47 -0
- package/dist/checkpoint.js +37 -0
- package/dist/commands.js +86 -6
- package/dist/compaction.js +76 -5
- package/dist/config.js +100 -12
- package/dist/cost.js +60 -3
- package/dist/doctor.js +92 -0
- package/dist/gateway/auth.js +2 -2
- package/dist/gateway/ledger.js +2 -2
- package/dist/gateway/scheduler.js +1 -0
- package/dist/gateway/serve.js +6 -4
- package/dist/gateway/server.js +10 -2
- package/dist/git.js +11 -2
- package/dist/hooks.js +43 -17
- package/dist/knowledge.js +48 -49
- package/dist/loop.js +182 -66
- package/dist/lsp/client.js +173 -0
- package/dist/lsp/framing.js +56 -0
- package/dist/lsp/index.js +138 -0
- package/dist/lsp/servers.js +82 -0
- package/dist/mcp-server.js +244 -0
- package/dist/mcp.js +184 -29
- package/dist/memory-store.js +559 -0
- package/dist/memory.js +143 -29
- package/dist/orchestrate.js +150 -0
- package/dist/providers/codex.js +2 -2
- package/dist/providers/keys.js +3 -2
- package/dist/providers/registry.js +133 -1
- package/dist/repomap.js +93 -0
- package/dist/search/chunk.js +158 -0
- package/dist/search/embed-store.js +187 -0
- package/dist/search/engine.js +203 -0
- package/dist/search/fuse.js +35 -0
- package/dist/search/index-core.js +187 -0
- package/dist/search/indexer.js +241 -0
- package/dist/search/store.js +77 -0
- package/dist/session.js +42 -8
- package/dist/skill-install.js +10 -10
- package/dist/skills.js +12 -9
- package/dist/summarize.js +31 -0
- package/dist/tools/bash.js +21 -2
- package/dist/tools/diagnostics.js +41 -0
- package/dist/tools/edit.js +29 -7
- package/dist/tools/index.js +8 -1
- package/dist/tools/list.js +7 -2
- package/dist/tools/permission.js +90 -9
- package/dist/tools/read.js +23 -4
- package/dist/tools/remember.js +1 -1
- package/dist/tools/sandbox.js +61 -0
- package/dist/tools/search.js +105 -4
- package/dist/tools/task.js +195 -29
- package/dist/tools/timeout.js +35 -0
- package/dist/tools/util.js +10 -0
- package/dist/tools/write.js +6 -4
- package/dist/trust.js +89 -0
- package/dist/ui/app.js +218 -27
- package/dist/ui/banner.js +4 -9
- package/dist/ui/history.js +30 -0
- package/dist/ui/mentions.js +44 -0
- package/dist/ui/setup.js +6 -5
- package/dist/ui/useEditor.js +83 -0
- package/dist/update.js +114 -0
- package/dist/worktree.js +173 -0
- package/package.json +11 -5
- package/scripts/postinstall.mjs +33 -0
- package/second-brain/.agents/_Index.md +30 -0
- package/second-brain/.agents/skills/_Index.md +30 -0
- package/second-brain/.agents/workflows/_Index.md +30 -0
- package/second-brain/AGENTS.md +4 -4
- package/second-brain/Acceptance/_Index.md +30 -0
- package/second-brain/Acceptance/golden-case-template.md +39 -0
- package/second-brain/Areas/_Index.md +30 -0
- package/second-brain/Bugs/System-OS/_Index.md +30 -0
- package/second-brain/Bugs/_Index.md +30 -0
- package/second-brain/CLAUDE.md +4 -1
- package/second-brain/Checklists/_Index.md +30 -0
- package/second-brain/Checklists/preflight-postflight-template.md +29 -0
- package/second-brain/Distillations/_Index.md +30 -0
- package/second-brain/Entities/_Index.md +30 -0
- package/second-brain/Entities/entity-template.md +33 -0
- package/second-brain/Evals/_Index.md +30 -0
- package/second-brain/Evals/correction-pairs.md +24 -0
- package/second-brain/Evals/failure-taxonomy.md +24 -0
- package/second-brain/Evals/golden-set.md +25 -0
- package/second-brain/Evals/quality-ledger.md +23 -0
- package/second-brain/Evals/self-eval-rubric.md +23 -0
- package/second-brain/GEMINI.md +4 -4
- package/second-brain/Goals/_Index.md +30 -0
- package/second-brain/Handoffs/_Index.md +30 -0
- package/second-brain/Home.md +7 -0
- package/second-brain/Intake/Raw Sources/_Index.md +30 -0
- package/second-brain/Intake/_Index.md +30 -0
- package/second-brain/Intake/_Quarantine/_Index.md +30 -0
- package/second-brain/Learning/_Index.md +30 -0
- package/second-brain/Playbooks/_Index.md +30 -0
- package/second-brain/Playbooks/playbook-template.md +23 -0
- package/second-brain/Projects/_Index.md +30 -0
- package/second-brain/Prompts/_Index.md +30 -0
- package/second-brain/README.md +2 -1
- package/second-brain/Research/_Index.md +30 -0
- package/second-brain/Retrospectives/_Index.md +30 -0
- package/second-brain/Reviews/_Index.md +30 -0
- package/second-brain/Runbooks/_Index.md +30 -0
- package/second-brain/Runbooks/eval-loop.md +24 -0
- package/second-brain/Sessions/_Index.md +30 -0
- package/second-brain/Shared/AI-Context-Index.md +20 -0
- package/second-brain/Shared/AI-Threads/_Index.md +30 -0
- package/second-brain/Shared/Archive/_Index.md +30 -0
- package/second-brain/Shared/Assets/_Index.md +30 -0
- package/second-brain/Shared/Context-Packs/_Index.md +30 -0
- package/second-brain/Shared/Context7-Docs/_Index.md +30 -0
- package/second-brain/Shared/Coordination/NOW.md +28 -0
- package/second-brain/Shared/Coordination/_Index.md +30 -0
- package/second-brain/Shared/Coordination/agent-registry.md +24 -0
- package/second-brain/Shared/Coordination/task-board/_Index.md +30 -0
- package/second-brain/Shared/Coordination/task-board/task-template.md +43 -0
- package/second-brain/Shared/Coordination/task-board.md +32 -0
- package/second-brain/Shared/Core-Facts/_Index.md +30 -0
- package/second-brain/Shared/Decision-Memory/_Index.md +30 -0
- package/second-brain/Shared/Glossary/_Index.md +30 -0
- package/second-brain/Shared/Memory-Inbox/_Index.md +30 -0
- package/second-brain/Shared/Operating-State/_Index.md +30 -0
- package/second-brain/Shared/Prompting/_Index.md +30 -0
- package/second-brain/Shared/Provenance/_Index.md +30 -0
- package/second-brain/Shared/Rules/_Index.md +30 -0
- package/second-brain/Shared/Rules/contextual-note-rule.md +30 -0
- package/second-brain/Shared/Rules/frontmatter-standard.md +10 -0
- package/second-brain/Shared/Rules/memory-write-protocol.md +28 -0
- package/second-brain/Shared/Rules/procedural-runbook-header.md +40 -0
- package/second-brain/Shared/Rules/review-and-staleness-policy.md +22 -0
- package/second-brain/Shared/Rules/rules-formatting.md +34 -0
- package/second-brain/Shared/Scripts/_Index.md +30 -0
- package/second-brain/Shared/Scripts-Archive/_Index.md +30 -0
- package/second-brain/Shared/Tech-Standards/_Index.md +30 -0
- package/second-brain/Shared/Tech-Standards/verification-standard.md +40 -0
- package/second-brain/Shared/User-Memory/_Index.md +30 -0
- package/second-brain/Shared/User-Persona/_Index.md +30 -0
- package/second-brain/Shared/User-Persona/owner-profile.md +25 -0
- package/second-brain/Shared/Working-Memory/_Index.md +30 -0
- package/second-brain/Shared/_Index.md +30 -0
- package/second-brain/Shared/mcp-servers/_Index.md +30 -0
- package/second-brain/Skills/_Index.md +30 -0
- package/second-brain/Templates/_Index.md +30 -0
- package/second-brain/Templates/bug.md +2 -0
- package/second-brain/Templates/handoff.md +2 -0
- package/second-brain/Templates/session.md +2 -0
- package/second-brain/Tools/_Index.md +30 -0
- package/second-brain/Traces/_Index.md +30 -0
- package/second-brain/Vault Structure Map.md +33 -1
- package/second-brain/copilot/_Index.md +30 -0
- package/skills/audit-license-compliance/SKILL.md +117 -0
- package/skills/author-codemod/SKILL.md +110 -0
- package/skills/build-audit-logging/SKILL.md +112 -0
- package/skills/build-cdc-streaming-pipeline/SKILL.md +123 -0
- package/skills/build-cli-tool/SKILL.md +108 -0
- package/skills/build-data-table/SKILL.md +141 -0
- package/skills/build-native-mobile-ui/SKILL.md +154 -0
- package/skills/build-offline-first-sync/SKILL.md +118 -0
- package/skills/build-realtime-channel/SKILL.md +122 -0
- package/skills/build-vector-search/SKILL.md +131 -0
- package/skills/compose-local-dev-stack/SKILL.md +149 -0
- package/skills/configure-bundler-build/SKILL.md +166 -0
- package/skills/configure-dns-tls/SKILL.md +142 -0
- package/skills/configure-reverse-proxy-lb/SKILL.md +129 -0
- package/skills/configure-security-headers-csp/SKILL.md +122 -0
- package/skills/contract-testing/SKILL.md +140 -0
- package/skills/datetime-timezone-correctness/SKILL.md +125 -0
- package/skills/debug-ci-pipeline-failure/SKILL.md +134 -0
- package/skills/debug-flaky-tests/SKILL.md +128 -0
- package/skills/defend-llm-prompt-injection/SKILL.md +110 -0
- package/skills/deliver-webhooks/SKILL.md +116 -0
- package/skills/design-api-pagination/SKILL.md +144 -0
- package/skills/design-authorization-model/SKILL.md +119 -0
- package/skills/design-backup-dr-recovery/SKILL.md +113 -0
- package/skills/design-event-sourcing-cqrs/SKILL.md +143 -0
- package/skills/design-multi-tenancy/SKILL.md +100 -0
- package/skills/design-protobuf-grpc-service/SKILL.md +146 -0
- package/skills/design-relational-schema/SKILL.md +129 -0
- package/skills/design-search-index-infra/SKILL.md +151 -0
- package/skills/design-state-machine/SKILL.md +108 -0
- package/skills/design-token-system/SKILL.md +109 -0
- package/skills/distributed-locks-leases/SKILL.md +120 -0
- package/skills/encrypt-sensitive-data/SKILL.md +148 -0
- package/skills/feature-flags-rollout/SKILL.md +130 -0
- package/skills/file-upload-object-storage/SKILL.md +107 -0
- package/skills/fuzz-dynamic-security-test/SKILL.md +111 -0
- package/skills/harden-llm-app-reliability/SKILL.md +126 -0
- package/skills/i18n-localization-setup/SKILL.md +113 -0
- package/skills/idempotency-keys/SKILL.md +107 -0
- package/skills/implement-push-notifications/SKILL.md +142 -0
- package/skills/ingest-webhook-secure/SKILL.md +120 -0
- package/skills/integrate-oauth-oidc/SKILL.md +126 -0
- package/skills/load-stress-test/SKILL.md +129 -0
- package/skills/map-privacy-data-gdpr/SKILL.md +146 -0
- package/skills/model-nosql-data/SKILL.md +118 -0
- package/skills/money-decimal-arithmetic/SKILL.md +123 -0
- package/skills/monitor-ml-drift/SKILL.md +109 -0
- package/skills/numeric-precision-units/SKILL.md +144 -0
- package/skills/optimize-llm-cost-latency/SKILL.md +103 -0
- package/skills/optimize-react-rerenders/SKILL.md +124 -0
- package/skills/orchestrate-agent-workflow/SKILL.md +100 -0
- package/skills/payments-billing-integration/SKILL.md +114 -0
- package/skills/pin-toolchain-versions/SKILL.md +116 -0
- package/skills/plan-strangler-migration/SKILL.md +95 -0
- package/skills/property-based-testing/SKILL.md +108 -0
- package/skills/publish-package-registry/SKILL.md +130 -0
- package/skills/recover-git-state/SKILL.md +119 -0
- package/skills/remediate-web-vulnerabilities/SKILL.md +125 -0
- package/skills/resilience-timeouts-retries/SKILL.md +104 -0
- package/skills/resolve-merge-rebase-conflict/SKILL.md +97 -0
- package/skills/rewrite-git-history/SKILL.md +109 -0
- package/skills/scaffold-cross-platform-app/SKILL.md +137 -0
- package/skills/schema-evolution-compatibility/SKILL.md +121 -0
- package/skills/send-transactional-email/SKILL.md +126 -0
- package/skills/serve-deploy-ml-model/SKILL.md +107 -0
- package/skills/setup-cdn-edge-waf/SKILL.md +107 -0
- package/skills/setup-devcontainer-env/SKILL.md +131 -0
- package/skills/setup-lint-format-precommit/SKILL.md +140 -0
- package/skills/setup-monorepo-tooling/SKILL.md +125 -0
- package/skills/ship-mobile-app-store-release/SKILL.md +137 -0
- package/skills/structured-output-llm/SKILL.md +86 -0
- package/skills/supply-chain-sbom-provenance/SKILL.md +120 -0
- package/skills/test-data-factories/SKILL.md +158 -0
- package/skills/threat-model-stride/SKILL.md +123 -0
- package/skills/train-evaluate-ml-model/SKILL.md +109 -0
- package/skills/unicode-text-correctness/SKILL.md +109 -0
- package/skills/visual-regression-testing/SKILL.md +120 -0
package/dist/update.js
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
const DEFAULT_REGISTRY = 'https://registry.npmjs.org';
|
|
3
|
+
export const UPDATE_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
|
4
|
+
function packageUrl(registry, packageName) {
|
|
5
|
+
const base = registry.replace(/\/+$/, '') || DEFAULT_REGISTRY;
|
|
6
|
+
const encoded = encodeURIComponent(packageName).replace(/^%40/, '@');
|
|
7
|
+
return `${base}/${encoded}`;
|
|
8
|
+
}
|
|
9
|
+
function splitVersion(version) {
|
|
10
|
+
const [withoutBuild] = version.trim().replace(/^v/, '').split('+');
|
|
11
|
+
const [corePart, prereleasePart = ''] = withoutBuild.split('-', 2);
|
|
12
|
+
return {
|
|
13
|
+
core: corePart.split('.').map((part) => Number.parseInt(part, 10)).map((n) => (Number.isFinite(n) ? n : 0)),
|
|
14
|
+
prerelease: prereleasePart ? prereleasePart.split('.') : [],
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function comparePrerelease(a, b) {
|
|
18
|
+
if (!a.length && !b.length)
|
|
19
|
+
return 0;
|
|
20
|
+
if (!a.length)
|
|
21
|
+
return 1;
|
|
22
|
+
if (!b.length)
|
|
23
|
+
return -1;
|
|
24
|
+
const len = Math.max(a.length, b.length);
|
|
25
|
+
for (let i = 0; i < len; i++) {
|
|
26
|
+
const pa = a[i];
|
|
27
|
+
const pb = b[i];
|
|
28
|
+
if (pa === undefined)
|
|
29
|
+
return -1;
|
|
30
|
+
if (pb === undefined)
|
|
31
|
+
return 1;
|
|
32
|
+
const na = /^\d+$/.test(pa) ? Number(pa) : Number.NaN;
|
|
33
|
+
const nb = /^\d+$/.test(pb) ? Number(pb) : Number.NaN;
|
|
34
|
+
if (Number.isFinite(na) && Number.isFinite(nb) && na !== nb)
|
|
35
|
+
return na > nb ? 1 : -1;
|
|
36
|
+
if (Number.isFinite(na) !== Number.isFinite(nb))
|
|
37
|
+
return Number.isFinite(na) ? -1 : 1;
|
|
38
|
+
if (pa !== pb)
|
|
39
|
+
return pa > pb ? 1 : -1;
|
|
40
|
+
}
|
|
41
|
+
return 0;
|
|
42
|
+
}
|
|
43
|
+
export function compareVersions(a, b) {
|
|
44
|
+
const va = splitVersion(a);
|
|
45
|
+
const vb = splitVersion(b);
|
|
46
|
+
const len = Math.max(va.core.length, vb.core.length, 3);
|
|
47
|
+
for (let i = 0; i < len; i++) {
|
|
48
|
+
const na = va.core[i] ?? 0;
|
|
49
|
+
const nb = vb.core[i] ?? 0;
|
|
50
|
+
if (na !== nb)
|
|
51
|
+
return na > nb ? 1 : -1;
|
|
52
|
+
}
|
|
53
|
+
return comparePrerelease(va.prerelease, vb.prerelease);
|
|
54
|
+
}
|
|
55
|
+
export function isNewerVersion(latest, current) {
|
|
56
|
+
return compareVersions(latest, current) > 0;
|
|
57
|
+
}
|
|
58
|
+
export function installCommand(packageName) {
|
|
59
|
+
return `npm install -g ${packageName}@latest`;
|
|
60
|
+
}
|
|
61
|
+
export function shouldCheckForUpdate(cache, nowMs = Date.now(), intervalMs = UPDATE_CHECK_INTERVAL_MS) {
|
|
62
|
+
if (!cache?.checkedAt)
|
|
63
|
+
return true;
|
|
64
|
+
const checkedAt = Date.parse(cache.checkedAt);
|
|
65
|
+
if (!Number.isFinite(checkedAt))
|
|
66
|
+
return true;
|
|
67
|
+
if (checkedAt > nowMs)
|
|
68
|
+
return true;
|
|
69
|
+
return nowMs - checkedAt >= intervalMs;
|
|
70
|
+
}
|
|
71
|
+
export async function fetchLatestVersion(meta, opts = {}) {
|
|
72
|
+
const fetchImpl = opts.fetchImpl ?? fetch;
|
|
73
|
+
const ctrl = new AbortController();
|
|
74
|
+
const timer = setTimeout(() => ctrl.abort(), opts.timeoutMs ?? 8000);
|
|
75
|
+
try {
|
|
76
|
+
const res = await fetchImpl(packageUrl(opts.registry ?? process.env.npm_config_registry ?? DEFAULT_REGISTRY, meta.name), {
|
|
77
|
+
headers: { accept: 'application/vnd.npm.install-v1+json' },
|
|
78
|
+
signal: ctrl.signal,
|
|
79
|
+
});
|
|
80
|
+
if (!res.ok) {
|
|
81
|
+
throw new Error(`npm registry ตอบ ${res.status}${res.statusText ? ` ${res.statusText}` : ''}`);
|
|
82
|
+
}
|
|
83
|
+
const body = (await res.json());
|
|
84
|
+
const latest = body['dist-tags']?.latest;
|
|
85
|
+
if (!latest)
|
|
86
|
+
throw new Error('npm registry ไม่มี dist-tag "latest"');
|
|
87
|
+
return latest;
|
|
88
|
+
}
|
|
89
|
+
finally {
|
|
90
|
+
clearTimeout(timer);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
export async function checkForUpdate(meta, opts = {}) {
|
|
94
|
+
const latestVersion = await fetchLatestVersion(meta, opts);
|
|
95
|
+
return {
|
|
96
|
+
packageName: meta.name,
|
|
97
|
+
currentVersion: meta.version,
|
|
98
|
+
latestVersion,
|
|
99
|
+
isOutdated: isNewerVersion(latestVersion, meta.version),
|
|
100
|
+
installCommand: installCommand(meta.name),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
export function installLatest(meta, opts = {}) {
|
|
104
|
+
const spawnImpl = opts.spawnImpl ?? spawn;
|
|
105
|
+
return new Promise((resolve, reject) => {
|
|
106
|
+
const child = spawnImpl('npm', ['install', '-g', `${meta.name}@latest`], {
|
|
107
|
+
stdio: 'inherit',
|
|
108
|
+
env: process.env,
|
|
109
|
+
shell: process.platform === 'win32', // Windows: npm = npm.cmd → spawn ตรงๆ ENOENT
|
|
110
|
+
});
|
|
111
|
+
child.once('error', reject);
|
|
112
|
+
child.once('close', (code) => resolve(code ?? 1));
|
|
113
|
+
});
|
|
114
|
+
}
|
package/dist/worktree.js
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// src/worktree.ts — throwaway git worktrees for ISOLATED parallel write agents.
|
|
3
|
+
//
|
|
4
|
+
// When several sub-agents edit files at once, they would clobber each other in
|
|
5
|
+
// one working tree. This gives each one its own `git worktree` (detached at the
|
|
6
|
+
// current HEAD), so their writes are physically isolated; afterwards each
|
|
7
|
+
// worktree's diff is captured and applied back to the main tree sequentially.
|
|
8
|
+
//
|
|
9
|
+
// Reuses runGit()/isGitRepo() from src/git.ts (execFile, no shell). Everything is
|
|
10
|
+
// best-effort + defensive: not a git repo → returns null (caller falls back to a
|
|
11
|
+
// shared tree); a failed apply is reported, never thrown past the orchestrator.
|
|
12
|
+
// ============================================================================
|
|
13
|
+
import { mkdtemp, rm, writeFile, readFile, realpath } from 'node:fs/promises';
|
|
14
|
+
import { tmpdir } from 'node:os';
|
|
15
|
+
import { join } from 'node:path';
|
|
16
|
+
import { randomUUID } from 'node:crypto';
|
|
17
|
+
import { runGit, isGitRepo } from './git.js';
|
|
18
|
+
/** repo root of `cwd` (the top-level working dir), or null if not a git repo. */
|
|
19
|
+
export async function getRepoRoot(cwd = process.cwd()) {
|
|
20
|
+
if (!(await isGitRepo(cwd)))
|
|
21
|
+
return null;
|
|
22
|
+
try {
|
|
23
|
+
return (await runGit(['rev-parse', '--show-toplevel'], cwd)).trim();
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Create a detached worktree at the current HEAD of the repo containing `cwd`.
|
|
31
|
+
* Returns null if `cwd` is not in a git repo (caller should then run un-isolated).
|
|
32
|
+
*/
|
|
33
|
+
export async function createWorktree(cwd = process.cwd()) {
|
|
34
|
+
const repoRoot = await getRepoRoot(cwd);
|
|
35
|
+
if (!repoRoot)
|
|
36
|
+
return null;
|
|
37
|
+
try {
|
|
38
|
+
const baseRef = (await runGit(['rev-parse', 'HEAD'], repoRoot)).trim();
|
|
39
|
+
const tmpParent = await mkdtemp(join(tmpdir(), 'sanook-wt-'));
|
|
40
|
+
const path = join(tmpParent, `t-${randomUUID().slice(0, 8)}`); // must not pre-exist; git creates it
|
|
41
|
+
await runGit(['worktree', 'add', '--detach', path, baseRef], repoRoot);
|
|
42
|
+
const real = await realpath(path).catch(() => path);
|
|
43
|
+
return { path: real, baseRef, repoRoot, tmpParent };
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Capture everything the sub-agent changed in its worktree as a unified diff
|
|
51
|
+
* (vs the base HEAD), including new/untracked files. Empty string = no changes.
|
|
52
|
+
*/
|
|
53
|
+
export async function captureDiff(wt) {
|
|
54
|
+
try {
|
|
55
|
+
await runGit(['add', '-A'], wt.path); // stage incl. untracked so they appear in the diff
|
|
56
|
+
return await runGit(['diff', '--cached', '--binary', wt.baseRef], wt.path);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return '';
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Apply a captured diff back into the main repo (at its root). Uses --3way so a
|
|
64
|
+
* clean patch lands and a conflicting one is reported rather than silently lost.
|
|
65
|
+
* Empty diff is a no-op success.
|
|
66
|
+
*/
|
|
67
|
+
export async function applyDiff(diff, repoRoot) {
|
|
68
|
+
if (!diff.trim())
|
|
69
|
+
return { ok: true };
|
|
70
|
+
const files = diffFiles(diff);
|
|
71
|
+
if (files.length) {
|
|
72
|
+
try {
|
|
73
|
+
await runGit(['diff', '--cached', '--quiet', '--', ...files], repoRoot);
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return { ok: false, reason: 'touched files have staged changes; refusing to disturb the index' };
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// Snapshot every touched file's exact pre-apply content (or absence). `git apply --3way`
|
|
80
|
+
// can leave conflict markers + unmerged index entries on failure, and across git versions
|
|
81
|
+
// `--check` doesn't always foresee a 3-way conflict — so on ANY failure we roll the working
|
|
82
|
+
// tree back to precisely this snapshot, preserving uncommitted changes that were already there.
|
|
83
|
+
const before = new Map();
|
|
84
|
+
await Promise.all(files.map(async (f) => {
|
|
85
|
+
before.set(f, await readFile(join(repoRoot, f)).catch(() => null));
|
|
86
|
+
}));
|
|
87
|
+
const patchFile = join(tmpdir(), `sanook-patch-${randomUUID().slice(0, 8)}.diff`);
|
|
88
|
+
try {
|
|
89
|
+
await writeFile(patchFile, diff, 'utf8');
|
|
90
|
+
await runGit(['apply', '--check', '--3way', '--whitespace=nowarn', patchFile], repoRoot); // fast reject
|
|
91
|
+
await runGit(['apply', '--3way', '--whitespace=nowarn', patchFile], repoRoot);
|
|
92
|
+
return { ok: true };
|
|
93
|
+
}
|
|
94
|
+
catch (e) {
|
|
95
|
+
// restore exact pre-apply content + clear any index/unmerged entries --3way may have created
|
|
96
|
+
await Promise.all([...before].map(async ([f, content]) => {
|
|
97
|
+
const abs = join(repoRoot, f);
|
|
98
|
+
if (content == null)
|
|
99
|
+
await rm(abs, { force: true }).catch(() => { });
|
|
100
|
+
else
|
|
101
|
+
await writeFile(abs, content).catch(() => { });
|
|
102
|
+
}));
|
|
103
|
+
if (files.length)
|
|
104
|
+
await runGit(['reset', '-q', '--', ...files], repoRoot).catch(() => { });
|
|
105
|
+
return { ok: false, reason: e.message.split('\n')[0] };
|
|
106
|
+
}
|
|
107
|
+
finally {
|
|
108
|
+
await rm(patchFile, { force: true }).catch(() => { });
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/** Remove the worktree and its temp parent (best-effort; prunes git's bookkeeping). */
|
|
112
|
+
export async function removeWorktree(wt) {
|
|
113
|
+
await runGit(['worktree', 'remove', '--force', wt.path], wt.repoRoot).catch(() => { });
|
|
114
|
+
await rm(wt.tmpParent, { recursive: true, force: true }).catch(() => { });
|
|
115
|
+
await runGit(['worktree', 'prune'], wt.repoRoot).catch(() => { });
|
|
116
|
+
}
|
|
117
|
+
/** changed file paths in a captured diff (for a human-readable summary). */
|
|
118
|
+
export function diffFiles(diff) {
|
|
119
|
+
const files = new Set();
|
|
120
|
+
for (const m of diff.matchAll(/^diff --git a\/(.+?) b\/(.+)$/gm))
|
|
121
|
+
files.add(m[2]);
|
|
122
|
+
return [...files];
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Run `work(task, cwd, i)` for each task in ITS OWN throwaway worktree (concurrently,
|
|
126
|
+
* via the injected `runConcurrently`), then capture+apply each worktree's diff back
|
|
127
|
+
* into the main tree sequentially. The work callback is injected so this whole
|
|
128
|
+
* lifecycle (create → isolate → merge → cleanup) unit-tests with no agent/network.
|
|
129
|
+
* Returns null if `root` is not a git repo or worktrees can't be created.
|
|
130
|
+
*/
|
|
131
|
+
export async function runInWorktrees(tasks, root, work, runConcurrently) {
|
|
132
|
+
if (!(await getRepoRoot(root)))
|
|
133
|
+
return null;
|
|
134
|
+
const wts = [];
|
|
135
|
+
for (let i = 0; i < tasks.length; i++) {
|
|
136
|
+
const wt = await createWorktree(root);
|
|
137
|
+
if (!wt) {
|
|
138
|
+
for (const w of wts)
|
|
139
|
+
await removeWorktree(w);
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
wts.push(wt);
|
|
143
|
+
}
|
|
144
|
+
let results;
|
|
145
|
+
try {
|
|
146
|
+
results = await runConcurrently(tasks.map((t, i) => () => work(t, wts[i].path, i)));
|
|
147
|
+
}
|
|
148
|
+
catch (e) {
|
|
149
|
+
for (const w of wts)
|
|
150
|
+
await removeWorktree(w);
|
|
151
|
+
throw e;
|
|
152
|
+
}
|
|
153
|
+
const out = [];
|
|
154
|
+
for (let i = 0; i < wts.length; i++) {
|
|
155
|
+
let merge;
|
|
156
|
+
try {
|
|
157
|
+
const diff = await captureDiff(wts[i]);
|
|
158
|
+
if (!diff.trim()) {
|
|
159
|
+
merge = { description: tasks[i].description, changed: [], applied: true };
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
const changed = diffFiles(diff);
|
|
163
|
+
const res = await applyDiff(diff, root); // sequential → deterministic conflict handling
|
|
164
|
+
merge = { description: tasks[i].description, changed, applied: res.ok, reason: res.reason };
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
finally {
|
|
168
|
+
await removeWorktree(wts[i]);
|
|
169
|
+
}
|
|
170
|
+
out.push({ result: results[i], merge });
|
|
171
|
+
}
|
|
172
|
+
return out;
|
|
173
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sanook-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "A terminal AI coding agent — BYOK, 12 providers, MCP, cron gateway, skills, and git awareness. Built from scratch in TypeScript.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"dist",
|
|
11
11
|
"skills",
|
|
12
12
|
"second-brain",
|
|
13
|
+
"scripts/postinstall.mjs",
|
|
13
14
|
"README.md",
|
|
14
15
|
"CHANGELOG.md",
|
|
15
16
|
"LICENSE",
|
|
@@ -21,6 +22,7 @@
|
|
|
21
22
|
"typecheck": "tsc --noEmit",
|
|
22
23
|
"test": "vitest run",
|
|
23
24
|
"eval": "tsx src/eval/run.ts",
|
|
25
|
+
"postinstall": "node scripts/postinstall.mjs",
|
|
24
26
|
"prepublishOnly": "npm run build"
|
|
25
27
|
},
|
|
26
28
|
"engines": {
|
|
@@ -28,15 +30,19 @@
|
|
|
28
30
|
},
|
|
29
31
|
"keywords": [
|
|
30
32
|
"ai",
|
|
31
|
-
"cli",
|
|
32
33
|
"coding-agent",
|
|
33
|
-
"agent",
|
|
34
|
+
"ai-agent",
|
|
35
|
+
"cli",
|
|
34
36
|
"llm",
|
|
35
37
|
"terminal",
|
|
36
38
|
"byok",
|
|
37
39
|
"mcp",
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
+
"second-brain",
|
|
41
|
+
"obsidian",
|
|
42
|
+
"cross-session-memory",
|
|
43
|
+
"claude-code-alternative",
|
|
44
|
+
"ai-coding",
|
|
45
|
+
"agent",
|
|
40
46
|
"claude",
|
|
41
47
|
"gemini"
|
|
42
48
|
],
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// postinstall — ชี้ทางให้ผู้ใช้พิมพ์คำสั่งที่ "ใช้ได้จริง" ทันทีหลัง `npm i`
|
|
2
|
+
// ปัญหาที่แก้: `npm i sanook-cli` (ไม่มี -g) = ลง local ไม่เข้า PATH → พิมพ์ `sanook` แล้วไม่เจอ
|
|
3
|
+
// กฎเหล็ก: ห้าม postinstall ทำให้การติดตั้งล้มเหลว (ครอบ try/catch, ออก 0 เสมอ)
|
|
4
|
+
import { resolve, dirname } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
|
|
7
|
+
try {
|
|
8
|
+
const isGlobal = process.env.npm_config_global === 'true';
|
|
9
|
+
const pkgRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
|
10
|
+
const initCwd = process.env.INIT_CWD ? resolve(process.env.INIT_CWD) : '';
|
|
11
|
+
|
|
12
|
+
// เงียบเมื่อ: dev ในรีโปตัวเอง (INIT_CWD === pkgRoot) หรือ CI (กัน log รก)
|
|
13
|
+
const selfInstall = initCwd && initCwd === pkgRoot;
|
|
14
|
+
if (selfInstall || (process.env.CI && !isGlobal)) process.exit(0);
|
|
15
|
+
|
|
16
|
+
const tty = process.stdout.isTTY;
|
|
17
|
+
const paint = (code, s) => (tty ? `\x1b[${code}m${s}\x1b[0m` : s);
|
|
18
|
+
const cyan = (s) => paint('36', s);
|
|
19
|
+
const dim = (s) => paint('2', s);
|
|
20
|
+
const bold = (s) => paint('1', s);
|
|
21
|
+
|
|
22
|
+
if (isGlobal) {
|
|
23
|
+
console.log(`\n${bold('✅ sanook-cli พร้อมใช้')} — พิมพ์ ${cyan('sanook')} เพื่อเริ่ม`);
|
|
24
|
+
console.log(dim(' ยังพิมพ์ "sanook" ไม่เจอ? ปิด-เปิด terminal ใหม่ · ตรวจ: ') + cyan('npx sanook doctor') + '\n');
|
|
25
|
+
} else {
|
|
26
|
+
console.log(`\n${bold('sanook-cli ลงแบบ local แล้ว')} — คำสั่ง ${cyan('sanook')} ยัง${bold('ไม่')}อยู่ใน PATH`);
|
|
27
|
+
console.log(` ${dim('• รันเลยตอนนี้:')} ${cyan('npx sanook')}`);
|
|
28
|
+
console.log(` ${dim('• ลงให้พิมพ์ sanook ตรงๆ:')} ${cyan('npm install -g sanook-cli')}`);
|
|
29
|
+
console.log(` ${dim('• ตรวจ/แก้ PATH:')} ${cyan('npx sanook doctor')}\n`);
|
|
30
|
+
}
|
|
31
|
+
} catch {
|
|
32
|
+
// ห้ามทำให้ install ล้ม — เงียบไว้
|
|
33
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
tags: [index, moc, -agents]
|
|
3
|
+
note_type: moc
|
|
4
|
+
created: {{DATE}}
|
|
5
|
+
updated: {{DATE}}
|
|
6
|
+
parent: "[[Home]]"
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# .agents
|
|
10
|
+
|
|
11
|
+
> agent-specific assets (skills/workflows) ของ vault นี้
|
|
12
|
+
|
|
13
|
+
## ใส่ที่นี่
|
|
14
|
+
skill + workflow guide ที่ agent ใช้
|
|
15
|
+
|
|
16
|
+
## ไม่ใส่ที่นี่
|
|
17
|
+
โน้ตงาน (→ปลายทางปกติ)
|
|
18
|
+
|
|
19
|
+
## AI Routing Contract
|
|
20
|
+
|
|
21
|
+
- ก่อนเขียน: เช็กว่าเนื้อหาตรง "ใส่ที่นี่" และไม่เข้า "ไม่ใส่ที่นี่"; ถ้าก้ำกึ่งอ่าน [[Vault Structure Map]] ก่อน
|
|
22
|
+
- ก่อนสร้างไฟล์ใหม่: ค้นหาโน้ตเดิมในโฟลเดอร์นี้และโฟลเดอร์ใกล้เคียงก่อน เพื่อ merge/update แทน append ซ้ำ
|
|
23
|
+
- เมื่อสร้างโน้ตในโฟลเดอร์นี้: ตั้ง `parent: "[[.agents/_Index]]"` และท้ายไฟล์ `up:: [[.agents/_Index]]`
|
|
24
|
+
- หลังเขียน: เชื่อม link ไป source/project/session/decision ที่เกี่ยวข้อง และอัปเดต hub/index ถ้าโน้ตนี้ควรถูกค้นเจอในอนาคต
|
|
25
|
+
|
|
26
|
+
> รายละเอียดทุกโฟลเดอร์ + decision rules → [[Vault Structure Map]]
|
|
27
|
+
|
|
28
|
+
_(ยังว่าง — โน้ตในโฟลเดอร์นี้จะถูกลิงก์ที่นี่)_
|
|
29
|
+
|
|
30
|
+
up:: [[Home]]
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
tags: [index, moc, skills]
|
|
3
|
+
note_type: moc
|
|
4
|
+
created: {{DATE}}
|
|
5
|
+
updated: {{DATE}}
|
|
6
|
+
parent: "[[.agents/_Index]]"
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# skills
|
|
10
|
+
|
|
11
|
+
> skill folders (SKILL.md) ที่ agent โหลด on-demand
|
|
12
|
+
|
|
13
|
+
## ใส่ที่นี่
|
|
14
|
+
SKILL.md ต่อ skill
|
|
15
|
+
|
|
16
|
+
## ไม่ใส่ที่นี่
|
|
17
|
+
prose how-to (→Runbooks)
|
|
18
|
+
|
|
19
|
+
## AI Routing Contract
|
|
20
|
+
|
|
21
|
+
- ก่อนเขียน: เช็กว่าเนื้อหาตรง "ใส่ที่นี่" และไม่เข้า "ไม่ใส่ที่นี่"; ถ้าก้ำกึ่งอ่าน [[Vault Structure Map]] ก่อน
|
|
22
|
+
- ก่อนสร้างไฟล์ใหม่: ค้นหาโน้ตเดิมในโฟลเดอร์นี้และโฟลเดอร์ใกล้เคียงก่อน เพื่อ merge/update แทน append ซ้ำ
|
|
23
|
+
- เมื่อสร้างโน้ตในโฟลเดอร์นี้: ตั้ง `parent: "[[.agents/skills/_Index]]"` และท้ายไฟล์ `up:: [[.agents/skills/_Index]]`
|
|
24
|
+
- หลังเขียน: เชื่อม link ไป source/project/session/decision ที่เกี่ยวข้อง และอัปเดต hub/index ถ้าโน้ตนี้ควรถูกค้นเจอในอนาคต
|
|
25
|
+
|
|
26
|
+
> รายละเอียดทุกโฟลเดอร์ + decision rules → [[Vault Structure Map]]
|
|
27
|
+
|
|
28
|
+
_(ยังว่าง — โน้ตในโฟลเดอร์นี้จะถูกลิงก์ที่นี่)_
|
|
29
|
+
|
|
30
|
+
up:: [[.agents/_Index]]
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
tags: [index, moc, workflows]
|
|
3
|
+
note_type: moc
|
|
4
|
+
created: {{DATE}}
|
|
5
|
+
updated: {{DATE}}
|
|
6
|
+
parent: "[[.agents/_Index]]"
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# workflows
|
|
10
|
+
|
|
11
|
+
> workflow guide (multi-step orchestration)
|
|
12
|
+
|
|
13
|
+
## ใส่ที่นี่
|
|
14
|
+
workflow ที่ทำซ้ำได้
|
|
15
|
+
|
|
16
|
+
## ไม่ใส่ที่นี่
|
|
17
|
+
one-off task
|
|
18
|
+
|
|
19
|
+
## AI Routing Contract
|
|
20
|
+
|
|
21
|
+
- ก่อนเขียน: เช็กว่าเนื้อหาตรง "ใส่ที่นี่" และไม่เข้า "ไม่ใส่ที่นี่"; ถ้าก้ำกึ่งอ่าน [[Vault Structure Map]] ก่อน
|
|
22
|
+
- ก่อนสร้างไฟล์ใหม่: ค้นหาโน้ตเดิมในโฟลเดอร์นี้และโฟลเดอร์ใกล้เคียงก่อน เพื่อ merge/update แทน append ซ้ำ
|
|
23
|
+
- เมื่อสร้างโน้ตในโฟลเดอร์นี้: ตั้ง `parent: "[[.agents/workflows/_Index]]"` และท้ายไฟล์ `up:: [[.agents/workflows/_Index]]`
|
|
24
|
+
- หลังเขียน: เชื่อม link ไป source/project/session/decision ที่เกี่ยวข้อง และอัปเดต hub/index ถ้าโน้ตนี้ควรถูกค้นเจอในอนาคต
|
|
25
|
+
|
|
26
|
+
> รายละเอียดทุกโฟลเดอร์ + decision rules → [[Vault Structure Map]]
|
|
27
|
+
|
|
28
|
+
_(ยังว่าง — โน้ตในโฟลเดอร์นี้จะถูกลิงก์ที่นี่)_
|
|
29
|
+
|
|
30
|
+
up:: [[.agents/_Index]]
|
package/second-brain/AGENTS.md
CHANGED
|
@@ -7,10 +7,10 @@
|
|
|
7
7
|
|
|
8
8
|
## 🔴 Red Lines
|
|
9
9
|
1. อ่าน `Shared/AI-Context-Index.md` ก่อนตอบ (vault = source of truth)
|
|
10
|
-
2.
|
|
11
|
-
3.
|
|
12
|
-
4.
|
|
13
|
-
5. ห้ามลบ durable note โดยไม่ถาม
|
|
10
|
+
2. ก่อนสร้าง/ย้ายโน้ต อ่าน `Vault Structure Map.md` + `_Index.md` ของโฟลเดอร์ปลายทาง แล้วทำตาม AI Routing Contract
|
|
11
|
+
3. verify ก่อนอ้าง ไม่แน่ใจบอกตรงๆ ห้ามแต่ง
|
|
12
|
+
4. ถามก่อนรัน destructive (`rm -rf` / `reset --hard` / `push --force` / drop data)
|
|
13
|
+
5. ห้ามเขียน secret ลงไฟล์ → `<secret:VAR>` · ห้ามลบ durable note โดยไม่ถาม
|
|
14
14
|
|
|
15
15
|
## Multi-agent
|
|
16
16
|
หลาย agent ทำงาน vault เดียว → อ่าน `Shared/Coordination/` ก่อนแตะ · เขียน session log หลังทำ (§2 ใน `CLAUDE.md`)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
tags: [index, moc, acceptance]
|
|
3
|
+
note_type: moc
|
|
4
|
+
created: {{DATE}}
|
|
5
|
+
updated: {{DATE}}
|
|
6
|
+
parent: "[[Home]]"
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Acceptance
|
|
10
|
+
|
|
11
|
+
> golden input→expected-output fixtures
|
|
12
|
+
|
|
13
|
+
## ใส่ที่นี่
|
|
14
|
+
case ที่ใช้ตัดสิน done/not-done
|
|
15
|
+
|
|
16
|
+
## ไม่ใส่ที่นี่
|
|
17
|
+
gate ticklist (→Checklists) · runner (→Evals)
|
|
18
|
+
|
|
19
|
+
## AI Routing Contract
|
|
20
|
+
|
|
21
|
+
- ก่อนเขียน: เช็กว่าเนื้อหาตรง "ใส่ที่นี่" และไม่เข้า "ไม่ใส่ที่นี่"; ถ้าก้ำกึ่งอ่าน [[Vault Structure Map]] ก่อน
|
|
22
|
+
- ก่อนสร้างไฟล์ใหม่: ค้นหาโน้ตเดิมในโฟลเดอร์นี้และโฟลเดอร์ใกล้เคียงก่อน เพื่อ merge/update แทน append ซ้ำ
|
|
23
|
+
- เมื่อสร้างโน้ตในโฟลเดอร์นี้: ตั้ง `parent: "[[Acceptance/_Index]]"` และท้ายไฟล์ `up:: [[Acceptance/_Index]]`
|
|
24
|
+
- หลังเขียน: เชื่อม link ไป source/project/session/decision ที่เกี่ยวข้อง และอัปเดต hub/index ถ้าโน้ตนี้ควรถูกค้นเจอในอนาคต
|
|
25
|
+
|
|
26
|
+
> รายละเอียดทุกโฟลเดอร์ + decision rules → [[Vault Structure Map]]
|
|
27
|
+
|
|
28
|
+
_(ยังว่าง — โน้ตในโฟลเดอร์นี้จะถูกลิงก์ที่นี่)_
|
|
29
|
+
|
|
30
|
+
up:: [[Home]]
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
tags: [template, acceptance, golden-case]
|
|
3
|
+
note_type: template
|
|
4
|
+
created: {{DATE}}
|
|
5
|
+
updated: {{DATE}}
|
|
6
|
+
parent: "[[Acceptance/_Index]]"
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Golden Case Template
|
|
10
|
+
|
|
11
|
+
> ใช้สร้าง fixture ที่บอก input→expected output เพื่อพิสูจน์ว่า behavior ใดถือว่า done; ไม่ใช้เป็น checklist หรือ eval report
|
|
12
|
+
|
|
13
|
+
## Task Family
|
|
14
|
+
|
|
15
|
+
_(What behavior this case evaluates.)_
|
|
16
|
+
|
|
17
|
+
## Input
|
|
18
|
+
|
|
19
|
+
```text
|
|
20
|
+
<input>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Expected Output
|
|
24
|
+
|
|
25
|
+
```text
|
|
26
|
+
<expected>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Pass Criteria
|
|
30
|
+
|
|
31
|
+
- [ ] Correct result
|
|
32
|
+
- [ ] No forbidden side effects
|
|
33
|
+
- [ ] Matches owner-facing tone/format
|
|
34
|
+
|
|
35
|
+
## Notes
|
|
36
|
+
|
|
37
|
+
_(Known edge cases, source links, or fixture paths.)_
|
|
38
|
+
|
|
39
|
+
up:: [[Acceptance/_Index]]
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
tags: [index, moc, areas]
|
|
3
|
+
note_type: moc
|
|
4
|
+
created: {{DATE}}
|
|
5
|
+
updated: {{DATE}}
|
|
6
|
+
parent: "[[Home]]"
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Areas
|
|
10
|
+
|
|
11
|
+
> PARA — โดเมนงานต่อเนื่องที่ไม่มีวันจบ
|
|
12
|
+
|
|
13
|
+
## ใส่ที่นี่
|
|
14
|
+
brand/trading/content/products ฯลฯ
|
|
15
|
+
|
|
16
|
+
## ไม่ใส่ที่นี่
|
|
17
|
+
งานที่มีวันจบ (→Projects/Goals)
|
|
18
|
+
|
|
19
|
+
## AI Routing Contract
|
|
20
|
+
|
|
21
|
+
- ก่อนเขียน: เช็กว่าเนื้อหาตรง "ใส่ที่นี่" และไม่เข้า "ไม่ใส่ที่นี่"; ถ้าก้ำกึ่งอ่าน [[Vault Structure Map]] ก่อน
|
|
22
|
+
- ก่อนสร้างไฟล์ใหม่: ค้นหาโน้ตเดิมในโฟลเดอร์นี้และโฟลเดอร์ใกล้เคียงก่อน เพื่อ merge/update แทน append ซ้ำ
|
|
23
|
+
- เมื่อสร้างโน้ตในโฟลเดอร์นี้: ตั้ง `parent: "[[Areas/_Index]]"` และท้ายไฟล์ `up:: [[Areas/_Index]]`
|
|
24
|
+
- หลังเขียน: เชื่อม link ไป source/project/session/decision ที่เกี่ยวข้อง และอัปเดต hub/index ถ้าโน้ตนี้ควรถูกค้นเจอในอนาคต
|
|
25
|
+
|
|
26
|
+
> รายละเอียดทุกโฟลเดอร์ + decision rules → [[Vault Structure Map]]
|
|
27
|
+
|
|
28
|
+
_(ยังว่าง — โน้ตในโฟลเดอร์นี้จะถูกลิงก์ที่นี่)_
|
|
29
|
+
|
|
30
|
+
up:: [[Home]]
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
tags: [index, moc, system-os]
|
|
3
|
+
note_type: moc
|
|
4
|
+
created: {{DATE}}
|
|
5
|
+
updated: {{DATE}}
|
|
6
|
+
parent: "[[Bugs/_Index]]"
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# System-OS
|
|
10
|
+
|
|
11
|
+
> bug report ระดับระบบ/OS/toolchain ที่ไม่ผูกกับ project เดียว
|
|
12
|
+
|
|
13
|
+
## ใส่ที่นี่
|
|
14
|
+
OS, shell, package manager, permission, filesystem, or app-runtime bugs
|
|
15
|
+
|
|
16
|
+
## ไม่ใส่ที่นี่
|
|
17
|
+
bug ของ project เฉพาะ (→Bugs หรือ Projects/<proj>/Bugs)
|
|
18
|
+
|
|
19
|
+
## AI Routing Contract
|
|
20
|
+
|
|
21
|
+
- ก่อนเขียน: เช็กว่าเนื้อหาตรง "ใส่ที่นี่" และไม่เข้า "ไม่ใส่ที่นี่"; ถ้าก้ำกึ่งอ่าน [[Vault Structure Map]] ก่อน
|
|
22
|
+
- ก่อนสร้างไฟล์ใหม่: ค้นหาโน้ตเดิมในโฟลเดอร์นี้และโฟลเดอร์ใกล้เคียงก่อน เพื่อ merge/update แทน append ซ้ำ
|
|
23
|
+
- เมื่อสร้างโน้ตในโฟลเดอร์นี้: ตั้ง `parent: "[[Bugs/System-OS/_Index]]"` และท้ายไฟล์ `up:: [[Bugs/System-OS/_Index]]`
|
|
24
|
+
- หลังเขียน: เชื่อม link ไป source/project/session/decision ที่เกี่ยวข้อง และอัปเดต hub/index ถ้าโน้ตนี้ควรถูกค้นเจอในอนาคต
|
|
25
|
+
|
|
26
|
+
> รายละเอียดทุกโฟลเดอร์ + decision rules → [[Vault Structure Map]]
|
|
27
|
+
|
|
28
|
+
_(ยังว่าง — โน้ตในโฟลเดอร์นี้จะถูกลิงก์ที่นี่)_
|
|
29
|
+
|
|
30
|
+
up:: [[Bugs/_Index]]
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
tags: [index, moc, bugs]
|
|
3
|
+
note_type: moc
|
|
4
|
+
created: {{DATE}}
|
|
5
|
+
updated: {{DATE}}
|
|
6
|
+
parent: "[[Home]]"
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Bugs
|
|
10
|
+
|
|
11
|
+
> bug report reproducible ลงวันที่ ไม่ลบ
|
|
12
|
+
|
|
13
|
+
## ใส่ที่นี่
|
|
14
|
+
bug report (global flat) + link กลับ project · system/OS → Bugs/System-OS/
|
|
15
|
+
|
|
16
|
+
## ไม่ใส่ที่นี่
|
|
17
|
+
bug ที่ reproduce ไม่ได้
|
|
18
|
+
|
|
19
|
+
## AI Routing Contract
|
|
20
|
+
|
|
21
|
+
- ก่อนเขียน: เช็กว่าเนื้อหาตรง "ใส่ที่นี่" และไม่เข้า "ไม่ใส่ที่นี่"; ถ้าก้ำกึ่งอ่าน [[Vault Structure Map]] ก่อน
|
|
22
|
+
- ก่อนสร้างไฟล์ใหม่: ค้นหาโน้ตเดิมในโฟลเดอร์นี้และโฟลเดอร์ใกล้เคียงก่อน เพื่อ merge/update แทน append ซ้ำ
|
|
23
|
+
- เมื่อสร้างโน้ตในโฟลเดอร์นี้: ตั้ง `parent: "[[Bugs/_Index]]"` และท้ายไฟล์ `up:: [[Bugs/_Index]]`
|
|
24
|
+
- หลังเขียน: เชื่อม link ไป source/project/session/decision ที่เกี่ยวข้อง และอัปเดต hub/index ถ้าโน้ตนี้ควรถูกค้นเจอในอนาคต
|
|
25
|
+
|
|
26
|
+
> รายละเอียดทุกโฟลเดอร์ + decision rules → [[Vault Structure Map]]
|
|
27
|
+
|
|
28
|
+
_(ยังว่าง — โน้ตในโฟลเดอร์นี้จะถูกลิงก์ที่นี่)_
|
|
29
|
+
|
|
30
|
+
up:: [[Home]]
|