@forwardimpact/outpost 3.12.0 → 3.12.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 (114) hide show
  1. package/README.md +2 -2
  2. package/bin/fit-outpost.js +3 -2
  3. package/package.json +4 -4
  4. package/src/agent-path.js +10 -6
  5. package/src/agent-runner.js +28 -24
  6. package/src/index.js +2 -3
  7. package/src/kb-manager.js +21 -19
  8. package/src/outpost.js +37 -33
  9. package/src/posture.js +3 -3
  10. package/src/privilege.js +11 -11
  11. package/src/scheduler.js +14 -12
  12. package/src/socket-server.js +19 -18
  13. package/src/spawn-env.js +7 -6
  14. package/src/state-manager.js +2 -2
  15. package/templates/.claude/agents/chief-of-staff.md +22 -22
  16. package/templates/.claude/agents/concierge.md +19 -17
  17. package/templates/.claude/agents/head-hunter.md +27 -26
  18. package/templates/.claude/agents/librarian.md +17 -16
  19. package/templates/.claude/agents/postman.md +19 -18
  20. package/templates/.claude/agents/recruiter.md +31 -27
  21. package/templates/.claude/skills/anarlog-follow/SKILL.md +50 -49
  22. package/templates/.claude/skills/anarlog-follow/references/coaching.md +22 -21
  23. package/templates/.claude/skills/anarlog-follow/scripts/follow.mjs +4 -4
  24. package/templates/.claude/skills/anarlog-process/SKILL.md +35 -32
  25. package/templates/.claude/skills/anarlog-process/references/extraction.md +26 -22
  26. package/templates/.claude/skills/anarlog-process/references/sessions.md +10 -10
  27. package/templates/.claude/skills/anarlog-process/scripts/scan.mjs +10 -9
  28. package/templates/.claude/skills/anarlog-trim/SKILL.md +43 -40
  29. package/templates/.claude/skills/candidate-report/SKILL.md +30 -28
  30. package/templates/.claude/skills/candidate-report/references/report-template.html +10 -10
  31. package/templates/.claude/skills/candidate-report/references/report.css +4 -4
  32. package/templates/.claude/skills/candidate-report/references/rubric.md +15 -14
  33. package/templates/.claude/skills/candidate-report/scripts/render-pdf.mjs +2 -2
  34. package/templates/.claude/skills/changelog/SKILL.md +55 -50
  35. package/templates/.claude/skills/deck-create/SKILL.md +30 -30
  36. package/templates/.claude/skills/deck-create/references/slide.css +4 -4
  37. package/templates/.claude/skills/deck-create/scripts/convert-to-pdf.mjs +6 -6
  38. package/templates/.claude/skills/deck-review/SKILL.md +59 -57
  39. package/templates/.claude/skills/deck-review/assets/slide-annotator.js +58 -49
  40. package/templates/.claude/skills/deck-summarize/SKILL.md +49 -43
  41. package/templates/.claude/skills/deck-summarize/references/brief-template.md +4 -3
  42. package/templates/.claude/skills/deck-summarize/scripts/extract-pptx.mjs +8 -7
  43. package/templates/.claude/skills/doc-collab/SKILL.md +25 -25
  44. package/templates/.claude/skills/doc-create/SKILL.md +21 -20
  45. package/templates/.claude/skills/doc-create/scripts/convert-to-pdf.mjs +6 -6
  46. package/templates/.claude/skills/draft-emails/SKILL.md +37 -33
  47. package/templates/.claude/skills/draft-emails/references/template.md +2 -2
  48. package/templates/.claude/skills/draft-emails/scripts/scan-emails.mjs +5 -4
  49. package/templates/.claude/skills/draft-emails/scripts/send-email.mjs +11 -9
  50. package/templates/.claude/skills/extract-entities/SKILL.md +65 -65
  51. package/templates/.claude/skills/extract-entities/references/TEMPLATES.md +5 -5
  52. package/templates/.claude/skills/extract-entities/references/conditions.md +14 -13
  53. package/templates/.claude/skills/extract-entities/references/content.md +10 -9
  54. package/templates/.claude/skills/extract-entities/references/links.md +5 -5
  55. package/templates/.claude/skills/extract-entities/references/recruitment.md +19 -18
  56. package/templates/.claude/skills/extract-entities/references/resolution.md +13 -13
  57. package/templates/.claude/skills/extract-entities/references/sources.md +8 -8
  58. package/templates/.claude/skills/extract-entities/references/templates-conditions.md +2 -2
  59. package/templates/.claude/skills/extract-entities/references/templates-priorities.md +2 -2
  60. package/templates/.claude/skills/extract-entities/scripts/state.mjs +9 -8
  61. package/templates/.claude/skills/meeting-prep/SKILL.md +25 -24
  62. package/templates/.claude/skills/organize-files/SKILL.md +24 -22
  63. package/templates/.claude/skills/organize-files/scripts/organize-by-type.mjs +5 -4
  64. package/templates/.claude/skills/organize-files/scripts/summarize.mjs +4 -4
  65. package/templates/.claude/skills/person-identify/SKILL.md +24 -22
  66. package/templates/.claude/skills/person-identify/scripts/identify.sh +17 -17
  67. package/templates/.claude/skills/person-lookup/SKILL.md +28 -29
  68. package/templates/.claude/skills/person-lookup/scripts/lookup.sh +19 -16
  69. package/templates/.claude/skills/req-assess/SKILL.md +27 -27
  70. package/templates/.claude/skills/req-assess/references/interview-template.md +1 -1
  71. package/templates/.claude/skills/req-assess/references/panel-template.md +3 -2
  72. package/templates/.claude/skills/req-assess/references/rubric.md +13 -13
  73. package/templates/.claude/skills/req-decide/SKILL.md +28 -25
  74. package/templates/.claude/skills/req-decide/references/rubric.md +11 -10
  75. package/templates/.claude/skills/req-forget/SKILL.md +28 -26
  76. package/templates/.claude/skills/req-forget/references/classify.md +13 -13
  77. package/templates/.claude/skills/req-forget/references/locations.md +2 -2
  78. package/templates/.claude/skills/req-forget/references/report-template.md +1 -1
  79. package/templates/.claude/skills/req-scan/SKILL.md +21 -22
  80. package/templates/.claude/skills/req-scan/references/fallbacks.md +9 -9
  81. package/templates/.claude/skills/req-scan/references/filters.md +13 -13
  82. package/templates/.claude/skills/req-scan/references/sources.md +7 -7
  83. package/templates/.claude/skills/req-scan/references/state.md +1 -1
  84. package/templates/.claude/skills/req-scan/scripts/state.mjs +3 -2
  85. package/templates/.claude/skills/req-screen/SKILL.md +31 -29
  86. package/templates/.claude/skills/req-screen/references/rubric.md +18 -18
  87. package/templates/.claude/skills/req-screen/references/template.md +4 -4
  88. package/templates/.claude/skills/req-track/SKILL.md +40 -40
  89. package/templates/.claude/skills/req-track/references/fields.md +19 -18
  90. package/templates/.claude/skills/req-track/references/signals.md +12 -12
  91. package/templates/.claude/skills/req-track/references/statuses.md +3 -2
  92. package/templates/.claude/skills/req-track/references/templates.md +4 -4
  93. package/templates/.claude/skills/req-workday/SKILL.md +24 -24
  94. package/templates/.claude/skills/req-workday/references/status-mapping.md +5 -5
  95. package/templates/.claude/skills/req-workday/references/templates.md +13 -13
  96. package/templates/.claude/skills/req-workday/references/xlsx-format.md +7 -7
  97. package/templates/.claude/skills/req-workday/scripts/parse-workday.mjs +19 -17
  98. package/templates/.claude/skills/send-chat/SKILL.md +47 -43
  99. package/templates/.claude/skills/sync-apple-calendar/SKILL.md +20 -19
  100. package/templates/.claude/skills/sync-apple-calendar/references/SCHEMA.md +7 -7
  101. package/templates/.claude/skills/sync-apple-calendar/scripts/query.mjs +6 -6
  102. package/templates/.claude/skills/sync-apple-calendar/scripts/sync.mjs +9 -8
  103. package/templates/.claude/skills/sync-apple-mail/SKILL.md +17 -14
  104. package/templates/.claude/skills/sync-apple-mail/references/SCHEMA.md +10 -10
  105. package/templates/.claude/skills/sync-apple-mail/scripts/parse-emlx.mjs +9 -9
  106. package/templates/.claude/skills/sync-apple-mail/scripts/sync-helpers.mjs +16 -13
  107. package/templates/.claude/skills/sync-apple-mail/scripts/sync.mjs +3 -3
  108. package/templates/.claude/skills/sync-teams/SKILL.md +43 -43
  109. package/templates/.claude/skills/sync-teams/scripts/idb-reader.mjs +22 -21
  110. package/templates/.claude/skills/sync-teams/scripts/leveldb-reader.mjs +4 -4
  111. package/templates/.claude/skills/sync-teams/scripts/sync.mjs +4 -4
  112. package/templates/.claude/skills/upstream-instructions/SKILL.md +39 -40
  113. package/templates/.claude/skills/upstream-instructions/references/examples.md +2 -2
  114. package/templates/CLAUDE.md +40 -37
