ff-lntools 0.1.0__tar.gz

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 (64) hide show
  1. ff_lntools-0.1.0/LICENSE +21 -0
  2. ff_lntools-0.1.0/PKG-INFO +94 -0
  3. ff_lntools-0.1.0/README.md +82 -0
  4. ff_lntools-0.1.0/pyproject.toml +27 -0
  5. ff_lntools-0.1.0/setup.cfg +4 -0
  6. ff_lntools-0.1.0/src/ff_lntools.egg-info/PKG-INFO +94 -0
  7. ff_lntools-0.1.0/src/ff_lntools.egg-info/SOURCES.txt +62 -0
  8. ff_lntools-0.1.0/src/ff_lntools.egg-info/dependency_links.txt +1 -0
  9. ff_lntools-0.1.0/src/ff_lntools.egg-info/entry_points.txt +2 -0
  10. ff_lntools-0.1.0/src/ff_lntools.egg-info/requires.txt +3 -0
  11. ff_lntools-0.1.0/src/ff_lntools.egg-info/top_level.txt +1 -0
  12. ff_lntools-0.1.0/src/lntools/__init__.py +3 -0
  13. ff_lntools-0.1.0/src/lntools/__main__.py +5 -0
  14. ff_lntools-0.1.0/src/lntools/for-agent-layerinfo.md +33 -0
  15. ff_lntools-0.1.0/src/lntools/l0/__init__.py +22 -0
  16. ff_lntools-0.1.0/src/lntools/l0/edge/__init__.py +3 -0
  17. ff_lntools-0.1.0/src/lntools/l0/edge/edge.py +20 -0
  18. ff_lntools-0.1.0/src/lntools/l0/for-agent-layerinfo-l0.md +40 -0
  19. ff_lntools-0.1.0/src/lntools/l0/initializer/__init__.py +3 -0
  20. ff_lntools-0.1.0/src/lntools/l0/initializer/initializer.py +63 -0
  21. ff_lntools-0.1.0/src/lntools/l0/module_ref/__init__.py +3 -0
  22. ff_lntools-0.1.0/src/lntools/l0/module_ref/module_ref.py +26 -0
  23. ff_lntools-0.1.0/src/lntools/l0/project_layout/__init__.py +3 -0
  24. ff_lntools-0.1.0/src/lntools/l0/project_layout/project_layout.py +51 -0
  25. ff_lntools-0.1.0/src/lntools/l0/raw_import/__init__.py +3 -0
  26. ff_lntools-0.1.0/src/lntools/l0/raw_import/raw_import.py +11 -0
  27. ff_lntools-0.1.0/src/lntools/l0/signature_extractor/__init__.py +3 -0
  28. ff_lntools-0.1.0/src/lntools/l0/signature_extractor/signature_extractor.py +78 -0
  29. ff_lntools-0.1.0/src/lntools/l0/violation/__init__.py +3 -0
  30. ff_lntools-0.1.0/src/lntools/l0/violation/violation.py +22 -0
  31. ff_lntools-0.1.0/src/lntools/l1/__init__.py +15 -0
  32. ff_lntools-0.1.0/src/lntools/l1/for-agent-layerinfo-l1.md +25 -0
  33. ff_lntools-0.1.0/src/lntools/l1/graph/__init__.py +3 -0
  34. ff_lntools-0.1.0/src/lntools/l1/graph/graph.py +92 -0
  35. ff_lntools-0.1.0/src/lntools/l1/layer_init_writer/__init__.py +3 -0
  36. ff_lntools-0.1.0/src/lntools/l1/layer_init_writer/layer_init_writer.py +39 -0
  37. ff_lntools-0.1.0/src/lntools/l1/scanner/__init__.py +3 -0
  38. ff_lntools-0.1.0/src/lntools/l1/scanner/scanner.py +189 -0
  39. ff_lntools-0.1.0/src/lntools/l2/__init__.py +15 -0
  40. ff_lntools-0.1.0/src/lntools/l2/blaster/__init__.py +3 -0
  41. ff_lntools-0.1.0/src/lntools/l2/blaster/blaster.py +23 -0
  42. ff_lntools-0.1.0/src/lntools/l2/checker/__init__.py +3 -0
  43. ff_lntools-0.1.0/src/lntools/l2/checker/checker.py +41 -0
  44. ff_lntools-0.1.0/src/lntools/l2/doc_generator/__init__.py +3 -0
  45. ff_lntools-0.1.0/src/lntools/l2/doc_generator/doc_generator.py +202 -0
  46. ff_lntools-0.1.0/src/lntools/l2/for-agent-layerinfo-l2.md +31 -0
  47. ff_lntools-0.1.0/src/lntools/l3/__init__.py +14 -0
  48. ff_lntools-0.1.0/src/lntools/l3/for-agent-layerinfo-l3.md +15 -0
  49. ff_lntools-0.1.0/src/lntools/l3/hook_runner/__init__.py +3 -0
  50. ff_lntools-0.1.0/src/lntools/l3/hook_runner/hook_runner.py +74 -0
  51. ff_lntools-0.1.0/src/lntools/l3/mover/__init__.py +3 -0
  52. ff_lntools-0.1.0/src/lntools/l3/mover/mover.py +164 -0
  53. ff_lntools-0.1.0/src/lntools/l4/__init__.py +14 -0
  54. ff_lntools-0.1.0/src/lntools/l4/cli/__init__.py +3 -0
  55. ff_lntools-0.1.0/src/lntools/l4/cli/cli.py +132 -0
  56. ff_lntools-0.1.0/src/lntools/l4/for-agent-layerinfo-l4.md +11 -0
  57. ff_lntools-0.1.0/src/lntools/protocol/CLAUDE-template.md +18 -0
  58. ff_lntools-0.1.0/src/lntools/protocol/SKILL-template.md +30 -0
  59. ff_lntools-0.1.0/src/lntools/protocol/for-agent-codingprotocol-ln-structure.md +367 -0
  60. ff_lntools-0.1.0/src/lntools/protocol/for-agent-codingprotocol-python.md +94 -0
  61. ff_lntools-0.1.0/src/lntools/protocol/for-agent-layerinfo-ln-template.md +109 -0
  62. ff_lntools-0.1.0/src/lntools/protocol/for-agent-layerinfo-template.md +47 -0
  63. ff_lntools-0.1.0/src/lntools/protocol/for-agent-moduleinfo-template.md +125 -0
  64. ff_lntools-0.1.0/src/lntools/protocol/settings-hooks-template.json +20 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 gatesplan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,94 @@
