nine-hundred 0.0.1

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 (107) hide show
  1. package/README.md +43 -0
  2. package/dist/agent/config/index.js +85 -0
  3. package/dist/agent/context/compress.js +145 -0
  4. package/dist/agent/context/index.js +68 -0
  5. package/dist/agent/context/token.js +30 -0
  6. package/dist/agent/hooks/config.js +41 -0
  7. package/dist/agent/hooks/core.js +177 -0
  8. package/dist/agent/hooks/events/index.js +9 -0
  9. package/dist/agent/hooks/events/post-tool-use-hook.js +7 -0
  10. package/dist/agent/hooks/events/pre-tool-use-hook.js +8 -0
  11. package/dist/agent/hooks/events/session-start-hook.js +8 -0
  12. package/dist/agent/hooks/events/stop-hook.js +10 -0
  13. package/dist/agent/hooks/events/user-prompt-submit-hook.js +7 -0
  14. package/dist/agent/hooks/index.js +7 -0
  15. package/dist/agent/hooks/types.js +32 -0
  16. package/dist/agent/index.js +465 -0
  17. package/dist/agent/llm/index.js +35 -0
  18. package/dist/agent/mcp/client.js +87 -0
  19. package/dist/agent/mcp/config.js +51 -0
  20. package/dist/agent/mcp/index.js +90 -0
  21. package/dist/agent/mcp/schema.js +56 -0
  22. package/dist/agent/permission/exec.js +71 -0
  23. package/dist/agent/permission/network.js +37 -0
  24. package/dist/agent/permission/read.js +27 -0
  25. package/dist/agent/permission/util/command-changes-directory.js +29 -0
  26. package/dist/agent/permission/util/dangerous-path.json +116 -0
  27. package/dist/agent/permission/util/detect-dangerous-operation.js +230 -0
  28. package/dist/agent/permission/util/detect-language-interpreter.js +66 -0
  29. package/dist/agent/permission/util/detect-safe-command.js +61 -0
  30. package/dist/agent/permission/util/index.js +16 -0
  31. package/dist/agent/permission/util/is-dangerous-path.js +98 -0
  32. package/dist/agent/permission/util/is-inside-cwd.js +83 -0
  33. package/dist/agent/permission/util/is-safe-domains.js +47 -0
  34. package/dist/agent/permission/write.js +31 -0
  35. package/dist/agent/prompt/index.js +42 -0
  36. package/dist/agent/skills/planner/SKILL.md +90 -0
  37. package/dist/agent/skills/programmer-resume/SKILL.md +113 -0
  38. package/dist/agent/skills/skill-creator/LICENSE.txt +202 -0
  39. package/dist/agent/skills/skill-creator/SKILL.md +495 -0
  40. package/dist/agent/skills/skill-creator/agents/analyzer.md +274 -0
  41. package/dist/agent/skills/skill-creator/agents/comparator.md +202 -0
  42. package/dist/agent/skills/skill-creator/agents/grader.md +223 -0
  43. package/dist/agent/skills/skill-creator/assets/eval_review.html +146 -0
  44. package/dist/agent/skills/skill-creator/eval-viewer/generate_review.py +471 -0
  45. package/dist/agent/skills/skill-creator/eval-viewer/viewer.html +1325 -0
  46. package/dist/agent/skills/skill-creator/references/schemas.md +430 -0
  47. package/dist/agent/skills/skill-creator/scripts/__init__.py +0 -0
  48. package/dist/agent/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
  49. package/dist/agent/skills/skill-creator/scripts/generate_report.py +326 -0
  50. package/dist/agent/skills/skill-creator/scripts/improve_description.py +247 -0
  51. package/dist/agent/skills/skill-creator/scripts/package_skill.py +136 -0
  52. package/dist/agent/skills/skill-creator/scripts/quick_validate.py +103 -0
  53. package/dist/agent/skills/skill-creator/scripts/run_eval.py +310 -0
  54. package/dist/agent/skills/skill-creator/scripts/run_loop.py +328 -0
  55. package/dist/agent/skills/skill-creator/scripts/utils.py +47 -0
  56. package/dist/agent/skills.js +129 -0
  57. package/dist/agent/tools/agent_tool/agent_tool.test.js +64 -0
  58. package/dist/agent/tools/agent_tool/index.js +33 -0
  59. package/dist/agent/tools/exec_tool/exec_tool.test.js +48 -0
  60. package/dist/agent/tools/exec_tool/index.js +44 -0
  61. package/dist/agent/tools/load_skill_tool/index.js +5 -0
  62. package/dist/agent/tools/load_skill_tool/load_skill_tool.test.js +122 -0
  63. package/dist/agent/tools/memory_create_tool/index.js +8 -0
  64. package/dist/agent/tools/memory_create_tool/memory_create_tool.test.js +54 -0
  65. package/dist/agent/tools/memory_delete_tool/index.js +10 -0
  66. package/dist/agent/tools/memory_delete_tool/memory_delete_tool.test.js +39 -0
  67. package/dist/agent/tools/memory_retrieve_tool/index.js +61 -0
  68. package/dist/agent/tools/memory_retrieve_tool/memory_retrieve_tool.test.js +102 -0
  69. package/dist/agent/tools/profile_update_tool/index.js +30 -0
  70. package/dist/agent/tools/profile_update_tool/profile_update_tool.test.js +49 -0
  71. package/dist/agent/tools/read_file_tool/index.js +24 -0
  72. package/dist/agent/tools/read_file_tool/read_file_tool.test.js +43 -0
  73. package/dist/agent/tools/run_js_tool/index.js +48 -0
  74. package/dist/agent/tools/run_js_tool/run_js_tool.test.js +67 -0
  75. package/dist/agent/tools/run_py_tool/index.js +48 -0
  76. package/dist/agent/tools/run_py_tool/run_py_tool.test.js +67 -0
  77. package/dist/agent/tools/tool_logger.js +16 -0
  78. package/dist/agent/tools/tool_logger.test.js +22 -0
  79. package/dist/agent/tools/web_fetch_url/index.js +76 -0
  80. package/dist/agent/tools/web_fetch_url/web_fetch_url.test.js +102 -0
  81. package/dist/agent/tools/web_search_tool/index.js +26 -0
  82. package/dist/agent/tools/web_search_tool/web_search_tool.test.js +61 -0
  83. package/dist/agent/tools/write_file_tool/index.js +22 -0
  84. package/dist/agent/tools/write_file_tool/write_file_tool.test.js +46 -0
  85. package/dist/agent/tools.js +218 -0
  86. package/dist/cli/command/compact/index.js +14 -0
  87. package/dist/cli/command/index.js +62 -0
  88. package/dist/cli/command/invalid/index.js +4 -0
  89. package/dist/cli/command/new/chat-session.js +10 -0
  90. package/dist/cli/command/new/index.js +8 -0
  91. package/dist/cli/command/rewind/index.js +19 -0
  92. package/dist/cli/command/rewind/rewind-command.test.js +22 -0
  93. package/dist/cli/command/session/format.js +32 -0
  94. package/dist/cli/command/session/index.js +32 -0
  95. package/dist/cli/command/session/session-command.test.js +49 -0
  96. package/dist/cli/command/unknown/index.js +4 -0
  97. package/dist/cli/index.js +144 -0
  98. package/dist/db/checkpointer.js +15 -0
  99. package/dist/db/index.js +2 -0
  100. package/dist/db/path.js +8 -0
  101. package/dist/db/sessions.js +81 -0
  102. package/dist/db/tables/memory.js +12 -0
  103. package/dist/db/tables/memory_fts.js +29 -0
  104. package/dist/index.js +87 -0
  105. package/dist/install.js +154 -0
  106. package/package.json +51 -0
  107. package/pnpm-workspace.yaml +3 -0
