@sbains2/lifeos 0.2.2 → 0.3.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.
- package/bin/lifeos.js +68 -5
- package/package.json +2 -3
- package/src/commands/context.js +40 -0
- package/src/commands/schedule.js +748 -0
- package/src/context.js +203 -0
- package/src/doctor.js +1 -1
- package/src/index.js +4 -4
- package/templates/packs/job-search/README.md +82 -0
- package/templates/packs/job-search/SCOPE.template.md +156 -0
- package/templates/packs/job-search/bridge.md +89 -0
- package/templates/packs/job-search/council_playbook.md +117 -0
- package/templates/packs/job-search/scout_prompt.template.md +111 -0
- package/templates/prompts/weekly_digest.md +47 -0
package/bin/lifeos.js
CHANGED
|
@@ -1,15 +1,52 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { resolve } from 'node:path';
|
|
2
3
|
import { run } from '../src/index.js';
|
|
3
4
|
import { runDoctorCommand } from '../src/commands/doctor.js';
|
|
4
5
|
import { runBrainCommand } from '../src/commands/brain.js';
|
|
6
|
+
import { runContext } from '../src/commands/context.js';
|
|
7
|
+
import { runSchedule } from '../src/commands/schedule.js';
|
|
5
8
|
|
|
6
9
|
// Parse argv: optional subcommand + positional target dir + flags
|
|
7
10
|
// Usage:
|
|
8
11
|
// lifeos [target-dir] [--minimal] # wizard (default)
|
|
9
12
|
// lifeos doctor [target-dir] [--json] [--quiet] [--strict]
|
|
10
13
|
const args = process.argv.slice(2);
|
|
11
|
-
|
|
12
|
-
|
|
14
|
+
|
|
15
|
+
// `schedule` dispatches EARLY, before the global flag/positional parsing:
|
|
16
|
+
// its value flags (--prompt <file>, --every 1d, ...) would otherwise be
|
|
17
|
+
// misread as positional args and corrupt targetDir resolution. It also owns
|
|
18
|
+
// its own --help (so `lifeos schedule --help` prints the schedule help, not
|
|
19
|
+
// the global help). schedule operates on cwd — no positional target-dir arg.
|
|
20
|
+
if (args[0] === 'schedule') {
|
|
21
|
+
const exitCode = await runSchedule(resolve(process.cwd()), args.slice(1));
|
|
22
|
+
process.exit(exitCode);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// `context`'s --quadrant takes a value; extract flag + value before the
|
|
26
|
+
// global parser treats the value as a positional target dir.
|
|
27
|
+
let quadrantId = null;
|
|
28
|
+
let filteredArgs = args;
|
|
29
|
+
const qIdx = args.indexOf('--quadrant');
|
|
30
|
+
if (qIdx !== -1) {
|
|
31
|
+
const qVal = args[qIdx + 1];
|
|
32
|
+
if (qVal !== undefined && !qVal.startsWith('-')) {
|
|
33
|
+
quadrantId = qVal;
|
|
34
|
+
filteredArgs = args.filter((_, i) => i !== qIdx && i !== qIdx + 1);
|
|
35
|
+
} else {
|
|
36
|
+
// dangling `--quadrant` (no value, or followed by another flag): drop only
|
|
37
|
+
// the flag so a trailing --json/--help isn't swallowed as its value.
|
|
38
|
+
filteredArgs = args.filter((_, i) => i !== qIdx);
|
|
39
|
+
}
|
|
40
|
+
} else {
|
|
41
|
+
const qEq = args.find((a) => a.startsWith('--quadrant='));
|
|
42
|
+
if (qEq) {
|
|
43
|
+
quadrantId = qEq.split('=')[1];
|
|
44
|
+
filteredArgs = args.filter((a) => a !== qEq);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const flags = new Set(filteredArgs.filter((a) => a.startsWith('--')));
|
|
49
|
+
const positional = filteredArgs.filter((a) => !a.startsWith('--') && !a.startsWith('-'));
|
|
13
50
|
|
|
14
51
|
if (flags.has('--help') || flags.has('-h') || positional[0] === 'help') {
|
|
15
52
|
console.log(`lifeos — scaffold and verify a personal LifeOS
|
|
@@ -17,6 +54,8 @@ if (flags.has('--help') || flags.has('-h') || positional[0] === 'help') {
|
|
|
17
54
|
Usage:
|
|
18
55
|
lifeos [target-dir] [options] # run the wizard
|
|
19
56
|
lifeos doctor [target-dir] [options] # verify scaffold health
|
|
57
|
+
lifeos context [target-dir] [options] # print the convening context block
|
|
58
|
+
lifeos schedule <add|list|remove> [options] # run agent prompts on a schedule
|
|
20
59
|
lifeos --help # show this
|
|
21
60
|
|
|
22
61
|
Wizard options:
|
|
@@ -37,6 +76,16 @@ Brain options:
|
|
|
37
76
|
--dry-run Show what would run without invoking graphify
|
|
38
77
|
--json Emit machine-readable JSON (for CI / scripting)
|
|
39
78
|
|
|
79
|
+
Context options:
|
|
80
|
+
--json Emit the same data as structured JSON (for runtimes / scripting)
|
|
81
|
+
--quadrant <id> Scope output to one quadrant (its council subset; foci stay global)
|
|
82
|
+
|
|
83
|
+
Schedule subcommands (run \`lifeos schedule --help\` for full details):
|
|
84
|
+
add <name> --prompt <file> (--every <n>{d|h} [--at HH:MM] | --cron "<expr>")
|
|
85
|
+
[--runtime claude-code|codex] [--dry-run]
|
|
86
|
+
list Show installed schedules
|
|
87
|
+
remove <name> Uninstall a schedule (launchd unload on macOS)
|
|
88
|
+
|
|
40
89
|
Note on brain: lifeos brain shells out to graphify (https://github.com/
|
|
41
90
|
safishamsi/graphify), an OSS knowledge-graph tool. Install once with
|
|
42
91
|
"pip install graphifyy && graphify install". Code parsing is 100% local;
|
|
@@ -53,14 +102,21 @@ Examples:
|
|
|
53
102
|
lifeos doctor # verify current dir
|
|
54
103
|
lifeos doctor ~/my-lifeos # verify ~/my-lifeos
|
|
55
104
|
lifeos doctor --json | jq '.summary' # programmatic check
|
|
105
|
+
lifeos context ~/my-lifeos # convening block to stdout (pipe-friendly)
|
|
106
|
+
lifeos context --quadrant job-search # scope to one quadrant's council
|
|
107
|
+
lifeos schedule add digest --prompt templates/prompts/weekly_digest.md --every 1d
|
|
56
108
|
`);
|
|
57
109
|
process.exit(0);
|
|
58
110
|
}
|
|
59
111
|
|
|
60
|
-
// Subcommand dispatch
|
|
61
|
-
const subcommand = ['doctor', 'brain', 'migrate'].includes(positional[0]) ? positional[0] : null;
|
|
112
|
+
// Subcommand dispatch (schedule handled earlier — see top of file)
|
|
113
|
+
const subcommand = ['doctor', 'brain', 'migrate', 'context'].includes(positional[0]) ? positional[0] : null;
|
|
62
114
|
const remainingPositional = subcommand ? positional.slice(1) : positional;
|
|
63
|
-
|
|
115
|
+
// BUGFIX 2026-05-01: always resolve to absolute path. If user passes '.' as
|
|
116
|
+
// the positional arg, downstream `path.join(targetDir, q.path)` collapses to a
|
|
117
|
+
// relative string ('Classes' instead of '/.../lifeos-demo-clean/Classes'),
|
|
118
|
+
// which breaks the validator's parent-writeable check (it walks up to '/').
|
|
119
|
+
const targetDir = resolve(remainingPositional[0] ?? process.cwd());
|
|
64
120
|
|
|
65
121
|
async function main() {
|
|
66
122
|
if (subcommand === 'doctor') {
|
|
@@ -79,6 +135,13 @@ async function main() {
|
|
|
79
135
|
});
|
|
80
136
|
process.exit(exitCode);
|
|
81
137
|
}
|
|
138
|
+
if (subcommand === 'context') {
|
|
139
|
+
const exitCode = await runContext(targetDir, {
|
|
140
|
+
json: flags.has('--json'),
|
|
141
|
+
quadrant: quadrantId,
|
|
142
|
+
});
|
|
143
|
+
process.exit(exitCode);
|
|
144
|
+
}
|
|
82
145
|
if (subcommand === 'migrate') {
|
|
83
146
|
console.error(`\nNot yet implemented: \`lifeos migrate\`. See docs/COMMANDS.md for the design.`);
|
|
84
147
|
process.exit(2);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sbains2/lifeos",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Scaffold a personal LifeOS — life-quadrant folders, a council of AI agent voices, and curated MCP wiring. Generates a working lifeos.config.json + .claude/agents/council/ in 2-4 minutes (or <60s with --minimal). BETA — early seed release.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -18,8 +18,7 @@
|
|
|
18
18
|
],
|
|
19
19
|
"scripts": {
|
|
20
20
|
"start": "node bin/lifeos.js",
|
|
21
|
-
"test": "node --test test/*.test.js"
|
|
22
|
-
"sync-templates": "rm -rf templates && cp -R ../templates ./templates"
|
|
21
|
+
"test": "node --test test/*.test.js"
|
|
23
22
|
},
|
|
24
23
|
"dependencies": {
|
|
25
24
|
"@inquirer/prompts": "^7.2.0",
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `lifeos context` subcommand orchestrator.
|
|
3
|
+
*
|
|
4
|
+
* Thin wrapper around src/context.js — handles flags, output formatting, and
|
|
5
|
+
* exit codes. The planning/rendering logic (rank foci/quadrants/council by
|
|
6
|
+
* priority) is pure and lives in context.js.
|
|
7
|
+
*
|
|
8
|
+
* Flags:
|
|
9
|
+
* --json Emit the same data as structured JSON (for runtimes / scripting)
|
|
10
|
+
* --quadrant <id> Scope output to one quadrant (its council subset + foci)
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { planContext, renderContextMarkdown } from '../context.js';
|
|
14
|
+
import { c } from '../utils/colors.js';
|
|
15
|
+
|
|
16
|
+
export async function runContext(targetDir, flags = {}) {
|
|
17
|
+
const { json = false, quadrant = null } = flags;
|
|
18
|
+
|
|
19
|
+
const plan = planContext(targetDir, { quadrant });
|
|
20
|
+
if (!plan.ok) {
|
|
21
|
+
if (json) {
|
|
22
|
+
console.log(JSON.stringify(plan, null, 2));
|
|
23
|
+
} else {
|
|
24
|
+
console.log('');
|
|
25
|
+
console.log(` ${c.err('✗')} ${plan.error}`);
|
|
26
|
+
if (plan.fix) console.log(c.cyan(` → fix: ${plan.fix}`));
|
|
27
|
+
console.log('');
|
|
28
|
+
}
|
|
29
|
+
return 1;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (json) {
|
|
33
|
+
console.log(JSON.stringify(plan, null, 2));
|
|
34
|
+
return 0;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Plain markdown to stdout — no ANSI, so it pipes cleanly into a convening.
|
|
38
|
+
console.log(renderContextMarkdown(plan));
|
|
39
|
+
return 0;
|
|
40
|
+
}
|