1
+ Metadata-Version: 2.4
2
+ Name: ff-lntools
3
+ Version: 0.1.0
4
+ Summary: ln-structure checker, blast radius, doc generator and Claude Code hooks
5
+ License: MIT
6
+ Requires-Python: >=3.10
7
+ Description-Content-Type: text/markdown
8
+ License-File: LICENSE
9
+ Provides-Extra: dev
10
+ Requires-Dist: pytest>=8; extra == "dev"
11
+ Dynamic: license-file
12
+
13
+ # ff-lntools
14
+
15
+ ln-structure 프로젝트용 정적 검사, 영향 범위, 문서 생성 도구와 Claude Code 훅.
16
+ 표준 라이브러리만 사용. Python 3.10+.
17
+
18
+ ln-structure 규칙: `src/lntools/protocol/for-agent-codingprotocol-ln-structure.md`
19
+
20
+ ## 설치
21
+
22
+ ```
23
+ pip install git+https://github.com/gatesplan/ff-lntools
24
+ ```
25
+
26
+ 프로젝트가 쓰는 Python 환경마다 설치한다. 훅이 `python -m lntools` 로 호출하기 때문이다.
27
+
28
+ ## 프로젝트 세팅
29
+
30
+ ```
31
+ lnt init --skill # 1회. ~/.claude/skills/init-protocol 스킬 설치
32
+ cd my-project
33
+ lnt init # .claude/ 에 프로토콜 문서 복사, CLAUDE.md 생성, 훅 병합
34
+ ```
35
+
36
+ Claude Code 에서는 `/init-protocol` 이 같은 일을 한다. 코딩 프로토콜 문서의 원본은 이 저장소의
37
+ `src/lntools/protocol/` 이다.
38
+
39
+ ## 명령
40
+
41
+ ```
42
+ lnt init [--skill] # 프로젝트에 코딩 프로토콜 설치
43
+ lnt check [--file PATH] # C1 방향, C2 표면, C3 층 일치, C4 순환. 위반 시 exit 1
44
+ lnt blast MODULE [-d N] # MODULE 변경 시 영향받는 상위 모듈. 인터페이스 상속 경유 포함
45
+ lnt map # 모듈 목록, 층, 의존
46
+ lnt doc # for-agent-layerinfo.md, for-agent-layerinfo-lN.md 생성
47
+ lnt doc --check # 생성 결과와 파일 비교. 다르면 exit 1
48
+ lnt doc --stamp MODULE # for-agent-moduleinfo.md 의 sources hash 갱신
49
+ lnt move MODULE lK # 모듈을 lK 로 이동. 패키지/tests 의 import 재작성, tests 미러 이동,
50
+ # 두 층의 __init__ 을 지연 re-export 로 재생성, 문서 재생성
51
+ lnt hook post-edit # Claude Code PostToolUse 훅 진입 (stdin JSON)
52
+ lnt hook session-start # Claude Code SessionStart 훅 진입
53
+ ```
54
+
55
+ `--root` 로 프로젝트 안의 아무 경로를 주면 `src/<pkg>/lN` 을 찾아 올라간다. 기본은 현재 디렉토리.
56
+
57
+ ## 검사 규칙
58
+
59
+ | 코드 | 내용 |
60
+ |---|---|
61
+ | C1 | import 대상 층이 자기 층보다 낮아야 한다. 같은 층도 위반. `TYPE_CHECKING` 안의 import 도 포함 |
62
+ | C2 | `pkg.lN.module` 표면까지만 import. 모듈 안의 파일이나 중첩 모듈 내부로 진입 금지 |
63
+ | C3 | 선언 층 == 계산 층 (의존 최고 층 + 1, 의존 없으면 0, 외부 패키지 의존이 있으면 최소 1) |
64
+ | C4 | 모듈 간 runtime import 순환 금지. 모듈 내부 파일 간 순환은 규칙 밖 |
65
+
66
+ ## Claude Code 훅
67
+
68
+ `.claude/settings.json`:
69
+
70
+ ```json
71
+ {
72
+ "hooks": {
73
+ "SessionStart": [{
74
+ "matcher": "startup|resume|clear|compact",
75
+ "hooks": [{"type": "command", "command": "python -m lntools hook session-start"}]
76
+ }],
77
+ "PostToolUse": [{
78
+ "matcher": "Edit|Write|MultiEdit",
79
+ "hooks": [{"type": "command", "command": "python -m lntools hook post-edit", "timeout": 30}]
80
+ }]
81
+ }
82
+ }
83
+ ```
84
+
85
+ - session-start: `src/<pkg>/for-agent-layerinfo.md` 를 세션 컨텍스트에 주입
86
+ - post-edit: 편집한 파일이 `src/**/*.py` 이면 그 모듈을 검사.
87
+ 위반은 exit 2 + stderr 로 에이전트에게 오류로 전달되고, 위반이 없으면 영향 범위와
88
+ 문서 stale 여부가 additionalContext 로 전달된다
89
+
90
+ ## 문서
91
+
92
+ - `for-agent-layerinfo.md` (패키지 루트, 중첩 모듈 루트): 층별 모듈 목록. 마커 사이는 생성, 한 줄 설명과 마커 밖은 보존
93
+ - `for-agent-layerinfo-lN.md` (각 층): 공개 클래스 메서드 시그니처. 마커 사이는 생성
94
+ - `for-agent-moduleinfo.md` (각 모듈): 사람이 쓴다. `sources` 헤더의 hash 로 stale 판정만 한다
@@ -0,0 +1,82 @@
1
+ # ff-lntools
2
+
3
+ ln-structure 프로젝트용 정적 검사, 영향 범위, 문서 생성 도구와 Claude Code 훅.
4
+ 표준 라이브러리만 사용. Python 3.10+.
5
+
6
+ ln-structure 규칙: `src/lntools/protocol/for-agent-codingprotocol-ln-structure.md`
7
+
8
+ ## 설치
9
+
10
+ ```
11
+ pip install git+https://github.com/gatesplan/ff-lntools
12
+ ```
13
+
14
+ 프로젝트가 쓰는 Python 환경마다 설치한다. 훅이 `python -m lntools` 로 호출하기 때문이다.
15
+
16
+ ## 프로젝트 세팅
17
+
18
+ ```
19
+ lnt init --skill # 1회. ~/.claude/skills/init-protocol 스킬 설치
20
+ cd my-project
21
+ lnt init # .claude/ 에 프로토콜 문서 복사, CLAUDE.md 생성, 훅 병합
22
+ ```
23
+
24
+ Claude Code 에서는 `/init-protocol` 이 같은 일을 한다. 코딩 프로토콜 문서의 원본은 이 저장소의
25
+ `src/lntools/protocol/` 이다.
26
+
27
+ ## 명령
28
+
29
+ ```
30
+ lnt init [--skill] # 프로젝트에 코딩 프로토콜 설치
31
+ lnt check [--file PATH] # C1 방향, C2 표면, C3 층 일치, C4 순환. 위반 시 exit 1
32
+ lnt blast MODULE [-d N] # MODULE 변경 시 영향받는 상위 모듈. 인터페이스 상속 경유 포함
33
+ lnt map # 모듈 목록, 층, 의존
34
+ lnt doc # for-agent-layerinfo.md, for-agent-layerinfo-lN.md 생성
35
+ lnt doc --check # 생성 결과와 파일 비교. 다르면 exit 1
36
+ lnt doc --stamp MODULE # for-agent-moduleinfo.md 의 sources hash 갱신
37
+ lnt move MODULE lK # 모듈을 lK 로 이동. 패키지/tests 의 import 재작성, tests 미러 이동,
38
+ # 두 층의 __init__ 을 지연 re-export 로 재생성, 문서 재생성
39
+ lnt hook post-edit # Claude Code PostToolUse 훅 진입 (stdin JSON)
40
+ lnt hook session-start # Claude Code SessionStart 훅 진입
41
+ ```
42
+
43
+ `--root` 로 프로젝트 안의 아무 경로를 주면 `src/<pkg>/lN` 을 찾아 올라간다. 기본은 현재 디렉토리.
44
+
45
+ ## 검사 규칙
46
+
47
+ | 코드 | 내용 |
48
+ |---|---|
49
+ | C1 | import 대상 층이 자기 층보다 낮아야 한다. 같은 층도 위반. `TYPE_CHECKING` 안의 import 도 포함 |
50
+ | C2 | `pkg.lN.module` 표면까지만 import. 모듈 안의 파일이나 중첩 모듈 내부로 진입 금지 |
51
+ | C3 | 선언 층 == 계산 층 (의존 최고 층 + 1, 의존 없으면 0, 외부 패키지 의존이 있으면 최소 1) |
52
+ | C4 | 모듈 간 runtime import 순환 금지. 모듈 내부 파일 간 순환은 규칙 밖 |
53
+
54
+ ## Claude Code 훅
55
+
56
+ `.claude/settings.json`:
57
+
58
+ ```json
59
+ {
60
+ "hooks": {
61
+ "SessionStart": [{
62
+ "matcher": "startup|resume|clear|compact",
63
+ "hooks": [{"type": "command", "command": "python -m lntools hook session-start"}]
64
+ }],
65
+ "PostToolUse": [{
66
+ "matcher": "Edit|Write|MultiEdit",
67
+ "hooks": [{"type": "command", "command": "python -m lntools hook post-edit", "timeout": 30}]
68
+ }]
69
+ }
70
+ }
71
+ ```
72
+
73
+ - session-start: `src/<pkg>/for-agent-layerinfo.md` 를 세션 컨텍스트에 주입
74
+ - post-edit: 편집한 파일이 `src/**/*.py` 이면 그 모듈을 검사.
75
+ 위반은 exit 2 + stderr 로 에이전트에게 오류로 전달되고, 위반이 없으면 영향 범위와
76
+ 문서 stale 여부가 additionalContext 로 전달된다
77
+
78
+ ## 문서
79
+
80
+ - `for-agent-layerinfo.md` (패키지 루트, 중첩 모듈 루트): 층별 모듈 목록. 마커 사이는 생성, 한 줄 설명과 마커 밖은 보존
81
+ - `for-agent-layerinfo-lN.md` (각 층): 공개 클래스 메서드 시그니처. 마커 사이는 생성
82
+ - `for-agent-moduleinfo.md` (각 모듈): 사람이 쓴다. `sources` 헤더의 hash 로 stale 판정만 한다
@@ -0,0 +1,27 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "ff-lntools"
7
+ version = "0.1.0"
8
+ description = "ln-structure checker, blast radius, doc generator and Claude Code hooks"
9
+ readme = "README.md"
10
+ license = {text = "MIT"}
11
+ requires-python = ">=3.10"
12
+ dependencies = []
13
+
14
+ [project.optional-dependencies]
15
+ dev = ["pytest>=8"]
16
+
17
+ [project.scripts]
18
+ lnt = "lntools.l4.cli:main"
19
+
20
+ [tool.setuptools.packages.find]
21
+ where = ["src"]
22
+
23
+ [tool.setuptools.package-data]
24
+ lntools = ["**/*.md", "protocol/*"]
25
+
26
+ [tool.pytest.ini_options]
27
+ testpaths = ["tests"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,94 @@
1
+ Metadata-Version: 2.4
2
+ Name: ff-lntools
3
+ Version: 0.1.0
4
+ Summary: ln-structure checker, blast radius, doc generator and Claude Code hooks
5
+ License: MIT
6
+ Requires-Python: >=3.10
7
+ Description-Content-Type: text/markdown
8
+ License-File: LICENSE
9
+ Provides-Extra: dev
10
+ Requires-Dist: pytest>=8; extra == "dev"
11
+ Dynamic: license-file
12
+
13
+ # ff-lntools
14
+
15
+ ln-structure 프로젝트용 정적 검사, 영향 범위, 문서 생성 도구와 Claude Code 훅.
16
+ 표준 라이브러리만 사용. Python 3.10+.
17
+
18
+ ln-structure 규칙: `src/lntools/protocol/for-agent-codingprotocol-ln-structure.md`
19
+
20
+ ## 설치
21
+
22
+ ```
23
+ pip install git+https://github.com/gatesplan/ff-lntools
24
+ ```
25
+
26
+ 프로젝트가 쓰는 Python 환경마다 설치한다. 훅이 `python -m lntools` 로 호출하기 때문이다.
27
+
28
+ ## 프로젝트 세팅
29
+
30
+ ```
31
+ lnt init --skill # 1회. ~/.claude/skills/init-protocol 스킬 설치
32
+ cd my-project
33
+ lnt init # .claude/ 에 프로토콜 문서 복사, CLAUDE.md 생성, 훅 병합
34
+ ```
35
+
36
+ Claude Code 에서는 `/init-protocol` 이 같은 일을 한다. 코딩 프로토콜 문서의 원본은 이 저장소의
37
+ `src/lntools/protocol/` 이다.
38
+
39
+ ## 명령
40
+
41
+ ```
42
+ lnt init [--skill] # 프로젝트에 코딩 프로토콜 설치
43
+ lnt check [--file PATH] # C1 방향, C2 표면, C3 층 일치, C4 순환. 위반 시 exit 1
44
+ lnt blast MODULE [-d N] # MODULE 변경 시 영향받는 상위 모듈. 인터페이스 상속 경유 포함
45
+ lnt map # 모듈 목록, 층, 의존
46
+ lnt doc # for-agent-layerinfo.md, for-agent-layerinfo-lN.md 생성
47
+ lnt doc --check # 생성 결과와 파일 비교. 다르면 exit 1
48
+ lnt doc --stamp MODULE # for-agent-moduleinfo.md 의 sources hash 갱신
49
+ lnt move MODULE lK # 모듈을 lK 로 이동. 패키지/tests 의 import 재작성, tests 미러 이동,
50
+ # 두 층의 __init__ 을 지연 re-export 로 재생성, 문서 재생성
51
+ lnt hook post-edit # Claude Code PostToolUse 훅 진입 (stdin JSON)
52
+ lnt hook session-start # Claude Code SessionStart 훅 진입
53
+ ```
54
+
55
+ `--root` 로 프로젝트 안의 아무 경로를 주면 `src/<pkg>/lN` 을 찾아 올라간다. 기본은 현재 디렉토리.
56
+
57
+ ## 검사 규칙
58
+
59
+ | 코드 | 내용 |
60
+ |---|---|
61
+ | C1 | import 대상 층이 자기 층보다 낮아야 한다. 같은 층도 위반. `TYPE_CHECKING` 안의 import 도 포함 |
62
+ | C2 | `pkg.lN.module` 표면까지만 import. 모듈 안의 파일이나 중첩 모듈 내부로 진입 금지 |
63
+ | C3 | 선언 층 == 계산 층 (의존 최고 층 + 1, 의존 없으면 0, 외부 패키지 의존이 있으면 최소 1) |
64
+ | C4 | 모듈 간 runtime import 순환 금지. 모듈 내부 파일 간 순환은 규칙 밖 |
65
+
66
+ ## Claude Code 훅
67
+
68
+ `.claude/settings.json`:
69
+
70
+ ```json
71
+ {
72
+ "hooks": {
73
+ "SessionStart": [{
74
+ "matcher": "startup|resume|clear|compact",
75
+ "hooks": [{"type": "command", "command": "python -m lntools hook session-start"}]
76
+ }],
77
+ "PostToolUse": [{
78
+ "matcher": "Edit|Write|MultiEdit",
79
+ "hooks": [{"type": "command", "command": "python -m lntools hook post-edit", "timeout": 30}]
80
+ }]
81
+ }
82
+ }
83
+ ```
84
+
85
+ - session-start: `src/<pkg>/for-agent-layerinfo.md` 를 세션 컨텍스트에 주입
86
+ - post-edit: 편집한 파일이 `src/**/*.py` 이면 그 모듈을 검사.
87
+ 위반은 exit 2 + stderr 로 에이전트에게 오류로 전달되고, 위반이 없으면 영향 범위와
88
+ 문서 stale 여부가 additionalContext 로 전달된다
89
+
90
+ ## 문서
91
+
92
+ - `for-agent-layerinfo.md` (패키지 루트, 중첩 모듈 루트): 층별 모듈 목록. 마커 사이는 생성, 한 줄 설명과 마커 밖은 보존
93
+ - `for-agent-layerinfo-lN.md` (각 층): 공개 클래스 메서드 시그니처. 마커 사이는 생성
94
+ - `for-agent-moduleinfo.md` (각 모듈): 사람이 쓴다. `sources` 헤더의 hash 로 stale 판정만 한다
@@ -0,0 +1,62 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ src/ff_lntools.egg-info/PKG-INFO
5
+ src/ff_lntools.egg-info/SOURCES.txt
6
+ src/ff_lntools.egg-info/dependency_links.txt
7
+ src/ff_lntools.egg-info/entry_points.txt
8
+ src/ff_lntools.egg-info/requires.txt
9
+ src/ff_lntools.egg-info/top_level.txt
10
+ src/lntools/__init__.py
11
+ src/lntools/__main__.py
12
+ src/lntools/for-agent-layerinfo.md
13
+ src/lntools/l0/__init__.py
14
+ src/lntools/l0/for-agent-layerinfo-l0.md
15
+ src/lntools/l0/edge/__init__.py
16
+ src/lntools/l0/edge/edge.py
17
+ src/lntools/l0/initializer/__init__.py
18
+ src/lntools/l0/initializer/initializer.py
19
+ src/lntools/l0/module_ref/__init__.py
20
+ src/lntools/l0/module_ref/module_ref.py
21
+ src/lntools/l0/project_layout/__init__.py
22
+ src/lntools/l0/project_layout/project_layout.py
23
+ src/lntools/l0/raw_import/__init__.py
24
+ src/lntools/l0/raw_import/raw_import.py
25
+ src/lntools/l0/signature_extractor/__init__.py
26
+ src/lntools/l0/signature_extractor/signature_extractor.py
27
+ src/lntools/l0/violation/__init__.py
28
+ src/lntools/l0/violation/violation.py
29
+ src/lntools/l1/__init__.py
30
+ src/lntools/l1/for-agent-layerinfo-l1.md
31
+ src/lntools/l1/graph/__init__.py
32
+ src/lntools/l1/graph/graph.py
33
+ src/lntools/l1/layer_init_writer/__init__.py
34
+ src/lntools/l1/layer_init_writer/layer_init_writer.py
35
+ src/lntools/l1/scanner/__init__.py
36
+ src/lntools/l1/scanner/scanner.py
37
+ src/lntools/l2/__init__.py
38
+ src/lntools/l2/for-agent-layerinfo-l2.md
39
+ src/lntools/l2/blaster/__init__.py
40
+ src/lntools/l2/blaster/blaster.py
41
+ src/lntools/l2/checker/__init__.py
42
+ src/lntools/l2/checker/checker.py
43
+ src/lntools/l2/doc_generator/__init__.py
44
+ src/lntools/l2/doc_generator/doc_generator.py
45
+ src/lntools/l3/__init__.py
46
+ src/lntools/l3/for-agent-layerinfo-l3.md
47
+ src/lntools/l3/hook_runner/__init__.py
48
+ src/lntools/l3/hook_runner/hook_runner.py
49
+ src/lntools/l3/mover/__init__.py
50
+ src/lntools/l3/mover/mover.py
51
+ src/lntools/l4/__init__.py
52
+ src/lntools/l4/for-agent-layerinfo-l4.md
53
+ src/lntools/l4/cli/__init__.py
54
+ src/lntools/l4/cli/cli.py
55
+ src/lntools/protocol/CLAUDE-template.md
56
+ src/lntools/protocol/SKILL-template.md
57
+ src/lntools/protocol/for-agent-codingprotocol-ln-structure.md
58
+ src/lntools/protocol/for-agent-codingprotocol-python.md
59
+ src/lntools/protocol/for-agent-layerinfo-ln-template.md
60
+ src/lntools/protocol/for-agent-layerinfo-template.md
61
+ src/lntools/protocol/for-agent-moduleinfo-template.md
62
+ src/lntools/protocol/settings-hooks-template.json
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ lnt = lntools.l4.cli:main
@@ -0,0 +1,3 @@
1
+
2
+ [dev]
3
+ pytest>=8
@@ -0,0 +1 @@
1
+ lntools
@@ -0,0 +1,3 @@
1
+ from .l4.cli import main
2
+
3
+ __all__ = ["main"]
@@ -0,0 +1,5 @@
1
+ import sys
2
+
3
+ from .l4.cli import main
4
+
5
+ sys.exit(main())
@@ -0,0 +1,33 @@
1
+ # for-agent-layerinfo.md
2
+
3
+ <!-- lnt:generated:start -->
4
+ ## l0
5
+ - edge: 모듈 간 의존 간선. kind runtime/type_only/inherits, 표면 초과 경로 extra
6
+ - initializer: lnt init. 프로토콜 문서 복사, CLAUDE.md, 훅 병합, 스킬 설치
7
+ - module_ref: ln 모듈 하나. 이름, 스코프, 선언 층, 파일 목록, 외부 의존 여부
8
+ - project_layout: 프로젝트 루트와 src/<pkg> 탐색. LAYER_RE
9
+ - raw_import: 해석 전 import 문 하나. target, names, kind, external
10
+ - signature_extractor: 모듈 __init__ 공개 이름과 클래스 메서드 시그니처를 ast 로 추출
11
+ - violation: 규칙 위반 C1~C4 레코드와 포맷
12
+
13
+ ## l1
14
+ - graph: 모듈 그래프. 계산 층, 역의존, 인터페이스 경유 blast, 순환 탐지
15
+ - layer_init_writer: 층 __init__.py 를 PEP 562 지연 re-export 형태로 생성
16
+ - scanner: src/<pkg> 를 훑어 모듈과 간선 생성. 상대/절대/층 단위 import 해석, TYPE_CHECKING 구분
17
+
18
+ ## l2
19
+ - blaster: blast 결과 텍스트 보고서
20
+ - checker: C1 방향, C2 표면, C3 층 일치, C4 순환 검사
21
+ - doc_generator: layerinfo/layerinfo-ln 생성과 검사, moduleinfo sources hash stamp/stale
22
+
23
+ ## l3
24
+ - hook_runner: Claude Code 훅 진입. stdin JSON 해석, exit 2 / additionalContext 채널 선택
25
+ - mover: lnt move. 모듈 층 이동, import 재작성, tests 미러, 층 __init__ 와 문서 재생성
26
+
27
+ ## l4
28
+ - cli: lnt 명령줄. check, blast, map, doc, hook
29
+ <!-- lnt:generated:end -->
30
+
31
+ ## Notes
32
+
33
+ 의존은 항상 아래로만. l4 cli 가 진입점이며 모든 하위를 조립한다.
@@ -0,0 +1,22 @@
1
+ import importlib
2
+
3
+ # 공개 이름 -> 모듈 폴더명. 지연 로드
4
+ _EXPORTS = {
5
+ "Edge": "edge",
6
+ "Initializer": "initializer",
7
+ "LAYER_RE": "project_layout",
8
+ "PROTOCOL_FILES": "initializer",
9
+ "ModuleRef": "module_ref",
10
+ "ProjectLayout": "project_layout",
11
+ "RawImport": "raw_import",
12
+ "SignatureExtractor": "signature_extractor",
13
+ "Violation": "violation",
14
+ }
15
+ __all__ = list(_EXPORTS)
16
+
17
+
18
+ def __getattr__(name: str):
19
+ if name in _EXPORTS:
20
+ mod = importlib.import_module(f".{_EXPORTS[name]}", __name__)
21
+ return getattr(mod, name)
22
+ raise AttributeError(name)
@@ -0,0 +1,3 @@
1
+ from .edge import Edge
2
+
3
+ __all__ = ["Edge"]
@@ -0,0 +1,20 @@
1
+ from dataclasses import dataclass
2
+ from pathlib import Path
3
+
4
+
5
+ # 모듈 간 의존 간선. import 문 하나가 간선 하나
6
+ @dataclass
7
+ class Edge:
8
+ src: str # 출발 모듈 이름
9
+ dst: str | None # 도착 모듈 이름. 층 단위 import에서 이름을 못 풀면 None
10
+ dst_layer: int # 도착 층 번호
11
+ kind: str # runtime | type_only | inherits
12
+ file: Path # import 문이 있는 파일
13
+ line: int
14
+ target: str # import 문에 쓰인 점 경로 (패키지명 제외)
15
+ extra: str = "" # 도착 모듈 표면을 지나 들어간 나머지 경로. 비어있지 않으면 표면 위반
16
+
17
+ @property
18
+ def counts_for_layer(self) -> bool:
19
+ # 층 계산에 포함되는 간선. 상속 간선은 이미 runtime 간선이 따로 있으므로 제외
20
+ return self.kind in ("runtime", "type_only")
@@ -0,0 +1,40 @@
1
+ # l0
2
+
3
+ <!-- lnt:generated:start -->
4
+ ## edge
5
+ Edge.counts_for_layer() -> bool # edge.py
6
+
7
+ ## initializer
8
+ Initializer.__init__(project_root: Path, home: Path | None=None) # initializer.py
9
+ Initializer.run() -> list[str] # initializer.py
10
+ Initializer.merge_hooks() -> str # initializer.py
11
+ Initializer.install_skill() -> Path # initializer.py
12
+ PROTOCOL_FILES # 정의를 찾지 못함
13
+
14
+ ## module_ref
15
+ ModuleRef.basename() -> str # module_ref.py
16
+ ModuleRef.layer_name() -> str # module_ref.py
17
+ ModuleRef.contains(other: str) -> bool # module_ref.py
18
+
19
+ ## project_layout
20
+ ProjectLayout.__init__(project_root: Path, package_root: Path) # project_layout.py
21
+ ProjectLayout.package_name() -> str # project_layout.py
22
+ ProjectLayout.src_dir() -> Path # project_layout.py
23
+ ProjectLayout.has_layers(d: Path) -> bool # project_layout.py
24
+ ProjectLayout.find(start: Path) -> 'ProjectLayout | None' # project_layout.py
25
+ ProjectLayout.relative(path: Path) -> str # project_layout.py
26
+ LAYER_RE # 정의를 찾지 못함
27
+
28
+ ## raw_import
29
+ RawImport # raw_import.py (공개 메서드 없음)
30
+
31
+ ## signature_extractor
32
+ SignatureExtractor.exports_of(module_dir: Path) -> list[str] # signature_extractor.py
33
+ SignatureExtractor.extract(module_dir: Path, exported: list[str]) -> list[str] # signature_extractor.py
34
+
35
+ ## violation
36
+ Violation.format(base: Path | None=None) -> str # violation.py
37
+ <!-- lnt:generated:end -->
38
+
39
+ ## Notes
40
+
@@ -0,0 +1,3 @@
1
+ from .initializer import Initializer, PROTOCOL_FILES
2
+
3
+ __all__ = ["Initializer", "PROTOCOL_FILES"]
@@ -0,0 +1,63 @@
1
+ import json
2
+ from importlib import resources
3
+ from pathlib import Path
4
+
5
+ PROTOCOL_FILES = [
6
+ "for-agent-codingprotocol-ln-structure.md",
7
+ "for-agent-codingprotocol-python.md",
8
+ "for-agent-layerinfo-template.md",
9
+ "for-agent-layerinfo-ln-template.md",
10
+ "for-agent-moduleinfo-template.md",
11
+ ]
12
+
13
+
14
+ # 프로젝트에 코딩 프로토콜을 설치한다. 문서 복사, CLAUDE.md, 훅 병합, 스킬 설치
15
+ class Initializer:
16
+ def __init__(self, project_root: Path, home: Path | None = None):
17
+ self.project_root = project_root
18
+ self.home = home if home is not None else Path.home()
19
+ self.claude_dir = project_root / ".claude"
20
+
21
+ @staticmethod
22
+ def _protocol(name: str) -> str:
23
+ return (resources.files("lntools") / "protocol" / name).read_text(encoding="utf-8")
24
+
25
+ # 반환: 한 일의 설명 줄 목록
26
+ def run(self) -> list[str]:
27
+ done: list[str] = []
28
+ self.claude_dir.mkdir(parents=True, exist_ok=True)
29
+ for name in PROTOCOL_FILES:
30
+ (self.claude_dir / name).write_text(self._protocol(name), encoding="utf-8")
31
+ done.append(f".claude/{name}")
32
+ claude_md = self.project_root / "CLAUDE.md"
33
+ if claude_md.is_file():
34
+ done.append("CLAUDE.md 이미 존재 (건너뜀)")
35
+ else:
36
+ claude_md.write_text(self._protocol("CLAUDE-template.md"), encoding="utf-8")
37
+ done.append("CLAUDE.md 생성")
38
+ done.append(self.merge_hooks())
39
+ return done
40
+
41
+ # settings.json 의 hooks 에 템플릿 훅을 합친다. 같은 command 가 이미 있으면 건너뛴다
42
+ def merge_hooks(self) -> str:
43
+ tpl = json.loads(self._protocol("settings-hooks-template.json"))
44
+ dst = self.claude_dir / "settings.json"
45
+ cur = json.loads(dst.read_text(encoding="utf-8")) if dst.is_file() else {}
46
+ hooks = cur.setdefault("hooks", {})
47
+ added = 0
48
+ for event, groups in tpl["hooks"].items():
49
+ existing = hooks.setdefault(event, [])
50
+ have = {h.get("command") for g in existing for h in g.get("hooks", [])}
51
+ for g in groups:
52
+ if not any(h.get("command") in have for h in g["hooks"]):
53
+ existing.append(g)
54
+ added += 1
55
+ dst.write_text(json.dumps(cur, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
56
+ return f".claude/settings.json 훅 {added}개 추가" if added else ".claude/settings.json 훅 이미 있음"
57
+
58
+ # ~/.claude/skills/init-protocol/SKILL.md 설치
59
+ def install_skill(self) -> Path:
60
+ p = self.home / ".claude" / "skills" / "init-protocol" / "SKILL.md"
61
+ p.parent.mkdir(parents=True, exist_ok=True)
62
+ p.write_text(self._protocol("SKILL-template.md"), encoding="utf-8")
63
+ return p
@@ -0,0 +1,3 @@
1
+ from .module_ref import ModuleRef
2
+
3
+ __all__ = ["ModuleRef"]
@@ -0,0 +1,26 @@
1
+ from dataclasses import dataclass, field
2
+ from pathlib import Path
3
+
4
+
5
+ # ln-structure 모듈 하나. 이름은 패키지 루트 기준 점 경로 (예: l1.order, l3.portfolio.l1.store)
6
+ @dataclass
7
+ class ModuleRef:
8
+ name: str
9
+ scope: str # 소속 스코프. 루트는 "", 중첩 모듈 내부는 감싸는 모듈 이름
10
+ layer: int # 선언 층 (디렉토리 번호)
11
+ path: Path # 모듈 디렉토리
12
+ files: list[Path] = field(default_factory=list) # 모듈에 속한 모든 .py (중첩 내부 포함)
13
+ has_external: bool = False # 표준 라이브러리 외 패키지 import 여부
14
+ is_nested: bool = False # 내부에 lN 디렉토리를 가지는지
15
+
16
+ @property
17
+ def basename(self) -> str:
18
+ return self.name.rsplit(".", 1)[-1]
19
+
20
+ @property
21
+ def layer_name(self) -> str:
22
+ # 소속 층의 이름. 예: l1, l3.portfolio.l1
23
+ return self.name.rsplit(".", 1)[0]
24
+
25
+ def contains(self, other: str) -> bool:
26
+ return other.startswith(self.name + ".")
@@ -0,0 +1,3 @@
1
+ from .project_layout import ProjectLayout, LAYER_RE
2
+
3
+ __all__ = ["ProjectLayout", "LAYER_RE"]