@@ -1,7 +1,7 @@
1
1
  # Priority Templates
2
2
 
3
- Priorities are **never auto-created** by `extract-entities`. They are set
4
- deliberately by the user. This template is for manual creation only
3
+ `extract-entities` **never auto-creates** Priorities. The user sets them
4
+ deliberately. Use this template only for a note you write by hand.
5
5
  `extract-entities` and `anarlog-process` only **link to** and **update progress
6
6
  on** existing notes.
7
7
 
@@ -2,12 +2,13 @@
2
2
  /**
3
3
  * Manage graph_processed state for entity extraction.
4
4
  *
5
- * Tracks which source files (synced emails and calendar events) have already
6
- * been processed by the extract-entities skill. The `check` command lists files
7
- * that are new or changed since their last recorded hash; `update` marks files
8
- * as processed by storing their current SHA-256 hash.
5
+ * The script tracks which source files the extract-entities skill already
6
+ * processed. Those files are synced emails and calendar events. The `check`
7
+ * command lists files that are new or changed since their last recorded hash.
8
+ * The `update` command marks files as processed. It stores the current
9
+ * SHA-256 hash of each file.
9
10
  *
10
- * State is persisted as a TSV file at
11
+ * The script keeps the state in a TSV file at
11
12
  * ~/.cache/fit/outpost/state/graph_processed (path<TAB>hash).
12
13
  */
13
14
 
@@ -43,7 +44,7 @@ const SOURCE_DIRS = [
43
44
  join(HOME, ".cache/fit/outpost/teams_chat"),
44
45
  ];
45
46
 