@@ -0,0 +1,66 @@
1
+ // Matches python / python2 / python3 / python3.11, and the pypy family.
2
+ // `py` (Windows launcher) is handled separately.
3
+ const PYTHON_INTERPRETER = /^(?:python[23]?(\.\d+)?|pypy[23]?(\.\d+)?|py)$/;
4
+ // JavaScript / TypeScript runtimes. Each entry is a recognized first-token
5
+ // that should be redirected to `run_js_tool`.
6
+ const JS_TS_INTERPRETERS = new Set(['node', 'nodejs', 'ts-node', 'tsx', 'deno', 'bun']);
7
+ // Other language runtimes / compilers. These do not have a dedicated tool,
8
+ // so the caller only needs the language name for the error message.
9
+ const OTHER_LANGUAGE_INTERPRETERS = {
10
+ java: 'Java',
11
+ javac: 'Java',
12
+ go: 'Go',
13
+ ruby: 'Ruby',
14
+ irb: 'Ruby',
15
+ rustc: 'Rust',
16
+ cargo: 'Rust',
17
+ dotnet: 'C#/.NET',
18
+ mono: 'C#/.NET',
19
+ php: 'PHP',
20
+ perl: 'Perl',
21
+ lua: 'Lua',
22
+ rscript: 'R',
23
+ scala: 'Scala',
24
+ kotlin: 'Kotlin',
25
+ groovy: 'Groovy',
26
+ swift: 'Swift',
27
+ dart: 'Dart',
28
+ elixir: 'Elixir',
29
+ julia: 'Julia',
30
+ };
31
+ /**
32
+ * Check whether a shell command string invokes an interpreter for a
33
+ * non-shell language. The first token of each chain segment is compared
34
+ * against known interpreter names. Python / JS / TS hits also carry a
35
+ * `tool` field pointing to the suggested dedicated tool.
36
+ *
37
+ * Shell and shell-script invocations (`bash script.sh`, `sh script.sh`,
38
+ * `zsh script.zsh`) are not flagged. Package managers (`npm install`,
39
+ * `cargo build`, `go build`) are likewise allowed because the first token
40
+ * is not a direct interpreter — but note that `go run`, `cargo run`,
41
+ * `npm test` etc. ARE flagged because they execute code under the hood
42
+ * and the LLM should be redirected to the dedicated tool when possible.
43
+ */
44
+ export function detectLanguageInterpreter(command) {
45
+ const segments = command.split(/&&|\|\||;|\||&/);
46
+ for (const raw of segments) {
47
+ const segment = raw.trim();
48
+ if (!segment)
49
+ continue;
50
+ const firstTokenMatch = segment.match(/^([^\s"']+)/);
51
+ if (!firstTokenMatch)
52
+ continue;
53
+ const firstToken = firstTokenMatch[1];
54
+ if (PYTHON_INTERPRETER.test(firstToken)) {
55
+ return { found: true, lang: 'Python', tool: 'run_py_tool', segment };
56
+ }
57
+ if (JS_TS_INTERPRETERS.has(firstToken)) {
58
+ return { found: true, lang: 'JavaScript/TypeScript', tool: 'run_js_tool', segment };
59
+ }
60
+ const otherLang = OTHER_LANGUAGE_INTERPRETERS[firstToken];
61
+ if (otherLang) {
62
+ return { found: true, lang: otherLang, segment };
63
+ }
64
+ }
65
+ return { found: false };
66
+ }
@@ -0,0 +1,61 @@
1
+ // Single-token safe commands. Compared case-insensitively so that
2
+ // PowerShell's mixed-case forms (`Get-ChildItem`) match.
3
+ //
4
+ // Cross-platform: POSIX (Linux / macOS), Windows CMD, and PowerShell
5
+ // aliases are all listed together.
6
+ //
7
+ // Note: `ls` etc. with sensitive-path arguments (e.g. `ls /etc/shadow`)
8
+ // are NOT filtered here. The risk surface is metadata only and the
9
+ // dangerous-op check still catches content reads via `cat`/`Get-Content`.
10
+ const SAFE_COMMANDS = new Set([
11
+ // POSIX (Linux / macOS)
12
+ 'ls', 'pwd', 'cat', 'head', 'tail', 'grep', 'find', 'echo', 'date', 'whoami',
13
+ // Windows CMD
14
+ 'dir', 'type', 'findstr',
15
+ // PowerShell (canonical + common aliases, all lowercased)
16
+ 'get-childitem', 'gci',
17
+ 'get-location', 'gl', 'pwd', // `pwd` doubles as a PowerShell alias for Get-Location
18
+ 'get-content', 'gc', 'cat', 'type',
19
+ 'select-string', 'sls',
20
+ 'get-item', 'gi',
21
+ 'write-output',
22
+ 'get-date',
23
+ ]);
24
+ // Git subcommands the user can run without confirmation. Each entry is
25
+ // compared as the second token of a `git ...` segment, lowercased.
26
+ // Only read-only subcommands are listed — anything that mutates the
27
+ // working tree (checkout, commit, push, pull, reset, clean, merge,
28
+ // rebase) or touches the network (fetch, clone) falls through to ask.
29
+ const SAFE_GIT_SUBCOMMANDS = new Set(['status', 'diff', 'log']);
30
+ /**
31
+ * Check whether a shell command string consists entirely of safe
32
+ * read-only commands. Every chain segment must independently qualify
33
+ * (first token in `SAFE_COMMANDS`, or `git` paired with a subcommand in
34
+ * `SAFE_GIT_SUBCOMMANDS`) for the whole command to be considered safe.
35
+ *
36
+ * The caller is expected to run this AFTER the dangerous-op check, so
37
+ * chains like `ls && rm -rf /` are already short-circuited and never
38
+ * reach this function as a candidate for `allow`.
39
+ */
40
+ export function detectSafeCommand(command) {
41
+ const segments = command.split(/&&|\|\||;|\||&/);
42
+ for (const raw of segments) {
43
+ const segment = raw.trim();
44
+ if (!segment)
45
+ continue;
46
+ const firstTokenMatch = segment.match(/^([^\s"']+)/);
47
+ if (!firstTokenMatch)
48
+ continue;
49
+ const firstToken = firstTokenMatch[1].toLowerCase();
50
+ if (SAFE_COMMANDS.has(firstToken))
51
+ continue;
52
+ if (firstToken === 'git') {
53
+ const subMatch = segment.match(/^git\s+(\S+)/);
54
+ const sub = subMatch?.[1]?.toLowerCase();
55
+ if (sub && SAFE_GIT_SUBCOMMANDS.has(sub))
56
+ continue;
57
+ }
58
+ return { safe: false };
59
+ }
60
+ return { safe: true };
61
+ }
@@ -0,0 +1,16 @@
1
+ // Public surface of the `util/` folder. Each scanner / helper below
2
+ // lives in its own file; this module re-exports the union so existing
3
+ // callers can keep importing from `./util.js`.
4
+ // ============= Path access =============
5
+ export { isDangerousPath } from './is-dangerous-path.js';
6
+ export { isInsideCwd } from './is-inside-cwd.js';
7
+ // ============= Shell command scanners =============
8
+ export { commandChangesDirectory } from './command-changes-directory.js';
9
+ export { detectLanguageInterpreter } from './detect-language-interpreter.js';
10
+ // ============= Dangerous operations =============
11
+ export { detectDangerousOperation } from './detect-dangerous-operation.js';
12
+ // ============= Safe commands =============
13
+ export { detectSafeCommand } from './detect-safe-command.js';
14
+ // ============= Network safety =============
15
+ export { isSafeDomain } from './is-safe-domains.js';
16
+ export { SAFE_DOMAINS } from './is-safe-domains.js';
@@ -0,0 +1,98 @@
1
+ import { homedir, platform } from 'node:os';
2
+ import { isAbsolute, posix, win32 } from 'node:path';
3
+ import dangerousPathsData from './dangerous-path.json' with { type: 'json' };
4
+ const dangerousPaths = dangerousPathsData;
5
+ const platformToOs = {
6
+ darwin: 'macos',
7
+ linux: 'linux',
8
+ win32: 'windows',
9
+ };
10
+ /**
11
+ * Determine whether a given filesystem path should be treated as dangerous,
12
+ * i.e. a path the agent is not allowed to read or write without explicit user
13
+ * confirmation. The set of dangerous paths is loaded from
14
+ * `dangerous-path.json` and selected by the current operating system.
15
+ *
16
+ * The input path may be expressed in any of the following forms:
17
+ * - absolute path (`/etc/passwd`, `C:\Windows\System32`)
18
+ * - relative path (`./.ssh/id_rsa`) — resolved against `cwd`
19
+ * - tilde-expanded (`~/.ssh`, `~/Documents`)
20
+ * - env-var form (`%USERPROFILE%\.ssh`, `%APPDATA%\Slack`)
21
+ *
22
+ * A path matches a dangerous entry when it equals that entry, lives inside
23
+ * that entry as a directory, or matches a wildcard entry (`*` in the entry
24
+ * matches any characters except the path separator).
25
+ */
26
+ export function isDangerousPath(input, options = {}) {
27
+ const osName = platformToOs[options.platform ?? platform()];
28
+ if (!osName)
29
+ return false;
30
+ const home = options.homeDir ?? homedir();
31
+ const env = options.env ?? process.env;
32
+ const cwd = options.cwd ?? process.cwd();
33
+ const isWindows = osName === 'windows';
34
+ const normalizedInput = normalizePath(input, home, env, cwd, isWindows);
35
+ const entries = dangerousPaths[osName];
36
+ for (const entry of entries) {
37
+ const normalizedEntry = normalizePath(entry, home, env, cwd, isWindows);
38
+ if (matches(normalizedInput, normalizedEntry, isWindows)) {
39
+ return true;
40
+ }
41
+ }
42
+ return false;
43
+ }
44
+ function normalizePath(raw, home, env, cwd, isWindows) {
45
+ let p = raw;
46
+ // Expand Windows-style environment variables. %USERPROFILE% is always
47
+ // treated as the home directory even when not present in `env` so the
48
+ // dangerous-path list stays portable.
49
+ p = p.replace(/%([A-Z_][A-Z0-9_]*)%/gi, (match, name) => {
50
+ if (name === 'USERPROFILE')
51
+ return home;
52
+ return env[name] ?? match;
53
+ });
54
+ // Expand a leading tilde to the home directory.
55
+ if (p === '~') {
56
+ p = home;
57
+ }
58
+ else if (p.startsWith('~/') || p.startsWith('~\\')) {
59
+ p = home + p.slice(1);
60
+ }
61
+ // Normalize path separators so the same path can be compared regardless
62
+ // of whether the user wrote forward or backward slashes.
63
+ if (isWindows) {
64
+ p = p.replace(/\//g, '\\');
65
+ }
66
+ else {
67
+ p = p.replace(/\\/g, '/');
68
+ }
69
+ // Resolve relative paths against cwd using the platform-appropriate
70
+ // resolver so non-Windows tests can still exercise Windows-style inputs.
71
+ if (!isAbsolute(p)) {
72
+ p = isWindows ? win32.resolve(cwd, p) : posix.resolve(cwd, p);
73
+ if (!isWindows)
74
+ p = p.replace(/\\/g, '/');
75
+ }
76
+ // Windows filesystems are case-insensitive, so case-fold both sides
77
+ // before comparing.
78
+ if (isWindows)
79
+ p = p.toLowerCase();
80
+ return p;
81
+ }
82
+ function matches(input, dangerous, isWindows) {
83
+ const sep = isWindows ? '\\' : '/';
84
+ if (input === dangerous)
85
+ return true;
86
+ if (input.startsWith(dangerous + sep))
87
+ return true;
88
+ if (dangerous.includes('*')) {
89
+ const sepClass = isWindows ? '\\\\' : '/';
90
+ const escaped = dangerous
91
+ .split('*')
92
+ .map((part) => part.replace(/[.+^${}()|[\]\\]/g, '\\$&'))
93
+ .join(`[^${sepClass}]*`);
94
+ const regex = new RegExp(`^${escaped}$`, isWindows ? 'i' : '');
95
+ return regex.test(input);
96
+ }
97
+ return false;
98
+ }
@@ -0,0 +1,83 @@
1
+ import { realpath } from 'node:fs/promises';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ function getAllowedRoot() {
5
+ return path.join(os.homedir(), '.900');
6
+ }
7
+ async function resolveExistingPrefix(p) {
8
+ let dir = path.dirname(p);
9
+ while (true) {
10
+ try {
11
+ const realDir = await realpath(dir);
12
+ const remaining = path.relative(dir, p);
13
+ return path.join(realDir, remaining);
14
+ }
15
+ catch {
16
+ const parent = path.dirname(dir);
17
+ if (parent === dir) {
18
+ return null;
19
+ }
20
+ dir = parent;
21
+ }
22
+ }
23
+ }
24
+ async function isInsideResolvedRoot(target, root, allowMissingAncestors = false) {
25
+ let realTarget;
26
+ try {
27
+ realTarget = await realpath(target);
28
+ }
29
+ catch {
30
+ if (allowMissingAncestors) {
31
+ const resolved = await resolveExistingPrefix(path.resolve(root, target));
32
+ if (!resolved) {
33
+ realTarget = path.resolve(root, target);
34
+ }
35
+ else {
36
+ realTarget = resolved;
37
+ }
38
+ }
39
+ else {
40
+ try {
41
+ const realParent = await realpath(path.dirname(path.resolve(root, target)));
42
+ realTarget = path.join(realParent, path.basename(target));
43
+ }
44
+ catch {
45
+ return false;
46
+ }
47
+ }
48
+ }
49
+ let realRoot;
50
+ try {
51
+ realRoot = await realpath(root);
52
+ }
53
+ catch {
54
+ if (allowMissingAncestors) {
55
+ realRoot = path.resolve(root);
56
+ }
57
+ else {
58
+ return false;
59
+ }
60
+ }
61
+ const relative = path.relative(realRoot, realTarget);
62
+ if (relative === '')
63
+ return true;
64
+ return !relative.startsWith('..') && !path.isAbsolute(relative);
65
+ }
66
+ /**
67
+ * Determine whether `target` lives inside `cwd` or inside the global
68
+ * `~/.900` directory (after symlink resolution).
69
+ *
70
+ * Used by the read / write permission checkers to decide whether a path
71
+ * is safe to auto-allow.
72
+ *
73
+ * If the target does not exist (e.g. a write creating a new file), the
74
+ * parent directory is resolved instead so a missing leaf can still be
75
+ * recognized as inside the allowed root.
76
+ */
77
+ export async function isInsideCwd(target, cwd) {
78
+ if (await isInsideResolvedRoot(target, cwd)) {
79
+ return true;
80
+ }
81
+ const absoluteTarget = path.isAbsolute(target) ? target : path.resolve(cwd, target);
82
+ return isInsideResolvedRoot(absoluteTarget, getAllowedRoot(), true);
83
+ }
@@ -0,0 +1,47 @@
1
+ // Public safe-domain allowlist for the network tool permission check.
2
+ // A hostname is considered safe when it equals one of these entries, or
3
+ // is an immediate subdomain of one (e.g. `api.github.com` matches via
4
+ // parent `github.com`). Deeper subdomains do NOT match — `a.b.github.com`
5
+ // is not covered by `github.com`.
6
+ //
7
+ // Match is case-insensitive. Wildcards are not supported; add explicit
8
+ // entries instead.
9
+ // Code hosting & package registries
10
+ // github.com — repos, API, gists
11
+ // raw.githubusercontent.com — direct raw file fetch (separate apex)
12
+ // npmjs.com — npm package metadata
13
+ // pypi.org — Python package index
14
+ // Documentation & reference
15
+ // developer.mozilla.org — MDN web docs
16
+ // stackoverflow.com — Q&A
17
+ // wikipedia.org — general knowledge
18
+ export const SAFE_DOMAINS = [
19
+ 'github.com',
20
+ 'raw.githubusercontent.com',
21
+ 'npmjs.com',
22
+ 'pypi.org',
23
+ 'developer.mozilla.org',
24
+ 'stackoverflow.com',
25
+ 'wikipedia.org',
26
+ ];
27
+ const SAFE_DOMAIN_SET = new Set(SAFE_DOMAINS.map((d) => d.toLowerCase()));
28
+ /**
29
+ * Check whether a hostname matches one of the safe domains. Comparison
30
+ * is case-insensitive. Only the immediate parent domain is consulted, so
31
+ * `api.github.com` matches via `github.com`, but `raw.githubusercontent.com`
32
+ * (different apex) needs its own entry.
33
+ */
34
+ export function isSafeDomain(hostname) {
35
+ if (!hostname)
36
+ return false;
37
+ const lower = hostname.toLowerCase();
38
+ if (SAFE_DOMAIN_SET.has(lower))
39
+ return true;
40
+ const firstDot = lower.indexOf('.');
41
+ if (firstDot > 0) {
42
+ const parent = lower.slice(firstDot + 1);
43
+ if (SAFE_DOMAIN_SET.has(parent))
44
+ return true;
45
+ }
46
+ return false;
47
+ }
@@ -0,0 +1,31 @@
1
+ import { isDangerousPath, isInsideCwd, } from './util/index.js';
2
+ /**
3
+ * Decide whether a write tool call should be auto-allowed, blocked, or sent to
4
+ * the human-in-the-loop interrupt.
5
+ *
6
+ * - no `filePath` in args → `allow`
7
+ * - `filePath` resolves inside cwd → `allow`
8
+ * - `filePath` matches a dangerous-path entry → `block` with a message
9
+ * that is returned to the LLM
10
+ * - otherwise (outside cwd, not dangerous) → `ask` (fall through to interrupt)
11
+ */
12
+ export async function checkWritePermission(args, options = {}) {
13
+ const filePath = args.filePath;
14
+ if (typeof filePath !== 'string' || filePath.length === 0) {
15
+ return { action: 'allow' };
16
+ }
17
+ const cwd = options.cwd ?? process.cwd();
18
+ if (await isInsideCwd(filePath, cwd)) {
19
+ return { action: 'allow' };
20
+ }
21
+ if (isDangerousPath(filePath)) {
22
+ return {
23
+ action: 'block',
24
+ message: `⚠️ 路径 \`${filePath}\` 是危险路径,不允许访问。` +
25
+ `危险路径包含 ~/.ssh、~/.aws、~/.gnupg、/etc、/var 等敏感目录,` +
26
+ `以及 Windows 上的 %APPDATA%、C:\\Windows\\System32 等。` +
27
+ `请改用工作目录内的其他文件。`,
28
+ };
29
+ }
30
+ return { action: 'ask' };
31
+ }
@@ -0,0 +1,42 @@
1
+ import fs from 'node:fs';
2
+ import * as os from 'node:os';
3
+ import path from 'node:path';
4
+ import { formatSkillCreationInstructions, formatSkillsForSystemPrompt } from '../../agent/skills.js';
5
+ const baseSystemPrompt = 'You are a helpful assistant. Your name is Argos.';
6
+ const skillCreationInstructions = formatSkillCreationInstructions();
7
+ const skillsPrompt = formatSkillsForSystemPrompt();
8
+ function loadProfileInfo() {
9
+ const profilePath = path.join(os.homedir(), '.900', '.data', 'profile.md');
10
+ if (!fs.existsSync(profilePath)) {
11
+ return '';
12
+ }
13
+ const content = fs.readFileSync(profilePath, 'utf8').trim();
14
+ return content;
15
+ }
16
+ const profileInfo = loadProfileInfo();
17
+ const profilePrompt = `用户画像模板(当记忆中有对应信息时,可据此构建用户画像):
18
+ <profile_template>
19
+ - 基本身份:姓名,昵称,性别,年龄、地区、语言
20
+ - 外貌:身高 体重 肤色 胖瘦
21
+ - 性格与沟通偏好
22
+ - 兴趣爱好
23
+ - 技能
24
+ - 工作
25
+ </profile_template>
26
+
27
+ <profile_info>
28
+ ${profileInfo}
29
+ </profile_info>`;
30
+ const memoryPrompt = `记忆操作规则:
31
+ - 删除记忆:必须先通过 memory_retrieve_tool 查询并确认具体的记忆 id,然后再调用 memory_delete_tool 删除。如果查询不到相关记忆,应友好地提示用户。
32
+ - 更新记忆:先调用 memory_delete_tool 删除旧记忆,再调用 memory_create_tool 插入新记忆。
33
+ - 当一个信息符合 <profile_template> 内容和范围时,不要作为 memory 记录,它会被记录在 profile 文件中。`;
34
+ export const systemPrompt = [
35
+ baseSystemPrompt,
36
+ skillCreationInstructions,
37
+ skillsPrompt,
38
+ profilePrompt,
39
+ memoryPrompt,
40
+ ]
41
+ .filter(Boolean)
42
+ .join('\n\n');
@@ -0,0 +1,90 @@
1
+ ---
2
+ name: planner
3
+ description: 帮用户创建 todo list、任务清单、日程计划或项目计划。当用户提到"待办"、"todo"、"计划"、"任务清单"、"日程"、"schedule"、"plan"、"任务分解"、"行程"时触发此 skill。即使用户只是说"帮我列一下要做的事"或"我想规划一下",也应使用此 skill。
4
+ ---
5
+
6
+ # Planner
7
+
8
+ 帮助用户创建结构清晰、可执行的 todo list 或计划。
9
+
10
+ ## 核心原则
11
+
12
+ - **具体可执行**:每个任务应清晰到可以直接行动,避免模糊表述
13
+ - **合理分组**:将相关任务归类,方便用户聚焦
14
+ - **优先级明确**:区分紧急/重要,帮助用户知道从哪里开始
15
+ - **时间感知**:有 deadline 时给出时间节点,无 deadline 时建议大致耗时
16
+
17
+ ---
18
+
19
+ ## 输出格式
20
+
21
+ 根据用户需求选择合适格式:
22
+
23
+ ### 简单 Todo List
24
+
25
+ 适用于:日常任务、购物清单、零散待办
26
+
27
+ ```
28
+ ## ✅ 今日待办
29
+
30
+ ### 🔴 优先处理
31
+ - [ ] 任务 A(预计 30 分钟)
32
+ - [ ] 任务 B
33
+
34
+ ### 🟡 今天内完成
35
+ - [ ] 任务 C
36
+ - [ ] 任务 D
37
+
38
+ ### 🟢 有空再做
39
+ - [ ] 任务 E
40
+ ```
41
+
42
+ ### 项目计划
43
+
44
+ 适用于:多步骤目标、有时间跨度的任务
45
+
46
+ ```
47
+ ## 📋 项目:[项目名]
48
+
49
+ **目标**:一句话说清楚要达成什么
50
+ **周期**:起止时间或预计时长
51
+
52
+ ### 阶段一:[名称](第 1-3 天)
53
+ - [ ] 子任务 1
54
+ - [ ] 子任务 2
55
+
56
+ ### 阶段二:[名称](第 4-7 天)
57
+ - [ ] 子任务 3
58
+ ```
59
+
60
+ ### 日程安排
61
+
62
+ 适用于:某一天或某段时间的时间表
63
+
64
+ ```
65
+ ## 🗓️ [日期] 日程
66
+
67
+ | 时间 | 事项 | 备注 |
68
+ |------|------|------|
69
+ | 09:00 | 任务 A | |
70
+ | 10:30 | 任务 B | 需提前准备材料 |
71
+ ```
72
+
73
+ ---
74
+
75
+ ## 操作步骤
76
+
77
+ 1. **理解需求**:从用户描述中提取目标、时间范围、资源限制
78
+ 2. **补充信息**(如有必要):若信息太少,问 1 个最关键的问题(不要问多个)
79
+ 3. **选择格式**:根据任务性质选择上方合适的模板
80
+ 4. **生成计划**:输出结构化内容,加入适当 emoji 提升可读性
81
+ 5. **给出建议**:末尾可简短说明"从哪里开始"或注意事项
82
+
83
+ ---
84
+
85
+ ## 注意事项
86
+
87
+ - 用户若未指定语言,跟随用户语言(中文输入→中文输出)
88
+ - 任务数量控制在合理范围:单日 todo 不超过 10 项,项目计划不超过 5 个阶段
89
+ - 若用户提供的目标过于宏大(如"学会编程"),主动拆解为近期可执行的第一步
90
+ - 允许用户说"太多了"或"再简单一点",灵活调整粒度
@@ -0,0 +1,113 @@
1
+ ---
2
+ name: programmer-resume
3
+ description: 指导程序员撰写简历的技巧、格式、内容结构和注意事项。当用户提到"写简历"、"简历格式"、"程序员简历"、"求职简历"、"简历内容"、"简历怎么写"、"resume"、"CV",或者询问如何展示技术经验、项目经历、技术栈等求职相关问题时,必须使用此 skill。即使用户只是问"简历要写什么"这类模糊问题,也应触发此 skill。
4
+ ---
5
+
6
+ # 程序员简历撰写指南
7
+
8
+ ## 核心原则
9
+
10
+ - **一页为主**:工作经验 < 5 年用一页,资深工程师可扩展至两页
11
+ - **量化成果**:每条经历尽量附上数据(如"性能提升 40%"、"日活用户 10万+")
12
+ - **针对性定制**:根据目标 JD(职位描述)调整关键词,通过 ATS(简历筛选系统)
13
+ - **简洁清晰**:避免花哨设计,使用标准字体(Arial、Calibri),留白充足
14
+
15
+ ---
16
+
17
+ ## 简历结构(推荐顺序)
18
+
19
+ ### 1. 基本信息
20
+
21
+ - 姓名(大号加粗)、手机、邮箱、所在城市
22
+ - GitHub 链接、个人博客/Portfolio(加分项)
23
+ - **不需要**:照片、年龄、婚姻状况、政治面貌(国内除外)
24
+
25
+ ### 2. 技术技能(Technical Skills)
26
+
27
+ 按类别列出,勿堆砌,只写**真正熟悉**的:
28
+
29
+ ```
30
+ 编程语言:Python, Go, TypeScript
31
+ 框架/库:FastAPI, React, Spring Boot
32
+ 数据库:PostgreSQL, Redis, MongoDB
33
+ 工具/平台:Docker, Kubernetes, AWS, Git
34
+ ```
35
+
36
+ ### 3. 工作经历(Work Experience)
37
+
38
+ 格式:`公司名 | 职位 | 时间段(年/月)`
39
+
40
+ 每条经历用 **STAR 法则** 描述(Situation → Task → Action → Result):
41
+
42
+ - ✅ "重构订单服务,将 P99 延迟从 800ms 降至 120ms,支撑大促期间 5 万 QPS"
43
+ - ❌ "负责订单系统的开发和维护工作"
44
+
45
+ ### 4. 项目经历(Projects)
46
+
47
+ 适用于经验不足或有亮眼个人项目的候选人:
48
+
49
+ - 项目名 + 一句话介绍
50
+ - 技术栈(加粗关键词)
51
+ - 核心贡献 + 可量化结果
52
+ - 附 GitHub/线上地址
53
+
54
+ ### 5. 教育背景(Education)
55
+
56
+ - 学校、专业、学历、毕业年份
57
+ - GPA(3.5+)、奖学金、竞赛获奖可列出
58
+
59
+ ### 6. 其他(可选)
60
+
61
+ - 开源贡献(列出 PR 或 Star 数)
62
+ - 技术文章/演讲
63
+ - 证书(AWS、CKA 等含金量高的优先)
64
+
65
+ ---
66
+
67
+ ## 常见错误 ❌
68
+
69
+ | 错误 | 正确做法 |
70
+ | ---------------------------- | -------------------------------------- |
71
+ | 写"熟悉 Java"但答不上问题 | 只写真实掌握的技术 |
72
+ | 职责描述用"负责…" | 改用动词开头:"设计、优化、实现、主导" |
73
+ | 简历有错别字/格式错乱 | 导出前检查 PDF 渲染效果 |
74
+ | 同一份简历投所有岗位 | 根据 JD 调整技能和关键词 |
75
+ | 写"工作认真负责、团队合作好" | 删掉,用事实说话 |
76
+
77
+ ---
78
+
79
+ ## 针对不同经验阶段的建议
80
+
81
+ ### 应届生 / 实习生
82
+
83
+ - 项目经历 > 工作经历,校园项目、毕设、开源贡献都可以写
84
+ - 突出技术广度和学习能力
85
+ - 如有知名实习经历(大厂、独角兽),放在最显眼位置
86
+
87
+ ### 1–3 年经验
88
+
89
+ - 工作经历为主,重点写**独立交付**的模块或功能
90
+ - 展示技术深度:不只写"用了 Redis",要写"用 Redis 实现分布式锁解决超卖问题"
91
+
92
+ ### 5 年以上 / 高级工程师
93
+
94
+ - 突出**架构设计、技术决策、团队带领**
95
+ - 用数据体现影响力(团队规模、系统规模、业务指标)
96
+ - 可附架构图或技术方案链接
97
+
98
+ ---
99
+
100
+ ## 格式工具推荐
101
+
102
+ - **在线模板**:Overleaf(LaTeX,适合外企)、超级简历、职徒简历
103
+ - **本地工具**:Word / Pages 导出 PDF
104
+ - **程序员专属**:[jsonresume.org](https://jsonresume.org)、用 Markdown 生成简历
105
+
106
+ ---
107
+
108
+ ## 投递注意事项
109
+
110
+ 1. **文件命名**:`姓名_岗位_简历.pdf`,勿命名为 `简历1.pdf`
111
+ 2. **格式**:统一发 PDF,避免 Word 格式错乱
112
+ 3. **投递渠道**:内推 > 官网 > Boss直聘/LinkedIn > 招聘平台
113
+ 4. **跟进**:投递 1 周未回音可发邮件礼貌跟进一次