46
- /** Compute SHA-256 hash of a file. */
47
+ /** Compute the SHA-256 hash of a file. */
47
48
  function fileHash(filePath) {
48
49
  const data = readFileSync(filePath);
49
50
  return createHash("sha256").update(data).digest("hex");
@@ -74,7 +75,7 @@ function saveState(state) {
74
75
  writeFileSync(STATE_FILE, text);
75
76
  }
76
77
 
77
- /** Find source files that are new or have changed since last processing. */
78
+ /** Find source files that are new or changed since the last run. */
78
79
  function check() {
79
80
  const state = loadState();
80
81
  const newFiles = [];
@@ -97,7 +98,7 @@ function check() {
97
98
  return newFiles.length;
98
99
  }
99
100
 
100
- /** Mark files as processed by updating their hashes in state. */
101
+ /** Mark files as processed. Update their hashes in the state file. */
101
102
  function update(filePaths) {
102
103
  const state = loadState();
103
104
  for (const fp of filePaths) {
@@ -1,13 +1,13 @@
1
1
  ---
2
2
  name: meeting-prep
3
- description: Prepare for meetings by gathering context from the knowledge base and calendar. Use when the user asks to prep for a meeting or wants a briefing on upcoming meetings. Creates personalized briefings with attendee history, open items, and suggested talking points.
3
+ description: Prepare for meetings. Gather context from the knowledge base and calendar. Use when the user asks to prep for a meeting or wants a briefing on upcoming meetings. Creates personalized briefings with attendee history, open items, and suggested talking points.
4
4
  ---
5
5
 
6
6
  # Meeting Prep
7
7
 
8
- Help the user prepare for meetings by gathering context from the knowledge base
9
- and calendar. Creates personalized briefing documents with attendee history,
10
- open items, and suggested talking points.
8
+ Help the user prepare for meetings. Gather context from the knowledge base and
9
+ calendar. Create personalized briefing documents with attendee history, open
10
+ items, and suggested talking points.
11
11
 
12
12
  ## Trigger
13
13
 
@@ -17,7 +17,7 @@ meetings.
17
17
  ## Prerequisites
18
18
 
19
19
  - Calendar data synced in `~/.cache/fit/outpost/apple_calendar/`
20
- - Knowledge base populated (from `extract-entities` skill)
20
+ - A populated knowledge base (from the `extract-entities` skill)
21
21
 
22
22
  ## Inputs
23
23
 
@@ -25,11 +25,11 @@ meetings.
25
25
  - `Knowledge/People/*.md` — attendee context
26
26
  - `Knowledge/Organizations/*.md` — company context
27
27
  - `Knowledge/Projects/*.md` — project context
28
- - `Knowledge/Priorities/*.md` — active priorities and strategic context for
29
- framing
28
+ - `Knowledge/Priorities/*.md` — active priorities and strategic context to
29
+ frame the brief
30
30
  - `Knowledge/Candidates/*/brief.md` — candidate context (for interview meetings)
31
- - `Knowledge/Roles/*.md` — role/requisition context (for interview meetings);
32
- check the `**Status:**` field to distinguish active from historical reqs
31
+ - `Knowledge/Roles/*.md` — role/requisition context (for interview meetings).
32
+ Check the `**Status:**` field to distinguish active from historical reqs
33
33
 
34
34
  ## Outputs
35
35
 
@@ -39,7 +39,7 @@ meetings.
39
39
 
40
40
  ## Critical: Always Look Up Context First
41
41
 
42
- **BEFORE creating any meeting brief, you MUST look up the attendees in the
42
+ **BEFORE you create any meeting brief, you MUST look up the attendees in the
43
43
  knowledge base.**
44
44
 
45
45
  When the user asks to prep for a meeting:
@@ -48,23 +48,23 @@ When the user asks to prep for a meeting:
48
48
  2. **SEARCH** — Look up each attendee: `rg -l "Attendee Name" Knowledge/`
49
49
  3. **READ** — Read their notes: `cat "Knowledge/People/Attendee Name.md"`
50
50
  4. **UNDERSTAND** — Extract role, organization, history, open items
51
- 5. **THEN BRIEF** — Create the meeting brief using this context
51
+ 5. **THEN BRIEF** — Create the meeting brief with this context
52
52
 
53
53
  ## Key Principles
54
54
 
55
- **Ask, don't guess:**
55
+ **Ask. Do not guess:**
56
56
 
57
57
  - If unclear which meeting, ASK
58
58
  - If multiple upcoming meetings, offer choices
59
59
  - **WRONG:** "Here's a generic meeting prep template"
60
60
  - **RIGHT:** "I see meetings with Sarah (2pm) and John (4pm). Which one?"
61
61
 
62
- **Be thorough, not generic:**
62
+ **Be thorough. Do not be generic:**
63
63
 
64
- - Include specific history, open items, and context from knowledge base
64
+ - Include specific history, open items, and context from the knowledge base
65
65
  - Reference actual past interactions and commitments
66
66
 
67
- ## Processing Flow
67
+ ## Process Flow
68
68
 
69
69
  ### Step 1: Identify the Meeting
70
70
 
@@ -82,7 +82,7 @@ If "prep me for my next meeting":
82
82
 
83
83
  - Query upcoming events with `--upcoming 2h`
84
84
  - Find the next meeting with external attendees
85
- - Confirm with user if unclear
85
+ - Confirm with the user if unclear
86
86
 
87
87
  ### Step 2: Parse Calendar Event
88
88
 
@@ -102,9 +102,9 @@ rg -l "attendee_name" Knowledge/Projects/
102
102
 
103
103
  Extract: role/title, company, key facts, previous interactions, open items.
104
104
 
105
- Also check `Knowledge/Priorities/` for context relevant
106
- to the meeting topic — e.g. if the meeting is about hiring, surface the relevant
107
- hiring Priority's status and progress.
105
+ Also check `Knowledge/Priorities/` for context relevant to the meeting topic.
106
+ For example, if the meeting is about hiring, surface the relevant hiring
107
+ Priority's status and progress.
108
108
 
109
109
  ### Step 4: Create Meeting Brief
110
110
 
@@ -134,19 +134,20 @@ Suggested Talking Points
134
134
  **Guidelines:**
135
135
 
136
136
  - Use `[[Name]]` wiki-link syntax for cross-references
137
- - Keep "About" section to 2-3 sentences max
137
+ - Keep the "About" section to 2-3 sentences max
138
138
  - History: reverse chronological, 3-5 most relevant items
139
- - Talking points: concrete, not generic
139
+ - Talking points: concrete. Avoid generic suggestions
140
140
  - If no notes exist for a person, mention that and offer to create one
141
141
 
142
142
  ### Interview Meeting Context
143
143
 
144
- When preparing for interview meetings (title contains "Interview", "Screening",
145
- "Decomposition", "Panel", or a candidate name from `Knowledge/Candidates/`):
144
+ When you prepare for interview meetings (title contains "Interview",
145
+ "Screening", "Decomposition", "Panel", or a candidate name from
146
+ `Knowledge/Candidates/`):
146
147
 
147
148
  1. **Read the candidate brief:** `Knowledge/Candidates/{Name}/brief.md`
148
149
  2. **Read the Role file:** Look up the `Req` field and read the corresponding
149
- `Knowledge/Roles/*.md` file; check the `**Status:**` field for context.
150
+ `Knowledge/Roles/*.md` file. Check the `**Status:**` field for context.
150
151
  3. **Include in the briefing:**
151
152
  - Candidate's current status, skills, and screening recommendation
152
153
  - Role context: hiring manager, domain lead, remaining positions
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  name: organize-files
3
- description: Organize, tidy up, and find files in ~/Desktop/ and ~/Downloads/. Use when the user asks to find, organize, clean up, or tidy files on their Mac. Always previews changes before acting and never deletes without explicit confirmation. Extracts entities from document files using the extract-entities skill.
3
+ description: Organize, tidy up, and find files in ~/Desktop/ and ~/Downloads/. Use when the user asks to find, organize, clean up, or tidy files on their Mac. Always previews changes before it acts. Never deletes without explicit confirmation. Extracts entities from document files with the extract-entities skill.
4
4
  compatibility: Requires macOS filesystem access
5
5
  ---
6
6
 
7
7
  # Organize Files
8
8
 
9
9
  Organize, tidy up, and find files in `~/Desktop/` and `~/Downloads/`. Always
10
- previews changes before acting and never deletes without explicit confirmation.
10
+ preview changes before you act. Never delete without explicit confirmation.
11
11
 
12
- After organizing, extract entities from document files by invoking the
12
+ After you organize, extract entities from document files. Invoke the
13
13
  **`extract-entities`** skill.
14
14
 
15
15
  ## Trigger
@@ -29,7 +29,7 @@ Run when the user asks to find, organize, clean up, or tidy files on their Mac.
29
29
 
30
30
  - Organized files moved to logical subdirectories within `~/Desktop/` and
31
31
  `~/Downloads/`
32
- - Entity extraction triggered on document files via the **`extract-entities`**
32
+ - Entity extraction triggered on document files with the **`extract-entities`**
33
33
  skill
34
34
  - Summary of actions taken
35
35
 
@@ -42,22 +42,23 @@ Run when the user asks to find, organize, clean up, or tidy files on their Mac.
42
42
  2. **Organize files** — Move files into logical subfolders
43
43
  3. **Tidy up** — Clean up cluttered `~/Desktop/` and `~/Downloads/`
44
44
  4. **Create structure** — Set up folder hierarchies
45
- 5. **Extract entities** — After organizing, invoke the **`extract-entities`**
46
- skill on document files to populate the knowledge graph
45
+ 5. **Extract entities** — After you organize, invoke the
46
+ **`extract-entities`** skill on document files to populate the knowledge
47
+ graph
47
48
 
48
49
  ## Key Principles
49
50
 
50
- **Always preview before acting:**
51
+ **Always preview before you act:**
51
52
 
52
- - Show what files will be affected BEFORE moving/deleting
53
+ - Show which files change BEFORE you move or delete them
53
54
  - List proposed changes and ask for confirmation
54
55
 
55
56
  **Be conservative with destructive operations:**
56
57
 
57
58
  - Never delete without explicit confirmation
58
- - Prefer moving to a "to-review" folder over deleting
59
+ - Move to a "to-review" folder rather than delete
59
60
 
60
- ## Summarizing Contents
61
+ ## Summarize the Contents
61
62
 
62
63
  Get an overview of both directories:
63
64
 
@@ -65,7 +66,7 @@ Get an overview of both directories:
65
66
  node scripts/summarize.mjs
66
67
  ```
67
68
 
68
- ## Finding Files
69
+ ## Find Files
69
70
 
70
71
  ```bash
71
72
  find ~/Downloads -maxdepth 1 -name "*.pdf" -type f
@@ -76,7 +77,7 @@ find ~/Downloads -maxdepth 1 -type f -mtime +30 # older than 30 days
76
77
  find ~/Desktop -maxdepth 1 \( -name "Screenshot*" -o -name "Screen Shot*" \)
77
78
  ```
78
79
 
79
- ## Organizing by File Type
80
+ ## Organize by File Type
80
81
 
81
82
  Organize a directory into type-based subdirectories (Documents, Images,
82
83
  Archives, Installers, Screenshots):
@@ -86,13 +87,13 @@ node scripts/organize-by-type.mjs ~/Downloads
86
87
  node scripts/organize-by-type.mjs ~/Desktop
87
88
  ```
88
89
 
89
- The script creates subdirectories and moves matching files. It does NOT delete
90
- anything.
90
+ The script creates subdirectories and moves the files that match. It does NOT
91
+ delete anything.
91
92
 
92
93
  ## Entity Extraction
93
94
 
94
- After organizing files, identify document files that may contain entity
95
- information (people, organizations, projects, topics) and invoke the
95
+ After you organize the files, identify document files that may contain entity
96
+ information (people, organizations, projects, topics). Then invoke the
96
97
  **`extract-entities`** skill to process them.
97
98
 
98
99
  ### Which files to send for extraction
@@ -103,8 +104,8 @@ information (people, organizations, projects, topics) and invoke the
103
104
 
104
105
  ### How to invoke
105
106
 
106
- After organizing, collect the paths of document files and invoke the
107
- **`extract-entities`** skill, passing the file paths as ad-hoc file inputs.
107
+ After you organize, collect the paths of the document files. Then invoke the
108
+ **`extract-entities`** skill and pass the file paths as ad-hoc file inputs.
108
109
 
109
110
  ## Output Format
110
111
 
@@ -141,10 +142,11 @@ Entity extraction: invoked extract-entities on 12 document files
141
142
 
142
143
  ## Safety Rules
143
144
 
144
- 1. **Never delete without permission** — "cleanup" means organize, not delete
145
+ 1. **Never delete without permission** — "cleanup" means organize. It does not
146
+ mean delete
145
147
  2. **Don't touch system folders** — /System, /Library, /Applications
146
- 3. **Don't touch hidden files** — files starting with `.` unless asked
148
+ 3. **Don't touch hidden files** — files that start with `.` unless asked
147
149
  4. **Limit scope** — only operate on `~/Desktop/` and `~/Downloads/`
148
- 5. **Limit depth** — use `-maxdepth 1` unless user wants recursive
149
- 6. **Show before doing** — always preview first
150
+ 5. **Limit depth** — use `-maxdepth 1` unless the user wants recursive
151
+ 6. **Show before you act** — always preview first
150
152
  7. **Quote paths** — handle spaces: `"$HOME/My Documents"`
@@ -2,10 +2,11 @@
2
2
  /**
3
3
  * Organize files in a directory by type into subdirectories.
4
4
  *
5
- * Scans the top level of the given directory and moves files into category
6
- * subdirectories: Screenshots, Documents, Images, Archives, and Installers.
7
- * Categories are determined by file extension and name prefix. Files that do
8
- * not match any category are left in place. Does NOT delete anything.
5
+ * The script scans the top level of the given directory. It moves files into
6
+ * category subdirectories: Screenshots, Documents, Images, Archives, and
7
+ * Installers. The file extension and the name prefix set the category. The
8
+ * script leaves a file in place if it matches no category. It does NOT delete
9
+ * anything.
9
10
  */
10
11
 
11
12
  import {
@@ -2,10 +2,10 @@
2
2
  /**
3
3
  * Summarize the contents of ~/Desktop/ and ~/Downloads/.
4
4
  *
5
- * Counts top-level files in both directories by type (Screenshots, PDFs,
6
- * Images, Documents, Archives, Installers, Other) and prints a human-readable
7
- * table for each. Used by the organize-files skill to preview directory
8
- * contents before organizing.
5
+ * The script counts top-level files in both directories by type
6
+ * (Screenshots, PDFs, Images, Documents, Archives, Installers, Other). It
7
+ * prints a human-readable table for each directory. The organize-files skill
8
+ * uses this script to preview directory contents before it organizes them.
9
9
  */
10
10
 
11
11
  import { existsSync, readdirSync, statSync } from "node:fs";
@@ -1,14 +1,14 @@
1
1
  ---
2
2
  name: person-identify
3
- description: Look up the current user's identity (real name, company, job title, department, email, employee ID, and manager) from the corporate directory via LDAP, authenticated with the existing Kerberos ticket. Use to establish who the knowledge base belongs to, when CLAUDE.md needs the user's identity, or when the user asks "who am I" / for their own directory record. To look up someone *other* than the current user, use the sibling `person-lookup` skill instead.
3
+ description: Look up the current user's identity (real name, company, job title, department, email, employee ID, and manager) from the corporate directory through LDAP. The bind uses the existing Kerberos ticket. Use to establish who the knowledge base belongs to, when CLAUDE.md needs the user's identity, or when the user asks "who am I" / for their own directory record. To look up someone *other* than the current user, use the sibling `person-lookup` skill instead.
4
4
  ---
5
5
 
6
6
  # Person Identify
7
7
 
8
8
  Resolve the current user's identity from the corporate Active Directory over
9
9
  LDAP. This is the canonical way to establish **who the knowledge base belongs
10
- to** replacing any static identity file. Results reflect the live directory,
11
- so a job change or reorg is picked up automatically.
10
+ to**. It replaces any static identity file. Results reflect the live
11
+ directory, so the skill picks up a job change or a reorg automatically.
12
12
 
13
13
  ## Trigger
14
14
 
@@ -20,12 +20,12 @@ so a job change or reorg is picked up automatically.
20
20
 
21
21
  - A valid **Kerberos ticket** for the user (`klist` shows a principal).
22
22
  If absent, get one with `kinit <user>@<REALM>`.
23
- - Network reachability to a domain controller (on-site or via VPN).
24
- - `ldapsearch` and `dig` — both ship with macOS; nothing to install.
23
+ - Network access to a domain controller (on-site or through VPN).
24
+ - `ldapsearch` and `dig` — both ship with macOS. You install nothing.
25
25
 
26
- Nothing is hardcoded: the username, realm, base DN, and domain controller are
27
- all derived at runtime from the ticket and DNS. No password is ever entered —
28
- the bind uses SASL/GSSAPI against the existing ticket.
26
+ Nothing is hardcoded. The script derives the username, realm, base DN, and
27
+ domain controller at runtime from the ticket and DNS. You never enter a
28
+ password. The bind uses SASL/GSSAPI against the existing ticket.
29
29
 
30
30
  ## Usage
31
31
 
@@ -38,10 +38,11 @@ writes the result to the identity cache (below).
38
38
 
39
39
  ## Identity cache
40
40
 
41
- The script writes `~/.cache/fit/outpost/state/identity.md` the **canonical
42
- identity source** for the rest of the knowledge base, replacing the old static
43
- `USER.md`. It is auto-generated markdown with `Name`, `Email`, and `Domain`
44
- fields (plus title, department, company, employee ID, office, and manager):
41
+ The script writes `~/.cache/fit/outpost/state/identity.md`. That file is the
42
+ **canonical identity source** for the rest of the knowledge base. It replaces
43
+ the old static `USER.md`. It is auto-generated markdown with `Name`, `Email`,
44
+ and `Domain` fields (plus title, department, company, employee ID, office, and
45
+ manager):
45
46
 
46
47
  ```markdown
47
48
  - **Name:** Jane Doe
@@ -51,9 +52,9 @@ fields (plus title, department, company, employee ID, office, and manager):
51
52
 
52
53
  Other skills (e.g. `extract-entities`, `anarlog-process`, `req-track`,
53
54
  `req-workday`, `candidate-report`, `sync-teams`) read this file for the user's
54
- name/email/domain for self-exclusion and author attribution. They run this
55
- skill first if the cache is missing or stale. Never hand-edit the cache; re-run
56
- the skill to refresh it.
55
+ name/email/domain. They use it for self-exclusion and author attribution. They
56
+ run this skill first if the cache is missing or stale. Never hand-edit the
57
+ cache. Run the skill again to refresh it.
57
58
 
58
59
  ## How it works
59
60
 
@@ -76,8 +77,9 @@ ldapsearch -Y GSSAPI -LLL -o ldif-wrap=no -H "ldap://$dc" -b "$base" \
76
77
  "(sAMAccountName=$user)" displayName company title department employeeID mail manager
77
78
  ```
78
79
 
79
- The `manager` attribute is a DN that may live in another domain, so the script
80
- resolves it against the **Global Catalog** (port 3268), which is forest-wide.
80
+ The `manager` attribute is a DN that may live in another domain. So the script
81
+ resolves it against the **Global Catalog** (port 3268). The Global Catalog is
82
+ forest-wide.
81
83
 
82
84
  ## Output
83
85
 
@@ -96,9 +98,9 @@ Key attributes returned (names per Active Directory schema):
96
98
 
97
99
  ## Notes
98
100
 
99
- - To look up **someone else**, use the sibling `person-lookup` skill it takes
100
- free-text input (email or name), searches the Global Catalog forest-wide
101
- (`ldap://$dc:3268 -b ''`), handles multiple matches, and does **not** touch
101
+ - To look up **someone else**, use the sibling `person-lookup` skill. It takes
102
+ free-text input (email or name). It searches the Global Catalog forest-wide
103
+ (`ldap://$dc:3268 -b ''`). It handles multiple matches. It does **not** touch
102
104
  the identity cache.
103
- - Not Active Directory? The same `ldapsearch -Y GSSAPI` shape works against any
104
- Kerberos-backed LDAP directory; only the attribute names differ.
105
+ - Active Directory is not required. The same `ldapsearch -Y GSSAPI` shape works
106
+ against any Kerberos-backed LDAP directory. Only the attribute names differ.
@@ -1,18 +1,18 @@
1
1
  #!/usr/bin/env bash
2
- # Identify the current user from the corporate directory (Active Directory over LDAP)
3
- # and cache the result at ~/.cache/fit/outpost/state/identity.md for other skills.
2
+ # Identify the current user from the corporate directory (Active Directory over LDAP).
3
+ # Cache the result at ~/.cache/fit/outpost/state/identity.md for other skills.
4
4
  #
5
- # Generic by design: nothing is hardcoded. The user, realm, and base DN are all
6
- # derived from the existing Kerberos ticket, and the domain controller is found
7
- # via DNS SRV records. Works for any AD domain, on or off VPN, as long as a
8
- # Kerberos ticket exists and a DC is reachable.
5
+ # The script is generic. Nothing is hardcoded. It derives the user, the realm,
6
+ # and the base DN from the existing Kerberos ticket. It finds the domain
7
+ # controller with DNS SRV records. It works for any AD domain, on or off VPN.
8
+ # It needs a Kerberos ticket and a reachable DC.
9
9
  #
10
- # Auth uses SASL/GSSAPI against the existing ticket no password is ever entered.
10
+ # Auth uses SASL/GSSAPI against the existing ticket. You never enter a password.
11
11
  set -u
12
12
 
13
13
  CACHE="$HOME/.cache/fit/outpost/state/identity.md"
14
14
 
15
- # 1. Who are we? Take the principal from the Kerberos ticket: USER@REALM.EXAMPLE.COM
15
+ # 1. Take the user principal from the Kerberos ticket: USER@REALM.EXAMPLE.COM
16
16
  princ=$(klist 2>/dev/null | sed -n 's/.*[Pp]rincipal: *//p' | head -1)
17
17
  if [ -z "$princ" ]; then
18
18
  echo "No Kerberos ticket found. Get one first, e.g.: kinit <user>@<REALM>" >&2
@@ -25,11 +25,11 @@ realm=${princ#*@}
25
25
  base=$(printf '%s' "$realm" | awk -F. '{for(i=1;i<=NF;i++) printf "%sDC=%s",(i>1?",":""),$i}')
26
26
  dom=$(printf '%s' "$realm" | tr '[:upper:]' '[:lower:]')
27
27
 
28
- # 3. Find a reachable domain controller via DNS SRV (msdcs first, then plain ldap)
28
+ # 3. Find a reachable domain controller with DNS SRV (msdcs first, then plain ldap)
29
29
  dc=$(dig +short SRV "_ldap._tcp.dc._msdcs.$dom" | awk 'NR==1{print $4}' | sed 's/\.$//')
30
30
  [ -z "$dc" ] && dc=$(dig +short SRV "_ldap._tcp.$dom" | awk 'NR==1{print $4}' | sed 's/\.$//')
31
31
  if [ -z "$dc" ]; then
32
- echo "Could not find a domain controller for $dom via DNS SRV." >&2
32
+ echo "Could not find a domain controller for $dom with DNS SRV." >&2
33
33
  exit 1
34
34
  fi
35
35
 
@@ -49,13 +49,13 @@ company=$(field company); title=$(field title); dept=$(field department)
49
49
  empid=$(field employeeID); mail=$(field mail)
50
50
  office=$(field physicalDeliveryOfficeName)
51
51
 
52
- # Real name from given+surname, falling back to displayName. Domain from the email.
52
+ # Real name from given+surname. Fall back to displayName. Domain from the email.
53
53
  name="$gn $sn"; name=$(printf '%s' "$name" | sed 's/^ *//;s/ *$//')
54
54
  [ -z "$name" ] && name="$disp"
55
55
  domain=$(printf '%s' "$mail" | sed 's/.*@//' | tr '[:upper:]' '[:lower:]')
56
56
 
57
- # 5. Resolve the manager DN to a name, if present. Manager may live in another
58
- # domain, so query the Global Catalog (port 3268), which is forest-wide.
57
+ # 5. Resolve the manager DN to a name, if present. The manager may live in
58
+ # another domain. Query the Global Catalog (port 3268). It is forest-wide.
59
59
  mgr=$(field manager)
60
60
  mgr_name=""
61
61
  if [ -n "$mgr" ]; then
@@ -63,14 +63,14 @@ if [ -n "$mgr" ]; then
63
63
  displayName 2>/dev/null | sed -n 's/^displayName: //p' | head -1)
64
64
  fi
65
65
 
66
- # 6. Write the cache. Same Name/Email/Domain shape other skills already parse,
67
- # plus extra fields. Auto-generated never hand-edited.
66
+ # 6. Write the cache. It keeps the Name/Email/Domain shape other skills already
67
+ # parse, plus extra fields. The script generates it. Never edit it by hand.
68
68
  mkdir -p "$(dirname "$CACHE")"
69
69
  {
70
70
  echo "# User Identity"
71
71
  echo
72
- echo "Auto-generated by the \`person-identify\` skill from the corporate directory."
73
- echo "Do not edit by hand re-run the skill to refresh."
72
+ echo "The \`person-identify\` skill generates this file from the corporate directory."
73
+ echo "Do not edit it by hand. Run the skill again to refresh it."
74
74
  echo
75
75
  echo "- **Name:** $name"
76
76
  echo "- **Email:** $mail"
@@ -1,37 +1,37 @@
1
1
  ---
2
2
  name: person-lookup
3
- description: Look up ANY person in the corporate directory from free-text input (an email address, or a first / last / full name) and return their record real name, title, department, company, email, employee ID, office, and manager. Searches the Global Catalog forest-wide via LDAP, authenticated with the existing Kerberos ticket. Use when the user asks "who is X", needs someone's title / department / manager / email, or wants to disambiguate a name. For the *current* user's own identity, use the sibling `person-identify` skill instead.
3
+ description: Look up ANY person in the corporate directory from free-text input (an email address, or a first / last / full name) and return their record: real name, title, department, company, email, employee ID, office, and manager. Searches the Global Catalog forest-wide through LDAP. The bind uses the existing Kerberos ticket. Use when the user asks "who is X", needs someone's title / department / manager / email, or wants to disambiguate a name. For the *current* user's own identity, use the sibling `person-identify` skill instead.
4
4
  ---
5
5
 
6
6
  # Person Lookup
7
7
 
8
8
  Resolve **any** person in the corporate Active Directory from natural-language
9
- input an email, a login, or any part of a name. The sibling of
10
- `person-identify`, but aimed at *other* people: it searches the **Global
11
- Catalog** (forest-wide), handles multiple matches, flags external contacts and
12
- vendor accounts, and unlike `person-identify` never writes the identity
13
- cache. It's a read-only, throwaway lookup.
9
+ input. The input is an email, a login, or any part of a name. This skill is the
10
+ sibling of `person-identify`, and it aims at *other* people. It searches the
11
+ **Global Catalog** (forest-wide). It handles multiple matches. It flags
12
+ external contacts and vendor accounts. Unlike `person-identify`, it never
13
+ writes the identity cache. It is a read-only, throwaway lookup.
14
14
 
15
15
  ## Trigger
16
16
 
17
17
  - The user asks "who is <name>", or for someone's title, department, manager,
18
18
  email, or employee ID.
19
- - A name is ambiguous and needs disambiguating against the directory.
19
+ - A name is ambiguous, and you must disambiguate it against the directory.
20
20
  - Another skill needs to resolve a person who is **not** the current user.
21
21
 
22
- For the current user's own record (and to populate the identity cache that other
23
- skills read), use `person-identify` instead.
22
+ For the current user's own record, use `person-identify` instead. It also
23
+ populates the identity cache that other skills read.
24
24
 
25
25
  ## Prerequisites
26
26
 
27
- - A valid **Kerberos ticket** (`klist` shows a principal). If absent:
27
+ - A valid **Kerberos ticket** (`klist` shows a principal). If absent, run
28
28
  `kinit <user>@<REALM>`.
29
- - Network reachability to a domain controller (on-site or VPN).
30
- - `ldapsearch` and `dig` — both ship with macOS; nothing to install.
29
+ - Network access to a domain controller (on-site or VPN).
30
+ - `ldapsearch` and `dig` — both ship with macOS. You install nothing.
31
31
 
32
- Nothing is hardcoded: the realm and a domain controller are derived at runtime
33
- from the ticket and DNS. The bind uses SASL/GSSAPI against the existing ticket —
34
- no password is ever entered.
32
+ Nothing is hardcoded. The script derives the realm and a domain controller at
33
+ runtime from the ticket and DNS. The bind uses SASL/GSSAPI against the existing
34
+ ticket. You never enter a password.
35
35
 
36
36
  ## Usage
37
37
 
@@ -44,20 +44,20 @@ The argument is free text: an email, a full name, or just a surname.
44
44
 
45
45
  ## How it works
46
46
 
47
- 1. **Derive a domain controller** from the Kerberos principal's realm via DNS
48
- SRV (same bootstrap as `person-identify`).
47
+ 1. **Derive a domain controller** from the Kerberos principal's realm through
48
+ DNS SRV (same bootstrap as `person-identify`).
49
49
  2. **Search the Global Catalog** (`ldap://$dc:3268`, base `""`). The GC spans
50
- *every* domain in the forest, so a colleague in another region is found from
51
- your own ticket a plain domain-scoped search would miss them. It also
50
+ *every* domain in the forest. So you find a colleague in another region from
51
+ your own ticket. A plain domain-scoped search would miss them. It also
52
52
  returns the common attributes (title, department, mail, manager), so one
53
53
  query is enough.
54
- 3. **Match with ANR** (Ambiguous Name Resolution): the `(anr=<input>)` filter
54
+ 3. **Match with ANR** (Ambiguous Name Resolution). The `(anr=<input>)` filter
55
55
  matches an email, a login, or any name part in one shot. If ANR finds
56
56
  nothing, fall back to a substring search on `mail` / `displayName` /
57
57
  `proxyAddresses`.
58
58
  4. **Resolve per match.** For a single hit, print the full record and resolve
59
59
  the `manager` DN to a name. For several hits, print a compact
60
- disambiguation list and suggest narrowing by email.
60
+ disambiguation list and suggest a narrower search by email.
61
61
 
62
62
  ## Output
63
63
 
@@ -80,18 +80,17 @@ The argument is free text: an email, a full name, or just a surname.
80
80
  external **Contact** object (e.g. a vendor email alias), returns several
81
81
  entries. The **Type** column distinguishes an internal **Employee** from an
82
82
  external **Contact** (standard `objectClass=contact`). Some directories also
83
- park external/vendor *user* accounts under a dedicated OU; set
83
+ park external/vendor *user* accounts under a dedicated OU. Set
84
84
  `VENDOR_OU_PATTERN` at the top of `scripts/lookup.sh` to your directory's OU
85
- substring (e.g. `OU=Contractors`) to flag those too. It is empty by default,
86
- since the OU convention is organization-specific. Narrow with an email for an
87
- exact hit.
85
+ substring (e.g. `OU=Contractors`). This flags those accounts too. It is empty
86
+ by default, since the OU convention is organization-specific. Narrow with an
87
+ email for an exact hit.
88
88
  - **Silent partial results.** Under load the directory occasionally returns an
89
89
  entry's DN with no attributes (exit 0, no error). Every attribute fetch
90
90
  retries with backoff, so a throttled response never masquerades as a person
91
91
  with a blank title or email.
92
92
  - **No cache.** This skill prints and exits. It never touches
93
- `~/.cache/fit/outpost/state/identity.md` that file is owned solely by
94
- `person-identify`.
93
+ `~/.cache/fit/outpost/state/identity.md`. `person-identify` owns that file
94
+ alone.
95
95
  - **Ethics.** This reads objective, work-relevant directory data only, in line
96
- with the knowledge base's integrity rules. It is not a tool for building
97
- dossiers.
96
+ with the knowledge base's integrity rules. Never use it to build dossiers.